0

You should be able to enter any movie title and genre into this program as long as they are smaller then 30 characters. Then insert a rating between 1-5. The insert function takes those three inputs and stores them into a doubly linked list. After Two entries into this list the program creates an unexpected breakpoint on this line newBlock = (movieInfo*)malloc(sizeof(movieInfo)); The problems is in the insert function.

#include<stdio.h>
#include<string.h> 
#include<stdlib.h>
typedef struct movieInfo {
    char* title; 
    char* genre;
    int rating;
    struct movieInfo* prev;
    struct movieInfo* next;
}movieInfo;

//prototypes
int getNum(void);
movieInfo insert(char* title, char *genre,int  rating, movieInfo** head, movienfo** tail);
void showList(movieInfo* head);
void eliminateEndOfLine(char* buffer);
void eliminateEndOfLine(char* buffer);

#define kWhileLoopCounter 1
#define kBuffer 30
 int main() {

    char title[kBuffer];
    char genre[kBuffer];
    int rating = 0;
    movieInfo* head = NULL; 
    movieInfo* tail = NULL; 
    movieInfo* node = NULL;

    while (kWhileLoopCounter) {
        printf("Please enter a title or exit the loop by entering ""."" and enter \n");
        fgets(title, kBuffer, stdin);
        if (strcmp(title, ".\n") == 0) {
            break;
        }
        eliminateEndOfLine(title);
        
        printf("Please enter a genre or exit the loop by entering ""."" and enter \n");
        fgets(genre, kBuffer, stdin);
        if (strcmp(genre, ".\n") == 0) {
            break;
        }
        eliminateEndOfLine(genre);
        
        for (;;) { //jut to check and make sure the user does not enter a value greater then one
            
            printf("Please enter a rating between 1-5\n");
            rating = getNum();
            if (rating < 1) {
                printf("The number you have entered in less then 1,\n\n");
            }
            else if (rating > 5) {
                printf("The number you have entered is greater than 5\n\n");
            }
            else {
                insert(title, genre, rating, &head, &tail);
                break; 
            }
            
        }
        
    }
    showList(head);
freeAll(head);
return 0;}

This function seems to be the problem!!!

    movieInfo insert(char title[],char genre[],int rating, movieInfo** head, movieInfo** tail) {
    
        movieInfo* newBlock = NULL;
        movieInfo* beforeElement = NULL;
        movieInfo* afterElement = NULL;
    
        
        newBlock = (movieInfo*)malloc(sizeof(movieInfo));
        newBlock->title = (char*)malloc((strlen(title) + 1));
        newBlock->genre = (char*)malloc((strlen(genre) + 1));
        /*newBlock->rating = (int*)malloc(1);*/
    
        
        if (newBlock == NULL) {
            printf("No memory was allocated \n");
            return **head;
        }
    
        strncpy(newBlock->title, title,30);
        strncpy(newBlock->genre, genre,30);
    
        newBlock->prev = newBlock->next = NULL;
    
        if (*head == NULL) {
            *head = *tail = newBlock;
            return**head;
        }
    
        else if (strcmp((*head)->title, title) >= 0) {
            newBlock->next = *head;
            (*head)->prev = newBlock;
            *head = newBlock;
        }
    
        else {
            beforeElement = *head;
            afterElement = (*head)->next;
    
            while (afterElement != NULL) {
                if (strcmp(afterElement->title, title) >= 0) {
                    break;
                }
                beforeElement = afterElement;
                afterElement = afterElement->next;
            }
            newBlock->prev = beforeElement;
            newBlock->next = afterElement;
            beforeElement->next = newBlock;
            if (afterElement == NULL)
            {
                *tail = newBlock;
            }
            else
            {
                afterElement->prev = newBlock;
            }
    
        }   
    
        return **head;
    }

This contains the smaller functions.

 void showList(movieInfo* head)
{
    movieInfo* item = NULL;

    item = head;
    char titleHeader[] = "Title";
    char genreHeader[] = "Genre";
    char ratingHeader[] = "Rating"; 

    printf("\n\n%-30s %-30s %-30s\n",titleHeader,genreHeader,ratingHeader);
    while (item != NULL)
    {
        printf("%-30s %-30s %-30d\n", item->title,item->genre, item->rating);
        item = item->next;
    }

}
int getNum(void)
{/* the array is 121 bytes in size; we'll see in a later lecture how we can improve this code */
    char record[121] = { 0 }; /* record stores the string */
    int number = 0;
    /* NOTE to student: indent and brace this function consistent with your others */
/* use fgets() to get a string from the keyboard */
    fgets(record, 121, stdin);
    /* extract the number from the string; sscanf() returns a number
 * corresponding with the number of items it found in the string */
    if (sscanf(record, "%d", &number) != 1)
    {
        /* if the user did not enter a number recognizable by
         * the system, set number to -1 */
        number = -1;
    }
    return number;
}

void eliminateEndOfLine(char* buffer)
{
    char* target = strchr(buffer, '\n');
    if (target != NULL)
    {
        *target = '\0';
    }
}
void freeAll(movieInfo* head)//This function frees the head
{
    movieInfo* curr = NULL, * next = NULL;

    curr = head;

    // traverse the list, being careful to not access freed blocks
    while (curr != NULL)
    {
        // keep a pointer to the next block so we can go there after it's freed
        next = curr->next;
        free(curr);
        curr = next;
    }
}
  • 3
    This needs to be an [mcve] so that someone could paste your code into a new file, compile it and run it. Right now that is impossible because you call functions whose code is not provided. You also need to provide the exact input which triggers the problem. – Nate Eldredge Feb 21 '21 at 18:03
  • 3
    Such errors are usually caused by having overrun a buffer in some previous part of the code. Tools like AddressSanitizer and valgrind can help you get an error at the point where that happens, instead of later. – Nate Eldredge Feb 21 '21 at 18:04
  • Added the missing **functions** –  Feb 21 '21 at 19:43
  • Hello Erik, please check your code again. I believe you made some copy and paste errors, e.g. `movienfo` in the typedef definition. Later you refer to it as `movieInfo` in the code. This is not correct. – Ely Feb 21 '21 at 19:50
  • Doesn't compile. First error: `fatal error: stlib.h: No such file or directory`. I know that's an obvious typo, but it's really tedious for someone who wants to help to go through and fix all those things before they can test the code. Please *test* the actual code you have posted. – Nate Eldredge Feb 21 '21 at 20:33
  • And as a reminder, we still need the input. – Nate Eldredge Feb 21 '21 at 20:35

1 Answers1

0

Read carefully what strncpy does, e.g. at https://en.cppreference.com/w/c/string/byte/strncpy.

If, after copying the terminating null character from src, count is not reached, additional null characters are written to dest until the total of count characters have been written.

So if for instance title is 12 characters long, then 13 bytes will be allocated for newBlock->title, but strncpy(newBlock->title, title, 30) will write a full 30 bytes into that 13-byte array, thus overflowing it.

There is a separate bug if title is more than 30 characters long. Say it's 50. In this case you have allocated a full 51 bytes, but strncpy will only copy 30, and moreover:

If count is reached before the entire array src was copied, the resulting character array is not null-terminated.

As such, later attempts to call strcmp on the created string are likely to crash and/or give incorrect results.

Since you have allocated enough space to hold the entire string, there is no reason to cut it off at 30 characters. It is safe to just do strcpy(newBlock->title, title); which will fix both bugs and avoid unnecessary truncation to boot. If you really want to limit the title to 30 characters, then check its length when it is first input.

This recent answer has some good advice on using strncpy, i.e. "don't". Avoiding buffer overflows is important but strncpy isn't a very good solution.

One more bug is that you've forgotten to fill in newBlock->rating.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82