56

How can I convert a relative path to an absolute path in C on Unix? Is there a convenient system function for this?

On Windows there is a GetFullPathName function that does the job, but I didn't find something similar on Unix...

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
evgenka
  • 662
  • 1
  • 6
  • 6

4 Answers4

69

Use realpath().

The realpath() function shall derive, from the pathname pointed to by file_name, an absolute pathname that names the same file, whose resolution does not involve '.', '..', or symbolic links. The generated pathname shall be stored as a null-terminated string, up to a maximum of {PATH_MAX} bytes, in the buffer pointed to by resolved_name.

If resolved_name is a null pointer, the behavior of realpath() is implementation-defined.


The following example generates an absolute pathname for the file identified by the symlinkpath argument. The generated pathname is stored in the actualpath array.

#include <stdlib.h>
...
char *symlinkpath = "/tmp/symlink/file";
char actualpath [PATH_MAX+1];
char *ptr;


ptr = realpath(symlinkpath, actualpath);
xsl
  • 17,116
  • 18
  • 71
  • 112
  • 14
    The 'plus one' is not necessary, thuogh it won't do any harm. – Jonathan Leffler Nov 01 '08 at 00:59
  • 5
    `GetFullPathName` on Windows works for non-existant files as well. `realpath` requires the path to exist. This kind of sucks when you want to create a path or file. – Joakim May 03 '13 at 09:28
  • 5
    The actual path contains the absolute path, but what does ptr contain? – Sam Thomas Jun 11 '18 at 18:38
  • @JonathanLeffler: PATH_MAX is "The uniform system limit (if any) for the length of an entire file name", so excludes the terminating nul which is added by `realpath`. The +1 is therefore required. – EML Apr 19 '20 at 15:45
  • 3
    @EML +1 is not required. From POSIX limits.h rationale: "IEEE PASC Interpretation 1003.1 #15 addressed the inconsistency in the standard with the definition of pathname and the description of {PATH_MAX}, allowing application developers to allocate either {PATH_MAX} or {PATH_MAX}+1 bytes. The inconsistency has been removed by correction to the {PATH_MAX} definition to include the null character. With this change, applications that previously allocated {PATH_MAX} bytes will continue to succeed." – osvein May 22 '20 at 00:32
  • @osvein - interesting - thanks. OTOH, there are presumably older systems that predate this interpretation, so not using +1 in those cases won't "continue to succeed", but will just fail. If you're writing new code to run across a range of systems, this sounds like a bad idea. – EML May 22 '20 at 07:07
  • 1
    @osvein [`PATH_MAX` does not have to exist at all](https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/limits.h.html#tag_13_23_03_02): "A definition of one of the symbolic constants in the following list **shall be omitted** from the header on specific implementations where the corresponding value is equal to or greater than the stated minimum, but where the value can vary depending on the file to which it is applied. ..." Systems like Linux that have variable maximum path lengths for different filesystem types shouldn't define `PATH_MAX`. – Andrew Henle Jan 31 '22 at 21:20
7

Try realpath() in stdlib.h

char filename[] = "../../../../data/000000.jpg";
char* path = realpath(filename, NULL);
if(path == NULL){
    printf("cannot find file with name[%s]\n", filename);
} else{
    printf("path[%s]\n", path);
    free(path);
}
Scott Yang
  • 339
  • 3
  • 6
2

There is also a small path library cwalk which works cross-platform. It has cwk_path_get_absolute to do that:

#include <cwalk.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  char buffer[FILENAME_MAX];

  cwk_path_get_absolute("/hello/there", "./world", buffer, sizeof(buffer));
  printf("The absolute path is: %s", buffer);

  return EXIT_SUCCESS;
}

Outputs:

The absolute path is: /hello/there/world
Julius
  • 1,155
  • 9
  • 19
0

Also try "getcwd"

#include <unistd.h>

char cwd[100000];
getcwd(cwd, sizeof(cwd));
std::cout << "Absolute path: "<< cwd << "/" << __FILE__ << std::endl;

Result:

Absolute path: /media/setivolkylany/WorkDisk/Programming/Sources/MichailFlenov/main.cpp

Testing environment:

setivolkylany@localhost$/ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 8.6 (jessie)
Release:    8.6
Codename:   jessie
setivolkylany@localhost$/ uname -a
Linux localhost 3.16.0-4-amd64 #1 SMP Debian 3.16.36-1+deb8u2 (2016-10-19) x86_64 GNU/Linux
setivolkylany@localhost$/ g++ --version
g++ (Debian 4.9.2-10) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
PADYMKO
  • 4,217
  • 2
  • 36
  • 41
  • 5
    100K for a path?! surely 5K is enough? or PATH_MAX? – Jachdich Nov 30 '18 at 15:01
  • .... as [`getcwd`](https://linux.die.net/man/3/getcwd) resolves the current working directory that code will likely fail for `__FILE__` of "/some/file/compiled/with/absolute.name` – Simon Sobisch Nov 02 '20 at 08:03