I/O issues writing on file - c

I'm having a hard time trying to figure out why this piece of code doesn't work as it should. I am learning the basics of I/O operations and I have to come up with a C program that writes on a 'log.txt' file what is given from standard input and as the 'stop' word is entered, the program must halt.
So my code is:
#include "main.h"
#define SIZE 1024
int main(int argc, char *argv[])
{
int fd;
int readBytes;
int writBytes;
char *buffer;
if ((fd = open("log.txt", O_WRONLY|O_APPEND)) < 0)
{
perror("open");
}
buffer = (char *) calloc (SIZE, sizeof(char));
while ((readBytes = read(0, buffer, SIZE) < SIZE)&&(strncmp(buffer, "stop", 4) != 0));
if ((writBytes = write(fd, buffer, SIZE)) < 0)
{
perror("write");
}
if ((close(fd)) < 0)
{
perror("close");
}
}
If I enter:
this is just a text
stop
The output is
stop
is just a text
If I enter more than a sentence:
this is just a text
this is more text
and text again
stop
This is what is logged:
stop
ext again
xt
t
And on top of that if I try to edit the log.txt file from vim or just a text editor I can see '\00's. I guess \00 stands for all the bytes left empty from the 1024 available, right? How can I prevent that from happening?

It looks like you're expecting
readBytes = read(0, buffer, SIZE) < SIZE)
to somehow accumulate things in buffer. It doesn't. Every subsequent read will put whatever it read at the start of the buffer, overwriting what the previous read has read.
You need to put your write in the while block - one write for every read, and only write as much as you read, otherwise you'll write garbage (zeros from the calloc and/or leftovers from the previous read) in your log file.
Also note that while your technique will probably work most of the time for a line-buffered input stream, it will not do what you expect if you redirect from a file or a pipe. You should be using formatted input functions (like getline if you your implementation has that, scanf, or fgets).

Related

How do I modify the stream from one file to another

I need to make a program that takes string data from one file and copy every third char from it to another file.
I am not sure if I am doing it right. The idea I got is to first create one array where I will store original data from file1 and then using 'for' loop I will modify the data and store in in the second array:
(eg for(i=0; i < arraysize; i+=3);
The thing is I dont have an idea how to transfer input to my array and how to do it backwards to have my modified data go to file2.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFFER_SIZE 50
int main(int argc, char *argv[]) {
char buffer[BUFFER_SIZE];
char modified[BUFFER_SIZE];
int input_fd, output_fd;
ssize_t ret_in, ret_out;
if(argc !=3 || strcmp(argv[1], "--help") == 0)
{
printf("Usage: %s file_origin file_destination\n", argv[0]);
return 2;
}
input_fd = open(argv[1], O_RDONLY);
if(input_fd == -1)
{
perror("There is no such file");
return 2;
}
out_fd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0644);
if(output_fd == -1)
{
perror("create");
return 3;
}
Could someone please tell me how to use function read/write correctly to stream my data to array and how to do it the other way.
Welcome to Stackoverflow!
Given the exact description of your assignment, I would not use a buffer; you could simply read from the input file one byte at a time, and write every third byte to the output file. This avoids any buffer-management overhead.
But, if you do read from the input file into a buffer, you do not need to modify that buffer in any way, nor do you need a second buffer. After reading all the data, simply iterate through the input buffer, outputting every third byte to the output file.
But, if you want/need to reuse the output in some way, you can simply populate a second buffer from the input buffer in the same manner (loop over the input buffer, skipping two bytes each iteration), and then write that second buffer to the output file. (This way, you still have the same output in that second buffer, and you can reuse it in some manner.)
The approach you take will dictate the best functions to use. I see you already know about open(). Read up on read(), write() and close(), but also read up on fopen(), fgetc(), fgets(), fread(), fwrite() and fclose(). There is a lot for you to learn from reading about these various functions, how they are similar to each other, how they differ from each other, and the pros and cons of each. Reading about them will lead you to learn about other related file operations (like seeking, rewinding, etc.), which will serve you well as you learn more about C and programming in general.
Please note that for the approaches using buffers, you need to be very careful about the size of your buffers vs. the size of the input file. There are many pitfalls here. If this is an assignment for a class of some sort, then those considerations might show up in later lessons / assignments, and maybe it's too much to take on just now. But it's never too early to start thinking about what you do and don't know about the input your program will need to handle.
If you do not need cin or cout, I would suggest the following (I assumed strings are ended with newline and those should be preserved in the output and that counting the 3rd character starts anew in every line read):
FILE *f1=fopen("_infile.txt","rt");
FILE *f2=fopen("_outfuile.txt","wt");
char buffer[MAXBUFLEN];
while (!feof(f1)) {
if (fgets(buffer,MAXBUFLEN,f1)>0) {
for(int i=2;i<strlen(buffer);i+=3) {
fprintf(f2,"%c",buffer[i]);
}
fprintf(f2,"\n");
} else break;
}
fclose(f1);
fclose(f2);
This will read input file and reprint every third character to output. You can adapt it to you situation.
#include <stdio.h>
#include <stdlib.h>
int main()
{
size_t i; // index
int c; // char read
FILE *FIN, *FOUT; // file streams
if ((FIN = fopen("in.txt", "rb")) == NULL) {
printf("Error opening input file.\n Exiting.\n");
exit(1);
}
if ((FOUT = fopen("out.txt", "wb")) == NULL) {
printf("Error opening output file.\n Exiting.\n");
exit(1);
}
// read input and reprint every third character
for(i=0;;i++)
{
c = fgetc(FIN); // read byte
if(c == EOF)
{
break; // reached end of file (input), leave loop
}
if((i%3)==2) // get every third character by modulo(i)
{
fputc(c, FOUT); // write output
}
}
fclose(FIN);
fclose(FOUT);
return 0;
}

Incorrect fprintf results

In the code below, I am trying to read from a socket and store the results in a file.
What actually happens, is that my client sends a GET request to my server for a file.html. My server finds the file and writes the contents of it to the socket. Lastly my client reads the content from thread_fd and recreates the file.
For some reason the recreated file has less content than the original. I have located the problem to be some lines in the end, that are missing. When I use printf("%s", buffer) inside the while loop everything seems fine in STDOUT but my fprintf misses somewhat 3.000 bytes for a file of 81.000 bytes size.
#define MAXSIZE 1000
int bytes_read, thread_fd;
char buffer[MAXSIZE];
FILE* new_file;
memset(buffer, 0, MAXSIZE);
if((new_file = fopen(path, "wb+")) == NULL)
{
printf("can not open file \n");
exit(EXIT_FAILURE);
}
while ((bytes_read = read(thread_fd, buffer, MAXSIZE)) > 0)
{
fprintf(new_file, "%s", buffer);
if(bytes_read < MAXSIZE)
break;
memset(buffer, 0, MAXSIZE);
}
You read binary data from the socket that may or may not contain a \0 byte. When you then fprintf that data the fprintf will stop at the first \0 it encounters. In your case that is 3000 bytes short of the full file. If your file contains no \0 byte the fprintf will simply continue printing the ram contents until it segfaults.
Use write() to write the data back to the file and check for errors. Don't forget to close() the file and check that for errors too.
Your code should/could look like:
int readfile(int thread_fd, char *path)
{
unsigned int bytes_read;
char buffer[MAXSIZE];
int new_file;
if ((new_file = open(path, _O_CREAT|_O_BINARY,_S_IWRITE)) == -1) return -1;
while ((bytes_read = read(thread_fd, buffer, MAXSIZE)) > 0)
{
if (write(new_file, buffer, bytes_read)!= bytes_read) {
close(new_file);
return -2;
}
}
close(new_file);
return 0;
}
There are a few issues with your code that can cause this.
The most likely cause is this :
if(bytes_read < MAXSIZE)
break;
This ends the loop when read returns less than the requested amount of bytes. This is however perfectly normal behavior, and can happen eg. when not enough bytes are available at the time of the read call (it's reading from a network socket after all). Just let the loop continue as long as read returns a value > 0 (assuming the socket is a blocking socket - if not, you'll also have to check for EAGAIN and EWOULDBLOCK).
Additionally, if the file you're receiving contains binary data, then it's not a good idea to use fprintf with "%s" to write to the target file. This will stop writing as soon as it finds a '\0' byte (which is not uncommon in binary data). Use fwrite instead.
Even if you're receiving text (suggested by the html file extension), it's still not a good idea to use fprintf with "%s", since the received data won't be '\0' terminated.
This worked!
ps: I don't know if I should be doing this, since I am new here, but really there is no reason for negativity. Any question is a good question. Just answer it if you know it. Do not judge it.
#define MAXSIZE 1000
int bytes_read, thread_fd, new_file;
char buffer[MAXSIZE];
memset(buffer, 0, MAXSIZE);
if((new_file = open(path, O_RDONLY | O_WRONLY | O_CREAT)) < 0)
{
printf("can not open file \n");
exit(EXIT_FAILURE);
}
while ((bytes_read = read(thread_fd, buffer, MAXSIZE)) > 0)
write(new_file, buffer, bytes_read);
close(new_file);

Getting characters past a certain point in a file in C

I want to take all characters past location 900 from a file called WWW, and put all of these in an array:
//Keep track of all characters past position 900 in WWW.
int Seek900InWWW = lseek(WWW, 900, 0); //goes to position 900 in WWW
printf("%d \n", Seek900InWWW);
if(Seek900InWWW < 0)
printf("Error seeking to position 900 in WWW.txt");
char EverythingPast900[appropriatesize];
int NextRead;
char NextChar[1];
int i = 0;
while((NextRead = read(WWW, NextChar, sizeof(NextChar))) > 0) {
EverythingPast900[i] = NextChar[0];
printf("%c \n", NextChar[0]);
i++;
}
I try to create a char array of length 1, since the read system call requires a pointer, I cannot use a regular char. The above code does not work. In fact, it does not print any characters to the terminal as expected by the loop. I think my logic is correct, but perhaps a misunderstanding of whats going on behind the scenes is what is making this hard for me. Or maybe i missed something simple (hope not).
If you already know how many bytes to read (e.g. in appropriatesize) then just read in that many bytes at once, rather than reading in bytes one at a time.
char everythingPast900[appropriatesize];
ssize_t bytesRead = read(WWW, everythingPast900, sizeof everythingPast900);
if (bytesRead > 0 && bytesRead != appropriatesize)
{
// only everythingPast900[0] to everythingPast900[bytesRead - 1] is valid
}
I made a test version of your code and added bits you left out. Why did you leave them out?
I also made a file named www.txt that has a hundred lines of "This is a test line." in it.
And I found a potential problem, depending on how big your appropriatesize value is and how big the file is. If you write past the end of EverythingPast900 it is possible for you to kill your program and crash it before you ever produce any output to display. That might happen on Windows where stdout may not be line buffered depending on which libraries you used.
See the MSDN setvbuf page, in particular "For some systems, this provides line buffering. However, for Win32, the behavior is the same as _IOFBF - Full Buffering."
This seems to work:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int WWW = open("www.txt", O_RDONLY);
if(WWW < 0)
printf("Error opening www.txt\n");
//Keep track of all characters past position 900 in WWW.
int Seek900InWWW = lseek(WWW, 900, 0); //goes to position 900 in WWW
printf("%d \n", Seek900InWWW);
if(Seek900InWWW < 0)
printf("Error seeking to position 900 in WWW.txt");
int appropriatesize = 1000;
char EverythingPast900[appropriatesize];
int NextRead;
char NextChar[1];
int i = 0;
while(i < appropriatesize && (NextRead = read(WWW, NextChar, sizeof(NextChar))) > 0) {
EverythingPast900[i] = NextChar[0];
printf("%c \n", NextChar[0]);
i++;
}
return 0;
}
As stated in another answer, read more than one byte. The theory behind "buffers" is to reduce the amount of read/write operations due to how slow disk I/O (or network I/O) is compared to memory speed and CPU speed. Look at it as if it is code and consider which is faster: adding 1 to the file size N times and writing N bytes individually, or adding N to the file size once and writing N bytes at once?
Another thing worth mentioning is the fact that read may read fewer than the number of bytes you requested, even if there is more to read. The answer written by #dreamlax illustrates this fact. If you want, you can use a loop to read as many bytes as possible, filling the buffer. Note that I used a function, but you can do the same thing in your main code:
#include <sys/types.h>
/* Read from a file descriptor, filling the buffer with the requested
* number of bytes. If the end-of-file is encountered, the number of
* bytes returned may be less than the requested number of bytes.
* On error, -1 is returned. See read(2) or read(3) for possible
* values of errno.
* Otherwise, the number of bytes read is returned.
*/
ssize_t
read_fill (int fd, char *readbuf, ssize_t nrequested)
{
ssize_t nread, nsum = 0;
while (nrequested > 0
&& (nread = read (fd, readbuf, nrequested)) > 0)
{
nsum += nread;
nrequested -= nread;
readbuf += nread;
}
return nsum;
}
Note that the buffer is not null-terminated as not all data is necessarily text. You can pass buffer_size - 1 as the requested number of bytes and use the return value to add a null terminator where necessary. This is useful primarily when interacting with functions that will expect a null-terminated string:
char readbuf[4096];
ssize_t n;
int fd;
fd = open ("WWW", O_RDONLY);
if (fd == -1)
{
perror ("unable to open WWW");
exit (1);
}
n = lseek (fd, 900, SEEK_SET);
if (n == -1)
{
fprintf (stderr,
"warning: seek operation failed: %s\n"
" reading 900 bytes instead\n",
strerror (errno));
n = read_fill (fd, readbuf, 900);
if (n < 900)
{
fprintf (stderr, "error: fewer than 900 bytes in file\n");
close (fd);
exit (1);
}
}
/* Read a file, printing its contents to the screen.
*
* Caveat:
* Not safe for UTF-8 or other variable-width/multibyte
* encodings since required bytes may get cut off.
*/
while ((n = read_fill (fd, readbuf, (ssize_t) sizeof readbuf - 1)) > 0)
{
readbuf[n] = 0;
printf ("Read\n****\n%s\n****\n", readbuf);
}
if (n == -1)
{
close (fd);
perror ("error reading from WWW");
exit (1);
}
close (fd);
I could also have avoided the null termination operation and filled all 4096 bytes of the buffer, electing to use the precision part of the format specifiers of printf in this case, changing the format specification from %s to %.4096s. However, this may not be feasible with unusually large buffers (perhaps allocated by malloc to avoid stack overflow) because the buffer size may not be representable with the int type.
Also, you can use a regular char just fine:
char c;
nread = read (fd, &c, 1);
Apparently you didn't know that the unary & operator gets the address of whatever variable is its operand, creating a value of type pointer-to-{typeof var}? Either way, it takes up the same amount of memory, but reading 1 byte at a time is something that normally isn't done as I've explained.
Mixing declarations and code is a no no. Also, no, that is not a valid declaration. C should complain about it along the lines of it being variably defined.
What you want is dynamically allocating the memory for your char buffer[]. You'll have to use pointers.
http://www.ontko.com/pub/rayo/cs35/pointers.html
Then read this one.
http://www.cprogramming.com/tutorial/c/lesson6.html
Then research a function called memcpy().
Enjoy.
Read through that guide, then you should be able to solve your problem in an entirely different way.
Psuedo code.
declare a buffer of char(pointer related)
allocate memory for said buffer(dynamic memory related)
Find location of where you want to start at
point to it(pointer related)
Figure out how much you want to store(technically a part of allocating memory^^^)
Use memcpy() to store what you want in the buffer

Methodology to read a virtual disk's MBR

I am trying to build my own C program that basically works just like fdisk vdisk 'p' command. I just want to be able to read in the first 512 bytes of the disk, lseek to the start of the partitions at (0x1BE) and then read the partition type, name, size, ect. I am unsure how to actually read these values. I have used the read() linux function to read in 512 bytes but when I try displaying/viewing them in any way, nothing is shown. What am I doing wrong?
int main(int argc, char *argv[]) {
int bytes_read;
char mbr[512];
int file;
if(argc == 1) {
// Print some help info
printf ("Here is some help info: \n\n");
} else if(argc < 3) {
printf("File: %s\n\n", argv[1]);
file = open(argv[1], O_RDONLY);
lseek(bytes_read, 0, 0);
//First get the MBR
bytes_read = read(file, mbr, 512);
printf("MBR=%s\n\nbytes_read=%d\n\n", mbr, bytes_read);
} else {
printf ("Incorrect usage: fdisk <disk>\n\n");
}
}
Don't try to use printf with binary data. If your binary data starts with a NUL (ASCII 0), then printf will assume you've got an empty string. You can use write() to write out arbitrary data (it takes a buffer and length), e.g:
#include <unistd.h>
write(STDOUT_FILENO, mbr, 512)
...but even this won't necessarily display anything useful, because your terminal may try to interpret control characters in the output. You're best bet would then be to pipe the output to something like xxd or od, both of which will produce a hexdump of their input data.
For example, the first 512 bytes of my local drive are all NUL. Using write() in your code (and removing that lseek) results in 512 NUL bytes on output. Try passing something other than disk to your code, e.g.:
myexe /etc/passwd
The structure of a standard DOS MBR is documented here, suggesting that you might start with data structures like this:
struct _partition {
uint8_t status;
uint8_t chs_start[3];
uint8_t part_type;
uint8_t chs_end[3];
uint32_t lba_start;
uint32_t sectors;
};
And populate it something like this:
fd = open(target, O_RDONLY);
lseek(fd, 446, SEEK_SET);
for (i=0; i<4; i++) {
struct _partition p;
bytes_read = read(fd, &p, sizeof(struct _partition));
// assume this prints information to stdout or something.
print_part(i, &p);
}
Get rid of the lseek. Your compiler should be throwing a warning right now, as you're passing it an argument (bytes_read) which has not been initialized.
Once that's done, you'll need to do something to display the contents; right now, you've got nothing that uses the data you read.

Socket Read/Write error

would install valgrind to tell me what the problem is, but unfortunately can't any new programs on this computer... Could anyone tell me if there's an obvious problem with this "echo" program? Doing this for a friend, so not sure what the layout of the client is on the other side, but I know that both reads and writes are valid socket descriptors, and I've tested that n = write(writes,"I got your message \n",20); and n = write(reads,"I got your message \n",20); both work so can confirm that it's not a case of an invalid fd. Thanks!
int
main( int argc, char** argv ) {
int reads = atoi(argv[1]) ;
int writes = atoi(argv[3]) ;
int n ;
char buffer[MAX_LINE];
memset(buffer, 0, sizeof(buffer));
int i = 0 ;
while (1) {
read(reads, buffer, sizeof(buffer));
n = write(writes,buffer,sizeof(buffer));
if (n < 0) perror("ERROR reading from socket");
}
There are a few problems, the most pressing of which is that you're likely pushing garbage data down the the write socket by using sizeof(buffer) when writing. Lets say you read data from the reads socket and it's less than MAX_LINES. When you go to write that data, you'll be writing whatever you read plus the garbage at the end of the buffer (even though you memset at the very beginning, continual use of the same buffer without reacting to different read sizes will probably generate some garbage.
Try getting the return value from read and using it in your write. If the read return indicates an error, clean up and either exit or try again, depending on how you want your program to behave.
int n, size;
while (1) {
size = read(reads, buffer, sizeof(buffer));
if (size > 0) {
n = write(writes, buffer, size);
if (n != size) {
// write error, do something
}
} else {
// Read error, do something
}
}
This, of course, assumes your writes and reads are valid file descriptors.
These two lines look very suspicious:
int reads = atoi(argv[1]) ;
int writes = atoi(argv[3]) ;
Do you really get file/socket descriptor numbers on the command line? From where?
Check the return value of your read(2) and write(2), and then the value of errno(3) - they probably tell you that your file descriptors are invalid (EBADF).
One point not made thus far: Although you know that the file descriptors are valid, you should include some sanity checking of the command line.
if (argc < 3) {
printf("usage: foo: input output\n");
exit(0);
}
Even with this sanity checking passing parameters like this on a command line can be dangerous.
The memset() is not needed, provided you change the following (which you should do nevertheless).
read() has a result, telling you how much it has actually read. This you should give to write() in order to write only what you actually have, removing the need for zeroing.
MAX_LINE should be at least 512, if not more.
There probably are some more issues, but I think I have the most important ones.

Resources