diff --git a/boards/z80/ez80/scripts/eZ80_Config.mk b/boards/z80/ez80/scripts/eZ80_Config.mk index 8c4bd8fa2a7..931c2e6aae6 100644 --- a/boards/z80/ez80/scripts/eZ80_Config.mk +++ b/boards/z80/ez80/scripts/eZ80_Config.mk @@ -22,45 +22,9 @@ # and assembly source files and to insert the resulting object files into an # archive. These replace the default definitions at tools/Config.mk -ifeq ($(CONFIG_WINDOWS_NATIVE),y) - -define PREPROCESS - @echo CPP: $(1)->$(2) - $(Q) $(CPP) $(CPPFLAGS) $($(strip $(1))_CPPFLAGS) $(1) -o $(2) -endef - -define COMPILE - $(call DELFILE, $(2)) - $(Q) $(CC) $(CFLAGS) $($(strip $(1))_CFLAGS) ${shell echo $(1) | sed -e "s/\//\\/g"} - if not exist $(2) $(call MOVEFILE, $(subst .c,.obj,$(1)), $(2)) -endef - -define ASSEMBLE - $(call DELFILE, $(2)) - $(Q) $(AS) $(AFLAGS) $($(strip $(1))_AFLAGS) ${shell echo $(1) | sed -e "s/\//\\/g"} - if not exist $(2) $(call MOVEFILE, $(subst .asm,.obj,$(1)), $(2)) -endef - -define MOVEOBJ - $(call MOVEFILE, "$(1).obj", "$(2)$(DELIM)$(1).obj") - $(call MOVEFILE, "$(1).lst", "$(2)$(DELIM)$(1).lst") - $(call MOVEFILE, "$(1).src", "$(2)$(DELIM)$(1).src") -endef - -define ARCHIVE - for %%G in ($(2)) do ( $(AR) $(ARFLAGS) $(1)=-+%%G ) -endef - -define CLEAN - $(Q) if exist *.obj (del /f /q *.obj) - $(Q) if exist *.src (del /f /q *.src) - $(Q) if exist *.lib (del /f /q *.lib) - $(Q) if exist *.hex (del /f /q *.hex) - $(Q) if exist *.lod (del /f /q *.lod) - $(Q) if exist *.lst (del /f /q *.lst) -endef - -else +# PREPROCESS +# +# Run a file through the C Pre-processor define PREPROCESS @echo "CPP: $(1)->$(2)" @@ -83,11 +47,26 @@ endef # lie in a lower directory, but lies in the current directory and is # handled within COMPILE and ASSEMBLE. +ifeq ($(CONFIG_WINDOWS_NATIVE),y) + +define COMPILE + $(call DELFILE, $(2)) + $(Q) $(CC) $(CFLAGS) $($(strip $(1))_CFLAGS) ${shell echo $(1) | sed -e "s/\//\\/g"} + if not exist $(2) $(call MOVEFILE, $(subst .c,.obj,$(1)), $(2)) +endef + +define ASSEMBLE + $(call DELFILE, $(2)) + $(Q) $(AS) $(AFLAGS) $($(strip $(1))_AFLAGS) ${shell echo $(1) | sed -e "s/\//\\/g"} + if not exist $(2) $(call MOVEFILE, $(subst .asm,.obj,$(1)), $(2)) +endef + +else + define COMPILE $(call DELFILE, $(2)) $(Q) $(CC) $(CFLAGS) $($(strip $(1))_CFLAGS) `cygpath -w "$(1)"` $(Q) ( \ - set -x ; \ __rename=`basename $(2)` ;\ if [ ! -e $${__rename} ] ; then \ __src=`basename $(1) | cut -d'.' -f1` ; \ @@ -103,7 +82,6 @@ define ASSEMBLE $(call DELFILE, $(2)) $(Q) $(AS) $(AFLAGS) $($(strip $(1))_AFLAGS) `cygpath -w "$(1)"` $(Q) ( \ - set -x ; \ __rename=`basename $(2)` ; \ if [ ! -e $${__rename} ] ; then \ __src=`basename $(1) | cut -d'.' -f1` ; \ @@ -114,6 +92,7 @@ define ASSEMBLE fi ; \ ) endef +endif define MOVEOBJ $(call MOVEFILE, "$(1).obj", "$(2)$(DELIM)$(1).obj") @@ -133,22 +112,31 @@ endef # this case, the base library name is extact as the ARCHIVE logic CD's # to the directory containing the library. +ifeq ($(CONFIG_WINDOWS_NATIVE),y) + define ARCHIVE - ( \ - set -x ;\ - __wd=`pwd` ;\ - __home=`dirname $(1)` ; \ - if [ -z "$${__home}" ] ; then \ - __lib=$(1) ; \ - else \ - __lib=`basename $(1)` ; \ - cd $${__home} ; \ - fi ; \ - for __obj in $(2) ; do \ - $(AR) $(ARFLAGS) $${__lib}=-+$$__obj ; \ - done ; \ - cd $${__wd} ; \ - ) + for %%G in ($(2)) do ( $(AR) $(ARFLAGS) $(1)=-+%%G ) +endef + +define ARCHIVE + $(MAKE) -C $(TOPDIR)\tools\zds zdsar.exe + $(TOPDIR)\tools\zds\zdsar.exe --ar "$(AR)" --ar_flags "$(ARFLAGS)" --library "$(1)" $(2) +endef + +define CLEAN + $(Q) if exist *.obj (del /f /q *.obj) + $(Q) if exist *.src (del /f /q *.src) + $(Q) if exist *.lib (del /f /q *.lib) + $(Q) if exist *.hex (del /f /q *.hex) + $(Q) if exist *.lod (del /f /q *.lod) + $(Q) if exist *.lst (del /f /q *.lst) +endef + +else + +define ARCHIVE + $(MAKE) -C $(TOPDIR)/tools/zds zdsar.exe + $(TOPDIR)/tools/zds/zdsar.exe --ar "$(AR)" --ar_flags "$(ARFLAGS)" --library $(1) $(2) endef define CLEAN diff --git a/tools/README.txt b/tools/README.txt index b5d17de44f9..130966db269 100644 --- a/tools/README.txt +++ b/tools/README.txt @@ -328,7 +328,7 @@ nxstyle.c pic32mx ------- - This directory contains build tools used only for PIC32MX platforms + This directory contains build tools used only for PIC32MX/Z platforms bdf-convert.c ------------- @@ -466,60 +466,6 @@ bdf-convert.c NULL }; -Makefile.host -------------- - - This is the makefile that is used to make the mkconfig program from - the mkconfig.c C file, the cmpconfig program from cmpconfig.c C file, - the mkversion program from the mkconfig.c C file, or the mksyscall - program from the mksyscall.c file. Usage: - - cd tools/ - make -f Makefile.host - -mkromfsimg.sh -------------- - - This script may be used to automate the generation of a ROMFS file system - image. It accepts an rcS script "template" and generates an image that - may be mounted under /etc in the NuttX pseudo file system. - - TIP: Edit the resulting header file and mark the generated data values - as 'const' so that they will be stored in FLASH. - -mkdeps.c, cnvwindeps.c, mkwindeps.sh, and mknulldeps.sh -------------------------------------------------------- - - NuttX uses the GCC compiler's capabilities to create Makefile dependencies. - The program mkdeps is used to run GCC in order to create the dependencies. - If a NuttX configuration uses the GCC toolchain, its Make.defs file (see - boards/README.txt) will include a line like: - - MKDEP = $(TOPDIR)/tools/mkdeps[.exe] (See NOTE below) - - If the NuttX configuration does not use a GCC compatible toolchain, then - it cannot use the dependencies and instead it uses mknulldeps.sh: - - MKDEP = $(TOPDIR)/tools/mknulldeps.sh - - The mknulldeps.sh is a stub script that does essentially nothing. - - mkwindeps.sh is a version that creates dependencies using the Windows - native toolchain. That generates Windows native paths in the dependency - file. But the mkwindeps.sh uses cnvwindeps.c to convert the Windows - paths to POSIX paths. This adds some time to the Windows dependency - generation but is generally the best option available for that mixed - environment of Cygwin with a native Windows GCC toolchain. - - mkdeps.c generates mkdeps (on Linux) or mkdeps.exe (on Windows). - However, this version is still under-development. It works well in - the all POSIX environment or in the all Windows environment but also - does not work well in mixed POSIX environment with a Windows toolchain. - In that case, there are still issues with the conversion of things like - 'c:\Program Files' to 'c:program files' by bash. Those issues may, - eventually be solvable but for now continue to use mkwindeps.sh in - that mixed environment. - define.sh and define.bat ------------------------ @@ -531,6 +477,15 @@ define.sh and define.bat The define.bat script is a counterpart for use in the native Windows build. +flash_writer.py +--------------- + + This flash writer is using the xmodem for firmware transfer on + boards based on cxd56 chip (Ex. Spresense) + + for flashing the .spk image to the board please use: + tools/flash_writer.py -s -c /dev/ttyUSB0 -d -b 115200 -n nuttx.spk + ide_exporter.py --------------- @@ -641,6 +596,64 @@ incdir.sh and incdir.bat build. However, there is currently only one compiler supported in that context: MinGW-GCC. +indent.sh +--------- + + This script can be used to indent .c and .h files in a manner similar + to the NuttX coding style. It doesn't do a really good job, however + (see below and the comments at the top of the indent.sh file). + + USAGE: + tools/indent.sh [-d] [-p] -o + tools/indent.sh [-d] [-p] + tools/indent.sh [-d] -h + + Where: + - + A single, unformatted input file + - + A list of unformatted input files that will be reformatted in place. + -o + Write the single, reformatted to . + will not be modified. + -d + Enable script debug + -p + Comments are pre-formatted. Do not reformat. + -h + Show this help message and exit + + The conversions make by the indent.sh script differs from the NuttX coding + style in that: + + 1. The coding standard requires that the trailing */ of a multi-line + comment be on a separate line. By default, indent.sh will put the + final */ on the same line as the last comment text. If your C file + already has properly formatted comments then using the -p option will + eliminate that bad behavior + 2. If your source file has highly formatted comments containing things + such as tables or lists, then use the -p option to preserve those + pre-formatted comments. + 3. I usually align things vertically (like '=' in assignments), + 4. indent.sh puts a bogus blank line at the top of the file, + 5. I don't like the way it handles nested conditional compilation + intermixed with code. I prefer the preprocessor conditional tests + be all right justified in that case. + 6. I also indent brackets differently on structures than does this script. + 7. I normally use no spaces in casts. indent.sh adds spaces in casts like + "(FAR void *)&foo" becomes "(FAR void *) & foo". + 8. When used with header files, the initial idempotence conditional test + causes all preprocessor directives to be indented in the file. So for + header files, you will need to substitute "^# " with "#" in the + converted header file. + + You will manually need to check for the issues listed above after + performing the conversions. nxstyle.c provides a good test that will + catch most of the indent.sh screw-ups. Together, they do a pretty good + job of formatting. + + See also nxstyle.c and uncrustify.cfg + kconfig.bat ----------- @@ -720,63 +733,59 @@ logparser.c logparser _git_log.tmp >_changelog.txt rm -f _git_log.tmp -indent.sh ---------- +Makefile.host +------------- - This script can be used to indent .c and .h files in a manner similar - to the NuttX coding style. It doesn't do a really good job, however - (see below and the comments at the top of the indent.sh file). + This is the makefile that is used to make the mkconfig program from + the mkconfig.c C file, the cmpconfig program from cmpconfig.c C file, + the mkversion program from the mkconfig.c C file, or the mksyscall + program from the mksyscall.c file. Usage: - USAGE: - tools/indent.sh [-d] [-p] -o - tools/indent.sh [-d] [-p] - tools/indent.sh [-d] -h + cd tools/ + make -f Makefile.host - Where: - - - A single, unformatted input file - - - A list of unformatted input files that will be reformatted in place. - -o - Write the single, reformatted to . - will not be modified. - -d - Enable script debug - -p - Comments are pre-formatted. Do not reformat. - -h - Show this help message and exit +mkromfsimg.sh +------------- - The conversions make by the indent.sh script differs from the NuttX coding - style in that: + This script may be used to automate the generation of a ROMFS file system + image. It accepts an rcS script "template" and generates an image that + may be mounted under /etc in the NuttX pseudo file system. - 1. The coding standard requires that the trailing */ of a multi-line - comment be on a separate line. By default, indent.sh will put the - final */ on the same line as the last comment text. If your C file - already has properly formatted comments then using the -p option will - eliminate that bad behavior - 2. If your source file has highly formatted comments containing things - such as tables or lists, then use the -p option to preserve those - pre-formatted comments. - 3. I usually align things vertically (like '=' in assignments), - 4. indent.sh puts a bogus blank line at the top of the file, - 5. I don't like the way it handles nested conditional compilation - intermixed with code. I prefer the preprocessor conditional tests - be all right justified in that case. - 6. I also indent brackets differently on structures than does this script. - 7. I normally use no spaces in casts. indent.sh adds spaces in casts like - "(FAR void *)&foo" becomes "(FAR void *) & foo". - 8. When used with header files, the initial idempotence conditional test - causes all preprocessor directives to be indented in the file. So for - header files, you will need to substitute "^# " with "#" in the - converted header file. + TIP: Edit the resulting header file and mark the generated data values + as 'const' so that they will be stored in FLASH. - You will manually need to check for the issues listed above after - performing the conversions. nxstyle.c provides a good test that will - catch most of the indent.sh screw-ups. Together, they do a pretty good - job of formatting. +mkdeps.c, cnvwindeps.c, mkwindeps.sh, and mknulldeps.sh +------------------------------------------------------- - See also nxstyle.c and uncrustify.cfg + NuttX uses the GCC compiler's capabilities to create Makefile dependencies. + The program mkdeps is used to run GCC in order to create the dependencies. + If a NuttX configuration uses the GCC toolchain, its Make.defs file (see + boards/README.txt) will include a line like: + + MKDEP = $(TOPDIR)/tools/mkdeps[.exe] (See NOTE below) + + If the NuttX configuration does not use a GCC compatible toolchain, then + it cannot use the dependencies and instead it uses mknulldeps.sh: + + MKDEP = $(TOPDIR)/tools/mknulldeps.sh + + The mknulldeps.sh is a stub script that does essentially nothing. + + mkwindeps.sh is a version that creates dependencies using the Windows + native toolchain. That generates Windows native paths in the dependency + file. But the mkwindeps.sh uses cnvwindeps.c to convert the Windows + paths to POSIX paths. This adds some time to the Windows dependency + generation but is generally the best option available for that mixed + environment of Cygwin with a native Windows GCC toolchain. + + mkdeps.c generates mkdeps (on Linux) or mkdeps.exe (on Windows). + However, this version is still under-development. It works well in + the all POSIX environment or in the all Windows environment but also + does not work well in mixed POSIX environment with a Windows toolchain. + In that case, there are still issues with the conversion of things like + 'c:\Program Files' to 'c:program files' by bash. Those issues may, + eventually be solvable but for now continue to use mkwindeps.sh in + that mixed environment. README.txt ---------- @@ -1043,18 +1052,15 @@ uncrustify.cfg See also indent.sh and nxstyle.c +zds +--- + + This directory contains build tools used only with the ZDS-II + platforms (z8, ez80, zNeo). + zipme.sh -------- I use this script to create the nuttx-xx.yy.tar.gz tarballs for release on Bitbucket.org. It is handy because it also does the kind of clean that you need to do to make a clean code release. - -flash_writer.py ---------------- - - This flash writer is using the xmodem for firmware transfer on - boards based on cxd56 chip (Ex. Spresense) - - for flashing the .spk image to the board please use: - tools/flash_writer.py -s -c /dev/ttyUSB0 -d -b 115200 -n nuttx.spk diff --git a/tools/zds/.gitignore b/tools/zds/.gitignore new file mode 100644 index 00000000000..7720a663bd0 --- /dev/null +++ b/tools/zds/.gitignore @@ -0,0 +1,3 @@ +/zdsar +/zdsar.exe +/*.dSYM diff --git a/tools/zds/Makefile b/tools/zds/Makefile new file mode 100644 index 00000000000..1b79b4cdfbe --- /dev/null +++ b/tools/zds/Makefile @@ -0,0 +1,69 @@ +############################################################################ +# tools/zds/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +TOPDIR ?= $(CURDIR)/.. +-include $(TOPDIR)/Make.defs + +# Define HOSTCC on the make command line if it differs from these defaults +# Define HOSTCFLAGS with -g on the make command line to build debug versions + +HOSTOS = ${shell uname -o 2>/dev/null || uname -s 2>/dev/null || echo "Other"} + +ifeq ($(HOSTOS),MinGW) + +# In the Windows native environment, the MinGW GCC compiler is used + +HOSTCC ?= mingw32-gcc.exe +HOSTCFLAGS ?= -O2 -Wall -Wstrict-prototypes -Wshadow -I. +HOSTCFLAGS += -DHOST_NATIVE=1 + +else + +# Must be Cygwin or MYS2 on Windows (Can't be Linux, macOS, BSD, ..) +# +# GCC or clang is assumed in all other POSIX environments. +# strtok_r is used in some tools, but does not seem to be available in +# the MinGW environment. + +HOSTCC ?= cc +HOSTCFLAGS ?= -O2 -Wall -Wstrict-prototypes -Wshadow -I. +HOSTCFLAGS += -DHAVE_STRTOK_C=1 -DHOST_CYGWIN=1 + +endif + +# Targets + +all: zdsar.exe +default: zdsar.exe +.PHONY: clean + +# Add CFLAGS=-g on the make command line to build debug versions + +CFLAGS = -O2 -Wall -Wstrict-prototypes -Wshadow -I. +CFLAGS += -DHAVE_STRTOK_C=1 + +# zdsar - Convert virtual addresses in nuttx.hex to physical addresses + +zdsar.exe: zdsar.c zdsar.c + $(Q) $(HOSTCC) $(HOSTCFLAGS) -o zdsar.exe zdsar.c + +clean: + @rm -f *.o *.a *.dSYM *~ .*.swp + @rm -f zdsar zdsar.exe diff --git a/tools/zds/README.txt b/tools/zds/README.txt new file mode 100644 index 00000000000..530cf364625 --- /dev/null +++ b/tools/zds/README.txt @@ -0,0 +1,11 @@ +README.txt +========== + +tools/zds/zdsar.c: This is a wrapper around the ZDS_II librarian. It + simplifies the build scripts by replacing large sequences of complex Bash + script that were added to the build files. Not only does this clean upi + the build files but it also improves performance and, more importantly,i + provides a common solution for the Windows native build case. + + This tool should work with all ZDS-II based platforms including z8, zNeo, and ez80. + diff --git a/tools/zds/zdsar.c b/tools/zds/zdsar.c new file mode 100644 index 00000000000..8acd42b95b4 --- /dev/null +++ b/tools/zds/zdsar.c @@ -0,0 +1,947 @@ +/**************************************************************************** + * tools/zdsar.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HOST_CYGWIN +# include +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_BUFFER (4096) +#define MAX_EXPAND (2048) + +/* MAX_PATH might be defined in stdlib.h */ + +#if !defined(MAX_PATH) +# define MAX_PATH (512) +#endif + +/* NAME_MAX is typically defined in limits.h */ + +#if !defined(NAME_MAX) + + /* FILENAME_MAX might be defined in stdio.h */ + +# if defined(FILENAME_MAX) +# define NAME_MAX FILENAME_MAX +# else + + /* MAXNAMELEN might be defined in dirent.h */ + +# include +# if defined(MAXNAMLEN) +# define NAME_MAX MAXNAMLEN +# else + + /* Lets not let a silly think like this stop us... just make something up */ + +# define NAME_MAX 256 +# endif +# endif +#endif + +/* Maximum objects per librarian call */ + +#define MAX_OBJECTS 64 + +#if defined(HOST_NATIVE) +# define SEPARATOR '\\' +# define HOSTNAME "Native" /* Windows native */ +#elif defined(HOST_CYGWIN) +# define SEPARATOR '/' +# define HOSTNAME "Cywgin" /* Cygwin or MSYS under Windows */ +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum slashmode_e +{ + MODE_FSLASH = 0, + MODE_BSLASH = 1, + MODE_DBLBACK = 2 +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static char *g_ar = NULL; /* Full path to the librarian program */ +static char *g_arflags = NULL; /* Flags to use with the librarian program */ +static char *g_libpath = NULL; /* Path to the library */ +static char *g_libname = NULL; /* Library file name*/ +static char *g_objects = NULL; /* List of object files */ +static int g_debug = 0; /* Debug output enabled if >0 */ + +static char g_command[MAX_BUFFER]; /* Full librarian command */ +static char g_wd[MAX_PATH]; /* Current working directory */ +static char g_path[MAX_PATH]; /* Buffer for expanding paths */ +static char g_objpath[MAX_PATH]; /* Path to the object files */ +#ifdef HOST_CYGWIN +static char g_expand[MAX_EXPAND]; /* Expanded path */ +static char g_dequoted[MAX_PATH]; /* De-quoted path */ +static char g_posixpath[MAX_PATH]; /* Full POSIX path */ +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* MinGW does not seem to provide strtok_r */ + +#ifndef HAVE_STRTOK_R +static char *MY_strtok_r(char *str, const char *delim, char **saveptr) +{ + char *pbegin; + char *pend = NULL; + + /* Decide if we are starting a new string or continuing from + * the point we left off. + */ + + if (str) + { + pbegin = str; + } + else if (saveptr && *saveptr) + { + pbegin = *saveptr; + } + else + { + return NULL; + } + + /* Find the beginning of the next token */ + + for (; + *pbegin && strchr(delim, *pbegin) != NULL; + pbegin++); + + /* If we are at the end of the string with nothing + * but delimiters found, then return NULL. + */ + + if (!*pbegin) + { + return NULL; + } + + /* Find the end of the token */ + + for (pend = pbegin + 1; + *pend && strchr(delim, *pend) == NULL; + pend++); + + /* pend either points to the end of the string or to + * the first delimiter after the string. + */ + + if (*pend) + { + /* Turn the delimiter into a null terminator */ + + *pend++ = '\0'; + } + + /* Save the pointer where we left off and return the + * beginning of the token. + */ + + if (saveptr) + { + *saveptr = pend; + } + + return pbegin; +} + +#undef strtok_r +# define strtok_r MY_strtok_r +#endif + +static void append(char **base, char *str) +{ + char *oldbase; + char *newbase; + int alloclen; + + oldbase = *base; + if (oldbase == NULL) + { + newbase = strdup(str); + if (!newbase) + { + fprintf(stderr, "ERROR: Failed to strdup %s\n", str); + exit(EXIT_FAILURE); + } + } + else + { + alloclen = strlen(oldbase) + strlen(str) + sizeof((char) ' ') + + sizeof((char) '\0'); + newbase = (char *)malloc(alloclen); + if (!newbase) + { + fprintf(stderr, "ERROR: Failed to allocate %d bytes\n", alloclen); + exit(EXIT_FAILURE); + } + + snprintf(newbase, alloclen, "%s %s", oldbase, str); + free(oldbase); + } + + *base = newbase; +} + +static const char *do_expand(const char *argument) +{ +#ifdef HOST_CYGWIN + const char *src; + char *dest; + int len; + + src = argument; + dest = g_expand; + len = 0; + + while (*src && len < MAX_EXPAND) + { + if (*src == '\\') + { + /* Copy backslash */ + + *dest++ = *src++; + if (++len >= MAX_EXPAND) + { + break; + } + + /* Already expanded? */ + + if (*src == '\\') + { + /* Yes... just copy all consecutive backslashes */ + + do + { + *dest++ = *src++; + if (++len >= MAX_EXPAND) + { + break; + } + } + while (*src == '\\'); + } + else + { + /* No.. expend */ + + *dest++ = '\\'; + if (++len >= MAX_EXPAND) + { + break; + } + } + } + else + { + *dest++ = *src++; + len++; + } + } + + if (*src) + { + fprintf(stderr, "ERROR: Truncated during expansion string is too long [%lu/%u]\n", + (unsigned long)strlen(argument), MAX_EXPAND); + exit(EXIT_FAILURE); + } + + *dest = '\0'; + return g_expand; +#else + return argument; +#endif +} + +/* Remove backslash quoting from a path */ + +#ifdef HOST_CYGWIN +static bool dequote_path(const char *winpath) +{ + char *dest = g_dequoted; + const char *src = winpath; + int len = 0; + bool quoted = false; + + while (*src && len < MAX_PATH) + { + if (src[0] != '\\' || (src[1] != ' ' && src[1] != '(' && src[1] != ')')) + { + *dest++ = *src; + len++; + } + else + { + quoted = true; + } + + src++; + } + + if (*src || len >= MAX_PATH) + { + fprintf(stderr, "# ERROR: Path truncated\n"); + exit(EXIT_FAILURE); + } + + *dest = '\0'; + return quoted; +} +#endif + +/* If using Cygwin with a Window's Toolchain, then we have to convert the + * POSIX path to a Windows path. + */ + +static const char *convert_path(const char *path) +{ +#ifdef HOST_CYGWIN + const char *retptr; + ssize_t size; + ssize_t ret; + bool quoted; + + quoted = dequote_path(path); + if (quoted) + { + retptr = g_posixpath; + } + else + { + retptr = &g_posixpath[1]; + } + + size = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_RELATIVE, g_dequoted, + NULL, 0); + if (size > (MAX_PATH - 3)) + { + fprintf(stderr, "# ERROR: POSIX path too long: %lu\n", + (unsigned long)size); + exit(EXIT_FAILURE); + } + + ret = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_RELATIVE, g_dequoted, + &g_posixpath[1], MAX_PATH - 3); + if (ret < 0) + { + fprintf(stderr, "# ERROR: cygwin_conv_path '%s' failed: %s\n", + g_dequoted, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (quoted) + { + size++; + g_posixpath[0] = '"'; + g_posixpath[size] = '"'; + } + + g_posixpath[size+1] = '\0'; + return retptr; +#else + return path; +#endif +} + +static void show_usage(const char *progname, const char *msg, int exitcode) +{ + if (msg) + { + fprintf(stderr, "\n"); + fprintf(stderr, "%s:\n", msg); + } + + fprintf(stderr, "\n"); + fprintf(stderr, "%s [OPTIONS] --ar \"\" --library \"\" obj [obj [obj...]]\n", + progname); + fprintf(stderr, "\n"); + fprintf(stderr, "Where:\n"); + fprintf(stderr, " --ar \n"); + fprintf(stderr, " A command line string that defines how to execute the ZDS-II librarian\n"); + fprintf(stderr, " --library \"\"\n"); + fprintf(stderr, " The library into which the object files will be inserted\n"); + fprintf(stderr, " obj\n"); + fprintf(stderr, " One or more object files that will be inserted into the archive. Each expected\n"); + fprintf(stderr, " to reside in the current directory unless --obj-path is provided on the command line\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "And [OPTIONS] include:\n"); + fprintf(stderr, " --ar_flags \"\"\n"); + fprintf(stderr, " Optional librarian flags\n"); + fprintf(stderr, " --obj-path \n"); + fprintf(stderr, " Do not look in the current directory for the object files. Instead, look in to\n"); + fprintf(stderr, " for the object files. --obj-path may be used once on the command line\n"); + fprintf(stderr, " --debug\n"); + fprintf(stderr, " Enable %s debug output\n", progname); + fprintf(stderr, " --help\n"); + fprintf(stderr, " Shows this message and exits\n"); + exit(exitcode); +} + +static void parse_args(int argc, char **argv) +{ + char *library = NULL; + char *objpath = NULL; + int argidx; + + /* Accumulate ARFLAGS up to "--" */ + + for (argidx = 1; argidx < argc; argidx++) + { + if (strcmp(argv[argidx], "--ar") == 0) + { + argidx++; + if (argidx >= argc) + { + show_usage(argv[0], "ERROR: Missing argument to --ar", EXIT_FAILURE); + } + else if (g_ar != NULL) + { + show_usage(argv[0], "ERROR: Multiple --ar arguments", EXIT_FAILURE); + } + + g_ar = argv[argidx]; + } + else if (strcmp(argv[argidx], "--ar_flags") == 0) + { + argidx++; + if (argidx >= argc) + { + show_usage(argv[0], "ERROR: Missing argument to --ar_flags", EXIT_FAILURE); + } + else if (g_arflags != NULL) + { + show_usage(argv[0], "ERROR: Multiple --ar_flags arguments", EXIT_FAILURE); + } + + g_arflags = argv[argidx]; + } + else if (strcmp(argv[argidx], "--library") == 0) + { + argidx++; + if (argidx >= argc) + { + show_usage(argv[0], "ERROR: Missing argument to --library", EXIT_FAILURE); + } + else if (library != NULL) + { + show_usage(argv[0], "ERROR: Multiple --library arguments", EXIT_FAILURE); + } + + /* Convert the library path a POSIX. NOTE this is a no-op in Windows + * native mode. + */ + + library = (char *)convert_path(argv[argidx]); + } + else if (strcmp(argv[argidx], "--obj-path") == 0) + { + argidx++; + if (argidx >= argc) + { + show_usage(argv[0], "ERROR: Missing argument to --obj-path", EXIT_FAILURE); + } + else if (objpath != NULL) + { + show_usage(argv[0], "ERROR: Multiple --obj-path arguments", EXIT_FAILURE); + } + + objpath = argv[argidx]; + } + else if (strcmp(argv[argidx], "--debug") == 0) + { + g_debug++; + } + else if (strcmp(argv[argidx], "--help") == 0) + { + show_usage(argv[0], NULL, EXIT_SUCCESS); + } + else if (strncmp(argv[argidx], "--", 2) == 0) + { + show_usage(argv[0], "ERROR: Unrecognized option", EXIT_FAILURE); + } + else + { + break; + } + } + + /* Accumulate object files */ + + for (; argidx < argc; argidx++) + { + append(&g_objects, argv[argidx]); + } + + if (g_debug) + { + fprintf(stderr, "Selections:\n"); + fprintf(stderr, " CWD : [%s]\n", g_wd); + fprintf(stderr, " AR : [%s]\n", g_ar ? g_ar : "(None)"); + fprintf(stderr, " AR Flags : [%s]\n", g_arflags ? g_arflags : "(None)"); + fprintf(stderr, " Library : [%s]\n", library ? library : "(None)"); + fprintf(stderr, " Object Path : [%s]\n", objpath ? objpath : "(None"); + fprintf(stderr, " Object Files : [%s]\n", g_objects ? g_objects : "(None)"); + fprintf(stderr, " Host Environ : [%s]\n\n", HOSTNAME); + } + + /* Check for required parameters */ + + if (g_ar == NULL) + { + show_usage(argv[0], "ERROR: No librarian specified", EXIT_FAILURE); + } + + if (library == NULL) + { + show_usage(argv[0], "ERROR: No library specified", EXIT_FAILURE); + } + else + { + /* Separate the library file name from the path. The ZDS-II librarian + * expects the library to be in the current working directory. + */ + + g_libpath = dirname(library); + g_libname = basename(library); + } + + if (g_objects == NULL) + { + /* Don't report an error -- this happens normally in some configurations */ + + printf("No object files specified\n"); + exit(EXIT_SUCCESS); + } + + g_objpath[0] = '\0'; + if (objpath != NULL) + { + const char *hostpath; + int pathlen; + + /* If the object path relative to the current working directory? */ + + /* It is a relative path if the path does not begin with the path + * segment separator or if in the Windows native case, it begins + * with a volume specified like C:. + */ + + pathlen = 0; + +#ifdef HOST_NATIVE + if (objpath[0] != SEPARATOR || + (isalpha(objpath[0]) && objpath[1] != ':')) +#else + if (objpath[0] != SEPARATOR) +#endif + { + /* Add the default working directory to the path */ + + /* Copy the obj_path */ + + pathlen = strlen(g_wd); + if (pathlen >= MAX_PATH) + { + fprintf(stderr, "ERROR: Working directory path is " + "too long [%d/%d]: %s\n", + pathlen, MAX_PATH, g_wd); + exit(EXIT_FAILURE); + } + + strcpy(g_path, g_wd); + + /* Append a separator is one is not already present */ + + if (g_path[pathlen - 1] != SEPARATOR) + { + pathlen++; + if (pathlen >= MAX_PATH) + { + fprintf(stderr, "ERROR: Object path is too long " + "with separator[%d/%d]: %s\n", + pathlen, MAX_PATH, g_wd); + exit(EXIT_FAILURE); + } + + g_path[pathlen] = SEPARATOR; + g_path[pathlen + 1] = '\0'; + } + } + + /* Add the object file path after the current working directory */ + + pathlen += strlen(objpath); + if (pathlen >= MAX_PATH) + { + fprintf(stderr, "ERROR: Path+objpath is too long [%d/%d]\n", + pathlen, MAX_PATH); + exit(EXIT_FAILURE); + } + + strcat(g_path, objpath); + +#ifdef HOST_CYGWIN + /* Convert the POSIX working directory to a Windows native path */ + + hostpath = convert_path(g_path); + strcpy(g_objpath, hostpath); +#endif + } + + if (g_debug) + { + fprintf(stderr, "Derived:\n"); + fprintf(stderr, " Abs Object Path: [%s]\n", g_objpath[0] != '\0' ? g_objpath : "(None"); + fprintf(stderr, " Library Path : [%s]\n", g_libpath ? g_libpath : "(None)"); + fprintf(stderr, " Library Name : [%s]\n\n", g_libname ? g_libname : "(None)"); + } +} + +static void do_archive(void) +{ + struct stat buf; + char *alloc; + char *objects; + char *object; + char *lasts; + int cmdlen; + int pathlen; + int objlen; + int totallen; + int nobjects; + int ret; + + /* Make a copy of g_objects. We need to do this because at least the version + * of strtok_r above does modify it. + */ + + alloc = strdup(g_objects); + if (alloc == NULL) + { + fprintf(stderr, "ERROR: Failed to strdup object list\n"); + exit(EXIT_FAILURE); + } + + objects = alloc; + + /* We may have to loop since we limit the number of objects in each call + * to the librarian. + */ + + for (; ; ) + { + /* Copy the librarian into the command buffer */ + + cmdlen = strlen(g_ar); + if (cmdlen >= MAX_BUFFER) + { + fprintf(stderr, "ERROR: Librarian string is too long [%d/%d]: %s\n", + cmdlen, MAX_BUFFER, g_ar); + exit(EXIT_FAILURE); + } + + strcpy(g_command, g_ar); + + + /* Add a space */ + + g_command[cmdlen] = ' '; + cmdlen++; + g_command[cmdlen] = '\0'; + + /* Copy the librarian flags into the command buffer */ + + if (g_arflags != NULL) + { + const char *expanded; + + expanded = do_expand(g_arflags); + cmdlen += strlen(expanded); + + if (cmdlen >= MAX_BUFFER) + { + fprintf(stderr, "ERROR: CFLAG string is too long [%d/%d]: %s\n", + cmdlen, MAX_BUFFER, g_arflags); + exit(EXIT_FAILURE); + } + + strcat(g_command, expanded); + } + + /* Add each object file. This loop will continue until each path has been + * tried (failure) or until stat() finds the object file + */ + + nobjects = 0; + while ((object = strtok_r(objects, " ", &lasts)) != NULL) + { + const char *expanded; + const char *converted; + + /* Set objects to NULL. This will force strtok_r to move from the + * the first object in the list. + */ + + objects = NULL; + + /* Add a space */ + + g_command[cmdlen] = ' '; + cmdlen++; + g_command[cmdlen] = '\0'; + + /* Create a full path to the object file */ + + g_path[0] = '\0'; + pathlen = 0; + + /* Add the path to buffer path buffer first */ + + if (g_objpath[0] != '\0') + { + /* Copy the obj_path */ + + pathlen = strlen(g_objpath); + if (pathlen >= MAX_PATH) + { + fprintf(stderr, "ERROR: Path is too long [%d/%d]: %s\n", + pathlen, MAX_PATH, g_objpath); + exit(EXIT_FAILURE); + } + + strcpy(g_path, g_objpath); + + /* Append a separator is one is not already present */ + + if (g_path[pathlen - 1] != SEPARATOR) + { + pathlen++; + if (pathlen >= MAX_PATH) + { + fprintf(stderr, "ERROR: Path is too long with " + "separator[%d/%d]: %s\n", + pathlen, MAX_PATH, g_path); + exit(EXIT_FAILURE); + } + + g_path[pathlen] = SEPARATOR; + g_path[pathlen + 1] = '\0'; + } + } + + /* Add the object file name after the path */ + + objlen = strlen(object); + pathlen += objlen; + if (pathlen >= MAX_PATH) + { + fprintf(stderr, "ERROR: Path+objfile is too long [%d/%d]\n", + pathlen, MAX_PATH); + exit(EXIT_FAILURE); + } + + strcat(g_path, object); + + /* Check that a object file actually exists at this path */ + + converted = convert_path(g_path); + ret = stat(converted, &buf); + if (ret < 0) + { + continue; + } + + if (!S_ISREG(buf.st_mode)) + { + fprintf(stderr, "ERROR: Object %s exists but is not a regular file\n", + g_path); + exit(EXIT_FAILURE); + } + + /* Expand the path */ + + /* Copy the librarian argument of form like: + * + * =+- + */ + + pathlen = 4; /* For =+- and terminator */ + + expanded = do_expand(g_path); + pathlen += strlen(expanded); + + /* Get the full length */ + + pathlen += strlen(g_libname); + totallen = cmdlen + pathlen; + + if (totallen >= MAX_BUFFER) + { + fprintf(stderr, "ERROR: object argument is too long [%d/%d]: %s=+-%s\n", + totallen, MAX_BUFFER, g_libname, expanded); + exit(EXIT_FAILURE); + } + + /* Append the next librarian command */ + + pathlen = snprintf(&g_command[cmdlen], MAX_BUFFER - cmdlen, "%s=+-%s", + g_libname, expanded); + cmdlen += pathlen; + + /* Terminate early if we have a LOT files in the command line */ + + if (++nobjects >= MAX_OBJECTS) + { + break; + } + } + + /* Okay.. we have everything. Add the object files to the library. On + * a failure to start the compiler, system() will return -1; Otherwise, + * the returned value from the compiler is in WEXITSTATUS(ret). + */ + + if (g_debug) + { + fprintf(stderr, "Executing: %s\n", g_command); + } + + ret = system(g_command); +#ifdef WEXITSTATUS + if (ret < 0 || WEXITSTATUS(ret) != 0) + { + if (ret < 0) + { + fprintf(stderr, "ERROR: system failed: %s\n", strerror(errno)); + } + else + { + fprintf(stderr, "ERROR: %s failed: %d\n", g_ar, WEXITSTATUS(ret)); + } + + fprintf(stderr, " command: %s\n", g_command); + exit(EXIT_FAILURE); + } +#else + if (ret < 0) + { + fprintf(stderr, "ERROR: system failed: %s\n", strerror(errno)); + fprintf(stderr, " command: %s\n", g_command); + exit(EXIT_FAILURE); + } +#endif + + /* We don't really know that the command succeeded... Let's assume + * that it did + */ + + /* Check if we have more objects to process */ + + if (object == NULL) + { + /* No, we are finished */ + + break; + } + } + + free(alloc); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char **argv, char **envp) +{ + char *wd; + int len; + int ret; + + /* Get the current working directory */ + + wd = getcwd(g_wd, MAX_PATH); + if (wd == NULL) + { + fprintf(stderr, "ERROR: getcwd failed: %d\n", errno); + return EXIT_FAILURE; + } + + len = strlen(wd); + if (len >= PATH_MAX) + { + fprintf(stderr, "ERROR: Current directory too long: [%s]\n", wd); + return EXIT_FAILURE; + } + + strcpy(g_wd, wd); + + /* Parse command line parameters */ + + parse_args(argc, argv); + + /* Change to the directory containing the library */ + + if (g_libpath != NULL) + { + ret = chdir(g_libpath); + if (ret < 0) + { + fprintf(stderr, "ERROR: getcwd failed: %d\n", errno); + return EXIT_FAILURE; + } + } + + /* Then generate dependencies for each path on the command line. */ + + do_archive(); + return EXIT_SUCCESS; +}