C Custom Database writing errors - c

I have an assignment for class that I have to write a program to read and write key, value pairs to disk. I am using a linked list to store the keys, and read in values whenever I need to from disk. However, I am having trouble changing and deleting values. I am using this to test it: http://gaming.jhu.edu/~phf/2010/fall/cs120/src/sdbm-examples.tar.gz. Code below. Basically, I need some help figuring out errors, because this is the first assignment we have had to use pointers on, and I am just dying in all the segfaults and everything else. Just some advice would be greatly appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
#include "sdbm.h"
FILE *db;
bool opened = false, needNewDB = false;
int err = 0, keyLen = 0;
char *filename;
typedef struct Key_{
char *name;
char *val;
long offset;
struct Key_ *next;
} Key;
Key *head = NULL,*tail = NULL, *lastHas = NULL, *beforeLastHas = NULL;
/**
* Create new database with given name. You still have
* to sdbm_open() the database to access it. Return true
* on success, false on failure.
*/
void listAdd() {
if (tail != NULL) {
tail->next = (Key *) malloc(sizeof(Key));
tail = tail->next;
}
else {
tail = (Key *)malloc(sizeof(Key));
head = tail;
}
tail->next = NULL;
tail->name = NULL;
tail->val = NULL;
}
bool sdbm_create( const char *name ) { //Errors: 1) fopen failed 2) fclose failed on new db
filename = malloc(sizeof(*name));
strcpy(filename,name);
FILE *temp = fopen(name, "w");
if (temp == NULL) {
printf("Couldn't create file %s\n",name);
err = 1;
return false;
}
if (fclose(temp) == EOF) {
printf("Couldn't close created file %s\n",name);
err = 2;
return false;
}
return true;
}
/**
* Open existing database with given name. Return true on
* success, false on failure.
*/
bool sdbm_open( const char *name ) { //Errors: 3) couldn't open database
db = fopen(name,"r+");
if (db == NULL) {
err = 3;
printf("Couldn't open database file %s\n",name);
return false;
}
opened = true;
int c;
bool inKey = true;
char currKey[MAX_KEY_LENGTH];
while ((c = getc(db)) != EOF) {
if (!inKey && c == '\0') {
inKey = true;
}
else if (inKey && c == '\0') {
currKey[keyLen] = '\0';
listAdd();
tail->offset = ftell(db);
tail->name = malloc(sizeof(*currKey));
strcpy(tail->name,currKey);
keyLen = 0;
inKey = false;
}
else if (inKey) {
currKey[keyLen] = c;
keyLen++;
}
}
Key *curr = head;
while (curr != NULL) {
printf("Key: %s\n",curr->name);
curr = curr->next;
}
return true;
}
void readVal(char *value, long offset) {
fseek(db,offset,SEEK_SET);
int c;
for (int i = 0; (c = getc(db)) != '\0'; i++) {
*(value + i) = c;
}
}
/**
* Synchronize all changes in database (if any) to disk.
* Useful if implementation caches intermediate results
* in memory instead of writing them to disk directly.
* Return true on success, false on failure.
*/
bool sdbm_sync() {
if (!needNewDB) {
Key *curr = head;
fseek(db,0,SEEK_END);
while (curr != NULL) {
if (curr->val != NULL) {
fprintf(db,"%s%c%s%c",curr->name,'\0',curr->val,'\0');
}
curr = curr->next;
}
}
else {
FILE *temp;
sdbm_create("tRpdxD.p4ed");
temp = fopen("tRpdxD.p4ed","w");
Key *curr = head;
while (curr != NULL) {
if (curr->val != NULL) {
fprintf(temp,"%s%c%s%c",curr->name, '\0', curr->val, '\0');
}else {
char *tempS = malloc(MAX_VALUE_LENGTH);
readVal(tempS, curr->offset);
fprintf(temp,"%s%c%s%c",curr->name,'\0',tempS,'\0');
free(tempS);
}
fflush(temp);
fflush(db);
curr = curr->next;
}
fclose(db);
remove(filename);
rename("tRpdxD.p4ed",filename);
db = fopen(filename,"r+");
}
fflush(db);
return true;
}
/**
* Close database, synchronizing changes (if any). Return
* true on success, false on failure.
*/
bool sdbm_close() { // Errors: 5) Couldn't close database
sdbm_sync();
Key *tmp = head;
while (head->next != NULL) {
tmp = head;
head = head->next;
free(tmp->name);
if (tmp->val != NULL) {
free(tmp->val);
}
free(tmp);
}
if (fclose(db) == EOF) {
err = 5;
printf("Couldn't close database.\n");
return false;
}
return true;
}
/**
* Return error code for last failed database operation.
*/
int sdbm_error() {
return err;
}
/**
* Is given key in database?
*/
bool sdbm_has( const char *key ) {
if (head == NULL) {
return false;
}
Key *curr = head;
lastHas = NULL;
beforeLastHas = NULL;
while (curr != NULL) {
if (!strcmp(curr->name,key)) {
lastHas = curr;
return true;
}
beforeLastHas = curr;
curr = curr->next;
}
return false;
}
/**
* Get value associated with given key in database.
* Return true on success, false on failure.
*
* Precondition: sdbm_has(key)
*/
bool sdbm_get( const char *key, char *value ) { //Errors: 6)Don't have key
if (!sdbm_has(key)) {
printf("Precondition sdbm_has(%s) failed", key);
err = 6;
return false;
}
readVal(value, lastHas->offset);
return true;
}
/**
* Update value associated with given key in database
* to given value. Return true on success, false on
* failure.
*
* Precondition: sdbm_has(key)
*/
bool sdbm_put( const char *key, const char *value ) {
if (!sdbm_has(key)) {
printf("Precondition !sdbm_has(%s) failed",key);
err = 7;
return false;
}
sdbm_remove(key);
sdbm_insert(key,value);
return true;
}
/**
* Insert given key and value into database as a new
* association. Return true on success, false on
* failure.
*
* Precondition: !sdbm_has(key)
*/
bool sdbm_insert( const char *key, const char *value ) { //Errors: 7)Already have key 8)Invalid key or value length
if (sdbm_has(key)) {
printf("Precondition !sdbm_has(%s) failed",key);
err = 7;
return false;
}
if (strlen(key) < MIN_KEY_LENGTH || strlen(key) > MAX_KEY_LENGTH || strlen(value) < MIN_VALUE_LENGTH || strlen(value) > MAX_VALUE_LENGTH) {
printf("Invalid key or value length");
err = 8;
return false;
}
listAdd();
tail->name = (char *)key;
tail->val = malloc(sizeof(*value));
strcpy(tail->val,value);
return true;
}
/**
* Remove given key and associated value from database.
* Return true on success, false on failure.
*
* Precondition: sdbm_has(key)
*/
bool sdbm_remove( const char *key ) {
if (!sdbm_has(key)) {
printf("Precondition !sdbm_has(%s) failed",key);
err = 7;
return false;
}
needNewDB = true;
if (beforeLastHas == NULL) {
head = lastHas->next;
}
else if (lastHas->next == NULL) {
tail = beforeLastHas;
}
else {
beforeLastHas->next = lastHas->next;
}
if (lastHas->val != NULL) {
free(lastHas->val);
}
free(lastHas->name);
free(lastHas);
return true;
}

There's a lot of errors in this code. To name just one:
filename = malloc(sizeof(*name));
*name is the first element of name, so it's a char, so sizeof(*name) == 1. To get the size of a string, use strlen(name) + 1. Better yet, use strdup if your system has it.

i would advise against global variables. you can not change the program (in the future) to use two of your databases in parallel.
so all your sdbm_xxx functions should get (or infer) all necessary values on their own.

Related

Cant insert Node to binary tree

I am trying to insert Node to Binary tree. This is my function for creating Node (rest is done).
void BVSCreate_function(TNodef *rootPtr, function_save token) {
TNodef *newPtr = malloc(sizeof(struct tnodef));
if (newPtr == NULL) {
fprintf(stderr, "99");
return;
}
TNodef init;
string initStr;
initStr.str = NULL;
initStr.length = 0;
initStr.alloc = 0;
newPtr = &init;
newPtr->content = &initStr;
newPtr->leftPtr = NULL;
newPtr->rightPtr = NULL;
newPtr->return_type = token.ret_value;
newPtr->parameters = token.param_count;
strCpyStr(newPtr->content, token.content);
rootPtr = newPtr;
}
void BVSInsert_function(TNodef *rootPtr, function_save token) {
if (rootPtr == NULL) {
BVSCreate_function(rootPtr, token);
} else {
if ((strCmpStr(token.content, rootPtr->content)) < 0) {
BVSCreate_function(rootPtr->leftPtr, token);
} else
if ((strCmpStr(token.content, rootPtr->content)) > 0) {
BVSCreate_function(rootPtr->rightPtr, token);
}
}
}
When TNodef and function_save are structs:
typedef struct {
string *content;
int param_count;
int ret_value;
} function_save;
typedef struct tnodef {
string *content;
struct tnodef *leftPtr;
struct tnodef *rightPtr;
int parameters;
int return_type;
} TNodef;
Where string is defined as this struct:
typedef struct {
char *str; // content of string
int length; // length of string
int alloc; // amount of memory allocated
} string;
strCpystr function :
int strCpyStr(string *s1, string *s2) {
int len2 = s2->length;
if (len2 > s1->alloc) {
if (((s1->str) = (char *)realloc(s1->str, len2 + 1)) == NULL) {
return 1;
}
s1->alloc = len2 + 1;
}
strcpy(s1->str, s2->str);
s1->length = len2 + 1;
return 0;
}
I am trying to create a node in binary tree and put there information from struct function_save.
But when I try to print this tree after insert it shows me that tree is still empty.
Your code in BVSCreate_function has undefined behavior because:
newPtr = &init; discards the allocated node and instead uses a local structure that will become invalid as soon as the function returns.
newPtr->content = &initStr; is incorrect for the same reason: you should allocate memory for the string too or possibly modify the TNodeDef to make content a string object instead of a pointer.
Function BVSInsert_function does not return the updated root pointer, hence the caller's root node is never updated. You could change the API, passing the address of the pointer to be updated.
There is also a confusion in BVSInsert_function: it should call itself recursively when walking down the tree instead of calling BVSCreate_function.
Here is a modified version:
/* Allocate the node and return 1 if successful, -1 on failure */
int BVSCreate_function(TNodef **rootPtr, function_save token) {
TNodef *newPtr = malloc(sizeof(*newPtr));
string *newStr = malloc(sizeof(*content));
if (newPtr == NULL || newStr == NULL) {
fprintf(stderr, "99");
free(newPtr);
free(newStr);
return -1;
}
newStr->str = NULL;
newStr->length = 0;
newStr->alloc = 0;
newPtr->content = newStr;
newPtr->leftPtr = NULL;
newPtr->rightPtr = NULL;
newPtr->return_type = token.ret_value;
newPtr->parameters = token.param_count;
strCpyStr(newPtr->content, token.content);
*rootPtr = newPtr;
return 1;
}
int BVSInsert_function(TNodef **rootPtr, function_save token) {
if (*rootPtr == NULL) {
return BVSCreate_function(rootPtr, token);
} else {
if (strCmpStr(token.content, rootPtr->content) < 0) {
return BVSInsert_function(&rootPtr->leftPtr, token);
} else
if ((strCmpStr(token.content, rootPtr->content)) > 0) {
return BVSInsert_function(&rootPtr->rightPtr, token);
} else {
/* function is already present: return 0 */
return 0;
}
}
}
Note also that function strCpyStr may write beyond the end of the allocated area is len2 == s1->alloc, assuming s1->len is the length of the string, excluding the null terminator.
Here is a modified version:
int strCpyStr(string *s1, const string *s2) {
int len2 = s2->length;
if (len2 >= s1->alloc) {
char *newstr = (char *)realloc(s1->str, len2 + 1);
if (newstr == NULL) {
return 1;
}
s1->str = newstr;
s1->alloc = len2 + 1;
}
strcpy(s1->str, s2->str);
s1->length = len2;
return 0;
}

Heap buffer overflow on a getline() - C

I am coding a local server, I need to parse a file to get the config of the server.
Problem : I have a heap buffer overflow indicated at on the while.
This probeme is shown when I run with -fsanitize but I don't have any trouble without.
Here is the code :
struct container *configParse(FILE *file)
{
char *line = NULL;
size_t n;
char *token = NULL;
char *saveptr = NULL;
struct container *head = NULL;
struct container *container = NULL;
int key = 0;
int first = 1;
while ((getline(&line, &n, file)) != -1)
{
token = strtok_r(saveptr, " =\n\r", &line);
while (token != NULL)
{
if (token[0] == '[')
{
if (first)
{
container = container_init();
container->title = token;
head = container;
first = 0;
}
else
{
container = container_add_back(container);
container = container->next;
container->item = NULL;
container->title = token;
}
key = 0;
}
else
{
if (key == 0)
{
if (container->item == NULL)
{
container->item = items_init();
container->item->key = token;
}
else
{
struct item *itemcpy = container->item;
while (itemcpy->next != NULL)
{
itemcpy = itemcpy->next;
}
itemcpy->next = items_init();
itemcpy->next->key = token;
}
key = 1;
}
else
{
struct item *itemcpy = container->item;
while (itemcpy->next != NULL)
{
itemcpy = itemcpy->next;
}
itemcpy->value = token;
key = 0;
}
}
token = strtok_r(NULL, " =\n\r", &line);
}
}
container_print(head);
printf("\n*****Parsing du .conf*****\n\n");
if (isvalid(head))
printf("Parsing OK\n");
else
{
printf("Parsing KO\n");
return NULL;
}
return head;
}
Thanks in advance.
As explained I try to run the program without -fsanitze, and everything was fine

Compare cJSON objects in the same code every time program is executed

I have a code which is always running in the background. compareContents() reads contents from a file periodically (based on an event) and stores it in a cSJON object. I have to compare current contents of the file (cJSON object) with the previous contents (again a cJSON object) everytime, but I am getting a memory leak. Please help.
cJSON * prev = NULL, *current = NULL;
bool compare = false;
bool compareJSON(cJSON *i,cJSON *j) /* referred from https://cjson.docsforge.com/master/api/cJSON_Compare/ */
{
if (i->type != j->type) {
return false;
}
if (i->type == cJSON_Number && (i->valueint != j->valueint)) {
return false;
}
if (i->type == cJSON_String && strcmp(i->valuestring,j->valuestring)){
return false;
}
if (i->type==cJSON_Array)
{
cJSON *ic = i->child, *jc = j->child;
while (ic && jc)
{
if (!compareJSON(ic, jc)) {
return false;
}
ic = ic->next, jc = jc->next;
}
if (ic || jc)
return false;
}
if (i->type == cJSON_Object)
{
cJSON *ic = i->child;
while (ic)
{
cJSON *jc = cJSON_GetObjectItem(j,ic->string);
if (!jc || !compareJSON(ic,jc)){
return false;
}
ic = ic->next;
}
// And again, for j == i.
cJSON *jc = j->child;
while (jc)
{
cJSON *ic=cJSON_GetObjectItem(i,jc->string);
if (!ic || !compareJSON(ic,jc)){
return false;
}
jc = jc->next;
}
}
return true;
}
bool compareContents(){
int fd = open("/tmp/abc.txt", O_RDONLY);
char* data = (char *)malloc(st.st_size + 1); //st.st_size obtained from fstat
int n = read(fd, data, st.st_size);
data[n] = '\0';
cJSON* root = cJSON_Parse(data);
if(prev == NULL){ //first time execution of program
prev = root;
}
else {
if(current){
prev = current;
}
}
current = cJSON_Duplicate(root,1);
if (prev && current)
{
compare = compareJSON(prev,current);
}
if (fd >= 0)
close(fd);
if (access("/tmp/abc.txt", F_OK) == 0 && remove("/tmp/abc.txt") != 0){
printf("Error deleting the file\n");
}
if (data)
free(data);
if (root)
cJSON_Delete(root);
return compare;
}
I am not sure, but this seems like dynamic copy?
current = cJSON_Duplicate(root,1);
That is you never free current and prev if it was not null. Maybe start from this point? Seems like memory leak.

circular linked list not working properly in c windows

I tried to implement circular linked list to manage a set of tasks by a server side application. The application is multi-threaded where one thread (updater() ) reads the linked list only for read while another two (push_stream() and delete_stream()) access the linked list to add to and delete from the linked list respectively.
My problem is not all the files to be deleted (after being processed) are deleted.
struct data_stream
{
bool processed;
int count;
char filename[30];
int TYPE_GRP;
int task_type;
struct data_stream *next;
};
struct data_stream *stream_head=NULL; //global variable
main(partial code)
main()
{
_beginthread(updater, 0, NULL);
while ((new_socket = accept(srv_sock, (struct sockaddr *)&client, &c)) != INVALID_SOCKET)
{
_beginthreadex(0, 0, handle_client, &new_socket, 0, 0);
}
}
handle_client function (partial code)
handle_client()
{
//some where in handle_client
EnterCriticalSection(&stream_lock);
stream_head = push_stream(&stream_head, TYPE_GRP, task_type);
LeaveCriticalSection(&stream_lock);
}
updater function (full source code)
void updater(void *data)
{
while (1)
{
struct data_stream*temp = stream_head;
struct data_stream*first = stream_head;
struct data_stream*prev = NULL;
if (stream_head != NULL)
{
struct data_stream*next = NULL;
do
{
next = temp->next;
if (temp->processed == false&&temp->task_type == 2)
{
process_files(temp);
}
else if (temp->processed == false&&temp->task_type == 3)
{
process_others();
}
EnterCriticalSection(&stream_lock);
temp->processed = true;
LeaveCriticalSection(&stream_lock);
temp = next;
} while (temp != first);
}
if (stream_head != NULL)
{
EnterCriticalSection(&stream_lock);
stream_head=delete_stream(&stream_head);
LeaveCriticalSection(&stream_lock);
}
Sleep(6000);
}
}
process_files
void process_files(struct data_stream*temp)
{
int count = 0;
char file_to_update[50] = { NULL };
size_t name_len = strlen(temp->filename);
memcpy_s(file_to_update, name_len, temp->filename, name_len);
file_to_update[name_len] = '\0';
temp->count = 0;
FILE *list_to_update ;
fopen_s(&list_to_update , file_to_update, "r+b");
if (list_to_update != NULL)
{
char readline[100] = { '\0' };
while (fgets(readline, sizeof(readline), list_to_update ) != NULL)
{
//read a line at a time and process the list
count++;
}
temp->count = count;
fclose(list_to_update );
}
else
printf("\nerror opening file\n");
}
process_others()
void process_others(struct data_stream*temp)
{
int count = 0;
char file_to_update[50] = { NULL };
size_t name_len = strlen(temp->filename);
memcpy_s(file_to_update, name_len, temp->filename, name_len);
file_to_update[name_len] = '\0';
temp->count = 0;
FILE *list_to_update ;
fopen_s(&list_to_update , file_to_update, "r+b");
if (list_to_update != NULL)
{
char readline[100] = { '\0' };
while (fgets(readline, sizeof(readline), list_to_update ) != NULL)
{
//read a line at a time and process the list
count++;
}
temp->count = count;
fclose(list_to_update );
}
else
{
printf("\nerror opening file\n");
}
}
}
push_stream (full source code)
struct data_stream* push_stream(struct data_data_stream**head_ref, int typ_grp, int task_type)
{
struct data_stream*new_data_stream=
(struct data_data_stream*)malloc(sizeof(struct data_stream));
new_stream->processed = false;
new_stream->task_type = task_type;
new_stream->TYPE_GRP = typ_grp;
new_stream->filename[0] = NULL;
new_stream->count = 0;
new_stream->next = NULL;
if (task_type == 2)
sprintf_s(new_stream->filename, "%s%03d.txt", "file_md5_list_", stream_count);
else
sprintf_s(new_stream->filename, "%s%03d.txt", "other_list_", stream_count);
if (*head_ref == NULL)
{
*head_ref = new_stream;
new_stream->next = new_stream;
}
else
{
struct data_stream* last = (*head_ref)->next;
struct data_stream* prev = (*head_ref)->next;
while (last != *head_ref)
{
prev = last;
last = last->next;
}
new_stream->next = *head_ref;
last->next = new_stream;
}
if (stream_count > 998)
stream_count = 1;
else
stream_count++;
return new_stream;
}
delete_stream function (full source code)
struct data_stream* delete_stream(struct data_data_stream**head)
{
if (head == NULL)
return NULL;
struct data_stream*prev = NULL;
struct data_stream*temp = *head;
struct data_stream*first = *head;
do
{
struct data_stream*next = temp->next;
if (temp->processed)
{
if (prev == NULL)
*head = temp->next;
else
prev->next = temp->next;
char file_to_delete[50] = { NULL };
memcpy_s(file_to_delete, strlen(temp->filename), temp->filename, strlen(temp->filename));
DeleteFileA(file_to_delete);
free(temp);
}
else
{
prev = temp;
}
temp = next;
} while (temp != first);
if (prev == NULL)
{
return NULL;
}
return *head;
}

Insert function for Ternary Search Tree - C

I am new to C and am trying to code up a data structure, primarily, a ternary search tree. I am working under the assumption (for now) that valid char inputs are being passed in. I am having some issues with my insert function. Note that I am also inserting the original string in the last TSTnode where the last character of str will also be held.
Here is what I have so far
struct TSTnode {
char* word; // NULL if no word ends here
char self;
struct TSTnode *left, *sub, *right;
};
int insert_tst(struct TSTnode** tree, const char* str) {
return _insert(tree, str, 0);
}
int _insert(struct TSTnode** tree, const char* str, int position) {
if((*tree) == NULL) {
*tree = new_tst_node(*(str+position));
position = position + 1;
if(*(str+position) == '\0') {
(*tree)->word = strcpy((*tree)->word,str);
return 1;
}
}
else if ((*tree)->self > *(str+position)) {
position = position + 1;
_insert( &((*tree)->left), str, position);
}
else if ((*tree)->self < *(str+position)) {
position = position + 1;
_insert( &((*tree)->right), str, position);
}
else {
position = position + 1;
_insert( &((*tree)->sub), str, position);
}
return 0;
}
struct TSTnode* new_tst_node(char self) {
struct TSTnode* newNode = (struct TSTnode*) malloc(sizeof(struct
TSTnode));
if (newNode == NULL) {
return NULL;
}
newNode->word = NULL;
newNode->self = self;
newNode->left = NULL;
newNode->right = NULL;
newNode->sub = NULL;
return newNode;
}
Here is how I am testing:
struct TSTnode* tree = NULL;
char* words[1] = {"hello"};
for (int i = 0; i < 1; i++) {
if (insert_tst(&tree, words[i]) == 0) {
//print some error
}
else { //success }
EDIT - My issue is that none of my conditional branches are being taken and the insert function simply goes straight to return 0.
Note: You confusingly use tree for both TSTnode* and TSTnode**. I'm going to use tree_ptr for the latter, and pretend that you did the same.
Your claim is false. The body of if((*tree_ptr) == NULL) is executed. You do have a number of problems, though.
You don't handle the case where *tree_ptr == NULL && *(str+position+1) != '\0'.
You don't correctly handle the case where *tree_ptr != NULL && *(str+position+1) == '\0'.
You always return 0 when *tree_ptr != NULL || str[1] != '\0'.
You never allocate word, but you deference it. The thing is, you shouldn't be storing the string again anyway!
You don't handle the case where str[0] == '\0' (empty string).
Fixed:
int insert_tst(struct TSTnode** tree_ptr, const char* str) {
if (!*str)
return 0; /* Zero-length strings are not supported. */
return insert_tst_helper(tree_ptr, str, 0);
}
int insert_tst_helper(struct TSTnode** tree_ptr, const char* str, int position) {
if (*tree_ptr == NULL) {
*tree_ptr = new_tst_node(*(str+position));
if (*tree_ptr == NULL)
return 0; /* Memory allocation error. */
}
if (*(str+position+1) == '\0') { /* If the next char is a NUL */
(*tree_ptr)->is_word = 1;
return 1;
}
else if ((*tree_ptr)->self > *(str+position)) {
position = position + 1;
return insert_tst_helper( &((*tree_ptr)->left), str, position);
}
else if ((*tree_ptr)->self < *(str+position)) {
position = position + 1;
return insert_tst_helper( &((*tree_ptr)->right), str, position);
}
else {
position = position + 1;
return insert_tst_helper( &((*tree_ptr)->sub), str, position);
}
}
Untested.
Let's clean this up, though.
*(str+position)simplifies tostr[position]
ch == '\0'simplifies toch == 0then to!ch
position = position + 1; return insert_tst_helper(..., str, position);simplifies to++position; return insert_tst_helper(..., str, position);then toreturn insert_tst_helper(..., str, position+1);then toreturn insert_tst_helper(..., str+1, 0);then toreturn insert_tst(..., str+1);
Why is recursion being used at all???
Fixed:
int insert_tst(struct TSTnode** tree_ptr, const char* str) {
if (!*str)
return 0; /* Zero-length strings are not supported. */
while (1) {
if (*tree_ptr == NULL) {
*tree_ptr = new_tst_node(*str);
if (*tree_ptr == NULL)
return 0; /* Memory allocation error. */
}
if (!*(str+1)) { /* If the next char is a NUL */
(*tree_ptr)->is_word = 1;
return 1;
}
int cmp = *str - (*tree_ptr)->self;
if (cmp < 0) { tree_ptr = &( (*tree_ptr)->left ); }
else if (cmp > 0) { tree_ptr = &( (*tree_ptr)->right ); }
else { tree_ptr = &( (*tree_ptr)->sub ); }
++str;
}
}
Untested.

Resources