Changeset 2791 for trunk/src/kLibTweaker


Ignore:
Timestamp:
Sep 16, 2015, 12:57:44 AM (10 years ago)
Author:
bird
Message:

kLibTweaker: New tool for playing with Microsoft import libraries (currently).

Location:
trunk/src/kLibTweaker
Files:
1 added
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/kLibTweaker/Makefile.kmk

    r2787 r2791  
    11# $Id$
    22## @file
    3 # Sub-makefile for kObjCache.
     3# Sub-makefile for kLibTweaker.
    44#
    55
    66#
    7 # Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
     7# Copyright (c) 2007-2015 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
    88#
    99# This file is part of kBuild.
     
    2727include $(PATH_KBUILD)/subheader.kmk
    2828
    29 PROGRAMS += kObjCache
    30 kObjCache_TEMPLATE = BIN
    31 kObjCache_DEFS.release = NASSERT
    32 kObjCache_SOURCES = kObjCache.c
    33 kObjCache_LIBS = \
     29PROGRAMS += kLibTweaker
     30kLibTweaker_TEMPLATE = BIN
     31kLibTweaker_DEFS.release = NASSERT
     32kLibTweaker_SOURCES = kLibTweaker.c
     33kLibTweaker_INCS = ../lib
     34kLibTweaker_LIBS = \
    3435        $(LIB_KDEP) \
    3536        $(LIB_KUTIL)
  • trunk/src/kLibTweaker/kLibTweaker.c

    r2787 r2791  
    11/* $Id$ */
    22/** @file
    3  * kObjCache - Object Cache.
     3 * kLibTweaker - Import library tweaker for windows.
    44 */
    55
    66/*
    7  * Copyright (c) 2007-2012 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
     7 * Copyright (c) 2007-2015 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
    88 *
    99 * This file is part of kBuild.
     
    2424 */
    2525
    26 /*******************************************************************************
    27 *   Header Files                                                               *
    28 *******************************************************************************/
     26/*********************************************************************************************************************************
     27*   Header Files                                                                                                                 *
     28*********************************************************************************************************************************/
    2929#if 0
    3030# define ELECTRIC_HEAP
     
    3232# include "../kmk/electric.c"
    3333#endif
     34#include <ctype.h>
    3435#include <string.h>
    3536#include <stdlib.h>
     
    3839#include <errno.h>
    3940#include <assert.h>
    40 #include <sys/stat.h>
    41 #include <fcntl.h>
    42 #include <limits.h>
    43 #include <ctype.h>
    44 #ifndef PATH_MAX
    45 # define PATH_MAX _MAX_PATH /* windows */
    46 #endif
    47 #if defined(__OS2__) || defined(__WIN__)
    48 # include <process.h>
    49 # include <io.h>
    50 # ifdef __OS2__
    51 #  include <unistd.h>
    52 #  include <sys/wait.h>
    53 #  include <sys/time.h>
    54 # endif
    55 # if defined(_MSC_VER)
    56 #  include <direct.h>
    57    typedef intptr_t pid_t;
    58 # endif
    59 # ifndef _P_WAIT
    60 #  define _P_WAIT   P_WAIT
    61 # endif
    62 # ifndef _P_NOWAIT
    63 #  define _P_NOWAIT P_NOWAIT
    64 # endif
    65 #else
    66 # include <unistd.h>
    67 # include <sys/wait.h>
    68 # include <sys/time.h>
    69 # ifndef O_BINARY
    70 #  define O_BINARY 0
    71 # endif
    72 #endif
    73 #if defined(__WIN__)
    74 # include <Windows.h>
    75 # include "quoted_spawn.h"
    76 #endif
    77 #if defined(__HAIKU__)
    78 # include <posix/sys/file.h>
    79 #endif
    80 
    81 #include "crc32.h"
    82 #include "md5.h"
    83 #include "kDep.h"
    84 
    85 
    86 /*******************************************************************************
    87 *   Defined Constants And Macros                                               *
    88 *******************************************************************************/
    89 /** The max line length in a cache file. */
    90 #define KOBJCACHE_MAX_LINE_LEN  16384
    91 #if defined(__WIN__)
    92 # define PATH_SLASH '\\'
    93 #else
    94 # define PATH_SLASH '/'
    95 #endif
    96 #if defined(__OS2__) || defined(__WIN__)
    97 # define IS_SLASH(ch)       ((ch) == '/' || (ch) == '\\')
    98 # define IS_SLASH_DRV(ch)   ((ch) == '/' || (ch) == '\\' || (ch) == ':')
    99 #else
    100 # define IS_SLASH(ch)       ((ch) == '/')
    101 # define IS_SLASH_DRV(ch)   ((ch) == '/')
    102 #endif
    103 
    104 #ifndef STDIN_FILENO
    105 # define STDIN_FILENO 0
    106 #endif
    107 #ifndef STDOUT_FILENO
    108 # define STDOUT_FILENO 1
    109 #endif
    110 #ifndef STDERR_FILENO
    111 # define STDERR_FILENO 2
    112 #endif
    113 
    114 #define MY_IS_BLANK(a_ch)   ((a_ch) == ' ' || (a_ch) == '\t')
    115 
    116 #define KOC_BUF_MIN         KOC_BUF_ALIGNMENT
    117 #define KOC_BUF_INCR        KOC_BUF_ALIGNMENT
    118 #define KOC_BUF_ALIGNMENT   (4U*1024U*1024U)
    119 
    120 
    121 /*******************************************************************************
    122 *   Global Variables                                                           *
    123 *******************************************************************************/
     41
     42#include "k/kLdrFmts/pe.h"
     43
     44
     45/*********************************************************************************************************************************
     46*   Structures and Typedefs                                                                                                      *
     47*********************************************************************************************************************************/
     48/**
     49 * Microsoft import library archive header.
     50 *
     51 * This has the same size as a COFF header, which is probably not a coincidence.
     52 */
     53typedef struct COFFIMPLIBHDR
     54{
     55    KU16            uSig1;    /* 0 */
     56    KU16            uSig2;    /* 0xffff */
     57    KU16            uVersion; /* 0 */
     58    KU16            uMachine; /* IMAGE_FILE_MACHINE_I386, ... */
     59    KU32            uTimeDateStamp;
     60    KU32            cbData;
     61    KU16            uOrdinalOrHint;
     62    KU16            uFlags;
     63} COFFIMPLIBHDR;
     64
     65/**
     66 * COFF symbol.
     67 *
     68 * This one has an odd size and will cause misaligned accesses on platforms
     69 * which cares about such.
     70 */
     71#pragma pack(1)
     72typedef struct COFFSYMBOL
     73{
     74    union
     75    {
     76        char        e_name[8];
     77        struct
     78        {
     79            KU32    e_zeros;    /**< Zero to distinguish it from ascii name. */
     80            KU32    e_offset;   /**< String table offset. */
     81        } e;
     82    } e;
     83    KU32            e_value;
     84    KI16            e_scnum;
     85    KU16            e_type;
     86    KU8             e_sclass;
     87    KU8             e_numaux;
     88} COFFSYMBOL;
     89#pragma pack()
     90
     91/**
     92 * Archive file header.
     93 */
     94typedef struct ARCHFILEHDR
     95{
     96    char        achName[16];
     97    char        achModtime[12];
     98    char        achOwnerId[6];
     99    char        achGroupId[6];
     100    char        achMode[8];
     101    char        achSize[10];
     102    char        achMagic[2];
     103} ARCHFILEHDR;
     104
     105
     106/*********************************************************************************************************************************
     107*   Global Variables                                                                                                             *
     108*********************************************************************************************************************************/
    124109/** Whether verbose output is enabled. */
    125110static unsigned g_cVerbosityLevel = 0;
     
    127112static char g_szErrorPrefix[128];
    128113
    129 /** Read buffer shared by the cache components. */
    130 static char g_szLine[KOBJCACHE_MAX_LINE_LEN + 16];
    131 
    132 /** How many times we've moved memory around. */
    133 static size_t g_cMemMoves = 0;
    134 /** How much memory we've moved. */
    135 static size_t g_cbMemMoved = 0;
    136 
    137 
    138 /*******************************************************************************
    139 *   Internal Functions                                                         *
    140 *******************************************************************************/
    141 static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir);
    142 static char *CalcRelativeName(const char *pszPath, const char *pszDir);
    143 static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode);
    144 static int   UnlinkFileInDir(const char *pszName, const char *pszDir);
    145 static int   RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir);
    146 static int   DoesFileInDirExist(const char *pszName, const char *pszDir);
    147 static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile);
    148 
    149114
    150115void FatalMsg(const char *pszFormat, ...)
     
    180145
    181146
    182 #if 0 /* unused */
    183 static void ErrorMsg(const char *pszFormat, ...)
     147static int ErrorMsg(const char *pszFormat, ...)
    184148{
    185149    va_list va;
     
    193157    vfprintf(stderr, pszFormat, va);
    194158    va_end(va);
    195 }
    196 #endif /* unused */
     159
     160    return 1;
     161}
    197162
    198163
     
    275240
    276241
    277 
    278 
    279 
    280 /**
    281  * Returns a millisecond timestamp.
    282  *
    283  * @returns Millisecond timestamp.
    284  */
    285 static uint32_t NowMs(void)
    286 {
    287 #if defined(__WIN__)
    288     return GetTickCount();
    289 #else
    290     int             iSavedErrno = errno;
    291     struct timeval  tv          = {0, 0};
    292 
    293     gettimeofday(&tv, NULL);
    294     errno = iSavedErrno;
    295 
    296     return tv.tv_sec * 1000 + tv.tv_usec / 1000;
    297 #endif
    298 }
    299 
    300 
    301 /**
    302  * Gets the absolute path
    303  *
    304  * @returns A new heap buffer containing the absolute path.
    305  * @param   pszPath     The path to make absolute. (Readonly)
    306  */
    307 static char *AbsPath(const char *pszPath)
    308 {
    309 /** @todo this isn't really working as it should... */
    310     char szTmp[PATH_MAX];
    311 #if defined(__OS2__)
    312     if (    _fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp))
    313         &&  !realpath(pszPath, szTmp))
    314         return xstrdup(pszPath);
    315 #elif defined(__WIN__)
    316     if (!_fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp)))
    317         return xstrdup(pszPath);
    318 #else
    319     if (!realpath(pszPath, szTmp))
    320         return xstrdup(pszPath);
    321 #endif
    322    return xstrdup(szTmp);
    323 }
    324 
    325 
    326 /**
    327  * Utility function that finds the filename part in a path.
    328  *
    329  * @returns Pointer to the file name part (this may be "").
    330  * @param   pszPath     The path to parse.
    331  */
    332 static const char *FindFilenameInPath(const char *pszPath)
    333 {
    334     const char *pszFilename = strchr(pszPath, '\0') - 1;
    335     if (pszFilename < pszPath)
    336         return pszPath;
    337     while (     pszFilename > pszPath
    338            &&   !IS_SLASH_DRV(pszFilename[-1]))
    339         pszFilename--;
    340     return pszFilename;
    341 }
    342 
    343 
    344 /**
    345  * Utility function that combines a filename and a directory into a path.
    346  *
    347  * @returns malloced buffer containing the result.
    348  * @param   pszName     The file name.
    349  * @param   pszDir      The directory path.
    350  */
    351 static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir)
    352 {
    353     size_t cchName = strlen(pszName);
    354     size_t cchDir = strlen(pszDir);
    355     char *pszBuf = xmalloc(cchName + cchDir + 2);
    356     memcpy(pszBuf, pszDir, cchDir);
    357     if (cchDir > 0 && !IS_SLASH_DRV(pszDir[cchDir - 1]))
    358         pszBuf[cchDir++] = PATH_SLASH;
    359     memcpy(pszBuf + cchDir, pszName, cchName + 1);
    360     return pszBuf;
    361 }
    362 
    363 
    364 /**
    365  * Compares two path strings to see if they are identical.
    366  *
    367  * This doesn't do anything fancy, just the case ignoring and
    368  * slash unification.
    369  *
    370  * @returns 1 if equal, 0 otherwise.
    371  * @param   pszPath1    The first path.
    372  * @param   pszPath2    The second path.
    373  */
    374 static int ArePathsIdentical(const char *pszPath1, const char *pszPath2)
    375 {
    376 #if defined(__OS2__) || defined(__WIN__)
    377     if (stricmp(pszPath1, pszPath2))
    378     {
    379         /* Slashes may differ, compare char by char. */
    380         const char *psz1 = pszPath1;
    381         const char *psz2 = pszPath2;
    382         for (;;)
    383         {
    384             if (*psz1 != *psz2)
    385             {
    386                 if (    tolower(*psz1) != tolower(*psz2)
    387                     &&  toupper(*psz1) != toupper(*psz2)
    388                     &&  *psz1 != '/'
    389                     &&  *psz1 != '\\'
    390                     &&  *psz2 != '/'
    391                     &&  *psz2 != '\\')
    392                     return 0;
    393             }
    394             if (!*psz1)
    395                 break;
    396             psz1++;
    397             psz2++;
    398         }
    399     }
    400     return 1;
    401 #else
    402     return !strcmp(pszPath1, pszPath2);
    403 #endif
    404 }
    405 
    406 /**
    407  * Compares two path strings to see if they are identical.
    408  *
    409  * This doesn't do anything fancy, just the case ignoring and
    410  * slash unification.
    411  *
    412  * @returns 1 if equal, 0 otherwise.
    413  * @param   pszPath1    The first path.
    414  * @param   pszPath2    The second path.
    415  * @param   cch         The number of characters to compare.
    416  */
    417 static int ArePathsIdenticalN(const char *pszPath1, const char *pszPath2, size_t cch)
    418 {
    419 #if defined(__OS2__) || defined(__WIN__)
    420     if (strnicmp(pszPath1, pszPath2, cch))
    421     {
    422         /* Slashes may differ, compare char by char. */
    423         const char *psz1 = pszPath1;
    424         const char *psz2 = pszPath2;
    425         for ( ; cch; psz1++, psz2++, cch--)
    426         {
    427             if (*psz1 != *psz2)
    428             {
    429                 if (    tolower(*psz1) != tolower(*psz2)
    430                     &&  toupper(*psz1) != toupper(*psz2)
    431                     &&  *psz1 != '/'
    432                     &&  *psz1 != '\\'
    433                     &&  *psz2 != '/'
    434                     &&  *psz2 != '\\')
    435                     return 0;
    436             }
    437         }
    438     }
    439     return 1;
    440 #else
    441     return !strncmp(pszPath1, pszPath2, cch);
    442 #endif
    443 }
    444 
    445 
    446 /**
    447  * Calculate how to get to pszPath from pszDir.
    448  *
    449  * @returns The relative path from pszDir to path pszPath.
    450  * @param   pszPath     The path to the object.
    451  * @param   pszDir      The directory it shall be relative to.
    452  */
    453 static char *CalcRelativeName(const char *pszPath, const char *pszDir)
    454 {
    455     char *pszRet = NULL;
    456     char *pszAbsPath = NULL;
    457     size_t cchDir = strlen(pszDir);
    458 
    459     /*
    460      * This is indeed a bit tricky, so we'll try the easy way first...
    461      */
    462     if (ArePathsIdenticalN(pszPath, pszDir, cchDir))
    463     {
    464         if (pszPath[cchDir])
    465             pszRet = (char *)pszPath + cchDir;
    466         else
    467             pszRet = "./";
    468     }
    469     else
    470     {
    471         pszAbsPath = AbsPath(pszPath);
    472         if (ArePathsIdenticalN(pszAbsPath, pszDir, cchDir))
    473         {
    474             if (pszPath[cchDir])
    475                 pszRet = pszAbsPath + cchDir;
    476             else
    477                 pszRet = "./";
    478         }
    479     }
    480     if (pszRet)
    481     {
    482         while (IS_SLASH_DRV(*pszRet))
    483             pszRet++;
    484         pszRet = xstrdup(pszRet);
    485         free(pszAbsPath);
    486         return pszRet;
    487     }
    488 
    489     /*
    490      * Damn, it's gonna be complicated. Deal with that later.
    491      */
    492     FatalDie("complicated relative path stuff isn't implemented yet. sorry.\n");
    493     return NULL;
    494 }
    495 
    496 
    497 /**
    498  * Utility function that combines a filename and directory and passes it onto fopen.
    499  *
    500  * @returns fopen return value.
    501  * @param   pszName     The file name.
    502  * @param   pszDir      The directory path.
    503  * @param   pszMode     The fopen mode string.
    504  */
    505 static FILE *FOpenFileInDir(const char *pszName, const char *pszDir, const char *pszMode)
    506 {
    507     char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
    508     FILE *pFile = fopen(pszPath, pszMode);
    509     free(pszPath);
    510     return pFile;
    511 }
    512 
    513 
    514 /**
    515  * Utility function that combines a filename and directory and passes it onto open.
    516  *
    517  * @returns open return value.
    518  * @param   pszName     The file name.
    519  * @param   pszDir      The directory path.
    520  * @param   fFlags      The open flags.
    521  * @param   fCreateMode The file creation mode.
    522  */
    523 static int OpenFileInDir(const char *pszName, const char *pszDir, int fFlags, int fCreateMode)
    524 {
    525     char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
    526     int fd = open(pszPath, fFlags, fCreateMode);
    527     free(pszPath);
    528     return fd;
    529 }
    530 
    531 
    532 
    533 /**
    534  * Deletes a file in a directory.
    535  *
    536  * @returns whatever unlink returns.
    537  * @param   pszName     The file name.
    538  * @param   pszDir      The directory path.
    539  */
    540 static int UnlinkFileInDir(const char *pszName, const char *pszDir)
    541 {
    542     char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
    543     int rc = unlink(pszPath);
    544     free(pszPath);
    545     return rc;
    546 }
    547 
    548 
    549 /**
    550  * Renames a file in a directory.
    551  *
    552  * @returns whatever rename returns.
    553  * @param   pszOldName  The new file name.
    554  * @param   pszNewName  The old file name.
    555  * @param   pszDir      The directory path.
    556  */
    557 static int RenameFileInDir(const char *pszOldName, const char *pszNewName, const char *pszDir)
    558 {
    559     char *pszOldPath = MakePathFromDirAndFile(pszOldName, pszDir);
    560     char *pszNewPath = MakePathFromDirAndFile(pszNewName, pszDir);
    561     int rc = rename(pszOldPath, pszNewPath);
    562     free(pszOldPath);
    563     free(pszNewPath);
    564     return rc;
    565 }
    566 
    567 
    568 /**
    569  * Check if a (regular) file exists in a directory.
    570  *
    571  * @returns 1 if it exists and is a regular file, 0 if not.
    572  * @param   pszName     The file name.
    573  * @param   pszDir      The directory path.
    574  */
    575 static int DoesFileInDirExist(const char *pszName, const char *pszDir)
    576 {
    577     char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
    578     struct stat st;
    579     int rc = stat(pszPath, &st);
    580     free(pszPath);
    581 #ifdef S_ISREG
    582     return !rc && S_ISREG(st.st_mode);
    583 #elif defined(_MSC_VER)
    584     return !rc && (st.st_mode & _S_IFMT) == _S_IFREG;
    585 #else
    586 #error "Port me"
    587 #endif
    588 }
    589 
    590 
    591 /**
    592  * Reads into memory an entire file.
    593  *
    594  * @returns Pointer to the heap allocation containing the file.
    595  *          On failure NULL and errno is returned.
    596  * @param   pszName     The file.
    597  * @param   pszDir      The directory the file resides in.
    598  * @param   pcbFile     Where to store the file size.
    599  */
    600 static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile)
    601 {
    602     int SavedErrno;
    603     char *pszPath = MakePathFromDirAndFile(pszName, pszDir);
    604     int fd = open(pszPath, O_RDONLY | O_BINARY);
    605     if (fd >= 0)
    606     {
    607         off_t cbFile = lseek(fd, 0, SEEK_END);
    608         if (    cbFile >= 0
    609             &&  lseek(fd, 0, SEEK_SET) == 0)
    610         {
    611             char *pb = malloc(cbFile + 1);
    612             if (pb)
    613             {
    614                 if (read(fd, pb, cbFile) == cbFile)
    615                 {
    616                     close(fd);
    617                     pb[cbFile] = '\0';
    618                     *pcbFile = (size_t)cbFile;
    619                     return pb;
    620                 }
    621                 SavedErrno = errno;
    622                 free(pb);
    623             }
    624             else
    625                 SavedErrno = ENOMEM;
    626         }
    627         else
    628             SavedErrno = errno;
    629         close(fd);
    630     }
    631     else
    632         SavedErrno = errno;
    633     free(pszPath);
    634     errno = SavedErrno;
    635     return NULL;
    636 }
    637 
    638 
    639 /**
    640  * Creates a directory including all necessary parent directories.
    641  *
    642  * @returns 0 on success, -1 + errno on failure.
    643  * @param   pszDir      The directory.
    644  */
    645 static int MakePath(const char *pszPath)
    646 {
    647     int iErr = 0;
    648     char *pszAbsPath = AbsPath(pszPath);
    649     char *psz = pszAbsPath;
    650 
    651     /* Skip to the root slash (PC). */
    652     while (!IS_SLASH(*psz) && *psz)
    653         psz++;
    654 /** @todo UNC */
    655     for (;;)
    656     {
    657         char chSaved;
    658 
    659         /* skip slashes */
    660         while (IS_SLASH(*psz))
    661             psz++;
    662         if (!*psz)
    663             break;
    664 
    665         /* find the next slash or end and terminate the string. */
    666         while (!IS_SLASH(*psz) && *psz)
    667             psz++;
    668         chSaved = *psz;
    669         *psz = '\0';
    670 
    671         /* try create the directory, ignore failure because the directory already exists. */
    672         errno = 0;
    673 #ifdef _MSC_VER
    674         if (    _mkdir(pszAbsPath)
    675             &&  errno != EEXIST)
    676 #else
    677         if (    mkdir(pszAbsPath, 0777)
    678             &&  errno != EEXIST
    679             &&  errno != ENOSYS /* Solaris nonsensical mkdir crap. */
    680             &&  errno != EACCES /* Solaris nonsensical mkdir crap. */
    681             )
    682 #endif
    683         {
    684             iErr = errno;
    685             break;
    686         }
    687 
    688         /* restore the slash/terminator */
    689         *psz = chSaved;
    690     }
    691 
    692     free(pszAbsPath);
    693     return iErr ? -1 : 0;
    694 }
    695 
    696 
    697242/**
    698243 * Adds the arguments found in the pszCmdLine string to argument vector.
     
    770315
    771316
    772 /**
    773  * Dependency collector state.
    774  */
    775 typedef struct KOCDEP
    776 {
    777     /** The statemachine for processing the preprocessed code stream. */
    778     enum KOCDEPSTATE
    779     {
    780         kOCDepState_Invalid = 0,
    781         kOCDepState_NeedNewLine,
    782         kOCDepState_NeedHash,
    783         kOCDepState_NeedLine_l,
    784         kOCDepState_NeedLine_l_HaveSpace,
    785         kOCDepState_NeedLine_i,
    786         kOCDepState_NeedLine_n,
    787         kOCDepState_NeedLine_e,
    788         kOCDepState_NeedSpaceBeforeDigit,
    789         kOCDepState_NeedFirstDigit,
    790         kOCDepState_NeedMoreDigits,
    791         kOCDepState_NeedQuote,
    792         kOCDepState_NeedEndQuote
    793     }   enmState;
    794     /** Current offset into the filename buffer. */
    795     uint32_t offFilename;
    796     /** The amount of space currently allocated for the filename buffer. */
    797     uint32_t cbFilenameAlloced;
    798     /** Pointer to the filename buffer. */
    799     char *pszFilename;
    800     /** The current dependency file. */
    801     PDEP pCurDep;
    802 } KOCDEP;
    803 /** Pointer to a KOCDEP.  */
    804 typedef KOCDEP *PKOCDEP;
    805 
    806 
    807 /**
    808  * Initializes the dependency collector state.
    809  *
    810  * @param   pDepState       The dependency collector state.
    811  */
    812 static void kOCDepInit(PKOCDEP pDepState)
    813 {
    814     pDepState->enmState = kOCDepState_NeedHash;
    815     pDepState->offFilename = 0;
    816     pDepState->cbFilenameAlloced = 0;
    817     pDepState->pszFilename = NULL;
    818     pDepState->pCurDep = NULL;
    819 }
    820 
    821 
    822 /**
    823  * Deletes the dependency collector state, releasing all resources.
    824  *
    825  * @param   pDepState       The dependency collector state.
    826  */
    827 static void kOCDepDelete(PKOCDEP pDepState)
    828 {
    829     pDepState->enmState = kOCDepState_Invalid;
    830     free(pDepState->pszFilename);
    831     pDepState->pszFilename = NULL;
    832     depCleanup();
    833 }
    834 
    835 
    836 /**
    837  * Unescapes a string in place.
    838  *
    839  * @returns The new string length.
    840  * @param   psz             The string to unescape (input and output).
    841  */
    842 static size_t kOCDepUnescape(char *psz)
    843 {
    844     char *pszSrc = psz;
    845     char *pszDst = psz;
    846     char ch;
    847 
    848     while ((ch = *pszSrc++) != '\0')
    849     {
    850         if (ch == '\\')
    851         {
    852             char ch2 = *pszSrc;
    853             if (ch2)
     317
     318
     319static fpos_t kLibTweakerAsciiToSize(const char *pch, size_t cch)
     320{
     321    fpos_t cb = 0;
     322
     323    /* strip leading spaces. */
     324    while (cch > 0 && (*pch == ' ' || *pch == '\t'))
     325        cch--, pch++;
     326
     327    /* Convert decimal to binary. */
     328    while (cch-- > 0)
     329    {
     330        char ch = *pch++;
     331        if (ch >= '0' && ch <= '9')
     332        {
     333            cb *= 10;
     334            cb += ch - '0';
     335        }
     336        else
     337            break;
     338    }
     339
     340    return cb;
     341}
     342
     343
     344static int kLibMyReadAt(FILE *pFile, void *pv, size_t cb, fpos_t off, int fEofOk)
     345{
     346    if (fsetpos(pFile, &off) == 0)
     347    {
     348        size_t cbActual = fread(pv, 1, cb, pFile);
     349        if (cbActual == cb)
     350            return 0;
     351        if (!fEofOk || !feof(pFile))
     352            ErrorMsg("fread returned %#lx, expected %#lx!\n", (unsigned long)cbActual, (unsigned long)cb);
     353    }
     354    else
     355        ErrorMsg("seek error!\n");
     356    return 1;
     357}
     358
     359
     360static int kLibMyWriteAt(FILE *pFile, const void *pv, size_t cb, fpos_t off)
     361{
     362    if (fsetpos(pFile, &off) == 0)
     363    {
     364        size_t cbActual = fwrite(pv, 1, cb, pFile);
     365        if (cbActual == cb)
     366            return 0;
     367        ErrorMsg("fwrite returned %#lx, expected %#lx!\n", (unsigned long)cbActual, (unsigned long)cb);
     368    }
     369    else
     370        ErrorMsg("seek error!\n");
     371    return 1;
     372}
     373
     374
     375static int kLibFillNullThunkData(FILE *pFile, fpos_t cbFile, fpos_t offFileBytes)
     376{
     377    size_t                  off;
     378    IMAGE_FILE_HEADER       CoffHdr;
     379    IMAGE_SECTION_HEADER    SecHdr;
     380    unsigned                iSecHdr;
     381    fpos_t                  offCur;
     382    unsigned                cbMachineWord;
     383    KU32                    cbStrTab;
     384    COFFSYMBOL             *paSymbols;
     385    int                     rcRet = 0;
     386
     387    /*
     388     * Read the COFF file header and filter out unlikly files based on
     389     * section and symbol counts.
     390     */
     391    if (cbFile <= sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_SECTION_HEADER) * 2 + 4)
     392        return 0;
     393    if (kLibMyReadAt(pFile, &CoffHdr, sizeof(CoffHdr), offFileBytes, 0) != 0)
     394        return 1;
     395    if (   CoffHdr.Machine != IMAGE_FILE_MACHINE_I386
     396        && CoffHdr.Machine != IMAGE_FILE_MACHINE_AMD64)
     397        return 0;
     398    cbMachineWord = CoffHdr.Machine == IMAGE_FILE_MACHINE_I386 ? 4 : 8;
     399    if (   CoffHdr.NumberOfSections == 0
     400        || CoffHdr.NumberOfSymbols == 0)
     401        return 0;
     402    off = sizeof(IMAGE_FILE_HEADER) + CoffHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
     403    if ((fpos_t)off >= cbFile)
     404        return 0;
     405    if (   CoffHdr.PointerToSymbolTable >= (KU64)cbFile
     406        || CoffHdr.PointerToSymbolTable < off)
     407        return 0;
     408
     409    /*
     410     * Search for the .idata$5 section which the thunk data is usually found in.
     411     */
     412    offCur = offFileBytes + sizeof(CoffHdr);
     413    for (iSecHdr = 0; iSecHdr < CoffHdr.NumberOfSections; iSecHdr++)
     414    {
     415        if (kLibMyReadAt(pFile, &SecHdr, sizeof(SecHdr), offCur, 0) != 0)
     416            return 1;
     417        InfoMsg(2, "#2: %.8s VirtualSize=%#lx\n", SecHdr.Name, SecHdr.SizeOfRawData);
     418        if (   !memcmp(SecHdr.Name, ".idata$5", 8)
     419            && SecHdr.SizeOfRawData == cbMachineWord)
     420            break;
     421        offCur += sizeof(SecHdr);
     422    }
     423    if (iSecHdr == CoffHdr.NumberOfSections)
     424        return 0;
     425
     426    /*
     427     * Read in the symbo and string tables.
     428     */
     429    off = CoffHdr.PointerToSymbolTable + CoffHdr.NumberOfSymbols * sizeof(COFFSYMBOL);
     430    if (kLibMyReadAt(pFile, &cbStrTab, sizeof(cbStrTab), offFileBytes + off, 0) != 0)
     431        return 1;
     432    InfoMsg(2, "#2: Found COFF file header, cbStrTab=%#x; off=%#lx NumberOfSymbols=%#x PointerToSymbolTable=%#x\n",
     433            cbStrTab, (long)off, CoffHdr.NumberOfSymbols, CoffHdr.PointerToSymbolTable);
     434    if (   cbStrTab <= 4U
     435        || cbStrTab >= 16*1024*1024U /* 16MB */
     436        || (fpos_t)off + cbStrTab > cbFile)
     437        return 0;
     438    paSymbols = xmalloc(CoffHdr.NumberOfSymbols * sizeof(COFFSYMBOL) + cbStrTab + 1);
     439    if (kLibMyReadAt(pFile, paSymbols, CoffHdr.NumberOfSymbols * sizeof(COFFSYMBOL) + cbStrTab,
     440                     offFileBytes + CoffHdr.PointerToSymbolTable, 0) == 0)
     441    {
     442        char       *pchStrTab = (char *)&paSymbols[CoffHdr.NumberOfSymbols];
     443        unsigned    iSym;
     444
     445        pchStrTab[cbStrTab] = '\0';
     446        pchStrTab[0] = '\0';
     447        pchStrTab[1] = '\0';
     448        pchStrTab[2] = '\0';
     449        pchStrTab[3] = '\0';
     450
     451        for (iSym = 0; iSym < CoffHdr.NumberOfSymbols; iSym++)
     452        {
     453            static char const s_szSuffix[] = "NULL_THUNK_DATA";
     454            const char *pchName;
     455            size_t      cchName;
     456            if (paSymbols[iSym].e.e.e_zeros != 0)
    854457            {
    855                 pszSrc++;
    856                 ch = ch2;
     458                pchName = &paSymbols[iSym].e.e_name[0];
     459                cchName = (char *)memchr(pchName, '\0', sizeof(paSymbols[iSym].e.e_name)) - pchName;
     460                if (cchName > sizeof(paSymbols[iSym].e.e_name))
     461                    cchName = sizeof(paSymbols[iSym].e.e_name);
    857462            }
    858             /* else: cannot happen / just ignore */
    859         }
    860         *pszDst++ = ch;
    861     }
    862 
    863     *pszDst = '\0';
    864     return pszDst - psz;
    865 }
    866 
    867 
    868 /**
    869  * Checks if the character at @a offChar is escaped or not.
    870  *
    871  * @returns 1 if escaped, 0 if not.
    872  * @param   pach            The string (not terminated).
    873  * @param   offChar         The offset of the character in question.
    874  */
    875 static int kOCDepIsEscaped(char *pach, size_t offChar)
    876 {
    877     while (offChar > 0 && pach[offChar - 1] == '\\')
    878     {
    879         if (   offChar == 1
    880             || pach[offChar - 2] != '\\')
    881             return 1;
    882         offChar -= 2;
    883     }
    884     return 0;
    885 }
    886 
    887 
    888 static void kOCDepEnter(PKOCDEP pDepState, const char *pszUnescFilename, size_t cchFilename)
    889 {
    890     if (cchFilename + 1 >= pDepState->cbFilenameAlloced)
    891     {
    892         pDepState->cbFilenameAlloced = (cchFilename + 1 + 15) & ~15;
    893         pDepState->pszFilename = (char *)xrealloc(pDepState->pszFilename, pDepState->cbFilenameAlloced);
    894     }
    895 
    896     memcpy(pDepState->pszFilename, pszUnescFilename, cchFilename);
    897     pDepState->pszFilename[cchFilename] = '\0';
    898     cchFilename = kOCDepUnescape(pDepState->pszFilename);
    899 
    900     if (   !pDepState->pCurDep
    901         || cchFilename != pDepState->pCurDep->cchFilename
    902         || strcmp(pDepState->pszFilename, pDepState->pCurDep->szFilename))
    903         pDepState->pCurDep = depAdd(pDepState->pszFilename, cchFilename);
    904 }
    905 
    906 
    907 /**
    908  * This consumes the preprocessor output and generate dependencies from it.
    909  *
    910  * The trick is to look at the line directives and which files get listed there.
    911  *
    912  * @returns The new state. This is a convenience for saving code space and it
    913  *          isn't really meant to be of any use to the caller.
    914  * @param   pDepState       The dependency collector state.
    915  * @param   pszInput        The input.
    916  * @param   cchInput        The input length.
    917  */
    918 static enum KOCDEPSTATE
    919 kOCDepConsumer(PKOCDEP pDepState, const char *pszInput, size_t cchInput)
    920 {
    921     enum KOCDEPSTATE enmState = pDepState->enmState;
    922     const char *psz;
    923 
    924     while (cchInput > 0)
    925     {
    926         switch (enmState)
    927         {
    928             case kOCDepState_NeedNewLine:
    929                 psz = (const char *)memchr(pszInput, '\n', cchInput);
    930                 if (!psz)
    931                     return enmState;
    932                 psz++;
    933                 cchInput -= psz - pszInput;
    934                 pszInput = psz;
    935 
    936             case kOCDepState_NeedHash:
    937                 while (cchInput > 0 && MY_IS_BLANK(*pszInput))
    938                     cchInput--, pszInput++;
    939                 if (!cchInput)
    940                     return pDepState->enmState = kOCDepState_NeedHash;
    941 
    942                 if (*pszInput != '#')
    943                     break;
    944                 pszInput++;
    945                 cchInput--;
    946                 enmState = kOCDepState_NeedLine_l;
    947 
    948             case kOCDepState_NeedLine_l:
    949             case kOCDepState_NeedLine_l_HaveSpace:
    950                 while (cchInput > 0 && MY_IS_BLANK(*pszInput))
     463            else if (   paSymbols[iSym].e.e.e_offset == 0
     464                     || paSymbols[iSym].e.e.e_offset >= cbStrTab)
     465                continue;
     466            else
     467            {
     468                pchName = &pchStrTab[paSymbols[iSym].e.e.e_offset];
     469                cchName = strlen(pchName);
     470            }
     471
     472            if (   *pchName == 0x7f
     473                && cchName >= sizeof(s_szSuffix)
     474                && memcmp(&pchName[cchName - sizeof(s_szSuffix) + 1], s_szSuffix, sizeof(s_szSuffix) - 1) == 0)
     475            {
     476                if (pchName[cchName] == '\0')
     477                    InfoMsg(1, "#2: Found '%s': value=%#lx\n", pchName, paSymbols[iSym].e_value);
     478                else
     479                    InfoMsg(1, "#2: Found '%.8s': value=%#lx\n", pchName, paSymbols[iSym].e_value);
     480                if (   paSymbols[iSym].e_scnum > 0
     481                    && paSymbols[iSym].e_scnum <= CoffHdr.NumberOfSections)
    951482                {
    952                     enmState = kOCDepState_NeedLine_l_HaveSpace;
    953                     cchInput--, pszInput++;
    954                 }
    955                 if (!cchInput)
    956                     return pDepState->enmState = enmState;
    957 
    958                 if (*pszInput != 'l')
    959                 {
    960                     /* # <digit> "<file>" */
    961                     if (enmState != kOCDepState_NeedLine_l_HaveSpace || !isdigit(*pszInput))
    962                         break;
    963                     pszInput++;
    964                     cchInput--;
    965                     enmState = kOCDepState_NeedMoreDigits;
    966                     continue;
    967                 }
    968                 pszInput++;
    969                 if (!--cchInput)
    970                     return pDepState->enmState = kOCDepState_NeedLine_i;
    971 
    972             case kOCDepState_NeedLine_i:
    973                 if (*pszInput != 'i')
    974                     break;
    975                 pszInput++;
    976                 if (!--cchInput)
    977                     return pDepState->enmState = kOCDepState_NeedLine_n;
    978 
    979             case kOCDepState_NeedLine_n:
    980                 if (*pszInput != 'n')
    981                     break;
    982                 pszInput++;
    983                 if (!--cchInput)
    984                     return pDepState->enmState = kOCDepState_NeedLine_e;
    985 
    986             case kOCDepState_NeedLine_e:
    987                 if (*pszInput != 'e')
    988                     break;
    989                 pszInput++;
    990                 if (!--cchInput)
    991                     return pDepState->enmState = kOCDepState_NeedSpaceBeforeDigit;
    992 
    993             case kOCDepState_NeedSpaceBeforeDigit:
    994                 if (!MY_IS_BLANK(*pszInput))
    995                     break;
    996                 pszInput++;
    997                 cchInput--;
    998 
    999             case kOCDepState_NeedFirstDigit:
    1000                 while (cchInput > 0 && MY_IS_BLANK(*pszInput))
    1001                     cchInput--, pszInput++;
    1002                 if (!cchInput)
    1003                     return pDepState->enmState = kOCDepState_NeedFirstDigit;
    1004 
    1005                 if (!isdigit(*pszInput))
    1006                     break;
    1007                 pszInput++;
    1008                 cchInput--;
    1009 
    1010             case kOCDepState_NeedMoreDigits:
    1011                 while (cchInput > 0 && isdigit(*pszInput))
    1012                     cchInput--, pszInput++;
    1013                 if (!cchInput)
    1014                     return pDepState->enmState = kOCDepState_NeedMoreDigits;
    1015 
    1016             case kOCDepState_NeedQuote:
    1017                 while (cchInput > 0 && MY_IS_BLANK(*pszInput))
    1018                     cchInput--, pszInput++;
    1019                 if (!cchInput)
    1020                     return pDepState->enmState = kOCDepState_NeedQuote;
    1021 
    1022                 if (*pszInput != '"')
    1023                     break;
    1024                 pszInput++;
    1025                 cchInput--;
    1026 
    1027             case kOCDepState_NeedEndQuote:
    1028             {
    1029                 uint32_t off = pDepState->offFilename;
    1030                 for (;;)
    1031                 {
    1032                     char ch;
    1033 
    1034                     if (!cchInput)
     483                    if (paSymbols[iSym].e_scnum != iSecHdr + 1)
     484                        InfoMsg(0, "#2: '%s' in section %u, expected %u\n", pchName, paSymbols[iSym].e_scnum, iSecHdr);
     485                    else if (paSymbols[iSym].e_value != 0)
     486                        InfoMsg(0, "#2: '%s' in value %#xu, expected 0x0\n", pchName, paSymbols[iSym].e_value);
     487                    else if (   SecHdr.PointerToRawData < sizeof(CoffHdr) + CoffHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)
     488                             || (fpos_t)SecHdr.PointerToRawData + cbMachineWord > cbFile)
     489                        InfoMsg(0, "#2: Unexpected PointerToRawData value: %#x\n", SecHdr.PointerToRawData);
     490                    else
    1035491                    {
    1036                         pDepState->offFilename = off;
    1037                         return pDepState->enmState = kOCDepState_NeedEndQuote;
    1038                     }
    1039 
    1040                     if (off + 1 >= pDepState->cbFilenameAlloced)
    1041                     {
    1042                         if (!pDepState->cbFilenameAlloced)
    1043                             pDepState->cbFilenameAlloced = 32;
    1044                         else
    1045                             pDepState->cbFilenameAlloced *= 2;
    1046                         pDepState->pszFilename = (char *)xrealloc(pDepState->pszFilename, pDepState->cbFilenameAlloced);
    1047                     }
    1048                     pDepState->pszFilename[off] = ch = *pszInput++;
    1049                     cchInput--;
    1050 
    1051                     if (   ch == '"'
    1052                         && (   off == 0
    1053                             || pDepState->pszFilename[off - 1] != '\\'
    1054                             || !kOCDepIsEscaped(pDepState->pszFilename, off)))
    1055                     {
    1056                         /* Done, unescape and add the file. */
    1057                         size_t cchFilename;
    1058 
    1059                         pDepState->pszFilename[off] = '\0';
    1060                         cchFilename = kOCDepUnescape(pDepState->pszFilename);
    1061 
    1062                         if (   !pDepState->pCurDep
    1063                             || cchFilename != pDepState->pCurDep->cchFilename
    1064                             || strcmp(pDepState->pszFilename, pDepState->pCurDep->szFilename))
    1065                             pDepState->pCurDep = depAdd(pDepState->pszFilename, cchFilename);
    1066                         pDepState->offFilename = 0;
     492                        union
     493                        {
     494                            KU8  ab[8];
     495                            KU32 u32;
     496                            KU64 u64;
     497                        } uBuf;
     498                        uBuf.u64 = 0;
     499                        off = offFileBytes + SecHdr.PointerToRawData;
     500                        if (kLibMyReadAt(pFile, &uBuf, cbMachineWord, off, 0) == 0)
     501                        {
     502                            static const KU8 s_abGarbage[8] = { 0xaa, 0x99, 0x88, 0xbb,  0xbb, 0xaa, 0x88, 0x99 };
     503                            static const KU8 s_abZero[8]    = { 0, 0, 0, 0,  0, 0, 0, 0 };
     504                            if (memcmp(&uBuf, s_abZero,cbMachineWord) == 0)
     505                            {
     506                                rcRet = kLibMyWriteAt(pFile, s_abGarbage, cbMachineWord, off);
     507                                if (!rcRet)
     508                                    InfoMsg(1, "#2: Updated '%s'\n", pchName);
     509                            }
     510                            else if (memcmp(&uBuf, s_abGarbage, cbMachineWord) == 0)
     511                            {
     512                                InfoMsg(1, "#2: Already modified '%s'\n", pchName);
     513                                rcRet = 0;
     514                            }
     515                            else
     516                                rcRet = ErrorMsg(0, "#2: Unexpected '%s' data: %02x %02x %02x %02x %02x %02x %02x %02x\n",
     517                                                 pchName,
     518                                                 uBuf.ab[0], uBuf.ab[1], uBuf.ab[2], uBuf.ab[3],
     519                                                 uBuf.ab[4], uBuf.ab[5], uBuf.ab[6], uBuf.ab[7]);
     520                        }
    1067521                        break;
    1068522                    }
    1069 
    1070                     off++;
    1071523                }
    1072524            }
    1073525        }
    1074526
    1075         /* next newline */
    1076         enmState = kOCDepState_NeedNewLine;
    1077     }
    1078 
    1079     return pDepState->enmState = enmState;
     527    }
     528    else
     529        rcRet = 1;
     530    free(paSymbols);
     531    return rcRet;
    1080532}
    1081533
    1082534
    1083535/**
    1084  * Writes the dependencies to the specified file.
    1085  *
    1086  * @param   pDepState       The dependency collector state.
    1087  * @param   pszFilename     The name of the dependency file.
    1088  * @param   pszObjFile      The object file name, relative to @a pszObjDir.
    1089  * @param   pszObjDir       The object file directory.
    1090  * @param   fFixCase        Whether to fix the case of dependency files.
    1091  * @param   fQuiet          Whether to be quiet about the dependencies.
    1092  * @param   fGenStubs       Whether to generate stubs.
    1093  */
    1094 static void kOCDepWriteToFile(PKOCDEP pDepState, const char *pszFilename, const char *pszObjFile, const char *pszObjDir,
    1095                               int fFixCase, int fQuiet, int fGenStubs)
    1096 {
    1097     char *pszObjFileAbs;
    1098     char *psz;
    1099     FILE *pFile = fopen(pszFilename, "w");
    1100     if (!pFile)
    1101         FatalMsg("Failed to open dependency file '%s': %s\n", pszFilename, strerror(errno));
    1102 
    1103     depOptimize(fFixCase, fQuiet);
    1104 
    1105     /* Make object file name with unix slashes. */
    1106     pszObjFileAbs = MakePathFromDirAndFile(pszObjFile, pszObjDir);
    1107     psz = pszObjFileAbs;
    1108     while ((psz = strchr(psz, '\\')) != NULL)
    1109         *psz++ = '/';
    1110 
    1111     fprintf(pFile, "%s:", pszObjFileAbs);
    1112     free(pszObjFileAbs);
    1113     depPrint(pFile);
    1114     if (fGenStubs)
    1115         depPrintStubs(pFile);
    1116 
    1117     if (fclose(pFile) != 0)
    1118         FatalMsg("Failed to write dependency file '%s': %s\n", pszFilename, strerror(errno));
    1119 }
    1120 
    1121 
    1122 /**
    1123  * Preprocessor output reader state.
    1124  */
    1125 typedef struct KOCCPPRD
    1126 {
    1127     /** Pointer to the preprocessor output. */
    1128     char *pszBuf;
    1129     /** Allocated buffer size. */
    1130     size_t cbBufAlloc;
    1131     /** Amount preprocessor output that we've completed optimizations for. */
    1132     size_t cchDstOptimized;
    1133     /** Offset to the start of the unoptimized source. */
    1134     size_t offSrcUnoptimized;
    1135     /** The offset of the next bits to process. */
    1136     size_t offSrcCur;
    1137     /** The offset where to put more raw preprocessor output. */
    1138     size_t offSrcRead;
    1139     /** The line number corresponding to offOptimized. */
    1140     uint32_t uOptLineNo;
    1141     /** The current line number. */
    1142     uint32_t uCurLineNo;
    1143     /** Set if we're done, clear if we're expecting more preprocessor output. */
    1144     int fDone;
    1145     /** The saved character at cchOptimized. */
    1146     char chSaved;
    1147     /** Whether the optimizations are enabled. */
    1148     int fOptimize;
    1149 
    1150     /** Buffer holding the current file name (unescaped). */
    1151     char *pszFileNmBuf;
    1152     /** The size of the file name buffer. */
    1153     size_t cbFileNmBuf;
    1154     /** The length of the current file string. */
    1155     size_t cchCurFileNm;
    1156 
    1157     /** Line directive / new line sequence buffer. */
    1158     char *pszLineBuf;
    1159     /** The size of the buffer pointed to by pszLineBuf. */
    1160     size_t cbLineBuf;
    1161 
    1162     /** Set if we should work the dependency generator as well. */
    1163     PKOCDEP pDepState;
    1164 } KOCCPPRD;
    1165 /** Pointer to a preprocessor reader state. */
    1166 typedef KOCCPPRD *PKOCCPPRD;
    1167 
    1168 
    1169 /**
    1170  * Allocate the initial C preprocessor output buffer.
    1171  *
    1172  * @param   pCppRd      The C preprocessor reader instance.
    1173  * @param   cbOldCpp    The size of the output the last time.  This is 0 if
    1174  *                      there was not previous run.
    1175  * @param   fOptimize   Whether optimizations are enabled.
    1176  * @param   pDepState   Pointer to the dependency generator.  Must only be set
    1177  *                      if @a fOptimize is also set.
    1178  */
    1179 static void kOCCppRdInit(PKOCCPPRD pCppRd, size_t cbOldCpp, int fOptimize, PKOCDEP pDepState)
    1180 {
    1181     assert(!pDepState || fOptimize);
    1182 
    1183     pCppRd->cbBufAlloc = cbOldCpp ? (cbOldCpp + KOC_BUF_INCR) & ~(KOC_BUF_ALIGNMENT - 1) : KOC_BUF_MIN;
    1184     pCppRd->pszBuf = xmalloc(pCppRd->cbBufAlloc);
    1185     pCppRd->cchCurFileNm = 0;
    1186     pCppRd->cchDstOptimized = 0;
    1187     pCppRd->offSrcUnoptimized = 0;
    1188     pCppRd->offSrcCur = 0;
    1189     pCppRd->offSrcRead = 0;
    1190     pCppRd->uOptLineNo = 1;
    1191     pCppRd->uCurLineNo = 1;
    1192     pCppRd->fDone = 0;
    1193     pCppRd->chSaved = 0;
    1194     pCppRd->fOptimize = fOptimize;
    1195 
    1196     pCppRd->pszFileNmBuf = NULL;
    1197     pCppRd->cbFileNmBuf = 0;
    1198     pCppRd->cchCurFileNm = 0;
    1199 
    1200     pCppRd->pszLineBuf = NULL;
    1201     pCppRd->cbLineBuf = 0;
    1202 
    1203     pCppRd->pDepState = pDepState;
    1204 }
    1205 
    1206 
    1207 static void kOCCppRdDelete(PKOCCPPRD pCppRd)
    1208 {
    1209     free(pCppRd->pszBuf);
    1210     pCppRd->pszBuf = NULL;
    1211 
    1212     free(pCppRd->pszFileNmBuf);
    1213     pCppRd->pszFileNmBuf = NULL;
    1214 
    1215     free(pCppRd->pszLineBuf);
    1216     pCppRd->pszLineBuf = NULL;
    1217 }
    1218 
    1219 
    1220 /**
    1221  * Allocate more buffer space for the C preprocessor output.
    1222  *
    1223  * @param   pCppRd      The C preprocessor reader instance.
    1224  */
    1225 static size_t kOCCppRdGrowBuffer(PKOCCPPRD pCppRd)
    1226 {
    1227     pCppRd->cbBufAlloc += KOC_BUF_INCR;
    1228     pCppRd->pszBuf = xrealloc(pCppRd->pszBuf, pCppRd->cbBufAlloc);
    1229 
    1230     return pCppRd->cbBufAlloc - pCppRd->offSrcRead;
    1231 }
    1232 
    1233 
    1234 static size_t kOCCppRdOptInsert(PKOCCPPRD pCppRd, size_t cchSrcReplaced, const char *pchInsert, size_t cchInsert)
    1235 {
    1236     size_t offDelta = 0;
    1237     size_t cchAvail;
    1238 
    1239     pCppRd->offSrcUnoptimized += cchSrcReplaced;
    1240     assert(pCppRd->offSrcUnoptimized <= pCppRd->offSrcCur);
    1241     cchAvail = pCppRd->offSrcUnoptimized - pCppRd->cchDstOptimized;
    1242     if (cchAvail < cchInsert)
    1243     {
    1244         size_t const cbToMove = pCppRd->offSrcRead - pCppRd->offSrcUnoptimized;
    1245         assert(cbToMove <= pCppRd->offSrcRead);
    1246         offDelta = cchInsert - cchAvail;
    1247 
    1248         while (pCppRd->offSrcRead + offDelta >= pCppRd->cbBufAlloc)
    1249             kOCCppRdGrowBuffer(pCppRd);
    1250 
    1251         g_cMemMoves++;
    1252         g_cbMemMoved += cbToMove + 1;
    1253         memmove(pCppRd->pszBuf + pCppRd->offSrcUnoptimized + offDelta,
    1254                 pCppRd->pszBuf + pCppRd->offSrcUnoptimized,
    1255                 cbToMove + 1);
    1256 
    1257         pCppRd->offSrcRead += offDelta;
    1258         pCppRd->offSrcUnoptimized += offDelta;
    1259         pCppRd->offSrcCur += offDelta;
    1260         assert(pCppRd->offSrcRead < 1 || pCppRd->pszBuf[pCppRd->offSrcRead - 1] != '\0');
    1261     }
    1262 
    1263     memcpy(pCppRd->pszBuf + pCppRd->cchDstOptimized, pchInsert, cchInsert);
    1264     pCppRd->cchDstOptimized += cchInsert;
    1265 
    1266     return offDelta;
    1267 }
    1268 
    1269 
    1270 static void kOCCppRdOptCommit(PKOCCPPRD pCppRd)
    1271 {
    1272     size_t cchToCommit = pCppRd->offSrcCur - pCppRd->offSrcUnoptimized;
    1273     assert(pCppRd->offSrcUnoptimized <= pCppRd->offSrcCur);
    1274 
    1275     if (cchToCommit)
    1276     {
    1277         memmove(pCppRd->pszBuf + pCppRd->cchDstOptimized, pCppRd->pszBuf + pCppRd->offSrcUnoptimized, cchToCommit);
    1278         pCppRd->cchDstOptimized += cchToCommit;
    1279         pCppRd->offSrcUnoptimized = pCppRd->offSrcCur;
    1280     }
    1281 
    1282     pCppRd->uOptLineNo = pCppRd->uCurLineNo;
    1283 }
    1284 
    1285 
    1286 
    1287 static char *kOCCppRdOptGetEol(PKOCCPPRD pCppRd, char *pszCur, size_t cbLeft)
    1288 {
    1289     char *pszEol = memchr(pszCur, '\n', cbLeft);
    1290     if (pszEol)
    1291     {
    1292         if (pszCur != pszEol && pszEol[-1] == '\r')
    1293             pszEol--;
    1294     }
    1295     else if (pCppRd->fDone && cbLeft)
    1296         pszEol = pszCur + cbLeft;
    1297     return pszEol;
    1298 }
    1299 
    1300 static void kOCCppRdOptSetFile(PKOCCPPRD pCppRd, const char *pchFile, size_t cchFile)
    1301 {
    1302     if (cchFile >= pCppRd->cbFileNmBuf)
    1303     {
    1304         pCppRd->cbFileNmBuf  = (cchFile + 15 + 1) & ~(size_t)15;
    1305         pCppRd->pszFileNmBuf = xrealloc(pCppRd->pszFileNmBuf, pCppRd->cbFileNmBuf);
    1306     }
    1307     memcpy(pCppRd->pszFileNmBuf, pchFile, cchFile);
    1308     pCppRd->pszFileNmBuf[cchFile] = '\0';
    1309     pCppRd->cchCurFileNm = cchFile;
    1310 }
    1311 
    1312 
    1313 static size_t kOCCppRdOptFmtLine(PKOCCPPRD pCppRd, uint32_t uLine, const char *pchFile, size_t cchFile)
    1314 {
    1315     size_t cchUsed;
    1316     size_t cbNeeded;
    1317 
    1318     /* Make sure we've got enough buffer space. */
    1319     cbNeeded = sizeof("#line 4888222111 \"\"\n") + cchFile;
    1320     if (cbNeeded > pCppRd->cbLineBuf)
    1321     {
    1322         pCppRd->cbLineBuf  = (cbNeeded + 32 + 15) & ~(size_t)15;
    1323         pCppRd->pszLineBuf = xrealloc(pCppRd->pszLineBuf, pCppRd->cbLineBuf);
    1324     }
    1325 
    1326     /* Do the formatting. */
    1327     cchUsed = sprintf(pCppRd->pszLineBuf, "#line %lu", (unsigned long)uLine);
    1328     if (cchFile)
    1329     {
    1330         pCppRd->pszLineBuf[cchUsed++] = ' ';
    1331         pCppRd->pszLineBuf[cchUsed++] = '"';
    1332         memcpy(&pCppRd->pszLineBuf[cchUsed], pchFile, cchFile);
    1333         cchUsed += cchFile;
    1334         pCppRd->pszLineBuf[cchUsed++] = '"';
    1335     }
    1336     pCppRd->pszLineBuf[cchUsed++] = '\n';
    1337     pCppRd->pszLineBuf[cchUsed] = '\0';
    1338 
    1339     return cchUsed;
    1340 }
    1341 
    1342 
    1343 static size_t kOCCppRdOptFmtNewLines(PKOCCPPRD pCppRd, uint32_t cNewLines)
    1344 {
    1345     if (cNewLines + 1 > pCppRd->cbLineBuf)
    1346     {
    1347         pCppRd->cbLineBuf  = (cNewLines + 1 + 32 + 15) & ~(size_t)15;
    1348         pCppRd->pszLineBuf = xrealloc(pCppRd->pszLineBuf, pCppRd->cbLineBuf);
    1349     }
    1350 
    1351     memset(pCppRd->pszLineBuf, '\n', cNewLines);
    1352     pCppRd->pszLineBuf[cNewLines] = '\0';
    1353     return cNewLines;
    1354 }
    1355 
    1356 
    1357 static size_t kOCCppRdOptFlush(PKOCCPPRD pCppRd, size_t offSrcCur, int fLineDirNext)
    1358 {
    1359     size_t offDelta = 0;
    1360     size_t const offSrcUnoptimized = pCppRd->offSrcUnoptimized;
    1361     assert(offSrcUnoptimized <= offSrcCur);
    1362 
    1363     if (offSrcCur > offSrcUnoptimized)
     536 * Clears timestamps to avoid rebuilding stuff just because the internal
     537 * timestamps changed in an import library.
     538 */
     539static int kLibClearTimestamps(FILE *pFile, fpos_t offFileHdr, ARCHFILEHDR *pFileHdr, fpos_t cbFile, fpos_t offFileBytes)
     540{
     541    union
     542    {
     543        IMAGE_FILE_HEADER   CoffHdr;
     544        COFFIMPLIBHDR       ImpLibHdr;
     545    } u;
     546    if (sizeof(u.CoffHdr) != sizeof(u.ImpLibHdr))
     547        FatalDie("Oops!");
     548
     549    /*
     550     * Clear the timestamp in the library file header.
     551     */
     552    memset(pFileHdr->achModtime, '0', sizeof(pFileHdr->achModtime));
     553    if (kLibMyWriteAt(pFile, pFileHdr, sizeof(*pFileHdr), offFileHdr) != 0)
     554        return 1;
     555
     556    /*
     557     * Clear the timestamp in the COFF header, if we find one.
     558     */
     559    if (cbFile <= sizeof(IMAGE_FILE_HEADER))
     560        return 0;
     561    if (kLibMyReadAt(pFile, &u.CoffHdr, sizeof(u.CoffHdr), offFileBytes, 0) != 0)
     562        return 1;
     563
     564    if (   (   u.CoffHdr.Machine == IMAGE_FILE_MACHINE_I386
     565            || u.CoffHdr.Machine == IMAGE_FILE_MACHINE_AMD64)
     566        &&     sizeof(IMAGE_FILE_HEADER)
     567             + u.CoffHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)
     568           <= (KU64)cbFile
     569        && u.CoffHdr.PointerToSymbolTable <= (KU64)cbFile)
     570    {
     571        InfoMsg(1, "Found COFF file header\n");
     572        if (u.CoffHdr.TimeDateStamp != 0)
     573        {
     574            u.CoffHdr.TimeDateStamp = 0;
     575            return kLibMyWriteAt(pFile, &u.CoffHdr, sizeof(u.CoffHdr), offFileBytes);
     576        }
     577    }
     578    else if (   u.ImpLibHdr.uSig1    == 0
     579             && u.ImpLibHdr.uSig2    == 0xffff
     580             && u.ImpLibHdr.uVersion == 0
     581             && (   u.ImpLibHdr.uMachine == IMAGE_FILE_MACHINE_I386
     582                 || u.ImpLibHdr.uMachine == IMAGE_FILE_MACHINE_AMD64)
     583             && u.ImpLibHdr.cbData <= cbFile)
     584    {
     585        InfoMsg(1, "Found COFF import library header\n");
     586        if (u.ImpLibHdr.uTimeDateStamp)
     587        {
     588            u.ImpLibHdr.uTimeDateStamp = 0;
     589            return kLibMyWriteAt(pFile, &u.ImpLibHdr, sizeof(u.ImpLibHdr), offFileBytes);
     590        }
     591    }
     592    else
     593        InfoMsg(1, "CoffHdr.Machine=%#x ImpLibHdr.Machine=%#x\n", u.CoffHdr.Machine, u.ImpLibHdr.uMachine);
     594
     595    return 0;
     596}
     597
     598
     599static int kLibTweakerDoIt(const char *pszLib, int fClearTimestamps, int fFillNullThunkData)
     600{
     601    int   rcRet = 0;
     602    FILE *pFile = fopen(pszLib, "r+b");
     603    if (pFile)
    1364604    {
    1365605        /*
    1366          * We've got unflushed whitelines.
     606         * Read the header.
    1367607         */
    1368         size_t const cchSrcInQuestion = offSrcCur - offSrcUnoptimized;
    1369         uint32_t const cLinesInQuestion = pCppRd->uCurLineNo - pCppRd->uOptLineNo;
    1370         size_t cchLineDir;
    1371 
    1372         if (   cLinesInQuestion <= 7
    1373             || (cchLineDir = kOCCppRdOptFmtLine(pCppRd, pCppRd->uCurLineNo, NULL, 0)) >= cLinesInQuestion)
    1374             cchLineDir = kOCCppRdOptFmtNewLines(pCppRd, cLinesInQuestion);
    1375 
    1376         offDelta = kOCCppRdOptInsert(pCppRd, cchSrcInQuestion, pCppRd->pszLineBuf, cchLineDir);
    1377     }
    1378 
    1379     (void)fLineDirNext; /* Use later if required. */
    1380     return offDelta;
    1381 }
    1382 
    1383 
    1384 static int kOCCppRdOptParseLine(PKOCCPPRD pCppRd, char *pszCur, char *pszEol,
    1385                                 uint32_t *puNewLineNo, char **ppszNewFile, size_t *pcchNewFile)
    1386 {
    1387     char    *psz = pszCur;
    1388     uint32_t uNewLineNo;
    1389     int      fIsShort;
    1390 
    1391     /*
    1392      * Check if it's a #line directive of some kind and parse it.
    1393      */
    1394     if (*psz != '#')
    1395         return 0;
    1396     psz++;
    1397 
    1398     fIsShort = MY_IS_BLANK(*psz);
    1399     while (MY_IS_BLANK(*psz))
    1400         psz++;
    1401 
    1402     if (   psz[0] == 'l'
    1403         && psz[1] == 'i'
    1404         && psz[2] == 'n'
    1405         && psz[3] == 'e'
    1406         && MY_IS_BLANK(psz[4]) )
    1407     {
    1408         fIsShort = 0;
    1409         psz += 5;
    1410         while (MY_IS_BLANK(*psz))
    1411             psz++;
    1412     }
    1413     else if (fIsShort && isdigit(*psz))
    1414         fIsShort = 1;
    1415     else
    1416         return 0;
    1417 
    1418     /* Parse the line number. */
    1419     if (!isdigit(*psz))
    1420         return 0;
    1421 
    1422     uNewLineNo = *psz++ - '0';
    1423     while (isdigit(*psz))
    1424     {
    1425         uNewLineNo *= 10;
    1426         uNewLineNo += *psz++ - '0';
    1427     }
    1428     if (   psz != pszEol
    1429         && !MY_IS_BLANK(*psz))
    1430         return 0;
    1431 
    1432     /*
    1433      * The file name part is optional.
    1434      */
    1435     while (MY_IS_BLANK(*psz))
    1436         psz++;
    1437 
    1438     if (   psz != pszEol
    1439         && *psz == '"')
    1440     {
    1441         *ppszNewFile = ++psz;
    1442         while (   psz != pszEol
    1443                && (   *psz != '"'
    1444                    || (   psz[-1] == '\\'
    1445                        && kOCDepIsEscaped(psz, psz - *ppszNewFile)) )
    1446               )
    1447             psz++;
    1448         if (psz == pszEol)
    1449         {
    1450             /** @todo complain? */
    1451             return 0;
    1452         }
    1453         *pcchNewFile = psz - *ppszNewFile;
    1454 
    1455         do
    1456             psz++;
    1457         while (psz != pszEol && MY_IS_BLANK(*psz));
    1458     }
    1459     else
    1460     {
    1461         /* No file given => Same as the current. */
    1462         *ppszNewFile = pCppRd->cchCurFileNm ? pCppRd->pszFileNmBuf : NULL;
    1463         *pcchNewFile = pCppRd->cchCurFileNm;
    1464     }
    1465     if (psz != pszEol)
    1466     {
    1467         /** @todo complain? */
    1468         return 0;
    1469     }
    1470 
    1471     *puNewLineNo = uNewLineNo;
    1472     return 1;
    1473 }
    1474 
    1475 
    1476 static char *kOCCppRdOptHandleLine(PKOCCPPRD pCppRd, char *pszCur, size_t *pcbLeft, int *pfEmptyLine, char *pszEol)
    1477 {
    1478     size_t const offSrcLine = pCppRd->offSrcCur;
    1479     size_t const cchSrcLine = pszEol - pCppRd->pszBuf - (pCppRd->fOptimize & 2 ? pCppRd->offSrcUnoptimized : pCppRd->offSrcCur);
    1480     size_t const cbLeftAssert = *pcbLeft;
    1481     char *pszNewFile;
    1482     size_t cchNewFile;
    1483     uint32_t uNewLineNo;
    1484     assert(*pszEol == '\r' || *pszEol == '\n' || *pszEol == '\0');
    1485 
    1486     /* Advance to the end of the line before we do anything.  This can be a
    1487        little confusing but it saves effort and avoid trouble in the end. */
    1488     pCppRd->offSrcCur = pszEol - pCppRd->pszBuf;
    1489     *pcbLeft -= pszEol - pszCur;
    1490     assert(*pcbLeft <= cbLeftAssert); (void)cbLeftAssert;
    1491 
    1492     /*
    1493      * Try parse the directive a '#line' one....
    1494      */
    1495     if (!kOCCppRdOptParseLine(pCppRd, pszCur, pszEol, &uNewLineNo, &pszNewFile, &cchNewFile))
    1496     {
    1497         /*
    1498          * No line directive.  Flush pending optimizations and indicate that
    1499          * the line isn't empty and needs to be commited at EOL.
    1500          */
    1501         kOCCppRdOptFlush(pCppRd, offSrcLine, 0);
    1502         *pfEmptyLine = 0;
    1503     }
    1504     else
    1505     {
    1506         char *pszCurFile = pCppRd->cchCurFileNm ? pCppRd->pszFileNmBuf : NULL;
    1507         if (   pszNewFile == pszCurFile
    1508             || (   cchNewFile == pCppRd->cchCurFileNm
    1509                 && !memcmp(pszNewFile, pszCurFile, cchNewFile)) )
    1510         {
    1511             /*
    1512              * A #line directive specifying the same file.
    1513              */
    1514             if (uNewLineNo >= pCppRd->uCurLineNo)
    1515                 *pfEmptyLine = 1;
    1516             else
     608        static char s_szMagic[] = "!<arch>\n";
     609        union
     610        {
     611            char                ab[1024];
     612            IMAGE_FILE_HEADER   CoffHdr;
     613        } uBuf;
     614        if (   fread(uBuf.ab, 1, sizeof(s_szMagic) - 1, pFile) == sizeof(s_szMagic) - 1
     615            && memcmp(uBuf.ab, s_szMagic, sizeof(s_szMagic) - 1) == 0)
     616        {
     617            fpos_t offFileHdr = sizeof(s_szMagic) - 1;
     618            while (!feof(pFile))
    1517619            {
    1518                 /*
    1519                  * It went backwards, so we need to flush the old section of
    1520                  * the file and emit another directive for starting the new one.
    1521                  */
    1522                 size_t cchLineDir;
    1523                 if (!(pCppRd->fOptimize & 2))
    1524                     kOCCppRdOptFlush(pCppRd, offSrcLine, 1);
    1525 
    1526                 cchLineDir = kOCCppRdOptFmtLine(pCppRd, uNewLineNo, NULL, 0) - 1; /* sans \n */
    1527                 kOCCppRdOptInsert(pCppRd, cchSrcLine, pCppRd->pszLineBuf, cchLineDir);
    1528 
    1529                 *pfEmptyLine = 0;
     620                ARCHFILEHDR     FileHdr;
     621                if (kLibMyReadAt(pFile, &FileHdr, sizeof(FileHdr), offFileHdr, 1) != 0)
     622                {
     623                    if (feof(pFile))
     624                        break;
     625                    rcRet = ErrorMsg("failed reading the file header (offset %ld)\n", (long)offFileHdr);
     626                    break;
     627                }
     628                if (   FileHdr.achMagic[0] == 0x60
     629                    && FileHdr.achMagic[1] == 0x0a)
     630                {
     631                    fpos_t const offFileBytes = offFileHdr + sizeof(FileHdr);
     632
     633                    /*
     634                     * Convert the size from decimal to binary as we need it to skip to
     635                     * the next file header.
     636                     */
     637                    fpos_t const cb = kLibTweakerAsciiToSize(FileHdr.achSize, sizeof(FileHdr.achSize));
     638                    InfoMsg(1, "Found header at %#lx: cbFile=%#lx, bytes at %#lx\n",
     639                            (unsigned long)offFileHdr, (unsigned long)cb, (unsigned long)offFileBytes);
     640
     641                    /*
     642                     * Make the requested changes.
     643                     */
     644                    if (fClearTimestamps)
     645                        rcRet |= kLibClearTimestamps(pFile, offFileHdr, &FileHdr, cb, offFileBytes);
     646
     647                    if (fFillNullThunkData)
     648                        rcRet |= kLibFillNullThunkData(pFile, cb, offFileBytes);
     649
     650                    /*
     651                     * Skip to the next header.
     652                     */
     653                    offFileHdr = offFileBytes + ((cb + 1) & ~(fpos_t)1);
     654                }
     655                else
     656                    rcRet = ErrorMsg("invalid file header magic (offset %ld)\n", (long)offFileHdr);
    1530657            }
    1531658        }
    1532659        else
    1533         {
    1534             /*
    1535              * The #line directive changed the file.
    1536              */
    1537             size_t cchLineDir;
    1538 
    1539             kOCCppRdOptSetFile(pCppRd, pszNewFile, cchNewFile); /* save to do this early */
    1540             if (!(pCppRd->fOptimize & 2))
    1541                 kOCCppRdOptFlush(pCppRd, offSrcLine, 1);
    1542 
    1543             cchLineDir = kOCCppRdOptFmtLine(pCppRd, uNewLineNo, pCppRd->pszFileNmBuf, cchNewFile) - 1; /* sans \n */
    1544             kOCCppRdOptInsert(pCppRd, cchSrcLine, pCppRd->pszLineBuf, cchLineDir);
    1545 
    1546             if (pCppRd->pDepState)
    1547                 kOCDepEnter(pCppRd->pDepState, pCppRd->pszFileNmBuf, cchNewFile);
    1548 
    1549             *pfEmptyLine = 0;
    1550         }
    1551 
    1552         pCppRd->uCurLineNo = uNewLineNo - 1;
    1553     }
    1554 
    1555     return pCppRd->pszBuf + pCppRd->offSrcCur;
    1556 }
    1557 
    1558 
    1559 static void kOCCppRdOpt(PKOCCPPRD pCppRd)
    1560 {
    1561     size_t cch;
    1562     char *pszEol;
    1563     char *pszCur = pCppRd->pszBuf + pCppRd->offSrcCur;
    1564     size_t cbTodo = pCppRd->offSrcRead - pCppRd->offSrcCur;
    1565     int fEmptyLine = 1;
    1566 
    1567     while (cbTodo > 0)
    1568     {
    1569         switch (*pszCur)
    1570         {
    1571             case ' ':
    1572             case '\t':
    1573                 break;
    1574 
    1575             case '\n':
    1576                 pCppRd->offSrcCur = pszCur - pCppRd->pszBuf + 1;
    1577                 pCppRd->uCurLineNo++;
    1578                 if (!fEmptyLine)
    1579                     kOCCppRdOptCommit(pCppRd);
    1580                 fEmptyLine = 1;
    1581                 break;
    1582 
    1583             case '\r': /* "\r\n" -> "\n" */
    1584                 if (cbTodo <= 1 && !pCppRd->fDone)
    1585                     return;
    1586                 if (pszCur[1] == '\n' && !fEmptyLine)
    1587                 {
    1588                     /* Commit the part up to the '\r' first, replace '\r\n' with '\n'. */
    1589                     pCppRd->offSrcCur = pszCur - pCppRd->pszBuf;
    1590                     kOCCppRdOptCommit(pCppRd);
    1591 
    1592                     pCppRd->offSrcCur += 2;
    1593                     kOCCppRdOptInsert(pCppRd, 2, "\n", 1);
    1594 
    1595                     assert(cbTodo >= 2);
    1596                     cbTodo -= 2;
    1597                     pszCur += 2;
    1598 
    1599                     fEmptyLine = 1;
    1600                     continue;
    1601                 }
    1602                 break;
    1603 
    1604             case '#':
    1605                 pszEol = kOCCppRdOptGetEol(pCppRd, pszCur + 1, cbTodo - 1);
    1606                 if (!pszEol)
    1607                     return;
    1608                 pszCur = kOCCppRdOptHandleLine(pCppRd, pszCur, &cbTodo, &fEmptyLine, pszEol);
    1609                 continue;
    1610 
    1611             default:
    1612                 /*
    1613                  * Some non-white stuff encountered, flush pending white
    1614                  * line optimizations and skip to the end of the line.
    1615                  */
    1616                 fEmptyLine = 0;
    1617                 pszEol = kOCCppRdOptGetEol(pCppRd, pszCur + 1, cbTodo - 1);
    1618                 if (!pszEol)
    1619                     return;
    1620                 cch = pszEol - pszCur;
    1621 
    1622                 pszCur += kOCCppRdOptFlush(pCppRd, pCppRd->offSrcCur, 0);
    1623 
    1624                 assert(cch <= cbTodo);
    1625                 cbTodo -= cch;
    1626                 pszCur += cch;
    1627                 continue;
    1628         }
    1629 
    1630         cbTodo--;
    1631         pszCur++;
    1632     }
    1633 }
    1634 
    1635 
    1636 static void kOCCppRdOptFinalize(PKOCCPPRD pCppRd)
    1637 {
    1638     pCppRd->fDone = 1;
    1639     assert(pCppRd->offSrcRead < 1 || pCppRd->pszBuf[pCppRd->offSrcRead - 1] != '\0');
    1640     pCppRd->pszBuf[pCppRd->offSrcRead] = '\0';
    1641     kOCCppRdOpt(pCppRd);
    1642 
    1643     assert(pCppRd->offSrcCur == pCppRd->offSrcRead);
    1644     kOCCppRdOptFlush(pCppRd, pCppRd->offSrcCur, 0);
    1645 }
    1646 
    1647 
    1648 
    1649 /**
    1650  * Read C preprocessor output from the given file descriptor, optionally
    1651  * optimzing it.
    1652  *
    1653  * @returns Number of bytes read. 0 indicates end of file.
    1654  *
    1655  * @param   pCppRd      The C preprocessor reader instance.
    1656  * @param   fdIn        The file descriptor to read the raw preprocessor output
    1657  *                      from.
    1658  * @param   ppszRet     Where to return the pointer to the output.
    1659  *
    1660  * @remarks Won't return on error, calls FatalDie on those occasions.
    1661  */
    1662 static long kOCCppRdRead(PKOCCPPRD pCppRd, int fdIn, const char **ppszRet)
    1663 {
    1664     size_t cbLeft;
    1665     long   cbRead;
    1666 
    1667     if (pCppRd->fOptimize)
    1668     {
    1669         /*
    1670          * Optimize the C preprocessor output on the way thru.
    1671          */
    1672         size_t const cchOldOptimized = pCppRd->cchDstOptimized;
    1673         if (pCppRd->chSaved)
    1674             pCppRd->pszBuf[pCppRd->cchDstOptimized] = pCppRd->chSaved;
    1675 
    1676         do
    1677         {
    1678             /* Read more raw C preprocessor output. */
    1679             cbLeft = pCppRd->cbBufAlloc - pCppRd->offSrcRead;
    1680             if (cbLeft <= 1)
    1681                 cbLeft = kOCCppRdGrowBuffer(pCppRd);
    1682 
    1683             do
    1684                 cbRead = read(fdIn, pCppRd->pszBuf + pCppRd->offSrcRead, (long)(cbLeft - 1));
    1685             while (cbRead < 0 && errno == EINTR);
    1686             if (cbRead < 0)
    1687                 FatalDie("kOCCppRdRead - read(%d,,%ld) failed: %s\n",
    1688                          fdIn, (long)(cbLeft - 1), strerror(errno));
    1689             pCppRd->offSrcRead += cbRead;
    1690 
    1691             /* Optimize it. */
    1692             if (!cbRead)
    1693             {
    1694                 kOCCppRdOptFinalize(pCppRd);
    1695                 break;
    1696             }
    1697             kOCCppRdOpt(pCppRd);
    1698         } while (pCppRd->cchDstOptimized == cchOldOptimized);
    1699 
    1700         *ppszRet = &pCppRd->pszBuf[cchOldOptimized];
    1701         pCppRd->chSaved = pCppRd->pszBuf[pCppRd->cchDstOptimized];
    1702         pCppRd->pszBuf[pCppRd->cchDstOptimized] = '\0';
    1703         cbRead = (long)(pCppRd->cchDstOptimized - cchOldOptimized);
    1704     }
    1705     else
    1706     {
    1707         /*
    1708          * Pass thru.
    1709          */
    1710         char *pszBuf;
    1711         cbLeft = pCppRd->cbBufAlloc - pCppRd->offSrcRead;
    1712         if (cbLeft <= 1)
    1713             cbLeft = kOCCppRdGrowBuffer(pCppRd);
    1714         pszBuf = pCppRd->pszBuf + pCppRd->offSrcRead;
    1715 
    1716         do
    1717             cbRead = read(fdIn, pszBuf, (long)(cbLeft - 1));
    1718         while (cbRead < 0 && errno == EINTR);
    1719         if (cbRead < 0)
    1720             FatalDie("kOCCppRdRead - read(%d,,%ld) failed: %s\n",
    1721                      fdIn, (long)(cbLeft - 1), strerror(errno));
    1722 
    1723         *ppszRet = pszBuf;
    1724         pCppRd->offSrcRead += cbRead;
    1725         pszBuf[cbRead] = '\0';
    1726     }
    1727 
    1728     return cbRead;
    1729 }
    1730 
    1731 
    1732 /**
    1733  * Grabs the output buffer from the C preprocessor reader.
    1734  *
    1735  * @param   pCppRd              The C preprocessor reader instance.
    1736  * @param   ppszRet             Where to return the pointer to the output.
    1737  * @param   pcbRet              Where to return the size of the output.
    1738  */
    1739 static void kOCCppRdGrabOutput(PKOCCPPRD pCppRd, char **ppszRet, size_t *pcbRet)
    1740 {
    1741     assert(pCppRd->offSrcRead < 1 || pCppRd->pszBuf[pCppRd->offSrcRead - 1] != '\0');
    1742     *ppszRet = pCppRd->pszBuf;
    1743     *pcbRet  = pCppRd->fOptimize ? pCppRd->cchDstOptimized : pCppRd->offSrcRead;
    1744     pCppRd->pszBuf = NULL;
    1745     pCppRd->offSrcRead = 0;
    1746 }
    1747 
    1748 
    1749 
    1750 
    1751 
    1752 
    1753 /** A checksum list entry.
    1754  * We keep a list checksums (of preprocessor output) that matches.
    1755  *
    1756  * The matching algorithm doesn't require the preprocessor output to be
    1757  * indentical, only to produce the same object files.
    1758  */
    1759 typedef struct KOCSUM
    1760 {
    1761     /** The next checksum. */
    1762     struct KOCSUM *pNext;
    1763     /** The crc32 checksum. */
    1764     uint32_t crc32;
    1765     /** The MD5 digest. */
    1766     unsigned char md5[16];
    1767     /** Valid or not. */
    1768     unsigned fUsed;
    1769 } KOCSUM;
    1770 /** Pointer to a KOCSUM. */
    1771 typedef KOCSUM *PKOCSUM;
    1772 /** Pointer to a const KOCSUM. */
    1773 typedef const KOCSUM *PCKOCSUM;
    1774 
    1775 
    1776 /**
    1777  * Temporary context record used when calculating the checksum of some data.
    1778  */
    1779 typedef struct KOCSUMCTX
    1780 {
    1781     /** The MD5 context. */
    1782     struct MD5Context MD5Ctx;
    1783 } KOCSUMCTX;
    1784 /** Pointer to a check context record. */
    1785 typedef KOCSUMCTX *PKOCSUMCTX;
    1786 
    1787 
    1788 
    1789 /**
    1790  * Initializes a checksum object with an associated context.
    1791  *
    1792  * @param   pSum    The checksum object.
    1793  * @param   pCtx    The checksum context.
    1794  */
    1795 static void kOCSumInitWithCtx(PKOCSUM pSum, PKOCSUMCTX pCtx)
    1796 {
    1797     memset(pSum, 0, sizeof(*pSum));
    1798     MD5Init(&pCtx->MD5Ctx);
    1799 }
    1800 
    1801 
    1802 /**
    1803  * Updates the checksum calculation.
    1804  *
    1805  * @param   pSum    The checksum.
    1806  * @param   pCtx    The checksum calcuation context.
    1807  * @param   pvBuf   The input data to checksum.
    1808  * @param   cbBuf   The size of the input data.
    1809  */
    1810 static void kOCSumUpdate(PKOCSUM pSum, PKOCSUMCTX pCtx, const void *pvBuf, size_t cbBuf)
    1811 {
    1812     /*
    1813      * Take in relativly small chunks to try keep it in the cache.
    1814      */
    1815     const unsigned char *pb = (const unsigned char *)pvBuf;
    1816     while (cbBuf > 0)
    1817     {
    1818         size_t cb = cbBuf >= 128*1024 ? 128*1024 : cbBuf;
    1819         pSum->crc32 = crc32(pSum->crc32, pb, cb);
    1820         MD5Update(&pCtx->MD5Ctx, pb, (unsigned)cb);
    1821         cbBuf -= cb;
    1822     }
    1823 }
    1824 
    1825 
    1826 /**
    1827  * Finalizes a checksum calculation.
    1828  *
    1829  * @param   pSum    The checksum.
    1830  * @param   pCtx    The checksum calcuation context.
    1831  */
    1832 static void kOCSumFinalize(PKOCSUM pSum, PKOCSUMCTX pCtx)
    1833 {
    1834     MD5Final(&pSum->md5[0], &pCtx->MD5Ctx);
    1835     pSum->fUsed = 1;
    1836 }
    1837 
    1838 
    1839 /**
    1840  * Init a check sum chain head.
    1841  *
    1842  * @param   pSumHead    The checksum head to init.
    1843  */
    1844 static void kOCSumInit(PKOCSUM pSumHead)
    1845 {
    1846     memset(pSumHead, 0, sizeof(*pSumHead));
    1847 }
    1848 
    1849 
    1850 /**
    1851  * Parses the given string into a checksum head object.
    1852  *
    1853  * @returns 0 on success, -1 on format error.
    1854  * @param   pSumHead    The checksum head to init.
    1855  * @param   pszVal      The string to initialized it from.
    1856  */
    1857 static int kOCSumInitFromString(PKOCSUM pSumHead, const char *pszVal)
    1858 {
    1859     unsigned i;
    1860     char *pszNext;
    1861     char *pszMD5;
    1862 
    1863     memset(pSumHead, 0, sizeof(*pSumHead));
    1864 
    1865     pszMD5 = strchr(pszVal, ':');
    1866     if (pszMD5 == NULL)
    1867         return -1;
    1868     *pszMD5++ = '\0';
    1869 
    1870     /* crc32 */
    1871     pSumHead->crc32 = (uint32_t)strtoul(pszVal, &pszNext, 16);
    1872     if (pszNext && *pszNext)
    1873         return -1;
    1874 
    1875     /* md5 */
    1876     for (i = 0; i < sizeof(pSumHead->md5) * 2; i++)
    1877     {
    1878         unsigned char ch = pszMD5[i];
    1879         int x;
    1880         if ((unsigned char)(ch - '0') <= 9)
    1881             x = ch - '0';
    1882         else if ((unsigned char)(ch - 'a') <= 5)
    1883             x = ch - 'a' + 10;
    1884         else if ((unsigned char)(ch - 'A') <= 5)
    1885             x = ch - 'A' + 10;
    1886         else
    1887             return -1;
    1888         if (!(i & 1))
    1889             pSumHead->md5[i >> 1] = x << 4;
    1890         else
    1891             pSumHead->md5[i >> 1] |= x;
    1892     }
    1893 
    1894     pSumHead->fUsed = 1;
    1895     return 0;
    1896 }
    1897 
    1898 
    1899 /**
    1900  * Delete a check sum chain.
    1901  *
    1902  * @param   pSumHead    The head of the checksum chain.
    1903  */
    1904 static void kOCSumDeleteChain(PKOCSUM pSumHead)
    1905 {
    1906     PKOCSUM pSum = pSumHead->pNext;
    1907     while (pSum)
    1908     {
    1909         void *pvFree = pSum;
    1910         pSum = pSum->pNext;
    1911         free(pvFree);
    1912     }
    1913     memset(pSumHead, 0, sizeof(*pSumHead));
    1914 }
    1915 
    1916 
    1917 /**
    1918  * Insert a check sum into the chain.
    1919  *
    1920  * @param   pSumHead    The head of the checksum list.
    1921  * @param   pSumAdd     The checksum to add (duplicate).
    1922  */
    1923 static void kOCSumAdd(PKOCSUM pSumHead, PCKOCSUM pSumAdd)
    1924 {
    1925     if (pSumHead->fUsed)
    1926     {
    1927         PKOCSUM pNew = xmalloc(sizeof(*pNew));
    1928         *pNew = *pSumAdd;
    1929         pNew->pNext = pSumHead->pNext;
    1930         pNew->fUsed = 1;
    1931         pSumHead->pNext = pNew;
    1932     }
    1933     else
    1934     {
    1935         *pSumHead = *pSumAdd;
    1936         pSumHead->pNext = NULL;
    1937         pSumHead->fUsed = 1;
    1938     }
    1939 }
    1940 
    1941 
    1942 /**
    1943  * Inserts an entrie chain into the given check sum chain.
    1944  *
    1945  * @param   pSumHead    The head of the checksum list.
    1946  * @param   pSumHeadAdd The head of the checksum list to be added.
    1947  */
    1948 static void kOCSumAddChain(PKOCSUM pSumHead, PCKOCSUM pSumHeadAdd)
    1949 {
    1950     while (pSumHeadAdd)
    1951     {
    1952         kOCSumAdd(pSumHead, pSumHeadAdd);
    1953         pSumHeadAdd = pSumHeadAdd->pNext;
    1954     }
    1955 }
    1956 
    1957 
    1958 
    1959 /**
    1960  * Prints the checksum to the specified stream.
    1961  *
    1962  * @param   pSum    The checksum.
    1963  * @param   pFile   The output file stream
    1964  */
    1965 static void kOCSumFPrintf(PCKOCSUM pSum, FILE *pFile)
    1966 {
    1967     fprintf(pFile, "%#x:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
    1968             pSum->crc32,
    1969             pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3],
    1970             pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7],
    1971             pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11],
    1972             pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]);
    1973 }
    1974 
    1975 
    1976 /**
    1977  * Displays the checksum (not chain!) using the InfoMsg() method.
    1978  *
    1979  * @param   pSum    The checksum.
    1980  * @param   uLevel  The info message level.
    1981  * @param   pszMsg  Message to prefix the info message with.
    1982  */
    1983 static void kOCSumInfo(PCKOCSUM pSum, unsigned uLevel, const char *pszMsg)
    1984 {
    1985     InfoMsg(uLevel,
    1986             "%s: crc32=%#010x md5=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
    1987             pszMsg,
    1988             pSum->crc32,
    1989             pSum->md5[0], pSum->md5[1], pSum->md5[2], pSum->md5[3],
    1990             pSum->md5[4], pSum->md5[5], pSum->md5[6], pSum->md5[7],
    1991             pSum->md5[8], pSum->md5[9], pSum->md5[10], pSum->md5[11],
    1992             pSum->md5[12], pSum->md5[13], pSum->md5[14], pSum->md5[15]);
    1993 }
    1994 
    1995 
    1996 /**
    1997  * Compares two check sum entries.
    1998  *
    1999  * @returns 1 if equal, 0 if not equal.
    2000  *
    2001  * @param pSum1     The first checksum.
    2002  * @param pSum2     The second checksum.
    2003  */
    2004 static int kOCSumIsEqual(PCKOCSUM pSum1, PCKOCSUM pSum2)
    2005 {
    2006     if (pSum1 == pSum2)
    2007         return 1;
    2008     if (!pSum1 || !pSum2)
    2009         return 0;
    2010     if (pSum1->crc32 != pSum2->crc32)
    2011         return 0;
    2012     if (memcmp(&pSum1->md5[0], &pSum2->md5[0], sizeof(pSum1->md5)))
    2013         return 0;
    2014     return 1;
    2015 }
    2016 
    2017 
    2018 /**
    2019  * Checks if the specified checksum equals one of the
    2020  * checksums in the chain.
    2021  *
    2022  * @returns 1 if equals one of them, 0 if not.
    2023  *
    2024  * @param pSumHead  The checksum chain too look in.
    2025  * @param pSum      The checksum to look for.
    2026  * @todo ugly name. fix.
    2027  */
    2028 static int kOCSumHasEqualInChain(PCKOCSUM pSumHead, PCKOCSUM pSum)
    2029 {
    2030     for (; pSumHead; pSumHead = pSumHead->pNext)
    2031     {
    2032         if (pSumHead == pSum)
    2033             return 1;
    2034         if (pSumHead->crc32 != pSum->crc32)
    2035             continue;
    2036         if (memcmp(&pSumHead->md5[0], &pSum->md5[0], sizeof(pSumHead->md5)))
    2037             continue;
    2038         return 1;
    2039     }
    2040     return 0;
    2041 }
    2042 
    2043 
    2044 /**
    2045  * Checks if the checksum (chain) empty.
    2046  *
    2047  * @returns 1 if empty, 0 if it there is one or more checksums.
    2048  * @param   pSum    The checksum to test.
    2049  */
    2050 static int kOCSumIsEmpty(PCKOCSUM pSum)
    2051 {
    2052     return !pSum->fUsed;
    2053 }
    2054 
    2055 
    2056 
    2057 
    2058 
    2059 
    2060 /**
    2061  * The representation of a cache entry.
    2062  */
    2063 typedef struct KOCENTRY
    2064 {
    2065     /** The name of the cache entry. */
    2066     const char *pszName;
    2067     /** The dir that all other names are relative to. */
    2068     char *pszDir;
    2069     /** The absolute path. */
    2070     char *pszAbsPath;
    2071     /** Set if the object needs to be (re)compiled. */
    2072     unsigned fNeedCompiling;
    2073     /** Whether the preprocessor runs in piped mode. If clear it's file
    2074      * mode (it could be redirected stdout, but that's essentially the
    2075      * same from our point of view). */
    2076     unsigned fPipedPreComp;
    2077     /** Whether the compiler runs in piped mode (preprocessor output on stdin). */
    2078     unsigned fPipedCompile;
    2079     /** The name of the pipe that we're feeding the preprocessed output to the
    2080      *  compiler via.  This is a Windows thing. */
    2081     char *pszNmPipeCompile;
    2082     /** Name of the dependency file (generated from #line statements in the
    2083      * preprocessor output). */
    2084     char *pszMakeDepFilename;
    2085     /** Whether to fix the case of the make depedencies. */
    2086     int fMakeDepFixCase;
    2087     /** Whether to do the make dependencies quietly. */
    2088     int fMakeDepQuiet;
    2089     /** Whether to generate stubs for headers files. */
    2090     int fMakeDepGenStubs;
    2091     /** The dependency collector state.  */
    2092     KOCDEP DepState;
    2093     /** Whether the optimizations are enabled. */
    2094     int fOptimizeCpp;
    2095     /** Cache entry key that's used for some quick digest validation. */
    2096     uint32_t uKey;
    2097 
    2098     /** The file data. */
    2099     struct KOCENTRYDATA
    2100     {
    2101         /** The name of file containing the preprocessor output. */
    2102         char *pszCppName;
    2103         /** Pointer to the preprocessor output. */
    2104         char *pszCppMapping;
    2105         /** The size of the preprocessor output. 0 if not determined. */
    2106         size_t cbCpp;
    2107         /** The preprocessor output checksums that will produce the cached object. */
    2108         KOCSUM SumHead;
    2109         /** The number of milliseconds spent precompiling. */
    2110         uint32_t cMsCpp;
    2111 
    2112         /** The object filename (relative to the cache file). */
    2113         char *pszObjName;
    2114         /** The compile argument vector used to build the object. */
    2115         char **papszArgvCompile;
    2116         /** The size of the compile  */
    2117         unsigned cArgvCompile;
    2118         /** The checksum of the compiler argument vector. */
    2119         KOCSUM SumCompArgv;
    2120         /** The number of milliseconds spent compiling. */
    2121         uint32_t cMsCompile;
    2122         /** @todo need a list of additional output files for MSC. */
    2123         /** @todo need compiler output (warnings). */
    2124 
    2125         /** The target os/arch identifier. */
    2126         char *pszTarget;
    2127     }
    2128     /** The old data.*/
    2129             Old,
    2130     /** The new data. */
    2131             New;
    2132 } KOCENTRY;
    2133 /** Pointer to a KOCENTRY. */
    2134 typedef KOCENTRY *PKOCENTRY;
    2135 /** Pointer to a const KOCENTRY. */
    2136 typedef const KOCENTRY *PCKOCENTRY;
    2137 
    2138 
    2139 /**
    2140  * Creates a cache entry for the given cache file name.
    2141  *
    2142  * @returns Pointer to a cache entry.
    2143  * @param   pszFilename     The cache file name.
    2144  */
    2145 static PKOCENTRY kOCEntryCreate(const char *pszFilename)
    2146 {
    2147     PKOCENTRY pEntry;
    2148     size_t off;
    2149 
    2150     /*
    2151      * Allocate an empty entry.
    2152      */
    2153     pEntry = xmallocz(sizeof(*pEntry));
    2154 
    2155     kOCDepInit(&pEntry->DepState);
    2156 
    2157     kOCSumInit(&pEntry->New.SumHead);
    2158     kOCSumInit(&pEntry->Old.SumHead);
    2159 
    2160     kOCSumInit(&pEntry->New.SumCompArgv);
    2161     kOCSumInit(&pEntry->Old.SumCompArgv);
    2162 
    2163     /*
    2164      * Setup the directory and cache file name.
    2165      */
    2166     pEntry->pszAbsPath = AbsPath(pszFilename);
    2167     pEntry->pszName = FindFilenameInPath(pEntry->pszAbsPath);
    2168     off = pEntry->pszName - pEntry->pszAbsPath;
    2169     if (!off)
    2170         FatalDie("Failed to find abs path for '%s'!\n", pszFilename);
    2171     pEntry->pszDir = xmalloc(off);
    2172     memcpy(pEntry->pszDir, pEntry->pszAbsPath, off - 1);
    2173     pEntry->pszDir[off - 1] = '\0';
    2174 
    2175     return pEntry;
    2176 }
    2177 
    2178 
    2179 /**
    2180  * Destroys the cache entry freeing up all it's resources.
    2181  *
    2182  * @param   pEntry      The entry to free.
    2183  */
    2184 static void kOCEntryDestroy(PKOCENTRY pEntry)
    2185 {
    2186     /** @todo free pEntry->pszName? */
    2187     free(pEntry->pszDir);
    2188     free(pEntry->pszAbsPath);
    2189     free(pEntry->pszNmPipeCompile);
    2190     free(pEntry->pszMakeDepFilename);
    2191 
    2192     kOCDepDelete(&pEntry->DepState);
    2193 
    2194     kOCSumDeleteChain(&pEntry->New.SumHead);
    2195     kOCSumDeleteChain(&pEntry->Old.SumHead);
    2196 
    2197     kOCSumDeleteChain(&pEntry->New.SumCompArgv);
    2198     kOCSumDeleteChain(&pEntry->Old.SumCompArgv);
    2199 
    2200     free(pEntry->New.pszCppName);
    2201     free(pEntry->Old.pszCppName);
    2202 
    2203     free(pEntry->New.pszCppMapping);
    2204     free(pEntry->Old.pszCppMapping);
    2205 
    2206     free(pEntry->New.pszObjName);
    2207     free(pEntry->Old.pszObjName);
    2208 
    2209     free(pEntry->New.pszTarget);
    2210     free(pEntry->Old.pszTarget);
    2211 
    2212     while (pEntry->New.cArgvCompile > 0)
    2213         free(pEntry->New.papszArgvCompile[--pEntry->New.cArgvCompile]);
    2214     while (pEntry->Old.cArgvCompile > 0)
    2215         free(pEntry->Old.papszArgvCompile[--pEntry->Old.cArgvCompile]);
    2216 
    2217     free(pEntry->New.papszArgvCompile);
    2218     free(pEntry->Old.papszArgvCompile);
    2219 
    2220     free(pEntry);
    2221 }
    2222 
    2223 
    2224 /**
    2225  * Calculates the checksum of an compiler argument vector.
    2226  *
    2227  * @param   pEntry          The cache entry.
    2228  * @param   papszArgv       The argument vector.
    2229  * @param   cArgc           The number of entries in the vector.
    2230  * @param   pszIgnorePath1  Path to ignore when encountered at the end of
    2231  *                          arguments. (Not quite safe for simple file names,
    2232  *                          but what the heck.)
    2233  * @param   pszIgnorePath2  Path to ignore when encountered at the end of
    2234  *                          arguments. (Not quite safe for simple file names,
    2235  *                          but what the heck.)
    2236  * @param   pSum            Where to store the check sum.
    2237  */
    2238 static void kOCEntryCalcArgvSum(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgc,
    2239                                 const char *pszIgnorePath1, const char *pszIgnorePath2, PKOCSUM pSum)
    2240 {
    2241     size_t cchIgnorePath1 = strlen(pszIgnorePath1);
    2242     size_t cchIgnorePath2 = pszIgnorePath2 ? strlen(pszIgnorePath2) : ~(size_t)0;
    2243     KOCSUMCTX Ctx;
    2244     unsigned i;
    2245 
    2246     kOCSumInitWithCtx(pSum, &Ctx);
    2247     for (i = 0; i < cArgc; i++)
    2248     {
    2249         size_t cch = strlen(papszArgv[i]);
    2250         if (   (   cch < cchIgnorePath1
    2251                 || !ArePathsIdenticalN(papszArgv[i] + cch - cchIgnorePath1, pszIgnorePath1, cch))
    2252             && (   cch < cchIgnorePath2
    2253                 || !ArePathsIdenticalN(papszArgv[i] + cch - cchIgnorePath2, pszIgnorePath2, cch)) )
    2254             kOCSumUpdate(pSum, &Ctx, papszArgv[i], cch + 1);
    2255     }
    2256     kOCSumFinalize(pSum, &Ctx);
    2257 
    2258     (void)pEntry;
    2259 }
    2260 
    2261 
    2262 /**
    2263  * Reads and parses the cache file.
    2264  *
    2265  * @param   pEntry      The entry to read it into.
    2266  */
    2267 static void kOCEntryRead(PKOCENTRY pEntry)
    2268 {
    2269     FILE *pFile;
    2270     pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "rb");
    2271     if (pFile)
    2272     {
    2273         InfoMsg(4, "reading cache entry...\n");
    2274 
    2275         /*
    2276          * Check the magic.
    2277          */
    2278         if (    !fgets(g_szLine, sizeof(g_szLine), pFile)
    2279             ||  (   strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.0\n")
    2280                  && strcmp(g_szLine, "magic=kObjCacheEntry-v0.1.1\n"))
    2281            )
    2282         {
    2283             InfoMsg(2, "bad cache file (magic)\n");
    2284             pEntry->fNeedCompiling = 1;
    2285         }
    2286         else
    2287         {
    2288             /*
    2289              * Parse the rest of the file (relaxed order).
    2290              */
    2291             unsigned i;
    2292             int fBad = 0;
    2293             int fBadBeforeMissing = 1;
    2294             while (fgets(g_szLine, sizeof(g_szLine), pFile))
    2295             {
    2296                 char *pszNl;
    2297                 char *pszVal;
    2298 
    2299                 /* Split the line and drop the trailing newline. */
    2300                 pszVal = strchr(g_szLine, '=');
    2301                 if ((fBad = pszVal == NULL))
    2302                     break;
    2303                 *pszVal++ = '\0';
    2304 
    2305                 pszNl = strchr(pszVal, '\n');
    2306                 if (pszNl)
    2307                     *pszNl = '\0';
    2308 
    2309                 /* string case on variable name */
    2310                 if (!strcmp(g_szLine, "obj"))
    2311                 {
    2312                     if ((fBad = pEntry->Old.pszObjName != NULL))
    2313                         break;
    2314                     pEntry->Old.pszObjName = xstrdup(pszVal);
    2315                 }
    2316                 else if (!strcmp(g_szLine, "cpp"))
    2317                 {
    2318                     if ((fBad = pEntry->Old.pszCppName != NULL))
    2319                         break;
    2320                     pEntry->Old.pszCppName = xstrdup(pszVal);
    2321                 }
    2322                 else if (!strcmp(g_szLine, "cpp-size"))
    2323                 {
    2324                     char *pszNext;
    2325                     if ((fBad = pEntry->Old.cbCpp != 0))
    2326                         break;
    2327                     pEntry->Old.cbCpp = strtoul(pszVal, &pszNext, 0);
    2328                     if ((fBad = pszNext && *pszNext))
    2329                         break;
    2330                 }
    2331                 else if (!strcmp(g_szLine, "cpp-sum"))
    2332                 {
    2333                     KOCSUM Sum;
    2334                     if ((fBad = kOCSumInitFromString(&Sum, pszVal)))
    2335                         break;
    2336                     kOCSumAdd(&pEntry->Old.SumHead, &Sum);
    2337                 }
    2338                 else if (!strcmp(g_szLine, "cpp-ms"))
    2339                 {
    2340                     char *pszNext;
    2341                     if ((fBad = pEntry->Old.cMsCpp != 0))
    2342                         break;
    2343                     pEntry->Old.cMsCpp = strtoul(pszVal, &pszNext, 0);
    2344                     if ((fBad = pszNext && *pszNext))
    2345                         break;
    2346                 }
    2347                 else if (!strcmp(g_szLine, "cc-argc"))
    2348                 {
    2349                     if ((fBad = pEntry->Old.papszArgvCompile != NULL))
    2350                         break;
    2351                     pEntry->Old.cArgvCompile = atoi(pszVal); /* if wrong, we'll fail below. */
    2352                     pEntry->Old.papszArgvCompile = xmallocz((pEntry->Old.cArgvCompile + 1) * sizeof(pEntry->Old.papszArgvCompile[0]));
    2353                 }
    2354                 else if (!strncmp(g_szLine, "cc-argv-#", sizeof("cc-argv-#") - 1))
    2355                 {
    2356                     char *pszNext;
    2357                     unsigned i = strtoul(&g_szLine[sizeof("cc-argv-#") - 1], &pszNext, 0);
    2358                     if ((fBad = i >= pEntry->Old.cArgvCompile || pEntry->Old.papszArgvCompile[i] || (pszNext && *pszNext)))
    2359                         break;
    2360                     pEntry->Old.papszArgvCompile[i] = xstrdup(pszVal);
    2361                 }
    2362                 else if (!strcmp(g_szLine, "cc-argv-sum"))
    2363                 {
    2364                     if ((fBad = !kOCSumIsEmpty(&pEntry->Old.SumCompArgv)))
    2365                         break;
    2366                     if ((fBad = kOCSumInitFromString(&pEntry->Old.SumCompArgv, pszVal)))
    2367                         break;
    2368                 }
    2369                 else if (!strcmp(g_szLine, "cc-ms"))
    2370                 {
    2371                     char *pszNext;
    2372                     if ((fBad = pEntry->Old.cMsCompile != 0))
    2373                         break;
    2374                     pEntry->Old.cMsCompile = strtoul(pszVal, &pszNext, 0);
    2375                     if ((fBad = pszNext && *pszNext))
    2376                         break;
    2377                 }
    2378                 else if (!strcmp(g_szLine, "target"))
    2379                 {
    2380                     if ((fBad = pEntry->Old.pszTarget != NULL))
    2381                         break;
    2382                     pEntry->Old.pszTarget = xstrdup(pszVal);
    2383                 }
    2384                 else if (!strcmp(g_szLine, "key"))
    2385                 {
    2386                     char *pszNext;
    2387                     if ((fBad = pEntry->uKey != 0))
    2388                         break;
    2389                     pEntry->uKey = strtoul(pszVal, &pszNext, 0);
    2390                     if ((fBad = pszNext && *pszNext))
    2391                         break;
    2392                 }
    2393                 else if (!strcmp(g_szLine, "the-end"))
    2394                 {
    2395                     fBadBeforeMissing = fBad = strcmp(pszVal, "fine");
    2396                     break;
    2397                 }
    2398                 else
    2399                 {
    2400                     fBad = 1;
    2401                     break;
    2402                 }
    2403             } /* parse loop */
    2404 
    2405             /*
    2406              * Did we find everything and does it add up correctly?
    2407              */
    2408             if (!fBad && fBadBeforeMissing)
    2409             {
    2410                 InfoMsg(2, "bad cache file (no end)\n");
    2411                 fBad = 1;
    2412             }
    2413             else
    2414             {
    2415                 fBadBeforeMissing = fBad;
    2416                 if (    !fBad
    2417                     &&  (   !pEntry->Old.papszArgvCompile
    2418                          || !pEntry->Old.pszObjName
    2419                          || !pEntry->Old.pszCppName
    2420                          || kOCSumIsEmpty(&pEntry->Old.SumHead)))
    2421                     fBad = 1;
    2422                 if (!fBad)
    2423                     for (i = 0; i < pEntry->Old.cArgvCompile; i++)
    2424                         if ((fBad = !pEntry->Old.papszArgvCompile[i]))
    2425                             break;
    2426                 if (!fBad)
    2427                 {
    2428                     KOCSUM Sum;
    2429                     kOCEntryCalcArgvSum(pEntry, (const char * const *)pEntry->Old.papszArgvCompile,
    2430                                         pEntry->Old.cArgvCompile, pEntry->Old.pszObjName, pEntry->Old.pszCppName,
    2431                                         &Sum);
    2432                     fBad = !kOCSumIsEqual(&pEntry->Old.SumCompArgv, &Sum);
    2433                 }
    2434                 if (fBad)
    2435                     InfoMsg(2, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff");
    2436                 else if (ferror(pFile))
    2437                 {
    2438                     InfoMsg(2, "cache file read error\n");
    2439                     fBad = 1;
    2440                 }
    2441 
    2442                 /*
    2443                  * Verify the existance of the object file.
    2444                  */
    2445                 if (!fBad)
    2446                 {
    2447                     struct stat st;
    2448                     char *pszPath = MakePathFromDirAndFile(pEntry->Old.pszObjName, pEntry->pszDir);
    2449                     if (stat(pszPath, &st) != 0)
    2450                     {
    2451                         InfoMsg(2, "failed to stat object file: %s\n", strerror(errno));
    2452                         fBad = 1;
    2453                     }
    2454                     else
    2455                     {
    2456                         /** @todo verify size and the timestamp. */
    2457                     }
    2458                 }
    2459             }
    2460             pEntry->fNeedCompiling = fBad;
    2461         }
    2462         fclose(pFile);
    2463     }
    2464     else
    2465     {
    2466         InfoMsg(2, "no cache file\n");
    2467         pEntry->fNeedCompiling = 1;
    2468     }
    2469 }
    2470 
    2471 
    2472 /**
    2473  * Writes the cache file.
    2474  *
    2475  * @param   pEntry      The entry to write.
    2476  */
    2477 static void kOCEntryWrite(PKOCENTRY pEntry)
    2478 {
    2479     FILE *pFile;
    2480     PCKOCSUM pSum;
    2481     unsigned i;
    2482 
    2483     InfoMsg(4, "writing cache entry '%s'...\n", pEntry->pszName);
    2484     pFile = FOpenFileInDir(pEntry->pszName, pEntry->pszDir, "wb");
    2485     if (!pFile)
    2486         FatalDie("Failed to open '%s' in '%s': %s\n",
    2487                  pEntry->pszName, pEntry->pszDir, strerror(errno));
    2488 
    2489 #define CHECK_LEN(expr) \
    2490         do { int cch = expr; if (cch >= KOBJCACHE_MAX_LINE_LEN) FatalDie("Line too long: %d (max %d)\nexpr: %s\n", cch, KOBJCACHE_MAX_LINE_LEN, #expr); } while (0)
    2491 
    2492     fprintf(pFile, "magic=kObjCacheEntry-v0.1.1\n");
    2493     CHECK_LEN(fprintf(pFile, "target=%s\n", pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget));
    2494     CHECK_LEN(fprintf(pFile, "key=%lu\n", (unsigned long)pEntry->uKey));
    2495     CHECK_LEN(fprintf(pFile, "obj=%s\n",        pEntry->New.pszObjName ? pEntry->New.pszObjName : pEntry->Old.pszObjName));
    2496     CHECK_LEN(fprintf(pFile, "cpp=%s\n",        pEntry->New.pszCppName ? pEntry->New.pszCppName : pEntry->Old.pszCppName));
    2497     CHECK_LEN(fprintf(pFile, "cpp-size=%lu\n",  pEntry->New.pszCppName ? pEntry->New.cbCpp      : pEntry->Old.cbCpp));
    2498     CHECK_LEN(fprintf(pFile, "cpp-ms=%lu\n",    pEntry->New.pszCppName ? pEntry->New.cMsCpp     : pEntry->Old.cMsCpp));
    2499     CHECK_LEN(fprintf(pFile, "cc-ms=%lu\n",     pEntry->New.pszCppName ? pEntry->New.cMsCompile : pEntry->Old.cMsCompile));
    2500 
    2501     if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv))
    2502     {
    2503         CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->New.cArgvCompile));
    2504         for (i = 0; i < pEntry->New.cArgvCompile; i++)
    2505             CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->New.papszArgvCompile[i]));
    2506         fprintf(pFile, "cc-argv-sum=");
    2507         kOCSumFPrintf(&pEntry->New.SumCompArgv, pFile);
    2508     }
    2509     else
    2510     {
    2511         CHECK_LEN(fprintf(pFile, "cc-argc=%u\n", pEntry->Old.cArgvCompile));
    2512         for (i = 0; i < pEntry->Old.cArgvCompile; i++)
    2513             CHECK_LEN(fprintf(pFile, "cc-argv-#%u=%s\n", i, pEntry->Old.papszArgvCompile[i]));
    2514         fprintf(pFile, "cc-argv-sum=");
    2515         kOCSumFPrintf(&pEntry->Old.SumCompArgv, pFile);
    2516     }
    2517 
    2518 
    2519     for (pSum = !kOCSumIsEmpty(&pEntry->New.SumHead) ? &pEntry->New.SumHead : &pEntry->Old.SumHead;
    2520          pSum;
    2521          pSum = pSum->pNext)
    2522     {
    2523         fprintf(pFile, "cpp-sum=");
    2524         kOCSumFPrintf(pSum, pFile);
    2525     }
    2526 
    2527     fprintf(pFile, "the-end=fine\n");
    2528 
    2529 #undef CHECK_LEN
    2530 
    2531     /*
    2532      * Flush the file and check for errors.
    2533      * On failure delete the file so we won't be seeing any invalid
    2534      * files the next time or upset make with new timestamps.
    2535      */
    2536     errno = 0;
    2537     if (    fflush(pFile) < 0
    2538         ||  ferror(pFile))
    2539     {
    2540         int iErr = errno;
    2541         fclose(pFile);
    2542         UnlinkFileInDir(pEntry->pszName, pEntry->pszDir);
    2543         FatalDie("Stream error occured while writing '%s' in '%s': %s\n",
    2544                  pEntry->pszName, pEntry->pszDir, strerror(iErr));
    2545     }
    2546     fclose(pFile);
    2547 }
    2548 
    2549 
    2550 /**
    2551  * Checks that the read cache entry is valid.
    2552  * It sets fNeedCompiling if it isn't.
    2553  *
    2554  * @returns 1 valid, 0 invalid.
    2555  * @param   pEntry      The cache entry.
    2556  */
    2557 static int kOCEntryCheck(PKOCENTRY pEntry)
    2558 {
    2559     return !pEntry->fNeedCompiling;
    2560 }
    2561 
    2562 
    2563 /**
    2564  * Sets the object name and compares it with the old name if present.
    2565  *
    2566  * @param   pEntry      The cache entry.
    2567  * @param   pszObjName  The new object name.
    2568  */
    2569 static void kOCEntrySetCompileObjName(PKOCENTRY pEntry, const char *pszObjName)
    2570 {
    2571     assert(!pEntry->New.pszObjName);
    2572     pEntry->New.pszObjName = CalcRelativeName(pszObjName, pEntry->pszDir);
    2573 
    2574     if (    !pEntry->fNeedCompiling
    2575         &&  (   !pEntry->Old.pszObjName
    2576              || strcmp(pEntry->New.pszObjName, pEntry->Old.pszObjName)))
    2577     {
    2578         InfoMsg(2, "object file name differs\n");
    2579         pEntry->fNeedCompiling = 1;
    2580     }
    2581 
    2582     if (    !pEntry->fNeedCompiling
    2583         &&  !DoesFileInDirExist(pEntry->New.pszObjName, pEntry->pszDir))
    2584     {
    2585         InfoMsg(2, "object file doesn't exist\n");
    2586         pEntry->fNeedCompiling = 1;
    2587     }
    2588 }
    2589 
    2590 
    2591 /**
    2592  * Set the new compiler args, calc their checksum, and comparing them with any old ones.
    2593  *
    2594  * @param   pEntry              The cache entry.
    2595  * @param   papszArgvCompile    The new argument vector for compilation.
    2596  * @param   cArgvCompile        The number of arguments in the vector.
    2597  *
    2598  * @remark  Must call kOCEntrySetCompileObjName before this function!
    2599  */
    2600 static void kOCEntrySetCompileArgv(PKOCENTRY pEntry, const char * const *papszArgvCompile, unsigned cArgvCompile)
    2601 {
    2602     unsigned i;
    2603 
    2604     /* call me only once! */
    2605     assert(!pEntry->New.cArgvCompile);
    2606     /* call kOCEntrySetCompilerObjName first! */
    2607     assert(pEntry->New.pszObjName);
    2608 
    2609     /*
    2610      * Copy the argument vector and calculate the checksum.
    2611      */
    2612     pEntry->New.cArgvCompile = cArgvCompile;
    2613     pEntry->New.papszArgvCompile = xmalloc((cArgvCompile + 1) * sizeof(pEntry->New.papszArgvCompile[0]));
    2614     for (i = 0; i < cArgvCompile; i++)
    2615         pEntry->New.papszArgvCompile[i] = xstrdup(papszArgvCompile[i]);
    2616     pEntry->New.papszArgvCompile[i] = NULL; /* for exev/spawnv */
    2617 
    2618     kOCEntryCalcArgvSum(pEntry, papszArgvCompile, cArgvCompile, pEntry->New.pszObjName, pEntry->New.pszCppName,
    2619                         &pEntry->New.SumCompArgv);
    2620     kOCSumInfo(&pEntry->New.SumCompArgv, 4, "comp-argv");
    2621 
    2622     /*
    2623      * Compare with the old argument vector.
    2624      */
    2625     if (    !pEntry->fNeedCompiling
    2626         &&  !kOCSumIsEqual(&pEntry->New.SumCompArgv, &pEntry->Old.SumCompArgv))
    2627     {
    2628         InfoMsg(2, "compiler args differs\n");
    2629         pEntry->fNeedCompiling = 1;
    2630     }
    2631 }
    2632 
    2633 
    2634 /**
    2635  * Sets the arch/os target and compares it with the old name if present.
    2636  *
    2637  * @param   pEntry      The cache entry.
    2638  * @param   pszObjName  The new object name.
    2639  */
    2640 static void kOCEntrySetTarget(PKOCENTRY pEntry, const char *pszTarget)
    2641 {
    2642     assert(!pEntry->New.pszTarget);
    2643     pEntry->New.pszTarget = xstrdup(pszTarget);
    2644 
    2645     if (    !pEntry->fNeedCompiling
    2646         &&  (   !pEntry->Old.pszTarget
    2647              || strcmp(pEntry->New.pszTarget, pEntry->Old.pszTarget)))
    2648     {
    2649         InfoMsg(2, "target differs\n");
    2650         pEntry->fNeedCompiling = 1;
    2651     }
    2652 }
    2653 
    2654 
    2655 /**
    2656  * Sets the preprocessor output filename. We don't generally care if this
    2657  * matches the old name or not.
    2658  *
    2659  * @param   pEntry      The cache entry.
    2660  * @param   pszCppName  The preprocessor output filename.
    2661  */
    2662 static void kOCEntrySetCppName(PKOCENTRY pEntry, const char *pszCppName)
    2663 {
    2664     assert(!pEntry->New.pszCppName);
    2665     pEntry->New.pszCppName = CalcRelativeName(pszCppName, pEntry->pszDir);
    2666 }
    2667 
    2668 
    2669 /**
    2670  * Sets the piped mode of the preprocessor and compiler.
    2671  *
    2672  * @param   pEntry                  The cache entry.
    2673  * @param   fRedirPreCompStdOut     Whether the preprocessor is in piped mode.
    2674  * @param   fRedirCompileStdIn      Whether the compiler is in piped mode.
    2675  * @param   pszNmPipeCompile        The name of the named pipe to use to feed
    2676  *                                  the microsoft compiler.
    2677  */
    2678 static void kOCEntrySetPipedMode(PKOCENTRY pEntry, int fRedirPreCompStdOut, int fRedirCompileStdIn,
    2679                                  const char *pszNmPipeCompile)
    2680 {
    2681     pEntry->fPipedPreComp = fRedirPreCompStdOut;
    2682     pEntry->fPipedCompile = fRedirCompileStdIn || pszNmPipeCompile;
    2683     pEntry->pszNmPipeCompile = xstrdup(pszNmPipeCompile);
    2684 }
    2685 
    2686 
    2687 /**
    2688  * Sets the dependency file.
    2689  *
    2690  * @param   pEntry                  The cache entry.
    2691  * @param   pszMakeDepFilename      The dependency filename.
    2692  * @param   fMakeDepFixCase         Whether to fix the case of dependency files.
    2693  * @param   fMakeDepQuiet           Whether to be quiet about the dependencies.
    2694  * @param   fMakeDepGenStubs        Whether to generate stubs.
    2695  */
    2696 static void kOCEntrySetDepFilename(PKOCENTRY pEntry, const char *pszMakeDepFilename,
    2697                                    int fMakeDepFixCase, int fMakeDepQuiet, int fMakeDepGenStubs)
    2698 {
    2699     pEntry->pszMakeDepFilename = xstrdup(pszMakeDepFilename);
    2700     pEntry->fMakeDepFixCase = fMakeDepFixCase;
    2701     pEntry->fMakeDepQuiet = fMakeDepQuiet;
    2702     pEntry->fMakeDepGenStubs = fMakeDepGenStubs;
    2703 }
    2704 
    2705 
    2706 /**
    2707  * Configures the preprocessor output optimizations.
    2708  *
    2709  * @param   pEntry                  The cache entry.
    2710  * @param   fOptimizeCpp            The one and only flag, so far.
    2711  */
    2712 static void kOCEntrySetOptimizations(PKOCENTRY pEntry, int fOptimizeCpp)
    2713 {
    2714     pEntry->fOptimizeCpp = fOptimizeCpp;
    2715 }
    2716 
    2717 
    2718 /**
    2719  * Spawns a child in a synchronous fashion.
    2720  * Terminating on failure.
    2721  *
    2722  * @param   papszArgv       Argument vector. The cArgv element is NULL.
    2723  * @param   pcMs            The cache entry member use for time keeping.  This
    2724  *                          will be set to the current timestamp.
    2725  * @param   cArgv           The number of arguments in the vector.
    2726  * @param   pszMsg          Which operation this is, for use in messages.
    2727  * @param   pszStdOut       Where to redirect standard out.
    2728  */
    2729 static void kOCEntrySpawn(PCKOCENTRY pEntry, uint32_t *pcMs, const char * const *papszArgv, unsigned cArgv,
    2730                           const char *pszMsg, const char *pszStdOut)
    2731 {
    2732 #if defined(__OS2__) || defined(__WIN__)
    2733     intptr_t rc;
    2734     int fdStdOut = -1;
    2735     if (pszStdOut)
    2736     {
    2737         int fdReDir;
    2738         fdStdOut = dup(STDOUT_FILENO);
    2739         close(STDOUT_FILENO);
    2740         fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666);
    2741         if (fdReDir < 0)
    2742             FatalDie("%s - failed to create stdout redirection file '%s': %s\n",
    2743                      pszMsg, pszStdOut, strerror(errno));
    2744 
    2745         if (fdReDir != STDOUT_FILENO)
    2746         {
    2747             if (dup2(fdReDir, STDOUT_FILENO) < 0)
    2748                 FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno));
    2749             close(fdReDir);
    2750         }
    2751     }
    2752 
    2753     *pcMs = NowMs();
    2754     errno = 0;
    2755 # ifdef __WIN__
    2756     rc = quoted_spawnvp(_P_WAIT, papszArgv[0], papszArgv);
    2757 # else
    2758     rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
    2759 # endif
    2760     *pcMs = NowMs() - *pcMs;
    2761     if (rc < 0)
    2762         FatalDie("%s - _spawnvp failed (rc=0x%p): %s\n", pszMsg, rc, strerror(errno));
    2763     if (rc > 0)
    2764         FatalDie("%s - failed rc=%d\n", pszMsg, (int)rc);
    2765     if (fdStdOut != -1)
    2766     {
    2767         close(STDOUT_FILENO);
    2768         fdStdOut = dup2(fdStdOut, STDOUT_FILENO);
    2769         close(fdStdOut);
    2770     }
    2771 
    2772 #else
    2773     int iStatus;
    2774     pid_t pidWait;
    2775     pid_t pid;
    2776 
    2777     *pcMs = NowMs();
    2778     pid = fork();
    2779     if (!pid)
    2780     {
    2781         if (pszStdOut)
    2782         {
    2783             int fdReDir;
    2784 
    2785             close(STDOUT_FILENO);
    2786             fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0666);
    2787             if (fdReDir < 0)
    2788                 FatalDie("%s - failed to create stdout redirection file '%s': %s\n",
    2789                          pszMsg, pszStdOut, strerror(errno));
    2790             if (fdReDir != STDOUT_FILENO)
    2791             {
    2792                 if (dup2(fdReDir, STDOUT_FILENO) < 0)
    2793                     FatalDie("%s - dup2 failed: %s\n", pszMsg, strerror(errno));
    2794                 close(fdReDir);
    2795             }
    2796         }
    2797 
    2798         execvp(papszArgv[0], (char **)papszArgv);
    2799         FatalDie("%s - execvp failed: %s\n",
    2800                  pszMsg, strerror(errno));
    2801     }
    2802     if (pid == -1)
    2803         FatalDie("%s - fork() failed: %s\n", pszMsg, strerror(errno));
    2804 
    2805     pidWait = waitpid(pid, &iStatus, 0);
    2806     while (pidWait < 0 && errno == EINTR)
    2807         pidWait = waitpid(pid, &iStatus, 0);
    2808     *pcMs = NowMs() - *pcMs;
    2809     if (pidWait != pid)
    2810         FatalDie("%s - waitpid failed rc=%d: %s\n",
    2811                  pszMsg, pidWait, strerror(errno));
    2812     if (!WIFEXITED(iStatus))
    2813         FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
    2814     if (WEXITSTATUS(iStatus))
    2815         FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
    2816 #endif
    2817 
    2818      (void)pEntry; (void)cArgv;
    2819 }
    2820 
    2821 
    2822 /**
    2823  * Spawns child with optional redirection of stdin and stdout.
    2824  *
    2825  * @param   pEntry          The cache entry.
    2826  * @param   pcMs            The cache entry member use for time keeping.  This
    2827  *                          will be set to the current timestamp.
    2828  * @param   papszArgv       Argument vector. The cArgv element is NULL.
    2829  * @param   cArgv           The number of arguments in the vector.
    2830  * @param   fdStdIn         Child stdin, -1 if it should inherit our stdin. Will be closed.
    2831  * @param   fdStdOut        Child stdout, -1 if it should inherit our stdout. Will be closed.
    2832  * @param   pszMsg          Message to start the info/error messages with.
    2833  */
    2834 static pid_t kOCEntrySpawnChild(PCKOCENTRY pEntry, uint32_t *pcMs, const char * const *papszArgv, unsigned cArgv,
    2835                                 int fdStdIn, int fdStdOut, const char *pszMsg)
    2836 {
    2837     pid_t pid;
    2838     int fdSavedStdOut = -1;
    2839     int fdSavedStdIn = -1;
    2840 
    2841     /*
    2842      * Setup redirection.
    2843      */
    2844     if (fdStdOut != -1 && fdStdOut != STDOUT_FILENO)
    2845     {
    2846         fdSavedStdOut = dup(STDOUT_FILENO);
    2847         if (dup2(fdStdOut, STDOUT_FILENO) < 0)
    2848             FatalDie("%s - dup2(,1) failed: %s\n", pszMsg, strerror(errno));
    2849         close(fdStdOut);
    2850 #ifndef __WIN__
    2851         fcntl(fdSavedStdOut, F_SETFD, FD_CLOEXEC);
    2852 #endif
    2853     }
    2854     if (fdStdIn != -1 && fdStdIn != STDIN_FILENO)
    2855     {
    2856         fdSavedStdIn = dup(STDIN_FILENO);
    2857         if (dup2(fdStdIn, STDIN_FILENO) < 0)
    2858             FatalDie("%s - dup2(,0) failed: %s\n", pszMsg, strerror(errno));
    2859         close(fdStdIn);
    2860 #ifndef __WIN__
    2861         fcntl(fdSavedStdIn, F_SETFD, FD_CLOEXEC);
    2862 #endif
    2863     }
    2864 
    2865     /*
    2866      * Create the child process.
    2867      */
    2868     *pcMs = NowMs();
    2869 #if defined(__OS2__) || defined(__WIN__)
    2870     errno = 0;
    2871 # ifdef __WIN__
    2872     pid = quoted_spawnvp(_P_NOWAIT, papszArgv[0], papszArgv);
    2873 # else
    2874     pid = _spawnvp(_P_NOWAIT, papszArgv[0], papszArgv);
    2875 # endif
    2876     if (pid == -1)
    2877         FatalDie("preprocess - _spawnvp failed: %s\n", strerror(errno));
    2878 
    2879 #else
    2880     pid = fork();
    2881     if (!pid)
    2882     {
    2883         execvp(papszArgv[0], (char **)papszArgv);
    2884         FatalDie("preprocess - execvp failed: %s\n", strerror(errno));
    2885     }
    2886     if (pid == -1)
    2887         FatalDie("preprocess - fork() failed: %s\n", strerror(errno));
    2888 #endif
    2889 
    2890     /*
    2891      * Restore stdout & stdin.
    2892      */
    2893     if (fdSavedStdIn != -1)
    2894     {
    2895         close(STDIN_FILENO);
    2896         dup2(fdStdOut, STDIN_FILENO);
    2897         close(fdSavedStdIn);
    2898     }
    2899     if (fdSavedStdOut != -1)
    2900     {
    2901         close(STDOUT_FILENO);
    2902         dup2(fdSavedStdOut, STDOUT_FILENO);
    2903         close(fdSavedStdOut);
    2904     }
    2905 
    2906     InfoMsg(3, "%s - spawned %ld\n", pszMsg, (long)pid);
    2907     (void)cArgv;
    2908     (void)pEntry;
    2909     return pid;
    2910 }
    2911 
    2912 
    2913 /**
    2914  * Waits for a child and exits fatally if the child failed in any way.
    2915  *
    2916  * @param   pEntry      The cache entry.
    2917  * @param   pcMs        The millisecond timestamp that should be convert to
    2918  *                      elapsed time.
    2919  * @param   pid         The child to wait for.
    2920  * @param   pszMsg      Message to start the info/error messages with.
    2921  */
    2922 static void kOCEntryWaitChild(PCKOCENTRY pEntry, uint32_t *pcMs, pid_t pid, const char *pszMsg)
    2923 {
    2924     int iStatus = -1;
    2925     pid_t pidWait;
    2926     InfoMsg(3, "%s - wait-child %ld\n", pszMsg, (long)pid);
    2927 
    2928 #ifdef __WIN__
    2929     pidWait = _cwait(&iStatus, pid, _WAIT_CHILD);
    2930     *pcMs = NowMs() - *pcMs;
    2931     if (pidWait == -1)
    2932         FatalDie("%s - waitpid failed: %s\n", pszMsg, strerror(errno));
    2933     if (iStatus)
    2934         FatalDie("%s - failed with rc %d\n", pszMsg, iStatus);
    2935 #else
    2936     pidWait = waitpid(pid, &iStatus, 0);
    2937     while (pidWait < 0 && errno == EINTR)
    2938         pidWait = waitpid(pid, &iStatus, 0);
    2939     *pcMs = NowMs() - *pcMs;
    2940     if (pidWait != pid)
    2941         FatalDie("%s - waitpid failed rc=%d: %s\n", pidWait, strerror(errno));
    2942     if (!WIFEXITED(iStatus))
    2943         FatalDie("%s - abended (iStatus=%#x)\n", pszMsg, iStatus);
    2944     if (WEXITSTATUS(iStatus))
    2945         FatalDie("%s - failed with rc %d\n", pszMsg, WEXITSTATUS(iStatus));
    2946 #endif
    2947     (void)pEntry;
    2948 }
    2949 
    2950 
    2951 /**
    2952  * Creates a pipe for setting up redirected stdin/stdout.
    2953  *
    2954  * @param   pEntry          The cache entry.
    2955  * @param   paFDs           Where to store the two file descriptors.
    2956  * @param   pszMsg          The operation message for info/error messages.
    2957  * @param   pszPipeName     The pipe name if it is supposed to be named. (Windows only.)
    2958  * @param   fText           Whether to read text mode or binary mode.
    2959  */
    2960 static void kOCEntryCreatePipe(PKOCENTRY pEntry, int *paFDs, const char *pszPipeName, const char *pszMsg, int fText)
    2961 {
    2962     paFDs[0] = paFDs[1] = -1;
    2963 #if defined(__WIN__)
    2964     if (pszPipeName)
    2965     {
    2966         HANDLE hPipe = CreateNamedPipeA(pszPipeName,
    2967                                        /*PIPE_ACCESS_OUTBOUND*/ PIPE_ACCESS_DUPLEX,
    2968                                        PIPE_READMODE_BYTE | PIPE_WAIT,
    2969                                        10 /* nMaxInstances */,
    2970                                        0x10000 /* nOutBuffer */,
    2971                                        0x10000 /* nInBuffer */,
    2972                                        NMPWAIT_WAIT_FOREVER,
    2973                                        NULL /* pSecurityAttributes */);
    2974 
    2975         if (hPipe == INVALID_HANDLE_VALUE)
    2976             FatalDie("%s - CreateNamedPipe(%s) failed: %d\n", pszMsg, pszPipeName, GetLastError());
    2977 
    2978         paFDs[1 /* write */] = _open_osfhandle((intptr_t)hPipe, _O_WRONLY | _O_TEXT | _O_NOINHERIT);
    2979         if (paFDs[1 /* write */] == -1)
    2980             FatalDie("%s - _open_osfhandle failed: %d\n", pszMsg, strerror(errno));
    2981     }
    2982     else if (   _pipe(paFDs, 256*1024, _O_NOINHERIT | (fText ? _O_TEXT : _O_BINARY)) < 0
    2983              && _pipe(paFDs,        0, _O_NOINHERIT | (fText ? _O_TEXT : _O_BINARY)) < 0)
    2984 #else
    2985     if (pipe(paFDs) < 0)
    2986 #endif
    2987         FatalDie("%s - pipe failed: %s\n", pszMsg, strerror(errno));
    2988 #if !defined(__WIN__)
    2989     fcntl(paFDs[0], F_SETFD, FD_CLOEXEC);
    2990     fcntl(paFDs[1], F_SETFD, FD_CLOEXEC);
    2991 #endif
    2992 
    2993     (void)pEntry;
    2994 }
    2995 
    2996 
    2997 /**
    2998  * Spawns a child that produces output to stdout.
    2999  *
    3000  * @param   papszArgv       Argument vector. The cArgv element is NULL.
    3001  * @param   cArgv           The number of arguments in the vector.
    3002  * @param   pszMsg          The operation message for info/error messages.
    3003  * @param   pfnConsumer     Pointer to a consumer callback function that is responsible
    3004  *                          for servicing the child output and closing the pipe.
    3005  */
    3006 static void kOCEntrySpawnProducer(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg,
    3007                                   void (*pfnConsumer)(PKOCENTRY, int))
    3008 {
    3009     int fds[2];
    3010     pid_t pid;
    3011 
    3012     kOCEntryCreatePipe(pEntry, fds, NULL, pszMsg, pEntry->fOptimizeCpp);
    3013     pid = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCpp, papszArgv, cArgv, -1, fds[1 /* write */], pszMsg);
    3014 
    3015     pfnConsumer(pEntry, fds[0 /* read */]);
    3016 
    3017     kOCEntryWaitChild(pEntry, &pEntry->New.cMsCpp, pid, pszMsg);
    3018 }
    3019 
    3020 
    3021 /**
    3022  * Spawns a child that consumes input on stdin or via a named pipe.
    3023  *
    3024  * @param   papszArgv       Argument vector. The cArgv element is NULL.
    3025  * @param   cArgv           The number of arguments in the vector.
    3026  * @param   pszMsg          The operation message for info/error messages.
    3027  * @param   pfnProducer     Pointer to a producer callback function that is responsible
    3028  *                          for serving the child input and closing the pipe.
    3029  */
    3030 static void kOCEntrySpawnConsumer(PKOCENTRY pEntry, const char * const *papszArgv, unsigned cArgv, const char *pszMsg,
    3031                                   void (*pfnProducer)(PKOCENTRY, int))
    3032 {
    3033     int fds[2];
    3034     pid_t pid;
    3035 
    3036     kOCEntryCreatePipe(pEntry, fds, pEntry->pszNmPipeCompile, pszMsg, 0 /*fText*/);
    3037     pid = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCompile, papszArgv, cArgv, fds[0 /* read */], -1, pszMsg);
    3038 #ifdef __WIN__
    3039     if (pEntry->pszNmPipeCompile && !ConnectNamedPipe((HANDLE)_get_osfhandle(fds[1 /* write */]), NULL))
    3040         FatalDie("compile - ConnectNamedPipe failed: %d\n", GetLastError());
    3041 #endif
    3042 
    3043     pfnProducer(pEntry, fds[1 /* write */]);
    3044 
    3045     kOCEntryWaitChild(pEntry, &pEntry->New.cMsCompile, pid, pszMsg);
    3046 }
    3047 
    3048 
    3049 /**
    3050  * Spawns two child processes, one producing output and one consuming.
    3051  * Terminating on failure.
    3052  *
    3053  * @param   papszArgv       Argument vector. The cArgv element is NULL.
    3054  * @param   cArgv           The number of arguments in the vector.
    3055  * @param   pszMsg          The operation message for info/error messages.
    3056  * @param   pfnConsumer     Pointer to a consumer callback function that is responsible
    3057  *                          for servicing the child output and closing the pipe.
    3058  */
    3059 static void kOCEntrySpawnTee(PKOCENTRY pEntry, const char * const *papszProdArgv, unsigned cProdArgv,
    3060                              const char * const *papszConsArgv, unsigned cConsArgv,
    3061                              const char *pszMsg, void (*pfnTeeConsumer)(PKOCENTRY, int, int))
    3062 {
    3063     int fds[2];
    3064     int fdIn, fdOut;
    3065     pid_t pidProducer, pidConsumer;
    3066 
    3067     /*
    3068      * The producer.
    3069      */
    3070     kOCEntryCreatePipe(pEntry, fds, NULL, pszMsg, pEntry->fOptimizeCpp);
    3071     pidConsumer = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCpp, papszProdArgv, cProdArgv, -1, fds[1 /* write */], pszMsg);
    3072     fdIn = fds[0 /* read */];
    3073 
    3074     /*
    3075      * The consumer.
    3076      */
    3077     kOCEntryCreatePipe(pEntry, fds, pEntry->pszNmPipeCompile, pszMsg, 0 /*fText*/);
    3078     pidProducer = kOCEntrySpawnChild(pEntry, &pEntry->New.cMsCompile, papszConsArgv, cConsArgv, fds[0 /* read */], -1, pszMsg);
    3079     fdOut = fds[1 /* write */];
    3080 
    3081     /*
    3082      * Hand it on to the tee consumer.
    3083      */
    3084     pfnTeeConsumer(pEntry, fdIn, fdOut);
    3085 
    3086     /*
    3087      * Reap the children.
    3088      */
    3089     kOCEntryWaitChild(pEntry, &pEntry->New.cMsCpp, pidProducer, pszMsg);
    3090     kOCEntryWaitChild(pEntry, &pEntry->New.cMsCompile, pidConsumer, pszMsg);
    3091 }
    3092 
    3093 
    3094 /**
    3095  * Reads the output from the preprocessor.
    3096  *
    3097  * @param   pEntry      The cache entry. New.cbCpp and New.pszCppMapping will be updated.
    3098  * @param   pWhich      Specifies what to read (old/new).
    3099  * @param   fNonFatal   Whether failure is fatal or not.
    3100  */
    3101 static int kOCEntryReadCppOutput(PKOCENTRY pEntry, struct KOCENTRYDATA *pWhich, int fNonFatal)
    3102 {
    3103     pWhich->pszCppMapping = ReadFileInDir(pWhich->pszCppName, pEntry->pszDir, &pWhich->cbCpp);
    3104     if (!pWhich->pszCppMapping)
    3105     {
    3106         if (!fNonFatal)
    3107             FatalDie("failed to open/read '%s' in '%s': %s\n",
    3108                      pWhich->pszCppName, pEntry->pszDir, strerror(errno));
    3109         InfoMsg(2, "failed to open/read '%s' in '%s': %s\n",
    3110                 pWhich->pszCppName, pEntry->pszDir, strerror(errno));
    3111         return -1;
    3112     }
    3113 
    3114     InfoMsg(3, "preprocessed file is %lu bytes long\n", (unsigned long)pWhich->cbCpp);
    3115     return 0;
    3116 }
    3117 
    3118 
    3119 /**
    3120  * Worker for kOCEntryPreProcess and calculates the checksum of
    3121  * the preprocessor output.
    3122  *
    3123  * @param   pEntry      The cache entry. NewSum will be updated.
    3124  */
    3125 static void kOCEntryCalcChecksum(PKOCENTRY pEntry)
    3126 {
    3127     KOCSUMCTX Ctx;
    3128     kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
    3129     kOCSumUpdate(&pEntry->New.SumHead, &Ctx, pEntry->New.pszCppMapping, pEntry->New.cbCpp);
    3130     kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
    3131     kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (file)");
    3132 }
    3133 
    3134 
    3135 /**
    3136  * This consumes the preprocessor output and checksums it.
    3137  *
    3138  * @param   pEntry  The cache entry.
    3139  * @param   fdIn    The preprocessor output pipe.
    3140  * @param   fdOut   The compiler input pipe, -1 if no compiler.
    3141  */
    3142 static void kOCEntryPreProcessConsumer(PKOCENTRY pEntry, int fdIn)
    3143 {
    3144     KOCSUMCTX Ctx;
    3145     KOCCPPRD CppRd;
    3146 
    3147     kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
    3148     kOCCppRdInit(&CppRd, pEntry->Old.cbCpp, pEntry->fOptimizeCpp,
    3149                  pEntry->pszMakeDepFilename ? &pEntry->DepState : NULL);
    3150 
    3151     for (;;)
    3152     {
    3153         /*
    3154          * Read data from the pipe.
    3155          */
    3156         const char *psz;
    3157         long cbRead = kOCCppRdRead(&CppRd, fdIn, &psz);
    3158         if (!cbRead)
    3159             break;
    3160 
    3161         /*
    3162          * Process the data.
    3163          */
    3164         kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead);
    3165         if (pEntry->pszMakeDepFilename && !pEntry->fOptimizeCpp)
    3166             kOCDepConsumer(&pEntry->DepState, psz, cbRead);
    3167     }
    3168 
    3169     close(fdIn);
    3170     kOCCppRdGrabOutput(&CppRd, &pEntry->New.pszCppMapping, &pEntry->New.cbCpp);
    3171     kOCCppRdDelete(&CppRd);
    3172     kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
    3173     kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (pipe)");
    3174 }
    3175 
    3176 
    3177 
    3178 
    3179 /**
    3180  * Run the preprocessor and calculate the checksum of the output.
    3181  *
    3182  * @param   pEntry              The cache entry.
    3183  * @param   papszArgvPreComp    The argument vector for executing preprocessor.
    3184  *                              The cArgvPreComp'th argument must be NULL.
    3185  * @param   cArgvPreComp        The number of arguments.
    3186  */
    3187 static void kOCEntryPreProcess(PKOCENTRY pEntry, const char * const *papszArgvPreComp, unsigned cArgvPreComp)
    3188 {
    3189     /*
    3190      * If we're executing the preprocessor in piped mode, it's relatively simple.
    3191      */
    3192     if (pEntry->fPipedPreComp)
    3193         kOCEntrySpawnProducer(pEntry, papszArgvPreComp, cArgvPreComp, "preprocess",
    3194                               kOCEntryPreProcessConsumer);
    3195     else
    3196     {
    3197         /*
    3198          * Rename the old preprocessed output to '-old' so the preprocessor won't
    3199          * overwrite it when we execute it.
    3200          */
    3201         if (    pEntry->Old.pszCppName
    3202             &&  DoesFileInDirExist(pEntry->Old.pszCppName, pEntry->pszDir))
    3203         {
    3204             size_t cch = strlen(pEntry->Old.pszCppName);
    3205             char *psz = xmalloc(cch + sizeof("-old"));
    3206             memcpy(psz, pEntry->Old.pszCppName, cch);
    3207             memcpy(psz + cch, "-old", sizeof("-old"));
    3208 
    3209             InfoMsg(3, "renaming '%s' to '%s' in '%s'\n", pEntry->Old.pszCppName, psz, pEntry->pszDir);
    3210             UnlinkFileInDir(psz, pEntry->pszDir);
    3211             if (RenameFileInDir(pEntry->Old.pszCppName, psz, pEntry->pszDir))
    3212                 FatalDie("failed to rename '%s' -> '%s' in '%s': %s\n",
    3213                          pEntry->Old.pszCppName, psz, pEntry->pszDir, strerror(errno));
    3214             free(pEntry->Old.pszCppName);
    3215             pEntry->Old.pszCppName = psz;
    3216         }
    3217 
    3218         /*
    3219          * Preprocess it and calculate the checksum on the output.
    3220          */
    3221         InfoMsg(3, "precompiling -> '%s'...\n", pEntry->New.pszCppName);
    3222         kOCEntrySpawn(pEntry, &pEntry->New.cMsCpp, papszArgvPreComp, cArgvPreComp, "preprocess", NULL);
    3223         kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */);
    3224         kOCEntryCalcChecksum(pEntry);
    3225         if (pEntry->pszMakeDepFilename)
    3226             kOCDepConsumer(&pEntry->DepState, pEntry->New.pszCppMapping, pEntry->New.cbCpp);
    3227     }
    3228 
    3229     if (pEntry->pszMakeDepFilename)
    3230         kOCDepWriteToFile(&pEntry->DepState, pEntry->pszMakeDepFilename, pEntry->New.pszObjName, pEntry->pszDir,
    3231                           pEntry->fMakeDepFixCase, pEntry->fMakeDepQuiet, pEntry->fMakeDepGenStubs);
    3232 }
    3233 
    3234 
    3235 /**
    3236  * Worker function for kOCEntryTeeConsumer and kOCEntryCompileIt that
    3237  * writes the preprocessor output to disk.
    3238  *
    3239  * @param   pEntry      The cache entry.
    3240  * @param   fFreeIt     Whether we can free it after writing it or not.
    3241  */
    3242 static void kOCEntryWriteCppOutput(PKOCENTRY pEntry, int fFreeIt)
    3243 {
    3244     /*
    3245      * Remove old files.
    3246      */
    3247     if (pEntry->Old.pszCppName)
    3248         UnlinkFileInDir(pEntry->Old.pszCppName, pEntry->pszDir);
    3249     if (pEntry->New.pszCppName)
    3250         UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir);
    3251 
    3252     /*
    3253      * Write it to disk if we've got a file name.
    3254      */
    3255     if (pEntry->New.pszCppName)
    3256     {
    3257         long cbLeft;
    3258         char *psz;
    3259         int fd = OpenFileInDir(pEntry->New.pszCppName, pEntry->pszDir,
    3260                                O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
    3261         if (fd == -1)
    3262             FatalDie("Failed to create '%s' in '%s': %s\n",
    3263                      pEntry->New.pszCppName, pEntry->pszDir, strerror(errno));
    3264         psz = pEntry->New.pszCppMapping;
    3265         cbLeft = (long)pEntry->New.cbCpp;
    3266         while (cbLeft > 0)
    3267         {
    3268             long cbWritten = write(fd, psz, cbLeft);
    3269             if (cbWritten < 0)
    3270             {
    3271                 int iErr = errno;
    3272                 if (iErr == EINTR)
    3273                     continue;
    3274                 close(fd);
    3275                 UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir);
    3276                 FatalDie("error writing '%s' in '%s': %s\n",
    3277                          pEntry->New.pszCppName, pEntry->pszDir, strerror(iErr));
    3278             }
    3279 
    3280             psz += cbWritten;
    3281             cbLeft -= cbWritten;
    3282         }
    3283         close(fd);
    3284     }
    3285 
    3286     /*
    3287      * Free it.
    3288      */
    3289     if (fFreeIt)
    3290     {
    3291         free(pEntry->New.pszCppMapping);
    3292         pEntry->New.pszCppMapping = NULL;
    3293     }
    3294 }
    3295 
    3296 
    3297 /**
    3298  * kOCEntrySpawnConsumer callback that passes the preprocessor output to the
    3299  * compiler and writes it to the disk (latter only when necesary).
    3300  *
    3301  * @param   pEntry      The cache entry.
    3302  * @param   fdOut       The pipe handle connected to the childs stdin.
    3303  */
    3304 static void kOCEntryCompileProducer(PKOCENTRY pEntry, int fdOut)
    3305 {
    3306     const char *psz = pEntry->New.pszCppMapping;
    3307     long cbLeft = (long)pEntry->New.cbCpp;
    3308     while (cbLeft > 0)
    3309     {
    3310         long cbWritten = write(fdOut, psz, cbLeft);
    3311         if (cbWritten < 0)
    3312         {
    3313             if (errno == EINTR)
    3314                 continue;
    3315 #ifdef __WIN__ /* HACK */
    3316             if (   errno == EINVAL
    3317                 && pEntry->pszNmPipeCompile
    3318                 && DisconnectNamedPipe((HANDLE)_get_osfhandle(fdOut))
    3319                 && ConnectNamedPipe((HANDLE)_get_osfhandle(fdOut), NULL))
    3320             {
    3321                 psz = pEntry->New.pszCppMapping;
    3322                 cbLeft = (long)pEntry->New.cbCpp;
    3323             }
    3324             FatalDie("compile - write(%d,,%ld) failed: %s - _doserrno=%d\n", fdOut, cbLeft, strerror(errno), _doserrno);
    3325 #else
    3326             FatalDie("compile - write(%d,,%ld) failed: %s\n", fdOut, cbLeft, strerror(errno));
    3327 #endif
    3328         }
    3329         psz += cbWritten;
    3330         cbLeft -= cbWritten;
    3331     }
    3332     close(fdOut);
    3333 
    3334     if (pEntry->fPipedPreComp)
    3335         kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
    3336 }
    3337 
    3338 
    3339 /**
    3340  * Does the actual compiling.
    3341  *
    3342  * @param   pEntry      The cache entry.
    3343  */
    3344 static void kOCEntryCompileIt(PKOCENTRY pEntry)
    3345 {
    3346     /*
    3347      * Delete the object files and free old cpp output that's no longer needed.
    3348      */
    3349     if (pEntry->Old.pszObjName)
    3350         UnlinkFileInDir(pEntry->Old.pszObjName, pEntry->pszDir);
    3351     UnlinkFileInDir(pEntry->New.pszObjName, pEntry->pszDir);
    3352 
    3353     free(pEntry->Old.pszCppMapping);
    3354     pEntry->Old.pszCppMapping = NULL;
    3355     if (!pEntry->fPipedPreComp && !pEntry->fPipedCompile)
    3356     {
    3357         free(pEntry->New.pszCppMapping);
    3358         pEntry->New.pszCppMapping = NULL;
    3359     }
    3360 
    3361     /*
    3362      * Do the (re-)compile job.
    3363      */
    3364     if (pEntry->fPipedCompile)
    3365     {
    3366         if (    !pEntry->fPipedPreComp
    3367             &&  !pEntry->New.pszCppMapping)
    3368             kOCEntryReadCppOutput(pEntry, &pEntry->New, 0 /* fatal */);
    3369         InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName);
    3370         kOCEntrySpawnConsumer(pEntry, (const char * const *)pEntry->New.papszArgvCompile,
    3371                               pEntry->New.cArgvCompile, "compile", kOCEntryCompileProducer);
    3372     }
    3373     else
    3374     {
    3375         if (pEntry->fPipedPreComp)
    3376             kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
    3377         InfoMsg(3, "compiling -> '%s'...\n", pEntry->New.pszObjName);
    3378         kOCEntrySpawn(pEntry, &pEntry->New.cMsCompile, (const char * const *)pEntry->New.papszArgvCompile,
    3379                       pEntry->New.cArgvCompile, "compile", NULL);
    3380     }
    3381 }
    3382 
    3383 
    3384 /**
    3385  * kOCEntrySpawnTee callback that works sort of like 'tee'.
    3386  *
    3387  * It will calculate the preprocessed output checksum and
    3388  * write it to disk while the compiler is busy compiling it.
    3389  *
    3390  * @param   pEntry  The cache entry.
    3391  * @param   fdIn    The input handle (connected to the preprocessor).
    3392  * @param   fdOut   The output handle (connected to the compiler).
    3393  */
    3394 static void kOCEntryTeeConsumer(PKOCENTRY pEntry, int fdIn, int fdOut)
    3395 {
    3396 #ifdef __WIN__
    3397     unsigned fConnectedToCompiler = fdOut == -1 || pEntry->pszNmPipeCompile == NULL;
    3398 #endif
    3399     KOCSUMCTX Ctx;
    3400     KOCCPPRD  CppRd;
    3401 
    3402     kOCSumInitWithCtx(&pEntry->New.SumHead, &Ctx);
    3403     kOCCppRdInit(&CppRd, pEntry->Old.cbCpp, pEntry->fOptimizeCpp,
    3404                  pEntry->pszMakeDepFilename ? &pEntry->DepState : NULL);
    3405     InfoMsg(3, "preprocessor|compile - starting passhtru...\n");
    3406     for (;;)
    3407     {
    3408         /*
    3409          * Read data from the pipe.
    3410          */
    3411         const char *psz;
    3412         long cbRead = kOCCppRdRead(&CppRd, fdIn, &psz);
    3413         if (!cbRead)
    3414             break;
    3415         InfoMsg(3, "preprocessor|compile - read %d\n", cbRead);
    3416 
    3417         /*
    3418          * Process the data.
    3419          */
    3420         kOCSumUpdate(&pEntry->New.SumHead, &Ctx, psz, cbRead);
    3421         if (pEntry->pszMakeDepFilename && !pEntry->fOptimizeCpp)
    3422             kOCDepConsumer(&pEntry->DepState, psz, cbRead);
    3423 
    3424 #ifdef __WIN__
    3425         if (   !fConnectedToCompiler
    3426             && !(fConnectedToCompiler = ConnectNamedPipe((HANDLE)_get_osfhandle(fdOut), NULL)))
    3427             FatalDie("preprocess|compile - ConnectNamedPipe failed: %d\n", GetLastError());
    3428 #endif
    3429         do
    3430         {
    3431             long cbWritten = write(fdOut, psz, cbRead);
    3432             if (cbWritten < 0)
    3433             {
    3434                 if (errno == EINTR)
    3435                     continue;
    3436                 FatalDie("preprocess|compile - write(%d,,%ld) failed: %s\n", fdOut, cbRead, strerror(errno));
    3437             }
    3438             psz += cbWritten;
    3439             cbRead -= cbWritten;
    3440         } while (cbRead > 0);
    3441 
    3442     }
    3443     InfoMsg(3, "preprocessor|compile - done passhtru\n");
    3444 
    3445     close(fdIn);
    3446     close(fdOut);
    3447     kOCCppRdGrabOutput(&CppRd, &pEntry->New.pszCppMapping, &pEntry->New.cbCpp);
    3448     kOCCppRdDelete(&CppRd);
    3449     kOCSumFinalize(&pEntry->New.SumHead, &Ctx);
    3450     kOCSumInfo(&pEntry->New.SumHead, 4, "cpp (tee)");
    3451 
    3452     /*
    3453      * Write the preprocessor output to disk and free the memory it
    3454      * occupies while the compiler is busy compiling.
    3455      */
    3456     kOCEntryWriteCppOutput(pEntry, 1 /* free it */);
    3457 }
    3458 
    3459 
    3460 /**
    3461  * Performs pre-compile and compile in one go (typical clean build scenario).
    3462  *
    3463  * @param   pEntry              The cache entry.
    3464  * @param   papszArgvPreComp    The argument vector for executing preprocessor.
    3465  *                              The cArgvPreComp'th argument must be NULL.
    3466  * @param   cArgvPreComp        The number of arguments.
    3467  */
    3468 static void kOCEntryPreProcessAndCompile(PKOCENTRY pEntry, const char * const *papszArgvPreComp, unsigned cArgvPreComp)
    3469 {
    3470     if (    pEntry->fPipedCompile
    3471         &&  pEntry->fPipedPreComp)
    3472     {
    3473         /*
    3474          * Clean up old stuff first.
    3475          */
    3476         if (pEntry->Old.pszObjName)
    3477             UnlinkFileInDir(pEntry->Old.pszObjName, pEntry->pszDir);
    3478         if (pEntry->New.pszObjName)
    3479             UnlinkFileInDir(pEntry->New.pszObjName, pEntry->pszDir);
    3480         if (pEntry->Old.pszCppName)
    3481             UnlinkFileInDir(pEntry->Old.pszCppName, pEntry->pszDir);
    3482         if (pEntry->New.pszCppName)
    3483             UnlinkFileInDir(pEntry->New.pszCppName, pEntry->pszDir);
    3484 
    3485         /*
    3486          * Do the actual compile and write the preprocessor output to disk.
    3487          */
    3488         kOCEntrySpawnTee(pEntry, papszArgvPreComp, cArgvPreComp,
    3489                          (const char * const *)pEntry->New.papszArgvCompile, pEntry->New.cArgvCompile,
    3490                          "preprocess|compile", kOCEntryTeeConsumer);
    3491         if (pEntry->pszMakeDepFilename)
    3492             kOCDepWriteToFile(&pEntry->DepState, pEntry->pszMakeDepFilename,  pEntry->New.pszObjName, pEntry->pszDir,
    3493                               pEntry->fMakeDepFixCase, pEntry->fMakeDepQuiet, pEntry->fMakeDepGenStubs);
    3494     }
    3495     else
    3496     {
    3497         kOCEntryPreProcess(pEntry, papszArgvPreComp, cArgvPreComp);
    3498         kOCEntryCompileIt(pEntry);
    3499     }
    3500 }
    3501 
    3502 
    3503 /**
    3504  * Check whether the string is a '#line' statement.
    3505  *
    3506  * @returns 1 if it is, 0 if it isn't.
    3507  * @param   psz         The line to examin.
    3508  * @parma   piLine      Where to store the line number.
    3509  * @parma   ppszFile    Where to store the start of the filename.
    3510  */
    3511 static int kOCEntryIsLineStatement(const char *psz, unsigned *piLine, const char **ppszFile)
    3512 {
    3513     unsigned iLine;
    3514 
    3515     /* Expect a hash. */
    3516     if (*psz++ != '#')
    3517         return 0;
    3518 
    3519     /* Skip blanks between '#' and the line / number */
    3520     while (*psz == ' ' || *psz == '\t')
    3521         psz++;
    3522 
    3523     /* Skip the 'line' if present. */
    3524     if (!strncmp(psz, "line", sizeof("line") - 1))
    3525         psz += sizeof("line");
    3526 
    3527     /* Expect a line number now. */
    3528     if ((unsigned char)(*psz - '0') > 9)
    3529         return 0;
    3530     iLine = 0;
    3531     do
    3532     {
    3533         iLine *= 10;
    3534         iLine += (*psz - '0');
    3535         psz++;
    3536     }
    3537     while ((unsigned char)(*psz - '0') <= 9);
    3538 
    3539     /* Expect one or more space now. */
    3540     if (*psz != ' ' && *psz != '\t')
    3541         return 0;
    3542     do  psz++;
    3543     while (*psz == ' ' || *psz == '\t');
    3544 
    3545     /* that's good enough. */
    3546     *piLine = iLine;
    3547     *ppszFile = psz;
    3548     return 1;
    3549 }
    3550 
    3551 
    3552 /**
    3553  * Scan backwards for the previous #line statement.
    3554  *
    3555  * @returns The filename in the previous statement.
    3556  * @param   pszStart        Where to start.
    3557  * @param   pszStop         Where to stop. Less than pszStart.
    3558  * @param   piLine          The line number count to adjust.
    3559  */
    3560 static const char *kOCEntryFindFileStatement(const char *pszStart, const char *pszStop, unsigned *piLine)
    3561 {
    3562     unsigned iLine = *piLine;
    3563     assert(pszStart >= pszStop);
    3564     while (pszStart >= pszStop)
    3565     {
    3566         if (*pszStart == '\n')
    3567             iLine++;
    3568         else if (*pszStart == '#')
    3569         {
    3570             unsigned iLineTmp;
    3571             const char *pszFile;
    3572             const char *psz = pszStart - 1;
    3573             while (psz >= pszStop && (*psz == ' ' || *psz =='\t'))
    3574                 psz--;
    3575             if (    (psz < pszStop || *psz == '\n')
    3576                 &&  kOCEntryIsLineStatement(pszStart, &iLineTmp, &pszFile))
    3577             {
    3578                 *piLine = iLine + iLineTmp - 1;
    3579                 return pszFile;
    3580             }
    3581         }
    3582         pszStart--;
    3583     }
    3584     return NULL;
    3585 }
    3586 
    3587 
    3588 /**
    3589  * Worker for kOCEntryCompareOldAndNewOutput() that compares the
    3590  * preprocessed output using a fast but not very good method.
    3591  *
    3592  * @returns 1 if matching, 0 if not matching.
    3593  * @param   pEntry      The entry containing the names of the files to compare.
    3594  *                      The entry is not updated in any way.
    3595  */
    3596 static int kOCEntryCompareFast(PCKOCENTRY pEntry)
    3597 {
    3598     const char *        psz1 = pEntry->New.pszCppMapping;
    3599     const char * const  pszEnd1 = psz1 + pEntry->New.cbCpp;
    3600     const char *        psz2 = pEntry->Old.pszCppMapping;
    3601     const char * const  pszEnd2 = psz2 + pEntry->Old.cbCpp;
    3602 
    3603     assert(*pszEnd1 == '\0');
    3604     assert(*pszEnd2 == '\0');
    3605 
    3606     /*
    3607      * Iterate block by block and backtrack when we find a difference.
    3608      */
    3609     for (;;)
    3610     {
    3611         size_t cch = pszEnd1 - psz1;
    3612         if (cch > (size_t)(pszEnd2 - psz2))
    3613             cch = pszEnd2 - psz2;
    3614         if (cch > 4096)
    3615             cch = 4096;
    3616         if (    cch
    3617             &&  !memcmp(psz1, psz2, cch))
    3618         {
    3619             /* no differences */
    3620             psz1 += cch;
    3621             psz2 += cch;
    3622         }
    3623         else
    3624         {
    3625             /*
    3626              * Pinpoint the difference exactly and the try find the start
    3627              * of that line. Then skip forward until we find something to
    3628              * work on that isn't spaces, #line statements or closing curly
    3629              * braces.
    3630              *
    3631              * The closing curly braces are ignored because they are frequently
    3632              * found at the end of header files (__END_DECLS) and the worst
    3633              * thing that may happen if it isn't one of these braces we're
    3634              * ignoring is that the final line in a function block is a little
    3635              * bit off in the debug info.
    3636              *
    3637              * Since we might be skipping a few new empty headers, it is
    3638              * possible that we will omit this header from the dependencies
    3639              * when using VCC. This might not be a problem, since it seems
    3640              * we'll have to use the preprocessor output to generate the deps
    3641              * anyway.
    3642              */
    3643             const char *psz;
    3644             const char *pszMismatch1;
    3645             const char *pszFile1 = NULL;
    3646             unsigned    iLine1 = 0;
    3647             unsigned    cCurlyBraces1 = 0;
    3648             const char *pszMismatch2;
    3649             const char *pszFile2 = NULL;
    3650             unsigned    iLine2 = 0;
    3651             unsigned    cCurlyBraces2 = 0;
    3652 
    3653             /* locate the difference. */
    3654             while (cch >= 512 && !memcmp(psz1, psz2, 512))
    3655                 psz1 += 512, psz2 += 512, cch -= 512;
    3656             while (cch >= 64 && !memcmp(psz1, psz2, 64))
    3657                 psz1 += 64, psz2 += 64, cch -= 64;
    3658             while (*psz1 == *psz2 && cch > 0)
    3659                 psz1++, psz2++, cch--;
    3660 
    3661             /* locate the start of that line. */
    3662             psz = psz1;
    3663             while (     psz > pEntry->New.pszCppMapping
    3664                    &&   psz[-1] != '\n')
    3665                 psz--;
    3666             psz2 -= (psz1 - psz);
    3667             pszMismatch2 = psz2;
    3668             pszMismatch1 = psz1 = psz;
    3669 
    3670             /* Parse the 1st file line by line. */
    3671             while (psz1 < pszEnd1)
    3672             {
    3673                 if (*psz1 == '\n')
    3674                 {
    3675                     psz1++;
    3676                     iLine1++;
    3677                 }
    3678                 else
    3679                 {
    3680                     psz = psz1;
    3681                     while (isspace(*psz) && *psz != '\n')
    3682                         psz++;
    3683                     if (*psz == '\n')
    3684                     {
    3685                         psz1 = psz + 1;
    3686                         iLine1++;
    3687                     }
    3688                     else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine1, &pszFile1))
    3689                     {
    3690                         psz1 = memchr(psz, '\n', pszEnd1 - psz);
    3691                         if (!psz1++)
    3692                             psz1 = pszEnd1;
    3693                     }
    3694                     else if (*psz == '}')
    3695                     {
    3696                         do psz++;
    3697                         while (isspace(*psz) && *psz != '\n');
    3698                         if (*psz == '\n')
    3699                             iLine1++;
    3700                         else if (psz != pszEnd1)
    3701                             break;
    3702                         cCurlyBraces1++;
    3703                         psz1 = psz;
    3704                     }
    3705                     else if (psz == pszEnd1)
    3706                         psz1 = psz;
    3707                     else /* found something that can be compared. */
    3708                         break;
    3709                 }
    3710             }
    3711 
    3712             /* Ditto for the 2nd file. */
    3713             while (psz2 < pszEnd2)
    3714             {
    3715                 if (*psz2 == '\n')
    3716                 {
    3717                     psz2++;
    3718                     iLine2++;
    3719                 }
    3720                 else
    3721                 {
    3722                     psz = psz2;
    3723                     while (isspace(*psz) && *psz != '\n')
    3724                         psz++;
    3725                     if (*psz == '\n')
    3726                     {
    3727                         psz2 = psz + 1;
    3728                         iLine2++;
    3729                     }
    3730                     else if (*psz == '#' && kOCEntryIsLineStatement(psz, &iLine2, &pszFile2))
    3731                     {
    3732                         psz2 = memchr(psz, '\n', pszEnd2 - psz);
    3733                         if (!psz2++)
    3734                             psz2 = pszEnd2;
    3735                     }
    3736                     else if (*psz == '}')
    3737                     {
    3738                         do psz++;
    3739                         while (isspace(*psz) && *psz != '\n');
    3740                         if (*psz == '\n')
    3741                             iLine2++;
    3742                         else if (psz != pszEnd2)
    3743                             break;
    3744                         cCurlyBraces2++;
    3745                         psz2 = psz;
    3746                     }
    3747                     else if (psz == pszEnd2)
    3748                         psz2 = psz;
    3749                     else /* found something that can be compared. */
    3750                         break;
    3751                 }
    3752             }
    3753 
    3754             /* Match the number of ignored closing curly braces. */
    3755             if (cCurlyBraces1 != cCurlyBraces2)
    3756                 return 0;
    3757 
    3758             /* Reaching the end of any of them means the return statement can decide. */
    3759             if (   psz1 == pszEnd1
    3760                 || psz2 == pszEnd2)
    3761                 break;
    3762 
    3763             /* Match the current line. */
    3764             psz = memchr(psz1, '\n', pszEnd1 - psz1);
    3765             if (!psz++)
    3766                 psz = pszEnd1;
    3767             cch = psz - psz1;
    3768             if (psz2 + cch > pszEnd2)
    3769                 break;
    3770             if (memcmp(psz1, psz2, cch))
    3771                 break;
    3772 
    3773             /* Check that we're at the same location now. */
    3774             if (!pszFile1)
    3775                 pszFile1 = kOCEntryFindFileStatement(pszMismatch1, pEntry->New.pszCppMapping, &iLine1);
    3776             if (!pszFile2)
    3777                 pszFile2 = kOCEntryFindFileStatement(pszMismatch2, pEntry->Old.pszCppMapping, &iLine2);
    3778             if (pszFile1 && pszFile2)
    3779             {
    3780                 if (iLine1 != iLine2)
    3781                     break;
    3782                 while (*pszFile1 == *pszFile2 && *pszFile1 != '\n' && *pszFile1)
    3783                     pszFile1++, pszFile2++;
    3784                 if (*pszFile1 != *pszFile2)
    3785                     break;
    3786             }
    3787             else if (pszFile1 || pszFile2)
    3788             {
    3789                 assert(0); /* this shouldn't happen. */
    3790                 break;
    3791             }
    3792 
    3793             /* Advance. We might now have a misaligned buffer, but that's memcmps problem... */
    3794             psz1 += cch;
    3795             psz2 += cch;
    3796         }
    3797     }
    3798 
    3799     return psz1 == pszEnd1
    3800         && psz2 == pszEnd2;
    3801 }
    3802 
    3803 
    3804 /**
    3805  * Worker for kOCEntryCompileIfNeeded that compares the
    3806  * preprocessed output.
    3807  *
    3808  * @returns 1 if matching, 0 if not matching.
    3809  * @param   pEntry      The entry containing the names of the files to compare.
    3810  *                      This will load the old cpp output (changing pszOldCppName and Old.cbCpp).
    3811  */
    3812 static int kOCEntryCompareOldAndNewOutput(PKOCENTRY pEntry)
    3813 {
    3814     /*
    3815      * I may implement a more sophisticated alternative method later... maybe.
    3816      */
    3817     if (kOCEntryReadCppOutput(pEntry, &pEntry->Old, 1 /* nonfatal */) == -1)
    3818         return 0;
    3819     /*if ()
    3820         return kOCEntryCompareBest(pEntry);*/
    3821     return kOCEntryCompareFast(pEntry);
    3822 }
    3823 
    3824 
    3825 /**
    3826  * Check if re-compilation is required.
    3827  * This sets the fNeedCompile flag.
    3828  *
    3829  * @param   pEntry              The cache entry.
    3830  */
    3831 static void kOCEntryCalcRecompile(PKOCENTRY pEntry)
    3832 {
    3833     if (pEntry->fNeedCompiling)
    3834         return;
    3835 
    3836     /*
    3837      * Check if the preprocessor output differ in any significant way?
    3838      */
    3839     if (!kOCSumHasEqualInChain(&pEntry->Old.SumHead, &pEntry->New.SumHead))
    3840     {
    3841         if (pEntry->fOptimizeCpp & 2)
    3842         {
    3843             InfoMsg(2, "no checksum match - no need to compare output, -O2.\n");
    3844             pEntry->fNeedCompiling = 1;
    3845         }
    3846         else
    3847         {
    3848             InfoMsg(2, "no checksum match - comparing output\n");
    3849             if (!kOCEntryCompareOldAndNewOutput(pEntry))
    3850                 pEntry->fNeedCompiling = 1;
    3851             else
    3852                 kOCSumAddChain(&pEntry->New.SumHead, &pEntry->Old.SumHead);
    3853         }
    3854     }
    3855 }
    3856 
    3857 
    3858 /**
    3859  * Does this cache entry need compiling or what?
    3860  *
    3861  * @returns 1 if it does, 0 if it doesn't.
    3862  * @param   pEntry      The cache entry in question.
    3863  */
    3864 static int kOCEntryNeedsCompiling(PCKOCENTRY pEntry)
    3865 {
    3866     return pEntry->fNeedCompiling;
    3867 }
    3868 
    3869 
    3870 /**
    3871  * Tries to hardlink a file.
    3872  *
    3873  * @returns 1 if it succeeded, 0 if it didn't.
    3874  * @param   pszLink     The name of the hardlink.
    3875  * @param   pszLinkTo   The file to hardlinkg @a pszDst to.
    3876  */
    3877 static int kOCEntryTryHardlink(const char *pszLink, const char *pszLinkTo)
    3878 {
    3879 #ifdef __WIN__
    3880     typedef BOOL (WINAPI *PFNCREATEHARDLINKA)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES);
    3881     static PFNCREATEHARDLINKA   s_pfnCreateHardLinkA = NULL;
    3882     static int                  s_fTried = FALSE;
    3883 
    3884     /* The API was introduced in Windows 2000, so resolve it dynamically. */
    3885     if (!s_pfnCreateHardLinkA)
    3886     {
    3887         if (!s_fTried)
    3888         {
    3889             HMODULE hmod = LoadLibrary("KERNEL32.DLL");
    3890             if (hmod)
    3891                 *(FARPROC *)&s_pfnCreateHardLinkA = GetProcAddress(hmod, "CreateHardLinkA");
    3892             s_fTried = TRUE;
    3893         }
    3894         if (!s_pfnCreateHardLinkA)
    3895             return 0;
    3896     }
    3897 
    3898     if (!s_pfnCreateHardLinkA(pszLink, pszLinkTo, NULL))
    3899         return 0;
    3900 #else
    3901     if (link(pszLinkTo, pszLink) != 0)
    3902         return 0;
    3903 #endif
    3904     return 1;
    3905 }
    3906 
    3907 
    3908 
    3909 /**
    3910  * Worker function for kOCEntryCopy.
    3911  *
    3912  * @param   pEntry      The entry we're coping to, which pszTo is relative to.
    3913  * @param   pszTo       The destination.
    3914  * @param   pszFrom     The source. This path will be freed.
    3915  */
    3916 static void kOCEntryCopyFile(PCKOCENTRY pEntry, const char *pszTo, char *pszSrc)
    3917 {
    3918     char *pszDst = MakePathFromDirAndFile(pszTo, pEntry->pszDir);
    3919     unlink(pszDst);
    3920     if (!kOCEntryTryHardlink(pszDst, pszSrc))
    3921     {
    3922         char *pszBuf = xmalloc(256 * 1024);
    3923         char *psz;
    3924         int fdSrc;
    3925         int fdDst;
    3926 
    3927         /*
    3928          * Open the files.
    3929          */
    3930         fdSrc = open(pszSrc, O_RDONLY | O_BINARY);
    3931         if (fdSrc == -1)
    3932             FatalDie("failed to open '%s': %s\n", pszSrc, strerror(errno));
    3933 
    3934         fdDst = open(pszDst, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
    3935         if (fdDst == -1)
    3936             FatalDie("failed to create '%s': %s\n", pszDst, strerror(errno));
    3937 
    3938         /*
    3939          * Copy them.
    3940          */
    3941         for (;;)
    3942         {
    3943             /* read a chunk. */
    3944             long cbRead = read(fdSrc, pszBuf, 256*1024);
    3945             if (cbRead < 0)
    3946             {
    3947                 if (errno == EINTR)
    3948                     continue;
    3949                 FatalDie("read '%s' failed: %s\n", pszSrc, strerror(errno));
    3950             }
    3951             if (!cbRead)
    3952                 break; /* eof */
    3953 
    3954             /* write the chunk. */
    3955             psz = pszBuf;
    3956             do
    3957             {
    3958                 long cbWritten = write(fdDst, psz, cbRead);
    3959                 if (cbWritten < 0)
    3960                 {
    3961                     if (errno == EINTR)
    3962                         continue;
    3963                     FatalDie("write '%s' failed: %s\n", pszSrc, strerror(errno));
    3964                 }
    3965                 psz += cbWritten;
    3966                 cbRead -= cbWritten;
    3967             } while (cbRead > 0);
    3968         }
    3969 
    3970         /* cleanup */
    3971         if (close(fdDst) != 0)
    3972             FatalDie("closing '%s' failed: %s\n", pszDst, strerror(errno));
    3973         close(fdSrc);
    3974         free(pszBuf);
    3975     }
    3976     free(pszDst);
    3977     free(pszSrc);
    3978 }
    3979 
    3980 
    3981 /**
    3982  * Copies the object (and whatever else) from one cache entry to another.
    3983  *
    3984  * This is called when a matching cache entry has been found and we don't
    3985  * need to recompile anything.
    3986  *
    3987  * @param   pEntry      The entry to copy to.
    3988  * @param   pFrom       The entry to copy from.
    3989  */
    3990 static void kOCEntryCopy(PKOCENTRY pEntry, PCKOCENTRY pFrom)
    3991 {
    3992     kOCEntryCopyFile(pEntry, pEntry->New.pszObjName,
    3993                      MakePathFromDirAndFile(pFrom->New.pszObjName
    3994                                             ? pFrom->New.pszObjName : pFrom->Old.pszObjName,
    3995                                             pFrom->pszDir));
    3996 }
    3997 
    3998 
    3999 /**
    4000  * Gets the absolute path to the cache entry.
    4001  *
    4002  * @returns absolute path to the cache entry.
    4003  * @param   pEntry      The cache entry in question.
    4004  */
    4005 static const char *kOCEntryAbsPath(PCKOCENTRY pEntry)
    4006 {
    4007     return pEntry->pszAbsPath;
    4008 }
    4009 
    4010 
    4011 
    4012 
    4013 
    4014 
    4015 /**
    4016  * Digest of one cache entry.
    4017  *
    4018  * This contains all the information required to find a matching
    4019  * cache entry without having to open each of the files.
    4020  */
    4021 typedef struct KOCDIGEST
    4022 {
    4023     /** The relative path to the entry. Optional if pszAbsPath is set. */
    4024     char *pszRelPath;
    4025     /** The absolute path to the entry. Optional if pszRelPath is set. */
    4026     char *pszAbsPath;
    4027     /** The target os/arch identifier. */
    4028     char *pszTarget;
    4029     /** A unique number assigned to the entry when it's (re)-inserted
    4030      * into the cache. This is used for simple consitency checking. */
    4031     uint32_t uKey;
    4032     /** The checksum of the compile argument vector. */
    4033     KOCSUM SumCompArgv;
    4034     /** The list of preprocessor output checksums that's . */
    4035     KOCSUM SumHead;
    4036 } KOCDIGEST;
    4037 /** Pointer to a file digest. */
    4038 typedef KOCDIGEST *PKOCDIGEST;
    4039 /** Pointer to a const file digest. */
    4040 typedef KOCDIGEST *PCKOCDIGEST;
    4041 
    4042 
    4043 /**
    4044  * Initializes the specified digest.
    4045  *
    4046  * @param   pDigest     The digest.
    4047  */
    4048 static void kOCDigestInit(PKOCDIGEST pDigest)
    4049 {
    4050     memset(pDigest, 0, sizeof(*pDigest));
    4051     kOCSumInit(&pDigest->SumHead);
    4052 }
    4053 
    4054 
    4055 /**
    4056  * Initializes the digest for the specified entry.
    4057  *
    4058  * @param   pDigest     The (uninitialized) digest.
    4059  * @param   pEntry      The entry.
    4060  */
    4061 static void kOCDigestInitFromEntry(PKOCDIGEST pDigest, PCKOCENTRY pEntry)
    4062 {
    4063     kOCDigestInit(pDigest);
    4064 
    4065     pDigest->uKey = pEntry->uKey;
    4066     pDigest->pszTarget = xstrdup(pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget);
    4067 
    4068     kOCSumInit(&pDigest->SumCompArgv);
    4069     if (!kOCSumIsEmpty(&pEntry->New.SumCompArgv))
    4070         kOCSumAdd(&pDigest->SumCompArgv, &pEntry->New.SumCompArgv);
    4071     else
    4072         kOCSumAdd(&pDigest->SumCompArgv, &pEntry->Old.SumCompArgv);
    4073 
    4074     kOCSumInit(&pDigest->SumHead);
    4075     if (!kOCSumIsEmpty(&pEntry->New.SumHead))
    4076         kOCSumAddChain(&pDigest->SumHead, &pEntry->New.SumHead);
    4077     else
    4078         kOCSumAddChain(&pDigest->SumHead, &pEntry->Old.SumHead);
    4079 
    4080     /** @todo implement selective relative path support. */
    4081     pDigest->pszRelPath = NULL;
    4082     pDigest->pszAbsPath = xstrdup(kOCEntryAbsPath(pEntry));
    4083 }
    4084 
    4085 
    4086 /**
    4087  * Purges a digest, freeing all resources and returning
    4088  * it to the initial state.
    4089  *
    4090  * @param   pDigest     The digest.
    4091  */
    4092 static void kOCDigestPurge(PKOCDIGEST pDigest)
    4093 {
    4094     free(pDigest->pszRelPath);
    4095     free(pDigest->pszAbsPath);
    4096     free(pDigest->pszTarget);
    4097     pDigest->pszTarget = pDigest->pszAbsPath = pDigest->pszRelPath = NULL;
    4098     pDigest->uKey = 0;
    4099     kOCSumDeleteChain(&pDigest->SumCompArgv);
    4100     kOCSumDeleteChain(&pDigest->SumHead);
    4101 }
    4102 
    4103 
    4104 /**
    4105  * Returns the absolute path to the entry, calculating
    4106  * the path if necessary.
    4107  *
    4108  * @returns absolute path.
    4109  * @param   pDigest     The digest.
    4110  * @param   pszDir      The cache directory that it might be relative to.
    4111  */
    4112 static const char *kOCDigestAbsPath(PCKOCDIGEST pDigest, const char *pszDir)
    4113 {
    4114     if (!pDigest->pszAbsPath)
    4115     {
    4116         char *pszPath = MakePathFromDirAndFile(pDigest->pszRelPath, pszDir);
    4117         ((PKOCDIGEST)pDigest)->pszAbsPath = AbsPath(pszPath);
    4118         free(pszPath);
    4119     }
    4120     return pDigest->pszAbsPath;
    4121 }
    4122 
    4123 
    4124 /**
    4125  * Checks that the digest matches the
    4126  *
    4127  * @returns 1 if valid, 0 if invalid in some way.
    4128  *
    4129  * @param   pDigest     The digest to validate.
    4130  * @param   pEntry      What to validate it against.
    4131  */
    4132 static int kOCDigestIsValid(PCKOCDIGEST pDigest, PCKOCENTRY pEntry)
    4133 {
    4134     PCKOCSUM pSum;
    4135     PCKOCSUM pSumEntry;
    4136 
    4137     if (pDigest->uKey != pEntry->uKey)
    4138         return 0;
    4139 
    4140     if (!kOCSumIsEqual(&pDigest->SumCompArgv,
    4141                        kOCSumIsEmpty(&pEntry->New.SumCompArgv)
    4142                        ? &pEntry->Old.SumCompArgv : &pEntry->New.SumCompArgv))
    4143         return 0;
    4144 
    4145     if (strcmp(pDigest->pszTarget, pEntry->New.pszTarget ? pEntry->New.pszTarget : pEntry->Old.pszTarget))
    4146         return 0;
    4147 
    4148     /* match the checksums */
    4149     pSumEntry = kOCSumIsEmpty(&pEntry->New.SumHead)
    4150               ? &pEntry->Old.SumHead : &pEntry->New.SumHead;
    4151     for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext)
    4152         if (!kOCSumHasEqualInChain(pSumEntry, pSum))
    4153             return 0;
    4154 
    4155     return 1;
    4156 }
    4157 
    4158 
    4159 
    4160 
    4161 
    4162 /**
    4163  * The structure for the central cache entry.
    4164  */
    4165 typedef struct KOBJCACHE
    4166 {
    4167     /** The entry name. */
    4168     const char *pszName;
    4169     /** The dir that relative names in the digest are relative to. */
    4170     char *pszDir;
    4171     /** The absolute path. */
    4172     char *pszAbsPath;
    4173 
    4174     /** The cache file descriptor. */
    4175     int fd;
    4176     /** The stream associated with fd. */
    4177     FILE *pFile;
    4178     /** Whether it's currently locked or not. */
    4179     unsigned fLocked;
    4180     /** Whether the cache file is dirty and needs writing back. */
    4181     unsigned fDirty;
    4182     /** Whether this is a new cache or not. */
    4183     unsigned fNewCache;
    4184 
    4185     /** The cache file generation. */
    4186     uint32_t uGeneration;
    4187     /** The next valid key. (Determin at load time.) */
    4188     uint32_t uNextKey;
    4189 
    4190     /** Number of digests in paDigests. */
    4191     unsigned cDigests;
    4192     /** Array of digests for the KOCENTRY objects in the cache. */
    4193     PKOCDIGEST paDigests;
    4194 
    4195 } KOBJCACHE;
    4196 /** Pointer to a cache. */
    4197 typedef KOBJCACHE *PKOBJCACHE;
    4198 /** Pointer to a const cache. */
    4199 typedef KOBJCACHE const *PCKOBJCACHE;
    4200 
    4201 
    4202 /**
    4203  * Creates an empty cache.
    4204  *
    4205  * This doesn't touch the file system, it just create the data structure.
    4206  *
    4207  * @returns Pointer to a cache.
    4208  * @param   pszCacheFile        The cache file.
    4209  */
    4210 static PKOBJCACHE kObjCacheCreate(const char *pszCacheFile)
    4211 {
    4212     PKOBJCACHE pCache;
    4213     size_t off;
    4214 
    4215     /*
    4216      * Allocate an empty entry.
    4217      */
    4218     pCache = xmallocz(sizeof(*pCache));
    4219     pCache->fd = -1;
    4220 
    4221     /*
    4222      * Setup the directory and cache file name.
    4223      */
    4224     pCache->pszAbsPath = AbsPath(pszCacheFile);
    4225     pCache->pszName = FindFilenameInPath(pCache->pszAbsPath);
    4226     off = pCache->pszName - pCache->pszAbsPath;
    4227     if (!off)
    4228         FatalDie("Failed to find abs path for '%s'!\n", pszCacheFile);
    4229     pCache->pszDir = xmalloc(off);
    4230     memcpy(pCache->pszDir, pCache->pszAbsPath, off - 1);
    4231     pCache->pszDir[off - 1] = '\0';
    4232 
    4233     return pCache;
    4234 }
    4235 
    4236 
    4237 /**
    4238  * Destroys the cache - closing any open files, freeing up heap memory and such.
    4239  *
    4240  * @param   pCache      The cache.
    4241  */
    4242 static void kObjCacheDestroy(PKOBJCACHE pCache)
    4243 {
    4244     if (pCache->pFile)
    4245     {
    4246         errno = 0;
    4247         if (fclose(pCache->pFile) != 0)
    4248             FatalMsg("fclose failed: %s\n", strerror(errno));
    4249         pCache->pFile = NULL;
    4250         pCache->fd = -1;
    4251     }
    4252     free(pCache->paDigests);
    4253     free(pCache->pszAbsPath);
    4254     free(pCache->pszDir);
    4255     free(pCache);
    4256 }
    4257 
    4258 
    4259 /**
    4260  * Purges the data in the cache object.
    4261  *
    4262  * @param   pCache      The cache object.
    4263  */
    4264 static void kObjCachePurge(PKOBJCACHE pCache)
    4265 {
    4266     while (pCache->cDigests > 0)
    4267         kOCDigestPurge(&pCache->paDigests[--pCache->cDigests]);
    4268     free(pCache->paDigests);
    4269     pCache->paDigests = NULL;
    4270     pCache->uGeneration = 0;
    4271     pCache->uNextKey = 0;
    4272 }
    4273 
    4274 
    4275 /**
    4276  * (Re-)reads the file.
    4277  *
    4278  * @param   pCache      The cache to (re)-read.
    4279  */
    4280 static void kObjCacheRead(PKOBJCACHE pCache)
    4281 {
    4282     unsigned i;
    4283     char szBuf[8192];
    4284     int fBad = 0;
    4285 
    4286     InfoMsg(4, "reading cache file...\n");
    4287 
    4288     /*
    4289      * Rewind the file & stream, and associate a temporary buffer
    4290      * with the stream to speed up reading.
    4291      */
    4292     if (lseek(pCache->fd, 0, SEEK_SET) == -1)
    4293         FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno));
    4294     rewind(pCache->pFile);
    4295     if (setvbuf(pCache->pFile, szBuf, _IOFBF, sizeof(szBuf)) != 0)
    4296         FatalDie("fdopen(cache-fd,rb) failed: %s\n", strerror(errno));
    4297 
    4298     /*
    4299      * Read magic and generation.
    4300      */
    4301     if (    !fgets(g_szLine, sizeof(g_szLine), pCache->pFile)
    4302         ||  strcmp(g_szLine, "magic=kObjCache-v0.1.0\n"))
    4303     {
    4304         InfoMsg(2, "bad cache file (magic)\n");
    4305         fBad = 1;
    4306     }
    4307     else if (    !fgets(g_szLine, sizeof(g_szLine), pCache->pFile)
    4308              ||  strncmp(g_szLine, "generation=", sizeof("generation=") - 1))
    4309     {
    4310         InfoMsg(2, "bad cache file (generation)\n");
    4311         fBad = 1;
    4312     }
    4313     else if (   pCache->uGeneration
    4314              && (long)pCache->uGeneration == atol(&g_szLine[sizeof("generation=") - 1]))
    4315     {
    4316         InfoMsg(3, "drop re-read unmodified cache file\n");
    4317         fBad = 0;
    4318     }
    4319     else
    4320     {
    4321         int fBadBeforeMissing;
    4322 
    4323         /*
    4324          * Read everything (anew).
    4325          */
    4326         kObjCachePurge(pCache);
    4327         do
    4328         {
    4329             PKOCDIGEST pDigest;
    4330             char *pszNl;
    4331             char *pszVal;
    4332             char *psz;
    4333 
    4334             /* Split the line and drop the trailing newline. */
    4335             pszVal = strchr(g_szLine, '=');
    4336             if ((fBad = pszVal == NULL))
    4337                 break;
    4338             *pszVal++ = '\0';
    4339 
    4340             pszNl = strchr(pszVal, '\n');
    4341             if (pszNl)
    4342                 *pszNl = '\0';
    4343 
    4344             /* digest '#'? */
    4345             psz = strchr(g_szLine, '#');
    4346             if (psz)
    4347             {
    4348                 char *pszNext;
    4349                 i = strtoul(++psz, &pszNext, 0);
    4350                 if ((fBad = pszNext && *pszNext))
    4351                     break;
    4352                 if ((fBad = i >= pCache->cDigests))
    4353                     break;
    4354                 pDigest = &pCache->paDigests[i];
    4355                 *psz = '\0';
    4356             }
    4357             else
    4358                 pDigest = NULL;
    4359 
    4360 
    4361             /* string case on value name. */
    4362             if (!strcmp(g_szLine, "sum-#"))
    4363             {
    4364                 KOCSUM Sum;
    4365                 if ((fBad = kOCSumInitFromString(&Sum, pszVal) != 0))
    4366                     break;
    4367                 kOCSumAdd(&pDigest->SumHead, &Sum);
    4368             }
    4369             else if (!strcmp(g_szLine, "digest-abs-#"))
    4370             {
    4371                 if ((fBad = pDigest->pszAbsPath != NULL))
    4372                     break;
    4373                 pDigest->pszAbsPath = xstrdup(pszVal);
    4374             }
    4375             else if (!strcmp(g_szLine, "digest-rel-#"))
    4376             {
    4377                 if ((fBad = pDigest->pszRelPath != NULL))
    4378                     break;
    4379                 pDigest->pszRelPath = xstrdup(pszVal);
    4380             }
    4381             else if (!strcmp(g_szLine, "key-#"))
    4382             {
    4383                 if ((fBad = pDigest->uKey != 0))
    4384                     break;
    4385                 pDigest->uKey = strtoul(pszVal, &psz, 0);
    4386                 if ((fBad = psz && *psz))
    4387                     break;
    4388                 if (pDigest->uKey >= pCache->uNextKey)
    4389                     pCache->uNextKey = pDigest->uKey + 1;
    4390             }
    4391             else if (!strcmp(g_szLine, "comp-argv-sum-#"))
    4392             {
    4393                 if ((fBad = !kOCSumIsEmpty(&pDigest->SumCompArgv)))
    4394                     break;
    4395                 if ((fBad = kOCSumInitFromString(&pDigest->SumCompArgv, pszVal) != 0))
    4396                     break;
    4397             }
    4398             else if (!strcmp(g_szLine, "target-#"))
    4399             {
    4400                 if ((fBad = pDigest->pszTarget != NULL))
    4401                     break;
    4402                 pDigest->pszTarget = xstrdup(pszVal);
    4403             }
    4404             else if (!strcmp(g_szLine, "digests"))
    4405             {
    4406                 if ((fBad = pCache->paDigests != NULL))
    4407                     break;
    4408                 pCache->cDigests = strtoul(pszVal, &psz, 0);
    4409                 if ((fBad = psz && *psz))
    4410                     break;
    4411                 i = (pCache->cDigests + 4) & ~3;
    4412                 pCache->paDigests = xmalloc(i * sizeof(pCache->paDigests[0]));
    4413                 for (i = 0; i < pCache->cDigests; i++)
    4414                     kOCDigestInit(&pCache->paDigests[i]);
    4415             }
    4416             else if (!strcmp(g_szLine, "generation"))
    4417             {
    4418                 if ((fBad = pCache->uGeneration != 0))
    4419                     break;
    4420                 pCache->uGeneration = strtoul(pszVal, &psz, 0);
    4421                 if ((fBad = psz && *psz))
    4422                     break;
    4423             }
    4424             else if (!strcmp(g_szLine, "the-end"))
    4425             {
    4426                 fBad = strcmp(pszVal, "fine");
    4427                 break;
    4428             }
    4429             else
    4430             {
    4431                 fBad = 1;
    4432                 break;
    4433             }
    4434         } while (fgets(g_szLine, sizeof(g_szLine), pCache->pFile));
    4435 
    4436         /*
    4437          * Did we find everything?
    4438          */
    4439         fBadBeforeMissing = fBad;
    4440         if (    !fBad
    4441             &&  !pCache->uGeneration)
    4442             fBad = 1;
    4443         if (!fBad)
    4444             for (i = 0; i < pCache->cDigests; i++)
    4445             {
    4446                 if ((fBad = kOCSumIsEmpty(&pCache->paDigests[i].SumCompArgv)))
    4447                     break;
    4448                 if ((fBad = kOCSumIsEmpty(&pCache->paDigests[i].SumHead)))
    4449                     break;
    4450                 if ((fBad = pCache->paDigests[i].uKey == 0))
    4451                     break;
    4452                 if ((fBad = pCache->paDigests[i].pszAbsPath == NULL
    4453                          && pCache->paDigests[i].pszRelPath == NULL))
    4454                     break;
    4455                 if ((fBad = pCache->paDigests[i].pszTarget == NULL))
    4456                     break;
    4457                 InfoMsg(4, "digest-%u: %s\n", i, pCache->paDigests[i].pszAbsPath
    4458                         ? pCache->paDigests[i].pszAbsPath : pCache->paDigests[i].pszRelPath);
    4459             }
    4460         if (fBad)
    4461             InfoMsg(2, "bad cache file (%s)\n", fBadBeforeMissing ? g_szLine : "missing stuff");
    4462         else if (ferror(pCache->pFile))
    4463         {
    4464             InfoMsg(2, "cache file read error\n");
    4465             fBad = 1;
    4466         }
    4467     }
    4468     if (fBad)
    4469     {
    4470         kObjCachePurge(pCache);
    4471         pCache->fNewCache = 1;
    4472     }
    4473 
    4474     /*
    4475      * Disassociate the buffer from the stream changing
    4476      * it to non-buffered mode.
    4477      */
    4478     if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
    4479         FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
    4480 }
    4481 
    4482 
    4483 /**
    4484  * Re-writes the cache file.
    4485  *
    4486  * @param   pCache      The cache to commit and unlock.
    4487  */
    4488 static void kObjCacheWrite(PKOBJCACHE pCache)
    4489 {
    4490     unsigned i;
    4491     off_t cb;
    4492     char szBuf[8192];
    4493     assert(pCache->fLocked);
    4494     assert(pCache->fDirty);
    4495 
    4496     /*
    4497      * Rewind the file & stream, and associate a temporary buffer
    4498      * with the stream to speed up the writing.
    4499      */
    4500     if (lseek(pCache->fd, 0, SEEK_SET) == -1)
    4501         FatalDie("lseek(cache-fd) failed: %s\n", strerror(errno));
    4502     rewind(pCache->pFile);
    4503     if (setvbuf(pCache->pFile, szBuf, _IOFBF, sizeof(szBuf)) != 0)
    4504         FatalDie("setvbuf failed: %s\n", strerror(errno));
    4505 
    4506     /*
    4507      * Write the header.
    4508      */
    4509     pCache->uGeneration++;
    4510     fprintf(pCache->pFile,
    4511             "magic=kObjCache-v0.1.0\n"
    4512             "generation=%d\n"
    4513             "digests=%d\n",
    4514             pCache->uGeneration,
    4515             pCache->cDigests);
    4516 
    4517     /*
    4518      * Write the digests.
    4519      */
    4520     for (i = 0; i < pCache->cDigests; i++)
    4521     {
    4522         PCKOCDIGEST pDigest = &pCache->paDigests[i];
    4523         PKOCSUM pSum;
    4524 
    4525         if (pDigest->pszAbsPath)
    4526             fprintf(pCache->pFile, "digest-abs-#%u=%s\n", i, pDigest->pszAbsPath);
    4527         if (pDigest->pszRelPath)
    4528             fprintf(pCache->pFile, "digest-rel-#%u=%s\n", i, pDigest->pszRelPath);
    4529         fprintf(pCache->pFile, "key-#%u=%u\n", i, pDigest->uKey);
    4530         fprintf(pCache->pFile, "target-#%u=%s\n", i, pDigest->pszTarget);
    4531         fprintf(pCache->pFile, "comp-argv-sum-#%u=", i);
    4532         kOCSumFPrintf(&pDigest->SumCompArgv, pCache->pFile);
    4533         for (pSum = &pDigest->SumHead; pSum; pSum = pSum->pNext)
    4534         {
    4535             fprintf(pCache->pFile, "sum-#%u=", i);
    4536             kOCSumFPrintf(pSum, pCache->pFile);
    4537         }
    4538     }
    4539 
    4540     /*
    4541      * Close the stream and unlock fhe file.
    4542      * (Closing the stream shouldn't close the file handle IIRC...)
    4543      */
    4544     fprintf(pCache->pFile, "the-end=fine\n");
    4545     errno = 0;
    4546     if (    fflush(pCache->pFile) < 0
    4547         ||  ferror(pCache->pFile))
    4548     {
    4549         int iErr = errno;
    4550         fclose(pCache->pFile);
    4551         UnlinkFileInDir(pCache->pszName, pCache->pszDir);
    4552         FatalDie("Stream error occured while writing '%s' in '%s': %s\n",
    4553                  pCache->pszName, pCache->pszDir, strerror(iErr));
    4554     }
    4555     if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
    4556         FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
    4557 
    4558     cb = lseek(pCache->fd, 0, SEEK_CUR);
    4559     if (cb == -1)
    4560         FatalDie("lseek(cache-file,0,CUR) failed: %s\n", strerror(errno));
    4561 #if defined(__WIN__)
    4562     if (_chsize(pCache->fd, cb) == -1)
    4563 #else
    4564     if (ftruncate(pCache->fd, cb) == -1)
    4565 #endif
    4566         FatalDie("file truncation failed: %s\n", strerror(errno));
    4567     InfoMsg(4, "wrote '%s' in '%s', %d bytes\n", pCache->pszName, pCache->pszDir, cb);
    4568 }
    4569 
    4570 
    4571 /**
    4572  * Cleans out all invalid digests.s
    4573  *
    4574  * This is done periodically from the unlock routine to make
    4575  * sure we don't accidentally accumulate stale digests.
    4576  *
    4577  * @param   pCache      The cache to chek.
    4578  */
    4579 static void kObjCacheClean(PKOBJCACHE pCache)
    4580 {
    4581     unsigned i = pCache->cDigests;
    4582     while (i-- > 0)
    4583     {
    4584         /*
    4585          * Try open it and purge it if it's bad.
    4586          * (We don't kill the entry file because that's kmk clean's job.)
    4587          */
    4588         PCKOCDIGEST pDigest = &pCache->paDigests[i];
    4589         PKOCENTRY pEntry = kOCEntryCreate(kOCDigestAbsPath(pDigest, pCache->pszDir));
    4590         kOCEntryRead(pEntry);
    4591         if (    !kOCEntryCheck(pEntry)
    4592             ||  !kOCDigestIsValid(pDigest, pEntry))
    4593         {
    4594             unsigned cLeft;
    4595             kOCDigestPurge(pDigest);
    4596 
    4597             pCache->cDigests--;
    4598             cLeft = pCache->cDigests - i;
    4599             if (cLeft)
    4600                 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest));
    4601 
    4602             pCache->fDirty = 1;
    4603         }
    4604         kOCEntryDestroy(pEntry);
    4605     }
    4606 }
    4607 
    4608 
    4609 /**
    4610  * Locks the cache for exclusive access.
    4611  *
    4612  * This will open the file if necessary and lock the entire file
    4613  * using the best suitable platform API (tricky).
    4614  *
    4615  * @param   pCache      The cache to lock.
    4616  */
    4617 static void kObjCacheLock(PKOBJCACHE pCache)
    4618 {
    4619     struct stat st;
    4620 #if defined(__WIN__)
    4621     OVERLAPPED OverLapped;
    4622 #endif
    4623 
    4624     assert(!pCache->fLocked);
    4625 
    4626     /*
    4627      * Open it?
    4628      */
    4629     if (pCache->fd < 0)
    4630     {
    4631         pCache->fd = OpenFileInDir(pCache->pszName, pCache->pszDir, O_CREAT | O_RDWR | O_BINARY, 0666);
    4632         if (pCache->fd == -1)
    4633         {
    4634             MakePath(pCache->pszDir);
    4635             pCache->fd = OpenFileInDir(pCache->pszName, pCache->pszDir, O_CREAT | O_RDWR | O_BINARY, 0666);
    4636             if (pCache->fd == -1)
    4637                 FatalDie("Failed to create '%s' in '%s': %s\n", pCache->pszName, pCache->pszDir, strerror(errno));
    4638         }
    4639 
    4640         pCache->pFile = fdopen(pCache->fd, "r+b");
    4641         if (!pCache->pFile)
    4642             FatalDie("fdopen failed: %s\n", strerror(errno));
    4643         if (setvbuf(pCache->pFile, NULL, _IONBF, 0) != 0)
    4644             FatalDie("setvbuf(,0,,0) failed: %s\n", strerror(errno));
    4645     }
    4646 
    4647     /*
    4648      * Lock it.
    4649      */
    4650 #if defined(__WIN__)
    4651     memset(&OverLapped, 0, sizeof(OverLapped));
    4652     if (!LockFileEx((HANDLE)_get_osfhandle(pCache->fd), LOCKFILE_EXCLUSIVE_LOCK, 0, ~0, 0, &OverLapped))
    4653         FatalDie("Failed to lock the cache file: Windows Error %d\n", GetLastError());
    4654 #elif defined(__sun__)
    4655     {
    4656         struct flock fl;
    4657         fl.l_whence = 0;
    4658         fl.l_start = 0;
    4659         fl.l_len = 0;
    4660         fl.l_type = F_WRLCK;
    4661         if (fcntl(pCache->fd, F_SETLKW, &fl) != 0)
    4662             FatalDie("Failed to lock the cache file: %s\n", strerror(errno));
    4663     }
    4664 #else
    4665     if (flock(pCache->fd, LOCK_EX) != 0)
    4666         FatalDie("Failed to lock the cache file: %s\n", strerror(errno));
    4667 #endif
    4668     pCache->fLocked = 1;
    4669 
    4670     /*
    4671      * Check for new cache and read it it's an existing cache.
    4672      *
    4673      * There is no point in initializing a new cache until we've finished
    4674      * compiling and has something to put into it, so we'll leave it as a
    4675      * 0 byte file.
    4676      */
    4677     if (fstat(pCache->fd, &st) == -1)
    4678         FatalDie("fstat(cache-fd) failed: %s\n", strerror(errno));
    4679     if (st.st_size)
    4680         kObjCacheRead(pCache);
    4681     else
    4682     {
    4683         pCache->fNewCache = 1;
    4684         InfoMsg(2, "the cache file is empty\n");
    4685     }
    4686 }
    4687 
    4688 
    4689 /**
    4690  * Unlocks the cache (without writing anything back).
    4691  *
    4692  * @param   pCache      The cache to unlock.
    4693  */
    4694 static void kObjCacheUnlock(PKOBJCACHE pCache)
    4695 {
    4696 #if defined(__WIN__)
    4697     OVERLAPPED OverLapped;
    4698 #endif
    4699     assert(pCache->fLocked);
    4700 
    4701     /*
    4702      * Write it back if it's dirty.
    4703      */
    4704     if (pCache->fDirty)
    4705     {
    4706         if (    pCache->cDigests >= 16
    4707             &&  (pCache->uGeneration % 19) == 19)
    4708             kObjCacheClean(pCache);
    4709         kObjCacheWrite(pCache);
    4710         pCache->fDirty = 0;
    4711     }
    4712 
    4713     /*
    4714      * Lock it.
    4715      */
    4716 #if defined(__WIN__)
    4717     memset(&OverLapped, 0, sizeof(OverLapped));
    4718     if (!UnlockFileEx((HANDLE)_get_osfhandle(pCache->fd), 0, ~0U, 0, &OverLapped))
    4719         FatalDie("Failed to unlock the cache file: Windows Error %d\n", GetLastError());
    4720 #elif defined(__sun__)
    4721     {
    4722         struct flock fl;
    4723         fl.l_whence = 0;
    4724         fl.l_start = 0;
    4725         fl.l_len = 0;
    4726         fl.l_type = F_UNLCK;
    4727         if (fcntl(pCache->fd, F_SETLKW, &fl) != 0)
    4728             FatalDie("Failed to lock the cache file: %s\n", strerror(errno));
    4729     }
    4730 #else
    4731     if (flock(pCache->fd, LOCK_UN) != 0)
    4732         FatalDie("Failed to unlock the cache file: %s\n", strerror(errno));
    4733 #endif
    4734     pCache->fLocked = 0;
    4735 }
    4736 
    4737 
    4738 /**
    4739  * Removes the entry from the cache.
    4740  *
    4741  * The entry doesn't need to be in the cache.
    4742  * The cache entry (file) itself is not touched.
    4743  *
    4744  * @param   pCache      The cache.
    4745  * @param   pEntry      The entry.
    4746  */
    4747 static void kObjCacheRemoveEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry)
    4748 {
    4749     unsigned i = pCache->cDigests;
    4750     while (i-- > 0)
    4751     {
    4752         PKOCDIGEST pDigest = &pCache->paDigests[i];
    4753         if (ArePathsIdentical(kOCDigestAbsPath(pDigest, pCache->pszDir),
    4754                               kOCEntryAbsPath(pEntry)))
    4755         {
    4756             unsigned cLeft;
    4757             kOCDigestPurge(pDigest);
    4758 
    4759             pCache->cDigests--;
    4760             cLeft = pCache->cDigests - i;
    4761             if (cLeft)
    4762                 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest));
    4763 
    4764             pCache->fDirty = 1;
    4765             InfoMsg(3, "removing entry '%s'; %d left.\n", kOCEntryAbsPath(pEntry), pCache->cDigests);
    4766         }
    4767     }
    4768 }
    4769 
    4770 
    4771 /**
    4772  * Inserts the entry into the cache.
    4773  *
    4774  * The cache entry (file) itself is not touched by this operation,
    4775  * the pEntry object otoh is.
    4776  *
    4777  * @param   pCache      The cache.
    4778  * @param   pEntry      The entry.
    4779  */
    4780 static void kObjCacheInsertEntry(PKOBJCACHE pCache, PKOCENTRY pEntry)
    4781 {
    4782     unsigned i;
    4783 
    4784     /*
    4785      * Find a new key.
    4786      */
    4787     pEntry->uKey = pCache->uNextKey++;
    4788     if (!pEntry->uKey)
    4789         pEntry->uKey = pCache->uNextKey++;
    4790     i = pCache->cDigests;
    4791     while (i-- > 0)
    4792         if (pCache->paDigests[i].uKey == pEntry->uKey)
    4793         {
    4794             pEntry->uKey = pCache->uNextKey++;
    4795             if (!pEntry->uKey)
    4796                 pEntry->uKey = pCache->uNextKey++;
    4797             i = pCache->cDigests;
    4798         }
    4799 
    4800     /*
    4801      * Reallocate the digest array?
    4802      */
    4803     if (    !(pCache->cDigests & 3)
    4804         &&  (pCache->cDigests || !pCache->paDigests))
    4805         pCache->paDigests = xrealloc(pCache->paDigests, sizeof(pCache->paDigests[0]) * (pCache->cDigests + 4));
    4806 
    4807     /*
    4808      * Create a new digest.
    4809      */
    4810     kOCDigestInitFromEntry(&pCache->paDigests[pCache->cDigests], pEntry);
    4811     pCache->cDigests++;
    4812     InfoMsg(4, "Inserted digest #%u: %s\n", pCache->cDigests - 1, kOCEntryAbsPath(pEntry));
    4813 
    4814     pCache->fDirty = 1;
    4815 }
    4816 
    4817 
    4818 /**
    4819  * Find a matching cache entry.
    4820  */
    4821 static PKOCENTRY kObjCacheFindMatchingEntry(PKOBJCACHE pCache, PCKOCENTRY pEntry)
    4822 {
    4823     unsigned i = pCache->cDigests;
    4824 
    4825     assert(pEntry->fNeedCompiling);
    4826     assert(!kOCSumIsEmpty(&pEntry->New.SumCompArgv));
    4827     assert(!kOCSumIsEmpty(&pEntry->New.SumHead));
    4828 
    4829     while (i-- > 0)
    4830     {
    4831         /*
    4832          * Matching?
    4833          */
    4834         PCKOCDIGEST pDigest = &pCache->paDigests[i];
    4835         if (    kOCSumIsEqual(&pDigest->SumCompArgv, &pEntry->New.SumCompArgv)
    4836             &&  kOCSumHasEqualInChain(&pDigest->SumHead, &pEntry->New.SumHead))
    4837         {
    4838             /*
    4839              * Try open it.
    4840              */
    4841             unsigned cLeft;
    4842             PKOCENTRY pRetEntry = kOCEntryCreate(kOCDigestAbsPath(pDigest, pCache->pszDir));
    4843             kOCEntryRead(pRetEntry);
    4844             if (    kOCEntryCheck(pRetEntry)
    4845                 &&  kOCDigestIsValid(pDigest, pRetEntry))
    4846                 return pRetEntry;
    4847             kOCEntryDestroy(pRetEntry);
    4848 
    4849             /* bad entry, purge it. */
    4850             InfoMsg(3, "removing bad digest '%s'\n", kOCDigestAbsPath(pDigest, pCache->pszDir));
    4851             kOCDigestPurge(pDigest);
    4852 
    4853             pCache->cDigests--;
    4854             cLeft = pCache->cDigests - i;
    4855             if (cLeft)
    4856                 memmove(pDigest, pDigest + 1, cLeft * sizeof(*pDigest));
    4857 
    4858             pCache->fDirty = 1;
    4859         }
    4860     }
    4861 
    4862     return NULL;
    4863 }
    4864 
    4865 
    4866 /**
    4867  * Is this a new cache?
    4868  *
    4869  * @returns 1 if new, 0 if not new.
    4870  * @param   pEntry      The entry.
    4871  */
    4872 static int kObjCacheIsNew(PKOBJCACHE pCache)
    4873 {
    4874     return pCache->fNewCache;
     660            rcRet = ErrorMsg("Didn't find '!<arch>\\n' magic in '%s' (or read error)\n", pszLib);
     661
     662        if (fclose(pFile) != 0)
     663            rcRet = ErrorMsg("Error closing '%s'\n");
     664    }
     665    else
     666        rcRet = ErrorMsg("Failed to open '%s' for read+write\n", pszLib);
     667    return rcRet;
    4875668}
    4876669
     
    4901694{
    4902695    fprintf(pOut,
    4903             "syntax: kObjCache [--kObjCache-options] [-v|--verbose]\n"
    4904             "            <  [-c|--cache-file <cache-file>]\n"
    4905             "             | [-n|--name <name-in-cache>] [[-d|--cache-dir <cache-dir>]] >\n"
    4906             "            <-f|--file <local-cache-file>>\n"
    4907             "            <-t|--target <target-name>>\n"
    4908             "            [-r|--redir-stdout] [-p|--passthru] [--named-pipe-compile <pipename>]\n"
    4909             "            --kObjCache-cpp <filename> <preprocessor + args>\n"
    4910             "            --kObjCache-cc <object> <compiler + args>\n"
    4911             "            [--kObjCache-both [args]]\n"
    4912             );
    4913     fprintf(pOut,
    4914             "            [--kObjCache-cpp|--kObjCache-cc [more args]]\n"
    4915             "        kObjCache <-V|--version>\n"
    4916             "        kObjCache [-?|/?|-h|/h|--help|/help]\n"
    4917             "\n"
    4918             "The env.var. KOBJCACHE_DIR sets the default cache diretory (-d).\n"
    4919             "The env.var. KOBJCACHE_OPTS allow you to specifie additional options\n"
    4920             "without having to mess with the makefiles. These are appended with "
    4921             "a --kObjCache-options between them and the command args.\n"
     696            "syntax: kLibTweaker [-v|--verbose] [--clear-timestamps] <lib>\n"
    4922697            "\n");
    4923698    return 0;
     
    4927702int main(int argc, char **argv)
    4928703{
    4929     PKOBJCACHE pCache;
    4930     PKOCENTRY pEntry;
    4931 
    4932     const char *pszCacheDir = getenv("KOBJCACHE_DIR");
    4933     const char *pszCacheName = NULL;
    4934     const char *pszCacheFile = NULL;
    4935     const char *pszEntryFile = NULL;
    4936 
    4937     const char **papszArgvPreComp = NULL;
    4938     unsigned cArgvPreComp = 0;
    4939     const char *pszPreCompName = NULL;
    4940     int fRedirPreCompStdOut = 0;
    4941 
    4942     const char **papszArgvCompile = NULL;
    4943     unsigned cArgvCompile = 0;
    4944     const char *pszObjName = NULL;
    4945     int fRedirCompileStdIn = 0;
    4946     const char *pszNmPipeCompile = NULL;
    4947 
    4948     const char *pszMakeDepFilename = NULL;
    4949     int fMakeDepFixCase = 0;
    4950     int fMakeDepGenStubs = 0;
    4951     int fMakeDepQuiet = 0;
    4952     int fOptimizePreprocessorOutput = 0;
    4953 
    4954     const char *pszTarget = NULL;
    4955 
    4956     enum { kOC_Options, kOC_CppArgv, kOC_CcArgv, kOC_BothArgv } enmMode = kOC_Options;
    4957 
    4958     size_t cch;
    4959704    char *psz;
    4960705    int i;
    4961706
    4962     SetErrorPrefix("kObjCache");
     707    int fClearTimestamps = 0;
     708    int fFillNullThunkData = 0;
     709    const char *pszLib = NULL;
     710
     711    SetErrorPrefix("kLibTweaker");
    4963712
    4964713    /*
    4965714     * Arguments passed in the environmnet?
    4966715     */
    4967     psz = getenv("KOBJCACHE_OPTS");
     716    psz = getenv("KLIBTWEAKER_OPTS");
    4968717    if (psz)
    4969         AppendArgs(&argc, &argv, psz, "--kObjCache-options");
     718        AppendArgs(&argc, &argv, psz, NULL);
     719
     720/** @todo Add the capability to produce import/stub libraries from ELF shared
     721 * objects that we can use while linking and break up linking dependencies
     722 * (i.e. not relink everything just because something in VBoxRT change that
     723 * didn't make any difference to the symbols it exports). */
    4970724
    4971725    /*
     
    4973727     */
    4974728    if (argc <= 1)
    4975         return usage(stderr);
     729        return usage(stderr) + 1;
    4976730    for (i = 1; i < argc; i++)
    4977731    {
    4978         if (!strcmp(argv[i], "--kObjCache-cpp"))
    4979         {
    4980             enmMode = kOC_CppArgv;
    4981             if (!pszPreCompName)
    4982             {
    4983                 if (++i >= argc)
    4984                     return SyntaxError("--kObjCache-cpp requires an object filename!\n");
    4985                 pszPreCompName = argv[i];
    4986             }
    4987         }
    4988         else if (!strcmp(argv[i], "--kObjCache-cc"))
    4989         {
    4990             enmMode = kOC_CcArgv;
    4991             if (!pszObjName)
    4992             {
    4993                 if (++i >= argc)
    4994                     return SyntaxError("--kObjCache-cc requires an preprocessor output filename!\n");
    4995                 pszObjName = argv[i];
    4996             }
    4997         }
    4998         else if (!strcmp(argv[i], "--kObjCache-both"))
    4999             enmMode = kOC_BothArgv;
    5000         else if (!strcmp(argv[i], "--kObjCache-options"))
    5001             enmMode = kOC_Options;
     732        if (!strcmp(argv[i], "--clear-timestamps"))
     733            fClearTimestamps = 1;
     734        else if (!strcmp(argv[i], "--fill-null_thunk_data"))
     735            fFillNullThunkData = 1;
     736        /* Standard stuff: */
    5002737        else if (!strcmp(argv[i], "--help"))
    5003738            return usage(stderr);
    5004         else if (enmMode != kOC_Options)
    5005         {
    5006             if (enmMode == kOC_CppArgv || enmMode == kOC_BothArgv)
    5007             {
    5008                 if (!(cArgvPreComp % 16))
    5009                     papszArgvPreComp = xrealloc((void *)papszArgvPreComp, (cArgvPreComp + 17) * sizeof(papszArgvPreComp[0]));
    5010                 papszArgvPreComp[cArgvPreComp++] = argv[i];
    5011                 papszArgvPreComp[cArgvPreComp] = NULL;
    5012             }
    5013             if (enmMode == kOC_CcArgv || enmMode == kOC_BothArgv)
    5014             {
    5015                 if (!(cArgvCompile % 16))
    5016                     papszArgvCompile = xrealloc((void *)papszArgvCompile, (cArgvCompile + 17) * sizeof(papszArgvCompile[0]));
    5017                 papszArgvCompile[cArgvCompile++] = argv[i];
    5018                 papszArgvCompile[cArgvCompile] = NULL;
    5019             }
    5020         }
    5021         else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--entry-file"))
    5022         {
    5023             if (i + 1 >= argc)
    5024                 return SyntaxError("%s requires a cache entry filename!\n", argv[i]);
    5025             pszEntryFile = argv[++i];
    5026         }
    5027         else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--cache-file"))
    5028         {
    5029             if (i + 1 >= argc)
    5030                 return SyntaxError("%s requires a cache filename!\n", argv[i]);
    5031             pszCacheFile = argv[++i];
    5032         }
    5033         else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--name"))
    5034         {
    5035             if (i + 1 >= argc)
    5036                 return SyntaxError("%s requires a cache name!\n", argv[i]);
    5037             pszCacheName = argv[++i];
    5038         }
    5039         else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--cache-dir"))
    5040         {
    5041             if (i + 1 >= argc)
    5042                 return SyntaxError("%s requires a cache directory!\n", argv[i]);
    5043             pszCacheDir = argv[++i];
    5044         }
    5045         else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--target"))
    5046         {
    5047             if (i + 1 >= argc)
    5048                 return SyntaxError("%s requires a target platform/arch name!\n", argv[i]);
    5049             pszTarget = argv[++i];
    5050         }
    5051         else if (!strcmp(argv[i], "--named-pipe-compile"))
    5052         {
    5053             if (i + 1 >= argc)
    5054                 return SyntaxError("%s requires a pipe name!\n", argv[i]);
    5055             pszNmPipeCompile = argv[++i];
    5056             fRedirCompileStdIn = 0;
    5057         }
    5058         else if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--make-dep-file"))
    5059         {
    5060             if (i + 1 >= argc)
    5061                 return SyntaxError("%s requires a filename!\n", argv[i]);
    5062             pszMakeDepFilename = argv[++i];
    5063         }
    5064         else if (!strcmp(argv[i], "--make-dep-fix-case"))
    5065             fMakeDepFixCase = 1;
    5066         else if (!strcmp(argv[i], "--make-dep-gen-stubs"))
    5067             fMakeDepGenStubs = 1;
    5068         else if (!strcmp(argv[i], "--make-dep-quiet"))
    5069             fMakeDepQuiet = 1;
    5070         else if (!strcmp(argv[i], "-O1") || !strcmp(argv[i], "--optimize-1"))
    5071             fOptimizePreprocessorOutput = 1;
    5072         else if (!strcmp(argv[i], "-O2") || !strcmp(argv[i], "--optimize-2"))
    5073             fOptimizePreprocessorOutput = 1 | 2;
    5074         else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--passthru"))
    5075             fRedirPreCompStdOut = fRedirCompileStdIn = 1;
    5076         else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--redir-stdout"))
    5077             fRedirPreCompStdOut = 1;
    5078739        else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
    5079740            g_cVerbosityLevel++;
     
    5082743        else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?")
    5083744              || !strcmp(argv[i], "/h") || !strcmp(argv[i], "/?") || !strcmp(argv[i], "/help"))
    5084         {
    5085             usage(stdout);
    5086             return 0;
    5087         }
     745            return usage(stdout);
    5088746        else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
    5089747        {
    5090             printf("kObjCache - kBuild version %d.%d.%d ($Revision$)\n"
    5091                    "Copyright (c) 2007-2012 knut st. osmundsen\n",
     748            printf("kLibTweaker - kBuild version %d.%d.%d ($Revision$)\n"
     749                   "Copyright (c) 2007-2015 knut st. osmundsen\n",
    5092750                   KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH);
    5093751            return 0;
    5094752        }
     753        else if (!strcmp(argv[i], "--"))
     754        {
     755            i++;
     756            if (i == argc)
     757                return SyntaxError("No library given!\n");
     758            if (i + 1 != argc || pszLib)
     759                return SyntaxError("Only one library can be tweaked at a time!\n");
     760            pszLib = argv[i];
     761            break;
     762        }
     763        else if (argv[i][0] == '-')
     764            return SyntaxError("Doesn't grok '%s'!\n", argv[i]);
     765        else if (!pszLib)
     766            pszLib = argv[i];
    5095767        else
    5096             return SyntaxError("Doesn't grok '%s'!\n", argv[i]);
    5097     }
    5098     if (!pszEntryFile)
    5099         return SyntaxError("No cache entry filename (-f)!\n");
    5100     if (!pszTarget)
    5101         return SyntaxError("No target name (-t)!\n");
    5102     if (!cArgvCompile)
    5103         return SyntaxError("No compiler arguments (--kObjCache-cc)!\n");
    5104     if (!cArgvPreComp)
    5105         return SyntaxError("No preprocessor arguments (--kObjCache-cc)!\n");
    5106 
    5107     /*
    5108      * Calc the cache file name.
    5109      * It's a bit messy since the extension has to be replaced.
    5110      */
    5111     if (!pszCacheFile)
    5112     {
    5113         if (!pszCacheDir)
    5114             return SyntaxError("No cache dir (-d / KOBJCACHE_DIR) and no cache filename!\n");
    5115         if (!pszCacheName)
    5116         {
    5117             psz = (char *)FindFilenameInPath(pszEntryFile);
    5118             if (!*psz)
    5119                 return SyntaxError("The cache file (-f) specifies a directory / nothing!\n");
    5120             cch = psz - pszEntryFile;
    5121             pszCacheName = memcpy(xmalloc(cch + 5), psz, cch + 1);
    5122             psz = strrchr(pszCacheName, '.');
    5123             if (!psz || psz <= pszCacheName)
    5124                 psz = (char *)pszCacheName + cch;
    5125             memcpy(psz, ".koc", sizeof(".koc"));
    5126         }
    5127         pszCacheFile = MakePathFromDirAndFile(pszCacheName, pszCacheDir);
    5128     }
    5129 
    5130     /*
    5131      * Create and initialize the two objects we'll be working on.
    5132      *
    5133      * We're supposed to be the only ones actually writing to the local file,
    5134      * so it's perfectly fine to read it here before we lock it. This simplifies
    5135      * the detection of object name and compiler argument changes.
    5136      */
    5137     SetErrorPrefix("kObjCache - %s", FindFilenameInPath(pszCacheFile));
    5138     pCache = kObjCacheCreate(pszCacheFile);
    5139 
    5140     pEntry = kOCEntryCreate(pszEntryFile);
    5141     kOCEntryRead(pEntry);
    5142     kOCEntrySetCppName(pEntry, pszPreCompName);
    5143     kOCEntrySetCompileObjName(pEntry, pszObjName);
    5144     kOCEntrySetCompileArgv(pEntry, papszArgvCompile, cArgvCompile);
    5145     kOCEntrySetTarget(pEntry, pszTarget);
    5146     kOCEntrySetPipedMode(pEntry, fRedirPreCompStdOut, fRedirCompileStdIn, pszNmPipeCompile);
    5147     kOCEntrySetDepFilename(pEntry, pszMakeDepFilename, fMakeDepFixCase, fMakeDepQuiet, fMakeDepGenStubs);
    5148     kOCEntrySetOptimizations(pEntry, fOptimizePreprocessorOutput);
    5149 
    5150     /*
    5151      * Open (& lock) the two files and do validity checks and such.
    5152      */
    5153     kObjCacheLock(pCache);
    5154     if (    kObjCacheIsNew(pCache)
    5155         &&  kOCEntryNeedsCompiling(pEntry))
    5156     {
    5157         /*
    5158          * Both files are missing/invalid.
    5159          * Optimize this path as it is frequently used when making a clean build.
    5160          */
    5161         kObjCacheUnlock(pCache);
    5162         InfoMsg(1, "doing full compile\n");
    5163         kOCEntryPreProcessAndCompile(pEntry, papszArgvPreComp, cArgvPreComp);
    5164         kObjCacheLock(pCache);
    5165     }
    5166     else
    5167     {
    5168         /*
    5169          * Do the preprocess (don't need to lock the cache file for this).
    5170          */
    5171         kObjCacheUnlock(pCache);
    5172         kOCEntryPreProcess(pEntry, papszArgvPreComp, cArgvPreComp);
    5173 
    5174         /*
    5175          * Check if we need to recompile. If we do, try see if the is a cache entry first.
    5176          */
    5177         kOCEntryCalcRecompile(pEntry);
    5178         if (kOCEntryNeedsCompiling(pEntry))
    5179         {
    5180             PKOCENTRY pUseEntry;
    5181             kObjCacheLock(pCache);
    5182             kObjCacheRemoveEntry(pCache, pEntry);
    5183             pUseEntry = kObjCacheFindMatchingEntry(pCache, pEntry);
    5184             if (pUseEntry)
    5185             {
    5186                 InfoMsg(1, "using cache entry '%s'\n", kOCEntryAbsPath(pUseEntry));
    5187                 kOCEntryCopy(pEntry, pUseEntry);
    5188                 kOCEntryDestroy(pUseEntry);
    5189             }
    5190             else
    5191             {
    5192                 kObjCacheUnlock(pCache);
    5193                 InfoMsg(1, "recompiling\n");
    5194                 kOCEntryCompileIt(pEntry);
    5195                 kObjCacheLock(pCache);
    5196             }
    5197         }
    5198         else
    5199         {
    5200             InfoMsg(1, "no need to recompile\n");
    5201             kObjCacheLock(pCache);
    5202         }
    5203     }
    5204 
    5205     /*
    5206      * Update the cache files.
    5207      */
    5208     kObjCacheRemoveEntry(pCache, pEntry);
    5209     kObjCacheInsertEntry(pCache, pEntry);
    5210     kOCEntryWrite(pEntry);
    5211     kObjCacheUnlock(pCache);
    5212     kObjCacheDestroy(pCache);
    5213     if (fOptimizePreprocessorOutput)
    5214     {
    5215         InfoMsg(3, "g_cbMemMoved=%#x (%d)\n", g_cbMemMoved, g_cbMemMoved);
    5216         InfoMsg(3, "g_cMemMoves=%#x (%d)\n", g_cMemMoves, g_cMemMoves);
    5217     }
    5218 
    5219     return 0;
    5220 }
    5221 
    5222 
    5223 /** @page kObjCache Benchmarks.
    5224  *
    5225  * (2007-06-10)
    5226  *
    5227  * Mac OS X debug -j 3 cached clobber build (rm -Rf out ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1):
    5228  *  real    11m28.811s
    5229  *  user    13m59.291s
    5230  *  sys     3m24.590s
    5231  *
    5232  * Mac OS X debug -j 3 cached depend build [cdefs.h] (touch include/iprt/cdefs.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1):
    5233  *  real    1m26.895s
    5234  *  user    1m26.971s
    5235  *  sys     0m32.532s
    5236  *
    5237  * Mac OS X debug -j 3 cached depend build [err.h] (touch include/iprt/err.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1):
    5238  *  real    1m18.049s
    5239  *  user    1m20.462s
    5240  *  sys     0m27.887s
    5241  *
    5242  * Mac OS X release -j 3 cached clobber build (rm -Rf out/darwin.x86/release ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1 BUILD_TYPE=release):
    5243  *  real    13m27.751s
    5244  *  user    18m12.654s
    5245  *  sys     3m25.170s
    5246  *
    5247  * Mac OS X profile -j 3 cached clobber build (rm -Rf out/darwin.x86/profile ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 USE_KOBJCACHE=1 BUILD_TYPE=profile):
    5248  *  real    9m9.720s
    5249  *  user    8m53.005s
    5250  *  sys     2m13.110s
    5251  *
    5252  * Mac OS X debug -j 3 clobber build (rm -Rf out/darwin.x86/debug ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=debug):
    5253  *  real    10m18.129s
    5254  *  user    12m52.687s
    5255  *  sys     2m51.277s
    5256  *
    5257  * Mac OS X debug -j 3 debug build [cdefs.h] (touch include/iprt/cdefs.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=debug):
    5258  *  real    4m46.147s
    5259  *  user    5m27.087s
    5260  *  sys     1m11.775s
    5261  *
    5262  * Mac OS X debug -j 3 debug build [err.h] (touch include/iprt/cdefs.h ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=debug):
    5263  *  real    4m17.572s
    5264  *  user    5m7.450s
    5265  *  sys     1m3.450s
    5266  *
    5267  * Mac OS X release -j 3 clobber build (rm -Rf out/darwin.x86/release ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=release):
    5268  *  real    12m14.742s
    5269  *  user    17m11.794s
    5270  *  sys     2m51.454s
    5271  *
    5272  * Mac OS X profile -j 3 clobber build (rm -Rf out/darwin.x86/profile ; sync ; svn diff ; sync ; sleep 1 ; time kmk -j 3 BUILD_TYPE=profile):
    5273  *  real    12m33.821s
    5274  *  user    17m35.086s
    5275  *  sys     2m53.312s
    5276  *
    5277  * Note. The profile build can pick object files from the release build.
    5278  * (all with KOBJCACHE_OPTS=-v; which means a bit more output and perhaps a second or two slower.)
    5279  */
    5280 
     768            return SyntaxError("Only one library can be tweaked at a time!\n");
     769    }
     770    if (!pszLib)
     771        return SyntaxError("No library given!\n");
     772
     773    return kLibTweakerDoIt(pszLib, fClearTimestamps, fFillNullThunkData);
     774}
     775
Note: See TracChangeset for help on using the changeset viewer.