Implementation of tree command (DOS) on Linux in C - c

I have to implement the tree command from DOS on Linux in C, so that when I run ./tree in a directory it prints it like this:
adir
bdir
cdir
d.java
edir
fdir
g.c
hdir
My current code looks like this, but it's not even terminating when I run it, but I don't know how to fix it. Please help me solve it!
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#define MAXLEN 256
char currentDir[256];
const char *pathname;
void tree(DIR *dp, struct dirent *dp1, char currentDir[256], int spaces);
void treeHelper(DIR *dp, struct dirent *dp1, char currentDir[256], int spaces);
int main(int argc, char* argv[]) {
DIR *dp = opendir(".");
struct dirent *dp1 = readdir(dp);
int spaces = 0;
//char currentDir[256];
getcwd(currentDir, sizeof(currentDir));
while(dp1 != NULL) {
if (strcmp(dp1->d_name, ".") == 0 || strcmp(dp1->d_name, "..") == 0) {
continue;
}
if (dp1->d_type == DT_REG) {
printf("%s\n", dp1->d_name);
}
if (dp1->d_type == DT_DIR) {
tree(dp, dp1, currentDir, spaces);
}
}
closedir(dp);
}
void tree(DIR *dp, struct dirent *dp1, char currentDir[256], int spaces) {
int i = 0;
strcat(currentDir, "/");
strcat(currentDir, dp1->d_name);
if((chdir(currentDir)) == -1){
chdir("..");
getcwd(currentDir, sizeof(currentDir));
strcat(currentDir, "/");
strcat(currentDir, dp1->d_name);
for(i = 0; i < spaces; i++) {
printf (" ");
}
printf("%s (subdirectory)\n", dp1->d_name);
spaces++;
treeHelper(dp, dp1, currentDir, spaces);
}
else {
for(i = 0; i < spaces; i++) {
printf(" ");
}
printf("%s (subdirectory)\n", dp1->d_name);
chdir(currentDir);
spaces++;
treeHelper(dp, dp1, currentDir, spaces);
}
}
void treeHelper(DIR *dp, struct dirent *dp1, char currentDir[256], int spaces) {
int i = 0;
while((dp1 = readdir(dp)) != NULL){
if(strcmp(dp1->d_name, ".") == 0 || strcmp(dp1->d_name, "..") == 0)
continue;
//stat(currentPath, &statbuf);
/*if(dit->d_type == 8 && argv[1] != NULL){*/
if(dp1->d_type == DT_REG){
for(i = 0; i < spaces; i++) {
printf(" ");
}
printf("%s\n", dp1->d_name);
}
/*if(dit->d_type == 4)*/
if(dp1->d_type == DT_DIR)
tree(dp, dp1, currentDir, spaces);
}
}
Thank you!

My current code looks like this, but it's not even terminating when I
run it…
struct dirent *dp1 = readdir(dp);
…
while(dp1 != NULL) {
You call readdir() only once and then have an endless loop operating on the same dirent again and again. Change that to
struct dirent *dp1;
…
while (dp1 = readdir(dp))
{

Related

Sorting and printing directories and subdirectories in C

I'm trying to sort a current working directory, sub-directories, and then display them. My program behavior begins the process and works for the first sub-directory but none after that. A few prints are to check behavior of the code. While there is a complete working version of this on stackoverflow, I just want to better understand where my code logic is failing. Any help is greatly appreciated. Here is the code:
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
void sortArr(char array[256][256], int amt) {
char temp[1000];
for(int i=0; i<amt; i++) {
for(int j=0; j<(amt-1-i); j++) {
if(strcmp(array[j], array[j+1]) > 0) {
strcpy(temp, array[j]);
strcpy(array[j], array[j+1]);
strcpy(array[j+1], temp);
}
}
}
printf("SortList: %s\n", array[0]);
}
void build(char *s_path, const int root) {
char path[1000];
strcat(path, s_path);
int amt=0,index;
struct dirent *dp;
DIR *dir =opendir(path);
char list[256][256];
struct stat statbuf;
if(!dir)
return;
while((dp =readdir(dir)) != NULL) {
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) {
strcpy(list[amt], dp->d_name);
amt++;
}
}
sortArr(list, amt);
for(int i=0; i <amt;i++) {
for (index=0; index<root; index++) {
if (index%2 == 0 || index == 0)
printf("%c", ' ');
else
printf(" ");
}
printf("%c %s\n", '-', list[i]);
strcpy(path, s_path);
strcat(path, "/");
strcat(path, list[i]);
stat(path, &statbuf);
if((S_ISDIR(statbuf.st_mode)) != 0 && (S_ISREG(statbuf.st_mode)) ==0) {
printf("DIR: %s\n", path);
build(path,root +2);
}
}
closedir(dir);
}
int main() {
build(".", 0);
};
Based on my comments.
The key problem is that you've no idea what's in path when you call build(), so concatenating on the end does nothing reliable. Either add path[0] = '\0'; before the call to strcat(), or (simpler) use strcpy().
That fixes your problems as long as your directories do not have more than 256 entries (other than . and ..) in them.
If you do have bigger directories, you run into problems. Those would be resolved by using dynamic memory allocation — malloc() et al, and using strdup(), probably. That which you allocate you should free, too, of course.
You should check that the calls to stat() succeed, but they won't often fail. One time they could fail is if a file is removed between the when readdir() returns the name and the time when the code calls stat() for the file.
There are POSIX tools for scanning directories. These include:
scandir()
nftw()
Amended source code (including some uncommented upon changes):
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static
void sortArr(char array[356][256], int amt)
{
char temp[1000];
for (int i = 0; i < amt; i++)
{
for (int j = 0; j < (amt - 1 - i); j++)
{
if (strcmp(array[j], array[j + 1]) > 0)
{
strcpy(temp, array[j]);
strcpy(array[j], array[j + 1]);
strcpy(array[j + 1], temp);
}
}
}
printf("SortList: %s\n", array[0]);
}
static
void build(const char *s_path, int root)
{
char path[1000];
strcpy(path, s_path);
int amt = 0;
struct dirent *dp;
DIR *dir = opendir(path);
char list[356][256];
struct stat statbuf;
if (!dir)
return;
while ((dp = readdir(dir)) != NULL)
{
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
{
strcpy(list[amt], dp->d_name);
amt++;
}
}
sortArr(list, amt);
for (int i = 0; i < amt; i++)
{
printf("%*s", root, "");
/*for (int index = 0; index < root; index++)*/
/* putchar(' ');*/
printf("%c %s\n", '-', list[i]);
strcpy(path, s_path);
strcat(path, "/");
strcat(path, list[i]);
if (stat(path, &statbuf) != 0)
{
fprintf(stderr, "failed to stat name '%s' (%d: %s)\n", path, errno, strerror(errno));
continue;
}
if ((S_ISDIR(statbuf.st_mode)) != 0 && (S_ISREG(statbuf.st_mode)) == 0)
{
printf("DIR: %s\n", path);
build(path, root + 2);
}
}
closedir(dir);
}
int main(void)
{
build(".", 0);
}

How to find a file in a directory and its subdirectories in C on Linux?

I want to write a program that given a filename searches in the current directory and its subdirectories for that file. The names of the subdirectories containing that file should be printed.
But my code prints just the subdirectories.
#include <dirent.h>
#include <stdio.h>
#include <string.h>
void listDir(char *path, char *name)
{
DIR *dir;
struct dirent *ent;
if ((dir = opendir(path)) != NULL) {
while (( ent = readdir(dir)) != NULL) {
if (ent->d_type == DT_REG && name == ent->d_name) {
printf("%s\n", ent->d_name);
}
if (ent->d_type == DT_DIR && strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0) {
printf("%s\n", ent->d_name);
listDir("/home/dir1/ent->d_name", name);
}
}
closedir(dir);
}
}
void main()
{
listDir("/home/dir1", "file1");
}
You either need to keep track of the full path, or descend into each directory. (if you are looking at foo/bar/baz, then opendir("baz") will fail if you are in the top level directory). Something like:
#include <dirent.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
void
listDir(char* path, const char *name)
{
DIR* dir;
struct dirent *ent;
char *end = strrchr(path, '\0');
if( (dir = opendir(path)) == NULL ){
perror(path);
return;
} else while( (ent = readdir(dir)) != NULL ){
if( ent->d_type == DT_DIR && strcmp(ent->d_name, ".")
&& strcmp(ent->d_name, "..") )
{
snprintf(end, PATH_MAX - (end - path), "/%s",
ent->d_name);
listDir(path, name);
*end = '\0';
} else if( !strcmp(ent->d_name, name) ){
printf("%s\n", path);
}
}
closedir(dir);
}
int
main(int argc, char **argv)
{
char path[PATH_MAX];
char *defaults[] = {NULL, NULL, ".", NULL};
if( argc < 2 ){
printf("usage: %s name [path...]", argv[0]);
return EXIT_SUCCESS;
}
const char *name = argv[1];
if( argc < 3 ){
argv = defaults;
}
for( argv += 2; *argv; argv += 1 ){
/* getcwd(fullpath, PATH_MAX); */
snprintf(path, PATH_MAX, "%s", *argv);
listDir(path, name);
}
return 0;
}
There are multiple problems in your code:
the comparison name == ent->d_name does not work for string pointers, you should use !strcmp(name, ent->d_name).
listDir("/home/dir1/ent->d_name", name); does not magically concatenate strings. You should use snprintf or asprintf (on linux and BSD systems) to construct the subdirectory name.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void listDir(const char *path, const char *name) {
DIR *dir;
struct dirent *ent;
if ((dir = opendir(path)) != NULL) {
while ((ent = readdir(dir)) != NULL) {
if (ent->d_type == DT_REG && !strcmp(name, ent->d_name)) {
printf("%s\n", path);
}
if (ent->d_type == DT_DIR && strcmp(ent->d_name, ".")
&& strcmp(ent->d_name, "..")) {
char *subdir;
size_t len = strlen(path);
const char *sep = "/";
if (len && path[len - 1] == '/')
sep++;
if (asprintf(&subdir, "%s%s%s", path, sep, ent->d_name) > 0) {
listDir(subdir, name);
free(subdir);
}
}
}
closedir(dir);
}
}
int main(int argc, char *argv[]) {
if (argc == 3) {
listDir(argv[1], argv[2]);
} else {
fprintf(stderr, "usage: %s FILE DIR\n", argv[0]);
}
return 0;
}

How to count only the number of directories from a path

I am trying to count only the directories from a path, but it doesn't work. So, i don't want to number both files and directories, i want only the directories. Could you help me, please?
The code:
int listdir(char *dir) {
struct dirent *dp;
struct stat s;
DIR *fd;
int count = 0;
if ((fd = opendir(dir)) == NULL) {
fprintf(stderr, "listdir: can't open %s\n", dir);
}
while ((dp = readdir(fd)) != NULL) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
stat(dp->d_name, &s);
if (S_ISDIR(s.st_mode))
count++;
}
closedir(fd);
return count;
}
your stat() call will fail, since you are not in the correct directory. This you can solve by either changing the current directory, or generate full paths and give to stat as argument.
Some Unixes, you can optimize away the stat call by looking at struct dirent, the d_type field
int listdir(char *dir) {
struct dirent *dp;
struct stat s;
DIR *fd;
int count = 0;
if ((fd = opendir(dir)) == NULL) {
fprintf(stderr, "listdir: can't open %s\n", dir);
}
chdir (dir); /* needed for stat to work */
while ((dp = readdir(fd)) != NULL) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
#ifdef _DIRENT_HAVE_D_TYPE
switch (dp->d_type)
{
case DT_UNKNOWN:
stat(dp->d_name, &s);
if (S_ISDIR(s.st_mode)) count++;
break;
case DT_DIR:
count++;
break;
}
#else
stat(dp->d_name, &s);
if (S_ISDIR(s.st_mode)) count++;
#endif
}
closedir(fd);
return count;
}
I think you want...
written in C
count the number of directoriesfrom a path.
the counting function will returns int value.
I have no idea about your environment, so it is just a example solution.
If you can use glob, it is so easy to count the number of directories. that is: main.c
#include <stdio.h>
#include <glob.h>
#include <string.h>
#define MAX_PATH 1023
int countDirectories(char* dir)
{
char path[MAX_PATH] = "";
strcat(path, dir);
strcat(path, "/*");
glob_t globbuf;
long i, count = 0;
if (glob(path, GLOB_NOSORT | GLOB_ONLYDIR, NULL, &globbuf) == 0)
{
count = globbuf.gl_pathc;
for (i = 0; i < globbuf.gl_pathc; i++)
{
count += countDirectories(globbuf.gl_pathv[i]);
}
}
globfree(&globbuf);
return count;
}
int main(int argc, char* argv[])
{
int count;
if (argc > 1)
{
count = countDirectories(argv[1]);
}
else
{
count = countDirectories(".");
}
printf("there are %d directories.\n", count);
return 0;
}
and you can try this like:
> gcc main.c -o testglob
> ./testglob /path/to/target/dir
then you'll receive output like this:
path = /path/to/target/dir/*, there are N directories
thanks.

Search and Print all non-duplicate struct names inside input file

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* Search and Print all non-duplicate struct names inside input file */
int main(int argc, char *argv[])
{
char temp[64], buf[64], filename[128], array[1024] = "";
char *ptr, *line = NULL;
char *tmp1, *tmp2;
ssize_t rv;
size_t len;
int count = 0;
FILE *fp;
if (argc < 2) {
printf("enter file name at cmd line...\n");
return -1;
}
sprintf(filename, argv[1], strlen(argv[1]));
fp = fopen(argv[1], "r");
if (!fp) {
printf("File could not be opened: %s\n", argv[1]);
return -1;
}
while ((rv = getline(&line, &len, fp)) != -1) {
ptr = strstr(line, "struct");
if (ptr) {
ptr += strlen("struct");
while (*ptr == ' ')
ptr++;
tmp1 = strchr(ptr, ' ');
tmp2 = strchr(ptr, ';');
len = 0;
if (tmp1 == NULL && tmp2 == NULL) {
continue;
}
else if (tmp1 == NULL && tmp2 != NULL) {
len = tmp2 - ptr;
}
else if (tmp1 != NULL && tmp2 == NULL) {
len = tmp1 - ptr;
}
else if (tmp1 && tmp2) {
len = tmp1 < tmp2 ? tmp1 - ptr : tmp2 - ptr;
}
if (len) {
snprintf(temp, len+1, "%s", ptr);
if (!strstr(array, temp)) {
sprintf(buf, "%2d. ", count++);
strcat(buf, temp);
strcat(array, buf);
strcat(array, "\n");
}
}
}
}
fclose(fp);
if (line)
free(line);
printf("%s\n", array);
return 0;
}
Above program finds struct names correctly, however I see chars like , and ) at the end of output names. How to remove it? Below is sample output:
[root#mnm-server programs]# ./a.out /usr/src/linux/drivers/net/ethernet/smsc/smsc911x.c
0. smsc911x_data
1. smsc911x_ops
2. smsc911x_platform_config
3. phy_device
4. mii_bus
5. net_device
6. napi_struct
7. regulator_bulk_data
8. clk
9. platform_device
10. smsc911x_data,
11. sk_buff
12. net_device_stats
13. netdev_hw_addr
14. sockaddr
15. ethtool_drvinfo
16. ethtool_eeprom
17. ethtool_ops
18. net_device_ops
19. ures,
20. resource
21. device_node
22. smsc911x_data))
23. dev_pm_ops
24. of_device_id
25. platform_driver
Notice output of line 10 and 22. One approach would be to do strchr for ,, ), ; and remove char from end. However, this is not a clean solution if the number of non-alphabetic characters increases.
NOTE: The best solution I found for this is here.
Thanks to inputs from Daniel Jour, the following code handles all cases of struct name* ptr;, struct name{ };, struct { };
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
/* Search and Print all non-duplicate struct names inside input file */
int main(int argc, char *argv[])
{
char temp[64], buf[64], filename[128], array[1024] = "";
char *ptr, *line = NULL;
size_t len;
int count = 0, flag = 0;
FILE *fp;
if (argc < 2) {
printf("enter file name at cmd line...\n");
return EXIT_FAILURE;
}
sprintf(filename, argv[1], strlen(argv[1]));
fp = fopen(argv[1], "r");
if (!fp) {
printf("File could not be opened: %s\n", argv[1]);
return EXIT_FAILURE;
}
while ((getline(&line, &len, fp)) != -1) {
ptr = flag ? line : strstr(line, "struct ");
if (ptr) {
if (!flag)
ptr += strlen("struct ");
while (*ptr == ' ')
ptr++;
len = 0;
while (isalnum(*ptr) || *ptr == '_' || *ptr == '{' || *ptr == '}') {
if (*ptr == '{') {
flag++;
}
else if (*ptr == '}') {
len = 0;
flag--;
do {
ptr++;
} while (*ptr == ' ');
ptr--;
}
else if ((*ptr != '{') || (*ptr != '}')) {
len++;
}
ptr++;
}
if (len && !flag) {
ptr -= len;
snprintf(temp, len+1, "%s", ptr);
if (!strstr(array, temp)) {
sprintf(buf, "%2d. ", count++);
strcat(buf, temp);
strcat(array, buf);
strcat(array, "\n");
}
}
}
}
fclose(fp);
if (line)
free(line);
printf("%s\n", array);
return EXIT_SUCCESS;
}
This program doesn't handle cases like func(struct x, struct y), interested users can fix it or just use grep -o "struct [^ ;,)]\+" # | awk '{print $2}' | sort -u. Output of the above program for pre-processed file hworld.i:
[root#server]# cat hworld.c
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
[root#server]# gcc -Wall --save-temps hworld.c
[root#server]# ./find_structs hworld.i
0. _IO_FILE
1. _IO_marker
2. _IO_FILE_plus

segmentation fault while freeing memory

Here is a program I made this program has segmentation fault I checked it in gdb in the second last of code free(somepath); .I do not have any reason for why is this segmentation fault coming?
Some one please suggest some thing.
#include<dirent.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<stdio.h>
char *directs[20], *files[20];
int i = 0;
int j = 0;
int count = 0;
void printdir(char *);
int count_dirs(char *);
int count_files(char *);
void new_printdir(int ,int ,char *);
int main()
{
char startdir[20];
printf("Scanning user directories\n");
scanf("%s", startdir);
printdir(startdir);
}
void printdir(char *dir)
{
DIR *dp = opendir(dir);
int nDirs, nFiles, nD, nF;
nDirs = 0;
nFiles = 0;
nD = 0;
nF = 0;
if (dp) {
struct dirent *entry = 0;
struct stat statBuf;
nDirs = count_dirs(dir);
nFiles = count_files(dir);
new_printdir(nDirs,nFiles,dir);
while ((entry = readdir(dp)) != 0) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
char * filepath = malloc(strlen(dir) + strlen(entry->d_name) + 2);
if (filepath) {
sprintf(filepath, "%s/%s", dir, entry->d_name);
if (lstat(filepath, &statBuf) == 0) {
if (S_ISDIR(statBuf.st_mode)) {
printdir(filepath);
}
else {
}
}
}
free(filepath);
} //2nd while
closedir(dp);
}
else {
fprintf(stderr, "Error, cannot open directory %s\n", dir);
}
} //printdir
int count_dirs(char *dir)
{
DIR *dp = opendir(dir);
int nD;
nD = 0;
if (dp) {
struct dirent *entry = 0;
struct stat statBuf;
while ((entry = readdir(dp)) != 0) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
char *filepath = malloc(strlen(dir) + strlen(entry->d_name) + 2);
if (filepath) {
sprintf(filepath, "%s/%s", dir, entry->d_name);
if (lstat(filepath, &statBuf) != 0) {
fprintf(stderr, "File Not found? %s\n",filepath);
}
if (S_ISDIR(statBuf.st_mode)) {
nD++;
} else {
continue;
}
free(filepath);
}
}
closedir(dp);
} else {
fprintf(stderr, "Error, cannot open directory %s\n", dir);
}
return nD;
}
int count_files(char *dir)
{
DIR *dp = opendir(dir);
int nF;
nF = 0;
if (dp) {
struct dirent *entry = 0;
struct stat statBuf;
while ((entry = readdir(dp)) != 0) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
char *filepath =
malloc(strlen(dir) + strlen(entry->d_name) + 2);
if (filepath) {
sprintf(filepath, "%s/%s", dir, entry->d_name);
if (lstat(filepath, &statBuf) != 0) {
fprintf(stderr, "File Not found? %s\n", filepath);
}
if (S_ISDIR(statBuf.st_mode)) {
continue;
} else {
nF++;
}
free(filepath);
}
}
closedir(dp);
} else {
fprintf(stderr, "Error, cannot open file %s\n", dir);
}
return nF;
}
void new_printdir(int nDirs,int nFiles,char *dir)
{
struct dirent **namelist;
DIR *dop;
int i, j,t,re,nD,nF;
char userd[20],*somepath;
i = scandir(dir, &namelist, 0, alphasort);
t=0;
if (i < 0)
perror ("Scandir failed to open directory I hope you understand \n");
else {
for (j = 0; j < i; j++) {
if(strcmp(".",namelist[j]->d_name)==0||strcmp("..",namelist[j]->d_name)==0)
continue;
somepath = malloc(strlen(dir)+strlen(namelist[j]->d_name)+2);
sprintf(somepath,"%s/%s",dir,namelist[j]->d_name);
dop=opendir(somepath);
if(dop)
{
nD++;
if ((nDirs-nD)<3)
{printf("%s ",namelist[j]->d_name);}
}
else {
nF++;
if ((nFiles-nF)<3)
printf("%s ",namelist[j]->d_name);
}
closedir(dop);
free(namelist[j]);
}
}
free(namelist);
free(somepath);
}
Why is this segmentation fault happening this is not clear to me.
What can I do to get rid of it?
In your code, you are not guaranteed to allocate memory and assign it to somepath (you also fail to free somepath except for the last iteration of the loop). You should put your free(somepath) statement at the end of the for-loop, and you should initialize somepath to NULL both at the very beginning of the function and at the start of each for loop to avoid similar careless errors.
You're not initializing the somepath variable - it has an undefined value unless the malloc() executes (i.e. for empty folders). You should initialize it to NULL at the place of definition.
You do not initialize somepath to 0 (NULL), but you could under some circumstances release the value. This means you release a random value - which is not a good idea.

Resources