1 /*
2  * $Id: stat.xs,v 1.31 2012/10/23 03:36:24 dankogai Exp $
3  */
4 
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <sys/time.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 
14 /*
15  * Perl prior to 5.6.0 lacks newSVuv()
16  * Though perl 5.00503 does have sv_setuv() statcache uses IV instead.
17  * so we simply define newSVuv newSViv (Ugh!)
18  * I thank Sergey Skvortsov <skv@protey.ru> for reporting this problem.
19  *
20  * Perl prior to 5.6.0 lacks PERL_API_VERSION macro so we use it
21  * to tell the difference
22  */
23 
24 #ifndef PERL_API_VERSION
25 #define newSVuv newSViv
26 #endif
27 
28 static int
not_here(char * s)29 not_here(char *s)
30 {
31     croak("%s not implemented on this architecture", s);
32     return -1;
33 }
34 
35 static int
setbang(int err)36 setbang(int err)
37 {
38     SV* bang = perl_get_sv("!", 1);
39     if (err){
40         sv_setpv(bang, strerror(errno));
41 	sv_setiv(bang, errno << 8);
42     }else{
43         sv_setpv(bang, "");
44 	sv_setiv(bang, 0);
45     }
46     return err;
47 }
48 
49 #define NUMSTATMEM 18
50 
51 static SV *
st2aref(struct stat * st)52 st2aref(struct stat *st){
53     SV* retval;
54     SV* sva[NUMSTATMEM];
55     int i;
56 
57     /* same as CORE::stat */
58 
59     sva[0]  = sv_2mortal(newSViv(PL_statcache.st_dev     = st->st_dev));
60     sva[1]  = sv_2mortal(newSViv(PL_statcache.st_ino     = st->st_ino));
61     sva[2]  = sv_2mortal(newSVuv(PL_statcache.st_mode    = st->st_mode));
62     sva[3]  = sv_2mortal(newSVuv(PL_statcache.st_nlink   = st->st_nlink));
63     sva[4]  = sv_2mortal(newSVuv(PL_statcache.st_uid     = st->st_uid));
64     sva[5]  = sv_2mortal(newSVuv(PL_statcache.st_gid     = st->st_gid));
65     sva[6]  = sv_2mortal(newSViv(PL_statcache.st_rdev    = st->st_rdev));
66     sva[7]  = sv_2mortal(newSVnv(PL_statcache.st_size    = st->st_size));
67     sva[8]  = sv_2mortal(newSViv(PL_statcache.st_atime   = st->st_atime));
68     sva[9]  = sv_2mortal(newSViv(PL_statcache.st_mtime   = st->st_mtime));
69     sva[10] = sv_2mortal(newSViv(PL_statcache.st_ctime   = st->st_ctime));
70     sva[11] = sv_2mortal(newSVuv(PL_statcache.st_blksize = st->st_blksize));
71     sva[12] = sv_2mortal(newSVuv(PL_statcache.st_blocks  = st->st_blocks));
72 
73 
74     /* BSD-specific */
75 
76     sva[13] = sv_2mortal(newSViv(st->st_atimespec.tv_nsec));
77     sva[14] = sv_2mortal(newSViv(st->st_mtimespec.tv_nsec));
78     sva[15] = sv_2mortal(newSViv(st->st_ctimespec.tv_nsec));
79     sva[16] = sv_2mortal(newSVuv(st->st_flags));
80     sva[17] = sv_2mortal(newSVuv(st->st_gen));
81 
82     retval = newRV_noinc((SV *)av_make(NUMSTATMEM, sva));
83     return retval;
84 }
85 
86 static SV *
xs_stat(char * path)87 xs_stat(char *path){
88     struct stat st;
89     int err = stat(path, &st);
90     if (setbang(err)){
91 	return &PL_sv_undef;
92     }else{
93 	PL_laststype = OP_STAT;
94 	return st2aref(&st);
95     }
96 }
97 
98 static SV *
xs_lstat(char * path)99 xs_lstat(char *path){
100     struct stat st;
101     int err = lstat(path, &st);
102     if (setbang(err)){
103 	return &PL_sv_undef;
104     }else{
105         PL_laststype = OP_LSTAT;
106 	return st2aref(&st);
107     }
108 }
109 
110 static SV *
xs_fstat(int fd,int waslstat)111 xs_fstat(int fd, int waslstat){
112     struct stat st;
113     int err = fstat(fd, &st);
114     if (setbang(err)){
115 	return &PL_sv_undef;
116     }else{
117         PL_laststype = waslstat ? OP_LSTAT : OP_STAT;
118 	return st2aref(&st);
119     }
120 }
121 
122 static int
xs_chflags(char * path,int flags)123 xs_chflags(char *path, int flags){
124     int err = chflags(path, flags);
125     return setbang(err);
126 }
127 
128 static int
xs_utimes(double atime,double mtime,char * path)129 xs_utimes(double atime, double mtime, char *path){
130     struct timeval times[2];
131     times[0].tv_sec = (int)atime;
132     times[0].tv_usec = (int)((atime - times[0].tv_sec) * 1e6);
133     times[1].tv_sec = (int)mtime;
134     times[1].tv_usec = (int)((mtime - times[1].tv_sec) * 1e6);
135     int err = utimes(path, times);
136     return setbang(err);
137 }
138 
139 static int
xs_lutimes(double atime,double mtime,char * path)140 xs_lutimes(double atime, double mtime, char *path){
141 #ifdef __OpenBSD__
142     struct timespec times[2];
143     times[0].tv_sec = (int)atime;
144     times[0].tv_nsec = (int)((atime - times[0].tv_sec) * 1e9);
145     times[1].tv_sec = (int)mtime;
146     times[1].tv_nsec = (int)((mtime - times[1].tv_sec) * 1e9);
147     int err = utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW);
148 #else
149     struct timeval times[2];
150     times[0].tv_sec = (int)atime;
151     times[0].tv_usec = (int)((atime - times[0].tv_sec) * 1e6);
152     times[1].tv_sec = (int)mtime;
153     times[1].tv_usec = (int)((mtime - times[1].tv_sec) * 1e6);
154     int err = lutimes(path, times);
155 #endif
156     return setbang(err);
157 }
158 
159 static int
xs_futimes(double atime,double mtime,int fd)160 xs_futimes(double atime, double mtime, int fd){
161     struct timeval times[2];
162     times[0].tv_sec = (int)atime;
163     times[0].tv_usec = (int)((atime - times[0].tv_sec) * 1e6);
164     times[1].tv_sec = (int)mtime;
165     times[1].tv_usec = (int)((mtime - times[1].tv_sec) * 1e6);
166     int err = futimes(fd, times);
167     return setbang(err);
168 }
169 
170 /* */
171 
172 MODULE = BSD::stat		PACKAGE = BSD::stat
173 
174 PROTOTYPES: ENABLE
175 
176 SV *
177 xs_stat(path)
178     char * path;
179     CODE:
180 	RETVAL = xs_stat(path);
181     OUTPUT:
182 	RETVAL
183 
184 SV *
185 xs_lstat(path)
186     char * path;
187     CODE:
188 	RETVAL = xs_lstat(path);
189     OUTPUT:
190 	RETVAL
191 
192 SV *
193 xs_fstat(fd, waslstat)
194     int    fd;
195     int    waslstat;
196     CODE:
197 	RETVAL = xs_fstat(fd, waslstat);
198     OUTPUT:
199 	RETVAL
200 
201 int
202 xs_chflags(path, flags)
203     char * path;
204     int    flags;
205     CODE:
206 	RETVAL = xs_chflags(path, flags);
207     OUTPUT:
208 	RETVAL
209 
210 int
211 xs_utimes(atime, mtime, path)
212     double atime;
213     double mtime;
214     char * path;
215     CODE:
216         RETVAL = xs_utimes(atime, mtime, path);
217     OUTPUT:
218 	RETVAL
219 
220 int
221 xs_lutimes(atime, mtime, path)
222     double atime;
223     double mtime;
224     char * path;
225     CODE:
226         RETVAL = xs_lutimes(atime, mtime, path);
227     OUTPUT:
228 	RETVAL
229 
230 int
231 xs_futimes(atime, mtime, fd)
232     double atime;
233     double mtime;
234     int fd;
235     CODE:
236         RETVAL = xs_futimes(atime, mtime, fd);
237     OUTPUT:
238 	RETVAL
239