Home > OS >  Get user command in C in multiple lines
Get user command in C in multiple lines

Time:09-26

I am trying to write a function that gets multiple lines if ends with \ and just stop if don't have that. I am very new with C so having trouble to do it. So something like

User input:

Hello, I am blablabla \
I like bablabla \
My favorite color is          (stop here)

But in my current function when the user press enter is over and just the first line is saved.

I know that I need to check if in the end have a backslash just keep going and appending, I am just not sure how to do that using getline.

char *getCommand(void){
    char* line; //string from user
    ssize_t linesize = 0;

    //getting command from user if reaches end of file exit or if something went wrong to read file.
    if(getline(&line, &linesize, stdin)==-1){
        if(feof(stdin)){
            exit(0);
        }
        else{
            fprintf(stderr, "Error reading the command: %s\n", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    return line;
}

CodePudding user response:

What your code is basically doing is reading one line. Few things to note:

  • feof(stdin) is wrong.
  • getline() dynamically allocates a string, so you need to free it yourself.

That said, you can implement it this way:

// Returns the number of commands read, -1 if it fails
int getCommand(char **commands, int max_commands)
{
    int i, num_cmds = 0;
    for (i = 0; i < max_commands; i  ) {
        char *line = NULL; // This must be initialized to NULL
        ssize_t linesize = 0;

        if(getline(&line, &linesize, stdin) == -1)
            return -1;
        
        line[strcspn(line, "\n")] = '\0'; // Replace \n with a null-terminator
        
        commands[i] = line;
        num_cmds  ;

        if (!strchr(line, '\\')) // if the line doesn't contain a \ then exit
            return num_cmds;
    }

    return num_cmds;
}

commands is an array of strings that will hold your commands. max_commands is the maximum number of commands your array may hold (i.e. its size).

You can use it this way:

int main(void)
{
    const int max_commands = 120;
    char *commands[max_commands];

    int num_cmds = getCommand(commands, max_commands);
    
    if (num_cmds == -1) {
        fprintf(stderr, "Error reading commands\n");
        return 1;
    }
    
    int i;
    for (i = 0; i < num_cmds; i  ) {
        printf("command %d: %s\n", i   1, commands[i]);
        free(commands[i]); // clear memory allocated by getline
    }
}

Given your input as an example, here is what you will get:

Hello, I am blablabla \
I like bablabla \
My favorite color is
command 1: Hello, I am blablabla \
command 2: I like bablabla \
command 3: My favorite color is

EDIT: If the \ needs to be at the end of the line, then replace if (!strchr(line, '\\')) with if (line[strlen(line)-1] != '\\').

CodePudding user response:

You can use this code to solve your problem, I opted to use scanf instead of getline, but the final working is the same:

#include <stdio.h>
#include <stdlib.h>
// Set limit to line
#define LINE_BUFFER 1024

// Get length a string
unsigned int length(const char * str) {
    int count = 0;

    while (str[count] != '\0') count  ;

    return count;
}

// Concatenate two strings to a target variable (dest)
int concat(const char * src_1, const char * src_2, char * dest, size_t sizeof_dest) {
    // Get lengths from sources
    unsigned int src_1_length = length(src_1);
    unsigned int src_2_length = length(src_2);
    // Calculate minimum length for dest
    unsigned int dst_length = src_1_length   src_2_length;

    if(sizeof_dest < dst_length)
        // Has no minimum length for concatenation
        return -1;

    int index = 0;
    for(int i = 0; i < src_1_length; i  ) {
        index  ;
        dest[i] = src_1[i];
    }
    for(int i = 0; i < src_2_length; i  ) dest[index   i] = src_2[i];

    return 0;
}

// Read multiline
char * getCommand() {
    char * command = NULL;
    while(1) {
        char line[LINE_BUFFER];
        scanf("%[^\n]s", line);
        fflush(stdin);

        // Get line length
        unsigned int line_length = length(line);

        // Checking last character
        // zero - false
        // nonzero - true
        char has_slash = line[line_length - 1] == '\\' ? 1 : 0;

        // Update slash to breakline
        if(has_slash) line[line_length - 1] = '\n';

        if(command == NULL) {
            command = (char *) malloc(line_length * sizeof(char));
            // Copy line to command
            for(int i = 0; i < line_length; i  ) command[i] = line[i];
        } else {
            // Concatenate command with current line for command update
            unsigned int command_length = length(command);
            unsigned int tmp_command_length = line_length   command_length;
            char tmp_command[tmp_command_length];
            if(concat(command, line, tmp_command, sizeof(tmp_command)) != 0) {
                printf("Error in concatenating '%s' with '%s'\n", command, line);
            }
            // Free memory from old command
            free(command);

            // Allocating memory for new updated command
            command = (char *) malloc(tmp_command_length * sizeof(char));
            // Copy command plus current line to new command
            for(int i = 0; i < tmp_command_length; i  ) command[i] = tmp_command[i];
        }

        if(!has_slash) break;
    }

    return command;
}

Click here to access the code repository on Github if you want to improve or fix something. Your collaboration is very welcome.


Quick example of implementation:

Let's assume this is the main file (main.c)

// IMPORTANT: Paste the code above here
// ...

int main() {
    char * command = getCommand();

    // Print result
    printf("\n---- BEGIN ---\n");
    printf("\n%s\n", command);
    printf("\n---- END ---\n");

    // Always clear data allocated in heap memory
    free(command);
    return 1;
}

Now let's compile the file via terminal, you can use the gcc or clang compilers. In this example I will use clang.

$ clang main.c -o getcommand

(if you are using gcc, just change the clang to gcc)

Run the compiled file:

$ ./getcommand

Right after type your test text

Hello, I am blablabla \
I like blablabla \
My favorite color is

The output should be as follows:

---- BEGIN ---

Hello, I am blablabla 
I like blablabla 
My favorite color is

---- END ---
  •  Tags:  
  • c
  • Related