I am using the Windows API and would like to be able to search through a specified directory and return the names of any files that reside within it.
I've made a start at it however i've hit a brick wall as i'm unsure of how to go any further.
Here is my progress so far:
#include <stdio.h>
#include <windows.h>
void main()
{
HANDLE fileHandle;
WIN32_FIND_DATAA fileData;
fileHandle = FindFirstFileA("*.txt", &fileData);
if(fileHandle != INVALID_HANDLE_VALUE)
{
printf("%s \n", fileData.cFileName);
}
}
You need to call FindNextFile in a loop to find all the files. There's a full example here, here are the interesting bits:
hFind = FindFirstFile(szDir, &ffd);
if (INVALID_HANDLE_VALUE == hFind)
return dwError;
do
{
printf("%s\n"), ffd.cFileName);
}
while (FindNextFile(hFind, &ffd) != 0);
#include <stdio.h>
#include <windows.h>
void main()
{
HANDLE fileHandle;
WIN32_FIND_DATA ffd;
LARGE_INTEGER szDir;
WIN32_FIND_DATA fileData;
fileHandle = FindFirstFile("C:\\Users\\rest_of_the_Address\\*", &ffd);
if (INVALID_HANDLE_VALUE == fileHandle)
printf("Invalid File Handle Value \n");
do
{
printf("%s\n", ffd.cFileName);
} while (FindNextFile(fileHandle, &ffd) != 0);
system("pause");
}
You were missing some declarations, and had some syntax errors, fixed up here, and also, remember to check the msdn documentation (here is a msdn example for the program)
Related
I am using Raspbian and this is a part of an exercise for my advanced C programming in Linux environments. I have to open a directory and list its content, checking if entries are subdirectories or files.
Here is what I did:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
//declarations
int main(int argc, char ** argv){
if(argc != 2){
fprintf(stderr, "Wrong number of arguments\n");
exit(EXIT_FAILURE);
}
DIR *dir;
dir = opendir(argv[1]);
if(!dir){
fprintf(stderr, "Error: unable to open directory\n");
exit(EXIT_FAILURE);
}
struct dirent * entry;
struct stat filestat;
while((entry = readdir(dir))){
if(!(strcmp(entry->d_name, ".") == 0) && !(strcmp(entry->d_name,"..") == 0)){
stat(entry->d_name, &filestat);
//printf("S_ISREG(%s) value is:%d\n", entry->d_name, S_ISREG(filestat.st_mode));
//printf("S_ISDIR(%s) value is:%d\n", entry->d_name, S_ISDIR(filestat.st_mode));
if(S_ISDIR(filestat.st_mode) == 0){
printf("Dir: %s\n", entry->d_name);
} else {
printf("File: %s\n", entry->d_name);
}
}
}
closedir(dir);
printf("END\n");
return EXIT_SUCCESS;
}
Now, it works if I am working with relative paths. If I am working on absolute paths, S_ISREG and S_ISDIR macros always return 0. What am I doing wrong?
It seems by doing stat(entry -> d_name, &filestat) you're giving as directory path just the name of the folder you're looking at, which should work if it belongs to the directory from which the file is being executed but might not work with absolute paths
Also, you are not checking if stat is producing any error, which might also be an issue here - or at least provide some insight to the real problem
you might want to try concatenating your path to entry->d_name and use that when you call stat instead, e.g.
char fullPath[MAX_LEN];
while((entry = readdir(dir))){
if(!(strcmp(entry->d_name, ".") == 0) && !(strcmp(entry->d_name,"..") == 0)){
sprintf(fullPath, "%s/%s", argv[1], entry->dname);
if(stat(fullPath, &filestat) < 0){
//handle error
}
if(S_ISDIR(filestat.st_mode) == 0){
printf("Dir: %s\n", entry->d_name);
} else {
printf("File: %s\n", entry->d_name);
}
}
}
where MAX_LEN is some predefined constant
Use
if (fstatat(dirfd(dir), entry->d_name, &filestat, AT_SYMLINK_NOFOLLOW) == -1) {
fprintf(stderr, "Cannot stat %s/%s: %s.\n",
argv[1], entry->d_name, strerror(errno));
/* Optionally, exit(EXIT_FAILURE); */
} else {
/* Directory entry information in filestat */
}
As explained in the man 3 readdir manual page, if entry->d_type == DT_UNKNOWN, you need to do the above fstatat() to obtain the type.
This is because some filesystems do not provide the information in the directory entries, and return d_type == DT_UNKNOWN for all directory entries. All applications are required to handle DT_UNKNOWN correctly.
Note that you need
#define _POSIX_C_SOURCE 200809L
#define _ATFILE_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
at the beginning of your file, to tell the C library to expose the functions (fstatat() and strerror()).
However, opendir()/readdir()/closedir() is the wrong way to check directory contents, because the contents may change during your traversal. You would be better off using [scandir()](https://man7.org/linux/man-pages/man3/scandir.3.html9, glob() (if searching for files matching a pattern), or nftw() (if traversing entire trees) which are all POSIX.1 standard functions provided by POSIXy C libraries (basically all except Windows).
An exercise claiming to show how to list files in a directory and using opendir()/readdir()/closedir() is at best misleading, because it leaves all the complexity – handling changes to the directory contents, like renamed files – to you the writer, without telling you such work is necessary! Yes, two decades ago filesystems were typically so simple that opendir()/readdir()/closedir() worked without issues, but that is not the case anymore. The other functions listed above are supposed to handle such cases gracefully, so you the programmer shouldn't need to do it yourself.
Here is one way to properly implement the directory scan in Linux:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <ftw.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
static int report_entry(const char *path,
const struct stat *info,
int typeflag,
struct FTW *ftwbuf)
{
/* If you need it, entry name is (path + ftwbuf->base). */
switch (typeflag) {
case FTW_D: /* Directory */
printf("%s is a directory\n", path);
/* If this is the path given to nftw(), recurse into it: */
if (ftwbuf->level == 0)
return FTW_CONTINUE;
/* Do not recurse into any other directories. */
return FTW_SKIP_SUBTREE;
case FTW_DNR: /* Directory, but no access to contents */
printf("%s is a directory but there is no read access\n", path);
return FTW_CONTINUE;
case FTW_DP: /* Directory that was already mentioned */
return FTW_CONTINUE;
case FTW_F: /* Regular file */
printf("%s is a file\n", path);
return FTW_CONTINUE;
case FTW_SL: /* Symlink, and FTW_PHYS was set for nftw() */
printf("%s is a symbolic link\n", path);
return FTW_CONTINUE;
case FTW_SLN: /* Symlink to a nonexistent file */
/* This will NOT be reported if FTW_PHYS is set in the nftw() call. */
printf("%s is a symbolic link to a nonexistent file\n", path);
return FTW_CONTINUE;
case FTW_NS: /* stat() failed */
printf("%s is unknown, and cannot be stat()'d\n", path);
return FTW_CONTINUE;
default: /* Should never occur */
printf("%s is of unknown an unexpected type (%d)\n", path, typeflag);
return FTW_STOP;
}
}
/* Number of file descriptors nftw() is allowed to use.
It mostly matters to applications that use many file descriptors,
like service daemons (servers). If nftw() runs out, it slows down,
but does not fail. In Linux, processes usually have at least
a thousand file descriptors available, so 64 is very conservative. */
#ifndef NFTW_FDS
#define NFTW_FDS 64
#endif
int report_directory(const char *path)
{
int result;
if (!path || !*path) {
/* No path specified; invalid parameter */
errno = EINVAL;
return -1;
}
result = nftw(path, report_entry, NFTW_FDS, FTW_ACTIONRETVAL | FTW_PHYS);
if (result == -1) {
/* nftw() error, errno set. */
return -1;
} else
if (result == FTW_STOP) {
/* report_entry() returned FTW_STOP. We assume error was already printed. */
errno = 0;
return -1;
} else
if (result != 0) {
/* Unexpected error */
errno = EIO;
return -1;
}
/* Done successfully. */
return 0;
}
int main(int argc, char *argv[])
{
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *thisname = (argc >= 1 && argv && argv[0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", thisname);
fprintf(stderr, " %s DIRECTORY-OR-FILE\n", thisname);
fprintf(stderr, "\n");
return (argc == 2) ? EXIT_SUCCESS : EXIT_FAILURE;
}
if (report_directory(argv[1]) != 0) {
if (errno) {
fprintf(stderr, "Error: %s: %s.\n", argv[1], strerror(errno));
}
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Try this:
while((entry = readdir(dir))){
if(!(strcmp(entry->d_name, ".") == 0) && !(strcmp(entry->d_name,"..") == 0)){
if(entry->d_type == DT_DIR){
printf("Dir: %s\n", entry->d_name);
} else {
printf("File: %s\n", entry->d_name);
}
}
}
It might not work on your platform though, read the readdir documentation for more information.
I've read some of the warnings against using the sysctl() call in C, and it seems if I cannot use sysctl() safely, the only other way I can find to make the needed change would be to use soemething like:
system("echo fs.inotify.max_user_watches=NEW_MAX_DIRECTORIES >> /etc/sysctl.conf");
system("sysctl -p");
(of course, this assumes ensuring the binary is running as root. However, I would rather NOT have to shell out using system calls.
Can someone point me in the correct and safe of using sysctl()?
here is a snippet of the code I am using.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
int main ()
{
int ret;
const char *LOGNAME="iNotifyMonitor";
logger(INFO, "================================================");
ret = startDaemon();
daemonRunning = ret;
if (ret == 0)
{
daemonRunning = 1;
FIRST_RUN = 0;
}
if(ret)
{
syslog(LOG_USER | LOG_ERR, "Error starting iNotifyMonitor");
logger(ERR, "Unable to start iNotifyMonitor");
closelog();
return EXIT_FAILURE;
}
signal(SIGINT, signalHandler);
signal(SIGHUP, signalHandler);
char *log_file_name = malloc(sizeof(char *) * sizeof(char *));
sprintf(log_file_name, "%s%s", INM_LOG_DIR, INM_LOG_FILE);
/* Try to open log file to this daemon */
if (INM_OPEN_LOG && INM_LOG_FILE)
{
log_stream = fopen(concatString(INM_LOG_DIR, INM_LOG_FILE), "a+");
if (log_stream == NULL)
{
char *errMsg;
sprintf(errMsg, "Cannot open log file %s, error: %s", concatString(INM_LOG_DIR, INM_LOG_FILE), strerror(errno));
log_stream = stdout;
}
}
else
{
log_stream = stdout;
}
while (daemonRunning == 1)
{
if (ret < 0)
{
logger(LOG_ERR, "Can not write to log stream: %s, error: %s", (log_stream == stdout) ? "stdout" : log_file_name, strerror(errno));
break;
}
ret = fflush(log_stream);
if (ret != 0)
{
logger(LOG_ERR, "Can not fflush() log stream: %s, error: %s",
(log_stream == stdout) ? "stdout" : log_file_name, strerror(errno));
break;
}
int curcount =countDirectory("/home/darrinw/Development/CrossRoads/");
directoryCount = curcount;
if(directoryCounrt > INM_MAX_DIRECTORIES)
{
int newVal = roundUp(directoryCount, 32768);
// call to sysctl() to modify fs.inotify.max_users_watches=newVal
}
sleep(INM_SCAN_INTERVAL);
}
My understanding is that the modern recommended approach to access sysctl variables is via the pseudo-files in /proc/sys. So just open /proc/sys/fs/inotify/max_user_watches and write there.
int fd = open("/proc/sys/fs/inotify/max_user_watches", O_WRONLY);
dprintf(fd, "%d", NEW_MAX_DIRECTORIES);
close(fd);
Error checking left as an exercise.
Modifying /etc/sysctl.conf would make the setting persist across reboots (assuming your distribution uses the file this way, I am not sure if all of them do). That's kind of rude to do automatically; probably better to use the documentation to advise the system administrator to do it themselves if it's needed.
I've completed writing of counting words code finally. It counts total number of words in files. (i.e. txt). Now, I want to use multiple fork() to access and read every file. I studied in the last week. Besides, I use global variable to hold number of counted words. As far as I know, If I apply fork(), used global variables are assigned as 0. To avoid it, I tried to use mmap() and similar functions this is okey. But, I also want to use pipe() also (fifo() if it is possible) to communicate (hold values of numbers).
I use nftw() function to go in folders and files. My logic is on the below picture. How can use fork() and pipe() (fifo()) on this code ? fork() is really complicated for me because of my inexperience. I'm new using of pipe() and fork(). According to my idea logic of the code is that if I can use fork() and pipe(), there will be fork() every file(i.e. txt) and access them by using fork. If there is another folder and there are files, again creates fork() from one of created forks , then access file. I try to explain also drawing below. Thank you. I want to learn using of them.
int countInEveryFolder(const char *dir)
is used because I don't know how to count files until the next folder in nftw() function. Number of files is necessary because it is number of fork.
Every folder should be parent of files. The files are included by the folder.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dirent.h>
#include <errno.h>
#include <ftw.h>
#include <ctype.h>
#include <sys/mman.h>
#include <locale.h>
#include <errno.h>
#define MAX_PATH_LEN 2048
unsigned long total_words = 0UL;
unsigned long total_dirs = 0UL;
unsigned long total_files = 0UL;
// Just proves counting number of file in a folder
int countInEveryFolder(const char *dir) {
struct stat stDirInfo;
struct dirent * stFiles;
DIR * stDirIn;
char szFullName[MAX_PATH_LEN];
char szDirectory[MAX_PATH_LEN];
struct stat stFileInfo;
int numOfFile = 0;
strncpy( szDirectory, dir, MAX_PATH_LEN - 1 );
if (lstat( szDirectory, &stDirInfo) < 0)
{
perror (szDirectory);
return 0;
}
if (!S_ISDIR(stDirInfo.st_mode))
return 0;
if ((stDirIn = opendir( szDirectory)) == NULL)
{
perror( szDirectory );
return 0;
}
while (( stFiles = readdir(stDirIn)) != NULL)
{
if (!strcmp(stFiles->d_name, ".") || !strcmp(stFiles->d_name, ".."))
continue;
sprintf(szFullName, "%s/%s", szDirectory, stFiles -> d_name );
if (lstat(szFullName, &stFileInfo) < 0)
perror ( szFullName );
/* is the file a directory? */
if (S_ISREG(stFileInfo.st_mode))
{
printf( "Filename: %s\n", szFullName );
numOfFile++;
}
} // end while
closedir(stDirIn);
return numOfFile;
}
// Count words in files.
unsigned long count_words_in_file(const char *const filename)
{
unsigned long count = 0UL;
int errnum = 0;
int c;
FILE *in;
in = fopen(filename, "rt");
if (in == NULL) {
errnum = errno;
fprintf(stderr, "%s: %s.\n", filename, strerror(errnum));
errno = errnum;
return 0UL;
}
/* Skip leading whitespace. */
do {
c = getc(in);
} while (isspace(c));
/* Token loop. */
while (c != EOF) {
/* This token is a word, if it starts with a letter. */
if (isalpha(c))
count++;
/* Skip the rest of this token. */
while (!isspace(c) && c != EOF)
c = getc(in);
/* Skip the trailing whitespace. */
while (isspace(c))
c = getc(in);
}
/* Paranoid checking for I/O errors. */
if (!feof(in) || ferror(in)) {
fclose(in);
fprintf(stderr, "Warning: %s: %s.\n", filename, strerror(EIO));
errnum = EIO;
} else
if (fclose(in)) {
fprintf(stderr, "Warning: %s: %s.\n", filename, strerror(EIO));
errnum = EIO;
}
errno = errnum;
return count;
}
// Recursively go in folders
int nftw_callback(const char *filepath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
// Directory
if (typeflag == FTW_DP || typeflag == FTW_D)
{
total_dirs++;
printf("%*s%s\n", ftwbuf->level * 4, "", filepath);
//countInEveryFolder(filepath);
}
// Folder
else if (typeflag == FTW_F)
{
total_files++;
total_words += count_words_in_file(filepath);
printf("%*s%s\n", ftwbuf->level * 4, "", filepath);
}
return 0;
}
/* Error message */
void err_sys(const char *msg)
{
perror(msg);
fflush(stdout);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
total_files = total_dirs = total_words = 0UL;
if (nftw(argv[1], nftw_callback, 15, FTW_PHYS) == 0) {
/* Success! */
printf("%s: %lu files, %lu directories, %lu words total.\n",
argv[1], total_files, total_dirs, total_words);
} else {
/* Failed... */
err_sys("ntfw");
}
putchar('\n');
//printf( "\nTotal words = %d\n\n", *wordCount);
//printf( "\nTotal folders = %d\n\n", *folderCount);
//printf( "\nTotal childs = %d\n\n", *childCount); //fork()
return 0;
}
To start I would write the program with two phases. A single-process phase in which all the file-paths are queued up (into a linked-list or dequeue), and a multi-process phase in which the worker processes receive work via their pipe() and send counts back to the main process via their pipe(). The main process would use select() to multiplex the input from its children.
Once you understand how to use select() with pipe()s, then work on having the filepath discovery be concurrent.
This design would be much easier to implement in Go, node.js, or greenlet with Python, but learning how to do it in C gives you a level of understanding for the underlying operations that you don't get with newer languages.
I tried to recursively get all files and folder list.But I can only get subdirectories of documents and inside of them.I can't get the other folders which in inside of subdirectory.
I don't know how to do it recursively.I hope you help me
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <windows.h>
#include <unistd.h>
#include <string.h>
void list(char *a);
void reader(char *path);
int
main (void)
{
DIR *dp;
struct dirent *ep;
dp = opendir ("C:\\Users\\pen\\Documents\\");
if (dp != NULL)
{
while (ep = readdir (dp)){
GetFileAttributes(ep->d_name);
if(FILE_ATTRIBUTE_DIRECTORY & GetFileAttributes(ep->d_name))
{
if (strcmp(".",ep->d_name)==0)
continue;
if (strcmp("..",ep->d_name)==0)
continue;
reader(ep->d_name);
}
}
closedir(dp);
}
else
perror ("Couldn't open the directory");
closedir(dp);
system("pause");
return 0;
}
void reader(char *path){
DIR *da;
struct dirent *ef;
da = opendir(path);
while (ef=readdir(da)){
printf ("%s\n",ef->d_name);
if(FILE_ATTRIBUTE_DIRECTORY & GetFileAttributes(ef->d_name))
{
if (strcmp(".",ef->d_name)==0)
continue;
if (strcmp("..",ef->d_name)==0)
continue;
reader(ef->d_name);
}
}
closedir(da);
}
1) In reader you need to call closedir(da); after the while loop.
2) every call to reader needs to have the absolute path you need to concatenate path
to ef->d_name and then call reader.
3) also to enable debugging you should call perror after a failed readdir call.
I'm trying to create a function in c which scans all my path C: \ temp (Windows) to search for a file that I pass (eg test.txt) and each time it finds one return the path to steps another function to write something in the bottom of this file.
I managed to do the function that writes to the file but can not figure out how to do that scans the folder and pass the address of the file found.
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
void printdir(char *dir, int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL) {
fprintf(stderr,"cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 ||
strcmp("..",entry->d_name) == 0)
continue;
printf("%*s%s/\n",depth,"",entry->d_name);
/* Recurse at a new indent level */
printdir(entry->d_name,depth+4);
}
else printf("%*s%s\n",depth,"",entry->d_name);
}
chdir("..");
closedir(dp);
}
int main()
{
printf("Directory scan of /home:\n");
printdir("/home",0);
printf("done.\n");
exit(0);
}
Use FindFirstFile function. Here's a good example of this function using.