Search code examples
cfgets

C - fgets hangs when array not filled


I am taking input from stdin with fgets() and seem to have a bit of trouble with buffer overflow. I've added in a while loop which is meant to eat all characters, however my code is running multiple times and should only run once.

void play_game(void) {
    int ch; /* Waste */
    /* Max input from fgets */
    char c[8];

    enum cell_contents board[BOARD_HEIGHT][BOARD_WIDTH];

    init_board(board);

    while(!is_game_over(board)) {
        display_board(board);

        printf("Please enter a move [enter Q or ctrl-D to quit]: ");

        if(fgets(c, sizeof(c), stdin)) {
            printf("Entered: %s\n", c);
            struct move calculated_move = calculate_move(c);

            if(is_valid_move(calculated_move, board))
                player_move(calculated_move, board);
            else
                printf("That is an invalid move!\n");
        }

        /* eat excess data */
        if (strlen(c) == sizeof(c)-1)
            while((ch = fgetc(stdin)) != '\n' && ch != EOF);

        /* eat overfilled data */
        //read_rest_of_line();

    }

    printf("Game over!\n");
    game_over(board);
}

Here's my results:

Please enter a move [enter Q or ctrl-D to quit]: E4, C4 gfgfd oigjdfogkf dohdfkho dk
Entered: E4, C4 
            +---+---+---+        
1           | o | o | o |        
            +---+---+---+        
2           | o | o | o |        
    +---+---+---+---+---+---+---+
3   | o | o | o | o | o | o | o |
    +---+---+---+---+---+---+---+
4   | o | . | o | . | . | o | o |
    +---+---+---+---+---+---+---+
5   | o | o | o | o | o | o | o |
    +---+---+---+---+---++---+---+
6           | o | o | o |        
            +---+---+---+        
7           | o | o | o |        
            +---+---+---+        
      A   B   C   D   E   F   G   
Please enter a move [enter Q or ctrl-D to quit]: Entered: gfgfd o
That is an invalid move!
            +---+---+---+        
1           | o | o | o |        
            +---+---+---+        
2           | o | o | o |        
    +---+---+---+---+---+---+---+
3   | o | o | o | o | o | o | o |
    +---+---+---+---+---+---+---+
4   | o | . | o | . | . | o | o |
    +---+---+---+---+---+---+---+
5   | o | o | o | o | o | o | o |
    +---+---+---+---+---++---+---+
6           | o | o | o |        
            +---+---+---+        
7           | o | o | o |        
            +---+---+---+        
      A   B   C   D   E   F   G   
Please enter a move [enter Q or ctrl-D to quit]: Entered: igjdfog
That is an invalid move!
            +---+---+---+        
1           | o | o | o |        
            +---+---+---+        
2           | o | o | o |        
    +---+---+---+---+---+---+---+
3   | o | o | o | o | o | o | o |
    +---+---+---+---+---+---+---+
4   | o | . | o | . | . | o | o |
    +---+---+---+---+---+---+---+
5   | o | o | o | o | o | o | o |
    +---+---+---+---+---++---+---+
6           | o | o | o |        
            +---+---+---+        
7           | o | o | o |        
            +---+---+---+        
      A   B   C   D   E   F   G   
Please enter a move [enter Q or ctrl-D to quit]: 

Solution

  • Fixed the problem by checking the last character in the array if it was a new line or not.

    /* eat excess data */
    if (strlen(c) == sizeof(c)-1 || c[sizeof(c)-1] != '\n')
        read_rest_of_line();
    
    void read_rest_of_line(void){
        int ch;
        /* remove all characters from the buffer */
        while (ch = getc(stdin), ch != '\n' && ch != EOF);
    
        /* clear the error status of the input pointer */
        clearerr(stdin);
    }