diff --git a/.gitignore b/.gitignore
index b83d22266ac8aa2f8df2edef68082c789727841d..431845ed05927f3a23c9b23d4ae3d7a2626dd668 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 /target/
+.idea/
diff --git a/src/string/src/lib.rs b/src/string/src/lib.rs
index 49bfe11be989518b266ec96e7942e38a3c819ad2..c97302b92bf44cefacf6f081c32275c5dc8ecb70 100644
--- a/src/string/src/lib.rs
+++ b/src/string/src/lib.rs
@@ -323,17 +323,49 @@ pub unsafe extern "C" fn strstr(s1: *const c_char, s2: *const c_char) -> *mut c_
 }
 
 #[no_mangle]
-pub extern "C" fn strtok(s1: *mut c_char, s2: *const c_char) -> *mut c_char {
-    unimplemented!();
+pub extern "C" fn strtok(s1: *mut c_char, delimiter: *const c_char) -> *mut c_char {
+    static mut HAYSTACK: *mut c_char = ptr::null_mut();
+    unsafe {
+        return strtok_r(s1, delimiter, &mut HAYSTACK);
+    }
 }
 
 #[no_mangle]
 pub extern "C" fn strtok_r(
     s: *mut c_char,
-    sep: *const c_char,
+    delimiter: *const c_char,
     lasts: *mut *mut c_char,
 ) -> *mut c_char {
-    unimplemented!();
+    // Loosely based on GLIBC implementation
+    unsafe {
+        let mut haystack = s;
+        if haystack.is_null() {
+            if (*lasts).is_null() {
+                return ptr::null_mut();
+            }
+            haystack = *lasts;
+        }
+
+        // Skip past any extra delimiter left over from previous call
+        haystack = haystack.add(strspn(haystack, delimiter));
+        if *haystack == 0 {
+            *lasts = ptr::null_mut();
+            return ptr::null_mut();
+        }
+
+        // Build token by injecting null byte into delimiter
+        let token = haystack;
+        haystack = strpbrk(token, delimiter);
+        if !haystack.is_null() {
+            haystack.write(0);
+            haystack = haystack.add(1);
+            *lasts = haystack;
+        } else {
+            *lasts = ptr::null_mut();
+        }
+
+        return token;
+    }
 }
 
 #[no_mangle]
diff --git a/tests/Makefile b/tests/Makefile
index 8dffd5edd5a88d3e05a1954db86492691ce7c3eb..e1b95b1839e1a3d66a2333907fb0470059a5bf99 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -30,6 +30,8 @@ EXPECT_BINS=\
 	string/strspn \
 	string/strstr \
 	string/strpbrk \
+	string/strtok \
+	string/strtok_r \
 	unlink \
 	waitpid \
 	write
diff --git a/tests/expected/string/strtok.stderr b/tests/expected/string/strtok.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/string/strtok.stdout b/tests/expected/string/strtok.stdout
new file mode 100644
index 0000000000000000000000000000000000000000..201357e8cbf62d9b06cde1d92cb068ae08b42c89
--- /dev/null
+++ b/tests/expected/string/strtok.stdout
@@ -0,0 +1,2 @@
+I'd_just_like_to_interject_for_a_moment.__What_you're_referring_to_as_Linux,
+is_in_fact,_GNU/Linux,_or_as_I've_recently_taken_to_calling_it,_GNU_plus_Linux.
diff --git a/tests/expected/string/strtok_r.stderr b/tests/expected/string/strtok_r.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/string/strtok_r.stdout b/tests/expected/string/strtok_r.stdout
new file mode 100644
index 0000000000000000000000000000000000000000..201357e8cbf62d9b06cde1d92cb068ae08b42c89
--- /dev/null
+++ b/tests/expected/string/strtok_r.stdout
@@ -0,0 +1,2 @@
+I'd_just_like_to_interject_for_a_moment.__What_you're_referring_to_as_Linux,
+is_in_fact,_GNU/Linux,_or_as_I've_recently_taken_to_calling_it,_GNU_plus_Linux.
diff --git a/tests/string/strtok.c b/tests/string/strtok.c
new file mode 100644
index 0000000000000000000000000000000000000000..9809fdf054f4ca51c77a657e3d30e86af60d5ace
--- /dev/null
+++ b/tests/string/strtok.c
@@ -0,0 +1,17 @@
+#include <string.h>
+#include <stdio.h>
+
+int main(int argc, char* argv[]) {
+    char source[] = "I'd just like to interject for a moment.  What you're referring to as Linux, "
+                    "is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux.\n";
+
+    char* token = strtok(source, " ");
+    while (token) {
+        printf("%s", token);
+        if (token = strtok(NULL, " ")) {
+            printf("_");
+        }
+    }
+
+    return 0;
+}
diff --git a/tests/string/strtok_r.c b/tests/string/strtok_r.c
new file mode 100644
index 0000000000000000000000000000000000000000..36becfe2969093dcd80246a1dcef0b03a0892770
--- /dev/null
+++ b/tests/string/strtok_r.c
@@ -0,0 +1,18 @@
+#include <string.h>
+#include <stdio.h>
+
+int main(int argc, char* argv[]) {
+    char source[] = "I'd just like to interject for a moment.  What you're referring to as Linux, "
+                    "is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux.\n";
+    char* sp;
+
+    char* token = strtok_r(source, " ", &sp);
+    while (token) {
+        printf("%s", token);
+        if (token = strtok_r(NULL, " ", &sp)) {
+            printf("_");
+        }
+    }
+
+    return 0;
+}