rosprite: Cleanup and modernization
[deark.git] / src / deark-unix.c
blob820fe0d11fa1e1c6f767942e80521f73293de1be
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Functions specific to Unix and other non-Windows builds
7 #define DE_NOT_IN_MODULE
8 #include "deark-config.h"
10 #ifdef DE_UNIX
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <sys/time.h>
15 #include <unistd.h>
16 #include <utime.h>
17 #include <errno.h>
19 // This file is overloaded, in that it contains functions intended to only
20 // be used internally, as well as functions intended only for the
21 // command-line utility. That's why we need both deark-user.h and
22 // deark-private.h.
23 #include "deark-private.h"
24 #include "deark-user.h"
26 // Unix-specific contextual data, not currently used.
27 struct de_platform_data {
28 int reserved;
31 void de_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap)
33 vsnprintf(buf,buflen,fmt,ap);
34 buf[buflen-1]='\0';
37 i64 de_strtoll(const char *string, char **endptr, int base)
39 return strtoll(string, endptr, base);
42 static FILE* de_fopen(deark *c, const char *fn, const char *mode,
43 char *errmsg, size_t errmsg_len)
45 FILE *f;
46 int errcode;
48 f = fopen(fn, mode);
49 if(!f) {
50 errcode = errno;
51 de_strlcpy(errmsg, strerror(errcode), errmsg_len);
53 return f;
56 // Test if the file seems suitable for reading, and return its size.
57 // returned flags: 0x1 = file is a FIFO (named pipe)
58 static int de_examine_file_by_fd(deark *c, int fd, i64 *len,
59 char *errmsg, size_t errmsg_len, unsigned int *returned_flags)
61 struct stat stbuf;
63 *returned_flags = 0;
64 de_zeromem(&stbuf, sizeof(struct stat));
66 if(0 != fstat(fd, &stbuf)) {
67 de_strlcpy(errmsg, strerror(errno), errmsg_len);
68 return 0;
71 if(S_ISFIFO(stbuf.st_mode)) {
72 *returned_flags |= 0x1;
73 *len = 0;
74 return 1;
76 else if(!S_ISREG(stbuf.st_mode)) {
77 de_strlcpy(errmsg, "Not a regular file", errmsg_len);
78 return 0;
81 *len = (i64)stbuf.st_size;
82 return 1;
85 FILE* de_fopen_for_read(deark *c, const char *fn, i64 *len,
86 char *errmsg, size_t errmsg_len, unsigned int *returned_flags)
88 int ret;
89 FILE *f;
91 f = de_fopen(c, fn, "rb", errmsg, errmsg_len);
92 if(!f) {
93 return NULL;
96 ret = de_examine_file_by_fd(c, fileno(f), len, errmsg, errmsg_len,
97 returned_flags);
98 if(!ret) {
99 de_fclose(f);
100 return NULL;
103 return f;
106 // flags: 0x1 = append instead of overwriting
107 FILE* de_fopen_for_write(deark *c, const char *fn,
108 char *errmsg, size_t errmsg_len, int overwrite_mode,
109 unsigned int flags)
111 const char *mode;
113 // A simple check to make it harder to accidentally overwrite the input
114 // file. (But it can easily be defeated.)
115 // TODO?: Make this more robust.
116 if(c->input_filename && !de_strcmp(fn, c->input_filename)) {
117 de_err(c, "Refusing to write to %s: Same as input filename", fn);
118 de_fatalerror(c);
119 de_strlcpy(errmsg, "", errmsg_len);
120 return NULL;
123 if(overwrite_mode!=DE_OVERWRITEMODE_STANDARD) {
124 // Check if the file already exists.
125 struct stat stbuf;
126 int s_ret;
128 de_zeromem(&stbuf, sizeof(struct stat));
129 #if DE_USE_LSTAT
130 s_ret = lstat(fn, &stbuf);
131 #else
132 s_ret = stat(fn, &stbuf);
133 #endif
135 // s_ret==0 = "success"
136 if(s_ret==0 && overwrite_mode==DE_OVERWRITEMODE_NEVER) {
137 de_strlcpy(errmsg, "Output file already exists", errmsg_len);
138 return NULL;
141 if(s_ret==0 && overwrite_mode==DE_OVERWRITEMODE_DEFAULT) {
142 if(S_ISLNK(stbuf.st_mode)) {
143 de_strlcpy(errmsg, "Output file is a symlink", errmsg_len);
144 return NULL;
149 mode = (flags&0x1) ? "ab" : "wb";
150 return de_fopen(c, fn, mode, errmsg, errmsg_len);
153 int de_fseek(FILE *fp, i64 offs, int whence)
155 int ret;
157 #if DE_USE_FSEEKO
158 ret = fseeko(fp, (off_t)offs, whence);
159 #else
160 ret = fseek(fp, (long)offs, whence);
161 #endif
162 return ret;
165 i64 de_ftell(FILE *fp)
167 i64 ret;
169 #if DE_USE_FSEEKO
170 ret = (i64)ftello(fp);
171 #else
172 ret = (i64)ftell(fp);
173 #endif
174 return ret;
177 int de_fclose(FILE *fp)
179 return fclose(fp);
182 struct upd_attr_ctx {
183 int tried_stat;
184 int stat_ret;
185 struct stat stbuf;
188 // If, based on f->mode_flags, we know that the file should be executable or
189 // non-executable, make it so.
190 static void update_file_perms(struct upd_attr_ctx *uactx, dbuf *f)
192 mode_t oldmode, newmode;
194 if(f->btype!=DBUF_TYPE_OFILE) return;
195 if(!f->fi_copy) return;
196 if(!f->name) return;
197 if(!(f->fi_copy->mode_flags&DE_MODEFLAG_NONEXE) &&!(f->fi_copy->mode_flags&DE_MODEFLAG_EXE)) return;
199 uactx->stat_ret = stat(f->name, &uactx->stbuf);
200 uactx->tried_stat = 1;
201 if(uactx->stat_ret != 0) {
202 return;
205 oldmode = uactx->stbuf.st_mode;
206 newmode = oldmode;
208 // Start by turning off the executable bits in the tentative new mode.
209 newmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
211 if(f->fi_copy->mode_flags&DE_MODEFLAG_EXE) {
212 // Set an Executable bit if its corresponding Read bit is set.
213 if(oldmode & S_IRUSR) newmode |= S_IXUSR;
214 if(oldmode & S_IRGRP) newmode |= S_IXGRP;
215 if(oldmode & S_IROTH) newmode |= S_IXOTH;
218 if(newmode != oldmode) {
219 de_dbg2(f->c, "changing file mode from %03o to %03o",
220 (unsigned int)oldmode, (unsigned int)newmode);
221 chmod(f->name, newmode);
225 static void update_file_time(struct upd_attr_ctx *uactx, dbuf *f)
227 const struct de_timestamp *ts;
228 struct timeval times[2];
230 if(f->btype!=DBUF_TYPE_OFILE) return;
231 if(!f->fi_copy) return;
232 ts = &f->fi_copy->timestamp[DE_TIMESTAMPIDX_MODIFY];
233 if(!ts->is_valid) return;
234 if(!f->name) return;
236 if(!uactx->tried_stat) {
237 uactx->stat_ret = stat(f->name, &uactx->stbuf);
238 uactx->tried_stat = 1;
241 // I know that this code is not Y2038-compliant, if sizeof(time_t)==4.
242 // But it's not likely to be a serious problem, and I'd rather not replace
243 // it with code that's less portable.
245 de_zeromem(&times, sizeof(times));
246 // times[0] = access time
247 // times[1] = mod time
248 times[1].tv_sec = (long)de_timestamp_to_unix_time(ts);
249 if(ts->precision>DE_TSPREC_1SEC) {
250 times[1].tv_usec = (long)(de_timestamp_get_subsec(ts)/10);
253 // We don't want to set the access time, but unfortunately the utimes()
254 // function forces us to.
255 if(uactx->tried_stat && (uactx->stat_ret==0)) {
256 // If we have the file's current access time recorded, use that.
257 // (Though this may lose precision. Which could be fixed at the cost of
258 // portability.)
259 times[0].tv_sec = (long)uactx->stbuf.st_atime;
260 times[0].tv_usec = 0;
262 else {
263 // Otherwise use the mod time.
264 times[0] = times[1];
266 utimes(f->name, times);
269 void de_update_file_attribs(dbuf *f, u8 preserve_file_times)
271 struct upd_attr_ctx uactx;
273 de_zeromem(&uactx, sizeof(struct upd_attr_ctx));
275 update_file_perms(&uactx, f);
276 if(preserve_file_times) {
277 update_file_time(&uactx, f);
281 // Note: Need to keep this function in sync with the implementation in deark-win.c.
282 void de_current_time_to_timestamp(struct de_timestamp *ts)
284 struct timeval tv;
285 int ret;
287 de_zeromem(&tv, sizeof(struct timeval));
288 ret = gettimeofday(&tv, NULL);
289 if(ret!=0) {
290 de_zeromem(ts, sizeof(struct de_timestamp));
291 return;
294 de_unix_time_to_timestamp((i64)tv.tv_sec, ts, 0x1);
295 de_timestamp_set_subsec(ts, ((double)tv.tv_usec)/1000000.0);
298 void de_exitprocess(int s)
300 exit(s);
303 struct de_platform_data *de_platformdata_create(void)
305 struct de_platform_data *plctx;
307 plctx = de_malloc(NULL, sizeof(struct de_platform_data));
308 return plctx;
311 void de_platformdata_destroy(struct de_platform_data *plctx)
313 if(!plctx) return;
314 de_free(NULL, plctx);
317 #endif // DE_UNIX