Get user command in C in multiple lines - c

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;
}

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] != '\\').

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 ---

Related

Override previous line in stdout with \r

So usually when I want to print something on the previous line in the terminal I just do:
printf("\rprint a number %d", number);
fflush(stdout);
So for a program I'm writing I wanted to do just that and tried:
printf("\r%s - %s", buffer1, buffer2);
fflush(stdout);
Where
buffer1, buffer2 = malloc(sizeof(char)*100);
This does however not work, it prints on a new line everytime. I'm assuming it has something to do with me using char* instead of char[100] or with the size of them?
Sample output (colors removed from code for simplicity):
edit:
buffer_1 gets populated in a different thread from stdin:
while (true) {
system("/bin/stty raw");
c = getchar();
system("/bin/stty cooked");
if (c == ' ') {
return;
}
// check if input is not to long
if (i == MAX_INPUT_LENGTH) {
return;
}
// Get the latest added char and add to buffer
t_a->input_buffer[i] = c;
i += 1;
}
And buffer_2 is assigned the output from system(find) command:
FILE *fp = popen("find -name a ", "r");
char temp_buf[1024];
// Get output of find command
int i = 0;
while (fgets(temp_buf, sizeof(temp_buf), fp) != 0 && i < MAX_NUM_RESULTS) {
strcpy(result_buffer[i], temp_buf);
i += 1;
}
pclose(fp);
Any hints are appreciated.
The problem was indeed that the output from system(find) contained a newline at the end of the result. I fixed this by not copying the last character:
strncpy(result_buffer[i], temp_buf, strlen(temp_buf)-1);
Thanks to the comments for pointing me there!
If you don't know how much line feed you have, you can use strspn function in a loop to remove all \n occurrences:
#include <stdio.h>
#include <string.h>
void remove_line_feeds(char *sz)
{
size_t len = strlen(sz);
size_t pos;
while ((pos = strspn(sz, "\n"))!= len)
{
sz[pos] = ' ';
}
}
int main(void)
{
char sz[] = "text\nwith\nline\nfeed";
printf("'%s'\n", sz);
remove_line_feeds(sz);
printf("'%s'\n", sz);
return 0;
}

Backslash and doublebackslash

I've got a file with paths . But I can't read them correctly in C .
an example of a line in the file :
C:\Trust\The\process.txt
and I want to have this :
C:\\Trust\\The\\process.txt
But how can I replace antislash by double antislash ?
I've got this function :
/* Replace a string */
char* replace(char* text, char* replace, char* element) {
int i, j, k;
int searchSize = strlen(text);
int replaceSize = strlen(replace);
int size = strlen(element);
char* ret;
if (!searchSize) {
ret = malloc(size + 1);
for (i = 0; i <= size; i++) {
ret[i] = element[i];
}
return ret;
}
int retAllocSize = (strlen(element) + 1) * 2;
ret = malloc(retAllocSize);
int bufferSize = 0;
char* foundBuffer = malloc(searchSize);
for (i = 0, j = 0; i <= size; i++) {
if (retAllocSize <= j + replaceSize) {
retAllocSize *= 2;
ret = (char*) realloc(ret, retAllocSize);
}
else if (element[i] == text[bufferSize]) {
foundBuffer[bufferSize] = element[i];
bufferSize++;
if (bufferSize == searchSize) {
bufferSize = 0;
for (k = 0; k < replaceSize; k++) {
ret[j++] = replace[k];
}
}
}
else {
for (k = 0; k < bufferSize; k++) {
ret[j++] = foundBuffer[k];
}
bufferSize = 0;
ret[j++] = element[i];
}
}
free(foundBuffer);
return ret;
}
I thought I could use like this , but it doesn't work :
char *token ;
char s[]="C:\Trust\The\process.txt";
token=replace("\0x5c","\\",s);
Pulling comments together, you need to understand that the backslash in a string in C source code is an escape charater. It means "the next character has a special meaning".
In order to put a single backslash character in a C string string, you must tell the compiler that "this backslash you must put in the string" and to do that, you put two backslashes in the string in your source code so the string in the compiled code will have a single backslash. In summary:
char s[]= "C:\\my\\dir"";
in your source code, will have a string in the compiled code:
C:\my\dir
If you're reading from an input file input.txt and each filename ends with a newline, this should work:
#define MAX_LINE_LEN 1024
int main(int argc, char *argv[])
{
/* File read variables */
FILE *fp;
char buf[MAX_LINE_LEN];
char *token;
/* Open input file */
fp=fopen(argv[1], "r");
if(fp == NULL)
{
fprintf(stderr, "Unable to open input file. Exiting...");
return 1;
}
/* Get each line and print result */
while ((fgets(buf, sizeof(buf), fp)) != NULL) {
token=replace("\\", "\\\\", buf);
printf("%s", token);
}
fclose(fp);
return 0;
}
Input: input.txt:
C:\Trust\The\process.txt
Output:
C:\\Trust\\The\\process.txt
When you do this:
char s[]="C:\Trust\The\process.txt";
your backslash is gone at compile time. The \T becomes a tab, for example. When you call
token=replace("\0x5c","\\",s);
the contents of s has already been 'edited' by the compiler and the backslashes are gone.
Your test case needs to be
char s[]="C:\\\\Trust\\\\The\\\\process.txt";
and when you call the replace function you will have the single backslashes in s.
There is some confusion in your problem statement:
The variable definition char s[]="C:\Trust\The\process.txt"; is incorrect. It should be written:
char s[] = "C:\\Trust\\The\\process.txt";
The compiler interprets the \ character in a string literal as an escape character to encode special and non-printing characters. The \\ represents a single \ character in the string.
The file contents should not need to have its \ characters doubled or escaped in any way. Unless you perform some sort of parsing when you read the file, the characters read will be stored untouched in memory and the sequence C:\Trust\The\process.txt in the file will be read as a string identical to s.

Taking input from command line as well as console(STDIN) in C

I am a beginner in coding and having difficulty trying to take input from both command line as well as console(STDIN). my program is supposed to search and replace a group of characters from a string. For example, concatenate cat gat : the output must be congatenate!
This is my code so far!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*Function to replace a string with another string*/
char *rep_str(const char *s, const char *old, const char *new1)
{
char *ret;
int i, count = 0;
int newlen = strlen(new1);
int oldlen = strlen(old);
for (i = 0; s[i] != '\0'; i++)
{
if (strstr(&s[i], old) == &s[i])
{
count++;
i += oldlen - 1;
}
}
ret = (char *)malloc(i + count * (newlen - oldlen));
if (ret == NULL)
exit(EXIT_FAILURE);
i = 0;
while (*s)
{
if (strstr(s, old) == s) //compare the substring with the newstring
{
strcpy(&ret[i], new1);
i += newlen; //adding newlength to the new string
s += oldlen;//adding the same old length the old string
}
else
ret[i++] = *s++;
}
ret[i] = '\0';
return ret;
}
int main(int argc, char*agrv[])
{
char mystr[100], c[10], d[10];
scanf("%s", mystr);
scanf(" %s",c);
scanf(" %s",d);
char *newstr = NULL;
newstr = rep_str(mystr, c,d);
printf("%s\n", newstr);
free(newstr);
return 0;
}
as for now, it shows correct output for either console input or commandline input, bur not both!
kindly suggest the changes to be made!
You can have a check on the variable argc of function int main().
// Path of executable file is passed by default so value of 'argc' will be at least 1
if(argc > 1)
{
// do stuff to work on input from command line
}else{
// do stuff to work on input from STDIN
}
Instead of trying to parse input file through argc and argv, simply pass all the input file through stdin. This means that you will always use scanf to read input data.
At command line you will need to call using pipes, something like this:
$ cat file.txt | yourprogram

putenv() and setenv() on Unix

I need to get input from user and deal with variables. I need to have next features:
set varname = somevalue: set the value of the environment variable named varname to the value specified by somevalue.
delete varname: remove the named environment variable.
print varname: prints out the current value of the named environment variable.
What I have till now is this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>
int main(int argc, char **argv) {
char * s;
char * command;
char * varName;
char * varValue;
while (s = readline("prompt> ")) {
/* Initialise char* variables */
command = NULL;
varName = NULL;
varValue = NULL;
add_history(s); /* adds the line to the readline history buffer */
printf("> %s\n", s); //print > sign
int cmdNo = 1;
int i;
// parse through entire string
for (i = 0; i < strlen(s); i++)
{
// check for space or = and jump over it
while ((isspace(s[i]) || s[i] == '=') && (i < strlen(s)))
{
i++;
}
// check if i is greater than string size
if (i >= strlen(s))
{
printf("Bad command format!\n");
break;
}
// if cmdNo == 1, get the command
if (cmdNo == 1)
{
int commandSize = 0;
int index = i;
// point index to space
while (!isspace(s[index]))
{
commandSize++;
index++;
}
// get command
command = (char*)malloc(commandSize + 1);
int destIndex = 0;
// copy command into command array
while (i<index)
{
command[destIndex] = s[i];
destIndex++;
i++;
}
// adding terminate character
command[destIndex] = '\0';
// increase command number by 1
cmdNo++;
}
// if cmdNo == 2 we deal with variable name
else if (cmdNo == 2)
{
// variable name size
int varNameSize = 0;
int index = i;
// point index to space
while (!isspace(s[index]))
{
varNameSize++;
index++;
}
// get var name
varName = (char*)malloc(varNameSize + 1);
int index2 = 0;
while (i<index)
{
varName[index2] = s[i];
index2++;
i++;
}
// add terminate char
varName[index2] = '\0';
// increment cmdNo by 1
cmdNo++;
}
// if cmdNo == 3 we deal with variable value
else if (cmdNo == 3)
{
int valueSize = 0;
int index = i;
// point index to space
while (!isspace(s[index]) && s[index] != '\0')
{
valueSize++;
index++;
}
// get variable value
varValue = (char*)malloc(valueSize + 1);
int index2 = 0;
while (i<index)
{
varValue[index2] = s[i];
index2++;
i++;
}
// add terminate char
varValue[index2] = '\0';
}
}
// print command, variable name and value
if (command != NULL)
{
printf("%s", command);
}
if (varName != NULL)
{
printf(" %s", varName);
}
if (varValue != NULL)
{
printf(" %s\n", varValue);
}
/* clean up! */
free(s);
free(command);
free(varName);
free(varValue);
}
return(0);
}
Obviously, I had to put somewhere putenv(), setenv() or clearenv(). I don't have much experience with these commands.
Also, there is some error (Segmentation fault). This is response of system
The crash is caused by your loops while (!isspace(s[index])) for cmdNo 1 and 2 -- if there is no third (or second) word on the input line, they'll run past the NUL terminator in the string and (probably) crash. You need a check for NUL in these loops as you check in cmdNo 3 case.
You also have a problem if you have more than 3 words on the input line -- you'll go into an infinite loop on the 4th word.
Rather than duplicating the code for the word in if/else if/else if as you have done, its much better to put the words in an array. You could even use strtok_r or strsep rather than manually parsing out the characters.

C parser for data separated with comma

What is the most effective way to write a parser in C for data with the following format:
atr#1,atr#2,...,atr#n
btr#1,btr#2,...,btr#n
...
Each record is in new line and attributes are separated with comma.
What function should be used? Do you have any examples?
Here's some example code that will read the file sparated by newlines line by line, then split the arguments and print them out (you can easily adapt it to, for example, parse it to an array of array of char *s):
#include <stdio.h>
#include <string.h>
int main()
{
FILE *f = fopen("file.txt", "r");
char ptr[1024];
char *token;
while (fgets(ptr, 1024, f) != NULL)
{
token = strtok(ptr, ",");
while(token)
{
printf("Token: %s\n", token);
token = strtok(NULL, ",");
}
}
fclose(f);
return 0;
}
This will work:
/* You need the following includes and defines */
#include <stdio.h>
#include <iostream.h>
#include <string.h>
#define NULL_CHAR 0x0
int parse(char* data) {
const int LINE_SIZE=255; /* Should be long enough for your unparsed data */
const int MAX_FIELDS=99; /* Maximum number of fields */
char output[MAX_FIELDS][LINE_SIZE];
int i;
int output_field_count;
int output_char_idx;
for (i = 0; i < MAX_FIELDS; i++) {
strcpy(output[i], "");
}
output_field_count = 0;
output_char_idx = 0;
for (i = 0; i < LINE_SIZE; i++) {
if ((data[i] != ',') &&
(output_field_count < MAX_FIELDS) &&
((output_char_idx+1) < LINE_SIZE)) {
output[output_field_count][output_char_idx] = data[i];
output[output_field_count][output_char_idx+1] = NULL_CHAR;
output_char_idx++;
}
else if (data[i] == ',') {
output_field_count++;
output_char_idx = 0;
}
}
output_field_count++;
output_char_idx = 0;
printf("OUTPUT FIELD COUNT IS: %d\n", output_field_count);
for (i = 0; i < output_field_count; i++) {
printf("FIELD %i IS: %s\n", i, output[i]);
}
return 0;
}
This can be called as follows:
char data[500]; /* Should be long enough for your unparsed data */
strcpy(data, "atr#1,atr#2,...,atr#n");
parse(data);
strcpy(data, "btr#1,btr#2,...,btr#n");
parse(data);
Pick the right tool for the job. It's about one line in Perl, Python, or best yet awk. If you have a compelling reason to use C, please explain in your post - otherwise I think the most judicious answer anyone can give you is to advise you to pick the right tool for the job instead of asking how to do something onerous in a language that's bad at it.
From the command line:
tr ',' '\n' < file.txt
Will turn the commas into new lines.

Resources