#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.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 folder[1000];
int filecheck[1000];
int coun = 0;
int e = 0;
int f = 0;
int u = 0;
int p = 20;
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] = malloc(strlen(entry->d_name) + 1);
folder[e] = coun; // Mark where the folder is.
e++;
f++;
strcpy(word[coun++], entry->d_name);
/* Recurse at a new indent level */
printdir(entry->d_name,depth+1);
f--;
}
else
{
if (f == 0)
{
filecheck[u] = coun;
u++;
}
word[coun] = malloc(strlen(entry->d_name) + 1);
strcpy(word[coun++], entry->d_name);
}
}
chdir("..");
closedir(dp);
}
int main(int argc, char* argv[])
{
word = calloc(1000, sizeof(*word));
printdir(".", 0);
printf("now, print the words in the order.\n");
int c = 0;
int l = 0;
for (int i = 0; i < coun; ++i) // Start readjusting the array in alphabetical order.
{
if (i == folder[c]) // If i matches with a folder, then increment and skip sorting.
{
c++;
}
else // If next is a file, then start the sorting.
{
int imin = i;
for (int j = i + 1; j < folder[c]; ++j) // Keep sorting until the folder ends.
{
char word1[1000], word2[1000];
strcpy(word1, word[j]);
strcpy(word2, word[imin]);
for(int p = 0; word1[p]; p++)
{
word1[p] = tolower(word1[p]);
}
for(int p = 0; word2[p]; p++)
{
word2[p] = tolower(word2[p]);
}
if (strcmp(word1, word2) < 0)
{
imin = j;
}
}
char *tmp = word[i];
word[i] = word[imin];
word[imin] = tmp;
}
}
c = 0;
l = 0;
printf(".\n");
for (int i = 0; i < coun; ++i) // Print the array in the specified format.
{
if (i == folder[c]) // If the folder is the same, print start of the folder.
{
printf("- %s\n", word[i]);
c++;
}
else if (i == filecheck[l]) // If it's a file, print the file normally.
{
printf("- %s\n", word[i]);
l++;
}
else // Print the file inside the folder.
{
printf(" - %s\n", word[i]);
}
}
exit(0);
}
My program is supposed to print the current directory in the tree format. It goes through the sorting perfectly fine, except for the final folder it's checking. For some reason, the hw2 works fine, but hw1 does not sort.
now, print the words in the order.
.
- hw2
- find.c
- ls.c
- Makefile
- tree.c
- hw1
- grep.c
- factor.c
- uniq.c
- monster.c
- sort.c
- tree.c
- tree
I feel like I'm missing something, but I can't find out how to make it work. I've tried different methods and non have worked. Is there anything I can do?
Related
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);
}
I want to read all .txt files in the directory and add those file names to an array. Catching the text files part is okay but I am having a problem storing those file names inside an array. What is the mistake I've done here? This is my code.
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <strings.h>
int main()
{
DIR *p;
struct dirent *pp;
p = opendir ("./");
char file_list[10][10];
char shades[10][10];
int i = 0;
if (p != NULL)
{
while ((pp = readdir (p))!=NULL) {
int length = strlen(pp->d_name);
if (strncmp(pp->d_name + length - 4, ".txt", 4) == 0) {
puts (pp->d_name);
strcpy(shades[i], pp->d_name);
}
}
i = i + 1;
(void) closedir (p);
for(int i=0; i<4; i++){
printf("\n %s", &shades[i]);
}
}
return(0);
}
What seems to be a problem here is that 'strings' in C works much different than in C++ or C# or Python.
To make it work you'll need to alloc memory for each string.
If I understood correctly what you want to do, here's an example:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#define FILE_LIST_MAX 10
int main()
{
DIR *p = opendir("./");
struct dirent *pp;
char **file_list = malloc(FILE_LIST_MAX * sizeof(char*));
int i = 0;
if (p == NULL) {
printf("Could not open current directory" );
return 0;
}
while ((pp = readdir(p)) != NULL) {
int length = strlen(pp->d_name);
printf ("%s\n", pp->d_name);
if(length > 4 && memcmp(pp->d_name + length - 4, ".txt", 4) == 0) {
char filename[length];
sprintf(filename, "%s", pp->d_name);
file_list[i] = malloc(length * sizeof(char));
strcpy(file_list[i], filename);
i++;
}
}
(void) closedir(p);
printf("\n.txt filenames within array");
for(int j = 0; j < i; j++) {
printf("\n%s", file_list[j]);
}
for(int j = 0; j < i; j++) {
free(file_list[j]);
}
free(file_list);
return(0);
}
It's not perfect though as you must manually change file_list capacity in FILE_LIST_MAX
Here's an example output from executing this code:
Much better approach would be implementing a dynamic array of chars which will automatically resizes as necessary.
I have a C code snippet:
I have used dirent.h and used opendir and readdir to read the files
I am doing a ls command duplicate using c code ,
for that the file should be is ascending order and case sensitivity also to be included.
#include <stdio.h>
#include <dirent.h>
int main(void)
{
DIR *d;
struct dirent *dir;
d = opendir(".");
printf("d: %d \n",d);
if (d)
{
while ((dir = readdir(d)) != NULL)
{
printf("\n %s \n", dir->d_name);
}
closedir(d);
}
else
{
printf("soory");
}
return (0);
}
I got result as
krishna-R
stat.c
temp
Bing
TempFolder
button
What i want is
Bing
button
krishna-R
stat.c
TempFolder
temp
#include <stdio.h>
#include <dirent.h>
#include <string.h>
int main(void)
{
char array[50][30]={};
int i=0, j=0, k=0;
DIR *d;
struct dirent *dir;
d = opendir(".");
printf("d: %d \n",d);
if (d)
{
while ((dir = readdir(d)) != NULL)
{
strcpy(array[i], dir->d_name);
i++;
}
for (k = 0; k <= i; k++) /* Sorting files alphabetically */
for (j = k + 1; j <= i; j++)
{
if (strcasecmp(array[k], array[j]) > 0)
{
strcpy(tmp, array[k]);
strcpy(array[k], array[j]);
strcpy(array[j], tmp);
}
}
closedir(d);
}
for (int a = 0 ; a < i ; a++)
{
printf("%s \n",array[a]);
}
return 0;
}
My results for cd and history:
sgraham#myshell:/home/class/sgraham/proj1>cd .. (works fine)
sgraham#myshell:/home/class/sgraham>cd .. (not working)
sgraham#myshell:/home/class/sgraham>cd .. (not working)
cd: Too many arguments
sgraham#myshell:/home/class/sgraham>history
sgraham#myshell:/home/class/sgraham> (not printing out history)
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define MAX_COMMAND_SIZE 80
#define MAX_ARGS 9
#define HIS_SIZE 100
typedef struct
{
int argument; // userCom arguments
char *arg[MAX_ARGS + 1]; // userCom arguments array
char *history[HIS_SIZE]; //history array
char *input; // hold input file
char *output; // hold output file
} Command;
int main()
{
Command userCom = {0}; //holds userCom struct
const char *whitespace = " \n\r\t\v\f"; // userCom delimiting chars
char* username = getenv("USER"); //Get user name
char* curDirect = getenv("HOME"); //get cwd
char* token[MAX_ARGS];
char* cwd;
char* buf;
char* cmd;
char buffer[MAX_COMMAND_SIZE + 1]; //hold userCom line
//char history[HIS_SIZE][MAX_ARGS +1]; //2D array for history
int tok = 0;
int new;
int i;
int limit;
int last = 0;
int hist = 0; //initialize history size to 0
long size;
struct stat buff; //holds file information
//gets current working directory and also changes buffer if necessary
size = pathconf(".", _PC_PATH_MAX);
if ((buf = (char *)malloc((size_t)size)) != NULL)
cwd = getcwd(buf, (size_t)size);
while(1){
//prints users prompt
printf("\n%s#myshell:%s>", username,cwd);
//gets string from userCom line
fgets(buffer, MAX_COMMAND_SIZE + 1, stdin);
buffer[strlen(buffer)-1] = 0 ;//set null
//set-up history function
userCom.history[last% HIS_SIZE] = cmd;
last++;
//parses tokens and looks for delimiters
token[tok] = strtok(buffer,whitespace);
while(token[tok])
{
++tok;
if(tok == MAX_ARGS)
printf("Reached MAX userCom arguments");
break;
token[tok] = strtok(NULL, whitespace);
}
i =0;
//sort tokens based on special characters
for (;i<tok;++i)
{
if(!strcmp(token[i], "<"))
{
userCom.output = token[++i];
}
else if(!strcmp(token[i], ">"))
{
userCom.input = token[++i];
}
else if (token[i][0] == '$')
{
char* toktok = getenv((const char*)&token[i][1]);
if (!toktok)
{
printf("%s: ERROR: variable.\n", token[i]);
return 0;
}
else
{
userCom.arg[userCom.argument] = toktok;
++(userCom.argument);
}
}
else
{
userCom.arg[userCom.argument] = token[i];
++(userCom.argument);
}
}
tok = 0;
userCom.arg[userCom.argument] = 0;
//handles the "cd" command
if((strcmp(userCom.arg[0],"cd") == 0))
{
if (userCom.argument > 2)
printf("cd: Too many arguments\n");
// change directories if valid target and update cwd
else if (userCom.argument == 1)
{
new = chdir(cwd);
if (new != 0)
printf("%s: No such file or directory\n");
// if no argument is given, new directory should be $HOME
else
{
new = chdir(curDirect);
// get the new current working directory
size = pathconf(".", _PC_PATH_MAX);
if ((buf = (char *)malloc((size_t)size)) != NULL)
cwd = getcwd(buf, (size_t)size);
}
}
}//end "cd" function
//handles the echo command
else if(strcmp(userCom.arg[0], "echo") == 0)
{
int p;
for(p=1;p < userCom.argument; ++p)
printf("%s ", userCom.arg[p]);
}//ends echo function
//handles exit
else if(strcmp(userCom.arg[0], "exit") == 0)
{
exit(0);
}
//handles history functions
else if(strcmp(userCom.arg[0], "history") == 0)
{
int j;
for(j = last, limit = 0; userCom.history[j] != NULL && limit != HIS_SIZE ; j = (j -1)% HIS_SIZE, limit++)
printf(" %s ",userCom.history[j]);
}
else {
break;
}
}//end while 1 loop
return 0;
}//End int main
while(token[tok])
{
++tok;
if(tok == MAX_ARGS)
printf("Reached MAX userCom arguments");
break;
token[tok] = strtok(NULL, whitespace);
}
This loop breaks at the first time. Probably the if should be like
if(tok == MAX_ARGS) {
printf("Reached MAX userCom arguments");
break;
}
Also userCom.argument is never set back to zero, which causes the cd .. to work first time.
I am working with hashtables for the first time and I think I have a basic understanding of how they work. I am using a hashtable to check to see if a word exists in a file. The program takes in a "dictionary" file and a word check file. The program works fine when I have a small dictionary but when I use a very large one, the words get overwritten. I was hoping to get some insight as to why. Here is my code:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <pthread.h>
#include <tgmath.h>
#include <ctype.h>
#include "hashtable_constants.h"
#define HASH_SIZE 500
#define MAX_WORD_SIZE 50
struct hashTable {
int collisions;
char** words;
};
struct hashTable hashTables[HASH_SIZE];
int hashKey(char * str)
{
int key = 0;
for(int j = 0; j <= 51; j++)
{
if(str[j] == '\0')
break;
key += (int)str[j];
}
key = key % HASH_SIZE;
return key;
}
int main(int argc, char** argv)
{
if(argc > 3)
{
fprintf(stderr, "Too many arguments!\n");
return -1;
}
else if(argc < 3)
{
fprintf(stderr, "Not enough arguments!\n");
return -1;
}
FILE *dictionary = fopen(argv[1], "r");
FILE *wordCheck = fopen(argv[2], "r");
if(dictionary == NULL || wordCheck == NULL ) //ensure input file exists
{
fprintf(stderr, "Error accessing input files\n");
return -1;
}
for(int i = 0; i < HASH_SIZE; i++)
{
hashTables[i].collisions = 0;
hashTables[i].words = malloc(HASH_SIZE * MAX_WORD_SIZE);
}
struct stat fileStat1;
struct stat fileStat2;
stat(argv[1], &fileStat1);
stat(argv[2], &fileStat2);
char* dictBuffer = (char*)malloc(fileStat1.st_size + 1);
char* wordCheckBuff = (char*)malloc(fileStat2.st_size + 1);
if (dictBuffer == NULL || wordCheckBuff == NULL)
{
fprintf (stderr, "Memory error");
return -1;
}
fread(dictBuffer, 1, (int)fileStat1.st_size, dictionary);
fread(wordCheckBuff, 1, (int)fileStat2.st_size, wordCheck);
char* word = malloc(MAX_WORD_SIZE + 1);
int count = 0;
for(int i = 0; i < (int)fileStat1.st_size; i++)
{
char c = dictBuffer[i];
if(isspace(c))
{
word[count] = '\0';
char* wordToAdd = word;
int key = hashKey(wordToAdd);
int collisionIndex = hashTables[key].collisions;
hashTables[key].words[collisionIndex] = wordToAdd;
hashTables[key].collisions++;
count = 0;
free(word);
word = malloc(MAX_WORD_SIZE + 1);
//printf("Added: %s to hashtable at key: %d\n",word,key);
}
else
{
word[count] = c;
count++;
}
}
count = 0;
for(int i = 0; i < (int)fileStat2.st_size; i++)
{
char c = wordCheckBuff[i];
if(isspace(c))
{
word[count] = '\0';
char* wordToCheck = word;
int key = hashKey(wordToCheck);
int collisionIndex = hashTables[key].collisions;
int foundWord = 0;
for(int j = 0; j < collisionIndex; j++)
{
if(hashTables[key].words[j] == wordToCheck)
{
printf("%s == %s\n",hashTables[key].words[j], wordToCheck);
foundWord = 1;
break;
}
}
if(foundWord == 0)
printf("Not a word: %s\n", wordToCheck);
/*else
printf("Key: %d -- Is a word: %s\n",key, word);*/
free(word);
word = malloc(MAX_WORD_SIZE + 1);
count = 0;
}
else
{
word[count] = c;
count++;
}
}
for(int i = 0; i < HASH_SIZE; i++)
free(hashTables[i].words);
free(word);
fclose(dictionary);
fclose(wordCheck);
printf("done\n");
return 0;
}
On problem is that in the line:
hashTables[key].words[collisionIndex] = wordToAdd;
You add 'wordToAdd' to the table.
But wordToAdd is equal to word. A few lines later you call
free(word);
So the hash table now holds a pointer to freed memory.
This will lead to all sorts of undefined behaviour in the program, quite possibly seg-faults too. Also it's very likely that since the memory is now 'free', a subsequent call to malloc might return this same pointer again - which you will then fill with another word. Hence you see the overwriting of strings.
You need to review how you use 'malloc' / 'free' generally in the program. If you want a pointer to refer to a valid string, you cannot call 'free' on that pointer during the intended lifetime of that string.
What you want to do is malloc each string, and add the pointers to the hashtable. Then when you've finished with the hashtable, and no longer need the string data, then call 'free' on all the pointers contained within it. In your case, this will probably need to be in your cleanup code at the end of your program's execution.