    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     *    Copyright (C) 2011 Norbert Thiebaud
     *    License: GPLv3
     */

    /* define to activate stats reporting on hash usage*/
    /* #define HASH_STAT */

    /* ===============================================
     * Set-up: defines to identify the system and system related properties
     * ===============================================
     */

    #ifdef __APPLE__
    #ifdef __x86_64__
    #undef CORE_BIG_ENDIAN
    #define CORE_LITTLE_ENDIAN
    #else
    #define CORE_BIG_ENDIAN
    #undef CORE_LITTLE_ENDIAN
    #endif

    #endif
    #ifdef _AIX
    #define CORE_BIG_ENDIAN
    #undef CORE_LITTLE_ENDIAN
    #endif /* Def _AIX */

    #ifdef _MSC_VER
    #undef CORE_BIG_ENDIAN
    #define CORE_LITTLE_ENDIAN
    #endif /* Def _MSC_VER */

    #if defined(__linux) || defined(__FreeBSD_kernel__)
    #include <sys/param.h>
    #if __BYTE_ORDER == __LITTLE_ENDIAN
    #undef CORE_BIG_ENDIAN
    #define CORE_LITTLE_ENDIAN
    #else /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
    #if __BYTE_ORDER == __BIG_ENDIAN
    #define CORE_BIG_ENDIAN
    #undef CORE_LITTLE_ENDIAN
    #endif /* __BYTE_ORDER == __BIG_ENDIAN */
    #endif /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
    #endif /* Def __linux */

    #if defined(__OpenBSD__) || defined(__FreeBSD__) || \
        defined(__NetBSD__) || defined(__DragonFly__)
    #include <machine/endian.h>
    #if _BYTE_ORDER == _LITTLE_ENDIAN
    #undef CORE_BIG_ENDIAN
    #define CORE_LITTLE_ENDIAN
    #else /* !(_BYTE_ORDER == _LITTLE_ENDIAN) */
    #if _BYTE_ORDER == _BIG_ENDIAN
    #define CORE_BIG_ENDIAN
    #undef CORE_LITTLE_ENDIAN
    #endif /* _BYTE_ORDER == _BIG_ENDIAN */
    #endif /* !(_BYTE_ORDER == _LITTLE_ENDIAN) */
    #endif /* Def *BSD */

    #if defined(__HAIKU__)
    #include <endian.h>
    #if __BYTE_ORDER == __LITTLE_ENDIAN
    #undef CORE_BIG_ENDIAN
    #define CORE_LITTLE_ENDIAN
    #else /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
    #if __BYTE_ORDER == __BIG_ENDIAN
    #define CORE_BIG_ENDIAN
    #undef CORE_LITTLE_ENDIAN
    #endif /* __BYTE_ORDER == __BIG_ENDIAN */
    #endif /* !(__BYTE_ORDER == __LITTLE_ENDIAN) */
    #endif /* Def __HAIKU__ */

    #ifdef __sun
    #ifdef __sparc
    #define CORE_BIG_ENDIAN
    #undef CORE_LITTLE_ENDIAN
    #else  /* Ndef __sparc */
    #undef CORE_BIG_ENDIAN
    #define CORE_LITTLE_ENDIAN
    #endif /* Ndef __sparc */
    #endif /* Def __sun */

    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <string.h>
    #include <ctype.h>

    #ifdef _MSC_VER
    #include <io.h>
    #else
    #include <unistd.h>
    #endif

    #include <config_options.h>

    /* modes */
    #ifdef _MSC_VER
    #define FILE_O_RDONLY     _O_RDONLY
    #define FILE_O_BINARY     _O_BINARY
    #define PATHNCMP _strnicmp /* MSVC converts paths to lower-case sometimes? */
    #define inline __inline
    #define ssize_t long
    #define S_ISREG(mode) (((mode) & _S_IFMT) == (_S_IFREG)) /* MSVC does not have this macro */
    #else /* not windaube */
    #define FILE_O_RDONLY     O_RDONLY
    #define FILE_O_BINARY     0
    #define PATHNCMP strncmp
    #endif /* not windaube */

    #ifndef TRUE
    #define TRUE 1
    #endif
    #ifndef FALSE
    #define FALSE 0
    #endif

    static int internal_boost = 0;
    static char* base_dir;
    static char* work_dir;
    size_t work_dir_len;

    #ifdef __GNUC__
    #define clz __builtin_clz
    #else
    static inline int clz(unsigned int value)
    {
    int result = 32;

        while(value)
        {
            value >>= 1;
            result -= 1;
        }
        return result;
    }
    #endif

    static inline unsigned int get_unaligned_uint(const unsigned char* cursor)
    {
    unsigned int   result;

        memcpy(&result, cursor, sizeof(unsigned int));
        return result;
    }

    /* ===============================================
     * memory pool for fast fix-size allocation (non-thread-safe)
     * ===============================================
     */
    struct pool
    {
        void*    head_free;  /**< head of a linked list of freed element */
        char*    fresh;      /**< top of a memory block to dig new element */
        char*    tail;       /**< to detect end of extent... when fresh pass tail */
        void*    extent;     /**< pointer to the primary extent block */
        int      size_elem;  /**< size of an element. */
        int      primary;    /**< primary allocation in bytes */
        int      secondary;  /**< secondary allocation in bytes */
    };
    #define POOL_ALIGN_INCREMENT 8 /**< alignment, must be a power of 2 and of size > to sizeof(void*) */

    static void* pool_take_extent(struct pool* pool, int allocate)
    {
    unsigned int size = 0;
    void* extent;
    void* data = NULL;

        if(pool->extent)
        {
            /* we already have an extent, so this is a secondary */
            if(pool->secondary)
            {
                size = pool->secondary;
            }
        }
        else
        {
            assert(pool->primary);
            size = pool->primary;
        }
        if(size)
        {
            extent = malloc(size);
            if(extent)
            {
                *(void**)extent = pool->extent;
                pool->extent = extent;
                if(allocate)
                {
                    data = ((char*)extent) + POOL_ALIGN_INCREMENT;
                    pool->fresh = ((char*)data) + pool->size_elem;
                    pool->tail = pool->fresh + (size - pool->size_elem);
                }
                else
                {
                    pool->fresh = ((char*)extent) + POOL_ALIGN_INCREMENT;
                    pool->tail = pool->fresh + (size - pool->size_elem);
                }
            }
        }
        return data;
    }

    /* Create a memory pool for fix size objects
     * this is a simplified implementation that
     * is _not_ thread safe.
     */
    static struct pool* pool_create(int size_elem, int primary, int secondary)
    {
    struct pool* pool;

        assert(primary > 0);
        assert(secondary >= 0);
        assert(size_elem > 0);

        pool = (struct pool*)calloc(1, sizeof(struct pool));
        if(!pool) return NULL;
        /* Adjust the element size so that it be aligned, and so that an element could
         * at least contain a void*
         */
        pool->size_elem = size_elem = (size_elem + POOL_ALIGN_INCREMENT - 1) & ~(POOL_ALIGN_INCREMENT - 1);

        pool->primary = (size_elem * primary) + POOL_ALIGN_INCREMENT;
        pool->secondary = secondary > 0 ? (size_elem * secondary) + POOL_ALIGN_INCREMENT : 0;
        pool_take_extent(pool, FALSE);

        return pool;

    }

    static void pool_destroy(struct pool* pool)
    {
    void* extent;
    void* next;

        if(pool != NULL)
        {
            extent = pool->extent;
            while(extent)
            {
                next = *(void**)extent;
                free(extent);
                extent = next;
            }
            free(pool);
        }
    }

    static inline void* pool_alloc(struct pool* pool)
    {
    void* data;

        data = pool->head_free;
        if(data == NULL)
        {
            /* we have no old-freed elem */
            if(pool->fresh <= pool->tail)
            {
                /* pick a slice of the current extent */
                data = (void*)pool->fresh;
                pool->fresh += pool->size_elem;
            }
            else
            {
                /* allocate a new extent */
                data = pool_take_extent(pool, TRUE);
            }
        }
        else
        {
            /* re-used old freed element by chopping the head of the free list */
            pool->head_free = *(void**)data;
        }

        return data;
    }

    /* ===============================================
     * Hash implementation customized to be just tracking
     * a unique list of string (i.e no data associated
     * with the key, no need for retrieval, etc..
     *
     * This is tuned for the particular use-case we have here
     * measures in tail_build showed that
     * we can get north of 4000 distinct values stored in a hash
     * the collision rate is at worse around 2%
     * the collision needing an expensive memcmp to resolve
     * have a rate typically at 1 per 1000
     * for tail_build we register 37229 unique key
     * with a total of 377 extra memcmp needed
     * which is completely negligible compared to the
     * number of memcmp required to eliminate duplicate
     * entry (north of 2.5 millions for tail_build)
     * ===============================================
     */

    struct hash_elem
    {
        struct hash_elem* next;
        const char*    key;
        int      key_len;
    };

    struct hash
    {
        struct hash_elem** array;
        struct pool* elems_pool;
        unsigned int used;
        unsigned int size;
        unsigned int load_limit;
    #ifdef HASH_STAT
        int stored;
        int collisions;
        int cost;
        int memcmp;
    #endif
    };

    /* The following hash_compute function was adapted from :
     * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
     *
     * The changes from the original are mostly cosmetic
     */
    #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))

    #if defined CORE_BIG_ENDIAN
    #define MASK_C1 0xFFFFFF00
    #define MASK_C2 0xFFFF0000
    #define MASK_C3 0xFF000000
    #elif defined CORE_LITTLE_ENDIAN
    #define MASK_C1 0xFFFFFF
    #define MASK_C2 0xFFFF
    #define MASK_C3 0xFF
    #else
    #error "Missing Endianness definition"
    #endif

    #define mix(a,b,c) \
    { \
      a -= c;  a ^= rot(c, 4);  c += b; \
      b -= a;  b ^= rot(a, 6);  a += c; \
      c -= b;  c ^= rot(b, 8);  b += a; \
      a -= c;  a ^= rot(c,16);  c += b; \
      b -= a;  b ^= rot(a,19);  a += c; \
      c -= b;  c ^= rot(b, 4);  b += a; \
    }
    #define final(a,b,c) \
    { \
      c ^= b; c -= rot(b,14); \
      a ^= c; a -= rot(c,11); \
      b ^= a; b -= rot(a,25); \
      c ^= b; c -= rot(b,16); \
      a ^= c; a -= rot(c,4);  \
      b ^= a; b -= rot(a,14); \
      c ^= b; c -= rot(b,24); \
    }

    static unsigned int hash_compute( struct hash const * hash, const char* key, int length)
    {
        unsigned int a;
        unsigned int b;
        unsigned int c;                                          /* internal state */
        const unsigned char* uk = (const unsigned char*)key;

        /* Set up the internal state */
        a = b = c = 0xdeadbeef + (length << 2);

        /* we use this to 'hash' full path with mostly a common root
         * let's now waste too much cycles hashing mostly constant stuff
         */
        if(length > 36)
        {
            uk += length - 36;
            length = 36;
        }
        /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
        while (length > 12)
        {
            a += get_unaligned_uint(uk);
            b += get_unaligned_uint(uk+4);
            c += get_unaligned_uint(uk+8);
            mix(a,b,c);
            length -= 12;
            uk += 12;
        }

        /*----------------------------- handle the last (probably partial) block */
        /* Note: we possibly over-read, which would trigger complaint from VALGRIND
         * but we mask the undefined stuff if any, so we are still good, thanks
         * to alignment of memory allocation and tail-memory management overhead
         * we always can read 3 bytes past the official end without triggering
         * a segfault -- if you find a platform/compiler couple for which that postulate
         * is false, then you just need to over-allocate by 2 more bytes in file_load()
         * file_load already over-allocate by 1 to stick a \0 at the end of the buffer.
         */
        switch(length)
        {
        case 12: c+=get_unaligned_uint(uk+8); b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
        case 11: c+=get_unaligned_uint(uk+8) & MASK_C1; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
        case 10: c+=get_unaligned_uint(uk+8) & MASK_C2; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
        case 9 : c+=get_unaligned_uint(uk+8) & MASK_C3; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
        case 8 : b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break;
        case 7 : b+=get_unaligned_uint(uk+4) & MASK_C1; a+=get_unaligned_uint(uk); break;
        case 6 : b+=get_unaligned_uint(uk+4) & MASK_C2; a+=get_unaligned_uint(uk); break;
        case 5 : b+=get_unaligned_uint(uk+4) & MASK_C3; a+=get_unaligned_uint(uk); break;
        case 4 : a+=get_unaligned_uint(uk); break;
        case 3 : a+=get_unaligned_uint(uk) & MASK_C1; break;
        case 2 : a+=get_unaligned_uint(uk) & MASK_C2; break;
        case 1 : a+=get_unaligned_uint(uk) & MASK_C3; break;
        case 0 : return c & hash->size;              /* zero length strings require no mixing */
        }

        final(a,b,c);
        return c & hash->size;
    }

    static void hash_destroy(struct hash* hash)
    {
        if(hash)
        {
            if(hash->array)
            {
                free(hash->array);
            }
            if(hash->elems_pool)
            {
                pool_destroy(hash->elems_pool);
            }
            free(hash);
        }
    }

    static struct hash* hash_create(unsigned int size)
    {
    struct hash* hash;

        assert(size > 0);
        hash = calloc(1, sizeof(struct hash));
        if(hash)
        {
            size += (size >> 2) + 1; /* ~ 75% load factor */
            if(size >= 15)
            {
                hash->size = (((unsigned int)0xFFFFFFFF) >> clz((unsigned int)size));
            }
            else
            {
                hash->size = size = 15;
            }
            hash->load_limit = hash->size - (hash->size >> 2);
            hash->used = 0;
            hash->array = (struct hash_elem**)calloc(hash->size + 1, sizeof(struct hash_elem*));
            if(hash->array == NULL)
            {
                hash_destroy(hash);
                hash = NULL;
            }
        }
        if(hash)
        {
            hash->elems_pool = pool_create(sizeof(struct hash_elem),
                                           size, size << 1);
            if(!hash->elems_pool)
            {
                hash_destroy(hash);
                hash = NULL;
            }
        }
        return hash;
    }

    static void hash_resize(struct hash* hash)
    {
    unsigned int old_size = hash->size;
    unsigned int hashed;
    struct hash_elem* hash_elem;
    struct hash_elem* next;
    struct hash_elem** array;
    unsigned int i;

        hash->size = (old_size << 1) + 1;
        /* we really should avoid to get there... so print a message to alert of the condition */
        fprintf(stderr, "resize hash %u -> %u\n", old_size, hash->size);
        if(hash->size == old_size)
        {
            return;
        }
        array = calloc(hash->size + 1, sizeof(struct hash_elem*));
        if(array)
        {
            hash->load_limit = hash->size - (hash->size >> 2);
            for(i=0; i <= old_size; i++)
            {
                hash_elem = (struct hash_elem*)hash->array[i];
                while(hash_elem)
                {
                    next = hash_elem->next;

                    hashed = hash_compute(hash, hash_elem->key, hash_elem->key_len);
                    hash_elem->next = array[hashed];
                    array[hashed] = hash_elem;
                    hash_elem = next;
                }
            }
            free(hash->array);
            hash->array = (struct hash_elem**)array;
        }
        else
        {
            hash->size = old_size;
        }
    }

    static inline int compare_key(struct hash const * hash, const char* a, const char* b, int len, int const * cost)
    {
    #ifdef HASH_STAT
        *cost += 1;
        hash->memcmp += 1;
    #else
        (void) hash;
        (void) cost;
    #endif
        return memcmp(a,b, len);
    }

    /* a customized hash_store function that just store the key and return
     * TRUE if the key was effectively stored, or FALSE if the key was already there
     */
    static int hash_store(struct hash* hash, const char* key, int key_len)
    {
    unsigned int hashed;
    struct hash_elem* hash_elem;
    int cost = 0;

        (void) cost;
        hashed = hash_compute(hash, key, key_len);
    #ifdef HASH_STAT
        hash->stored += 1;
    #endif
        hash_elem = (struct hash_elem*)hash->array[hashed];
        while(hash_elem && (hash_elem->key_len != key_len || compare_key(hash, hash_elem->key, key, key_len, &cost)))
        {
            hash_elem = hash_elem->next;
        }

        if(!hash_elem)
        {
            hash_elem = pool_alloc(hash->elems_pool);
            if(hash_elem)
            {
                hash_elem->key = key;
                hash_elem->key_len = key_len;
                hash_elem->next = hash->array[hashed];

    #ifdef HASH_STAT
                if(hash_elem->next)
                {
                    hash->collisions += 1;
                    hash->cost += cost;
                }
    #endif
                hash->array[hashed] = hash_elem;
                hash->used += 1;
                if(hash->used > hash->load_limit)
                {
                    hash_resize(hash);
                }
            }
            return TRUE;
        }
        return FALSE;
    }

    static int file_stat(const char* name, struct stat* buffer_stat, int* rc)
    {
    int rc_local = 0;

        rc_local = stat(name, buffer_stat);
        if (rc_local  < 0)
        {
            *rc = errno;
        }
        return rc_local;
    }

    static off_t file_get_size(const char* name, int* rc)
    {
    struct stat buffer_stat;
    off_t       size = -1;

        if (!file_stat(name, &buffer_stat, rc))
        {
            if(S_ISREG(buffer_stat.st_mode))
            {
                size = buffer_stat.st_size;
            }
            else
            {
                *rc = EINVAL;
            }
        }
        return size;
    }

    #if !ENABLE_RUNTIME_OPTIMIZATIONS
    static void * file_load_buffers[100000];
    static size_t file_load_buffer_count = 0;
    #endif

    static char* file_load(const char* name, off_t* size, int* return_rc)
    {
    off_t local_size = 0;
    int rc = 0;
    char* buffer = NULL;
    int fd;

        assert(name != NULL);

        if(!size)
        {
            size = &local_size;
        }
        *size = file_get_size(name, &rc);
        if (!rc && *size >= 0)
        {
            fd = open(name, FILE_O_RDONLY | FILE_O_BINARY);
            if (!(fd == -1))
            {
                buffer = malloc((size_t)(*size + 1));
    #if !ENABLE_RUNTIME_OPTIMIZATIONS
                if (buffer != NULL)
                {
                    if (file_load_buffer_count == 100000)
                    {
                        free(buffer);
                        buffer = NULL;
                    }
                    else
                    {
                        file_load_buffers[file_load_buffer_count++] = buffer;
                    }
                }
    #endif
                if (buffer == NULL)
                {
                    rc = ENOMEM;
                }
                else
                {
                ssize_t i;

                  REDO:
                    i = read(fd, buffer, (size_t)(*size));
                    if(i == -1)
                    {
                        if(errno == EINTR)
                        {
                            goto REDO;
                        }
                        else
                        {
                            rc = errno;
                        }
                    }
                    else
                    {
                        if (i != *size)
                        {
                            rc = EIO;
                        }
                    }
                    buffer[*size] = 0;
                }
                close(fd);
            }
        }

        if(rc && buffer)
        {
            free(buffer);
            buffer = NULL;
        }
        if(return_rc)
        {
            *return_rc = rc;
        }
        return buffer;
    }

    static void cancel_relative(char const * base, char** ref_cursor, char** ref_cursor_out, char const * end)
    {
        char* cursor = *ref_cursor;
        char* cursor_out = *ref_cursor_out;

        do
        {
            cursor += 3;
            while(cursor_out > base && cursor_out[-1] == '/')
                cursor_out--;
            while(cursor_out > base && *--cursor_out != '/');
        }
        while(cursor + 3 < end && !memcmp(cursor, "/../", 4));
        *ref_cursor = cursor;
        *ref_cursor_out = cursor_out;
    }

    static inline void eat_space(char ** token)
    {
        while ((' ' == **token) || ('\t' == **token)) {
            ++(*token);
        }
    }

    /*
     * Prune LibreOffice specific duplicate dependencies to improve
     * gnumake startup time, and shrink the disk-space footprint.
     */
    static inline int
    elide_dependency(const char* key, int key_len, const char **unpacked_end)
    {
    #if 0
        {
            int i;
            fprintf (stderr, "elide?%d!: '", internal_boost);
            for (i = 0; i < key_len; i++) {
                fprintf (stderr, "%c", key[i]);
            }
            fprintf (stderr, "'\n");
        }
    #endif

        /* boost brings a plague of header files */
        int i;
        int unpacked = 0;
        /* walk down path elements */
        for (i = 0; i < key_len - 1; i++)
        {
            if (key[i] == '/')
            {
                if (0 == unpacked)
                {
                    if (!PATHNCMP(key + i + 1, "workdir/", 8))
                    {
                        unpacked = 1;
                        continue;
                    }
                }
                else
                {
                    if (!PATHNCMP(key + i + 1, "UnpackedTarball/", 16))
                    {
                        if (unpacked_end)
                            *unpacked_end = strchr(key + i + 17, '/');
                        return 1;
                    }
                }
            }
        }

        return 0;
    }

    /*
     * We collapse tens of internal boost headers to the unpacked target, such
     * that you can re-compile / install boost and all is well.
     */
    static void emit_single_boost_header(void)
    {
    #define BOOST_TARGET "/UnpackedTarball/boost.done"
        fprintf(stdout, "%s" BOOST_TARGET " ", work_dir);
    }

    static void emit_unpacked_target(const char* token, const char* end)
    {
        fwrite(token, 1, end-token, stdout);
        fputs(".done ", stdout);
    }

    /* prefix paths to absolute */
    static inline void print_fullpaths(char* line)
    {
        char* token;
        char* end;
        int boost_count = 0;
        int token_len;
        const char * unpacked_end = NULL; /* end of UnpackedTarget match (if any) */
        /* for UnpackedTarget the target is GenC{,xx}Object, don't mangle! */
        int target_seen = 0;

        token = line;
        eat_space(&token);
        while (*token)
        {
            end = token;
            /* hard to believe that in this day and age drive letters still exist */
            if (*end && (':' == *(end+1)) &&
                (('\\' == *(end+2)) || ('/' == *(end+2))) &&
                isalpha((unsigned char)*end))
            {
                end = end + 3; /* only one cross, err drive letter per filename */
            }
            while (*end && (' ' != *end) && ('\t' != *end) && (':' != *end)) {
                ++end;
            }
            token_len = end - token;
            if (target_seen &&
                elide_dependency(token, token_len, &unpacked_end))
            {
                if (unpacked_end)
                {
                    if (internal_boost && !PATHNCMP(unpacked_end - 5, "boost", 5))
                    {
                        ++boost_count;
                        if (boost_count == 1)
                            emit_single_boost_header();
                        else
                        {
                            /* don't output, and swallow trailing \\\n if any */
                            token = end;
                            eat_space(&token);
                            if (token[0] == '\\' && token[1] == '\n')
                                end = token + 2;
                        }
                    }
                    else
                    {
                        emit_unpacked_target(token, unpacked_end);
                    }
                    unpacked_end = NULL;
                }
            }
            else
            {
                if (fwrite(token, token_len, 1, stdout) != 1)
                    abort();
                fputc(' ', stdout);
            }
            token = end;
            eat_space(&token);
            if (!target_seen)
            {
                if (':' == *token)
                {
                    target_seen = 1;
                    fputc(':', stdout);
                    ++token;
                    eat_space(&token);
                }
            }
        }
    }

    static inline char * eat_space_at_end(char * end)
    {
        char * real_end;
        assert('\0' == *end);
        real_end = end - 1;
        while (' ' == *real_end || '\t' == *real_end || '\n' == *real_end
                    || ':' == *real_end)
        {    /* eat colon and whitespace at end */
             --real_end;
        }
        return real_end;
    }

    static char* phony_content_buffer;
    static inline char* generate_phony_line(char const * phony_target, char const * extension)
    {
    char const * src;
    char* dest;
    char* last_dot = NULL;
        //fprintf(stderr, "generate_phony_line called with phony_target %s and extension %s\n", phony_target, extension);
        for(dest = phony_content_buffer+work_dir_len+1, src = phony_target; *src != 0; ++src, ++dest)
        {
            *dest = *src;
            if(*dest == '.')
            {
                last_dot = dest;
            }
        }
        //fprintf(stderr, "generate_phony_line after phony_target copy: %s\n", phony_content_buffer);
        for(dest = last_dot+1, src = extension; *src != 0; ++src, ++dest)
        {
            *dest = *src;
        }
        //fprintf(stderr, "generate_phony_line after extension add: %s\n", phony_content_buffer);
        strcpy(dest, ": $(gb_Helper_PHONY)\n");
        //fprintf(stderr, "generate_phony_line after phony add: %s\n", phony_content_buffer);
        return phony_content_buffer;
    }

    static inline int generate_phony_file(char* fn, char const * content)
    {
    FILE* depfile;
        depfile = fopen(fn, "w");
        if(!depfile)
        {
            fprintf(stderr, "Could not open '%s' for writing: %s\n", fn, strerror(errno));
        }
        else
        {
            fputs(content, depfile);
            fclose(depfile);
        }
        return !depfile;
    }

    static int process(struct hash* dep_hash, char* fn)
    {
    int rc;
    char* buffer;
    char* end;
    char* cursor;
    char* cursor_out;
    char* base;
    char* created_line = NULL;
    char* src_relative;
    int continuation = 0;
    char last_ns = 0;
    off_t size;

        buffer = file_load(fn, &size, &rc);
        if(!rc)
        {
            base = cursor_out = cursor = end = buffer;
            end += size;

            /* first eat unneeded space at the beginning of file
             */
            while(cursor < end && (*cursor == ' ' || *cursor == '\\'))
                ++cursor;

            while(cursor < end)
            {
                if(*cursor == '\\')
                {
                    continuation = 1;
                    *cursor_out++ = *cursor++;
                }
                else if(*cursor == '/')
                {
                    if(cursor + 3 < end)
                    {
                        if(!memcmp(cursor, "/../", 4))
                        {
                            cancel_relative(base, &cursor, &cursor_out, end);
                        }
                    }
                    *cursor_out++ = *cursor++;
                }
                else if(*cursor == '\n')
                {
                    if(!continuation)
                    {
                        *cursor_out = 0;
                        if(base < cursor)
                        {
                            /* here we have a complete rule */
                            if(last_ns == ':')
                            {
                                /* if the rule ended in ':' that is a no-dep rule
                                 * these are the one for which we want to filter
                                 * duplicate out
                                 */
                                int key_len = eat_space_at_end(cursor_out) - base;
                                if (!elide_dependency(base,key_len + 1, NULL)
                                    && hash_store(dep_hash, base, key_len))
                                {
                                    /* DO NOT modify base after it has been added
                                       as key by hash_store */
                                    print_fullpaths(base);
                                    putc('\n', stdout);
                                }
                            }
                            else
                            {
                                /* rule with dep, just write it */
                                print_fullpaths(base);
                                putc('\n', stdout);
                            }
                            last_ns = ' '; // cannot hurt to reset it
                        }
                        cursor += 1;
                        base = cursor_out = cursor;
                    }
                    else
                    {
                        /* here we have a '\' followed by \n this is a continuation
                         * i.e not a complete rule yet
                         */
                        *cursor_out++ = *cursor++;
                        continuation = 0; // cancel current one (empty lines!)
                    }
                }
                else
                {
                    continuation = 0;
                    /* not using isspace() here save 25% of I refs and 75% of D refs based on cachegrind */
                    if(*cursor != ' ' && *cursor != '\n' && *cursor != '\t' )
                    {
                        last_ns = *cursor;
                    }
                    *cursor_out++ = *cursor++;
                }
            }
            /* just in case the file did not end with a \n, there may be a pending rule */
            if(base < cursor_out)
            {
                if(last_ns == ':')
                {
                    int key_len = eat_space_at_end(cursor_out) - base;
                    if (!elide_dependency(base,key_len + 1, NULL) &&
                        hash_store(dep_hash, base, key_len))
                    {
                        puts(base);
                        putc('\n', stdout);
                    }
                }
                else
                {
                    puts(base);
                    putc('\n', stdout);
                }
            }
        }
        else
        {
            if(strncmp(fn, work_dir, work_dir_len) == 0)
            {
                if(strncmp(fn+work_dir_len, "/Dep/", 5) == 0)
                {
                    src_relative = fn+work_dir_len+5;
                    // cases ordered by frequency
                    if(strncmp(src_relative, "CxxObject/", 10) == 0)
                    {
                        created_line = generate_phony_line(src_relative, "o");
                        rc = generate_phony_file(fn, created_line);
                    }
                    else if(strncmp(src_relative, "GenCxxObject/", 13) == 0)
                    {
                        created_line = generate_phony_line(src_relative, "o");
                        rc = generate_phony_file(fn, created_line);
                    }
                    else if(strncmp(src_relative, "CObject/", 8) == 0)
                    {
                        created_line = generate_phony_line(src_relative, "o");
                        rc = generate_phony_file(fn, created_line);
                    }
                    else if(strncmp(src_relative, "GenCObject/", 11) == 0)
                    {
                        created_line = generate_phony_line(src_relative, "o");
                        rc = generate_phony_file(fn, created_line);
                    }
                    else if(strncmp(src_relative, "SdiObject/", 10) == 0)
                    {
                        created_line = generate_phony_line(src_relative, "o");
                        rc = generate_phony_file(fn, created_line);
                    }
                    else if(strncmp(src_relative, "AsmObject/", 10) == 0)
                    {
                        created_line = generate_phony_line(src_relative, "o");
                        rc = generate_phony_file(fn, created_line);
                    }
                    else if(strncmp(src_relative, "ObjCxxObject/", 13) == 0)
                    {
                        created_line = generate_phony_line(src_relative, "o");
                        rc = generate_phony_file(fn, created_line);
                    }
                    else if(strncmp(src_relative, "ObjCObject/", 11) == 0)
                    {
                        created_line = generate_phony_line(src_relative, "o");
                        rc = generate_phony_file(fn, created_line);
                    }
                    else if(strncmp(src_relative, "CxxClrObject/", 13) == 0)
                    {
                        created_line = generate_phony_line(src_relative, "o");
                        rc = generate_phony_file(fn, created_line);
                    }
                    else if(strncmp(src_relative, "GenCxxClrObject/", 16) == 0)
                    {
                        created_line = generate_phony_line(src_relative, "o");
                        rc = generate_phony_file(fn, created_line);
                    }
                    else
                    {
                        fprintf(stderr, "no magic for %s(%s) in %s\n", fn, src_relative, work_dir);
                    }
                }
                if(!rc)
                {
                    puts(created_line);
                }
            }
        }
        /* Note: yes we are going to leak 'buffer'
         * this is on purpose, to avoid cloning the 'key' out of it and our special
         * 'hash' just store the pointer to the key inside of buffer, hence it need
         * to remain allocated
         */
        // coverity[leaked_storage] - this is on purpose
        return rc;
    }

    static void usage(void)
    {
        fputs("Usage: concat-deps <file that contains dep_files>\n", stderr);
    }

    #define kDEFAULT_HASH_SIZE 4096
    #define PHONY_TARGET_BUFFER 4096

    static int get_var(char **var, const char *name)
    {
        *var = (char *)getenv(name);
        if(!*var)
        {
            fprintf(stderr,"Error: %s is missing in the environment\n", name);
            return 1;
        }
        return 0;
    }

    int main(int argc, char** argv)
    {
    int rc = 0;
    off_t in_list_size = 0;
    char* in_list;
    char* in_list_cursor;
    char* in_list_base;
    struct hash* dep_hash = NULL;
    const char *env_str;

        if(argc < 2)
        {
            usage();
            return 1;
        }
        if(get_var(&base_dir, "SRCDIR") || get_var(&work_dir, "WORKDIR"))
            return 1;
        work_dir_len = strlen(work_dir);
        phony_content_buffer = malloc(PHONY_TARGET_BUFFER);
        strcpy(phony_content_buffer, work_dir);
        phony_content_buffer[work_dir_len] = '/';

        env_str = getenv("SYSTEM_BOOST");
        internal_boost = !env_str || strcmp(env_str,"TRUE");

        in_list = file_load(argv[1], &in_list_size, &rc);
        if(!rc)
        {
            dep_hash = hash_create( kDEFAULT_HASH_SIZE);
            in_list_base = in_list_cursor = in_list;

            /* extract filename of dep file from a 'space' separated list */
            while(*in_list_cursor)
            {
                /* the input here may contain Win32 \r\n EOL */
                if(*in_list_cursor == ' '
                    || *in_list_cursor == '\n' || *in_list_cursor == '\r')
                {
                    *in_list_cursor = 0;
                    if(in_list_base < in_list_cursor)
                    {
                        rc = process(dep_hash, in_list_base);
                        if(rc)
                        {
                            break;
                        }
                    }
                    in_list_cursor += 1;
                    in_list_base = in_list_cursor;
                }
                else
                {
                    in_list_cursor += 1;
                }
            }
            if(!rc)
            {
                /* catch the last entry in case the input did not terminate with a 'space' */
                if(in_list_base < in_list_cursor)
                {
                    rc = process(dep_hash, in_list_base);
                }
            }
    #ifdef HASH_STAT
            fprintf(stderr, "stats: u:%d s:%d l:%d t:%d c:%d m:%d $:%d\n",
                    dep_hash->used, dep_hash->size, dep_hash->load_limit, dep_hash->stored,
                    dep_hash->collisions, dep_hash->memcmp, dep_hash->cost);
    #endif
        }
    #if !ENABLE_RUNTIME_OPTIMIZATIONS
        {
            size_t i;
            hash_destroy(dep_hash);
            for (i = 0; i != file_load_buffer_count; ++i)
            {
                free(file_load_buffers[i]);
            }
        }
    #endif
        return rc;
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V575 The potential null pointer is passed into 'strcpy' function. Inspect the first argument. Check lines: 1153, 1152.