So I am trying to take the users input using read(). The while loop runs until ctrl+d is entered in linux terminal and the input is stopped. I wrote some code and it works, but the problem I am running into is that I don't know how to take exactly whats in the input and how to use it.
In the code below, right after if(buff[offset] == '\n') (runs the given input, and after its finished clears the buffer and moves on to the next input) I don't know how to go into the 3 possible cases of input and also I don't know how to get the number from the second case:
"e" - exit the program
"p N" - do something, N is a number which I also need to get into a variable (I think that buff[offset-1] should get the number but I am not quite sure.)
everything else - print a message
Code:
int fd = 0; // set read() to read from STDIN_FILENO, because it's number is 0
const size_t read_size = 1; // set chunk size
size_t size = read_size;
size_t offset = 0;
size_t res = 0;
char *buff = malloc(size+1);
*buff = '\0';
while((res = read(fd, buff + offset, read_size)) > 0) // read from stdin and save to buff
{
if(buff[offset] == '\n')
{ // THIS PART
buff[offset] = '\0';
if(buff == "e")
{
// exit the program
return 0;
}
else if(buff == "p")
{
// do sth
}
else
{
// print a message
}
// reset the buffer (free its memory and allocate new memory for the next input)
offset = 0;
size = read_size;
free(buff);
buff = NULL;
buff = malloc(size + 1);
*buff = '\0';
}
else
{
offset += res;
buff[offset] = '\0';
if (offset + read_size > size)
{
size *= 2;
buff = realloc(buff, size+1);
}
}
}
If this isn't possible with read() I can try with something else like fgets() perhaps?
if(buff == "p")
This line checks if the string buff is stored at the same address as the string "p". However, you don't want to check if the strings are stored at the same address, but if they have the same content.
... how would strcmp() work ...
Using strcmp() you can check if two strings are identical:
if(!strcmp(buff, "e"))
"p N" ... since the number N could be any number?
Simply check if the first character of buff is 'p':
if(buff[0] == 'p')
Note that single quotes (') are used instead of double quotes (") for character constants (in contrast to string constants).
As I described in my comment above, here is a full answer:
int main(void)
{
char line[100], cmd;
int num, cnt;
while(fgets(line,sizeof(line),stdin)) != EOF) {
cnt = sscanf(line," %c %d",&cmd,&num);
if (cnt == 0) {
// empty line, no command
} else if (cmd == 'e') {
// exit the program
break;
} else if (cmd == 'p' && cnt == 2) {
// no something with num
} else {
// print a message
}
}
return 0;
}
Related
i'm writing a function (get_next_line) that returns a line read from a file descriptor, so i have the line stored in a char array that i return, now i need to process this line
so i read values into a buffer, whenever there are values read, the buffer should be processed, the newly added values should be joined with the result char array, if however, we encounter a newline, an extra step is required, i append the characters to the resulting array, until i reach the newline, which will be replaced by a null char, then the remaining values in the buffer should be stored in a static array that will append those values to the resulting string next time gnl is called.
the output of my function is correct my i need to deal with the leaks , i solved all of the leaks except the one related to the static char. btw im only allowed to use read() malloc() and free , all the other functions should be made by me (that explains the ft_ before each function in my code) here is my code :
valgrind results for the leaks
#include "get_next_line.h"
char * get_next_line(int fd) {
// // the reminder will contain the values after '\n' because the function should let
// you read the text file one line at a time. for example if the the buffer size is 5 and
// the first line is abc\nz we need to store the 'z' for the next line.
static char *reminder = "";
char buf[BUFFER_SIZE + 1];
int nbytes;
char * line = malloc(BUFFER_SIZE + 1);
if (!line || reminder == NULL) {
return NULL;
}
if (fd < 0 || fd > 999) {
free(line);
return NULL;
}
while ((nbytes = read(fd, buf, BUFFER_SIZE)) > 0) {
buf[nbytes] = '\0';
// ft_strjoin allocates and returns a new
// string, which is the result of the concatenation of ’s1’ and ’s2’.
reminder = ft_strjoin(reminder, buf);
//i modified strchr to returns a pointer to the character after '\n'
if (ft_strchr(reminder, '\n')) {
// each line from the txt file start from the index 0 to '\n'
line = ft_substr(reminder, 0, '\n');
reminder = ft_strchr(reminder, '\n');
return line;
}
}
if (ft_strcmp(reminder, "") == 0 || nbytes < 0 || reminder == NULL) {
free(line);
return NULL;
}
if ((!(ft_strchr(reminder, '\n')))) {
ptr = reminder;
reminder = NULL;
return ptr;
}
if (ft_strchr(reminder, '\n')) {
ptr = ft_substr(reminder, 0, '\n');
reminder = ft_strchr(reminder, '\n');
return ptr;
}
return NULL;
}
// here is my strjoin
char *ft_strjoin(char *s1, char *s2)
{
size_t size_s1;
size_t size_s2;
char *strjoin;
size_s1 = ft_strlen(s1);
size_s2 = ft_strlen(s2);
if (!(strjoin = malloc(size_s1 + size_s2 + 1)))
return (NULL);
ft_strcpy(strjoin, s1);
ft_strcat(strjoin, s2);
if(!(ft_strcmp(s1, "") == 0))
{
free(s1);
}
return (strjoin);
}
I'm trying to read line from stdin and store it in a pointer array, when I print the array I get the last entered value, the other values are replaced by the last entered value. The if statement works the first time only, after that it comparison doesn't come true. How can I read a line into pointer array?
void read_line(int fd, char *s)
{
char line[100];
char *list[100];
int i = 0;
while ((read(1, line, 100)))
{
if (!strncmp(line, s, strlen(line) - 1))
break;
else
list[i++] = line;
}
i = 0;
while (list[i])
{
write(fd, list[i], strlen(list[i]));
i++;
}
}
I call the function
read_line(1, "exit");
//if I type more words before typing exit, then type exit program doesn't terminate
First you should ensure that read did not fail by checking its return value:
while ((read(1, line, 100)) > 0)
Then you must copy line content in list[i++] using strdup, otherwise all list items will end up with the last value pointed by line.
Finally, what do you expect to happen when read reaches the limit of your buffer? You might want to handle that as well.
Code could look like this:
#define SIZE 100
void read_line(int fd, char *s)
{
char line[SIZE];
char *list[SIZE] = { 0 };
int i = 0;
ssize_t retval;
while ((retval = read(1, line, SIZE)) > 0)
{
if (retval == SIZE)
// adapt to the desired behavior here... for now we'll abort
break;
else if (!strncmp(line, s, strlen(line) - 1))
break;
else
list[i++] = strdup(line);
memset(line, 0, SIZE);
}
i = 0;
while (list[i])
{
write(fd, list[i], strlen(list[i]));
i++;
}
}
I need to read in only the values of a header that terminate with \r\n\r\n
Something like GETFILE OK 1024\r\n\r\n <content>
I'm trying to get the first \r\n and then get the next pair in a subsequent recv call.
The call to this function is: read_in_header(gfr, headerRecvBuff, 1);
Issue: The logic in the while referring to \n is completely ignored, or does not show any matches, when I know they exist. Is this the right way to compare the char newline?
int read_in_header(gfcrequest_t *gfr, char *buf, int len) {
char *s = buf;
int slen = len;
int c = 0;
int count = 0;
//get the first \r\n pair
do {
c = recv(gfr->client_fd, s, slen, 0);
printf("checking to see what s has now: %s\n", s);
count += c;
} while ((c > 0) && (s[count - 1] != '\n'));
//get the second \r\n pair
count = 0;
do {
c = recv(gfr->client_fd, s, slen, 0);
printf("checking to see what s has now: %s\n", s);
count += c;
} while ((c > 0) && (s[count - 1] != '\n'));
printf("checking to see what s has now: %s\n", s);
if (c < 0) {
return c;
} else if (c == 0) {
puts("Time to disconnect, the server is done.");
//total bytes received should not include header length
gfr->totalbytesReceived -= gfr->headerbytes_received;
return 0;
} else {
s[c - 1] = '\0';
}
gfr->totalbytesReceived += count;
return c;
}
Regarding Is this the right way to compare the char newline?
Since s is a buffer (not a single char), for the first loop the comparison method can be changed to
while ((c > 0) && (strstr(s, "\r\n") == NULL));
To require both "\r" & "\n" are there. This takes advantage of string searching to check both values are present in one line.
resulting in:
do {
c = recv(gfr->client_fd, s, slen, 0);
printf("checking to see what s has now: %s\n", s);
// count += c; //not needed
} while ((c > 0) && (strstr(s, "\r\n") == NULL));
If you decided to capture the line that has all 4, i.e. \r\n\r\n, then make that the argument of the comparison.
One side note, unless you have set socket options to non-blocking, recv() is a blocking call. Look into how to set a socket to non-blocking
I want to make a function that reads a line of your choice, from a given text file. Moving on to the function as parameters (int fd of the open, and int line_number)
It must do so using the language C and Unix system calls (read and / or open).
It should also read any spaces, and it must not have real limits (ie the line must be able to have a length of your choice).
The function I did is this:
char* read_line(int file, int numero_riga){
char myb[1];
if (numero_riga < 1) {
return NULL;
}
char* myb2 = malloc(sizeof(char)*100);
memset(myb2, 0, sizeof(char));
ssize_t n;
int i = 1;
while (i < numero_riga) {
if((n = read(file, myb, 1)) == -1){
perror("read fail");
exit(EXIT_FAILURE);
}
if (strncmp(myb, "\n", 1) == 0) {
i++;
}else if (n == 0){
return NULL;
}
}
numero_riga++;
int j = 0;
while (i < numero_riga) {
ssize_t n = read(file, myb, 1);
if (strncmp(myb, "\n", 1) == 0) {
i++;
}else if (n == 0){
return myb2;
}else{
myb2[j] = myb[0];
j++;
}
}
return myb2;
}
Until recently, I thought that this would work but it really has some problems.
Using message queues, the string read by the read_line is received as a void string ( "\0" ). I know the message queues are not the problem because trying to pass a normal string did not create the problem.
If possible I would like a fix with explanation of why I should correct it in a certain way. This is because if I do not understand my mistakes I risk repeating them in the future.
EDIT 1. Based upon the answers I decided to add some questions.
How do I end myb2? Can someone give me an example based on my code?
How do I know in advance the amount of characters that make up a line of txt to read?
EDIT 2. I don't know the number of char the line have so I don't know how many char to allocate; that's why I use *100.
Partial Analysis
You've got a memory leak at:
char* myb2 = (char*) malloc((sizeof(char*))*100);
memset(myb2, 0, sizeof(char));
if (numero_riga < 1) {
return NULL;
}
Check numero_riga before you allocate the memory.
The following loop is also dubious at best:
int i = 1;
while (i < numero_riga) {
ssize_t n = read(file, myb, 1);
if (strncmp(myb, "\n", 1) == 0) {
i++;
}else if (n == 0){
return NULL;
}
}
You don't check whether read() actually returned anything quick enough, and when you do check, you leak memory (again) and ignore anything that was read beforehand, and you don't detect errors (n < 0). When you do detect a newline, you simply add 1 to i. At no point do you save the character read in a buffer (such as myb2). All in all, that seem's pretty thoroughly broken…unless…unless you're trying to read the Nth line in the file from scratch, rather than the next line in the file, which is more usual.
What you need to be doing is:
scan N-1 lines, paying attention to EOF
while another byte is available
if it is newline, terminate the string and return it
otherwise, add it to the buffer, allocating space if there isn't room.
Implementation
I think I'd probably use a function get_ch() like this:
static inline int get_ch(int fd)
{
char c;
if (read(fd, &c, 1) == 1)
return (unsigned char)c;
return EOF;
}
Then in the main char *read_nth_line(int fd, int line_no) function you can do:
char *read_nth_line(int fd, int line_no)
{
if (line_no <= 0)
return NULL;
/* Skip preceding lines */
for (int i = 1; i < line_no; i++)
{
int c;
while ((c = get_ch(fd)) != '\n')
{
if (c == EOF)
return NULL;
}
}
/* Capture next line */
size_t max_len = 8;
size_t act_len = 0;
char *buffer = malloc(8);
int c;
while ((c = get_ch(fd)) != EOF && c != '\n')
{
if (act_len + 2 >= max_len)
{
size_t new_len = max_len * 2;
char *new_buf = realloc(buffer, new_len);
if (new_buf == 0)
{
free(buffer);
return NULL;
}
buffer = new_buf;
max_len = new_len;
}
buffer[act_len++] = c;
}
if (c == '\n')
buffer[act_len++] = c;
buffer[act_len] = '\0';
return buffer;
}
Test code added:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
extern char *read_nth_line(int fd, int line_no);
…code from main answer…
int main(void)
{
char *line;
while ((line = read_nth_line(0, 3)) != NULL)
{
printf("[[%s]]\n", line);
free(line);
}
return 0;
}
This reads every third line from standard input. It seems to work correctly. It would be a good idea to do more exhaustive checking of boundary conditions (short lines, etc) to make sure it doesn't abuse memory. (Testing lines of lengths 1 — newline only — up to 18 characters with valgrind shows it is OK. Random longer tests also seemed to be correct.)
I am trying to use a function that returns char * buffer to trap random user input from standard input and return a char* (that is, a string) with the input until '\n' or '\0' is detected as input.
However, the function user_input() crashed at the second occurence of c = getchar(); near the end of the function (at the //Error Here comment).
What am I doing wrong?
char* user_input(){
char *buffer=NULL;
size_t num_read = 0;
size_t buffer_size = 100;
char c = '\0';
char *new_buffer=NULL;
buffer = malloc(buffer_size);
fflush(stdin);
c = getchar();
while ( c != '\n' || c !='\0' ) {
if (num_read >= buffer_size) {
new_buffer=NULL;
buffer_size *= 2; // try a buffer that's twice as big as before
new_buffer = realloc(buffer, buffer_size);
if (new_buffer == NULL) {
free(buffer);
return NULL;
/* Abort - out of memory */
}
buffer = new_buffer;
}
buffer[num_read] = c;
num_read++;
c = getchar(); //Error Here
}//while
buffer[num_read] = '\0';
return buffer;
}
while ( c != '\n' || c !='\0' ) {
should use && instead of || or the loop will never exit
while ( c != '\n' && c !='\0' ) {
I built it and it worked although there are other bugs in the code such as the "buffer[num_read] = '\0';" at the end could overflow the buffer if there are exactly buffer_size items in the array.
Here are the issues that I found:
You cannot call fflush() on stdin. It leads to undefined behavior. fflush() is only for output streams.
The type for c should be an int. The getchar() will return EOF when there is no more input, and EOF is a value not equal to any valid value for unsigned char. That is why getchar() returns an int, so your variable receiving the return value of getchar() should match that type.
The sense of the while check is incorrect. You will only leave the loop if c equals both \n and \0 at the same time, which is not possible. Thus the loop will never exit. If you actually ended the input given to your program, you probably entered an infinite loop, continually allocating until your system ran out of memory. So, you want to leave the loop if c equals \n or \0.
You should add a check to leave the loop if c is EOF.
You should make sure num_read is less than buffer_size before you assign the \0 character. This can be done by moving your realloc() code to be at the bottom of the while loop instead of at the top.
With these changes, the code looks like:
char *user_input() {
char *buffer = NULL;
size_t num_read = 0;
size_t buffer_size = 100;
int c = '\0';
char *new_buffer = NULL;
buffer = malloc(buffer_size);
c = getchar();
while (!(c == EOF || c == '\n' || c == '\0')) {
assert(num_read < buffer_size);
buffer[num_read++] = c;
if (num_read >= buffer_size) {
buffer_size *= 2; // try a buffer that's twice as big as before
new_buffer = realloc(buffer, buffer_size);
if (new_buffer == NULL) {
free(buffer);
return NULL;
}
buffer = new_buffer;
}
c = getchar();
} //while
assert(num_read < buffer_size);
buffer[num_read] = '\0';
return buffer;
}
I have modified your code below. Try to run it as is, using the 'q' character at first, then you can experiment with other exit criteria later. By the way, your code worked without modification and did not crash for me (unless buffer overrun occured, but it would never leave the while loop otherwise (as Brian Walker pointed out). In any case, Try this in any ANSI C compiler: (just don't enter more than 1000 entries :)
#include <ansi_c.h>
char* user_input(void);
int main(void)
{
char *buf;
buf = malloc(1000);
//SearchDirectory("C:\\dev");
sprintf(buf, "%s", user_input());
printf("%s", buf);
free(buf);
return 0;
}
char* user_input(void){
char *buffer=NULL;
size_t num_read = 0;
size_t buffer_size = 100;
char c = '\0';
char *new_buffer=NULL;
buffer = malloc(buffer_size);
fflush(stdin);
c = getchar();
while ( c != 'q' ) {
if (num_read >= buffer_size) {
new_buffer=NULL;
buffer_size *= 2; // try a buffer that's twice as big as before
new_buffer = realloc(buffer, buffer_size);
if (new_buffer == NULL) {
free(buffer);
return NULL;
/* Abort - out of memory */
}
buffer = new_buffer;
}
buffer[num_read] = c;
num_read++;
c = getchar();
}//while
buffer[num_read] = '\0';
return buffer;
}