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โpathisNULL. (Also: before glibc 2.3, this error was also returned ifresolved_pathisNULL.)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 exceededNAME_MAXcharacters, or an entire pathname exceededPATH_MAXcharacters.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-1to signify thatPATH_MAXis 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
- Copyright 1999, Andries E. Brouwer <aeb@cwi.nl>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms