Obtain the name of the current directory (not the path) - c

I had seen, and I had used a couple of time the function cwd() to get the absolute path of a folder, but there's a question, and that's if it's possible with C to get just the name of a folder.
For example, let's suppose that I execute a program on this folder:
/home/sealcuadrado/work1/include
If I don't give any arguments to my program, I will use cwd() and surely I will get the absolute path to that folder.
But what I want is just the name of the actual folder, in this case include. Can this be done in C (i had seen in in Python and C#)?

Apply the basename()
function to the result of getcwd().
An alternative is to mess around getting the inode number of the current directory (.) and then open and scan the parent directory (..) looking for the name with the corresponding inode number. That gets tricky if the parent directory contains NFS auto-mount points (home directories, for example); you can end up auto-mounting an awful lot of file systems if you aren't careful (which is slow and mostly pointless).

you can parse the result of getcwd()

Maybe not the most elegant way, but this should work by using a combination of strchr() and memmove().
#include <stdio.h>
#include <string.h>
int main() {
char *s;
char buf[] = "/home/folder/include";
s = strrchr (buf, '/');
if (s != NULL) {
memmove(s, s+1, strlen(s+1)+1);
printf ("%s\n", s);
}
return 0;
}
prints include
EDIT: The following code is better and also calls getcwd()
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
char buffer[200];
char *s;
getcwd(buffer,sizeof(buffer));
s = strrchr(buffer, '/');
if (s != NULL) {
printf ("%s\n", s+1);
}
return 0;
}

Related

Create md5sum in C

Im trying to use the md5sum command in a C program, right now im using dirent.h to get all the files in a folder, now, I want to get all the md5 of all those files, I am doing this:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <dirent.h>
int main(void){
char *word = ".gz";
int i=0;
char *word2 = ".";
char *word3 = "..";
unsigned int md5;
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d) {
while ((dir = readdir(d)) != NULL)
{
if((strstr(dir->d_name, word) == NULL) && (strcmp(dir->d_name, word2) != 0) && (strcmp(dir->d_name, word3)!= 0)) {
md5 = system("md5sum dir->d_name");
printf("The md5 of %s is %d\n", dir->d_name, md5);
}
}
}
return(0);
}
but when I run it, it says, for example:
md5sum: dir-: No such file or directory
The md5 of ej1_signal.c is 256
md5sum: dir-: No such file or directory
The md5 of pipeL.c is 256
Could you please explain me why is this happening? Thanks !
The system function doesn't returns you what you think. system is used to launch a command and when that command finished, it (generally) exits with an exit code. This is the value you catched.
What you need is the output of the command not its return value. So what you need is popen which lets you launch some external command and read/write to it through a pipe. See http://pubs.opengroup.org/onlinepubs/009695399/functions/popen.html for example.
system does not return the output of a command. To get the output of a command, you need to create a process and tie the standard output stream to a file descriptor you can read data off in the other process. For an example on how to do that, you can refer to the pipe man page (section 2).
Another option is to use a library that provides an MD5 implementation (eg. OpenSSL). The man page of EVP_DigestInit (section 3) provides an example for that.
Another problem is that your code tries to calculate the digest of d->d_name, not the file which name is in d->d_name. You could use sprintf or strncat with a suitably sized buffer (ie. the length of the static string part md5sum plus the maximum size of the file name (usually 256 bytes, may vary between library implementations and file systems) plus another byte for safely terminating the string (as some implementations may report an unterminated string in d->d_name)). Please note that this does not apply if you use a library for digest calculation, as the library uses either the file name or you need to pass the file contents to a library function (eg. EVP_DigestUpdate).
The first problem is that you launch a new shell process executing "md5sum dir->d_name", meaning it does a md5 on the "file" named dir->d_name, instead of using the value you get from readdir.
So you could add a temp variable, and prepare the command in it prior to running system.
limits.h is for Linux, adjust it if necessary to get the max length of a path
...
#include <linux/limits.h>
char temp[PATH_MAX];
then instead of
md5 = system("md5sum dir->d_name");
add
strcpy(temp, "md5sum ");
strcat(temp, dir->d_name);
system(temp);
as for the other problem (system will not return the md5 string), this will display the md5 of the file in the directory. And you can just remove the printf ...
There is no command in C to return the output of an external command, but there exists popen you can just open a command as a FILE * and read the output from it. This is how you can do it, and it's all explained within the code
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
int main(void)
{
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d == NULL)
return -1;
while ((dir = readdir(d)) != NULL)
{
char command[sizeof dir->d_name + 10];
struct stat st;
FILE *pipe;
if (stat(dir->d_name, &st) == -1)
continue;
/* check if the entry is a directory, md5sum does not work with them */
if (S_ISDIR(st.st_mode) != 0)
continue;
/*
* md5sum dir->d_name will pass `dir->d_name` as the argument to the md5sum command,
* we need to build the command string, I like snprintf in this case
*/
snprintf(command, sizeof command, "md5sum \"%s\"", dir->d_name);
/*
* Open the pipe, it will execute the new command in a new process (fork)
* and create a pipe for communication with the current porcess
*/
pipe = popen(command, "r");
if (pipe != NULL)
{
char md5[33];
/* read the md5 digest string from the command output */
fread(md5, 1, sizeof md5 - 1, pipe);
/* append a null terminator */
md5[sizeof md5 - 1] = '\0';
printf("The md5 of %s is %s\n", dir->d_name, md5);
}
/* close the pipe */
pclose(pipe);
}
/* you should always call closedir() if opendir() succeded */
closedir(d);
return 0;
}

How can I get the application path in C?

I'm using GTK to create an interface for my C program running Linux.
I'm using this function to load my XML interface:
gtk_builder_add_from_file(builder, g_build_filename("myInterface.glade", NULL), &error);
As long as I'm in the same directory as my compiled file, it works.
But if I'm in another directory, let say a child one and I execute it: ../a.out, my program doesn't find the file.
So the idea is to first get the program path ("/home/.../program") and to add the file name to get an absolute path.
But I have no idea how can I get the program path. And I'm not sure this is the best idea to make it work.
Any idea? Thanks!
argv[0] contain the relative/full path you ran to run the program.
just scan up to the last '/' and this will be the run dir from your current location
'edit' after some more research, i found this, which works in all cases:
#include<stdio.h>
#include <unistd.h>
#include <string.h>
#include <libgen.h>
int main()
{
char path[500] = {0};
int dest_len = 500;
if (readlink ("/proc/self/exe", path, dest_len) != -1)
{
dirname (path);
strcat (path, "/");
printf("path=%s\n", path);
}
}
In your case where you are using GTK, it is better to use GResource and compile myInterface.glade directly into your program.

Getting and Setting the path

I've been giving the task of writing a simple shell, I've managed to get some really basic functionality however for one of the stages it's asking me to get and set the environment. This is the details of the task.
Stage 4: Getting and setting the path – 10 marks
Keep original path
The reason this is necessary is because we would like to restore the path to what was originally on exiting the shell. This is important because any changes you do to the shell’s environment (i.e. setenv() function above), does not only affect the shell itself but also any other program that will be executed from the same terminal afterwards. For this reason, it is a good idea to put things back the way they were on exit.
A single string will be enough to keep the original path.
Saving the path should be the first thing your shell does when it starts up.
Print and change the path – built-in commands
From a C program we can access the environment using the getenv() function and we can change the environment using the setenv() function. If you look at the manual pages for setenv(), you will find how it works (i.e. parameters needed and return values) as well as what you need to include to use it.
getpath – print system path & setpath – set system path
These two commands are about the environment parameter PATH. The first just gets and prints its value, while the second takes a path (string of colon separated list of directories) as a parameter and makes it the value of PATH. You can getenv() and setenv() respectively for this purpose.
Restore path
You just change the PATH environment parameter to its original value (i.e. the one you saved at the start up of the shell).
Restoring the path should the last thing your shell does before it exits.
Stage 4: Testing
First, make sure that all the tests you carried out for stage 3 still work. Be careful though, as we are now changing the path this will affect the execution of external programs.
To check the additional functionality, you should start by checking that the save and restore of the path work. A good idea here is to print the path when you save it at the beginning of the execution of the shell and then again when you exit at the end. In both cases the printed path should be exactly the same!
Following that you should check that when getpath is called you print the current path, which should be the same as the original one.
Then you should focus on testing setpath. First, setpath the path to a new value and test that getpath prints it, then try also to see how changing the path really affects the execution of external commands (e.g. set the path to only ‘.’ and try ‘ls’ or try the shell itself, etc).
This is my code:
/* S.NO:201148541 Simple Shell Example
Completed upto Stage 3 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#define BUFFER_SIZE 1<<16
#define ARR_SIZE 1<<16
void parse_args(char *buffer, char** args,
size_t args_size, size_t *nargs)
{
char *buf_args[args_size];
char **cp;
char *wbuf;
size_t i, j;
wbuf=buffer;
buf_args[0]=buffer;
args[0] =buffer;
for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
break;
}
for (j=i=0; buf_args[i]!=NULL; i++){
if(strlen(buf_args[i])>0)
args[j++]=buf_args[i];
}
*nargs=j;
args[j]=NULL;
}
int main(int argc, char *argv[], char *envp[]){
char buffer[BUFFER_SIZE];
char *args[ARR_SIZE];
int *ret_status;
size_t nargs;
pid_t pid;
char curDir[100];
while(1){
getcwd(curDir, 100);
printf("%s->", curDir);
fgets(buffer, BUFFER_SIZE, stdin);
parse_args(buffer, args, ARR_SIZE, &nargs);
if (nargs==0) continue;
if(strcmp(args[0], "cd") == 0){
chdir(args[1]);
}
else if (!strcmp(args[0], "exit" )) exit(0);
pid = fork();
if (pid){
pid = wait(ret_status);
printf("finished\n", pid);
}
else {
if( execvp(args[0], args)) {
puts(strerror(errno));
exit(127);
}
}
}
}
return 0;
}
I'm really at a loss and any guidance would be helpful.
Given that we don't know what your previous steps are, and going by the advice
Then you should focus on testing setpath. First, setpath the path to a
new value and test that getpath prints it, then try also to see how
changing the path really affects the execution of external commands
(e.g. set the path to only ‘.’ and try ‘ls’ or try the shell itself,
etc).
You can do like this...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *path, *old, *tobeSet;
path = malloc(1024);
path = getenv("PATH");
old = malloc(strlen(path));
tobeSet = malloc(10); // just to be safe
strcpy(tobeSet,".");
if(setenv("PATH",tobeSet,1)!=0)
{
printf("Couldn't set path\n");
return 0;
}
printf("\nPATH::\t%s\n",path);
printf("\n\nNewPath::\t%s\n",tobeSet);
if(setenv("PATH",path,1)!=0)
{
printf("Couldn't restore path\n");
return 0;
}
printf("\n\nOld path ::\t%s\n",path);
free(path);
free(old);
free(tobeSet);
return 0;
}

File I/O management in C

My first post :), am starting out with C language as basic learning step into programming arena. I am using following code which reads string from text file, makes directory with that string name and opens a file for writing in that created directory. But am not able to create a file inside directory made, here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <direct.h>
#include <string.h>
int main()
{
char file_name[25], cwd[100];
FILE *fp, *op;
fp = fopen("myfile.txt", "r");
if (fp == NULL)
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
fgets(file_name, 25, fp);
_mkdir(file_name);
if (_getcwd(cwd,sizeof(cwd)) != 0)
{
fprintf(stdout, "Your dir name: %s\\%s\n", cwd,file_name);
op = fopen("cwd\\file_name\\mynewfile.txt","w");
fclose(op);
}
fclose(fp);
return 0;
}
What you need is to store the file name (with the path) in a c-string before opening. What you are opening is cwd\file_name\mynewfile.txt. I doubt that your directory is named cwd.
A sample could could be:
char file_path[150];
sprintf(file_path, "%s\\%s\\mynewfile.txt", cwd, file_name);
op = fopen(file_path,"w");
use
#include <sys/stat.h>
#include <sys/types.h>
instead of
#include <direct.h>
and modify
op = fopen("cwd\\file_name\\mynewfile.txt","w”);
I see you are using the return values. That is a good start for a beginner. You can refine your error messages by including "errno.h". Instead of printing your own error messages call
printf("%s", strerror(errno));
You get more precise error messages that way.
op = fopen("cwd\\file_name\\mynewfile.txt","w”);
You’re actually passing the string literals “cwd” and “file_name” as part of the path of the file, when I think you actually mean to put the contents of the variables with those names in there. You will probably have to piece together a string for the path. Try looking into strcat()
http://www.cplusplus.com/reference/cstring/strcat/

C Test For File Existence Before Calling execvp

I'm writing a UNIX minishell on ubuntu, and am trying to add built-in commands at this point. When it's not a built-in command I fork and then the child executes it, however for built-in commands I'll just execute it in the current process.
So, I need a way to see if the files exist(if they do it's not a built-in command), however execvp uses the environment PATH variable to automatically look for them, so I have no idea how I would manually check beforehand.
So, do you guys know how I could test an argument to see if it's a built-in command simply by supplying the name?
Thanks guys.
I have tested the answer by Tom
It contained a number of problems. I have fixed them here and provided a test program.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
int is_file(const char* path) {
struct stat buf;
stat(path, &buf);
return S_ISREG(buf.st_mode);
}
/*
* returns non-zero if the file is a file in the system path, and executable
*/
int is_executable_in_path(char *name)
{
char *path = getenv("PATH");
char *item = NULL;
int found = 0;
if (!path)
return 0;
path = strdup(path);
char real_path[4096]; // or PATH_MAX or something smarter
for (item = strtok(path, ":"); (!found) && item; item = strtok(NULL, ":"))
{
sprintf(real_path, "%s/%s", item, name);
// printf("Testing %s\n", real_path);
if ( is_file(real_path) && !(
access(real_path, F_OK)
|| access(real_path, X_OK))) // check if the file exists and is executable
{
found = 1;
}
}
free(path);
return found;
}
int main()
{
if (is_executable_in_path("."))
puts(". is executable");
if (is_executable_in_path("echo"))
puts("echo is executable");
}
Notes
the test for access return value was reversed
the second strtok call had the wrong delimiter
strtok changed the path argument. My sample uses a copy
there was nothing to guarantee a proper path separator char in the concatenated real_path
there was no check whether the matched file was actually a file (directories can be 'executable' too). This leads to strange things like . being recognized as an external binary
What you can do is you can change the path to the particular directory and then use #include<dirent.h> header file and its readdir and scandir functions to walk through the directory or stat structure to see if the file exists in the directory or not.
You can iterate yourself through the PATH directories, and for each entry in PATH (You will have to split PATH with :, probably using strtok) concatenate at the end of each path the name of the command called. When you have create this path, check if the file exists and if it is executable using access.
int is_built_in(char *path, char *name)
{
char *item = strtok(path, ":");
do {
char real_path[4096] = strcat(item, name); // you would normally alloc exactly the size needed but lets stick to it for the sake of the example
if (!access(real_path, F_OK) && !access(real_path, X_OK)) // check if the file exists and is executable
return 0;
} while ((item = strtok(NULL, ":")) != NULL);
return 1;
}
Why do you want to test before calling execvp? That's the wrong approach. Just call execvp and it will tell you if the program does not exist.

Resources