How to prevent infinite loop writing to file - c

This is a c code to create a copy of a file or directory, compiled using gcc in fedora 19. It runs but it doesn't stop and I can see the new file created keeps on increasing in size ridiculously. What is wrong with this code?
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
char buffer[2048];
int version = 1;
int main(int argc, char *argv[])
{
void copy (int, int);
int fdold, fdnew;
if (argc != 3)
{
printf("insufficient arguments !!! \n");
return (1);
}
fdold = open(argv[1], O_RDONLY);
if (fdold == -1)
{
printf("cannot open file %s\n",argv[1]);
return (1);
}
fdnew = creat(argv[2],0666);
if (fdnew == -1)
{
printf("cannot create file %s\n",argv[2]);
return (1);
}
copy (fdold, fdnew)
return (0);
}
void copy (int fdold, int fdnew)
{
int count;
count = read(fdold, buffer, sizeof(buffer));
while (count > 0)
write(fdnew, buffer, count);
}

You never update count and you keep writing the same data over and over again. In this code:
count = read(fdold, buffer, sizeof(buffer));
while (count > 0)
write(fdnew, buffer, count);
You read from the file descriptor once, pulling in count bytes, and while it's greater than 0 (which is presumably is), you keep writing the buffer out to the new file. You never read any more data from the old file. If you can see the file getting bigger and bigger, you might also be able to see (depending on how the content gets flushed to disk) the same content repeated over and over again.
What you actually need to be doing is something more like this:
while there is data to read from the old file
read it into a buffer
while there is data in the buffer to write to the new file
write data from the buffer into the new file
In slightly less pseudo-codish, but highly untested form, I think you'd be looking for something sort of like this:
int count = 0;
int written;
while ( 0 < (count = read(fdold, buffer, sizeof(buffer))) ) {
written = 0;
while ( count > (written += write(fdnew, buffer+written, count-written))) );
}
The outer loop makes sure that you read until there's nothing left to read, and the inner while make sure that you call write until written is as big as count, that is, until you've written all the bytes that you read. This is “clever” code, but it's actually too clever; you actually need to check whether written is -1, or else you'll start doing some strange things.
Something with more error checking, and hopefully more idiomatic, might be:
for ( int count_in = -1; count_in != 0; ) {
if ( -1 == (count_in = read(fd, buf, bufsize))) {
perror("Problem reading from file");
exit(-1);
}
else {
for ( int count_out = 0, out = 0; count_out < count_in; count_out += out ) {
if ( -1 == (out = write(fd, buf+count_out, count_in-count_out)) ) {
perror("Problem writing to file");
exit(-1);
}
}
}
}

Related

Is there a way to make this code concise?

I'm currently learning and practicing c, but the exercise I'm doing wants each functions to have 25 lines limit (without changing { } or using single-line if statements!)
Please help if there's a way to make this even shorter.
void ft_write_file(void)
{
char c;
int fd;
int i;
i = 0;
if ((fd = open("write_exam", O_WRONLY | O_TRUNC | O_CREAT, 00777)) == -1)
{
ft_putstr("map error");
return ;
}
while (read(0, &c, 1))
{
write(fd, &c, 1);
if (c == '\n')
break ;
ft_allocate_g_var(i, c, 0);
i++;
}
int j = 0;
while (j < g_line)
{
while (read(0, &c, 1))
{
write(fd, &c, 1);
if (c == '\n')
break ;
}
j++;
}
close(fd);
}
To start with, don't try to do two things in one function. And try to write your functions with sensible arguments instead of hard-coding their subjects.
For example, your function is really doing two things:
Finding and potentially creating the output file (with a hard-coded name).
Copying the entire contents of one stream (hard-coded to stdin) to another stream.
So you could break that down: (prototypes only)
/* Returns fd or -1 on error */
int open_output(const char* name);
/* Returns number of bytes copied or -1 on error */
ssize_t copy_fd(int fd_dest, int fd_source);
Then your driver could be:
ssize_t copy_stdin_to_file(const char *name)
{
int fd = open_output(name);
if (fd < 0)
{
ft_putstr("Could not open output file");
return -1;
}
ssize_t copied = copy_fd(fd, 0);
if (copied < 0) {
ft_putstr("Could not write data.");
return copied;
}
}
A simple way would be to declare all the variables at the top in one line, for exemple :
char c; int fd; int i; i = 0;
Except from that I dont know, hope it can help a bit a least !

Copy data from file X to file Y program in C

I tried to write basic program in C which copy data from file to another with given source path, destination path and buffer size as input.
my problem is the destination file filled with junk or something because its way larger than the source (get bigger depending on buffer size) and can't be open.
How do i read and write just the bytes in the source?
i'm working in linux, and this is the actually copying part:
char buffer[buffer_size];
int readable=1;
int writeable;
while(readable != 0){
readable = read(sourcef, buffer, buffer_size);
if(readable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not read.");
}
writeable = write(destf, buffer, buffer_size);
if(writeable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
writeable = write(destf, buffer, buffer_size);
must be
writeable = write(destf, buffer, readable);
Currently you do not write the number of characters you read but all the buffer, so the output file is too large
You also manage wrongly the end of the input file
The return value of read is :
On success, the number of bytes read is returned (zero indicates end of file)
On error, -1 is returned
A proposal :
/* you already check input and output file was open with success */
char buffer[buffer_size];
for(;;){
ssize_t readable = read(sourcef, buffer, buffer_size);
if(readable <= 0){
close(sourcef);
close(destf);
if (readable != 0)
/* not EOF */
exit_with_usage("Could not read.");
/* EOF */
break;
}
if (write(destf, buffer, n) != n) {
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
I suppose exit_with_usage calls exit() so does not return
Note in theory write may write less than the expected number of characters without being an error, and the write has to be done in a loop, but in that case it is useless to manage that
read function returns how many bytes were read to buffer(which has buffer_size). Its not always the case actual bytes read has same value as buffer size(consider scenario if there are not enough bytes left in source file to fully fill your buffer). So you should write to destination file not buffer_size(third argument of the write function), but how many bytes have you read - that is readable variable in your code
You should exit when readable returns an error.So
while(readable != 0){
should be
while(readable != -1){
So that loop could be terminataed when an readfile is exhausted.
You see currently after the whole readfile has been read, calling read fails but write is being called repeatedly since execution has no exit path for failure on read. Also write should only write the number of bytes read. So the code would look like this:
char buffer[buffer_size];
int readable=1;
int writeable;
while(readable != -1){
readable = read(sourcef, buffer, buffer_size);
if(readable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not read.");
}
writeable = write(destf, buffer, readable);
if(writeable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
Simple code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // For system calls write, read e close
#include <fcntl.h>
#define BUFFER_SIZE 4096
int main(int argc, char* argv[]) {
if (argc != 3) {
printf("Usage %s Src_file Dest_file\n", argv[0]);
exit(1);
}
unsigned char buffer[BUFFER_SIZE] = {0};
ssize_t ReadByte = 0;
int src_fd, dst_fd;
// open file in read mode
if ((src_fd = open(argv[1], O_RDONLY)) == -1) {
printf("Failed to open input file %s\n", argv[1]);
exit(1);
}
// open file in write mode and already exists to overwrite
if ((dst_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 644)) == -1) {
printf("Failed to create output file %s\n", argv[2]);
exit(1);
}
// loop
while (1) {
// read buffer
ReadByte = read(src_fd, buffer, sizeof(buffer));
// error with reading
if (ReadByte == -1) {
printf("Encountered an error\n");
break;
} else if (ReadByte == 0) {
// file end exit loop
printf("File copying successful.\n");
break;
}
// error with writing
if (write(dst_fd, buffer, ReadByte) == -1) {
printf("Failed to copying file\n");
break;
}
}
// Close file
close(src_fd);
close(dst_fd);
exit(0);
}
Run
./program src_file dest_file

Read a file a number of bytes per time in c

I am trying to write a program on how to read a file 10 bytes per time using read, however, I do not know how to go about it. How should I modify this code to read 10bytes per time. Thanks!!!!
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
int main (int argc, char *argv[])
{
printf("I am here1\n");
int fd, readd = 0;
char* buf[1024];
printf("I am here2\n");
fd =open("text.txt", O_RDWR);
if (fd == -1)
{
perror("open failed");
exit(1);
}
else
{
printf("I am here3\n");
if(("text.txt",buf, 1024)<0)
printf("read error\n");
else
{
printf("I am here3\n");
/*******************************
* I suspect this should be the place I make the modification
*******************************/
if(read("text.txt",buf, 1024)<0)
printf("read error\n");
else
{
printf("I am here4\n");
printf("\nN: %c",buf);
if(write(fd,buf,readd) != readd)
printf("write error\n");
}
}
return 0;
}
The final parameter of read() is the maximum size of the data you wish to read so, to try and read ten bytes at a time, you would need:
read (fd, buf, 10)
You'll notice I've also changed the first parameter to the file descriptor rather than the file name string.
Now, you'll probably want that in a loop since you'll want to do something with the data, and you also need to check the return value since it can give you less than what you asked for.
A good example for doing this would be:
int copyTenAtATime (char *infile, char *outfile) {
// Buffer details (size and data).
int sz;
char buff[10];
// Try open input and output.
int ifd = open (infile, O_RDWR);
int ofd = open (outfile, O_WRONLY|O_CREAT);
// Do nothing unless both opened okay.
if ((ifd >= 0) && (ofd >= 0)) {
// Read chunk, stopping on error or end of file.
while ((sz = read (ifd, buff, sizeof (buff))) > 0) {
// Write chunk, flagging error if not all written.
if (write (ofd, buff, sz) != sz) {
sz = -1;
break;
}
}
}
// Finished or errored here, close files that were opened.
if (ifd >= 0) close (ifd);
if (ofd >= 0) close (ofd);
// Return zero if all okay, otherwise error indicator.
return (sz == 0) ? 0 : -1;
}
change the value in read,
read(fd,buf,10);
From man of read
ssize_t read(int fd, void *buf, size_t count);
read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.
if(read("text.txt",buf, 1024)<0)// this will give you the error.
First argument must be an file descriptor.

Copy input file, binary and ASCII files to new file

I just started learning C a little bit ago and I need help with a particular program. I need to read lines from a file (about 50 bytes at a time) and copy them over to a new file, including binary files. It's pretty simple but I'm having trouble on how exactly I should go about doing it. So far I have
while ( ) {
read (infile, buffer, 50);
if (r< 0) {
perror("cannot read infile");
exit (-1);
}
if (r==0) {
break;
}
write (outfile, buffer, r);
}
while I already open and close the files I will need. The loop should read the file 50 bytes at a time and write them to a new file, but I'm not sure what the condition of the while loop should be. I was thinking maybe taking the number of bytes in the file and make it so it will loop through the file as long as there are more bytes left. However, I am not entirely sure how to do that. Any help in the right direction?
You need to capture the return value of read:
r = read (infile, buffer, 50);
Given how the loop is set up, you could use the always-true condition: while(1) (since you already break when you reach the end of the file)
while (1) {
int r = read (infile, buffer, 50);
if (r< 0) {
perror("cannot read infile");
exit (-1);
}
if (r==0) {
break;
}
if(write (outfile, buffer, r) != r)
printf("write error\n");
}
Firstly, you can change (r == 0) to (r < 50) because the last line probably won't be a full 50 bytes. If it is, the next run through will be 0 anyway, which will still evaluate to true. And you never actually capture r as a return value from read().
For your actual question, you could do it in a variety of ways. Currently, you could write
while(1){
r = read(infile, buffer, 50);
if ( r < 50) {
...
break;
}
}
will work just fine. The break statement will exit the loop when the r < 50 condition is satisfied, implying no bytes were read.
For code that is more clear and readable, you could use an int as a boolean like this:
int FileDone = 0;
while(!FileDone){
...
if ( r < 50) {
FileDone = 1
}
}
or maybe like this:
int r = 1;
while (r > 0){
r = read(infile, buffer, 50);
if (r < 0) {
perror("cannot read infile");
exit (-1);
}
write (outfile, buffer, r);
}

how to send an image in winsock2, using c

I am writing a very simple webserver in c (winsock2).
I am able to return the contents of my html pages.
Currently, what I am doing is writing the contents of a file into a char* buffer and sending it using "send()"
Although when I try to read an image (jpg, bmp), I can't write the characters into a buffer a some characters are "null" (0).
How can I send a whole image file ?
Thanks.
You can store null character in a char* buffer. You just have to use a counter to remember how many characters were written, instead of recomputing it by counting number of non-null characters (this can either be an integer or a pointer to the next point of insertion in the buffer).
To send a file, you'll do something like that:
int sendFile(int sock, const char* filename) {
FILE* file = fopen(filename, "rb");
if (file == NULL)
return -1;
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return -1;
}
off_t size = ftello(file);
if (fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
return -1;
}
if (SendBinaryFileHeaderAndSize(sock, size) < 0) {
fclose(file);
return -1;
}
char buffer[4096];
for (;;) {
size_t read = fread(buffer, 1, sizeof(buffer), file);
if (read == 0) {
int retcode = 0;
if (ferror(file))
retcode = -1;
fclose(file);
return retcode;
}
for (size_t sent = 0; sent < read;) {
int ret = send(sock, buffer + sent, read - sent, 0);
if (ret < 0) {
fclose(file);
return -1;
}
assert(ret <= read - sent);
sent += ret;
}
}
}
You need to understand how send() and fread() work. 0s in the buffer are not a problem for send or fread - they do not interpret their buffers as null-terminated strings.
Depending on how you load the image into your webserver, you would need to use either Winsock:TransmitPackets or Winsock:TransmitFile, also also wrapping the image in the appropriate HTTP headers
Note that these are MS specific extensions.
Also see c++ - Bitmap transfer using winsock getdibits and setdibits

Resources