c++scanfnewlinegets

not terminating scanf() or gets() after taking newline


I have to take three inputs in a single string.
The code to take the input is:

char msg_send[1000];
gets(msg_send);

The input is like

GET /api HTTP/1.1  

id=1&name=phoenix&mail=bringchills@ppks.com

That means there is a newline after the fist line GET /api HTTP/1.1. The next line is an empty newline. The input taking function should terminate after taking the 3rd newline.

Also, I have to terminate the input after the first line GET /something HTTP/1.1 if the line doesn't have a /api word at the place of /something word.

But when I'm using gets(), it terminates the string after taking GET /api HTTP/1.1 part of the input. When using scanf, it terminates after taking only the first GET word. Is there any way to take the input as a single string?


Solution

  • In cases like these, you should just use getchar in a loop:

    #define MAX_MSG_BUF 1000
    
    char char msg_send[MAX_MSG_BUF];    
    
    // you should wrap the below code in a function, like get3lines(buf, buf_size)
    unsigned index = 0;
    unsigned line_count = 0; 
    const unsigned buf_size = MAX_MSG_BUF;
    do {
        int tmp = getchar();
        if (tmp < 0) break; // end of file or error
        msg_send[index++] = tmp;
        if (tmp == '\n') {
            ++line_count;
            if (line_count == 1) {
                // check that first line contains /api                
                if (!first_line_valid(msg_send, index)) break;
            }
        }
    } while (index < (MAX_MSG_BUF-1) && line_count < 3;
    msg_send[index] = '\0';
    // print error if line_count < 3 ?
    

    The first_line_valid function might look like this (note: needs #include <stdbool.h> for bool type):

    bool starts_with(const char *str, size_t length, const char *prefix) {
        size_t i = 0;
        for(;;) {            
            if (i == length || prefix[i] == '\0') return true;
            if (str[i] != prefix[i]) return false; // does catch end of str
            ++i;
         }
    }
    
    bool first_line_valid(const char *buf, size_t buf_size) {
        // note: buf might not be NUL-terminated so use buf_size
        if (starts_with(buf, buf_size, "GET /api ")) return true;
        if (starts_with(buf, buf_size, "GET /api/")) return true;
        return false;
    }