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