diff --git a/build/jam/OptionalPackages b/build/jam/OptionalPackages index b66d04fa08..978be00202 100644 --- a/build/jam/OptionalPackages +++ b/build/jam/OptionalPackages @@ -197,6 +197,21 @@ if [ IsOptionalHaikuImagePackageAdded Development ] && $(TARGET_ARCH) = x86 { # cc and c++ wrapper scripts AddFilesToHaikuImage beos bin : cc c++ ; + # mkdepend build tool, which is part of the Haiku source tree + # (same as make, but make is already included independent of the + # Development package) + AddFilesToHaikuImage beos bin : mkdepend ; + + # skeleton makefile and makefile-engine + local makefileEngineFiles = + makefile + makefile-engine + ; + SEARCH on $(makefileEngineFiles) + = [ FDirName $(HAIKU_TOP) data develop ] ; + AddFilesToHaikuImage develop etc + : $(makefileEngineFiles) ; + # headers AddHeaderDirectoryToHaikuImage gnu : 3rdparty ; AddHeaderDirectoryToHaikuImage os ; diff --git a/data/develop/makefile b/data/develop/makefile new file mode 100644 index 0000000000..8e86110d36 --- /dev/null +++ b/data/develop/makefile @@ -0,0 +1,120 @@ +## BeOS Generic Makefile v2.2 ## + +## Fill in this file to specify the project being created, and the referenced +## makefile-engine will do all of the hard work for you. This handles both +## Intel and PowerPC builds of the BeOS and Haiku. + +## Application Specific Settings --------------------------------------------- + +# specify the name of the binary +NAME= + +# specify the type of binary +# APP: Application +# SHARED: Shared library or add-on +# STATIC: Static library archive +# DRIVER: Kernel Driver +TYPE= + +# add support for new Pe and Eddie features +# to fill in generic makefile + +#%{ +# @src->@ + +# specify the source files to use +# full paths or paths relative to the makefile can be included +# all files, regardless of directory, will have their object +# files created in the common object directory. +# Note that this means this makefile will not work correctly +# if two source files with the same name (source.c or source.cpp) +# are included from different directories. Also note that spaces +# in folder names do not work well with this makefile. +SRCS= + +# specify the resource files to use +# full path or a relative path to the resource file can be used. +RSRCS= + +# @<-src@ +#%} + +# end support for Pe and Eddie + +# specify additional libraries to link against +# there are two acceptable forms of library specifications +# - if your library follows the naming pattern of: +# libXXX.so or libXXX.a you can simply specify XXX +# library: libbe.so entry: be +# +# - if your library does not follow the standard library +# naming scheme you need to specify the path to the library +# and it's name +# library: my_lib.a entry: my_lib.a or path/my_lib.a +LIBS= + +# specify additional paths to directories following the standard +# libXXX.so or libXXX.a naming scheme. You can specify full paths +# or paths relative to the makefile. The paths included may not +# be recursive, so include all of the paths where libraries can +# be found. Directories where source files are found are +# automatically included. +LIBPATHS= + +# additional paths to look for system headers +# thes use the form: #include
+# source file directories are NOT auto-included here +SYSTEM_INCLUDE_PATHS = + +# additional paths to look for local headers +# thes use the form: #include "header" +# source file directories are automatically included +LOCAL_INCLUDE_PATHS = + +# specify the level of optimization that you desire +# NONE, SOME, FULL +OPTIMIZE= + +# specify any preprocessor symbols to be defined. The symbols will not +# have their values set automatically; you must supply the value (if any) +# to use. For example, setting DEFINES to "DEBUG=1" will cause the +# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" +# would pass "-DDEBUG" on the compiler's command line. +DEFINES= + +# specify special warning levels +# if unspecified default warnings will be used +# NONE = supress all warnings +# ALL = enable all warnings +WARNINGS = + +# specify whether image symbols will be created +# so that stack crawls in the debugger are meaningful +# if TRUE symbols will be created +SYMBOLS = + +# specify debug settings +# if TRUE will allow application to be run from a source-level +# debugger. Note that this will disable all optimzation. +DEBUGGER = + +# specify additional compiler flags for all files +COMPILER_FLAGS = + +# specify additional linker flags +LINKER_FLAGS = + +# specify the version of this particular item +# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL") +# This may also be specified in a resource. +APP_VERSION = + +# (for TYPE == DRIVER only) Specify desired location of driver in the /dev +# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will +# instruct the driverinstall rule to place a symlink to your driver's binary in +# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at +# /dev/video/usb when loaded. Default is "misc". +DRIVER_PATH = + +## include the makefile-engine +include $(BUILDHOME)/etc/makefile-engine diff --git a/data/develop/makefile-engine b/data/develop/makefile-engine new file mode 100644 index 0000000000..cf89065f9c --- /dev/null +++ b/data/develop/makefile-engine @@ -0,0 +1,361 @@ +## BeOS and Haiku Generic Makefile Engine v2.2.0 +## Does all the hard work for the Generic Makefile +## which simply defines the project parameters + +## Supports Generic Makefile v2.0, 2.01, 2.1, 2.2 + +# determine wheather running on x86 or ppc +MACHINE=$(shell uname -m) +ifeq ($(MACHINE), BePC) + CPU = x86 +else + CPU = ppc +endif + +# set the directory where object files and binaries will be created + OBJ_DIR := obj.$(CPU) + +# create some default settings +ifeq ($(NAME), ) + NAME = NameThisApp +endif + +ifeq ($(TYPE), ) + TYPE = APP +endif + +ifeq ($(DRIVER_PATH), ) + DRIVER_PATH = misc +endif + +# specify that the binary should be created in the object directory +# NOTE: make doesn't find the target if its name is enclosed in +# quotation marks +# TARGET := "$(OBJ_DIR)/$(NAME)" + TARGET := $(OBJ_DIR)/$(NAME) + +# specify the mimeset tool + MIMESET := mimeset + +# specify the tools for adding and removing resources + XRES = xres + + +# platform specific settings + +# x86 Settings +ifeq ($(CPU), x86) +# set the compiler and compiler flags + CC = gcc + +# SETTING: set the CFLAGS for each binary type + ifeq ($(TYPE), DRIVER) + CFLAGS += -D_KERNEL_MODE=1 -no-fpic + else + CFLAGS += + endif + +# SETTING: set the proper optimization level + ifeq ($(OPTIMIZE), FULL) + OPTIMIZER = -O3 + else + ifeq ($(OPTIMIZE), SOME) + OPTIMIZER = -O1 + else + ifeq ($(OPTIMIZE), NONE) + OPTIMIZER = -O0 + else +# OPTIMIZE not set so set to full + OPTIMIZER = -O3 + endif + endif + endif + + + +# SETTING: set proper debugger flags + ifeq ($(DEBUGGER), TRUE) + DEBUG += -g + OPTIMIZER = -O0 + endif + + CFLAGS += $(OPTIMIZER) $(DEBUG) + +# SETTING: set warning level + ifeq ($(WARNINGS), ALL) + CFLAGS += -Wall -Wno-multichar -Wno-ctor-dtor-privacy + else + ifeq ($(WARNINGS), NONE) + CFLAGS += -w + endif + endif + +# set the linker and linker flags + LD = gcc + LDFLAGS += $(DEBUG) + +# SETTING: set linker flags for each binary type + ifeq ($(TYPE), APP) + LDFLAGS += -Xlinker -soname=_APP_ + else + ifeq ($(TYPE), SHARED) + LDFLAGS += -nostart -Xlinker -soname=$(NAME) + else + ifeq ($(TYPE), DRIVER) + LDFLAGS += -nostdlib /boot/develop/lib/x86/_KERNEL_ + endif + endif + endif + + +else + +# ppc Settings +ifeq ($(CPU), ppc) +# set the compiler and compiler flags + CC = mwcc + CFLAGS += + +# SETTING: set the proper optimization level + ifeq ($(OPTIMIZE), FULL) + OPTIMIZER = -O7 + else + ifeq ($(OPTIMIZE), SOME) + OPTIMIZER = -O3 + else + ifeq ($(OPTIMIZE), NONE) + OPTIMIZER = -O0 + else +# OPTIMIZE not set so set to full + OPTIMIZER = -O7 + endif + endif + endif + + #set the proper debugger settings + ifeq ($(DEBUGGER), TRUE) + DEBUG += -g + endif + + CFLAGS += $(OPTIMIZER) $(DEBUG) + +# SETTING: set warning level + ifeq ($(WARNINGS), ALL) + CFLAGS += -w on -requireprotos + else + ifeq ($(WARNINGS), NONE) + CFLAGS += -w off + endif + endif + + # clear the standard environment variable + # now there are no standard libraries to link against + BELIBFILES= + +# set the linker and linker flags + LD = mwldppc + +# SETTING: set linker flags for each binary type + ifeq ($(TYPE), APP) + LDFLAGS += + else + ifeq ($(TYPE), SHARED) + LDFLAGS += -xms + endif + endif + + ifeq ($(TYPE), DRIVER) + LDFLAGS += -nodefaults \ + -export all \ + -G \ + /boot/develop/lib/ppc/glue-noinit.a \ + /boot/develop/lib/ppc/_KERNEL_ + else + LDFLAGS += -export pragma \ + -init _init_routine_ \ + -term _term_routine_ \ + -lroot \ + /boot/develop/lib/ppc/glue-noinit.a \ + /boot/develop/lib/ppc/init_term_dyn.o \ + /boot/develop/lib/ppc/start_dyn.o + endif + + +# SETTING: output symbols in an xMAP file + ifeq ($(SYMBOLS), TRUE) + LDFLAGS += -map $(TARGET).xMAP + endif + +# SETTING: output debugging info to a .SYM file + ifeq ($(DEBUGGER), TRUE) + LDFLAGS += -g -osym $(TARGET).SYM + endif + +endif +endif + + +# psuedo-function for converting a list of source files in SRCS variable +# to a corresponding list of object files in $(OBJ_DIR)/xxx.o +# The "function" strips off the src file suffix (.ccp or .c or whatever) +# and then strips of the directory name, leaving just the root file name. +# It then appends the .o suffix and prepends the $(OBJ_DIR)/ path +define SRCS_LIST_TO_OBJS + $(addprefix $(OBJ_DIR)/, $(addsuffix .o, $(foreach file, $(SRCS), \ + $(basename $(notdir $(file)))))) +endef + +define SRCS_LIST_TO_DEPENDS + $(addprefix $(OBJ_DIR)/, $(addsuffix .d, $(foreach file, $(SRCS), \ + $(basename $(notdir $(file)))))) +endef + +OBJS = $(SRCS_LIST_TO_OBJS) +DEPENDS = $(SRCS_LIST_TO_DEPENDS) + +# create a unique list of paths to our sourcefiles +SRC_PATHS += $(sort $(foreach file, $(SRCS), $(dir $(file)))) + +# add source paths to VPATH if not already present +VPATH := +VPATH += $(addprefix :, $(subst ,:, $(filter-out $($(subst, :, ,$(VPATH))), $(SRC_PATHS)))) + +# SETTING: build the local and system include paths +ifeq ($(CPU), x86) + LOC_INCLUDES = $(foreach path, $(SRC_PATHS) $(LOCAL_INCLUDE_PATHS), $(addprefix -I, $(path))) + SYS_INCLUDES += -I- + SYS_INCLUDES += $(foreach path, $(SYSTEM_INCLUDE_PATHS), $(addprefix -I, $(path))) +else +ifeq ($(CPU), ppc) + LOC_INCLUDES = $(foreach path, $(SRC_PATHS) $(LOCAL_INCLUDE_PATHS), $(addprefix -I, $(path))) + SYS_INCLUDES += -i- + SYS_INCLUDES += $(foreach path, $(SYSTEM_INCLUDE_PATHS), $(addprefix -i , $(path))) +endif +endif + +INCLUDES = $(LOC_INCLUDES) $(SYS_INCLUDES) + +# SETTING: add the -L prefix to all library paths to search +LINK_PATHS = $(foreach path, $(SRC_PATHS) $(LIBPATHS), \ + $(addprefix -L, $(path))) + +# SETTING: specify the additional libraries to link against +# if the libraries have a .so or .a prefix, or if they are _APP_ or _KERNEL_ +# simply add them to the list +LINK_LIBS += $(filter %.so %.a _APP_ _KERNEL_, $(LIBS)) +# if the libraries do not have suffixes and are not _APP_ or _KERNEL_ +# prepend -l to each name: be becomes -lbe +LINK_LIBS += $(foreach lib, $(filter-out %.so %.a _APP_ _KERNEL_, $(LIBS)), $(addprefix -l, $(lib))) + +# add to the linker flags +LDFLAGS += $(LINK_PATHS) $(LINK_LIBS) + +# SETTING: add the defines to the compiler flags +CFLAGS += $(foreach define, $(DEFINES), $(addprefix -D, $(define))) + +# SETTING: add the additional compiler flags +CFLAGS += $(COMPILER_FLAGS) + +# SETTING: add the additional linker flags +LDFLAGS += $(LINKER_FLAGS) + +# SETTING: use the archive tools if building a static library +# otherwise use the linker +ifeq ($(TYPE), STATIC) + BUILD_LINE = ar -cru "$(TARGET)" $(OBJS) +else + BUILD_LINE = $(LD) -o $@ $(OBJS) $(LDFLAGS) +endif + +# create the resource instruction + ifeq ($(RSRCS), ) + DO_RSRCS := + else + DO_RSRCS := $(XRES) -o "$(TARGET)" $(RSRCS) + endif + + +# define the actual work to be done +default: $(TARGET) + +$(TARGET): $(OBJ_DIR) $(OBJS) $(RSRCS) + $(BUILD_LINE) + $(DO_RSRCS) + $(MIMESET) -f $@ + + +# rule to create the object file directory if needed +$(OBJ_DIR):: + @[ -d $(OBJ_DIR) ] || mkdir $(OBJ_DIR) > /dev/null 2>&1 + +# rules to make the dependency files +$(OBJ_DIR)/%.d : %.c + [ -d $(OBJ_DIR) ] || mkdir $(OBJ_DIR) > /dev/null 2>&1; \ + mkdepend $(LOC_INCLUDES) -p .c:$(OBJ_DIR)/%n.o -m -f $@ $< +$(OBJ_DIR)/%.d : %.cpp + [ -d $(OBJ_DIR) ] || mkdir $(OBJ_DIR) > /dev/null 2>&1; \ + mkdepend $(LOC_INCLUDES) -p .cpp:$(OBJ_DIR)/%n.o -m -f $@ $< +$(OBJ_DIR)/%.d : %.cp + [ -d $(OBJ_DIR) ] || mkdir $(OBJ_DIR) > /dev/null 2>&1; \ + mkdepend $(LOC_INCLUDES) -p .cp:$(OBJ_DIR)/%n.o -m -f $@ $< +$(OBJ_DIR)/%.d : %.cc + [ -d $(OBJ_DIR) ] || mkdir $(OBJ_DIR) > /dev/null 2>&1; \ + mkdepend $(LOC_INCLUDES) -p .cc:$(OBJ_DIR)/%n.o -m -f $@ $< +$(OBJ_DIR)/%.d : %.C + [ -d $(OBJ_DIR) ] || mkdir $(OBJ_DIR) > /dev/null 2>&1; \ + mkdepend $(LOC_INCLUDES) -p .C:$(OBJ_DIR)/%n.o -m -f $@ $< +$(OBJ_DIR)/%.d : %.CC + [ -d $(OBJ_DIR) ] || mkdir $(OBJ_DIR) > /dev/null 2>&1; \ + mkdepend $(LOC_INCLUDES) -p .CC:$(OBJ_DIR)/%n.o -m -f $@ $< +$(OBJ_DIR)/%.d : %.CPP + [ -d $(OBJ_DIR) ] || mkdir $(OBJ_DIR) > /dev/null 2>&1; \ + mkdepend $(LOC_INCLUDES) -p .CPP:$(OBJ_DIR)/%n.o -m -f $@ $< + +-include $(DEPENDS) + +# rules to make the object files +$(OBJ_DIR)/%.o : %.c + $(CC) -c $< $(INCLUDES) $(CFLAGS) -o $@ +$(OBJ_DIR)/%.o : %.cpp + $(CC) -c $< $(INCLUDES) $(CFLAGS) -o $@ +$(OBJ_DIR)/%.o : %.cp + $(CC) -c $< $(INCLUDES) $(CFLAGS) -o $@ +$(OBJ_DIR)/%.o : %.cc + $(CC) -c $< $(INCLUDES) $(CFLAGS) -o $@ +$(OBJ_DIR)/%.o : %.C + $(CC) -c $< $(INCLUDES) $(CFLAGS) -o $@ +$(OBJ_DIR)/%.o : %.CC + $(CC) -c $< $(INCLUDES) $(CFLAGS) -o $@ +$(OBJ_DIR)/%.o : %.CPP + $(CC) -c $< $(INCLUDES) $(CFLAGS) -o $@ + +# rules to handle lex/flex and yacc/bison files + +$(OBJ_DIR)/%.o: %.l + flex $< + $(CC) -c $(INCLUDES) $(CFLAGS) lex.yy.c -o $@ +$(OBJ_DIR)/%.o: %.y + bison -d -y $< + $(CC) -c $(INCLUDES) $(CFLAGS) y.tab.c -o $@ + +# empty rule. Things that depend on this rule will always get triggered +FORCE: + +# The generic clean command. Delete everything in the object folder. +clean :: FORCE + -rm -rf $(OBJ_DIR) + +# remove just the application from the object folder +rmapp :: + -rm -f $(TARGET) + +# make it easy to install drivers for testing +USER_BIN_PATH = /boot/home/config/add-ons/kernel/drivers/bin +USER_DEV_PATH = /boot/home/config/add-ons/kernel/drivers/dev + +driverinstall :: +ifeq ($(TYPE), DRIVER) + copyattr --data $(OBJ_DIR)/$(NAME) $(USER_BIN_PATH)/$(NAME) + mkdir -p $(USER_DEV_PATH)/$(DRIVER_PATH) + ln -sf $(USER_BIN_PATH)/$(NAME) $(USER_DEV_PATH)/$(DRIVER_PATH)/$(NAME) +endif diff --git a/src/apps/aboutsystem/AboutSystem.cpp b/src/apps/aboutsystem/AboutSystem.cpp index 44af8c5e29..22b81ffb8b 100644 --- a/src/apps/aboutsystem/AboutSystem.cpp +++ b/src/apps/aboutsystem/AboutSystem.cpp @@ -788,6 +788,11 @@ AboutView::AboutView(const BRect &rect) "All rights reserved.", "http://tc.umn.edu/~ringx004"); + // MkDepend 1.7 copyright (Makefile dependency generator) + AddCopyrightEntry("MkDepend", + "Copyright " B_UTF8_COPYRIGHT "1995-2001 Lars Düning. " + "All rights reserved."); + // OpenSound // AddCopyrightEntry("OpenSound", // "Copyright " B_UTF8_COPYRIGHT " 1996-2008 4Front Technologies ", diff --git a/src/bin/Jamfile b/src/bin/Jamfile index 6389bbdaf1..ae20c0e409 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -194,6 +194,7 @@ SubInclude HAIKU_TOP src bin listdev ; SubInclude HAIKU_TOP src bin make ; SubInclude HAIKU_TOP src bin makebootable ; #SubInclude HAIKU_TOP src bin makeudfimage ; +SubInclude HAIKU_TOP src bin mkdepend ; SubInclude HAIKU_TOP src bin mkdos ; SubInclude HAIKU_TOP src bin mkfs ; SubInclude HAIKU_TOP src bin multiuser ; diff --git a/src/bin/mkdepend/HISTORY b/src/bin/mkdepend/HISTORY new file mode 100644 index 0000000000..8e194c49e8 --- /dev/null +++ b/src/bin/mkdepend/HISTORY @@ -0,0 +1,110 @@ +** 14-Oct-2001, Lars Düning ** + - RELEASE 1.7 + - (main, args) New options '--no-warn-exit' to return a zero exitcode + ('success') when a warning was generated (for use in Makefiles). + - (main, args) New options '--ignore-missing' to ignore missing include + files (apart from printing a diagnostic). + - (Makefile, libglob/Makefile) Added x86->PPC cross-compilation. + +** 01-Mar-2000, Lars Düning ** + - RELEASE 1.6 + - (main, args, nodes) New option '--select' to narrow the dependency + generation to a few selected files. + - (Makefile, libglob/Makefile) Added crosscompilation PPC->x86. + +** 03-Feb-1999, Lars Düning ** + - (main, args) When out of memory, the error message also contains a slightly + cryptic hint where the program failed. + - (Makefile) The 'archive' target also creates a checksum file for the + archive, using the /bin/cksum command. + The .dependencies are included only when no 'clean' or 'clobber' is + requested (this solves the behavior Dianne noticed, that a 'make clean' + first recreates .dependencies only to delete it again). + +** 02-Feb-1999, Lars Düning ** + - (args.*) All information about an include directory is now combined + in one structure. + - (main.c, nodes.*) Rewrote the file identification and searching routines, + the old code failed to discriminate files which are included from + different directories. + - (util.*) New. + + - (getargs.c) A long option matched even when only the first characters + were given. A test for the proper length corrected this. + +** 25-Nov-1998, Lars Düning ** + - (getargs.c) A long option matched even when only the first characters + were given. A test for the proper length corrected this. + +** 01-Nov-1998, Lars Düning ** + - (getargs.c) If a value for an option was missing, the parser printed + the wrong option name in the error message. + +** 25-Oct-1998, Lars Düning ** + - RELEASE 1.4.5 + - (Makefile, libglob/Makefile) Adapted for R4 compilations. + - (Makefile) Improved the dependency generation: dependencies are + kept in the file .dependencies; and the rule is clever enough + to fall back to the compiler facilities if mkdepend is not + on the system. + - (getargs) Separated the commandline parser into here. + - (args) New options '--longhelp' to print a long help text, and + '--version' to print the program version. + '--include' distinguishes between normal and system include paths. + - (main) Default file suffixes now also cover a whole range of + C++ suffixes (.cc, .cpp, .cxx, .cc). + Filenames with embedded spaces are written in quotes. + - (nodes, reader, main) Added support for system includes. + +** 15-Oct-1998, Lars Düning ** + - (main.c) The name for the makefile backup was allocated one character + too short. + +** 04-Jul-1998, Lars Düning ** + - RELEASE 1.4 + RCS-Name is 'Rel-1_4', State is 'Rel'. + - Converted the program to BeOS. I decided to not keep the old Amiga + code - it's available on Aminet and I won't further develop it anyway. + New files: LICENSE. + Changed files: Just about everything else. + - Added libglob/ for filename globbing. + +** 02-Mar-1996, Lars Düning ** + - RELEASE 1.3 + - Oops, when confronted with target/source relations, MkDepend put the + source name into the dependency list as well. Though intentional, this + had the potential to confuse implicit make rules. Now this behaviour + is optional, triggered by specifying '+' with the object extension. + (main.c, args.ch) + - RCS tweaks to the Makefiles. + +** 25-Feb-1996, Lars Düning ** + - RELEASE 1.2 + - main.c (output_tree, output): Added output of includers of a file. + - main.c (readfiles): If a file can't be found, it's users known so far + are printed in the warning notice. + - nodes.c,h: Added .pUsers (List of includers of this file) to the Node + structure, extended routines nodes_depend() and nodes_deplist() + accordingly. + - Put under RCS (the Makefiles still need tweaking for this, though). + +** 04-Feb-1996, Lars Düning ** + - main.c (output, getargs, main): The program is now able to write the + list of dependencies into a separate file as well. + - main.c (output_tree): New to write the dependency tree into a file. + - args.ch, main.c: Exported argument parsing into args.c. + - reader.c (reader_openrw): Immediate writing if no file to read is + opened is now possible. + +** 13-Oct-1995, Lars Düning ** + - RELEASE 1.1 + - reader.c (reader_copymake2): Copies the remainder of the original + Makefile starting with the second tagline. + +** 12-Oct-1995, Lars Düning ** + - main.c (make_objname): %p pattern expanded to %[-][<][]p. + - Makefile, DMakefile: Making the 'archive' missed some files. + +** 18-Sep-1995, Lars Düning ** + - RELEASE 1.0 + diff --git a/src/bin/mkdepend/Jamfile b/src/bin/mkdepend/Jamfile new file mode 100644 index 0000000000..8a52d1bbda --- /dev/null +++ b/src/bin/mkdepend/Jamfile @@ -0,0 +1,14 @@ +SubDir HAIKU_TOP src bin mkdepend ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +SubDirHdrs [ FDirName $(SUBDIR) .. make glob ] ; + +BinCommand mkdepend : + args.c + getargs.c + main.c + reader.c + nodes.c + util.c +: libglob.a : mkdepend.rdef ; diff --git a/src/bin/mkdepend/LICENSE b/src/bin/mkdepend/LICENSE new file mode 100644 index 0000000000..0fd9d2d9e4 --- /dev/null +++ b/src/bin/mkdepend/LICENSE @@ -0,0 +1,28 @@ +MkDepend License + +Copyright (c) 1995-2000, Lars Düning. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Modifications of the original software, in parts or as a whole, + must be clearly recognizable as such. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +$Id: LICENSE 1.2 Wed, 01 Mar 2000 21:15:40 -0700 lars $ diff --git a/src/bin/mkdepend/MkDepend.doc b/src/bin/mkdepend/MkDepend.doc new file mode 100644 index 0000000000..ea60672356 --- /dev/null +++ b/src/bin/mkdepend/MkDepend.doc @@ -0,0 +1,622 @@ +$Id: MkDepend.doc 1.10 Wed, 01 Mar 2000 21:15:40 -0700 lars $ + + MkDepend 1.7 + + Copyright (c) 1995-2001 by Lars Düning + All Rights Reserved. + + + "Good information are hard to get. It is even more difficult to put + it into use." -- Sherlock Holmes + + +Introduction +------------ + Makefiles are very useful in the development of larger program systems, + as they notate the various dependencies and associated needed actions + in a machine-useable notation. Unfortunately it's the most important + dependency between C sources and their includes which have to be + maintained by hand, a most boring and errorprone task. + + MkDepend is one of those programs which attempt to automate this part + of development by scanning source files for includes and repeating this + step recursively until the complete transitive closure of included files + is determined. The found dependencies are then written into an existing + makefile, keeping it up to date, and/or into a separate file (the 'depfile') + for use by the makefile or for documentary purposes. + + The advantage of using MkDepend instead of the built-in dependency + facilities of modern compilers is that (a) MkDepend is faster, + (b) simplifies the maintenance of multiple and/or portable Makefiles, + and (c) you can modify it for your special needs. + + +Usage +----- + MkDepend is a shell-only program, compiled for R4.5 + + + mkdepend {-i|-I|--include [::]} + {-x|--except } + {-S|--select } + {-s|--suffix {,}[+][:] | [+]:} + {-p|--objpat {,}[+]:} + [-f|--file ] + [-d|--dep ] + [-l|--flat] + [-m|--ignore-missing] + [-w|--no-warn-exit] + [-v|--verbose] + [-h|-?|--help|--longhelp] + [-V|--version] + {} + + + All option keywords are case sensitive. The space between the short + form of the keywords and their operands may be omitted. For example + + mkdepend -I/include + mkdepend -i /include + mkdepend -I=/include + mkdepend --include=/include + mkdepend --include /include + + are all equivalent. Several short options may be combined, but only + one of them may take an argument, and it must be the last one. For + example: + + mkdepend -vl + mkdepend -vf Makefile + + are legal, but + + mkdepend -fv Makefile + + will be misunderstood as 'mkdepend -f=v Makefile'. + + The option '--' is special in that it marks the end of all option + recognition. All following arguments are considered filenames even + if they start with '-' or '--'. + + +General Operation +----------------- + + MkDepend is given a 'skeleton' of C source files and searches them for + included files. The includes found are recursively searched for includes + themselves, until the complete transitive closure has been determined. + MkDepend searches the includes first in the current directory, then in + the given paths (if any), imitating the C preprocessor's behaviour. + The files are scanned for include statements of the form + + #include "filename" + #include + + excluding those which are commented out using C-style /* */- or + C++-style //-comments. Every other content of the files is ignored. + + After all includes have been found, the dependencies are written into + a makefile (creating it if necessary). The makefile is searched for + a line + + # --- DO NOT MODIFY THIS LINE -- AUTO-DEPENDS FOLLOW --- + + after which MkDepend adds the dependencies, overwriting old ones, then + terminating the dependencies with the line + + # --- DO NOT MODIFY THIS LINE -- AUTO-DEPENDS PRECEDE --- + + Text following the second tagline in the makefile is not modified. + + If the first tagline is not found, the tagged dependencies are simply + appended to the file. Existing makefiles are modified by renaming it + to .bak and then re-creating the makefile by copying the + data not to be modified. + + The found dependencies for the 'skeleton' files are written as + + : ... + + Lines are limited to 80 chars, longer entries are folded into several + lines, ending each but the last line with a backslash (\). If a + name contains spaces, it is written within double quotes. + + MkDepend can be taught to know about source/object file relationships + to be used in the entries written. E,g, the entry generated for a typical + C source file would be of the form + + .o : ... + + Includes appear with that path in the list under which MkDepend found + them. To support multi-platform development, replacement texts may + be specified for the paths which are then used instead in the output. + + + For documentary purposes MkDepend may additionally (or instead) write + the dependencies into a separate file, either as flat lists or in + tree form. Here it writes the 'a includes b' relationships in the + form "a <- b" as well as the 'b is included by a' relationsships + in the form "b -> a". + + +Selected-File Operation +----------------------- + + MkDepend can be instructed just look for dependencies involving + some selected include files. This mode is activated by the use + of one or more '-S' arguments; and the output in this mode is + restricted to just the skeleton and the selected file. + + For example, where a normal MkDepend run would generate + + foobar.o : foobar.h yacc.tab.h main.h + + call MkDepend with the argument '-S yacc.tab.h' would reduce this output + to + + foobar.o : yacc.tab.h + + In the Makefile, these selected dependencies are put between + + # --- DO NOT MODIFY THIS LINE -- SELECTED AUTO-DEPENDS FOLLOW --- + # --- DO NOT MODIFY THIS LINE -- SELECTED AUTO-DEPENDS PRECEDE --- + + delimiter lines, so that both kinds of operations can be used on the + same Makefile. + + + In addition, the output of the other dependency files is modified in + a similar way. + + +Arguments +--------- + + + + Any command argument not recognizable as option is considered as + specification of one of the 'skeleton' sources. + Wildcards are recognized. + + Default: *.c + + Example: + + mkdepend *.c *.cc grammar.y + + All files ending in ".c" or ".cc" and the file "grammar.y" + are considered. + + The expansion of the wildcards is done by the shell. + + ------ + + -x + --except + + Files which are not part of the 'skeleton', even if they are specified + as such. The filepattern may contain the typical bash-wildcards, which + are expanded by MkDepend. + + Example: + + mkdepend *.c -x 'test*' + + All files ending in ".c", except those whose name start with "test", + are considered as sources. Note the quotes around "test*" to protect + it from shell name expansion. + + ------ + + -S + --select + + The output of dependency information is restricted to just those + 'skeleton' source which include at least one of the files. + Furthermore, only the named files are printed in the dependency lists. + + The filepattern may contain the typical bash-wildcards, which are + expanded by MkDepend _against the files existing at the time of + program start_. The pattern has to match the full filename + of the included file, including the directory part implicitely given + by any -I argument. + + Example: + + mkdepend *.c -S 'yacc.tab.h' + + Only the files which include "yacc.tab.h" will be listed in the + output. + + ------ + + -I [::]} + --include [::]} + + Specify a directory to be searched for include files. The paths are + searched in the order given on the command line. Unless '-I-' was + specified, the directory the including file is in is searched first. + + If is specified, it replaces the when writing + dependencies to a makefile. + + Usually, the path, made up from all given s, is searched + fully for both ""- and <>-includes. This behaviour changes if the + path is split using the special dirname '-' (like in '-I-'). + + If '-I-' is used, all directories given before the '-I-' are searched + only for ""-includes. The directories given after the '-I-' are + searched for both ""- and <>-includes again. Also the directories of + the including files are no longer searched automatically. + + If a ""-include is not found, MkDepend will print a warning, but + still list the file in the generated dependencies. Missing <>-includes + on the other hand are silently ignored. + + Example: + + mkdepend -I /include -I- -I libglob + + ""-include files are searched in the current directory, in /include + and in libglob. <>-include files are searched only in the + current directory and libglob. + + mkdepend -I '/include/pdlibs::$(PDLIBS)' + + Include files are searched in the current directory and + in /include/pdlibs. A file like "/include/pdlibs/tree.h" will be + written as "$(PDLIBS)tree.h" into the makefile. + Again note the use of quotes to keep the shell from expanding + the text '$(PDLIBS)'. + + Note that the syntax of this option is similar, but not identical to + what most compilers expect. One example of an exception is the GNU + C-Compiler which distinguishes between '-I' and '-i' in meaning. It is + therefore advisable to only use the uppercase '-I' for both compiler + and MkDepend. + + ------ + + -s {,}[+][:] | [+]:} + --suffix {,}[+][:] | [+]:} + + Specify a name relationship between source and object files. + If a source name ends in one of the given s, an Makefile entry + with the object name constructed using the associated . + The given with no associated serves as default + extension used for those which are given without own + . + When constructing the dependency list for a defined source/object + relation, the sourcefile itself is not included in the list, as + usually implicit makerules take care of them. This can be overridden + by specifying the '+' sign (which is associated with the given source + extensions). + + Default: -s :.o -s .c,.cc,.cpp,.cp,.cxx + + Example: + + mkdepend -s :.o -s '.c,.cc' + + Default is ".obj", which is used for files with the + extensions ".c" and ".cc". + + ------ + + -p {,}[+]: + --objpat {,}[+]: + + Similar to SUFFIX, but allows more complicated name relations. + gives the object file name verbatim, using placeholders + for the actual filenames: + + %s: the full sourcename (w/o suffix) + %p: the path part of the sourcename + %[-][<][]p: the path part of the sourcename + : skip the first directories of the path, + defaults to 0. + < : count the directories from the end or the path part. + - : use, don't skip the counted directories. + %n: the base of the sourcename (w/o suffix) + %%: the character '%'. + %x: the character x for every other character. + + Example: + + mkdepend -p .c:obj/%n.o + + For every sourcefile .c, an entry for obj/.o is + created. + + mkdepend -p .c:obj/%1p%n.o 'source/*.c' + + For every sourcefile source/.c, an entry for obj/.o is + created. + + ------ + + -f + --file + + Specify the makefile to modify. If none is specified and the --dep + option is missing as well, MkDepend searches in order for "Makefile", + "makefile", "Makefile.mk", "makefile.mk" and "GNUMakefile". If none + of these exist, 'Makefile' is used and thus created. + + ------ + + -d + --dep + + Specify the depfile to write the dependencies into. + The output hereinto notes just the plain dependencies between the + skeleton and the included files, i.e. ignores any --objpat or --objpat + setting. However, symbolic include paths are used here as well. + MkDepend prints both the 'a is included by b' as well as the + 'b includes a' relationships. The former are tagged with '<-' in + the output, the latter with '->'. + + If --flat is not specified, the dependencies are written in tree + form with indentation representating the nesting. + If --flat is specified, a flat list like that in Makefiles will + be written. + + ------ + + -l + --flat + + Print the plain dependency file (see option --dep) as flat list instead + of tree form. + + ------ + + -m|--ignore-missing + + If an include file is not found, it is NOT added to the dependencies. + However, MkDepend will still print diagnostic. + + ------ + + -w|--no-warn-exit + + If MkDepend generates a warning, it does not return a non-zero exit + code to the caller (useful for use within makefiles). + + ------ + + -v + --verbose + + Print some output during operation, namely the program name and the + file currently under examination. + ------ + + -h + --help + --longhelp + + Print a (short) summary of the commandline arguments and exit the + program. + + ------ + + -V + --version + + Print the program version and exit. + + +Installation +------------ + Copy the executable "mkdepend" into a suitable directory, profitably + into one in your search path. The PPC executable is in the subdirectory + obj.ppc/, the x86 executable in the subdirectory obj.x86/. + + +Using MkDepend +-------------- + The straightforward use of MkDepend is of course the call from the + commandline a la 'mkdepend *.c', give or take a few command options. This + would scan all C source files, compute the dependencies and update the + Makefile in the same directory with these dependencies. Easy to use and + as easy to forget to do after an important change (trust me, it happens!). + Also, modifying the Makefile may not be what you want; after all, it adds + quite a bunch of junk to the file which is of no direct interest to you. + + Let's solve the second problem first: the solution is to instruct MkDepend + to write all dependencies into an auxiliary file '.dependencies', which is + done with the '-f' command option. The command now looks like this: + 'mkdepend -f .dependencies *.c'. To make use of this new file, the following + statement must be added to the Makefile on a separate line: + '-include .dependencies'. When make processes this line, it will read the + contents of .dependencies and treat them as if they were written in the + Makefile. The '-' in the command tells make to ignore the whole statement + should .dependencies not exist. + + The solution to the first problem (calling MkDepend automatically whenever + a file changes) is to put the MkDepend call into the Makefile, too. + Fortunately for us, Be's standard make allows a Makefile to change itself + or any included file: if such a change is detected, make will simply + re-read the affected files. Putting MkDepend into a Makefile requires the + following changes/additions to be done (modified according to your special + needs where necessary): + + - the main build rule for $(TARGET) must be changed to include .dependencies + as one of the requisites: + + $(TARGET) : .dependencies $(OBJS) ... + + - since MkDepend is not part of the BeOS distribution, the Makefile must + be able to use the normal compiler as a standin for MkDepend. The variable + DEP_FLAGS is set to the appropriate value to do so: + + ifeq ($(CC), gcc) + DEP_FLAGS = -M + endif + + ifeq ($(CC), mwcc$(CPU)) + DEP_FLAGS = -make + endif + + - the rule to generate the .dependencies file: + + .dependencies: $(SRCS) + $(SHELL) -ec '\ + if [ -n "`type -t mkdepend`" ]; then \ + mkdepend $(SRCS) $(INCLUDES) -p '\''.c:obj.$$(CPU)/%n.o'\'' -f$@; \ + else \ + $(CC) $(DEP_FLAGS) $(INCLUDES) $(SRCS) \ + | tr '\''\011'\'' '\''\040'\'' \ + | sed '\''s;^\([^ :][^ :]*\);$$(OBJ)/\1;g'\'' > $@; \ + fi' + + - gmake automatically makes '.dependencies' when it is missing; however, + if our intent is to 'clean' all auxiliary files, this behaviour is + rather annoying. The solution is to include the .dependencies into + the Makefile only when no cleaning action is requested: + + ifneq (,$(filter clean clobber cleanall clobberall, $MAKECMDGOALS)) + -include .dependencies + endif + + - a rule to point out missing header files (this allows a smoother make + process in case of outdated .dependencies): + + %.h : + @echo "Warning: Can't find $@." + + And that's all. + + The same changes apply if you chose to let mkdepend modify the Makefile + directly, just replace every occurence of '.dependencies' with 'Makefile' + (and of course do not '-include Makefile' in your Makefile). + + For a real-life example take a look at MkDepend's own Makefile. + + +Program Information +------------------- + MkDepend consists of these source files: + + main.c,.h : Program control, data generation routines. + args.c,.h : Argument parsing. + getargs.c,.h : Generic commandline parser. + reader.c,.h : Reading of source files and scanning for #include statements. + Copying and writing of makefiles. + nodes.c,.h : Management of the names and locations of the files analysed. + It is implemented as a binary search tree with an additional + linked list. + util.c,.h : Some helpful functions - ok, right now just one function. + version.h : The version number, automatically updated by PRCS. + + Filename expansion is provided by the static library libglob, which comes + under the GNU Library Public License in the subdirectory libglob/. + + The provided Makefile takes care of all the necessary steps to compile and + link both MkDepend and the libglob. See the head comment of the Makefile + for further information about available targets and cross-compilation. + + MkDepend was developed with Metrowerks C, Gnu C and GNU make; PRCS provides + the source code version management. + + +Bugs/TODO +--------- + Maybe allow the modification of several makefiles with one call. + Maybe omit from the depends list those files that could not be found. + The Makefile should be more generic. + + +History +------- + v 1.6 (01-Mar-2000) + Added the possibility to select certain files for dependency generation. + + v 1.5 (03-Feb-1999) + Added the search for include files in the directory of the including + file; older versions of MkDepend searched in the directory from where + MkDepend was started. + + v 1.4.5 (25-Oct-1998) + R4 support (PPC and x86). + The version number is now handled by PRCS. + New command options '--longhelp' and '--version'. + Default file suffixes now also cover a whole range of + C++ suffixes (.cc, .cpp, .cxx, .cc). + Systemincludes (<>-includes) are now handled; with the help of + the new command option -i-. + Filenames with embedded spaces are quoted when written to a Makefile. + + v 1.4 (04-Jul-1998) : + Moved to BeOS, Amiga support dropped. + New option -?|-h|--help for online help. + + v 1.3 (02-Mar-1996) : + For source/object relations, the source is no longer included in + dependency list, except when demanded by '+'-command option. + + v 1.2 (25-Feb-1996) : + New option -D=DEP/K for separate file with then plain source + dependencies. + New option -L=FLAT/S to write the plain dependencies as flat + list instead of a tree. + + v 1.1 (13-Oct-1995) : + The dependencies may now be placed in midst of a makefile. + Object file name pattern %p expanded to %[-][<][]p . + + v 1.0 (18-Sep-1995) : + Initial release, at that time for Amiga OS. + + +Authors +------- + Lars Düning, 10 Morningside Gardens, Edinburgh, EH10 5LA, United Kingdom + EMail: lars@bearnip.com, or duening@ibr.cs.tu-bs.de (deprecated) + + The file reader was inspired by the input module of lcc by David Hanson + and Christopher Fraser. + + The elaborate %p pattern follows a suggestion by Michael A. Wiedmer + (). + + The initial support for R4/x86 and a nice generic Makefile was provided + by Stephen van Egmond (). + + Dianne Kyra Hackborn () and + Stephen van Egmond provided valuable input. + + +Legalese +-------- + The following license is replicated in the file LICENSE. It applies to + all files and derivates thereof which are part of the MkDepend package, + except for those files which explicitely state otherwise. + + + Copyright (c) 1995-1999, Lars Düning. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Modifications of the original software, of parts or of the whole, + must be clearly recognizable as such. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/src/bin/mkdepend/MkDepend.txt b/src/bin/mkdepend/MkDepend.txt new file mode 100644 index 0000000000..b77cd0741a --- /dev/null +++ b/src/bin/mkdepend/MkDepend.txt @@ -0,0 +1,21 @@ +======================================================================== + File: mkdepend-17.zip + Author: Lars Duening (lars@bearnip.com) + Release: 1.7 (14. Oct 2001) +Compatibility: R5 + Location: contrib/development + Description: Dependencygenerator for Makefiles + Notes: + +MkDepend scans C-source files recursively for includes and +writes the found dependency trees into an existing makefile. +Special features of MkDepend are: + - generates the complete transitive closure of all included files; + - searches the included files in several definable paths, and allows + to represent these pathnames by symbols in the generated Makefile; + - the source/object filename relationships may be flexibly defined; + - quite fast file scanner :-). + +The archive comes with full source and precompiled executables for +both x86 and (cross-compiled) PPC. +======================================================================== diff --git a/src/bin/mkdepend/args.c b/src/bin/mkdepend/args.c new file mode 100644 index 0000000000..9536e8a7ff --- /dev/null +++ b/src/bin/mkdepend/args.c @@ -0,0 +1,670 @@ +/* $Id: args.c 1.9 Wed, 01 Mar 2000 21:15:40 -0700 lars $ */ + +/*--------------------------------------------------------------------------- +** Arguments parsing and variables. +** +** Copyright (c) 1996-2001 by Lars Düning. All Rights Reserved. +** This file is free software. For terms of use see the file LICENSE. +**--------------------------------------------------------------------------- +** Parsed arguments: +** +** {-i|--include [::]} +** [-i-] +** {-x|--except } +** {-S|--select } +** {-s|--suffix {,}[+][:] | [+]:} +** {-p|--objpat {,}[+]: +** [-f|--file ] +** [-d|--dep ] +** [-l|--flat] +** [-m|--ignore-missing] +** [-w|--no-warn-exit] +** [-v|--verbose] +** [-h|-?|--help|--longhelp] +** [-V|--version] +** {} +** +**--------------------------------------------------------------------------- +*/ + +#include +#include +#include +#include +#include + +#include "args.h" +#include "getargs.h" +#include "main.h" +#include "glob.h" +#include "version.h" + +/*-------------------------------------------------------------------------*/ + +/* Program arguments */ + +short bVerbose = FALSE; /* TRUE: Verbose action */ +SArray aFiles = { 0, 0, NULL }; /* Source files */ +SArray aAvoid = { 0, 0, NULL }; /* Source files to avoid */ +SArray aSelect = { 0, 0, NULL }; /* Selected include files to print */ +Include * aIncl = NULL; /* Include directories */ +int iInclSize = 0; /* Number of valid entries in aIncl */ +static int allocIncl = 0; /* Allcoated size of pIncl */ +short bSplitPath = FALSE; /* TRUE: distinguish between normal + * and system include path. */ +SArray aSrcExt = { 0, 0, NULL }; /* Source file extensions */ +static int allocGiveSrc = 0, sizeGiveSrc = 0; + /* Allocated/used size of *bGiveSrc */ +short * bGiveSrc = NULL; /* For each entry in aSrcExt: + * TRUE if the source name shall appear + * in the dependency list, too. */ +SArray aObjExt = { 0, 0, NULL }; /* Associated object file extensions */ +SArray aObjPat = { 0, 0, NULL }; /* Associated object file patterns */ +char * sObjExt = NULL; /* Default object file extension */ +short bDefGSrc = FALSE; /* For sObjExt: TRUE if the source + * name shall appear in the depency + * list, too */ +char * sMake = NULL; /* Name of the Makefile */ +char * sDepfile = NULL; /* Name of the dependency file */ +short bFlat = FALSE; /* TRUE: Depfile in flat format */ +short bNoWarnExit = FALSE; /* TRUE: Don't return RETURN_WARN + * from program */ +short bIgnoreMiss = FALSE; /* TRUE: Ignore missing includes */ + +/*-------------------------------------------------------------------------*/ + +/* Every recognized option has a ordinal number */ + +typedef enum OptNumber { + cUnknown = OPTION_UNKNOWN /* unknown option */ + , cArgument = OPTION_ARGUMENT /* normal argument (for us: filename) */ + , cInclude /* --include */ + , cExcept /* --except */ + , cSelect /* --select */ + , cSuffix /* --suffix */ + , cObjPat /* --objpat */ + , cFile /* --file */ + , cDep /* --dep */ + , cFlat /* --flat */ + , cNoWarn // --no-warn-exit + , cIgnMiss // --ignore-missing + , cHelp /* --help */ + , cLongHelp /* --longhelp */ + , cVerbose /* --verbose */ + , cVersion /* --version */ +} OptNumber; + +/* Comprehensive lists of recognized options */ + +static OptionDesc aOptions[] + = { { 'i', "include", cInclude, TRUE } + , { 'I', NULL, cInclude, TRUE } + , { 'x', "except", cExcept, TRUE } + , { 'S', "select", cSelect, TRUE } + , { 's', "suffix", cSuffix, TRUE } + , { 'p', "objpat", cObjPat, TRUE } + , { 'f', "file", cFile, TRUE } + , { 'd', "dep", cDep, TRUE } + , { 'l', "flat", cFlat, FALSE } + , { 'm', "ignore-missing", cIgnMiss, FALSE } + , { 'w', "no-warn-exit", cNoWarn, FALSE } + , { 'h', "help", cHelp, FALSE } + , { '?', NULL, cHelp, FALSE } + , { '\0', "longhelp", cLongHelp, FALSE } + , { 'v', "verbose", cVerbose, FALSE } + , { 'V', "version", cVersion, FALSE } + }; + +/*-------------------------------------------------------------------------*/ +static void +version (void) + +/* Print the version of the program. */ + +{ + fputs("MkDepend " VERSION " - Makefile Dependency Generator\n" + "\nReleased: " RELEASE_DATE + "\nCompiled: " __DATE__ +#ifdef __TIME__ + " " __TIME__ +#endif + "\n", stdout); +} + +/*-------------------------------------------------------------------------*/ +static void +shortusage (void) + +/* Print the short help information to stdout. */ + +{ + version(); + printf("\nUsage: %s [options] ...\n", aPgmName); + fputs( +"\nOptions are:\n" +"\n" +" -i|--include [::]\n" +" -x|--except \n" +" -S|--select \n" +" -s|--suffix {,}[+][:]\n" +" -s|--suffix [+]:\n" +" -p|--objpat {,}[+]:\n" +" -f|--file \n" +" -d|--dep \n" +" -l|--flat\n" +" -m|--ignore-missing\n" +" -w|--no-warn-exit\n" +" -v|--verbose\n" +" -h|-?|--help|--longhelp\n" +" -V|--version\n" + , stdout); + +} /* shortusage() */ + +/*-------------------------------------------------------------------------*/ +static void +usage (void) + +/* Print the help information to stdout. */ + +{ + version(); + printf("\nUsage: %s [options] ...\n", aPgmName); + fputs( +"\nOptions are:\n" +"\n" +" -i|--include [::]\n" +" Add to the list of searched directories. If given,\n" +" is used in the generated dependencies to denote\n" +" the .\n" +" The special notation '-i-' is used to split the search path into\n" +" a non-system and a common part.\n" +"\n" +" -x|--except \n" +" Except all matching files from the root set.\n" +"\n" +" -S|--select \n" +" Only the dependencies for files including are printed,\n" +" and in those only the included files matching are listed.\n" +" Except all matching files from the root set.\n" +"\n" +" -s|--suffix {,}[+][:]\n" +" Set the recognized source file suffixes (and optionally associated\n" +" object file suffixes).\n" +"\n" +" -s|--suffix [+]:\n" +" Set the default object file suffix.\n" +"\n" +" -p|--objpat {,}[+]:\n" +" Define a complex source/object file name relation.\n" +" The recognizes as meta-symbols:\n" +" %s: the full sourcename (w/o suffix)\n" +" %[-][<][]p: the path part of the sourcename\n" +" : skip first directories of the path,\n" +" defaults to 0.\n" +" < : directories are counted from the end.\n" +" - : use, don't skip the counted directories.\n" +" %n: the base of the sourcename (w/o suffix)\n" +" %%: the character %\n" +" %x: the character 'x' for every other character.\n" +"\n" +" -f|--file \n" +" Update with the found dependencies (default is 'Makefile').\n" +"\n" +" -d|--dep \n" +" Write the found dependencies into .\n" +"\n" +" -l|--flat\n" +" When generating a write the dependencies in flat form.\n" +"\n" +" -m|--ignore-missing\n" +" If an include file is not found, it is NOT added to the dependencies.\n" +"\n" +" -w|--no-warn-exit\n" +" If MkDepend generates a warning, it does not return a non-zero exit\n" +" code to the caller (useful for use within makefiles).\n" +"\n" +" -v|--verbose\n" +" Verbose operation.\n" +"\n" +" -V|--version\n" +" Display the version of the program and exit.\n" +"\n" +" --longhelp\n" +" Display this help and exit.\n" +"\n" +" -h|-?|--help\n" +" Display the short help text and exit.\n" +"\n" + , stdout); + +} + +/*-------------------------------------------------------------------------*/ +static int +givesrc_addflag (short bFlag) + +/* Add to *bGiveSrc. + * Return 0 on success, non-0 on error. + */ + +{ + short * pFlags; + assert(sizeGiveSrc <= allocGiveSrc); + if (sizeGiveSrc+1 > allocGiveSrc) + { + pFlags = (short*)realloc(bGiveSrc, sizeof(short)*(allocGiveSrc+4)); + if (!pFlags) + return 1; + bGiveSrc = pFlags; + allocGiveSrc += 4; + } + bGiveSrc[sizeGiveSrc] = bFlag; + sizeGiveSrc++; + return 0; +} + +/*-------------------------------------------------------------------------*/ +static int +include_addinfo (char * pPath, char * pSymbol, int bSystem) + +/* Add the given data to to *bIncl. + * Return 0 on success, non-0 on error. + */ + +{ + Include * pNew; + + assert(iInclSize <= allocIncl); + if (iInclSize+1 > allocIncl) + { + pNew = realloc(aIncl, sizeof(Include)*(allocIncl+4)); + if (!pNew) + return 1; + aIncl = pNew; + allocIncl += 4; + } + aIncl[iInclSize].pPath = pPath; + aIncl[iInclSize].pSymbol = pSymbol; + aIncl[iInclSize].bSysPath = bSystem; + iInclSize++; + return 0; +} + +/*-------------------------------------------------------------------------*/ +int +array_addfile (SArray *pArray, const char * pName) + +/* Add a copy of <*pName> to the string array . + * Return 0 on success, non-0 on error. + * If pName is NULL, the array is simply extended by a NULL pointer. + */ + +{ + char ** pStrs; + assert(pArray->size <= pArray->alloc); + if (pArray->size+1 > pArray->alloc) + { + pStrs = (char **)realloc(pArray->strs, sizeof(char *)*(pArray->alloc+4)); + if (!pStrs) + return 1; + pArray->strs = pStrs; + pArray->alloc += 4; + } + else + pStrs = pArray->strs; + if (pName) + { + pStrs[pArray->size] = strdup(pName); + if (!pStrs[pArray->size]) + return 1; + } + else + pStrs[pArray->size] = NULL; + pArray->size++; + return 0; +} + +/*-------------------------------------------------------------------------*/ +int +array_addlist (SArray *pArray, int count, const char ** pList) + +/* Add the strings from to string array . + * pList and contained strings may be freed after return. + * Return 0 on success, non-0 else. + */ + +{ + char ** pStrs; + int i; + + assert(pList); + if (!count) + return 0; + assert(pArray->size <= pArray->alloc); + if (pArray->size+count > pArray->alloc) + { + pStrs = (char **)realloc(pArray->strs, sizeof(char *)*(pArray->alloc+count)); + if (!pStrs) + return 1; + pArray->strs = pStrs; + pArray->alloc += count; + } + else + pStrs = pArray->strs; + for (i = 0; i < count; i++) + { + char * pString; + + pString = strdup(pList[i]); + if (!pString) + return 1; + pStrs[pArray->size++] = pString; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ +int +add_expfile (SArray *pArray, const char * pName) + +/* Glob filename <*pName> and add the filenames to string array . + * Return 0 on success, non-0 else. + */ + +{ + glob_t aGlob; + int rc; + + memset(&aGlob, 0, sizeof(aGlob)); + + switch (glob(pName, GLOB_NOSORT|GLOB_BRACE|GLOB_TILDE, NULL, &aGlob)) + { + case GLOB_NOSPACE: + globfree(&aGlob); + exit_nomem(" (add_expfile: glob)"); + break; + + case GLOB_ABORTED: + printf("%s: Error expanding %s\n", aPgmName, pName); + globfree(&aGlob); + set_rc(RETURN_ERROR, TRUE); + break; + + case GLOB_NOMATCH: + globfree(&aGlob); + return 0; + + case 0: + /* No error */ + break; + + default: + globfree(&aGlob); + exit_doserr(RETURN_ERROR); + break; + } + + assert(aGlob.gl_pathc > 0); + + rc = 0; + if (aGlob.gl_pathc > 0) + rc = array_addlist(pArray, aGlob.gl_pathc, (const char **)aGlob.gl_pathv); + + globfree(&aGlob); + return rc; +} + +/*-------------------------------------------------------------------------*/ +static int +handler (int eOption, const char * pValue) + +/* Callback function for getargs(). It is called when option + * was recognized, possibly also passing an associated value in . + * Return 0 on success, non-zero else. + */ + +{ + int i; /* All purpose */ + + switch (eOption) + { + case cArgument: + i = array_addfile(&aFiles, pValue); + if (i) + exit_nomem(" (args: add file)"); + break; + + case cInclude: + { + char * pSym, * pMark; + char * pVal; + + /* Special case: check for the '-' marker */ + if (!strcmp(pValue, "-")) + { + bSplitPath = TRUE; + break; + } + + /* Since we're going to hack into pValue, possibly extending it + * by one character, we better make a copy of it. We need it + * anyway to store the data in aIncl[]. + */ + pVal = malloc(strlen(pValue)+2); + if (!pVal) + exit_nomem(" (args: dup include arg)"); + strcpy(pVal, pValue); + + /* Allow for :: notation */ + pSym = NULL; + pMark = pVal+strlen(pVal); + if (pMark != pVal) + { + pMark--; + while (!pSym && pMark >= pVal+1) + { + if (':' != *pMark) + { + pMark--; + continue; + } + if (':' != *(pMark-1)) + { + pMark -= 2; + continue; + } + pSym = pMark+1; + *(pMark-1) = '\0'; + } + } + if (pSym && !strlen(pSym)) + pSym = NULL; + else if (pSym) + { + pSym = strdup(pSym); + if (!pSym) + exit_nomem(" (args: dup include sym)"); + } + + /* Make sure ends in a ':' or '/' */ + pMark = pVal+strlen(pVal); + if (pMark > pVal && ':' != *(pMark-1) && '/' != *(pMark-1)) + { + *pMark = '/'; + *(pMark+1) = '\0'; + } + i = include_addinfo(pVal, pSym, bSplitPath); + if (i) + exit_nomem(" (args: add include info)"); + } + break; + + case cExcept: + i = add_expfile(&aAvoid, pValue); + if (i) + exit_nomem(" (args: add except)"); + break; + + case cSelect: + i = add_expfile(&aSelect, pValue); + if (i) + exit_nomem(" (args: add select)"); + break; + + case cSuffix: + case cObjPat: + { + char * pMark, *pOsfix; + short bGotPlus; + + /* Allow for [+]: notation */ + bGotPlus = FALSE; + pOsfix = NULL; + pMark = (char *)pValue+strlen(pValue); + if (pMark != pValue) + { + pMark--; + while (!pOsfix && pMark >= pValue) + { + if (':' != *pMark) + { + pMark--; + continue; + } + pOsfix = pMark+1; + if (pMark != pValue && *(pMark-1) == '+') + { + bGotPlus = TRUE; + pMark--; + } + *pMark = '\0'; + } + } + if (pOsfix && !strlen(pOsfix)) + pOsfix = NULL; + + /* --objpat needs the [+]: part! */ + if (cObjPat == eOption && !pOsfix) + { + printf("%s: not specified.\n", aPgmName); + set_rc(RETURN_ERROR, TRUE); + } + + /* [+]: alone defines default object suffix */ + if (pOsfix && !strlen(pValue)) + { + if (sObjExt) + free(sObjExt); + sObjExt = strdup(pOsfix); + if (!sObjExt) + exit_nomem(" (args: dup obj suffix)"); + bDefGSrc = bGotPlus; + } + else + { + char * pSfix; + + /* Allow for ,,..., for source suffixes */ + for ( pSfix = (char *)pValue + ; NULL != (pMark = strchr(pSfix, ',')) + ; pSfix = pMark+1 + ) + { + *pMark = '\0'; + if (strlen(pSfix)) + { + if ( array_addfile(&aSrcExt, pSfix) + || givesrc_addflag(bGotPlus) + || (cSuffix == eOption ? array_addfile(&aObjExt, pOsfix) + : array_addfile(&aObjPat, pOsfix) + ) + ) + exit_nomem(" (args: add obj suffix)"); + } + } + if (strlen(pSfix)) + { + if ( array_addfile(&aSrcExt, pSfix) + || givesrc_addflag(bGotPlus) + || (cSuffix == eOption ? array_addfile(&aObjExt, pOsfix) + : array_addfile(&aObjPat, pOsfix) + ) + ) + exit_nomem(" (args: add last obj suffix)"); + } + } + } + break; + + case cFile: + if (sMake) + free(sMake); + sMake = strdup(pValue); + if (!sMake) + exit_nomem(" (args: dup make)"); + break; + + case cDep: + if (sDepfile) + free(sDepfile); + sDepfile = strdup(pValue); + if (!sDepfile) + exit_nomem(" (args: dup dep)"); + break; + + case cFlat: + bFlat = TRUE; + break; + + case cIgnMiss: + bIgnoreMiss = TRUE; + break; + + case cNoWarn: + bNoWarnExit = TRUE; + break; + + case cVerbose: + bVerbose = TRUE; + break; + + case cHelp: + shortusage(); + set_rc(RETURN_WARN, TRUE); + break; + + case cLongHelp: + usage(); + set_rc(RETURN_WARN, TRUE); + break; + + case cVersion: + version(); + set_rc(RETURN_WARN, TRUE); + break; + + default: + /* This shouldn't happen. */ + printf("%s: (getargs) Internal error, eOption is %d\n", aPgmName, eOption); + assert(0); + break; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ +void +parseargs (int argc, char ** argv) + +/* Get the arguments from the commandline and fill in the exported variables. + * In case of an error, the program is terminated. + */ + +{ + if (getargs(argc, argv, aOptions, handler)) + set_rc(RETURN_ERROR, TRUE); +} + +/***************************************************************************/ diff --git a/src/bin/mkdepend/args.h b/src/bin/mkdepend/args.h new file mode 100644 index 0000000000..db2b9a832b --- /dev/null +++ b/src/bin/mkdepend/args.h @@ -0,0 +1,61 @@ +/* $Id: args.h 1.6 Wed, 01 Mar 2000 21:15:40 -0700 lars $ */ + +/*--------------------------------------------------------------------------- + * Copyright (c) 1999-2001 by Lars Düning. All Rights Reserved. + * This file is free software. For terms of use see the file LICENSE. + *--------------------------------------------------------------------------- + */ + +#ifndef __ARGS_H__ +#define __ARGS_H__ 1 + +/* Dynamic string array */ + +typedef struct _sarray +{ + int size; /* Number of used strings */ + int alloc; /* Number of allocated string pointers */ + char ** strs; /* Array of string pointers */ +} SArray; + +/* Information about one include directory */ + +typedef struct _inclinfo { + char * pPath; /* Pathname of the directory */ + char * pSymbol; /* Symbol to use in Makefiles, NULL if none */ + int bSysPath; /* TRUE for system directories */ +} Include; + +/* Program arguments */ + +extern short bVerbose; /* TRUE: Verbose action */ +extern SArray aFiles; /* Source files */ +extern SArray aAvoid; /* Source files to avoid */ +extern SArray aSelect; /* Selected include files to print */ +extern Include * aIncl; /* Include directory information */ +extern int iInclSize; /* Number of valid entries in aIncl */ +extern short bSplitPath; /* Include path handling */ +extern SArray aSrcExt; /* Source file extensions */ +extern SArray aObjExt; /* Associated object file extensions */ +extern SArray aObjPat; /* Associated object file patterns */ +extern short * bGiveSrc; /* For each entry in aSrcExt: TRUE if the + * source name shall appear in the + * dependency list, too. */ +extern char * sObjExt; /* Default object file extension */ +extern short bDefGSrc; /* For sObjExt: TRUE if the source name + * shall appear in the depency list, too + */ +extern char * sMake; /* Name of the Makefile */ +extern char * sDepfile; /* Name of the dependency file */ +extern short bFlat; /* TRUE: Depfile in flat format */ +extern short bNoWarnExit; /* TRUE: Don't return RETURN_WARN from program */ +extern short bIgnoreMiss; /* TRUE: Ignore missing includes */ + +/* Prototypes */ + +extern void parseargs (int argc, char ** argv); +extern int array_addlist (SArray *, int, const char **); +extern int array_addfile (SArray *, const char *); +extern int add_expfile (SArray *, const char *); + +#endif diff --git a/src/bin/mkdepend/getargs.c b/src/bin/mkdepend/getargs.c new file mode 100644 index 0000000000..891c36c27c --- /dev/null +++ b/src/bin/mkdepend/getargs.c @@ -0,0 +1,326 @@ +/* $Id: getargs.c 1.5 Wed, 01 Mar 2000 21:15:40 -0700 lars $ */ + +/*--------------------------------------------------------------------------- + * Commandline argument parser. + * + * Copyright (c) 1998-2000 by Lars Düning. All Rights Reserved. + * This file is free software. For terms of use see the file LICENSE. + *--------------------------------------------------------------------------- + * This code parses the arguments passed to the program in the count + * and the array of strings . The parser distinguishes options, which + * start with a '-', from normal arguments; options are further distinguished + * by their name and may take an additional value. The parser neither + * imposes nor expects any order of options and arguments. + * + * Options are recognized in two forms. In the short form the option must + * be given as a single '-' followed by a single letter. In the long form, + * options start with '--' followed by a string of arbitrary length. + * Short options are case sensitive, long options aren't. + * Examples are: '-r' and '--recursive'. + * + * If an option takes a value, it must follow the option immediately after + * a separating space or '='. Additionally, the value for a short option + * may follow the option without separator. Examples are: '-fMakefile', + * '-f Makefile', '--file=Makefile' and '--file Makefile'. + * + * Short options may be collated into one argument, e.g. '-rtl', but + * of these only the last may take a value. + * + * The option '--' marks the end of options. All following command arguments + * are considered proper arguments even if they start with a '-' or '--'. + *------------------------------------------------------------------------- + * Internally every option recognized by the program is associated with + * an id number, assigned by the user. The parser itself uses the two + * id numbers 'OPTION_UNKNOWN' for unrecognized options, and + * 'OPTION_ARGUMENT' for proper command arguments. + * + * Id numbers are associated with their option strings/letters by the + * the array aOptions, which is passed to the parser function. Every element + * in this array is a structure defining the option's name (string + * or letter), the associated id number, and whether or not the option + * takes a value. An element can hold just one type of option (short or + * long) by setting the description the other option to \0 resp. NULL. + * The order of the elements does not matter. The end of the array is + * marked with an element using OPTION_UNKNOWN as assigned id number. + * + * The parsing is done by calling the function + * + * int getargs( int argc, char ** argv + * , const OptionDesc aOptions[] + * , OptionHandler handler) + * + * The function is passed the argument count and vector as + * they were received from the main() function, the option description + * array and a callback function* . getargs() + * returns 0 if the parsing completed successfully, and non-zero else. + * + * The handler function is called for every successfully recognized option + * and argument. Its prototype is + * + * int handler(int iOption, const char *pValue) + * + * Parameter denotes the recognized option, and pValue points + * to the beginning of the value string if the option takes a value. + * Proper arguments are parsed with eOption==OPTION_ARGUMENT and pValue + * pointing to the argument string. The handler has to return 0 if the + * option/argument was processed correctly, and non-zero else. + * + * Additionally, getargs() puts the basename of the program (as determined + * by argv[0]) into the exported variable + * + * const char *aPgmName + * + * when called the first time. + *------------------------------------------------------------------------- + */ + +#include +#include +#include +#include + +#include "getargs.h" + +const char *aPgmName = NULL; + /* The exported program name. */ + +#define MY_TRUE 1 +#define MY_FALSE 0 + +/*-------------------------------------------------------------------------*/ +int +getargs ( int argc, char ** argv + , OptionDesc aOptions[] + , OptionHandler opt_eval + ) + +/* Get the arguments from the commandline and pass them + * as (number, optional value) to the callback. + * The options are defined by the array , with an OPTION_UNKNOWN + * element marking the last entry. + * + * If opt_eval() returns non-zero, argument scanning is terminated. + * In that case, or if getargs() detects an error itself, getargs() returns + * non-zero. + * A zero return means 'success' in both cases. + * + * Additionally, on the first call getargs() sets aPgmName to the basename + * of the program as derived from argv[0]. + */ + +{ + int i; /* all purpose */ + int nOptions; /* Number of options */ + int iArg; /* Number of argument under inspection */ + int eOption; /* The current recognized option */ + int xOption; /* The index of the recog otion in aOptions */ + short bCont; /* True: find another option in the same argument */ + short bDone; /* True: all options parsed, only args left */ + short bShort; /* True: current argument is a short option */ + char * pArg; /* Next argument character to consider */ + + assert(aOptions != NULL); + assert(opt_eval != NULL); + + /* Determine the program's name */ + if (NULL == aPgmName) + { + char * newName; + + newName = strdup(basename(argv[0])); + if (NULL == newName) + aPgmName = (const char *)argv[0]; + else + aPgmName = (const char *)newName; + } + + /* Make the compiler happy */ + bShort = MY_FALSE; + pArg = NULL; + + /* Count the options */ + for (nOptions = 0; aOptions[nOptions].iNumber != OPTION_UNKNOWN; nOptions++) + /* SKIP */ ; + + /* Scan all arguments */ + bCont = MY_FALSE; + bDone = MY_FALSE; + for (iArg = 1; iArg < argc; !bCont ? iArg++ : iArg) + { + ssize_t iArglen; /* Length of remaining argument */ + char * pValue; /* First character of an option value, or NULL */ + int bTakesValue; /* This option takes a value */ + + /* Make the compiler happy */ + iArglen = 0; + pValue = NULL; + bTakesValue = MY_FALSE; + + if (bDone) + eOption = OPTION_ARGUMENT; + else + /* If this is not a continuation, reinitialise the inspection vars. + * For --opt=val arguments, pValue is set to the first character of val. + */ + if (!bCont) + { + pArg = argv[iArg]; + if ('-' == pArg[0] && '-' == pArg[1]) /* Long option? */ + { + eOption = OPTION_UNKNOWN; + bShort = MY_FALSE; + pArg += 2; + /* Special case: if the argument is just '--', it marks the + * end of all options. + * We set a flag and continue with the next argument. + */ + if ('\0' == *pArg) + { + bDone = MY_TRUE; + continue; + } + pValue = strchr(pArg, '='); + if (pValue != NULL) + { + iArglen = pValue - pArg; + pValue++; + } + else + iArglen = (signed)strlen(pArg); + } + else if ('-' == pArg[0]) /* Short option? */ + { + eOption = OPTION_UNKNOWN; + bShort = MY_TRUE; + pArg++; + iArglen = (signed)strlen(pArg); + pValue = NULL; + } + else /* No option */ + { + eOption = OPTION_ARGUMENT; + pValue = pArg; + iArglen = 0; + } + } + else + eOption = OPTION_UNKNOWN; + + /* If the option is not determined yet, do it. + * Set pValue to the first character of the value if any. + */ + if (OPTION_UNKNOWN == eOption) + { + xOption = 0; + if (bShort) /* search the short option */ + { + for (i = 0; i < nOptions; i++) + if ('\0' != aOptions[i].cOption && *pArg == aOptions[i].cOption) + { + xOption = i; + eOption = aOptions[i].iNumber; + bTakesValue = aOptions[i].bValue; + pArg++; iArglen--; /* Consume this character */ + break; + } + + /* Consume a following '=' if appropriate */ + if (OPTION_UNKNOWN != eOption + && bTakesValue + && iArglen > 0 && '=' == *pArg) + { + pArg++; iArglen--; + } + + /* If there is a value following in the same argument, set pValue to + * it and mark the remaining characters as 'consumed' + */ + if (OPTION_UNKNOWN != eOption && bTakesValue && iArglen > 0) + { + pValue = pArg; + pArg += iArglen; + iArglen = 0; + } + } + else /* search the long option */ + { + for (i = 0; i < nOptions; i++) + if (NULL != aOptions[i].pOption + && (size_t)iArglen == strlen(aOptions[i].pOption) + && !strncasecmp(pArg, aOptions[i].pOption, (unsigned)iArglen)) + { + xOption = i; + eOption = aOptions[i].iNumber; + bTakesValue = aOptions[i].bValue; + break; + } + } + + if (OPTION_UNKNOWN == eOption) + { + fprintf(stderr, "%s: Unknown option '", aPgmName); + if (bShort) + fprintf(stderr, "-%c", *pArg); + else + fprintf(stderr, "--%*.*s", (int)iArglen, (int)iArglen, pArg); + fputs("'.\n", stderr); + return 1; + } + + /* If at this point bTakesValue is true, but pValue is still NULL, + * then the value is in the next argument. Get it if it's there. + */ + if (bTakesValue && pValue == NULL && iArg + 1 < argc) + { + iArg++; + pValue = argv[iArg]; + } + + /* Signal an error if pValue is still NULL or if it's empty. */ + if (bTakesValue && (pValue == NULL || !strlen(pValue))) + { + fprintf(stderr, "%s: Option '", aPgmName); + if (bShort) + putc(aOptions[xOption].cOption, stderr); + else + fputs(aOptions[xOption].pOption, stderr); + fputs("' expects a value.\n", stderr); + return 1; + } + + } /* if (unknown option) */ + + /* Before evaluation of the parsed option, determine 'bCont' */ + bCont = bShort && (iArglen > 0) && !bTakesValue; + + /* --- The option evaluation --- */ + + i = (*opt_eval)(eOption, pValue); + if (i) + return i; + + } /* for (iArg) */ + + return 0; +} /* getargs() */ + +/*-------------------------------------------------------------------------*/ +char * +basename (const char *pName) + +/* Find the basename (filename) in a UNIX-style pathname and return + * a pointer to its first character. + * Example: basename("/boot/home/data/l.txt") returns a pointer to "l.txt". + */ + +{ + char * pBase, * cp; + + pBase = (char *)pName; + for (cp = (char *)pName; *cp; cp++) + if ('/' == *cp) + pBase = cp+1; + return pBase; +} + +/***************************************************************************/ diff --git a/src/bin/mkdepend/getargs.h b/src/bin/mkdepend/getargs.h new file mode 100644 index 0000000000..35c0a7e387 --- /dev/null +++ b/src/bin/mkdepend/getargs.h @@ -0,0 +1,37 @@ +/* $Id: getargs.h 1.2 Wed, 01 Mar 2000 21:15:40 -0700 lars $ */ + +/*--------------------------------------------------------------------------- +** Copyright (c) 1995-1998 by Lars Düning. All Rights Reserved. +** This file is free software. For terms of use see the file LICENSE. +**--------------------------------------------------------------------------- +*/ + +#ifndef __GETARGS_H__ +#define __GETARGS_H__ 1 + +/* Description of an option */ + +typedef struct OptionDesc { + unsigned char cOption; /* The option character (may be \0) */ + char * pOption; /* The option string (may be NULL) */ + int iNumber; /* The associated option number */ + short bValue; /* True: option takes a value */ +} OptionDesc; + +/* Callback function type for option handling */ + +typedef int (*OptionHandler)(int iOption, const char * pValue); + +/* Predefined values for OptionDesc.eNumber */ + +#define OPTION_UNKNOWN 0 /* Option unknown, or 'end of descriptor array' */ +#define OPTION_ARGUMENT 1 /* Not an option, but a real argument */ + +/* --- Prototypes --- */ +extern char * basename (const char *); +extern int getargs(int, char **, OptionDesc[], OptionHandler); + +/* --- Variables --- */ +extern const char * aPgmName; + +#endif /* __GETARGS_H__ */ diff --git a/src/bin/mkdepend/main.c b/src/bin/mkdepend/main.c new file mode 100644 index 0000000000..a3c06ec953 --- /dev/null +++ b/src/bin/mkdepend/main.c @@ -0,0 +1,1503 @@ +/* $Id: main.c 1.8 Wed, 01 Mar 2000 21:15:40 -0700 lars $ */ + +/*--------------------------------------------------------------------------- + * Main module of MkDepend. + * + * Copyright (c) 1995-2001 by Lars Düning. All Rights Reserved. + * This file is free software. For terms of use see the file LICENSE. + *--------------------------------------------------------------------------- + * The module implements the argument parsing and the control flow. + * + * Usage: + * mkdepend {-i|--include [::]} + * [-i-] + * {-x|--except } + * {-S|--select } + * {-s|--suffix {,}[+][:] | [+]:} + * {-p|--objpat {,}[+]: + * [-f|--file ] + * [-d|--dep ] + * [-l|--flat] + * [-m|--ignore-missing] + * [-w|--no-warn-exit] + * [-v|--verbose] + * [-h|-?|--help|--longhelp] + * [-V|--version] + * {} + * + * The recognizes as meta-symbols: + * %s: the full sourcename (w/o suffix) + * %[-][<][]p: the path part of the sourcename + * : skip first directories of the path, defaults to 0. + * < : directories are counted from the end. + * - : use, don't skip the counted directories. + * %n: the base of the sourcename (w/o suffix) + * %%: the character % + * %x: the character 'x' for every other character. + * + * The '+' in the pattern means that the sourcename itself should also be + * listed as dependency of the targetname. + * + *--------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "args.h" +#include "getargs.h" +#include "main.h" +#include "reader.h" +#include "nodes.h" +#include "util.h" +#include "version.h" + +/*-------------------------------------------------------------------------*/ +/* Stack structure for the tree output routines */ +typedef struct stacknode + { + struct stacknode * pNext; /* Next stacknode */ + struct stacknode * pPrev; /* Previous stacknode */ + NodeRef * pRef; /* Next node to work on */ + } +StackNode; +/* Misc Variables */ + +char * aVersion = "$VER: MkDepend " VERSION " (" __DATE__ ")"; + /* Amiga-style version string */ + +static struct stat aMakeStat; /* Stat buffer for the Makefile */ +static int bMakeExists; /* TRUE: Old Makefile exists */ + +static int returncode = RETURN_OK; /* Global return code */ + +static StackNode * pStack = NULL; /* Stackbase for tree output routine */ + +/*-----------------------------------------------------------------------*/ +void +set_rc (int code, int bAbort) + +/* Set the global returncode to given that is higher + * than the current one. + * If , exit immediately then. + */ + +{ + if (code > returncode) + returncode = code; + if (bAbort) + exit(returncode); +} + +/*-------------------------------------------------------------------------*/ +void +exit_doserr (int code) + +/* Print a message according to errno, then exit with . + */ + +{ + perror(aPgmName); + set_rc(code, TRUE); +} + +/*-------------------------------------------------------------------------*/ +void +exit_nomem (char * msg) + +/* Print a "Out of Memory." message and exit with RETURN_FAIL. + */ + +{ + printf("%s: Out of memory%s.\n", aPgmName, msg ? msg : ""); + set_rc(RETURN_FAIL, TRUE); +} + +/*-------------------------------------------------------------------------*/ +static int +readerror (Node * pNode, int rc, int bFindError) + +/* Print an error message after the file for couldn't be opened + * for reading resp. if the file could not be found. + * + * Result is the new return code, based on the given . + */ + +{ + size_t slen; /* Linelen so far */ + size_t len; + int bFlag; /* TRUE if at least one name has been printed in this line */ + NodeRef * pRef; + Node * pRNode; + + if (rc < RETURN_ERROR && (!bFindError || !bIgnoreMiss)) + rc = RETURN_WARN; + if (!bFindError) + { + perror(aPgmName); + printf("%s: Warning: Can't read '%s'.\n", aPgmName, pNode->pName); + } + else if (!bIgnoreMiss) + printf("%s: Warning: Can't find '%s'.\n", aPgmName, pNode->pName); + else + printf("%s: Info: Can't find '%s'.\n", aPgmName, pNode->pName); + + pRef = pNode->pUsers; + if (pRef) + { + printf("%s: Included at least by:", aPgmName); + slen = strlen(aPgmName)+25; + bFlag = FALSE; + + for ( ; pRef ; pRef = pRef->pNext) + { + pRNode = pRef->pNode; + len = strlen(pRNode->pName); + if (slen + len + 2 > 75) + { + if (bFlag) + putchar(','); + putchar('\n'); + slen = 0; + bFlag = FALSE; + } + if (!slen) + { + printf("%s: ", aPgmName); + slen = strlen(aPgmName)+5; + } + if (bFlag) + { + putchar(','); + slen++; + } + printf(" %s", pRNode->pName); + slen += len+1; + bFlag = TRUE; + } + if (slen) + putchar('\n'); + } + + return rc; +} + +/*-------------------------------------------------------------------------*/ +static int +readfiles (void) + +/* Read and analyse all files. + * If a file can't be read, print a message. + * Return 0 if all went well, RETURN_WARN if one of the files could not be + * found, RETURN_ERROR if one of the files could not be read properly. + */ + +{ + int rc; /* Return value */ + Node * pNode; /* Node of the file under examination */ + int srcRead; /* Number of source files read */ + const char * pName; /* Includefile name returned by reader */ + + rc = 0; + srcRead = 0; + + while (NULL != (pNode = nodes_todo()) ) + { + int bSystem; /* TRUE if it's a <> include */ + + if (pNode->flags & NODE_NOTFND) + continue; + + if (!reader_open(pNode->pName)) + { + rc = readerror(pNode, rc, FALSE); + continue; + } /* If (can't open pNode->pName) */ + + if (bVerbose) + { + printf(" reading %-65s\r", pNode->pName); fflush(stdout); + } + + while (NULL != (pName = reader_get(&bSystem)) ) + { + int index; /* index into aIncl[] */ + Node * pChild; /* Node of included file, NULL if not found */ + Node * pChFirst; /* First 'direct' child node */ + + for (index = -1, pChild = NULL, pChFirst = NULL + ; !pChild && index < iInclSize + ; index++) + { + char aName[FILENAME_MAX+1]; + char * pCName; /* aName[] cleaned up */ + char * pCBase; /* Namepart in *pCName */ + + /* Don't look for system includes in non-system directories. */ + if (index >= 0 && bSplitPath && bSystem && !aIncl[index].bSysPath) + continue; + + aName[FILENAME_MAX] = '\0'; + + if (index < 0) + { + /* Special case: look directly for the filename. + * If bSplitPath is given, or if the filename is absolute, + * use it unchanged. Else, use the path of the including + * file as anchor. + */ + + if (bSplitPath || '/' == *pName) + strcpy(aName, pName); + else + { + size_t oFilename; + + oFilename = (unsigned)(pNode->pBase - pNode->pName); + strncpy(aName, pNode->pName, oFilename); + strcpy(aName+oFilename, pName); + } + } + else + { + /* Standard case: build the pathname from the include + * pathname. + */ + + strcpy(aName, aIncl[index].pPath); + strcat(aName, pName); + } + + /* Cleanup the filename and check if it is listed in + * the tree + */ + pCName = util_getpath(aName, &pCBase); + if (!pCName) + { + if (bVerbose) + printf("%-78s\r", ""); + reader_close(); + exit_nomem(" (readfiles: 1. util_getpath)"); + } + + pChild = nodes_findadd(pCName, TRUE); + + if (pChild->flags & NODE_NEW) + { + /* New file: check if it exists and set up the + * node properly + */ + + struct stat aStat; + + pChild->pName = pCName; + pChild->pBase = pCBase; + + if (index >= 0) + { + pChild->pPath = util_getpath(pName, NULL); + pChild->iInclude = index; + } + else if (pNode->iInclude < 0 || pNode->flags & NODE_SOURCE) + { + pChild->pPath = util_getpath(pCName, NULL); + pChild->iInclude = -1; + } + else + { + strcpy(aName, pNode->pPath); + strcat(aName, pName); + pChild->pPath = util_getpath(aName, NULL); + pChild->iInclude = pNode->iInclude; + } + if (!pChild->pPath) + { + if (bVerbose) + printf("%-78s\r", ""); + reader_close(); + exit_nomem(" (readfiles: 2. util_getpath)"); + } + + pChild->flags = (short)(bSystem ? NODE_SYSTEM : 0); + + /* It's ok to not find <>-includes - they just don't have + * to appear in the output then. + */ + if (stat(pCName, &aStat)) + pChild->flags |= NODE_NOTFND | (bSystem ? NODE_IGNORE : 0); + } + + /* The pChFirst is needed if the include can't be found + * so we can keep track from where it is included. + */ + if (index < 0) + pChFirst = pChild; + + /* Test if the file for pChild exists. + */ + if (pChild->flags & NODE_NOTFND) + { + /* Make sure that the file is listed in the warnings + * if appropriate. + */ + if (index >= 0) + pChild->flags |= NODE_IGNORE; + else if ((pChild->flags & NODE_IGNORE) && !bSystem) + pChild->flags ^= NODE_IGNORE; + pChild = NULL; + } + + /* For absolute pNames, only index -1 is tried */ + if ('/' == *pName) + break; + } /* for (index = -1..iInclSize) */ + + assert(pChFirst != NULL); + + if (pChild ? nodes_depend(pChild, pNode) + : nodes_depend(pChFirst, pNode)) + { + if (bVerbose) + printf("%-78s\r", ""); + reader_close(); + exit_nomem(" (readfiles: nodes_depend)"); + } + } /* while(get pName from file */ + + if (bVerbose) + printf("%-78s\r", ""); + if (!reader_eof() || reader_close()) + { + perror(aPgmName); + printf("%s: Error reading '%s'\n", aPgmName, pNode->pName); + rc = RETURN_ERROR; + } + else if (pNode->flags & NODE_SOURCE) + { + srcRead++; + } + } /* while (nodes_todo()) */ + + if (!srcRead) + { + printf("%s: No source file read.\n", aPgmName); + rc = RETURN_ERROR; + } + + /* Walk tree and print all include files not found */ + nodes_initwalk(); + while (rc != RETURN_ERROR && NULL != (pNode = nodes_inorder()) ) + { + if ((pNode->flags & (NODE_NOTFND|NODE_IGNORE)) == NODE_NOTFND) + { + if (pNode->pUsers) + rc = readerror(pNode, rc, TRUE); + else + pNode->flags |= NODE_IGNORE; + } + } + + if (bVerbose) + fflush(stdout); + + return rc; +} + +/*-------------------------------------------------------------------------*/ +static void +make_objname ( char *pBuf, const char *pName, int slen + , const char * pObjExt, const char *pObjPat + ) + +/* Construct the name of the dependency target. + * + * pBuf : Buffer to construct the name in. + * pName : Name of the sourcefile. + * slen : Length of the sourcefile suffix. + * pObjExt: Object extensions for this sourcefile, may be NULL. + * pObjPat: Object pattern for this sourcefile, may be NULL. + * + * If pObjPat is not NULL, the pattern is used to construct the name. + * If pObjPat is NULL, the pObjExt string is appended to the sourcefile + * name (minus its source suffix) to construct the name. If pObjExt is + * NULL, the default object extension is used. + */ + +{ + size_t nlen, plen; + char * pBasename; + const char * pSrc, * pMark; + char *pDst, ch; + short flags; + long number; + +#define GOT_MINUS (1<<0) +#define GOT_ANGLE (1<<1) +#define GOT_NUMBER (1<<2) +#define NOT_DONE (1<<3) +#define NO_PATTERN (1<<4) + + number = 0; + nlen = strlen(pName)-slen; + if (!pObjPat) + { + strcpy(pBuf, pName); + if (pObjExt) + strcpy(pBuf+nlen, pObjExt); + else + strcpy(pBuf+nlen, sObjExt); + return; + } + pBasename = basename(pName); + plen = (unsigned)(pBasename-pName); + pSrc = pObjPat; + pDst = pBuf; + while ('\0' != (ch = *pSrc++)) + { + if (ch != '%') + { + *pDst++ = ch; + continue; + } + + pMark = pSrc; + flags = 0; + do + { + ch = *pSrc++; + switch(ch) + { + case '-': + if (flags & (GOT_MINUS|GOT_ANGLE|GOT_NUMBER)) + flags = NO_PATTERN; + else + flags |= GOT_MINUS|NOT_DONE; + break; + + case '<': + if (flags & (GOT_ANGLE|GOT_NUMBER)) + flags = NO_PATTERN; + else + flags |= GOT_ANGLE|NOT_DONE; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (flags & GOT_NUMBER) + flags = NO_PATTERN; + else + { + char * pTail; + + number = strtol(pSrc-1, &pTail, 10); + pSrc = pTail; + flags |= GOT_NUMBER|NOT_DONE; + } + break; + + case 's': + strncpy(pDst, pName, nlen); + pDst += nlen; + flags = 0; + break; + + case 'p': + if (pName != pBasename + && (!(flags & GOT_MINUS) || (flags & GOT_NUMBER)) + ) + { + const char * cp; + size_t cplen; + + cp = pName; + cplen = plen; + if (flags & GOT_NUMBER) + { + if (flags & GOT_ANGLE) + { + cp = pName+plen; + number++; + while (number) + { + --cp; + if (*cp == '/' || *cp == ':') + { + number--; + if (!number) + cp++; + } + if (cp == pName) + break; + } + } + else + { + cp = pName; + while (number && cp != pBasename) + { + if (*cp == '/' || *cp == ':') + { + number--; + } + cp++; + } + } + /* cp now points to the character after the + * determined path part. + */ + + if (flags & GOT_MINUS) + { + cplen = (unsigned)(cp - pName); + cp = pName; + } + else + cplen = plen - (cp-pName); + } + if (cplen) + { + strncpy(pDst, cp, cplen); + pDst += cplen; + } + } + flags = 0; + break; + + case 'n': + strncpy(pDst, pBasename, nlen-plen); + pDst += nlen-plen; + flags = 0; + break; + + case '%': + *pDst++ = '%'; + flags = 0; + break; + + default: + flags = NO_PATTERN; + break; + } + } + while (flags & NOT_DONE); + if (flags & NO_PATTERN) + pSrc = pMark; /* to be read again as normal text */ + } + *pDst = '\0'; + +#undef GOT_MINUS +#undef GOT_ANGLE +#undef GOT_NUMBER +#undef NOT_DONE +#undef NO_PATTERN +} + +/*-------------------------------------------------------------------------*/ +static int +output_node_tree (Node * pNode, int bUsers, int bSelect) + +/* Output the collected dependencies (bUsers is FALSE) resp. the collected + * users (bUsers is TRUE) in tree form into the Depfile. + * If is TRUE, only files marked as selected are considered. + * + * Return 0 on success, RETURN_WARN on a mild error, RETURN_ERROR on a + * severe error. + */ + +{ + StackNode * pTop; /* Stackbase, next free Stacktop */ + int rc; + Node * pRNode; /* referenced node */ + NodeRef * pRef; /* Next node to work on */ + int level; /* Depth in dependency tree */ + char aObjname[FILENAME_MAX+1]; + int i, len; + + assert(pNode); + + rc = RETURN_OK; + + /* If bSelect is true, but the pNode is not marked as selected, + * there is nothing to print. + */ + if (bSelect && !(pNode->flags & (NODE_SELECT|NODE_ISELECT))) + return rc; + + do + { + pTop = pStack; + + aObjname[FILENAME_MAX] = '\0'; + + /* Print name of the basenode */ + if (!bUsers) + { + if (reader_write(pNode->pName)) + { + rc = RETURN_ERROR; + break; /* outer while */ + } + if (reader_write((pNode->flags & NODE_NOTFND) ? " : (not found)\n" + : " :\n") + ) + { + rc = RETURN_ERROR; + break; /* outer while */ + } + } + + /* Initialize the tree output */ + pTop->pRef = bUsers ? pNode->pUsers : pNode->pDeps; + level = 1; + pRef = pTop->pRef; + + /* Tree output loop + */ + while (1) + { + int bQuoteIt; + + /* On end of this level: pop stack */ + if (!pRef) + { + if (pTop == pStack) + break; + pRef = pTop->pRef; + level--; + pTop = pTop->pPrev; + continue; + } + + /* If the file wasn't found, skip this node. */ + if (pRef->pNode->flags & NODE_IGNORE) + { + pRef = pRef->pNext; + continue; + } + + /* If bSelect is true, but this node is not marked, skip it + */ + if (bSelect && !(pRef->pNode->flags & (NODE_SELECT|NODE_ISELECT))) + { + pRef = pRef->pNext; + continue; + } + + /* Output filename of referenced node */ + pRNode = pRef->pNode; + if (!(pRNode->flags & NODE_SOURCE) && pRNode->iInclude >= 0) + { + if (aIncl[pRNode->iInclude].pSymbol) + strcpy(aObjname, aIncl[pRNode->iInclude].pSymbol); + else + strcpy(aObjname, aIncl[pRNode->iInclude].pPath); + strcat(aObjname, pRNode->pPath); + strcat(aObjname, pRNode->pBase); + } + else + { + strcpy(aObjname, pRNode->pName); + } + assert(aObjname[FILENAME_MAX] == '\0'); + len = (signed)strlen(aObjname); + bQuoteIt = (strchr(aObjname+1, ' ') != NULL); + for (i = 0; i < level-1; i++) + if (reader_writen(" ", 4)) + { + rc = RETURN_ERROR; + break; /* for */ + } + if (!rc && i < level && reader_writen(bUsers ? " -> " : " <- ", 4)) + rc = RETURN_ERROR; + if (!rc && bQuoteIt && reader_writen("'", 1)) + rc = RETURN_ERROR; + if (!rc && reader_writen(aObjname, (size_t)len)) + rc = RETURN_ERROR; + if (!rc && bQuoteIt && reader_writen("'", 1)) + rc = RETURN_ERROR; + if (!rc && (pRNode->flags & NODE_NOTFND) && reader_writen(" (not found)", 12)) + rc = RETURN_ERROR; + if (!rc && reader_writen("\n", 1)) + rc = RETURN_ERROR; + if (rc) + break; /* tree-while() */ + + /* Push this level onto the stack */ + if (!pTop->pNext) + { + StackNode * pNew; + pNew = malloc(sizeof(*pNew)); + if (!pNew) + { + if (bVerbose) + printf("%-78s\r", ""); + printf("%s: Out of memory.\n", aPgmName); + rc = RETURN_ERROR; + break; /* tree-while */ + } + memset(pNew, 0, sizeof(*pNew)); + pTop->pNext = pNew; + pNew->pPrev = pTop; + pTop = pNew; + } + else + { + pTop = pTop->pNext; + } + pTop->pRef = pRef->pNext; + + /* Continue with next deeper level */ + level++; + pRef = bUsers ? pRNode->pUsers : pRNode->pDeps; + } /* end while(1) - tree output loop */ + + } while(0); + + return rc; +} + +/*-------------------------------------------------------------------------*/ +static int +output_tree (void) + +/* Output the collected dependencies in tree form into the Depfile. + * Return 0 on success, RETURN_WARN on a mild error, RETURN_ERROR on a + * severe error. + */ + +{ + int rc; + char * pFile; /* Name of file to create */ + Node * pNode; + + pFile = sDepfile; + assert(pFile); + + rc = RETURN_OK; + do + { + if (!pStack) + { + /* Initialise stack */ + pStack = malloc(sizeof(*pStack)); + if (!pStack) + { + printf("%s: Out of memory\n", aPgmName); + rc = RETURN_ERROR; + break; + } + memset(pStack, 0, sizeof(*pStack)); + } + + /* Open files */ + if (reader_openrw(NULL, pFile)) + { + perror(aPgmName); + printf("%s: Can't write '%s'\n", aPgmName, pFile); + rc = RETURN_ERROR; + break; + } + + if (bVerbose) + { + printf(" creating '%s'\r", pFile); + fflush(stdout); + } + + /* Walk and output the dependencies and users of the + * skeleton sources. + */ + nodes_initwalk(); + while (rc != RETURN_ERROR && NULL != (pNode = nodes_inorder()) ) + { + if (!(pNode->flags & (NODE_IGNORE)) + && (pNode->flags & (NODE_SOURCE|NODE_AVOID)) == NODE_SOURCE + && (!IS_SELECT_MODE || (pNode->flags & (NODE_SELECT|NODE_ISELECT))) + && !((pNode->flags & NODE_NOTFND) && bIgnoreMiss) + ) + { + rc = output_node_tree(pNode, FALSE, IS_SELECT_MODE); + if (!rc) + rc = output_node_tree(pNode, TRUE, IS_SELECT_MODE); + if (!rc && reader_writen("\n", 1)) + rc = RETURN_ERROR; + } + } + + /* Walk and output the dependencies and users of the + * non-skeleton sources + */ + nodes_initwalk(); + while (rc != RETURN_ERROR && NULL != (pNode = nodes_inorder()) ) + { + if (!(pNode->flags & (NODE_IGNORE|NODE_SOURCE)) + && (!IS_SELECT_MODE || (pNode->flags & (NODE_SELECT|NODE_ISELECT))) + && !((pNode->flags & NODE_NOTFND) && bIgnoreMiss) + ) + { + rc = output_node_tree(pNode, FALSE, IS_SELECT_MODE); + if (!rc) + rc = output_node_tree(pNode, TRUE, IS_SELECT_MODE); + if (!rc && reader_writen("\n", 1)) + rc = RETURN_ERROR; + } + } + + if (rc == RETURN_ERROR) + { + int i = errno; + if (bVerbose) + printf("%-78s\r", ""); + errno = i; + perror(aPgmName); + printf("%s: Error writing '%s'.\n", aPgmName, pFile); + rc = RETURN_ERROR; + break; + } + + if (reader_writeflush()) + { + int i = errno; + if (bVerbose) + printf("%-78s\r", ""); + errno = i; + perror(aPgmName); + printf("%s: Error writing '%s'.\n", aPgmName, sDepfile); + rc = RETURN_ERROR; + break; + } + /* Finish up */ + if (bVerbose) + printf("%-78s\r", ""); + + if (reader_close()) + { + perror(aPgmName); + printf("%s: Error writing '%s'.\n", aPgmName, pFile); + rc = RETURN_ERROR; + break; + } + + } while(0); + + while (pStack) + { + StackNode * pTop; + + pTop = pStack; + pStack = pStack->pNext; + free(pTop); + } + + /* Error cleanup */ + if (rc == RETURN_ERROR) + { + reader_close(); + remove(pFile); + } + + if (bVerbose && rc != RETURN_ERROR) + printf("%-78s\r", ""); + + return rc; +} + +/*-------------------------------------------------------------------------*/ +static int +output_list_flat ( NodeRef * pRef, int startlen + , const char * pIndent, int bAsMake) + +/* Output the given list of dependencies/users, starting with pRef, + * with bAsMake interpreted as below. startlen is the line length + * printed at time of call, pIndent is the indentation string to use. + * + * Return 0 on success, RETURN_WARN on a mild error, RETURN_ERROR on a + * severe error. + */ + +{ + int rc; + Node * pRNode; + size_t slen; /* Linelen so far */ + size_t len; + size_t ilen; /* Length of pIndent */ + char aObjname[FILENAME_MAX+1]; + + assert(pIndent); + + rc = RETURN_OK; + aObjname[FILENAME_MAX] = '\0'; + + slen = (size_t)startlen; + ilen = strlen(pIndent); + + for ( ; rc != RETURN_ERROR && pRef; pRef = pRef->pNext) + { + pRNode = pRef->pNode; + + /* If the file was not found and system include, skip the node */ + if (pRNode->flags & (NODE_IGNORE)) + continue; + + /* If the file was not found and the user wants to ignore missing + * includes, skip the node. + */ + if ((pRNode->flags & NODE_NOTFND) && bIgnoreMiss) + continue; + + /* Construct the name of the dependee */ + aObjname[0] = ' '; + aObjname[1] = '\0'; + if (pRNode->iInclude >= 0) + { + short bQuoteIt; + char * pIPath; + + if (aIncl[pRNode->iInclude].pSymbol) + pIPath = aIncl[pRNode->iInclude].pSymbol; + else + pIPath = aIncl[pRNode->iInclude].pPath; + + bQuoteIt = (NULL != strchr(pIPath, ' ')) + || (NULL != strchr(pRNode->pPath, ' ')) + || (NULL != strchr(pRNode->pBase, ' ')); + + if (bQuoteIt) strcat(aObjname, "\""); + strcat(aObjname, pIPath); + strcat(aObjname, pRNode->pPath); + strcat(aObjname, pRNode->pBase); + if (bQuoteIt) strcat(aObjname, "\""); + } + else + { + short bQuoteIt; + bQuoteIt = (NULL != strchr(pRNode->pName, ' ')); + + if (bQuoteIt) strcat(aObjname, "\""); + strcat(aObjname, pRNode->pName); + if (bQuoteIt) strcat(aObjname, "\""); + } + assert(aObjname[FILENAME_MAX] == '\0'); + len = strlen(aObjname); + + /* Fit it into the line */ + if (slen > 0) + { + if (slen+len > 75) + { + if (reader_writen(bAsMake ? " \\\n" : "\n", (size_t)(bAsMake ? 3 : 1))) + { + rc = RETURN_ERROR; + break; /* inner for */ + } + slen = 0; + } + } + if (!slen) + { + if (reader_writen(pIndent, ilen)) + { + rc = RETURN_ERROR; + break; /* inner for */ + } + slen = ilen; + } + if (reader_writen(aObjname, len)) + { + rc = RETURN_ERROR; + break; /* inner for */ + } + slen += len; + } /* for () */ + if (slen && reader_writen("\n", 1)) + rc = RETURN_ERROR; + + return rc; +} + +/*-------------------------------------------------------------------------*/ +static int +output_node_flat (Node * pNode, int bAsMake, int bSelect) + +/* For a given node pNode Output the collected dependencies/users into a + * file, either as Makefile (bAsMake is TRUE) or as Depfile (bAsMake is + * FALSE). + * The file must have been opened by the caller in the reader module. + * If is true, only files marked as SELECTed are printed. + * + * Return 0 on success, RETURN_WARN on a mild error, RETURN_ERROR on a + * severe error. + */ + +{ + int rc; + NodeRef * pList, * pRef; + long suffix; /* Suffix index */ + size_t slen; /* Linelen so far */ + size_t len; + char aObjname[FILENAME_MAX+1]; + + assert(pNode); + + rc = RETURN_OK; + aObjname[FILENAME_MAX] = '\0'; + + /* If bSelect is true, but the pNode is not marked as selected, + * there is nothing to print. + */ + if (bSelect && !(pNode->flags & (NODE_SELECT|NODE_ISELECT))) + return rc; + + /* First, output the dependencies list always */ + pList = nodes_deplist(pNode, FALSE, bSelect); + assert(pList); + pRef = pList; + + /* Check for a given suffix. + * Search backwards in case later definitions overwrote + * earlier ones. + */ + slen = strlen(pNode->pName); + len = 0; + if (!bAsMake) + suffix = -1; + else + for (suffix = aSrcExt.size-1; suffix >= 0; suffix--) + { + len = strlen(aSrcExt.strs[suffix]); + if (!strcmp(pNode->pName+slen-len, aSrcExt.strs[suffix])) + break; + } + + do { + /* Construct the name of the dependency target and write it */ + if (suffix >= 0) + { + char * pObjPat, * pObjExt; + short bQuoteIt; + + pObjExt = aObjExt.size ? aObjExt.strs[suffix] : NULL; + pObjPat = aObjPat.size ? aObjPat.strs[suffix] : NULL; + + make_objname( aObjname, pNode->pName, (int)len, pObjExt, pObjPat); + assert(aObjname[FILENAME_MAX] == '\0'); + bQuoteIt = (NULL != strchr(aObjname, ' ')); + + if ((bQuoteIt && reader_write("\"")) + || reader_write(aObjname) + || (bQuoteIt && reader_write("\"")) + ) + { + rc = RETURN_ERROR; + break; /* while */ + } + slen = strlen(aObjname); + + if (!((pObjExt || pObjPat) ? bGiveSrc[suffix] : bDefGSrc)) + pRef = pRef->pNext; + } + else + { + short bQuoteIt; + + bQuoteIt = (NULL != strchr(pNode->pName, ' ')); + if ((bQuoteIt && reader_write("\"")) + || reader_write(pNode->pName) + || (bQuoteIt && reader_write("\"")) + ) + { + rc = RETURN_ERROR; + break; /* outer while */ + } + pRef = pRef->pNext; + } + + if (bAsMake) + { + if (reader_write(" :")) + { + rc = RETURN_ERROR; + break; /* while */ + } + slen += 2; + rc = output_list_flat(pRef, (int)slen, " ", TRUE); + } + else + { + if (reader_write(" :\n")) + { + rc = RETURN_ERROR; + break; /* while */ + } + if (pRef) + rc = output_list_flat(pRef, 0, " <-", FALSE); + } + nodes_freelist(pList); + slen = 0; + + /* If demanded, output the list of users as well */ + if (RETURN_OK == rc && !bAsMake) + { + pList = nodes_deplist(pNode, TRUE, bSelect); + assert(pList); + if (pList->pNext) + rc = output_list_flat(pList->pNext, 0, " ->", FALSE); + nodes_freelist(pList); + } + + } while(0); + + if (reader_writen("\n", 1)) + rc = RETURN_ERROR; + + return rc; +} + +/*-------------------------------------------------------------------------*/ +static int +output (int bAsMake) + +/* Output the collected dependencies into the Makefile (bAsMake is TRUE) + * resp. into the Depfile (bAsMake is FALSE). + * Return 0 on success, RETURN_WARN on a mild error, RETURN_ERROR on a + * severe error. + */ + +{ + int rc; + char * pBackup; /* Name of the Makefilebackup */ + char * pFile; /* Name of file to create */ + Node * pNode; + + pBackup = NULL; + pFile = bAsMake ? sMake : sDepfile; + + assert(pFile); + + /* Rename old Makefile if necessary */ + if (bAsMake && bMakeExists) + { + pBackup = (char *)malloc(strlen(sMake)+5); + strcpy(pBackup, sMake); + strcat(pBackup, ".bak"); + remove(pBackup); + if (rename(sMake, pBackup)) + { + perror(aPgmName); + printf("%s: Can't rename '%s' to '%s'.\n", aPgmName, sMake, pBackup); + free(pBackup); + return RETURN_ERROR; + } + } + + rc = RETURN_OK; + do + { + char * pTagline; + + /* Open files */ + if (reader_openrw(pBackup, pFile)) + { + perror(aPgmName); + printf("%s: Can't write '%s'\n", aPgmName, pFile); + rc = RETURN_ERROR; + break; + } + + if (bVerbose) + { + printf(" %s '%s'\r", (bMakeExists && bAsMake) ? "updating" : "creating", pFile); + fflush(stdout); + } + + /* Copy the Makefile up to the tagline */ + pTagline = IS_SELECT_MODE + ? "# --- DO NOT MODIFY THIS LINE -- SELECTED AUTO-DEPENDS FOLLOW ---\n" + : "# --- DO NOT MODIFY THIS LINE -- AUTO-DEPENDS FOLLOW ---\n"; + if (bAsMake && reader_copymake(pTagline)) + { + int i = errno; + if (bVerbose) + printf("%-78s\r", ""); + errno = i; + perror(aPgmName); + printf("%s: Error copying '%s' to '%s'.\n", aPgmName, pBackup, sMake); + rc = RETURN_ERROR; + break; + } + + /* Walk and output the dependencies and users. + * First all the files specified as root. + */ + nodes_initwalk(); + while (rc != RETURN_ERROR && NULL != (pNode = nodes_inorder()) ) + { + if ((pNode->flags & (NODE_SOURCE|NODE_AVOID|NODE_NOTFND)) == NODE_SOURCE) + rc = output_node_flat(pNode, bAsMake, IS_SELECT_MODE); + } /* while (treewalk */ + + /* Second (if wanted), all the files not specified as root. */ + if (RETURN_OK == rc && !bAsMake) + { + nodes_initwalk(); + while (rc != RETURN_ERROR && NULL != (pNode = nodes_inorder()) ) + { + if ((pNode->flags & (NODE_SOURCE|NODE_AVOID)) != NODE_SOURCE + && !(pNode->flags & NODE_NOTFND) + ) + rc = output_node_flat(pNode, bAsMake, IS_SELECT_MODE); + } /* while (treewalk */ + } + + if (rc == RETURN_ERROR) + { + int i = errno; + if (bVerbose) + printf("%-78s\r", ""); + errno = i; + perror(aPgmName); + printf("%s: Error writing '%s'.\n", aPgmName, pFile); + rc = RETURN_ERROR; + break; + } + + pTagline = IS_SELECT_MODE + ? "# --- DO NOT MODIFY THIS LINE -- SELECTED AUTO-DEPENDS PRECEDE ---\n" + : "# --- DO NOT MODIFY THIS LINE -- AUTO-DEPENDS PRECEDE ---\n"; + if (bAsMake && reader_copymake2(pTagline)) + { + int i = errno; + if (bVerbose) + printf("%-78s\r", ""); + errno = i; + perror(aPgmName); + printf("%s: Error copying '%s' to '%s'.\n", aPgmName, pBackup, sMake); + rc = RETURN_ERROR; + break; + } + else if (!bAsMake && reader_writeflush()) + { + int i = errno; + if (bVerbose) + printf("%-78s\r", ""); + errno = i; + perror(aPgmName); + printf("%s: Error writing '%s'.\n", aPgmName, sDepfile); + rc = RETURN_ERROR; + break; + } + /* Finish up */ + if (bVerbose) + printf("%-78s\r", ""); + + if (reader_close()) + { + perror(aPgmName); + printf("%s: Error writing '%s'.\n", aPgmName, pFile); + rc = RETURN_ERROR; + break; + } + + if (bAsMake && bMakeExists && chmod(sMake, aMakeStat.st_mode)) + { + /* perror(aPgmName); */ + printf("%s: Warning: Can't update mode of '%s'.\n", aPgmName, sMake); + rc = RETURN_WARN; + } + + if (pBackup && remove(pBackup)) + { + perror(aPgmName); + printf("%s: Warning: Can't remove backup '%s'.\n", aPgmName, pBackup); + rc = RETURN_WARN; + break; + } + + } while(0); + + /* Error cleanup */ + if (rc == RETURN_ERROR) + { + reader_close(); + remove(pFile); + if (pBackup && rename(pBackup, sMake)) + { + perror(aPgmName); + printf("%s: Can't restore '%s' from backup '%s'\n", aPgmName, sMake, pBackup); + } + } + + if (bVerbose && rc != RETURN_ERROR) + printf("%-78s\r", ""); + + if (pBackup) + free(pBackup); + + return rc; +} + +/*-------------------------------------------------------------------------*/ +int main (int argc, char *argv[]) + +{ + int i; + + reader_init(); + + /* Get arguments, set up defaults */ + parseargs(argc, argv); + if (!aFiles.size) + { + if (add_expfile(&aFiles, "*.c")) + exit_nomem(" (main: add_expfile)"); + } + if (!sObjExt) + sObjExt = ".o"; + + if (!aSrcExt.size) + { + if (array_addfile(&aSrcExt, ".c") || array_addfile(&aObjExt, NULL)) + exit_nomem(" (main: add suffix)"); + if (array_addfile(&aSrcExt, ".cc") || array_addfile(&aObjExt, NULL)) + exit_nomem(" (main: add suffix)"); + if (array_addfile(&aSrcExt, ".cp") || array_addfile(&aObjExt, NULL)) + exit_nomem(" (main: add suffix)"); + if (array_addfile(&aSrcExt, ".cpp") || array_addfile(&aObjExt, NULL)) + exit_nomem(" (main: add suffix)"); + if (array_addfile(&aSrcExt, ".cxx") || array_addfile(&aObjExt, NULL)) + exit_nomem(" (main: add suffix)"); + if (array_addfile(&aSrcExt, ".C") || array_addfile(&aObjExt, NULL)) + exit_nomem(" (main: add suffix)"); + if (array_addfile(&aSrcExt, ".CC") || array_addfile(&aObjExt, NULL)) + exit_nomem(" (main: add suffix)"); + if (array_addfile(&aSrcExt, ".CP") || array_addfile(&aObjExt, NULL)) + exit_nomem(" (main: add suffix)"); + if (array_addfile(&aSrcExt, ".CPP") || array_addfile(&aObjExt, NULL)) + exit_nomem(" (main: add suffix)"); + if (array_addfile(&aSrcExt, ".CXX") || array_addfile(&aObjExt, NULL)) + exit_nomem(" (main: add suffix)"); + } + + if (bVerbose) + { + printf("MkDepend %s -- Make Dependency Generator\n", VERSION); + putchar('\n'); + } + + /* Look for the Makefile to modify */ + if (sMake) + { + bMakeExists = !stat(sMake, &aMakeStat); + } + else if (!sDepfile) + { + sMake = "Makefile"; + bMakeExists = !stat(sMake, &aMakeStat); + if (!bMakeExists) + { + sMake = "makefile"; + bMakeExists = !stat(sMake, &aMakeStat); + } + if (!bMakeExists) + { + sMake = "Makefile.mk"; + bMakeExists = !stat(sMake, &aMakeStat); + } + if (!bMakeExists) + { + sMake = "makefile.mk"; + bMakeExists = !stat(sMake, &aMakeStat); + } + if (!bMakeExists) + { + sMake = "GNUMakefile"; + bMakeExists = !stat(sMake, &aMakeStat); + } + if (!bMakeExists) + sMake = "Makefile"; + } + + /* Add the source files to the tree */ + if (!aFiles.size) + { + printf("%s: No files given.\n", aPgmName); + set_rc(RETURN_WARN, TRUE); + } + else + { + struct stat aStat; /* buffer for stat() */ + + for (i = 0; i < aFiles.size; i++) + { + if (stat(aFiles.strs[i], &aStat)) + { + printf("%s: Warning: Can't find source '%s'.\n", aPgmName + , aFiles.strs[i]); + set_rc(RETURN_WARN, FALSE); + } + else if (nodes_addsource(aFiles.strs[i], FALSE)) + exit_nomem(" (main: add source)"); + } + + /* If this leaves us with no source files at all, readfiles() + * will complain. + */ + } + + /* Mark the exceptional files */ + for (i = 0; i < aAvoid.size; i++) + { + if (nodes_addsource(aAvoid.strs[i], TRUE)) + exit_nomem(" (main: add avoided source)"); + } + + /* Read and analyse all those files */ + set_rc(readfiles(), 0); + + /* Handle the selection of files */ + if (IS_SELECT_MODE) + { + Node * pNode; + + /* First, mark all nodes with selected files */ + for (i = 0; i < aSelect.size; i++) + { + pNode = nodes_find(aSelect.strs[i]); + if (pNode) + pNode->flags |= NODE_SELECT; + } + + /* Now walk the tree and propagate the select information */ + nodes_initwalk(); + while (NULL != (pNode = nodes_inorder()) ) + { + if (!(pNode->flags & (NODE_IGNORE)) + && (pNode->flags & (NODE_SOURCE|NODE_AVOID)) == NODE_SOURCE) + { + NODES_UNMARK(pNode); + nodes_mark_select(pNode); + } + } + } + + if (returncode < RETURN_ERROR && sMake) + set_rc(output(TRUE), 0); + + if (returncode < RETURN_ERROR && sMake && bVerbose) + printf("%s '%s'.\n", bMakeExists ? "Updated" : "Created", sMake); + + if (returncode < RETURN_ERROR && sDepfile) + set_rc(bFlat ? output(FALSE) : output_tree(), 0); + + if (returncode < RETURN_ERROR && sDepfile && bVerbose) + printf("Created '%s'.\n", sDepfile); + + return (returncode == RETURN_WARN && bNoWarnExit) ? RETURN_OK : returncode; +} + +/***************************************************************************/ diff --git a/src/bin/mkdepend/main.h b/src/bin/mkdepend/main.h new file mode 100644 index 0000000000..6abdb545b8 --- /dev/null +++ b/src/bin/mkdepend/main.h @@ -0,0 +1,37 @@ +/* $Id: main.h 1.4 Wed, 01 Mar 2000 21:15:40 -0700 lars $ */ + +/*--------------------------------------------------------------------------- +** Copyright (c) 1995-2000 by Lars Düning. All Rights Reserved. +** This file is free software. For terms of use see the file LICENSE. +**--------------------------------------------------------------------------- +*/ + +#ifndef __MAIN_H__ +#define __MAIN_H__ 1 + +/* Return codes */ + +#define RETURN_OK 0 /* Everything is ok */ +#define RETURN_WARN 1 /* A warning was issued */ +#define RETURN_ERROR 2 /* An error occured */ +#define RETURN_FAIL 3 /* A fatal error occured */ + +/* Boolean values */ + +#define FALSE 0 +#define TRUE 1 + +/* Macros */ + +#define IS_SELECT_MODE (aSelect.size != 0) + /* The macro returns TRUE if only selected dependencies are to be + * generated. + */ + +/* Prototypes */ + +extern void set_rc (int, int); +extern void exit_doserr (int); +extern void exit_nomem (char *); + +#endif diff --git a/src/bin/mkdepend/mkdepend.rdef b/src/bin/mkdepend/mkdepend.rdef new file mode 100644 index 0000000000..90e86d2909 --- /dev/null +++ b/src/bin/mkdepend/mkdepend.rdef @@ -0,0 +1,11 @@ +resource app_version +{ + major = 1, + middle = 7, + minor = 1, + variety = 5, + internal = 0, + short_info = "1.7.1", + long_info = "1.7.1 © 1995-2001 Lars Düning. All rights reserved." +}; + diff --git a/src/bin/mkdepend/nodes.c b/src/bin/mkdepend/nodes.c new file mode 100644 index 0000000000..0a5fe6aa50 --- /dev/null +++ b/src/bin/mkdepend/nodes.c @@ -0,0 +1,587 @@ +/* $Id: nodes.c 1.6 Wed, 01 Mar 2000 21:15:40 -0700 lars $ */ + +/*--------------------------------------------------------------------------- + * Management of the dependency tree and nodes. + * + * Copyright (c) 1995-2000 by Lars Düning. All Rights Reserved. + * This file is free software. For terms of use see the file LICENSE. + *--------------------------------------------------------------------------- + * Each file (source or include) is associated one node which is kept in + * a binary search tree with the name as index. Glued to each node is + * a list of that nodes which files the master nodes includes. + * Each node contains an extra node pointer the module uses to implement + * tree-independant lists, namely the TODO list of files still to read + * and a virtual stack for inorder tree traversals. + * + * Adding and retrieving the files to analyse is done using the functions + * nodes_addsource() + * nodes_depend() + * nodes_todo() + * nodes_select_mark() + * + * The output of the dependency tree is done inorder using + * nodes_initwalk() + * nodes_inorder() + * + * The list of dependencies for one single node is managed using + * nodes_deplist() + * nodes_freelist() + * + * Tree traversal and creation must not be mixed! + *--------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include "main.h" +#include "nodes.h" +#include "util.h" + +/*-------------------------------------------------------------------------*/ + +/* Types for block allocation of Nodes and NodeRefs. + * Note that Nodes are never freed. + */ + +#define NBLOCKSIZE 16 + +typedef struct nodeblock + { + struct nodeblock * pNext; /* Next nodeblock */ + int iFree; /* Number of first free node */ + Node aNodes[NBLOCKSIZE]; /* Block of nodes */ + } +NodeBlock; + +#define RBLOCKSIZE 16 + +typedef struct refblock + { + struct refblock * pNext; /* Next refblock */ + int iFree; /* Number of first free noderef */ + NodeRef aRefs[NBLOCKSIZE]; /* Block of noderefs */ + } +RefBlock; + +/*-------------------------------------------------------------------------*/ + +static NodeBlock *pFreeNBlocks = NULL; /* Nodeblocks */ +static RefBlock *pFreeRBlocks = NULL; /* Refblocks */ +static NodeRef *pFreeRefs = NULL; /* List of free NodeRefs */ +static Node *pTree = NULL; /* Dependency tree */ +static Node *pList = NULL; /* List of (tree) nodes */ + /* The List is used as TODO list for the files to analyse as well as + * stack simulation for the tree traversal. + */ + +/*-------------------------------------------------------------------------*/ +static Node * +nodes_newnode (void) + +/* Allocate a new Node. + * + * Result: + * Pointer to the new Node, or NULL on error. + * + * The memory of the Node is cleared, except for .flags which is set + * to NODE_NEW. + */ + +{ + Node * pNode; + + if (!pFreeNBlocks || !pFreeNBlocks->iFree) + { + NodeBlock * pNewBlock; + pNewBlock = (NodeBlock *)malloc(sizeof(*pNewBlock)); + if (!pNewBlock) + return NULL; + memset(pNewBlock, 0, sizeof(*pNewBlock)); + pNewBlock->iFree = NBLOCKSIZE; + pNewBlock->pNext = pFreeNBlocks; + pFreeNBlocks = pNewBlock; + } + pNode = &(pFreeNBlocks->aNodes[--pFreeNBlocks->iFree]); + pNode->flags |= NODE_NEW; + pNode->iInclude = -1; + return pNode; +} + +/*-------------------------------------------------------------------------*/ +static NodeRef * +nodes_newref (void) + +/* Allocate a new NodeRef. + * + * Result: + * Pointer to the new NodeRef, or NULL on error. + * + * The memory of the NodeRef is cleared. + */ + +{ + NodeRef * pRef; + + if (pFreeRefs) + { + pRef = pFreeRefs; + pFreeRefs = pRef->pNext; + } + else + { + if (!pFreeRBlocks || !pFreeRBlocks->iFree) + { + RefBlock * pNewBlock; + pNewBlock = (RefBlock *)malloc(sizeof(*pNewBlock)); + if (!pNewBlock) + return NULL; + pNewBlock->iFree = NBLOCKSIZE; + pNewBlock->pNext = pFreeRBlocks; + pFreeRBlocks = pNewBlock; + } + pRef = &(pFreeRBlocks->aRefs[--pFreeRBlocks->iFree]); + } + memset(pRef, 0, sizeof(*pRef)); + return pRef; +} + +/*-------------------------------------------------------------------------*/ +Node * +nodes_findadd (const char *pName, int bAddToList) + +/* Find the node associated with file *pName. + * If necessary, add the node. + * + * pName: Pointer to the (minimalized) filename of the node. + * bAddToList: If true, a newly allocated node is also added to + * the TODO list. + * + * Result: + * Pointer to the node associated with this filename, or NULL on error. + * + * If the node has been added by this call, NODE_NEW is set in its .flags, + * but it is empty otherwise. + */ + +{ + Node * pThis, * pPrev; + int i; + + assert(pName); + + if (!pTree) + { + pTree = nodes_newnode(); + if (bAddToList) + { + pTree->pNext = pList; + pList = pTree; + } + return pTree; + } + + pPrev = NULL; + pThis = pTree; + while(pThis) + { + i = strcmp(pThis->pName, pName); + if (!i) + return pThis; + pPrev = pThis; + if (i > 0) + pThis = pThis->pLeft; + else + pThis = pThis->pRight; + } + pThis = nodes_newnode(); + if (pThis) + { + if (i > 0) + pPrev->pLeft = pThis; + else + pPrev->pRight = pThis; + + if (bAddToList) + { + pThis->pNext = pList; + pList = pThis; + } + } + return pThis; +} + +/*-------------------------------------------------------------------------*/ +Node * +nodes_find (const char *pName) + +/* Find the node associated with file *pName. + * + * pName: Pointer to the (minimalized) filename of the node. + * + * Result: + * Pointer to the node associated with this filename, or NULL on error. + */ + +{ + Node * pThis, * pPrev; + int i; + + assert(pName); + + if (!pTree) + return NULL; + + pPrev = NULL; + pThis = pTree; + while(pThis) + { + i = strcmp(pThis->pName, pName); + if (!i) + return pThis; + pPrev = pThis; + if (i > 0) + pThis = pThis->pLeft; + else + pThis = pThis->pRight; + } + return NULL; +} + +/*-------------------------------------------------------------------------*/ +int +nodes_addsource (char *pName, int bAvoid) + +/* Add a source file to the dependency tree. + * + * pName : Name of the file to add (it will be duplicated). + * bAvoid: TRUE if this is a file to be avoided. + * + * Result: + * 0 on success, non-0 on error (out of memory). + * + * The file is also added to the internal TODO list of files if it is + * a genuine source file. + */ + +{ + Node *pNode; + char *pBasename; + + assert(pName); + + pName = util_getpath(pName, &pBasename); + if (!pName) + return 1; + pNode = nodes_findadd(pName, !bAvoid); + if (!pNode) + return 1; + if (pNode->flags & NODE_NEW) + { + pNode->flags = NODE_SOURCE; + pNode->pName = pName; + pNode->pBase = pBasename; + pNode->pPath = util_getpath(pName, NULL); + if (!pNode->pPath) + return 1; + } + else + free(pName); + + if (bAvoid) + pNode->flags |= NODE_AVOID; + + return 0; +} + +/*-------------------------------------------------------------------------*/ +int +nodes_depend (Node * pChild, Node * pParent) + +/* Add node pChild to the list of dependees of pParent, vice versa add + * pParent to the list of users of pName. + * + * pChild : Node of the file included by pParent. + * pParent: Node which depends on pChild. + * + * Result: + * 0 on success, non-0 on error (out of memory). + * + * The file is added to the dependee- and user-lists. + */ + +{ + NodeRef * pRef; + + assert(pParent); + assert(pChild); + + /* Add pChild to pParent->pDeps if not already there */ + for ( pRef = pParent->pDeps + ; pRef && pRef->pNode != pChild + ; pRef = pRef->pNext + ) /* SKIP */; + if (!pRef) + { + pRef = nodes_newref(); + if (!pRef) + return 1; + pRef->pNode = pChild; + pRef->pNext = pParent->pDeps; + pParent->pDeps = pRef; + } + + /* Add pParent to pChild->pUsers if not already there */ + for ( pRef = pChild->pUsers + ; pRef && pRef->pNode != pParent + ; pRef = pRef->pNext + ) /* SKIP */; + if (!pRef) + { + pRef = nodes_newref(); + if (!pRef) + return 1; + pRef->pNode = pParent; + pRef->pNext = pChild->pUsers; + pChild->pUsers = pRef; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ +Node * +nodes_todo (void) + +/* Return the tree node of the next file to analyse. + * + * Result: + * Pointer to the node of the next file to analyse, or NULL if there is + * none. + */ + +{ + Node * pNode; + + /* NODE_DONE should not happen, but checking it is free */ + while (pList && (pList->flags & (NODE_AVOID|NODE_DONE))) + pList = pList->pNext; + if (pList) + { + pNode = pList; + pList = pList->pNext; + pNode->flags |= NODE_DONE; + } + else + pNode = NULL; + return pNode; +} + +/*-------------------------------------------------------------------------*/ +int +nodes_mark_select (Node *pNode) + +/* Check if this node, or one of its dependency nodes has been marked + * as selected. If yes, mark pNode as well and return TRUE, otherwise + * return FALSE. + * + * This is a recursive function. We avoid loops by temporarily marking + * the node. + */ + +{ + int rc; + NodeRef * pRef; + + assert(pNode); + + if (NODES_MARKED(pNode)) + return FALSE; + + if (pNode->flags & NODE_ISELECT) + return TRUE; + + NODES_MARK(pNode); + /* First, recurse into the dependency nodes */ + rc = FALSE; + for (pRef = pNode->pDeps; pRef != NULL; pRef = pRef->pNext) + { + if (nodes_mark_select(pRef->pNode)) + rc = TRUE; + } + + if (rc && !(pNode->flags & NODE_SELECT)) + { + pNode->flags |= NODE_ISELECT; + } + + NODES_UNMARK(pNode); + + return rc || (pNode->flags & (NODE_SELECT|NODE_ISELECT)); +} /* nodes_mark_select() */ + +/*-------------------------------------------------------------------------*/ +void +nodes_initwalk (void) + +/* Initialise a tree traversal. + */ + +{ + assert(!pList); + pList = pTree; + if (pList) + { + pList->iStage = 0; + pList->pNext = NULL; + } +} + +/*-------------------------------------------------------------------------*/ +Node * +nodes_inorder (void) + +/* Return the next Node of an inorder tree traversal. + * + * Result: + * Pointer to the next node, or NULL if traversal is complete. + */ + +{ + Node *pNode = NULL; + if (!pList) + return NULL; + while (pList) + { + switch (pList->iStage) + { + case 0: + pList->iStage++; + pNode = pList->pLeft; + break; + case 1: + pList->iStage++; + return pList; + break; + case 2: + pList->iStage++; + pNode = pList->pRight; + break; + case 3: + pList = pList->pNext; + pNode = NULL; + break; + default: + assert(0); + break; + } + if (pNode) + { + pNode->pNext = pList; + pList = pNode; + pNode->iStage = 0; + } + } + return NULL; +} + +/*-------------------------------------------------------------------------*/ +NodeRef * +nodes_deplist (Node * pNode, int bUsers, int bSelect) + +/* Gather the list of dependees or users of *pNode. + * + * pNode: the Node to get the list for. + * bUsers: TRUE: gather the list of users instead of the dependees. + * bSelect: TRUE: gather only files marked as NODE_SELECT. + * + * Result: + * Pointer to the list of dependees/users, or NULL if an error occurs. + * First entry in the list is pNode itself. + */ + +{ + NodeRef *pDList; + NodeRef *pMark; + NodeRef *pThis; + NodeRef *pRover; + Node *pDep; + + assert(pNode); + pDList = nodes_newref(); + if (!pDList) + return NULL; + + /* The list of dependencies is gathered in a wide search. */ + pDList->pNode = pNode; + NODES_MARK(pNode); + for ( pThis = pDList, pMark = pDList + ; pMark + ; pMark = pMark->pNext + ) + { + for ( pRover = (bUsers ? pMark->pNode->pUsers : pMark->pNode->pDeps) + ; pRover + ; pRover = pRover->pNext + ) + { + pDep = pRover->pNode; + if (!NODES_MARKED(pDep)) + { + pThis->pNext = nodes_newref(); + if (!pThis->pNext) + return NULL; + pThis = pThis->pNext; + pThis->pNode = pDep; + NODES_MARK(pDep); + } + } + } + + /* If bSelect is requested, remove all non-SELECT nodes from the list + * (but always keep the first one). + */ + if (bSelect) + { + pThis = pDList; + while (pThis->pNext != NULL) + { + pMark = pThis->pNext; + if (!(pMark->pNode->flags & NODE_SELECT)) + { + NODES_UNMARK(pMark->pNode); + pThis->pNext = pMark->pNext; + pMark->pNext = pFreeRefs; + pFreeRefs = pMark; + } + else + pThis = pMark; + } + } + + return pDList; +} + +/*-------------------------------------------------------------------------*/ +void +nodes_freelist (NodeRef * pDList) + +/* Free a dependency list produced earlier by nodes_deplist(). + * + * pDList: Base of the list to free. + */ + +{ + NodeRef * pThis; + + while (pDList) + { + NODES_UNMARK(pDList->pNode); + pThis = pDList; + pDList = pDList->pNext; + pThis->pNext = pFreeRefs; + pFreeRefs = pThis; + } +} + +/***************************************************************************/ diff --git a/src/bin/mkdepend/nodes.h b/src/bin/mkdepend/nodes.h new file mode 100644 index 0000000000..65d7556fec --- /dev/null +++ b/src/bin/mkdepend/nodes.h @@ -0,0 +1,95 @@ +/* $Id: nodes.h 1.5 Wed, 01 Mar 2000 21:15:40 -0700 lars $ */ + +/*--------------------------------------------------------------------------- +** Copyright (c) 1995-2000 by Lars Düning. All Rights Reserved. +** This file is free software. For terms of use see the file LICENSE. +**--------------------------------------------------------------------------- +*/ + +#ifndef __NODES_H__ +#define __NODES_H__ 1 + +struct noderef; /* forward */ + +/* Node structure to build the dependency tree + */ +typedef struct node + { + struct node * pLeft, * pRight; /* Tree pointers */ + struct node * pNext; /* List of Files to do; stack for traversals */ + + char * pName; + /* Searchkey: Name of this file. + * This is the minimal full pathname with which the file can be read. + * For source files (and files to be avoided) it is identical with the name + * given on the commandline. + */ + char * pBase; + /* The filename part of the the file's pathname. + * This is a pointer into pName. + */ + char * pPath; + /* The directory part of the file's pathname. + * Relative paths are interpreted depending on the type of the file: + * source files are relative to the current working directory, + * non-source files are relative to aIncl[.iInclude]. + */ + int iInclude; + /* Included non-sourcefiles: the index into aIncl[]/aSymbol[]. + * A negative index denotes an absolute filename, or one relative + * to the cwd. + */ + short flags; /* misc flags */ + struct noderef * pDeps; /* What this file depends on */ + struct noderef * pUsers; /* Who depends on this file */ + short iStage; /* Stage counter for tree traversals */ + } +Node; + +/* Node.flags + */ +#define NODE_MARK (1<<0) /* Generic Node marker, used e.g. in tree walks */ +#define NODE_NEW (1<<1) /* Node is unused */ +#define NODE_SOURCE (1<<2) /* Node is a skeleton source */ +#define NODE_AVOID (1<<3) /* Node is not part of the skeleton sources */ +#define NODE_NOTFND (1<<4) /* Node describes a non-existant file */ +#define NODE_DONE (1<<5) /* Node has been evaluated */ +#define NODE_SYSTEM (1<<6) /* File is an <>-include */ +#define NODE_IGNORE (1<<7) /* Data for this node shall not be printed */ +#define NODE_SELECT (1<<8) /* Marks selected include files */ +#define NODE_ISELECT (1<<9) /* Marks nodes between source and selected + * include files */ + +/* Node marking + */ +#define NODES_MARK(node) ((node)->flags |= NODE_MARK) +#define NODES_UNMARK(node) ((node)->flags &= (short)(~NODE_MARK)) +#define NODES_MARKED(node) ((node)->flags & NODE_MARK) + +/* Test if the _SYSTEM flag has a specific value + */ +#define NODES_SYSTEST(node,value) ( (node)->flags & NODE_SYSTEM ? (value) : !(value) ) + +/* Reference structure to treenodes + */ +typedef struct noderef + { + struct noderef * pNext; /* next NodeRef */ + Node * pNode; /* referenced Node */ + } +NodeRef; + +/* Prototypes */ + +extern Node * nodes_findadd (const char *, int); +extern Node * nodes_find (const char *); +extern int nodes_addsource (char *, int); +extern int nodes_depend (Node *, Node *); +extern int nodes_mark_select (Node *); +extern Node * nodes_todo (void); +extern void nodes_initwalk (void); +extern Node * nodes_inorder (void); +extern NodeRef * nodes_deplist (Node *, int, int); +extern void nodes_freelist (NodeRef *); + +#endif diff --git a/src/bin/mkdepend/reader.c b/src/bin/mkdepend/reader.c new file mode 100644 index 0000000000..83dd4cc52d --- /dev/null +++ b/src/bin/mkdepend/reader.c @@ -0,0 +1,658 @@ +/* $Id: reader.c 1.9 Wed, 01 Mar 2000 22:36:57 -0700 lars $ */ + +/*--------------------------------------------------------------------------- +** Reading of C-Sources and filter for #include statements. +** Also here are routines for the modification of Makefiles. +** +** Copyright (c) 1995-1998 by Lars Düning. All Rights Reserved. +** This files is free software. For terms of use see the file LICENSE. +**--------------------------------------------------------------------------- +** Credits: +** The buffering scheme was inspired by the input module of lcc (by +** Chris Fraser & David Hanson). +**--------------------------------------------------------------------------- +*/ + +#include +#include +#include +#include +#include +#include + +#include "reader.h" + +/*-------------------------------------------------------------------------*/ + +struct filestate + { + int len; /* Number of bytes buffered */ + char buf[1]; /* allocated to bytes */ + }; + +#define BUFSIZE 10240 /* Size of the file buffer */ +static char aBuffer[BUFSIZE+1]; /* File buffer */ +static int fdFile; /* Filehandle */ +static int fdFile2; /* Filehandle Writer */ +static char * pLimit; /* Pointer to closing sentinel in buffer */ +static char * pch; /* Pointer to actual char in buffer */ +static char * pIName; /* Start of include filename */ +static struct filestate * pState; /* Saved state of the Makefile */ + +#define END_OF_BUF() (pch >= pLimit) +#define END_OF_FILE() (pLimit == aBuffer) + +/*-------------------------------------------------------------------------*/ +void +reader_init (void) + +/* Initialize the reader. + */ + +{ + fdFile = -1; + fdFile2 = -1; + aBuffer[BUFSIZE] = '\n'; + pLimit = aBuffer+BUFSIZE; + pch = pLimit; + pIName = NULL; + pState = NULL; +} + +/*-------------------------------------------------------------------------*/ +static int +fillbuffer (void) + +/* Read in the next chunk from file such that incrementing pch will + * access the newly read data. + * + * Result: + * 0 on success, non-0 else (errno holds error code then). + * pch is set to the start of the new data. + */ + +{ + int iRead; + + if (END_OF_FILE()) + return 1; + if (pIName) + { + if (pch > pLimit) + pch = pLimit; + memcpy(aBuffer, pIName, (unsigned)(pLimit-pIName)); + pch -= (pLimit-pIName); + } + else + pch = aBuffer; + iRead = read(fdFile, pch, (unsigned)(aBuffer+BUFSIZE-pch)); + if (iRead >= 0) + { + pLimit = pch+iRead; + *pLimit = '\n'; + } + return iRead < 0; +} + +/*-------------------------------------------------------------------------*/ +int +reader_open (const char *pName) + +/* Open a file for reading. + * + * pName: Name of the file to read. + * + * Result: + * 0 on success, non-0 else (errno contains error code then). + * + * The reader is initialized to read and filter the file name *pName. + */ + +{ + assert(pName); + assert(fdFile < 0); + reader_init(); + fdFile = open(pName, O_RDONLY); + return fdFile >= 0; +} + +/*-------------------------------------------------------------------------*/ +int +reader_openrw (const char *pNameR, const char *pNameW) + +/* Open two makefiles for modification. + * + * pNameR: Name of the file to read, may be NULL. + * pNameW: Name of the file to read. + * + * Result: + * 0 on success, non-0 else (errno contains error code then). + * + * The reader is initialized to read the old makefile *pNameR and writing + * of new makefile *pNameW. + * If no pNameR is given, the reader is immediately initialized to + * write *pNameW. + */ + +{ + assert(pNameW); + assert(fdFile < 0); + assert(fdFile2 < 0); + reader_init(); + fdFile2 = open(pNameW, O_WRONLY|O_CREAT|O_TRUNC, 0664); + if (fdFile2 < 0) + return 1; + if (pNameR) + { + fdFile = open(pNameR, O_RDONLY); + if (fdFile < 0) + { + close(fdFile2); + fdFile2 = -1; + } + } + else + pch = aBuffer; + return fdFile2 < 0; +} + +/*-------------------------------------------------------------------------*/ +int +reader_close (void) + +/* Close the file currently read. + * Result: + * 0 on success, non-0 else. + * errno set to error code in case. + */ + +{ + int i; + + i = 0; + if (fdFile >= 0) + { + i = close(fdFile); + } + fdFile = -1; + if (fdFile2 >= 0) + { + i = close(fdFile2) || i; + } + fdFile2 = -1; + if (pState) + { + free(pState); + } + pState = NULL; + return i; +} + +/*-------------------------------------------------------------------------*/ +int +reader_eof (void) + +/* Test if the file read is at its end. + * + * Result: + * Non-0 on end of file. + */ + +{ + return END_OF_FILE(); +} + +/*-------------------------------------------------------------------------*/ +const char * +reader_get (int * pType) + +/* Extract the next include statement from the file being read. + * + * Result: + * Pointer to the filename extracted from the statement, or NULL on error + * or end of file. + * The memory is property of reader_get(), the pointer valid just until + * the next call to reader_get(). + * <*pType> is set to 0 for an ""-include, and non-zero for an <>-include. + * o set to an error code in case. + */ + +{ + enum { StartOfLine, InLine, FoundHash, SkipComment, SkipLComment, FoundInclude + } eState; + /* For debugging purposes: + char *stab[] + = { + "Start " + , "InLine" + , "Found#" + , "SkipC " + , "SkipLC" + , "FoundI" + }; + */ + char ch; + int iFound; /* Number of chars found of "include" */ + int bDone; /* 0: still searching, 1: found include */ + int bType; /* 0: ""-include, 1: <>-include */ + +#define GETCHAR() \ + ch = *pch++; \ + if (ch == '\n' && pch >= pLimit) \ + { \ + if (fillbuffer()) { \ + pIName = NULL; \ + return NULL; \ + } \ + ch = *pch++; \ + } + + if (pIName) + eState = InLine; + else + eState = StartOfLine; + pIName = NULL; + + iFound = 0; + bType = 0; + bDone = 0; + while (!bDone) + { + /* Get the next interesting non-linefeed character */ + do { + GETCHAR() + if (ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f') + { + eState = StartOfLine; + pIName = NULL; + } + else + break; + } while (1); + + switch(eState) + { + case StartOfLine: + switch (ch) + { + case '#': + eState = FoundHash; + iFound = 0; + break; + case '/': + GETCHAR() + if (ch == '/') + eState = SkipLComment; + else if (ch == '*') + eState = SkipComment; + else + eState = InLine; + break; + case ' ': case '\t': + /* still: eState = StartOfLine; */ + break; + default: + eState = InLine; + break; + } + break; + + case InLine: + switch (ch) + { + case '/': + GETCHAR() + if (ch == '/') + eState = SkipLComment; + else if (ch == '*') + eState = SkipComment; + else + eState = InLine; + break; + case '\\': + GETCHAR() + /* still: eState = InLine; */ + break; + default: + /* still: eState = InLine; */ + break; + } + break; + + case FoundHash: + switch(ch) + { + case ' ': case '\t': + if (iFound) + { + eState = InLine; + iFound = 0; + } + /* else still: eState = FoundHash */ + break; + case 'i': case 'n': case 'c': case 'l': + case 'u': case 'd': case 'e': + if ("include"[iFound] != ch) + { + eState = InLine; + iFound = 0; + } + else if (ch == 'e') + { + eState = FoundInclude; + pIName = NULL; + } + else + iFound++; + break; + default: + eState = InLine; + } + break; + + case FoundInclude: + switch(ch) + { + case ' ': case '\t': + /* still: eState = FoundInclude */ + break; + case '<': + if (!pIName) + { + pIName = pch; + bType = 1; + } + else + eState = InLine; + break; + case '>': + if (!pIName) + { + eState = InLine; + } + else if (bType) + { + *(pch-1) = '\0'; + if (pch-1 == pIName) + pIName = NULL; + else + bDone = 1; + eState = InLine; + } + break; + case '"': + if (!pIName) + { + pIName = pch; + bType = 0; + } + else if (!bType) + { + *(pch-1) = '\0'; + if (pch-1 == pIName) + pIName = NULL; + else + bDone = 1; + eState = InLine; + } + break; + + default: + if (!pIName) + eState = InLine; + break; + } + break; + + case SkipComment: + case SkipLComment: + break; + } /* switch (eState) */ + + /* Skip comments immediately */ + if (eState == SkipComment) + { + while(1) { + do { + GETCHAR() + } while (ch != '*'); + GETCHAR() + if (ch == '/') + break; + } + eState = StartOfLine; + } + else if (eState == SkipLComment) + { + do { + GETCHAR() + } while (ch != '\n' && ch != '\r' && ch != '\v' && ch != '\f'); + eState = StartOfLine; + } + } /* while(!bDone) */ + + *pType = bType; + return pIName; + +#undef GETCHAR +} + +/*-------------------------------------------------------------------------*/ +int +reader_writeflush (void) + +/* Write out the buffer to the written makefile. + * Return 0 on success, non-0 else (errno holds the error code then). + */ + +{ + if (pch > aBuffer + && write(fdFile2, aBuffer, (unsigned)(pch-aBuffer)) != pch-aBuffer) + return 1; + pch = aBuffer; + return 0; +} + +/*-------------------------------------------------------------------------*/ +int +reader_writen (const char *pText, size_t len) + +/* Append the first characters of to the written file. + * Return 0 on success, non-0 else (errno holds the error code then) + */ + +{ + assert(pText); + assert(fdFile2 >= 0); + if (pLimit-pch < (signed)len && reader_writeflush()) + return 1; + memcpy(pch, pText, len); + pch += len; + return 0; +} + +/*-------------------------------------------------------------------------*/ +int +reader_write (const char *pText) + +/* Append to the written file. + * Return 0 on success, non-0 else (errno holds the error code then) + */ + +{ + return reader_writen(pText, strlen(pText)); +} + +/*-------------------------------------------------------------------------*/ +int +reader_copymake (const char * pTagline) + +/* Copy the file from fdFile to fdFile2 up to including the tagline. + * If the tagline is not found, it is appended. + * The tagline MUST end in a newline character! + * Return 0 on success, non-0 on error (errno holds the error code then). + */ + +{ + size_t len; + int count; /* Next charactor to compare in pTagline, or -1 if to skip + * up to the next Newline */ + char ch; + + + assert(pTagline); + assert(fdFile2); + len = strlen(pTagline); + assert(len < 120); + + /* Simplest case: no old makefile to copy */ + if (fdFile < 0) + { + strcpy(aBuffer, pTagline); + pch = aBuffer+len; + return 0; + } + if (fillbuffer()) + return 1; + count = 0; + + while (!END_OF_FILE() && count < (signed)len) + { + if (END_OF_BUF()) + { + if (write(fdFile2, aBuffer, (unsigned)(pch-aBuffer)) != pch-aBuffer) + return 1; + if (fillbuffer()) + return 1; + if (END_OF_FILE()) + break; + } + ch = *pch; + if (count < 0) + { + if (ch == '\n') + count = 0; + } + else + { + if (ch == pTagline[count]) + count++; + else if (ch == '\n') + count = 0; + else + count = -1; + } + pch++; + } + + /* Remember the state of the Makefile read */ + if (!END_OF_FILE()) + { + pState = (struct filestate *) malloc(sizeof(*pState)+(pLimit-pch)); + if (pState) + { + pState->len = pLimit-pch; + memcpy(pState->buf, pch, (size_t)pState->len); + } + } + + /* Switch to write mode */ + pLimit = aBuffer+BUFSIZE; + + /* Append a newline if missing */ + if (count < 0 && reader_writen("\n", 1)) + return 1; + + /* Put the Tagline into the buffer if necessary */ + if (count < (signed)len && reader_writen(pTagline, (size_t)len)) + return 1; + + return 0; +} + +/*-------------------------------------------------------------------------*/ +int +reader_copymake2 (const char * pTagline) + +/* Copy the remainder from fdFile to fdFile2 starting with the tagline. + * The tagline MUST end in a newline character! + * Return 0 on success, non-0 on error (errno holds the error code then). + */ + +{ + size_t len; + int count; /* Next charactor to compare in pTagline, or -1 if to skip + * up to the next Newline */ + char ch; + + + assert(pTagline); + assert(fdFile); + assert(fdFile2); + len = strlen(pTagline); + assert(len < 120); + + /* Append the tagline to the file written */ + if (reader_write(pTagline) || reader_writeflush()) + return 1; + + /* Easiest case: nothing to left copy */ + if (!pState) + return 0; + + /* Restore the aBuffer to the point where the reading stopped */ + pLimit = aBuffer+BUFSIZE; + pch = pLimit-pState->len; + memcpy(pch, pState->buf, (size_t)pState->len); + free(pState); + pState = NULL; + pIName = NULL; + + /* Fill up the buffer */ + if (END_OF_BUF() && fillbuffer()) + return 1; + + /* Skip the Makefile read until the tagline has been found */ + count = 0; + while (!END_OF_FILE() && count < (signed)len) + { + if (END_OF_BUF()) + { + if (fillbuffer()) + return 1; + if (END_OF_FILE()) + break; + } + ch = *pch; + if (count < 0) + { + if (ch == '\n') + count = 0; + } + else + { + if (ch == pTagline[count]) + count++; + else if (ch == '\n') + count = 0; + else + count = -1; + } + pch++; + } + + /* Copy the remainder of the Makefile */ + do + { + if (!END_OF_BUF() + && write(fdFile2, pch, (unsigned)(pLimit-pch)) != pLimit-pch) + return 1; + if (!END_OF_FILE() && fillbuffer()) + return 1; + } + while (!END_OF_FILE()); + + return 0; +} + +/***************************************************************************/ diff --git a/src/bin/mkdepend/reader.h b/src/bin/mkdepend/reader.h new file mode 100644 index 0000000000..9b3112512d --- /dev/null +++ b/src/bin/mkdepend/reader.h @@ -0,0 +1,24 @@ +/* $Id: reader.h 1.4 Wed, 01 Mar 2000 21:15:40 -0700 lars $ */ + +/*--------------------------------------------------------------------------- +** Copyright (c) 1995-1998 by Lars Düning. All Rights Reserved. +** This file is free software. For terms of use see the file LICENSE. +**--------------------------------------------------------------------------- +*/ + +#ifndef __READER_H__ +#define __READER_H__ 1 + +extern void reader_init (void); +extern int reader_open (const char *); +extern int reader_openrw (const char *, const char *); +extern int reader_close (void); +extern int reader_eof (void); +extern const char * reader_get (int *); +extern int reader_writeflush (void); +extern int reader_writen (const char *, size_t); +extern int reader_write (const char *); +extern int reader_copymake (const char *); +extern int reader_copymake2 (const char *); + +#endif diff --git a/src/bin/mkdepend/util.c b/src/bin/mkdepend/util.c new file mode 100644 index 0000000000..1d7c1ad0b1 --- /dev/null +++ b/src/bin/mkdepend/util.c @@ -0,0 +1,146 @@ +/* $Id: util.c 1.2 Wed, 01 Mar 2000 21:23:27 -0700 lars $ */ + +/*--------------------------------------------------------------------------- + * Useful functions. + * + * Copyright (c) 1999 by Lars Düning. All Rights Reserved. + * This file is free software. For terms of use see the file LICENSE. + *--------------------------------------------------------------------------- + */ + +#include +#include +#include + +#include "util.h" + +/*-------------------------------------------------------------------------*/ +char * +util_getpath (const char * pDirtyName, char ** ppBasename) + +/* Take the and eliminate as many '.' and '..' from it + * as possible. Return a freshly allocated copy of the cleaned up path. + * If ppBasename is NULL, the returned name will only contain the + * directory parts, the filename will be dropped. If ppBasename is not + * NULL, the returned name will contain the filename, and *ppBasename + * will be set to point to its first character. + * + * Return NULL when out of memory. + */ + +{ + char * pPath; /* The newly created pathname */ + char * pStart; /* The first non-'/'-character of pPath */ + char * pSrc, * pDest; /* Working pointers */ + size_t oFilename; /* Offset of filename in pDirtyName */ + + assert(pDirtyName); + + pPath = strdup(pDirtyName); + if (!pPath) + return NULL; + + pStart = ('/' == *pPath) ? pPath+1 : pPath; + + /* First scan: find the first character of the filename + * and end the string there, leaving just the directory path. + * But remember the offset of the filename. + */ + pDest = pStart; /* First character after last '/' found */ + pSrc = pStart; /* Rover */ + while ('\0' != *pSrc) + { + if ('/' == *pSrc++) + pDest = pSrc; + } + *pDest = '\0'; + oFilename = (unsigned)(pDest - pPath); + + /* pPath now ends in a '/', or *pStart is a '\0' */ + + /* Second scan: visit all directory parts and eliminate '.' + * and '..' where possible. + */ + pDest = pStart; /* First character after last valid '/' */ + pSrc = pStart; + + /* Skip initial '.' entries */ + while ('.' == *pSrc) + { + if ('/' == pSrc[1]) + { + pSrc += 2; + } + else + break; + } + + while ('\0' != *pSrc) + { + char ch; + + /* Copy forward until the next '/' */ + do + { + ch = *pSrc++; + *pDest++ = ch; + } while ('/' != ch); + + /* If a '.' follows, it might be a '.' or '..' entry */ + while ('.' == *pSrc) + { + pSrc++; + + if ('/' == *pSrc) + { + /* Simply skip the '.' entry */ + pSrc++; + } + else if ('.' == pSrc[0] && '/' == pSrc[1] && pDest != pStart) + { + /* For a '..' entry, move pDest back one directory unless + * it is at the start of the string + */ + char * pLastDest; + + for ( pLastDest = pDest-1 + ; pLastDest != pStart && '/' != pLastDest[-1] + ; pLastDest--) /* SKIP */; + + /* Special case: if the part just unrolled is '..' itself, + * don't do the unrolling and instead break the loop. + */ + if (pLastDest + 3 == pDest && pLastDest[0] == '.' && pLastDest[1] == '.') + { + *pDest++ = '.'; + break; + } + else + { + pSrc += 2; + pDest = pLastDest; + } + } + else + { + /* Its not a '.' or handleable '..' entry */ + *pDest++ = '.'; + break; + } + } + } + *pDest = '\0'; + + /* If the caller wants to have the filename too, append it to + * the clean directory. + */ + if (ppBasename) + { + strcpy(pDest, pDirtyName+oFilename); + *ppBasename = pDest; + } + + return pPath; +} + +/***************************************************************************/ diff --git a/src/bin/mkdepend/util.h b/src/bin/mkdepend/util.h new file mode 100644 index 0000000000..60f2cc46d1 --- /dev/null +++ b/src/bin/mkdepend/util.h @@ -0,0 +1,14 @@ +/* $Id: util.h 1.1 Tue, 02 Feb 1999 17:57:55 -0700 lars $ */ + +/*--------------------------------------------------------------------------- + * Copyright (c) 1999 by Lars Düning. All Rights Reserved. + * This file is free software. For terms of use see the file LICENSE. + *--------------------------------------------------------------------------- + */ + +#ifndef __UTIL_H__ +#define __UTIL_H__ 1 + +extern char * util_getpath(const char *, char **); + +#endif diff --git a/src/bin/mkdepend/version.h b/src/bin/mkdepend/version.h new file mode 100644 index 0000000000..c8a98371ce --- /dev/null +++ b/src/bin/mkdepend/version.h @@ -0,0 +1,38 @@ +/* $Id: version.h 1.2 Sat, 24 Oct 1998 19:54:13 -0600 lars $ */ + +/*--------------------------------------------------------------------------- + * Version information for MkDepend. + * + * Copyright (c) 1998 by Lars Düning. All Rights Reserved. + * This file is free software. For terms of use see the file LICENSE. + *--------------------------------------------------------------------------- + * This macros are exported: + * + * VERSION: a string of the form "x.y" or "x.y.z", with z being >= 2. + * RELEASEDATE: a string with date and time when the current version was + * checked in. + * PRCS_LEVEL: the internal minor version of MkDepend, used for + * in VERSION when the value is >= 2. + * + * The values are created by PRCS. + *--------------------------------------------------------------------------- + */ + +#ifndef __VERSION_H__ +#define __VERSION_H__ + +/* $Format: "#define RELEASE_DATE \"$ReleaseDate$\""$ */ +#define RELEASE_DATE "Thu, 19 Mar 2009 11:20:00 -0100" + +/* $Format: "#define PRCS_LEVEL $ProjectMinorVersion$"$ */ +#define PRCS_LEVEL 1 + +#if PRCS_LEVEL > 1 +/* $Format: "#define VERSION \"$ReleaseVersion$.$ProjectMinorVersion$\""$ */ +#define VERSION "1.7.1" +#else +/* $Format: "#define VERSION \"$ReleaseVersion$\""$ */ +#define VERSION "1.7" +#endif + +#endif /* __VERSION_H__ */