Index: native_client_sdk/src/libraries/nacl_io/syscalls/realpath.c |
diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/realpath.c b/native_client_sdk/src/libraries/nacl_io/syscalls/realpath.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9451243c70feee7603032590e2341b25d0fd1cea |
--- /dev/null |
+++ b/native_client_sdk/src/libraries/nacl_io/syscalls/realpath.c |
@@ -0,0 +1,131 @@ |
+/* Copyright 2013 The Chromium Authors. All rights reserved. |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. */ |
+ |
+#include <assert.h> |
+#include <stdlib.h> |
+#include <errno.h> |
+#include <limits.h> |
+#include <stdlib.h> |
+#include <string.h> |
+#include <sys/stat.h> |
+#include <unistd.h> |
+ |
+#include "sdk_util/macros.h" |
+ |
+EXTERN_C_BEGIN |
+ |
+#if defined(__native_client__) |
+ |
+// TODO(binji): glibc has realpath, but it fails for all tests. Investigate. |
+ |
+char* realpath(const char* path, char* resolved_path) { |
+ if (path == NULL) { |
+ errno = EINVAL; |
+ return NULL; |
+ } |
+ |
+ int needs_free = 0; |
+ if (resolved_path == NULL) { |
+ resolved_path = (char*)malloc(PATH_MAX); |
+ needs_free = 1; |
+ } |
+ |
+ struct stat statbuf; |
+ const char* in = path; |
+ char* out = resolved_path; |
+ char* out_end = resolved_path + PATH_MAX - 1; |
+ int done = 0; |
+ |
+ *out = 0; |
+ |
+ if (*in == '/') { |
+ // Absolute path. |
+ strcat(out, "/"); |
+ in++; |
+ out++; |
+ } else { |
+ // Relative path. |
+ if (getcwd(out, out_end - out) == NULL) |
+ goto fail; |
+ |
+ out += strlen(out); |
+ } |
+ |
+ if (stat(resolved_path, &statbuf) != 0) |
+ goto fail; |
+ |
+ while (!done) { |
+ const char* next_slash = strchr(in, '/'); |
+ size_t namelen; |
+ const char* next_in; |
+ if (next_slash) { |
+ namelen = next_slash - in; |
+ next_in = next_slash + 1; |
+ } else { |
+ namelen = strlen(in); |
+ next_in = in + namelen; // Move to the '\0' |
+ done = 1; |
+ } |
+ |
+ if (namelen == 0) { |
+ // Empty name, do nothing. |
+ } else if (namelen == 1 && strncmp(in, ".", 1) == 0) { |
+ // Current directory, do nothing. |
+ } else if (namelen == 2 && strncmp(in, "..", 2) == 0) { |
+ // Parent directory, find previous slash in resolved_path. |
+ char* prev_slash = strrchr(resolved_path, '/'); |
+ assert(prev_slash != NULL); |
+ |
+ out = prev_slash; |
+ if (prev_slash == resolved_path) { |
+ // Moved to the root. Keep the slash. |
+ ++out; |
+ } |
+ |
+ *out = 0; |
+ } else { |
+ // Append a slash if not at root. |
+ if (out != resolved_path + 1) { |
+ if (out + 1 > out_end) { |
+ errno = ENAMETOOLONG; |
+ goto fail; |
+ } |
+ |
+ strncat(out, "/", namelen); |
+ out++; |
+ } |
+ |
+ if (out + namelen > out_end) { |
+ errno = ENAMETOOLONG; |
+ goto fail; |
+ } |
+ |
+ strncat(out, in, namelen); |
+ out += namelen; |
+ } |
+ |
+ in = next_in; |
+ |
+ if (stat(resolved_path, &statbuf) != 0) |
+ goto fail; |
+ |
+ // If there is more to the path, then the current path must be a directory. |
+ if (!done && !S_ISDIR(statbuf.st_mode)) { |
+ errno = ENOTDIR; |
+ goto fail; |
+ } |
+ } |
+ |
+ return resolved_path; |
+ |
+fail: |
+ if (needs_free) { |
+ free(resolved_path); |
+ } |
+ return NULL; |
+} |
+ |
+EXTERN_C_END |
+ |
+#endif // defined(__native_client__) |