diff options
Diffstat (limited to 'src/fastdep')
-rw-r--r-- | src/fastdep/Makefile | 121 | ||||
-rw-r--r-- | src/fastdep/Makefile.kmk | 57 | ||||
-rw-r--r-- | src/fastdep/avl.c | 823 | ||||
-rw-r--r-- | src/fastdep/avl.h | 102 | ||||
-rw-r--r-- | src/fastdep/fastdep.c | 4136 | ||||
-rw-r--r-- | src/fastdep/fastdep.def | 3 | ||||
-rw-r--r-- | src/fastdep/os2fake-win.c | 297 | ||||
-rw-r--r-- | src/fastdep/os2fake.h | 197 |
8 files changed, 5736 insertions, 0 deletions
diff --git a/src/fastdep/Makefile b/src/fastdep/Makefile new file mode 100644 index 0000000..91f4af1 --- /dev/null +++ b/src/fastdep/Makefile @@ -0,0 +1,121 @@ +# $Id: Makefile 2413 2010-09-11 17:43:04Z bird $ + +# +# Odin32 API +# +# Makefile for the Quick-and-Dirty dependency utility. (FastDep) +# +# Copyright (c) 1999-2010 knut st. osmundsen <bird-kBuild-spamx@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 diff --git a/src/fastdep/Makefile.kmk b/src/fastdep/Makefile.kmk new file mode 100644 index 0000000..3467df9 --- /dev/null +++ b/src/fastdep/Makefile.kmk @@ -0,0 +1,57 @@ +# $Id: $ +## @file +# Sub-makefile for testing the VAC308 tool / ancient dependency generator. +# + +# +# Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net> +# +# This file is part of kBuild. +# +# kBuild is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# kBuild is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with kBuild. If not, see <http://www.gnu.org/licenses/> +# +# + +SUB_DEPTH = ../.. +include $(KBUILD_PATH)/subheader.kmk + + +# +# The base package. +# +PROGRAMS += fastdep +fastdep_TOOL = VAC308 +fastdep_SOURCES = avl.c fastdep.c +fastdep_INCS = f:/toolkit/v4.52/h +fastdep_LIBPATH = f:/toolkit/v4.52/lib + + +LIBRARIES += libfastdep +libfastdep_TOOL = VAC308 +libfastdep_SOURCES = avl.c fastdep.c +libfastdep_INCS = f:/toolkit/v4.52/h + +LIBRARIES += libfastdll +libfastdll_TOOL = VAC308 +libfastdll_SOURCES = fastdll.def + +DLLS += fastdll +fastdll_TOOL = VAC308 +fastdll_SOURCES = fastdll.def avl.c fastdep.c +fastdll_INCS = f:/toolkit/v4.52/h +fastdll_LIBPATH = f:/toolkit/v4.52/lib + + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/fastdep/avl.c b/src/fastdep/avl.c new file mode 100644 index 0000000..e7995a9 --- /dev/null +++ b/src/fastdep/avl.c @@ -0,0 +1,823 @@ +/* $Id: avl.c 2243 2009-01-10 02:24:02Z bird $ + * + * AVL-Tree (lookalike) implementation. + * + * Copyright (c) 1999 knut st. osmundsen + * + * GPL + * + */ + +/******************************************************************************* +* 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; + } + } +} + diff --git a/src/fastdep/avl.h b/src/fastdep/avl.h new file mode 100644 index 0000000..90db3da --- /dev/null +++ b/src/fastdep/avl.h @@ -0,0 +1,102 @@ +/* $Id: avl.h 2243 2009-01-10 02:24:02Z bird $ + * + * 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-2009 knut st. osmundsen + * + * GPL + * + */ +#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 diff --git a/src/fastdep/fastdep.c b/src/fastdep/fastdep.c new file mode 100644 index 0000000..72d918e --- /dev/null +++ b/src/fastdep/fastdep.c @@ -0,0 +1,4136 @@ +/* $Id: fastdep.c 2413 2010-09-11 17:43:04Z bird $ + * + * Fast dependents. (Fast = Quick and Dirty!) + * + * Copyright (c) 1999-2010 knut st. osmundsen <bird-kBuild-spamx@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-2010 knut st. osmundsen (bird-kBuild-spamx@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) + */ +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. + */ +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. + */ +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. + */ +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. + */ +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. + */ +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. + */ +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 diff --git a/src/fastdep/fastdep.def b/src/fastdep/fastdep.def new file mode 100644 index 0000000..6fe0b5f --- /dev/null +++ b/src/fastdep/fastdep.def @@ -0,0 +1,3 @@ +NAME fastdef WINDOWCOMPAT +DESCRIPTION "Knut''s Quick and Dirty Dependency Generator" + diff --git a/src/fastdep/os2fake-win.c b/src/fastdep/os2fake-win.c new file mode 100644 index 0000000..1e9d362 --- /dev/null +++ b/src/fastdep/os2fake-win.c @@ -0,0 +1,297 @@ +/* $Id: os2fake-win.c 2243 2009-01-10 02:24:02Z bird $ + * + * OS/2 Fake library for Win32. + * + * Copyright (c) 2001 knut st. osmundsen (knut.stange.osmundsen@mynd.no) + * + * GPL + * + */ + + +/******************************************************************************* +* 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; +} + + + diff --git a/src/fastdep/os2fake.h b/src/fastdep/os2fake.h new file mode 100644 index 0000000..4036fb0 --- /dev/null +++ b/src/fastdep/os2fake.h @@ -0,0 +1,197 @@ +/* $Id: os2fake.h 2243 2009-01-10 02:24:02Z bird $ + * + * Structures, defines and function prototypes for the OS/2 fake library. + * + * Copyright (c) 2001-2009 knut st. osmundsen (knut.stange.osmundsen@mynd.no) + * + * GPL + * + */ + + +#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 + |