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