So I've written a short C program that explores the files on my computer to look for a certain file. I wrote a simple function that takes a directory, opens it an looks around:
int exploreDIR (char stringDIR[], char search[])
{
DIR* dir;
struct dirent* ent;
if ((dir = opendir(stringDIR)) == NULL)
{
printf("Error: could not open directory %s\n", stringDIR);
return 0;
}
while ((ent = readdir(dir)) != NULL)
{
if(strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
continue;
if (strlen(stringDIR) + 1 + strlen(ent->d_name) > 1024)
{
perror("\nError: File path is too long!\n");
continue;
}
char filePath[1024];
strcpy(filePath, stringDIR);
strcat(filePath, "/");
strcat(filePath, ent->d_name);
if (strcmp(ent->d_name, search) == 0)
{
printf(" Found it! It's at: %s\n", filePath);
return 1;
}
struct stat st;
if (lstat(filePath, &st) < 0)
{
perror("Error: lstat() failure");
continue;
}
if (st.st_mode & S_IFDIR)
{
DIR* tempdir;
if ((tempdir = opendir (filePath)))
{
exploreDIR(filePath, search);
}
}
}
closedir(dir);
return 0;
}
However, I keep getting the output:
Error: could not open directory /Users/Dan/Desktop/Box/Videos
Error: could not open directory /Users/Dan/Desktop/compilerHome
The problem is, I have no idea what it is about these files that could cause opendir() to fail. I don't have them open in any program. They're just simple folders I created on my desktop. Does anyone have any idea what the problem could be?
You are calling opendir() twice for each closedir(). Maybe you are running out of resources.
Related
I'm having problem understanding how lstat() in c actually works. I have already read the following documentation but still have some problems understating how lstat() really works and why doesn't it work the way i
I use it.
https://man7.org/linux/man-pages/man0/sys_stat.h.0p.html
https://www.ibm.com/docs/en/zos/2.1.0?topic=functions-lstat-get-status-file-symbolic-link
https://man7.org/linux/man-pages/man2/lstat.2.html
https://man7.org/linux/man-pages/man3/lstat.3p.html
https://man7.org/linux/man-pages/man3/fstatat.3p.html
Basically what I want to do is write a program similar to the command find, that list all files/directories and subdirectories. I want to use lstat() to check if a file is a symbolic link and if so skip it. The st_mode gives information about the file type.
#includes ...
void error_handling(char* msg){
perror(msg);
exit(1);
}
void print_cwd(){} //prints the current working directory
void print_path(char* path){} //prints the current path
void handle_path(char * path) {
int success = chdir(path);
if (success != 0) {
if (errno == ENOTDIR || errno == ENOENT) {
return; // OK, just ignore non-directory entries for recursion
} else {
error_handling("chdir");
}
}
DIR *current_dir = opendir(".");
if (current_dir == NULL) {
error_handling("opendir");
}
struct dirent *current_dirent;
while (1) {
errno = 0;
current_dirent = readdir(current_dir);
if (current_dirent == NULL) {
if (errno != 0) { // readdir doesn't change errno on success, hence, it should be zero
error_handling("readdir");
}
break;
}
char *name = current_dirent->d_name;
if (strcmp(name, "..") == 0 || strcmp(name, ".") == 0) {
continue;
}
print_path(current_dirent->d_name);
handle_path(current_dirent->d_name);
}
closedir(current_dir);
/* it doesn't matter where i call lstat() it always falis, be it before opening the directory or anywhere else. As soon as this is calledit fails
struct stat buf;
int s = lstat(path, &buf);
if(s != 0)
error_handling("lstat");
*/
chdir("..");
}
It fails with the following error: lstat: No such file or directory
As described here https://man7.org/linux/man-pages/man2/lstat.2.html in the return value section, these function return 0 on success and -1 on error.
According to this post I found on here Symlinks and File Redirection you should call lstat() after you call open(). Still fails with the same error
I am trying to print the names of all the processes currently in the system, in the terminal. For that I have to get into all the directories named after the process ID in the "proc" directory. So I am looping till before the "acpi" directory and trying to read the status file in each process directory. But I don't exactly understand how to read a file in a directory which is inside a directory. On running my code below :
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <dirent.h>
int main(int argc, const char *argv[])
{
DIR* FD;
struct dirent* in_file;
FILE *process_file;
char ch, pname[1024];
int i=0;
FD = opendir ("/proc");
while ((in_file = readdir(FD)))
{
if (strcmp (in_file->d_name, "acpi") == 0)
break;
else
{
opendir(in_file->d_name);
process_file = fopen("status", "r");
while( ( ch = fgetc(process_file) ) != '\n' )
{
pname[i] = ch;
i++;
}
printf(" %s \n",pname);
fclose(process_file);
closedir(in_file->d_name);
}
}
closedir(FD);
return 0;
}
I get the error :
myps.c: In function ‘main’:
myps.c:38:13: warning: passing argument 1 of ‘closedir’ from incompatible pointer type
closedir(in_file->d_name);
^
In file included from myps.c:5:0:
/usr/include/dirent.h:149:12: note: expected ‘struct DIR *’ but argument is of type ‘char *’
extern int closedir (DIR *__dirp) __nonnull ((1));
^
This is a good example of when to use a recursive function.
The function would take a directory name, open that directory, and loop through the results. For each result that is not . or .., call the stat function to get status on each entry. Then use the S_ISREG and S_ISDIR macros against the file mode to see if it's a regular file or a directory. If it's a directory, build a new string from the parent directory and the one you just found and pass that to the recursive function call.
So the function would look something like this:
void processDirectory(char dirname[])
{
struct stat statbuf;
DIR *dir;
struct dirent *de;
char *subdirname;
int rval, ;
if ((dir = opendir(dirname)) == NULL) {
perror("Failed to open directory %s", dirname);
exit(1);
}
while ((errno = 0, de = readdir(dir)) != NULL) {
rval = stat(de->d_name, &statbuf);
if (rval == -1) {
perror("stat failed");
exit(1);
}
if (S_ISREG(statbuf.st_mode)) {
// process as a regular file
} else if (S_ISDIR(statbuf.st_mode)) {
if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
subdirname = malloc(strlen(dirname) + strlen(de->d_name) + 2);
if (subdirname == NULL) {
perror("malloc failed");
exit(1);
}
strcpy(subdirname, dirname);
strcat(subdirname, "/");
strcat(subdirname, de->d_name);
processDirectory(subdirname);
free(subdirname);
}
}
}
if (errno && (errno != ENOENT)) {
perror("Failed to read directory %s", dirname);
exit(1);
}
closedir(dir);
}
To solve the error, save the directory pointer you open. Then use that to close the directory.
DIR *process_dir = opendir(in_file->d_name);
closedir(process_dir);
I have this code I modified but the problem is, I can't get it to jet me only files with certain extensions. I tried so many things and every time I don't get the desired output. Any help would be appreciated. This part of a very big project and I got stuck at this specific function.
static void list_dir (const char * dir_name)
{
DIR * d;
/* Open the directory specified by "dir_name". */
d = opendir (dir_name);
/* Check it was opened. */
if (! d) {
fprintf (stderr, "Cannot open directory '%s': %s\n",
dir_name, strerror (errno));
exit (EXIT_FAILURE);
}
while (1) {
struct dirent * entry;
char * d_name;
/* "Readdir" gets subsequent entries from "d". */
entry = readdir (d);
if (! entry) {
/* There are no more entries in this directory, so break
out of the while loop. */
break;
}
d_name = entry->d_name;
if(!strstr (d_name, ".jpg") == 0 && !strstr (d_name, ".JPG") == 0){
continue;
}
/* Print the name of the file and directory. */
printf ("%s\n", d_name);
/* If you don't want to print the directories, use the
following line: */
//if (! (entry->d_type & DT_DIR)) {
//printf ("%s/%s\n", dir_name, d_name);
//}
if (entry->d_type & DT_DIR) {
/* Check that the directory is not "d" or d's parent. */
if (strcmp (d_name, "..") != 0 && strcmp (d_name, ".") != 0) {
int path_length;
char path[PATH_MAX];
path_length = snprintf (path, PATH_MAX,
"%s/%s", dir_name, d_name);
//printf ("%s\n", path);
if (path_length >= PATH_MAX) {
fprintf (stderr, "Path length has got too long.\n");
exit (EXIT_FAILURE);
}
/* Recursively call "list_dir" with the new path. */
if(!strstr (d_name, ".jpg") == 0 && !strstr(d_name, ".JPG") == 0){
continue;
}
list_dir (path);
}
}
}
/* After going through all the entries, close the directory. */
if (closedir (d)) {
fprintf (stderr, "Could not close '%s': %s\n",
dir_name, strerror (errno));
exit (EXIT_FAILURE);
}
}
You should know that case sensitivity is not solved by just using ".jpg" and ".JPG" since there could be more like ".jPg" and so on.
You can try this
int endsWith(const char *const filename, const char *const extension)
{
size_t fileNameLength;
size_t extensionLength;
if ((filename == NULL) || (extension == NULL))
return 0;
fileNameLength = strlen(filename);
extensionLength = strlen(extension);
return (strcasecmp(extension, filename + filenameLength - extensionLength) == 0);
}
then you can do
if (endsWidth(d_name, ".jpg") != 0) /* it has jpeg extension */
Note: strcasecmp() is not standard, you can find the correct alternative for your c library googling.
And also, there are libraries that examine the file type by using the so called magic bytes, they are normally the first 4 or so bytes in the file content, there is even one called libmagic, although I have never worked with it, so I can't give you advice on how to use it.
I would replace the line
if(!strstr (d_name, ".jpg") == 0 && !strstr (d_name, ".JPG") == 0){
with more readable code:
if( fileIsNotJPGFile(d_name) ) {
And implement the function as:
int fileIsJPGFile(char const* name)
{
return (strstr(name, ".jpg") || strstr(name, ".JPG"));
}
int fileIsNotJPGFile(char const* name)
{
return !(fileIsJPGFile(name));
}
I am trying to get the number of directories in a folder except the files in but I cannot get the correct result. Somebody help me to solve this problem? Especially what should I sent to the isDirectory() function?
int listFilesIndir(char *currDir)
{
struct dirent *direntp;
DIR *dirp;
int x ,y =0 ;
if ((dirp = opendir(currDir)) == NULL)
{
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
{
printf("%s\n", direntp->d_name);
x= isDirectory(dirp);
if(x != 0)
y++;
}
printf("direc Num : %d\n",y );
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
int isDirectory(char *path)
{
struct stat statbuf;
if (stat(path, &statbuf) == -1)
return 0;
else
return S_ISDIR(statbuf.st_mode);
}
You're sending a directory stream to the function, and treating it like a path.
Linux and some other Unix systems include a way to get this info directly:
while ((direntp = readdir(dirp)) != NULL)
{
printf("%s\n", direntp->d_name);
if (direntp->d_type == DT_DIR)
y++;
}
Otherwise, make sure you send the right details to the function, i.e.
x= isDirectory(direntp->d_name);
The call for your function is wrong.
x= isDirectory(dirp);
While the prototype of function is:
int isDirectory(char *path)
It need a string as parameter, but you give it a "DIR *dirp;". I changed the code as:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int listFilesIndir(char *currDir)
{
struct dirent *direntp;
DIR *dirp;
int x ,y =0 ;
if ((dirp = opendir(currDir)) == NULL)
{
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
{
printf("%s\n", direntp->d_name);
if(direntp->d_type == DT_DIR)
y++;
}
printf("direc Num : %d\n",y );
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
int main(int argc, char **argv){
if(argc == 2){
// Check whether the argv[1] is a directory firstly.
listFilesIndir(argv[1]);
}
else{
printf("Usage: %s directory", argv[0]);
}
return 0;
}
I tested it on my Linux server. And it works well. SO #teppic is right. But pay attention, in the code, the number of directory includes two specific ".." (parent directory) and "." (current directory). If you do not want to include it, you could change:
printf("direc Num : %d\n",y );
into:
printf("direc Num : %d\n",y-2 );
Hope it helps!
I want to list regular files in a directory. However, stat fails for every file.
DIR* dp = NULL;
struct dirent* entry = NULL;
dp = opendir(directory);
if (!dp) { log_err("Could not open directory"); return -1; }
while (entry = readdir(dp))
{
struct stat s;
char path[1024]; path[0] = 0;
strcat(path, directory);
strcat(path, entry->d_name);
int status = 0;
if (status = stat(path, &s))
{
if (S_ISREG(s.st_mode))
{
printf("%s\n", entry->d_name);
}
}
else
{
fprintf(stderr, "Can't stat: %s\n", strerror(errno));
}
}
closedir(dp);
The output is
Can't stat: Resource temporarily
unavailable
Can't stat: Resource temporarily
unavailable
Can't stat: Resource temporarily
unavailable
(... many times)
errno is set to E_AGAIN (11).
Now, if I printf the resulting path, they are indeed valid file and directory names. The directory is readable, the user I run with does have the rights to do so (it's the directory where I write the program in).
What is causing this problem, and how can I do this correctly?
stat and many other system calls return 0 on success and -1 on failure. You are incorrectly testing the return value of stat.
Your code should be:
if (!stat(path, &s))
{
if (S_ISREG(s.st_mode))
{
printf("%s\n", entry->d_name);
}
}
else
{
fprintf(stderr, "Can't stat: %s\n", strerror(errno));
}
You are probably missing a delimitier.
strcat(path, directory);
strcat(path, "/"); //this is missing
strcat(path, entry->d_name);
Don't forget to account for the extra '/' when allocating your string.