I'm currently using select() to tell if there is data to be read in a file descriptor because I don't want fgets to block. This works 99% of the time, however, if select() detects data in fp, but the data doesn't have a newline and the data is smaller than my buffer, it will block. Is there any way to tell how many bytes are ready to be read?
//See if there is data in fp, waiting up to 5 seconds
select_rv = checkForData(fp, 5);
//If there is data in fp...
if (select_rv > 0)
{
//Blocks if data doesn't have a newline and the data in fp is smaller than the size of command_out!!
if (fgets(command_out, sizeof(command_out)-1, fp) != NULL)
{
printf("WGET: %s", command_out);
}
}
else if (select_rv == 0)
{
printf("select() timed out... No output from command process!\n");
}
I guess what I really want is a way to know if a full line is ready to be read before calling fgets.
As MBlanc mentions, implementing your own buffering using read() is the way to go here.
Here's a program that demonstrates the general method. I don't recommend doing exactly this, since:
The function presented here uses static variables, and will only work for one single file, and will be unusable once that's over. In reality, you'd want to set up a separate struct for each file and store the state for each file in there, passing it into your function each time.
This maintains the buffer by simply memmove()ing the remaining data after some is removed from the buffer. In reality, implementing a circular queue would probably be a better approach, although the basic usage will be the same.
If the output buffer here is larger than the internal buffer, it'll never use that extra space. In reality, if you get into this situation, you'd either resize the internal buffer, or copy the internal buffer into the output string, and go back and try for a second read() call before returning.
but implementing all this would add too much complexity to an example program, and the general approach here will show how to accomplish the task.
To simulate delays in receiving input, the main program will pipe the output from the following program, which just outputs a few times, sometimes with newlines, sometimes without, and sleep()s in between outputs:
delayed_output.c:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("Here is some input...");
fflush(stdout);
sleep(3);
printf("and here is some more.\n");
printf("Yet more output is here...");
fflush(stdout);
sleep(3);
printf("and here's the end of it.\n");
printf("Here's some more, not ending with a newline. ");
printf("There are some more words here, to exceed our buffer.");
fflush(stdout);
return 0;
}
The main program:
buffer.c:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/select.h>
#define INTBUFFERSIZE 1024
#define BUFFERSIZE 60
#define GET_LINE_DEBUG true
/* Prints a debug message if debugging is on */
void get_line_debug_msg(const char * msg, ...)
{
va_list ap;
va_start(ap, msg);
if ( GET_LINE_DEBUG ) {
vfprintf(stderr, msg, ap);
}
va_end(ap);
}
/*
* Gets a line from a file if one is available.
*
* Returns:
* 1 if a line was successfully gotten
* 0 if a line is not yet available
* -1 on end-of-file (no more input available)
*
* NOTE: This function can be used only with one file, and will
* be unusable once that file has reached the end.
*/
int get_line_if_ready(int fd, char * out_buffer, const size_t size)
{
static char int_buffer[INTBUFFERSIZE + 1] = {0}; /* Internal buffer */
static char * back = int_buffer; /* Next available space in buffer */
static bool end_of_file = false;
if ( !end_of_file ) {
/* Check if input is available */
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
struct timeval tv = {0, 0};
int status;
if ( (status = select(fd + 1, &fds, NULL, NULL, &tv)) == -1 ) {
perror("error calling select()");
exit(EXIT_FAILURE);
}
else if ( status == 0 ) {
/* Return zero if no input available */
return 0;
}
/* Get as much available input as will fit in buffer */
const size_t bufferspace = INTBUFFERSIZE - (back - int_buffer) - 1;
const ssize_t numread = read(fd, back, bufferspace);
if ( numread == -1 ) {
perror("error calling read()");
exit(EXIT_FAILURE);
}
else if ( numread == 0 ) {
end_of_file = true;
}
else {
const char * oldback = back;
back += numread;
*back = 0;
get_line_debug_msg("(In function, just read [%s])\n"
"(Internal buffer is [%s])\n",
oldback, int_buffer);
}
}
/* Write line to output buffer if a full line is available,
* or if we have reached the end of the file. */
char * endptr;
const size_t bufferspace = INTBUFFERSIZE - (back - int_buffer) - 1;
if ( (endptr = strchr(int_buffer, '\n')) ||
bufferspace == 0 ||
end_of_file ) {
const size_t buf_len = back - int_buffer;
if ( end_of_file && buf_len == 0 ) {
/* Buffer empty, we're done */
return -1;
}
endptr = (end_of_file || bufferspace == 0) ? back : endptr + 1;
const size_t line_len = endptr - int_buffer;
const size_t numcopy = line_len > (size - 1) ? (size - 1) : line_len;
strncpy(out_buffer, int_buffer, numcopy);
out_buffer[numcopy] = 0;
memmove(int_buffer, int_buffer + numcopy, INTBUFFERSIZE - numcopy);
back -= numcopy;
return 1;
}
/* No full line available, and
* at end of file, so return 0. */
return 0;
}
int main(void)
{
char buffer[BUFFERSIZE];
FILE * fp = popen("./delayed_output", "r");
if ( !fp ) {
perror("error calling popen()");
return EXIT_FAILURE;
}
sleep(1); /* Give child process some time to write output */
int n = 0;
while ( n != -1 ) {
/* Loop until we get a line */
while ( !(n = get_line_if_ready(fileno(fp), buffer, BUFFERSIZE)) ) {
/* Here's where you could do other stuff if no line
* is available. Here, we'll just sleep for a while. */
printf("Line is not ready. Sleeping for five seconds.\n");
sleep(5);
}
/* Output it if we're not at end of file */
if ( n != -1 ) {
const size_t len = strlen(buffer);
if ( buffer[len - 1] == '\n' ) {
buffer[len - 1] = 0;
}
printf("Got line: %s\n", buffer);
}
}
if ( pclose(fp) == -1 ) {
perror("error calling pclose()");
return EXIT_FAILURE;
}
return 0;
}
and the output:
paul#thoth:~/src/sandbox/buffer$ ./buffer
(In function, just read [Here is some input...])
(Internal buffer is [Here is some input...])
Line is not ready. Sleeping for five seconds.
(In function, just read [and here is some more.
Yet more output is here...])
(Internal buffer is [Here is some input...and here is some more.
Yet more output is here...])
Got line: Here is some input...and here is some more.
Line is not ready. Sleeping for five seconds.
(In function, just read [and here's the end of it.
Here's some more, not ending with a newline. There are some more words here, to exceed our buffer.])
(Internal buffer is [Yet more output is here...and here's the end of it.
Here's some more, not ending with a newline. There are some more words here, to exceed our buffer.])
Got line: Yet more output is here...and here's the end of it.
Got line: Here's some more, not ending with a newline. There are some
Got line: more words here, to exceed our buffer.
paul#thoth:~/src/sandbox/buffer$
Is there any way to tell how many bytes are ready to be read?
Not that I'm aware of in C99/POSIX. I'm guessing this functionality wasn't deemed useful since files have a fixed size (most of the time, anyways). Unfortunately select() is very rudimentary as you have already seen.
I guess what I really want is a way to know if a full line is ready to be read before calling fgets.
fgets() buffers in a loop until a '\n' is reached. This action consumes the input from the underlying file descriptor, so you'll need to implement a non-blocking version yourself.
Related
I'm trying to use the functions read() and write() from unistd.h, but whenever I try input anything, it does not work. And I am only alowed to use functions from fcntl.h and unistd.h, not those from stdio.h.
Here is my code:
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd_in = open("/dev/pts/5", O_RDONLY);
int fd_write = open("/dev/pts/log.txt", O_RDWR);
char buf[20];
ssize_t bytes_read;
if (fd_in == -1){
char out[] = "Error in opening file";
write(fd_write, out, sizeof(out));
}
//using a while loop to read from input
while ((bytes_read = read(fd_in, buf, sizeof(buf))) > 0) {
char msg[] = "Block read: \n<%s>\n";
read(fd_write, msg, sizeof(msg));
//continue with other parts
}
}
The problem is that I don't get the desired output for the inputs I provide. For example:
//input
Hello
//output
Block read:
<Hello>
I wrote example code how to use read(2) and write(2). I don't know whether you need to use /dev/pts/ or not. I never used it, so also now I don't use it. Maybe my example will be helpful anyway.
The header string.h is included only for strlen(3).
#include <unistd.h>
#include <string.h>
int main (void) {
size_t input_size = 50;
// "+ 1" is for storing '\0'
char buffer[input_size + 1];
// We don't use the return value of
// memset(3), but it's good to know
// anyway that there is one. See also
// https://stackoverflow.com/q/13720428/20276305
memset(buffer, '\0', input_size + 1);
ssize_t bytes_read_count = -1;
ssize_t bytes_written_count = -1;
// Reading
bytes_read_count = read(STDIN_FILENO,
buffer,
input_size);
if (bytes_read_count == -1) {
// No return value checking (and also below). It
// would make little sense here since we exit the
// function directly after write(2), no matter if
// write(2) succeeded or not
write(STDERR_FILENO, "Error1\n", strlen("Error1\n"));
return 1;
}
// We want to be sure to have a proper string, just in
// case we would like to perform more operations on it
// one day. So, we need to explicitly end the array
// with '\0'. We need to do it regardless of the earlier
// memset(3) call because the user might input more
// than input_size, so all the '\0' would be
// overwritten
buffer[input_size] = '\0';
// Writing
bytes_written_count = write(STDOUT_FILENO,
buffer,
bytes_read_count);
if (bytes_written_count == -1) {
write(STDERR_FILENO, "Error2\n", strlen("Error2\n"));
return 1;
}
return 0;
}
Edit: I add a comment about memset(3) return value, and also remove checking it since it seemed unnecessary.
So, I asked here just a while ago, but half of that question was just me being dumb. And I still have issues. I hope that this will be clearer than the question before.
I'm writing POSIX cat, I nearly got it working, but I have couple of issues:
My cat can not read from a pipe and I really do not know why (redirecting (<) works fine)
I can not figure out how to make it continuously read stdin, without some issues. I had a version that worked "fine", but would create a stack-overflow. The other version wouldn't stop reading from stdin if there was only stdin i.e.: my-cat < file would read from stdin until it got terminated which it shouldn't, but it has to read from stdin and wait for termination if no files are suplied.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
char opt;
while ((opt = getopt(argc, argv, "u")) != EOF) {
switch(opt) {
case 'u':
/* Make the output un-buffered */
setbuf(stdout, NULL);
break;
default:
break;
}
}
argc -= optind;
argv += optind;
int i = 0, fildes, fs = 0;
do {
/* Check for operands, if none or operand = "-". Read from stdin */
if (argc == 0 || !strcmp(argv[i], "-")) {
fildes = STDIN_FILENO;
} else {
fildes = open(argv[i], O_RDONLY);
}
/* Check for directories */
struct stat fb;
if (!fstat(fildes, &fb) && S_ISDIR(fb.st_mode)) {
fprintf(stderr, "pcat: %s: Is a directory\n", argv[i]);
i++;
continue;
}
/* Get file size */
fs = fb.st_size;
/* If bytes are read, write them to stdout */
char *buf = malloc(fs * sizeof(char));
while ((read(fildes, buf, fs)) > 0)
write(STDOUT_FILENO, buf, fs);
free(buf);
/* Close file if it's not stdin */
if (fildes != STDIN_FILENO)
close(fildes);
i++;
} while (i < argc);
return 0;
}
Pipes don't have a size, and nor do terminals. The contents of the st_size field is undefined for such files. (On my system it seems to always contain 0, but I don't think there is any cross-platform guarantee of that.)
So your plan of reading the entire file at one go and writing it all out again is not workable for non-regular files, and is risky even for them (the read is not guaranteed to return the full number of bytes requested). It's also an unnecessary memory hog if the file is large.
A better strategy is to read into a fixed-size buffer, and write out only the number of bytes you successfully read. You repeat this until end-of-file is reached, which is indicated by read() returning 0. This is how you solve your second problem.
On a similar note, write() is not guaranteed to write out the full number of bytes you asked it to, so you need to check its return value, and if it was short, try again to write out the remaining bytes.
Here's an example:
#define BUFSIZE 65536 // arbitrary choice, can be tuned for performance
ssize_t nread;
char buf[BUFSIZE]; // or char *buf = malloc(BUFSIZE);
while ((nread = read(filedes, buf, BUFSIZE)) > 0) {
ssize_t written = 0;
while (written < nread) {
ssize_t ret = write(STDOUT_FILENO, buf + written, nread - written);
if (ret <= 0)
// handle error
written += ret;
}
}
if (nread < 0)
// handle error
As a final comment, your program lacks error checking in general; e.g. if the file cannot be opened, it will proceed anyway with filedes == -1. It is important to check the return value of every system call you issue, and handle errors accordingly. This would be essential for a program to be used in real life, and even for toy programs created just as an exercise, it will be very helpful in debugging them. (Error checking would probably have given you some clues in figuring out what was wrong with this program, for instance.)
Your cat (You can call it my-cat, but I preferred to call it felix, just permit me the pun) should be used with stdio all the time to get the benefit of the buffering done by the stdio package. Below is a simplified version of cat using exclusively stdio package (almost exactly equal as it appears in K&R) and you'll see that is completely efficient as shown (you will see that the structure is almost exactly as yours, but I simplify the processing of the data copy /like K&R book/ and the processing of arguments /yours is a bit meshy/):
felix.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#define ERR(_code, _fmt, ...) do { \
fprintf(stderr,"%s: " _fmt, progname, \
##__VA_ARGS__); \
if (_code) exit(_code); \
} while (0)
char *progname = "cat";
void process(FILE *f);
int main(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "u")) != EOF) {
switch (opt) {
case 'u': setbuf(stdout, NULL); break;
}
}
/* for the case it has been renamed, calculate the basename
* of argv[0] (progname is used in the macro ERR above) */
progname = strrchr(argv[0], '/');
progname = progname
? progname + 1
: argv[0];
/* shift options */
argc -= optind;
argv += optind;
if (argc) {
int i;
for (i = 0; i < argc; i++) {
FILE *f = fopen(argv[i], "r");
if (!f) {
ERR(EXIT_FAILURE,
"%s: %s (errno = %d)\n",
argv[i], strerror(errno), errno);
}
process(f);
fclose(f);
}
} else {
process(stdin);
}
exit(EXIT_SUCCESS);
}
/* you don't need to complicate here, fgetc and putchar use buffering as you stated in main
* (no output buffering if you do the setbuf(NULL) and input buffering all the time). The buffer
* size is best to leave stdio to calculate it, as it queries the filesystem to get the best
* input/output size and create buffers this size. and the processing is simple with a loop like
* the one below. You'll get no appreciable difference between this and any other input/output.
* you can believe me, I've tested it. */
void process(FILE *f)
{
int c;
while ((c = fgetc(f)) != EOF) {
putchar(c);
}
}
As you see, nothing has been specially done to support redirection, as redirection is not done inside a program, but done by the program that calls it (in this case by the shell) When you start a program, you receive three already open file descriptors. These are the ones that the shell is using, or the ones that the shell just puts in the places of 0, 1, and 2 before starting your program. So your program has nothing to do to cope with redirection. Everything is done (in this case) in the shell... and this is why your program redirection works, even if you have not done anything for it to work. You have only to do redirection if you are going to call a program with its input, output or standard error redirected somewhere (and this somewhere is not the standard input, output or error you have received from your parent process)... but this is not the case of my-cat.
I have to create a program that asks from standard input a string and write in standard error the string previously written.
This is my program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main() {
char *buffer = malloc(sizeof(char)*20);
int len = 0;
do {
len = read(STDIN_FILENO, buffer, 20);
if(len == -1)
write(STDERR_FILENO, "Error read\n", 10);
else
write(STDERR_FILENO, buffer, len);
} while(strncmp(buffer,"fine\n", 5));
free(buffer);
return 0;
}
The code works but I'm not satisfied..there is one problem:
The buffer is a 20char but I can insert more than 20 char...why? How I can limit the buffer to only 20 char?
The code works but I'm not satisfied..there is one problem: The buffer is a 20char but I can insert more than 20 char...why?
Because your program can't stop someone inputting more than 20 chars; all it can do is limit that it doesn't overflow the buffer which it already does - read() doesn't read more than the requested bytes. It only appears as if a read() call is reading more than size (20) but acutally read() reads only (upto) 20 chars and the rest is read in the next iteration.
No matter what method you use to read input and/or increase buffer size, this problem of "extra input" is always going to be there.
What you can do instead is check if if len is 20 and buffer[19] is not \n:
else {
write(STDERR_FILENO, buffer, len);
/* Read out the left over chars. */
if (len == 20 && buffer[19] != '\n') {
char c;
do {
read(STDIN_FILENO, &c, 1); /* left out the error checking */
} while (c != '\n');
}
Or increase the buffer size, say, to 512 bytes and then only look at the first 20 bytes that you're interested in.
Note: Add error checking for all read() and write() calls.
You're not allocating enough memory for your buffer. You always need 1 more to store the NUL terminating character. And you also need to remember to add that NUL character to the end of the string read in by read as it won't do it for you.
When you get an error, you should exit the loop.
#define BUF_SIZE (20)
int main() {
char *buffer = malloc(sizeof(char)*(BUF_SIZE+1));
int len = 0;
do {
len = read(STDIN_FILENO, buffer, BUF_SIZE);
if(len == -1) {
write(STDERR_FILENO, "Error read\n", 10);
break;
} else {
buffer[len]='\0';
write(STDERR_FILENO, buffer, len);
}
} while(strncmp(buffer,"fine\n", 5));
free(buffer);
return 0;
}
You'll probably also find that the strncmp(buffer,"fine\n", 5) isn't going to work as you'd need to process the read in string to handle lines of input as read will happily read in multiple lines at a time (assuming they all fit in the buffer size).
I'm making an upload form via a CGI interface. I'm writing it in C and don't want to use any outside libraries (ie. cgic).
I thought the program was complete, as the first test files uploaded correctly. But they were ASCII files. When I tested with a binary file (JPG). It seems that STDIN is trying to read the binary data as ASCII which creates a problem for characters like \0 which is present at the end of an ASCII file, but is a common character in binary files. The results of uploading a 1.9MB file end up with a 38kB file.
When searching how to change the STDIN stream to binary, I was referred to the command freopen and told to use NULL as the argument for the file. example 1
It says:
If filename is a null pointer, the freopen() function shall attempt to
change the mode of the stream to that specified by mode, as if the
name of the file currently associated with the stream had been used.
In this case, the file descriptor associated with the stream need not
be closed if the call to freopen() succeeds. It is
implementation-defined which changes of mode are permitted (if any),
and under what circumstances.
But when I check the man page on my system with man 3 freopen, it doesn't say any of
this at all. Furthermore, reading the man page, I find out the the
option for binary (adding 'b' to the mode) is no longer recognized and
only exists for archaic compliancy:
The mode string can also include
the letter 'b' either as a last character or as a character between
the characters in any of the two-character strings described above.
This is strictly for compatibility with C89 and has no effect; the 'b'
is ignored on all POSIX conforming systems, including Linux.
So right now I'm completely lost. How can I change the STDIN stream to read binary input?
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
// Declare constants.
#define BUF_SIZE 4096
#define FILENAME_SIZE 500
#define MARKER_SIZE 100
#define RETURN_FAILURE 0
#define RETURN_SUCCESS 1
#define SEARCH_STRING_1 "filename=\""
#define SEARCH_STRING_2 "\r\n\r\n"
// Declare global variables.
char filename[FILENAME_SIZE + 1];
char *program_name;
// Declare function prototype.
void print_footer (void);
void print_header (void);
void process_input (char *data);
int main (int argc, char *argv[])
{
// Declare variables.
long long ret;
char buf[BUF_SIZE + 1];
// Get program name for error reporting.
program_name = basename(argv[0]);
// Prepare output for browser.
print_header();
// Protect variable against buffer overflow.
buf[BUF_SIZE] = '\0';
// Loop through all the file data.
while(1)
{
// Read in the next block of data.
if((ret = (long long) fread(buf, 1, BUF_SIZE, stdin)) != BUF_SIZE)
{
// Check for error.
if(ferror(stdin) != 0)
{
printf("%s: An error occurred while reading the input file.<br>\n", program_name);
process_input(NULL);
exit(EXIT_FAILURE);
}
// Check for EOF.
else if(feof(stdin) != 0)
break;
}
// Terminate and process uploaded data.
buf[ret] = '\0';
process_input(buf);
}
// Terminate and process uploaded data.
buf[ret] = '\0';
process_input(buf);
// Finish user output, close output file and exit.
print_footer();
process_input(NULL);
exit(EXIT_SUCCESS);
}
void process_input (char *data)
{
// Declare variables.
char *ptr1= NULL;
char *ptr2;
int x = 0;
static FILE *fp;
static int flag = 0;
static char marker[MARKER_SIZE + 1];
// If data is NULL, close output file.
if(data == NULL)
{
if(fclose(fp) == EOF)
{
printf("%s: process_input: close failed (%s)<br>\n", program_name, strerror(errno));
exit(EXIT_FAILURE);
}
return;
}
// Check if this is the first time through.
if(flag == 0)
{
// Get marker.
if((ptr1 = strchr(data, '\n')) == NULL)
{
printf("%s: process_input: strchr(1) failed (\n)<br>\n", program_name);
exit(EXIT_FAILURE);
}
ptr1[0] = '\0';
strcpy(marker, data);
ptr1[0] = '\n';
// Get filename.
if((ptr1 = strstr(data, SEARCH_STRING_1)) == NULL)
{
printf("%s: process_input: strstr(1) failed (%s)<br>\n", program_name, SEARCH_STRING_1);
exit(EXIT_FAILURE);
}
// Advance pointer to start of filename.
ptr1 += 10;
// Find end of filename.
if((ptr2 = strchr(ptr1, '"')) == NULL)
{
printf("%s: process_input: strchr(2) failed (\")<br>\n", program_name);
exit(EXIT_FAILURE);
}
// Terminate and store filename.
ptr2[0] = '\0';
strcpy(filename, ptr1);
ptr2[0] = '"';
// Remove spaces from filename.
while(filename[x] != '\0')
{
if(filename[x] == ' ')
filename[x] = '.';
x++;
}
// Open output file.
if((fp = fopen(filename, "wb")) == NULL)
{
printf("%s: process_input: fopen failed (%s) (%s)<br>\n", program_name, strerror(errno), filename);
exit(EXIT_FAILURE);
}
// Find start of file data.
if((ptr1 = strstr(data, SEARCH_STRING_2)) == NULL)
{
printf("%s: process_input: strstr(2) failed (%s)<br>\n", program_name, SEARCH_STRING_2);
fclose(fp);
exit(EXIT_FAILURE);
}
// Set flag.
flag++;
// Advance pointer to start of file data.
ptr1 += 4;
// Change STDIN stream to binary.
if(freopen(NULL, "rb", stdin) == NULL)
{
printf("%s: process_input: freopen failed (%s)<br>\n", program_name, strerror(errno));
fclose(fp);
exit(EXIT_FAILURE);
}
}
// Catch everything else.
else
{
ptr1 = data;
if((ptr2 = strstr(ptr1, marker)) != NULL)
ptr2[0 - 2] = '\0';
}
// Write file data.
if(fwrite(ptr1, 1, strlen(ptr1), fp) != strlen(ptr1))
{
printf("%s: process_input: write failed (%s)<br>\n", program_name, strerror(errno));
fclose(fp);
exit(EXIT_FAILURE);
}
}
void print_footer (void)
{
printf("\nMade it!\n");
}
void print_header (void)
{
printf("Content-type: text/plain\r\n\r\n");
}
Ok, it appears what #NominalAnimal said was correct. You can store binary data in a string, but the moment you use any function in the string.h library, it almost always changes what is stored in that string (if the data is binary).
The easy solution is to make a separate function that takes a pointer to the binary data and do your string searches in that function, returning what pertinent information is needed. That way, the original data is never changed.
'stdin' is a macro of STDIN_FILENO, which is egal to 0. See also 'unistd.h'.
You are not showing your code, but I think you stop when you encounter a '\0' or a non-ascii char, since you said you were using 'fread()'.
You have to stop when fread() function returns 0, which means it stopped to read : it encountered EOF.
I'm programming in C an IRC chat client. everything it's working well except I can't read the whole answer sent by the server. here's the code:
char buffer[2048];
write_on_screen(current_page(), "LOG COMMAND", command);
write(sockfd, command, strlen(command)); //write to socket
bzero(buffer, sizeof(buffer));
read(sockfd, buffer, sizeof(buffer));
write_on_screen(current_page(), "RESPONSE", buffer);
return buffer;
most of the time buffer will contain just a piece of the response (which is shorter than 2048 bytes) and other times it contains nothing. in both cases if I do another read() after the first one, it returns me the rest of the answer or another small piece (and then I've to do another read() again). if I put a sleep(1) between write() and read() I get the whole answer, but I'm sure this not a good pratice.
Is there some way I can avoid this?
thank you in advance
You're making the usual mistakes. It is impossible to write correct network code without storing the result of read() or recv() into a variable. You have to:
Check it for -1, and if so look at errno to see whether was fatal, which it almost always is except for EAGAIN/EWOULDBLOCK, and if fatal close the socket and abandon the process.
Check it for zero, which means the peer disconnected. Again you must close the socket and abandon the process.
Use it as the count of bytes actually received. These functions are not obliged nor guaranteed to fill the buffer. Their contract in blocking mode is that they block until an error, end of stream, or at least one byte is transferred. If you're expecting more than one byte, you normally have to loop until you get it.
According to RFC-1459, a single line of text in IRC can contain up to 512 characters and is terminated by a CRLF (\r\n) pair. However:
You're not guaranteed to receive exactly 512 bytes each time. For example, you might receive a comparatively short message from someone else one in the channel: Hi!
Related to the above: A group of 512 bytes might represent more than one message. For example, the buffer might contain a whole line, plus part of the next line: PRIVMSG <msgtarget> <message>\r\nPRIVMS
Given that you could have zero-or-more complete lines plus zero-or-one incomplete lines in your buffer[] at any time, you could try doing something along the lines of:
char buffer[2048];
while(keep_going)
{
char **lines;
int i, num_lines;
// Receive data from the internet.
receiveData(buffer);
// Create an array of all COMPLETE lines in the buffer (split on \r\n).
lines = getCompleteLines(buffer, &num_lines);
removeCompleteLinesFromBuffer(buffer);
// Handle each COMPLETE line in the array.
for (i = 0; i < num_lines; ++i) { handle_line(lines[i]); }
freeLines(lines);
}
This would allow you to handle zero or more complete lines in one go, with any incomplete line (i.e anything after the final \r\n pair) being kept around until the next call to receiveData().
You need to loop around read() until a CRLF had been detected.
A possible way to do this would be:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
ssize_t read_until_crlf(int sd, char * p, size_t s, int break_on_interupt)
{
ssize_t bytes_read = 0;
ssize_t result = 0;
int read_cr = 0;
int read_crlf = 0;
while (bytes_read < s)
{
result = read(sd, p + bytes_read, 1);
if (-1 == result)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
continue;
}
else if (EINTR == errno)
{
if (break_on_interupt)
{
break;
}
continue;
}
else
{
perror("read() failed");
break;
}
}
else if (0 == result)
{
break; /* peer disconnected */
}
if ('\r' == p[bytes_read])
{
read_cr = 1;
}
else if (('\n' == p[bytes_read]) && read_cr)
{
read_crlf = 1;
break; /* CRLF detected */
}
else
{
read_cr = 0;
}
++bytes_read;
}
if (!read_crlf)
{
result = -1; /* Buffer full without having read a CRLF. */
errno = ENOSPC; /* ... or whatever might suite. */
}
return (0 >= result) ?result :bytes_read;
}
Call it like this:
#include <stdio.h>
ssize_t read_until_crlf(int sd, char * p, size_t s, int break_on_interupt);
int main(void)
{
int sd = -1;
/* init sd here */
{
char line[2048] = "";
ssize_t result = read_until_crlf(sd, line, sizeof line, 0);
if (-1 == result)
{
perror("read_until_newline() failed");
}
printf("read '%s'\n", line);
}
return 0;
}