I'm trying to build a C portable code (for Windows, MacOS and Linux) that creates an output .txt
file to receive the results of a numerical simulation.
Summarizing, the code takes the name of the file and the extension and checks if the file already exists in the directory. If so, it creates another file with the same name, but with a number between parenthesis (#) in the end to distinguish the old from the new one.
The problem is: it is working properly on the mac environment, however when I compile and run it on windows, the file is not created in the end of execution. I could not find what I'm doing wrong.
Also, I'm using Intel C/C Classic compiler. If I use another compiler, for example, the Intel® oneAPI DPC /C Compiler for windows, it complains about the usage of sizeof(src)
when I call the strncat(...)
function.
So far, this is the version of the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
bool file_exists(char *filename);
FILE *create_output_file(char *name, char *ext, bool (*file_exists)(char *));
#define funcname "test"
int main(void) {
// Create output files to store results
char *output_filename = malloc(200 * sizeof(*output_filename));
sprintf(output_filename, "%s_out", funcname);
printf("output_filename = %s\n", output_filename);
char *ext = ".txt";
FILE *output_file = create_output_file(output_filename, ext, file_exists);
}
bool file_exists(char *filename) {
// Try to open file with same name as filename
FILE *testfile = fopen(filename, "r");
// Bool variable to check the existence of file
bool exists = false;
// Check the existence of a file called filename
if (testfile != NULL) {
// Returns true if the file exists
exists = true;
}
// Close the file
fclose(testfile);
// Returns the existence of a file (1) = does exist, (0) = does not exist
return exists;
}
FILE *create_output_file(char *name, char *ext, bool (*file_exists)(char *)) {
// Form the full filename
name = strncat(name, ext, sizeof(ext));
printf("fullfilename = %s\n", name);
// Check if a file with the filename exists in the directory
if (file_exists(name)) {
// If exists, assign the same name with "(number)" to differentiate the new version
int j = 1;
char *numb = malloc(10 * sizeof(*numb));
sprintf(numb, "(%i)", j);
// Remove the extension from the name string
name[strlen(name) - strlen(ext)] = '\0';
// Add (number) to the name and then add the file extension again
name = strncat(name, numb, sizeof(numb));
name = strncat(name, ext, sizeof(ext));
// Check if the name with numbers exists until it doesn't
int limit = 1e1;
while (file_exists(name)) {
j ;
sprintf(numb, "(%i)", j);
if (j == limit) {
name[strlen(name) - strlen(numb) 1 - strlen(ext)] = '\0';
limit = limit*10;
} else {
name[strlen(name) - strlen(numb) - strlen(ext)] = '\0';
}
name = strncat(name, numb, sizeof(numb));
name = strncat(name, ext, sizeof(ext));
}
// Free allocated memory
free(numb);
}
// After assign the proper name, create the output file
FILE *output_file = fopen(name, "w");
// Returns the file
return output_file;
}
What am I missing here?
CodePudding user response:
There are multiple problems:
In
file_exists
you callfclose(testfile)
even iffopen
failed This has undefined behavior.in
create_output_file
, your usage ofsizeof
is incorrect: instrncat(name, ext, sizeof(ext));
sizeof
is applied to a pointer, hence it evaluates to the size of a pointer, not the length of the string it points to. You could writestrncat(name, ext, strlen(ext));
but it would be exactly equivalent to
strcat(name, ext);
The function
strncat
is defined aschar *strncat(char *dest, const char *src, size_t n);
it copies at most
n
characters plus a null terminator from the string pointed to bysrc
at the end of the string pointed to bydest
.The code is too complicated, you have multiple memory leaks and you do not check for buffer overflow when composing the filename.
Here is a simplified version:
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#define funcname "test"
bool file_exists(const char *filename) {
// Try to open file with same name as filename
FILE *testfile = fopen(filename, "r");
if (testfile != NULL) {
fclose(testfile);
return true;
} else {
return false;
}
}
#define MAX_FILE_NUM 10000
FILE *create_output_file(char *name, size_t size,
const char *ext,
bool (*file_exists)(const char *))
{
size_t len = strlen(name);
int i = 0;
snprintf(name len, size - len, "%s", ext);
while (file_exists(name)) {
if ( i > MAX_FILE_NUM) {
// all file versions tried.
return NULL;
}
snprintf(name len, size - len, "(%d)%s", i, ext);
}
return fopen(name, "w");
}
int main() {
// Create output files to store results
char output_filename[200];
snprintf(output_filename, sizeof output_filename, "%s_out", funcname);
printf("output_filename = %s\n", output_filename);
const char *ext = ".txt";
FILE *output_file = create_output_file(output_filename,
sizeof output_filename, ext, file_exists);
if (output_file == NULL) {
printf("could not open output file, last try: %s\n", output_filename);
} else {
printf("actual output_filename = %s\n", output_filename);
fclose(output_file);
}
return 0;
}