+\f
+/*
+ * readlink(2) stuff
+ */
+
+#ifndef LISP_FEATURE_WIN32
+/* a wrapped version of readlink(2):
+ * -- If path isn't a symlink, or is a broken symlink, return 0.
+ * -- If path is a symlink, return a newly allocated string holding
+ * the thing it's linked to. */
+char *
+wrapped_readlink(char *path)
+{
+ int bufsiz = strlen(path) + 16;
+ while (1) {
+ char *result = malloc(bufsiz);
+ int n_read = readlink(path, result, bufsiz);
+ if (n_read < 0) {
+ free(result);
+ return 0;
+ } else if (n_read < bufsiz) {
+ result[n_read] = 0;
+ return result;
+ } else {
+ free(result);
+ bufsiz *= 2;
+ }
+ }
+}
+#endif
+\f
+/*
+ * realpath(3), including a wrapper for Windows.
+ */
+char * sb_realpath (char *path)
+{
+#ifndef LISP_FEATURE_WIN32
+ char *ret;
+ int errnum;
+
+ if ((ret = calloc(PATH_MAX, sizeof(char))) == NULL)
+ return NULL;
+ if (realpath(path, ret) == NULL) {
+ errnum = errno;
+ free(ret);
+ errno = errnum;
+ return NULL;
+ }
+ return(ret);
+#else
+ char *ret;
+ char *cp;
+ int errnum;
+
+ if ((ret = calloc(MAX_PATH, sizeof(char))) == NULL)
+ return NULL;
+ if (GetFullPathName(path, MAX_PATH, ret, cp) == 0) {
+ errnum = errno;
+ free(ret);
+ errno = errnum;
+ return NULL;
+ }
+ return(ret);
+#endif
+}
+\f
+/* readdir, closedir, and dirent name accessor. The first three are not strictly
+ * necessary, but should save us some #!+netbsd in the build, and this also allows
+ * building Windows versions using the non-ANSI variants of FindFirstFile &co
+ * under the same API. (Use a structure that appends the handle to the WIN32_FIND_DATA
+ * as the return value from sb_opendir, on sb_readdir grab the name from the previous
+ * call and save the new one.) Nikodemus thought he would have to do that to support
+ * DIRECTORY on UNC paths, but turns out opendir &co do TRT on Windows already -- so
+ * leaving that bit of tedium for a later date, once we figure out the whole *A vs. *W
+ * issue out properly. ...FIXME, obviously, as per above.
+ *
+ * Once that is done, the lisp side functions are best named OS-OPENDIR, etc.
+ */
+extern DIR *
+sb_opendir(char * name)
+{
+ return opendir(name);
+}
+
+extern struct dirent *
+sb_readdir(DIR * dirp)
+{
+ return readdir(dirp);
+}
+
+extern int
+sb_closedir(DIR * dirp)
+{
+ return closedir(dirp);
+}
+
+extern char *
+sb_dirent_name(struct dirent * ent)
+{
+ return ent->d_name;
+}
+\f
+/*
+ * stat(2) stuff
+ */
+
+static void
+copy_to_stat_wrapper(struct stat_wrapper *to, struct stat *from)
+{
+#define FROB(stem) to->wrapped_st_##stem = from->st_##stem
+#ifndef LISP_FEATURE_WIN32
+#define FROB2(stem) to->wrapped_st_##stem = from->st_##stem
+#else
+#define FROB2(stem) to->wrapped_st_##stem = 0;
+#endif
+ FROB(dev);
+ FROB2(ino);
+ FROB(mode);
+ FROB(nlink);
+ FROB2(uid);
+ FROB2(gid);
+ FROB(rdev);
+ FROB(size);
+ FROB2(blksize);
+ FROB2(blocks);
+ FROB(atime);
+ FROB(mtime);
+ FROB(ctime);
+#undef FROB
+}
+
+int
+stat_wrapper(const char *file_name, struct stat_wrapper *buf)
+{
+ struct stat real_buf;
+ int ret;
+
+#ifdef LISP_FEATURE_WIN32
+ /*
+ * Windows won't match the last component of a pathname if there
+ * is a trailing #\/ or #\\, except if it's <drive>:\ or <drive>:/
+ * in which case it behaves the other way around. So we remove the
+ * trailing directory separator unless we are being passed just a
+ * drive name (e.g. "c:\\"). Some, but not all, of this
+ * strangeness is documented at Microsoft's support site (as of
+ * 2006-01-08, at
+ * <http://support.microsoft.com/default.aspx?scid=kb;en-us;168439>)
+ */
+ char file_buf[MAX_PATH];
+ strcpy(file_buf, file_name);
+ int len = strlen(file_name);
+ if (len != 0 && (file_name[len-1] == '/' || file_name[len-1] == '\\') &&
+ !(len == 3 && file_name[1] == ':' && isalpha(file_name[0])))
+ file_buf[len-1] = '\0';
+ file_name = file_buf;
+#endif
+
+ if ((ret = stat(file_name,&real_buf)) >= 0)
+ copy_to_stat_wrapper(buf, &real_buf);
+ return ret;
+}
+
+#ifndef LISP_FEATURE_WIN32
+int
+lstat_wrapper(const char *file_name, struct stat_wrapper *buf)
+{
+ struct stat real_buf;
+ int ret;
+ if ((ret = lstat(file_name,&real_buf)) >= 0)
+ copy_to_stat_wrapper(buf, &real_buf);
+ return ret;
+}
+#else
+/* cleaner to do it here than in Lisp */
+int lstat_wrapper(const char *file_name, struct stat_wrapper *buf)
+{
+ return stat_wrapper(file_name, buf);
+}
+#endif
+