How to get a line from csv file with a custom fgets - c

I'm currently writing a program in C that reads in from a CSV file, I have a defined buffer size but am having trouble separating each line from the buffer. I can see where the line ends by checking for a '\n' char. I cannot extract that line from the buffer for parsing however. Anybody have some ideas?
#ifndef BUFFSIZE
#define BUFFSIZE 4096
#endif
int main() {
int fd;
int fdBin;
char * buf = malloc(BUFFSIZE);
int count = 0;
bool EOFFlag = false;
fd = open("SongCSV.csv", O_RDONLY);
fdBin = open("BinarySongData.bin", O_CREAT | O_WRONLY, "0600");
if (fd == -1) {
printf("failed to open a file\n");
exit(1);
}
off_t offset = 0;
off_t offsetOld = 0;
int readBytes;
while (!EOFFlag) {
offsetOld = offset;
offset = lseek(fd, offset - offsetOld, SEEK_CUR);
readBytes = read(fd, buf, BUFFSIZE);
printf("\n\n%lld\n\n", (offset));
int i = 0;
int commaCounter = 0;
while (i < readBytes) {
if (buf[i] != '\n') {
}
if (buf[i] == '\n') {
printf("\t\t THIS IS END OF LINE \t%d", i);
commaCounter = 0;
}
if (buf[i] == ',') {
commaCounter++;
if (commaCounter == 4) {
printf("****Album Name****");
}
}
write(fdBin, buf, BUFFSIZE);
printf("%c", buf[i]);
i++;
}
if (readBytes < BUFFSIZE) {
EOFFlag = true;
printf("\nREACHED END OF FILE");
}
printf("\n");
printf("AA: END OF LINE ****%d*****", count);
count++;
}
close(fd);
close(fdBin);
return 0;
}

I do it this way, easy and simple. I just did it quickly, any doubts just ask me, Cheers.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int len = sending();
char *firstline;
int i = 0;
char buf[0];
int rd ;
int fd = open("hey.csv", O_RDONLY);
rd = read(fd, buf, 1);
firstline = malloc(sizeof(char) * len);
while (i != len)
{
firstline[i] = buf[0];
i++;
rd = read(fd, buf, 1);
}
firstline[i] = '\0';
printf("%s\n", firstline);
return (0);
}
int sending()
{
int fd = open("hey.csv", O_RDONLY);
char buf[1];
int r = 0;
r = read(fd, buf, 1);
int len = 0;
while (buf[0] != '\n')//getting exact size to malloc
{
len++;
r = read(fd, buf, 1);
}
return len;
}

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

Why am I getting heap-buffer-overflow in this C code?

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
char *get_next_line(int fd);
int main (void)
{
int i = 0;
char *s;
int fd;
fd = open("./text", O_RDONLY);
s = get_next_line (fd);
}
char *get_next_line(int fd)
{
char *buf;
char c;
int nread;
int cnt;
if (fd < 0 || BUFFER_SIZE < 1)
return (NULL);
buf = (char*)malloc(BUFFER_SIZE + 1);
if (!buf)
return (NULL);
while(nread = (read(fd, &c, 1)) > 0)
{
*buf = c;
buf++;
cnt++;
if (c == '\n')
break;
}
if (nread < 0)
return (NULL);
*buf = '\n';
printf("%s\n", buf);
return (buf - cnt - 1);
}
When I compile with no flags, I just get two empty line. Compiling with -fsanitize=address and I know heap-buffer-overflow happens at the line printf("%s\n", buf);
But I don't know why this happen. I tried STDIN to fix it but didn't work. Can someone check this please?
You are not terminating the buf with null character.
*buf = '\n';
*buf = '\0';
Make sure you reserve the space for null character while allocating memory to buf.
Free the memory if number of bytes read are less than 0.
if (nread < 0) {
return (NULL);
}
to
if (nread < 0) {
free(startAddress);
return (NULL);
}
You can have temporary pointer to preserve the starting address of buf instead of calculating the starting address.
char *get_next_line(int fd)
{
char *buf;
char c;
int nread;
if (fd < 0 || BUFFER_SIZE < 1)
return (NULL);
buf = (char*)malloc(BUFFER_SIZE + 2);
if (!buf)
return (NULL);
char *startAddress = buf;
while(nread = (read(fd, &c, 1)) > 0)
{
*buf = c;
buf++;
if (c == '\n')
break;
}
if (nread < 0) {
free(startAddress);
return (NULL);
}
*buf = '\0';
printf("%s\n", buf);
return startAddress;
}

C mmap implementation for linux command

I have the following code which basically reproduce the functionality of the wc command in linux. My question is how I can rewrite the code using mmap? I know I can use struct stat sb; and then char *file_in_memory = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); but I can't get it work/ I don't know how to implement it correctly in the while loop while ((n = read(file, buffer, LUNG_BUF - 1)) > 0). In my tries after I run the code it will display only values of 0.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#define LUNG_BUF 4096
int main(int argc, char** argv)
{
int bytes = 0;
int words = 0;
int newLine = 0;
int max_value; // the maximum of the above three
int dim; // string width of the max value
char buffer[LUNG_BUF];
enum states { WHITESPACE, WORD };
int state = WHITESPACE;
if ( argc !=2 )
{
printf( "No file name\n%s", argv[0]);
}
else
{
int file = open(argv[1], O_RDONLY);
if(file < 0)
{
printf("can not open :%s\n",argv[1]);
}
else
{
char *thefile = argv[1];
size_t n;
while ((n = read(file, buffer, LUNG_BUF - 1)) > 0)
{
buffer[n] = '\0';
char *ptr = buffer;
while (*ptr)
{
bytes++;
if (*ptr == ' ' || *ptr == '\t')
{
state = WHITESPACE;
}
else if (*ptr == '\n')
{
newLine++;
state = WHITESPACE;
}
else
{
if (state == WHITESPACE)
{
words++;
}
state = WORD;
}
ptr++;
}
}
// find out the largest value of all and determine the printed width of it
max_value = newLine;
if (words > max_value)
max_value = words;
if (bytes > max_value)
max_value = bytes;
dim = snprintf(NULL, 0, "%d", max_value);
// print lines, words, bytes and filename aligned to the longest number
printf("%*d %*d %*d %s\n", dim, newLine, dim, words, dim, bytes, thefile);
}
}
}
The script that I was trying:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#define LUNG_BUF 4096
int main(int argc, char** argv)
{
int bytes = 0;
int words = 0;
int newLine = 0;
int max_value; // the maximum of the above three
int dim; // string width of the max value
char buffer[LUNG_BUF];
enum states { WHITESPACE, WORD };
int state = WHITESPACE;
if ( argc !=2 )
{
printf( "No file name\n%s", argv[0]);
}
else
{
int file = open(argv[1], O_RDONLY);
if(file < 0)
{
printf("can not open :%s\n",argv[1]);
}
else
{
char *thefile = argv[1];
size_t n;
struct stat sb;
char *file_in_memory = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
for(int i=0;i<=sb.st_size;i++)
{
buffer[i] = '\0';
char *ptr = buffer;
while (*ptr)
{
bytes++;
if (*ptr == ' ' || *ptr == '\t')
{
state = WHITESPACE;
}
else if (*ptr == '\n')
{
newLine++;
state = WHITESPACE;
}
else
{
if (state == WHITESPACE)
{
words++;
}
state = WORD;
}
ptr++;
}
}
// find out the largest value of all and determine the printed width of it
max_value = newLine;
if (words > max_value)
max_value = words;
if (bytes > max_value)
max_value = bytes;
dim = snprintf(NULL, 0, "%d", max_value);
// print lines, words, bytes and filename aligned to the longest number
printf("%*d %*d %*d %s\n", dim, newLine, dim, words, dim, bytes, thefile);
munmap(file_in_memory, sb.st_size);
close(file);
}
}
}
The code you posted above didn't compile and had quite a few problems. I've tidied it up a bit below, hopefully this will help. I tried not to change it too much so you could see what I did.
You hadn't actually called stat and the fd variable you had passed to mmap was not the variable you used to open the file.
I would always compile your code with "-Wall -Werror" if you can.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
int main(int argc, char** argv)
{
if ( argc !=2 )
{
printf( "No file name\n%s", argv[0]);
exit(-1);
}
char *fileName = argv[1];
int file = open(fileName, O_RDONLY);
if(file < 0)
{
perror("Error: ");
exit(-1);
}
struct stat sb = {0};
stat(fileName, &sb);
char *filePtr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, file, 0);
if (filePtr == MAP_FAILED)
{
perror("Error:");
exit(-1);
}
int bytes = sb.st_size;
int words = 0;
int newLine = 0;
enum states { WHITESPACE, WORD };
int state = WHITESPACE;
for(size_t pos=0;pos<=sb.st_size;pos++)
{
if (state == WHITESPACE)
{
if (filePtr[pos] == '\n')
{
newLine++;
}
else if ((filePtr[pos] != ' ') && (filePtr[pos] != '\t'))
{
state = WORD;
}
}
else // (state == WORD)
{
if (filePtr[pos] == ' ' || filePtr[pos] == '\t')
{
state = WHITESPACE;
words++;
}
else if (filePtr[pos] == '\n')
{
state = WHITESPACE;
words++;
newLine++;
}
}
}
// Max value is always bytes
int dim = snprintf(NULL, 0, "%d", bytes);
// print lines, words, bytes and filename aligned to the longest number
printf("%*d %*d %*d %s\n", dim, newLine, dim, words, dim, bytes, fileName);
munmap(filePtr, sb.st_size);
close(file);
}

Printing Partition Table - C program

I am trying to print a partition table using C programming language, everything seems to work fine: Opening and reading, but I don't understand why it is printing garbage values.
Here is the code:
struct partition
{
unsigned char drive;
unsigned char chs_begin[3];
unsigned char sys_type;
unsigned char chs_end[3];
unsigned char start_sector[4];
unsigned char nr_sector[4];
};
int main()
{
int gc = 0, i = 1, nr = 0, pos = -1, nw = 0;
int fd =0;
char buf[512] ;
struct partition *sp;
printf("Ok ");
if ( (fd = open("/dev/sda", O_RDONLY | O_SYNC )) == -1)
{
perror("Open");
exit(1);
}
printf("fd is %d \n", fd);
pos = lseek (fd, 0, SEEK_CUR);
printf("Position of pointer is :%d\n", pos);
if ((nr = read(fd, buf, sizeof(buf))) == -1)
{
perror("Read");
exit(1);
}
close(fd);
printf("Size of buf = %d\n and number of bytes read are %d ", sizeof(buf), nr);
if ((nw = write(1, buf, 64)) == -1)
{
printf("Write: Error");
exit(1);
}
printf("\n\n %d bytes are just been written on stdout\n", nw,"this can also be printed\n");
printf("\n\t\t*************Partition Table****************\n\n");
for (i=0 ; i<4 ; i++)
{
sp = (struct partition *)(buf + 446 + (16 * i));
putchar(sp -> drive);
}
return 0;
}
It is printing garbage instead of partition table.
I might have some basic understanding issues but I searched with Google for a long time but it did not really help. I also saw the source code of fdisk but it is beyond my understanding at this point. Could anyone please guide me? I am not expecting someone to clear my mistake and give me the working code. Just a sentence or two - or any link.
Try this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct partition
{
unsigned char boot_flag; /* 0 = Not active, 0x80 = Active */
unsigned char chs_begin[3];
unsigned char sys_type; /* For example : 82 --> Linux swap, 83 --> Linux native partition, ... */
unsigned char chs_end[3];
unsigned char start_sector[4];
unsigned char nr_sector[4];
};
void string_in_hex(void *in_string, int in_string_size);
void dump_partition(struct partition *part, int partition_number);
void dump_partition(struct partition *part, int partition_number)
{
printf("Partition /dev/sda%d\n", partition_number + 1);
printf("boot_flag = %02X\n", part->boot_flag);
printf("chs_begin = ");
string_in_hex(part->chs_begin, 3);
printf("sys_type = %02X\n", part->sys_type);
printf("chs_end = ");
string_in_hex(part->chs_end, 3);
printf("start_sector = ");
string_in_hex(part->start_sector, 4);
printf("nr_sector = ");
string_in_hex(part->nr_sector, 4);
}
void string_in_hex(void *in_string, int in_string_size)
{
int i;
int k = 0;
for (i = 0; i < in_string_size; i++)
{
printf("%02x ", ((char *)in_string)[i]& 0xFF);
k = k + 1;
if (k == 16)
{
printf("\n");
k = 0;
}
}
printf("\n");
}
int main(int argc, char **argv)
{
int /*gc = 0,*/ i = 1, nr = 0, pos = -1/*, nw = 0*/;
int fd = 0;
char buf[512] ;
struct partition *sp;
int ret = 0;
printf("Ok ");
if ((fd = open("/dev/sda", O_RDONLY | O_SYNC)) == -1)
{
perror("Open");
exit(1);
}
printf("fd is %d\n", fd);
pos = lseek (fd, 0, SEEK_CUR);
printf("Position of pointer is :%d\n", pos);
if ((nr = read(fd, buf, sizeof(buf))) == -1)
{
perror("Read");
exit(1);
}
ret = close(fd);
if (ret == -1)
{
perror("close");
exit(1);
}
/* Dump the MBR buffer, you can compare it on your system with the output of the command:
* hexdump -n 512 -C /dev/sda
*/
string_in_hex(buf, 512);
printf("Size of buf = %d - and number of bytes read are %d\n", sizeof(buf), nr);
/*if ((nw = write(1, buf, 64)) == -1)
{
printf("Write: Error");
exit(1);
}
printf("\n\n%d bytes are just been written on stdout\nthis can also be printed\n", nw);
*/
//printf("\n\t\t*************Partition Table****************\n\n");
printf("\n\t\t*************THE 4 MAIN PARTITIONS****************\n\n");
/* Dump main partitions (4 partitions) */
/* Note : the 4 partitions you are trying to dump are not necessarily existing! */
for (i = 0 ; i < 4 ; i++)
{
sp = (struct partition *)(buf + 446 + (16 * i));
//putchar(sp->boot_flag);
dump_partition(sp, i);
}
return 0;
}

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