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