Create "copy" of FILE* state - c

I want to something like this:
char* parse_path(FILE* original_state) {
FILE* copied_state = copyfp(original_state);
// logic for reading the path
if (error) {
return NULL;
}
else {
somehow_set_state(original_state, copied_state);
return path;
}
}
int main() {
FILE* fp = open("file_with_or_without_path.txt");
if (fd == NULL) return 1;
char* path = parse_path(fd);
if (path != NULL)
printf("path fould: %s\n", path);
else
printf("no path found\n");
...
}
So basically I want to copy the state of a FILE* and modify it (like read or seek) and being able to write the new state to the other FILE* in case parsing was a success. I could remember the current cursor location and seek back later, but that would make my code a lot more complex. So I was wondering, can I

You can't copy the state of a FILE* object. You can copy the FILE* object itself, but that's not what you want. You want to copy the state of the underlying file descriptor. You can do that with dup() or dup2().

Related

Can't delete file within folder in C

Im making a txt editor in the terminal, one of it's features is to edit a specific line.
To do so,
I am creating a new temporary txt file,
deleting the old/original one
and renaming the temporary one to the original.
Here's the code:
FileLineEdit(char filename[20], int line, char newline[1000]){
FILE * fp;
FILE * fptmp;
char buffer[1000];
int count;
int ret;
fp = fopen(filename, "r");
fptmp = fopen("tmp/replace.txt", "w");
if (fp == NULL || fptmp == NULL)
{
printf("\nErro!\n");
exit(1);
}
count = 0;
while ((fgets(buffer, 1000, fp)) != NULL)
{
count++;
if (count == line)
fputs(newline, fptmp);
else
fputs(buffer, fptmp);
}
fclose(fp);
fclose(fptmp);
//strcat(fullpath, filename);
//printf("%s", fullpath);
ret = remove(filename);
if(ret == 0) {
printf("File deleted successfully");
} else {
printf("Error: unable to delete the file");
}
rename("tmp/replace.txt", "tmp/a.txt");
getch();
}
The output is constantly:
Error: unable to delete the file
btw once I try this outside the "tmp/" folder it works just fine
The /tmp folder has the sticky bit (s) set, and that means, that anyone can read and create/modify files in it, but only its owner (root) can remove them.
So, if is what you want your program to do, you should do it in some directory other than /tmp
Also, as jarmod pointed out, you shouldn't have a hardcoded filename for your temporary filename. You should use tmpfile or tmpnam for this purpose:
Instead of:
fptmp = fopen("tmp/replace.txt", "w");
Write:
fptmp = tmpfile();
The file will be automatically deleted when the file stream is closed.
(You can read a more about the /tmp dir here)

check if function was called before

Here is my basic code that I'm trying to work with.
void test(){
FILE *input;
input = fopen("input.txt.", "r");
}
So I'm trying to check if file was already opened before, meaning that void test() function was called once before. I realy have no idea how to do that, i tried it with while and if.
Like this.
void test(){
FILE *input;
int open = 0;
while (open == 0){
input = fopen("input.txt", "r");
if (input == NULL){
printf("File wasnt opened.\n");
}
if (input != NULL){
printf("File is opened.\n");
}
open = open + 1;
}
if(open!=0){
printf("file is already opened.\n");
}
}
Use a local static variable.
void test (void)
{
static bool called_before = false;
if(called_before)
{
do_this();
}
else
{
do_that();
called_before = true;
}
}
Supposing that your intent is for test to open the file just once but to read from the file each time it is called, you can make input static:
void test(void)
{
/* Since input is static, it will be initialized with NULL when the
program starts and we will retain its value between calls to
the function.
*/
static FILE *input = NULL;
// If input has not been set for an open file yet, try to open it.
if (!input)
{
input = fopen("input.txt", "r");
if (!input)
{
fprintf(stderr, "Error, unable to open input.txt.\n");
exit(EXIT_FAILURE);
}
}
for (int c = fgetc(input); c != EOF && c != '\n'; c = fgetc(input))
putchar(c);
putchar('\n');
}
Note static objects should generally be avoided, as they complicate program state and so may lead to more bugs. It is okay to play with them in student programs to learn how they work, but their use in real-world applications should be limited.

I want to copy my in file on to my out file.

In this code I opened my files in my open_file function. Then the process_file function needs to copy the text from my in file and Copy it to an out file. Right now it produces a new file but it is blank. It does not give me any error messages. I do not know what is wrong.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MAX_LEN 100
FILE* open_file(char prompt[], char mode[]);
FILE* process_file(FILE* in, FILE* out);
int main(int argc, const char * argv[]) {
FILE* in = NULL;
FILE* out = NULL;
printf("MAD-LIBS Text Processor\n");
printf("The Program will open a mad-libs file, ask you to fill various words, and produce a funny story.\n");
open_file("Enter mad-lib file name:\n", "r");
open_file("Enter file name for resulting story:\n", "w");
process_file(in, out);
fclose(in);
fclose(out);
return 0;
}
/* open_file = prompts user for file name & and attempts to open it, if it fails it prompts the user again. */
FILE* open_file(char prompt [], char mode[]) {
char filename[255];
FILE* in;
do {
printf("%s", prompt);
scanf("%s", filename);
in = fopen(filename, mode);
if (in == NULL) {
printf("Unable to open file: %s. Try Again!\n", filename);
}
} while(in == NULL);
return in;
}
/* process_file = processes entire input file and writes it to output file */
FILE* process_file(FILE* in, FILE* out) {
char content[MAX_LEN];
char NewContent[MAX_LEN];
//gets whats in file in
while(fgets(content, content[MAX_LEN], in) != NULL) {
fputs (content, stdout);
strcat(NewContent, content);
}
// copies it
while (fgets(content, content[MAX_LEN], in) != NULL) {
fprintf(out, "%s", content);
}
printf("Successfully copied file\n");
return in;
}
You never assign the FILE* from open_file function to your variable, so it never gets processed.
in = open_file("Enter mad-lib file name:\n", "r");
out = open_file("Enter file name for resulting story:\n", "w");
You are not storing the FILE pointers that open_file is returning, so in
and out remain uninitialized.
You have to do:
in = open_file("Enter mad-lib file name:\n", "r");
out = open_file("Enter file name for resulting story:\n", "w");
process_file(in, out);
Also your process_file is wrong. NewContent is not initialized, when you do
strcat(NewContent, content);
this yields undefined behaviour. Declare NewContent like this:
char NewContent[MAX_LEN] = { 0 };
so that it is properly \0-terminated.
Also depending on the size of the file you are copying, MAX_LEN might not be
long enough to hold the whole file. In that case you would overflow the buffer.
It would be better not to use NewContent in the first place and write to out
in the same reading loop:
FILE* process_file(FILE* in, FILE* out) {
char content[MAX_LEN];
//gets whats in file in
while(fgets(content, MAX_LEN, in) != NULL) { //<- your fgets was wrong
fputs (content, stdout);
fprintf(out, "%s", content); // or fputs(content, out);
}
printf("Successfully copied file\n");
return in;
}
And you were calling fgets incorrectly (look at my corrected code)
Also bear in mind, that you did have 2 loop doing while(fgets(...) != NULL.
Well, the first loop ends, that's because fgets returns NULL, most likely
because the whole file was read or there was an I/O error. In either case
subsequent calls of fgets will return NULL as well, so your second loop
would not even be executed at all.

How to open a file in a subdirectory in c

I am trying to open a file in a subdirectory called "Files". I know how to open a file in a subdirectory if I know the name:
fopen("./Files/file.txt", "r");
However, I am trying to create a file that will open any and all files in the subdirectory without having to know the file names ahead of time. I am using the reaaddir() function to be able to locate the file name, however, I am not sure how to plug it into fopen to open and read the file. The readdir() function automatically saves the file name in dp_>d_name. Then If I do this:
fopen("./Files/dp->d_name", "r");
the program is going to try to open the file named "dp->d_name" rather than the file name stored at that location.
IF there is not a way to do this, is there a way to change the current working directory from within the program? Every file I want to open is within the same subdirectory, so that would be an acceptable solution.
Here is my code, for reference:
#include <stdio.h>
#include <dirent.h>
#include <string.h>
void readFile(int)
{
DIR *dir;
struct dirent *dp;
char * file_name;
char buffer[100];
FILE *out;
FILE *in;
char outName[] = "filenames.txt";
if (( out = fopen(outName, "w+")) == NULL )
{
printf("Can't open %s for writing.\n", outName);
return 2;
}
dir = opendir("./Files/");
while ((dp=readdir(dir)) != NULL)
{
if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
{
//do nothing...
}
else
{
if (( in = fopen( dp->d_name, "r")) == NULL)
{
printf("Can't open %s for writing.\n", dp->d_name);
return 2;
}
fgets(buffer, 100, in);
printf("\"%s\"\n", buffer);
}
}
closedir(dir);
fclose(out);
return 0;
}
you can create the full file location in another variable by joining "./Files/" before dp->d_name. you can either use strcat() for it or use create char* with enough size to store the entire location and then fill it using a for loop, and then use this variable in fopen. eg.
if dp->d_name = "f1.txt" then create a variable,
newFileName = "./Files/f1.txt" and then fopen(newFileName, "r");

how to keep file handle open in c after exiting function

I am trying to read proc file /proc/stat at periodic interval but I want to avoid having to open and close the proc file each time I want to access it.
I want to open the file in sort of init function, and then keep using it in some other function, then close it later on.
It seems file handles opened by a function gets closed when the function exits
How can I keep it open ?
Please let me know if I should be doing it in some other manner
Sample of what I am trying to do:
#include <stdio.h>
int printer(FILE* fin)
{
/* I am getting fin as NULL */
if(!fin)
return 1;
char buf[16*1024];
rewind(fin);
size_t sz = fread(buf, 1, sizeof(buf), fin);
if (sz) {
buf[sz]=0;
printf(buf);
}
return 0;
}
int opener(FILE *fin)
{
fin = fopen("/proc/stat", "r");
if (!fin) {
perror("fopen");
return 1;
}
return 0;
}
int main() {
FILE *fin;
/*
* I know it works if I open the file handle in here instead of
* in another function but I want to avoid this
*/
if(opener(fin))
{
printf("ERROR1\n");
return 0;
}
while(1) {
if(printer(fin))
{
printf("ERROR2\n");
break;
}
sleep(1);
}
return 0;
}
Functions in c are pass by value. So when you pass a file handle to a function, it receives a copy of that handle and will update it locally. If you want those updates to propagate to your caller, you need pass file handle pointers. So your open would look like:
int opener(FILE **fin)
{
*fin = fopen("/proc/stat", "r");
if (!(*fin)) {
perror("fopen");
return 1;
}
return 0;
}
And you would call it like:
int main() {
FILE *fin;
/*
* I know it works if I open the file handle in here instead of
* in another function but I want to avoid this
*/
if(opener(&fin))
{
printf("ERROR1\n");
return 0;
}
/...
}
You need to pass a reference to the pointer to fin in order to keep it in main.
if(opener(&fin)) {}
pass it as double pointer :
int opener(FILE **fin) {}
and use it with derefencing
*fin = fopen("/proc/stat", "r");
otherwise you initiate it everytime you call your subfonction.
The C language passes arguments by value, so the fin that opener has is a copy of the fin that main has. Changing fin in opener has no effect on main's copy.
One solution is to use a temporary file pointer in opener and then return that pointer. To indicate an error, return NULL.
FILE *opener( char *name )
{
FILE *temp = fopen( name, "r" );
if ( !temp )
{
perror( "fopen" );
return( NULL );
}
return( temp );
}
int main( void )
{
FILE *fin = opener( "/proc/stat" );
if ( !fin )
printf( "failed\n" );
else
printf( "fin=%p\n", fin );
}

Resources