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 ---
I am getting used to writing eBPF code as of now and want to avoid using pointers in my BPF text due to how difficult it is to get a correct output out of it. Using strtok() seems to be out of the question due to all of the example codes requiring pointers. I also want to expand it to CSV files in the future since this is a means of practice for me. I was able to find another user's code here but it gives me an error with the BCC terminal due to the one pointer.
char str[256];
bpf_probe_read_user(&str, sizeof(str), (void *)PT_REGS_RC(ctx));
char token[] = strtok(str, ",");
char input[] ="first second third forth";
char delimiter[] = " ";
char firstWord, *secondWord, *remainder, *context;
int inputLength = strlen(input);
char *inputCopy = (char*) calloc(inputLength + 1, sizeof(char));
strncpy(inputCopy, input, inputLength);
str = strtok_r (inputCopy, delimiter, &context);
secondWord = strtok_r (NULL, delimiter, &context);
remainder = context;
getchar();
free(inputCopy);
Pointers are powerful, and you wont be able to avoid them for very long. The time you invest in learning them is definitively worth it.
Here is an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
Extracts the word with the index "n" in the string "str".
Words are delimited by a blank space or the end of the string.
}*/
char *getWord(char *str, int n)
{
int words = 0;
int length = 0;
int beginIndex = 0;
int endIndex = 0;
char currentchar;
while ((currentchar = str[endIndex++]) != '\0')
{
if (currentchar == ' ')
{
if (n == words)
break;
if (length > 0)
words++;
length = 0;
beginIndex = endIndex;
continue;
}
length++;
}
if (n == words)
{
char *result = malloc(sizeof(char) * length + 1);
if (result == NULL)
{
printf("Error while allocating memory!\n");
exit(1);
}
memcpy(result, str + beginIndex, length);
result[length] = '\0';
return result;
}else
return NULL;
}
You can easily use the function:
int main(int argc, char *argv[])
{
char string[] = "Pointers are cool!";
char *word = getWord(string, 2);
printf("The third word is: '%s'\n", word);
free(word); //Don't forget to de-allocate the memory!
return 0;
}
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;
}
I want to use this function to show lines from a file that starts with a string.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "RangeSelection.h"
bool RangeSelection(char * buffer, int len, char * argv[])
{
int i = 0, j, c;
char word[4];
bool is_break;
while((c = getchar()) != EOF)
{
is_break = false; //a variable for checking if instruction break was used
for(j = 0; j < 3; ++j) //load three first chars
{
word[j] = c;
c = getchar(); //to load chars into c
if(c == '\0' || c == EOF) //if there's less than 3 chars in this word, don't check it
{
is_break = true;
break;
} //if that's EOF or new line, break
}
if(!is_break)
{
word[3] = '\0';
}
if(strcmp(word, argv) == 0) //they're the same
{
printf("%c", word[0]);
printf("%c", word[1]);
printf("%c", word[2]);
do //do while as it will show the output at least three times
{
printf("%c", c); //print that char
}
while((c = getchar()) != '\0' && c != EOF);
}
else
{
while((c = getchar()) != '\0' && c != EOF) //load chars until a new line
{
;
}
}
}
return false;
}
But it doesn't work as I want to. Run with parameter "xxx" it has to show only lines starting with "xxx". For this input:
xxx
dasfdad
xxx works
x
it should print only:
xxx
xxx works
while it prints:
xxx
dasfdad
xxx works
x
However, while the input is like this one:
dasfdad
xxx works
x
It prints nothing although the program ought to show
xxx works
Char * argv[] is an argument provided by the user while running the program in a console(for example xxx). Thanks in advance.
Use memcmp() instead of strcmp() to make life much easier for yourself. The parameter len is never used in the RangeSelection() function. This should be removed unless it is needed for some future development. Unless the string indicated by buffer will be changed in the function, it would be good to use const char *buffer in the parameter list.
Also note that char *argv[] is converted to a pointer to a pointer to char in the function parameter list; I don't think that this is what is expected in the posted code. The function strcmp() expects pointers to char (pointers to the first elements of null-terminated character arrays), so the expression strcmp(word, argv) leads to undefined behavior due to type mismatch in the function call.
Here is an example program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
bool filter_line(const char * buffer, const char *tag)
{
bool is_tagged;
size_t tag_sz = strlen(tag);
if (strlen(buffer) < tag_sz) {
is_tagged = false;
} else {
is_tagged = !memcmp(buffer, tag, tag_sz);
}
return is_tagged;
}
int main(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "Usage: %s line_tag\n", argv[0]);
exit(EXIT_FAILURE);
}
const char *filter_tag = argv[1];
char line[4096];
while (fgets(line, sizeof line, stdin) != NULL) {
if (filter_line(line, filter_tag)) {
printf("%s", line);
}
}
return 0;
}
Sample interactions using text files as input:
test_filter_line.txt
xxx
dasfdad
xxx works
x
abc xxx
xxx
λ> ./line_filter xxx < test_filter_line.txt
xxx
xxx works
test_filter_line2.txt:
dasfdad
xxx works
x
λ> ./line_filter xxx < test_filter_line2.txt
xxx works
Your program prints the whole file when it starts with given pattern and prints nothing if it doesn't because you are not matching newline character \n at any time whereas you want to read your file line by line.
I would suggest you split the job into separate functions. This is how I would do it in C99 :
int starts_with(char *buf, char *pattern)
{
char *p = pattern;
for (int i = 0; i < strlen(pattern); i++)
{
if (!buf[i] || buf[i] != pattern[i])
return 0;
}
return 1;
}
Now you can store each line inside a buffer within your RangeSelection function and check if each buffer start with your pattern
I have a text file as data.txt and I want to delete the last members of each line:
Here's the text file:
2031,2,0,0,0,0,0,0,54,0,
2027,2,0,0,0,0,0,0,209,0,
2029,2,0,0,0,0,0,0,65,0,
2036,2,0,0,0,0,0,0,165,0,
I would like to delete so it becomes:
2031,2,0,0,0,0,0,0,
2027,2,0,0,0,0,0,0,
2029,2,0,0,0,0,0,0,
2036,2,0,0,0,0,0,0,
I'm working in C but as the numbers can have two or three digits, I'm not sure how to do this.
A couple of uses of strrchr() can do the job:
#include <string.h>
void zap_last_field(char *line)
{
char *last_comma = strrchr(line, ',');
if (last_comma != 0)
{
*last_comma = '\0';
last_comma = strrchr(line, ',');
if (last_comma != 0)
*(last_comma + 1) = '\0';
}
}
Compiled code that seems to work. Note that given a string containing a single comma, it will zap that comma. If you don't want that to happen, then you have to work a little harder.
Test code for zap_last_field()
#include <string.h>
extern void zap_last_field(char *line);
void zap_last_field(char *line)
{
char *last_comma = strrchr(line, ',');
if (last_comma != 0)
{
*last_comma = '\0';
last_comma = strrchr(line, ',');
if (last_comma != 0)
*(last_comma + 1) = '\0';
}
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *line = malloc(4096);
if (line != 0)
{
while (fgets(line, 4096, stdin) != 0)
{
printf("Line: %s", line);
zap_last_field(line);
printf("Zap1: %s\n", line);
}
free(line);
}
return(0);
}
This has been vetted with valgrind and is OK on both the original data file and the mangled data file listed below. The dynamic memory allocation is there to give valgrind the maximum chance of spotting any problems.
I strongly suspect that the core dump reported in a comment happens because the alternative test code tried to pass a literal string to the function, which won't work because literal strings are not generally modifiable and this code modifies the string in situ.
Test code for zap_last_n_fields()
If you want to zap the last couple of fields (a controlled number of fields), then you'll probably want to pass in a count of the number of fields to be zapped and add a loop. Note that this code uses a VLA so it requires a C99 compiler.
#include <string.h>
extern void zap_last_n_fields(char *line, size_t nfields);
void zap_last_n_fields(char *line, size_t nfields)
{
char *zapped[nfields+1];
for (size_t i = 0; i <= nfields; i++)
{
char *last_comma = strrchr(line, ',');
if (last_comma != 0)
{
zapped[i] = last_comma;
*last_comma = '\0';
}
else
{
/* Undo the damage wrought above */
for (size_t j = 0; j < i; j++)
*zapped[j] = ',';
return;
}
}
zapped[nfields][0] = ',';
zapped[nfields][1] = '\0';
}
#include <stdio.h>
int main(void)
{
char line1[4096];
while (fgets(line1, sizeof(line1), stdin) != 0)
{
printf("Line: %s", line1);
char line2[4096];
for (size_t i = 1; i <= 3; i++)
{
strcpy(line2, line1);
zap_last_n_fields(line2, i);
printf("Zap%zd: %s\n", i, line2);
}
}
return(0);
}
Example run — using your data.txt as input:
Line: 2031,2,0,0,0,0,0,0,54,0,
Zap1: 2031,2,0,0,0,0,0,0,54,
Zap2: 2031,2,0,0,0,0,0,0,
Zap3: 2031,2,0,0,0,0,0,
Line: 2027,2,0,0,0,0,0,0,209,0,
Zap1: 2027,2,0,0,0,0,0,0,209,
Zap2: 2027,2,0,0,0,0,0,0,
Zap3: 2027,2,0,0,0,0,0,
Line: 2029,2,0,0,0,0,0,0,65,0,
Zap1: 2029,2,0,0,0,0,0,0,65,
Zap2: 2029,2,0,0,0,0,0,0,
Zap3: 2029,2,0,0,0,0,0,
Line: 2036,2,0,0,0,0,0,0,165,0,
Zap1: 2036,2,0,0,0,0,0,0,165,
Zap2: 2036,2,0,0,0,0,0,0,
Zap3: 2036,2,0,0,0,0,0,
It also correctly handles a file such as:
2031,0,0,
2031,0,
2031,
2031
,