Browse Source

libc: add realpath (with ToaruOS path mechanics; not Unix)

K. Lange 2 years ago
parent
commit
acdb4a71b9
2 changed files with 107 additions and 0 deletions
  1. 5 0
      base/usr/include/stdlib.h
  2. 102 0
      libc/stdlib/realpath.c

+ 5 - 0
base/usr/include/stdlib.h

@@ -65,4 +65,9 @@ typedef struct { long int quot; long int rem; } ldiv_t;
 extern div_t div(int numerator, int denominator);
 extern ldiv_t ldiv(long numerator, long denominator);
 
+/* These are supposed to be in limits, but gcc screwed us */
+#define PATH_MAX 4096
+#define NAME_MAX 255
+extern char *realpath(const char *path, char *resolved_path);
+
 _End_C_Header

+ 102 - 0
libc/stdlib/realpath.c

@@ -0,0 +1,102 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifndef __toaru__
+#undef realpath
+#define realpath _realpath_toaru
+#endif
+
+#define SYMLINK_MAX 5
+
+static void _append_dir(char *out, char *element) {
+	strcat(out,"/");
+	strcat(out,element);
+}
+
+static void _remove_last(char * out) {
+	char * last = strrchr(out,'/');
+	if (last) {
+		*last = '\0';
+	}
+}
+
+/**
+ * This is accurate to how we handle paths in ToaruOS.
+ * It's not correct for real symbolic link handling,
+ * so it needs some work for that.
+ */
+char *realpath(const char *path, char *resolved_path) {
+	/*
+	 * Basically the same as what we do in the kernel for canonicalize_path
+	 * but slightly more complicated because of the requirement to check
+	 * symlinks... this is going to get interesting.
+	 */
+	if (!path) {
+		errno = -EINVAL;
+		return NULL;
+	}
+
+	if (!resolved_path) {
+		/* Can't support this yet. */
+		errno = -EINVAL;
+		return NULL;
+	}
+
+	/* If we're lucky, we can do this with no allocations, so let's start here... */
+	char working_path[PATH_MAX+1];
+	memcpy(working_path, path, strlen(path)+1);
+
+	*resolved_path = 0;
+
+	if (working_path[0] != '/') {
+		/* Begin by retreiving the current working directory */
+		char cwd[PATH_MAX+1];
+		if (!getcwd(cwd, PATH_MAX)) {
+			/* Not actually sure if this is the right choice for this, but whatever. */
+			errno = -ENOTDIR;
+			return NULL;
+		}
+
+		char *save;
+		char *tok = strtok_r(cwd,"/",&save);
+		do {
+			_append_dir(resolved_path, tok);
+		} while ((tok = strtok_r(NULL,"/",&save)));
+	}
+
+	char *save;
+	char *tok = strtok_r(working_path,"/",&save);
+	do {
+		if (!strcmp(tok,".")) continue;
+		if (!strcmp(tok,"..")) {
+			_remove_last(resolved_path);
+			continue;
+		} else {
+			_append_dir(resolved_path, tok);
+		}
+	} while ((tok = strtok_r(NULL,"/",&save)));
+
+	if (resolved_path[0] == '\0') {
+		strcat(resolved_path,"/");
+	}
+
+	return resolved_path;
+}
+
+#ifndef __toaru__
+int main(int argc, char * argv[]) {
+	char tmp[PATH_MAX+1];
+
+	if (!realpath(argv[1], tmp)) {
+		fprintf(stderr, "invalid path, errno=%d\n", errno);
+		return 1;
+	}
+
+	fprintf(stderr, "%s=%s\n", argv[1], tmp);
+	return 0;
+}
+#endif