So I'm looping through readdir() function calls and adding the resulting file name to a new node in a linked list. After fixing an issue by setting file_list = add_file_node(), I'm running into and issue where the dir_list loop is having problems accessing the directory.
hls: cannot access hls: cannot access h: No such file or directory
code:
#include "header.h"
/**
* main - main ls function
*
* #argc: argument count
* #argv: argument vector
*
* Return: 0, or the errno of the error
*/
int main(int argc, char **argv)
{
struct dirent *read;
char dir[400], error_message[400], format, hidden;
int i, j, dir_count, max_src_bytes = 397;
dir_list_t *dir_list, *dir_node;
file_list_t *file_list;
DIR *dirp;
int errno;
format = ' ';
hidden = ' ';
dir_count = 0;
strcpy(dir, ".");
for (i = 1; i < argc; i++)
{
if (argv[i][0] == '-')
{
for (j = 1; argv[i][j]; j++)
{
if (argv[i][j] == '1')
format = '1';
else if (argv[i][j] == 'l')
format = 'l';
if (argv[i][j] == 'a')
hidden = 'a';
else if (argv[i][j] == 'A')
hidden = 'A';
}
}
else
{
memset(dir, 0, strlen(dir));
strcpy(dir, argv[i]);
dir_list = add_dir_list(&dir_list, dir);
dir_count++;
}
}
if (dir_count == 0)
dir_list = add_dir_list(&dir_list, dir);
for (dir_node = dir_list; dir_node != NULL; dir_node = dir_node->next)
{
dirp = opendir(dir_node->dir);
if (dirp == NULL)
{
strcpy(error_message, "hls: cannot access ");
max_src_bytes = 381;
perror(strncat(error_message, dir_node->dir, max_src_bytes));
return (errno);
}
if (dir_count > 1)
printf("%s:\n", dir_node->dir);
while ((read = readdir(dirp)) != NULL)
{
file_list = add_file_list(&file_list, read->d_name);
}
switch (format)
{
case '1':
print_ls(hidden, '\n', file_list);
break;
case 'l':
print_ls(hidden, '\n', file_list);
break;
default:
print_ls(hidden, '\t', file_list);
}
if (dir_node->next != NULL)
putchar('\n');
free_file_list(&file_list);
}
free_dir_list(&dir_list);
closedir(dirp);
return (0);
}
/**
* print_ls - print contents in the default ls format, i.e. columns
*
* #hidden: parameter denoting the option for revealing hidden files
* #format: printing format parameter
* #dirp: pointer to the directory data
*
* Return: 0 for success, 1 for failure
*/
int print_ls(char hidden, char format, file_list_t *file_list)
{
file_list_t *file_node;
for (file_node = file_list; file_node != NULL; file_node = file_node->next)
{
if (hidden == 'a')
{
printf("%s", file_list->file);
if (file_list->next != NULL)
putchar(format);
}
else if (hidden == 'A')
{
if (strcmp(file_list->file, ".") != 0 &&
strcmp(file_list->file, "..") != 0)
{
printf("%s", file_list->file);
if (file_list->next != NULL)
putchar(format);
}
}
else
{
if (file_list->file[0] != '.')
{
printf("%s", file_list->file); // (line 139)
if (file_list->next != NULL)
putchar(format);
}
}
}
if (format == '\t')
printf("\n");
return (0);
}
add_file_list():
/**
* add_file_list - add a new node at the start of a file_list_t linked list
*
* #head: start of linked list
* #file: file data to add to node
*
* Return: address of new node; NULL if failure
*/
file_list_t *add_file_list(file_list_t **head, const char file[256])
{
file_list_t *node;
node = malloc(sizeof(file_list_t));
if (node == NULL)
return (NULL);
strcpy(node->file, file);
node->next = *head;
node->prev = NULL;
*head = node;
return (node);
}
I'm thinking about trying this out with an array of pointers instead, but I don't want to throw away my code before getting some insight. Am I not inputting the data into the node correctly? If so, how would I do that?
this is wrong, just as valgrind says
file_head = file_list; <<<< file_list is not initlaized, file_head = junk
while ((read = readdir(dirp)) != NULL)
{
printf("read: %s\n", read->d_name);
append_file_list(&file_list, read->d_name);
printf("file_list: %s\n", file_list->file);
}
printf("file_head: %s\n", file_head->file); <<<<<= (line 78) file_head = junk
Related
I'm trying to display the contents of the directory, I am adding the directory name to the array of strings and printing it, but instead of printing the array the program prints "tmp".
void do_ls(char * dir){
struct dirent * entry;
DIR * dp = opendir(dir);
if(dp == NULL){
fprintf(stderr, "Can not open directory:%s\n", dir);
return;
}
setLenAndNum(dir);
char fileNames[totalNumOfFiles][lenOfLongestFile]; //array to store all file names
int i = 0; //index to iterate through filenames
errno = 0;
while((entry = readdir(dp)) != NULL){
if(entry == NULL && errno != 0){
perror("readdir failed");
exit(errno);
}
else{
if(entry->d_name[0] == '.')
continue;
while(i < totalNumOfFiles){
for(int j=0; j<lenOfLongestFile; j++){
fileNames[i][j] = entry->d_name[j];
}
i++;
}
}
}
i = 0;
while(i < totalNumOfFiles){
printf("%s\n", fileNames[i++]);
}
closedir(dp);
}
This is the function in question, the variables totalNumOfFiles and lenOfLongestFile are global variables and they work correctly I have tested them.
Let's see what we have here... My comments... well, in the comments ;)
void do_ls(char * dir) {
struct dirent *entry;
DIR *dp = opendir(dir);
if (dp == NULL){
fprintf(stderr, "Can not open directory:%s\n", dir);
return;
}
// I don't know what function below does
// I assume it sets totalNumOfFiles and lenOfLongestFile;
setLenAndNum(dir);
char fileNames[totalNumOfFiles][lenOfLongestFile]; //array to store all file names
int i = 0; //index to iterate through filenames
// You don't have to set errno to zero
errno = 0;
while ((entry = readdir(dp)) != NULL) {
// Statement above guarantees that the loop won't be run
// when entry == NULL, so what for is the if statement below?
// It's a dead code. get rid of it.
if (entry == NULL && errno != 0) {
perror("readdir failed");
exit(errno);
} else {
if(entry->d_name[0] == '.')
continue;
// Ok, so here we are with a SINGLE file entry, and with this
// SINGLE file entry you fill the WHOLE table with it.
// This is clearly wrong.
while (i < totalNumOfFiles) {
// Also, you should use a strcpy function to copy string.
// It will copy necessary number of bytes and trailing 0.
for(int j=0; j<lenOfLongestFile; j++){
fileNames[i][j] = entry->d_name[j];
}
i++;
}
}
// So here all rows in the table are filled with the last read
// file name
}
i = 0;
while(i < totalNumOfFiles){
printf("%s\n", fileNames[i++]);
}
closedir(dp);
}
Here is corrected code:
void do_ls(char *dir) {
struct dirent *entry;
DIR *dp = opendir(dir);
if (dp == NULL){
fprintf(stderr, "Can not open directory:%s\n", dir);
return;
}
setLenAndNum(dir);
char fileNames[totalNumOfFiles][lenOfLongestFile]; //array to store all file names
int i = 0; //index to iterate through filenames
while ((entry = readdir(dp)) != NULL) {
if (entry->d_name[0] == '.')
continue;
strcpy(fileNames[i], entry->d_name);
i++;
}
i = 0;
while (i < totalNumOfFiles) {
printf("%s\n", fileNames[i++]);
}
closedir(dp);
}
I need to to open and read multiple text files which will be stored similar to binary search tree construction.
NOTE: They all need to be opened and their content stored before user enters his input.
I really do need some advice, because I do not see my errors any more.
However, what I really don't get, is how to make it interactive ?
When I press 'A', it needs to read text from the left node and opposite.
Structure of output:
------------------\n
\n
Title\n
file1 // file present - 'A'
file2 // file empty/not empty - 'B'\n
Text\n
\n
Your choice (A/B):\n
This is how the programs output should look at the beginning:
terminal
Elements of struct
typedef struct _Elements_
{
char* title_;
struct _Elements_* left;
struct _Elements_* right;
char* text_;
} Elements;
//Forward initialization is present
int main (int argc, char* argv[])
{
char returned_value;
Elements element;
if (argc != 2)
{
printf("Usage: ./ass2 [file-name]\n");
return 1;
}
returned_value = openFile(&element, argv[1]);
while(returned_value != ('A' || 'B'))
{
repeatEntry(returned_value); // Function to scan the value
}
// Tricky part! Read from left ???
if(returned_value == 'A')
{
printf("%s",element.left->text_);
}
else
{
printf("%s",element.right->text_);
}
return 0;
}
//Here i wanna initialize node elements
// For every malloc, I have to make sure that it succeeded
Elements* newNode(Elements* element)
{
Elements* newNode = (Elements*)malloc(sizeof(Elements));
if(newNode == NULL)
{
printf("[ERR] Out of memory.\n");
return (void*)2; // void* - to avoid warning of different type?
}
newNode->title_ = NULL;
newNode->left = NULL;
newNode->right = NULL;
newNode->text_ = NULL;
return newNode;
}
//Function that opens the files
char openFile(Elements* element, char* input)
{
// Create new memory if tree is empty
if(element == NULL)
{
return newNode(element);
}
FILE* file_open = fopen(input, "r");
if (file_open == NULL)
{
printf("[ERR] Could not read file %s.\n", input);
return 3;
}
// Local variables to handle the parsing file ?
char line[80];
int lenght_of_the_line; // Was just for me
int line_number = 0; // this also
// Do i really need local variables beside struct ?
char* title_local = NULL;
char* first_file = NULL;
char* second_file = NULL;
char* text_local = NULL;
while(fgets(line, 80, file_open))
{
lenght_of_the_line = strlen(line);
//printf("%d ", lenght_of_the_line);
//printf("%s ", line);
// Get title
if((line[lenght_of_the_line - 1] == '\n') && (line_number == 0))
{
title_local = (char*)malloc(lenght_of_the_line * (sizeof(char)) + 1);
if(title_local == NULL)
{
printf("[ERR] Out of memory.\n");
return 2;
}
strcpy(title_local, line);
printf("%s ", element->title_ = title_local);
printf("\n");
}
// Get 1st file name
else if( (line[lenght_of_the_line - 1] == '\n') && (line_number == 1) )
{
if( (line[lenght_of_the_line - 5] == '.') &&
(line[lenght_of_the_line - 4] == 't') &&
(line[lenght_of_the_line - 3] == 'x') &&
(line[lenght_of_the_line - 2] == 't') )
{
// Allocate enough memory for first file name
first_file = (char*)malloc(lenght_of_the_line * (sizeof(char)) + 1);
if(first_file == NULL)
{
printf("[ERR] Out of memory.\n");
return 2;
}
strcpy(first_file, line);
//name of file to open and store to left node
//Seems to work, since I got no error
openFile(element->left, first_file);
}
}
// Get 2nd file name
else if( (line[lenght_of_the_line - 1] == '\n') && (line_number == 2) )
{
if( (line[lenght_of_the_line - 5] == '.') &&
(line[lenght_of_the_line - 4] == 't') &&
(line[lenght_of_the_line - 3] == 'x') &&
(line[lenght_of_the_line - 2] == 't') )
{
second_file = (char*)malloc(lenght_of_the_line * (sizeof(char)) + 1);
if(second_file == NULL)
{
printf("[ERR] Out of memory.\n");
return 2;
}
strcpy(second_file, line);
openFile(element->right, second_file);
}
}
else
{
text_local = (char*)malloc(lenght_of_the_line * (sizeof(char)) + 1);
if(text_local == NULL)
{
printf("[ERR] Out of memory.\n");
return 2;
}
strcpy(text_local, line);
element->text_ = text_local;
printf("%s", element->text_);
}
// Increase line number for 1
line_number++;
}
//free(line);
fclose(file_open);
printf("\n");
printf("Your choice (A/B)? ");
char user_input;
scanf("%c", &user_input);
// DONT FORGET TO FREE THE MEMORY !
return user_input;
}
This
while(returned_value != ('A' || 'B'))
does not do what you probably expect. It compares the returned value to whatever value your compiler uses to represent true.
Currently your loop is hence likely to seem an endless loop, while waiting for returned_value to be identical to true.
You need
while((returned_value != 'A') && (returned_value != 'B'))
This will leave the loop when returned_value is identical to either 'A' or 'B'.
That way the endless loop problem is solved, which is likely the core problem preventing your file BST from being interactive.
By the way, for users convenience I recommend to also accept 'a' or 'b'.
For avoiding a "try again" after each input, use
scanf(" %c", &user_input);
with a space at the start, for ignoring leading white space, including the newline/return after each entered 'A' or 'B'.
I was wondering if you could help me out I'm really stuck on this code I wrote. I'm supposed to pre-order fill a tree from a file that is structured in the following way.
Hello
goodbye
yes
' ' <- This is an actual space in the file
no
end of file
When ever there is a space I wanna return back to the node previous and go right and return whenever it reaches EOF.
So the text above would be structured in the following way
hello
goodbye null no right child
yes no
This function is supposed to fill my tree in preorder. I'm grabbing the data using my readFile function here
char *readFile(FILE *fp)
{
int c = 0;
int i = 0;
size_t capacity = 10;
char *arr = NULL;
char* temp = NULL;
arr = (char*) malloc(capacity * sizeof(char));
while (((c = fgetc(fp)) != '\n') && (c != EOF))
{
if(capacity == (i+2))
{
capacity *= 2 ;
temp = (char*)realloc(arr,capacity * sizeof(char));
/*Assigns Address to newly allocated array*/
arr = temp;
}
arr[i] = c;
i++;
}
arr[i-1] = '\0';
if(arr[0] == '\r') /* Line spaces to move up*/
{
printf("Got here SPACE\n");
arr[0] = ' ';
}
else if(c == EOF)/*if its an end of file return*/
{
printf("Got here EOF \n");
arr[0] = ' ';
}
return arr;
}
My recursive algorithm to fill the tree is called populate. I'm not really that great at recursion but I've walked though my code multiple times and can't see what's obviously wrong. Currently it seems I can only print the root. Trying to read the left or right root data gives me an immediate seg fault
void populate(FILE *fp,struct node *root)
{
char *data = readFile(fp);
if(data[0] == ' ') /*possible issues with usingn null charater? */ /*statement is true for both EOF and spaces*/
{
return;
}
printf("Creating Node\n");
if(root == NULL)
{
root = createNode();/* create the current root */
root->data = data;/*assign data*/
}
printf("%s\n",data);
populate(fp,root->left);
populate(fp,root->right);
}
my main
int main(int argc, char *argv[])
{
FILE *fp;
node *root = createNode();
fp = fopen("qa.db","r+");
if(fp == NULL)
{
printf("File not open");
}
populate(fp,root);
if(root == NULL){
printf("Equal to NULL\n");
}
printTree(root);
/*check = checkFile(fp)*/
fclose(fp);
return 0;
I've got a problem with reading words from file and passing it to binary tree. When I debug it, it says:
Unhandled exception at 0x76E7773B(ntdll.dll) in Projekt.exe: 0.C00000005:
Access violation reading location 0x0037902A.
Here is the source code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
typedef struct Tree {
int val;
char *word;
struct Tree *left;
struct Tree *right;
} Tree;
void show(Tree *hd) {
if (hd != NULL) {
show(hd->left);
show(hd->right);
printf("%s -- %d\n", hd->word, hd->val);
}
}
void zero(Tree *aTree) {
if (aTree == NULL)
return;
zero(aTree->left);
free(aTree);
zero(aTree->right);
}
int alpha(char *word1, char *word2) {
if (word1[0] == 0 && word2[0] == 0)
return 2;
else
if (word1[0] == word2[0])
return alpha(&word1[1], &word2[1]);
else
if (word1[0] < word2[0])
return 1;
else
return 0;
}
Tree *create(char *word) {
Tree *temp;
temp = (Tree*)malloc(sizeof(Tree));
temp->left = temp->right = NULL;
temp->val = 1;
temp->word = (char*)malloc(sizeof(char));
strcpy(temp->word, word);
return temp;
}
Tree *insert(Tree *aTree, char *word) {
if (aTree == NULL) {
aTree = create(word);
} else
if (alpha(aTree->word, word) == 0) {
aTree->left = insert(aTree->left,word);
} else
if (alpha(aTree->word, word) == 1) {
aTree->right = insert(aTree->right, word);
} else
if (alpha(aTree->word, word) == 2) {
aTree->val++;
}
return aTree;
}
int main(int argc, char *argv[]) {
Tree *myTree = NULL;
char buffer[256] = { 0 };
char temp = 0;
int i = 0;
FILE *fp = fopen(argv[1], "r");
if (fp) {
while (temp != EOF) {
temp = getc(fp);
temp = toupper(temp);
if (temp >= 65 && temp <= 90) {
buffer[i] = temp;
i++;
} else {
if (buffer[0] != 0) {
puts(buffer);
myTree = insert(myTree, buffer);
memset(buffer, 0, sizeof(buffer));
i = 0;
}
}
}
}
fclose(fp);
show(myTree);
return 0;
}
Your program has several problems:
in function zero, you free the pointer too soon, you should move the free(aTree); as the last statement, otherwise you invoke undefined behavior, possibly a crash (but not the one you have, since you never call this function):
void zero(Tree *aTree) {
if (aTree != NULL) {
zero(aTree->left);
zero(aTree->right);
free(aTree);
}
In function alpha, you use recursion where a simple loop would suffice. The compiler may convert this to a loop, but it does have to. This is not a bug but why not use a more idiomatic approach such as:
int alpha(const char *word1, const char *word2) {
for (size_t i = 0;; i++) {
if (word1[i] == '\0' && word2[i] == '\0')
return 2;
if (word1[i] == word2[i])
continue;
if (word1[i] < word2[i])
return 1;
else
return 0;
}
}
In function create, you allocate a single byte for the string, this is definitely a cause for the crash. You should allocate strlen(word) + 1 or use strdup(word). You should not cast the return value of malloc() either:
Tree *create(const char *word) {
Tree *temp;
temp = malloc(sizeof(Tree));
temp->left = temp->right = NULL;
temp->val = 1;
temp->word = strdup(word);
return temp;
}
In function insert you call alpha multiple times, this is inefficient: you could use a switch statement:
Tree *insert(Tree *aTree, const char *word) {
if (aTree == NULL) {
return create(word);
switch (alpha(aTree->word, word)) {
case 0:
aTree->left = insert(aTree->left, word);
break;
case 1:
aTree->right = insert(aTree->right, word);
break;
case 2:
aTree->val++;
break;
}
}
return aTree;
}
function main has multiple issues:
You do not check if argv[1] is provided to the program. It would be NULL if the program is run without a command line argument.
Your test for end of file is incorrect: temp should be defined as int and you should test its value after reading the byte from the file with getc(), it is idiomatic to name c a variable used for this.
You should use character literals instead of hard coded ASCII values.
the test if (c >= 'A' && c <= 'Z') would work for ASCII, which is almost universal today, but it is more reliable to use isupper(c) instead.
You do not need to clear the buffer, setting a '\0' at the end before inserting the word is enough.
You should also check for buffer overflow and refuse to handle words longer than 255 characters.
You should not call fclose(fp) when fp is NULL, this is undefined behavior.
Here is a corrected version:
int main(int argc, char *argv[]) {
Tree *myTree = NULL;
char buffer[256];
int c;
size_t i;
FILE *fp;
if (argc < 2) {
printf("missing argument\n");
return 2;
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
printf("cannot open %s\n", argv[1]);
return 1;
}
i = 0;
while ((c = getc(fp)) != EOF) {
c = toupper(c);
if (isupper(c)) {
if (i < sizeof(buffer))
buffer[i] = c;
i++;
} else {
if (i > 0 && i < sizeof(buffer)) {
buffer[i] = '\0';
puts(buffer);
myTree = insert(myTree, buffer);
i = 0;
}
}
}
fclose(fp);
show(myTree);
return 0;
}
Incorrect code to check if a word can be made of smaller given words (word break).This is the code I wrote for the above mentioned problem, however an online judge declares it as incorrect, what could be the possible reasons? And how should I modify my code?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Node structure */
typedef struct node {
int letter[26];
struct node* next[26];
int is_word;
} node;
/* Create node */
node* getnode(void) {
node* p = malloc(sizeof(node));
int i;
for (i = 0; i < 1004; i++) {
p->letter[i] = 0;
p->next[i] = NULL;
}
p->is_word = 0;
return p;
}
/* make dictionary */
void fill_dictionary(char word[], node* start) {
int len = strlen(word), i;
node* temp = start;
for (i = 0; i < len; i++) {
if (temp->letter[word[i] % 'a'] == 0) {
temp->letter[word[i] % 'a'] = 1;
temp->next[word[i] % 'a'] = getnode();
temp = temp->next[word[i] % 'a'];
} else {
temp = temp->next[word[i] % 'a'];
}
}
temp->is_word = 1;
return;
}
int spell_check(char line[100003], node* start) {
int len = strlen(line), i, flag = 0;
node* temp = start;
for (i = 0; i < len; i++) {
if (temp->letter[line[i] % 'a'] == 0) {
return 1;
} else {
temp = temp->next[line[i] % 'a'];
flag = 0;
if (temp->is_word == 1) {
flag = 1;
temp = start;
}
}
}
if (flag == 1) {
return 0;
} else {
return 1;
}
}
int main(void) {
int n, i, ans, m;
scanf("%d %d", &n,&m); // no. of words in dictionary
node* start = getnode();
for (i = 0; i < n; i++) {
char word[11]; // max length of dictionary word
scanf("%s", word);
fill_dictionary(word, start);
}
scanf("%d", &n); // no. of lines to be checked
for (i = 0; i < n; i++) {
char line[100003]; // max length of a line
scanf("%s", line);
ans = spell_check(line, start);
if (ans == 0) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}
Here's one way to to it. This compiles and runs. It displays the parsed result. It tries to read the dictionary from a file called "dictionary.text" in the current directory. You can change it to put the dictionary wherever you want. I commented it heavily to help you understand it but it has some subtle C things you may need to really think about and figure out. One bit of advice: Name everything in a program as extremely accurately for what it is/does as possible (but reasonably succinct). That will help immensely when trying to debug or figure out what you did wrong. Careless names really make code confusing and hard to debug.
Good luck!
Example:
$ gcc -o wordsplitter wordsplitter.c
$ wordsplitter xyzhellogoodbyefoodogcatpigcarwhereareyouhorse
xyz "hello" "goodbye" foo "dog" "cat" pigcar "where" "are" "you" horse
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DICTIONARY_FILEPATH "dictionary.txt"
#define MAX_WORD_SIZE 100
/*
* Error codes (usually this is put in a header file and included)
*/
#define SUCCESS 0
#define FILE_NOT_FOUND -1
#define OUT_OF_MEMORY -2
typedef struct word {
struct word *next;
char *word;
} word_t;
word_t *dictionaryListhead = NULL;
typedef struct wordsubcomponent {
struct wordsubcomponent *next;
char *text;
int isDictionaryWord;
} wordsubcomponent_t;
int
loadDictionaryFromFile(char *filename, word_t **listhead)
{
char wordFromFile[MAX_WORD_SIZE];
word_t *lastWordStored = NULL;
FILE *dictionaryFile = fopen(filename, "r");
if (dictionaryFile == NULL) {
return FILE_NOT_FOUND;
}
while(fgets(wordFromFile, sizeof(wordFromFile), dictionaryFile)) {
word_t *newDictionaryWordNode;
if ((newDictionaryWordNode = calloc(sizeof(word_t), 1)) == NULL) { // calloc automatically zeroes memory
return OUT_OF_MEMORY;
}
char *cp = strchr(wordFromFile, '\n');
if (cp != NULL)
*cp = '\0'; // get rid of trailing \n
newDictionaryWordNode->word = strdup(wordFromFile);
if (*listhead == NULL) {
lastWordStored = *listhead = newDictionaryWordNode;
} else {
lastWordStored = lastWordStored->next = newDictionaryWordNode;
}
}
fclose(dictionaryFile);
return SUCCESS;
}
wordsubcomponent_t
*newsubcomponent() {
wordsubcomponent_t *subcomp = NULL;
if ((subcomp = calloc(sizeof(wordsubcomponent_t), 1)) != NULL) {
subcomp->text = strdup(""); // seed with empty string (instead of NULL) so we can append
} else {
fprintf(stderr, "out of memory (fatal). program exiting\n");
exit(-1);
}
return subcomp;
}
/*
* Returns an linked list of word subcomponents for the given word, split up around dictionary words
*/
wordsubcomponent_t *getWordSubcomponents(char *wordToParse, word_t *listhead) {
wordsubcomponent_t *subcomponents, *currSubcomp;
subcomponents = currSubcomp = newsubcomponent();
for (char *cp = wordToParse; cp < wordToParse + strlen(wordToParse);) { // exit when cp gets to end of word to parse.
int matchFlag = 0;
for (word_t *wordNode = listhead; wordNode != NULL; wordNode = wordNode->next) {
if (strncasecmp(cp, wordNode->word, strlen(wordNode->word)) == 0) { // prefix of cur. ptr is dict word.
if (strlen(currSubcomp->text) != 0) // Detected non-dict text in subcomp.
currSubcomp = currSubcomp->next = newsubcomponent(); // leave in list & add new subcomp for dict word.
currSubcomp->text = wordNode->word; // save dict-word in subcomp
currSubcomp->isDictionaryWord = 1;
currSubcomp = currSubcomp->next = newsubcomponent(); // dict-word in list, so get new subcomp
cp += strlen(wordNode->word); // advance cp past extracted dict-word
matchFlag = 1;
break; // break out of inner-loop
}
}
if (!matchFlag) { // No dict-word found at cp
char oneNullTerminatedLetter[2] = { *cp++, '\0' }; // put 1st ltr into NULL-terminated string & adv cp.
strcat(currSubcomp->text, oneNullTerminatedLetter); // append letter-as-string to curr subcomp
}
}
return subcomponents;
}
void
dumpDictionary(word_t *listhead) {
printf("\nList of dictionary words:\n");
printf("----------------\n");
for (word_t *wordNode = listhead; wordNode != NULL; wordNode = wordNode->next) {
printf(" %s\n", wordNode->word);
}
printf("----------------\n\n");
}
int
main(int argc, char **argv)
{
int status;
if ((status = loadDictionaryFromFile(DICTIONARY_FILEPATH, &dictionaryListhead)) < 0) {
switch(status) {
case FILE_NOT_FOUND:
fprintf(stderr, "Error accessing dictionary: %s\n", argv[0]);
break;
case OUT_OF_MEMORY:
fprintf(stderr, "Out of memory");
break;
}
return EXIT_FAILURE;
}
/*
* Load dictionary first so we can show them the list of words if they didn't
* pass in a command line argument with the word to parse.
*/
if (argc < 2) {
fprintf(stderr, "Usage: %s <word_to_parse>\n\n", argv[0]);
dumpDictionary(dictionaryListhead);
return EXIT_FAILURE;
}
wordsubcomponent_t *subcomp = getWordSubcomponents(argv[1], dictionaryListhead);
while(subcomp != NULL && strlen(subcomp->text) > 0) {
if (subcomp->isDictionaryWord)
printf("\"%s\" ", subcomp->text);
else
printf("%s ", subcomp->text);
subcomp = subcomp->next;
}
printf("\n");
return EXIT_SUCCESS;
}
#nerdist colony:
There is a resource leak in loadDictionaryFromFile. This means a file pointer was not closed when returning from this function in case of an error.
Here is a corrected copy of this function
int loadDictionaryFromFile(char *filename, word_t **listhead)
{
char wordFromFile[MAX_WORD_SIZE];
word_t *lastWordStored = NULL;
FILE *dictionaryFile = fopen(filename, "r");
if (dictionaryFile == NULL) {
return FILE_NOT_FOUND;
}
while(fgets(wordFromFile, sizeof(wordFromFile), dictionaryFile)) {
word_t *newDictionaryWordNode;
if ((newDictionaryWordNode = calloc(sizeof(word_t), 1)) == NULL) { // calloc automatically zeroes memory
fclose(dictionaryFile); // <-- Close the file pointer
return OUT_OF_MEMORY;
}
char *cp = strchr(wordFromFile, '\n');
if (cp != NULL)
*cp = '\0'; // get rid of trailing \n
newDictionaryWordNode->word = strdup(wordFromFile);
if (*listhead == NULL) {
lastWordStored = *listhead = newDictionaryWordNode;
} else {
lastWordStored = lastWordStored->next = newDictionaryWordNode;
}
}
fclose(dictionaryFile);
return SUCCESS;
}