I am trying to print out the contents of every file given a directory recursively. I have some logical error in my code and I can't figure out where it's going wrong.
Go through directory/subdirectories and get filename:
char filename[500], filepath[500];
void listdir(char *dir)
{
DIR *dp;
struct dirent *name;
struct stat statbuf;
if((dp = opendir(dir)) == NULL)
{
fprintf(stderr,"cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while((name = readdir(dp)) != NULL)
{
if(lstat(name->d_name, &statbuf) == 0)
{
if(statbuf.st_mode & S_IFDIR)
{
/* Found a directory, but ignore . and .. */
if(strcmp(".", name->d_name) == 0 || strcmp("..", name->d_name) == 0)
continue;
// Directory name
int len = strlen(filepath);
strcat(filepath, name->d_name);
strcat(filepath, "/");
/* Recurse at a new indent level */
listdir(name->d_name);
filepath[len] = '\0';
}
else
{
// Filename
strcpy(filename, filepath);
strcat(filename, name->d_name);
// Prevent double printing
if(file[strlen(filename) - 1] != '~')
readFile(filename);
}
}
}
chdir("..");
closedir(dp);
}
Open/Read file contents:
void readFile(char *filepath)
{
char ch;
FILE *file;
file = fopen(filepath, "r");
if(file)
{
while((ch = fgetc(file)) != EOF)
printf("%c", ch);
fclose(file);
}
}
Main:
int main(int argc, int *argv[])
{
listdir(argv[1]);
return 0;
}
So if I run the program like this ./fileread ~/Desktop and I have this on the Desktop:
Songs.txt, test/test.txt, test/random.txt
It will print out the contents of Songs.txt, but won't print out anything inside the test folder. If I take out "Prevent double printing" line, then it will print out the contents of Songs.txt twice and still not print out anything inside the test folder.
What am I doing wrong? Any help would be appreciated!
UPDATED
I added local strings:
void getdir(char *dir)
{
char *direct;
char filepath[250];
...
direct = malloc(strlen(dir) + strlen(name->d_name) + 2);
strcpy(direct, dir);
strcat(direct, "/");
strcat(direct, name->d_name);
getdir(direct);
free(direct);
}
else
{
// Concatenate file name
strcpy(filepath, dir);
strcat(filepath, "/");
strcat(filepath, name->d_name);
if(filepath[strlen(filepath) - 1] != '~')
readFile(filepath);
}
This seems to work great. If there's anything I shouldn't do, please let me know. Thanks for the help everyone!
Related
This code is fetching the subdirectories names that are present in a given directory. What I am trying to do is to split the subdirectories names using using a delimiter " " but when I print the token it should print the first subdirectory only but instead the code prints all the subdirectories names. I don't know what is wrong that the strtok is not working.
Given result is like
/home/bilal/Videos/folder1/rasta
/home/bilal/Videos/folder1/fd
/home/bilal/Videos/folder1/fd/fds
/home/bilal/Videos/folder1/fd/sub
Expected result
/home/bilal/Videos/folder1/rasta
char path[PATH_MAX] = "";
char path1[PATH_MAX];
void listdir(void) {
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(path))) {
perror("opendir-path not found");
return;
}
while ((entry = readdir(dir))) {
char *name = entry->d_name;
if (entry->d_type == DT_DIR) /* if "." or ".." skip */
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
snprintf(path1, 100, "%s/%s ", path, name);
char *token = strtok(path1, " ");
printf("%s\n", token);
if (entry->d_type == DT_DIR) {
char *p;
strcat(path, "/");
strcat(path, name);
listdir(); /* recursive call */
if ((p = strrchr(path, '/'))) {
if (p != path)
*p = 0;
}
}
}
closedir(dir);
}
int main(int argc, char **argv) {
if (argc > 1)
strcpy(path, argv[1]);
else
strcpy(path, "/home/bilal/Videos/folder1");
listdir();
}
There are several issues in the posted code:
There is no need to use 2 separate path arrays, you could use path and remember its original length to strip the trailing part after each iteration.
You append a space after the pathname in snprintf(path1, 100, "%s/%s ", path, name); and strtok() immediately cuts the pathname on this space: it seems redundant and even counterproductive if a component of the pathname contains a space as strtok will stop there instead.
It is unclear what your goal is: each entry is printed as the function enumerates the directory entries and recurses on subdirectories.
Here is a modified version:
#include <stdio.h>
#include <string.h>
#include <dirent.h>
char path[PATH_MAX];
void listdir(void) {
DIR *dir;
struct dirent *entry;
size_t len = strlen(path);
if (len >= sizeof(path) - 1) {
// array too small to construct pathnames
return;
}
if (!(dir = opendir(path)) != NULL) {
perror("opendir-path not found");
return;
}
while ((entry = readdir(dir)) != NULL) {
char *name = entry->d_name;
if (entry->d_type == DT_DIR) { /* if "." or ".." skip */
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
}
snprintf(path + len, sizeof(path) - len, "/%s", name);
printf("%s\n", path);
if (entry->d_type == DT_DIR) {
listdir(); /* recursive call */
}
path[len] = '\0';
}
closedir(dir);
}
int main(int argc, char **argv) {
if (argc > 1)
strcpy(path, argv[1]);
else
strcpy(path, "/home/bilal/Videos/folder1");
listdir();
return 0;
}
I'm currently working on a problem that is a C program to allow me to keep directories in sync. The jist of it is:
If a file is in Directory A and not in Directory B, copy the file to Directory B.
If the file is in both Directory A and Directory B, copy the newest version of the file to the Directory with the oldest.
If a file is in Directory B but not Directory A, delete the file.
I was having issues getting the full path of the files I need to open and delete. So I decided to use the C function chdir(path).
I am able to create a new file and remove the files, but for some reason, the data in the file is not copied even after opening two file streams to do so and I'm not sure why. The code is definitely messy, but I can't figure out where I'm going wrong. Insight would be appreciated. I am new to C and dealing with buffers and memory management is something I'm not quite good at just yet. Any ideas?
#include "csapp.h"
#include <stdio.h>
#include <dirent.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
// prototypes
bool inOtherDir(char*, char*);
void copyFile(char*, char*, char*, char*);
int isNewer(char*, char*, char*, char*);
int main(int argc, char **argv ) {
// variables
struct dirent *dirone;
struct dirent *dirtwo;
char* dirAPath = argv[1]; // this will be used for absolute paths
char* dirBPath = argv[2];
char cwd[256];
char* cwdptr = cwd;
// get the CWD for use later
if(getcwd(cwd, sizeof(cwd)) == NULL) {
perror("getcwd() error()");
return 1;
}
// Testing
// printf("CWD is: %s\n", cwd);
if (argc < 3) {
printf("Not enough arguments provided, please provide two directories.\n");
return 1;
}
// open directory a
DIR *ptrone = opendir(argv[1]);
// open directory b
DIR *ptrtwo = opendir(argv[2]);
if (ptrone == NULL || ptrtwo == NULL) {
printf("Could not open directory\n");
return 1;
}
printf("Checking Sync Status: \n");
// Open the directory and check to see if the file is a regular file
// if it is regular, then do work on it based on if its in the other
// directory or not.
while ((dirone = readdir(ptrone)) != NULL) {
if (dirone->d_type == DT_REG) {
if(inOtherDir(dirone->d_name, argv[2])) {
printf("File %s is in both directories.\n", dirone->d_name);
} else {
printf("File %s is in Directory A but not Directory B.\n", dirone->d_name);
printf("Copying %s in Directory A to Directory B.\n",dirone->d_name);
copyFile(dirone->d_name,dirAPath, dirBPath,cwdptr);
}
}
}
// check for files only in directory B, then delete them
while ((dirtwo = readdir(ptrtwo)) != NULL) {
if (dirtwo->d_type == DT_REG) {
if(inOtherDir(dirtwo->d_name, argv[1])) {
printf("File %s is in both directories.\n", dirtwo->d_name);
} else {
printf("File %s will be removed from the directory.\n",dirtwo->d_name);
chdir(dirBPath);
remove(dirtwo->d_name);
chdir(cwd);
}
}
}
closedir(ptrone);
closedir(ptrtwo);
return 0;
}
// Look through the directory to see if the same file is in there returns
// true if the file is in that directory and returns false if not.
bool inOtherDir(char* filename, char* direct) {
struct dirent *buf;
DIR *directory = opendir(direct);
if(directory == NULL) {
printf("Could not open directory.");
exit(1);
}
while((buf = readdir(directory)) != NULL) {
if ((strcmp(filename, buf->d_name)) == 0) {
return true;
}
}
closedir(directory);
return false;
}
// Copies the file from one directory to the other
void copyFile(char* filename, char* directory_from,char* directory_to, char* cwd) {
FILE *file, *copyfile;
char c;
chdir(directory_from);
file = fopen(filename,"r");
if(file == NULL) {
printf("Cannot open the file %s in copyFile.\n", filename);
}
chdir(cwd);
chdir(directory_to);
copyfile = fopen(filename, "w+");
if(copyfile == NULL) {
printf("Cannot open copy of the file %s in copyFile.\n", filename);
}
c = fgetc(file);
while(c != EOF){
fputc(c, copyfile);
c = fgetc(file);
}
printf("Contents copied successfully.\n");
fclose(file);
fclose(copyfile);
chdir(cwd);
return;
}
I'm using miniz to create a .zip file in C, on Windows.
I used the doc to produce my code and it works. I can create an archive with the files I want, ONLY if I give relative path to the zip function.
I don't get why the "file_name" variable must be something like "../test/file.txt" and not "C:/../test/file.txt".
if (!(status = mz_zip_add_mem_to_archive_file_in_place(archive, file_name, data, strlen(data) + 1, s_pComment,
(uint16) strlen(s_pComment), MZ_BEST_COMPRESSION)))
return (merror("add file to archive failed !!"));
Before this function, I open my file, get the data inside and call the zip_function with it.
if (!(src = fopen(file_name, "r")))
return (merror("can't open this file"));
char *line = NULL;
char *data= NULL;
size_t n = 0;
getline(&line, &n, src);
data= strdup(line);
while (getline(&line, &n, src) != -1){
data = realloc(save, sizeof(char) * (strlen(data) + strlen(line)) + 1);
data = strcat(data, line);
}
fopen(src);
So I call the zip function with the archive name, the file name (with the absolute path) and the datas inside it (in char * format).
This is the "full" code : the function init_zip is the first function called by my program. The arg parameter is the archive name I want to be create(it can be an absolute path and works) and the args parameter are the names of the differents files I want to add to the archive file (relative path works but not absolute).
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint;
static const char *s_pComment = "";
static int isDirectory(const char *path) {
struct stat statbuf;
if (stat(path, &statbuf) != 0)
return 0;
return S_ISDIR(statbuf.st_mode);
}
int get_data(const char *archive, const char *file)
{
FILE *src;
if (!isDirectory(file)) {
if (!(src = fopen(file, "r")))
return (merror("can't open this file"));
char *line = NULL;
char *save = NULL;
size_t n = 0;
getline(&line, &n, src);
save = strdup(line);
while (getline(&line, &n, src) != -1) {
save = realloc(save, sizeof(char) * (strlen(save) + strlen(line)) + 1);
save = strcat(save, line);
}
printf("compressing %s ..\n", file);
if (m_compress(archive, file, save))
return (merror("compress function failed"));
printf(("\tOK.\n"));
fclose(src);
}
else
{
DIR *dir;
struct dirent *entry;
char *new_file;
if (!(dir = opendir(file)))
return (merror("opendir failed: ", "wrong directory path in init_zip.get_data command : ", file, NULL));
while ((entry = readdir(dir)) != NULL)
{
if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
new_file = add_path(file, entry->d_name);
get_data(archive, new_file);
}
}
if (new_file)
free(new_file);
closedir(dir);
}
}
int init_zip(const char *arg, const char **args)
{
printf("\nZIP cmd:\n >");
remove(arg);
for (int counter = 0; args[counter]; ++counter)
{
get_data(arg, args[counter]);
}
printf("All the files are added to %s archive file.\n", arg);
return (0);
}
int m_compress(const char *archive, const char *file_name, const char *data)
{
mz_bool status;
if (data)
if (!(status = mz_zip_add_mem_to_archive_file_in_place(archive, file_name, data, strlen(data) + 1, s_pComment,
(uint16) strlen(s_pComment), MZ_BEST_COMPRESSION)))
return (merror("add file to archive failed !!"));
else
if (!(status = mz_zip_add_mem_to_archive_file_in_place(archive, file_name, NULL, 0, "no comment", (uint16)strlen("no comment"), MZ_BEST_COMPRESSION)))
return (merror("add directory to archive failed !!"));
return (0);
}
This is the add_path() function used in get_data():
char *add_path(const char *str1, const char *str2)
{
char *path;
path = malloc(sizeof(char) * (strlen(str1) + 1 + strlen(str2) + 1));
path = strcpy(path, str1);
path = strcat(path, "/");
path = strcat(path, str2);
return (path);
}
Anyone knows something about it?
If nothing helps, then you should lookup the sources. Following the code in miniz on Github, file miniz_zip.c line 4297 I see:
mz_bool mz_zip_add_mem_to_archive_file_in_place(...
which calls function mz_zip_writer_validate_archive_name to check the second filename provided that it cannot start with a drive letter (line 3069) and if so
returns FALSE with error set to MZ_ZIP_INVALID_FILENAME.
As to why this second filename may not be an absolute path, I don't know. If it is important to you, you could get the code from Github and adapt it.
I've been trying for a few days now to write a code which will inspect all subfolders from C:\Users, and print their files, for example:
C:\Users\Public
files
C:\Users\Somefolder
files
Here is my code:
main() {
DIR *dr,*dirp;
struct dirent *dr_ent,*sec;
char *buf,*baf;
char get[50],x[50];
char folder[] = "C:\\Users\\";
dr = opendir(folder);
if (dr != NULL)
goto next;
else return -1;
next:
while ((dr_ent = readdir(dr))!=NULL) {
buf=dr_ent->d_name;
strcpy(get,buf);
if (strstr(get,".")==NULL && strstr(get,"..")==NULL) {
strcpy(x,folder);
strcat(x,get);
printf("%s\n",x);
continue;
goto read;
Sleep(300);
}
}
read:
dirp = opendir(get);
while ((sec = readdir(dirp))!=NULL) {
baf=sec->d_name;
printf("%s\n",baf);
Sleep(300);
}
system("PAUSE");
return EXIT_SUCCESS;
}`enter code here`
And in this case, only one folder was read. So am I actually making mistake by taking a variable from loop before so only one line was taken? And why is second label completely ignored by program? By the way, I'm sort of beginner in C programming, so don't get surprised by potential errors.
The second label is completely ignored because before the goto read; you use continue; which takes you to the next iteration of the while loop ignoring everything else after it.
Also in your code you don't check if the directory's entry is a file or a directory to handle it appropriately. What I mean is you should descend into a directory but you should print a file's name when you encounter one (as you do).
You can use a recursive function to avoid the use of goto, as goto is a bad practice.
void list_dir(char const* dirName)
{
DIR* dir;
if ((dir = opendir(dirName)) == NULL) {
fprintf(stderr, "Cannot open directory '%s': %s\n", dirName, strerror(errno));
exit(EXIT_FAILURE);
}
struct dirent* entry;
// For every entry in the directory
while ((entry = readdir(dir)) != NULL) {
char* path; // path = dirName/entryName
int pathLength; // length of the path in chars
struct stat entryStats; // info of the entry
char* entryName = entry->d_name; // entry filename
pathLength = strlen(dirName) + 1 + strlen(entryName);
path = malloc((pathLength + 1) * sizeof(char));
strcpy(path, dirName);
strcat(path, "/");
strcat(path, entryName);
if (stat(path, &entryStats) == 0) {
// If the entry is a directory
if (entryStats.st_mode & S_IFDIR) {
// If the directory is not "." or ".." get its entries
if (strcmp(entryName, ".") != 0 &&
strcmp(entryName, "..") != 0) {
list_dir(path);
}
}
// If the entry is a file
else if (entryStats.st_mode & S_IFREG) {
printf("%s\n",path);
}
}
free(path);
}
closedir(dir);
}
The above code works on Unix but on Windows you may have to change stat and struct stat to _stat and struct _stat if you are using Visual Studio.
I'm writing a program that takes all the files from a directory declared in the command line. The command line takes 2 arguments, the directory path and a optional flag "-s" which spits out directory information in non-decreasing order if applied. I'm 90% done but my program only spits out files and file information from the current directory, not the directory specified in the command line. Any advice would be appreciated
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <string.h>
#include <ctype.h>
int mygetFstat(char *name);
char *removeWhiteSpaces(char *str);
#define PATH 1
#define FLAG 2
#define LEN 10
#define STRLEN 54
#define MAXLEN 100
struct stat fileStat;
time_t t;
struct tm lt;
char timbuf[MAXLEN];
int main(int argc, char *argv[]){
DIR *dir;
struct dirent *ent;
FILE *ptr;
char myBuf[MAXLEN];
char filename[MAXLEN];
char finalfile[MAXLEN];
char length;
char str[MAXLEN];
char cmd[MAXLEN];
if ((dir = opendir (argv[PATH])) != NULL) {
// print all the files and directories within directory
if(argv[FLAG] != NULL){
if(strcmp(argv[FLAG], "-s") == 0){
system("ls -al | sort -k5n >> tempfile.txt");
//sprintf(finalfile, "cd %s | ls -al | sort -k5n >> tempfile.txt", (char *)argv[PATH]);
// printf("\nfinal file = %s\n", finalfile);
// system(finalfile);
if(NULL == (ptr = fopen("tempfile.txt","rw"))){
printf("\nCan't open file.\n");
exit(1);
}
while(!feof(ptr)){ // loop through every line in tempfile.txt
fgets(myBuf, MAXLEN, ptr);
if(strlen(myBuf) > LEN){ // I chose 11 here because some lines were only 10 characters
// And they were unnecessary.
length = strlen(myBuf); // Grabs length of entire line from ptr
strncpy(filename, myBuf + STRLEN, length); // the file names start at bit position 54,
if((filename[0] == '.') && (filename[1] != '.') && (strlen(filename) != 2)){
removeWhiteSpaces(filename);
mygetFstat(filename);
}
}
}
system("rm tempfile.txt");
exit(1);
}else{
printf("Error: Flag not recognized.\n");
return 0;
}
}else{
while((ent = readdir(dir)) != NULL){
if((ent->d_name[0] == '.') && (ent->d_name[1] != '.') && (strlen(ent->d_name) != 1))
mygetFstat(ent->d_name);
}
closedir (dir);
}
} else {
// could not open directory
printf("No such File or Directory.\n");
return EXIT_FAILURE;
}
}
int mygetFstat(char *name)
{
// Function for finding info about files.
int file = 0;
if((file = open(name,O_RDONLY)) < -1)
return 1;
if(fstat(file,&fileStat) < 0)
return 1;
printf("Information for %s\n",name);
printf("---------------------------\n");
printf("File Size: \t\t%d bytes\n",fileStat.st_size);
// Adjusts time to display date. Not just seconds from epoch
t = fileStat.st_mtime;
localtime_r(&t, <);
strftime(timbuf, sizeof(timbuf), "%c", <);
printf("Date Last Modified: \t%s\n",timbuf);
printf("\n");
//return 0;
}
char *removeWhiteSpaces(char *str){ //removes white spaces from input
char *end;
while(isspace(*str)) str++;
if(*str == 0)
return str;
end = str + strlen(str)-1;
while(end > str && isspace(*end)) end--;
*(end+1) = 0;
return str;
}
ent->d_name only contains the name of the entry in that directory. It does not include the full path to the entry.
If the first argument is "folder1" and there are files "file-1.txt" and "file-2.txt" in that directory, ent->d_name will be "file-1.txt" and "file-2.txt". You need to pass "folder1/file-1.txt" to mygetFstat(), not just "file-1.txt".
You should change the while loop to:
while((ent = readdir(dir)) != NULL){
if((ent->d_name[0] == '.') && (ent->d_name[1] != '.') && (strlen(ent->d_name) != 1))
{
strcpy(filename, argv[PATH]);
strcat(filename, "/");
strcat(filename, ent->d_name);
mygetFstat(filename);
}
}
Update
I would add a function
int is_dot_or_dot_dot(char const* entryName)
{
return (strcmp(entryName, ".") == 0) || (strcmp(entryName, "..") == 0);
}
and change the code inside the while loop to:
while((ent = readdir(dir)) != NULL){
if ( is_dot_or_dot_dot(ent->d_name) )
{
continue;
}
strcpy(filename, argv[PATH]);
strcat(filename, "/");
strcat(filename, ent->d_name);
mygetFstat(filename);
}
Keep your code, do a chdir right after you open the directory:
if ((dir = opendir (argv[PATH])) != NULL) {
// print all the files and directories within directory
// Change the working directory to the one given.
if (chdir(argv[PATH]) == -1) {
perror(argv[PATH]); exit(1);
}
P.S I would recommend using perror but you have to include errno.h.
Improvement on the rest of your code.
system("ls -al | sort -k5n >> tempfile.txt");
While you can do that it would be a faster way to insert it into a linked list sorting it as you insert them. But if you want to keep it see below for improvements.
The whole point of this project is to use system calls.See http://www.albany.edu/~csi402/pdfs/lect_10.pdf
while(!feof(ptr)){ // loop through every line in tempfile.txt
fgets(myBuf, MAXLEN, ptr);
feof is not reliable, use
while( fgets (myBuf, MAXLEN, ptr)!=NULL ){