I have a file called "input.txt" that holds some values. I'm writing program that will find minimum value in that file, and replace that minimum with number given as command line argument - if that command line argument is bigger then minimum. These values represent room temperatures so that fact can be used in order to find minimum. In addition, that part of the file (where new number replaces minimum) needs to be locked.
Example:
$ ./prog 23
FILE: 21 25 19 22 24
FILE: 21 25 23 22 24
$ ./prog 29
FILE: 21 25 23 22 24
FILE: 29 25 23 22 24
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdint.h>
/* Function that handles errors */
void fatal_error(const char *message){
perror(message);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv){
if(argc != 2)
fatal_error("Bad arguments.\n");
/* Fetching command line argument */
int temp = atoi(argv[1]);
/* Opening file and checking for errors */
FILE *file = fopen("input.txt", "r+");
if(!file)
fatal_error("Unable to open file.\n");
/* Finding minimum in the file */
int min = 200;
int value;
while(fscanf(file, "%d", &value) != EOF)
if(value < min)
min = value;
/* Exiting if nothing needs to change */
if(temp <= min)
return 0;
/* Creating file descriptor from stream and checking for errors */
int fdOpen = fileno(file);
if(fdOpen == -1)
fatal_error("Unable to open file descriptor.\n");
/* Moving offset to the beginning of the file */
off_t of = lseek(fdOpen,0,SEEK_SET);
printf("Ofset pre petlje: %jd\n", (intmax_t)of);
while(1){
/* I'm reading file all over again */
if(fscanf(file, "%d", &value) == EOF)
fatal_error("Reached end of file.\n");
/* If I reached minimum */
if(value == min){
/* I lock that part of the file - temperatures are two digit numbers*/
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_CUR;
lock.l_start = -2;
lock.l_len = 2;
/* I create lock */
if(fcntl(fdOpen,F_SETLK, &lock) == -1){
if(errno == EACCES || errno == EAGAIN)
fatal_error("File is locked.\n");
}
/* Moving two positions back from current position */
off_t offset;
if((offset = lseek(fdOpen, -2, SEEK_CUR)) == -1)
fatal_error("lseek error.\n");
/* Inserting read command line value into file */
fprintf(file, "%d", temp);
/* Unlocking */
lock.l_type = F_UNLCK;
if(fcntl(fdOpen, F_SETLK, &lock) == -1)
fatal_error("Unable to destroy lock.\n");
/* Closing file descriptor, and breaking loop */
close(fdOpen);
break;
}
}
return 0;
}
This however doesn't work. File won't change. The problem is - I know this is correct way to do it. I have written basically the same program that changes for example every appearance of word "aa" to "bb". It is basically the same concept.
I've tried:
- I've tried fetching offset before and right after
fscanf()
inwhile()
loop. Before firstfscanf
offset is set to 0 - beginning of the file. After firstfscanf
offset is set to end of the file, and offset remains at the end of the file after each iteration. - I've tried using
ftell()
in these sameprintf()
andftell()
gives me correct offset. However thatftell()
in while loop still gives end of the file. I've also tried usingfseek()
instead oflseek()
(Even tho I knowfseek()
useslseek()
in its implementation). Still, same result. - I've tried using
char*
buffer instead of values. Basically to usefscanf(file,"%s",buffer)
, convert that value to check whether read value is minimum, and afterwards I've usedfprintf(file,"%s",buffer)
to write that. However same result. - I've tried locking whole file (I thought - maybe there's the problem), however, same result.
Code:
Here is second program I mentioned that uses same concept. This code works, but I've tried printing offset here too, and offset is also at the end of the file.
int main(int argc, char **argv){
if(argc != 4)
fatal_error("You must enter exactly 4 arguments.\n");
FILE *f = fopen(argv[1], "r+");
if(!f)
fatal_error("Unable to open file for reading and writing.\n");
int fd = fileno(f);
if(fd == -1)
fatal_error("Unable to fetch file descriptor for file.\n");
char word[MAX_LEN + 1];
int word_len = strlen(argv[2]);
while(fscanf(f,"%s",word) != EOF){
printf("%jd\n", lseek(fd,0,SEEK_CUR));
if(!strcmp(argv[2],word)){
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_CUR;
lock.l_start = -word_len;
lock.l_len = word_len;
if(fcntl(fd, F_SETLKW, &lock) == -1)
fatal_error("File locking failed.\n");
if(lseek(fd, -word_len, SEEK_CUR) == -1)
fatal_error("Lseek error.\n");
fprintf(f, "%s", argv[3]);
lock.l_type = F_UNLCK;
if(fcntl(fd, F_SETLK, &lock) == -1)
fatal_error("Failed to release lock.\n");
}
}
}
As you can see it is exactly same concept. Second program works, first one doesn't.
I'm very confused now. If I create file descriptor from file stream, and then use lseek()
on that file descriptor to change offset, does offset of stream also change? And if you use fscanf()
to read something from stream, do offset_t
change just as much as you read from file? Is there any difference in changing offset if I use fscanf()
with format specifier %d
and %s
?