@@ -51,6 +51,8 @@ fn C.uname(name &C.utsname) int
5151
5252fn C.symlink (& char, & char) int
5353
54+ fn C.readlink (& char, & char, int ) int
55+
5456fn C.link (& char, & char) int
5557
5658fn C.gethostname (& char, int ) int
@@ -362,16 +364,58 @@ pub fn raw_execute(cmd string) Result {
362364 return execute (cmd)
363365}
364366
365- // symlink creates a symbolic link named target , which points to origin .
367+ // symlink creates a symbolic link named link_name , which points to target .
366368// It returns a POSIX error message, if it can not do so.
367- pub fn symlink (origin string , target string ) ! {
368- res := C.symlink (& char (origin .str), & char (target .str))
369+ pub fn symlink (target string , link_name string ) ! {
370+ res := C.symlink (& char (target .str), & char (link_name .str))
369371 if res == 0 {
370372 return
371373 }
372374 return error (posix_get_error_msg (C.errno))
373375}
374376
377+ // readlink reads the target of a symbolic link.
378+ // It returns a POSIX error message if it can not do so.
379+ //
380+ // Note that the target of a symbolic link can be any string:
381+ // it is often used to point to another path, but the target is not guaranteed
382+ // to resolve as a path, nor to point to a path that exists.
383+ @[manualfree]
384+ pub fn readlink (path string ) ! string {
385+ // Use a region of stack to get information into; we'll return new memory of more precise size later.
386+ mut buf := [max_path_buffer_size]u8 {}
387+ // readlink returns the number of bytes written into buf, or -1 for errors.
388+ res := C.readlink (& char (path.str), & char (& buf[0 ]), max_path_buffer_size)
389+ if res < 0 {
390+ return last_error ()
391+ }
392+ // Common case: we got a complete read into our buffer on the stack.
393+ // In this case, copy the data into a new heap-allocated string that's right-sized
394+ // (we can't return memory from our stack).
395+ if res < max_path_buffer_size {
396+ return unsafe { (& buf[0 ]).vstring_with_len (res).clone () }
397+ }
398+ // If the number of bytes read wasn't less than as many as we said we'd accept: that means we might not have gotten a complete read.
399+ // In this case, we have to start doing heap allocations, increasingly large, and simply check until we get a complete one.
400+ // Whenever we do succeed: we'll return a string that refers to a subset of that possibly excessively sized buffer,
401+ // because we're already on the heap and returning it is valid; and because allocating a new buffer just
402+ // to save some resident memory is usually a poor trade of spending of time just to reclaim a very minor amount of space.
403+ mut size := max_path_buffer_size
404+ for {
405+ size * = 2
406+ mut buf2 := unsafe { & char (malloc_noscan (size)) }
407+ res2 := C.readlink (& char (path.str), buf2 , size)
408+ if res2 < 0 {
409+ return last_error ()
410+ }
411+ if res2 < size {
412+ return unsafe { tos (& u8 (& buf2 [0 ]), res2 ) }
413+ }
414+ unsafe { free (buf2 ) } // and then loop around to try again with a larger one.
415+ }
416+ return error ('${@METHOD } unreachable code' )
417+ }
418+
375419// link creates a new link (also known as a hard link) to an existing file.
376420// It returns a POSIX error message, if it can not do so.
377421pub fn link (origin string , target string ) ! {
0 commit comments