90eed5c8f8
This tools needs to CD to directory holding the library because it requires that the library be in the current working directory when objects are inserted. Previously, the tool did this by creating an absolute path to the object file itself. So instead of the object file being in the current directory and the library in a directory at an absolute path, this tool used an abolte path to the object file with the library in the current directory. For the most part, that worked fine. However, in the apps directory the object file names are greatly extended in length. In the object file name was extended to a full absolute path and these huge file names were were exceeding some internal limit within the ZDS-II librarian. But useing shorter, relative paths, we were able to avoid hitting this limit and the entire build completed correctly.
1066 lines
28 KiB
C
1066 lines
28 KiB
C
/****************************************************************************
|
|
* tools/zds/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 <sys/stat.h>
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
#include <libgen.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef HOST_CYGWIN
|
|
# include <sys/cygwin.h>
|
|
#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 <dirent.h>
|
|
# 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.
|
|
*
|
|
* REVISIT: The librarian is supposed to handle multiple object insertions
|
|
* per call, but my experience was that it was unreliable in that case. That
|
|
* may have improved, however, and perhaps we can increase MAX_OPBJEXT.. TRY
|
|
* IT!
|
|
*/
|
|
|
|
#define MAX_OBJECTS 1 /* 64 */
|
|
|
|
/* Name of the host. The ZDS-II toolchain runs only on Windows. Therefore,
|
|
* the only options are (1) Windows native, or (2) Cygwin or environments
|
|
* that derive for Cygwin (like MSYS2).
|
|
*/
|
|
|
|
#define WINSEPARATOR '\\'
|
|
|
|
#if defined(HOST_NATIVE)
|
|
# define SEPARATOR '\\'
|
|
# define HOSTNAME "Native" /* Windows native */
|
|
#elif defined(HOST_CYGWIN)
|
|
# define SEPARATOR '/'
|
|
# define HOSTNAME "Cygwin" /* Cygwin or MSYS under Windows */
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static char *g_current_wd = NULL; /* Current working directory */
|
|
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_initial_wd[MAX_PATH]; /* Initial working directory */
|
|
static char g_path[MAX_PATH]; /* Temporary for path generation */
|
|
static char g_objpath[MAX_PATH]; /* Holds the relative path to the objects */
|
|
#ifdef HOST_CYGWIN
|
|
static char g_expand[MAX_EXPAND]; /* Temporary for quoted path */
|
|
static char g_dequoted[MAX_PATH]; /* Temporary for de-quoted path */
|
|
static char g_hostpath[MAX_PATH]; /* Temporary for host path conversions */
|
|
#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 *quote_backslash(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 quoted? */
|
|
|
|
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 or POSIX path.
|
|
*/
|
|
|
|
#ifdef HOST_CYGWIN
|
|
static const char *convert_path(const char *path, cygwin_conv_path_t what)
|
|
{
|
|
const char *retptr;
|
|
ssize_t size;
|
|
ssize_t ret;
|
|
bool quoted;
|
|
|
|
quoted = dequote_path(path);
|
|
if (quoted)
|
|
{
|
|
retptr = g_hostpath;
|
|
}
|
|
else
|
|
{
|
|
retptr = &g_hostpath[1];
|
|
}
|
|
|
|
size = cygwin_conv_path(what | 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(what | CCP_RELATIVE, g_dequoted,
|
|
&g_hostpath[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_hostpath[0] = '"';
|
|
g_hostpath[size] = '"';
|
|
}
|
|
|
|
g_hostpath[size + 1] = '\0';
|
|
return retptr;
|
|
}
|
|
#endif
|
|
|
|
static const char *convert_path_windows(const char *path)
|
|
{
|
|
#ifdef HOST_CYGWIN
|
|
return convert_path(path, CCP_POSIX_TO_WIN_A);
|
|
#else
|
|
strcpy(g_path, path);
|
|
return g_path;
|
|
#endif
|
|
}
|
|
|
|
static const char *convert_path_posix(const char *path)
|
|
{
|
|
#ifdef HOST_CYGWIN
|
|
return convert_path(path, CCP_WIN_A_TO_POSIX);
|
|
#else
|
|
strcpy(g_path, path);
|
|
return g_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 \"<AR>\" --library \"<LIBRARY>\" "
|
|
"obj [obj [obj...]]\n",
|
|
progname);
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Where:\n");
|
|
fprintf(stderr, " --ar <AR>\n");
|
|
fprintf(stderr, " A command line string that defines how to execute the "
|
|
"ZDS-II librarian\n");
|
|
fprintf(stderr, " --library \"<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 \"<ARFLAGS>\"\n");
|
|
fprintf(stderr, " Optional librarian flags\n");
|
|
fprintf(stderr, " --obj-path <path>\n");
|
|
fprintf(stderr, " Do not look in the current directory for the object "
|
|
"files. Instead, look in <path> for\n");
|
|
fprintf(stderr, " the object files. --obj-path may be used only 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)
|
|
{
|
|
const char *tmp = NULL;
|
|
char *library = NULL;
|
|
char *objpath = NULL;
|
|
int pathlen;
|
|
int argidx;
|
|
|
|
/* Parse arguments */
|
|
|
|
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)
|
|
{
|
|
const char *tmp_path;
|
|
|
|
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.
|
|
*/
|
|
|
|
tmp_path = convert_path_posix(argv[argidx]);
|
|
library = strdup(tmp_path);
|
|
if (library == NULL)
|
|
{
|
|
fprintf(stderr, "ERROR: strdup() failed\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
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_initial_wd);
|
|
fprintf(stderr, " Host Environ : [%s]\n",
|
|
HOSTNAME);
|
|
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\n",
|
|
g_objects ? g_objects : "(None)");
|
|
}
|
|
|
|
/* 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_libname = basename(library); /* Must come first */
|
|
g_libpath = dirname(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)
|
|
{
|
|
/* 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 initial working directory */
|
|
|
|
pathlen = strlen(g_initial_wd);
|
|
if (pathlen >= MAX_PATH)
|
|
{
|
|
fprintf(stderr, "ERROR: Working directory path is "
|
|
"too long [%d/%d]: %s\n",
|
|
pathlen, MAX_PATH, g_initial_wd);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
strcpy(g_path, g_initial_wd);
|
|
|
|
/* Append a separator is one is not already present */
|
|
|
|
if (g_path[pathlen - 1] != SEPARATOR)
|
|
{
|
|
int newlen = pathlen + 1;
|
|
if (newlen >= MAX_PATH)
|
|
{
|
|
fprintf(stderr, "ERROR: Object path is too long "
|
|
"with separator[%d/%d]: %s\n",
|
|
newlen, MAX_PATH, g_initial_wd);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
g_path[pathlen] = SEPARATOR;
|
|
g_path[pathlen + 1] = '\0';
|
|
pathlen = newlen;
|
|
}
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* The object was in the current working directory. If a library path
|
|
* is NOT the current working directory, then the library path will now
|
|
* be the current working directory and the path to the objects will be
|
|
* the working directory when the program was started.
|
|
*/
|
|
|
|
else if (g_libpath != NULL && strcmp(g_libpath, ".") != 0)
|
|
{
|
|
strcpy(g_path, g_initial_wd);
|
|
}
|
|
|
|
/* Convert the absolute objection file path to the native host path form
|
|
* NOTE that convert_path_posix() is a no-op in Windows native mode.
|
|
*/
|
|
|
|
tmp = convert_path_posix(g_path);
|
|
strcpy(g_path, tmp);
|
|
|
|
/* Check for a relative path. We will CD to g_libpath because the
|
|
* library must be in the current working directory.
|
|
*/
|
|
|
|
pathlen = strlen(g_libpath);
|
|
if (strncmp(g_path, g_libpath, pathlen) == 0)
|
|
{
|
|
const char *relpath = &g_path[pathlen];
|
|
|
|
/* Skip over leading path segment delimiters.. that should be as
|
|
* least one.
|
|
*/
|
|
|
|
while (*relpath == SEPARATOR)
|
|
{
|
|
relpath++;
|
|
}
|
|
|
|
/* Convert the relative object file path to the Windows path form
|
|
* for the ZDS-II tool tool. NOTE that convert_path_windows() is
|
|
* a no-op in Windows native mode.
|
|
*/
|
|
|
|
tmp = convert_path_windows(relpath);
|
|
}
|
|
else
|
|
{
|
|
/* Convert the absolute object file path to the Windows path form
|
|
* for the ZDS-II tool tool. NOTE that convert_path_windows() is
|
|
* a no-op in Windows native mode.
|
|
*/
|
|
|
|
tmp = convert_path_windows(g_path);
|
|
}
|
|
|
|
/* And save the path in as a native Windows path */
|
|
|
|
strcpy(g_objpath, tmp);
|
|
|
|
/* Dump some intermediate results */
|
|
|
|
if (g_debug)
|
|
{
|
|
fprintf(stderr, "Derived:\n");
|
|
fprintf(stderr, " 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.
|
|
*/
|
|
|
|
lasts = NULL;
|
|
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 *quoted;
|
|
|
|
quoted = quote_backslash(g_arflags);
|
|
cmdlen += strlen(quoted);
|
|
|
|
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, quoted);
|
|
}
|
|
|
|
/* 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 *quoted;
|
|
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] != WINSEPARATOR)
|
|
{
|
|
int newlen = pathlen + 1;
|
|
if (newlen >= MAX_PATH)
|
|
{
|
|
fprintf(stderr, "ERROR: Path is too long with "
|
|
"separator[%d/%d]: %s\n",
|
|
newlen, MAX_PATH, g_path);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
g_path[pathlen] = WINSEPARATOR;
|
|
g_path[pathlen + 1] = '\0';
|
|
pathlen = newlen;
|
|
}
|
|
}
|
|
|
|
/* 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. NOTE
|
|
* that convert_path_posix() is a no-op in Windows native mode.
|
|
*/
|
|
|
|
converted = convert_path_posix(g_path);
|
|
ret = stat(converted, &buf);
|
|
if (ret < 0)
|
|
{
|
|
fprintf(stderr, "WARNING: Stat of object %s failed: %s\n",
|
|
g_path, strerror(errno));
|
|
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:
|
|
*
|
|
* <libname>=-+<objpath>
|
|
*/
|
|
|
|
pathlen = 4; /* For =-+ and terminator */
|
|
|
|
quoted = quote_backslash(g_path);
|
|
pathlen += strlen(quoted);
|
|
|
|
/* 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, quoted);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Append the next librarian command */
|
|
|
|
pathlen = snprintf(&g_command[cmdlen], MAX_BUFFER - cmdlen, "%s=-+%s",
|
|
g_libname, quoted);
|
|
cmdlen += pathlen;
|
|
|
|
/* Terminate early if we have a LOT files in the command line */
|
|
|
|
if (++nobjects >= MAX_OBJECTS)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Handling the final command which may have not objects to insert */
|
|
|
|
if (nobjects > 0)
|
|
{
|
|
/* 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 ret;
|
|
|
|
/* Get the current working directory */
|
|
|
|
wd = getcwd(g_initial_wd, MAX_PATH);
|
|
if (wd == NULL)
|
|
{
|
|
fprintf(stderr, "ERROR: getcwd failed: %s\n", strerror(errno));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
g_current_wd = g_initial_wd;
|
|
|
|
/* Parse command line parameters */
|
|
|
|
parse_args(argc, argv);
|
|
|
|
/* Change to the directory containing the library */
|
|
|
|
if (g_libpath != NULL && strcmp(g_libpath, ".") != 0)
|
|
{
|
|
ret = chdir(g_libpath);
|
|
if (ret < 0)
|
|
{
|
|
fprintf(stderr, "ERROR: getcwd failed: %s\n", strerror(errno));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
g_current_wd = g_libpath;
|
|
}
|
|
|
|
/* Then generate dependencies for each path on the command line. */
|
|
|
|
do_archive();
|
|
return EXIT_SUCCESS;
|
|
}
|