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