The canonical way of determining a file's type is to use the commented
out code in this snippet:
// Return the number of files in dirName. Ignore directories and links.
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
int fCt = 0;
struct dirent *dir;
DIR *d;
d = opendir(argv[1]);
if (d == NULL) {
printf("%s was not opened!\n", argv[1]);
exit(0);
}
// Count all of the files.
while ((dir = readdir(d)) != NULL) {
// struct stat buf;
// stat(dir->d_name, &buf);
// if (S_ISREG(buf.st_mode)) { fCt++; }
if (dir->d_type == 8) { fCt++; }
}
return fCt;
}
The element buf.st_mode returns 41ED (hex), 16877 (decimal) for both directories and regular files. S_ISREG fails to find the right bit set for both types.
Note that the line:
if (dir->d_type == 8) { fCt++; }
returns an accurate file count.
Why did the commented out method fail?
Related
I am trying to open a directory and read the files and folders and add '/' at the end of if it is a folder. This is my current code.
#include <dirent.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int isDir(const char* target)
{
struct stat statbuf;
stat(target, &statbuf);
return S_ISDIR(statbuf.st_mode);
}
int main(int argc, char * argv[])
{
puts("");
struct stat sfile;
struct dirent *dp;
char * dir;
if(argc != 2)
dir = getenv("PWD"); //default
else
dir = argv[1];
DIR *dirp = opendir(dir);
dp = readdir(dirp);
while ( dp != NULL )
{
stat(dp->d_name,&sfile);
//opendir(dp->d_name);
printf("Size : %ld ",sfile.st_size);
if (isDir(dp->d_name))
{
printf("%s/\n",dp->d_name);
}
else
{
printf("%s \n",dp->d_name);
}
dp = readdir(dirp);
}
printf("\n");
closedir(dirp);
}
This works when I pass no arguments, ie, it is reading files and folders in the current working directory. When I pass a directory as argument, it fails to detect further folders in that as directories. Curiously, when tried using errno when I try to open using opendir(), all the contents are detected as directories.
int isDir2(const char* target)
{
opendir(target);
if(errno == ENOTDIR)
{
puts("Not directory");
return 0;
}
else
{
puts("Directory"); //only this is getting printed
return 1;
}
}
In either case, when both '.' and '..' are detected as directories but the rest as either directories or files depending on whether I am using opendir()'s errno or S_ISDIR.
Your isDir function isn't actually checking the return value from stat to validate that it succeeded. And since your statbuf variable is uninitialized, it's undefined behavior.
Better:
int isDir(const char* target)
{
struct stat statbuf = {0};
int result = 0;
if (stat(target, &statbuf) != -1)
{
result = S_ISDIR(statbuf.st_mode);
}
return result;
}
Further, you are invoking opendir without assigning it's return value:
while ( dp != NULL )
{
stat(dp->d_name,&sfile);
opendir(dp->d_name); // WHERE'S THE RESULT?
You need to rethink your logic in this while loop with regards to invoking opendir, readdir, and don't forget to invoke closedir.
So the issue was of working directories. When I checked what the stat function in the isDir() function was returning, it was returning -1. It wasn't able to find the file/folder because it's current working directory was not the folder it was given, rather the cwd where the program was run. What I did was change the directory to the given folder and it works.
The code now:
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
int isDir(const char* target)
{
struct stat statbuf = {0};
int result = 0;
int err = stat(target, &statbuf);
if (err != -1)
{
result = S_ISDIR(statbuf.st_mode);
}
return result;
}
int main(int argc, char * argv[])
{
puts("");
struct stat sfile;
struct dirent *dp;
char * dir;
if(argc != 2)
dir = getenv("PWD"); //default
else
dir = argv[1];
DIR *dirp = opendir(dir);
dp = readdir(dirp);
chdir(dir);
while ( dp != NULL )
{
stat(dp->d_name,&sfile);
printf("Size : %ld ",sfile.st_size);
if (isDir(dp->d_name))
{
printf("%s/\n",dp->d_name);
}
else
{
printf("%s \n",dp->d_name);
}
dp = readdir(dirp);
}
printf("\n");
closedir(dirp);
}
My code prints the files/directory names in a given path(user enters it as a command-line argument). When executing with a given path in the directory, it just works fine but it is supposed to do the same for the current working directory if user does not provide any command-line argument.
I am getting seg fault if I just run as: ./a.out
It works when I run as: ./a.out /path
Please fix my code by providing the necessary code fragment
I have tried to do debugging and found out that it gives the error right after it executes the line following line in the depthFirst function
printf("%s\n", sd->d_name);
My faulty code:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <limits.h>
void depthFirst(char * path){
struct dirent *sd;
DIR *dir;
//char path[PATH_MAX];
dir = opendir(path);
if(dir == NULL){
printf("Error, unable to open\n");
exit(1);
}
while( (sd = readdir(dir)) != NULL){
if(strcmp(sd->d_name, ".") != 0 && strcmp(sd->d_name, "..") != 0){
printf("%s\n", sd->d_name);
realpath(sd->d_name,path);
if(isdirectory(path)){
printf("\t");
depthFirst(sd->d_name);
}
}
}
closedir(dir);
}
int isdirectory(char *path) {
struct stat statbuf;
if (stat(path, &statbuf) == -1)
return 0;
else
return S_ISDIR(statbuf.st_mode);
}
int main(int argc, char *argv[]){
char * path;
char * currentDirectory;
if(argc<2){
currentDirectory = ".";
depthFirst(currentDirectory);
}
else{
path = argv[1];
depthFirst(path);
}
return 0;
}
The output is shown below:
.git
Segmentation fault
Jonathan beat me to it in the comments, but this change prevents the problem.
## -9,7 +9,7 ##
void depthFirst(char * path){
struct dirent *sd;
DIR *dir;
- //char path[PATH_MAX];
+ char rpath[PATH_MAX];
dir = opendir(path);
## -22,8 +22,8 ##
while( (sd = readdir(dir)) != NULL){
if(strcmp(sd->d_name, ".") != 0 && strcmp(sd->d_name, "..") != 0){
printf("%s\n", sd->d_name);
- realpath(sd->d_name,path);
- if(isdirectory(path)){
+ realpath(sd->d_name,rpath);
+ if(isdirectory(rpath)){
printf("\t");
depthFirst(sd->d_name);
As another comment pointed out, you cannot reuse the char* path because it is stored in a page of memory that is not writable by your program. Therefore, realpath() will crash upon attempting to write to it.
I cannot locate argv[i] in /bin (or /sbin), package only checks the directory it was run from. How do I check for argv[i] in /bin and /sbin?
I am currently working on my own package manager and I am writing it in pure C. I am currently writing the check to see if the package(s) passed (using argv[]) are already installed. The issue I am having is that when I run for the check my code only checks in the directory it was run from and I need it to check /bin and /sbin (I am going to handle to checking of /sbin) and I am trying to get it to check /bin, but am having zero luck. I cannot seem to get this to work, each time they just check the current working directory and I need them to check /bin. I can't figure this out, has anyone by chance done this in pure C before? Thank you
These are all the methods I have tried so far, none of them work...
using stat()
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
struct dirent *de = calloc(1, sizeof(struct dirent));
DIR *dr = opendir("/bin"); /* directory to open */
short i;
struct stat *program = calloc(1, sizeof(struct stat));
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
for (i = 1; i < argc; i++) {
if (stat(argv[i], program) == 0) {
printf("found\n");
closedir(dr);
}
else {
printf("not found\n");
closedir(dr);
}
}
}
}
using realpath
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
struct dirent *de = calloc(1, sizeof(struct dirent));
DIR *dr = opendir("/bin"); /* directory to open */
short i;
char *res = realpath(argv[i], NULL);
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
for (i = 1; i < argc; i++) {
if (res == NULL) {
printf("found\n");
closedir(dr);
}
else {
printf("not found\n");
closedir(dr);
}
}
}
}
using strcmp
#include <stdio.h>
#include <string.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
struct dirent *de;
DIR *dr = opendir("/bin"); /* directory to open */
short i;
struct stat program;
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
for (i = 1; i < argc; i++) {
if (strcmp(de->d_name, argv[i]) == 0) {
printf("found\n");
closedir(dr);
}
else {
printf("not found\n");
closedir(dr);
}
}
}
}
I am expecting them all to work as follows:
check echo
// it would go to /bin and find echo and then print
found
but when I run them they only check the current working directory, so for example:
check connection.c
// finds connection.c in the same directory
found
Solution
Naha! I found a way to do it! So using the function chdir() I can run stat() in /bin like so:
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
short i;
struct stat *program = calloc(1, sizeof(struct stat));
for (i = 1; i < argc; i++) {
chdir("/bin"); /* move to /bin */
if (chdir("/bin") != 0)
return 1;
if (stat(argv[i], program) == 0)
return 0;
else
return 1;
}
}
I tried simplifying your code just to output the contents of the folder, I get the contents of the '/bin' folder each time.
As a general rule of thumb, I like to get my code to compile without warnings, test it does what I expect it to, then work on the next bit.
The 'struct stat program' was causing the code not to compile, my guess is you were running an old version of the code.
#include <stdio.h>
#include <dirent.h>
#include <string.h>
int main(int argc, char *argv[])
{
struct dirent *de;
DIR *dr = opendir("/bin"); /* directory to open */
short i;
// struct stat program;
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
printf(de->d_name);
printf("\n");
}
}
If I understand you correctly then you’re confusing the semantics of opendir/readdir with those of chdir:
opendir does not change the working directory. This means that, e.g. realpath("echo", resolved_path) won’t resolve echo as /bin/echo but as $PWD/echo (where $PWD is the environment variable holding your current working directory).
That said, as a general rule you should not chdir inside your process. Instead, you can construct the path explicitly … e.g. via sprintf:
const char *binpath = "/bin";
char *fullpath = malloc(strlen(binpath) + 1 + strlen(argv[1]) + 1);
sprintf(fullpath, "%s/%s", binpath, argv[1]);
… and then stat the result (no need to dynamically allocate your struct stat either):
struct stat st;
if (stat(fullpath, &st) != 0) {
fprintf(stderr, "error in stat(): %s", strerror(errno));
}
printf("%s has inode %ju\n", fullpath, (unsigned long) st.st_ino);
The goal is to compare files by size and filter those of the same size.
For that you need to compare every file to every file.
However the first loop doesnt work so the search of the first directory is stuck at the first file.
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
int main(int argc, char *v[]) {
struct dirent *d_verzeichnis1;
struct dirent *d_verzeichnis2;
DIR *dir1;
DIR *dir2;
FILE *file = fopen(v[3], "W");
dir1 = opendir(v[1]);
dir2 = opendir(v[2]);
struct stat filesize1;
struct stat filesize2;
while ((d_verzeichnis1 = readdir(dir1)) != NULL) {
stat((d_verzeichnis1->d_name), &filesize1);
while ((d_verzeichnis2 = readdir(dir2)) != NULL) {
stat((d_verzeichnis2->d_name), &filesize2);
if (filesize1.st_size == filesize2.st_size);
{
printf("%s und %s sind gleich\n",
d_verzeichnis1->d_name, d_verzeichnis2->d_name);
}
}
d_verzeichnis1 = readdir(dir1);
}
}
There are multiple problems in your code:
you should verify the actual number of arguments provided on the command line to avoid undefined behavior if fewer than 3 were provided.
fopen(v[3], "W"); uses an invalid mode string, you should use "w". It is unclear what this stream pointer is used for anyway.
dir1 and dir2 are not tested: you have undefined behavior if opendir() fails.
stat is called with the directory entry name, which is not a relative pathname to the file if the directory is different from the current directory. You should construct the path name from the directory name and entry name.
if (filesize1.st_size == filesize2.st_size); has an extra ; at the end of the line, causing the following block to execute unconditionally. You should use K&R style with{` at the end of the line to avoid such silly mistakes.
the logic of parallel scan is incorrect: you should reopen or at least rewind the second directory for each entry in the first to allow a full scan for potential matches.
Here is a corrected version:
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
char *buildpath(char *dest, size_t size, const char *dir, const char *name) {
size_t len = strlen(dir);
const char *sep = "";
if (len > 0 && dir[len - 1] != '/')
sep = "/";
if ((unsigned)snprintf(dest, size, "%s%s%s", dir, sep, name) < size)
return dest;
else
return NULL;
}
int main(int argc, char *argv[]) {
char path1[1024];
char path2[1024];
struct dirent *dp1;
struct dirent *dp2;
DIR *dir1;
DIR *dir2;
struct stat filesize1;
struct stat filesize2;
if (argc < 3) {
fprintf(stderr, "missing argument\n");
fprintf(stderr, "usage: cmpdir dir1 dir2\n");
return 1;
}
dir1 = opendir(argv[1]);
if (dir1 == NULL) {
fprintf(stderr, "cannt open directory %s: %s\n", argv[1], strerror(errno));
return 1;
}
dir2 = opendir(argv[2]);
if (dir2 == NULL) {
fprintf(stderr, "cannt open directory %s: %s\n", argv[2], strerror(errno));
return 1;
}
while ((dp1 = readdir(dir1)) != NULL) {
/* ignore . and .. entries */
if (!strcmp(dp1->d_name, ".")
|| !strcmp(dp1->d_name, ".."))
continue;
if (!buildpath(path1, sizeof path1, argv[1], dp1->d_name)) {
/* path too long */
continue;
}
if (stat(path1, &filesize1)) {
/* cannot stat entry */
continue;
}
if (!S_ISREG(filesize1.st_mode)) {
/* not a regular file */
continue;
}
rewinddir(dir2);
while ((dp2 = readdir(dir2)) != NULL) {
/* ignore . and .. entries */
if (!strcmp(dp2->d_name, ".")
|| !strcmp(dp2->d_name, ".."))
continue;
if (!buildpath(path2, sizeof path2, argv[2], dp2->d_name)) {
/* path too long */
continue;
}
if (stat(path2, &filesize2)) {
/* cannot stat entry */
continue;
}
if (!S_ISREG(filesize2.st_mode)) {
/* not a regular file */
continue;
}
if (filesize1.st_size == filesize2.st_size) {
printf("%s and %s have the same size %llu\n",
path1, path2, (unsigned long long)filesize1.st_size);
/* perform actual comparison... */
}
}
}
closedir(dir1);
closedir(dir2);
return 0;
}
Is there a way that I can write a C function that gives me the file size for each file in a directory tree? (similar to the output of du -a)?
I don't have trouble getting any one of the file sizes, but I run into trouble recursing through directories within the main directory.
Is there a way that I can write a C function that gives me the file size for each file in a directory tree?
Yes, there is. You can use the <dirent.h> API to traverse a directory:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <sys/stat.h>
void recursive_dump(DIR *dir, const char *base)
{
struct dirent *ent;
for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) {
if (ent->d_name[0] == '.') {
continue;
}
char fname[PATH_MAX];
snprintf(fname, sizeof(fname), "%s/%s", base, ent->d_name);
struct stat st;
stat(fname, &st);
if (S_ISREG(st.st_mode)) {
printf("Size of %s is %llu\n", fname, st.st_size);
} else {
DIR *ch = opendir(fname);
if (ch != NULL) {
recursive_dump(ch, fname);
closedir(ch);
}
}
}
}
int main(int argc, char *argv[])
{
DIR *dir = opendir(argv[1]);
recursive_dump(dir, argv[1]);
closedir(dir);
return 0;
}
Yes. You need to use opendir and stat. See 'man 3 opendir', and 'man 2 stat'.
In a nutshell:
#include <dirent.h>
#include <sys/stat.h>
// etc...
void the_du_c_function() {
struct dirent direntBuf;
struct dirent* dirEntry = 0;
const char* theDir = ".";
DIR* dir = opendir(theDir);
while (readdir_r(dir,&direntBuf,dirEntry) && dirEntry) {
struct stat filestat;
char filename[1024];
snprintf(filename,sizeof(filename),"%s/%s",theDir,dirEntry.d_name);
stat(filename,&filestat);
fprintf(stdout,"%s - %u bytes\n",filename,filestat.st_size);
}
}
I just typed that code segment. I did not compile it, but that's the gist of it.