C - word array keeps getting overridden - arrays

#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
// This program is going to scan all files in the current directory. It will make a tree for every folder
// and the folder will have a subsection of files in the tree format. YES SORTING!
char **word;
int coun = 0;
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)) // Check if it's a directory
{
/* Found a directory, but ignore . and .. */
if (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0)
{
continue;
}
word[coun] = ("%s", entry->d_name); // Put the file name in the array.
coun++;
printf("- %*s%s\n", depth, "", entry->d_name); // Print the name of the dir entry.
/* Recurse at a new indent level */
printdir(entry->d_name, depth + 1);
}
else
{
word[coun] = ("%s", entry->d_name); // Put the file name in the array.
coun++;
printf("%*s - %s\n", depth, "", entry->d_name); // This will print the file.
}
}
chdir("..");
closedir(dp);
}
int main(int argc, char* argv[])
{
word = calloc(1000, sizeof(*word));
printdir(".", 0);
printf("now, print the words in the order they were printed.\n");
for (int i = 0; i < coun; ++i)
{
printf("%s\n", word[i]);
}
exit(0);
}
My main aim for this code is to make a tree structure of the files that are currently in the directory. When I run it, I get this output.
- hw2
- tree
- Makefile
- ls.c
- tree.c
- find.c
- hw1
- grep.c
- factor.c
- uniq.c
- monster.c
- sort.c
- .nfs0000000006c543ea0000e073
- tree.c
- tree
now, print the words in the order they were printed.
hw2
grep.c
hw1
grep.c
factor.c
uniq.c
monster.c
sort.c
.nfs0000000006c543ea0000e073
tree.c
tree
The tree works fine, but I still need to sort the files afterward. My plan is to put all of the file names into the global words array, then discern between folders and files, and print in the same format, but ordered alphabetically case-insensitive.
I check the array, but the hw2 folder and its files gets overridden completely. I don't understand why this is happening because it should be working fine.
Does anyone know a fix or maybe a better way to do this?

I believe your issue lies here:
word[coun] = ("%s", entry->d_name);
You have not allocated any memory space for word[coun] to point to.
I have no idea what ("%s", ...) is meant to accomplish, but gcc throws warnings about it. It actually does return entry->d_name but the "%s is unused.
You have word[coun] pointing to something that changes before you try to access it. Instead you need to use strcpy to copy entry->d_name into the memory space you allocate.
Instead of:
word[coun] = ("%s", entry->d_name);
coun++;
You want:
word[coun] = malloc(strlen(entry->d_name) + 1);
strcpy(word[coun++], entry->d_name);
Make sure to allocate an extra byte for the '\0' that terminates every C string.

Related

How to implement the find() function in Unix using recursive function (with C)

find [path]
The way what i want it is shown in the attached picture.
I use Mac Terminal.
All i want to do is show the path with find function.
I want to implement the UNIX-find() command in C language.
There's a code i wrote, but I don't understand any of it, and I think it's too messy, so I'll ask you guys for help.
Isn't there a simpler code and one that's easier to understand?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#define MAX_SIZE 1024
void do_ls(char[], char[]);
void print_error(int, char[]);
int main( int ac, char *av[] )
{
if( ac == 1 ) {
print_error(1, NULL);
}
else {
printf("%s\n", av[1]);
do_ls(av[1], av[1]);
}
}
void do_ls( char path[], char dirname[] ) { // recursive func
DIR *dir_ptr;
struct dirent *direntp;
char buffer[MAX_SIZE] = "";
strcat(buffer, path);
if((dir_ptr = opendir(dirname)) == NULL) {
print_error(2, dirname);
}
else {
chdir(dirname);
while((direntp = readdir(dir_ptr)) != NULL) {
if(opendir(direntp->d_name) != NULL) {
if(strcmp(direntp->d_name, ".") != 0 && strcmp(direntp->d_name, "..") != 0) {
strcat(buffer, "/");
strcat(buffer, direntp->d_name);
printf("%s\n", buffer);
do_ls(buffer, direntp->d_name);
strcpy(buffer, dirname);
if(strcmp(buffer, ".") != 0) {
strcpy(buffer, "./");
strcat(buffer, dirname);
}
}
}
else {
printf("%s/%s\n", buffer, direntp->d_name);
if(strcmp(buffer, ".") != 0) {
chdir("..");
strcpy(buffer, ".");
}
}
}
}
}
void print_error(int error_num, char dirname[]) {
switch(error_num) {
case 1:
printf("usage : find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]\n");
printf(" find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]\n");
break;
case 2:
fprintf(stderr, "error: cannot open %s\n", dirname);
break;
}
}
This is my code.
At first, I wrote the code without a buffer, but it didn't work. I thought I could understand it and solve it on my own, but I couldn't write the perfect code because I didn't have enough understand about C.
You have a function that has a 5 level of nested conditional blocks, which is too much, because it is harder to read. You should try to create smaller functions, with one -or maybe 2- level. Moreover on recursive functions, the less it uses variable, the less it will eat your stack. Small functions often
lead to functions that individually have smaller memory footprint.
The C, unlike python, is not seen as a try something and see if it works. Your second opendir may cause issues. let's say it opens the directory /home/users/toto/work. You opened your directory, and just after you will call do_ls which .... will open /home/users/toto/work an other time. Having the same directory opened twice... well, I have no idea if it works, but it is not good. Do not use opendir to see if a file is a directory, use stat ( man 2 )
You never close your directories. You won't be able to use your find on a big tree. In C you have to release manually every ressources that is not a chunk of memory stack.
Using a recursive function in that case is not a problem, in my opinion it is easier to implement this kind of program recursively. But it would be better to open a directory, store all it's content, close the directory, and then read through the stored content. So you always keep as few directory as possible.
Create a function that is dedicated to concatenante path components ( and which will ), when beginning, smalls functionnalities like this are often kinda boring to debug and you may want to have a dedicated and easy to test function to do this job.

Program to print directories using c not working, issues with recursive call

I need to create a program that basically acts similarly to the list utility on Linux. I've been trying to get this to work and I'm pretty close but now I've gotten stuck. Essentially it will print whatever files and sub-directories that are contained withing a directory(i.e. if i run ./project3, it lists whatevers in that directory). However, once I try to get the recursive call working it spits out something like:
sh: 1: /home/RageKage/Documents/Project3/dir1: Permission denied
That's where I'm stuck, I'm not exactly sure what to do from here. I'm getting the path of the directory to explore using realpath and that works fine, but the recursive call just isn't working and I'm not exactly sure what I'm doing wrong. Any help would be appreciated as I'm relatively new to this.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <limits.h>
int main (int argc, char *argv[])
{
DIR *dir;
struct dirent *sd;
const char *direct;
char buf[PATH_MAX + 1];
if (argc < 2)
{
direct = ".";
}else{
direct = argv[1];
//printf("Hey this is argv[1]: %s\n", argv[1]);
}
dir = opendir(direct);
if (dir == NULL)
{
printf("ERROR! NO DIRECTORY TO OPEN!\n");
exit(1);
}
while( (sd=readdir(dir)) != NULL )
{
if (!strcmp(sd->d_name, ".") || !strcmp(sd->d_name, ".."))
{
}else{
printf("\n>> %s\n", sd->d_name);
}
if (!strcmp(sd->d_name, "..") || !strcmp(sd->d_name, "."))
{
}else if (sd->d_type == 4){
printf("Attempting to Run!\n");
realpath(sd->d_name, buf);
printf("[%s]\n", buf);
system(("./project3 %s", buf));
printf("\n");
}
}
closedir(dir);
return 0;
}
system(("./project3 %s", buf));
Are you recursively calling the program itself again? That sounds a bit inefficient, and hard to do since you'd need to know where the executable file is. In general it could be just about anywhere (starting with /bin, /usr/bin etc.), and all you are likely to get in argv[0] is the filename part, not the whole path.
Also, as said in the comments, func((this, that)) is the same as func(that), not func(this, that), since the parenthesis make the comma act as the comma operator, not as an argument separator. And system() only takes one argument anyway, so you'd need to use sprintf() to build the command line. (Or perhaps use the exec() functions to actually give separate arguments without invoking a shell, but then you need to do the fork(), too.)
I'd suggest scrapping that idea, and putting the directory tree walking into a function of it's own, and calling that recursively:
void walkpath(void)
{
DIR *dir = opendir(".");
struct dirent *sd;
while((sd = readdir(dir)) != NULL) {
/* ... */
if (sd->d_type == DT_DIR) {
chdir(sd->d_name);
walkpath();
chdir("..");
}
}
}
int main(...)
{
/* ... */
chdir(directory);
walkpath();
}
I used chdir here to change the process's working directory along with the walk. If you need to track the full directory name, then you'll need to add that.
Also, now you have the test for . and .. twice. Use continue to end that iteration of the loop so you don't need to test the same thing again.
if (strcmp(sd->d_name, ".") == 0 || strcmp(sd->d_name, "..") == 0) {
continue;
}

Printing directory: chdir not working

I was wondering if someone could tell me what I'm doing wrong. This code is supposed to walk though all the directories and files and print them out exactly the same way the UNIX utility FIND does. But for some reason I cant get chdir to change the working directory. I'm trying to limit the number of file descriptors im using.
MAIN
#include <stdio.h>
#include "sfind.h"
#include <unistd.h>
#include <dirent.h>
int main(int argv, char *argc[]){
char cwd[1024]; /* current working directory limit*/
char *path = NULL;
DIR *dp = NULL;
if (getcwd(cwd, sizeof(cwd)) != NULL){ /*allow us to grab the current working directory*/
fprintf(stdout, "Current working dir: %s\n", cwd);
}
else{
perror("getcwd() error");
}
dp = opendir(cwd);
path = ".";
directoryList(dp,path);
return 0;
}
Directory Method Definition
#include <stdio.h>
#include <stdlib.h>
#include "sfind.h"
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
void directoryList(DIR *dp, char *path){
char newPath[PATH_MAX] = {0};/*To store new path*/
struct dirent *element; /*get file name*/
struct stat statbuf;/*determine type of file*/
int status = 0; /*My base case should be once the directory I'm in runs outs out of files I should return;*/
if(dp == NULL){
fprintf(stderr,"FILE DID NOT OPEN!");
exit(-1);
}
/*change the current file directory even if its the first one*/
if((status = chdir(path)) == -1){
printf("ERROOR!");
}/*change the current working directory whether that the same on or not*/
while((element = readdir(dp)) != NULL) /*while current file directory pointer is not equal to zero*/{
/* from here we only have two cases once were reading from the directory either is a file or directory!*/
/*using lstat*/
lstat(element->d_name,&statbuf);
if((S_ISDIR(statbuf.st_mode))) /*is of type directory*/{
if((strcmp(".",element->d_name) == 0) || (strcmp("..",element->d_name) == 0))
continue;
/*create new directory name*/
newPath[0] = '\0';
strcat(newPath,path);/* this will give us the "."*/
strcat(newPath,"/");
strcat(newPath,element->d_name);
printf("%s\n", newPath);
directoryList(dp,newPath); /*recursion*/
file*/
}
else /*Its a file!*/{
printf("%s/%s\n",path,element->d_name);
}
}
}
The issue seems to be with the call to readdir(dp)...
Even though you change the current working directory, you don't update the dp pointer to open the new folder.
Here's a poor-man's working example (I wouldn't do it this way, but it works for small trees).
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void directoryList(DIR *dp, char *path) {
char newPath[PATH_MAX] = {0}; /*To store new path*/
struct dirent *element; /*get file name*/
struct stat statbuf; /*determine type of file*/
int status = 0; /*My base case should be once the directory I'm in runs outs
out of files I should return;*/
DIR *dp_tmp;
if (dp == NULL) {
fprintf(stderr, "FILE DID NOT OPEN!");
exit(-1);
}
while ((element = readdir(dp)) !=
NULL) /*while current file directory pointer is not equal to zero*/ {
/* from here we only have two cases once were reading from the directory
* either is a file or directory!*/
/*using lstat*/
lstat(element->d_name, &statbuf);
if ((S_ISDIR(statbuf.st_mode))) /*is of type directory*/ {
if ((strcmp(".", element->d_name) == 0) ||
(strcmp("..", element->d_name) == 0))
continue;
/*create new directory name*/
newPath[0] = '\0';
strcat(newPath, path); /* this will give us the "."*/
strcat(newPath, "/");
strcat(newPath, element->d_name);
printf("%s\n", newPath);
if ((dp_tmp = opendir(newPath)) == NULL) {
perror("hmm?! ");
exit(1);
}
directoryList(dp_tmp, newPath); /*recursion*/
} else /*Its a file!*/ {
printf("* %s/%s\n", path, element->d_name);
}
}
closedir(dp);
}
int main(void) {
char cwd[1024]; /* current working directory limit*/
char *path = NULL;
DIR *dp = NULL;
if (getcwd(cwd, sizeof(cwd)) !=
NULL) { /*allow us to grab the current working directory*/
fprintf(stdout, "Current working dir: %s\n", cwd);
} else {
perror("getcwd() error");
}
dp = opendir(cwd);
path = ".";
directoryList(dp, path);
return 0;
}
EDIT:
To answer the question in the comment...
Open directories (that should be closed using closedir) are different (and quite unrelated) to the current working directory.
The current working directory is mostly used to resolve the path to any file/folder you're referencing.
Open directory pointers (DIR *) are just pointers to data in the memory. That data relates to a specific directory and you can open a number of directories at the same time.
EDIT2:
A few people in the comments recommended nftw (file tree walk) which is a great alternative to doing it yourself.
If this isn't a learning project, I would recommend it's use.
However, note that POSIX.1-2008 marks ftw as obsolete, so make sure to use the nftw flavor.
Is your goal to learn to implement this yourself, or do you just want results? Because you should take a look at fts.h if you want some very powerful stuff to implement something like find.

How to code my own version of mv (rename/move) unix command in C language?

I want to write my own code for move(mv) Unix command. I am completely new to C language and apparently lost on how to fix my code. I want to perform actions like renaming a file if both the inputs are file names. If the the dest_folder is a directory I would like to move the file into the directory.
But I am unable to fix code for the particular problem as I am not much familiar with directories and C in particular. The program takes 2 inputs source and destination after which it performs necessary functions. I am apparently able to rename my files but I am unable to move the file to a particular folder for some reason I don't know?
Need help with moving file to a particular directory.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define SBUF 256
#define DBUF 256
int main(int ac, char *argv[])
{
DIR* dir_ptr; // the directory
struct dirent* direntp;
if( ac == 1 )
{
printf("Usage: %s MOVE\n", argv[0] );
exit(0);
}
if(ac>1 && ac<3)
{
printf("Error! few arguments provided " );
exit(0);
}
char src_folder[SBUF];
char dest_folder[DBUF];
strcpy(src_folder, argv[1]);
strcpy(dest_folder, argv[2]);
dir_ptr = opendir("."); //open directory
if ( dir_ptr == NULL )
{
perror( "." );
exit( 1 );
}
while( (direntp = readdir( dir_ptr )) != NULL )
{
if ( strcmp(direntp->d_name, dest_folder) !=0) //search file or directory
{
printf("found the file %s", dest_folder);
break;
}else
printf("not found");
break;
}
rename(src_folder, dest_folder);
closedir( dir_ptr );
return 0;
}
rename(3) does not work the way you want it to work (I don't know why, ask the committee). You cannot do a rename(some_file, some_directory), just as the man-page says.
Just use stat(2) (or lstat(2) if necessary) and check what you have been given. Here is a short, runnable sketch.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
// check if it is the same inode on the same device
#define SAME_INODE(a, b) ((a).st_ino == (b).st_ino && (a).st_dev == (b).st_dev)
// ALL CHECKS OMMITTED!
int main(int argc, char **argv)
{
struct stat statbuf_src, statbuf_dest;
char *src, *dest, *new_src, *new_dest;
char *current_directory;
if (argc != 3) {
fprintf(stderr, "usage: %s src dest\n", argv[0]);
exit(EXIT_FAILURE);
}
// work on copy
src = malloc(strlen(argv[1]) + 1);
dest = malloc(strlen(argv[2]) + 1);
strcpy(src, argv[1]);
strcpy(dest, argv[2]);
stat(src, &statbuf_src);
stat(dest, &statbuf_dest);
// there are many more, of course
printf("\"%s\" is a ", src);
if (S_ISREG(statbuf_src.st_mode)) {
puts("a regular file");
}
if (S_ISDIR(statbuf_src.st_mode)) {
puts("a directory");
}
printf("\"%s\" is a ", dest);
if (S_ISREG(statbuf_dest.st_mode)) {
puts("a regular file");
}
if (S_ISDIR(statbuf_dest.st_mode)) {
puts("a directory");
}
if (SAME_INODE(statbuf_dest, statbuf_src)) {
printf("%s and %s are the identical\n", src, dest);
}
// if that is not set you have to do it by hand:
// climb up the tree, concatenating names until the inodes are the same
current_directory = getenv("PWD");
printf("current directory is \"%s\"\n", current_directory);
// I'm pretty sure it can be done in a much more elegant way
new_src = malloc(strlen(src) + 1 + strlen(current_directory) + 1);
strcpy(new_src,current_directory);
strcat(new_src,"/");
strcat(new_src,src);
printf("new_src = %s\n",new_src);
new_dest = malloc(strlen(dest) + 1 + strlen(current_directory) + 1 + strlen(src) + 1);
strcpy(new_dest,current_directory);
strcat(new_dest,"/");
strcat(new_dest,dest);
strcat(new_dest,"/");
strcat(new_dest,src);
printf("new_dest = %s\n",new_dest);
if(rename(new_src,new_dest) != 0){
fprintf(stderr,"rename failed with error %s\n",strerror(errno));
}
free(new_src);
free(new_dest);
free(src);
free(dest);
exit(EXIT_SUCCESS);
}
Edit: added code for the desciption below
At the end you have a the path where you are, the information if the arguments given are directories or regular files and the path. If the source is a regular file and the destination a directory, you concatenate the path with the name of the regular file, the path with the name of the directory and the name of the regular file (your source)
Out of
Path = /home/foo
src = bar
dest = coffee
build
new_src = /home/foo/bar
new_dest = /home/foo/coffee/bar
Such that the call to rename() is
rename(new_src, new_dest);
That way you rename a regular file to a regular file which rename() accepts.
Please be aware that rename() does not work across every filesystem, but most.
Like you know, mv is implemented by rename. rename is a atomic system call that can rename a file to a file , an emtpy directory to an empty directory or a directory to a directory(the dest must be nonentity). So there are following situation to deal with:
mv file1 file2 - use rename function
mv dir1 dir2(nonentity or empty) - use rename function
mv dir1 dir2(not empty) - rename dir1 to dir2/dir1
mv file dir(exist) - rename file to dir/file
mv dir file - illegal operation
can you understand?

Searching Through a Directory to see if a File Exists in C

I'm trying to search for a file in a directory which are both specififed by the user on the command line when executing my program. It should look into the directory specified, and also check in sub-directories within that directory and recursively search for the file.
I have print statements in here trying to analyze the variables being passed around and how they're changing. Within my while loop, it's never reaching the checks for if it's a file or just the else statement saying it wasn't found. The check for if it's a directory is true every time, which is obviously not the case.
Thank you for any help. I'm not very familiar/comfortable with dirent and stat so I've been trying to review and make sure I'm using them correctly in the meantime.
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
void traverse(char *dir, char *file) {
DIR *directory;
struct dirent *structure;
struct stat info;
printf("Current directory to search through is: %s\n", dir);
printf("Current file to search for is: %s\n", file);
printf("\n");
printf("\n");
// make sure the directory can be opened
if((directory = opendir(dir)) == NULL) {
fprintf(stderr, "The directory could not be opened. %s\n", strerror(errno));
return;
}
chdir(dir); // change to the directory
while((structure = readdir(directory)) != NULL) { // loop through it
fprintf(stderr, "before the change it is: %s\n", dir);
lstat(structure->d_name, &info); // get the name of the next item
if(S_ISDIR(info.st_mode)) { // is it a directory?
printf("checking if it's a directory\n");
if(strcmp(".", structure->d_name) == 0 ||
strcmp("..", structure->d_name) == 0)
continue; // ignore the . and .. directories
dir = structure->d_name;
fprintf(stderr, "after the change it is: %s\n", dir);
printf("About to recurse...\n");
printf("\n");
traverse(structure->d_name, file); // recursively traverse through that directory as well
}
else if(S_ISREG(info.st_mode)) { // is it a file?
printf("checking if it's a file\n");
if(strcmp(file, structure->d_name) == 0) { // is it what they're searching for?
printf("The file was found.\n");
}
}
else {
printf("The file was nout found.\n");
}
}
closedir(directory);
}
int main(int argc, char *argv[]) {
// make sure they entered enough arguments
if (argc < 3) {
fprintf(stderr, "You didn't enter enough arguments on the command line!\n");
return 3;
}
traverse(argv[2], argv[1]);
}
There is a POSIX function for tree walking like this. It's called nftw().
It provides a callback mechanism and it also detects links caused by badly constructed symbolic link chaining.
So I'd recommend you use that rather than the way you're doing it.
As usual man nftw will explain it's operation in detail. The standard Linux/Unix include file is ftw.h.
Note their was a function called ftw() which is apparently obsolete now.
As Andrew Medico noted: You chdir down into directories but never go back up. Thus, just insert
chdir(".."); // change back to upper directory
between the end of the while loop and the end of the traverse() function.

Resources