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 ..
Related
I am trying to read a given directory filePath and get the names of all non folder files into a array of strings. So the issue that I need solved is how to specifically not get folder type files but also get all the other file type names and store them into the string array. Later I plan to use threads to read these individual files as well, but I need to be able to store the files names properly. The code that I am currently using is below. It should also be noted that this code is being executed by a child process from the fork() command, but I am not sure if that is relevant to the issue anyway. Any help would be appreciated. Thanks.
Example:
In the Home/Documents there are 4 files: hello.txt something.dat folder1 something2.dat
My string array should have the values hello.txt, something.dat, and something2.dat
Note: It is okay for me not to do the files as a I go through the directory as the files themselves are not going to be changed at all content wise.
//char* directory is an absolute filePath to the directory
void getFilesFromDirectory(char* directory, pid_t process)
{
int index =0;
DIR *dir;
struct dirent *ent;
//Can hold only 500 valid files in the folder
char *stringArray[500];
if ((dir = opendir (directory)) != NULL)
{
while ((ent = readdir (dir)) != NULL)
{
strcpy(stringArray[index],ent->d_name);
index++;
}
closedir (dir);
}
else
{
/* could not open directory */
perror ("");
}
//Everything Below is not related to the problem. Just what I am using it for.
pthread_t threadArray[index];
pthread_t senderThread;
//thread_param_t parameterSender;
thread_param_t paramterArray[index];
sem_init(&empty,0,bufferSize);
sem_init(&full, 0, 0);
sem_init(&mutex, 0, 1);
//parameterSender.listItem = listHead;
pthread_create(&senderThread, NULL, senderFunction, NULL);
//int threadCounter = 0;
for(int i =0; i<index; i++)
{
paramterArray[i].fileLocation = strcat(directory, stringArray[i]);
pthread_create(&threadArray[i], NULL, threadFunction, paramterArray + i);
}
}
You could check stringArray for files without "." and set this pointer to Null. (There are files missing)
A better option is this:
#include <stdio.h>
#include <dirent.h>
int main()
{
DIR *folder;
struct dirent *entry;
int files = 0;
folder = opendir(".");
if(folder == NULL)
{
perror("Unable to read directory");
return(1);
}
printf("debug\n");
while( (entry=readdir(folder)) )
{
files++;
printf("File %3d: %s :: %s\n",
files,
entry->d_name,
(entry->d_type == DT_DIR)?"Directory" : "File"
);
}
closedir(folder);
return(0);
}
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.
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
In my program stat is only working for a current directory. Can any one please help me with this.
even though I am passing parameter from main it is only working for current directory.
and source path is good it is printing correct path which I have passed from the main.
DIR *dr;
struct dirent *cur;
struct stat fi;
long int total_size = 0;
dr = opendir(source);
char *name;
printf("%s\n\n\n", source);
if (!(dr))
{
perror("opendir()");
return(1);
}
while (cur = readdir(dr))
{
if(cur->d_name[0] != '.')
{
if(stat(cur->d_name, &fi) == -1)
{
printf("error \n\n");
}
else
{
printf("%s ",cur->d_name);
printf("%ld ",fi.st_blocks);
total_size = total_size + fi.st_blocks;
}
}
}
printf("\n\ntotal_size = %ld \n", total_size);
printf("\n\n\n");
return 0;
}
cur->d_name only contains the file name.
to get a 'stat()' outside the current directory,
need to prefix with the path string.
Also need to check if the returned struct from readdir()
is a file or a sub directory.
The main problem is that stat expects a file path but d_name is just the file name. You can find a working example of how to use stat with d_name here
I am doing an assignment that requires me to create a function similar to ls.
My code works fine but when it comes to implementing the behaviour of
ls -l child //where child is a folder
there is a weird behaviour.
Let's say i am in the folder 'parent' and in it i have a subfolder, 'child' which contains some text files. When i run my program from the parent folder, it finds the folder and prints out the attributes of the text files in it. However, it will only print the files in the child folder only if the same files itself exists in the parent folder.
Here is a snippet of the code that i am using,
char CurrDir[100];
DIR *pDir = NULL;
struct dirent *pFileNames = NULL;
getcwd(CurrDir, sizeof(CurrDir))
strncat(CurrDir, "/", strlen(CurrDir));
unsigned int CurrDirLen = strlen(CurrDir);
unsigned int CombSize = CurrDirLen + strlen(argv[1]);
char SuperCharArr[CombSize];
for(int i = 0; i < CombSize; ++i)
{
if( i < strlen(CurrDir) )
SuperCharArr[i] = CurrDir[i];
else
SuperCharArr[i] = argv[1][i%CurrDirLen];
}//for
//insert null character at the end of the character
SuperCharArr[CombSize] = '\0';
pDir = opendir( SuperCharArr );
printf("%s\n", SuperCharArr);
if( pDir != NULL )
{
//Directory detected as pDir is a DirectoryStream
printf("%s\n", "pDir not null");
PrintHeader();
while( (pFileNames = readdir(pDir)) != NULL )
{
PrintFileDeails(pFileNames);
}
}//if
In my original code posted here, there was a function called, PrintFileDeails(pFileNames), which takes in a parameter of type direct.
Within PrintFileDeails(), there is a function that checks on the status of the file and the code is as follows,
struct stat FileStat;
if( stat(pFileNames->d_name, &FileStat) == -1 )
{
perror("stat");
exit(EXIT_FAILURE);
}//if
This line of code would print out an error where they couldn't find the file and with AAT's comment made me go thru my code again as i suspect that it was not reading the correct folder. Hence, after i passed the full path of where it is supposed to read the file from and it worked fine. Hence, the code was changed to this instead.
if( stat(pFullPath, &FileStat) == -1 )
{
perror("stat");
exit(EXIT_FAILURE);
}//if
where pFullPath was passed the variable of SuperCharArr which contained the full path of where the file to be searched was.
The man page for stat() helped too and it can be found here