Graphviz 13.0.0~dev.20241220.2304
Loading...
Searching...
No Matches
gv_fopen.c
Go to the documentation of this file.
1
3
4#include <assert.h>
5#include <errno.h>
6#include <fcntl.h>
7#include <stdbool.h>
8#include <stddef.h>
9#include <stdio.h>
10#include <string.h>
11#include <unistd.h>
12#include <util/gv_fopen.h>
13#include <util/streq.h>
14
16static FILE *fopen_(const char *filename, const char *mode) {
17 assert(filename != NULL);
18 assert(mode != NULL);
19#ifdef _MSC_VER
20 {
21 FILE *result = NULL;
22 if (fopen_s(&result, filename, mode) != 0) {
23 return NULL;
24 }
25 return result;
26 }
27#else
28 return fopen(filename, mode);
29#endif
30}
31
32FILE *gv_fopen(const char *filename, const char *mode) {
33 assert(filename != NULL);
34 assert(mode != NULL);
35 assert(streq(mode, "r") || streq(mode, "rb") || streq(mode, "w") ||
36 streq(mode, "wb"));
37
38 // does `fopen` support 'e' for setting close-on-exec?
39 bool have_e = false;
40
41 // does `fopen` support 'N' for setting close-on-exec?
42 bool have_n = false;
43
44#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__linux__) || \
45 defined(__NetBSD__) || defined(__OpenBSD__)
46 have_e = true;
47#endif
48
49#ifdef _MSC_VER
50 have_n = true;
51#endif
52
53 // Attempt 1: `fopen`+'e'
54 if (have_e) {
55 char mode_with_cloexec[4] = {0};
56 snprintf(mode_with_cloexec, sizeof(mode_with_cloexec), "%se", mode);
57 return fopen_(filename, mode_with_cloexec);
58 }
59
60 // Attempt 2: `fopen`+'N'
61 if (have_n) {
62 char mode_with_cloexec[4] = {0};
63 snprintf(mode_with_cloexec, sizeof(mode_with_cloexec), "%sN", mode);
64 return fopen_(filename, mode_with_cloexec);
65 }
66
67 // Attempt 3: `open`+`O_CLOEXEC`; `fdopen` (e.g. for macOS)
68#ifdef O_CLOEXEC
69 {
70 int flags = mode[0] == 'w' ? (O_WRONLY | O_CREAT | O_TRUNC) : O_RDONLY;
71#ifdef O_BINARY
72 if (strchr(mode, 'b') != NULL) {
73 flags |= O_BINARY;
74 }
75#endif
76#ifdef _O_BINARY
77 if (strchr(mode, 'b') != NULL) {
78 flags |= _O_BINARY;
79 }
80#endif
81 const int fd = open(filename, flags | O_CLOEXEC, 0666);
82 if (fd < 0) {
83 return NULL;
84 }
85
86 FILE *f = fdopen(fd, mode);
87 if (f == NULL) {
88 const int err = errno;
89 (void)close(fd);
90 errno = err;
91 }
92 return f;
93 }
94#endif
95
96 // Attempt 4: `fopen`; `fcntl`+`FD_CLOEXEC`
97 // We are on a platform that supported none of the above, so we need to
98 // fallback on an unmodified `fopen`. The platform either does not have a
99 // concept of close-on-exec or does not support a race-free way of achieving
100 // it.
101
102 FILE *f = fopen_(filename, mode);
103 if (f == NULL) {
104 return NULL;
105 }
106
107#ifdef FD_CLOEXEC
108 {
109 const int fd = fileno(f);
110 const int flags = fcntl(fd, F_GETFD);
111 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
112 const int err = errno;
113 (void)fclose(f);
114 errno = err;
115 return NULL;
116 }
117 }
118#endif
119
120 return f;
121}
mode
Definition cvtgxl.c:33
static char * err
Definition delaunay.c:708
static int flags
Definition gc.c:61
node NULL
Definition grammar.y:163
static FILE * fopen_(const char *filename, const char *mode)
platform abstraction over fopen
Definition gv_fopen.c:16
FILE * gv_fopen(const char *filename, const char *mode)
Definition gv_fopen.c:32
wrapper around fopen for internal library usage
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11