static void expand_elt (kpathsea kpse, str_llist_type * str_list_ptr, string elt, unsigned start) { string dir = elt + start, post; while (*dir != 0) { if (IS_DIR_SEP_CH (*dir)) { /* If two or more consecutive /'s, find subdirectories. */ if (IS_DIR_SEP_CH (dir[1])) { for (post = dir + 1; IS_DIR_SEP_CH (*post); post++) ; do_subdir (kpse, str_list_ptr, elt, dir - elt + 1, post); return; } /* No special stuff at this slash. Keep going. */ } dir++; } /* When we reach the end of ELT, it will be a normal filename. */ checked_dir_list_add (kpse, str_list_ptr, elt); }
static boolean ignore_dir_p (const_string dirname) { const_string dot_pos = dirname; while ((dot_pos = strchr (dot_pos + 1, '.'))) { /* If / before and no / after, skip it. */ if (IS_DIR_SEP_CH (dot_pos[-1]) && dot_pos[1] && !IS_DIR_SEP_CH (dot_pos[1])) return true; } return false; }
static void dir_list_add (str_llist_type *l, string dir) { char last_char = dir[strlen (dir) - 1]; string saved_dir = IS_DIR_SEP_CH (last_char) || IS_DEVICE_SEP (last_char) ? xstrdup (dir) : concat (dir, DIR_SEP_STRING); str_llist_add (l, saved_dir); }
unsigned kpathsea_normalize_path (kpathsea kpse, string elt) { unsigned ret; unsigned i; #if defined(WIN32) for (i = 0; elt[i]; i++) { if (elt[i] == '\\') elt[i] = '/'; else if (IS_KANJI(elt + i)) i++; } #endif if (NAME_BEGINS_WITH_DEVICE(elt)) { if (*elt >= 'A' && *elt <= 'Z') *elt += 'a' - 'A'; ret = 2; } else if (IS_UNC_NAME(elt)) { for (ret = 2; elt[ret] && !IS_DIR_SEP_CH(elt[ret]); ret++) ; } else ret = 0; for (i = ret; IS_DIR_SEP_CH(elt[i]); ++i) ; if (i > ret + 1) { #ifdef KPSE_DEBUG if (KPATHSEA_DEBUG_P (KPSE_DEBUG_STAT)) DEBUGF2 ("kpse_normalize_path (%s) => %u\n", elt, ret); #endif /* KPSE_DEBUG */ memmove (elt + ret + 1, elt + i, strlen (elt + i) + 1); } return ret; }
static void do_subdir (kpathsea kpse, str_llist_type *str_list_ptr, string elt, unsigned elt_length, string post) { #ifdef WIN32 WIN32_FIND_DATAW find_file_data; HANDLE hnd; int proceed; int nlinks = 2; #else DIR *dir; struct dirent *e; #endif /* not WIN32 */ fn_type name; /* Some old compilers don't allow aggregate initialization. */ name = fn_copy0 (elt, elt_length); assert (IS_DIR_SEP_CH (elt[elt_length - 1]) || IS_DEVICE_SEP (elt[elt_length - 1])); #if defined (WIN32) strcpy(dirname, FN_STRING(name)); strcat(dirname, "/*.*"); /* "*.*" or "*" -- seems equivalent. */ get_wstring_from_fsyscp(dirname, dirnamew); hnd = FindFirstFileW(dirnamew, &find_file_data); if (hnd == INVALID_HANDLE_VALUE) { fn_free(&name); return; } /* Include top level before subdirectories, if nothing to match. */ if (*post == 0) dir_list_add (str_list_ptr, FN_STRING (name)); else { /* If we do have something to match, see if it exists. For example, POST might be `pk/ljfour', and they might have a directory `$TEXMF/fonts/pk/ljfour' that we should find. */ fn_str_grow (&name, post); expand_elt (kpse, str_list_ptr, FN_STRING (name), elt_length); fn_shrink_to (&name, elt_length); } proceed = 1; while (proceed) { if (find_file_data.cFileName[0] != L'.') { int links; /* Construct the potential subdirectory name. */ potname = get_fsyscp_from_wstring(find_file_data.cFileName, potname=NULL); fn_str_grow (&name, potname); free(potname); /* Maybe we have cached the leafness of this directory. The function will return 0 if unknown, else the actual (Unix-like) value. */ links = kpathsea_dir_links (kpse, FN_STRING (name), 0); if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { unsigned potential_len = FN_LENGTH (name); /* in any case, compute the leafness */ nlinks++; /* It's a directory, so append the separator. */ fn_str_grow (&name, DIR_SEP_STRING); if (*post != 0) { fn_str_grow (&name, post); /* Unfortunately we can't check if the new element is a leaf directory, because we don't have a directory name here, we just have a path spec. This means we may descend into a leaf directory cm/pk, if the spec is ...fonts//pk//. */ expand_elt (kpse, str_list_ptr, FN_STRING (name), potential_len); fn_shrink_to (&name, potential_len); } /* Should we recurse? To see if the subdirectory is a leaf, check if it has two links (one for . and one for ..). This means that symbolic links to directories do not affect the leaf-ness. This is arguably wrong, but the only alternative I know of is to stat every entry in the directory, and that is unacceptably slow. */ if (links == 0 || links > 2) /* All criteria are met; find subdirectories. */ do_subdir (kpse, str_list_ptr, FN_STRING (name), potential_len, post); else if (*post == 0) /* Nothing to match, no recursive subdirectories to look for: we're done with this branch. Add it. */ dir_list_add (str_list_ptr, FN_STRING (name)); } fn_shrink_to (&name, elt_length); } proceed = FindNextFileW (hnd, &find_file_data); } /* Update the leafness of name. */ kpathsea_dir_links(kpse, FN_STRING(name), nlinks); fn_free (&name); FindClose(hnd); #else /* not WIN32 */ /* If we can't open it, quit. */ dir = opendir (FN_STRING (name)); if (dir == NULL) { fn_free (&name); return; } /* Include top level before subdirectories, if nothing to match. */ if (*post == 0) dir_list_add (str_list_ptr, FN_STRING (name)); else { /* If we do have something to match, see if it exists. For example, POST might be `pk/ljfour', and they might have a directory `$TEXMF/fonts/pk/ljfour' that we should find. */ fn_str_grow (&name, post); expand_elt (kpse, str_list_ptr, FN_STRING (name), elt_length); fn_shrink_to (&name, elt_length); } while ((e = readdir (dir)) != NULL) { /* If it begins with a `.', never mind. (This allows ``hidden'' directories that the algorithm won't find.) */ if (e->d_name[0] != '.') { int links; /* Construct the potential subdirectory name. */ fn_str_grow (&name, e->d_name); /* If we can't stat it, or if it isn't a directory, continue. */ links = kpathsea_dir_links (kpse, FN_STRING (name), 0); if (links >= 0) { unsigned potential_len = FN_LENGTH (name); /* It's a directory, so append the separator. */ fn_str_grow (&name, DIR_SEP_STRING); if (*post != 0) { fn_str_grow (&name, post); /* Unfortunately we can't check if the new element is a leaf directory, because we don't have a directory name here, we just have a path spec. This means we may descend into a leaf directory cm/pk, if the spec is ...fonts//pk//. */ expand_elt (kpse, str_list_ptr, FN_STRING (name), potential_len); fn_shrink_to (&name, potential_len); } /* Should we recurse? To see if the subdirectory is a leaf, check if it has two links (one for . and one for ..). This means that symbolic links to directories do not affect the leaf-ness. This is arguably wrong, but the only alternative I know of is to stat every entry in the directory, and that is unacceptably slow. The #ifdef here makes all this configurable at compile-time, so that if we're using VMS directories or some such, we can still find subdirectories, even if it is much slower. */ #ifdef ST_NLINK_TRICK /* With SAS/C++ 6.55 on the Amiga, stat sets the st_nlink field to -1 for a file, or to 1 for a directory. Cygwin 1.7 also leaves st_nlink as 1: http://cygwin.com/ml/cygwin-developers/2008-04/msg00110.html */ if (links != 2) #endif /* ST_NLINK_TRICK */ /* All criteria are met; find subdirectories. */ do_subdir (kpse, str_list_ptr, FN_STRING (name), potential_len, post); #ifdef ST_NLINK_TRICK else if (*post == 0) /* Nothing to match, no recursive subdirectories to look for: we're done with this branch. Add it. */ dir_list_add (str_list_ptr, FN_STRING (name)); #endif } /* Remove the directory entry we just checked from `name'. */ fn_shrink_to (&name, elt_length); } } fn_free (&name); xclosedir (dir); #endif /* not WIN32 */ }
static boolean match (const_string filename, const_string path_elt) { const_string original_filename = filename; boolean matched = false; for (; *filename && *path_elt; filename++, path_elt++) { if (FILECHARCASEEQ (*filename, *path_elt)) /* normal character match */ ; else if (IS_DIR_SEP_CH (*path_elt) /* at // */ && original_filename < filename && IS_DIR_SEP_CH (path_elt[-1])) { while (IS_DIR_SEP_CH (*path_elt)) path_elt++; /* get past second and any subsequent /'s */ if (*path_elt == 0) { /* Trailing //, matches anything. We could make this part of the other case, but it seems pointless to do the extra work. */ matched = true; break; } else { /* Intermediate //, have to match rest of PATH_ELT. */ for (; !matched && *filename; filename++) { /* Try matching at each possible character. */ if (IS_DIR_SEP_CH (filename[-1]) && FILECHARCASEEQ (*filename, *path_elt)) matched = match (filename, path_elt); } /* Prevent filename++ when *filename='\0'. */ break; } } else /* normal character nonmatch, quit */ break; } /* If we've reached the end of PATH_ELT, check that we're at the last component of FILENAME (that is, no directory separators remaining); only then have we matched. */ if (!matched && *path_elt == 0) { /* Typically PATH_ELT ends with, say, `vf', and FILENAME ends with `vf/ptmr.vf'. In that case, we'll be at the /. On the other hand, if PATH_ELT ended with a / (as in `vf/'), FILENAME being the same `vf/ptmr.vf', we'll be at the `p'. Upshot: if we're at a dir sep in FILENAME, skip it. */ if (IS_DIR_SEP_CH (*filename)) filename++; /* Here are the basic possibilities for the check on being at the last component: 1) PATH_ELT is empty and FILENAME is `ptmr.vf' => match. (we now have original_filename == filename) 2) PATH_ELT is empty and FILENAME is `foo/ptmr.vf' => no match. (we now have original_filename == filename) 3) PATH_ELT is `vf/' and FILENAME is `vf/ptmr.vf' (we are now after the / in each) => match. 4) PATH_ELT is `vf' and FILENAME is `vfoo.ext' (we are now after the f in each) => no match. When (the original) PATH_ELT was the empty string, we want to match a FILENAME without dir seps. (This could be argued, and may never happen in practice, but is the historical behavior.) */ /* if original_filename != filename then original_filename < filename */ if (original_filename == filename || IS_DIR_SEP_CH (filename[-1])) { while (*filename && !IS_DIR_SEP_CH (*filename)) filename++; matched = *filename == 0; } } return matched; }