Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I'm working on a c programming mimicking UNIX shell and I wonder if there's a way to find an executable file(command file) in a specific directory
By executable file, I am assuming you are looking for any file that is not a directory that has the execution flag set.
One way you could go about achieving your goal is to:
Get a directory stream corresponding to the directory by using the opendir.
Iterate through the directory stream using readdir .
At each iteration, find the absolute path of the file, which can be computed by concatenating the directory path and file name (can be obtained by accessing the d_name of the dirent struct), pass this path to stat, and use bit mask specified in inode documentation to check for the execution permission.
Code:
#include <stdio.h>
#include <sys/types.h> //opendir, stat
#include <dirent.h> //opendir
#include <errno.h>
#include <string.h>
#include <sys/stat.h> //stat
#include <stdlib.h> //free, malloc
int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "Usage: ./a.out path_to_a_directory \n");
return 1;
}
char* path_to_directory = argv[1];
int path_length = strlen(path_to_directory);
int modified = 0;
//Modify path so that a dash is at the end.
if (path_to_directory[path_length - 1] != '/') {
char* modified_path = (char *) malloc(sizeof(char) * (strlen(path_to_directory) + 2));
//Copies the null character as well.
strcpy(modified_path, path_to_directory);
modified_path[path_length] = '/';
modified_path[path_length + 1] = '\0';
path_to_directory = modified_path;
//Set flag to true so that the dynamically allocated memory is freed later.
modified = 1;
}
//Get the directory stream corresponding to the directory.
DIR* in_directory_stream = opendir(path_to_directory);
if (in_directory_stream == NULL) {
fprintf(stderr, "Error: the specified directory cannot be found or opened. \n", errno);
if (modified) free(path_to_directory);
return 1;
}
dirent* entry = NULL;
printf("Files that are executable by at least one of the permission classes (owner, group, others) are: \n");
while ((entry = readdir(in_directory_stream)) != NULL) {
//All directories contain . and .., which corresponds to current and parent directory respectively,
//in unix systems. Since we are looking for only executable files, we can ignore them.
if (!strcmp(".", entry->d_name)) {
continue;
}
if (!strcmp("..", entry->d_name)) {
continue;
}
//Get file information.
struct stat entry_info;
/* Create the absolute path of the entry.
* Without it, as mentioned by Shawn below,
* stat will look for a file with the entry's name in current working directory
* instead of the specified directory.
*/
char* entry_absolute_path = (char *) malloc(sizeof(char) * (strlen(path_to_directory)
+ strlen(entry->d_name) + 1));
strcat(entry_absolute_path, path_to_directory);
strcat(entry_absolute_path, entry->d_name);
if (stat(entry_absolute_path, &entry_info) == -1) {
fprintf(stderr, "Error in obtaining file information about %s\n", entry->d_name);
} else {
// Check if the file is not a directory and
// is executable by one of the permission classes (owner, group, others).
if (((entry_info.st_mode & S_IFMT) != S_IFDIR) &&
((entry_info.st_mode & S_IXUSR) || (entry_info.st_mode & S_IXGRP)
|| (entry_info.st_mode & S_IXOTH))) {
printf("%s\n", entry->d_name);
}
}
free(entry_absolute_path);
}
//Close directory stream.
closedir(in_directory_stream);
if (modified) free(path_to_directory);
return 0;
}
EDIT: Corrected the program to pass the absolute path of the file to stat. Prior to this edit, the stat was given only the file name resulting in the program searching only the current working directory. Error identified by Shawn
Related
I want to write my own code for move(mv) Unix command. I am completely new to C language and apparently lost on how to fix my code. I want to perform actions like renaming a file if both the inputs are file names. If the the dest_folder is a directory I would like to move the file into the directory.
But I am unable to fix code for the particular problem as I am not much familiar with directories and C in particular. The program takes 2 inputs source and destination after which it performs necessary functions. I am apparently able to rename my files but I am unable to move the file to a particular folder for some reason I don't know?
Need help with moving file to a particular directory.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define SBUF 256
#define DBUF 256
int main(int ac, char *argv[])
{
DIR* dir_ptr; // the directory
struct dirent* direntp;
if( ac == 1 )
{
printf("Usage: %s MOVE\n", argv[0] );
exit(0);
}
if(ac>1 && ac<3)
{
printf("Error! few arguments provided " );
exit(0);
}
char src_folder[SBUF];
char dest_folder[DBUF];
strcpy(src_folder, argv[1]);
strcpy(dest_folder, argv[2]);
dir_ptr = opendir("."); //open directory
if ( dir_ptr == NULL )
{
perror( "." );
exit( 1 );
}
while( (direntp = readdir( dir_ptr )) != NULL )
{
if ( strcmp(direntp->d_name, dest_folder) !=0) //search file or directory
{
printf("found the file %s", dest_folder);
break;
}else
printf("not found");
break;
}
rename(src_folder, dest_folder);
closedir( dir_ptr );
return 0;
}
rename(3) does not work the way you want it to work (I don't know why, ask the committee). You cannot do a rename(some_file, some_directory), just as the man-page says.
Just use stat(2) (or lstat(2) if necessary) and check what you have been given. Here is a short, runnable sketch.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
// check if it is the same inode on the same device
#define SAME_INODE(a, b) ((a).st_ino == (b).st_ino && (a).st_dev == (b).st_dev)
// ALL CHECKS OMMITTED!
int main(int argc, char **argv)
{
struct stat statbuf_src, statbuf_dest;
char *src, *dest, *new_src, *new_dest;
char *current_directory;
if (argc != 3) {
fprintf(stderr, "usage: %s src dest\n", argv[0]);
exit(EXIT_FAILURE);
}
// work on copy
src = malloc(strlen(argv[1]) + 1);
dest = malloc(strlen(argv[2]) + 1);
strcpy(src, argv[1]);
strcpy(dest, argv[2]);
stat(src, &statbuf_src);
stat(dest, &statbuf_dest);
// there are many more, of course
printf("\"%s\" is a ", src);
if (S_ISREG(statbuf_src.st_mode)) {
puts("a regular file");
}
if (S_ISDIR(statbuf_src.st_mode)) {
puts("a directory");
}
printf("\"%s\" is a ", dest);
if (S_ISREG(statbuf_dest.st_mode)) {
puts("a regular file");
}
if (S_ISDIR(statbuf_dest.st_mode)) {
puts("a directory");
}
if (SAME_INODE(statbuf_dest, statbuf_src)) {
printf("%s and %s are the identical\n", src, dest);
}
// if that is not set you have to do it by hand:
// climb up the tree, concatenating names until the inodes are the same
current_directory = getenv("PWD");
printf("current directory is \"%s\"\n", current_directory);
// I'm pretty sure it can be done in a much more elegant way
new_src = malloc(strlen(src) + 1 + strlen(current_directory) + 1);
strcpy(new_src,current_directory);
strcat(new_src,"/");
strcat(new_src,src);
printf("new_src = %s\n",new_src);
new_dest = malloc(strlen(dest) + 1 + strlen(current_directory) + 1 + strlen(src) + 1);
strcpy(new_dest,current_directory);
strcat(new_dest,"/");
strcat(new_dest,dest);
strcat(new_dest,"/");
strcat(new_dest,src);
printf("new_dest = %s\n",new_dest);
if(rename(new_src,new_dest) != 0){
fprintf(stderr,"rename failed with error %s\n",strerror(errno));
}
free(new_src);
free(new_dest);
free(src);
free(dest);
exit(EXIT_SUCCESS);
}
Edit: added code for the desciption below
At the end you have a the path where you are, the information if the arguments given are directories or regular files and the path. If the source is a regular file and the destination a directory, you concatenate the path with the name of the regular file, the path with the name of the directory and the name of the regular file (your source)
Out of
Path = /home/foo
src = bar
dest = coffee
build
new_src = /home/foo/bar
new_dest = /home/foo/coffee/bar
Such that the call to rename() is
rename(new_src, new_dest);
That way you rename a regular file to a regular file which rename() accepts.
Please be aware that rename() does not work across every filesystem, but most.
Like you know, mv is implemented by rename. rename is a atomic system call that can rename a file to a file , an emtpy directory to an empty directory or a directory to a directory(the dest must be nonentity). So there are following situation to deal with:
mv file1 file2 - use rename function
mv dir1 dir2(nonentity or empty) - use rename function
mv dir1 dir2(not empty) - rename dir1 to dir2/dir1
mv file dir(exist) - rename file to dir/file
mv dir file - illegal operation
can you understand?
This question already has answers here:
stat() error 'No such file or directory' when file name is returned by readdir()
(2 answers)
Closed 2 years ago.
I wrote a program that print the directory name or file name. It's easy but I got something trouble.
It couldn't distinguish directory and file type. I know and I used stat.st_mode to finish it. But something is wrong:
When I use gdb to check the st_mode value, I found it was 0, except "." and "..", so here is the question: why st_mode is 0?
and that is my code:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
int main(void)
{
DIR *pDir = opendir("MyDirectory");
struct dirent *pDirent;
struct stat vStat;
if (pDir == NULL)
{
printf("Can't open the directory \"MyDirectory\"");
exit(1);
}
while ((pDirent = readdir(pDir)) != NULL)
{
stat(pDirent->d_name, &vStat);
if (S_ISDIR(vStat.st_mode))
printf("Directory: %s\n", pDirent->d_name);
else
printf("File: %s\n", pDirent->d_name);
}
closedir(pDir);
return 0;
}
Classic readdir mistake: pDirent->d_name is the name of the directory entry, not a path to the file. It's "1", "4-5.c", etc. So your stat calls are looking for a file with that name in the current directory, not under MyDirectory.
Check the return value of stat. You'll see that it's ENOENT — except for . and .., which exist in the current directory as well. When stat fails, the content of the stat structure is undefined.
If you're calling opendir in a directory other than ., then to do pretty much anything useful with the returned names, you need to build a full path. Copy the path you passed to opendir to a buffer with enough room for a slash and file name in addition, and copy each file name to that buffer. Proof-of-concept code (error checking omitted, etc.):
char *directory = "MyDirectory";
size_t directory_length = strlen(directory);
char *path = malloc(directory_length + 1 + NAME_MAX);
strcpy(path, directory);
path[directory_length] = '/';
while ((pDirent = readdir(pDir)) != NULL) {
strcpy(path + directory_length + 1, pDirent->d_name);
if (stat(path, &vStat) == -1) {
perror(path);
continue;
}
…
}
I'm trying to search for a file in a directory which are both specififed by the user on the command line when executing my program. It should look into the directory specified, and also check in sub-directories within that directory and recursively search for the file.
I have print statements in here trying to analyze the variables being passed around and how they're changing. Within my while loop, it's never reaching the checks for if it's a file or just the else statement saying it wasn't found. The check for if it's a directory is true every time, which is obviously not the case.
Thank you for any help. I'm not very familiar/comfortable with dirent and stat so I've been trying to review and make sure I'm using them correctly in the meantime.
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
void traverse(char *dir, char *file) {
DIR *directory;
struct dirent *structure;
struct stat info;
printf("Current directory to search through is: %s\n", dir);
printf("Current file to search for is: %s\n", file);
printf("\n");
printf("\n");
// make sure the directory can be opened
if((directory = opendir(dir)) == NULL) {
fprintf(stderr, "The directory could not be opened. %s\n", strerror(errno));
return;
}
chdir(dir); // change to the directory
while((structure = readdir(directory)) != NULL) { // loop through it
fprintf(stderr, "before the change it is: %s\n", dir);
lstat(structure->d_name, &info); // get the name of the next item
if(S_ISDIR(info.st_mode)) { // is it a directory?
printf("checking if it's a directory\n");
if(strcmp(".", structure->d_name) == 0 ||
strcmp("..", structure->d_name) == 0)
continue; // ignore the . and .. directories
dir = structure->d_name;
fprintf(stderr, "after the change it is: %s\n", dir);
printf("About to recurse...\n");
printf("\n");
traverse(structure->d_name, file); // recursively traverse through that directory as well
}
else if(S_ISREG(info.st_mode)) { // is it a file?
printf("checking if it's a file\n");
if(strcmp(file, structure->d_name) == 0) { // is it what they're searching for?
printf("The file was found.\n");
}
}
else {
printf("The file was nout found.\n");
}
}
closedir(directory);
}
int main(int argc, char *argv[]) {
// make sure they entered enough arguments
if (argc < 3) {
fprintf(stderr, "You didn't enter enough arguments on the command line!\n");
return 3;
}
traverse(argv[2], argv[1]);
}
There is a POSIX function for tree walking like this. It's called nftw().
It provides a callback mechanism and it also detects links caused by badly constructed symbolic link chaining.
So I'd recommend you use that rather than the way you're doing it.
As usual man nftw will explain it's operation in detail. The standard Linux/Unix include file is ftw.h.
Note their was a function called ftw() which is apparently obsolete now.
As Andrew Medico noted: You chdir down into directories but never go back up. Thus, just insert
chdir(".."); // change back to upper directory
between the end of the while loop and the end of the traverse() function.
This question already has answers here:
stat() error 'No such file or directory' when file name is returned by readdir()
(2 answers)
Closed 2 years ago.
I wrote a program that print the directory name or file name. It's easy but I got something trouble.
It couldn't distinguish directory and file type. I know and I used stat.st_mode to finish it. But something is wrong:
When I use gdb to check the st_mode value, I found it was 0, except "." and "..", so here is the question: why st_mode is 0?
and that is my code:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
int main(void)
{
DIR *pDir = opendir("MyDirectory");
struct dirent *pDirent;
struct stat vStat;
if (pDir == NULL)
{
printf("Can't open the directory \"MyDirectory\"");
exit(1);
}
while ((pDirent = readdir(pDir)) != NULL)
{
stat(pDirent->d_name, &vStat);
if (S_ISDIR(vStat.st_mode))
printf("Directory: %s\n", pDirent->d_name);
else
printf("File: %s\n", pDirent->d_name);
}
closedir(pDir);
return 0;
}
Classic readdir mistake: pDirent->d_name is the name of the directory entry, not a path to the file. It's "1", "4-5.c", etc. So your stat calls are looking for a file with that name in the current directory, not under MyDirectory.
Check the return value of stat. You'll see that it's ENOENT — except for . and .., which exist in the current directory as well. When stat fails, the content of the stat structure is undefined.
If you're calling opendir in a directory other than ., then to do pretty much anything useful with the returned names, you need to build a full path. Copy the path you passed to opendir to a buffer with enough room for a slash and file name in addition, and copy each file name to that buffer. Proof-of-concept code (error checking omitted, etc.):
char *directory = "MyDirectory";
size_t directory_length = strlen(directory);
char *path = malloc(directory_length + 1 + NAME_MAX);
strcpy(path, directory);
path[directory_length] = '/';
while ((pDirent = readdir(pDir)) != NULL) {
strcpy(path + directory_length + 1, pDirent->d_name);
if (stat(path, &vStat) == -1) {
perror(path);
continue;
}
…
}
My program reads the content of a directory specified at command line. It reads the directory recursively i.e. if we are reading contents of directory "test" and inside it we have another directory "inside", then it will also read the content of the directory named "inside". The issue is it works fine if i do not read hidden directories i.e. directories that start with "." . But in case i read hidden directories too it says Segmentation fault.
The code is as below:
The main file:
#include "helper.h"
/*
* Display's content of String array passed to it,
* that should conatin full path to files.
*/
void display(char **);
/*
* Free's the memory utilized by the program
*/
void cleanup(char **);
int main(int argc, char *argv[])
{
// ensure proper usage
if (argc != 2)
{
printf("Usage: %s [dir]\n\n", argv[0]);
return 1;
}
char **files = calloc(1, sizeof(char *));
// get files from the directory specified
getFiles(&files, argv[1]);
// display files
display(files);
// free memory utilized by files array
cleanup(files);
// that's all folks
return 0;
}
/*
* Display's content of String array passed to it,
* that should conatin full path to files.
*/
void display(char **files)
{
// Color Red
// printf("[0;31;40m");
// display files
for (int i = 0; files[i]; i++)
{
printf("%s\n", files[i]);
}
// turn off color
// printf("[0;37;40m");
}
/*
* Free's the memory utilized by the program
*/
void cleanup(char **files)
{
// free memory utilized by files array
for (int i = 0; files[i]; i++)
free(files[i]);
free(files);
}
The getFiles function is defined in helpers.c file which contain the following code as below:
#include "helper.h"
#include <dirent.h>
#include <stdlib.h>
#include <sys/types.h>
/*
* Stores the list of files present in direectory pointed by 'dir'
* in array of strings pointed by 'files'
*/
void getFiles(char ***files, const char* dir)
{
static int i;
// ensure directory is valid
if (dir == NULL)
{
printf("Error: Invalid Directory\n\n");
exit(1);
}
// declare and initialize directory handler
DIR *dd = opendir(dir);
if (dd == NULL)
{
printf("Error: Directory Not Found\n\n");
exit(2);
}
// structure that store file attributes read
struct dirent *content;
// read directory until all files are scanned
while ((content = readdir(dd)) != NULL)
{
// ignore '.' and '..' directories
if (strcmp(content->d_name, ".") == 0 ||
strcmp(content->d_name, "..") == 0)
continue;
/*if (content->d_name[0] == '.')
continue;*/
//store full file path from current directory
char temp[1024] = {0};
// make full path
makepath(temp, dir, content->d_name);
// recall itself if another directory found
if (isdir(temp))
{
// read this new directory found
getFiles(files, temp);
continue;
}
// allocate memory to store locations of char *
*files = realloc(*files, (i + 2)*(sizeof(char *)));
// allocate heap memory and store location
*(*(files + 0) + i) = (char *)strdup(temp);
// move to next location
i++;
}
// free directory handler
closedir(dd);
// set NULL after last file name
*(*(files + 0) + i) = '\0';
}
/*
* returns true if 'dir' refers to a directory, false otherwise
*/
bool isdir(const char * dir)
{
DIR *temp;
temp = opendir(dir);
if (temp != NULL)
{
closedir(temp);
return true;
}
return false;
}
/*
* appends dir and file/directory name to src,
* thus makes a full file/directory path, from current directory
*/
void makepath(char src[], const char *dir, const char *file)
{
// prepend directory name
strcat(src, dir);
strcat(src, "/");
// append file/directory name
strcat(src, file);
}
Necessary header files are included by me in helper.h file.
Also i wanted to know am i making mistake in memory allocation. (In realloc in getFiles Function).
Ignore hidden files line is commented by me at this time.
/*if (content->d_name[0] == '.')
continue;*/
If i uncomment the above line then program works fine.
If you are thinking why i am storing file names as read by readdir function, because those names are necessary for me later so that is why i am not right away displaying the file names.
Any suggestion how i can better implement this program and also how to fix the issue that occurs when i read hidden directories.
I don't know if that's the problem but here:
// set NULL after last file name
*(*(files + 0) + i) == '\0';
^ you are not setting to NULL, you are comparing