realpath(3)

โœจ realpath(3) โœจ

okay bestie, gather round, because we're talking about realpath โ€” the libc glow-up function that takes your messy little path and makes it canonical ๐Ÿ’… and yes she's from the Linux man-pages (we'll get to that at the end, don't rush me).


๐ŸŒธ NAME ๐ŸŒธ

realpath โ€” return the canonicalized absolute pathname


๐Ÿ“š LIBRARY ๐Ÿ“š

Standard C library (libc, -lc)


๐ŸŽ€ SYNOPSIS ๐ŸŽ€

#include <limits.h>
#include <stdlib.h>

char *realpath(const char *restrict path,
               char *restrict resolved_path);

๐ŸŒˆ Feature Test Macro Requirements for glibc ๐ŸŒˆ

(psst โ€” see feature_test_macros(7) if you don't know what these are)

realpath():

_XOPEN_SOURCE >= 500
    || /* glibc >= 2.19: */ _DEFAULT_SOURCE
    || /* glibc <= 2.19: */ _BSD_SOURCE

๐Ÿ’– DESCRIPTION ๐Ÿ’–

So likeโ€ฆ realpath() takes the null-terminated string named by path and it expands all symbolic links, and it resolves every reference to /./, /../, and any extra / characters hanging around being extra. Result? A canonicalized absolute pathname, no cap, that's the whole job. ๐Ÿซถ

That resulting pathname gets stored as a null-terminated string in the buffer that resolved_path points at โ€” up to a maximum of PATH_MAX bytes. And the path you get back has no symbolic link, /./ or /../ components left in it. Clean. Pristine. Iconic. โœจ

But wait, the plot twist: if resolved_path is specified as NULL, then realpath() goes and uses malloc(3) to allocate a buffer of up to PATH_MAX bytes to hold the resolved pathname, and returns a pointer to that buffer. And you โ€” yes you, bestie โ€” are the one who has to deallocate it with free(3). Don't forget. I'm serious. ๐Ÿ˜ญ


๐Ÿ”ฅ RETURN VALUE ๐Ÿ”ฅ

If there's no error, realpath() returns a pointer to the resolved_path.

Otherwise? It returns NULL, the contents of the array resolved_path are undefined (๐Ÿšจ do not read it, do not touch it, do not look at it), and errno is set to tell you what went wrong.


๐Ÿ’€ ERRORS ๐Ÿ’€

  • EACCES โ€” Read or search permission was denied for a component of the path prefix. (she said no access, and she meant it ๐Ÿ™ˆ)
  • EINVAL โ€” path is NULL. (Also: before glibc 2.3, this error was also returned if resolved_path is NULL.)
  • EIO โ€” An I/O error occurred while reading from the filesystem.
  • ELOOP โ€” Too many symbolic links were encountered in translating the pathname. (link loop, link loop, link loop ๐Ÿ”)
  • ENAMETOOLONG โ€” A component of a pathname exceeded NAME_MAX characters, or an entire pathname exceeded PATH_MAX characters.
  • ENOENT โ€” The named file does not exist.
  • ENOMEM โ€” Out of memory.
  • ENOTDIR โ€” A component of the path prefix is not a directory.

๐Ÿ‘‘ ATTRIBUTES ๐Ÿ‘‘

For an explanation of the terms used in this section, see attributes(7). The original ships this as a lil table, and here's that exact info:

  • Interface: realpath() โ€” Attribute: Thread safety โ€” Value: MT-Safe โœ…

๐Ÿฆ‹ VERSIONS ๐Ÿฆ‹

๐Ÿงš GNU extensions ๐Ÿงš

If the call fails with either EACCES or ENOENT and resolved_path is not NULL, then the prefix of path that is not readable or does not exist is returned in resolved_path. cute lil consolation prize, honestly. ๐ŸŽ


โญ STANDARDS โญ

POSIX.1-2008.


๐ŸŒˆ HISTORY ๐ŸŒˆ

4.4BSD, POSIX.1-2001, Solaris. the ancestry is giving legacy โœจ

POSIX.1-2001 says that the behavior if resolved_path is NULL is implementation-defined. POSIX.1-2008 specifies the behavior described in this page. so like, the vibes got standardized. ๐Ÿ’…

In 4.4BSD and Solaris, the limit on the pathname length is MAXPATHLEN (found in <sys/param.h>). SUSv2 prescribes PATH_MAX and NAME_MAX, as found in <limits.h> or provided by the pathconf(3) function. A typical source fragment would be:

#ifdef PATH_MAX
  path_max = PATH_MAX;
#else
  path_max = pathconf(path, _PC_PATH_MAX);
  if (path_max <= 0)
    path_max = 4096;
#endif

(But see the BUGS section. ๐Ÿ‘€ foreshadowing, bestie.)


๐Ÿ˜ญ BUGS ๐Ÿ˜ญ

okay so this is the part where I hold your hand, because: The POSIX.1-2001 standard version of this function is broken by design, since it is impossible to determine a suitable size for the output buffer, resolved_path. Not "a little awkward". Broken by design. ๐Ÿ’€

According to POSIX.1-2001 a buffer of size PATH_MAX suffices โ€” but PATH_MAX need not be a defined constant, and may have to be obtained using pathconf(3). And asking pathconf(3) does not really help either, because:

  • on the one hand, POSIX warns that the result of pathconf(3) may be huge and unsuitable for mallocing memory ๐Ÿ˜ญ
  • on the other hand, pathconf(3) may return -1 to signify that PATH_MAX is not bounded ๐Ÿ™ˆ

The resolved_path == NULL feature โ€” not standardized in POSIX.1-2001, but standardized in POSIX.1-2008 โ€” allows this design problem to be avoided. she really said "new standard, new me" and honestly? slay. ๐Ÿซถ


๐Ÿ’œ SEE ALSO ๐Ÿ’œ

realpath(1), readlink(2), canonicalize_file_name(3), getcwd(3), pathconf(3), sysconf(3)


and that's the whole page, no cap ๐Ÿงš this lil beauty comes to you from Linux man-pages 6.20, dated 2026-02-08 โ€” mwah, go canonically resolve something bestie, and free your buffer ๐ŸŽ€๐Ÿ’–๐ŸŒธ

๐Ÿ“ฆ where this page comes from
  • arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • ๐Ÿ“œ the page's license: Linux-man-pages-copyleft
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms