2005-11-30 Alexandre Oliva * gcc.c (find_a_file): Use update_path before access tests. Mostly from Thomas Walker * prefix.c (update_path): Move dir/../-stripping code to... (maybe_strip_dotdots): New function. Reorganize. --- gcc/gcc.c.orig 2005-12-01 18:38:38.000000000 -0200 +++ gcc/gcc.c 2005-12-01 18:41:01.000000000 -0200 @@ -2371,7 +2371,7 @@ find_a_file (struct path_prefix *pprefix, const char *name, int mode, int multilib) { - char *temp; + char *temp, *temp2; const char *const file_suffix = ((mode & X_OK) != 0 ? HOST_EXECUTABLE_SUFFIX : ""); struct prefix_list *pl; @@ -2407,19 +2407,18 @@ NULL)); } - temp = xmalloc (len); - /* Determine the filename to execute (special case for absolute paths). */ if (IS_ABSOLUTE_PATH (name)) { - if (access (name, mode) == 0) - { - strcpy (temp, name); - return temp; - } + /* IS_ABSOLUTE_PATHNAME lets anything through that starts with '/' */ + temp = update_path (name, NULL); + if (access (temp, mode) == 0) + return temp; } else + { + temp = xmalloc (len); for (pl = pprefix->plist; pl; pl = pl->next) { const char *this_name @@ -2435,24 +2434,30 @@ strcat (temp, machine_suffix); strcat (temp, multilib_name); strcat (temp, file_suffix); - if (access_check (temp, mode) == 0) + temp2 = update_path (temp, NULL); + if (access_check (temp2, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; - return temp; + free (temp); + return temp2; } + free (temp2); } /* Now try just the multilib_name. */ strcpy (temp, pl->prefix); strcat (temp, machine_suffix); strcat (temp, multilib_name); - if (access_check (temp, mode) == 0) + temp2 = update_path (temp, NULL); + if (access_check (temp2, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; - return temp; + free (temp); + return temp2; } + free (temp2); } /* Certain prefixes are tried with just the machine type, @@ -2467,23 +2472,29 @@ strcat (temp, just_machine_suffix); strcat (temp, multilib_name); strcat (temp, file_suffix); - if (access_check (temp, mode) == 0) + temp2 = update_path (temp, NULL); + if (access_check (temp2, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; - return temp; + free (temp); + return temp2; } + free (temp2); } strcpy (temp, pl->prefix); strcat (temp, just_machine_suffix); strcat (temp, multilib_name); - if (access_check (temp, mode) == 0) + temp2 = update_path (temp, NULL); + if (access_check (temp2, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; - return temp; + free (temp); + return temp2; } + free (temp2); } /* Certain prefixes can't be used without the machine suffix @@ -2497,24 +2508,31 @@ strcpy (temp, pl->prefix); strcat (temp, this_name); strcat (temp, file_suffix); - if (access_check (temp, mode) == 0) + temp2 = update_path (temp, NULL); + if (access_check (temp2, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; - return temp; + free (temp); + return temp2; } + free (temp2); } strcpy (temp, pl->prefix); strcat (temp, this_name); - if (access_check (temp, mode) == 0) + temp2 = update_path (temp, NULL); + if (access_check (temp2, mode) == 0) { if (pl->used_flag_ptr != 0) *pl->used_flag_ptr = 1; - return temp; + free (temp); + return temp2; } + free (temp2); } } + } free (temp); return 0; --- gcc/prefix.c.orig 2005-12-01 18:38:38.000000000 -0200 +++ gcc/prefix.c 2005-12-01 18:46:37.000000000 -0200 @@ -238,6 +238,105 @@ while (*string++); } +/* Strip dir/.. from a pathname when it makes sense, e.g., when this + would turn an inaccessible pathname into an accessible one. + + We short-circuit dir/.. when dir does not exist, and when + some/dir/../thing does not exist but some/thing does. In case + there are multiple possible dir/../ stripping possibilities that + would turn an inaccessible pathname into an accessible one, the one + closer to the end of the pathname is preferred. + + RESULT is the pathname that might contain such dotdot sequences to + be stripped. P points into RESULT, and indicates the location + where we should start looking for ../ sequences. + + Even though RESULT is const, P is not, and that's because + characters in it may be temporarily overwritten, so RESULT must not + be in read-only storage. + + The returned value is either a newly-allocated memory area, holding + a string that is the result of dotdot-stripping from the original + input strip, or RESULT itself, in which case any modifications made + to the string will have been undone. */ + +static const char * +maybe_strip_dotdots (const char *result, char *p) +{ + char *temp; + const char *path, *before, *after; + size_t len; + + while (1) + { + p = strchr (p, '.'); + if (p == NULL) + return result; + /* Look for `/../' */ + if (p[1] == '.' + && IS_DIR_SEPARATOR (p[2]) + && (p != result && IS_DIR_SEPARATOR (p[-1]))) + break; + else + ++p; + } + + *p = 0; + if (access (result, X_OK) == 0) + { + *p = '.'; + + path = maybe_strip_dotdots (result, p + 3); + if (access (path, F_OK) == 0) + return path; + if (path != result) + free ((char *) path); + } + else + *p = '.'; + + /* If we couldn't access the dir, or if recursion resulted in a + non-accessible pathname, we try stripping out dir/../. If `dir' + turns out to be `.', strip one more path component. */ + before = p; + do + { + --before; + while (before != result && IS_DIR_SEPARATOR (*before)) + --before; + while (before != result && !IS_DIR_SEPARATOR (before[-1])) + --before; + } + while (before != result && *before == '.' + && IS_DIR_SEPARATOR (*(before + 1))); + /* If we have something like `./..' or `/..', don't + strip anything more. */ + if (*before == '.' || IS_DIR_SEPARATOR (*before)) + return result; + + after = p + 3; + while (IS_DIR_SEPARATOR (*after)) + ++after; + + len = (after - result) + strlen (after); + + temp = xmalloc (len + 1 - (after - before)); + memcpy (temp, result, before - result); + memcpy (temp + (before - result), after, len + 1 - (after - result)); + + path = maybe_strip_dotdots (temp, temp + (before - result)); + + if (path != temp) + free (temp); + + if (access (path, F_OK) == 0) + result = path; + else if (path != result) + free ((char *) path); + + return result; +} + /* Update PATH using KEY if PATH starts with PREFIX. The returned string is always malloc-ed, and the caller is responsible for freeing it. */ @@ -245,7 +344,7 @@ char * update_path (const char *path, const char *key) { - char *result, *p; + char *result, *temp; if (! strncmp (path, std_prefix, strlen (std_prefix)) && key != 0) { @@ -265,62 +364,11 @@ else result = xstrdup (path); -#ifndef ALWAYS_STRIP_DOTDOT -#define ALWAYS_STRIP_DOTDOT 0 -#endif + temp = result; + result = (char *) maybe_strip_dotdots (temp, temp); - p = result; - while (1) - { - char *src, *dest; - - p = strchr (p, '.'); - if (p == NULL) - break; - /* Look for `/../' */ - if (p[1] == '.' - && IS_DIR_SEPARATOR (p[2]) - && (p != result && IS_DIR_SEPARATOR (p[-1]))) - { - *p = 0; - if (!ALWAYS_STRIP_DOTDOT && access (result, X_OK) == 0) - { - *p = '.'; - break; - } - else - { - /* We can't access the dir, so we won't be able to - access dir/.. either. Strip out `dir/../'. If `dir' - turns out to be `.', strip one more path component. */ - dest = p; - do - { - --dest; - while (dest != result && IS_DIR_SEPARATOR (*dest)) - --dest; - while (dest != result && !IS_DIR_SEPARATOR (dest[-1])) - --dest; - } - while (dest != result && *dest == '.'); - /* If we have something like `./..' or `/..', don't - strip anything more. */ - if (*dest == '.' || IS_DIR_SEPARATOR (*dest)) - { - *p = '.'; - break; - } - src = p + 3; - while (IS_DIR_SEPARATOR (*src)) - ++src; - p = dest; - while ((*dest++ = *src++) != 0) - ; - } - } - else - ++p; - } + if (result != temp) + free (temp); #ifdef UPDATE_PATH_HOST_CANONICALIZE /* Perform host dependent canonicalization when needed. */