/* 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 <string.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #if !defined(__MINGW32__) #include <sys/wait.h> #endif #include <unistd.h> #include <dirent.h> #include <errno.h> #include <assert.h> #ifdef __EMX__ # define SHELL_CMD "sh" # define GEN_EXPORTS "emxexp" # define DEF2IMPLIB_CMD "emximp" # define SHARE_SW "-Zdll -Zmtd" # define USE_OMF 1 # 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" # define LIBRARIAN_OPTS "cr" # else /* but the alternative, a.out, can fork() which is sometimes necessary */ # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # endif #endif #if defined(__APPLE__) # define SHELL_CMD "/bin/sh" # define DYNAMIC_LIB_EXT "dylib" # define MODULE_LIB_EXT "bundle" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" /* man libtool(1) documents ranlib option of -c. */ # define RANLIB "ranlib" # define PIC_FLAG "-fPIC -fno-common" # define SHARED_OPTS "-dynamiclib" # define MODULE_OPTS "-bundle" # define DYNAMIC_LINK_OPTS "-flat_namespace" # define DYNAMIC_LINK_UNDEFINED "-undefined suppress" # define dynamic_link_version_func darwin_dynamic_link_function # define DYNAMIC_INSTALL_NAME "-install_name" # define DYNAMIC_LINK_NO_INSTALL "-dylib_file" # define HAS_REALPATH /*-install_name /Users/jerenk/apache-2.0-cvs/lib/libapr.0.dylib -compatibility_version 1 -current_version 1.0 */ # define LD_LIBRARY_PATH "DYLD_LIBRARY_PATH" #endif #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) # define SHELL_CMD "/bin/sh" # define DYNAMIC_LIB_EXT "so" # define MODULE_LIB_EXT "so" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # define RANLIB "ranlib" # define PIC_FLAG "-fPIC" # define RPATH "-rpath" # define SHARED_OPTS "-shared" # define MODULE_OPTS "-shared" # define DYNAMIC_LINK_OPTS "-export-dynamic" # define LINKER_FLAG_PREFIX "-Wl," # define ADD_MINUS_L # define LD_RUN_PATH "LD_RUN_PATH" # define LD_LIBRARY_PATH "LD_LIBRARY_PATH" #endif #if defined(sun) # define SHELL_CMD "/bin/sh" # define DYNAMIC_LIB_EXT "so" # define MODULE_LIB_EXT "so" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # define RANLIB "ranlib" # define PIC_FLAG "-KPIC" # define RPATH "-R" # define SHARED_OPTS "-G" # define MODULE_OPTS "-G" # define DYNAMIC_LINK_OPTS "" # define LINKER_FLAG_NO_EQUALS # define ADD_MINUS_L # define HAS_REALPATH # define LD_RUN_PATH "LD_RUN_PATH" # define LD_LIBRARY_PATH "LD_LIBRARY_PATH" #endif #if defined(_OSD_POSIX) # define SHELL_CMD "/usr/bin/sh" # define DYNAMIC_LIB_EXT "so" # define MODULE_LIB_EXT "so" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # define SHARED_OPTS "-G" # define MODULE_OPTS "-G" # define LINKER_FLAG_PREFIX "-Wl," # define NEED_SNPRINTF #endif #if defined(sinix) && defined(mips) && defined(__SNI_TARG_UNIX) # define SHELL_CMD "/usr/bin/sh" # define DYNAMIC_LIB_EXT "so" # define MODULE_LIB_EXT "so" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # define RPATH "-Brpath" # define SHARED_OPTS "-G" # define MODULE_OPTS "-G" # define DYNAMIC_LINK_OPTS "-Wl,-Blargedynsym" # define LINKER_FLAG_PREFIX "-Wl," # define NEED_SNPRINTF # define LD_RUN_PATH "LD_RUN_PATH" # define LD_LIBRARY_PATH "LD_LIBRARY_PATH" #endif #if defined(__MINGW32__) # define SHELL_CMD "sh" # define DYNAMIC_LIB_EXT "dll" # define MODULE_LIB_EXT "dll" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # define RANLIB "ranlib" # define LINKER_FLAG_PREFIX "-Wl," # define SHARED_OPTS "-shared" # define MODULE_OPTS "-shared" # define MKDIR_NO_UMASK # define EXE_EXT ".exe" #endif #ifndef SHELL_CMD #error Unsupported platform: Please add defines for SHELL_CMD etc. for your platform. #endif #ifdef NEED_SNPRINTF #include <stdarg.h> #endif #ifdef __EMX__ #include <process.h> #endif #ifndef PATH_MAX #define PATH_MAX 1024 #endif /* We want to say we are libtool 1.4 for shlibtool compatibility. */ #define VERSION "1.4" enum tool_mode_t { mUnknown, mCompile, mLink, mInstall, }; enum output_t { otGeneral, otObject, otProgram, otLibrary, otStaticLibraryOnly, otDynamicLibraryOnly, otModule, }; enum pic_mode_e { pic_UNKNOWN, pic_PREFER, pic_AVOID, }; enum shared_mode_e { share_UNSET, share_STATIC, share_SHARED, }; enum lib_type { type_UNKNOWN, type_DYNAMIC_LIB, type_STATIC_LIB, type_MODULE_LIB, type_OBJECT, }; typedef struct { const char **vals; int num; } count_chars; typedef struct { const char *normal; const char *install; } library_name; typedef struct { count_chars *normal; count_chars *install; count_chars *dependencies; } library_opts; typedef struct { int silent; enum shared_mode_e shared; int export_all; int dry_run; enum pic_mode_e pic_mode; int export_dynamic; int no_install; } options_t; typedef struct { enum tool_mode_t mode; enum output_t output; options_t options; char *output_name; char *fake_output_name; char *basename; const char *install_path; const char *compiler; const char *program; count_chars *program_opts; count_chars *arglist; count_chars *tmp_dirs; count_chars *obj_files; count_chars *dep_rpaths; count_chars *rpaths; library_name static_name; library_name shared_name; library_name module_name; library_opts static_opts; library_opts shared_opts; const char *version_info; const char *undefined_flag; } command_t; #ifdef RPATH void add_rpath(count_chars *cc, const char *path); #endif #if defined(NEED_SNPRINTF) /* Write at most n characters to the buffer in str, return the * number of chars written or -1 if the buffer would have been * overflowed. * * This is portable to any POSIX-compliant system has /dev/null */ static FILE *f=NULL; static int vsnprintf( char *str, size_t n, const char *fmt, va_list ap ) { int res; if (f == NULL) f = fopen("/dev/null","w"); if (f == NULL) return -1; setvbuf( f, str, _IOFBF, n ); res = vfprintf( f, fmt, ap ); if ( res > 0 && res < n ) { res = vsprintf( str, fmt, ap ); } return res; } static int snprintf( char *str, size_t n, const char *fmt, ... ) { va_list ap; int res; va_start( ap, fmt ); res = vsnprintf( str, n, fmt, ap ); va_end( ap ); return res; } #endif void init_count_chars(count_chars *cc) { cc->vals = (const char**)malloc(PATH_MAX*sizeof(char*)); cc->num = 0; } void clear_count_chars(count_chars *cc) { int i; for (i = 0; i < cc->num; i++) { cc->vals[i] = 0; } cc->num = 0; } void push_count_chars(count_chars *cc, const char *newval) { cc->vals[cc->num++] = newval; } void pop_count_chars(count_chars *cc) { cc->num--; } void insert_count_chars(count_chars *cc, const char *newval, int position) { int i; for (i = cc->num; i > position; i--) { cc->vals[i] = cc->vals[i-1]; } cc->vals[position] = newval; cc->num++; } void append_count_chars(count_chars *cc, count_chars *cctoadd) { int i; for (i = 0; i < cctoadd->num; i++) { if (cctoadd->vals[i]) { push_count_chars(cc, cctoadd->vals[i]); } } } const char *flatten_count_chars(count_chars *cc, int space) { int i, size; char *newval; size = 0; for (i = 0; i < cc->num; i++) { if (cc->vals[i]) { size += strlen(cc->vals[i]) + 1; if (space) { size++; } } } newval = (char*)malloc(size + 1); newval[0] = 0; for (i = 0; i < cc->num; i++) { if (cc->vals[i]) { strcat(newval, cc->vals[i]); if (space) { strcat(newval, " "); } } } return newval; } char *shell_esc(const char *str) { int in_quote = 0; char *cmd; unsigned char *d; const unsigned char *s; cmd = (char *)malloc(2 * strlen(str) + 3); d = (unsigned char *)cmd; s = (const unsigned char *)str; #ifdef __MINGW32__ *d++ = '\"'; #endif for (; *s; ++s) { if (*s == '"') { *d++ = '\\'; in_quote++; } else if (*s == '\\' || (*s == ' ' && (in_quote % 2))) { *d++ = '\\'; } *d++ = *s; } #ifdef __MINGW32__ *d++ = '\"'; #endif *d = '\0'; return cmd; } int external_spawn(command_t *cmd, const char *file, const char **argv) { if (!cmd->options.silent) { const char **argument = argv; printf("Executing: "); while (*argument) { printf("%s ", *argument); argument++; } puts(""); } if (cmd->options.dry_run) { return 0; } #if defined(__EMX__) || defined(__MINGW32__) return spawnvp(P_WAIT, argv[0], argv); #else { pid_t pid; pid = fork(); if (pid == 0) { return execvp(argv[0], (char**)argv); } else { int statuscode; waitpid(pid, &statuscode, 0); if (WIFEXITED(statuscode)) { return WEXITSTATUS(statuscode); } return 0; } } #endif } int run_command(command_t *cmd_data, count_chars *cc) { char *command; const char *spawn_args[4]; count_chars tmpcc; init_count_chars(&tmpcc); if (cmd_data->program) { push_count_chars(&tmpcc, cmd_data->program); } append_count_chars(&tmpcc, cmd_data->program_opts); append_count_chars(&tmpcc, cc); command = shell_esc(flatten_count_chars(&tmpcc, 1)); spawn_args[0] = SHELL_CMD; spawn_args[1] = "-c"; spawn_args[2] = command; spawn_args[3] = NULL; return external_spawn(cmd_data, spawn_args[0], (const char**)spawn_args); } /* * print configuration * shlibpath_var is used in configure. */ void print_config() { #ifdef LD_RUN_PATH printf("runpath_var=%s\n", LD_RUN_PATH); #endif #ifdef LD_LIBRARY_PATH printf("shlibpath_var=%s\n", LD_LIBRARY_PATH); #endif #ifdef SHELL_CMD printf("SHELL=\"%s\"\n", SHELL_CMD); #endif } /* * Add a directory to the runtime library search path. */ void add_runtimedirlib(char *arg, command_t *cmd_data) { #ifdef RPATH add_rpath(cmd_data->shared_opts.dependencies, arg); #else #endif } int parse_long_opt(char *arg, command_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) { cmd_data->options.silent = 1; } else if (strcmp(var, "mode") == 0) { if (strcmp(value, "compile") == 0) { cmd_data->mode = mCompile; cmd_data->output = otObject; } if (strcmp(value, "link") == 0) { cmd_data->mode = mLink; cmd_data->output = otLibrary; } if (strcmp(value, "install") == 0) { cmd_data->mode = mInstall; } } else if (strcmp(var, "shared") == 0) { if (cmd_data->mode == mLink) { cmd_data->output = otDynamicLibraryOnly; } cmd_data->options.shared = share_SHARED; } else if (strcmp(var, "export-all") == 0) { cmd_data->options.export_all = 1; } else if (strcmp(var, "dry-run") == 0) { printf("Dry-run mode on!\n"); cmd_data->options.dry_run = 1; } else if (strcmp(var, "version") == 0) { printf("Version " VERSION "\n"); } else if (strcmp(var, "help") == 0) { printf("Sorry. No help available.\n"); } else if (strcmp(var, "config") == 0) { print_config(); } else if (strcmp(var, "tag") == 0) { if (strcmp(value, "CC") == 0) { /* Do nothing. */ } if (strcmp(value, "CXX") == 0) { /* Do nothing. */ } } else { return 0; } return 1; } /* Return 1 if we eat it. */ int parse_short_opt(char *arg, command_t *cmd_data) { if (strcmp(arg, "export-dynamic") == 0) { cmd_data->options.export_dynamic = 1; return 1; } if (strcmp(arg, "module") == 0) { cmd_data->output = otModule; return 1; } if (strcmp(arg, "shared") == 0) { if (cmd_data->mode == mLink) { cmd_data->output = otDynamicLibraryOnly; } cmd_data->options.shared = share_SHARED; return 1; } if (strcmp(arg, "Zexe") == 0) { return 1; } if (strcmp(arg, "avoid-version") == 0) { return 1; } if (strcmp(arg, "prefer-pic") == 0) { cmd_data->options.pic_mode = pic_PREFER; return 1; } if (strcmp(arg, "prefer-non-pic") == 0) { cmd_data->options.pic_mode = pic_AVOID; return 1; } if (strcmp(arg, "static") == 0) { cmd_data->options.shared = share_STATIC; return 1; } if (cmd_data->mode == mLink) { if (strcmp(arg, "no-install") == 0) { cmd_data->options.no_install = 1; return 1; } if (arg[0] == 'L' || arg[0] == 'l') { /* Hack... */ arg--; push_count_chars(cmd_data->shared_opts.dependencies, arg); return 1; } else if (arg[0] == 'R' && arg[1]) { /* -Rdir Add dir to runtime library search path. */ add_runtimedirlib(&arg[1], cmd_data); return 1; } } return 0; } 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; } long safe_strtol(const char *nptr, const char **endptr, int base) { long rv; errno = 0; rv = strtol(nptr, (char**)endptr, 10); if (errno == ERANGE) { return 0; } return rv; } void safe_mkdir(const char *path) { mode_t old_umask; old_umask = umask(0); umask(old_umask); #ifdef MKDIR_NO_UMASK mkdir(path); #else mkdir(path, ~old_umask); #endif } /* version_info is in the form of MAJOR:MINOR:PATCH */ const char *darwin_dynamic_link_function(const char *version_info) { char *newarg; long major, minor, patch; major = 0; minor = 0; patch = 0; if (version_info) { major = safe_strtol(version_info, &version_info, 10); if (version_info) { if (version_info[0] == ':') { version_info++; } minor = safe_strtol(version_info, &version_info, 10); if (version_info) { if (version_info[0] == ':') { version_info++; } patch = safe_strtol(version_info, &version_info, 10); } } } /* Avoid -dylib_compatibility_version must be greater than zero errors. */ if (major == 0) { major = 1; } newarg = (char*)malloc(100); snprintf(newarg, 99, "-compatibility_version %ld -current_version %ld.%ld", major, major, minor); return newarg; } /* genlib values * 0 - static * 1 - dynamic * 2 - module */ char *gen_library_name(const char *name, int genlib) { char *newarg, *newext; newarg = (char *)malloc(strlen(name) + 10); strcpy(newarg, ".libs/"); if (genlib == 2 && strncmp(name, "lib", 3) == 0) { name += 3; } strcat(newarg, name); newext = strrchr(newarg, '.') + 1; switch (genlib) { case 0: strcpy(newext, STATIC_LIB_EXT); break; case 1: strcpy(newext, DYNAMIC_LIB_EXT); break; case 2: strcpy(newext, MODULE_LIB_EXT); break; } return newarg; } /* genlib values * 0 - static * 1 - dynamic * 2 - module */ char *gen_install_name(const char *name, int genlib) { struct stat sb; char *newname; int rv; newname = gen_library_name(name, genlib); /* Check if it exists. If not, return NULL. */ rv = stat(newname, &sb); if (rv) { return NULL; } return newname; } char *check_object_exists(command_t *cmd, const char *arg, int arglen) { char *newarg, *ext; int pass, rv; newarg = (char *)malloc(arglen + 10); memcpy(newarg, arg, arglen); newarg[arglen] = 0; ext = newarg + arglen; pass = 0; do { struct stat sb; switch (pass) { case 0: strcpy(ext, OBJECT_EXT); break; /* case 1: strcpy(ext, NO_PIC_EXT); break; */ default: break; } if (!cmd->options.silent) { printf("Checking (obj): %s\n", newarg); } rv = stat(newarg, &sb); } while (rv != 0 && ++pass < 1); if (rv == 0) { if (pass == 1) { cmd->options.pic_mode = pic_AVOID; } return newarg; } return NULL; } /* libdircheck values: * 0 - no .libs suffix * 1 - .libs suffix */ char *check_library_exists(command_t *cmd, const char *arg, int pathlen, int libdircheck, enum lib_type *libtype) { char *newarg, *ext; int pass, rv, newpathlen; newarg = (char *)malloc(strlen(arg) + 10); strcpy(newarg, arg); newarg[pathlen] = 0; newpathlen = pathlen; if (libdircheck) { strcat(newarg, ".libs/"); newpathlen += sizeof(".libs/") - 1; } strcpy(newarg+newpathlen, arg+pathlen); ext = strrchr(newarg, '.') + 1; pass = 0; do { struct stat sb; switch (pass) { case 0: if (cmd->options.pic_mode != pic_AVOID && cmd->options.shared != share_STATIC) { strcpy(ext, DYNAMIC_LIB_EXT); *libtype = type_DYNAMIC_LIB; break; } pass = 1; /* Fall through */ case 1: strcpy(ext, STATIC_LIB_EXT); *libtype = type_STATIC_LIB; break; case 2: strcpy(ext, MODULE_LIB_EXT); *libtype = type_MODULE_LIB; break; case 3: strcpy(ext, OBJECT_EXT); *libtype = type_OBJECT; break; default: *libtype = type_UNKNOWN; break; } if (!cmd->options.silent) { printf("Checking (lib): %s\n", newarg); } rv = stat(newarg, &sb); } while (rv != 0 && ++pass < 4); if (rv == 0) { return newarg; } return NULL; } char * load_install_path(const char *arg) { FILE *f; char *path; path = malloc(PATH_MAX); f = fopen(arg,"r"); if (f == NULL) { return NULL; } fgets(path, PATH_MAX, f); fclose(f); if (path[strlen(path)-1] == '\n') { path[strlen(path)-1] = '\0'; } /* Check that we have an absolute path. * Otherwise the file could be a GNU libtool file. */ if (path[0] != '/') { return NULL; } return path; } char * load_noinstall_path(const char *arg, int pathlen) { char *newarg, *expanded_path; int newpathlen; newarg = (char *)malloc(strlen(arg) + 10); strcpy(newarg, arg); newarg[pathlen] = 0; newpathlen = pathlen; strcat(newarg, ".libs"); newpathlen += sizeof(".libs") - 1; newarg[newpathlen] = 0; #ifdef HAS_REALPATH expanded_path = malloc(PATH_MAX); expanded_path = realpath(newarg, expanded_path); /* Uh, oh. There was an error. Fall back on our first guess. */ if (!expanded_path) { expanded_path = newarg; } #else /* We might get ../ or something goofy. Oh, well. */ expanded_path = newarg; #endif return expanded_path; } void add_dynamic_link_opts(command_t *cmd_data, count_chars *args) { #ifdef DYNAMIC_LINK_OPTS if (cmd_data->options.pic_mode != pic_AVOID) { if (!cmd_data->options.silent) { printf("Adding: %s\n", DYNAMIC_LINK_OPTS); } push_count_chars(args, DYNAMIC_LINK_OPTS); if (cmd_data->undefined_flag) { push_count_chars(args, "-undefined"); #if defined(__APPLE__) /* -undefined dynamic_lookup is used by the bundled Python in * 10.4, but if we don't set MACOSX_DEPLOYMENT_TARGET to 10.3+, * we'll get a linker error if we pass this flag. */ if (strcasecmp(cmd_data->undefined_flag, "dynamic_lookup") == 0) { insert_count_chars(cmd_data->program_opts, "MACOSX_DEPLOYMENT_TARGET=10.3", 0); } #endif push_count_chars(args, cmd_data->undefined_flag); } else { #ifdef DYNAMIC_LINK_UNDEFINED if (!cmd_data->options.silent) { printf("Adding: %s\n", DYNAMIC_LINK_UNDEFINED); } push_count_chars(args, DYNAMIC_LINK_UNDEFINED); #endif } } #endif } /* Read the final install location and add it to runtime library search path. */ #ifdef RPATH void add_rpath(count_chars *cc, const char *path) { int size = 0; char *tmp; #ifdef LINKER_FLAG_PREFIX size = strlen(LINKER_FLAG_PREFIX); #endif size = size + strlen(path) + strlen(RPATH) + 2; tmp = malloc(size); if (tmp == NULL) { return; } #ifdef LINKER_FLAG_PREFIX strcpy(tmp, LINKER_FLAG_PREFIX); strcat(tmp, RPATH); #else strcpy(tmp, RPATH); #endif #ifndef LINKER_FLAG_NO_EQUALS strcat(tmp, "="); #endif strcat(tmp, path); push_count_chars(cc, tmp); } void add_rpath_file(count_chars *cc, const char *arg) { const char *path; path = load_install_path(arg); if (path) { add_rpath(cc, path); } } void add_rpath_noinstall(count_chars *cc, const char *arg, int pathlen) { const char *path; path = load_noinstall_path(arg, pathlen); if (path) { add_rpath(cc, path); } } #endif #ifdef DYNAMIC_LINK_NO_INSTALL void add_dylink_noinstall(count_chars *cc, const char *arg, int pathlen, int extlen) { const char *install_path, *current_path, *name; char *exp_argument; int i_p_len, c_p_len, name_len, dyext_len, cur_len; install_path = load_install_path(arg); current_path = load_noinstall_path(arg, pathlen); if (!install_path || !current_path) { return; } push_count_chars(cc, DYNAMIC_LINK_NO_INSTALL); i_p_len = strlen(install_path); c_p_len = strlen(current_path); name = arg+pathlen; name_len = extlen-pathlen; dyext_len = sizeof(DYNAMIC_LIB_EXT) - 1; /* No, we need to replace the extension. */ exp_argument = (char *)malloc(i_p_len + c_p_len + (name_len*2) + (dyext_len*2) + 2); cur_len = 0; strcpy(exp_argument, install_path); cur_len += i_p_len; exp_argument[cur_len++] = '/'; strncpy(exp_argument+cur_len, name, extlen-pathlen); cur_len += name_len; strcpy(exp_argument+cur_len, DYNAMIC_LIB_EXT); cur_len += dyext_len; exp_argument[cur_len++] = ':'; strcpy(exp_argument+cur_len, current_path); cur_len += c_p_len; exp_argument[cur_len++] = '/'; strncpy(exp_argument+cur_len, name, extlen-pathlen); cur_len += name_len; strcpy(exp_argument+cur_len, DYNAMIC_LIB_EXT); cur_len += dyext_len; push_count_chars(cc, exp_argument); } #endif /* use -L -llibname to allow to use installed libraries */ void add_minus_l(count_chars *cc, const char *arg) { char *newarg; char *name = strrchr(arg, '/'); char *file = strrchr(arg, '.'); char *lib = strstr(name, "lib"); if (name !=NULL && file != NULL && lib == name+1) { *name = '\0'; *file = '\0'; file = name; file = file+4; push_count_chars(cc, "-L"); push_count_chars(cc, arg); /* we need one argument like -lapr-1 */ newarg = malloc(strlen(file) + 3); strcpy(newarg, "-l"); strcat(newarg, file); push_count_chars(cc, newarg); } else { push_count_chars(cc, arg); } } void add_linker_flag_prefix(count_chars *cc, const char *arg) { #ifndef LINKER_FLAG_PREFIX push_count_chars(cc, arg); #else char *newarg; newarg = (char*)malloc(strlen(arg) + sizeof(LINKER_FLAG_PREFIX) + 1); strcpy(newarg, LINKER_FLAG_PREFIX); strcat(newarg, arg); push_count_chars(cc, newarg); #endif } /* returns just a file's name without the path */ const char *jlibtool_basename(const char *fullpath) { const char *name = strrchr(fullpath, '/'); if (name == NULL) { name = strrchr(fullpath, '\\'); } if (name == NULL) { name = fullpath; } else { name++; } return name; } /* returns just a file's name without path or extension */ const char *nameof(const char *fullpath) { const char *name; const char *ext; name = jlibtool_basename(fullpath); ext = strrchr(name, '.'); if (ext) { char *trimmed; trimmed = malloc(ext - name + 1); strncpy(trimmed, name, ext - name); trimmed[ext-name] = 0; return trimmed; } return name; } int explode_static_lib(command_t *cmd_data, const char *lib) { count_chars tmpdir_cc, libname_cc; const char *tmpdir, *libname; char savewd[PATH_MAX]; const char *name; DIR *dir; struct dirent *entry; const char *lib_args[4]; /* Bah! */ if (cmd_data->options.dry_run) { return 0; } name = jlibtool_basename(lib); init_count_chars(&tmpdir_cc); push_count_chars(&tmpdir_cc, ".libs/"); push_count_chars(&tmpdir_cc, name); push_count_chars(&tmpdir_cc, ".exploded/"); tmpdir = flatten_count_chars(&tmpdir_cc, 0); if (!cmd_data->options.silent) { printf("Making: %s\n", tmpdir); } safe_mkdir(tmpdir); push_count_chars(cmd_data->tmp_dirs, tmpdir); getcwd(savewd, sizeof(savewd)); if (chdir(tmpdir) != 0) { if (!cmd_data->options.silent) { printf("Warning: could not explode %s\n", lib); } return 1; } if (lib[0] == '/') { libname = lib; } else { init_count_chars(&libname_cc); push_count_chars(&libname_cc, "../../"); push_count_chars(&libname_cc, lib); libname = flatten_count_chars(&libname_cc, 0); } lib_args[0] = LIBRARIAN; lib_args[1] = "x"; lib_args[2] = libname; lib_args[3] = NULL; external_spawn(cmd_data, LIBRARIAN, lib_args); chdir(savewd); dir = opendir(tmpdir); while ((entry = readdir(dir)) != NULL) { #if defined(__APPLE__) && defined(RANLIB) /* Apple inserts __.SYMDEF which isn't needed. * Leopard (10.5+) can also add '__.SYMDEF SORTED' which isn't * much fun either. Just skip them. */ if (strstr(entry->d_name, "__.SYMDEF") != NULL) { continue; } #endif if (entry->d_name[0] != '.') { push_count_chars(&tmpdir_cc, entry->d_name); name = flatten_count_chars(&tmpdir_cc, 0); if (!cmd_data->options.silent) { printf("Adding: %s\n", name); } push_count_chars(cmd_data->obj_files, name); pop_count_chars(&tmpdir_cc); } } closedir(dir); return 0; } int parse_input_file_name(char *arg, command_t *cmd_data) { char *ext = strrchr(arg, '.'); char *name = strrchr(arg, '/'); int pathlen; enum lib_type libtype; char *newarg; if (!ext) { return 0; } 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 = check_object_exists(cmd_data, arg, ext - arg); if (!newarg) { printf("Can not find suitable object file for %s\n", arg); exit(1); } if (cmd_data->mode != mLink) { push_count_chars(cmd_data->arglist, newarg); } else { push_count_chars(cmd_data->obj_files, newarg); } return 1; } if (strcmp(ext, "la") == 0) { switch (cmd_data->mode) { case mLink: /* Try the .libs dir first! */ newarg = check_library_exists(cmd_data, arg, pathlen, 1, &libtype); if (!newarg) { /* Try the normal dir next. */ newarg = check_library_exists(cmd_data, arg, pathlen, 0, &libtype); if (!newarg) { printf("Can not find suitable library for %s\n", arg); exit(1); } } /* It is not ok to just add the file: a library may added with: 1 - -L path library_name. (For *.so in Linux). 2 - library_name. */ #ifdef ADD_MINUS_L if (libtype == type_DYNAMIC_LIB) { add_minus_l(cmd_data->shared_opts.dependencies, newarg); } else if (cmd_data->output == otLibrary && libtype == type_STATIC_LIB) { explode_static_lib(cmd_data, newarg); } else { push_count_chars(cmd_data->shared_opts.dependencies, newarg); } #else if (cmd_data->output == otLibrary && libtype == type_STATIC_LIB) { explode_static_lib(cmd_data, newarg); } else { push_count_chars(cmd_data->shared_opts.dependencies, newarg); } #endif if (libtype == type_DYNAMIC_LIB) { if (cmd_data->options.no_install) { #ifdef RPATH add_rpath_noinstall(cmd_data->shared_opts.dependencies, arg, pathlen); #endif #ifdef DYNAMIC_LINK_NO_INSTALL /* * This doesn't work as Darwin's linker has no way to * override at link-time the search paths for a * non-installed library. */ /* add_dylink_noinstall(cmd_data->shared_opts.dependencies, arg, pathlen, ext - arg); */ #endif } else { #ifdef RPATH add_rpath_file(cmd_data->shared_opts.dependencies, arg); #endif } } break; case mInstall: /* If we've already recorded a library to install, we're most * likely getting the .la file that we want to install as. * The problem is that we need to add it as the directory, * not the .la file itself. Otherwise, we'll do odd things. */ if (cmd_data->output == otLibrary) { arg[pathlen] = '\0'; push_count_chars(cmd_data->arglist, arg); } else { cmd_data->output = otLibrary; cmd_data->output_name = arg; cmd_data->static_name.install = gen_install_name(arg, 0); cmd_data->shared_name.install = gen_install_name(arg, 1); cmd_data->module_name.install = gen_install_name(arg, 2); } break; default: break; } return 1; } if (strcmp(ext, "c") == 0) { /* If we don't already have an idea what our output name will be. */ if (cmd_data->basename == NULL) { cmd_data->basename = (char *)malloc(strlen(arg) + 4); strcpy(cmd_data->basename, arg); strcpy(strrchr(cmd_data->basename, '.') + 1, "lo"); cmd_data->fake_output_name = strrchr(cmd_data->basename, '/'); if (cmd_data->fake_output_name) { cmd_data->fake_output_name++; } else { cmd_data->fake_output_name = cmd_data->basename; } } } return 0; } int parse_output_file_name(char *arg, command_t *cmd_data) { char *name = strrchr(arg, '/'); char *ext = strrchr(arg, '.'); char *newarg = NULL; int pathlen; cmd_data->fake_output_name = arg; if (name) { name++; } else { name = strrchr(arg, '\\'); if (name == NULL) { name = arg; } else { name++; } } #ifdef EXE_EXT if (!ext || strcmp(ext, EXE_EXT) == 0) { #else if (!ext) { #endif cmd_data->basename = arg; cmd_data->output = otProgram; #if defined(_OSD_POSIX) cmd_data->options.pic_mode = pic_AVOID; #endif newarg = (char *)malloc(strlen(arg) + 5); strcpy(newarg, arg); #ifdef EXE_EXT if (!ext) { strcat(newarg, EXE_EXT); } #endif cmd_data->output_name = newarg; return 1; } ext++; pathlen = name - arg; if (strcmp(ext, "la") == 0) { assert(cmd_data->mode == mLink); cmd_data->basename = arg; cmd_data->static_name.normal = gen_library_name(arg, 0); cmd_data->shared_name.normal = gen_library_name(arg, 1); cmd_data->module_name.normal = gen_library_name(arg, 2); cmd_data->static_name.install = gen_install_name(arg, 0); cmd_data->shared_name.install = gen_install_name(arg, 1); cmd_data->module_name.install = gen_install_name(arg, 2); #ifdef TRUNCATE_DLL_NAME if (shared) { arg = truncate_dll_name(arg); } #endif cmd_data->output_name = arg; return 1; } if (strcmp(ext, "lo") == 0) { cmd_data->basename = arg; cmd_data->output = otObject; newarg = (char *)malloc(strlen(arg) + 2); strcpy(newarg, arg); ext = strrchr(newarg, '.') + 1; strcpy(ext, OBJECT_EXT); cmd_data->output_name = newarg; return 1; } return 0; } void parse_args(int argc, char *argv[], command_t *cmd_data) { int a; char *arg; int argused; for (a = 1; a < argc; a++) { arg = argv[a]; argused = 1; if (arg[0] == '-') { if (arg[1] == '-') { argused = parse_long_opt(arg + 2, cmd_data); } else { argused = parse_short_opt(arg + 1, cmd_data); } /* We haven't done anything with it yet, try some of the * more complicated short opts... */ if (argused == 0 && a + 1 < argc) { if (arg[1] == 'o' && !arg[2]) { arg = argv[++a]; argused = parse_output_file_name(arg, cmd_data); } else if (strcmp(arg+1, "MT") == 0) { if (!cmd_data->options.silent) { printf("Adding: %s\n", arg); } push_count_chars(cmd_data->arglist, arg); arg = argv[++a]; if (!cmd_data->options.silent) { printf(" %s\n", arg); } push_count_chars(cmd_data->arglist, arg); argused = 1; } else if (strcmp(arg+1, "rpath") == 0) { /* Aha, we should try to link both! */ cmd_data->install_path = argv[++a]; argused = 1; } else if (strcmp(arg+1, "version-info") == 0) { /* Store for later deciphering */ cmd_data->version_info = argv[++a]; argused = 1; } else if (strcmp(arg+1, "export-symbols-regex") == 0) { /* Skip the argument. */ ++a; argused = 1; } else if (strcmp(arg+1, "undefined") == 0) { cmd_data->undefined_flag = argv[++a]; argused = 1; } else if (arg[1] == 'R' && !arg[2]) { /* -R dir Add dir to runtime library search path. */ add_runtimedirlib(argv[++a], cmd_data); argused = 1; } } } else { argused = parse_input_file_name(arg, cmd_data); } if (!argused) { if (!cmd_data->options.silent) { printf("Adding: %s\n", arg); } push_count_chars(cmd_data->arglist, arg); } } } #ifdef GEN_EXPORTS void generate_def_file(command_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; external_spawn(cmd_data, export_args[0], (const char**)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->basename); 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; external_spawn(cmd_data, export_args[0], (const char**)export_args); } } } #endif const char* expand_path(const char *relpath) { char foo[PATH_MAX], *newpath; getcwd(foo, PATH_MAX-1); newpath = (char*)malloc(strlen(foo)+strlen(relpath)+2); strcat(newpath, foo); strcat(newpath, "/"); strcat(newpath, relpath); return newpath; } void link_fixup(command_t *c) { /* If we were passed an -rpath directive, we need to build * shared objects too. Otherwise, we should only create static * libraries. */ if (!c->install_path && (c->output == otDynamicLibraryOnly || c->output == otModule || c->output == otLibrary)) { c->output = otStaticLibraryOnly; } if (c->output == otDynamicLibraryOnly || c->output == otModule || c->output == otLibrary) { push_count_chars(c->shared_opts.normal, "-o"); if (c->output == otModule) { push_count_chars(c->shared_opts.normal, c->module_name.normal); } else { char *tmp; push_count_chars(c->shared_opts.normal, c->shared_name.normal); #ifdef DYNAMIC_INSTALL_NAME push_count_chars(c->shared_opts.normal, DYNAMIC_INSTALL_NAME); tmp = (char*)malloc(PATH_MAX); strcat(tmp, c->install_path); strcat(tmp, strrchr(c->shared_name.normal, '/')); push_count_chars(c->shared_opts.normal, tmp); #endif } append_count_chars(c->shared_opts.normal, c->obj_files); append_count_chars(c->shared_opts.normal, c->shared_opts.dependencies); if (c->options.export_all) { #ifdef GEN_EXPORTS generate_def_file(c); #endif } } if (c->output == otLibrary || c->output == otStaticLibraryOnly) { push_count_chars(c->static_opts.normal, "-o"); push_count_chars(c->static_opts.normal, c->output_name); } if (c->output == otProgram) { if (c->output_name) { push_count_chars(c->arglist, "-o"); push_count_chars(c->arglist, c->output_name); append_count_chars(c->arglist, c->obj_files); append_count_chars(c->arglist, c->shared_opts.dependencies); add_dynamic_link_opts(c, c->arglist); } } } void post_parse_fixup(command_t *cmd_data) { switch (cmd_data->mode) { case mCompile: #ifdef PIC_FLAG if (cmd_data->options.pic_mode != pic_AVOID) { push_count_chars(cmd_data->arglist, PIC_FLAG); } #endif if (cmd_data->output_name) { push_count_chars(cmd_data->arglist, "-o"); push_count_chars(cmd_data->arglist, cmd_data->output_name); } break; case mLink: link_fixup(cmd_data); break; case mInstall: if (cmd_data->output == otLibrary) { link_fixup(cmd_data); } default: break; } #if USE_OMF if (cmd_data->output == otObject || cmd_data->output == otProgram || cmd_data->output == otLibrary || cmd_data->output == otDynamicLibraryOnly) { push_count_chars(cmd_data->arglist, "-Zomf"); } #endif if (cmd_data->options.shared && (cmd_data->output == otObject || cmd_data->output == otLibrary || cmd_data->output == otDynamicLibraryOnly)) { #ifdef SHARE_SW push_count_chars(cmd_data->arglist, SHARE_SW); #endif } } int run_mode(command_t *cmd_data) { int rv; count_chars *cctemp; cctemp = (count_chars*)malloc(sizeof(count_chars)); init_count_chars(cctemp); switch (cmd_data->mode) { case mCompile: rv = run_command(cmd_data, cmd_data->arglist); if (rv) { return rv; } break; case mInstall: /* Well, we'll assume it's a file going to a directory... */ /* For brain-dead install-sh based scripts, we have to repeat * the command N-times. install-sh should die. */ if (!cmd_data->output_name) { rv = run_command(cmd_data, cmd_data->arglist); if (rv) { return rv; } } if (cmd_data->output_name) { append_count_chars(cctemp, cmd_data->arglist); insert_count_chars(cctemp, cmd_data->output_name, cctemp->num - 1); rv = run_command(cmd_data, cctemp); if (rv) { return rv; } clear_count_chars(cctemp); } if (cmd_data->static_name.install) { append_count_chars(cctemp, cmd_data->arglist); insert_count_chars(cctemp, cmd_data->static_name.install, cctemp->num - 1); rv = run_command(cmd_data, cctemp); if (rv) { return rv; } #if defined(__APPLE__) && defined(RANLIB) /* From the Apple libtool(1) manpage on Tiger/10.4: * ---- * With the way libraries used to be created, errors were possible * if the library was modified with ar(1) and the table of * contents was not updated by rerunning ranlib(1). Thus the * link editor, ld, warns when the modification date of a library * is more recent than the creation date of its table of * contents. Unfortunately, this means that you get the warning * even if you only copy the library. * ---- * * This means that when we install the static archive, we need to * rerun ranlib afterwards. */ const char *lib_args[3], *static_lib_name; char *tmp; size_t len1, len2; len1 = strlen(cmd_data->arglist->vals[cmd_data->arglist->num - 1]); static_lib_name = jlibtool_basename(cmd_data->static_name.install); len2 = strlen(static_lib_name); tmp = malloc(len1 + len2 + 2); snprintf(tmp, len1 + len2 + 2, "%s/%s", cmd_data->arglist->vals[cmd_data->arglist->num - 1], static_lib_name); lib_args[0] = RANLIB; lib_args[1] = tmp; lib_args[2] = NULL; external_spawn(cmd_data, RANLIB, lib_args); free(tmp); #endif clear_count_chars(cctemp); } if (cmd_data->shared_name.install) { append_count_chars(cctemp, cmd_data->arglist); insert_count_chars(cctemp, cmd_data->shared_name.install, cctemp->num - 1); rv = run_command(cmd_data, cctemp); if (rv) { return rv; } clear_count_chars(cctemp); } if (cmd_data->module_name.install) { append_count_chars(cctemp, cmd_data->arglist); insert_count_chars(cctemp, cmd_data->module_name.install, cctemp->num - 1); rv = run_command(cmd_data, cctemp); if (rv) { return rv; } clear_count_chars(cctemp); } break; case mLink: if (!cmd_data->options.dry_run) { /* Check first to see if the dir already exists! */ safe_mkdir(".libs"); } if (cmd_data->output == otStaticLibraryOnly || cmd_data->output == otLibrary) { #ifdef RANLIB const char *lib_args[3]; #endif /* Removes compiler! */ cmd_data->program = LIBRARIAN; push_count_chars(cmd_data->program_opts, LIBRARIAN_OPTS); push_count_chars(cmd_data->program_opts, cmd_data->static_name.normal); rv = run_command(cmd_data, cmd_data->obj_files); if (rv) { return rv; } #ifdef RANLIB lib_args[0] = RANLIB; lib_args[1] = cmd_data->static_name.normal; lib_args[2] = NULL; external_spawn(cmd_data, RANLIB, lib_args); #endif } if (cmd_data->output == otDynamicLibraryOnly || cmd_data->output == otModule || cmd_data->output == otLibrary) { cmd_data->program = NULL; clear_count_chars(cmd_data->program_opts); append_count_chars(cmd_data->program_opts, cmd_data->arglist); if (cmd_data->output != otModule) { #ifdef SHARED_OPTS push_count_chars(cmd_data->program_opts, SHARED_OPTS); #endif #ifdef dynamic_link_version_func push_count_chars(cmd_data->program_opts, dynamic_link_version_func(cmd_data->version_info)); #endif } if (cmd_data->output == otModule) { #ifdef MODULE_OPTS push_count_chars(cmd_data->program_opts, MODULE_OPTS); #endif } add_dynamic_link_opts(cmd_data, cmd_data->program_opts); rv = run_command(cmd_data, cmd_data->shared_opts.normal); if (rv) { return rv; } } if (cmd_data->output == otProgram) { rv = run_command(cmd_data, cmd_data->arglist); if (rv) { return rv; } } break; default: break; } return 0; } void cleanup_tmp_dir(const 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(command_t *cmd_data) { int d; for (d = 0; d < cmd_data->tmp_dirs->num; d++) { cleanup_tmp_dir(cmd_data->tmp_dirs->vals[d]); } } int ensure_fake_uptodate(command_t *cmd_data) { /* FIXME: could do the stat/touch here, but nah... */ const char *touch_args[3]; if (cmd_data->mode == mInstall) { return 0; } if (!cmd_data->fake_output_name) { return 0; } touch_args[0] = "touch"; touch_args[1] = cmd_data->fake_output_name; touch_args[2] = NULL; return external_spawn(cmd_data, "touch", touch_args); } /* Store the install path in the *.la file */ int add_for_runtime(command_t *cmd_data) { if (cmd_data->mode == mInstall) { return 0; } if (cmd_data->output == otDynamicLibraryOnly || cmd_data->output == otLibrary) { FILE *f=fopen(cmd_data->fake_output_name,"w"); if (f == NULL) { return -1; } fprintf(f,"%s\n", cmd_data->install_path); fclose(f); return(0); } else { return(ensure_fake_uptodate(cmd_data)); } } int main(int argc, char *argv[]) { int rc; command_t cmd_data; memset(&cmd_data, 0, sizeof(cmd_data)); cmd_data.options.pic_mode = pic_UNKNOWN; cmd_data.program_opts = (count_chars*)malloc(sizeof(count_chars)); init_count_chars(cmd_data.program_opts); cmd_data.arglist = (count_chars*)malloc(sizeof(count_chars)); init_count_chars(cmd_data.arglist); cmd_data.tmp_dirs = (count_chars*)malloc(sizeof(count_chars)); init_count_chars(cmd_data.tmp_dirs); cmd_data.obj_files = (count_chars*)malloc(sizeof(count_chars)); init_count_chars(cmd_data.obj_files); cmd_data.dep_rpaths = (count_chars*)malloc(sizeof(count_chars)); init_count_chars(cmd_data.dep_rpaths); cmd_data.rpaths = (count_chars*)malloc(sizeof(count_chars)); init_count_chars(cmd_data.rpaths); cmd_data.static_opts.normal = (count_chars*)malloc(sizeof(count_chars)); init_count_chars(cmd_data.static_opts.normal); cmd_data.shared_opts.normal = (count_chars*)malloc(sizeof(count_chars)); init_count_chars(cmd_data.shared_opts.normal); cmd_data.shared_opts.dependencies = (count_chars*)malloc(sizeof(count_chars)); init_count_chars(cmd_data.shared_opts.dependencies); cmd_data.mode = mUnknown; cmd_data.output = otGeneral; parse_args(argc, argv, &cmd_data); post_parse_fixup(&cmd_data); if (cmd_data.mode == mUnknown) { exit(0); } rc = run_mode(&cmd_data); if (!rc) { add_for_runtime(&cmd_data); } cleanup_tmp_dirs(&cmd_data); return rc; }
.htaccess Tutorial
Find information you are looking for on the AskApache Home Page.