| /* 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. |
| */ |
| |
| #include <stdio.h> |
| #include <process.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <dirent.h> |
| |
| typedef char bool; |
| #define false 0 |
| #define true (!false) |
| |
| bool silent = false; |
| bool shared = false; |
| bool export_all = false; |
| enum mode_t { mCompile, mLink, mInstall }; |
| enum output_type_t { otGeneral, otObject, otProgram, otStaticLibrary, otDynamicLibrary }; |
| |
| #ifdef __EMX__ |
| # define SHELL_CMD "sh" |
| # define CC "gcc" |
| # define GEN_EXPORTS "emxexp" |
| # define DEF2IMPLIB_CMD "emximp" |
| # define SHARE_SW "-Zdll -Zmtd" |
| # define USE_OMF true |
| # define TRUNCATE_DLL_NAME |
| # define DYNAMIC_LIB_EXT "dll" |
| # define EXE_EXT ".exe" |
| |
| # if USE_OMF |
| /* OMF is the native format under OS/2 */ |
| # define STATIC_LIB_EXT "lib" |
| # define OBJECT_EXT "obj" |
| # define LIBRARIAN "emxomfar" |
| # else |
| /* but the alternative, a.out, can fork() which is sometimes necessary */ |
| # define STATIC_LIB_EXT "a" |
| # define OBJECT_EXT "o" |
| # define LIBRARIAN "ar" |
| # endif |
| #endif |
| |
| |
| typedef struct { |
| char *arglist[1024]; |
| int num_args; |
| enum mode_t mode; |
| enum output_type_t output_type; |
| char *output_name; |
| char *stub_name; |
| char *tmp_dirs[1024]; |
| int num_tmp_dirs; |
| char *obj_files[1024]; |
| int num_obj_files; |
| } cmd_data_t; |
| |
| void parse_args(int argc, char *argv[], cmd_data_t *cmd_data); |
| bool parse_long_opt(char *arg, cmd_data_t *cmd_data); |
| int parse_short_opt(char *arg, cmd_data_t *cmd_data); |
| bool parse_input_file_name(char *arg, cmd_data_t *cmd_data); |
| bool parse_output_file_name(char *arg, cmd_data_t *cmd_data); |
| void post_parse_fixup(cmd_data_t *cmd_data); |
| bool explode_static_lib(char *lib, cmd_data_t *cmd_data); |
| int execute_command(cmd_data_t *cmd_data); |
| char *shell_esc(const char *str); |
| void cleanup_tmp_dirs(cmd_data_t *cmd_data); |
| void generate_def_file(cmd_data_t *cmd_data); |
| char *nameof(char *fullpath); |
| char *truncate_dll_name(char *path); |
| |
| |
| int main(int argc, char *argv[]) |
| { |
| int rc; |
| cmd_data_t cmd_data; |
| |
| memset(&cmd_data, 0, sizeof(cmd_data)); |
| cmd_data.mode = mCompile; |
| cmd_data.output_type = otGeneral; |
| |
| parse_args(argc, argv, &cmd_data); |
| rc = execute_command(&cmd_data); |
| |
| if (rc == 0 && cmd_data.stub_name) { |
| fopen(cmd_data.stub_name, "w"); |
| } |
| |
| cleanup_tmp_dirs(&cmd_data); |
| return rc; |
| } |
| |
| |
| |
| void parse_args(int argc, char *argv[], cmd_data_t *cmd_data) |
| { |
| int a; |
| char *arg; |
| bool argused; |
| |
| for (a=1; a < argc; a++) { |
| arg = argv[a]; |
| argused = false; |
| |
| if (arg[0] == '-') { |
| if (arg[1] == '-') { |
| argused = parse_long_opt(arg + 2, cmd_data); |
| } else if (arg[1] == 'o' && a+1 < argc) { |
| cmd_data->arglist[cmd_data->num_args++] = arg; |
| arg = argv[++a]; |
| argused = parse_output_file_name(arg, cmd_data); |
| } else { |
| int num_used = parse_short_opt(arg + 1, cmd_data); |
| argused = num_used > 0; |
| |
| if (num_used > 1) { |
| a += num_used - 1; |
| } |
| } |
| } else { |
| argused = parse_input_file_name(arg, cmd_data); |
| } |
| |
| if (!argused) { |
| cmd_data->arglist[cmd_data->num_args++] = arg; |
| } |
| } |
| |
| post_parse_fixup(cmd_data); |
| } |
| |
| |
| |
| bool parse_long_opt(char *arg, cmd_data_t *cmd_data) |
| { |
| char *equal_pos = strchr(arg, '='); |
| char var[50]; |
| char value[500]; |
| |
| if (equal_pos) { |
| strncpy(var, arg, equal_pos - arg); |
| var[equal_pos - arg] = 0; |
| strcpy(value, equal_pos + 1); |
| } else { |
| strcpy(var, arg); |
| } |
| |
| if (strcmp(var, "silent") == 0) { |
| silent = true; |
| } else if (strcmp(var, "mode") == 0) { |
| if (strcmp(value, "compile") == 0) { |
| cmd_data->mode = mCompile; |
| cmd_data->output_type = otObject; |
| } |
| |
| if (strcmp(value, "link") == 0) { |
| cmd_data->mode = mLink; |
| } |
| |
| if (strcmp(value, "install") == 0) { |
| cmd_data->mode = mInstall; |
| } |
| } else if (strcmp(var, "shared") == 0) { |
| shared = true; |
| } else if (strcmp(var, "export-all") == 0) { |
| export_all = true; |
| } else { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| |
| int parse_short_opt(char *arg, cmd_data_t *cmd_data) |
| { |
| if (strcmp(arg, "export-dynamic") == 0) { |
| return 1; |
| } |
| |
| if (strcmp(arg, "module") == 0) { |
| return 1; |
| } |
| |
| if (strcmp(arg, "Zexe") == 0) { |
| return 1; |
| } |
| |
| if (strcmp(arg, "avoid-version") == 0) { |
| return 1; |
| } |
| |
| if (strcmp(arg, "prefer-pic") == 0) { |
| return 1; |
| } |
| |
| if (strcmp(arg, "prefer-non-pic") == 0) { |
| return 1; |
| } |
| |
| if (strcmp(arg, "version-info") == 0 ) { |
| return 2; |
| } |
| |
| if (strcmp(arg, "no-install") == 0) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| |
| |
| bool parse_input_file_name(char *arg, cmd_data_t *cmd_data) |
| { |
| char *ext = strrchr(arg, '.'); |
| char *name = strrchr(arg, '/'); |
| int pathlen; |
| char *newarg; |
| |
| if (!ext) { |
| return false; |
| } |
| |
| ext++; |
| |
| if (name == NULL) { |
| name = strrchr(arg, '\\'); |
| |
| if (name == NULL) { |
| name = arg; |
| } else { |
| name++; |
| } |
| } else { |
| name++; |
| } |
| |
| pathlen = name - arg; |
| |
| if (strcmp(ext, "lo") == 0) { |
| newarg = (char *)malloc(strlen(arg) + 10); |
| strcpy(newarg, arg); |
| strcpy(newarg + (ext - arg), OBJECT_EXT); |
| cmd_data->arglist[cmd_data->num_args++] = newarg; |
| cmd_data->obj_files[cmd_data->num_obj_files++] = newarg; |
| return true; |
| } |
| |
| if (strcmp(ext, "la") == 0) { |
| newarg = (char *)malloc(strlen(arg) + 10); |
| strcpy(newarg, arg); |
| newarg[pathlen] = 0; |
| strcat(newarg, ".libs/"); |
| |
| if (strncmp(name, "lib", 3) == 0) { |
| name += 3; |
| } |
| |
| strcat(newarg, name); |
| ext = strrchr(newarg, '.') + 1; |
| |
| if (shared && cmd_data->mode == mInstall) { |
| strcpy(ext, DYNAMIC_LIB_EXT); |
| newarg = truncate_dll_name(newarg); |
| } else { |
| strcpy(ext, STATIC_LIB_EXT); |
| } |
| |
| cmd_data->arglist[cmd_data->num_args++] = newarg; |
| return true; |
| } |
| |
| if (strcmp(ext, "c") == 0) { |
| if (cmd_data->stub_name == NULL) { |
| cmd_data->stub_name = (char *)malloc(strlen(arg) + 4); |
| strcpy(cmd_data->stub_name, arg); |
| strcpy(strrchr(cmd_data->stub_name, '.') + 1, "lo"); |
| } |
| } |
| |
| if (strcmp(name, CC) == 0 || strcmp(name, CC EXE_EXT) == 0) { |
| if (cmd_data->output_type == otGeneral) { |
| cmd_data->output_type = otObject; |
| } |
| } |
| |
| return false; |
| } |
| |
| |
| |
| bool parse_output_file_name(char *arg, cmd_data_t *cmd_data) |
| { |
| char *name = strrchr(arg, '/'); |
| char *ext = strrchr(arg, '.'); |
| char *newarg = NULL, *newext; |
| int pathlen; |
| |
| if (name == NULL) { |
| name = strrchr(arg, '\\'); |
| |
| if (name == NULL) { |
| name = arg; |
| } else { |
| name++; |
| } |
| } else { |
| name++; |
| } |
| |
| if (!ext) { |
| cmd_data->stub_name = arg; |
| cmd_data->output_type = otProgram; |
| newarg = (char *)malloc(strlen(arg) + 5); |
| strcpy(newarg, arg); |
| strcat(newarg, EXE_EXT); |
| cmd_data->arglist[cmd_data->num_args++] = newarg; |
| cmd_data->output_name = newarg; |
| return true; |
| } |
| |
| ext++; |
| pathlen = name - arg; |
| |
| if (strcmp(ext, "la") == 0) { |
| cmd_data->stub_name = arg; |
| cmd_data->output_type = shared ? otDynamicLibrary : otStaticLibrary; |
| newarg = (char *)malloc(strlen(arg) + 10); |
| mkdir(".libs", 0); |
| strcpy(newarg, ".libs/"); |
| |
| if (strncmp(arg, "lib", 3) == 0) { |
| arg += 3; |
| } |
| |
| strcat(newarg, arg); |
| newext = strrchr(newarg, '.') + 1; |
| strcpy(newext, shared ? DYNAMIC_LIB_EXT : STATIC_LIB_EXT); |
| |
| #ifdef TRUNCATE_DLL_NAME |
| if (shared) { |
| newarg = truncate_dll_name(newarg); |
| } |
| #endif |
| |
| cmd_data->arglist[cmd_data->num_args++] = newarg; |
| cmd_data->output_name = newarg; |
| return true; |
| } |
| |
| if (strcmp(ext, "lo") == 0) { |
| cmd_data->stub_name = arg; |
| cmd_data->output_type = otObject; |
| newarg = (char *)malloc(strlen(arg) + 2); |
| strcpy(newarg, arg); |
| ext = strrchr(newarg, '.') + 1; |
| strcpy(ext, OBJECT_EXT); |
| cmd_data->arglist[cmd_data->num_args++] = newarg; |
| cmd_data->output_name = newarg; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| |
| |
| void post_parse_fixup(cmd_data_t *cmd_data) |
| { |
| int a; |
| char *arg; |
| char *ext; |
| |
| if (cmd_data->output_type == otStaticLibrary && cmd_data->mode == mLink) { |
| /* We do a real hatchet job on the args when making a static library |
| * removing all compiler switches & any other cruft that ar won't like |
| * We also need to explode any libraries listed |
| */ |
| |
| for (a=0; a < cmd_data->num_args; a++) { |
| arg = cmd_data->arglist[a]; |
| |
| if (arg) { |
| ext = strrchr(arg, '.'); |
| |
| if (ext) { |
| ext++; |
| } |
| |
| if (arg[0] == '-') { |
| cmd_data->arglist[a] = NULL; |
| |
| if (strcmp(arg, "-rpath") == 0 && a+1 < cmd_data->num_args) { |
| cmd_data->arglist[a+1] = NULL; |
| } |
| |
| if (strcmp(arg, "-R") == 0 && a+1 < cmd_data->num_args) { |
| cmd_data->arglist[a+1] = NULL; |
| } |
| |
| if (strcmp(arg, "-version-info") == 0 && a+1 < cmd_data->num_args) { |
| cmd_data->arglist[a+1] = NULL; |
| } |
| |
| if (strcmp(arg, "-Zstack") == 0 && a+1 < cmd_data->num_args) { |
| cmd_data->arglist[a+1] = NULL; |
| } |
| |
| if (strcmp(arg, "-o") == 0) { |
| a++; |
| } |
| } |
| |
| if (strcmp(arg, CC) == 0 || strcmp(arg, CC EXE_EXT) == 0) { |
| cmd_data->arglist[a] = LIBRARIAN " cr"; |
| } |
| |
| if (ext) { |
| if (strcmp(ext, "h") == 0 || strcmp(ext, "c") == 0) { |
| /* ignore source files, they don't belong in a library */ |
| cmd_data->arglist[a] = NULL; |
| } |
| |
| if (strcmp(ext, STATIC_LIB_EXT) == 0) { |
| cmd_data->arglist[a] = NULL; |
| explode_static_lib(arg, cmd_data); |
| } |
| } |
| } |
| } |
| } |
| |
| if (cmd_data->output_type == otDynamicLibrary) { |
| for (a=0; a < cmd_data->num_args; a++) { |
| arg = cmd_data->arglist[a]; |
| |
| if (arg) { |
| if (strcmp(arg, "-rpath") == 0 && a+1 < cmd_data->num_args) { |
| cmd_data->arglist[a] = NULL; |
| cmd_data->arglist[a+1] = NULL; |
| } |
| } |
| } |
| |
| if (export_all) { |
| generate_def_file(cmd_data); |
| } |
| } |
| |
| #if USE_OMF |
| if (cmd_data->output_type == otObject || |
| cmd_data->output_type == otProgram || |
| cmd_data->output_type == otDynamicLibrary) { |
| cmd_data->arglist[cmd_data->num_args++] = "-Zomf"; |
| } |
| #endif |
| |
| if (shared && (cmd_data->output_type == otObject || cmd_data->output_type == otDynamicLibrary)) { |
| cmd_data->arglist[cmd_data->num_args++] = SHARE_SW; |
| } |
| } |
| |
| |
| |
| int execute_command(cmd_data_t *cmd_data) |
| { |
| int target = 0; |
| char *command; |
| int a, total_len = 0; |
| char *args[4]; |
| |
| for (a=0; a < cmd_data->num_args; a++) { |
| if (cmd_data->arglist[a]) { |
| total_len += strlen(cmd_data->arglist[a]) + 1; |
| } |
| } |
| |
| command = (char *)malloc( total_len ); |
| command[0] = 0; |
| |
| for (a=0; a < cmd_data->num_args; a++) { |
| if (cmd_data->arglist[a]) { |
| strcat(command, cmd_data->arglist[a]); |
| strcat(command, " "); |
| } |
| } |
| |
| command[strlen(command)-1] = 0; |
| |
| if (!silent) { |
| puts(command); |
| } |
| |
| cmd_data->num_args = target; |
| cmd_data->arglist[cmd_data->num_args] = NULL; |
| command = shell_esc(command); |
| |
| args[0] = SHELL_CMD; |
| args[1] = "-c"; |
| args[2] = command; |
| args[3] = NULL; |
| return spawnvp(P_WAIT, args[0], args); |
| } |
| |
| |
| |
| char *shell_esc(const char *str) |
| { |
| char *cmd; |
| unsigned char *d; |
| const unsigned char *s; |
| |
| cmd = (char *)malloc(2 * strlen(str) + 1); |
| d = (unsigned char *)cmd; |
| s = (const unsigned char *)str; |
| |
| for (; *s; ++s) { |
| if (*s == '"' || *s == '\\') { |
| *d++ = '\\'; |
| } |
| *d++ = *s; |
| } |
| |
| *d = '\0'; |
| return cmd; |
| } |
| |
| |
| |
| bool explode_static_lib(char *lib, cmd_data_t *cmd_data) |
| { |
| char tmpdir[1024]; |
| char savewd[1024]; |
| char cmd[1024]; |
| char *name; |
| DIR *dir; |
| struct dirent *entry; |
| |
| strcpy(tmpdir, lib); |
| strcat(tmpdir, ".exploded"); |
| |
| mkdir(tmpdir, 0); |
| cmd_data->tmp_dirs[cmd_data->num_tmp_dirs++] = strdup(tmpdir); |
| getcwd(savewd, sizeof(savewd)); |
| |
| if (chdir(tmpdir) != 0) |
| return false; |
| |
| strcpy(cmd, LIBRARIAN " x "); |
| name = strrchr(lib, '/'); |
| |
| if (name) { |
| name++; |
| } else { |
| name = lib; |
| } |
| |
| strcat(cmd, "../"); |
| strcat(cmd, name); |
| system(cmd); |
| chdir(savewd); |
| dir = opendir(tmpdir); |
| |
| while ((entry = readdir(dir)) != NULL) { |
| if (entry->d_name[0] != '.') { |
| strcpy(cmd, tmpdir); |
| strcat(cmd, "/"); |
| strcat(cmd, entry->d_name); |
| cmd_data->arglist[cmd_data->num_args++] = strdup(cmd); |
| } |
| } |
| |
| closedir(dir); |
| return true; |
| } |
| |
| |
| |
| void cleanup_tmp_dir(char *dirname) |
| { |
| DIR *dir; |
| struct dirent *entry; |
| char fullname[1024]; |
| |
| dir = opendir(dirname); |
| |
| if (dir == NULL) |
| return; |
| |
| while ((entry = readdir(dir)) != NULL) { |
| if (entry->d_name[0] != '.') { |
| strcpy(fullname, dirname); |
| strcat(fullname, "/"); |
| strcat(fullname, entry->d_name); |
| remove(fullname); |
| } |
| } |
| |
| rmdir(dirname); |
| } |
| |
| |
| |
| void cleanup_tmp_dirs(cmd_data_t *cmd_data) |
| { |
| int d; |
| |
| for (d=0; d < cmd_data->num_tmp_dirs; d++) { |
| cleanup_tmp_dir(cmd_data->tmp_dirs[d]); |
| } |
| } |
| |
| |
| |
| void generate_def_file(cmd_data_t *cmd_data) |
| { |
| char def_file[1024]; |
| char implib_file[1024]; |
| char *ext; |
| FILE *hDef; |
| char *export_args[1024]; |
| int num_export_args = 0; |
| char *cmd; |
| int cmd_size = 0; |
| int a; |
| |
| if (cmd_data->output_name) { |
| strcpy(def_file, cmd_data->output_name); |
| strcat(def_file, ".def"); |
| hDef = fopen(def_file, "w"); |
| |
| if (hDef != NULL) { |
| fprintf(hDef, "LIBRARY '%s' INITINSTANCE\n", nameof(cmd_data->output_name)); |
| fprintf(hDef, "DATA NONSHARED\n"); |
| fprintf(hDef, "EXPORTS\n"); |
| fclose(hDef); |
| |
| for (a=0; a < cmd_data->num_obj_files; a++) { |
| cmd_size += strlen(cmd_data->obj_files[a]) + 1; |
| } |
| |
| cmd_size += strlen(GEN_EXPORTS) + strlen(def_file) + 3; |
| cmd = (char *)malloc(cmd_size); |
| strcpy(cmd, GEN_EXPORTS); |
| |
| for (a=0; a < cmd_data->num_obj_files; a++) { |
| strcat(cmd, " "); |
| strcat(cmd, cmd_data->obj_files[a] ); |
| } |
| |
| strcat(cmd, ">>"); |
| strcat(cmd, def_file); |
| puts(cmd); |
| export_args[num_export_args++] = SHELL_CMD; |
| export_args[num_export_args++] = "-c"; |
| export_args[num_export_args++] = cmd; |
| export_args[num_export_args++] = NULL; |
| spawnvp(P_WAIT, export_args[0], export_args); |
| cmd_data->arglist[cmd_data->num_args++] = strdup(def_file); |
| |
| /* Now make an import library for the dll */ |
| num_export_args = 0; |
| export_args[num_export_args++] = DEF2IMPLIB_CMD; |
| export_args[num_export_args++] = "-o"; |
| |
| strcpy(implib_file, ".libs/"); |
| strcat(implib_file, cmd_data->stub_name); |
| ext = strrchr(implib_file, '.'); |
| |
| if (ext) |
| *ext = 0; |
| |
| strcat(implib_file, "."); |
| strcat(implib_file, STATIC_LIB_EXT); |
| |
| export_args[num_export_args++] = implib_file; |
| export_args[num_export_args++] = def_file; |
| export_args[num_export_args++] = NULL; |
| spawnvp(P_WAIT, export_args[0], export_args); |
| } |
| } |
| } |
| |
| |
| |
| /* returns just a file's name without path or extension */ |
| char *nameof(char *fullpath) |
| { |
| char buffer[1024]; |
| char *ext; |
| char *name = strrchr(fullpath, '/'); |
| |
| if (name == NULL) { |
| name = strrchr(fullpath, '\\'); |
| } |
| |
| if (name == NULL) { |
| name = fullpath; |
| } else { |
| name++; |
| } |
| |
| strcpy(buffer, name); |
| ext = strrchr(buffer, '.'); |
| |
| if (ext) { |
| *ext = 0; |
| return strdup(buffer); |
| } |
| |
| return name; |
| } |
| |
| |
| |
| char *truncate_dll_name(char *path) |
| { |
| /* Cut DLL name down to 8 characters after removing any mod_ prefix */ |
| char *tmppath = strdup(path); |
| char *newname = strrchr(tmppath, '/') + 1; |
| char *ext = strrchr(tmppath, '.'); |
| int len; |
| |
| if (ext == NULL) |
| return tmppath; |
| |
| len = ext - newname; |
| |
| if (strncmp(newname, "mod_", 4) == 0) { |
| strcpy(newname, newname + 4); |
| len -= 4; |
| } |
| |
| if (len > 8) { |
| strcpy(newname + 8, strchr(newname, '.')); |
| } |
| |
| return tmppath; |
| } |