1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3 #include "bl_file.h"
4
5 #include <fcntl.h> /* fcntl() */
6 #include <sys/file.h> /* flock() */
7 #include <string.h> /* memcpy */
8 #include <errno.h>
9 #include <sys/stat.h> /* stat */
10
11 #include "bl_mem.h" /* malloc */
12 #include "bl_debug.h"
13
14 #define BUF_UNIT_SIZE 512
15
16 /* --- global functions --- */
17
bl_file_new(FILE * fp)18 bl_file_t *bl_file_new(FILE* fp) {
19 bl_file_t *file;
20
21 if ((file = malloc(sizeof(bl_file_t))) == NULL) {
22 return NULL;
23 }
24
25 file->file = fp;
26 file->buffer = NULL;
27 #ifndef HAVE_FGETLN
28 file->buf_size = 0;
29 #endif
30
31 return file;
32 }
33
bl_file_destroy(bl_file_t * file)34 void bl_file_destroy(bl_file_t *file) {
35 /* not fclose(file->fp) */
36
37 free(file->buffer);
38 free(file);
39 }
40
bl_file_open(const char * file_path,const char * mode)41 bl_file_t *bl_file_open(const char *file_path, const char *mode) {
42 FILE* fp;
43
44 if ((fp = fopen(file_path, mode)) == NULL) {
45 return NULL;
46 }
47
48 return bl_file_new(fp);
49 }
50
bl_file_close(bl_file_t * file)51 void bl_file_close(bl_file_t *file) {
52 fclose(file->file);
53 bl_file_destroy(file);
54 }
55
bl_fopen_with_mkdir(const char * file_path,const char * mode)56 FILE* bl_fopen_with_mkdir(const char *file_path, const char *mode) {
57 FILE* fp;
58 char *p;
59
60 if ((fp = fopen(file_path, mode))) {
61 return fp;
62 }
63
64 if ((p = alloca(strlen(file_path) + 1)) == NULL ||
65 !bl_mkdir_for_file(strcpy(p, file_path), 0700)) {
66 return NULL;
67 }
68
69 return fopen(file_path, mode);
70 }
71
72 #ifdef HAVE_FGETLN
73
bl_file_get_line(bl_file_t * from,size_t * len)74 char *bl_file_get_line(bl_file_t *from, size_t *len) {
75 char *line;
76
77 if ((line = fgetln(from->file, len)) == NULL) {
78 return NULL;
79 }
80
81 if (line[*len - 1] == '\n') {
82 if (*len > 1 && line[*len - 2] == '\r') {
83 (*len) -= 2; /* \r\n -> \0\n */
84 } else {
85 (*len)--; /* \n -> \0 */
86 }
87 } else {
88 void *p;
89
90 /* If 'line' doesn't end with '\n', append '\0' to it. */
91 if ((p = realloc(from->buffer, *len + 1)) == NULL) {
92 return NULL;
93 }
94
95 line = memcpy((from->buffer = p), line, *len);
96 }
97
98 line[*len] = '\0';
99
100 return line;
101 }
102
103 #else
104
105 /*
106 * This behaves like fgetln().
107 *
108 * This returns the pointer to the beginning of line , and it becomes invalid
109 * after the next bl_file_get_line() (whether successful or not) or as soon as
110 * bl_file_close() is executed.
111 */
bl_file_get_line(bl_file_t * from,size_t * len)112 char *bl_file_get_line(bl_file_t *from, size_t *len) {
113 size_t filled;
114 int c;
115
116 filled = 0;
117
118 if ((c = fgetc(from->file)) < 0) {
119 return NULL;
120 }
121
122 while (1) {
123 if (filled == from->buf_size) {
124 void *p;
125
126 if ((p = realloc(from->buffer, from->buf_size + BUF_UNIT_SIZE)) == NULL) {
127 return NULL;
128 }
129
130 from->buffer = p;
131 from->buf_size += BUF_UNIT_SIZE;
132 }
133
134 if (c == '\n') {
135 if (filled > 0 && from->buffer[filled - 1] == '\r') {
136 filled--;
137 }
138 break;
139 } else {
140 from->buffer[filled++] = c;
141 }
142
143 if ((c = fgetc(from->file)) < 0) {
144 break;
145 }
146 }
147
148 from->buffer[filled] = '\0';
149 *len = filled;
150
151 return from->buffer;
152 }
153
154 #endif
155
156 #if defined(HAVE_FLOCK) && defined(LOCK_EX) && defined(LOCK_UN)
157
bl_file_lock(int fd)158 int bl_file_lock(int fd) {
159 if (flock(fd, LOCK_EX) == -1) {
160 return 0;
161 } else {
162 return 1;
163 }
164 }
165
bl_file_unlock(int fd)166 int bl_file_unlock(int fd) {
167 if (flock(fd, LOCK_UN) == -1) {
168 return 0;
169 } else {
170 return 1;
171 }
172 }
173
174 #else
175
bl_file_lock(int fd)176 int bl_file_lock(int fd) { return 0; }
177
bl_file_unlock(int fd)178 int bl_file_unlock(int fd) { return 0; }
179
180 #endif
181
182 #ifdef F_GETFD
183
bl_file_set_cloexec(int fd)184 int bl_file_set_cloexec(int fd) {
185 int old_flags;
186
187 old_flags = fcntl(fd, F_GETFD);
188 if (old_flags == -1) {
189 return 0;
190 }
191
192 if (!(old_flags & FD_CLOEXEC) && (fcntl(fd, F_SETFD, old_flags | FD_CLOEXEC) == -1)) {
193 return 0;
194 }
195 return 1;
196 }
197
bl_file_unset_cloexec(int fd)198 int bl_file_unset_cloexec(int fd) {
199 int old_flags;
200
201 old_flags = fcntl(fd, F_GETFD);
202 if (old_flags == -1) {
203 return 0;
204 }
205 if ((old_flags & FD_CLOEXEC) && (fcntl(fd, F_SETFD, old_flags & (~FD_CLOEXEC)) == -1)) {
206 return 0;
207 }
208 return 1;
209 }
210
211 #else /* F_GETFD */
212
bl_file_set_cloexec(int fd)213 int bl_file_set_cloexec(int fd) {
214 /* do nothing */
215
216 return 0;
217 }
218
bl_file_unset_cloexec(int fd)219 int bl_file_unset_cloexec(int fd) {
220 /* do nothing */
221
222 return 0;
223 }
224
225 #endif
226
227 /*
228 * /a/b/c => mkdir /a ; mkdir /a/b
229 * /a/b/c/ => mkdir /a ; mkdir /a/b ; mkdir /a/b/c
230 * /a => do nothing
231 */
bl_mkdir_for_file(char * file_path,mode_t dir_mode)232 int bl_mkdir_for_file(char *file_path, /* Not const. Don't specify read only data. */
233 mode_t dir_mode) {
234 char *p;
235
236 p = file_path + 1;
237 while (*p) {
238 if (*p == '/'
239 #ifdef USE_WIN32API
240 || *p == '\\'
241 #endif
242 ) {
243 struct stat s;
244 char c;
245
246 c = *p; /* save */
247
248 *p = '\0';
249 if (stat(file_path, &s) != 0) {
250 if (errno == ENOENT &&
251 #ifdef USE_WIN32API
252 mkdir(file_path) != 0
253 #else
254 mkdir(file_path, dir_mode) != 0
255 #endif
256 ) {
257 bl_msg_printf("Failed to mkdir %s\n", file_path);
258
259 *p = c; /* restore */
260
261 return 0;
262 }
263 }
264
265 *p = c; /* restore */
266 }
267
268 p++;
269 }
270
271 return 1;
272 }
273