1 /*
2 ** file.c - File class
3 */
4
5 #include "mruby.h"
6 #include "mruby/class.h"
7 #include "mruby/data.h"
8 #include "mruby/string.h"
9 #include "mruby/ext/io.h"
10
11 #if MRUBY_RELEASE_NO < 10000
12 #include "error.h"
13 #else
14 #include "mruby/error.h"
15 #endif
16
17 #include <sys/types.h>
18 #include <sys/stat.h>
19
20 #if defined(_WIN32) || defined(_WIN64)
21 #define LSTAT stat
22 #include <winsock.h>
23 #else
24 #define LSTAT lstat
25 #include <sys/file.h>
26 #include <sys/param.h>
27 #include <sys/wait.h>
28 #include <libgen.h>
29 #include <pwd.h>
30 #include <unistd.h>
31 #endif
32
33 #include <fcntl.h>
34
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 extern struct mrb_data_type mrb_io_type;
41
42 static int
mrb_stat0(mrb_state * mrb,mrb_value obj,struct stat * st,int do_lstat)43 mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
44 {
45 mrb_value tmp;
46 mrb_value io_klass, str_klass;
47
48 io_klass = mrb_obj_value(mrb_class_get(mrb, "IO"));
49 str_klass = mrb_obj_value(mrb_class_get(mrb, "String"));
50
51 tmp = mrb_funcall(mrb, obj, "is_a?", 1, io_klass);
52 if (mrb_test(tmp)) {
53 struct mrb_io *fptr;
54 fptr = (struct mrb_io *)mrb_get_datatype(mrb, obj, &mrb_io_type);
55
56 if (fptr && fptr->fd >= 0) {
57 return fstat(fptr->fd, st);
58 }
59
60 mrb_raise(mrb, E_IO_ERROR, "closed stream");
61 return -1;
62 }
63
64 tmp = mrb_funcall(mrb, obj, "is_a?", 1, str_klass);
65 if (mrb_test(tmp)) {
66 char *path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, obj), -1);
67 int ret;
68 if (do_lstat) {
69 ret = LSTAT(path, st);
70 } else {
71 ret = stat(path, st);
72 }
73 mrb_locale_free(path);
74 return ret;
75 }
76
77 return -1;
78 }
79
80 static int
mrb_stat(mrb_state * mrb,mrb_value obj,struct stat * st)81 mrb_stat(mrb_state *mrb, mrb_value obj, struct stat *st)
82 {
83 return mrb_stat0(mrb, obj, st, 0);
84 }
85
86 static int
mrb_lstat(mrb_state * mrb,mrb_value obj,struct stat * st)87 mrb_lstat(mrb_state *mrb, mrb_value obj, struct stat *st)
88 {
89 return mrb_stat0(mrb, obj, st, 1);
90 }
91
92 /*
93 * Document-method: directory?
94 *
95 * call-seq:
96 * File.directory?(file_name) -> true or false
97 *
98 * Returns <code>true</code> if the named file is a directory,
99 * or a symlink that points at a directory, and <code>false</code>
100 * otherwise.
101 *
102 * File.directory?(".")
103 */
104
105 mrb_value
mrb_filetest_s_directory_p(mrb_state * mrb,mrb_value klass)106 mrb_filetest_s_directory_p(mrb_state *mrb, mrb_value klass)
107 {
108 #ifndef S_ISDIR
109 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
110 #endif
111
112 struct stat st;
113 mrb_value obj;
114
115 mrb_get_args(mrb, "o", &obj);
116
117 if (mrb_stat(mrb, obj, &st) < 0)
118 return mrb_false_value();
119 if (S_ISDIR(st.st_mode))
120 return mrb_true_value();
121
122 return mrb_false_value();
123 }
124
125 /*
126 * call-seq:
127 * File.pipe?(file_name) -> true or false
128 *
129 * Returns <code>true</code> if the named file is a pipe.
130 */
131
132 mrb_value
mrb_filetest_s_pipe_p(mrb_state * mrb,mrb_value klass)133 mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass)
134 {
135 #if defined(_WIN32) || defined(_WIN64)
136 mrb_raise(mrb, E_NOTIMP_ERROR, "pipe is not supported on this platform");
137 #else
138 #ifdef S_IFIFO
139 # ifndef S_ISFIFO
140 # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
141 # endif
142
143 struct stat st;
144 mrb_value obj;
145
146 mrb_get_args(mrb, "o", &obj);
147
148 if (mrb_stat(mrb, obj, &st) < 0)
149 return mrb_false_value();
150 if (S_ISFIFO(st.st_mode))
151 return mrb_true_value();
152
153 #endif
154 return mrb_false_value();
155 #endif
156 }
157
158 /*
159 * call-seq:
160 * File.symlink?(file_name) -> true or false
161 *
162 * Returns <code>true</code> if the named file is a symbolic link.
163 */
164
165 mrb_value
mrb_filetest_s_symlink_p(mrb_state * mrb,mrb_value klass)166 mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
167 {
168 #if defined(_WIN32) || defined(_WIN64)
169 mrb_raise(mrb, E_NOTIMP_ERROR, "symlink is not supported on this platform");
170 #else
171 #ifndef S_ISLNK
172 # ifdef _S_ISLNK
173 # define S_ISLNK(m) _S_ISLNK(m)
174 # else
175 # ifdef _S_IFLNK
176 # define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK)
177 # else
178 # ifdef S_IFLNK
179 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
180 # endif
181 # endif
182 # endif
183 #endif
184
185 #ifdef S_ISLNK
186 struct stat st;
187 mrb_value obj;
188
189 mrb_get_args(mrb, "o", &obj);
190
191 if (mrb_lstat(mrb, obj, &st) == -1)
192 return mrb_false_value();
193 if (S_ISLNK(st.st_mode))
194 return mrb_true_value();
195 #endif
196
197 return mrb_false_value();
198 #endif
199 }
200
201 /*
202 * call-seq:
203 * File.socket?(file_name) -> true or false
204 *
205 * Returns <code>true</code> if the named file is a socket.
206 */
207
208 mrb_value
mrb_filetest_s_socket_p(mrb_state * mrb,mrb_value klass)209 mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
210 {
211 #if defined(_WIN32) || defined(_WIN64)
212 mrb_raise(mrb, E_NOTIMP_ERROR, "socket is not supported on this platform");
213 #else
214 #ifndef S_ISSOCK
215 # ifdef _S_ISSOCK
216 # define S_ISSOCK(m) _S_ISSOCK(m)
217 # else
218 # ifdef _S_IFSOCK
219 # define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK)
220 # else
221 # ifdef S_IFSOCK
222 # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
223 # endif
224 # endif
225 # endif
226 #endif
227
228 #ifdef S_ISSOCK
229 struct stat st;
230 mrb_value obj;
231
232 mrb_get_args(mrb, "o", &obj);
233
234 if (mrb_stat(mrb, obj, &st) < 0)
235 return mrb_false_value();
236 if (S_ISSOCK(st.st_mode))
237 return mrb_true_value();
238 #endif
239
240 return mrb_false_value();
241 #endif
242 }
243
244 /*
245 * call-seq:
246 * File.exist?(file_name) -> true or false
247 * File.exists?(file_name) -> true or false
248 *
249 * Return <code>true</code> if the named file exists.
250 */
251
252 mrb_value
mrb_filetest_s_exist_p(mrb_state * mrb,mrb_value klass)253 mrb_filetest_s_exist_p(mrb_state *mrb, mrb_value klass)
254 {
255 struct stat st;
256 mrb_value obj;
257
258 mrb_get_args(mrb, "o", &obj);
259 if (mrb_stat(mrb, obj, &st) < 0)
260 return mrb_false_value();
261
262 return mrb_true_value();
263 }
264
265 /*
266 * call-seq:
267 * File.file?(file_name) -> true or false
268 *
269 * Returns <code>true</code> if the named file exists and is a
270 * regular file.
271 */
272
273 mrb_value
mrb_filetest_s_file_p(mrb_state * mrb,mrb_value klass)274 mrb_filetest_s_file_p(mrb_state *mrb, mrb_value klass)
275 {
276 #ifndef S_ISREG
277 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
278 #endif
279
280 struct stat st;
281 mrb_value obj;
282
283 mrb_get_args(mrb, "o", &obj);
284
285 if (mrb_stat(mrb, obj, &st) < 0)
286 return mrb_false_value();
287 if (S_ISREG(st.st_mode))
288 return mrb_true_value();
289
290 return mrb_false_value();
291 }
292
293 /*
294 * call-seq:
295 * File.zero?(file_name) -> true or false
296 *
297 * Returns <code>true</code> if the named file exists and has
298 * a zero size.
299 */
300
301 mrb_value
mrb_filetest_s_zero_p(mrb_state * mrb,mrb_value klass)302 mrb_filetest_s_zero_p(mrb_state *mrb, mrb_value klass)
303 {
304 struct stat st;
305 mrb_value obj;
306
307 mrb_get_args(mrb, "o", &obj);
308
309 if (mrb_stat(mrb, obj, &st) < 0)
310 return mrb_false_value();
311 if (st.st_size == 0)
312 return mrb_true_value();
313
314 return mrb_false_value();
315 }
316
317 /*
318 * call-seq:
319 * File.size(file_name) -> integer
320 *
321 * Returns the size of <code>file_name</code>.
322 *
323 * _file_name_ can be an IO object.
324 */
325
326 mrb_value
mrb_filetest_s_size(mrb_state * mrb,mrb_value klass)327 mrb_filetest_s_size(mrb_state *mrb, mrb_value klass)
328 {
329 struct stat st;
330 mrb_value obj;
331
332 mrb_get_args(mrb, "o", &obj);
333
334 if (mrb_stat(mrb, obj, &st) < 0)
335 mrb_sys_fail(mrb, "mrb_stat");
336
337 return mrb_fixnum_value(st.st_size);
338 }
339
340 /*
341 * call-seq:
342 * File.size?(file_name) -> Integer or nil
343 *
344 * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the
345 * file otherwise.
346 */
347
348 mrb_value
mrb_filetest_s_size_p(mrb_state * mrb,mrb_value klass)349 mrb_filetest_s_size_p(mrb_state *mrb, mrb_value klass)
350 {
351 struct stat st;
352 mrb_value obj;
353
354 mrb_get_args(mrb, "o", &obj);
355
356 if (mrb_stat(mrb, obj, &st) < 0)
357 return mrb_nil_value();
358 if (st.st_size == 0)
359 return mrb_nil_value();
360
361 return mrb_fixnum_value(st.st_size);
362 }
363
364 void
mrb_init_file_test(mrb_state * mrb)365 mrb_init_file_test(mrb_state *mrb)
366 {
367 struct RClass *f;
368
369 f = mrb_define_class(mrb, "FileTest", mrb->object_class);
370
371 mrb_define_class_method(mrb, f, "directory?", mrb_filetest_s_directory_p, MRB_ARGS_REQ(1));
372 mrb_define_class_method(mrb, f, "exist?", mrb_filetest_s_exist_p, MRB_ARGS_REQ(1));
373 mrb_define_class_method(mrb, f, "exists?", mrb_filetest_s_exist_p, MRB_ARGS_REQ(1));
374 mrb_define_class_method(mrb, f, "file?", mrb_filetest_s_file_p, MRB_ARGS_REQ(1));
375 mrb_define_class_method(mrb, f, "pipe?", mrb_filetest_s_pipe_p, MRB_ARGS_REQ(1));
376 mrb_define_class_method(mrb, f, "size", mrb_filetest_s_size, MRB_ARGS_REQ(1));
377 mrb_define_class_method(mrb, f, "size?", mrb_filetest_s_size_p, MRB_ARGS_REQ(1));
378 mrb_define_class_method(mrb, f, "socket?", mrb_filetest_s_socket_p, MRB_ARGS_REQ(1));
379 mrb_define_class_method(mrb, f, "symlink?", mrb_filetest_s_symlink_p, MRB_ARGS_REQ(1));
380 mrb_define_class_method(mrb, f, "zero?", mrb_filetest_s_zero_p, MRB_ARGS_REQ(1));
381 }
382