+/* The SUFFIXES argument in any of the locate_file* functions can be
+ nil, a list, or a string (for backward compatibility), with the
+ following semantics:
+
+ a) nil - no suffix, just search for file name intact
+ (semantically different from "empty suffix list", which
+ would be meaningless.)
+ b) list - list of suffixes to append to file name. Each of these
+ must be a string.
+ c) string - colon-separated suffixes to append to file name (backward
+ compatibility).
+
+ All of this got hairy, so I decided to use a mapper. Calling a
+ function for each suffix shouldn't slow things down, since
+ locate_file is rarely called with enough suffixes for funcalls to
+ make any difference. */
+
+/* Map FUN over SUFFIXES, as described above. FUN will be called with a
+ char * containing the current file name, and ARG. Mapping stops when
+ FUN returns non-zero. */
+static void
+locate_file_map_suffixes (Lisp_Object filename, Lisp_Object suffixes,
+ int (*fun) (char *, void *),
+ void *arg)
+{
+ /* This function can GC */
+ char *fn;
+ int fn_len, max;
+
+ /* Calculate maximum size of any filename made from
+ this path element/specified file name and any possible suffix. */
+ if (CONSP (suffixes))
+ {
+ /* We must traverse the list, so why not do it right. */
+ Lisp_Object tail;
+ max = 0;
+ LIST_LOOP (tail, suffixes)
+ {
+ if (XSTRING_LENGTH (XCAR (tail)) > max)
+ max = XSTRING_LENGTH (XCAR (tail));
+ }
+ }
+ else if (NILP (suffixes))
+ max = 0;
+ else
+ /* Just take the easy way out */
+ max = XSTRING_LENGTH (suffixes);
+
+ fn_len = XSTRING_LENGTH (filename);
+ fn = (char *) alloca (max + fn_len + 1);
+ memcpy (fn, (char *) XSTRING_DATA (filename), fn_len);
+
+ /* Loop over suffixes. */
+ if (!STRINGP (suffixes))
+ {
+ if (NILP (suffixes))
+ {
+ /* Case a) discussed in the comment above. */
+ fn[fn_len] = 0;
+ if ((*fun) (fn, arg))
+ return;
+ }
+ else
+ {
+ /* Case b) */
+ Lisp_Object tail;
+ LIST_LOOP (tail, suffixes)
+ {
+ memcpy (fn + fn_len, XSTRING_DATA (XCAR (tail)),
+ XSTRING_LENGTH (XCAR (tail)));
+ fn[fn_len + XSTRING_LENGTH (XCAR (tail))] = 0;
+ if ((*fun) (fn, arg))
+ return;
+ }
+ }
+ }
+ else
+ {
+ /* Case c) */
+ const char *nsuffix = (const char *) XSTRING_DATA (suffixes);
+
+ while (1)
+ {
+ char *esuffix = (char *) strchr (nsuffix, ':');
+ int lsuffix = esuffix ? esuffix - nsuffix : strlen (nsuffix);
+
+ /* Concatenate path element/specified name with the suffix. */
+ strncpy (fn + fn_len, nsuffix, lsuffix);
+ fn[fn_len + lsuffix] = 0;
+
+ if ((*fun) (fn, arg))
+ return;
+
+ /* Advance to next suffix. */
+ if (esuffix == 0)
+ break;
+ nsuffix += lsuffix + 1;
+ }
+ }
+}
+
+struct locate_file_in_directory_mapper_closure {
+ int fd;
+ Lisp_Object *storeptr;
+ int mode;
+};
+
+static int
+locate_file_in_directory_mapper (char *fn, void *arg)
+{
+ struct locate_file_in_directory_mapper_closure *closure =
+ (struct locate_file_in_directory_mapper_closure *)arg;
+ struct stat st;
+
+ /* Ignore file if it's a directory. */
+ if (stat (fn, &st) >= 0
+ && (st.st_mode & S_IFMT) != S_IFDIR)
+ {
+ /* Check that we can access or open it. */
+ if (closure->mode >= 0)
+ closure->fd = access (fn, closure->mode);
+ else
+ closure->fd = open (fn, O_RDONLY | OPEN_BINARY, 0);
+
+ if (closure->fd >= 0)
+ {
+ /* We succeeded; return this descriptor and filename. */
+ if (closure->storeptr)
+ *closure->storeptr = build_string (fn);
+
+#ifndef WINDOWSNT
+ /* If we actually opened the file, set close-on-exec flag
+ on the new descriptor so that subprocesses can't whack
+ at it. */
+ if (closure->mode < 0)
+ (void) fcntl (closure->fd, F_SETFD, FD_CLOEXEC);
+#endif
+
+ return 1;
+ }
+ }
+ /* Keep mapping. */
+ return 0;
+}
+
+
+/* look for STR in PATH, optionally adding SUFFIXES. DIRECTORY need
+ not have been expanded. */