/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * @(#)misc.cc 1.50 06/12/12 */ #pragma ident "@(#)misc.cc 1.34 95/10/04" /* * misc.cc * * This file contains various unclassified routines. Some main groups: * getname * Memory allocation * String handling * Property handling * Error message handling * Make internal state dumping * main routine support */ /* * Included files */ #include #include #include /* SETVAR() */ #include /* enable_interrupt() */ #include /* va_list, va_start(), va_end() */ #include /* SUNPRO_DEPENDENCIES */ #if defined(HP_UX) || defined(linux) #include #endif #ifdef TEAMWARE_MAKE_CMN #define MAXJOBS_ADJUST_RFE4694000 #ifdef MAXJOBS_ADJUST_RFE4694000 extern void job_adjust_fini(); #endif /* MAXJOBS_ADJUST_RFE4694000 */ #endif /* TEAMWARE_MAKE_CMN */ #if defined(linux) #include /* localtime() */ #endif /* * Defined macros */ /* * typedefs & structs */ /* * Static variables */ /* * File table of contents */ static void print_rule(register Name target); static void print_target_n_deps(register Name target); /***************************************** * * getname */ /***************************************** * * Memory allocation */ /* * free_chain() * * frees a chain of Name_vector's * * Parameters: * ptr Pointer to the first element in the chain * to be freed. * * Global variables used: */ void free_chain(Name_vector ptr) { if (ptr != NULL) { if (ptr->next != NULL) { free_chain(ptr->next); } free((char *) ptr); } } /***************************************** * * String manipulation */ /***************************************** * * Nameblock property handling */ /***************************************** * * Error message handling */ /* * fatal(format, args...) * * Print a message and die * * Parameters: * format printf type format string * args Arguments to match the format * * Global variables used: * fatal_in_progress Indicates if this is a recursive call * parallel_process_cnt Do we need to wait for anything? * report_pwd Should we report the current path? */ /*VARARGS*/ void fatal(char * message, ...) { va_list args; va_start(args, message); (void) fflush(stdout); #ifdef DISTRIBUTED (void) fprintf(stderr, catgets(catd, 1, 262, "dmake: Fatal error: ")); #else (void) fprintf(stderr, catgets(catd, 1, 263, "make: Fatal error: ")); #endif (void) vfprintf(stderr, message, args); (void) fprintf(stderr, "\n"); va_end(args); if (report_pwd) { (void) fprintf(stderr, catgets(catd, 1, 156, "Current working directory %s\n"), get_current_path()); } (void) fflush(stderr); if (fatal_in_progress) { #if defined(SUN5_0) || defined(HP_UX) || defined(linux) exit_status = 1; #endif exit(1); } fatal_in_progress = true; #ifdef TEAMWARE_MAKE_CMN /* Let all parallel children finish */ if ((dmake_mode_type == parallel_mode) && (parallel_process_cnt > 0)) { (void) fprintf(stderr, catgets(catd, 1, 157, "Waiting for %d %s to finish\n"), parallel_process_cnt, parallel_process_cnt == 1 ? catgets(catd, 1, 158, "job") : catgets(catd, 1, 159, "jobs")); (void) fflush(stderr); } while (parallel_process_cnt > 0) { #ifdef DISTRIBUTED if (dmake_mode_type == distributed_mode) { (void) await_dist(false); } else { await_parallel(true); } #else await_parallel(true); #endif finish_children(false); } #endif #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000) job_adjust_fini(); #endif #if defined(SUN5_0) || defined(HP_UX) || defined(linux) exit_status = 1; #endif exit(1); } /* * warning(format, args...) * * Print a message and continue. * * Parameters: * format printf type format string * args Arguments to match the format * * Global variables used: * report_pwd Should we report the current path? */ /*VARARGS*/ void warning(char * message, ...) { va_list args; va_start(args, message); (void) fflush(stdout); #ifdef DISTRIBUTED (void) fprintf(stderr, catgets(catd, 1, 264, "dmake: Warning: ")); #else (void) fprintf(stderr, catgets(catd, 1, 265, "make: Warning: ")); #endif (void) vfprintf(stderr, message, args); (void) fprintf(stderr, "\n"); va_end(args); if (report_pwd) { (void) fprintf(stderr, catgets(catd, 1, 161, "Current working directory %s\n"), get_current_path()); } (void) fflush(stderr); } /* * time_to_string(time) * * Take a numeric time value and produce * a proper string representation. * * Return value: * The string representation of the time * * Parameters: * time The time we need to translate * * Global variables used: */ char * time_to_string(const timestruc_t &time) { struct tm *tm; char buf[128]; if (time == file_doesnt_exist) { return catgets(catd, 1, 163, "File does not exist"); } if (time == file_max_time) { return catgets(catd, 1, 164, "Younger than any file"); } tm = localtime(&time.tv_sec); strftime(buf, sizeof (buf), NOCATGETS("%c %Z"), tm); buf[127] = (int) nul_char; return strdup(buf); } /* * get_current_path() * * Stuff current_path with the current path if it isnt there already. * * Parameters: * * Global variables used: */ char * get_current_path(void) { char pwd[(MAXPATHLEN * MB_LEN_MAX)]; static char *current_path; if (current_path == NULL) { #if defined(SUN5_0) || defined(HP_UX) || defined(linux) getcwd(pwd, sizeof(pwd)); #else (void) getwd(pwd); #endif if (pwd[0] == (int) nul_char) { pwd[0] = (int) slash_char; pwd[1] = (int) nul_char; #ifdef DISTRIBUTED current_path = strdup(pwd); } else if (IS_EQUALN(pwd, NOCATGETS("/tmp_mnt"), 8)) { current_path = strdup(pwd + 8); } else { current_path = strdup(pwd); } #else } current_path = strdup(pwd); #endif } return current_path; } /***************************************** * * Make internal state dumping * * This is a set of routines for dumping the internal make state * Used for the -p option */ /* * dump_make_state() * * Dump make's internal state to stdout * * Parameters: * * Global variables used: * svr4 Was ".SVR4" seen in makefile? * svr4_name The Name ".SVR4", printed * posix Was ".POSIX" seen in makefile? * posix_name The Name ".POSIX", printed * default_rule Points to the .DEFAULT rule * default_rule_name The Name ".DEFAULT", printed * default_target_to_build The first target to print * dot_keep_state The Name ".KEEP_STATE", printed * dot_keep_state_file The Name ".KEEP_STATE_FILE", printed * hashtab The make hash table for Name blocks * ignore_errors Was ".IGNORE" seen in makefile? * ignore_name The Name ".IGNORE", printed * keep_state Was ".KEEP_STATE" seen in makefile? * percent_list The list of % rules * precious The Name ".PRECIOUS", printed * sccs_get_name The Name ".SCCS_GET", printed * sccs_get_posix_name The Name ".SCCS_GET_POSIX", printed * get_name The Name ".GET", printed * get_posix_name The Name ".GET_POSIX", printed * sccs_get_rule Points to the ".SCCS_GET" rule * silent Was ".SILENT" seen in makefile? * silent_name The Name ".SILENT", printed * suffixes The suffix list from ".SUFFIXES" * suffixes_name The Name ".SUFFIX", printed */ void dump_make_state(void) { Name_set::iterator p, e; register Property prop; register Dependency dep; register Cmd_line rule; Percent percent, percent_depe; /* Default target */ if (default_target_to_build != NULL) { print_rule(default_target_to_build); } (void) printf("\n"); /* .POSIX */ if (posix) { (void) printf("%s:\n", posix_name->string_mb); } /* .DEFAULT */ if (default_rule != NULL) { (void) printf("%s:\n", default_rule_name->string_mb); for (rule = default_rule; rule != NULL; rule = rule->next) { (void) printf("\t%s\n", rule->command_line->string_mb); } } /* .IGNORE */ if (ignore_errors) { (void) printf("%s:\n", ignore_name->string_mb); } /* .KEEP_STATE: */ if (keep_state) { (void) printf("%s:\n\n", dot_keep_state->string_mb); } /* .PRECIOUS */ (void) printf("%s:", precious->string_mb); for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { if ((p->stat.is_precious) || (all_precious)) { (void) printf(" %s", p->string_mb); } } (void) printf("\n"); /* .SCCS_GET */ if (sccs_get_rule != NULL) { (void) printf("%s:\n", sccs_get_name->string_mb); for (rule = sccs_get_rule; rule != NULL; rule = rule->next) { (void) printf("\t%s\n", rule->command_line->string_mb); } } /* .SILENT */ if (silent) { (void) printf("%s:\n", silent_name->string_mb); } /* .SUFFIXES: */ (void) printf("%s:", suffixes_name->string_mb); for (dep = suffixes; dep != NULL; dep = dep->next) { (void) printf(" %s", dep->name->string_mb); build_suffix_list(dep->name); } (void) printf("\n\n"); /* % rules */ for (percent = percent_list; percent != NULL; percent = percent->next) { (void) printf("%s:", percent->name->string_mb); for (percent_depe = percent->dependencies; percent_depe != NULL; percent_depe = percent_depe->next) { (void) printf(" %s", percent_depe->name->string_mb); } (void) printf("\n"); for (rule = percent->command_template; rule != NULL; rule = rule->next) { (void) printf("\t%s\n", rule->command_line->string_mb); } } /* Suffix rules */ for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { Wstring wcb(p); if (wcb.get_string()[0] == (int) period_char) { print_rule(p); } } /* Macro assignments */ for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { if (((prop = get_prop(p->prop, macro_prop)) != NULL) && (prop->body.macro.value != NULL)) { (void) printf("%s", p->string_mb); print_value(prop->body.macro.value, (Daemon) prop->body.macro.daemon); } } (void) printf("\n"); /* Conditional macro assignments */ for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { for (prop = get_prop(p->prop, conditional_prop); prop != NULL; prop = get_prop(prop->next, conditional_prop)) { (void) printf("%s := %s", p->string_mb, prop->body.conditional.name-> string_mb); if (prop->body.conditional.append) { printf(" +"); } else { printf(" "); } print_value(prop->body.conditional.value, no_daemon); } } (void) printf("\n"); /* All other dependencies */ for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { if (p->colons != no_colon) { print_rule(p); } } (void) printf("\n"); } /* * print_rule(target) * * Print the rule for one target * * Parameters: * target Target we print rule for * * Global variables used: */ static void print_rule(register Name target) { register Cmd_line rule; register Property line; register Dependency dependency; if (target->dependency_printed || ((line = get_prop(target->prop, line_prop)) == NULL) || ((line->body.line.command_template == NULL) && (line->body.line.dependencies == NULL))) { return; } target->dependency_printed = true; (void) printf("%s:", target->string_mb); for (dependency = line->body.line.dependencies; dependency != NULL; dependency = dependency->next) { (void) printf(" %s", dependency->name->string_mb); } (void) printf("\n"); for (rule = line->body.line.command_template; rule != NULL; rule = rule->next) { (void) printf("\t%s\n", rule->command_line->string_mb); } } void dump_target_list(void) { Name_set::iterator p, e; Wstring str; for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { str.init(p); wchar_t * wcb = str.get_string(); if ((p->colons != no_colon) && ((wcb[0] != (int) period_char) || ((wcb[0] == (int) period_char) && (wschr(wcb, (int) slash_char))))) { print_target_n_deps(p); } } } static void print_target_n_deps(register Name target) { register Cmd_line rule; register Property line; register Dependency dependency; if (target->dependency_printed) { return; } target->dependency_printed = true; (void) printf("%s\n", target->string_mb); if ((line = get_prop(target->prop, line_prop)) == NULL) { return; } for (dependency = line->body.line.dependencies; dependency != NULL; dependency = dependency->next) { if (!dependency->automatic) { print_target_n_deps(dependency->name); } } } /***************************************** * * main() support */ /* * load_cached_names() * * Load the vector of cached names * * Parameters: * * Global variables used: * Many many pointers to Name blocks. */ void load_cached_names(void) { char *cp; Name dollar; /* Load the cached_names struct */ MBSTOWCS(wcs_buffer, NOCATGETS(".BUILT_LAST_MAKE_RUN")); built_last_make_run = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("@")); c_at = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(" *conditionals* ")); conditionals = GETNAME(wcs_buffer, FIND_LENGTH); /* * A version of make was released with NSE 1.0 that used * VERSION-1.1 but this version is identical to VERSION-1.0. * The version mismatch code makes a special case for this * situation. If the version number is changed from 1.0 * it should go to 1.2. */ MBSTOWCS(wcs_buffer, NOCATGETS("VERSION-1.0")); current_make_version = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".SVR4")); svr4_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".POSIX")); posix_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".DEFAULT")); default_rule_name = GETNAME(wcs_buffer, FIND_LENGTH); #ifdef NSE MBSTOWCS(wcs_buffer, NOCATGETS(".DERIVED_SRC")); derived_src= GETNAME(wcs_buffer, FIND_LENGTH); #endif MBSTOWCS(wcs_buffer, NOCATGETS("$")); dollar = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".DONE")); done = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".")); dot = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".KEEP_STATE")); dot_keep_state = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".KEEP_STATE_FILE")); dot_keep_state_file = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("")); empty_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(" FORCE")); force = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("HOST_ARCH")); host_arch = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("HOST_MACH")); host_mach = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".IGNORE")); ignore_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".INIT")); init = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".LOCAL")); localhost_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".make.state")); make_state = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("MAKEFLAGS")); makeflags = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".MAKE_VERSION")); make_version = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".NO_PARALLEL")); no_parallel_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".NOT_AUTO")); not_auto = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".PARALLEL")); parallel_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("PATH")); path_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("+")); plus = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".PRECIOUS")); precious = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("?")); query = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("^")); hat = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".RECURSIVE")); recursive_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".SCCS_GET")); sccs_get_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".SCCS_GET_POSIX")); sccs_get_posix_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".GET")); get_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".GET_POSIX")); get_posix_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("SHELL")); shell_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".SILENT")); silent_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".SUFFIXES")); suffixes_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, SUNPRO_DEPENDENCIES); sunpro_dependencies = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("TARGET_ARCH")); target_arch = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("TARGET_MACH")); target_mach = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("VIRTUAL_ROOT")); virtual_root = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS("VPATH")); vpath_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, NOCATGETS(".WAIT")); wait_name = GETNAME(wcs_buffer, FIND_LENGTH); wait_name->state = build_ok; /* Mark special targets so that the reader treats them properly */ svr4_name->special_reader = svr4_special; posix_name->special_reader = posix_special; built_last_make_run->special_reader = built_last_make_run_special; default_rule_name->special_reader = default_special; #ifdef NSE derived_src->special_reader= derived_src_special; #endif dot_keep_state->special_reader = keep_state_special; dot_keep_state_file->special_reader = keep_state_file_special; ignore_name->special_reader = ignore_special; make_version->special_reader = make_version_special; no_parallel_name->special_reader = no_parallel_special; parallel_name->special_reader = parallel_special; localhost_name->special_reader = localhost_special; precious->special_reader = precious_special; sccs_get_name->special_reader = sccs_get_special; sccs_get_posix_name->special_reader = sccs_get_posix_special; get_name->special_reader = get_special; get_posix_name->special_reader = get_posix_special; silent_name->special_reader = silent_special; suffixes_name->special_reader = suffixes_special; /* The value of $$ is $ */ (void) SETVAR(dollar, dollar, false); dollar->dollar = false; /* Set the value of $(SHELL) */ #ifdef HP_UX MBSTOWCS(wcs_buffer, NOCATGETS("/bin/posix/sh")); #else #if defined(SUN5_0) if (posix) { MBSTOWCS(wcs_buffer, NOCATGETS("/usr/xpg4/bin/sh")); } else { MBSTOWCS(wcs_buffer, NOCATGETS("/bin/sh")); } #else /* ^SUN5_0 */ MBSTOWCS(wcs_buffer, NOCATGETS("/bin/sh")); #endif /* ^SUN5_0 */ #endif (void) SETVAR(shell_name, GETNAME(wcs_buffer, FIND_LENGTH), false); /* * Use " FORCE" to simulate a FRC dependency for :: type * targets with no dependencies. */ (void) append_prop(force, line_prop); force->stat.time = file_max_time; /* Make sure VPATH is defined before current dir is read */ if ((cp = getenv(vpath_name->string_mb)) != NULL) { MBSTOWCS(wcs_buffer, cp); (void) SETVAR(vpath_name, GETNAME(wcs_buffer, FIND_LENGTH), false); } /* Check if there is NO PATH variable. If not we construct one. */ if (getenv(path_name->string_mb) == NULL) { vroot_path = NULL; add_dir_to_path(NOCATGETS("."), &vroot_path, -1); add_dir_to_path(NOCATGETS("/bin"), &vroot_path, -1); add_dir_to_path(NOCATGETS("/usr/bin"), &vroot_path, -1); } } /* * iterate on list of conditional macros in np, and place them in * a String_rec starting with, and separated by the '$' character. */ void cond_macros_into_string(Name np, String_rec *buffer) { Macro_list macro_list; /* * Put the version number at the start of the string */ MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION); append_string(wcs_buffer, buffer, FIND_LENGTH); /* * Add the rest of the conditional macros to the buffer */ if (np->depends_on_conditional){ for (macro_list = np->conditional_macro_list; macro_list != NULL; macro_list = macro_list->next){ append_string(macro_list->macro_name, buffer, FIND_LENGTH); append_char((int) equal_char, buffer); append_string(macro_list->value, buffer, FIND_LENGTH); append_char((int) dollar_char, buffer); } } } /* * Copyright (c) 1987-1992 Sun Microsystems, Inc. All Rights Reserved. * Sun considers its source code as an unpublished, proprietary * trade secret, and it is available only under strict license * provisions. This copyright notice is placed here only to protect * Sun in the event the source is deemed a published work. Dissassembly, * decompilation, or other means of reducing the object code to human * readable form is prohibited by the license agreement under which * this code is provided to the user or company in possession of this * copy. * RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the * Government is subject to restrictions as set forth in subparagraph * (c)(1)(ii) of the Rights in Technical Data and Computer Software * clause at DFARS 52.227-7013 and in similar clauses in the FAR and * NASA FAR Supplement. * * 1.3 91/09/30 */ /* Some includes are commented because of the includes at the beginning */ /* #include */ #include #include #include /* #include */ #include #include /* #include */ /* #include */ /* #ifndef TEAMWARE_MAKE_CMN #include #endif */ /* Routines to find the base directory name from which the various components * -executables, *crt* libraries etc will be accessed */ /* This routine checks to see if a given filename is an executable or not. Logically similar to the csh statement : if ( -x $i && ! -d $i ) */ static int check_if_exec(char *file) { struct stat stb; if (stat(file, &stb) < 0) { return ( -1); } if (S_ISDIR(stb.st_mode)) { return (-1); } if (!(stb.st_mode & S_IEXEC)) { return ( -1); } return (0); } /* resolve - check for specified file in specified directory * sets up dir, following symlinks. * returns zero for success, or * -1 for error (with errno set properly) */ static int resolve (char *indir, /* search directory */ char *cmd, /* search for name */ char *dir, /* directory buffer */ char **run) /* resultion name ptr ptr */ { char *p; int rv = -1; int sll; char symlink[MAXPATHLEN + 1]; do { errno = ENAMETOOLONG; if ((strlen (indir) + strlen (cmd) + 2) > (size_t) MAXPATHLEN) break; sprintf(dir, "%s/%s", indir, cmd); if (check_if_exec(dir) != 0) /* check if dir is an executable */ { break; /* Not an executable program */ } /* follow symbolic links */ while ((sll = readlink (dir, symlink, MAXPATHLEN)) >= 0) { symlink[sll] = 0; if (*symlink == '/') strcpy (dir, symlink); else sprintf (strrchr (dir, '/'), "/%s", symlink); } if (errno != EINVAL) break; p = strrchr (dir, '/'); *p++ = 0; if (run) /* user wants resolution name */ *run = p; rv = 0; /* complete, with success! */ } while (0); return rv; } /* *find_run_directory - find executable file in PATH * * PARAMETERS: * cmd filename as typed by user (argv[0]) * cwd buffer from which is read the working directory * if first character is '/' or into which is * copied working directory name otherwise * dir buffer into which is copied program's directory * pgm where to return pointer to tail of cmd (may be NULL * if not wanted) * run where to return pointer to tail of final resolved * name ( dir/run is the program) (may be NULL * if not wanted) * path user's path from environment * * Note: run and pgm will agree except when symbolic links have * renamed files * * RETURNS: * returns zero for success, * -1 for error (with errno set properly). * * EXAMPLE: * find_run_directory (argv[0], ".", &charray1, (char **) 0, (char **) 0, * getenv(NOGETTEXT("PATH"))); */ extern int find_run_directory (char *cmd, char *cwd, char *dir, char **pgm, char **run, char *path) { int rv = 0; char *f, *s; int i; char tmp_path[MAXPATHLEN]; if (!cmd || !*cmd || !cwd || !dir) { errno = EINVAL; /* stupid arguments! */ return -1; } if (*cwd != '/') if (!(getcwd (cwd, MAXPATHLEN))) return -1; /* can not get working directory */ f = strrchr (cmd, '/'); if (pgm) /* user wants program name */ *pgm = f ? f + 1 : cmd; /* get program directory */ rv = -1; if (*cmd == '/') /* absname given */ rv = resolve ("", cmd + 1, dir, run); else if (f) /* relname given */ rv = resolve (cwd, cmd, dir, run); else { /* from searchpath */ if (!path || !*path) { /* if missing or null path */ tmp_path[0] = '.'; /* assume sanity */ tmp_path[1] = '\0'; } else { strcpy(tmp_path, path); } f = tmp_path; rv = -1; errno = ENOENT; /* errno gets this if path empty */ while (*f && (rv < 0)) { s = f; while (*f && (*f != ':')) ++f; if (*f) *f++ = 0; if (*s == '/') rv = resolve (s, cmd, dir, run); else { char abuf[MAXPATHLEN]; sprintf (abuf, "%s/%s", cwd, s); rv = resolve (abuf, cmd, dir, run); } } } /* Remove any trailing /. */ i = strlen(dir); if ( dir[i-2] == '/' && dir[i-1] == '.') { dir[i-2] = '\0'; } return rv; }