Index: /trunk/src/fastdep/Makefile
===================================================================
--- /trunk/src/fastdep/Makefile	(revision 7)
+++ /trunk/src/fastdep/Makefile	(revision 7)
@@ -0,0 +1,121 @@
+# $Id$
+
+#
+# Odin32 API
+#
+#   Makefile for the Quick-and-Dirty dependency utility. (FastDep)
+#
+#   Copyright (c) 1999-2002 knut st. osmundsen (bird@anduin.net)
+#
+#   GPL
+#
+
+
+!ifdef BUILD_SETUP_MAK
+
+
+#
+# Setup config
+#
+ALL_NO_DBGMEM = 1
+PATH_ROOT = ..\..
+!include $(PATH_ROOT)\$(BUILD_SETUP_MAK)
+
+#
+# Target config
+#
+TARGET_NAME = fastdep
+TARGET_MODE = EXE
+TARGET_NEEDED = 1
+TARGET_PUB_BASE = $(PATH_TOOLS)
+
+TARGET_OBJS =\
+$(PATH_TARGET)\fastdep.$(EXT_OBJ)\
+$(PATH_TARGET)\avl.$(EXT_OBJ)\
+
+TARGET_LIBS =\
+$(LIB_OS)\
+$(LIB_C_OBJ)
+
+#
+# Rules config
+#
+RULES_FORWARD =
+!include $(MAKE_INCLUDE_PROCESS)
+
+
+!else
+#
+# Directory macro.
+#
+ODIN32_BIN     = $(ODIN32_TOOLS)
+
+
+#
+# Tell buildenvironment that we're making an VIO .exe.
+# Tell buildenvironment that we like to use static linked CRT.
+# Tell buildenvironment that we should not copy this into /bin.
+#
+EXETARGET = 1
+VIO = 1
+STATIC_CRT = 1
+NO_MAIN_BIN_COPY = 1
+
+
+#
+# include common definitions
+#
+!include ../../makefile.inc
+
+
+#
+# Addjust common definitions
+#
+!if "$(VAC3)" == "1" || "$(VAC36)" == "1"
+CFLAGS   = $(CFLAGS) -W3 -Wall+ppt-ppc-inl-cnv-gnr-vft-gen-uni-ext- \
+!ifdef DEBUG
+        -O+ -Tm-
+!endif
+!endif
+
+
+#
+# Object files. Prefix with OBJDIR and one space before the '\'.
+#
+OBJS = \
+$(OBJDIR)\fastdep.obj \
+$(OBJDIR)\avl.obj
+
+
+#
+# Libraries. One space before the '\'.
+#
+LIBS = \
+$(RTLLIB) \
+os2386.lib
+
+
+#
+# Target name - name of the exe without extention and path.
+#
+TARGET = FastDep
+
+
+#
+# Includes the common rules.
+#
+!include $(ODIN32_POST_INC)
+
+
+#
+# We need all.
+#
+needed: all
+!endif
+
+
+#
+# NT version using Watcom C/C++.
+#
+fastdepnt.exe:
+    wcl386 -bt=nt -l=nt -d2 /hc /"option map" -DOS2FAKE=1 -I$(WATCOM)\h\nt fastdep.c avl.c os2fake-win.c /Fe=$@ kernel32.lib
Index: /trunk/src/fastdep/avl.c
===================================================================
--- /trunk/src/fastdep/avl.c	(revision 7)
+++ /trunk/src/fastdep/avl.c	(revision 7)
@@ -0,0 +1,823 @@
+/* $Id$
+ *
+ * AVL-Tree (lookalike) implementation.
+ *
+ * Copyright (c) 1999 knut st. osmundsen
+ *
+ * Project Odin Software License can be found in LICENSE.TXT
+ *
+ */
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+
+/*
+ * AVL helper macros.
+ */
+#define AVL_HEIGHTOF(pNode) ((unsigned char)((pNode) != NULL ? pNode->uchHeight : 0))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+#ifndef INLINE
+#   if defined(__IBMC__)
+#       define INLINE _Inline
+#   elif defined(__IBMCPP__)
+#       define INLINE inline
+#   elif defined(__WATCOMC__)
+#       define INLINE __inline
+#   elif defined(__WATCOM_CPLUSPLUS__)
+#       define INLINE inline
+#   else
+#       error message("unknown compiler - inline keyword unknown!")
+#   endif
+#endif
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+#include <os2.h>
+#include "avl.h"
+#if defined(RING0) || defined(RING3)
+    #include "dev32.h"
+#else
+    #define SSToDS(a) (a)
+#endif
+#include "string.h"
+
+#if defined(__IBMCPP__) || defined(__IBMC__)
+#include <builtin.h>
+#define assert(a) ((a) ? (void)0 : __interrupt(3) )
+#else
+#include <assert.h>
+#endif
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/*
+ * A stack used to avoid recursive calls...
+ */
+typedef struct _AVLStack
+{
+    unsigned     cEntries;
+    PPAVLNODECORE aEntries[AVL_MAX_HEIGHT];
+} AVLSTACK, *PAVLSTACK;
+typedef struct _AVLStack2
+{
+    unsigned     cEntries;
+    PAVLNODECORE aEntries[AVL_MAX_HEIGHT];
+    char         achFlags[AVL_MAX_HEIGHT];
+} AVLSTACK2, *PAVLSTACK2;
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+INLINE void AVLRebalance(PAVLSTACK pStack);
+
+
+/**
+ * Inserts a node into the AVL-tree.
+ * @returns   TRUE if inserted.
+ *            FALSE if node exists in tree.
+ * @param     ppTree  Pointer to the AVL-tree root node pointer.
+ * @param     pNode   Pointer to the node which is to be added.
+ * @sketch    Find the location of the node (using binary three algorithm.):
+ *            LOOP until NULL leaf pointer
+ *            BEGIN
+ *                Add node pointer pointer to the AVL-stack.
+ *                IF new-node-key < node key THEN
+ *                    left
+ *                ELSE
+ *                    right
+ *            END
+ *            Fill in leaf node and insert it.
+ *            Rebalance the tree.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+BOOL AVLInsert(PPAVLNODECORE ppTree, PAVLNODECORE pNode)
+{
+    AVLSTACK                AVLStack;
+    PPAVLNODECORE           ppCurNode = ppTree;
+    register AVLKEY         Key = pNode->Key;
+    register PAVLNODECORE   pCurNode;
+
+    AVLStack.cEntries = 0;
+
+    while ((pCurNode = *ppCurNode) != NULL)
+    {
+        assert(AVLStack.cEntries < AVL_MAX_HEIGHT);
+        assert(pNode != pCurNode);
+        AVLStack.aEntries[AVLStack.cEntries++] = ppCurNode;
+        #ifdef AVL_MAY_TRY_INSERT_EQUAL
+        /* check if equal */
+        if (AVL_E(pCurNode->Key, Key))
+            return FALSE;
+        #endif
+        if (AVL_G(pCurNode->Key, Key))
+            ppCurNode = &pCurNode->pLeft;
+        else
+            ppCurNode = &pCurNode->pRight;
+    }
+
+    pNode->pLeft = pNode->pRight = NULL;
+    pNode->uchHeight = 1;
+    *ppCurNode = pNode;
+
+    AVLRebalance(SSToDS(&AVLStack));
+    return TRUE;
+}
+
+
+/**
+ * Removes a node from the AVL-tree.
+ * @returns   Pointer to the node.
+ * @param     ppTree  Pointer to the AVL-tree root node pointer.
+ * @param     Key     Key value of the node which is to be removed.
+ * @sketch    Find the node which is to be removed:
+ *            LOOP until not found
+ *            BEGIN
+ *                Add node pointer pointer to the AVL-stack.
+ *                IF the keys matches THEN break!
+ *                IF remove key < node key THEN
+ *                    left
+ *                ELSE
+ *                    right
+ *            END
+ *            IF found THEN
+ *            BEGIN
+ *                IF left node not empty THEN
+ *                BEGIN
+ *                    Find the right most node in the left tree while adding the pointer to the pointer to it's parent to the stack:
+ *                    Start at left node.
+ *                    LOOP until right node is empty
+ *                    BEGIN
+ *                        Add to stack.
+ *                        go right.
+ *                    END
+ *                    Link out the found node.
+ *                    Replace the node which is to be removed with the found node.
+ *                    Correct the stack entry for the pointer to the left tree.
+ *                END
+ *                ELSE
+ *                BEGIN
+ *                    Move up right node.
+ *                    Remove last stack entry.
+ *                END
+ *                Balance tree using stack.
+ *            END
+ *            return pointer to the removed node (if found).
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+PAVLNODECORE AVLRemove(PPAVLNODECORE ppTree, AVLKEY Key)
+{
+    AVLSTACK                AVLStack;
+    PPAVLNODECORE           ppDeleteNode = ppTree;
+    register PAVLNODECORE   pDeleteNode;
+
+    AVLStack.cEntries = 0;
+
+    while ((pDeleteNode = *ppDeleteNode) != NULL)
+    {
+        assert(AVLStack.cEntries < AVL_MAX_HEIGHT);
+        AVLStack.aEntries[AVLStack.cEntries++] = ppDeleteNode;
+        #ifndef AVL_CMP
+        if (AVL_E(pDeleteNode->Key, Key))
+            break;
+
+        if (AVL_G(pDeleteNode->Key, Key))
+            ppDeleteNode = &pDeleteNode->pLeft;
+        else
+            ppDeleteNode = &pDeleteNode->pRight;
+        #else
+        {
+        int register iDiff;
+        if ((iDiff = AVL_CMP(pDeleteNode->Key, Key)) == 0)
+            break;
+
+        if (iDiff > 0)
+            ppDeleteNode = &pDeleteNode->pLeft;
+        else
+            ppDeleteNode = &pDeleteNode->pRight;
+        }
+        #endif
+    }
+
+    if (pDeleteNode != NULL)
+    {
+        if (pDeleteNode->pLeft != NULL)
+        {
+            unsigned                iStackEntry = AVLStack.cEntries;
+            PPAVLNODECORE           ppLeftLeast = &pDeleteNode->pLeft;
+            register PAVLNODECORE   pLeftLeast;
+
+            while ((pLeftLeast = *ppLeftLeast)->pRight != NULL) /* Left most node. */
+            {
+                assert(AVLStack.cEntries < AVL_MAX_HEIGHT);
+                AVLStack.aEntries[AVLStack.cEntries++] = ppLeftLeast;
+                ppLeftLeast = &pLeftLeast->pRight;
+                pLeftLeast  = pLeftLeast->pRight;
+            }
+
+            /* link out pLeftLeast */
+            *ppLeftLeast = pLeftLeast->pLeft;
+
+            /* link in place of the delete node. */
+            pLeftLeast->pLeft = pDeleteNode->pLeft;
+            pLeftLeast->pRight = pDeleteNode->pRight;
+            pLeftLeast->uchHeight = pDeleteNode->uchHeight;
+            *ppDeleteNode = pLeftLeast;
+            AVLStack.aEntries[iStackEntry] = &pLeftLeast->pLeft;
+        }
+        else
+        {
+            *ppDeleteNode = pDeleteNode->pRight;
+            AVLStack.cEntries--;
+        }
+
+        AVLRebalance(SSToDS(&AVLStack));
+    }
+
+    return pDeleteNode;
+}
+
+
+/**
+ * Gets a node from the tree (does not remove it!)
+ * @returns   Pointer to the node holding the given key.
+ * @param     ppTree  Pointer to the AVL-tree root node pointer.
+ * @param     Key     Key value of the node which is to be found.
+ * @sketch
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+PAVLNODECORE AVLGet(PPAVLNODECORE ppTree, AVLKEY Key)
+{
+    #ifndef AVL_CMP
+    register PAVLNODECORE   pNode = *ppTree;
+
+    while (pNode != NULL && AVL_NE(pNode->Key, Key))
+    {
+        if (AVL_G(pNode->Key, Key))
+            pNode = pNode->pLeft;
+        else
+            pNode = pNode->pRight;
+    }
+
+    #else
+
+    register int            iDiff;
+    register PAVLNODECORE   pNode = *ppTree;
+
+    while (pNode != NULL && (iDiff = AVL_CMP(pNode->Key, Key)) != 0)
+    {
+        if (iDiff > 0)
+            pNode = pNode->pLeft;
+        else
+            pNode = pNode->pRight;
+    }
+
+    #endif
+
+    return pNode;
+}
+
+
+/**
+ * Gets a node from the tree and its parent node (if any) (does not remove any nodes!)
+ * @returns   Pointer to the node holding the given key.
+ * @param     ppTree    Pointer to the AVL-tree root node pointer.
+ * @param     ppParent  Pointer to a variable which will hold the pointer to the partent node on
+ *                      return. When no node is found, this will hold the last searched node.
+ * @param     Key       Key value of the node which is to be found.
+ * @sketch
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+PAVLNODECORE    AVLGetWithParent(PPAVLNODECORE ppTree, PPAVLNODECORE ppParent, AVLKEY Key)
+{
+    #ifndef AVL_CMP
+
+    register PAVLNODECORE   pNode = *ppTree;
+    register PAVLNODECORE   pParent = NULL;
+
+    while (pNode != NULL && AVL_NE(pNode->Key, Key))
+    {
+        pParent = pNode;
+        if (AVL_G(pNode->Key, Key))
+            pNode = pNode->pLeft;
+        else
+            pNode = pNode->pRight;
+    }
+
+    #else
+
+    register PAVLNODECORE   pNode = *ppTree;
+    register PAVLNODECORE   pParent = NULL;
+    register int            iDiff;
+
+    while (pNode != NULL && (iDiff = AVL_CMP(pNode->Key, Key)) != 0)
+    {
+        pParent = pNode;
+        if (iDiff > 0)
+            pNode = pNode->pLeft;
+        else
+            pNode = pNode->pRight;
+    }
+
+    #endif
+
+    *ppParent = pParent;
+    return pNode;
+}
+
+
+
+/**
+ * Gets node from the tree (does not remove it!) and it's adjecent (by key value) nodes.
+ * @returns   Pointer to the node holding the given key.
+ * @param     ppTree   Pointer to the AVL-tree root node pointer.
+ * @param     Key      Key value of the node which is to be found.
+ * @param     ppLeft   Pointer to left node pointer.
+ * @param     ppRight  Pointer to right node pointer.
+ * @sketch    Find node with the given key, record search path on the stack.
+ *            IF found THEN
+ *            BEGIN
+ *                Find the right-most node in the left subtree.
+ *                Find the left-most node in the right subtree.
+ *                Rewind the stack while searching for more adjecent nodes.
+ *            END
+ *            return node with adjecent nodes.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+PAVLNODECORE AVLGetWithAdjecentNodes(PPAVLNODECORE ppTree, AVLKEY Key, PPAVLNODECORE ppLeft, PPAVLNODECORE ppRight)
+{
+    AVLSTACK        AVLStack;
+    PPAVLNODECORE   ppNode = ppTree;
+    PAVLNODECORE    pNode;
+    #ifdef AVL_CMP
+    int     iDiff;
+    #endif
+
+    AVLStack.cEntries = 0;
+
+    #ifndef AVL_CMP
+    while ((pNode = *ppNode) != NULL && AVL_NE(pNode->Key, Key))
+    #else
+    while ((pNode = *ppNode) != NULL && (iDiff = AVL_CMP(pNode->Key, Key)) != 0)
+    #endif
+    {
+        assert(AVLStack.cEntries < AVL_MAX_HEIGHT);
+        AVLStack.aEntries[AVLStack.cEntries++] = ppNode;
+        #ifndef AVL_CMP
+        if (AVL_G(pNode->Key, Key))
+        #else
+        if (iDiff > 0)
+        #endif
+            ppNode = &pNode->pLeft;
+        else
+            ppNode = &pNode->pRight;
+    }
+
+    if (pNode != NULL)
+    {
+        PAVLNODECORE    pCurNode;
+
+        /* find rigth-most node in left subtree. */
+        pCurNode = pNode->pLeft;
+        if (pCurNode != NULL)
+            while (pCurNode->pRight != NULL)
+                pCurNode = pCurNode->pRight;
+        *ppLeft = pCurNode;
+
+        /* find left-most node in right subtree. */
+        pCurNode = pNode->pRight;
+        if (pCurNode != NULL)
+            while (pCurNode->pLeft != NULL)
+                pCurNode = pCurNode->pLeft;
+        *ppRight = pCurNode;
+
+        /* rewind stack */
+        while (AVLStack.cEntries-- > 0)
+        {
+            pCurNode = *AVLStack.aEntries[AVLStack.cEntries];
+            #ifndef AVL_CMP
+            if (AVL_L(pCurNode->Key, Key) && (*ppLeft == NULL || AVL_G(pCurNode->Key, (*ppLeft)->Key)))
+                *ppLeft = pCurNode;
+            else if (AVL_G(pCurNode->Key, Key) && (*ppRight == NULL || AVL_L(pCurNode->Key, (*ppRight)->Key)))
+                *ppRight = pCurNode;
+            #else
+            if ((iDiff = AVL_CMP(pCurNode->Key, Key)) < 0 && (*ppLeft == NULL || AVL_G(pCurNode->Key, (*ppLeft)->Key)))
+                *ppLeft = pCurNode;
+            else if (iDiff > 0 && (*ppRight == NULL || AVL_L(pCurNode->Key, (*ppRight)->Key)))
+                *ppRight = pCurNode;
+            #endif
+        }
+    }
+    else
+        *ppLeft = *ppRight = NULL;
+
+    return pNode;
+}
+
+
+/**
+ * Iterates tru all nodes in the given tree.
+ * @returns   0 on success. Return from callback on failiure.
+ * @param     ppTree   Pointer to the AVL-tree root node pointer.
+ * @param     fFromLeft    TRUE:  Left to right.
+ *                         FALSE: Right to left.
+ * @param     pfnCallBack  Pointer to callback function.
+ * @param     pvParam      Userparameter passed on to the callback function.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+unsigned AVLDoWithAll(PPAVLNODECORE ppTree, int fFromLeft, PAVLCALLBACK pfnCallBack, void *pvParam)
+{
+    AVLSTACK2       AVLStack;
+    PAVLNODECORE    pNode;
+    unsigned        rc;
+
+    if (*ppTree == NULL)
+        return 0;
+
+    AVLStack.cEntries = 1;
+    AVLStack.achFlags[0] = 0;
+    AVLStack.aEntries[0] = *ppTree;
+
+    if (fFromLeft)
+    {   /* from left */
+        while (AVLStack.cEntries > 0)
+        {
+            pNode = AVLStack.aEntries[AVLStack.cEntries - 1];
+
+            /* left */
+            if (!AVLStack.achFlags[AVLStack.cEntries - 1]++)
+            {
+                if (pNode->pLeft != NULL)
+                {
+                    AVLStack.achFlags[AVLStack.cEntries] = 0; /* 0 first, 1 last */
+                    AVLStack.aEntries[AVLStack.cEntries++] = pNode->pLeft;
+                    continue;
+                }
+            }
+
+            /* center */
+            rc = pfnCallBack(pNode, pvParam);
+            if (rc != 0)
+                return rc;
+
+            /* right */
+            AVLStack.cEntries--;
+            if (pNode->pRight != NULL)
+            {
+                AVLStack.achFlags[AVLStack.cEntries] = 0;
+                AVLStack.aEntries[AVLStack.cEntries++] = pNode->pRight;
+            }
+        } /* while */
+    }
+    else
+    {   /* from right */
+        while (AVLStack.cEntries > 0)
+        {
+            pNode = AVLStack.aEntries[AVLStack.cEntries - 1];
+
+
+            /* right */
+            if (!AVLStack.achFlags[AVLStack.cEntries - 1]++)
+            {
+                if (pNode->pRight != NULL)
+                {
+                    AVLStack.achFlags[AVLStack.cEntries] = 0;  /* 0 first, 1 last */
+                    AVLStack.aEntries[AVLStack.cEntries++] = pNode->pRight;
+                    continue;
+                }
+            }
+
+            /* center */
+            rc = pfnCallBack(pNode, pvParam);
+            if (rc != 0)
+                return rc;
+
+            /* left */
+            AVLStack.cEntries--;
+            if (pNode->pLeft != NULL)
+            {
+                AVLStack.achFlags[AVLStack.cEntries] = 0;
+                AVLStack.aEntries[AVLStack.cEntries++] = pNode->pLeft;
+            }
+        } /* while */
+    }
+
+    return 0;
+}
+
+
+/**
+ * Starts an enumeration of all nodes in the given AVL tree.
+ * @returns   Pointer to the first node in the tree.
+ * @param     ppTree     Pointer to the AVL-tree root node pointer.
+ * @param     pEnumData  Pointer to enumeration control data.
+ * @param     fFromLeft  TRUE:  Left to right.
+ *                       FALSE: Right to left.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+PAVLNODECORE AVLBeginEnumTree(PPAVLNODECORE ppTree, PAVLENUMDATA pEnumData, int fFromLeft)
+{
+    if (*ppTree != NULL)
+    {
+        pEnumData->fFromLeft = (char)fFromLeft;
+        pEnumData->cEntries = 1;
+        pEnumData->aEntries[0] = *ppTree;
+        pEnumData->achFlags[0] = 0;
+    }
+    else
+        pEnumData->cEntries = 0;
+
+    return AVLGetNextNode(pEnumData);
+}
+
+
+/**
+ * Get the next node in the tree enumeration.
+ * @returns   Pointer to the first node in the tree.
+ * @param     pEnumData  Pointer to enumeration control data.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+PAVLNODECORE AVLGetNextNode(PAVLENUMDATA pEnumData)
+{
+    PAVLNODECORE    pNode;
+
+    if (pEnumData->fFromLeft)
+    {   /* from left */
+        while (pEnumData->cEntries > 0)
+        {
+            pNode = pEnumData->aEntries[pEnumData->cEntries - 1];
+
+            /* left */
+            if (pEnumData->achFlags[pEnumData->cEntries - 1] == 0)
+            {
+                pEnumData->achFlags[pEnumData->cEntries - 1]++;
+                if (pNode->pLeft != NULL)
+                {
+                    pEnumData->achFlags[pEnumData->cEntries] = 0; /* 0 left, 1 center, 2 right */
+                    pEnumData->aEntries[pEnumData->cEntries++] = pNode->pLeft;
+                    continue;
+                }
+            }
+
+            /* center */
+            if (pEnumData->achFlags[pEnumData->cEntries - 1] == 1)
+            {
+                pEnumData->achFlags[pEnumData->cEntries - 1]++;
+                return pNode;
+            }
+
+            /* right */
+            pEnumData->cEntries--;
+            if (pNode->pRight != NULL)
+            {
+                pEnumData->achFlags[pEnumData->cEntries] = 0;
+                pEnumData->aEntries[pEnumData->cEntries++] = pNode->pRight;
+            }
+        } /* while */
+    }
+    else
+    {   /* from right */
+        while (pEnumData->cEntries > 0)
+        {
+            pNode = pEnumData->aEntries[pEnumData->cEntries - 1];
+
+
+            /* right */
+            if (pEnumData->achFlags[pEnumData->cEntries - 1] == 0)
+            {
+                pEnumData->achFlags[pEnumData->cEntries - 1]++;
+                if (pNode->pRight != NULL)
+                {
+                    pEnumData->achFlags[pEnumData->cEntries] = 0;  /* 0 right, 1 center, 2 left */
+                    pEnumData->aEntries[pEnumData->cEntries++] = pNode->pRight;
+                    continue;
+                }
+            }
+
+            /* center */
+            if (pEnumData->achFlags[pEnumData->cEntries - 1] == 1)
+            {
+                pEnumData->achFlags[pEnumData->cEntries - 1]++;
+                return pNode;
+            }
+
+            /* left */
+            pEnumData->cEntries--;
+            if (pNode->pLeft != NULL)
+            {
+                pEnumData->achFlags[pEnumData->cEntries] = 0;
+                pEnumData->aEntries[pEnumData->cEntries++] = pNode->pLeft;
+            }
+        } /* while */
+    }
+
+    return NULL;
+
+}
+
+
+
+
+/**
+ * Finds the best fitting node in the tree for the given Key value.
+ * @returns   Pointer to the best fitting node found.
+ * @param     ppTree  Pointer to Pointer to the tree root node.
+ * @param     Key     The Key of which is to be found a best fitting match for..
+ * @param     fAbove  TRUE:  Returned node is have the closest key to Key from above.
+ *                    FALSE: Returned node is have the closest key to Key from below.
+ * @status    completely implemented.
+ * @sketch    The best fitting node is always located in the searchpath above you.
+ *            >= (above): The node where you last turned left.
+ *            <= (below): the node where you last turned right.
+ * @author    knut st. osmundsen
+ */
+PAVLNODECORE    AVLGetBestFit(PPAVLNODECORE ppTree, AVLKEY Key, int fAbove)
+{
+    #ifdef AVL_CMP
+    register int            iDiff;
+    #endif
+    register PAVLNODECORE   pNode = *ppTree;
+    PAVLNODECORE            pNodeLast = NULL;
+
+    if (fAbove)
+    {   /* pNode->Key >= Key */
+        #ifndef AVL_CMP
+        while (pNode != NULL && AVL_NE(pNode->Key, Key))
+        #else
+        while (pNode != NULL && (iDiff = AVL_CMP(pNode->Key, Key)) != 0)
+        #endif
+        {
+            #ifndef AVL_CMP
+            if (AVL_G(pNode->Key, Key))
+            #else
+            if (iDiff > 0)
+            #endif
+            {
+                pNodeLast = pNode;
+                pNode = pNode->pLeft;
+            }
+            else
+                pNode = pNode->pRight;
+        }
+    }
+    else
+    {   /* pNode->Key <= Key */
+        #ifndef AVL_CMP
+        while (pNode != NULL && AVL_NE(pNode->Key, Key))
+        #else
+        while (pNode != NULL && (iDiff = AVL_CMP(pNode->Key, Key)) != 0)
+        #endif
+        {
+            #ifndef AVL_CMP
+            if (AVL_L(pNode->Key, Key))
+            #else
+            if (iDiff < 0)
+            #endif
+            {
+                pNodeLast = pNode;
+                pNode = pNode->pRight;
+            }
+            else
+                pNode = pNode->pLeft;
+        }
+    }
+
+    return pNode == NULL ? pNodeLast /* best fit */ : pNode /* perfect match */;
+}
+
+
+/**
+ * Rewindes a stack of pointer to pointers to nodes, rebalancing the tree.
+ * @param     pStack  Pointer to stack to rewind.
+ * @sketch    LOOP thru all stack entries
+ *            BEGIN
+ *                Get pointer to pointer to node (and pointer to node) from stack.
+ *                IF 2 higher left subtree than in right subtree THEN
+ *                BEGIN
+ *                    IF higher (or equal) left-sub-subtree than right-sub-subtree THEN
+ *                                *                       n+2|n+3
+ *                              /   \                     /     \
+ *                            n+2    n       ==>         n+1   n+1|n+2
+ *                           /   \                             /     \
+ *                         n+1 n|n+1                          n|n+1  n
+ *
+ *                         Or with keys:
+ *
+ *                               4                           2
+ *                             /   \                       /   \
+ *                            2     5        ==>          1     4
+ *                           / \                               / \
+ *                          1   3                             3   5
+ *
+ *                    ELSE
+ *                                *                         n+2
+ *                              /   \                      /   \
+ *                            n+2    n                   n+1   n+1
+ *                           /   \           ==>        /  \   /  \
+ *                          n    n+1                    n  L   R   n
+ *                               / \
+ *                              L   R
+ *
+ *                         Or with keys:
+ *                               6                           4
+ *                             /   \                       /   \
+ *                            2     7        ==>          2     6
+ *                          /   \                       /  \  /  \
+ *                          1    4                      1  3  5  7
+ *                              / \
+ *                             3   5
+ *                END
+ *                ELSE IF 2 higher in right subtree than in left subtree THEN
+ *                BEGIN
+ *                    Same as above but left <==> right. (invert the picture)
+ *                ELSE
+ *                    IF correct height THEN break
+ *                    ELSE correct height.
+ *            END
+ * @status
+ * @author    knut st. osmundsen
+ * @remark
+ */
+INLINE void AVLRebalance(PAVLSTACK pStack)
+{
+    while (pStack->cEntries > 0)
+    {
+        PPAVLNODECORE   ppNode = pStack->aEntries[--pStack->cEntries];
+        PAVLNODECORE    pNode = *ppNode;
+        PAVLNODECORE    pLeftNode = pNode->pLeft;
+        unsigned char   uchLeftHeight = AVL_HEIGHTOF(pLeftNode);
+        PAVLNODECORE    pRightNode = pNode->pRight;
+        unsigned char   uchRightHeight = AVL_HEIGHTOF(pRightNode);
+
+        if (uchRightHeight + 1 < uchLeftHeight)
+        {
+            PAVLNODECORE    pLeftLeftNode = pLeftNode->pLeft;
+            PAVLNODECORE    pLeftRightNode = pLeftNode->pRight;
+            unsigned char   uchLeftRightHeight = AVL_HEIGHTOF(pLeftRightNode);
+
+            if (AVL_HEIGHTOF(pLeftLeftNode) >= uchLeftRightHeight)
+            {
+                pNode->pLeft = pLeftRightNode;
+                pLeftNode->pRight = pNode;
+                pLeftNode->uchHeight = (unsigned char)(1 + (pNode->uchHeight = (unsigned char)(1 + uchLeftRightHeight)));
+                *ppNode = pLeftNode;
+            }
+            else
+            {
+                pLeftNode->pRight = pLeftRightNode->pLeft;
+                pNode->pLeft = pLeftRightNode->pRight;
+                pLeftRightNode->pLeft = pLeftNode;
+                pLeftRightNode->pRight = pNode;
+                pLeftNode->uchHeight = pNode->uchHeight = uchLeftRightHeight;
+                pLeftRightNode->uchHeight = uchLeftHeight;
+                *ppNode = pLeftRightNode;
+            }
+        }
+        else if (uchLeftHeight + 1 < uchRightHeight)
+        {
+            PAVLNODECORE    pRightLeftNode = pRightNode->pLeft;
+            unsigned char   uchRightLeftHeight = AVL_HEIGHTOF(pRightLeftNode);
+            PAVLNODECORE    pRightRightNode = pRightNode->pRight;
+
+            if (AVL_HEIGHTOF(pRightRightNode) >= uchRightLeftHeight)
+            {
+                pNode->pRight = pRightLeftNode;
+                pRightNode->pLeft = pNode;
+                pRightNode->uchHeight = (unsigned char)(1 + (pNode->uchHeight = (unsigned char)(1 + uchRightLeftHeight)));
+                *ppNode = pRightNode;
+            }
+            else
+            {
+                pRightNode->pLeft = pRightLeftNode->pRight;
+                pNode->pRight = pRightLeftNode->pLeft;
+                pRightLeftNode->pRight = pRightNode;
+                pRightLeftNode->pLeft = pNode;
+                pRightNode->uchHeight = pNode->uchHeight = uchRightLeftHeight;
+                pRightLeftNode->uchHeight = uchRightHeight;
+                *ppNode = pRightLeftNode;
+            }
+        }
+        else
+        {
+            register unsigned char uchHeight = (unsigned char)(max(uchLeftHeight, uchRightHeight) + 1);
+            if (uchHeight == pNode->uchHeight)
+                break;
+            pNode->uchHeight = uchHeight;
+        }
+    }
+}
+
Index: /trunk/src/fastdep/avl.h
===================================================================
--- /trunk/src/fastdep/avl.h	(revision 7)
+++ /trunk/src/fastdep/avl.h	(revision 7)
@@ -0,0 +1,102 @@
+/* $Id$
+ *
+ * AVL-Tree (lookalike) declaration.
+ *
+ * This AVL implementation is configurable from this headerfile. By
+ * for example alterning the AVLKEY typedefinition an the AVL_<L|G|E|N>[E]
+ * macros you are able to create different trees. Currently you may only have
+ * one type of trees within one program (module).
+ *
+ * TREETYPE: Case Sensitive Strings (Key is pointer).
+ *
+ *
+ * Copyright (c) 1999-2000 knut st. osmundsen
+ *
+ * Project Odin Software License can be found in LICENSE.TXT
+ *
+ */
+#ifndef _AVL_H_
+#define _AVL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * AVL configuration. PRIVATE!
+ */
+#define AVL_MAX_HEIGHT              19          /* Up to 2^16 nodes. */
+#define AVL_MAY_TRY_INSERT_EQUAL    1           /* Ignore attempts to insert existing nodes. */
+
+/*
+ * AVL Compare macros
+ */
+#define AVL_L(key1, key2)  (strcmp(key1, key2) <  0)
+#define AVL_LE(key1, key2) (strcmp(key1, key2) <= 0)
+#define AVL_G(key1, key2)  (strcmp(key1, key2) >  0)
+#define AVL_GE(key1, key2) (strcmp(key1, key2) >= 0)
+#define AVL_E(key1, key2)  (strcmp(key1, key2) == 0)
+#define AVL_NE(key1, key2) (strcmp(key1, key2) != 0)
+#define AVL_CMP(key1, key2) strcmp(key1, key2)
+
+/**
+ * AVL key type
+ */
+typedef const char *AVLKEY;
+
+
+/**
+ * AVL Core node.
+ */
+typedef struct _AVLNodeCore
+{
+    AVLKEY                  Key;       /* Key value. */
+    struct  _AVLNodeCore *  pLeft;     /* Pointer to left leaf node. */
+    struct  _AVLNodeCore *  pRight;    /* Pointer to right leaf node. */
+    unsigned char           uchHeight; /* Height of this tree: max(heigth(left), heigth(right)) + 1 */
+} AVLNODECORE, *PAVLNODECORE, **PPAVLNODECORE;
+
+
+/**
+ * AVL Enum data - All members are PRIVATE! Don't touch!
+ */
+typedef struct _AVLEnumData
+{
+    char         fFromLeft;
+    char         cEntries;
+    char         achFlags[AVL_MAX_HEIGHT];
+    PAVLNODECORE aEntries[AVL_MAX_HEIGHT];
+} AVLENUMDATA, *PAVLENUMDATA;
+
+
+/*
+ * callback type
+ */
+typedef unsigned ( _PAVLCALLBACK)(PAVLNODECORE, void*);
+typedef _PAVLCALLBACK *PAVLCALLBACK;
+
+
+BOOL            AVLInsert(PPAVLNODECORE ppTree, PAVLNODECORE pNode);
+PAVLNODECORE    AVLRemove(PPAVLNODECORE ppTree, AVLKEY Key);
+PAVLNODECORE    AVLGet(PPAVLNODECORE ppTree, AVLKEY Key);
+PAVLNODECORE    AVLGetWithParent(PPAVLNODECORE ppTree, PPAVLNODECORE ppParent, AVLKEY Key);
+PAVLNODECORE    AVLGetWithAdjecentNodes(PPAVLNODECORE ppTree, AVLKEY Key, PPAVLNODECORE ppLeft, PPAVLNODECORE ppRight);
+unsigned        AVLDoWithAll(PPAVLNODECORE ppTree, int fFromLeft, PAVLCALLBACK pfnCallBack, void *pvParam);
+PAVLNODECORE    AVLBeginEnumTree(PPAVLNODECORE ppTree, PAVLENUMDATA pEnumData, int fFromLeft);
+PAVLNODECORE    AVLGetNextNode(PAVLENUMDATA pEnumData);
+PAVLNODECORE    AVLGetBestFit(PPAVLNODECORE ppTree, AVLKEY Key, int fAbove);
+
+
+
+/*
+ * Just in case NULL is undefined.
+ */
+#ifndef NULL
+    #define NULL ((void*)0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /trunk/src/fastdep/fastdep.c
===================================================================
--- /trunk/src/fastdep/fastdep.c	(revision 7)
+++ /trunk/src/fastdep/fastdep.c	(revision 7)
@@ -0,0 +1,4143 @@
+/* $Id$
+ *
+ * Fast dependents. (Fast = Quick and Dirty!)
+ *
+ * Copyright (c) 1999-2002 knut st. osmundsen (bird@anduin.net)
+ *
+ * GPL
+ *
+ */
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+#define INCL_DOSERRORS
+#define INCL_FILEMGR
+#define INCL_DOSMISC
+
+
+/*
+ * Size of the \n charater (forget '\r').
+ * If you're compiling this under a UNICODE system this may perhaps change,
+ * but I doubd that fastdep will work at all under a UNICODE system. ;-)
+ */
+#if defined(UNICODE) && !defined(__WIN32OS2__)
+#define CBNEWLINE       (2)
+#else
+#define CBNEWLINE       (1)
+#endif
+
+
+/*
+ * Time stamp size.
+ */
+#define TS_SIZE         (48)
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#if defined(OS2FAKE)
+#include "os2fake.h"
+#else
+#include <os2.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <direct.h>
+#include <assert.h>
+
+#include "avl.h"
+
+#ifdef __WIN32OS2__
+#   define WIN32API
+#   include <odinbuild.h>
+#else
+#   define ODIN32_BUILD_NR -1
+#endif
+
+#ifndef INLINE
+#   if defined(__IBMC__)
+#       define INLINE _Inline
+#   elif defined(__IBMCPP__)
+#       define INLINE inline
+#   elif defined(__WATCOMC__)
+#       define INLINE __inline
+#   elif defined(__WATCOM_CPLUSPLUS__)
+#       define INLINE inline
+#   else
+#       error message("unknown compiler - inline keyword unknown!")
+#   endif
+#endif
+
+/*
+ * This following section is used while testing fastdep.
+ * stdio.h should be included; string.h never included.
+ */
+/*
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+*/
+
+#if 1
+#include <stdio.h>
+#else
+#include <string.h>
+#include <string.h>
+#endif
+
+/*
+ */ /* */ /*
+#include <string.h>
+ */
+#if 1
+#    if 1
+        #if 0
+# include <string.h>
+        #else
+#            if 1
+                #if 1
+                    #if 0
+# include <string.h>
+                    #else /* */ /*
+*/
+ # include <stdio.h>
+                    #endif
+                #endif
+            #endif
+        #endif
+    #endif
+#endif
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+typedef struct _Options
+{
+    const char *    pszInclude;
+    const char *    pszExclude;
+    BOOL            fExcludeAll;
+    const char *    pszObjectExt;
+    const char *    pszObjectDir;
+    BOOL            fObjectDir;         /* replace object directory? */
+    const char *    pszRsrcExt;
+    BOOL            fObjRule;
+    BOOL            fNoObjectPath;
+    BOOL            fSrcWhenObj;
+    BOOL            fAppend;            /* append to the output file, not overwrite it. */
+    BOOL            fCheckCyclic;       /* allways check for cylic dependency before inserting an dependent. */
+    BOOL            fCacheSearchDirs;   /* cache entire search dirs. */
+    const char *    pszExcludeFiles;    /* List of excluded files. */
+    BOOL            fForceScan;         /* Force scan of all files. */
+} OPTIONS, *POPTIONS;
+
+
+/*
+ * Language specific analysis functions type.
+ */
+typedef int ( _FNLANG)  (const char *pszFilename, const char *pszNormFilename,
+                         const char *pszTS, BOOL fHeader, void **ppvRule);
+typedef _FNLANG    *PFNLANG;
+
+
+/**
+ * This struct holds the static configuration of the util.
+ */
+typedef struct _ConfigEntry
+{
+    char         szId[16];              /* Config ID. */
+    const char **papszExts;             /* Pointer to an array of pointer to extentions for this handler. */
+                                        /* If NULL this is the last entry. */
+    int          iFirstHdr;             /* Index into the papszExts array of the first headerfile/copybook. */
+                                        /* Set it to the NULL element of the array if no headers for this extention. */
+                                        /* A non-header file may get a object rule. */
+    PFNLANG      pfn;                   /* Pointer to handler function. */
+    char        *pszzAddDeps;           /* Pointer to an string of string of additional dependencies. */
+} CONFIGENTRY, *PCONFIGENTRY;
+
+
+/**
+ * Dependant Rule
+ */
+typedef struct _DepRule
+{
+    AVLNODECORE     avlCore;
+    char *          pszRule;            /* Pointer to rule name */
+    int             cDeps;              /* Entries in the dependant array. */
+    char **         papszDep;           /* Pointer to an array of pointers to dependants. */
+    BOOL            fUpdated;           /* If we have updated this entry during the run. */
+    char            szTS[TS_SIZE];      /* Time stamp. */
+} DEPRULE, *PDEPRULE;
+
+
+/**
+ * Filename cache entry.
+ */
+#define FCACHEENTRY     AVLNODECORE
+#define PFCACHEENTRY    PAVLNODECORE
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+static void syntax(void);
+static int makeDependent(const char *pszFilename, const char *pszTS);
+
+static int langC_CPP(const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader, void **ppvRule);
+static int langAsm(  const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader, void **ppvRule);
+static int langRC(   const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader, void **ppvRule);
+static int langCOBOL(const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader, void **ppvRule);
+static int langIPF(  const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader, void **ppvRule);
+
+
+/* string operations */
+static int strnicmpwords(const char *pszS1, const char *pszS2, int cch);
+
+/* file operations */
+static char *fileNormalize(char *pszFilename);
+static char *fileNormalize2(const char *pszFilename, char *pszBuffer);
+       char *filePath(const char *pszFilename, char *pszBuffer);
+static char *filePathSlash(const char *pszFilename, char *pszBuffer);
+static char *filePathSlash2(const char *pszFilename, char *pszBuffer);
+static char *fileName(const char *pszFilename, char *pszBuffer);
+static char *fileNameNoExt(const char *pszFilename, char *pszBuffer);
+static char *fileExt(const char *pszFilename, char *pszBuffer);
+
+/* filecache operations */
+static BOOL filecacheAddFile(const char *pszFilename);
+static BOOL filecacheAddDir(const char *pszDir);
+INLINE BOOL filecacheFind(const char *pszFilename);
+INLINE BOOL filecacheIsDirCached(const char *pszDir);
+static char*filecacheFileExist(const char *pszFilename, char *pszBuffer);
+
+/* pathlist operations */
+static char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer);
+static BOOL  pathlistFindFile2(const char *pszPathList, const char *pszFilename);
+
+/* word operations */
+static char *findEndOfWord(char *psz);
+#if 0 /* not used */
+static char *findStartOfWord(char *psz, const char *pszStart);
+#endif
+
+/* file helpers */
+static signed long fsize(FILE *phFile);
+
+/* text helpers */
+INLINE char *trim(char *psz);
+INLINE char *trimR(char *psz);
+INLINE char *trimQuotes(char *psz);
+
+/* preprocessors */
+static char *PreProcessLine(char *pszOut, const char *pszIn);
+
+/* textbuffer */
+static void *textbufferCreate(const char *pszFilename);
+static void  textbufferDestroy(void *pvBuffer);
+static char *textbufferNextLine(void *pvBuffer, char *psz);
+static char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer);
+
+/* depend workers */
+static BOOL  depReadFile(const char *pszFilename, BOOL fAppend);
+static BOOL  depWriteFile(const char *pszFilename, BOOL fWriteUpdatedOnly);
+static void  depRemoveAll(void);
+static void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt, const char *pszTS, BOOL fConvertName);
+static BOOL  depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic, BOOL fConvertName);
+static int   depNameToReal(char *pszName);
+static int   depNameToMake(char *pszName, int cchName, const char *pszSrc);
+static void  depMarkNotFound(void *pvRule);
+static BOOL  depCheckCyclic(PDEPRULE pdepRule, const char *pszDep);
+static BOOL  depValidate(PDEPRULE pdepRule);
+INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3);
+static void  depAddSrcAddDeps(void *pvRule, const char *pszz);
+
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/*
+ * Pointer to the list of dependencies.
+ */
+static PDEPRULE pdepTree = NULL;
+
+
+/*
+ * Filecache - tree starts here.
+ */
+static PFCACHEENTRY pfcTree = NULL;
+static unsigned     cfcNodes = 0;
+static PFCACHEENTRY pfcDirTree = NULL;
+
+
+/*
+ * Current directory stuff
+ */
+static char     szCurDir[CCHMAXPATH];
+static int      aiSlashes[CCHMAXPATH];
+static int      cSlashes;
+
+
+/*
+ * Environment variables used.
+ * (These has the correct case.)
+ */
+static char *   pszIncludeEnv;
+
+
+/*
+ * Configuration stuff.
+ */
+static const char pszDefaultDepFile[] = ".depend";
+static const char *apszExtC_CPP[] = {"c", "sqc", "cpp", "h", "hpp", NULL};
+static const char *apszExtAsm[]   = {"asm", "inc", NULL};
+static const char *apszExtRC[]    = {"rc",  "dlg", NULL};
+static const char *apszExtORC[]   = {"orc", "dlg", NULL};
+static const char *apszExtCOBOL[] = {"cbl", "cob", "sqb", "wbl", NULL};
+static const char *apszExtIPF[]   = {"ipf", "man", NULL};
+static const char *apszExtIPP[]   = {"ipp", NULL};
+static CONFIGENTRY aConfig[] =
+{
+    {
+        "CX",
+        apszExtC_CPP,
+        3,
+        langC_CPP,
+        NULL,
+    },
+
+    {
+        "AS",
+        apszExtAsm,
+        1,
+        langAsm,
+        NULL,
+    },
+
+    {
+        "RC",
+        apszExtRC,
+        1,
+        langRC,
+        NULL,
+    },
+
+    {
+        "ORC",
+        apszExtORC,
+        1,
+        langRC,
+        NULL,
+    },
+
+    {
+        "COB",
+        apszExtCOBOL,
+        -1,
+        langCOBOL,
+        NULL,
+    },
+
+    {
+        "IPF",
+        apszExtIPF,
+        -1,
+        langIPF,
+        NULL,
+    },
+
+    {
+        "IPP",
+        apszExtIPP,
+        -1,
+        langC_CPP,
+        NULL,
+    },
+
+    /* terminating entry */
+    {
+        "",
+        NULL,
+        -1,
+        NULL,
+        NULL
+    }
+};
+
+
+static char szObjectDir[CCHMAXPATH];
+static char szObjectExt[64] = "obj";
+static char szRsrcExt[64]   = "res";
+static char szInclude[32768] = ";";
+static char szExclude[32768] = ";";
+static char szExcludeFiles[65536] = "";
+
+OPTIONS options =
+{
+    szInclude,       /* pszInclude */
+    szExclude,       /* pszExclude */
+    FALSE,           /* fExcludeAll */
+    szObjectExt,     /* pszObjectExt */
+    szObjectDir,     /* pszObjectDir */
+    FALSE,           /* fObjectDir */
+    szRsrcExt,       /* pszRsrcExt */
+    TRUE,            /* fObjRule */
+    FALSE,           /* fNoObjectPath */
+    TRUE,            /* fSrcWhenObj */
+    FALSE,           /* fAppend */
+    TRUE,            /* fCheckCyclic */
+    TRUE,            /* fCacheSearchDirs */
+    szExcludeFiles,  /* pszExcludeFiles */
+    FALSE            /* fForceScan */
+};
+
+
+/**
+ * Main function.
+ * @returns   0 on success.
+ *           -n count of failiures.
+ * @param
+ * @param
+ * @equiv
+ * @precond
+ * @methdesc
+ * @result
+ * @time
+ * @sketch
+ * @algo
+ * @remark
+ */
+int main(int argc, char **argv)
+{
+    int         rc   = 0;
+    int         argi = 1;
+    int         i;
+    char *      psz;
+    char *      psz2;
+    const char *pszDepFile = pszDefaultDepFile;
+    char        achBuffer[4096];
+
+    szObjectDir[0] = '\0';
+
+    if (argc == 1)
+    {
+        syntax();
+        return -87;
+    }
+
+    /*
+     * Initiate current directory stuff
+     */
+    if (_getcwd(szCurDir, sizeof(szCurDir)) == NULL)
+    {
+        fprintf(stderr, "fatal error: failed to get current directory\n");
+        return -88;
+    }
+    strlwr(szCurDir);
+    aiSlashes[0] = 0;
+    for (i = 1, cSlashes; szCurDir[i] != '\0'; i++)
+    {
+        if (szCurDir[i] == '/')
+            szCurDir[i] = '\\';
+        if (szCurDir[i] == '\\')
+            aiSlashes[cSlashes++] = i;
+    }
+    if (szCurDir[i-1] != '\\')
+    {
+        aiSlashes[cSlashes] = i;
+        szCurDir[i++] = '\\';
+        szCurDir[i] = '\0';
+    }
+
+
+    /*
+     * Initiate environment variables used: INCLUDE
+     */
+    psz = getenv("INCLUDE");
+    if (psz != NULL)
+    {
+        pszIncludeEnv = strdup(psz);
+        strlwr(pszIncludeEnv);
+    }
+    else
+        pszIncludeEnv = "";
+
+
+    /*
+     * Disable hard errors.
+     */
+    DosError(FERR_DISABLEHARDERR | FERR_ENABLEEXCEPTION);
+
+
+    /*
+     * parse arguments
+     */
+    while (argi < argc)
+    {
+        if (argv[argi][0] == '-' || argv[argi][0] == '/')
+        {
+            /* parameters */
+            switch (argv[argi][1])
+            {
+                case 'A':
+                case 'a': /* Append to the output file */
+                    options.fAppend = argv[argi][2] != '-';
+                    break;
+
+                case 'D':
+                case 'd': /* "-d <filename>" */
+                {
+                    const char *pszOld = pszDepFile;
+                    if (argv[argi][2] != '\0')
+                        pszDepFile = &argv[argi][2];
+                    else
+                    {
+                        argi++;
+                        if (argi < argc)
+                            pszDepFile = argv[argi];
+                        else
+                        {
+                            fprintf(stderr, "invalid parameter -d, filename missing!\n");
+                            return -1;
+                        }
+                    }
+
+                    /* if dependencies are generated we'll flush them to the old filename */
+                    if (pdepTree != NULL && pszOld != pszDepFile)
+                    {
+                        if (!depWriteFile(pszOld, !options.fAppend))
+                            fprintf(stderr, "error: failed to write (flush) dependencies.\n");
+                        depRemoveAll();
+                    }
+                    break;
+                }
+
+                case 'C': /* forced directory cache  'ca' or cylic check 'cy'*/
+                case 'c':
+                    if (argv[argi][2] == 'a' || argv[argi][2] == 'A')
+                        options.fCacheSearchDirs = TRUE;
+                    else if ((argv[argi][2] == 'y' || argv[argi][2] == 'Y'))
+                        options.fCheckCyclic = argv[argi][3] != '-';
+                    break;
+
+                case 'E': /* list of paths. If a file is found in one of these directories the */
+                case 'e': /* filename will be used without the directory path. */
+                    /* Eall<[+]|-> ? */
+                    if (strlen(&argv[argi][1]) <= 5 && strnicmp(&argv[argi][1], "Eall", 4) == 0)
+                    {
+                        options.fExcludeAll = argv[argi][5] != '-';
+                        break;
+                    }
+                    /* path or path list */
+                    if (strlen(argv[argi]) > 2)
+                        psz = &argv[argi][2];
+                    else
+                    {
+                        if (++argi >= argc)
+                        {
+                            fprintf(stderr, "syntax error! Option -e.\n");
+                            return 1;
+                        }
+                        psz = argv[argi];
+                    }
+                    /* check if enviroment variable */
+                    if (*psz == '%')
+                    {
+                        psz2 = strdup(psz+1);
+                        if (psz2 != NULL && *psz2 != '\0')
+                        {
+                            if (psz2[strlen(psz2)-1] == '%')
+                                psz2[strlen(psz2)-1] = '\0';
+                            psz = getenv(psz2);
+                            free(psz2);
+                            if (psz == NULL)
+                                break;
+                        }
+                        else
+                        {
+                            fprintf(stderr, "error: -E% is not an valid argument!\n");
+                            return -1;
+                        }
+                    }
+                    if (psz != NULL)
+                    {
+                        strcat(szExclude, psz);
+                        strlwr(szExclude);
+                        if (szExclude[strlen(szExclude)-1] != ';')
+                            strcat(szExclude, ";");
+                    }
+                    break;
+
+                case 'f':
+                case 'F': /* force scan of all files. */
+                    options.fForceScan = argv[argi][2] != '-';
+                    break;
+
+                case 'I': /* optional include path. This has precedence over the INCLUDE environment variable. */
+                case 'i':
+                    if (strlen(argv[argi]) > 2)
+                        psz = &argv[argi][2];
+                    else
+                    {
+                        if (++argi >= argc)
+                        {
+                            fprintf(stderr, "syntax error! Option -i.\n");
+                            return 1;
+                        }
+                        psz = argv[argi];
+                    }
+                    /* check if enviroment variable */
+                    if (*psz == '%')
+                    {
+                        psz2 = strdup(psz+1);
+                        if (psz2 != NULL && *psz2 != '\0')
+                        {
+                            if (psz2[strlen(psz2)-1] == '%')
+                                psz2[strlen(psz2)-1] = '\0';
+                            psz = getenv(psz2);
+                            free(psz2);
+                            if (psz == NULL)
+                                break;
+                        }
+                        else
+                        {
+                            fprintf(stderr, "error: -I% is not an valid argument!\n");
+                            return -1;
+                        }
+                    }
+                    if (psz != NULL)
+                    {
+                        strcat(szInclude, psz);
+                        strlwr(szInclude);
+                        if (szInclude[strlen(szInclude)-1] != ';')
+                            strcat(szInclude, ";");
+                    }
+                    break;
+
+                case 'n': /* no object path , -N<[+]|-> */
+                case 'N':
+                    if (strlen(argv[argi]) <= 1+1+1)
+                        options.fNoObjectPath = argv[argi][2] != '-';
+                    else
+                    {
+                        fprintf(stderr, "error: invalid parameter!, '%s'\n", argv[argi]);
+                        return -1;
+                    }
+                    break;
+
+                case 'o': /* object base directory, Obj or Obr<[+]|-> */
+                case 'O':
+                    if (strlen(&argv[argi][1]) <= 4 && strnicmp(&argv[argi][1], "Obr", 3) == 0)
+                    {
+                        options.fObjRule = argv[argi][4] != '-';
+                        break;
+                    }
+
+                    if (strlen(&argv[argi][1]) >= 4 && strnicmp(&argv[argi][1], "Obj", 3) == 0)
+                    {
+                        if (strlen(argv[argi]) > 4)
+                            strcpy(szObjectExt, argv[argi]+4);
+                        else
+                        {
+                            if (++argi >= argc)
+                            {
+                                fprintf(stderr, "syntax error! Option -obj.\n");
+                                return 1;
+                            }
+                            strcpy(szObjectExt, argv[argi]);
+                        }
+                        break;
+                    }
+
+                    /* path: -o or -o- */
+                    options.fObjectDir = TRUE;
+                    if (strlen(argv[argi]) > 2)
+                    {
+                        if (argv[argi][2] == '-')  /* no object path */
+                            szObjectDir[0] = '\0';
+                        else
+                            strcpy(szObjectDir, argv[argi]+2);
+                    }
+                    else
+                    {
+                        if (++argi >= argc)
+                        {
+                            fprintf(stderr, "syntax error! Option -o.\n");
+                            return 1;
+                        }
+                        strcpy(szObjectDir, argv[argi]);
+                    }
+                    if (szObjectDir[0] != '\0'
+                        && szObjectDir[strlen(szObjectDir)-1] != '\\'
+                        && szObjectDir[strlen(szObjectDir)-1] != '/'
+                        )
+                        strcat(szObjectDir, "\\");
+                    break;
+
+                case 'r':
+                case 'R':
+                    if (strlen(argv[argi]) > 2)
+                        strcpy(szRsrcExt, argv[argi]+2);
+                    else
+                    {
+                        if (++argi >= argc)
+                        {
+                            fprintf(stderr, "syntax error! Option -r.\n");
+                            return 1;
+                        }
+                        strcpy(szRsrcExt, argv[argi]);
+                    }
+                    break;
+
+                case 's':
+                case 'S':
+                    if (!strnicmp(argv[argi]+1, "srcadd", 6))
+                    {
+                        if (strlen(argv[argi]) > 7)
+                            psz = argv[argi]+2;
+                        else
+                        {
+                            if (++argi >= argc)
+                            {
+                                fprintf(stderr, "syntax error! Option -srcadd.\n");
+                                return 1;
+                            }
+                            psz = argv[argi];
+                        }
+                        if (!(psz2 = strchr(psz, ':')))
+                        {
+                            fprintf(stderr, "syntax error! Option -srcadd malformed!\n");
+                            return 1;
+                        }
+                        for (i = 0; aConfig[i].pfn; i++)
+                        {
+                            if (    !strnicmp(aConfig[i].szId, psz, psz2 - psz)
+                                &&  !aConfig[i].szId[psz2 - psz])
+                            {
+                                int cch, cch2;
+                                if (!*++psz2)
+                                {
+                                    fprintf(stderr, "error: Option -srcadd no additioanl dependancy!\n",
+                                            psz2 - psz, psz);
+                                    return 1;
+                                }
+                                cch = 0;
+                                psz = aConfig[i].pszzAddDeps;
+                                if (psz)
+                                {
+                                    do
+                                    {
+                                        cch += (cch2 = strlen(psz)) + 1;
+                                        psz += cch2 + 1;
+                                    } while (*psz);
+                                }
+                                cch2 = strlen(psz2);
+                                aConfig[i].pszzAddDeps = realloc(aConfig[i].pszzAddDeps, cch + cch2 + 2);
+                                if (!aConfig[i].pszzAddDeps)
+                                {
+                                    fprintf(stderr, "error: Out of memory!\n");
+                                    return 1;
+                                }
+                                strcpy(aConfig[i].pszzAddDeps + cch, psz2);
+                                aConfig[i].pszzAddDeps[cch + cch2 + 1] = '\0';
+                                psz = NULL;
+                                break;
+                            }
+                        }
+                        if (psz)
+                        {
+                            fprintf(stderr, "error: Option -srcadd, invalid language id '%.*s%'\n",
+                                    psz2 - psz, psz);
+                            return 1;
+                        }
+                    }
+                    else
+                    {
+                        fprintf(stderr, "syntax error! Invalid option %s\n", argv[argi]);
+                        return 1;
+                    }
+                    break;
+
+                case 'x':
+                case 'X': /* Exclude files */
+                    psz = &achBuffer[CCHMAXPATH+8];
+                    if (strlen(argv[argi]) > 2)
+                        strcpy(psz, &argv[argi][2]);
+                    else
+                    {
+                        if (++argi >= argc)
+                        {
+                            fprintf(stderr, "syntax error! Option -x.\n");
+                            return 1;
+                        }
+                        strcpy(psz, argv[argi]);
+                    }
+                    while (psz != NULL && *psz != ';')
+                    {
+                        char *  pszNext = strchr(psz, ';');
+                        int     cch = strlen(szExcludeFiles);
+                        if (pszNext)
+                            *pszNext++ = '\0';
+                        if (DosQueryPathInfo(psz, FIL_QUERYFULLNAME, &szExcludeFiles[cch], CCHMAXPATH))
+                        {
+                            fprintf(stderr, "error: Invalid exclude name\n");
+                            return -1;
+                        }
+                        strlwr(&szExcludeFiles[cch]);
+                        strcat(&szExcludeFiles[cch], ";");
+                        psz = pszNext;
+                    }
+                    break;
+
+                case 'h':
+                case 'H':
+                case '?':
+                    syntax();
+                    return 1;
+
+                default:
+                    fprintf(stderr, "error: invalid parameter! '%s'\n", argv[argi]);
+                    return -1;
+            }
+
+        }
+        else if (argv[argi][0] == '@')
+        {   /*
+             * Parameter file (debugger parameter length restrictions led to this):
+             *    Create a textbuffer.
+             *    Parse the file and create a new parameter vector.
+             *    Set argv to the new parameter vector, argi to 0 and argc to
+             *    the parameter count.
+             *    Restrictions: Parameters enclosed in "" is not implemented.
+             *                  No commandline parameters are processed after the @file
+             */
+            char *pszBuffer = (char*)textbufferCreate(&argv[argi][1]); /* !ASSUMS! that pvBuffer is the file string! */
+            if (pszBuffer != NULL)
+            {
+                char **apszArgs = NULL;
+                char *psz = pszBuffer;
+                int  i = 0;
+
+                while (*psz != '\0')
+                {
+                    /* find end of parameter word */
+                    char *pszEnd = psz + 1;
+                    char  ch = *pszEnd;
+                    while (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r' && ch != '\0')
+                        ch = *++pszEnd;
+
+                    /* allocate more arg array space? */
+                    if ((i % 512) == 0)
+                    {
+                        apszArgs = realloc(apszArgs, sizeof(char*) * 512);
+                        if (apszArgs == NULL)
+                        {
+                            fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
+                            return -8;
+                        }
+                    }
+                    *pszEnd = '\0';
+                    apszArgs[i++] = psz;
+
+                    /* next */
+                    psz = pszEnd + 1;
+                    ch = *psz;
+                    while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
+                        ch = *++psz;
+                }
+
+                argc = i;
+                argi = 0;
+                argv = apszArgs;
+                continue;
+            }
+            else
+            {
+                fprintf(stderr, "error: could not open parameter file\n");
+                return -1;
+            }
+        }
+        else
+        {   /* not a parameter! */
+            ULONG           ulRc;
+            PFILEFINDBUF3   pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
+            HDIR            hDir = HDIR_CREATE;
+            ULONG           cFiles = ~0UL;
+            int             i;
+
+
+            /*
+             * If append option is or if the forcescan option isn't is
+             * we'll have to read the existing dep file before starting
+             * adding new dependencies.
+             */
+            if (pdepTree == NULL && (options.fAppend || !options.fForceScan))
+                depReadFile(pszDepFile, options.fAppend);
+
+            /*
+             * Search for the files specified.
+             */
+            ulRc = DosFindFirst(argv[argi], &hDir,
+                                FILE_READONLY |  FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
+                                pfindbuf3, sizeof(achBuffer), &cFiles, FIL_STANDARD);
+            if (!options.fCacheSearchDirs)
+                options.fCacheSearchDirs = cFiles > 25;
+            while (ulRc == NO_ERROR)
+            {
+                for (i = 0;
+                     i < cFiles;
+                     i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
+                     )
+                {
+                    const char *    psz;
+                    char            szSource[CCHMAXPATH];
+                    BOOL            fExcluded;
+                    char            szTS[TS_SIZE];
+
+                    /*
+                     * Make full path.
+                     */
+                    if ((psz = strrchr(argv[argi], '\\')) || (psz = strrchr(argv[argi], '/')) || (*(psz = &argv[argi][1]) == ':'))
+                    {
+                        strncpy(szSource, argv[argi], psz - argv[argi] + 1);
+                        szSource[psz - argv[argi] + 1]  = '\0';
+                    }
+                    else
+                        szSource[0]  = '\0';
+                    strcat(szSource, pfindbuf3->achName);
+                    strlwr(szSource);
+                    fileNormalize(szSource);
+
+                    /*
+                     * Check if this is an excluded file.
+                     */
+                    fExcluded = FALSE;
+                    psz = options.pszExcludeFiles;
+                    while (*psz != '\0' && *psz != ';')
+                    {
+                        const char * pszNext = strchr(psz, ';');
+                        if (strlen(szSource) == pszNext - psz && strncmp(szSource, psz, pszNext - psz) == 0)
+                            fExcluded = TRUE;
+                        psz = pszNext + 1;
+                    }
+                    if (fExcluded)
+                        continue;
+
+                    /*
+                     * Analyse the file.
+                     */
+                    depMakeTS(szTS, pfindbuf3);
+                    rc -= makeDependent(&szSource[0], szTS);
+                }
+
+                /* next file */
+                cFiles = ~0UL;
+                pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
+                ulRc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
+            }
+            DosFindClose(hDir);
+        }
+        /* next */
+        argi++;
+    }
+
+    /* Write the depend file! */
+    if (!depWriteFile(pszDepFile, !options.fAppend))
+        fprintf(stderr, "error: failed to write dependencies file!\n");
+    #if 0
+    printf("cfcNodes=%d\n", cfcNodes);
+    #endif
+
+    return rc;
+}
+
+
+/**
+ * Displays the syntax description for this util.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+void syntax(void)
+{
+    printf(
+        "FastDep v0.48 (build %d)\n"
+        "Dependency scanner. Creates a makefile readable depend file.\n"
+        " - was quick and dirty, now it's just quick -\n"
+        "\n"
+        "Syntax: FastDep [options] <files> [more options [more files [...]]]\n"
+        "    or\n"
+        "        FastDep [options] @<parameterfile>\n"
+        "\n"
+        "Options:\n"
+        "   -a<[+]|->       Append to the output file.            Default: Overwrite.\n"
+        "   -ca             Force search directory caching.\n"
+        "                   Default: cache if more that 25 files are to be searched.\n"
+        "                            (more than 25 in the first file expression.)\n"
+        "   -cy<[+]|->      Check for cylic dependencies.         Default: -cy-\n"
+        "   -d <outputfn>   Output filename.                      Default: %s\n"
+        "   -e excludepath  Exclude paths. If a filename is found in any\n"
+        "                   of these paths only the filename is used, not\n"
+        "                   the path+filename (which is default).\n"
+        "   -eall<[+]|->    Include and source filenames, paths or no paths.\n"
+        "                   -eall+: No path are added to the filename.\n"
+        "                   -eall-: The filename is appended the include path\n"
+        "                           was found in.\n"
+        "                   Default: eall-\n"
+        "   -f<[+]|->       Force scanning of all files. If disabled we'll only scan\n"
+        "                   files which are younger or up to one month older than the\n"
+        "                   dependancy file (if it exists).       Default: disabled\n"
+        "   -i <include>    Additional include paths. INCLUDE is searched after this.\n"
+        "   -n<[+]|->       No path for object files in the rules.\n"
+        "   -o <objdir>     Path were object files are placed. This path replaces the\n"
+        "                   entire filename path\n"
+        "   -o-             No object path\n"
+        "   -obr<[+]|->     -obr+: Object rule.\n"
+        "                   -obr-: No object rule, rule for source filename is generated.\n"
+        "   -obj[ ]<objext> Object extention.                     Default: obj\n"
+        "   -srcadd[ ]<langid>:<dep>\n"
+        "                   Additional dependants for source file of the given language\n"
+        "                   type. langid: AS,CX,RC,ORC,COB,IPF\n"
+        "                   This is very usfull for compiler configuration files.\n"
+        "   -r[ ]<rsrcext>  Resource binary extention.            Default: res\n"
+        "   -x[ ]<f1[;f2]>  Files to exclude. Only exact filenames.\n"
+        "   <files>         Files to scan. Wildchars are allowed.\n"
+        "\n"
+        "Options and files could be mixed.\n"
+        " copyright (c) 1999-2002 knut st. osmundsen (bird@anduin.net)\n",
+        ODIN32_BUILD_NR,
+        pszDefaultDepFile
+        );
+}
+
+
+/**
+ * Generates depend info on this file, these are stored internally
+ * and written to file later.
+ * @returns
+ * @param   pszFilename     Pointer to source filename. Correct case is assumed!
+ * @param   pszTS           File time stamp.
+ * @status  completely implemented.
+ * @author  knut st. osmundsen
+ */
+int makeDependent(const char *pszFilename, const char *pszTS)
+{
+    int    rc = -1;
+
+    char            szExt[CCHMAXPATH];
+    PCONFIGENTRY    pCfg = &aConfig[0];
+    BOOL            fHeader;
+
+    /*
+     * Find which filetype this is...
+     */
+    fileExt(pszFilename, szExt);
+    while (pCfg->papszExts != NULL)
+    {
+        const char **ppsz = pCfg->papszExts;
+        while (*ppsz != NULL && stricmp(*ppsz, szExt) != 0)
+            ppsz++;
+        if (*ppsz != NULL)
+        {
+            fHeader = pCfg->iFirstHdr > 0 && &pCfg->papszExts[pCfg->iFirstHdr] <= ppsz;
+            break;
+        }
+        pCfg++;
+    }
+
+    /* Found? */
+    if (pCfg->papszExts != NULL)
+    {
+        void *  pvRule = NULL;
+        char    szNormFile[CCHMAXPATH];
+        fileNormalize2(pszFilename, szNormFile);
+        rc = (*pCfg->pfn)(pszFilename, &szNormFile[0], pszTS, fHeader, &pvRule);
+        if (!rc && pvRule)
+        {
+            if (!fHeader && pCfg->pszzAddDeps)
+                depAddSrcAddDeps(pvRule, pCfg->pszzAddDeps);
+        }
+    }
+    else
+    {
+        if (*fileName(pszFilename, szExt) != '.') /* these are 'hidden' files, like .cvsignore, let's ignore them. */
+            fprintf(stderr, "warning: '%s' has an unknown file type.\n", pszFilename);
+        rc = 0;
+    }
+
+
+    return rc;
+}
+
+
+/**
+ * Generates depend info on this C or C++ file, these are stored internally
+ * and written to file later.
+ * @returns 0 on success.
+ *          !0 on error.
+ * @param   pszFilename         Pointer to source filename. Correct case is assumed!
+ * @param   pszNormFilename     Pointer to normalized source filename.
+ * @param   pszTS               File time stamp.
+ * @parma   fHeader             True if header file is being scanned.
+ * @param   ppvRule             Variabel to return any new rule handle.
+ * @status  completely implemented.
+ * @author  knut st. osmundsen
+ */
+int langC_CPP(const char *pszFilename, const char *pszNormFilename,
+              const char *pszTS, BOOL fHeader, void **ppvRule)
+{
+    void *  pvFile;                     /* Text buffer pointer. */
+    void *  pvRule;                     /* Handle to the current rule. */
+    char    szBuffer[4096];             /* Max line length is 4096... should not be a problem. */
+    int     iLine;                      /* Linenumber. */
+    void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
+    BOOL    fComment;                   /* TRUE when within a multiline comment. */
+                                        /* FALSE when not within a multiline comment. */
+    int     iIfStack;                   /* StackPointer. */
+    struct  IfStackEntry
+    {
+        int fIncluded : 1;              /* TRUE:  include this code;
+                                         * FALSE: excluded */
+        int fIf : 1;                    /* TRUE:  #if part of the expression.
+                                         * FALSE: #else part of the expression. */
+        int fSupported : 1;             /* TRUE:  supported if/else statement
+                                         * FALSE: unsupported all else[<something>] are ignored
+                                         *        All code is included.
+                                         */
+    } achIfStack[256];
+    char    szSrcDir[CCHMAXPATH];
+    filePath(pszNormFilename, szSrcDir);/* determin source code directory. */
+
+    /**********************************/
+    /* Add the depend rule            */
+    /**********************************/
+    if (options.fObjRule && !fHeader)
+    {
+        if (options.fNoObjectPath)
+            pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
+        else
+            pvRule = depAddRule(options.fObjectDir ?
+                                    options.pszObjectDir :
+                                    filePathSlash(pszFilename, szBuffer),
+                                fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
+                                options.pszObjectExt, pszTS, FALSE);
+
+        if (options.fSrcWhenObj && pvRule)
+            depAddDepend(pvRule,
+                         options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
+                            fileName(pszFilename, szBuffer) : pszNormFilename,
+                         options.fCheckCyclic,
+                         FALSE);
+    }
+    else
+        pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
+                            fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
+
+    /* duplicate rule? */
+    *ppvRule = pvRule;
+    if (pvRule == NULL)
+        return 0;
+
+
+    /********************/
+    /* Make file buffer */
+    /********************/
+    pvFile = textbufferCreate(pszFilename);
+    if (!pvFile)
+    {
+        fprintf(stderr, "failed to open '%s'\n", pszFilename);
+        return -1;
+    }
+
+
+    /*******************/
+    /* find dependants */
+    /*******************/
+    /* Initiate the IF-stack, comment state and line number. */
+    iIfStack = 0;
+    achIfStack[iIfStack].fIf = TRUE;
+    achIfStack[iIfStack].fIncluded = TRUE;
+    achIfStack[iIfStack].fSupported = TRUE;
+    fComment = FALSE;
+    iLine = 0;
+    while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
+    {
+        /* search for #include */
+        register char *pszC;
+        int cbLen;
+        int i = 0;
+        iLine++;
+
+        /* skip blank chars */
+        cbLen = strlen(szBuffer);
+        while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
+            i++;
+
+        /* preprocessor statement? */
+        if (!fComment && szBuffer[i] == '#')
+        {
+            /*
+             * Preprocessor checks
+             * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
+             * Depending on the word afterwards we'll take some different actions.
+             * So we'll start of by extracting that word and make a string swich on it.
+             * Note that there might be some blanks between the hash and the word.
+             */
+            int     cchWord;
+            char *  pszEndWord;
+            char *  pszArgument;
+            i++;                /* skip hash ('#') */
+            while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
+                i++;
+            pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
+            cchWord = pszEndWord - &szBuffer[i];
+
+            /*
+             * Find the argument by skipping the blanks.
+             */
+            while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
+                pszArgument++;
+
+            /*
+             * string switch.
+             */
+            if (strncmp(&szBuffer[i], "include", cchWord) == 0)
+            {
+                /*
+                 * #include
+                 *
+                 * Are we in a state where this file is to be included?
+                 */
+                if (achIfStack[iIfStack].fIncluded)
+                {
+                    char    szFullname[CCHMAXPATH];
+                    char *  psz;
+                    BOOL    f = FALSE;
+                    int     j;
+                    BOOL    fQuote;
+
+                    /* extract info between "" or <> */
+                    while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
+                        i++;
+                    fQuote = szBuffer[i] == '"';
+                    i++; /* skip '"' or '<' */
+
+                    /* if invalid statement then continue with the next line! */
+                    if (!f) continue;
+
+                    /* find end */
+                    j = f = 0;
+                    while (i + j < cbLen &&  j < CCHMAXPATH &&
+                           !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
+                        j++;
+
+                    /* if invalid statement then continue with the next line! */
+                    if (!f) continue;
+
+                    /* copy filename */
+                    strncpy(szFullname, &szBuffer[i], j);
+                    szFullname[j] = '\0'; /* ensure terminatition. */
+                    strlwr(szFullname);
+
+                    /* find include file! */
+                    psz = fQuote ? pathlistFindFile(szSrcDir, szFullname, szBuffer) : NULL;
+                    if (psz == NULL)
+                        psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
+                    if (psz == NULL)
+                        psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
+
+                    /* did we find the include? */
+                    if (psz != NULL)
+                    {
+                        if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
+                        {   /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
+                            if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
+                                depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
+                            else
+                                fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
+                                        pszFilename, iLine, szFullname);
+                        }
+                        else
+                            depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
+                    }
+                    else
+                    {
+                        fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
+                                pszFilename, iLine, szFullname);
+                        depMarkNotFound(pvRule);
+                    }
+                }
+            }
+            else
+                /*
+                 * #if
+                 */
+                if (strncmp(&szBuffer[i], "if", cchWord) == 0)
+            {   /* #if 0 and #if <1-9> are supported */
+                pszEndWord = findEndOfWord(pszArgument);
+                iIfStack++;
+                if ((pszEndWord - pszArgument) == 1
+                    && *pszArgument >= '0' && *pszArgument <= '9')
+                {
+                    if (*pszArgument != '0')
+                        achIfStack[iIfStack].fIncluded =  TRUE;
+                    else
+                        achIfStack[iIfStack].fIncluded =  FALSE;
+                }
+                else
+                    achIfStack[iIfStack].fSupported = FALSE;
+                achIfStack[iIfStack].fIncluded = TRUE;
+                achIfStack[iIfStack].fIf = TRUE;
+            }
+            else
+                /*
+                 * #else
+                 */
+                if (strncmp(&szBuffer[i], "else", cchWord) == 0)
+            {
+                if (achIfStack[iIfStack].fSupported)
+                {
+                    if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
+                        achIfStack[iIfStack].fIncluded = FALSE;
+                    else
+                        achIfStack[iIfStack].fIncluded = TRUE;
+                }
+                achIfStack[iIfStack].fIf = FALSE;
+            }
+            else
+                /*
+                 * #endif
+                 */
+                if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
+            {   /* Pop the if-stack. */
+                if (iIfStack > 0)
+                    iIfStack--;
+                else
+                    fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
+            }
+            /*
+             * general if<something> and elseif<something> implementations
+             */
+            else
+                if (strncmp(&szBuffer[i], "elseif", 6) == 0)
+            {
+                achIfStack[iIfStack].fSupported = FALSE;
+                achIfStack[iIfStack].fIncluded = TRUE;
+            }
+            else
+                if (strncmp(&szBuffer[i], "if", 2) == 0)
+            {
+                iIfStack++;
+                achIfStack[iIfStack].fIf = TRUE;
+                achIfStack[iIfStack].fSupported = FALSE;
+                achIfStack[iIfStack].fIncluded = TRUE;
+            }
+            /* The rest of them aren't implemented yet.
+            else if (strncmp(&szBuffer[i], "if") == 0)
+            {
+            }
+            */
+        }
+
+        /*
+         * Comment checks.
+         *  -Start at first non-blank.
+         *  -Loop thru the line since we might have more than one
+         *   comment statement on a single line.
+         */
+        pszC = &szBuffer[i];
+        while (pszC != NULL && *pszC != '\0')
+        {
+            if (fComment)
+                pszC = strstr(pszC, "*/");  /* look for end comment mark. */
+            else
+            {
+                char *pszLC;
+                pszLC= strstr(pszC, "//");  /* look for single line comment mark. */
+                pszC = strstr(pszC, "/*");  /* look for start comment mark */
+                if (pszLC && pszLC < pszC)  /* if there is an single line comment mark before the */
+                    break;                  /* muliline comment mark we'll ignore the multiline mark. */
+            }
+
+            /* Comment mark found? */
+            if (pszC != NULL)
+            {
+                fComment = !fComment;
+                pszC += 2;          /* skip comment mark */
+
+                /* debug */
+                /*
+                if (fComment)
+                    fprintf(stderr, "starts at line %d\n", iLine);
+                else
+                    fprintf(stderr, "ends   at line %d\n", iLine);
+                    */
+            }
+        }
+    } /*while*/
+
+    textbufferDestroy(pvFile);
+
+    return 0;
+}
+
+
+/**
+ * Generates depend info on this file, these are stored internally
+ * and written to file later.
+ * @returns 0 on success.
+ *          !0 on error.
+ * @param   pszFilename         Pointer to source filename. Correct case is assumed!
+ * @param   pszNormFilename     Pointer to normalized source filename.
+ * @param   pszTS               File time stamp.
+ * @parma   fHeader             True if header file is being scanned.
+ * @param   ppvRule             Variabel to return any new rule handle.
+ * @status  completely implemented.
+ * @author  knut st. osmundsen
+ */
+int langAsm(const char *pszFilename, const char *pszNormFilename,
+            const char *pszTS, BOOL fHeader, void **ppvRule)
+{
+    void *  pvFile;                     /* Text buffer pointer. */
+    void *  pvRule;                     /* Handle to the current rule. */
+    char    szBuffer[4096];             /* Temporary buffer (max line lenght size...) */
+    int     iLine;                      /* current line number */
+    void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
+
+
+    /**********************************/
+    /* Add the depend rule            */
+    /**********************************/
+    if (options.fObjRule && !fHeader)
+    {
+        if (options.fNoObjectPath)
+            pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
+        else
+            pvRule = depAddRule(options.fObjectDir ?
+                                    options.pszObjectDir :
+                                    filePathSlash(pszFilename, szBuffer),
+                                fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
+                                options.pszObjectExt, pszTS, FALSE);
+
+        if (options.fSrcWhenObj && pvRule)
+            depAddDepend(pvRule,
+                         options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
+                            fileName(pszFilename, szBuffer) : pszNormFilename,
+                         options.fCheckCyclic, FALSE);
+    }
+    else
+        pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
+                            fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
+
+    /* duplicate rule? */
+    *ppvRule = pvRule;
+    if (pvRule == NULL)
+        return 0;
+
+
+    /********************/
+    /* Make file buffer */
+    /********************/
+    pvFile = textbufferCreate(pszFilename);
+    if (!pvFile)
+    {
+        fprintf(stderr, "failed to open '%s'\n", pszFilename);
+        return -1;
+    }
+
+
+    /*******************/
+    /* find dependants */
+    /*******************/
+    iLine = 0;
+    while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
+    {
+        /* search for include */
+        int cbLen;
+        int i = 0;
+        iLine++;
+
+        /* skip blank chars */
+        cbLen = strlen(szBuffer);
+        while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
+            i++;
+
+        /* is this an include? */
+        if (strnicmp(&szBuffer[i], "include", 7) == 0
+            && (szBuffer[i + 7] == '\t' || szBuffer[i + 7] == ' ')
+            )
+        {
+            char szFullname[CCHMAXPATH];
+            char *psz;
+            int  j;
+
+            /* skip to first no blank char  */
+            i += 7;
+            while (i < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
+                i++;
+
+            /* comment check - if comment found, no filename was given. continue. */
+            if (szBuffer[i] == ';') continue;
+
+            /* find end */
+            j = 0;
+            while (i + j < cbLen
+                   &&  j < CCHMAXPATH
+                   && szBuffer[i+j] != ' '  && szBuffer[i+j] != '\t' && szBuffer[i+j] != '\n'
+                   && szBuffer[i+j] != '\0' && szBuffer[i+j] != ';'  && szBuffer[i+j] != '\r'
+                   )
+                j++;
+
+            /* copy filename */
+            strncpy(szFullname, &szBuffer[i], j);
+            szFullname[j] = '\0'; /* ensure terminatition. */
+            strlwr(szFullname);
+
+            /* find include file! */
+            psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
+            if (psz == NULL)
+                psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
+
+            /* Did we find the include? */
+            if (psz != NULL)
+            {
+                if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
+                {   /* include sys/stats.inc makes trouble, check for '/' and '\'. */
+                    if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
+                        depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
+                    else
+                        fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
+                                pszFilename, iLine, szFullname);
+                }
+                else
+                    depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
+            }
+            else
+            {
+                fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
+                        pszFilename, iLine, szFullname);
+                depMarkNotFound(pvRule);
+            }
+        }
+    } /*while*/
+
+    textbufferDestroy(pvFile);
+
+    return 0;
+}
+
+
+/**
+ * Generates depend info on this Resource file, these are stored internally
+ * and written to file later.
+ * @returns 0 on success.
+ *          !0 on error.
+ * @param   pszFilename         Pointer to source filename. Correct case is assumed!
+ * @param   pszNormFilename     Pointer to normalized source filename.
+ * @param   pszTS               File time stamp.
+ * @parma   fHeader             True if header file is being scanned.
+ * @status  completely implemented.
+ * @author  knut st. osmundsen
+ */
+#if 0
+int langRC(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader)
+{
+    void *  pvFile;                     /* Text buffer pointer. */
+    void *  pvRule;                     /* Handle to the current rule. */
+    char    szBuffer[4096];             /* Temporary buffer (max line lenght size...) */
+    int     iLine;                      /* current line number */
+    void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
+
+
+    /**********************************/
+    /* Add the depend rule            */
+    /**********************************/
+    if (options.fObjRule && !fHeader)
+    {
+        if (options.fNoObjectPath)
+            pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszRsrcExt, pszTS, FALSE);
+        else
+            pvRule = depAddRule(options.fObjectDir ?
+                                    options.pszObjectDir :
+                                    filePathSlash(pszFilename, szBuffer),
+                                fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
+                                options.pszRsrcExt, pszTS, FALSE);
+
+        if (options.fSrcWhenObj && pvRule)
+            depAddDepend(pvRule,
+                         options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
+                            fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
+                         options.fCheckCyclic,
+                         FALSE);
+    }
+    else
+        pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
+                            fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
+
+    /* duplicate rule? */
+    *ppvRule = pvRule;
+    if (pvRule == NULL)
+        return 0;
+
+
+    /********************/
+    /* Make file buffer */
+    /********************/
+    pvFile = textbufferCreate(pszFilename);
+    if (!pvFile)
+    {
+        fprintf(stderr, "failed to open '%s'\n", pszFilename);
+        return -1;
+    }
+
+
+    /*******************/
+    /* find dependants */
+    /*******************/
+    iLine = 0;
+    while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
+    {
+        /* search for #include */
+        int cbLen;
+        int i = 0;
+        int i1;
+        iLine++;
+
+        /* skip blank chars */
+        cbLen = strlen(szBuffer);
+        while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
+            i++;
+
+        /* is this an include? */
+        i1 = 1;
+        if (   strncmp(&szBuffer[i], "#include", 8) == 0
+            || (i1 = strnicmp(&szBuffer[i], "RCINCLUDE", 9)) == 0
+            || strnicmp(&szBuffer[i], "DLGINCLUDE", 10) == 0
+            )
+        {
+            char szFullname[CCHMAXPATH];
+            char *psz;
+            BOOL f = FALSE;
+            int  j;
+
+            if (i1 != 0)
+            {   /*
+                 * #include <file.h>,  #include "file.h" or DLGINCLUDE 1 "file.h"
+                 *
+                 * extract info between "" or <>
+                 */
+                while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
+                    i++;
+                i++; /* skip '"' or '<' */
+
+                /* if invalid statement then continue with the next line! */
+                if (!f) continue;
+
+                /* find end */
+                j = f = 0;
+                while (i + j < cbLen &&  j < CCHMAXPATH &&
+                       !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
+                    j++;
+
+                /* if invalid statement then continue with the next line! */
+                if (!f) continue;
+            }
+            else
+            {   /*
+                 * RCINCLUDE ["]filename.dlg["]
+                 * Extract filename.
+                 */
+
+                /* skip to filename.dlg start - if eol will continue to loop. */
+                i += 9;
+                while (szBuffer[i] == ' ' || szBuffer[i] == '\t' || szBuffer[i] == '"')
+                    i++;
+                if (szBuffer[i] == '\0')
+                    continue;
+
+                /* search to end of filename. */
+                j = i+1;
+                while (   szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
+                       && szBuffer[i+j] != '"' && szBuffer[i+j] != '\0')
+                    j++;
+            }
+
+            /* copy filename */
+            strncpy(szFullname, &szBuffer[i], j);
+            szFullname[j] = '\0'; /* ensure terminatition. */
+            strlwr(szFullname);
+
+            /* find include file! */
+            psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
+            if (psz == NULL)
+                psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
+
+            /* did we find the include? */
+            if (psz != NULL)
+            {
+                if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
+                {   /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
+                    if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
+                        depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
+                    else
+                        fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
+                                pszFilename, iLine, szFullname);
+                }
+                else
+                    depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
+            }
+            else
+            {
+                fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
+                        pszFilename, iLine, szFullname);
+                depMarkNotFound(pvRule);
+            }
+        }
+    } /*while*/
+
+    textbufferDestroy(pvFile);
+    return 0;
+}
+#else
+int langRC(const char *pszFilename, const char *pszNormFilename,
+           const char *pszTS, BOOL fHeader, void **ppvRule)
+{
+    void *  pvFile;                     /* Text buffer pointer. */
+    void *  pvRule;                     /* Handle to the current rule. */
+    char    szBuffer[4096];             /* Max line length is 4096... should not be a problem. */
+    int     iLine;                      /* Linenumber. */
+    void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
+    BOOL    fComment;                   /* TRUE when within a multiline comment. */
+                                        /* FALSE when not within a multiline comment. */
+    int     iIfStack;                   /* StackPointer. */
+    struct  IfStackEntry
+    {
+        int fIncluded : 1;              /* TRUE:  include this code;
+                                         * FALSE: excluded */
+        int fIf : 1;                    /* TRUE:  #if part of the expression.
+                                         * FALSE: #else part of the expression. */
+        int fSupported : 1;             /* TRUE:  supported if/else statement
+                                         * FALSE: unsupported all else[<something>] are ignored
+                                         *        All code is included.
+                                         */
+    } achIfStack[256];
+
+
+    /**********************************/
+    /* Add the depend rule            */
+    /**********************************/
+    if (options.fObjRule && !fHeader)
+    {
+        if (options.fNoObjectPath)
+            pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszRsrcExt, pszTS, FALSE);
+        else
+            pvRule = depAddRule(options.fObjectDir ?
+                                    options.pszObjectDir :
+                                    filePathSlash(pszFilename, szBuffer),
+                                fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
+                                options.pszRsrcExt, pszTS, FALSE);
+
+        if (options.fSrcWhenObj && pvRule)
+            depAddDepend(pvRule,
+                         options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
+                            fileName(pszFilename, szBuffer) : pszNormFilename,
+                         options.fCheckCyclic,
+                         FALSE);
+    }
+    else
+        pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
+                            fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
+
+    /* duplicate rule? */
+    *ppvRule = pvRule;
+    if (pvRule == NULL)
+        return 0;
+
+
+    /********************/
+    /* Make file buffer */
+    /********************/
+    pvFile = textbufferCreate(pszFilename);
+    if (!pvFile)
+    {
+        fprintf(stderr, "failed to open '%s'\n", pszFilename);
+        return -1;
+    }
+
+
+    /*******************/
+    /* find dependants */
+    /*******************/
+    /* Initiate the IF-stack, comment state and line number. */
+    iIfStack = 0;
+    achIfStack[iIfStack].fIf = TRUE;
+    achIfStack[iIfStack].fIncluded = TRUE;
+    achIfStack[iIfStack].fSupported = TRUE;
+    fComment = FALSE;
+    iLine = 0;
+    while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
+    {
+        register char * pszC;
+        char            szFullname[CCHMAXPATH];
+        int             cbLen;
+        int             i1 = 1;
+        int             i = 0;
+        iLine++;
+
+        /* skip blank chars */
+        cbLen = strlen(szBuffer);
+        while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
+            i++;
+
+        /* preprocessor statement? */
+        if (!fComment && szBuffer[i] == '#')
+        {
+            /*
+             * Preprocessor checks
+             * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
+             * Depending on the word afterwards we'll take some different actions.
+             * So we'll start of by extracting that word and make a string swich on it.
+             * Note that there might be some blanks between the hash and the word.
+             */
+            int     cchWord;
+            char *  pszEndWord;
+            char *  pszArgument;
+            i++;                /* skip hash ('#') */
+            while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
+                i++;
+            pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
+            cchWord = pszEndWord - &szBuffer[i];
+
+            /*
+             * Find the argument by skipping the blanks.
+             */
+            while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
+                pszArgument++;
+
+            /*
+             * string switch.
+             */
+            if (strncmp(&szBuffer[i], "include", cchWord) == 0)
+            {
+                /*
+                 * #include
+                 *
+                 * Are we in a state where this file is to be included?
+                 */
+                if (achIfStack[iIfStack].fIncluded)
+                {
+                    char *psz;
+                    BOOL f = FALSE;
+                    int  j;
+
+                    /* extract info between "" or <> */
+                    while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
+                        i++;
+                    i++; /* skip '"' or '<' */
+
+                    /* if invalid statement then continue with the next line! */
+                    if (!f) continue;
+
+                    /* find end */
+                    j = f = 0;
+                    while (i + j < cbLen &&  j < CCHMAXPATH &&
+                           !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
+                        j++;
+
+                    /* if invalid statement then continue with the next line! */
+                    if (!f) continue;
+
+                    /* copy filename */
+                    strncpy(szFullname, &szBuffer[i], j);
+                    szFullname[j] = '\0'; /* ensure terminatition. */
+                    strlwr(szFullname);
+
+                    /* find include file! */
+                    psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
+                    if (psz == NULL)
+                        psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
+
+                    /* did we find the include? */
+                    if (psz != NULL)
+                    {
+                        if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
+                        {   /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
+                            if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
+                                depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
+                            else
+                                fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
+                                        pszFilename, iLine, szFullname);
+                        }
+                        else
+                            depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
+                    }
+                    else
+                    {
+                        fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
+                                pszFilename, iLine, szFullname);
+                        depMarkNotFound(pvRule);
+                    }
+                }
+            }
+            else
+                /*
+                 * #if
+                 */
+                if (strncmp(&szBuffer[i], "if", cchWord) == 0)
+            {   /* #if 0 and #if <1-9> are supported */
+                pszEndWord = findEndOfWord(pszArgument);
+                iIfStack++;
+                if ((pszEndWord - pszArgument) == 1
+                    && *pszArgument >= '0' && *pszArgument <= '9')
+                {
+                    if (*pszArgument != '0')
+                        achIfStack[iIfStack].fIncluded =  TRUE;
+                    else
+                        achIfStack[iIfStack].fIncluded =  FALSE;
+                }
+                else
+                    achIfStack[iIfStack].fSupported = FALSE;
+                achIfStack[iIfStack].fIncluded = TRUE;
+                achIfStack[iIfStack].fIf = TRUE;
+            }
+            else
+                /*
+                 * #else
+                 */
+                if (strncmp(&szBuffer[i], "else", cchWord) == 0)
+            {
+                if (achIfStack[iIfStack].fSupported)
+                {
+                    if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
+                        achIfStack[iIfStack].fIncluded = FALSE;
+                    else
+                        achIfStack[iIfStack].fIncluded = TRUE;
+                }
+                achIfStack[iIfStack].fIf = FALSE;
+            }
+            else
+                /*
+                 * #endif
+                 */
+                if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
+            {   /* Pop the if-stack. */
+                if (iIfStack > 0)
+                    iIfStack--;
+                else
+                    fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
+            }
+            /*
+             * general if<something> and elseif<something> implementations
+             */
+            else
+                if (strncmp(&szBuffer[i], "elseif", 6) == 0)
+            {
+                achIfStack[iIfStack].fSupported = FALSE;
+                achIfStack[iIfStack].fIncluded = TRUE;
+            }
+            else
+                if (strncmp(&szBuffer[i], "if", 2) == 0)
+            {
+                iIfStack++;
+                achIfStack[iIfStack].fIf = TRUE;
+                achIfStack[iIfStack].fSupported = FALSE;
+                achIfStack[iIfStack].fIncluded = TRUE;
+            }
+            /* The rest of them aren't implemented yet.
+            else if (strncmp(&szBuffer[i], "if") == 0)
+            {
+            }
+            */
+        } else
+            /*
+             * Check for resource compiler directives.
+             */
+            if (    !fComment
+                &&  !strchr(&szBuffer[i], ',')
+                &&  (   !strnicmp(&szBuffer[i], "ICON", 4)
+                     || !strnicmp(&szBuffer[i], "FONT", 4)
+                     || !strnicmp(&szBuffer[i], "BITMAP", 6)
+                     || !strnicmp(&szBuffer[i], "POINTER", 7)
+                     || !strnicmp(&szBuffer[i], "RESOURCE", 8)
+                     || !(i1 = strnicmp(&szBuffer[i], "RCINCLUDE", 9))
+                   /*|| !strnicmp(&szBuffer[i], "DLGINCLUDE", 10) - only used by the dlgeditor */
+                     || !strnicmp(&szBuffer[i], "DEFAULTICON", 11)
+                     )
+                )
+        {
+            /*
+             * RESOURCE 123 1 ["]filename.ext["]
+             */
+            char    szLine[1024];
+            char *  pszFile;
+            char    chQuote = ' ';
+
+            PreProcessLine(szLine, &szBuffer[i]);
+
+            pszFile = &szLine[strlen(szLine)-1];
+            if (*pszFile == '\"' || *pszFile == '\'')
+            {
+                chQuote = *pszFile;
+                *pszFile-- = '\0';
+            }
+            while (*pszFile != chQuote)
+                pszFile--;
+            *pszFile++ = '\0'; /* We now have extracted the filename - pszFile. */
+            strlwr(pszFile);
+
+            /* Add filename to the dependencies. */
+            if (i1)
+                depAddDepend(pvRule, pszFile, options.fCheckCyclic, FALSE);
+            else
+            {
+                char *psz;
+                /* find include file! */
+                psz = pathlistFindFile(options.pszInclude, pszFile, szFullname);
+                if (psz == NULL)
+                    psz = pathlistFindFile(pszIncludeEnv, pszFile, szFullname);
+
+                /* did we find the include? */
+                if (psz != NULL)
+                {
+                    if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szFullname))
+                    {   /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
+                        if (!strchr(pszFile, '/') && !strchr(pszFile, '\\'))
+                            depAddDepend(pvRule, pszFile, options.fCheckCyclic, FALSE);
+                        else
+                            fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
+                                    pszFilename, iLine, pszFile);
+                    }
+                    else
+                        depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
+                }
+                else
+                {
+                    fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
+                            pszFilename, iLine, pszFile);
+                    depMarkNotFound(pvRule);
+                }
+            }
+        }
+
+
+        /*
+         * Comment checks.
+         *  -Start at first non-blank.
+         *  -Loop thru the line since we might have more than one
+         *   comment statement on a single line.
+         */
+        pszC = &szBuffer[i];
+        while (pszC != NULL && *pszC != '\0')
+        {
+            if (fComment)
+                pszC = strstr(pszC, "*/");  /* look for end comment mark. */
+            else
+            {
+                char *pszLC;
+                pszLC= strstr(pszC, "//");  /* look for single line comment mark. */
+                pszC = strstr(pszC, "/*");  /* look for start comment mark */
+                if (pszLC && pszLC < pszC)  /* if there is an single line comment mark before the */
+                    break;                  /* muliline comment mark we'll ignore the multiline mark. */
+            }
+
+            /* Comment mark found? */
+            if (pszC != NULL)
+            {
+                fComment = !fComment;
+                pszC += 2;          /* skip comment mark */
+
+                /* debug */
+                /*
+                if (fComment)
+                    fprintf(stderr, "starts at line %d\n", iLine);
+                else
+                    fprintf(stderr, "ends   at line %d\n", iLine);
+                    */
+            }
+        }
+    } /*while*/
+
+    textbufferDestroy(pvFile);
+
+    return 0;
+}
+#endif
+
+
+/**
+ * Generates depend info on this COBOL file, these are stored internally
+ * and written to file later.
+ * @returns 0 on success.
+ *          !0 on error.
+ * @param   pszFilename         Pointer to source filename. Correct case is assumed!
+ * @param   pszNormFilename     Pointer to normalized source filename.
+ * @param   pszTS               File time stamp.
+ * @parma   fHeader             True if header file is being scanned.
+ * @param   ppvRule             Variabel to return any new rule handle.
+ * @status  completely implemented.
+ * @author  knut st. osmundsen
+ */
+int langCOBOL(const char *pszFilename, const char *pszNormFilename,
+              const char *pszTS, BOOL fHeader, void **ppvRule)
+{
+    void *  pvFile;                     /* Text buffer pointer. */
+    void *  pvRule;                     /* Handle to the current rule. */
+    char    szBuffer[4096];             /* Temporary buffer (max line lenght size...) */
+    int     iLine;                      /* current line number */
+    void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
+
+
+    /**********************************/
+    /* Add the depend rule            */
+    /**********************************/
+    if (options.fObjRule && !fHeader)
+    {
+        if (options.fNoObjectPath)
+            pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
+        else
+            pvRule = depAddRule(options.fObjectDir ?
+                                    options.pszObjectDir :
+                                    filePathSlash(pszFilename, szBuffer),
+                                fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
+                                options.pszObjectExt, pszTS, FALSE);
+
+        if (options.fSrcWhenObj && pvRule)
+            depAddDepend(pvRule,
+                         options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename)
+                            ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
+                         options.fCheckCyclic,
+                         FALSE);
+    }
+    else
+        pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
+                            fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
+
+    /* duplicate rule? */
+    *ppvRule = pvRule;
+    if (pvRule == NULL)
+        return 0;
+
+
+    /********************/
+    /* Make file buffer */
+    /********************/
+    pvFile = textbufferCreate(pszFilename);
+    if (!pvFile)
+    {
+        fprintf(stderr, "failed to open '%s'\n", pszFilename);
+        return -1;
+    }
+
+
+    /*******************/
+    /* find dependants */
+    /*******************/
+    iLine = 0;
+    while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
+    {
+        /* search for #include */
+        int cbLen;
+        int i = 0;
+        int i1, i2;
+        iLine++;
+
+        /* check for comment mark (column 7) */
+        if (szBuffer[6] == '*')
+            continue;
+
+        /* skip blank chars */
+        cbLen = strlen(szBuffer);
+        while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
+            i++;
+
+        /* is this an include? */
+        if (   (i1 = strnicmp(&szBuffer[i], "COPY", 4)) == 0
+            || (i2 = strnicmpwords(&szBuffer[i], "EXEC SQL INCLUDE", 16)) == 0
+            )
+        {
+            char szFullname[CCHMAXPATH];
+            char *psz;
+            int  j;
+
+            /* skip statement */
+            i += 4;
+            if (i1 != 0)
+            {
+                int y = 2; /* skip two words */
+                do
+                {
+                    /* skip blanks */
+                    while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
+                        i++;
+                    /* skip word */
+                    while (szBuffer[i] != ' ' && szBuffer[i] != '\t'
+                           && szBuffer[i] != '\0' && szBuffer[i] != '\n')
+                        i++;
+                    y--;
+                } while (y > 0);
+            }
+
+            /* check for blank */
+            if (szBuffer[i] != ' ' && szBuffer[i] != '\t') /* no copybook specified... */
+                continue;
+
+            /* skip blanks */
+            while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
+                i++;
+
+            /* if invalid statement then continue with the next line! */
+            if (szBuffer[i] == '\0' || szBuffer[i] == '\n')
+                continue;
+
+            /* find end */
+            j = 0;
+            while (i + j < cbLen && j < CCHMAXPATH
+                   && szBuffer[i+j] != '.'
+                   && szBuffer[i+j] != ' '  && szBuffer[i+j] != '\t'
+                   && szBuffer[i+j] != '\0' && szBuffer[i+j] != '\n'
+                   )
+                j++;
+
+            /* if invalid statement then continue with the next line! */
+            if (szBuffer[i+j] != '.' && szBuffer[i+j] != ' ' && szBuffer[i] != '\t')
+                continue;
+
+            /* copy filename */
+            strncpy(szFullname, &szBuffer[i], j);
+            szFullname[j] = '\0'; /* ensure terminatition. */
+            strlwr(szFullname);
+
+            /* add extention .cpy - hardcoded for the moment. */
+            strcpy(&szFullname[j], ".cbl");
+
+            /* find include file! */
+            psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
+            if (!psz)
+            {
+                strcpy(&szFullname[j], ".cpy");
+                psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
+            }
+
+            /* did we find the include? */
+            if (psz != NULL)
+            {
+                if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
+                    depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
+                else
+                    depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
+            }
+            else
+            {
+                szFullname[j] = '\0';
+                fprintf(stderr, "%s(%d): warning copybook '%s' was not found!\n",
+                        pszFilename, iLine, szFullname);
+                depMarkNotFound(pvRule);
+            }
+        }
+    } /*while*/
+
+    textbufferDestroy(pvFile);
+
+    return 0;
+}
+
+
+/**
+ * Generates depend info on this IPF file, these are stored internally
+ * and written to file later.
+ * @returns 0 on success.
+ *          !0 on error.
+ * @param   pszFilename         Pointer to source filename. Correct case is assumed!
+ * @param   pszNormFilename     Pointer to normalized source filename.
+ * @param   pszTS               File time stamp.
+ * @param   fHeader             True if header file is being scanned.
+ * @param   ppvRule             Variabel to return any new rule handle.
+ * @status  completely implemented.
+ * @author  knut st. osmundsen
+ */
+int langIPF(  const char *pszFilename, const char *pszNormFilename,
+              const char *pszTS, BOOL fHeader, void **ppvRule)
+{
+    void *  pvFile;                     /* Text buffer pointer. */
+    void *  pvRule;                     /* Handle to the current rule. */
+    char    szBuffer[4096];             /* Temporary buffer (max line lenght size...) */
+    int     iLine;                      /* current line number */
+    void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
+
+
+    /**********************************/
+    /* Add the depend rule            */
+    /**********************************/
+    /*if (options.fObjRule && !fHeader)
+    {
+        if (options.fNoObjectPath)
+            pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
+        else
+            pvRule = depAddRule(options.fObjectDir ?
+                                    options.pszObjectDir :
+                                    filePathSlash(pszFilename, szBuffer),
+                                fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
+                                options.pszObjectExt, pszTS, FALSE);
+
+        if (options.fSrcWhenObj && pvRule)
+            depAddDepend(pvRule,
+                         options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename)
+                            ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
+                         options.fCheckCyclic,
+                         FALSE);
+    }
+    else */
+        pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
+                            fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
+
+    /* duplicate rule? */
+    *ppvRule = pvRule;
+    if (pvRule == NULL)
+        return 0;
+
+
+    /********************/
+    /* Make file buffer */
+    /********************/
+    pvFile = textbufferCreate(pszFilename);
+    if (!pvFile)
+    {
+        fprintf(stderr, "failed to open '%s'\n", pszFilename);
+        return -1;
+    }
+
+
+    /*******************/
+    /* find dependants */
+    /*******************/
+    iLine = 0;
+    while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
+    {
+        iLine++;
+
+        /* is this an imbed statement? */
+        if (!strncmp(&szBuffer[0], ".im", 3))
+        {
+            char    szFullname[CCHMAXPATH];
+            char *  psz;
+            int     i;
+            int     j;
+            char    chQuote = 0;
+
+            /* skip statement and blanks */
+            i = 4;
+            while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
+                i++;
+
+            /* check for quotes */
+            if (szBuffer[i] == '\'' || szBuffer[i] == '\"')
+                chQuote = szBuffer[i++];
+
+            /* find end */
+            j = 0;
+            if (chQuote != 0)
+            {
+                while (szBuffer[i+j] != chQuote && szBuffer[i+j] != '\n' && szBuffer[i+j] != '\r' && szBuffer[i+j] != '\0')
+                    j++;
+            }
+            else
+            {
+                while (szBuffer[i+j] != '\n' && szBuffer[i+j] != '\r' && szBuffer[i+j] != '\0')
+                    j++;
+            }
+
+            /* find end */
+            if (j >= CCHMAXPATH)
+            {
+                fprintf(stderr, "%s(%d) warning: Filename too long ignored.\n", pszFilename, iLine);
+                continue;
+            }
+
+            /* copy filename */
+            strncpy(szFullname, &szBuffer[i], j);
+            szFullname[j] = '\0'; /* ensure terminatition. */
+            strlwr(szFullname);
+
+            /* find include file! */
+            psz = filecacheFileExist(szFullname, szBuffer);
+
+            /* did we find the include? */
+            if (psz != NULL)
+            {
+                if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
+                    depAddDepend(pvRule, fileName(szFullname, szBuffer), options.fCheckCyclic, FALSE);
+                else
+                    depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
+            }
+            else
+            {
+                fprintf(stderr, "%s(%d): warning imbeded file '%s' was not found!\n",
+                        pszFilename, iLine, szFullname);
+                depMarkNotFound(pvRule);
+            }
+        }
+    } /*while*/
+
+    textbufferDestroy(pvFile);
+    fHeader = fHeader;
+
+    return 0;
+}
+
+
+#define upcase(ch)   \
+     (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
+
+/**
+ * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
+ * @returns   0 equal. (same as strnicmp)
+ * @param     pszS1  String 1
+ * @param     pszS2  String 2
+ * @param     cch    Length to compare (relative to string 1)
+ * @author    knut st. osmundsen (bird@anduin.net)
+ */
+int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
+{
+    do
+    {
+        while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
+            pszS1++, pszS2++, cch--;
+
+        /* blank test and skipping */
+        if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
+        {
+            while (cch > 0 && *pszS1 == ' ')
+                pszS1++, cch--;
+
+            while (*pszS2 == ' ')
+                pszS2++;
+        }
+        else
+            break;
+    } while (cch > 0);
+
+    return cch == 0 ? 0 : *pszS1 - *pszS2;
+}
+
+
+/**
+ * Normalizes the path slashes for the filename. It will partially expand paths too.
+ * @returns   pszFilename
+ * @param     pszFilename  Pointer to filename string. Not empty string!
+ *                         Much space to play with.
+ */
+char *fileNormalize(char *pszFilename)
+{
+    char *psz = pszFilename;
+
+    /* correct slashes */
+    while ((pszFilename = strchr(pszFilename, '//')) != NULL)
+        *pszFilename++ = '\\';
+
+    /* expand path? */
+    pszFilename = psz;
+    if (pszFilename[1] != ':')
+    {   /* relative path */
+        int     iSlash;
+        char    szFile[CCHMAXPATH];
+        char *  psz = szFile;
+
+        strcpy(szFile, pszFilename);
+        iSlash = *psz == '\\' ? 1 : cSlashes;
+        while (*psz != '\0')
+        {
+            if (*psz == '.' && psz[1] == '.'  && psz[2] == '\\')
+            {   /* up one directory */
+                if (iSlash > 0)
+                    iSlash--;
+                psz += 3;
+            }
+            else if (*psz == '.' && psz[1] == '\\')
+            {   /* no change */
+                psz += 2;
+            }
+            else
+            {   /* completed expantion! */
+                strncpy(pszFilename, szCurDir, aiSlashes[iSlash]+1);
+                strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
+                break;
+            }
+        }
+    }
+    /* else: assume full path */
+
+    return psz;
+}
+
+
+/**
+ * Normalizes the path slashes for the filename. It will partially expand paths too.
+ * Makes name all lower case too.
+ * @returns   pszFilename
+ * @param     pszFilename  Pointer to filename string. Not empty string!
+ *                         Much space to play with.
+ * @param     pszBuffer    Pointer to output buffer.
+ */
+char *fileNormalize2(const char *pszFilename, char *pszBuffer)
+{
+    char *  psz = pszBuffer;
+    int     iSlash;
+
+    if (pszFilename[1] != ':')
+    {
+        /* iSlash */
+        if (*pszFilename == '\\' || *pszFilename == '/')
+            iSlash = 1;
+        else
+            iSlash = cSlashes;
+
+        /* interpret . and .. */
+        while (*pszFilename != '\0')
+        {
+            if (*pszFilename == '.' && pszFilename[1] == '.'  && (pszFilename[2] == '\\' || pszFilename[1] == '/'))
+            {   /* up one directory */
+                if (iSlash > 0)
+                    iSlash--;
+                pszFilename += 3;
+            }
+            else if (*pszFilename == '.' && (pszFilename[1] == '\\' || pszFilename[1] == '/'))
+            {   /* no change */
+                pszFilename += 2;
+            }
+            else
+            {   /* completed expantion! - TODO ..\ or .\ may appare within the remaining path too... */
+                strncpy(pszBuffer, szCurDir, aiSlashes[iSlash]+1);
+                strcpy(pszBuffer + aiSlashes[iSlash]+1, pszFilename);
+                break;
+            }
+        }
+    }
+    else
+    {   /* have drive letter specified - assume ok (TODO)*/
+        strcpy(pszBuffer, pszFilename);
+    }
+
+    /* correct slashes */
+    while ((pszBuffer = strchr(pszBuffer, '//')) != NULL)
+        *pszBuffer++ = '\\';
+
+    /* lower case it */
+    /*strlwr(psz);*/
+
+    return psz;
+}
+
+
+/**
+ * Copies the path part (excluding the slash) into pszBuffer and returns
+ * a pointer to the buffer.
+ * If no path is found "" is returned.
+ * @returns   Pointer to pszBuffer with path.
+ * @param     pszFilename  Pointer to readonly filename.
+ * @param     pszBuffer    Pointer to output Buffer.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+char *filePath(const char *pszFilename, char *pszBuffer)
+{
+    char *psz = strrchr(pszFilename, '\\');
+    if (psz == NULL)
+        psz = strrchr(pszFilename, '/');
+
+    if (psz == NULL)
+        *pszBuffer = '\0';
+    else
+    {
+        strncpy(pszBuffer, pszFilename, psz - pszFilename);
+        pszBuffer[psz - pszFilename] = '\0';
+    }
+
+    return pszBuffer;
+}
+
+
+/**
+ * Copies the path part including the slash into pszBuffer and returns
+ * a pointer to the buffer.
+ * If no path is found "" is returned.
+ * @returns   Pointer to pszBuffer with path.
+ * @param     pszFilename  Pointer to readonly filename.
+ * @param     pszBuffer    Pointer to output Buffer.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+char *filePathSlash(const char *pszFilename, char *pszBuffer)
+{
+    char *psz = strrchr(pszFilename, '\\');
+    if (psz == NULL)
+        psz = strrchr(pszFilename, '/');
+
+    if (psz == NULL)
+        *pszBuffer = '\0';
+    else
+    {
+        strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
+        pszBuffer[psz - pszFilename + 1] = '\0';
+    }
+
+    return pszBuffer;
+}
+
+
+/**
+ * Copies the path part including the slash into pszBuffer and returns
+ * a pointer to the buffer. If no path is found "" is returned.
+ * The path is normalized to only use '\\'.
+ * @returns   Pointer to pszBuffer with path.
+ * @param     pszFilename  Pointer to readonly filename.
+ * @param     pszBuffer    Pointer to output Buffer.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+char *filePathSlash2(const char *pszFilename, char *pszBuffer)
+{
+    char *psz = strrchr(pszFilename, '\\');
+    if (psz == NULL)
+        psz = strrchr(pszFilename, '/');
+
+    if (psz == NULL)
+        *pszBuffer = '\0';
+    else
+    {
+        strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
+        pszBuffer[psz - pszFilename + 1] = '\0';
+
+        /* normalize all '/' to '\\' */
+        psz = pszBuffer;
+        while ((psz = strchr(psz, '/')) != NULL)
+               *psz++ = '\\';
+    }
+
+    return pszBuffer;
+}
+
+
+/**
+ * Copies the filename (with extention) into pszBuffer and returns
+ * a pointer to the buffer.
+ * @returns   Pointer to pszBuffer with path.
+ * @param     pszFilename  Pointer to readonly filename.
+ * @param     pszBuffer    Pointer to output Buffer.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+char *fileName(const char *pszFilename, char *pszBuffer)
+{
+    char *psz = strrchr(pszFilename, '\\');
+    if (psz == NULL)
+        psz = strrchr(pszFilename, '/');
+
+    strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
+
+    return pszBuffer;
+}
+
+
+/**
+ * Copies the name part with out extention into pszBuffer and returns
+ * a pointer to the buffer.
+ * If no name is found "" is returned.
+ * @returns   Pointer to pszBuffer with path.
+ * @param     pszFilename  Pointer to readonly filename.
+ * @param     pszBuffer    Pointer to output Buffer.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
+{
+    char *psz = strrchr(pszFilename, '\\');
+    if (psz == NULL)
+        psz = strrchr(pszFilename, '/');
+
+    strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
+
+    psz = strrchr(pszBuffer, '.');
+    if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
+        *psz = '\0';
+
+    return pszBuffer;
+}
+
+
+/**
+ * Copies the extention part into pszBuffer and returns
+ * a pointer to the buffer.
+ * If no extention is found "" is returned.
+ * The dot ('.') is not included!
+ * @returns   Pointer to pszBuffer with path.
+ * @param     pszFilename  Pointer to readonly filename.
+ * @param     pszBuffer    Pointer to output Buffer.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+char *fileExt(const char *pszFilename, char *pszBuffer)
+{
+    char *psz = strrchr(pszFilename, '.');
+    if (psz != NULL)
+    {
+        if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
+            *pszBuffer = '\0';
+        else
+            strcpy(pszBuffer, psz + 1);
+    }
+    else
+        *pszBuffer = '\0';
+
+    return pszBuffer;
+}
+
+
+/**
+ * Adds a file to the cache.
+ * @returns   Success indicator.
+ * @param     pszFilename   Name of the file which is to be added. (with path!)
+ */
+BOOL filecacheAddFile(const char *pszFilename)
+{
+    PFCACHEENTRY pfcNew;
+
+    /* allocate new block and fill in data */
+    pfcNew = malloc(sizeof(FCACHEENTRY) + strlen(pszFilename) + 1);
+    if (pfcNew == NULL)
+    {
+        fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
+        return FALSE;
+    }
+    pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
+    strcpy((char*)(unsigned)pfcNew->Key, pszFilename);
+    if (!AVLInsert(&pfcTree, pfcNew))
+    {
+        free(pfcNew);
+        return TRUE;
+    }
+    cfcNodes++;
+
+    return TRUE;
+}
+
+
+/**
+ * Adds a file to the cache.
+ * @returns   Success indicator.
+ * @param     pszDir   Name of the path which is to be added. (with slash!)
+ */
+BOOL filecacheAddDir(const char *pszDir)
+{
+    PFCACHEENTRY    pfcNew;
+    APIRET          rc;
+    char            szDir[CCHMAXPATH];
+    int             cchDir;
+    char            achBuffer[32768];
+    PFILEFINDBUF3   pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
+    HDIR            hDir = HDIR_CREATE;
+    ULONG           cFiles = 0xFFFFFFF;
+    int             i;
+
+    /* Make path */
+    filePathSlash2(pszDir, szDir);
+    //strlwr(szDir); /* Convert name to lower case to allow faster searchs! */
+    cchDir = strlen(szDir);
+
+
+    /* Add directory to pfcDirTree. */
+    pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + 1);
+    if (pfcNew == NULL)
+    {
+        fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
+        DosFindClose(hDir);
+        return FALSE;
+    }
+    pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
+    strcpy((char*)(unsigned)pfcNew->Key, szDir);
+    AVLInsert(&pfcDirTree, pfcNew);
+
+
+    /* Start to search directory - all files */
+    strcat(szDir + cchDir, "*");
+    rc = DosFindFirst(szDir, &hDir, FILE_NORMAL,
+                      pfindbuf3, sizeof(achBuffer),
+                      &cFiles, FIL_STANDARD);
+    while (rc == NO_ERROR)
+    {
+        for (i = 0;
+             i < cFiles;
+             i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
+             )
+        {
+            pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + pfindbuf3->cchName + 1);
+            if (pfcNew == NULL)
+            {
+                fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
+                DosFindClose(hDir);
+                return FALSE;
+            }
+            pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
+            strcpy((char*)(unsigned)pfcNew->Key, szDir);
+            strcpy((char*)(unsigned)pfcNew->Key + cchDir, pfindbuf3->achName);
+            strlwr((char*)(unsigned)pfcNew->Key + cchDir); /* Convert name to lower case to allow faster searchs! */
+            if (!AVLInsert(&pfcTree, pfcNew))
+                free(pfcNew);
+            else
+                cfcNodes++;
+        }
+
+        /* next */
+        cFiles = 0xFFFFFFF;
+        pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
+        rc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
+    }
+
+    DosFindClose(hDir);
+
+    return TRUE;
+}
+
+
+/**
+ * Checks if pszFilename is exists in the cache.
+ * @return    TRUE if found. FALSE if not found.
+ * @param     pszFilename   Name of the file to be found. (with path!)
+ *                          This is in lower case!
+ */
+INLINE BOOL filecacheFind(const char *pszFilename)
+{
+    return AVLGet(&pfcTree, (AVLKEY)pszFilename) != NULL;
+}
+
+
+/**
+ * Checks if pszFilename is exists in the cache.
+ * @return    TRUE if found. FALSE if not found.
+ * @param     pszFilename   Name of the file to be found. (with path!)
+ *                          This is in lower case!
+ */
+INLINE BOOL filecacheIsDirCached(const char *pszDir)
+{
+    return AVLGet(&pfcDirTree, (AVLKEY)pszDir) != NULL;
+}
+
+
+/**
+ * Checks if a file exist, uses file cache if possible.
+ * @returns   Pointer to a filename consiting of the path part + the given filename.
+ *            (pointer into pszBuffer)
+ *            NULL if file is not found. ("" in buffer)
+ * @parma     pszFilename  Filename to find.
+ * @parma     pszBuffer    Ouput Buffer.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+char *filecacheFileExist(const char *pszFilename, char *pszBuffer)
+{
+    APIRET          rc;
+
+    *pszBuffer = '\0';
+
+    fileNormalize2(pszFilename, pszBuffer);
+
+    /*
+     * Search for the file in this directory.
+     *   Search cache first
+     */
+    if (!filecacheFind(pszBuffer))
+    {
+        char szDir[CCHMAXPATH];
+
+        filePathSlash(pszBuffer, szDir);
+        if (!filecacheIsDirCached(szDir))
+        {
+            /*
+             * If caching of entire dirs are enabled, we'll
+             * add the directory to the cache and search it.
+             */
+            if (options.fCacheSearchDirs && filecacheAddDir(szDir))
+            {
+                if (filecacheFind(pszBuffer))
+                    return pszBuffer;
+            }
+            else
+            {
+                FILESTATUS3 fsts3;
+
+                /* ask the OS */
+                rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
+                if (rc == NO_ERROR)
+                {   /* add file to cache. */
+                    filecacheAddFile(pszBuffer);
+                    return pszBuffer;
+                }
+            }
+        }
+    }
+    else
+        return pszBuffer;
+
+    return NULL;
+}
+
+
+/**
+ * Finds a filename in a specified pathlist.
+ * @returns   Pointer to a filename consiting of the path part + the given filename.
+ *            (pointer into pszBuffer)
+ *            NULL if file is not found. ("" in buffer)
+ * @param     pszPathList  Path list to search for filename.
+ * @parma     pszFilename  Filename to find.
+ * @parma     pszBuffer    Ouput Buffer.
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer)
+{
+    const char *psz = pszPathList;
+    const char *pszNext = NULL;
+
+    *pszBuffer = '\0';
+
+    if (pszPathList == NULL)
+        return NULL;
+
+    while (*psz != '\0')
+    {
+        /* find end of this path */
+        pszNext = strchr(psz, ';');
+        if (pszNext == NULL)
+            pszNext = psz + strlen(psz);
+
+        if (pszNext - psz > 0)
+        {
+            APIRET          rc;
+
+            /* make search statment */
+            strncpy(pszBuffer, psz, pszNext - psz);
+            pszBuffer[pszNext - psz] = '\0';
+            if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
+                strcpy(&pszBuffer[pszNext - psz], "\\");
+            strcat(pszBuffer, pszFilename);
+            fileNormalize(pszBuffer);
+
+            /*
+             * Search for the file in this directory.
+             *   Search cache first
+             */
+            if (!filecacheFind(pszBuffer))
+            {
+                char szDir[CCHMAXPATH];
+
+                filePathSlash(pszBuffer, szDir);
+                if (!filecacheIsDirCached(szDir))
+                {
+                    /*
+                     * If caching of entire dirs are enabled, we'll
+                     * add the directory to the cache and search it.
+                     */
+                    if (options.fCacheSearchDirs && filecacheAddDir(szDir))
+                    {
+                        if (filecacheFind(pszBuffer))
+                            return pszBuffer;
+                    }
+                    else
+                    {
+                        FILESTATUS3 fsts3;
+
+                        /* ask the OS */
+                        rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
+                        if (rc == NO_ERROR)
+                        {   /* add file to cache. */
+                            filecacheAddFile(pszBuffer);
+                            return pszBuffer;
+                        }
+                    }
+                }
+            }
+            else
+                return pszBuffer;
+        }
+
+        /* next */
+        if (*pszNext != ';')
+            break;
+        psz = pszNext + 1;
+    }
+
+    return NULL;
+}
+
+
+/**
+ * Checks if the given filename may exist within any of the given paths.
+ * This check only matches the filename path agianst the paths in the pathlist.
+ * @returns   TRUE: if exists.
+ *            FALSE: don't exist.
+ * @param     pszPathList  Path list to search for filename.
+ * @parma     pszFilename  Filename to find. The filename should be normalized!
+ * @status    completely implemented.
+ * @author    knut st. osmundsen
+ */
+BOOL pathlistFindFile2(const char *pszPathList, const char *pszFilename)
+{
+    const char *psz = pszPathList;
+    const char *pszNext = NULL;
+    char        szBuffer[CCHMAXPATH];
+    char        szBuffer2[CCHMAXPATH];
+    char       *pszPathToFind = &szBuffer2[0];
+
+    /*
+     * Input checking
+     */
+    if (pszPathList == NULL)
+        return FALSE;
+
+    /*
+     * Normalize the filename and get it's path.
+     */
+    filePath(pszFilename, pszPathToFind);
+
+
+    /*
+     * Loop thru the path list.
+     */
+    while (*psz != '\0')
+    {
+        /* find end of this path */
+        pszNext = strchr(psz, ';');
+        if (pszNext == NULL)
+            pszNext = psz + strlen(psz);
+
+        if (pszNext - psz > 0)
+        {
+            char *  pszPath = &szBuffer[0];
+
+            /*
+             * Extract and normalize the path
+             */
+            strncpy(pszPath, psz, pszNext - psz);
+            pszPath[pszNext - psz] = '\0';
+            if (pszPath[pszNext - psz - 1] == '\\' && pszPath[pszNext - psz - 1] == '/')
+                pszPath[pszNext - psz - 1] = '\0';
+            fileNormalize(pszPath);
+
+            /*
+             * Check if it matches the path of the filename
+             */
+            if (strcmp(pszPath, pszPathToFind) == 0)
+                return TRUE;
+        }
+
+        /*
+         * Next part of the path list.
+         */
+        if (*pszNext != ';')
+            break;
+        psz = pszNext + 1;
+    }
+
+    return FALSE;
+}
+
+
+/**
+ * Finds the first char after word.
+ * @returns   Pointer to the first char after word.
+ * @param     psz  Where to start.
+ * @author    knut st. osmundsen (bird@anduin.net)
+ */
+char *findEndOfWord(char *psz)
+{
+
+    while (*psz != '\0' &&
+            (
+              (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
+              ||
+              (*psz >= '0' && *psz <= '9')
+              ||
+              *psz == '_'
+            )
+          )
+        ++psz;
+    return (char *)psz;
+}
+
+#if 0 /* not used */
+/**
+ * Find the starting char of a word
+ * @returns   Pointer to first char in word.
+ * @param     psz       Where to start.
+ * @param     pszStart  Where to stop.
+ * @author    knut st. osmundsen (bird@anduin.net)
+ */
+char *findStartOfWord(const char *psz, const char *pszStart)
+{
+    const char *pszR = psz;
+    while (psz >= pszStart &&
+            (
+                 (*psz >= 'A' && *psz <= 'Z')
+              || (*psz >= 'a' && *psz <= 'z')
+              || (*psz >= '0' && *psz <= '9')
+              || *psz == '_'
+             )
+          )
+        pszR = psz--;
+    return (char*)pszR;
+}
+#endif
+
+/**
+ * Find the size of a file.
+ * @returns   Size of file. -1 on error.
+ * @param     phFile  File handle.
+ */
+signed long fsize(FILE *phFile)
+{
+    int ipos;
+    signed long cb;
+
+    if ((ipos = ftell(phFile)) < 0
+        ||
+        fseek(phFile, 0, SEEK_END) != 0
+        ||
+        (cb = ftell(phFile)) < 0
+        ||
+        fseek(phFile, ipos, SEEK_SET) != 0
+        )
+        cb = -1;
+    return cb;
+}
+
+
+/**
+ * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
+ * @returns   Pointer to first not space or tab char in the string.
+ * @param     psz   Pointer to the string which is to be trimmed.
+ * @status    completely implmented.
+ * @author    knut st. osmundsen (bird@anduin.net)
+ */
+INLINE char *trim(char *psz)
+{
+    int i;
+    if (psz == NULL)
+        return NULL;
+    while (*psz == ' ' || *psz == '\t')
+        psz++;
+    i = strlen(psz) - 1;
+    while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
+        i--;
+    psz[i+1] = '\0';
+    return psz;
+}
+
+
+/**
+ * Right trims a string, ie. removing spaces (and tabs) from the end of the stri
+ * @returns   Pointer to the string passed in.
+ * @param     psz   Pointer to the string which is to be right trimmed.
+ * @status    completely implmented.
+ * @author    knut st. osmundsen (bird@anduin.net)
+ */
+INLINE char *trimR(char *psz)
+{
+    int i;
+    if (psz == NULL)
+        return NULL;
+    i = strlen(psz) - 1;
+    while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
+        i--;
+    psz[i+1] = '\0';
+    return psz;
+}
+
+
+/**
+ * Trims any quotes of a possibly quoted string.
+ * @returns   Pointer to the string passed in.
+ * @param     psz   Pointer to the string which is to be quote-trimmed.
+ * @status    completely implmented.
+ * @author    knut st. osmundsen (bird@anduin.net)
+ */
+INLINE char *trimQuotes(char *psz)
+{
+    int i;
+    if (psz == NULL)
+        return NULL;
+
+    if (*psz == '\"' || *psz == '\'')
+        psz++;
+    i = strlen(psz) - 1;
+    if (psz[i] == '\"' || psz[i] == '\'')
+        psz[i] = '\0';
+
+    return psz;
+}
+
+
+/**
+ * C/C++ preprocess a single line. Assumes that we're not starting
+ * with at comment.
+ * @returns Pointer to output buffer.
+ * @param   pszOut  Ouput (preprocessed) string.
+ * @param   pszIn   Input string.
+ * @author  knut st. osmundsen (bird@anduin.net)
+ */
+char *PreProcessLine(char *pszOut, const char *pszIn)
+{
+    char *  psz = pszOut;
+    BOOL    fComment = FALSE;
+    BOOL    fQuote = FALSE;
+
+    /*
+     * Loop thru the string.
+     */
+    while (*pszIn != '\0')
+    {
+        if (fQuote)
+        {
+            *psz++ = *pszIn;
+            if (*pszIn == '\\')
+            {
+                *psz++ = *++pszIn;
+                pszIn++;
+            }
+            else if (*pszIn++ == '"')
+                fQuote = FALSE;
+        }
+        else if (fComment)
+        {
+            if (*pszIn == '*' && pszIn[1] == '/')
+            {
+                fComment = FALSE;
+                pszIn += 2;
+            }
+            else
+                pszIn++;
+        }
+        else
+        {
+            if (   (*pszIn == '/' && pszIn[1] == '/')
+                ||  *pszIn == '\0')
+            {   /* End of line. */
+                break;
+            }
+
+            if (*pszIn == '/' && pszIn[1] == '*')
+            {   /* Start comment */
+                fComment = TRUE;
+                pszIn += 2;
+            }
+            else
+                *psz++ = *pszIn++;
+        }
+    }
+
+    /*
+     * Trim right.
+     */
+    psz--;
+    while (psz >= pszOut && (*psz == ' ' || *psz == '\t'))
+        psz--;
+    psz[1] = '\0';
+
+    return pszOut;
+}
+
+
+/**
+ * Creates a memory buffer for a text file.
+ * @returns   Pointer to file memoryblock. NULL on error.
+ * @param     pszFilename  Pointer to filename string.
+ * @remark    This function is the one using most of the execution
+ *            time (DosRead + DosOpen) - about 70% of the execution time!
+ */
+void *textbufferCreate(const char *pszFilename)
+{
+    void *pvFile = NULL;
+    FILE *phFile;
+
+    phFile = fopen(pszFilename, "rb");
+    if (phFile != NULL)
+    {
+        signed long cbFile = fsize(phFile);
+        if (cbFile >= 0)
+        {
+            pvFile = malloc(cbFile + 1);
+            if (pvFile != NULL)
+            {
+                memset(pvFile, 0, cbFile + 1);
+                if (cbFile > 0 && fread(pvFile, 1, cbFile, phFile) == 0)
+                {   /* failed! */
+                    free(pvFile);
+                    pvFile = NULL;
+                }
+            }
+            else
+                fprintf(stderr, "warning/error: failed to open file %s\n", pszFilename);
+        }
+        fclose(phFile);
+    }
+    return pvFile;
+}
+
+
+/**
+ * Destroys a text textbuffer.
+ * @param     pvBuffer   Buffer handle.
+ */
+void textbufferDestroy(void *pvBuffer)
+{
+    free(pvBuffer);
+}
+
+
+/**
+ * Gets the next line from an textbuffer.
+ * @returns   Pointer to the next line.
+ * @param     pvBuffer  Buffer handle.
+ * @param     psz       Pointer to current line.
+ *                      NULL is passed in to get the first line.
+ */
+char *textbufferNextLine(void *pvBuffer, register char *psz)
+{
+    register char ch;
+
+    /* if first line psz is NULL. */
+    if (psz == NULL)
+        return (char*)pvBuffer;
+
+    /* skip till end of file or end of line. */
+    ch = *psz;
+    while (ch != '\0' && ch != '\n' && ch != '\r')
+        ch = *++psz;
+
+    /* skip line end */
+    if (ch == '\r')
+        ch = *++psz;
+    if (ch == '\n')
+        psz++;
+
+    return psz;
+}
+
+
+/**
+ * Gets the next line from an textbuffer.
+ * (fgets for textbuffer)
+ * @returns   Pointer to pszOutBuffer. NULL when end of file.
+ * @param     pvBuffer  Buffer handle.
+ * @param     ppv       Pointer to a buffer index pointer. (holds the current buffer index)
+ *                      Pointer to a null pointer is passed in to get the first line.
+ * @param     pszLineBuffer  Output line buffer. (!= NULL)
+ * @param     cchLineBuffer  Size of the output line buffer. (> 0)
+ * @remark    '\n' and '\r' are removed!
+ */
+char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer)
+{
+    char *          pszLine = pszLineBuffer;
+    char *          psz = *(char**)(void*)ppv;
+    register char   ch;
+
+    /* first line? */
+    if (psz == NULL)
+        psz = pvBuffer;
+
+    /* Copy to end of the line or end of the linebuffer. */
+    ch = *psz;
+    cchLineBuffer--; /* reserve space for '\0' */
+    while (cchLineBuffer > 0 && ch != '\0' && ch != '\n' && ch != '\r')
+    {
+        *pszLine++ = ch;
+        ch = *++psz;
+    }
+    *pszLine = '\0';
+
+    /* skip line end */
+    if (ch == '\r')
+        ch = *++psz;
+    if (ch == '\n')
+        psz++;
+
+    /* check if position has changed - if unchanged it's the end of file! */
+    if (*ppv == (void*)psz)
+        pszLineBuffer = NULL;
+
+    /* store current position */
+    *ppv = (void*)psz;
+
+    return pszLineBuffer;
+}
+
+
+/**
+ * Appends a depend file to the internal file.
+ * This will update the date in the option struct.
+ */
+BOOL  depReadFile(const char *pszFilename, BOOL fAppend)
+{
+    void *      pvFile;
+    char *      pszNext;
+    char *      pszPrev;                /* Previous line, only valid when finding new rule. */
+    BOOL        fMoreDeps = FALSE;
+    void *      pvRule = NULL;
+
+
+    /* read depend file */
+    pvFile = textbufferCreate(pszFilename);
+    if (pvFile == NULL)
+        return FALSE;
+
+    /* parse the original depend file */
+    pszPrev = NULL;
+    pszNext = pvFile;
+    while (*pszNext != '\0')
+    {
+        int   i;
+        int   cch;
+        char *psz;
+
+        /* get the next line. */
+        psz = pszNext;
+        pszNext = textbufferNextLine(pvFile, pszNext);
+
+        /*
+         * Process the current line:
+         *   Start off by terminating the line.
+         *   Trim the line,
+         *   Skip empty lines.
+         *   If not looking for more deps Then
+         *     Check if new rule starts here.
+         *   Endif
+         *
+         *   If more deps to last rule Then
+         *     Get dependant name.
+         *   Endif
+         */
+        i = -1;
+        while (psz <= &pszNext[i] && pszNext[i] == '\n' || pszNext[i] == '\r')
+            pszNext[i--] = '\0';
+        trimR(psz);
+        cch = strlen(psz);
+        if (cch == 0)
+        {
+            fMoreDeps = FALSE;
+            continue;
+        }
+
+        if (*psz == '#')
+        {
+            pszPrev = psz;
+            continue;
+        }
+
+        /* new rule? */
+        if (!fMoreDeps)
+        {
+            if (*psz != ' ' && *psz != '\t' && *psz != '\0')
+            {
+                i = 0;
+                while (psz[i] != '\0')
+                {
+                    if (psz[i] == ':'
+                        && (psz[i+1] == ' '
+                            || psz[i+1] == '\t'
+                            || psz[i+1] == '\0'
+                            || (psz[i+1] == '\\' && psz[i+2] == '\0')
+                            )
+                        )
+                    {
+                        char    szTS[TS_SIZE];
+                        char *  pszCont = strchr(&psz[i], '\\');
+                        fMoreDeps = pszCont != NULL && pszCont[1] == '\0';
+
+                        /* read evt. timestamp. */
+                        szTS[0] = '\0';
+                        if (pszPrev && strlen(pszPrev) > 25 && *pszPrev == '#')
+                            strcpy(szTS, pszPrev + 2);
+
+                        psz[i] = '\0';
+                        pvRule = depAddRule(trimQuotes(trimR(psz)), NULL, NULL, szTS, TRUE);
+                        if (pvRule)
+                            ((PDEPRULE)pvRule)->fUpdated = fAppend;
+                        psz += i + 1;
+                        cch -= i + 1;
+                        break;
+                    }
+                    i++;
+                }
+            }
+            pszPrev = NULL;
+        }
+
+
+        /* more dependants */
+        if (fMoreDeps)
+        {
+            if (cch > 0 && psz[cch-1] == '\\')
+            {
+                fMoreDeps = TRUE;
+                psz[cch-1] = '\0';
+            }
+            else
+                fMoreDeps = FALSE;
+
+            /* if not duplicate rule */
+            if (pvRule != NULL)
+            {
+                psz = trimQuotes(trim(psz));
+                if (*psz != '\0')
+                    depAddDepend(pvRule, psz, options.fCheckCyclic, TRUE);
+            }
+        }
+    } /* while */
+
+
+    /* return succesfully */
+    textbufferDestroy(pvFile);
+    return TRUE;
+}
+
+/**
+ *
+ * @returns   Success indicator.
+ * @param     pszFilename           Pointer to name of the output file.
+ * @param     fWriteUpdatedOnly     If set we'll only write updated rules.
+ */
+BOOL  depWriteFile(const char *pszFilename, BOOL fWriteUpdatedOnly)
+{
+    FILE *phFile;
+    phFile = fopen(pszFilename, "w");
+    if (phFile != NULL)
+    {
+        AVLENUMDATA EnumData;
+        PDEPRULE    pdep;
+        static char szBuffer[0x10000];
+        int         iBuffer = 0;
+        int         cch;
+
+        /*
+         * Write warning on top of file.
+         */
+        fputs("#\n"
+              "# This file was automatically generated by FastDep.\n"
+              "# FastDep was written by knut st. osmundsen, and it's GPL software.\n"
+              "#\n"
+              "# THIS FILE SHOULD   N O T   BE EDITED MANUALLY!!!\n"
+              "#\n"
+              "# (As this may possibly make it unreadable for fastdep\n"
+              "#  and ruin the caching methods of FastDep.)\n"
+              "#\n"
+              "\n",
+              phFile);
+
+        /* normal dependency output */
+        pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
+        while (pdep != NULL)
+        {
+            if (!fWriteUpdatedOnly || pdep->fUpdated)
+            {
+                int cchTS = strlen(pdep->szTS);
+                int fQuoted = strpbrk(pdep->pszRule, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
+
+                /* Write rule. Flush the buffer first if necessary. */
+                cch = strlen(pdep->pszRule);
+                if (iBuffer + cch*3 + fQuoted * 2 + cchTS + 9 >= sizeof(szBuffer))
+                {
+                    fwrite(szBuffer, iBuffer, 1, phFile);
+                    iBuffer = 0;
+                }
+
+                memcpy(szBuffer + iBuffer, "# ", 2);
+                memcpy(szBuffer + iBuffer + 2, pdep->szTS, cchTS);
+                iBuffer += cchTS + 2;
+                szBuffer[iBuffer++] = '\n';
+
+                if (fQuoted) szBuffer[iBuffer++] = '"';
+                iBuffer += depNameToMake(szBuffer + iBuffer, sizeof(szBuffer) - iBuffer, pdep->pszRule);
+                if (fQuoted) szBuffer[iBuffer++] = '"';
+                strcpy(szBuffer + iBuffer++, ":");
+
+                /* write rule dependants. */
+                if (pdep->papszDep != NULL)
+                {
+                    char **ppsz = pdep->papszDep;
+                    while (*ppsz != NULL)
+                    {
+                        /* flush buffer? */
+                        fQuoted = strpbrk(*ppsz, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
+                        cch = strlen(*ppsz);
+                        if (iBuffer + cch*3 + fQuoted * 2 + 20 >= sizeof(szBuffer))
+                        {
+                            fwrite(szBuffer, iBuffer, 1, phFile);
+                            iBuffer = 0;
+                        }
+                        strcpy(szBuffer + iBuffer, " \\\n    ");
+                        iBuffer += 7;
+                        if (fQuoted) szBuffer[iBuffer++] = '"';
+                        iBuffer += depNameToMake(szBuffer + iBuffer, sizeof(szBuffer) - iBuffer, *ppsz);
+                        if (fQuoted) szBuffer[iBuffer++] = '"';
+
+                        /* next dependant */
+                        ppsz++;
+                    }
+                }
+
+                /* Add two new lines. Flush buffer first if necessary. */
+                if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
+                {
+                    fwrite(szBuffer, iBuffer, 1, phFile);
+                    iBuffer = 0;
+                }
+
+                /* add 2 linefeeds */
+                strcpy(szBuffer + iBuffer, "\n\n");
+                iBuffer += CBNEWLINE*2;
+            }
+
+            /* next rule */
+            pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
+        }
+
+
+        /* flush buffer. */
+        fwrite(szBuffer, iBuffer, 1, phFile);
+
+        fclose(phFile);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+
+/**
+ * Removes all nodes in the tree of dependencies. (pdepTree)
+ */
+void  depRemoveAll(void)
+{
+    AVLENUMDATA EnumData;
+    PDEPRULE    pdep;
+
+    pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
+    while (pdep != NULL)
+    {
+        /* free this */
+        if (pdep->papszDep != NULL)
+        {
+            char ** ppsz = pdep->papszDep;
+            while (*ppsz != NULL)
+                free(*ppsz++);
+            free(pdep->papszDep);
+        }
+        free(pdep);
+
+        /* next */
+        pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
+    }
+    pdepTree = NULL;
+}
+
+
+/**
+ * Adds a rule to the list of dependant rules.
+ * @returns   Rule handle. NULL if rule exists/error.
+ * @param     pszRulePath   Pointer to rule text. Empty strings are banned!
+ *                          This string might only contain the path of the rule. (with '\\')
+ * @param     pszName       Name of the rule.
+ *                          NULL if pszRulePath contains the entire rule.
+ * @param     pszExt        Extention (without '.')
+ *                          NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
+ * @param     fConvertName  If set we'll convert from makefile name to realname.
+ */
+void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt, const char *pszTS, BOOL fConvertName)
+{
+    char     szRule[CCHMAXPATH*2];
+    PDEPRULE pNew;
+    int      cch;
+
+    /* make rulename */
+    strcpy(szRule, pszRulePath);
+    cch = strlen(szRule);
+    if (pszName != NULL)
+    {
+        strcpy(szRule + cch, pszName);
+        cch += strlen(szRule + cch);
+    }
+    if (pszExt != NULL)
+    {
+        strcat(szRule + cch++, ".");
+        strcat(szRule + cch, pszExt);
+        cch += strlen(szRule + cch);
+    }
+    if (fConvertName)
+        cch = depNameToReal(szRule);
+
+    /*
+     * Allocate a new rule structure and fill in data
+     * Note. One block for both the DEPRULE and the pszRule string.
+     */
+    pNew = malloc(sizeof(DEPRULE) + cch + 1);
+    if (pNew == NULL)
+    {
+        fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
+        return NULL;
+    }
+    pNew->pszRule = (char*)(void*)(pNew + 1);
+    strcpy(pNew->pszRule, szRule);
+    pNew->cDeps = 0;
+    pNew->papszDep = NULL;
+    pNew->fUpdated = TRUE;
+    pNew->avlCore.Key = pNew->pszRule;
+    strcpy(pNew->szTS, pszTS);
+
+    /* Insert the rule */
+    if (!AVLInsert((PPAVLNODECORE)(void*)&pdepTree, &pNew->avlCore))
+    {   /*
+         * The rule existed.
+         * If it's allready touched (updated) during this session
+         *   there is nothing to be done.
+         * If not force scan and it's newer than depfile-1month then
+         *   we'll use the information we've got.
+         * Reuse the node in the tree.
+         */
+        PDEPRULE    pOld = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pNew->avlCore.Key);
+        assert(pOld);
+        free(pNew);
+        if (pOld->fUpdated)
+            return NULL;
+
+        pOld->fUpdated = TRUE;
+        if (!options.fForceScan && !strcmp(pOld->szTS, pszTS) && depValidate(pOld))
+            return NULL;
+        strcpy(pOld->szTS, pszTS);
+
+        if (pOld->papszDep)
+        {
+            free(pOld->papszDep);
+            pOld->papszDep = NULL;
+        }
+        pOld->cDeps = 0;
+
+        return pOld;
+    }
+
+    return pNew;
+}
+
+
+/**
+ * Adds a dependant to a rule.
+ * @returns   Successindicator. TRUE = success.
+ *            FALSE = cyclic or out of memory.
+ * @param     pvRule        Rule handle.
+ * @param     pszDep        Pointer to dependant name
+ * @param     fCheckCyclic  When set we'll check that we're not creating an cyclic dependency.
+ * @param     fConvertName  If set we'll convert from makefile name to realname.
+ */
+BOOL  depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic, BOOL fConvertName)
+{
+    PDEPRULE    pdep = (PDEPRULE)pvRule;
+    int         cchDep;
+
+    if (pszDep[0] == '\0')
+    {
+        fprintf(stderr, "warning-internal: empty dependancy filename to '%s'. Ignored.\n",
+                pdep->pszRule);
+        /* __interrupt(3); */
+        return FALSE;
+    }
+
+    if (fCheckCyclic && depCheckCyclic(pdep, pszDep))
+    {
+        fprintf(stderr, "warning: Cylic dependancy caused us to ignore '%s' in rule '%s'.\n",
+                pszDep, pdep->pszRule);
+        return FALSE;
+    }
+
+    /* allocate more array space */
+    if (((pdep->cDeps) % 48) == 0)
+    {
+        pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
+        if (pdep->papszDep == NULL)
+        {
+            pdep->cDeps = 0;
+            fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
+            return FALSE;
+        }
+    }
+
+    /* allocate string space and copy pszDep */
+    cchDep = strlen(pszDep) + 1;
+    if ((pdep->papszDep[pdep->cDeps] = malloc(cchDep)) == NULL)
+    {
+        fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
+        return FALSE;
+    }
+    strcpy(pdep->papszDep[pdep->cDeps], pszDep);
+
+    /* convert ^# and other stuff */
+    if (fConvertName)
+        depNameToReal(pdep->papszDep[pdep->cDeps]);
+
+    /* terminate array and increment dep count */
+    pdep->papszDep[++pdep->cDeps] = NULL;
+
+    /* successful! */
+    return TRUE;
+}
+
+
+/**
+ * Converts from makefile filename to real filename.
+ * @returns New name length.
+ * @param   pszName     Pointer to the string to make real.
+ */
+int depNameToReal(char *pszName)
+{
+    int cchNewName = strlen(pszName);
+    int iDisplacement = 0;
+
+    /*
+     * Look for '^' and '$$'.
+     */
+    while (*pszName)
+    {
+        if (    *pszName == '^'
+            ||  (*pszName == '$' && pszName[1] == '$'))
+        {
+            iDisplacement--;
+            pszName++;
+            cchNewName--;
+        }
+        if (iDisplacement)
+            pszName[iDisplacement] = *pszName;
+        pszName++;
+    }
+    pszName[iDisplacement] = '\0';
+
+    return cchNewName;
+}
+
+
+/**
+ * Converts from real filename to makefile filename.
+ * @returns New name length.
+ * @param   pszName     Output name buffer.
+ * @param   cchName     Size of name buffer.
+ * @param   pszSrc      Input name.
+ */
+int   depNameToMake(char *pszName, int cchName, const char *pszSrc)
+{
+    char *pszNameOrg = pszName;
+
+    /*
+     * Convert real name to makefile name.
+     */
+    while (*pszSrc)
+    {
+        if (    *pszSrc == '#'
+            ||  *pszSrc == '!'
+            ||  (*pszSrc == '$' && pszSrc[1] != '(')
+            ||  *pszSrc == '@'
+            ||  *pszSrc == '-'
+            ||  *pszSrc == '^'
+           /* ||  *pszSrc == '('
+            ||  *pszSrc == ')'
+            ||  *pszSrc == '{'
+            ||  *pszSrc == '}'*/)
+        {
+            if (!cchName--)
+            {
+                fprintf(stderr, "error: buffer too small, (line=%d)\n", __LINE__);
+                return pszName - pszNameOrg + strlen(pszName);
+            }
+            *pszName++ = '^';
+        }
+        if (!cchName--)
+        {
+            fprintf(stderr, "error: buffer too small, (line=%d)\n", __LINE__);
+            return pszName - pszNameOrg + strlen(pszName);
+        }
+        *pszName++ = *pszSrc++;
+    }
+    *pszName = '\0';
+
+    return pszName - pszNameOrg;
+}
+
+
+
+/**
+ * Marks the file as one which is to be rescanned next time
+ * since not all dependencies was found...
+ * @param   pvRule  Rule handle...
+ */
+void  depMarkNotFound(void *pvRule)
+{
+    ((PDEPRULE)pvRule)->szTS[0] = '\0';
+}
+
+
+/**
+ * Checks if adding this dependent will create a cyclic dependency.
+ * @returns   TRUE: Cyclic.
+ *            FALSE: Non-cylic.
+ * @param     pdepRule  Rule pszDep is to be inserted in.
+ * @param     pszDep    Depend name.
+ */
+BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep)
+{
+#define DEPTH_FIRST 1
+#ifdef DEPTH_FIRST
+    #define DEPTH 32
+#else
+    #define DEPTH 128
+#endif
+    #define HISTORY 256
+    char *  pszRule = pdepRule->pszRule;
+    char ** appsz[DEPTH];
+#if HISTORY
+    char *  apszHistory[HISTORY];
+    int     iHistory;
+    int     j;
+    int     iStart;
+    int     iEnd;
+    int     iCmp;
+#endif
+    PDEPRULE pdep;
+    int     i;
+
+    /* self check */
+    if (strcmp(pdepRule->pszRule, pszDep) == 0)
+        return TRUE;
+
+    /* find rule for the dep. */
+    if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
+        || pdep->papszDep == NULL)
+        return FALSE; /* no rule, or no dependents, not cyclic */
+
+    i = 1;
+    appsz[0] = pdep->papszDep;
+#ifdef HISTORY
+    iHistory = 1;
+    apszHistory[0] = pdep->pszRule;
+#endif
+    while (i > 0)
+    {
+        /* pop off element */
+        register char **  ppsz = appsz[--i];
+
+        while (*ppsz != NULL)
+        {
+            /* check if equal to the main rule */
+            if (strcmp(pszRule, *ppsz) == 0)
+                return TRUE;
+
+            /* push onto stack (ppsz is incremented in this test!) */
+            if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
+                && pdep->papszDep != NULL)
+            {
+                if (i >= DEPTH)
+                {
+                    fprintf(stderr, "error: too deep chain (%d). pszRule=%s  pszDep=%s\n",
+                            i, pszRule, pszDep);
+                    return FALSE;
+                }
+#ifdef HISTORY
+                /*
+                 * Check if in history, if so we'll skip it.
+                 */
+                #if 0
+                for (j = 0;  j < iHistory; j++)
+                    if (!strcmp(apszHistory[j], pdep->pszRule))
+                        break;
+                if (j != iHistory)
+                    continue;           /* found */
+
+                /*
+                 * Push into history - might concider make this binary sorted one day.
+                 */
+                if (iHistory < HISTORY)
+                    apszHistory[iHistory++] = pdep->pszRule;
+
+                #else
+
+                /*
+                 * Check if in history, if so we'll skip it.
+                 *  (Binary search)
+                 * ASSUMES: Always something in the history!
+                 */
+                iEnd = iHistory - 1;
+                iStart = 0;
+                j = iHistory / 2;
+                while (    (iCmp = strcmp(pdep->pszRule, apszHistory[j])) != 0
+                       &&   iEnd != iStart)
+                {
+                    if (iCmp < 0)
+                        iEnd = j - 1;
+                    else
+                        iStart = j + 1;
+                    if (iStart > iEnd)
+                        break;
+                    j = (iStart + iEnd) / 2;
+                }
+
+                if (!iCmp)
+                    continue;           /* found */
+
+                /*
+                 * Push into history - might concider make this binary sorted one day.
+                 */
+                if (iHistory < HISTORY)
+                {
+                    int k;
+                    if (iCmp > 0)       /* Insert after. */
+                        j++;
+                    for (k = iHistory; k > j; k--)
+                        apszHistory[k] = apszHistory[k - 1];
+                    apszHistory[j] = pdep->pszRule;
+                    iHistory++;
+                }
+
+                #endif
+
+#endif
+                /*
+                 * Push on to the stack.
+                 */
+                #ifdef DEPTH_FIRST
+                /* dept first */
+                appsz[i++] = ppsz;      /* save current posistion */
+                ppsz = pdep->papszDep;  /* process new node */
+                #else
+                /* complete current node first. */
+                appsz[i++] = pdep->papszDep;
+                #endif
+            }
+        }
+    }
+
+    return FALSE;
+}
+
+
+/**
+ * Validates that the dependencies for the file exists
+ * in the given locations. Dependants without path is ignored.
+ * @returns TRUE if all ok.
+ *          FALSE if one (or possibly more) dependants are non-existing.
+ * @param   pdepRule    Pointer to rule we're to validate.
+ */
+BOOL depValidate(PDEPRULE pdepRule)
+{
+    int i;
+
+    for (i = 0; i < pdepRule->cDeps; i++)
+    {
+        char *psz = pdepRule->papszDep[i];
+        if (    !strchr(psz, '$')
+            &&
+            (   psz[1] == ':'
+            ||  strchr(psz, '\\')
+            ||  strchr(psz, '/')
+                 )
+            )
+        {
+            /*
+             * Check existance of the file.
+             *   Search cache first
+             */
+            if (!filecacheFind(psz))
+            {
+                char szDir[CCHMAXPATH];
+
+                filePathSlash(psz, szDir);
+                if (!filecacheIsDirCached(szDir))
+                {
+                    /*
+                     * If caching of entire dirs are enabled, we'll
+                     * add the directory to the cache and search it.
+                     */
+                    if (options.fCacheSearchDirs && filecacheAddDir(szDir))
+                    {
+                        if (!filecacheFind(psz))
+                            return FALSE;
+                    }
+                    else
+                    {
+                        FILESTATUS3 fsts3;
+
+                        /* ask the OS */
+                        if (DosQueryPathInfo(psz, FIL_STANDARD, &fsts3, sizeof(fsts3)))
+                            return FALSE;
+                        /* add file to cache. */
+                        filecacheAddFile(psz);
+                    }
+                }
+                /*
+                 * Dir was cached, hence the file doesn't exist
+                 * and the we should rescan the source file.
+                 */
+                else
+                    return FALSE;
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+
+/**
+ * Make a timestamp from the file data provided thru the
+ * search API.
+ * @returns Pointer to pszTS
+ * @param   pszTS       Pointer to timestamp (output).
+ * @param   pfindbuf3   Pointer to search result.
+ */
+INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3)
+{
+    sprintf(pszTS, "%04d-%02d-%02d-%02d.%02d.%02d 0x%04x%04x %d",
+            pfindbuf3->fdateLastWrite.year + 1980,
+            pfindbuf3->fdateLastWrite.month,
+            pfindbuf3->fdateLastWrite.day,
+            pfindbuf3->ftimeLastWrite.hours,
+            pfindbuf3->ftimeLastWrite.minutes,
+            pfindbuf3->ftimeLastWrite.twosecs * 2,
+            (ULONG)*(PUSHORT)(void*)&pfindbuf3->fdateCreation,
+            (ULONG)*(PUSHORT)(void*)&pfindbuf3->ftimeCreation,
+            pfindbuf3->cbFile);
+    return pszTS;
+}
+
+
+/**
+ * Adds the src additioanl dependenies to a rule.
+ * @param   pvRule  Rule to add them to.
+ * @param   pszz    Pointer to the string of strings of extra dependencies.
+ */
+void depAddSrcAddDeps(void *pvRule, const char *pszz)
+{
+    while (*pszz)
+    {
+        depAddDepend(pvRule, pszz, FALSE, FALSE);
+        pszz += strlen(pszz) + 1;
+    }
+}
+
+
+
+
+
+/*
+ * Testing purpose.
+ */
+
+#if !defined(OS2FAKE)
+#include <os2.h>
+#endif
+#ifdef OLEMANN
+#include "olemann.h"
+#endif
Index: /trunk/src/fastdep/fastdep.def
===================================================================
--- /trunk/src/fastdep/fastdep.def	(revision 7)
+++ /trunk/src/fastdep/fastdep.def	(revision 7)
@@ -0,0 +1,3 @@
+NAME fastdef WINDOWCOMPAT
+DESCRIPTION "Knut''s Quick and Dirty Dependency Generator"
+
Index: /trunk/src/fastdep/os2fake-win.c
===================================================================
--- /trunk/src/fastdep/os2fake-win.c	(revision 7)
+++ /trunk/src/fastdep/os2fake-win.c	(revision 7)
@@ -0,0 +1,297 @@
+/* $Id$
+ * 
+ * OS/2 Fake library for Win32.
+ * 
+ * Copyright (c) 2001 knut st. osmundsen (knut.stange.osmundsen@mynd.no)
+ *
+ * Project Odin Software License can be found in LICENSE.TXT
+ *
+ */
+
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+#define VALIDPTR(pv) (((PVOID)pv) >= (PVOID)0x10000 && ((PVOID)pv) <= (PVOID)0xc0000000)
+
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <windows.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "os2fake.h"
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+static ULONG ConvertAttributes(DWORD dwFileAttributes);
+static ULONG ConvertFileTime(PFILETIME pFileTime);
+
+/**
+ * Converts Win32 file attribute to OS/2 file attributes.
+ * @returns OS/2 fileattributes.
+ * @param   dwFileAttributes    Win32 fileattributes.
+ */
+ULONG ConvertAttributes(DWORD dwFileAttributes)
+{
+    static struct _ConvAttr
+    {
+        ULONG   ulWin;
+        ULONG   ulOs2;
+    }       aConvAttr[] = 
+    {                                  
+        {FILE_ATTRIBUTE_READONLY,   FILE_READONLY},
+        {FILE_ATTRIBUTE_HIDDEN,     FILE_HIDDEN},
+        {FILE_ATTRIBUTE_SYSTEM,     FILE_SYSTEM},
+        {FILE_ATTRIBUTE_DIRECTORY,  FILE_DIRECTORY},
+        {FILE_ATTRIBUTE_ARCHIVE,    FILE_ARCHIVED}
+    };                                    
+    ULONG   ulOS2Attr = 0;
+    int     i;
+
+    for (i = 0; i < sizeof(aConvAttr) / sizeof(aConvAttr[0]); i++)
+        if (dwFileAttributes & aConvAttr[i].ulWin)
+            ulOS2Attr |= aConvAttr[i].ulOs2;
+
+    return ulOS2Attr;
+}
+
+
+/**
+ * Converts Win32 filetime to OS/2 filetime.
+ * @returns OS/2 filetime.
+ * @param   pFileTime   Pointer to Win32 filetime.
+ */
+ULONG ConvertFileTime(PFILETIME pFileTime)
+{
+    ULONG       ulOS2FileTime;
+    SYSTEMTIME  SystemTime;
+
+    if (   FileTimeToSystemTime(pFileTime, &SystemTime)
+        && SystemTime.wYear >= 1980 && SystemTime.wYear < (1980 + 0x7F))
+    {
+        ulOS2FileTime =    SystemTime.wDay
+                        | (SystemTime.wMonth << 5)
+                        | (((SystemTime.wYear - 1980) & 0x7F) << (5+4))
+                        | ((SystemTime.wSecond / 2) << (16))
+                        | (SystemTime.wMinute << (16+5))
+                        | (SystemTime.wHour << (16+5+6));
+    }
+    else
+        ulOS2FileTime = 0;
+
+    return ulOS2FileTime;
+}
+
+
+
+APIRET OS2ENTRY         DosQueryPathInfo(
+                            PCSZ        pszPathName,
+                            ULONG       ulInfoLevel,
+                            PVOID       pInfoBuf,
+                            ULONG       cbInfoBuf)
+{
+    APIRET  rc;                         /* Return code. */
+
+    if (!VALIDPTR(pszPathName))
+    {
+        fprintf(stderr, "DosQueryPathInfo: pszPathName is an invalid pointer - %p\n", pszPathName);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if (!VALIDPTR(pInfoBuf))
+    {
+        fprintf(stderr, "DosQueryPathInfo: pInfoBuf is an invalid pointer - %p\n", pInfoBuf);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+
+    rc = ERROR_INVALID_PARAMETER;
+    switch (ulInfoLevel)
+    {
+        case FIL_QUERYFULLNAME:
+        {
+            LPTSTR   lpDummy;
+            if (GetFullPathName(pszPathName, cbInfoBuf, pInfoBuf, &lpDummy) > 0)
+                rc = NO_ERROR;
+            else
+                rc = GetLastError();
+            break;
+        }
+
+        case FIL_STANDARD:
+            if (cbInfoBuf == sizeof(FILESTATUS3))
+            {
+                WIN32_FILE_ATTRIBUTE_DATA   fad;
+                
+                if (GetFileAttributesEx(pszPathName, GetFileExInfoStandard, &fad)) //W98, NT4 and above.
+                {
+                    PFILESTATUS3    pfst3 = (PFILESTATUS3)(pInfoBuf);
+                    
+                    if (fad.nFileSizeHigh > 0)
+                        rc = ERROR_BAD_LENGTH;
+                    pfst3->cbFile = pfst3->cbFileAlloc = fad.nFileSizeLow;
+                    pfst3->attrFile = ConvertAttributes(fad.dwFileAttributes);
+                    *(PULONG)(&pfst3->fdateCreation) = ConvertFileTime(&fad.ftCreationTime);
+                    *(PULONG)(&pfst3->fdateLastAccess) = ConvertFileTime(&fad.ftLastAccessTime);
+                    *(PULONG)(&pfst3->fdateLastWrite) = ConvertFileTime(&fad.ftLastWriteTime);
+                    rc = NO_ERROR;
+                }
+                else
+                    rc = GetLastError();
+            }
+            else
+                fprintf(stderr, "DosQueryPathInfo: FIL_STANDARD - invalid structure size (cbInfoBuf=%d)\n", cbInfoBuf);
+            break;
+
+        default:
+            fprintf(stderr, "DosQueryPathInfo: ulInfoLevel=%d not supported\n", ulInfoLevel);
+    }
+
+    return rc;
+}
+            
+
+APIRET OS2ENTRY         DosFindFirst(
+                            PCSZ        pszFileSpec,
+                            PHDIR       phdir,
+                            ULONG       flAttribute,
+                            PVOID       pFindBuf,
+                            ULONG       cbFindBuf,
+                            PULONG      pcFileNames,
+                            ULONG       ulInfoLevel)
+{
+    WIN32_FIND_DATA FindData;           /* Win32 Find data (returned by FindFirstFile) */
+    APIRET          rc;
+
+    if (!VALIDPTR(pszFileSpec))
+    {
+        fprintf(stderr, "DosFindFirst: pszFileSpec - %p\n", pszFileSpec);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if (!VALIDPTR(phdir))
+    {
+        fprintf(stderr, "DosFindFirst: phdir - %p\n", phdir);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if (!VALIDPTR(pFindBuf))
+    {
+        fprintf(stderr, "DosFindFirst: pfindbuf - %p\n", pFindBuf);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if (!VALIDPTR(pcFileNames))
+    {
+        fprintf(stderr, "DosFindFirst: pcFileNames - %p\n", pcFileNames);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if (*phdir != HDIR_CREATE)
+    {
+        fprintf(stderr, "DosFindFirst: *phdir != HDIR_CREATE - 0x%08x\n", *phdir);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    switch (ulInfoLevel)
+    {
+        case FIL_STANDARD:
+            if (cbFindBuf < sizeof(FILEFINDBUF3))
+            {
+                fprintf(stderr, "DosFindFirst: unsupported buffer size - %d\n", cbFindBuf);
+                return ERROR_INVALID_PARAMETER;
+            }
+            break;
+
+        default:
+            fprintf(stderr, "DosFindFirst: invalid infolevel %d\n", ulInfoLevel);
+            return ERROR_INVALID_PARAMETER;
+    }
+
+    *phdir = (HDIR)FindFirstFile(pszFileSpec, &FindData);
+    if (*phdir != (HDIR)INVALID_HANDLE_VALUE)
+    {
+        PFILEFINDBUF3   pfindbuf = (PFILEFINDBUF3)pFindBuf;
+        
+        memcpy(pfindbuf->achName, FindData.cFileName, pfindbuf->cchName = strlen(FindData.cFileName) + 1);
+        pfindbuf->cbFile = pfindbuf->cbFileAlloc = FindData.nFileSizeHigh > 0 ? 0xffffffff : FindData.nFileSizeLow;
+        pfindbuf->attrFile = ConvertAttributes(FindData.dwFileAttributes);
+        *(PULONG)(&pfindbuf->fdateCreation) = ConvertFileTime(&FindData.ftCreationTime);
+        *(PULONG)(&pfindbuf->fdateLastAccess) = ConvertFileTime(&FindData.ftLastAccessTime);
+        *(PULONG)(&pfindbuf->fdateLastWrite) = ConvertFileTime(&FindData.ftLastWriteTime);
+        pfindbuf->oNextEntryOffset = 0;
+        *pcFileNames = 1;
+        rc = NO_ERROR;
+    }
+    else
+        rc = GetLastError();
+
+    return rc;
+}
+
+
+APIRET OS2ENTRY         DosFindNext(
+                            HDIR        hDir,
+                            PVOID       pFindBuf,
+                            ULONG       cbFindBuf,
+                            PULONG      pcFileNames)
+{
+    WIN32_FIND_DATA FindData;           /* Win32 Find data (returned by FindFirstFile) */
+    APIRET          rc;
+
+    if (!VALIDPTR(pFindBuf))
+    {
+        fprintf(stderr, "DosFindNext: pfindbuf - %p\n", pFindBuf);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if (cbFindBuf < sizeof(FILEFINDBUF3))
+    {
+        fprintf(stderr, "DosFindNext: unsupported buffer size - %d\n", cbFindBuf);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if (!VALIDPTR(pcFileNames))
+    {
+        fprintf(stderr, "DosFindNext: pcFileNames - %p\n", pcFileNames);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if (FindNextFile((HANDLE)hDir, &FindData))
+    {
+        PFILEFINDBUF3   pfindbuf = (PFILEFINDBUF3)pFindBuf;
+        
+        memcpy(pfindbuf->achName, FindData.cFileName, pfindbuf->cchName = strlen(FindData.cFileName) + 1);
+        pfindbuf->cbFile = pfindbuf->cbFileAlloc = FindData.nFileSizeHigh > 0 ? 0xffffffff : FindData.nFileSizeLow;
+        pfindbuf->attrFile = ConvertAttributes(FindData.dwFileAttributes);
+        *(PULONG)(&pfindbuf->fdateCreation) = ConvertFileTime(&FindData.ftCreationTime);
+        *(PULONG)(&pfindbuf->fdateLastAccess) = ConvertFileTime(&FindData.ftLastAccessTime);
+        *(PULONG)(&pfindbuf->fdateLastWrite) = ConvertFileTime(&FindData.ftLastWriteTime);
+        pfindbuf->oNextEntryOffset = 0;
+        *pcFileNames = 1;
+        rc = NO_ERROR;
+    }
+    else
+        rc = GetLastError();
+
+    return rc;
+
+}
+
+
+APIRET OS2ENTRY         DosFindClose(
+                            HDIR        hDir)
+{
+    if (FindClose((HANDLE)hDir))
+        return NO_ERROR;
+    return ERROR_INVALID_HANDLE;
+}
+
+
+                   
Index: /trunk/src/fastdep/os2fake.h
===================================================================
--- /trunk/src/fastdep/os2fake.h	(revision 7)
+++ /trunk/src/fastdep/os2fake.h	(revision 7)
@@ -0,0 +1,197 @@
+/* $Id$
+ * 
+ * Structures, defines and function prototypes for the OS/2 fake library.
+ * 
+ * Copyright (c) 2001 knut st. osmundsen (knut.stange.osmundsen@mynd.no)
+ *
+ * Project Odin Software License can be found in LICENSE.TXT
+ *
+ */
+ 
+ 
+#ifndef _os2fake_h_
+#define _os2fake_h_
+
+                    
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+#ifndef OS2ENTRY
+#define OS2ENTRY    
+#endif
+
+#ifndef CCHMAXPATHCOMP
+#define CCHMAXPATHCOMP          256
+#endif
+
+#ifndef CCHMAXPATH
+#define CCHMAXPATH              260
+#endif
+
+#ifndef FIL_STANDARD
+#define FIL_STANDARD            1
+#define FIL_QUERYEASIZE         2
+#define FIL_QUERYEASFROMLIST    3
+#define FIL_QUERYFULLNAME       5
+#endif
+   
+#define FILE_NORMAL             0x0000
+#define FILE_READONLY           0x0001
+#define FILE_HIDDEN             0x0002
+#define FILE_SYSTEM             0x0004
+#define FILE_DIRECTORY          0x0010
+#define FILE_ARCHIVED           0x0020
+
+#ifndef HDIR_CREATE
+#define HDIR_CREATE             (-1)
+#endif
+
+#ifndef TRUE
+#define TRUE                    1
+#endif
+#ifndef FALSE
+#define FALSE                   0
+#endif
+
+#ifndef NO_ERROR
+#define NO_ERROR                0
+#endif
+
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+typedef char *          PCH;
+
+typedef char *          PSZ;
+typedef const char *    PCSZ;
+
+typedef unsigned long   ULONG;
+typedef ULONG *         PULONG;
+
+typedef unsigned short  USHORT;
+typedef USHORT *        PUSHORT;
+
+#if !defined(_WINDEF_)
+typedef unsigned int    UINT;
+typedef UINT *          PUINT;
+
+typedef unsigned char   UCHAR;
+typedef UCHAR *         PUCHAR;
+
+#if !defined(CHAR)
+typedef char            CHAR;
+typedef CHAR *          PCHAR;
+#endif
+
+typedef unsigned long   BOOL;
+typedef BOOL *          PBOOL;
+#endif
+
+#if !defined(VOID)
+typedef void            VOID;
+#endif
+#if !defined(_WINNT_) && !defined(PVOID)
+typedef VOID *          PVOID;  
+#endif
+
+typedef unsigned long   HDIR;
+typedef HDIR *          PHDIR;
+
+typedef unsigned long   APIRET;
+
+
+typedef struct _FTIME   /* ftime */
+{
+#if defined(__IBMC__) || defined(__IBMCPP__)
+    UINT    twosecs : 5;
+    UINT    minutes : 6;
+    UINT    hours   : 5;
+#else
+    USHORT  twosecs : 5;
+    USHORT  minutes : 6;
+    USHORT  hours   : 5;
+#endif
+} FTIME;
+typedef FTIME *PFTIME;
+
+
+typedef struct _FDATE   /* fdate */
+{
+#if defined(__IBMC__) || defined(__IBMCPP__)
+    UINT    day     : 5;
+    UINT    month   : 4;
+    UINT    year    : 7;
+#else
+    USHORT  day     : 5;
+    USHORT  month   : 4;
+    USHORT  year    : 7;
+#endif
+} FDATE;
+typedef FDATE   *PFDATE;
+
+
+typedef struct _FILESTATUS3     /* fsts3 */
+{
+    FDATE   fdateCreation;
+    FTIME   ftimeCreation;
+    FDATE   fdateLastAccess;
+    FTIME   ftimeLastAccess;
+    FDATE   fdateLastWrite;
+    FTIME   ftimeLastWrite;
+    ULONG   cbFile;
+    ULONG   cbFileAlloc;
+    ULONG   attrFile;
+} FILESTATUS3;
+typedef FILESTATUS3 *PFILESTATUS3;
+
+typedef struct _FILEFINDBUF3    /* findbuf3 */
+{
+    ULONG   oNextEntryOffset;    
+    FDATE   fdateCreation;
+    FTIME   ftimeCreation;
+    FDATE   fdateLastAccess;
+    FTIME   ftimeLastAccess;
+    FDATE   fdateLastWrite;
+    FTIME   ftimeLastWrite;
+    ULONG   cbFile;
+    ULONG   cbFileAlloc;
+    ULONG   attrFile;            
+    UCHAR   cchName;
+    CHAR    achName[CCHMAXPATHCOMP];
+} FILEFINDBUF3;
+typedef FILEFINDBUF3 *PFILEFINDBUF3;
+
+
+/*******************************************************************************
+*   Function Prototypes                                                        *
+*******************************************************************************/
+APIRET OS2ENTRY         DosQueryPathInfo(
+                            PCSZ        pszPathName,
+                            ULONG       ulInfoLevel,
+                            PVOID       pInfoBuf,
+                            ULONG       cbInfoBuf);
+
+APIRET OS2ENTRY         DosFindFirst(
+                            PCSZ        pszFileSpec,
+                            PHDIR       phdir,
+                            ULONG       flAttribute,
+                            PVOID       pFindBuf,
+                            ULONG       cbFindBuf,
+                            PULONG      pcFileNames,
+                            ULONG       ulInfoLevel);
+
+APIRET OS2ENTRY         DosFindNext(
+                            HDIR        hDir,
+                            PVOID       pFindBuf,
+                            ULONG       cbFindBuf,
+                            PULONG      pcFileNames);
+
+APIRET OS2ENTRY         DosFindClose(
+                            HDIR        hDir);
+
+
+
+#endif
+
