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