2

I made this thread a few hours ago:

Tricky Segmentation faults with BST recursion in C

Managed to figure it out after hours of trying. I've run into another snag, however. It's a weird one. Relevant code for perspective:

extern char *optarg;
extern int optind;
int c, err = 0;
char currentLine[STRING_SIZE];
char fileDirectory[STRING_SIZE];

static char usage[] = "Usage: %s [-c] [-o output_file_name] [input_file_name]\n";

while ((c = getopt(argc, argv, "co:")) != -1)
    switch (c)
    {
        case 'c':
            cflag = 1;
            break;
        case 'o':
            oflag = 1;

            // If an output file name
            // was entered, copy it
            // to fileDirectory

            if (optarg != NULL)
            {
                strcpy(fileDirectory, optarg);
            }

            break;
        default:
            err = 1;
            break;
    }

if (err)
{
    fprintf(stderr, usage, argv[0]);
    exit(1);
}

// --- BST SORT CODE STARTS HERE ---

// This is the BST. As the assignment instructions
// specify, it is initially set to NULL

BST *root = NULL;

// Pointer to the mode the files
// will be opened in. Starts as
// "w" since we're opening the output file
// first

char *mode = (char*)malloc(sizeof(char*));
strcpy(mode, "w");

FILE *outFile;
outFile = fopen(fileDirectory, mode);

// Now update mode and fileDirectory so
// we can open the INPUT file
strcpy(mode, "r");

// Check if we have an input file name
// If argv[optind] isn't NULL, that means we have
// an input file name, so copy it into fileDirectory

if (argv[optind] != NULL)
{
    strcpy(fileDirectory, argv[optind]);
}
FILE *inFile;

// Attempt to open the input file
//inFile = fopen(fileDirectory, mode);
fopen(fileDirectory, mode);

    if (inFile != NULL)
{
    // Process the file while EOF isn't
    // returned
    while (!feof(inFile))
    {
        // Get a single line (one string)
        fgets(currentLine, STRING_SIZE, inFile);

        // Check whether the line is an empty line
        if (*currentLine != '\n')
        {
            // If the string isn't an empty line, call
            // the insert function
            root = insert(root, currentLine);
        }
    }
    // At this point, we're done processing
    // the input file, so close it
    fclose(inFile);
}

// Otherwise, process user input from standard input
else
{
    do
    {
        printf("Please enter a string (or blank line to exit): ");

        // Get a single line of input from the user
        fgets(currentLine, STRING_SIZE, stdin);

        // If this is our first call to the
        // insert function, set root to point
        // to the branch created (which is
        // the actual "root" of the tree)
        if (root == NULL)
        {
            root = insert(root, currentLine);
        }
        // Otherwise, simply call the insert
        // function on the root of the tree
        else
        {
            insert(root, currentLine);
        }
    } while (caseSenStrCmp(currentLine, "\n") != 0);

}

// At this point, we've read all the input, so
// perform in-order traversal and print all the
// strings as per assignment specification

inOrderPrint(root, oflag, outFile);

// We're done, so reclaim the tree
deallocateTree(root);

// Finally, if we have an outFile, close it
if (outFile)
{
    fclose(outFile);
}

}

The above is an excerpt from my main file (you can see the entire thing in my other thread if you're curious). The assignment to inFile is commented out because I'm also having trouble with that - just ignore it (I don't THINK it has anything to do with this problem)

What follows is a function that performs an in-order traversal of a binary search tree and prints the strings stored in its branches to either stdout (if the oflag isn't set or there's no output file) or an output file (if the flag IS set and an output file exists):

void inOrderPrint(BST *root, int oflag, FILE *outFile)
{
// Check if the current branch is null

if (root == NULL)
{
    return;
}
    // Otherwise, perform the in-order traversal
else
{
    // This counter will be used in a loop
    // that will print the strings in the branches
    int count;

    if (root->left != NULL)
    {
        inOrderPrint(root->left, oflag, outFile);
    }

    // If the oflag is set and outFile
    // isn't NULL, then we can print to
    // the output file

    if (oflag && (outFile != NULL))
    {
        // Print the string as many times
        // as it occurs in the tree
        for (count = 0; count < root->counter; count++)
        {
            //fprintf(outFile, "%s\n", root->key);
            fputs(root->key, outFile);

            // !!! Does the fflush go here? !!!
            fflush(outFile);
        }
    }

        // Otherwise, print to stdout
    else
    {
        // Same idea as above: print
        // as many times as there are
        // instances of the string in the tree

        for (count = 0; count < root->counter; count++)
        {
            printf("%s\n", root->key);
        }
    }

    if (root->right != NULL)
    {
        inOrderPrint(root->right, oflag, outFile);
    }
}
}

So the problem this time (there's actually two, but I'll take care of this one first) is that when the oflag ISN'T set (that is, when I don't specify an output file) and the strings in the nodes have to be printed to stdout (the console in this case), which happens through this block of code:

else
    {
        // Same idea as above: print
        // as many times as there are
        // instances of the string in the tree
        for (count = 0; count < root->counter; count++)
        {
            printf("%s\n", root->key);
        }
    }

It works beautifully - the strings print perfectly in order just like they're supposed to. However, when I try to print to an output file, which happens based on this block:

if (oflag && (outFile != NULL))
        {
            // Print the string as many times
            // as it occurs in the tree
            for (count = 0; count < root->counter; count++)
            {
                //fprintf(outFile, "%s\n", root->key);
                fputs(root->key, outFile);
            }
        }

It prints garbage (ignore the commented-out fprintf statement; that was just me trying to see if using fputs instead would make a difference - it didn't). Not a lot of it, mind you.

Just a single symbol, namely this one: ø

Interestingly enough, the output file changes in size in a manner consistent with the amount of strings that get "written" to the file - the more strings in the tree that have to be written, the greater the size (as in, the amount of memory it takes up) of the output file. No matter how big the file is, though, all that's ever actually written on the file is just a single ø

I don't understand why this is, or how to fix it. The fact that printing to the console using root->key works perfectly tells me that it's probably not because the strings being written are NULL or invalid.

My only other guess is that it has something to do with the way I'm passing the pointer to the string (i.e. root->key vs *(root->key))?

createBranch() and insert() functions that create new nodes and insert them into the BST, for reference

BST* createBranch(char *keyVal)
{
// Create the new branch
BST* newBranch = (BST*)malloc(sizeof(BST));

// Allocate memory for newBranch's C-string
newBranch->key = (char*)malloc(STRING_SIZE * sizeof(char));

// Copy the user-provided string into newBranch's
// key field
strcpy(newBranch->key, keyVal);

// Set newBranch's counter value to 1. This
// will be incremented if/when other instances
// of the key are inserted into the tree

newBranch->counter = 1;

newBranch->left = NULL;
newBranch->right = NULL;

return newBranch;
}


// Adds items to the BST. Includes functionality
// to verify whether an item already exists in the tree

BST* insert(BST* root, char *key)
{

// If branch is null, call createBranch
// to make one, then return it

if (root == NULL)
{
    return createBranch(key);
}

// Otherwise, check whether the key
// already exists in the tree

if (!existsInTree(root, key, cflag))
{
    // If it doesn't, check whether
    // the case sensitivity flag is set
    if (cflag)
    {
        // If it is, use the case-sensitive
        // string comparison function

        if (caseSenStrCmp(root->key, key))
        {
            // If the key provided is greater
            // than the string stored at the
            // current branch, insert into
            // right child

            root->right = insert(root->right, key);
        }
        else
        {
            // Otherwise, insert into left child

            root->left = insert(root->left, key);
        }
    }
    // If it isn't, use the case-INsensitive string
    // comparison function to decide where to insert
    else
    {
        // Same logic as before
        if (caseInsenStrCmp(root->key, key))
        {
            root->right = insert(root->right, key);
        }
        else
        {
            root->left = insert(root ->left, key);
        }
    }
}

return root;
}

EDIT:

I've updated the first big code block to include the rest of the main function.

@dmuir:

I do close the outFile at the end of main (check end of first big block of code in my post), but I've never used fflush or even heard of it. I took a quick look at the documentation for it and it looks like it's meant to be used after every instance of printing something to an output file so the buffer is clear for the next value to be printed?

I've included a test usage of it in my printInOrder() function (second big block of code in this post, flanked by !!!). A test run of the code with this added didn't solve the problem, unfortunately, but I'm not even sure if I used it correctly?

@Iharob Al Asimi

Apologies about the excess whitespace. I mostly do that only while I'm still putting the program together - once everything works I usually go back and trim down the comments and spaces considerably so it looks a little more like it is now.

As far as how I insert values into the tree, I've added my createBranch() and insert() functions to the post (the big block of code right above the bolded "EDIT").

I'm not so sure it's a problem with how I inserted the values or with them not being NULL-character terminated, because as I said, printing the values to the CONSOLE works perfectly fine. It's only when I try to print to a FILE that I get the single garbage character ø

@William Pursell:

Apologies about the error statement. That was put in there as a test while I was writing the getopt code, I just forgot to erase it. I've removed it for now since it has nothing to do with the problem I'm having (I think).

enharmonics
  • 259
  • 3
  • 9
  • This is common, when you forget or don't know that you need a `'\0'` for strings termination. – Iharob Al Asimi Mar 04 '18 at 14:16
  • Ok after taking a quick look at your question, I think you didn't post the important code. There is no known bug in `fputs()` nor in `printf()` but the way you populate the tree is for sure very relevant here, and perhaps the cause of the problem. I'd recommend [valgrind](http://valgrind.org) to find such memory issues. – Iharob Al Asimi Mar 04 '18 at 14:18
  • 1
    Using blank lines to separate chunks of related code, is ok. But you are using too many and that makes the code actually harder to read, not easier. – Iharob Al Asimi Mar 04 '18 at 14:19
  • A usage statement is not an error, so it ought to be printed to stdout. An error message like "ERROR: Invalid input.\n" *is* an error messages and ought to be printed to stderr (and not be accompanied by a usage statement.) An all too common workflow: run a command and see the last 35 lines of a usage statement. Rerun the command, piping the data to a PAGER hoping to see the first few lines of the previously printed data. Run it a third time with stderr dup'd to stdout and redirected to a PAGER. Then waste a second or two trying to figure out what the error is. Just print the error. – William Pursell Mar 04 '18 at 14:21
  • Do you fclose or fflush the outfile? – dmuir Mar 04 '18 at 14:55

0 Answers0