I'm having trouble reading in a file line by line. Apparently the read() system call grabs the whole file. I'm trying to read in a file with lines of variable length, however I do know that no line's length can exceed SBUFSIZE bytes. I'm supposed to read in each line in the file and put each line of the file onto a data structure. However my approach pushes the whole file as one line onto the data structure, which is not acceptable. Is there a modified version of read() which stops at the '\n' character?
#define SBUFSIZE 1025
pthread_mutex_t buffer_lock;
void* process_file(void* file_name)
{
int input_fd;
/* Temporary buffer, for reading in the files, one line at a time. */
char buf[SBUFSIZE];
memset(buf, '\0', SBUFSIZE);
if ((input_fd = open((char*) file_name, O_RDONLY)) == -1) {
fprintf(stderr, "Cannot open the file '%s'\n", (char*) file_name);
pthread_exit((void*) 1); /* This is my error flag. */
}
while (read(input_fd, buf, SBUFSIZE)) {
int ret;
printf("|%s|\n", buf);
while (true) {
pthread_mutex_lock(&buffer_lock);
ret = stack_push(buf);
if (ret == STACK_FULL) {
pthread_mutex_unlock(&buffer_lock);
usleep(rand() % 101);
} else {
break;
}
}
pthread_mutex_unlock(&buffer_lock);
memset(buf, '\0', SBUFSIZE);
if (ret != STACK_SUCCESS) {
exit(EXIT_FAILURE);
}
}
close(input_fd);
pthread_exit((void*) 0); /* This is my good flag. */
}
You can process line-by-line as follows:
char buf[SBUFSIZE + 1];
size_t bufsize = 0;
for(;;)
{
ssize_t nread = read(input_fd, buf + bufsize, SBUFSIZE - bufsize);
if(nread < 0)
perror("read failed");
bufsize += nread;
if(!bufsize)
break; // end of file
const char *eol = memchr(buf, '\n', bufsize);
if(!eol)
eol = buf + bufsize++;
*eol = 0;
printf("processing line: |%s|\n", buf);
process_line(buf);
++eol;
bufsize -= eol - buf;
memmove(buf, eol, bufsize);
}
Related
I have to implement standard unix command "tail" without fopen, fclose, fread, fseek, printf. I am ready with everything else except handle of partial reads and writes.
And here is my print with handle of partial read and write of last 10 lines. It's happening after lseek to the right position for fd!
I test it several times with different files and it works like real tail, but i cant handle partial reads and writes like i said.
'''code'''
do
{
memset(buff, 0, SIZE);
read_value = read(fd, buff, SIZE);
ssize_t bytes_read_total = read_value;
int count=2;
char buff_copy[SIZE];
while (bytes_read_total < SIZE)
{
read_value = read(fd, buff + bytes_read_total, SIZE - bytes_read_total);
if(count%2==0)
strcpy(buff_copy, buff);
if (read_value == -1)
{
char *err;
err = (char *)malloc(sizeof(char)*(50+strlen(file)));
strcpy(err, "tail: error reading '");
strcat(err, file);
strcat(err, "'");
perror(err);
free(err);
return 2;
}
if(read_value==0)
{
strcpy(buff, buff_copy);
read_value = strlen(buff_copy);
break;
}
bytes_read_total+= read_value;
}
ssize_t bytes_written;
ssize_t bytes_written_total = 0;
while (read_value != bytes_written_total)
{
bytes_written = write(STDOUT_FILENO, buff+bytes_written_total, read_value - bytes_written_total);
if (bytes_written == -1)
{
perror("tail: error writing 'standard output'");
return 3;
}
bytes_written_total += bytes_written;
}
}while(read_value);
'''code'''
I have written the server client program using linux tcp sockets.
Client ask server for present directory files list by
sending the ls command
server replies all the list of files in server dir.
I was testing it for more files in server working dir.
server response format in the buffer
file/dir [tab] file_name [tab] file_change_time
for each 1000 files to client.
Server Sending Code:
#define BUFSIZE 1400
void lsfun(node_t *pclient)
{
DIR *directory;
int status;
int cpylen = 0;
int msglen = 0;
unsigned int tt_count = 0;
unsigned int no_files = 0;
unsigned int no_sends = 0;
int clientfd = *(pclient->client_socket);
char *filectime;
char *buffer = malloc(BUFSIZE * sizeof(char));
char *tmp = malloc(BUFSIZE * sizeof(char));
char ending[] = "#####";
struct dirent *dir;
struct stat type;
pthread_mutex_lock(&lock);
chdir(pclient->pwd);
directory = opendir(".");
pthread_mutex_unlock(&lock);
if(tmp == NULL || buffer == NULL)
printf("malloc error for client conn:%d\n", clientfd);
if(directory)
{
while((dir = readdir(directory)) != NULL)
{
if(!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))
continue;
status = stat(dir->d_name, &type);
if(status == 0)
{
filectime = ctime(&type.st_ctime);
if(dir->d_type != DT_REG)
cpylen = snprintf(tmp, BUFSIZE, "dir\t%s\t%s", dir->d_name, filectime);
else
cpylen = snprintf(tmp, BUFSIZE, "file\t%s\t%s", dir->d_name, filectime);
tmp[cpylen] = 0;
if((cpylen + msglen) < BUFSIZE)
{
strlcpy(buffer + msglen, tmp, cpylen);
msglen += cpylen;
no_files += 1;
}
else
{
tt_count += msglen;
printf("%s", buffer);
fflush(stdout);
send(clientfd, buffer, strlen(buffer), 0);
memset(buffer, 0, BUFSIZE + 5);
snprintf(buffer, cpylen, "%s", tmp);
msglen = cpylen;
cpylen = 0;
no_files += 1;
no_sends += 1;
}
}
else
{
cpylen = snprintf(buffer + msglen, BUFSIZE, "%s%s\n", "file stat error:", dir->d_name);
msglen += cpylen;
}
memset(tmp, 0, BUFSIZE);
}
}
cpylen = strlen(buffer);
if(msglen == cpylen)
send(clientfd, buffer, strlen(buffer), 0);
send(clientfd, ending, strlen(ending), 0); //sending msg ending for client read to close
printf("\nlssize :%d\tnofile:%d, msglen:%d\tcpylen:%d\tno_sends:%d\n", tt_count + msglen, no_files, msglen, cpylen, no_sends);
free(tmp);
free(buffer);
closedir(directory);
}
Client receiving Code:
#define BUFSIZE 1400
while(true)
{
msgsize = read(socketfd, buffer, BUFSIZE);
buffer[msgsize] = 0;
snprintf(ending, 6, "%s", buffer + (strlen(buffer) - 5));
if(strcmp(ending, "#####") == 0)
{
buffer[strlen(buffer) - 5] = 0;
if(buffer[strlen(buffer) - 1] == '\n')
printf("%s", buffer);
else
printf("%s\n", buffer);
fflush(stdout);
break;
}
else
{
printf("%s", buffer);
memset(buffer, 0, BUFSIZE);
}
}
Server replay debug print:
lssize :19931 nofile:501, msglen:437 cpylen:39 no_sends:14
why am I only receiving two packet instead of 14 packets from
server packets of around 1400 bytes each ?
where is the mistake ?
also welcome any code improvement suggestions.
In addition to the bugs pointed out in the comments, there are more fundamental issues with your code that are too extensive to merely comment on.
This code implies multithreaded use:
pthread_mutex_lock(&lock);
chdir(pclient->pwd);
directory = opendir(".");
pthread_mutex_unlock(&lock);
BUT, this code assumes the current working directory is always the current function's pclient->pwd:
status = stat(dir->d_name, &type);
It won't be if another thread calls chdir() to another directory while the loop is running, so your results
stat() always checks a relative path from the current working directory of the entire process. Which, in your posted code, can change.
A good rule to follow is to NEVER do things that change any global property of a process if you're writing multithreaded code.
Your use of snprintf() is also bug-prone. For example:
cpylen = snprintf(buffer + msglen, BUFSIZE, "%s%s\n", "file stat error:", dir->d_name);
Per 7.21.6.5 The snprintf function, paragraph 2 of the C11 standard(bolding mine):
The snprintf function returns the number of characters that would have been written had n been sufficiently large, not counting the terminating null character, or a negative value if an encoding error occurred. Thus, the null-terminated output has been completely written if and only if the returned value is nonnegative and less than n.
You are blindly assuming that every one of your calls to snprintf() work. If any one call to snprintf() fails, your message contents are wildly indeterminate and your value for msglen will not accurately reflect the contents of your buffer.
That means this code won't send anything:
cpylen = strlen(buffer);
if(msglen == cpylen)
send(clientfd, buffer, strlen(buffer), 0);
I have this code and run it with Flawinder, and i get this output on the read() functions:
Check buffer boundaries if used in a loop including recursive loops
Can anyone see the problem?
#include <stdlib.h>
void func(int fd)
{
char *buf;
size_t len;
read(fd, &len, sizeof(len));
if (len > 1024)
return;
buf = malloc(len+1);
read(fd, buf, len);
buf[len] = '\0';
}
you should check the return value of read() to know whether call to read() was success or failure or if read() was interrupted by a signal then set the errno. For e.g
ssize_t ret = read(fd, &len, sizeof len);
if( (ret == -1 || ret != sizeof len) {
/* error handling #TODO */
}
Most importantly here
ret = read(fd, buf, len); /* read() may read less than len characters */
read() returns the number of bytes read, so instead of this
buf[len] = '\0';
use
buf[ret] = '\0'; /* correct way */
Sample Code
void func(int fd) { /* assume fd is a valid file descriptor */
char *buf = NULL;
size_t len;
errno = 0; /* set this to 0 */
ssize_t ret = read(fd, &len, sizeof len);
if( (ret == -1 || ret != sizeof len) {
/* error handling #TODO */
}
if (len > 1024) {
return;
}
buf = malloc(len+1);
if(buf == NULL) {
/* error handling #TODO */
}
ret = read(fd, buf, len);
if(ret!=-1) {
buf[ret] = '\0';
/* do something with buf and free it once usage is done*/
} free(buf); /* free the buf */
else { /* if read failed */
free(buf); /* free the buf */
}
}
I have a file, partitioned in fixed sized blocks. I am copying a test_file.txt into the 3rd block of the file. I read and copied 18 bytes.
Then I am trying to copy from the file that very same .txt file I just imported to a newly created .txt, but I am writing 256 bytes to the new file. Moreover, when I try to read it, it is full of garbage.
The first function is used to import the .txt and the second one to export it.
void copy_file(int mfs_desc, char* filename, Superblock* s, MDS mds) {
if(mds.size == 0)
return;
char buffer[s->block_size];
int i = 0;
for (; i < s->block_size; ++i) {
buffer[i] = '\0';
}
int source_desc = open(filename, O_RDONLY);
// error handling
if (source_desc == -1) {
perror("opening file in copy file");
exit(1);
}
ssize_t nread;
int total = 0;
off_t seek = lseek(mfs_desc,
sizeof(Superblock) + mds.datablocks[0] * s->block_size,
SEEK_SET);
printf("offset = %d\n", mds.datablocks[0]);
if (seek < 0) {
perror("seek");
exit(1);
}
total = 0;
while ((nread = read(source_desc, buffer, s->block_size)) > 0) {
total += nread;
write(mfs_desc, buffer, s->block_size);
}
printf("read and copied: %d bytes\n", total);
if (close(source_desc) == -1) {
perror("closing file in copy file");
exit(1);
}
}
int copy_file_export(int mfs_desc, char* filename, Superblock s, MDS mds) {
if(mds.size == 0) {
printf("File is empty, abort\n");
return 0;
}
char buffer[s.block_size];
int i = 0;
for (; i < s.block_size; ++i) {
buffer[i] = '\0';
}
int destination_desc = open(filename, O_CREAT | O_WRONLY);
// error handling
if (destination_desc == -1) {
printf("filename = |%s|\n", filename);
perror("opening file in copy file export");
exit(1);
}
ssize_t nread;
int total = 0;
off_t seek = lseek(mfs_desc,
sizeof(Superblock) + mds.datablocks[0] * s.block_size,
SEEK_SET);
printf("offset = %d\n", mds.datablocks[0]);
if (seek < 0) {
perror("seek");
exit(1);
}
for(i = 0; i < mds.size; ++i) {
nread = read(mfs_desc, buffer, s.block_size);
total += nread;
write(destination_desc, buffer, nread);
}
printf("wrote: %d bytes\n", total);
if (close(destination_desc) == -1) {
perror("closing file in copy file");
exit(1);
}
return 1;
}
Output:
import test_file.txt ... / <-- just a command
offset = 2
read and copied: 18 bytes
export test_file.txt ... ../../ <-- just a command
offset = 2
wrote: 256 bytes
What I am doing wrong?
I would replace
write(mfs_desc, buffer, s->block_size);
with
write(mfs_desc, buffer, nread);
In this chunk of code:
while ((nread = read(source_desc, buffer, s->block_size)) > 0) {
total += nread;
write(mfs_desc, buffer, s->block_size);
}
You're very likely handling the last write() incorrectly. You need to write only the bytes you read.
write(mfs_desc, buffer, nread);
Also, these lines are most likely bogus:
char buffer[s->block_size];
char buffer[s.block_size];
You're trying to use a variable sized allocation for an array on the stack. You Can't Do That™. Those allocations have to be fixed (compile time constant) sized.
I have a "file" as a resource. I can only use read(), write() and fstat() it. This file is a text file which I would like to parse.
Normally I use fgets() to read the text file line by line and parse it. How can I do this in this case?
FILE *fp;
char buffer[128];
fp = fopen( "/home/txtfile", "r" );
if (fp == NULL){
perror("file missing");
}
while (fgets (buffer, sizeof(buffer), fp) != NULL) {
//some code
}
How can I do the same with read() ?
Is this right?
int fd = open("/dev/file",O_RDONLY);
if (fd==-1) {
printf("Failed to open file!!!\n");
}
while (fgets (buffer, sizeof (buffer), fd) != NULL) {
//some code
}
Unless your file is huge, if you're using read(), it would be easier to read in the entire file, then operate on the memory buffer, rather than in discrete chunks. That is, unless each line is of a fixed length.
I'd do something like this:
int rc;
int fd = open("data", O_RDONLY); // open the file for reading
if (fd == -1) {
// error
}
// to be thorough, do a stat() here to find how big to make the buffer
struct stat sb;
rc = fstat(fd, &sb);
if (rc == -1) {
// error
}
char *buffer = calloc(1, sb.st_size);
int bytes_read = 0;
// read in entire file; each read() can be incomplete, hence why it's in a loop,
// and reading from/writing to increasing sections of the memory and file
while ((rc = read(fd, (buffer + bytes_read), (sb.st_size - bytes_read))) > 0) {
if (rc == -1) {
// error
}
bytes_read += rc;
}
close(fd);
// Now, to read it line-by-line...
char line[128]; // 128 is arbitrary
int pos = 0;
while ((rc = sscanf(buffer + pos, "%127[^\n]\n", line)) > 0) {
pos += strlen(line) + 1;
// do stuff with line
}
return 0;
Then you can operate on your memory buffer line-by-line by scanning for newlines, or using sscanf(). Also make sure to free() your buffer!
Edit: I've added some example code for using sscanf() to handle your buffer. If you know the format of the lines (you say you're parsing them) you might be able to make better use of sscanf() by using the format specifiers. All of this is untested, by the way.
Something like this :
int fd = open("/dev/file",O_RDONLY);
ssize_t res = 0;
while((res = read(fd, buffer, sizeof(buffer))) > 0) {
//some code
}
if (res < 0) {
//handle error
} else{
//close fd
}
Is this right?
No.
read() is a system call that operates on a Unix file descriptor, not a stdio FILE*. Other than that, it works by reading data from the file and putting it in the buffer you supply.
int fd = open("/dev/file",O_RDONLY);
if (fd==-1)
{
printf("Failed to open file!!!\n");
}
else
{
char buffer[BUF_SIZE];
ssize_t bytesRead = read(fd, buffer, BUF_SIZE);
while (bytesRead > 0)
{
// do something with the buffer
bytesRead = read(fd, buffer, BUF_SIZE);
}
if (bytesRead == -1)
{
// error
}
// bytesRead == 0 => end of file
}