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;
|
|
}
|