How to get absolute path of a symbolic link? - c

How can I get the absolute path of a symbolic link? If I do it in the following way:
char buf[100];
realpath(symlink, buf);
I won't get the absolute path of the symlink, but instead I would get the absolute path this symlink links to. Now my question is: What If I want to get the absolute path of the symlink itself? Is there any function in Linux C that allows me to do so?
Note: What I'd like to achieve is the absolute path of the symbolic link itself, not the path it's pointing to! For example, if the relative path of a symbolic link is Task2/sym_lnk, I want its absolute path, which can be /home/user/kp/Task2/sym_lnk.

You can use realpath() function with the parent folder of the symlink, then you concatenate the symlink name.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
// Find the last occurrence of c in str, otherwise returns NULL
char* find_last_of( char *str, char c )
{
for( char *i = str + strlen(str) ; i >= str ; i-- )
if( *i == c )
return i;
return NULL;
}
// Does the job
char* getAbsPath( char *path )
{
char *name; // Stores the symlink name
char *tmp; // Aux for store the last /
char *absPath = malloc( PATH_MAX ); // Stores the absolute path
tmp = find_last_of( path, '/' );
// If path is only the symlink name (there's no /), then the
// parent folder is the current work directory
if( tmp == NULL ){
name = strdup( path );
getcwd( absPath, PATH_MAX ); // Is already absolute path
}
else{
// Extract the name and erase it from the original
// path.
name = strdup( tmp + 1 );
*tmp = '\0';
// Get the real path of the parent folder.
realpath( path, absPath );
}
// Concatenate the realpath of the parent and "/name"
strcat( absPath, "/" );
strcat( absPath, name );
free( name );
return absPath;
}
// Test the function
int main( int argc, char **argv )
{
char *absPath;
if( argc != 2 ){
fprintf( stderr, "Use:\n\n %s <symlink>\n", *argv );
return -1;
}
// Verify if path exists
if( access( argv[1], F_OK ) ){
perror( argv[1] );
return -1;
}
absPath = getAbsPath( argv[1] );
printf( "Absolute Path: %s\n", absPath );
free( absPath );
return 0;
}
If you use the above code with directories, it needs an special case for "." and "..", but works with "./" and "../"

You can use the system call readlink():
int readlink(const char* fdpath, char* filepath, size_t pathsize);
fdpath is the path of the symbolic link, something like /proc/pid/fd/link_number.
filepath is the path of the file it's pointing to.
Note that readlink() does not null terminate the output string, but does reports its length in the return value.

Related

Function strtok() does not work with strcat() (illegal hardware instruction)

I'm new at c and I'm writing a script that inputs a file path as arguments. I want to have the last element of the path and the rest of the path.
Here is what I wrote:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *file = argv[1];
char base[sizeof(file)] = "";
char *tok = strtok(file, "/");
while (tok != NULL)
{
strcat(base, tok);
tok = strtok(NULL, "/");
}
printf("Base folder: %s\n", base);
printf("Last element: %s\n", tok);
return 0;
}
Input: ./getlast /this/is/some/path/file.txt
Expected result:
Base folder: /this/is/some/path
Last element: file.txt
It gives me this error when I concatenate base with tok:
[1] 15245 illegal hardware instruction ./getlast /Users/<myusername>/Desktop/getlast/getlast.c
I keep trying different solutions but I can't figure out what is wrong.
(I haven't a good English so sorry for that)
To use strtok() for this task would be complicated.
Determining which is the last token (filename) involves not commiting to concatentating until the next token is retrieved from the string.
One could 'hang onto' one pointer and append when another token is found, but that's confusing.
To find what might be called "the last token", strrchr() is perfectly suited to the task.
I regard "enviroment vars" (like argv) to be readonly, so strdup() makes a working copy that can be altered with impunity.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main( int argc, char *argv[] ) {
if( argc != 2 ) { // always test before using
printf( "Bad usage\n" );
return;
}
char *copy = strdup( argv[1] ); // temp working copy to manipulate
char *lastSep = strrchr( copy, '/' ); // find LAST slash char
if( lastSep == NULL )
printf( "No prefix path: %s\n", argv[1] );
else {
*lastSep++ = '\0'; // split into two strings
printf( "Base folder: %s\n", copy );
printf( "Last element: %s\n", lastSep );
}
free( copy );
}
Update
You can avoid the overhead of making a working copy by capitalising on some pointer arithmetic and printf()'s length specifiers. Here's another way to achieve the same thing without using the heap:
char *p = "/one/two/three/four.txt";
char *lastSep = strrchr( p, '/' ); // find LAST slash char
if( lastSep == NULL )
printf( "No prefix path: %s\n", p );
else {
printf( "Base folder: %.*s\n", lastSep - p, p );
printf( "Last element: %s\n", lastSep + 1 );
}

C - Obtaining directory path

I'm writing a program that when executed in a directory will generate a text file with all of the contents in that directory. I'm getting the directory path from the **argv to main and because I'm using netbeans and cygwin I have to do some string manipulation of the obtained path in my char* get_path(char **argv) function. The directory path size will always vary therefore I'm assigning space with malloc to store it in the memory.
Program snippet:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include "dbuffer.h" //my own library
#include "darray.h" //my own library
ARR* get_dir_contents( char* path)
{
DBUFF *buff = NULL;
ARR *car = NULL;
DIR *dir_stream = NULL;
struct dirent *entry = NULL;
dir_stream = opendir(path);
if(opendir(path)==NULL) printf("NULL");
//... more code here
return car;
}
char* get_path(char **argv)
{
char *path = malloc(sizeof(char)* sizeof_pArray( &argv[0][11] ) + 3 );
strcpy(path, "C:");
strcat(path, &argv[0][11]);
printf("%s, sizeof: %d \n",path, sizeof_pArray(path));
return path;
}
int main(int argc, char** argv)
{
char *p = get_path(argv);
ARR *car = get_dir_contents(&p[0]);
//... more code here
return (EXIT_SUCCESS);
}
The problem is that the string that I have doesn't initialize the dir_stream pointer. I suspect it is because of some discrepancy between pointers and string literals but I can't pinpoint what it is exactly. Also the fact that dirent library function expects DIR *opendir(const char *dirname); const char might have something to do with it.
Output:
C:/Users/uk676643/Documents/NetBeansProjects/__OpenDirectoryAndListFiles/dist/Debug/Cygwin_4.x-Windows/__opendirectoryandlistfiles, sizeof: 131
NULL
RUN FAILED (exit value -1,073,741,819, total time: 2s)
there are some things here that can go wrong so
I would suggest doing something like this instead
char* get_path(char *argv)
{
char *path = malloc(sizeof(char)* strlen(argv) );
if (path != NULL)
{
strcpy(path, "C:");
strcat(path, argv + 11);
printf("%s, sizeof: %d \n",path, strlen(path));
}
return path;
}
...
char* p = get_path(*argv);
note: you don't need the extra 3 bytes, since you allocate including the 11 bytes you later skip. although instead of having the 11 bytes offset you may want to decompose the string and then later put it together so that it is portable. E.g. using strtok you could split that path and replace the parts you don't need.
Could it be a simple confusion about argv ? Please insert the following lines
just at the beginning of your main() , is it what you expected ?
printf("\n argv[0]== %s" , argv[0] );
getchar();
printf("\n argv[1]== %s" , argv[1] );
getchar();
OK, so we work from argv[0] , please try this for get_path
char *get_path(char *argv)
{
int i=0;
// +2 to add the drive letter
char *path = malloc(sizeof(char)* strlen(argv)+2 );
if (path != NULL)
{
strcpy(path, "C:");
strcat(path, argv);
// we get the path and the name of calling program
printf("\n path and program== %s",path);
printf("%s, sizeof: %d \n",path, strlen(path));
// now remove calling program name
for( i=strlen(path) ; ; i--)
{
// we are working in windows
if(path[i]=='\\') break;
path[i]='\0';
}
}
return path;
}

C - finding full path of command

I am trying to find the full path of a command someone would type in the terminal or console window. I am trying to use
getenv(PATH)
to get the ':' delimited strings of different paths the command may live in, and then use
stat()
to see if it exists in each one.
I am having trouble parsing through the returns of getenv() since I cannot use the string library.
getenv(path) returns:
PATH = /Library/Frameworks/Python.framework/Versions/3.2/bin:/Library/Frameworks/Python.framework/Versions/3.2/bin:/Library/Frameworks/Python.framework/Versions/2.7/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
I am trying:
char* fullPath = getenv( "PATH" );
struct stat buffer;
int exists;
char* fileOrDirectory = usersCommand
exists = stat( file_or_dir, &buffer );
if ( exists == 0 && ( S_IFDIR & buffer.st_mode ) ) {
//dir exists
} else if ( exists == 0 && ( S_IFREG & buffer.st_mode ) ) {
//file exists
} else {
//neither exists
}
As of now I am not using my fullPath variable. As it is now is it just searching my local directory for the command?
An example command would be 'cd', or 'ls' , etc.
How can I parse through the ':' delimited string and then call stat on each one? I don't exactly understand the purpose of buffer besides having some info on the file or directory status but I feel like it should be taking another parameter so I can input what I am searching for as well as the fullPath.
Thanks
The function strtok is the standard method of tokenizing a string. With that you can build the full path-name of the file.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
main(int argc, char **argv)
{
char* fullPath = getenv( "PATH" );
struct stat buffer;
int exists;
char* fileOrDirectory = argv[0];
char fullfilename[1024];
char *token = strtok(fullPath, ":");
/* walk through other tokens */
while( token != NULL )
{
sprintf(fullfilename, "%s/%s", token, fileOrDirectory);
exists = stat( fullfilename, &buffer );
if ( exists == 0 && ( S_IFREG & buffer.st_mode ) ) {
printf("found file %s\n", fullfilename);
}
token = strtok(NULL, ":"); /* next token */
}
exit(0);
}
Because string standard library functions are not allowed you need to write a string tokenizer yourself you can do something on the lines of the following code below, You might would have to refine it a little more.
Basically what we are doing here
Get the PATH
find token ':'
memcpy substring upto token -1
update the PATH
repeat until '\0'.
#define MAX_DIR_PATH_SIZE 500
char *get_directory(char **u_path, int *done)
{
int i;
char *temp = malloc(MAX_DIR_PATH_SIZE);
//handle error here
memset(temp,0,MAX_DIR_PATH_SIZE);
if(!u_path || !(*u_path))
return NULL;
int index =0 ;
for(i = 0;i <= MAX_DIR_PATH_SIZE ; i++)
{
if(index)
break;
switch((*u_path)[i]) // proximity of the brackets and * is important
{
case '\0':
*done = 1;
index = 1;
memcpy(temp,*u_path,i+1);
printf("Last substring %s\n",temp);
break;
//Search for token ': ascii = 58'
case 0x3A:
index = 1;
memcpy(temp,*u_path,i);
*u_path = *u_path+i+1;
printf("token found : %s\n",temp);
break;
default:
break;
}
}
//handle error for maximum size overlimit
return temp;
}
int main(int argc, char **argv)
{
char *fullPath = getenv( "PATH" );
char *u_path = fullPath;
struct stat buffer;
int exists;
int done = 0;
char* fileOrDirectory = NULL;
while(!done)
{
fileOrDirectory = get_directory(&u_path,&done);
printf("new path is : %s\n",u_path);
if(fileOrDirectory)
{
exists = stat( fileOrDirectory, &buffer );
if ( exists == 0 && ( S_IFDIR & buffer.st_mode ) ) {
printf("directory size %lu\n",buffer.st_size);
}
else {
//do something else
}
free(fileOrDirectory);
}
}
return 0;
}
Here's something to try:
// Variables needed during iteration.
char* start = fullPath;
char sep = ':';
char* iter;
char trialPath[BUFSIZ];
// Get the path
char* originalPath = getenv( "PATH" );
// Make a copy of the path since we are going to modify it
// while we are iterating on it.
char* fullPath = malloc(strlen(originalPath) + 1);
strcpy(fullPath, originalPath);
start = fullPath;
// Iterate over the path.
for ( char* iter = start; *iter != '\0'; ++iter )
{
if ( *iter == sep )
{
*iter = '\0';
// Now, start is a directory.
// Check whether the user command is at this location.
strcpy(trialPath, start);
strcat(trialPath, "/");
strcat(trialPath, usersCommand);
// Now use stat to check whether the file exists and
// it is an executable.
// ....
// If not, reset where start points to.
start = iter + 1;
}
}
// Deallocate memory allocated earliner.
free(fullPath);

Getting folder from a path

Let's say that I have a path as a string (like this one):
/ROOT/DIRNAME/FILE.TXT
How can I get the parent folder of file.txt (DIRNAME in this case)?
For a path that should have at least one directory in it:
char str[1024]; // arbitrary length. just for this example
char *p;
strcpy(str, "/ROOT/DIRNAME/FILE.TXT"); // just get the string from somewhere
p = strrchr(str, '/');
if (p && p != str+1)
{
*p = 0;
p = strrchr(p-1, '/');
if (p)
print("folder : %s\n", p+1); // print folder immediately before the last path element (DIRNAME as requested)
else
printf("folder : %s\n", str); // print from beginning
}
else
printf("not a path with at least one directory in it\n");
Locate last occurrence of / using strrchr. Copy everything from beginning of string to the found location. Here is the code:
char str[] = "/ROOT/DIRNAME/FILE.TXT";
char * ch = strrchr ( str, '/' );
int len = ch - str + 1;
char base[80];
strncpy ( base, str, len );
printf ( "%s\n", base );
Working just with string; no knowledge of symlink or other types assumed.
You can also do it simply using pointers. Just iterate to the end of the path and then backup until you hit a /, replace it with a null-terminating char and then print the string:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[]) {
if (argc < 2 ) {
fprintf (stderr, "Error: insufficient input, usage: %s path\n", argv[0]);
return 1;
}
char *path = strdup (argv[1]);
char *p = path;
while (*p != 0) p++;
while (--p)
if (*p == '/') {
*p = 0;
break;
}
printf ("\n path = %s\n\n", path);
if (path) free (path);
return 0;
}
output:
$ ./bin/spath "/this/is/a/path/to/file.txt"
path = /this/is/a/path/to

Splitting PATH environ variable into individual filepaths

I am trying to do simple shell as an exercise to myself. I am writing a function that should find an executable in the PATH, and return a pointer to a string, that contains the full path to executable. Here is what I have so far;
/*bunch of includes here*/
/*
* Find executable in path, return NULL
* if can't find.
*/
char *find_executable(char *command)
{
const char *PATH = getenv("PATH");
DIR *dp;
/* get each pathname, and try to find executable in there. */
}
int main(int argc,char *argv[])
{ /* nothing intersting here ...*/
}
I was wondering how should I separate each part of the path, and process these parts in a for loop.
say paths would be separated by ;
You can use strtok function to generate splitted token.
e.g.
char *str = "/foo/a1/b1;/bar/a1/b1"
Now you can use strtok function as
char delims[] = ";"
char *result = NULL;
result = strtok( str, delims );
while( result != NULL ) {
dp = result;
result = strtok( NULL, delims );
}

Resources