I am trying to copy files from one directory to another using pthreads. Each thread is responsible for copying exactly one file. The maximum number of threads is specified via a command line argument.
What I want to do is IF the current threads is less than the max threads, create a thread to do work. ELSE, wait for the current threads and when one of them finishes, decrease the number of current threads.
I can't figure out how to wait for a thread via pthread_join without blocking the main thread.
Here is what I have so far:
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#define MAXNAME 80
#define R_FLAGS O_RDONLY
#define W_FLAGS (O_WRONLY | O_CREAT)
#define W_PERMS (S_IRUSR | S_IWUSR)
#define KILOBYTE 0.001
void *copyfilepass(void *arg);
int main(int argc, char* argv[]){
int num_threads; //number of threads to execute in parallel
int cur_threads = 0; //number of threads currently executing
char filename[MAXNAME]; //will temporarily hold the file name
DIR* source; //pointer to the source directory
DIR* dest; //pointer to the destination directory
struct dirent* dentry; //pointer to the internal structure of the source directory
struct stat fst; //stats for each file in a directory
int error;
void *status;
//***BEGIN ERROR CHECKING***
if (argc != 4) {
fprintf(stderr, "Usage: %s sourceDir destDir numThreads\n", argv[0]);
return 1;
}
//check if source directory name is too long
if ( snprintf(filename, MAXNAME, "%s", argv[1]) == MAXNAME ) {
fprintf(stderr, "Source directory name %s too long\n", argv[1]);
return 1;
}
//check if destination directory name is too long
if ( snprintf(filename, MAXNAME, "%s", argv[2]) == MAXNAME ) {
fprintf(stderr, "Source directory name %s too long\n", argv[2]);
return 1;
}
//check if we can successfully open the source directory
if( (source = opendir(argv[1])) == NULL ) {
fprintf(stderr, "Error opening source directory %s\n", argv[1]);
return 1;
}
//check if we can successfully open the destination directory
if( (dest = opendir(argv[2])) == NULL ) {
fprintf(stderr, "Error opening destination directory %s\n", argv[2]);
return 1;
}
//***END ERROR CHECKING***
num_threads = atoi(argv[3]);
while( (dentry = readdir(source)) != NULL ){
//source path
char* path = (char*)malloc(sizeof(char) * (strlen(dentry->d_name) + strlen(argv[1]) + 2)); //need '.' + '/' + '\0'
sprintf(path, "%s%c%s", argv[1], '/', dentry->d_name);
//destination path
char* dest_path = (char*)malloc(sizeof(char) * (strlen(dentry->d_name) + strlen(argv[2]) + 2)); //need '.' + '/' + '\0'
sprintf(dest_path, "%s%c%s", argv[2], '/', dentry->d_name);
if(!stat(path, &fst)){ //stat() return 0 if successful
if(S_ISREG(fst.st_mode)){
int args[3];
pthread_t tid;
if ( (args[0] = open(path, R_FLAGS)) == -1 ) {
fprintf(stderr, "Failed to open source file %s: %s\n", path, strerror(errno));
continue;
}
if ( (args[1] = open(dest_path, W_FLAGS, W_PERMS)) == -1 ) {
fprintf(stderr, "Failed to open destination file %s: %s\n", dest_path, strerror(errno));
continue;
}
if(cur_threads < num_threads) {
++cur_threads;
if ( (error = pthread_create((&tid), NULL, copyfilepass, args)) ) {
--cur_threads;
fprintf(stderr, "Failed to create thread: %s\n", strerror(error));
tid = pthread_self(); /* cannot be value for new thread */
}
printf("file: %.03fKB %s\n", (fst.st_size * KILOBYTE), path);
}
}
}
}
//close directory
closedir(source);
return 0;
}
Better than spawning and reaping threads is to just create a fixed-size pool at the beginning and have them all consume from a work queue. This will reduce overhead and simplify your code.
By the way, using threads to solve this problem may not improve performance, depending on the filesystem you're operating on. Food for thought.
Related
I need to create a hard link to an existing file or directory.
I also need to create a soft/symbolic link to an existing file or directory. This is part of a larger program which is shown below. Commands are executed by typing "-f pathname linkname" and each break argument is a different command. I believe the functions -f, -d and -h have been created correctly so far. However I am having trouble creating these links.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
//Handles filename, absolute pathname
//Need relative pathname for -f
int main(int argc, char **argv)
{
char command[200], flag[20], pathname[100], linkname[100];
struct stat st = {0};
char cmd[200];
char *token; //Pointer
int counter = 1; //Counter variable
FILE *fp;
char mode2[] = "0750"; //To set the permission of a file/path
long j;
char mode[] = "0640"; //To set the permission of a file/path
long i;
printf("Enter command: ");
fgets(cmd, 420, stdin);
//User input is tokenized to determine the proper commands are entered and executed
token = strtok(cmd, " "); //Input is tokenized by white spaces.
if(token == NULL)
{
printf("Error with command input.\n");
exit(EXIT_FAILURE);
}
strcpy(command, token);
token = strtok(NULL, " ");
if(token != NULL)
strcpy(flag, token);
token = strtok(NULL, " ");
if(token != NULL)
strcpy(pathname, token);
token = strtok(NULL, " ");
if(token != NULL)
strcpy(linkname, token);
//Switch statement to determine which command the user is choosing and execute that command.
switch(flag[1]) {
//The f case will create a file who's name is chosen by the user.
case 'f':
fp=fopen(pathname,"w");
fclose(fp);
char mode[] = "0640"; //Sets the permission of file to 0640.
i = strtol(mode, 0, 8);
if (chmod(pathname, i) < 0)
{
fprintf(stderr, "%s: error in chmod(%s, %s) - %d (%s)\n",
argv[0], pathname, mode, errno, strerror(errno));
return -1;
}
return 0;
break;
//The d case will create a new directory chosen by the user.
case 'd':
if (stat(pathname, &st) == -1) {
mkdir(pathname, 0750); //Directory is given permission 0750.
}
j = strtol(mode, 0, 8);
if (chmod (pathname,j) < 0)
{
fprintf(stderr, "%s: error in chmod(%s, %s) - %d (%s)\n",
argv[0], pathname, mode, errno, strerror(errno));
return -1;
}
return 0;
break;
//The h case will create a hardlink to an existing file.
case 'h':
char *pathname; //Existing file
char *linkname; //Name of desired hardlink
int hlink; //Stores path
hlink = link(pathname, linkname); //Links linkname to pathname
if (chmod (pathname,j) < 0)
{
fprintf(stderr, "%s: error linking(%s, %s) - %d (%s)\n",
argv[0], pathname, mode, errno, strerror(errno));
return -1;
}
return 0;
break;
//The s case will create a symbol link to an existing file.
case 's':
char *pathname;
char *linkname;
int slink;
slink = symlink(pathname, linkname); //Using the symlink function to create a symbolic link of pathname
if (chmod (pathname,j) < 0)
{
fprintf(stderr, "%s: error linking(%s, %s) - %d (%s)\n",
argv[0], pathname, mode, errno, strerror(errno));
return -1;
}
return 0;
break;
}
}
POSIX
http://pubs.opengroup.org/onlinepubs/009695399/functions/link.html
http://pubs.opengroup.org/onlinepubs/009695399/functions/symlink.html
#include <unistd.h>
char *path1 = "/home/cnd/mod1";
char *path2 = "/modules/pass1";
int status;
...
status = link (path1, path2);
Windows
https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createhardlinka
https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createsymboliclinka
BOOL fCreatedLink = CreateHardLink( pszNewLinkName,
pszExistingFileName,
NULL ); // reserved, must be NULL
if ( fCreatedLink == FALSE )
{
;// handle error condition
}
The following code is supposed to work as follows: print the list of the files in a directory, and print the content of each .c file.
it works fine when executed in UNIX for the same directory: ./a.out ./
However, I was not able to make it work for ./a.out ../differentDir execution.
I know that if the absolute path is provided as an argument, I could use argv[1] for that. However, when it is provided in a form of a relative path I am lost.
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFSIZE 32768
int main(int argc, char **argv) {
char buf[BUFFSIZE];
DIR *dp;
struct dirent *dirp;
char filename[80];
int name_length;
FILE *fp;
if (argc != 2) {
fprintf(stderr, "usage: %s dir_name\n", argv[0]);
exit(1);
}
if ((dp = opendir(argv[1])) == NULL ) {
fprintf(stderr, "can't open '%s'\n", argv[1]);
exit(1);
}
while ((dirp = readdir(dp)) != NULL ){
printf("%s\n", dirp->d_name);
memset(filename, '\0', sizeof(filename));
strcpy(filename, dirp->d_name);
printf(" ** %s ", filename);
name_length = strlen(filename);
printf(" name_length=%d \n", name_length);
if (findC(filename)) // checking if the file has a .c extension
{
fp=fopen(filename, "r");
if (fp == NULL)
fprintf(stderr, "Can't open .C file!\n");
else
{// if the file was opened successfuly:
do
{
fgets(buf,BUFFSIZE,fp); // reading each line until buffer is full or until reaching whitespace
buf[strlen(buf)-1]='\0'; // removing the trailing whitespace from the buffer
puts(buf);
}
while (!feof(fp));
printf("\n\n");
fclose(fp);
}
}
}
closedir(dp);
return(0);
}
/*FindC method gets a c-string that represents a file name; returns 1 if the file ends with .C extension, else returns 0*/
int findC(char * name)
{
int len = strlen(name);
if (len>=2 && name[len-2]=='.' && tolower(name[len-1])=='c')
return 1;
return 0;
}
Upon opening the file to read, the file pathname needs to also be relative.
// Form prefix for complete relative file name
char filename[MAXPATH];
strcpy(filename, argv[1]);
// append '/' if directory path does not end in '/'
if (TBD_code(filename)) {
strcat(filename, "/");
}
char *end = filename[strlen(filename)];
while ((dirp = readdir(dp)) != NULL ){
printf("%s\n", dirp->d_name);
if (findC(dirp->d_name)) {
// append filename to prefix
strcpy(end, dirp->d_name);
fp=fopen(filename, "r");
...
You can use realpath(argv1...) like in this example. realpath will return the absolute path for a relative path.
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
int main(int argc, char **argv) {
char *path = "../..";
char buff[PATH_MAX + 1]; /* not sure about the "+ 1" */
char *res = realpath(path, buff);
if (res) {
printf("This source is at %s.\n", buff);
} else {
perror("realpath");
exit(EXIT_FAILURE);
}
return 0;
}
To include the desired behavior in your program, you can use realpathin your code:
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
#include <dirent.h>
#include <string.h>
#define BUFFSIZE 32768
int main(int argc, char **argv) {
char buf[BUFFSIZE];
DIR *dp;
struct dirent *dirp;
char filename[80];
int name_length;
FILE *fp;
char buff[PATH_MAX + 1]; /* not sure about the "+ 1" */
if (argc != 2) {
fprintf(stderr, "usage: %s dir_name\n", argv[0]);
exit(1);
}
char *res = realpath(argv[1], buff);
if ((dp = opendir(res)) == NULL ) {
fprintf(stderr, "can't open '%s'\n", argv[1]);
exit(1);
}
while ((dirp = readdir(dp)) != NULL ){
printf("%s\n", dirp->d_name);
memset(filename, '\0', sizeof(filename));
strcpy(filename, dirp->d_name);
printf(" ** %s ", filename);
name_length = strlen(filename);
printf(" name_length=%d \n", name_length);
if (findC(filename)) // checking if the file has a .c extension
{
fp=fopen(filename, "r");
if (fp == NULL)
fprintf(stderr, "Can't open .C file!\n");
else
{// if the file was opened successfuly:
do
{
fgets(buf,BUFFSIZE,fp); // reading each line until buffer is full or until reaching whitespace
buf[strlen(buf)-1]='\0'; // removing the trailing whitespace from the buffer
puts(buf);
}
while (!feof(fp));
printf("\n\n");
fclose(fp);
}
}
}
closedir(dp);
return(0);
}
/*FindC method gets a c-string that represents a file name; returns 1 if the file ends with .C extension, else returns 0*/
int findC(char * name)
{
int len = strlen(name);
if (len>=2 && name[len-2]=='.' && tolower(name[len-1])=='c')
return 1;
return 0;
}
You could first change to the directory chdir either with relative or absolute path and the get the absolute path via the getcwd
#include <sys/types.h>
#include <string.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFSIZE 32768
#define PATH_SIZE 512
int main(int argc, char **argv) {
char buf[BUFFSIZE];
char path[PATH_SIZE];
DIR *dp;
struct dirent *dirp;
char filename[80];
int name_length, r;
FILE *fp;
if (argc != 2) {
fprintf(stderr, "usage: %s dir_name\n", argv[0]);
exit(1);
}
strcpy(path, argv[1]);
r = chdir(path);
if( r != 0 )
{
printf("Invalid path '%s'\n",path);
exit(1);
}
getcwd(path,PATH_SIZE);
if ((dp = opendir(path)) == NULL ) {
fprintf(stderr, "can't open '%s'\n", argv[1]);
exit(1);
}
while ((dirp = readdir(dp)) != NULL ){
printf("%s\n", dirp->d_name);
memset(filename, '\0', sizeof(filename));
strcpy(filename, dirp->d_name);
printf(" ** %s ", filename);
name_length = strlen(filename);
printf(" name_length=%d \n", name_length);
if (findC(filename)) // checking if the file has a .c extension
{
fp=fopen(filename, "r");
if (fp == NULL)
fprintf(stderr, "Can't open .C file!\n");
else
{// if the file was opened successfuly:
do
{
fgets(buf,BUFFSIZE,fp); // reading each line until buffer is full or until reaching whitespace
buf[strlen(buf)-1]='\0'; // removing the trailing whitespace from the buffer
puts(buf);
}
while (!feof(fp));
printf("\n\n");
fclose(fp);
}
}
}
closedir(dp);
return(0);
}
/*FindC method gets a c-string that represents a file name; returns 1 if the file ends with .C extension, else returns 0*/
int findC(char * name)
{
int len = strlen(name);
if (len>=2 && name[len-2]=='.' && tolower(name[len-1])=='c')
return 1;
return 0;
}
I am using JaetBrains' Clion with MinGW 3.2.1 on windows. and I'm trying to build a project in c.
I keep getting the following linkage error:
undefined reference to `printf'
any Idea How to solve it?
this is my code:
#include <fcntl.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h> // for time measurement
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <libintl.h>
#define BUFFERSIZE 1
int main(int argc, char** argv) {
assert(argc == 3);
char* inputDirPath = argv[0];
char* keyFilePath = argv[1];
char* outputDirPath = argv[2];
// open key file
int key_fd = open(keyFilePath, O_RDONLY);
if (key_fd < 0) {
printf("Failed opening Key file %s. Error: %s\n", keyFilePath, strerror(errno));
return errno;
}
// making sure the file is not empty
char keyFirstChar;
if (read(key_fd, (void*)keyFirstChar, 1) == 0)
{
printf("Error. Key file is empty %s.", keyFilePath);
return errno;
}
else {
// go back to the begining of the file.
assert(!close(key_fd));
key_fd = open(keyFilePath, O_RDONLY);
if (key_fd < 0) {
printf("Failed opening Key file %s. Error: %s\n", keyFilePath,
strerror(errno)
);
return errno;
}
}
// Temp file name
char inputFilepath[200] ;
struct dirent *dirEntity;
DIR *inputDir_dfd;
// open directory stream
assert((inputDir_dfd = opendir(inputDirPath)) != NULL);
while ((dirEntity = readdir(inputDir_dfd)) != NULL)
{
// full path to input file
sprintf(inputFilepath, "%s/%s",inputDirPath, dirEntity->d_name) ;
// call stat to get file metadata
struct stat statbuf ;
assert( stat(inputFilepath,&statbuf ) != -1 );
// skip directories
if ( ( statbuf.st_mode & S_IFMT ) == S_IFDIR )
{
continue;
}
// open input file
int inputFile_fd = open(inputFilepath, O_RDONLY);
if (inputFile_fd < 0) {
printf("Failed opening file in input directory, %s. Error: %s\n", inputFilepath, strerror(errno));
return errno;
}
// Temp file name
char outputFilePath[200] ;
// full path to file
sprintf(outputFilePath, "%s/%s",outputDirPath, dirEntity->d_name) ;
// open input file
int outputFile_fd = open(outputFilePath, O_WRONLY | O_CREAT | O_TRUNC);
if (outputFile_fd < 0) {
printf("Failed opening file in output directory, %s. Error: %s\n", outputFilePath, strerror(errno));
return errno;
}
char inputFileBuf[BUFFERSIZE];
while (read(inputFile_fd, inputFileBuf, BUFFERSIZE) == BUFFERSIZE){
char keyFileBuf[BUFFERSIZE];
if (read(key_fd, keyFileBuf, BUFFERSIZE) == 0) {
assert(!close(key_fd));
key_fd = open(keyFilePath, O_RDONLY);
if (key_fd < 0) {
printf("Failed opening Key file %s. Error: %s\n", keyFilePath, strerror(errno));
return errno;
}
read(key_fd,keyFileBuf, BUFFERSIZE);
}
char outputToWrite[BUFFERSIZE];
int i;
for(i = 0; i < BUFFERSIZE; i++){
outputToWrite[i] = keyFileBuf[i] ^ inputFileBuf[1];
}
if( write(outputFile_fd, outputToWrite, BUFFERSIZE) == -1){
printf("Failed writing to output file, %s. Error: %s\n", outputFilePath, strerror(errno));
return errno;
};
}
if(close(inputFile_fd) ); // close key file
}
closedir(inputDir_dfd); // close Dir
assert(!close(key_fd)); // close key file
}
thanks.
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 am trying to simulate linux command ls using linux api from c. Looking at the code it does make sense, but when I run it I get "stat error: No such file or directory". I have checked that opendir is working ok. I think the problem is in stat, which is returning -1 even though I think it should return 0.
What am I missing?
Thanks for your help.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, char *argv[])
{
DIR *dirp;
struct dirent *direntp;
struct stat stat_buf;
char *str;
if (argc != 2)
{
fprintf( stderr, "Usage: %s dir_name\n", argv[0]);
exit(1);
}
if ((dirp = opendir( argv[1])) == NULL)
{
perror(argv[1]);
exit(2);
}
while ((direntp = readdir( dirp)) != NULL)
{
if (stat(direntp->d_name, &stat_buf)==-1)
{
perror("stat ERROR");
exit(3);
}
if (S_ISREG(stat_buf.st_mode)) str = "regular";
else if (S_ISDIR(stat_buf.st_mode)) str = "directory";
else str = "other";
printf("%-25s - %s\n", direntp->d_name, str);
}
closedir(dirp);
exit(0);
}
It's because you aren't stating the actual file. It's in a different directory. If you want the real filename, combine argv[1] and direntp->d_name with a '/' between them.
Also, hungarian naming is icky, even the minor bit like 'p' on the end. If you have so many variables you need to keep track of their types in their names you're doing something wrong.
Here is a revised version of your program:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <sys/param.h>
int main(int argc, char *argv[])
{
DIR *dirp;
struct dirent *direntp;
struct stat stat_buf;
char *str;
char fullpath[MAXPATHLEN + 1];
size_t dirnamelen;
if (argc != 2)
{
fprintf( stderr, "Usage: %s dir_name\n", argv[0]);
exit(1);
}
strncpy(fullpath, argv[1], MAXPATHLEN - 1); /* account for trailing '/' */
fullpath[MAXPATHLEN - 1] = '\0';
dirnamelen = strlen(fullpath);
if (strlen(argv[1]) > dirnamelen) {
fprintf( stderr, "Directory name is too long: %s", argv[1] );
exit(2);
}
fullpath[dirnamelen++] = '/';
fullpath[dirnamelen] = '\0';
if ((dirp = opendir( argv[1])) == NULL)
{
perror(argv[1]);
exit(2);
}
while ((direntp = readdir( dirp)) != NULL)
{
fullpath[dirnamelen] = '\0';
if ((dirnamelen + strlen(direntp->d_name)) > MAXPATHLEN) {
fprintf(stderr, "File %s + directory %s is too long.", direntp->d_name, fullpath);
continue;
} else {
/* strncpy is mild overkill because the if statement has verified that
there's enough space. */
strncpy(fullpath + dirnamelen, direntp->d_name, MAXPATHLEN - dirnamelen);
fullpath[MAXPATHLEN] = '\0';
}
if (stat(fullpath, &stat_buf)==-1)
{
perror("stat ERROR");
exit(3);
}
if (S_ISREG(stat_buf.st_mode)) str = "regular";
else if (S_ISDIR(stat_buf.st_mode)) str = "directory";
else str = "other";
printf("%-25s - %s\n", direntp->d_name, str);
}
closedir(dirp);
exit(0);
}
Note that I use MAXPATHLEN (from <limits.h>) and carefully check to make sure there aren't any buffer overflows. You should do the same in your code.
Edit: Changed code to use strn family functions for added safety.
Add
#include <unistd.h>
...
chdir(argv[1]);
or call stat with the full pathname like this
...
char fullpath[MAXPATHLEN];
snprintf(fullpath, sizeof(fullpath), "%s/%s", argv[1], direntp->d_name);
if (stat(fullpath, &stat_buf) == -1)
...
Others have suggested building a full path for stat(), or using chdir(). Both those will work (although they are subject to a race condition, if the directory is renamed while you are in the middle of reading it).
An alternative, which is not subject to the race condition, and is therefore arguably more "correct", is to use fstatat(). Just replace your existing stat() call with:
fstatat(dirfd(dirp), direntp->d_name, &stat_buf, 0)
(The chdir() method can be made race-condition-free too: either by using fchdir(dirfd(dirp)) instead of chdir(), or by changing directory to argv[1] and then opening "." with opendir(). The pathname construction method can't be made race-condition-free).
Why dont you try this? Just give the path to argv[1] like this /home/sabri/Desktop/Test
int main(int argc, char *argv[])
{
struct dirent *direntp;
DIR *dirp;
if (argc != 2)
{
fprintf(stderr, "Usage: %s directory_name\n", argv[0]);
return 1;
}
if ((dirp = opendir(argv[1])) == NULL)
{
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
printf("%s\n", direntp->d_name);
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
If you are using on unix, then you may use the system command.
system("ls -ltr | grep -d");