Why rewind() followed by fscanf() do not reflect changes made on disk? - c

I am repeatedly reading a previously-opened file using fscanf and rewind:
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
void read_opened_file(FILE *file, int *value)
{
errno = 0;
fscanf(file, "%d", value);
if (errno != 0) perror("fscanf error");
errno = 0;
rewind(file);
if (errno != 0) perror("rewind error");
}
int main(int argc, char* argv[])
{
int value = 0;
FILE *file = fopen(argv[1], "r");
errno = 0;
if (setvbuf(file, NULL, _IONBF, 0) != 0) perror("setvbuf error");
for (int i = 0; i < 10; ++i)
{
read_opened_file(file, &value);
printf("value = %d\n", value); fflush(stdout);
sleep(1);
}
fclose(file);
return 0;
}
However, any changes made to the file on disk behind the scenes by gedit, echo >, cp are not reflected: the function keeps reporting the first (cached?) value read (and errno is not set).
When I setvbuf(file, NULL, _IONBUF, 0); as suggested here, changes made by cp and echo > are reflected, but changes made by gedit are still not reflected.
When I use fopen and fclose every loop iteration everything is as expected.
How do I fix the above code without fopen and fclose every time?

You can't. The gedit program does not change the file. That's simply not how it works. So you will not see any change to the file.
The changes gedit makes are done by replacing the old file with a new file. You must close the old file and open the new file to see the changes that gedit made.

Related

How to change STDIN stream to binary

I'm making an upload form via a CGI interface. I'm writing it in C and don't want to use any outside libraries (ie. cgic).
I thought the program was complete, as the first test files uploaded correctly. But they were ASCII files. When I tested with a binary file (JPG). It seems that STDIN is trying to read the binary data as ASCII which creates a problem for characters like \0 which is present at the end of an ASCII file, but is a common character in binary files. The results of uploading a 1.9MB file end up with a 38kB file.
When searching how to change the STDIN stream to binary, I was referred to the command freopen and told to use NULL as the argument for the file. example 1
It says:
If filename is a null pointer, the freopen() function shall attempt to
change the mode of the stream to that specified by mode, as if the
name of the file currently associated with the stream had been used.
In this case, the file descriptor associated with the stream need not
be closed if the call to freopen() succeeds. It is
implementation-defined which changes of mode are permitted (if any),
and under what circumstances.
But when I check the man page on my system with man 3 freopen, it doesn't say any of
this at all. Furthermore, reading the man page, I find out the the
option for binary (adding 'b' to the mode) is no longer recognized and
only exists for archaic compliancy:
The mode string can also include
the letter 'b' either as a last character or as a character between
the characters in any of the two-character strings described above.
This is strictly for compatibility with C89 and has no effect; the 'b'
is ignored on all POSIX conforming systems, including Linux.
So right now I'm completely lost. How can I change the STDIN stream to read binary input?
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
// Declare constants.
#define BUF_SIZE 4096
#define FILENAME_SIZE 500
#define MARKER_SIZE 100
#define RETURN_FAILURE 0
#define RETURN_SUCCESS 1
#define SEARCH_STRING_1 "filename=\""
#define SEARCH_STRING_2 "\r\n\r\n"
// Declare global variables.
char filename[FILENAME_SIZE + 1];
char *program_name;
// Declare function prototype.
void print_footer (void);
void print_header (void);
void process_input (char *data);
int main (int argc, char *argv[])
{
// Declare variables.
long long ret;
char buf[BUF_SIZE + 1];
// Get program name for error reporting.
program_name = basename(argv[0]);
// Prepare output for browser.
print_header();
// Protect variable against buffer overflow.
buf[BUF_SIZE] = '\0';
// Loop through all the file data.
while(1)
{
// Read in the next block of data.
if((ret = (long long) fread(buf, 1, BUF_SIZE, stdin)) != BUF_SIZE)
{
// Check for error.
if(ferror(stdin) != 0)
{
printf("%s: An error occurred while reading the input file.<br>\n", program_name);
process_input(NULL);
exit(EXIT_FAILURE);
}
// Check for EOF.
else if(feof(stdin) != 0)
break;
}
// Terminate and process uploaded data.
buf[ret] = '\0';
process_input(buf);
}
// Terminate and process uploaded data.
buf[ret] = '\0';
process_input(buf);
// Finish user output, close output file and exit.
print_footer();
process_input(NULL);
exit(EXIT_SUCCESS);
}
void process_input (char *data)
{
// Declare variables.
char *ptr1= NULL;
char *ptr2;
int x = 0;
static FILE *fp;
static int flag = 0;
static char marker[MARKER_SIZE + 1];
// If data is NULL, close output file.
if(data == NULL)
{
if(fclose(fp) == EOF)
{
printf("%s: process_input: close failed (%s)<br>\n", program_name, strerror(errno));
exit(EXIT_FAILURE);
}
return;
}
// Check if this is the first time through.
if(flag == 0)
{
// Get marker.
if((ptr1 = strchr(data, '\n')) == NULL)
{
printf("%s: process_input: strchr(1) failed (\n)<br>\n", program_name);
exit(EXIT_FAILURE);
}
ptr1[0] = '\0';
strcpy(marker, data);
ptr1[0] = '\n';
// Get filename.
if((ptr1 = strstr(data, SEARCH_STRING_1)) == NULL)
{
printf("%s: process_input: strstr(1) failed (%s)<br>\n", program_name, SEARCH_STRING_1);
exit(EXIT_FAILURE);
}
// Advance pointer to start of filename.
ptr1 += 10;
// Find end of filename.
if((ptr2 = strchr(ptr1, '"')) == NULL)
{
printf("%s: process_input: strchr(2) failed (\")<br>\n", program_name);
exit(EXIT_FAILURE);
}
// Terminate and store filename.
ptr2[0] = '\0';
strcpy(filename, ptr1);
ptr2[0] = '"';
// Remove spaces from filename.
while(filename[x] != '\0')
{
if(filename[x] == ' ')
filename[x] = '.';
x++;
}
// Open output file.
if((fp = fopen(filename, "wb")) == NULL)
{
printf("%s: process_input: fopen failed (%s) (%s)<br>\n", program_name, strerror(errno), filename);
exit(EXIT_FAILURE);
}
// Find start of file data.
if((ptr1 = strstr(data, SEARCH_STRING_2)) == NULL)
{
printf("%s: process_input: strstr(2) failed (%s)<br>\n", program_name, SEARCH_STRING_2);
fclose(fp);
exit(EXIT_FAILURE);
}
// Set flag.
flag++;
// Advance pointer to start of file data.
ptr1 += 4;
// Change STDIN stream to binary.
if(freopen(NULL, "rb", stdin) == NULL)
{
printf("%s: process_input: freopen failed (%s)<br>\n", program_name, strerror(errno));
fclose(fp);
exit(EXIT_FAILURE);
}
}
// Catch everything else.
else
{
ptr1 = data;
if((ptr2 = strstr(ptr1, marker)) != NULL)
ptr2[0 - 2] = '\0';
}
// Write file data.
if(fwrite(ptr1, 1, strlen(ptr1), fp) != strlen(ptr1))
{
printf("%s: process_input: write failed (%s)<br>\n", program_name, strerror(errno));
fclose(fp);
exit(EXIT_FAILURE);
}
}
void print_footer (void)
{
printf("\nMade it!\n");
}
void print_header (void)
{
printf("Content-type: text/plain\r\n\r\n");
}
Ok, it appears what #NominalAnimal said was correct. You can store binary data in a string, but the moment you use any function in the string.h library, it almost always changes what is stored in that string (if the data is binary).
The easy solution is to make a separate function that takes a pointer to the binary data and do your string searches in that function, returning what pertinent information is needed. That way, the original data is never changed.
'stdin' is a macro of STDIN_FILENO, which is egal to 0. See also 'unistd.h'.
You are not showing your code, but I think you stop when you encounter a '\0' or a non-ascii char, since you said you were using 'fread()'.
You have to stop when fread() function returns 0, which means it stopped to read : it encountered EOF.

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.

How to prevent infinite loop writing to file

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

beginner in C under linux. write function doesn't work properly

I'm trying to write a C program, that make user able to write stuff in a file. My Problem is that after making and running the program the file stay empty ?? any idea how can I solve this.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
// the user should give a file to write the file
int main (int argc , char**argv)
{
int fd; // file descriptor
char ret; // the character
int offset;
if(argc != 2) {
printf("You have to give the name or the path of the file to work with \n");
printf("Exiting the program \n")
return -1;
}
fd = open (argv[1], O_WRONLY/*write*/|O_CREAT/*create if not found */, S_IRUSR|S_IWUSR/*user can read and write*/);
if (fd == -1) {
printf("can'T open the file ");
return -1;
}
printf("At wich position you want to start ");
scanf("%d",&offset);
lseek(fd,offset,SEEK_SET);
while(1) {
ret = getchar();
if(ret == '1') {
printf("closing the file");
close (fd);
return 1;
}
else
write (fd,red, sizeof(char));
}
return 0;
}
thanks in advance for you help.
I have made some changes,this should work:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main (int argc , char**argv)
{
int fd; // file descriptor
char ret; // the character
int offset;
if(argc != 2){
printf("You have to give the name or the path of the file to work with \n");
printf("Exiting the program \n"); **//There was ';' missing here**
return -1;
}
fd = open (argv[1], O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
if (fd == -1) {
printf("can'T open the file ");
return -1;
}
printf("At wich position you want to start ");
scanf("%d",&offset);
lseek(fd,offset,SEEK_SET);
while(1){
ret = getchar();
if(ret == '1'){
printf("closing the file");
close (fd);
return 1;
}
else
write (fd,&ret, sizeof(char)); **//red has been changed to &ret**
}
return 0;
}
One error I can notice, the call of write function:
write (fd,red, sizeof(char));
should be:
write (fd, &red, sizeof(char));
You forgot & before red, write need address.
syntax of write: int write( int handle, void *buffer, int nbyte );
This will cause an undefined behavior in your code at run time
Edit: in write function you are using red that is not defined, I think it should be ret variable in your code. correct it as write (fd, &ret, sizeof(char));
second, you forgot ; after printf("Exiting the program \n") in if, but I also think its mistake while posting question as you says you are getting run time error.
side note: If you are using gcc compiler then you can use gcc -Wall -pedantic to generate warnings
It should be:
write (fd,&ret, sizeof(char));
write takes the pointer to the memory position, and since ret is a single char, you need to pass a pointer to it.

zlib, c and gzread

so I'm using the zlib package on Ubuntu. I'm trying to figure out how to use gzopen and gzread correctly, this is what I have so far
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <zlib.h>
#define NUM_BUFFERS 8
#define BUFFER_LENGTH 1024
char buf[BUFFER_LENGTH];
int main(int argc, const char* argv[])
{
int status;
gzFile file;
file = gzopen("beowulf.txt", "w");
int counter = 0; /*when the counter reachers BUFFERS_FULL, stop*/
if(file == NULL)
{
printf("COULD NOT OPEN FILE\n");
return 1;
}
while(counter < NUM_BUFFERS)
{
status = gzread(file, buf, BUFFER_LENGTH - 2);
printf("STATUS: %d\n", status);
buf[BUFFER_LENGTH - 1] = "\0";
printf("%s\n", buf);
counter++;
}
gzclose(file);
printf("STATUS: %d\n", status);
return 0;
}
The gzread("STATUS: %d\n",status); returns -2, and I have no clue why. Any help would be appreciated.
Mode "w" indicates that you are preparing to create a new archive:
file = gzopen("beowulf.txt", "w");
You've just truncated the file to zero length.
Also, you should use the binary mode flag: "wb" or "rb".
Also, it's a bit weird that your supposed .gz-archive has an extension .txt.
Read the docs, docs rule. :)
Log the error type using function gzerror(). Since it is -2, it won't be an end-of-file error. Possibly any of the following errors.
Z_DATA_ERROR
A CRC error occurred when reading data; the file is corrupt.
Z_STREAM_ERROR
The stream is invalid, or is in an invalid state.
Z_NEED_DICT
A dictionary is needed (see inflateSetDictionary()).
Z_MEM_ERROR
Insufficient memory available to decompress.
From http://refspecs.linuxbase.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/zlib-gzread-1.html :
If the return is positive it is the number of bytes read, you can use that to put the NULL terminator in the right place.
You can use gzerror if the return code is <0 to work out what the error is.

Resources