From 55a7dfc9a7d0fe5b621767236c9b039d65c4bd2a Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 23 Feb 2020 18:13:24 -0300 Subject: [PATCH] tools/zds/zdsar.c: Fix several errors This zdsar librarian wrapper is code complete and has no known bugs. However, there are still some build instabilities so perhaps there are still some bug to be identified. Or perhaps there are other undiscovered bugs unrelated to zdsar which appears to be behaving correctly in all cases. It will take some additional testing know for sure. --- tools/zds/zdsar.c | 323 ++++++++++++++++++++++++++++------------------ 1 file changed, 200 insertions(+), 123 deletions(-) diff --git a/tools/zds/zdsar.c b/tools/zds/zdsar.c index 271bf96bef..5b3db5fa9f 100644 --- a/tools/zds/zdsar.c +++ b/tools/zds/zdsar.c @@ -75,9 +75,20 @@ # endif #endif -/* Maximum objects per librarian call */ +/* Maximum objects per librarian call. + * + * REVISIT: The librarian is supposed to handle multiple object insertions + * per call, but my experience is that it is unreliable in that case. + */ -#define MAX_OBJECTS 64 +#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 '\\' @@ -102,21 +113,22 @@ enum slashmode_e * 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_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_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 */ +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]; /* 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 */ +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 /**************************************************************************** @@ -126,7 +138,7 @@ static char g_posixpath[MAX_PATH]; /* Full POSIX path */ /* MinGW does not seem to provide strtok_r */ #ifndef HAVE_STRTOK_R -static char *MY_strtok_r(char *str, const char *delim, char **saveptr) +static char *my_strtok_r(char *str, const char *delim, char **saveptr) { char *pbegin; char *pend = NULL; @@ -193,7 +205,7 @@ static char *MY_strtok_r(char *str, const char *delim, char **saveptr) } #undef strtok_r -# define strtok_r MY_strtok_r +# define strtok_r my_strtok_r #endif static void append(char **base, char *str) @@ -225,7 +237,7 @@ static void append(char **base, char *str) snprintf(newbase, alloclen, "%s %s", oldbase, str); free(oldbase); - } + } *base = newbase; } @@ -289,7 +301,8 @@ static const char *do_expand(const char *argument) if (*src) { - fprintf(stderr, "ERROR: Truncated during expansion string is too long [%lu/%u]\n", + fprintf(stderr, "ERROR: Truncated during expansion string " + "is too long [%lu/%u]\n", (unsigned long)strlen(argument), MAX_EXPAND); exit(EXIT_FAILURE); } @@ -313,7 +326,8 @@ static bool dequote_path(const char *winpath) while (*src && len < MAX_PATH) { - if (src[0] != '\\' || (src[1] != ' ' && src[1] != '(' && src[1] != ')')) + if (src[0] != '\\' || + (src[1] != ' ' && src[1] != '(' && src[1] != ')')) { *dest++ = *src; len++; @@ -341,9 +355,9 @@ static bool dequote_path(const char *winpath) * POSIX path to a Windows path. */ -static const char *convert_path(const char *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; @@ -359,8 +373,7 @@ static const char *convert_path(const char *path) retptr = &g_posixpath[1]; } - size = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_RELATIVE, g_dequoted, - NULL, 0); + 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", @@ -368,7 +381,7 @@ static const char *convert_path(const char *path) exit(EXIT_FAILURE); } - ret = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_RELATIVE, g_dequoted, + ret = cygwin_conv_path(what | CCP_RELATIVE, g_dequoted, &g_posixpath[1], MAX_PATH - 3); if (ret < 0) { @@ -386,6 +399,22 @@ static const char *convert_path(const char *path) g_posixpath[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 + return path; +#endif +} + +static const char *convert_path_posix(const char *path) +{ +#ifdef HOST_CYGWIN + return convert_path(path, CCP_WIN_A_TO_POSIX); #else return path; #endif @@ -400,24 +429,31 @@ static void show_usage(const char *progname, const char *msg, int exitcode) } fprintf(stderr, "\n"); - fprintf(stderr, "%s [OPTIONS] --ar \"\" --library \"\" obj [obj [obj...]]\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, " 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, " 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, " 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, " 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"); @@ -440,11 +476,13 @@ static void parse_args(int argc, char **argv) argidx++; if (argidx >= argc) { - show_usage(argv[0], "ERROR: Missing argument to --ar", EXIT_FAILURE); + 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); + show_usage(argv[0], "ERROR: Multiple --ar arguments", + EXIT_FAILURE); } g_ar = argv[argidx]; @@ -454,11 +492,13 @@ static void parse_args(int argc, char **argv) argidx++; if (argidx >= argc) { - show_usage(argv[0], "ERROR: Missing argument to --ar_flags", EXIT_FAILURE); + 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); + show_usage(argv[0], "ERROR: Multiple --ar_flags arguments", + EXIT_FAILURE); } g_arflags = argv[argidx]; @@ -470,18 +510,20 @@ static void parse_args(int argc, char **argv) argidx++; if (argidx >= argc) { - show_usage(argv[0], "ERROR: Missing argument to --library", EXIT_FAILURE); + 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); + 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(argv[argidx]); + tmp_path = convert_path_posix(argv[argidx]); library = strdup(tmp_path); if (library == NULL) { @@ -494,11 +536,13 @@ static void parse_args(int argc, char **argv) argidx++; if (argidx >= argc) { - show_usage(argv[0], "ERROR: Missing argument to --obj-path", EXIT_FAILURE); + 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); + show_usage(argv[0], "ERROR: Multiple --obj-path arguments", + EXIT_FAILURE); } objpath = argv[argidx]; @@ -531,25 +575,34 @@ static void parse_args(int argc, char **argv) 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); + 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); + show_usage(argv[0], "ERROR: No librarian specified", + EXIT_FAILURE); } if (library == NULL) { - show_usage(argv[0], "ERROR: No library specified", EXIT_FAILURE); + show_usage(argv[0], "ERROR: No library specified", + EXIT_FAILURE); } else { @@ -557,8 +610,8 @@ static void parse_args(int argc, char **argv) * expects the library to be in the current working directory. */ + g_libname = basename(library); /* Must come first */ g_libpath = dirname(library); - g_libname = basename(library); } if (g_objects == NULL) @@ -593,34 +646,35 @@ static void parse_args(int argc, char **argv) { /* Add the default working directory to the path */ - /* Copy the obj_path */ + /* Copy the initial working directory */ - pathlen = strlen(g_wd); + 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_wd); + pathlen, MAX_PATH, g_initial_wd); exit(EXIT_FAILURE); } - strcpy(g_path, g_wd); + strcpy(g_path, g_initial_wd); /* Append a separator is one is not already present */ - if (g_path[pathlen - 1] != SEPARATOR) + if (g_path[pathlen - 1] != WINSEPARATOR) { - pathlen++; - if (pathlen >= MAX_PATH) + int newlen = pathlen + 1; + if (newlen >= MAX_PATH) { fprintf(stderr, "ERROR: Object path is too long " "with separator[%d/%d]: %s\n", - pathlen, MAX_PATH, g_wd); + newlen, MAX_PATH, g_initial_wd); exit(EXIT_FAILURE); } - g_path[pathlen] = SEPARATOR; + g_path[pathlen] = WINSEPARATOR; g_path[pathlen + 1] = '\0'; + pathlen = newlen; } } @@ -636,20 +690,35 @@ static void parse_args(int argc, char **argv) strcat(g_path, objpath); -#ifdef HOST_CYGWIN - /* Convert the POSIX working directory to a Windows native path */ + /* Convert the POSIX working directory to a Windows native path. NOTE + * that convert_path_windows() is a no-op in Windows native mode. + */ - hostpath = convert_path(g_path); + hostpath = convert_path_windows(g_path); strcpy(g_objpath, hostpath); -#endif + } + + /* 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) + { + const char *converted = convert_path_windows(g_initial_wd); + strcpy(g_objpath, converted); } 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)"); + 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)"); } } @@ -698,7 +767,6 @@ static void do_archive(void) strcpy(g_command, g_ar); - /* Add a space */ g_command[cmdlen] = ' '; @@ -769,19 +837,20 @@ static void do_archive(void) /* Append a separator is one is not already present */ - if (g_path[pathlen - 1] != SEPARATOR) + if (g_path[pathlen - 1] != WINSEPARATOR) { - pathlen++; - if (pathlen >= MAX_PATH) + int newlen = pathlen + 1; + if (newlen >= MAX_PATH) { fprintf(stderr, "ERROR: Path is too long with " "separator[%d/%d]: %s\n", - pathlen, MAX_PATH, g_path); + newlen, MAX_PATH, g_path); exit(EXIT_FAILURE); } - g_path[pathlen] = SEPARATOR; + g_path[pathlen] = WINSEPARATOR; g_path[pathlen + 1] = '\0'; + pathlen = newlen; } } @@ -798,18 +867,23 @@ static void do_archive(void) strcat(g_path, object); - /* Check that a object file actually exists at this path */ + /* 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(g_path); + 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", + fprintf(stderr, "ERROR: Object %s exists but is not a regular " + "file\n", g_path); exit(EXIT_FAILURE); } @@ -818,10 +892,10 @@ static void do_archive(void) /* Copy the librarian argument of form like: * - * =+- + * =-+ */ - pathlen = 4; /* For =+- and terminator */ + pathlen = 4; /* For =-+ and terminator */ expanded = do_expand(g_path); pathlen += strlen(expanded); @@ -833,14 +907,15 @@ static void do_archive(void) if (totallen >= MAX_BUFFER) { - fprintf(stderr, "ERROR: object argument is too long [%d/%d]: %s=+-%s\n", + 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", + pathlen = snprintf(&g_command[cmdlen], MAX_BUFFER - cmdlen, "%s=-+%s", g_libname, expanded); cmdlen += pathlen; @@ -852,48 +927,56 @@ static void do_archive(void) } } - /* 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). - */ + /* Handling the final command which may have not objects to insert */ - if (g_debug) + if (nobjects > 0) { - fprintf(stderr, "Executing: %s\n", g_command); - } + /* 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). + */ - ret = system(g_command); + 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 || 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); } - 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 - */ + /* We don't really know that the command succeeded... Let's + * assume that it did + */ + } - /* Check if we have more objects to process */ + /* Check if we have more objects to process */ - if (object == NULL) + if (object == NULL) { /* No, we are finished */ @@ -911,26 +994,18 @@ static void do_archive(void) 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); + wd = getcwd(g_initial_wd, MAX_PATH); if (wd == NULL) { - fprintf(stderr, "ERROR: getcwd failed: %d\n", errno); + fprintf(stderr, "ERROR: getcwd failed: %s\n", strerror(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); + g_current_wd = g_initial_wd; /* Parse command line parameters */ @@ -938,14 +1013,16 @@ int main(int argc, char **argv, char **envp) /* Change to the directory containing the library */ - if (g_libpath != NULL) + if (g_libpath != NULL && strcmp(g_libpath, ".") != 0) { ret = chdir(g_libpath); if (ret < 0) { - fprintf(stderr, "ERROR: getcwd failed: %d\n", errno); + 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. */