Recursively searching directories for a file - c

I'm trying to recursively search a directory for a file and output the file if it matches the given:
static int *search(char *path, const char *request, int depth, bool verbose)
{
DIR *dir;
struct dirent *ent;
char *start_dir = strcmp(path, "/") == 0 ? "root" : path;
printf("\nStarting in '%s' directory..\n\n", start_dir);
if ((dir = opendir(path)) != NULL)
{
while ((ent = readdir(dir)) != NULL)
{
if (verbose == true)
{
printf("Searching directory %s for file %s\n", ent->d_name, request);
}
if (ent->d_type == DT_DIR)
{
if ((strlen(path) + strlen(ent->d_name) + 1) > PATH_MAX)
{
puts("Path to long, cannot continue..");
}
else
{
if (ent->d_name == DT_DIR && strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0 )
{
printf("%s\n", ent->d_name);
}
}
}
}
}
return 0;
}
This will work but it will not output the files or directories inside of the directory. Example:
#ubuntu:~/bin/c/find-files$ ./utilis test / -V
Initialized to search for file: 'test'..
Starting in 'root' directory..
Searching directory vmlinuz.old for file test
Searching directory boot for file test
Searching directory home for file test
Searching directory libx32 for file test
Searching directory lib32 for file test
Searching directory lib64 for file test
Searching directory initrd.img for file test
Searching directory srv for file test
Searching directory usr for file test
Searching directory . for file test
Searching directory cdrom for file test
Searching directory tmp for file test
Searching directory initrd.img.old for file test
Searching directory bin for file test
Searching directory .. for file test
Searching directory proc for file test
Searching directory lib for file test
Searching directory var for file test
Searching directory dev for file test
Searching directory sys for file test
Searching directory media for file test
Searching directory root for file test
Searching directory snap for file test
Searching directory run for file test
Searching directory sbin for file test
Searching directory opt for file test
Searching directory lost+found for file test
Searching directory mnt for file test
Searching directory vmlinuz for file test
Searching directory etc for file test
How can I refactor this function in order to recursively search for the given filename in all directories and sub directories?

As already point out in the comments by many. Your code is not recursive with the missing recursive call (function calling itself). You have a lot of mistake. Not checking for errors with various system calls and forgetting to close the directory stream. I don't know what you want to achieve with your various parameters int depth, bool verbose. But this is another way of looking for a specific file and returning 0 --> EXIT_SUCCESS on success and 1 -->EXIT_FAILURE on failure.
The are various ways things can go wrong when using system calls and some them set errnos, which is important to look at them and print to STDERR
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#define DEBUG 1
static int search(char *path,const char *file){
DIR *dir;
char *slash = "/";
int ret = 1;
struct dirent *entry;
//checking if it failed to open and report errors to STDERR
if((dir = opendir(path)) == NULL){
fprintf(stderr,"opendir: %s\n",strerror(errno));
return EXIT_FAILURE;
}
while ((entry = readdir(dir))){
//if is . or .. we continue to prevent winging back and forth
if(strcmp(entry->d_name,".") == 0 || strcmp(entry->d_name,"..") == 0)
continue;
//we check if the path has already a / if not we add one
int length = strlen(path);
if(path[length-1] != '/'){
slash = "/";
}
length += strlen(entry->d_name)+2;
char *newpath = malloc(length);
if (!newpath){
fprintf(stderr,"malloc: %s\n",strerror(errno));
break;
}
snprintf(newpath,length,"%s%s%s",path,slash,entry->d_name);
if(strcmp(entry->d_name,file) ==0){
#if DEBUG
printf("Was found here %s\n",newpath);
#endif
ret = EXIT_SUCCESS;
break;
}
//checking if is a directory to do a recursive call
// using DT_DIR to avoid the use of lstat or stat
// if not directory we free the memory and move on
if(entry->d_type == DT_DIR)
search(newpath,file);
else{
free(newpath);
continue;
}
free(newpath);
}
if(closedir(dir) != 0){
fprintf(stderr,"closedir: %s\n",strerror(errno));
return EXIT_FAILURE;
}
return ret;
}
int main() {
char *file = "algo.c";
int ret = search("/",file);
printf("%d The file %s was %s\n",ret,file,(ret == 0 ? "found":"not found"));
return 0;
}
With DEBUG set to 1
Was found here /Users/addodennis/CLionProjects/BluePrint/algo.c
The file algo.c was found
With DEBUG set to 0
file algo.c was found

Related

C program for creating folders based on file extensions and copying files with the extensions into them

I am trying to create a c program that reads the extensions of the files in its current working directory.
The program then creates folders that correspond to the extensions of the files. It then copies each file
from its cwd into the corresponding folder.
e.g:
hello.txt into created .txt folder
The code successfully creates folders for all the file extensions in the current directory, but crashes when it begins to copy.
Here is the whole code.
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <dir.h>
#include <stdlib.h>
#include <errno.h>
int main(void) {
FILE *input, *output; // Two files, input and output
char ch; // ch is used to assign characters from input file which will then be copied into the output file
char *exe = ".exe";
struct dirent *de;
DIR *dr = opendir("."); // Open directory for reading
printf("%s", dr->dd_name);
// If directory doesn't exist, quit
if(dr == NULL) {
printf("Can't open current directory.");
return 0;
}
// Loop first to create a directory corresponding to all
// extensions present in the current directory
while((de = readdir(dr)) != NULL) {
char *filename = de->d_name; // Get the filename
char *ext = strrchr(filename, '.'); // Get the extension
if(!(!ext || ext == filename)){ // Compare extension
int check = mkdir(ext);
if(!check)//Check if the directory was created
printf("Directory created successfully.\n");
else {
printf("Unable to create directory.\n");
}
}
}
// Close the directory so as to reset the pointer ready for the next read.
closedir(dr);
dr = opendir(".");
// Loop reading each file and checking which
// directory it corresponds to.
while((de = readdir(dr)) != NULL) {
char *filename = de->d_name; // Get the filename
char *ext = strrchr(filename, '.'); // Get the extension
if(!(!ext || ext == filename)){ // Check for a valid extension
DIR *file_ext_dir = opendir(ext); // Open the dir that corresponds to the extension of the file.
char *dir_name = file_ext_dir->dd_name; //Get the dir name of the opened directory
if(file_ext_dir && (strcmp(dir_name, "..") != 0)&& (strcmp(ext, exe) !=0) ) { //ignore .exe and the cwd
printf("Successfully opened: %s dir\n", file_ext_dir->dd_name);
char *output_path = strcat(dir_name, filename);
printf("Ready to copy files from %s to: %s\n", filename, output_path);
output = fopen(output_path, "a+"); // Open output.txt for appending, if doesn't exist, create it.
input = fopen(filename, "r"); // Open the input file ()'filename') for reading
while(1) { // Loop through the input file
ch = fgetc(input); // Get the current character
if(ch == EOF) break; // Stop if EOF is found
putc(ch, output); // Put current character from the input file into output.txt
}
fclose(input); // Close input file
fclose(output); // Close output file
closedir(file_ext_dir);
} else if(ENOENT == errno){ //Check if there is no such directory and handle the error
printf("Dir does not exist.");
}else {
continue; //Skip that file if for some reason the directory cannot be opened.
}
}
}
closedir(dr); // Close directory
printf("Created directories and copied all files to that correspond to them.");
return 0;
}

Show the names of executable files by pressing TAB Linux C program

I have to do a program, which in the activation of the TAB by the user searches for all executable files in the current directory and consecutive directories (if the current one contains directories).
The executable files are stored in a dynamic matrix to be later compared to the user input
My problem is that the search starts in the PATH, and for some reason whenever it tries to open the directory it gives me and error saying "No such file or directory".
#include "header.h"
#include <dirent.h>
char **files = NULL;
char size = 0;
char heapsize = 0;
char **tabActivation(const char *text, int start, int end);
char *filesCatch(const char *text, int state);
int main(int argc, const char *argv[])
{
char *line;
rl_attempted_completion_function = tabActivation;
while (1)
{
line = readline("msh$ ");
if (line == NULL)
{
perror("malloc error!\n");
exit(1);
}
if (!strcmp(line, "exit")) exit(0);
if (strlen(line) == 0)continue;
add_history(line);
CMD *root = parse_line(line);
print_command_list(root);
free_command_list(root);
free(line);
}
}
char **tabActivation(const char *text, int start, int end)
{
rl_attempted_completion_over = 1;
return rl_completion_matches(text, filesCatch);
}
char *filesCatch(const char *text, int state)
{
char pwd[1024] = "", *p=NULL, *limit="/:",temp[1024]="";
char oldpwd[1024] = "";
files = (char **) malloc(10 * sizeof(char *));
heapsize = 10;
strcpy(pwd, getenv("PATH"));
strcpy(oldpwd,pwd);
DIR *dir;
struct dirent *entry;
p = strtok(pwd,limit);
strcat(temp,"/");
strcat(temp,p);
while(p!=NULL)
{
printf("CURRENT DIRECTORY= %s\n",temp);
if ((dir = opendir(temp)) == NULL)
perror("opendir() error");
else
{
puts("contents of root:");
while ((entry = readdir(dir)) != NULL)
printf(" %s\n", entry->d_name);
closedir(dir);
}
p = strtok(NULL,limit);
strcat(temp,"/");
strcat(temp,p);
printf("\n");
printf("\n");
}
return NULL;
}
pwd(PATH) output: /home/user/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
Program Output:
CURRENT DIRECTORY= /home
Contents of DIRECTORY:
.
..
user
CURRENT DIRECTORY= /home/user
Contents of DIRECTORY:
.icons
.dmrc
worksheet8
.thumbnails
Templates
.bashrc
file
.calendar
.themes
up-down-mutex.c
Pictures
ls2.txt
main.c
exerc1
teste.txt
.
Public
Downloads
7-sigchld.c
worksheet7
project
tmp
Music
run
.Xresources
.vboxclient-draganddrop.pid
worksheet6
.bash_logout
.dbus
Documents
bin
teste
.vboxclient-clipboard.pid
.vboxclient-seamless.pid
.bash_aliases
.config
.xsessionrc
worksheet3
worksheet5
.pbuilderrc
.gtkrc-2.0.mine
.profile
.profile~2018-02-19T13:24:58~
.Xauthority
teste.c
ls.txt
.conkyrc
.gconf
.gtk-bookmarks
.bash_history
.xsession-errors
.gtkrc-2.0
worksheet1
.vboxclient-display.pid
.local
worksheet4
.lesshst
Videos
.cache
.fonts
.gmrunrc
..
project.zip
.xsession-errors.old
worksheet2
.mozilla
.gksu.lock
CURRENT DIRECTORY= /home/user/bin
Contents of DIRECTORY:
.
..
CURRENT DIRECTORY= /home/user/bin/usr
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr/local
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr/local/bin
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr/local/bin/usr
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr/local/bin/usr/bin
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr/local/bin/usr/bin/bin
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr/local/bin/usr/bin/bin/usr
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr/local/bin/usr/bin/bin/usr/local
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr/local/bin/usr/bin/bin/usr/local/games
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr/local/bin/usr/bin/bin/usr/local/games/usr
opendir() error: No such file or directory
CURRENT DIRECTORY= /home/user/bin/usr/sbin/sbin/usr/local/bin/usr/bin/bin/usr/local/games/usr/games
opendir() error: No such file or directory
Segmentation fault
PATH contains multiple directories. Make sure you split the variable into multiple paths, and search each one separately.
Edit: Specifically, you could use the strtok() function, which splits the string at a specified character (The ":" in this case). You could use the function to split the PATH into separate directories.
Edit 2: Make sure to clear out the content of "temp" after every ":", as the program is currently merging the different directories in PATH, which shouldn't be merged.
Think delimiter "/:" you chose wasn't token'izing the PATH variable as expected. I changed it to ":" and I am getting every entry in path properly delimited:
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
int main (int argc, char **argv)
{
char pwd[4096]="", *limit=":";
char *p = NULL;
strcpy (pwd, getenv("PATH"));
p = strtok (pwd, limit);
printf ("%s\n", p);
while (p != NULL){
p = strtok(NULL,limit);
printf ("%s\n", p);
}
return (0);
}
Output: /usr/local/sbin /usr/local/bin /usr/sbin ...
Not sure if that's what you want.

implementation of ls -l command in C [duplicate]

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;
}
…
}

writing ls from scratch recursively

I am working on a simple project to implement "ls -R" from scratch. Whenever I run what I have, my program just keeps searching the root directory over and over again. What am I doing wrong?
void lsR(char dirName[]) {
/*
The recursive function call.
*/
DIR *dir;
struct dirent *directory;
struct stat fileStat;
char type;
char **nameList[MAX_RECURSIVE_FILES];
struct passwd *user;
int count = 0;
int i = 0;
printf("\n");
printf("./%s :\n", dirName);
printf("\n");
if ((dir = opendir(dirName)) == NULL) {
perror("opendir error:");
return;
}
while ((directory = readdir(dir)) != NULL) {
if (stat(directory->d_name, &fileStat) < 0) {
perror("fstat error:");
return;
}
if (fileStat.st_uid == 1) {
continue;
}
user = getpwuid(fileStat.st_uid);
printf("%s ", directory->d_name);
fileType(&fileStat, &type);
if ((type == 'd') && (count < MAX_RECURSIVE_FILES)) {
nameList[count] = malloc(sizeof(char)*MAX_STRING_LENGTH);
strncpy(nameList[count++], directory->d_name, MAX_STRING_LENGTH);
}
}
closedir(dir);
printf("\n");
for (i=0; i<count; i++) {
printf("Calling lsR on: %s\n", nameList[i]);
lsR(nameList[i]);
}
}
When it executes, I get the following output:
"./. :
., .., ... all other files in my current working directory ....
./. :
., .., ... all other files in my current working directory...
"
Among the list of files in the current directory you've noticed . and .. The first one is a hardlink to the current directory and the second one to the parent directory. So when you recurse through your dir entries you will want to skip those two. Otherwise the first directory you will recurse into will be ., in other words the directory you've just gone through.
This is the reason of your program current behavior, but once you fix that you will run into the issue lurker mentioned in his answer.
Additional notes :
Are you sure about the char **nameList[MAX_RECURSIVE_FILES]; variable? Seems to me you want an array of char * not an array of char **.
Are you aware you can use the S_ISDIR macro on the st_mode field of your stat struct, in order to check that the current file is not a directory instead of your custom function?
You need to include the path relative to your program's current directory. Each nameList element will need to be dirName + "/" + directory->d_name.
If you started out calling lsR on the local directory, ./foo and foo has directory named bar under it, then to open bar you need to open ./foo/bar since your program is running from the directory represented by ..

readdir() beginning with dots instead of files [duplicate]

This question already has answers here:
Why do directory listings contain the current (.) and parent (..) directory?
(8 answers)
Closed 8 years ago.
I have a little problem. I'm reading files from directory and it works, but it read two extra files on the beginning ...what is it?
for example, there is a list of files: "A348", "A348A", "A348B"
and this is what i get: ".", "..", "A348", "A348A", "A348B" ???
DIR *dir;
struct dirent *dp;
char * file_name;
while ((dp=readdir(dir)) != NULL) {
file_name = dp->d_name;
}
. is a directory entry for current directory
.. is a directory entry for the directory one level up in hierarchy
You have to just filter them out using:
if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") )
{
// do nothing (straight logic)
} else {
file_name = dp->d_name; // use it
}
More on using . and .. on Windows:
".\\file" - this is a file named file in current working directory
"..\\file" - this is a file in a parent directory
"..\\otherdir\\file" - this is a file that is in directory named otherdir, that is at the same level as current directory (we don't have to know what directory are we in).
Edit: selfcontained example usage of readdir:
#include <stdio.h>
#include <dirent.h>
#include <string.h>
int main()
{
DIR *dir;
struct dirent *dp;
char * file_name;
dir = opendir(".");
while ((dp=readdir(dir)) != NULL) {
printf("debug: %s\n", dp->d_name);
if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") )
{
// do nothing (straight logic)
} else {
file_name = dp->d_name; // use it
printf("file_name: \"%s\"\n",file_name);
}
}
closedir(dir);
return 0;
}
Avoid taking the files whose name . and ..

Resources