Why am I getting extra character when trying to write to file? - c

This is supposed to flips upper and lower case letters but its not flipping just adding random characters.
int in = open(argv[1], O_RDONLY);
int out = open(argv[2], O_CREAT | O_WRONLY, 0624);
char buff[65];
buff[64] = '\0';
if(argc < 2){
printf("Not enough arguments");
return 1;
}
else if(argv[1] == 0 || argv[2] == 0){
printf("No file");
return 1;
}
int i = read(in,buff,64);
for (i = 0; buff[i]!='\0'; i++) {
if(buff[i] >= 'a' && buff[i] <= 'z') {
printf("%d", buff[i]-32);
} else if (buff[i] >= 'A' && buff[i] <= 'Z') {
printf("%d", buff[i]+32);
} else {
printf("%d", buff[i]);
}
}
write(out, buff, 64);
close(in);
close(out);
return 0;
}
How do I get it to read the character and flip without extras?

If your input file does not contain a '\0' as last character, your condition buff[i]!='\0' depends on random contents.
Change these lines:
char buff[65];
buff[64] = '\0';
to this line:
char buff[65] = { 0 };
However, read() tells you the number of bytes it read. You can use that value to mark the end:
int n = read(in,buff,64);
for (i = 0; i < n; i++) {
/* ... */
}
write(out, buff, n);

Write a function that reads a line, up to some maximum size; separate the logic of reading the file from other processing,
int readline(int fh, char* buff, int maxsize) {
int rc = read(fh,buff,maxsize);
if( rc < 0 ) {
printf("read error, %d\n",rc);
return rc;
}
return rc;
}
Write a function that writes the converted buffer, separate the logic of writing the file and other processing,
int writeline(int fh, char* buff, int len) {
int wc = write(fh, buff, len);
return wc;
}
Write a function that flips the case; separate the logic from reading and writing the file,
char* flipcase(char* buff, int len) {
if(!buff || len<1) return buff;
char* cp = buff;
for (int ix = 0; ix<len; ix++, cp++ ) {
if( isupper(*cp) { // in [A-Z]
// printf("%d", *cp-32); // not portable
*cp = tolower(*cp); // modify buff[ix]
}
else if( islower(*cp) ) { // in [a-z]
// printf("%d", *cp+32); // not portable
*cp = toupper(*cp); // modify buff[ix]
}
// else {
// unchanged
// }
// printf("%d", *cp);
}
return buff;
}
Build a function that handles each line separately,
# define MAXLINE (256) // named 'constant'
int doline(int fin, int fout) {
char buff[MAXLINE+1] = { 0 };
int rc = readline(fin, buff, MAXLINE);
// check results of readline here
flipcase(buff, rc);
int wc = writeline(fout, buff, rc);
// check results of writeline here
return rc;
}
Here you would handle your (argc, argv) and open your files,
if(argc < 3) {
printf("Not enough arguments");
return 1;
}
if(argv[1] == 0 || argv[2] == 0) {
printf("No file");
return 1;
}
int fin = open(argv[1], O_RDONLY);
if( !fin ) {
printf("open %s failed\n",argv[1]);
return 2;
}
int fout = open(argv[2], O_CREAT | O_WRONLY, 0624);
if( !fout ) {
printf("open %s failed\n",argv[2]);
close(fout);
return 2;
}
int rc = 0;
// process one line
rc = doline(fin,fout);
// or, process every line in file
for( ; rc = doline(fin,fout) >= 0; ) {
}
close(fin);
close(fh);

Related

testing the program for various memory allocation errors and memory leaks

The tee utility copies its standard input to both stdout and to a file. This allows the user to view the output of a command on the console while writing a log to a file at the same time.
My program implements the tee command from linux POSIX system calls, with the -a option.
How can I modify the program to test for possible memory allocation errors? Positive memory leaks.
Also, the memory allocation doesn't seem right to me. When creating a new buffer each time I call getline(), should I declare and initialize line outside the loop and reallocate it only after the loop has ended?
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "apue.h"
int writeAll(int fd, char *buf, int buflen);
int main(int argc, char *argv[]) {
struct stat status;
int option;
bool append = false;
int errCode = 0;
while ((option = getopt(argc, argv, "a")) != -1) {
switch (option) {
case 'a':
append = true;
break;
}
}
// We need to write in all the files given as parameter AND stdout.
int numFileDescriptors = argc - optind + 1;
int *fileDescriptors = malloc((numFileDescriptors + 1) * sizeof(*fileDescriptors));
char **fileNames = malloc((numFileDescriptors + 1) * sizeof(*fileNames));
int lastFileDescriptor = 0;
fileDescriptors[0] = STDOUT_FILENO;
fileNames[0] = "stdout";
int flags = O_CREAT | O_WRONLY;
if (append) {
flags = flags | O_APPEND;
} else {
flags = flags | O_TRUNC;
}
for (int i = optind; i < argc; i++) {
if (access(argv[i], F_OK) == 0) {
if (access(argv[i], W_OK) < 0) {
err_msg("%s: Permission denied", argv[i]);
errCode = 1;
continue;
}
}
if (lstat(argv[i], &status) < 0) {
status.st_mode = 0;
}
if (S_ISDIR(status.st_mode)) {
err_msg("%s: Is a directory", argv[i]);
errCode = 1;
continue;
}
int fd = open(argv[i], flags, 0644);
if (fd < 0) {
err_msg("%s: Failed to open", argv[i]);
errCode = 1;
continue;
}
lastFileDescriptor = lastFileDescriptor + 1;
fileDescriptors[lastFileDescriptor] = fd;
fileNames[lastFileDescriptor] = argv[i];
}
while (true) {
size_t len = 0;
ssize_t read = 0;
char *line = NULL;
read = getline(&line, &len, stdin);
if (read == -1) {
break;
}
for (int i = 0; i <= lastFileDescriptor; i++) {
int written = writeAll(fileDescriptors[i], line, strlen(line));
if (written < 0) {
err_msg("%s: Failed to write", fileNames[i]);
errCode = 1;
}
}
}
for (int i = 0; i <= lastFileDescriptor; i++) {
close(fileDescriptors[i]);
}
free(fileDescriptors);
free(fileNames);
return errCode;
}
int writeAll(int fd, char *buf, int buflen) {
ssize_t written = 0;
while (written < buflen) {
int writtenThisTime = write(fd, buf + written, buflen - written);
if (writtenThisTime < 0) {
return writtenThisTime;
}
written = written + writtenThisTime;
}
return written;
}
Testing for memory allocation failure is simple: just add tests, report the failure and exit with a non zero exit status.
To avoid memory leaks, you must free the line that was allocated by getline inside the while (true) loop:
while (true) {
size_t len = 0;
char *line = NULL;
ssize_t nread = getline(&line, &len, stdin);
if (nread == -1) {
if (errno == ENOMEM) {
fprintf(stderr, "out of memory\n");
exit(1);
}
free(line);
break;
}
for (int i = 0; i <= lastFileDescriptor; i++) {
int written = writeAll(fileDescriptors[i], line, nread);
if (written < 0) {
err_msg("%s: Failed to write", fileNames[i]);
errCode = 1;
}
}
free(line);
}
Alternately, you can reuse the same line for the next iteration and only free the buffer after the while loop:
size_t len = 0;
char *line = NULL;
while (true) {
ssize_t nread = getline(&line, &len, stdin);
if (nread == -1) {
if (errno == ENOMEM) {
fprintf(stderr, "out of memory\n");
exit(1);
}
break;
}
for (int i = 0; i <= lastFileDescriptor; i++) {
int written = writeAll(fileDescriptors[i], line, nread);
if (written < 0) {
err_msg("%s: Failed to write", fileNames[i]);
errCode = 1;
}
}
}
free(line);
Note that reading a full line at a time is risky as the input might contain very long, possibly unlimited lines (eg: /dev/zero). You might want to use fgets() to read a line with a limited length and dispatch the contents as you read, possibly splitting long lines:
char line[4096];
while (fgets(line, sizeof line, stdin)) {
size_t len = strlen(line);
for (int i = 0; i <= lastFileDescriptor; i++) {
int written = writeAll(fileDescriptors[i], line, len);
if (written < 0) {
err_msg("%s: Failed to write", fileNames[i]);
errCode = 1;
}
}
}
The above code has a limitation: if the input streams contains null bytes, they will cause some data to be lost in translation. A solution is to not use fgets(), but getchar() directly:
for (;;) {
char line[4096];
size_t len = 0;
int c;
while (len < sizeof(line) && (c = getchar()) != EOF)) {
if ((line[len++] = c) == '\n')
break;
}
if (len > 0) {
for (int i = 0; i <= lastFileDescriptor; i++) {
int written = writeAll(fileDescriptors[i], line, len);
if (written < 0) {
err_msg("%s: Failed to write", fileNames[i]);
errCode = 1;
}
}
}
if (c == EOF)
break;
}

Changing STDOUT to file in ncat source code

I managed to compile ncat. I am using -k option to keep server open. Instead of accepting data to STDOUT, my goal is to write to files instead. So far I was able to write to a file instead of STDOUT but my goal is to loop through new files on each new connection. Right now it is appending to the same filename_0 and f++ is not incrementing. Here is what I have so far. The original code will be below. The difference is in the else clause, basically if n is actually greater than 0. On each loop, n is 512 bytes until the last chunk. I just want to be able to have new files from each new connection. filename_0, filename_1, filename_3, etc.
MODIFIED CODE:
/* Read from a client socket and write to stdout. Return the number of bytes
read from the socket, or -1 on error. */
int read_socket(int recv_fd)
{
char buf[DEFAULT_TCP_BUF_LEN];
struct fdinfo *fdn;
int nbytes, pending;
int f = 0;
fdn = get_fdinfo(&client_fdlist, recv_fd);
ncat_assert(fdn != NULL);
nbytes = 0;
do {
int n, s;
n = ncat_recv(fdn, buf, 512, &pending);
if (n <= 0) {
if (o.debug)
logdebug("Closing fd %d.\n", recv_fd);
#ifdef HAVE_OPENSSL
if (o.ssl && fdn->ssl) {
if (nbytes == 0)
SSL_shutdown(fdn->ssl);
SSL_free(fdn->ssl);
}
#endif
close(recv_fd);
checked_fd_clr(recv_fd, &master_readfds);
rm_fd(&client_fdlist, recv_fd);
checked_fd_clr(recv_fd, &master_broadcastfds);
rm_fd(&broadcast_fdlist, recv_fd);
conn_inc--;
if (get_conn_count() == 0)
checked_fd_clr(STDIN_FILENO, &master_readfds);
return n;
}
else {
char filename[20];
snprintf(filename, sizeof(char) * 20, "filename_%i", f);
FILE *fp = fopen(filename, "a");
if (fp == NULL)
{
printf("Could not open file");
return 0;
}
//Write(STDOUT_FILENO, buf, n);
s = fwrite(buf, 1, n, fp);
fclose(fp);
f++;
nbytes += n;
}
} while (pending);
return nbytes;
}
ORIGINAL CODE:
int read_socket(int recv_fd)
{
char buf[DEFAULT_TCP_BUF_LEN];
struct fdinfo *fdn;
int nbytes, pending;
fdn = get_fdinfo(&client_fdlist, recv_fd);
ncat_assert(fdn != NULL);
nbytes = 0;
do {
int n;
n = ncat_recv(fdn, buf, sizeof(buf), &pending);
if (n <= 0) {
if (o.debug)
logdebug("Closing fd %d.\n", recv_fd);
#ifdef HAVE_OPENSSL
if (o.ssl && fdn->ssl) {
if (nbytes == 0)
SSL_shutdown(fdn->ssl);
SSL_free(fdn->ssl);
}
#endif
close(recv_fd);
checked_fd_clr(recv_fd, &master_readfds);
rm_fd(&client_fdlist, recv_fd);
checked_fd_clr(recv_fd, &master_broadcastfds);
rm_fd(&broadcast_fdlist, recv_fd);
conn_inc--;
if (get_conn_count() == 0)
checked_fd_clr(STDIN_FILENO, &master_readfds);
return n;
}
else {
Write(STDOUT_FILENO, buf, n);
nbytes += n;
}
} while (pending);
return nbytes;
}
I was able to figure out using the other functions involved. i passed a pointer into this function to write to it. the handler is a function i added the open() file pointer to.

Bad address in my file I/O operation function

I am learning Linux file I/O operation functions and trying to write a program that could creat a new file, read or write it as well as change the permissions and attributes of a file. When I try to use function "open(pathname, O_RDWR);" to get a file descriptor and use "read()" function to read current file, there is always "bad address" error. However, when I use "open(pathname, O_RDWR|O_CREATE,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)" before write, the file will be writen currectly.
Here are the source code when I using that two function.
//the welcome function is a function that serve as a function nemu bar
void Create()
{
char filepath[8];
int fd;
printf("请输入要创建的文件所在的目录及其名称:\n");
scanf("%s",&filepath);
fd = open(filepath, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH );//总是以默认的方式创建文件
if(fd == -1)
{
printf("创建失败,请重新输入文件名及其所在目录创建\n");
sleep(5);
Create();
}
else
{
char is;
printf("创建成功!是否对新创建的文件进行写操作?");
scanf("%s",&is);
if(is == 'y'||is == 'Y')
Write(fd,filepath);
else if(is == 'n'||is == 'N')
{
close(fd);
welcome();
}
else
{
printf("您的输入有误,回到欢迎界面中…\n");
close(fd);
sleep(5);
welcome();
}
}
}
void Write(int fd, char *path)
{
volatile int len;
if(fd == -1)
{
len = 0;
char filepath[8];
printf("请输入要写的文件的完整路径及其名称:");
scanf("%s",&filepath);
fd = open(filepath, O_RDWR);
if(fd == -1)
{
perror("open during write");
Write(-1, "");
}
char w1[BUFFERSIZE], *w;
printf("请输入要写入文件的内容,以回车结束:\n");
scanf("%s",&w1);
w = w1;
//用于写入的模块
int n = 1;
while(n > 0)
{
if(n = write(fd, w+len, (strlen(w)-len))<0)
{
perror("write");
return;
}
len += n;
}
char is;
printf("是否查看写入的内容?");
scanf("%s",&is);
if(is == 'Y'||is == 'y')
{
close(fd);
Read(filepath,strlen(w));
}
else
{
close(fd);
welcome();
}
}
else
{
printf("请输入要写入文件的数据:\n");
char w1[BUFFERSIZE];
char *w;
scanf("%s",&w1);
w = w1;
len = 0;
int n = 1;
while(n > 0)
{
n = write(fd, w+len, (strlen(w)-len));
len += n;
}
char is;
printf("是否查看写入的内容?");
scanf("%s",&is);
if(is == 'Y'||is == 'y')
{
close(fd);
Read(path,strlen(w));
}
else
{
close(fd);
welcome();
}
}
};
Could you please help me solve this question? I would be highly appreciated if you could help me!

Read does not receive EOF even though I close all my pipes C

Trying to give a Minimal Viable Example of the problem. Basically the method send_chars_to_reducers sends a character to the proper reducer_pipe. The fork_reducers function remains in its while loop until it receives EOF but it never does even though I close all reducer pipes in send_chars_to_reducers. I know it doesnt exit the while loop because it never prints exiting reducers.
C Code
void send_chars_to_reducers(char * line) {
printf("SEND_CHARS_TO_REDUCERS read: %s\n\n", line);
int i;
int ob_size = 1;
int wlen = 0;
for (i = 0; i < strlen(line); i++) {
if (line[i] >= ALPHA_OFFSET && line[i] < ALPHA_OFFSET + LETTERS) {
int pipe_num = line[i] - ALPHA_OFFSET;
printf("SENDING %c TO REDUCER PIPE %d\n", line[i], pipe_num);
wlen = print_if_err(write(reducer_pipes[pipe_num][PIPE_WRITE_END], &line[i], ob_size), "write");
printf("WROTE %s to REDUCER %d\n", line[i], i);
}
}
close_reducer_pipes();
}
void close_reducer_pipes(void) {
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
close(reducer_pipes[i][PIPE_WRITE_END]);
close(reducer_pipes[i][PIPE_READ_END]);
}
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
if (mapper_pid == 0) {
int j;
for (j=0; j < NUM_OF_MAPPERS; j++) {
close(mapper_pipes[i][PIPE_WRITE_END]);
if (j != i) {
close(mapper_pipes[j][PIPE_READ_END]);
}
}
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
send_chars_to_reducers(ibuf);
close_reducer_pipes();
//printf("forked mapper%d read: %s\n\n", i, ibuf);
close(mapper_pipes[i][PIPE_READ_END]);
_exit(0);
}
}
}
void fork_reducers(void) {
printf("HELLLOOOO FROM REDUCER\n");
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
while (1) {
rlen = print_if_err(read(reducer_pipes[i][PIPE_READ_END], ibuf, 1), "read");
if (rlen > 0) {
printf("REDUCER #%d, read %s\n", i, ibuf);
} else {
break;
}
}
printf("exiting reducer\n");
_exit(0);
}
}
}
Entire C Code
#include <sys/wait.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define BUFFER_SIZE 1024
#define ALPHA_OFFSET 97
#define LETTERS 26
const int NUM_OF_MAPPERS = 4;
const int NUM_OF_REDUCERS = 26;
const int PIPE_READ_END = 0;
const int PIPE_WRITE_END = 1;
const int PIPE_BUFFER_SIZE = 1000;
int mapper_pipes[4][2];
int reducer_pipes[26][2];
int letter_count[26];
void init_letter_count(void) {
int i;
for (i =0; i < 26; i++) {
letter_count[i] = 0;
}
}
void pipe_wrapper(int pipefd[]) {
int ret = pipe(pipefd);
if (ret == -1) {
perror("Error. Failed when trying to create pipes.");
exit(EXIT_FAILURE);
}
}
void create_mapper_pipes(void) {
int i;
for (i = 0; i < NUM_OF_MAPPERS; i++) {
pipe_wrapper(mapper_pipes[i]);
}
}
void create_reducer_pipes(void) {
int i;
for (i=0; i < NUM_OF_REDUCERS; i++) {
pipe_wrapper(reducer_pipes[i]);
}
}
// Prints an error msg and exits if one occurs. Else, returns the system call value.
int print_if_err(int syscall_val, const char* syscall_name) {
if (syscall_val < 0) {
perror(syscall_name);
exit(errno);
} else {
//No syscall error we can return
return syscall_val;
}
}
void send_chars_to_reducers(char * line) {
printf("SEND_CHARS_TO_REDUCERS read: %s\n\n", line);
int i;
int ob_size = 1;
int wlen = 0;
for (i = 0; i < strlen(line); i++) {
if (line[i] >= ALPHA_OFFSET && line[i] < ALPHA_OFFSET + LETTERS) {
int pipe_num = line[i] - ALPHA_OFFSET;
printf("SENDING %c TO REDUCER PIPE %d\n", line[i], pipe_num);
wlen = print_if_err(write(reducer_pipes[pipe_num][PIPE_WRITE_END], &line[i], ob_size), "write");
printf("WROTE %c to REDUCER %d\n", line[i], pipe_num);
}
}
printf("END OF SEND CHAR FOR LOOP");
close_reducer_pipes();
}
void close_reducer_pipes(void) {
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
print_if_err(close(reducer_pipes[i][PIPE_WRITE_END]), "close");
print_if_err(close(reducer_pipes[i][PIPE_READ_END]), "close");
}
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
if (mapper_pid == 0) {
int j;
for (j=0; j < NUM_OF_MAPPERS; j++) {
close(mapper_pipes[i][PIPE_WRITE_END]);
if (j != i) {
close(mapper_pipes[j][PIPE_READ_END]);
}
}
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
send_chars_to_reducers(ibuf);
//printf("forked mapper%d read: %s\n\n", i, ibuf);
close(mapper_pipes[i][PIPE_READ_END]);
_exit(0);
}
}
}
void fork_reducers(void) {
printf("HELLLOOOO FROM REDUCER\n");
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
while (1) {
rlen = print_if_err(read(reducer_pipes[i][PIPE_READ_END], ibuf, 1), "read");
printf("RLEN = %d\n", rlen);
if (rlen > 0) {
int letter_count_i = ibuf[0] - ALPHA_OFFSET;
printf("REDUCER #%d, read %s, letter_count_i = %d\n", i, ibuf, letter_count_i);
letter_count[letter_count_i]++;
} else {
break;
}
}
printf("REDUCER EXITING\n");
_exit(0);
}
}
}
void send_lines_to_mappers(void) {
int wlen = 0;
char obuf[PIPE_BUFFER_SIZE];
int ob_size;
int count = 0;
char buff[BUFFER_SIZE]; // a buffer for each line of the file
FILE *input_file = fopen("input.txt", "r");
// read the input file line by line
while(fgets(buff, BUFFER_SIZE, input_file) > 0) {
//printf("send_lines_to_mappers read: %s\n\n", buff);
ob_size = sizeof buff;
switch(count) {
case 0 :
write(mapper_pipes[0][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[0][PIPE_WRITE_END]);
close(mapper_pipes[0][PIPE_READ_END]);
break;
case 1 :
write(mapper_pipes[1][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[1][PIPE_WRITE_END]);
close(mapper_pipes[1][PIPE_READ_END]);
break;
case 2 :
write(mapper_pipes[2][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[2][PIPE_WRITE_END]);
close(mapper_pipes[2][PIPE_READ_END]);
break;
case 3 :
write(mapper_pipes[3][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[3][PIPE_WRITE_END]);
close(mapper_pipes[3][PIPE_READ_END]);
break;
default :
printf("you did something wrong in send_lines_to_mappers loop");
}
count++;
}
fclose(input_file);
}
int main(void) {
init_letter_count();
// Setup the mapper pipes
create_mapper_pipes();
create_reducer_pipes();
fork_reducers();
fork_mappers();
send_lines_to_mappers();
return 0;
}
When you create a pipe, there are two ends ("file descriptors"), the reading end and the writing end.
When you fork(), the child process inherits ALL the open file descriptors, including both ends of ANY open pipe.
So, if you want:
[child1] >===pipe1===> [parent] >===pipe2===> [child2]
for example, then you have a several file descriptors to close.
In child1, you need to close the READ end of pipe1 AND BOTH ends of
pipe2.
In child2, you need to close the WRITE end of pipe2 and BOTH ends of
pipe1.
In the parent, after you are done forking, you need to close the
WRITE end of pipe1 and the READ end of pipe2.
As you have lots of pipes, you will have even more closing to do.
If you are not sure you have everything closed, the program lsof ("list open file-descriptors") can be helpful.

Read line by line from a socket buffer

I want to write a function that read line by line from a socket buffer obtained from third parameter from read() function from unistd.h header.
I have wrote this:
int sgetline(int fd, char ** out)
{
int buf_size = 128;
int bytesloaded = 0;
char buf[2];
char * buffer = malloc(buf_size);
char * newbuf;
int size = 0;
assert(NULL != buffer);
while( read(fd, buf, 1) > 0 )
{
strcat(buffer, buf);
buf[1] = '\0';
bytesloaded += strlen(buf);
size = size + buf_size;
if(buf[0] == '\n')
{
*out = buffer;
return bytesloaded;
}
if(bytesloaded >= size)
{
size = size + buf_size;
newbuf = realloc(buffer, size);
if(NULL != newbuf)
{
buffer = newbuf;
}
else
{
printf("sgetline() allocation failed!\n");
exit(1);
}
}
}
*out = buffer;
return bytesloaded;
}
but I have some problems with this function, for example, if the input is something like:
HTTP/1.1 301 Moved Permanently\r\n
Cache-Control:no-cache\r\n
Content-Length:0\r\n
Location\r\nhttp://bing.com/\r\n
\r\n\r\n
and I do
int sockfd = socket( ... );
//....
char* tbuf;
while(sgetline(sockfd, &tbuf) > 0)
{
if(strcmp(tbuf,"\r\n\r\n") == 0)
{
printf("End of Headers detected.\n");
}
}
the above C application does not output "End of Header detected.". Why is this, and how can I fix this?
It's not OK to read one byte at a time, because you are making too many system calls - better is to use a buffer, read a chunk and check if you got \n. After getting a line, the rest of the bytes read remains in the buffer, so you cannot mix read/recv with read_line. Another version of read n bytes using this kind of buffer can be write...
My version to read a line, and a little example to use it.
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#define CBSIZE 2048
typedef struct cbuf {
char buf[CBSIZE];
int fd;
unsigned int rpos, wpos;
} cbuf_t;
int read_line(cbuf_t *cbuf, char *dst, unsigned int size)
{
unsigned int i = 0;
ssize_t n;
while (i < size) {
if (cbuf->rpos == cbuf->wpos) {
size_t wpos = cbuf->wpos % CBSIZE;
//if ((n = read(cbuf->fd, cbuf->buf + wpos, (CBSIZE - wpos))) < 0) {
if((n = recv(cbuf->fd, cbuf->buf + wpos, (CBSIZE - wpos), 0)) < 0) {
if (errno == EINTR)
continue;
return -1;
} else if (n == 0)
return 0;
cbuf->wpos += n;
}
dst[i++] = cbuf->buf[cbuf->rpos++ % CBSIZE];
if (dst[i - 1] == '\n')
break;
}
if(i == size) {
fprintf(stderr, "line too large: %d %d\n", i, size);
return -1;
}
dst[i] = 0;
return i;
}
int main()
{
cbuf_t *cbuf;
char buf[512];
struct sockaddr_in saddr;
struct hostent *h;
char *ip;
char host[] = "www.google.com";
if(!(h = gethostbyname(host))) {
perror("gethostbyname");
return NULL;
}
ip = inet_ntoa(*(struct in_addr*)h->h_addr);
cbuf = calloc(1, sizeof(*cbuf));
fprintf(stdout, "Connecting to ip: %s\n", ip);
if((cbuf->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return 1;
}
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(80);
inet_aton(ip, &saddr.sin_addr);
if(connect(cbuf->fd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) {
perror("connect");
return 1;
}
snprintf(buf, sizeof(buf), "GET / HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", host);
write(cbuf->fd, buf, strlen(buf));
while(read_line(cbuf, buf, sizeof(buf)) > 0) {
// if it's an empty \r\n on a line, header ends //
if(buf[0]=='\r' && buf[1] == '\n') {
printf("------------------------\n");
}
printf("[%s]", buf);
}
close(cbuf->fd);
free(cbuf);
return 0;
}
Try this implementation instead:
int sgetline(int fd, char ** out)
{
int buf_size = 0;
int in_buf = 0;
int ret;
char ch;
char * buffer = NULL;
char * new_buffer;
do
{
// read a single byte
ret = read(fd, &ch, 1);
if (ret < 1)
{
// error or disconnect
free(buffer);
return -1;
}
// has end of line been reached?
if (ch == '\n')
break; // yes
// is more memory needed?
if ((buf_size == 0) || (in_buf == buf_size))
{
buf_size += 128;
new_buffer = realloc(buffer, buf_size);
if (!new_buffer)
{
free(buffer);
return -1;
}
buffer = new_buffer;
}
buffer[in_buf] = ch;
++in_buf;
}
while (true);
// if the line was terminated by "\r\n", ignore the
// "\r". the "\n" is not in the buffer
if ((in_buf > 0) && (buffer[in_buf-1] == '\r'))
--in_buf;
// is more memory needed?
if ((buf_size == 0) || (in_buf == buf_size))
{
++buf_size;
new_buffer = realloc(buffer, buf_size);
if (!new_buffer)
{
free(buffer);
return -1;
}
buffer = new_buffer;
}
// add a null terminator
buffer[in_buf] = '\0';
*out = buffer; // complete line
return in_buf; // number of chars in the line, not counting the line break and null terminator
}
int sockfd = socket( ... );
//....
char* tbuf;
int ret;
// keep reading until end of headers is detected.
// headers are terminated by a 0-length line
do
{
// read a single line
ret = sgetline(sockfd, &tbuf);
if (ret < 0)
break; // error/disconnect
// is it a 0-length line?
if (ret == 0)
{
printf("End of Headers detected.\n");
free(tbuf);
break;
}
// tbuf contains a header line, use as needed...
free(tbuf);
}
while (true);
You are making things more difficult for yourself than they need to be. You really don't need to do strcats to get the single character you read on each read added at the current position.
But your bug is that the routine returns as soon as it sees a \n, so the string it returns can never contain anything following the first \n.

Resources