From 07563de2316656fabceab4b52ef1af4b9b28be91 Mon Sep 17 00:00:00 2001
From: jD91mZM2 <me@krake.one>
Date: Sun, 12 Aug 2018 07:43:23 +0200
Subject: [PATCH] Implement setenv/unsetenv

---
 src/stdlib/src/lib.rs            | 125 +++++++++++++++++++++----------
 tests/expected/stdlib/env.stdout |   7 ++
 tests/stdlib/env.c               |  34 ++++++++-
 3 files changed, 124 insertions(+), 42 deletions(-)

diff --git a/src/stdlib/src/lib.rs b/src/stdlib/src/lib.rs
index 9a9cfe8e..724f1363 100644
--- a/src/stdlib/src/lib.rs
+++ b/src/stdlib/src/lib.rs
@@ -258,7 +258,7 @@ pub extern "C" fn gcvt(value: c_double, ndigit: c_int, buf: *mut c_char) -> *mut
     unimplemented!();
 }
 
-unsafe fn find_env(name: *const c_char) -> Option<(usize, *mut c_char)> {
+unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> {
     for (i, item) in platform::inner_environ.iter().enumerate() {
         let mut item = *item;
         if item == ptr::null_mut() {
@@ -269,23 +269,25 @@ unsafe fn find_env(name: *const c_char) -> Option<(usize, *mut c_char)> {
             );
             break;
         }
-        let mut name = name;
+        let mut search = search;
         loop {
-            let end_of_name = *name == 0 || *name == b'=' as c_char;
-            if *item == 0 || *item == b'=' as c_char || end_of_name {
-                if *item == b'=' as c_char || end_of_name {
+            let end_of_query = *search == 0 || *search == b'=' as c_char;
+            assert_ne!(*item, 0, "environ has an item without value");
+            if *item == b'=' as c_char || end_of_query {
+                if *item == b'=' as c_char && end_of_query {
+                    // Both keys env here
                     return Some((i, item.offset(1)));
                 } else {
                     break;
                 }
             }
 
-            if *item != *name {
+            if *item != *search {
                 break;
             }
 
             item = item.offset(1);
-            name = name.offset(1);
+            search = search.offset(1);
         }
     }
     None
@@ -513,43 +515,16 @@ pub extern "C" fn ptsname(fildes: c_int) -> *mut c_char {
 }
 
 #[no_mangle]
-pub unsafe extern "C" fn putenv(s: *mut c_char) -> c_int {
-    let mut s_len = 0;
-    while *s.offset(s_len) != 0 {
-        s_len += 1;
-    }
-
-    let ptr;
-
-    if let Some((i, _)) = find_env(s) {
-        let mut item = platform::inner_environ[i];
-        let mut item_len = 0;
-        while *item.offset(item_len) != 0 {
-            item_len += 1;
-        }
-
-        if item_len > s_len {
-            ptr = item;
-        } else {
-            platform::free(item as *mut c_void);
-            ptr = platform::alloc(s_len as usize + 1) as *mut c_char;
-            platform::inner_environ[i] = ptr;
-        }
+pub unsafe extern "C" fn putenv(insert: *mut c_char) -> c_int {
+    assert_ne!(insert, ptr::null_mut(), "putenv(NULL)");
+    if let Some((i, _)) = find_env(insert) {
+        platform::free(platform::inner_environ[i] as *mut c_void);
+        platform::inner_environ[i] = insert;
     } else {
-        ptr = platform::alloc(s_len as usize + 1) as *mut c_char;
         let i = platform::inner_environ.len() - 1;
-        assert_eq!(
-            platform::inner_environ[i],
-            ptr::null_mut(),
-            "last element in environ vector was not null"
-        );
-        platform::inner_environ[i] = ptr;
+        assert_eq!(platform::inner_environ[i], ptr::null_mut(), "environ did not end with null");
+        platform::inner_environ[i] = insert;
         platform::inner_environ.push(ptr::null_mut());
-
-        platform::environ = platform::inner_environ.as_mut_ptr();
-    }
-    for i in 0..=s_len {
-        *ptr.offset(i) = *s.offset(i);
     }
     0
 }
@@ -609,6 +584,64 @@ pub extern "C" fn seed48(seed16v: [c_ushort; 3]) -> c_ushort {
     unimplemented!();
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn setenv(mut key: *const c_char, mut value: *const c_char, overwrite: c_int) -> c_int {
+    let mut key_len = 0;
+    while *key.offset(key_len) != 0 {
+        key_len += 1;
+    }
+    let mut value_len = 0;
+    while *value.offset(value_len) != 0 {
+        value_len += 1;
+    }
+
+    let index = if let Some((i, existing)) = find_env(key) {
+        if overwrite == 0 {
+            return 0;
+        }
+
+        let mut existing_len = 0;
+        while *existing.offset(existing_len) != 0 {
+            existing_len += 1;
+        }
+
+        if existing_len >= value_len {
+            // Reuse existing element's allocation
+            for i in 0..=value_len {
+                *existing.offset(i) = *value.offset(i);
+            }
+            return 0;
+        } else {
+            i
+        }
+    } else {
+        let i = platform::inner_environ.len() - 1;
+        assert_eq!(platform::inner_environ[i], ptr::null_mut(), "environ did not end with null");
+        platform::inner_environ.push(ptr::null_mut());
+        i
+    };
+
+    //platform::free(platform::inner_environ[index] as *mut c_void);
+    let mut ptr = platform::alloc(key_len as usize + 1 + value_len as usize) as *mut c_char;
+    platform::inner_environ[index] = ptr;
+
+    while *key != 0 {
+        *ptr = *key;
+        ptr = ptr.offset(1);
+        key = key.offset(1);
+    }
+    *ptr = b'=' as c_char;
+    ptr = ptr.offset(1);
+    while *value != 0 {
+        *ptr = *value;
+        ptr = ptr.offset(1);
+        value = value.offset(1);
+    }
+    *ptr = 0;
+
+    0
+}
+
 // #[no_mangle]
 pub extern "C" fn setkey(key: *const c_char) {
     unimplemented!();
@@ -987,6 +1020,16 @@ pub extern "C" fn unlockpt(fildes: c_int) -> c_int {
     unimplemented!();
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn unsetenv(key: *mut c_char) -> c_int {
+    if let Some((i, _)) = find_env(key) {
+        // No need to worry about updating the pointer, this does not
+        // reallocate in any way. And the final null is already shifted back.
+        platform::inner_environ.remove(i);
+    }
+    0
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn valloc(size: size_t) -> *mut c_void {
     memalign(4096, size)
diff --git a/tests/expected/stdlib/env.stdout b/tests/expected/stdlib/env.stdout
index a0c71315..a0596ab5 100644
--- a/tests/expected/stdlib/env.stdout
+++ b/tests/expected/stdlib/env.stdout
@@ -1 +1,8 @@
 It's working!!
+Updates accordingly.
+in place
+TEST=in place
+Value overwritten and not in place because it's really long
+TEST=in place
+Value overwritten and not in place because it's really long
+Value deleted successfully!
diff --git a/tests/stdlib/env.c b/tests/stdlib/env.c
index a6b2ea0f..e0a821c8 100644
--- a/tests/stdlib/env.c
+++ b/tests/stdlib/env.c
@@ -1,11 +1,43 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 int main() {
     //puts(getenv("SHELL"));
     //puts(getenv("CC"));
 
-    putenv("TEST=It's working!!");
+    char* owned = malloc(26); // "TEST=Updates accordingly." + NUL
+    strcpy(owned, "TEST=It's working!!");
 
+    putenv(owned);
     puts(getenv("TEST"));
+
+    strcpy(owned, "TEST=Updates accordingly.");
+    puts(getenv("TEST"));
+
+    // Allocation is reused
+    setenv("TEST", "in place", 1);
+    puts(getenv("TEST"));
+    puts(owned);
+
+    // Allocation is not reused
+    setenv("TEST", "Value overwritten and not in place because it's really long", 1);
+    puts(getenv("TEST"));
+    puts(owned);
+
+    // Value is not overwritten
+    setenv("TEST", "Value not overwritten", 0);
+    puts(getenv("TEST"));
+
+    unsetenv("TEST");
+    char* env = getenv("TEST");
+    if (env) {
+        puts("This should be null, but isn't");
+        puts(env);
+        return 1;
+    } else {
+        puts("Value deleted successfully!");
+    }
+
+    free(owned);
 }
-- 
GitLab