]> granicus.if.org Git - re2c/commitdiff
Write output to temporary file and then rename it to output file atomically.
authorUlya Trofimovich <skvadrik@gmail.com>
Tue, 5 Mar 2019 17:24:34 +0000 (17:24 +0000)
committerUlya Trofimovich <skvadrik@gmail.com>
Tue, 5 Mar 2019 17:48:46 +0000 (17:48 +0000)
This is more conveniet for build systems: if code generation is interrupted
for some reason, we don't end up with half-generated or empty output file.

Note that re2c there still can be discrepancy between the generated header
file and output file.

re2c/Makefile.am
re2c/src/codegen/output.cc
re2c/src/util/temp_file.cc [new file with mode: 0644]
re2c/src/util/temp_file.h [new file with mode: 0644]

index 9d11ad211d8cb5b0184023519141c15998ea24ad..0231ab2764057d165046753fba85762e5e07c6fd 100644 (file)
@@ -75,6 +75,7 @@ re2c_HDR = \
        src/util/smart_ptr.h \
        src/util/static_assert.h \
        src/util/string_utils.h \
+       src/util/temp_file.h \
        src/util/u32lim.h \
        src/util/uniq_vector.h \
        src/util/wrap_iter.h
@@ -149,6 +150,7 @@ re2c_SRC = \
        src/parse/validate.cc \
        src/util/get_dir.cc \
        src/util/s_to_n32_unsafe.cc \
+       src/util/temp_file.cc \
        src/util/range.cc
 re2c_SOURCES = \
        src/main.cc \
index df9df84cdb78b7e72a2f44d00700ddb50fcee810..7b33d38045bb3494c1022263551d0db64b1755c4 100644 (file)
@@ -11,6 +11,7 @@
 #include "src/options/opt.h"
 #include "src/encoding/enc.h"
 #include "src/util/string_utils.h"
+#include "src/util/temp_file.h"
 #include "src/util/uniq_vector.h"
 
 namespace re2c
@@ -440,17 +441,19 @@ bool Output::emit_blocks(const std::string &fname, blocks_t &blocks,
     const std::set<std::string> &global_mtags)
 {
     FILE *file = NULL;
-    std::string filename = fname;
+    bool temp = false;
+    std::string filename = fname, tempname = fname;
+
     if (filename.empty()) {
         filename = "<stdout>";
         file = stdout;
     }
-    else {
-        file = fopen(filename.c_str(), "w");
-        if (!file) {
-            error("cannot open output file: %s", filename.c_str());
-            return false;
-        }
+    else if ((file = temp_file(tempname))) {
+        temp = true;
+    }
+    else if (!(file = fopen(filename.c_str(), "w"))) {
+        error("cannot open output file %s", filename.c_str());
+        return false;
     }
 
     fix_first_block_opts(blocks);
@@ -547,6 +550,12 @@ bool Output::emit_blocks(const std::string &fname, blocks_t &blocks,
     }
 
     fclose(file);
+    if (temp && rename(tempname.c_str(), fname.c_str()) != 0) {
+        error("cannot rename temporary file %s to output file %s"
+            , tempname.c_str(), fname.c_str());
+        return false;
+    }
+
     return true;
 }
 
diff --git a/re2c/src/util/temp_file.cc b/re2c/src/util/temp_file.cc
new file mode 100644 (file)
index 0000000..7141df9
--- /dev/null
@@ -0,0 +1,56 @@
+#include <time.h>
+#include "src/msg/msg.h"
+#include "src/util/temp_file.h"
+
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+
+// MSVC
+#include <io.h>
+#define OPEN   _open
+#define FDOPEN _fdopen
+#define FLAGS  _O_CREAT | _O_EXCL | _O_RDWR
+#define MODE   _S_IREAD | _S_IWRITE
+
+#else
+
+// POSIX
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#define OPEN   open
+#define FDOPEN fdopen
+#define FLAGS  O_CREAT | O_EXCL | O_RDWR
+#define MODE   S_IRUSR | S_IWUSR
+
+#endif
+
+namespace re2c {
+
+FILE *temp_file(std::string &fname)
+{
+    // append "random enough" suffix to filename
+    const time_t t = time(NULL);
+    char buffer[20];
+    strftime(buffer, sizeof(buffer), ".tmp.%Y%m%d%H%M%S", localtime(&t));
+    fname += buffer;
+
+    // open file for writing, unless it exists already
+    FILE *f = NULL;
+    int fd = OPEN(fname.c_str(), FLAGS, MODE);
+    if (fd != -1) {
+        f = FDOPEN(fd, "w");
+    }
+
+    // we don't try too hard
+    if (!f) {
+        error("cannot open temporary file %s", fname.c_str());
+    }
+    return f;
+}
+
+} // namespace re2c
+
+#undef OPEN
+#undef FDOPEN
+#undef FLAGS
diff --git a/re2c/src/util/temp_file.h b/re2c/src/util/temp_file.h
new file mode 100644 (file)
index 0000000..9b2e6d6
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _RE2C_UTIL_TEMP_FILE_
+#define _RE2C_UTIL_TEMP_FILE_
+
+#include <stdio.h>
+#include <string>
+
+
+namespace re2c {
+
+FILE *temp_file(std::string &fname);
+
+} // namespace re2c
+
+#endif // _RE2C_UTIL_TEMP_FILE_