/* 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 #include #include #include #include #include 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, '/'); char *newarg; if (!ext) { return false; } ext++; if (name == NULL) { name = strrchr(arg, '\\'); if (name == NULL) { name = arg; } else { name++; } } else { name++; } 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; 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++; 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; }