1/* Copyright 2013 The Chromium Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. */ 4 5#include <assert.h> 6#include <stdlib.h> 7#include <errno.h> 8#include <limits.h> 9#include <stdlib.h> 10#include <string.h> 11#include <sys/stat.h> 12#include <unistd.h> 13 14#include "sdk_util/macros.h" 15 16EXTERN_C_BEGIN 17 18#if defined(__native_client__) 19 20// TODO(binji): glibc has realpath, but it fails for all tests. Investigate. 21 22char* realpath(const char* path, char* resolved_path) { 23 if (path == NULL) { 24 errno = EINVAL; 25 return NULL; 26 } 27 28 int needs_free = 0; 29 if (resolved_path == NULL) { 30 resolved_path = (char*)malloc(PATH_MAX); 31 needs_free = 1; 32 } 33 34 struct stat statbuf; 35 const char* in = path; 36 char* out = resolved_path; 37 char* out_end = resolved_path + PATH_MAX - 1; 38 int done = 0; 39 40 *out = 0; 41 42 if (*in == '/') { 43 // Absolute path. 44 strcat(out, "/"); 45 in++; 46 out++; 47 } else { 48 // Relative path. 49 if (getcwd(out, out_end - out) == NULL) 50 goto fail; 51 52 out += strlen(out); 53 } 54 55 if (stat(resolved_path, &statbuf) != 0) 56 goto fail; 57 58 while (!done) { 59 const char* next_slash = strchr(in, '/'); 60 size_t namelen; 61 const char* next_in; 62 if (next_slash) { 63 namelen = next_slash - in; 64 next_in = next_slash + 1; 65 } else { 66 namelen = strlen(in); 67 next_in = in + namelen; // Move to the '\0' 68 done = 1; 69 } 70 71 if (namelen == 0) { 72 // Empty name, do nothing. 73 } else if (namelen == 1 && strncmp(in, ".", 1) == 0) { 74 // Current directory, do nothing. 75 } else if (namelen == 2 && strncmp(in, "..", 2) == 0) { 76 // Parent directory, find previous slash in resolved_path. 77 char* prev_slash = strrchr(resolved_path, '/'); 78 assert(prev_slash != NULL); 79 80 out = prev_slash; 81 if (prev_slash == resolved_path) { 82 // Moved to the root. Keep the slash. 83 ++out; 84 } 85 86 *out = 0; 87 } else { 88 // Append a slash if not at root. 89 if (out != resolved_path + 1) { 90 if (out + 1 > out_end) { 91 errno = ENAMETOOLONG; 92 goto fail; 93 } 94 95 strncat(out, "/", namelen); 96 out++; 97 } 98 99 if (out + namelen > out_end) { 100 errno = ENAMETOOLONG; 101 goto fail; 102 } 103 104 strncat(out, in, namelen); 105 out += namelen; 106 } 107 108 in = next_in; 109 110 if (stat(resolved_path, &statbuf) != 0) 111 goto fail; 112 113 // If there is more to the path, then the current path must be a directory. 114 if (!done && !S_ISDIR(statbuf.st_mode)) { 115 errno = ENOTDIR; 116 goto fail; 117 } 118 } 119 120 return resolved_path; 121 122fail: 123 if (needs_free) { 124 free(resolved_path); 125 } 126 return NULL; 127} 128 129EXTERN_C_END 130 131#endif // defined(__native_client__) 132