diff --git a/code/logic/fossil/io/stream.h b/code/logic/fossil/io/stream.h index 04116e8..c333ec6 100644 --- a/code/logic/fossil/io/stream.h +++ b/code/logic/fossil/io/stream.h @@ -278,6 +278,15 @@ int32_t fossil_fstream_delete(const char *filename); */ int fossil_fstream_get_type(const char *filename); +/** + * Create a temporary file. + * + * This function creates a temporary file and returns its name. + * + * @return A pointer to the name of the temporary file, or NULL on failure. + */ +fossil_fstream_t fossil_fstream_tempfile(void); + /** * Check if a file is readable. * @@ -776,6 +785,17 @@ namespace fossil { return fossil_fstream_get_type(filename.c_str()); } + /** + * Create a temporary file. + * + * This function creates a temporary file and returns its name. + * + * @return A pointer to the name of the temporary file, or NULL on failure. + */ + static fossil_fstream_t tempfile() { + return fossil_fstream_tempfile(); + } + /** * Check if a file is readable. * diff --git a/code/logic/stream.c b/code/logic/stream.c index 20c28c1..c88b11e 100644 --- a/code/logic/stream.c +++ b/code/logic/stream.c @@ -23,6 +23,9 @@ #include #else #include + #ifndef _POSIX_C_SOURCE + extern int mkstemp(char *); + #endif #include #endif @@ -501,6 +504,34 @@ int32_t fossil_fstream_is_readable(const char *filename) { #endif } +fossil_fstream_t fossil_fstream_tempfile(void) { + fossil_fstream_t temp_stream; + char temp_filename[FOSSIL_BUFFER_MEDIUM]; + +#ifdef _WIN32 + if (GetTempFileNameA(".", "fossil", 0, temp_filename) == 0) { + fprintf(stderr, "Error: Failed to create temporary file\n"); + return (fossil_fstream_t){NULL, ""}; + } +#else + char template[] = "fossil_tempfile_XXXXXX"; + int fd = mkstemp(template); + if (fd == -1) { + fprintf(stderr, "Error: Failed to create temporary file\n"); + return (fossil_fstream_t){NULL, ""}; + } + close(fd); // Close the file descriptor as it's no longer needed + strncpy(temp_filename, template, FOSSIL_BUFFER_MEDIUM); +#endif + + if (fossil_fstream_open(&temp_stream, temp_filename, "wb+") != FOSSIL_ERROR_OK) { + fprintf(stderr, "Error: Failed to open temporary file - %s\n", temp_filename); + return (fossil_fstream_t){NULL, ""}; + } + + return temp_stream; +} + int32_t fossil_fstream_is_writable(const char *filename) { #ifdef _WIN32 DWORD attrs = GetFileAttributesA(filename); diff --git a/code/tests/cases/test_stream.c b/code/tests/cases/test_stream.c index faec186..f178bca 100644 --- a/code/tests/cases/test_stream.c +++ b/code/tests/cases/test_stream.c @@ -45,6 +45,31 @@ FOSSIL_TEARDOWN(c_stream_suite) { // as samples for library usage. // * * * * * * * * * * * * * * * * * * * * * * * * +FOSSIL_TEST_CASE(c_test_stream_tempfile_creation) { + // Create a temporary file + fossil_fstream_t temp_stream = fossil_fstream_tempfile(); + + // Check if the temporary file is open + ASSUME_ITS_TRUE(fossil_fstream_is_open(&temp_stream)); + + // Close the temporary file + fossil_fstream_close(&temp_stream); +} + +FOSSIL_TEST_CASE(c_test_stream_tempfile_cleanup) { + // Create a temporary file + fossil_fstream_t temp_stream = fossil_fstream_tempfile(); + + // Get the temporary file name + const char *temp_filename = temp_stream.filename; + + // Close the temporary file + fossil_fstream_close(&temp_stream); + + // Verify the temporary file is deleted + ASSUME_NOT_EQUAL_I32(0, fossil_fstream_file_exists(temp_filename)); +} + FOSSIL_TEST_CASE(c_test_stream_let_write_and_read_file) { const char *filename = "testfile.txt"; const char *content = "This is a test."; @@ -238,6 +263,8 @@ FOSSIL_TEST_CASE(c_test_stream_setpos_and_getpos) { // * * * * * * * * * * * * * * * * * * * * * * * * FOSSIL_TEST_GROUP(c_file_tests) { + FOSSIL_TEST_ADD(c_stream_suite, c_test_stream_tempfile_creation); + FOSSIL_TEST_ADD(c_stream_suite, c_test_stream_tempfile_cleanup); FOSSIL_TEST_ADD(c_stream_suite, c_test_stream_let_write_and_read_file); FOSSIL_TEST_ADD(c_stream_suite, c_test_stream_let_open_and_close_file); FOSSIL_TEST_ADD(c_stream_suite, c_test_stream_multiple_files); diff --git a/code/tests/cases/test_stream.cpp b/code/tests/cases/test_stream.cpp index d682ea0..fd6de15 100644 --- a/code/tests/cases/test_stream.cpp +++ b/code/tests/cases/test_stream.cpp @@ -45,6 +45,31 @@ FOSSIL_TEARDOWN(cpp_stream_suite) { // as samples for library usage. // * * * * * * * * * * * * * * * * * * * * * * * * +FOSSIL_TEST_CASE(cpp_test_stream_tempfile_creation) { + // Create a temporary file + fossil_fstream_t temp_stream = fossil_fstream_tempfile(); + + // Check if the temporary file is open + ASSUME_ITS_TRUE(fossil_fstream_is_open(&temp_stream)); + + // Close the temporary file + fossil_fstream_close(&temp_stream); +} + +FOSSIL_TEST_CASE(cpp_test_stream_tempfile_cleanup) { + // Create a temporary file + fossil_fstream_t temp_stream = fossil_fstream_tempfile(); + + // Get the temporary file name + const char *temp_filename = temp_stream.filename; + + // Close the temporary file + fossil_fstream_close(&temp_stream); + + // Verify the temporary file is deleted + ASSUME_NOT_EQUAL_I32(0, fossil_fstream_file_exists(temp_filename)); +} + FOSSIL_TEST_CASE(cpp_test_stream_let_write_and_read_file) { const char *filename = "testfile.txt"; const char *content = "This is a test."; @@ -233,6 +258,31 @@ FOSSIL_TEST_CASE(cpp_test_stream_setpos_and_getpos) { fossil_fstream_close(&cpp_stream); } +FOSSIL_TEST_CASE(cpp_test_stream_class_tempfile_creation) { + // Create a temporary file using the class method + fossil_fstream_t temp_stream = fossil::io::Stream::tempfile(); + + // Check if the temporary file is open + ASSUME_ITS_TRUE(fossil::io::Stream::is_open(&temp_stream)); + + // Close the temporary file + fossil::io::Stream::close(&temp_stream); +} + +FOSSIL_TEST_CASE(cpp_test_stream_class_tempfile_cleanup) { + // Create a temporary file using the class method + fossil_fstream_t temp_stream = fossil::io::Stream::tempfile(); + + // Get the temporary file name + const char *temp_filename = temp_stream.filename; + + // Close the temporary file + fossil::io::Stream::close(&temp_stream); + + // Verify the temporary file is deleted + ASSUME_NOT_EQUAL_I32(0, fossil::io::Stream::file_exists(temp_filename)); +} + FOSSIL_TEST_CASE(cpp_test_stream_class_write_and_read_file) { const char *filename = "testfile.txt"; const char *content = "This is a test."; @@ -373,6 +423,8 @@ FOSSIL_TEST_CASE(cpp_test_stream_class_get_permissions) { // * * * * * * * * * * * * * * * * * * * * * * * * FOSSIL_TEST_GROUP(cpp_file_tests) { + FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_tempfile_creation); + FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_tempfile_cleanup); FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_let_write_and_read_file); FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_let_open_and_close_file); FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_multiple_files); @@ -387,6 +439,8 @@ FOSSIL_TEST_GROUP(cpp_file_tests) { FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_flush_file); FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_setpos_and_getpos); + FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_class_tempfile_creation); + FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_class_tempfile_cleanup); FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_class_write_and_read_file); FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_class_open_and_close_file); FOSSIL_TEST_ADD(cpp_stream_suite, cpp_test_stream_class_multiple_files);