Index: trunk/src/kObjCache/kObjCache.c
===================================================================
--- trunk/src/kObjCache/kObjCache.c	(revision 1001)
+++ trunk/src/kObjCache/kObjCache.c	(revision 1002)
@@ -37,4 +37,8 @@
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <limits.h>
+#ifndef PATH_MAX
+# define PATH_MAX _MAX_PATH /* windows */
+#endif 
 #if defined(__OS2__) || defined(__WIN__)
 # include <process.h>
@@ -54,10 +58,4 @@
 
 
-/* for later: */
-#define xmalloc     malloc
-#define xrealloc    realloc
-#define xstrdup     strdup
-
-
 /*******************************************************************************
 *   Structures and Typedefs                                                    *
@@ -88,5 +86,5 @@
     char *pszDir;
     /** The name of the cache file. */
-    char *pszName;
+    const char *pszName;
     /** Set if the object needs to be (re)compiled. */
     unsigned fNeedCompiling;
@@ -137,4 +135,5 @@
 *******************************************************************************/
 static const char *FindFilenameInPath(const char *pszPath);
+static char *AbsPath(const char *pszPath);
 static char *MakePathFromDirAndFile(const char *pszName, const char *pszDir);
 static char *CalcRelativeName(const char *pszPath, const char *pszDir);
@@ -144,4 +143,8 @@
 static int DoesFileInDirExist(const char *pszName, const char *pszDir);
 static void *ReadFileInDir(const char *pszName, const char *pszDir, size_t *pcbFile);
+static void *xmalloc(size_t);
+static void *xrealloc(void *, size_t);
+static char *xstrdup(const char *);
+
 
 /* crc.c */
@@ -172,67 +175,4 @@
 
 /**
- * Creates a cache entry for the given cache file name.
- *  
- * @returns Pointer to a cache entry.
- * @param   pszFilename     The cache file name.
- */
-static PKOBJCACHE kObjCacheCreate(const char *pszFilename)
-{
-    PKOBJCACHE pEntry;
-    const char *pszDir;
-    size_t cchDir;
-
-    /*
-     * Allocate an empty entry.
-     */
-    pEntry = xmalloc(sizeof(*pEntry));
-    memset(pEntry, 0, sizeof(*pEntry));
-
-    /*
-     * Setup the directory and cache file name.
-     */
-    pszDir = pszFilename;
-    assert(*pszFilename);
-    pszFilename = FindFilenameInPath(pszFilename);
-    if (pszFilename <= pszDir)
-    {
-        pszDir = "./";
-        cchDir = 2;
-    }
-    else
-        cchDir = pszFilename - pszDir; /* includes the separator */
-
-    pEntry->pszDir = xmalloc(cchDir + 1);
-    memcpy(pEntry->pszDir, pszDir, cchDir);
-    pEntry->pszDir[cchDir] = '\0';
-    pEntry->pszName = xstrdup(pszFilename);
-
-    return pEntry;
-}
-
-
-#if 0 /* don't bother. */
-/**
- * Destroys the cache entry freeing up all it's resources. 
- *  
- * @param   pEntry      The entry to free.
- */
-static void kObjCacheDestroy(PKOBJCACHE pEntry)
-{
-    free(pEntry->pszDir);
-    free(pEntry->pszName);
-    while (pEntry->SumHead.pNext)
-    {
-        void *pv = pEntry->SumHead.pNext;
-        pEntry->SumHead.pNext = pEntry->SumHead.pNext->pNext;
-        if (pv != &pEntry->NewSum)
-            free(pv);
-    }
-    free(pEntry);
-}
-#endif
-
-
-/**
  * Print a fatal error message and exit with rc=1.
  * 
@@ -276,4 +216,55 @@
 
 /**
+ * Creates a cache entry for the given cache file name.
+ *  
+ * @returns Pointer to a cache entry.
+ * @param   pszFilename     The cache file name.
+ */
+static PKOBJCACHE kObjCacheCreate(const char *pszFilename)
+{
+    PKOBJCACHE pEntry;
+
+    /*
+     * Allocate an empty entry.
+     */
+    pEntry = xmalloc(sizeof(*pEntry));
+    memset(pEntry, 0, sizeof(*pEntry));
+
+    /*
+     * Setup the directory and cache file name.
+     */
+    pEntry->pszDir = AbsPath(pszFilename);
+    pEntry->pszName = FindFilenameInPath(pEntry->pszDir);
+    if (pEntry->pszDir == pEntry->pszName)
+        kObjCacheFatal(pEntry, "Failed to find abs path for '%s'!\n", pszFilename);
+    ((char *)pEntry->pszName)[-1] = '\0';
+
+    return pEntry;
+}
+
+
+#if 0 /* don't bother. */
+/**
+ * Destroys the cache entry freeing up all it's resources. 
+ *  
+ * @param   pEntry      The entry to free.
+ */
+static void kObjCacheDestroy(PKOBJCACHE pEntry)
+{
+    free(pEntry->pszDir);
+    free(pEntry->pszName);
+    while (pEntry->SumHead.pNext)
+    {
+        void *pv = pEntry->SumHead.pNext;
+        pEntry->SumHead.pNext = pEntry->SumHead.pNext->pNext;
+        if (pv != &pEntry->NewSum)
+            free(pv);
+    }
+    free(pEntry);
+}
+#endif
+
+
+/**
  * Reads and parses the cache file.
  *  
@@ -282,5 +273,6 @@
 static void kObjCacheRead(PKOBJCACHE pEntry)
 {
-
+    kObjCacheVerbose(pEntry, "reading cache file...\n");
+    pEntry->fNeedCompiling = 1;
 }
 
@@ -293,4 +285,5 @@
 static void kObjCacheWrite(PKOBJCACHE pEntry)
 {
+    kObjCacheVerbose(pEntry, "writing cache file...\n");
     
 }
@@ -304,15 +297,38 @@
  * @param   cArgv           The number of arguments in the vector.
  */
-static void kObjCacheSpawn(PCKOBJCACHE pEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg)
-{
-#if defined(__OS2__)
-    int rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
-    if (rc)
-        kObjCacheFatal(pEntry, "%s - _spawnvp / command failed, rc=%#x\n", pszMsg, rc);
-
-#elif defined(__WIN__)
-    intptr_t rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
-    if (rc)
-        kObjCacheFatal(pEntry, "%s - _spawnvp / command failed, rc=0x%p\n", pszMsg, rc);
+static void kObjCacheSpawn(PCKOBJCACHE pEntry, const char **papszArgv, unsigned cArgv, const char *pszMsg, const char *pszStdOut)
+{
+#if defined(__OS2__) || defined(__WIN__)
+    intptr_t rc;
+    int fdStdOut = -1;
+    if (pszStdOut)
+    {
+        int fdReDir;
+        fdStdOut = dup(1); /* dup2(1,-1) doesn't work right on windows */
+        close(1);
+        fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0777);
+        if (fdReDir < 0)
+            kObjCacheFatal(pEntry, "%s - failed to create stdout redirection file '%s': %s\n", 
+                           pszMsg, pszStdOut, strerror(errno));
+        if (fdReDir != 1)
+        {
+            if (dup2(fdReDir, 1) < 0)
+                kObjCacheFatal(pEntry, "%s - dup2 failed: %s\n", pszMsg, strerror(errno));
+            close(fdReDir);
+        }
+    }
+
+    errno = 0;
+    rc = _spawnvp(_P_WAIT, papszArgv[0], papszArgv);
+    if (rc < 0)
+        kObjCacheFatal(pEntry, "%s - _spawnvp failed (rc=0x%p): %s\n", pszMsg, rc, strerror(errno));
+    if (rc > 0)
+        kObjCacheFatal(pEntry, "%s - failed rc=%d\n", pszMsg, (int)rc);
+    if (fdStdOut)
+    {
+        close(1);
+        fdStdOut = dup2(fdStdOut, 1);
+        close(fdStdOut);
+    }
 
 #else
@@ -322,4 +338,19 @@
     if (!pid)
     {
+        if (pszStdOut)
+        {
+            close(1);
+            fdReDir = open(pszStdOut, O_CREAT | O_TRUNC | O_WRONLY, 0777);
+            if (fdReDir < 0)
+                kObjCacheFatal(pEntry, "%s - failed to create stdout redirection file '%s': %s\n", 
+                               pszMsg, pszStdOut, strerror(errno));
+            if (fdReDir != 1)
+            {
+                if (dup2(fdReDir, 1) < 0)
+                    kObjCacheFatal(pEntry, "%s - dup2 failed: %s\n", pszMsg, strerror(errno));
+                close(fdReDir);
+            }
+        }
+
         execvp(papszArgv[0], papszArgv);
         kObjCacheFatal(pEntry, "%s - execvp failed rc=%d errno=%d %s\n", 
@@ -357,7 +388,8 @@
      */
     pEntry->pszNewCppMapping = ReadFileInDir(pEntry->pszNewCppName, pEntry->pszDir, &pEntry->cbNewCppMapping);
-    if (pEntry->pszNewCppMapping)
+    if (!pEntry->pszNewCppMapping)
         kObjCacheFatal(pEntry, "failed to open/read '%s' in '%s': %s\n", 
                        pEntry->pszNewCppName, pEntry->pszDir, strerror(errno));
+    kObjCacheVerbose(pEntry, "precompiled file is %lu bytes long\n", (unsigned long)pEntry->cbNewCppMapping);
 
     pEntry->NewSum.crc32 = crc32(0, pEntry->pszNewCppMapping, pEntry->cbNewCppMapping);
@@ -365,4 +397,9 @@
     MD5Update(&MD5Ctx, pEntry->pszNewCppMapping, pEntry->cbNewCppMapping);
     MD5Final(&pEntry->NewSum.md5[0], &MD5Ctx);
+    kObjCacheVerbose(pEntry, "crc32=%#lx md5=08x%08x%08x%08x\n", pEntry->NewSum.crc32, 
+                     ((uint32_t *)&pEntry->NewSum.md5[0])[0],
+                     ((uint32_t *)&pEntry->NewSum.md5[0])[1],
+                     ((uint32_t *)&pEntry->NewSum.md5[0])[2],
+                     ((uint32_t *)&pEntry->NewSum.md5[0])[3]);
 }
 
@@ -375,6 +412,7 @@
  * @param   cArgvPreComp        The number of arguments.
  * @param   pszPreCompName      Precompile output name. (must kick around)
- */
-static void kObjCachePreCompile(PKOBJCACHE pEntry, const char **papszArgvPreComp, unsigned cArgvPreComp, const char *pszPreCompName)
+ * @param   fRedirStdOut        Whether stdout needs to be redirected or not.
+ */
+static void kObjCachePreCompile(PKOBJCACHE pEntry, const char **papszArgvPreComp, unsigned cArgvPreComp, const char *pszPreCompName, int fRedirStdOut)
 {
     /*
@@ -383,5 +421,6 @@
      * we might with to do a quick matchup later we can't remove it just now.
      */
-    if (pEntry->pszOldCppName)
+    if (    pEntry->pszOldCppName 
+        &&  DoesFileInDirExist(pEntry->pszOldCppName, pEntry->pszDir))
     {
         size_t cch = strlen(pEntry->pszOldCppName);
@@ -390,4 +429,5 @@
         memcpy(psz + cch, "-old", sizeof("-old"));
 
+        kObjCacheVerbose(pEntry, "renaming '%s' to '%s' in '%s'\n", pEntry->pszOldCppName, psz, pEntry->pszDir);
         UnlinkFileInDir(psz, pEntry->pszDir);
         if (RenameFileInDir(pEntry->pszOldCppName, psz, pEntry->pszDir))
@@ -397,15 +437,14 @@
         pEntry->pszOldCppName = psz;
     }
-    pEntry->pszNewCppName = pszPreCompName;
+    pEntry->pszNewCppName = CalcRelativeName(pszPreCompName, pEntry->pszDir);
 
     /*
      * Precompile it and calculate the checksum on the output.
      */
-    if (!pszPreCompName)
-    {
-        kObjCacheFatal(pEntry, "redirection feature is not implemented\n");
-        /** @todo piped output. */
-    }
-    kObjCacheSpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile");
+    kObjCacheVerbose(pEntry, "precompiling -> '%s'...\n", pEntry->pszNewCppName);
+    if (fRedirStdOut)
+        kObjCacheSpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", pszPreCompName);
+    else
+        kObjCacheSpawn(pEntry, papszArgvPreComp, cArgvPreComp, "precompile", NULL);
     kObjCacheCalcChecksum(pEntry);
 }
@@ -449,5 +488,5 @@
         pEntry->pszObjName = NULL;
     }
-    pEntry->pszNewObjName = CalcRelativeName(pEntry->pszDir, pszObjName);
+    pEntry->pszNewObjName = CalcRelativeName(pszObjName, pEntry->pszDir);
 
     /*
@@ -462,7 +501,8 @@
      * Do the recompilation.
      */
+    kObjCacheVerbose(pEntry, "compiling -> '%s'...\n", pEntry->pszNewObjName);
     pEntry->papszArgvCompile = papszArgvCompile;
     pEntry->cArgvCompile = cArgvCompile;
-    kObjCacheSpawn(pEntry, papszArgvCompile, cArgvCompile, "compile");
+    kObjCacheSpawn(pEntry, papszArgvCompile, cArgvCompile, "compile", NULL);
 }
 
@@ -482,9 +522,13 @@
      * Does the object name differ?
      */
-    if (    !pEntry->fNeedCompiling
-        &&  strcmp(FindFilenameInPath(pszObjName), pEntry->pszObjName))
-    {
-        pEntry->fNeedCompiling = 1;
-        kObjCacheVerbose(pEntry, "object name changed\n");
+    if (!pEntry->fNeedCompiling)
+    {
+        char *pszTmp = CalcRelativeName(pszObjName, pEntry->pszDir);
+        if (strcmp(pEntry->pszObjName, pszTmp))
+        {
+            pEntry->fNeedCompiling = 1;
+            kObjCacheVerbose(pEntry, "object name changed '%s' -> '%s'\n", pEntry->pszObjName, pszTmp);
+        }
+        free(pszTmp);
     }
 
@@ -514,5 +558,6 @@
      * Does the object file exist?
      */
-    if (!DoesFileInDirExist(pEntry->pszObjName, pEntry->pszDir))
+    if (    !pEntry->fNeedCompiling
+        &&  !DoesFileInDirExist(pEntry->pszObjName, pEntry->pszDir))
     {
         pEntry->fNeedCompiling = 1;
@@ -562,4 +607,24 @@
     if (pEntry->fNeedCompiling)
         kObjCacheCompileIt(pEntry, papszArgvCompile, cArgvCompile, pszObjName);
+}
+
+
+/**
+ * Gets the absolute path 
+ * 
+ * @returns A new heap buffer containing the absolute path.
+ * @param   pszPath     The path to make absolute. (Readonly)
+ */
+static char *AbsPath(const char *pszPath)
+{
+    char szTmp[PATH_MAX];
+#if defined(__OS2__) || defined(__WIN__)
+    if (!_fullpath(szTmp, *pszPath ? pszPath : ".", sizeof(szTmp)))
+        return xstrdup(pszPath);
+#else
+    if (!realpath(pszPath, szTmp))
+        return xstrdup(pszPath);
+#endif
+   return xstrdup(szTmp);
 }
 
@@ -610,34 +675,26 @@
 
 /**
- * Calculate how to get to pszPath from pszDir.
- * 
- * @returns The relative path from pszDir to path pszPath.
- * @param   pszPath     The path to the object.
- * @param   pszDir      The directory it shall be relative to.
- */
-static char *CalcRelativeName(const char *pszPath, const char *pszDir)
-{
-    size_t cchDir = strlen(pszDir);
+ * Compares two path strings to see if they are identical.
+ * 
+ * This doesn't do anything fancy, just the case ignoring and 
+ * slash unification.
+ * 
+ * @returns 1 if equal, 0 otherwise.
+ * @param   pszPath1    The first path.
+ * @param   pszPath2    The second path.
+ * @param   cch         The number of characters to compare.
+ */
+static int ArePathsIdentical(const char *pszPath1, const char *pszPath2, size_t cch)
+{
 #if defined(__OS2__) || defined(__WIN__)
-    int fMatches;
-#endif 
-
-    /*
-     * This is indeed a bit tricky, so we'll try the easy way first...
-     */
-#if defined(__OS2__) || defined(__WIN__)
-    fMatches = strnicmp(pszPath, pszDir, cchDir);
-    if (!fMatches)
+    if (strnicmp(pszPath1, pszPath2, cch))
     {
         /* Slashes may differ, compare char by char. */
-        const char *psz1 = pszDir;
-        const char *psz2 = pszPath;
-        fMatches = 1;
-        for (;; psz1++, psz2++)
+        const char *psz1 = pszPath1;
+        const char *psz2 = pszPath2;
+        for (;cch; psz1++, psz2++, cch--)
         {
             if (*psz1 != *psz2)
             {
-                if (!*psz1) /* dir */
-                    break;
                 if (    tolower(*psz1) != tolower(*psz2)
                     &&  toupper(*psz1) != toupper(*psz2)
@@ -646,19 +703,60 @@
                     &&  *psz2 != '/'
                     &&  *psz2 != '\\')
-                {
-                    fMatches = 0;
-                    break;
-                }
+                    return 0;
             }
         }
     }
-    if (fMatches)
+    return 1;
 #else
-    if (!strncmp(pszPath, pszDir, cchDir))
+    return !strncmp(pszPath1, pszPath2, cch);
 #endif 
+}
+
+
+/**
+ * Calculate how to get to pszPath from pszDir.
+ * 
+ * @returns The relative path from pszDir to path pszPath.
+ * @param   pszPath     The path to the object.
+ * @param   pszDir      The directory it shall be relative to.
+ */
+static char *CalcRelativeName(const char *pszPath, const char *pszDir)
+{
+    char *pszRet = NULL;
+    char *pszAbsPath = NULL;
+    size_t cchDir = strlen(pszDir);
+
+    /*
+     * This is indeed a bit tricky, so we'll try the easy way first...
+     */
+    if (ArePathsIdentical(pszPath, pszDir, cchDir))
     {
         if (pszPath[cchDir])
-            return xstrdup(pszPath + cchDir);
-        return xstrdup("./");
+            pszRet = (char *)pszPath + cchDir;
+        else
+            pszRet = "./";
+    }
+    else
+    {
+        pszAbsPath = AbsPath(pszPath);
+        if (ArePathsIdentical(pszAbsPath, pszDir, cchDir))
+        {
+            if (pszPath[cchDir])
+                pszRet = pszAbsPath + cchDir;
+            else
+                pszRet = "./";
+        }
+    }
+    if (pszRet)
+    {
+#if defined(__OS2__) || defined(__WIN__)
+        while (*pszRet == ':' || *pszRet == '/' || *pszRet == '\\')
+#else
+        while (*pszRet == '/')
+#endif 
+            pszRet++;
+        pszRet = xstrdup(pszRet);
+        free(pszAbsPath);
+        return pszRet;
     }
 
@@ -795,4 +893,30 @@
 
 
+static void *xmalloc(size_t cb)
+{
+    void *pv = malloc(cb);
+    if (!pv)
+        kObjCacheFatal(NULL, "out of memory (%d)\n", (int)cb);
+    return pv;
+}
+
+
+static void *xrealloc(void *pvOld, size_t cb)
+{
+    void *pv = realloc(pvOld, cb);
+    if (!pv)
+        kObjCacheFatal(NULL, "out of memory (%d)\n", (int)cb);
+    return pv;
+}
+
+
+static char *xstrdup(const char *pszIn)
+{
+    char *psz = strdup(pszIn);
+    if (!psz)
+        kObjCacheFatal(NULL, "out of memory (%d)\n", (int)strlen(pszIn));
+    return psz;
+}
+
 
 /**
@@ -820,5 +944,5 @@
 static int usage(void)
 {
-    printf("syntax: kObjCache [-v|--verbose] [-f|--file] <cache-file> [-V|--version]\n"
+    printf("syntax: kObjCache [-v|--verbose] [-f|--file] <cache-file> [-V|--version] [-r|--redir-stdout]\n"
            "                  --kObjCache-cpp <filename> <precompiler + args> \n"
            "                  --kObjCache-cc <object> <compiler + args>\n"
@@ -839,4 +963,5 @@
     unsigned cArgvPreComp = 0;
     const char *pszPreCompName = NULL;
+    int fRedirStdOut = 0;
 
     const char **papszArgvCompile = NULL;
@@ -866,5 +991,5 @@
         {
             enmMode = kOC_CcArgv;
-            if (pszObjName)
+            if (!pszObjName)
             {
                 if (++i >= argc)
@@ -882,8 +1007,5 @@
             {
                 if (!(cArgvPreComp % 16))
-                {
-                    cArgvPreComp += 16;
-                    papszArgvPreComp = xrealloc((void *)papszArgvPreComp, (cArgvPreComp + 2) * sizeof(papszArgvPreComp[0]));
-                }
+                    papszArgvPreComp = xrealloc((void *)papszArgvPreComp, (cArgvPreComp + 17) * sizeof(papszArgvPreComp[0]));
                 papszArgvPreComp[cArgvPreComp++] = argv[i];
                 papszArgvPreComp[cArgvPreComp] = NULL;
@@ -892,8 +1014,5 @@
             {
                 if (!(cArgvCompile % 16))
-                {
-                    cArgvCompile += 16;
-                    papszArgvCompile = xrealloc((void *)papszArgvCompile, (cArgvCompile + 2) * sizeof(papszArgvCompile[0]));
-                }
+                    papszArgvCompile = xrealloc((void *)papszArgvCompile, (cArgvCompile + 17) * sizeof(papszArgvCompile[0]));
                 papszArgvCompile[cArgvCompile++] = argv[i];
                 papszArgvCompile[cArgvCompile] = NULL;
@@ -906,4 +1025,6 @@
             pszCacheFile = argv[++i];
         }
+        else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--redir-stdout"))
+            fRedirStdOut = 1;
         else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
             g_fVerbose = 1;
@@ -936,5 +1057,5 @@
      * Do the compiling.
      */
-    kObjCachePreCompile(pEntry, papszArgvPreComp, cArgvPreComp, pszPreCompName);
+    kObjCachePreCompile(pEntry, papszArgvPreComp, cArgvPreComp, pszPreCompName, fRedirStdOut);
     kObjCacheCompileIfNeeded(pEntry, papszArgvCompile, cArgvCompile, pszObjName);
 
@@ -948,3 +1069,2 @@
 }
 
-
