Skip to content

linux: fix musl compatibility (non-glibc systems)#56

Open
zuixalias wants to merge 3 commits into4coder-community:developfrom
zuixalias:develop
Open

linux: fix musl compatibility (non-glibc systems)#56
zuixalias wants to merge 3 commits into4coder-community:developfrom
zuixalias:develop

Conversation

@zuixalias
Copy link

These changes allow successful compilation on standard Linux distributions without relying on GNU extensions.

Note for reviewers:
Stack memory is used for temporary path buffers via alloca. For normal filesystem paths, this is safe. Extremely long paths could theoretically overflow the stack.

#AI

@Jack-Punter
Copy link
Contributor

Thanks for opening this! I havent had chance to fully verify this but this looks like a good opportunity to address the TODO in system_get_canonical. It might also be better to just use a PATH_MAX sized stack allocation for the temporary buffers rather than rely on alloca. I've also used PATH_MAX for the arena allocation of output as im pretty sure it could be possible for the canonical name to be longer than the input name, if we dont want to "waste" arena space we could just let realpath allocate with malloc then copy onto the arena, then free. idk if @B-Y-P or @mrmixer have any input here?

diff --git a/code/platform_linux/linux_4ed_functions.cpp b/code/platform_linux/linux_4ed_functions.cpp
index 88184e0..78d1d98 100644
--- a/code/platform_linux/linux_4ed_functions.cpp
+++ b/code/platform_linux/linux_4ed_functions.cpp
@@ -64,70 +64,23 @@ system_get_path(Arena* arena, System_Path_Code path_code){
 
 internal String_Const_u8
 system_get_canonical(Arena* arena, String_Const_u8 name){
-    
-    // first remove redundant ../, //, ./ parts
-    
-    char *tmp = (char*)alloca(name.size + 1);
-    memcpy(tmp, name.str, name.size);
-    tmp[name.size] = 0;
-    const u8* input = (u8*)tmp;
-    u8* output = push_array(arena, u8, name.size + 1);
-    
-    const u8* p = input;
-    u8* q = output;
-    
-    while(*p) {
-        
-        // not a slash - copy char
-        if(p[0] != '/') {
-            *q++ = *p++;
-            continue;
-        }
-        
-        // two slashes in a row, skip one.
-        if(p[1] == '/') {
-            ++p;
-        }
-        else if(p[1] == '.') {
-            
-            // skip "/./" or trailing "/."
-            if(p[2] == '/' || p[2] == '\0') {
-                p += 2;
-            }
-            
-            // if we encounter "/../" or trailing "/..", remove last directory instead
-            else if(p[2] == '.' && (p[3] == '/' || p[3] == '\0')) {
-                while(q > output && *--q != '/'){};
-                p += 3;
-            }
-            
-            else {
-                *q++ = *p++;
-            }
-        }
-        else {
-            *q++ = *p++;
-        }
-    }
-    
-#ifdef INSO_DEBUG
-    if(name.size != q - output) {
-        LINUX_FN_DEBUG("[%.*s] -> [%.*s]", (int)name.size, name.str, (int)(q - output), output);
-    }
-#endif
-    
-    // TODO: use realpath at this point to resolve symlinks?
-    return SCu8(output, q - output);
+    Assert(name.size < PATH_MAX - 1);
+    char tmp[PATH_MAX] = { 0 };
+    strncpy(tmp, (const char *)name.str, name.size);
+    char * out = push_array_zero(arena, char, PATH_MAX);
+    realpath(tmp, out);
+    return SCu8((u8*)out, cstring_length(out));
 }
 
 internal File_List
 system_get_file_list(Arena* arena, String_Const_u8 directory){
     //LINUX_FN_DEBUG("%.*s", (int)directory.size, directory.str);
     File_List result = {};
-    
-    char *path = (char*)alloca(directory.size + 1);
-    memcpy(path, directory.str, directory.size);
-    path[directory.size] = 0;
+
+    Assert(directory.size < PATH_MAX - 1);
+    char path[PATH_MAX] = { 0 };
+    strncpy(path, (const char *)directory.str, directory.size);
+
     int fd = open(path, O_RDONLY | O_DIRECTORY);
     if(fd == -1) {
         perror("open");

@zuixalias
Copy link
Author

This also fixes the relative path issue. Previously, running 4ed path/from/pwd did not work and required using the full path from the repository root. It now correctly resolves paths relative to the current working directory... nice!

@mrmixer
Copy link
Contributor

mrmixer commented Feb 26, 2026

Looks good to me with @Jack-Punter changes, but I didn't try running it to confirm there are no issues.

@Jack-Punter
Copy link
Contributor

I've had this on the back of my mind and I think i will do the approach of letting realpath allocate (if you call it like char *real = realpath(tmp, NULL) it will allocate a buffer for the output), then copy it to the arena and free the allocated buffer. 4kb seams like a lot to put on the arena for most usecases. I'll try and make the changes to push when i get home but idk if i can just push to a PR that i dont own like that. i think theres a setting to let maintianers push to the pr but idk if its on the pr OP's side or the repo side.

@Jack-Punter
Copy link
Contributor

Jack-Punter commented Feb 26, 2026

As there was a need to modify it anyway i have updated the context cracking with a more recent one that allen has been using, i think it coveres a few more bases (like compiling with clang on windows), can revert if there's any objecions. This change still needs some testing, i wont be able to get onto a linux machine to test till tomorrow.
@zuixalias If you could test these changes on your machine to make sure the fix is still working that would be great!

@zuixalias
Copy link
Author

Yes, it’s working as expected. I’m able to compile and open files using relative paths.
Btw there's now an additional similar message printed to stderr when starting 4ed, both with and without args:

+ open: No such file or directory
  open: No such file or directory
  ALSA ERR: snd_pcm.hw_params_set_buffer_size (pcm, hw, BufferSize * BufferCount): [-22]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants