Updated version number for development
[deark.git] / src / deark-unix.c
blob898686c775137f18aa93db76f7a8aae1c40c2623
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 char *de_strdup(deark *c, const char *s)
39 char *s2;
41 s2 = strdup(s);
42 if(!s2) {
43 de_err(c, "Memory allocation failed");
44 de_fatalerror(c);
45 return NULL;
47 return s2;
50 i64 de_strtoll(const char *string, char **endptr, int base)
52 return strtoll(string, endptr, base);
55 static FILE* de_fopen(deark *c, const char *fn, const char *mode,
56 char *errmsg, size_t errmsg_len)
58 FILE *f;
59 int errcode;
61 f = fopen(fn, mode);
62 if(!f) {
63 errcode = errno;
64 de_strlcpy(errmsg, strerror(errcode), errmsg_len);
66 return f;
69 // Test if the file seems suitable for reading, and return its size.
70 // returned flags: 0x1 = file is a FIFO (named pipe)
71 static int de_examine_file_by_fd(deark *c, int fd, i64 *len,
72 char *errmsg, size_t errmsg_len, unsigned int *returned_flags)
74 struct stat stbuf;
76 *returned_flags = 0;
77 de_zeromem(&stbuf, sizeof(struct stat));
79 if(0 != fstat(fd, &stbuf)) {
80 de_strlcpy(errmsg, strerror(errno), errmsg_len);
81 return 0;
84 if(S_ISFIFO(stbuf.st_mode)) {
85 *returned_flags |= 0x1;
86 *len = 0;
87 return 1;
89 else if(!S_ISREG(stbuf.st_mode)) {
90 de_strlcpy(errmsg, "Not a regular file", errmsg_len);
91 return 0;
94 *len = (i64)stbuf.st_size;
95 return 1;
98 FILE* de_fopen_for_read(deark *c, const char *fn, i64 *len,
99 char *errmsg, size_t errmsg_len, unsigned int *returned_flags)
101 int ret;
102 FILE *f;
104 f = de_fopen(c, fn, "rb", errmsg, errmsg_len);
105 if(!f) {
106 return NULL;
109 ret = de_examine_file_by_fd(c, fileno(f), len, errmsg, errmsg_len,
110 returned_flags);
111 if(!ret) {
112 de_fclose(f);
113 return NULL;
116 return f;
119 // flags: 0x1 = append instead of overwriting
120 FILE* de_fopen_for_write(deark *c, const char *fn,
121 char *errmsg, size_t errmsg_len, int overwrite_mode,
122 unsigned int flags)
124 const char *mode;
126 // A simple check to make it harder to accidentally overwrite the input
127 // file. (But it can easily be defeated.)
128 // TODO?: Make this more robust.
129 if(c->input_filename && !de_strcmp(fn, c->input_filename)) {
130 de_err(c, "Refusing to write to %s: Same as input filename", fn);
131 de_fatalerror(c);
132 de_strlcpy(errmsg, "", errmsg_len);
133 return NULL;
136 if(overwrite_mode!=DE_OVERWRITEMODE_STANDARD) {
137 // Check if the file already exists.
138 struct stat stbuf;
139 int s_ret;
141 de_zeromem(&stbuf, sizeof(struct stat));
142 #if DE_USE_LSTAT
143 s_ret = lstat(fn, &stbuf);
144 #else
145 s_ret = stat(fn, &stbuf);
146 #endif
148 // s_ret==0 = "success"
149 if(s_ret==0 && overwrite_mode==DE_OVERWRITEMODE_NEVER) {
150 de_strlcpy(errmsg, "Output file already exists", errmsg_len);
151 return NULL;
154 if(s_ret==0 && overwrite_mode==DE_OVERWRITEMODE_DEFAULT) {
155 if ((stbuf.st_mode & S_IFMT) == S_IFLNK) {
156 de_strlcpy(errmsg, "Output file is a symlink", errmsg_len);
157 return NULL;
162 mode = (flags&0x1) ? "ab" : "wb";
163 return de_fopen(c, fn, mode, errmsg, errmsg_len);
166 int de_fseek(FILE *fp, i64 offs, int whence)
168 int ret;
170 #if DE_USE_FSEEKO
171 ret = fseeko(fp, (off_t)offs, whence);
172 #else
173 ret = fseek(fp, (long)offs, whence);
174 #endif
175 return ret;
178 i64 de_ftell(FILE *fp)
180 i64 ret;
182 #if DE_USE_FSEEKO
183 ret = (i64)ftello(fp);
184 #else
185 ret = (i64)ftell(fp);
186 #endif
187 return ret;
190 int de_fclose(FILE *fp)
192 return fclose(fp);
195 struct upd_attr_ctx {
196 int tried_stat;
197 int stat_ret;
198 struct stat stbuf;
201 // If, based on f->mode_flags, we know that the file should be executable or
202 // non-executable, make it so.
203 static void update_file_perms(struct upd_attr_ctx *uactx, dbuf *f)
205 mode_t oldmode, newmode;
207 if(f->btype!=DBUF_TYPE_OFILE) return;
208 if(!f->fi_copy) return;
209 if(!f->name) return;
210 if(!(f->fi_copy->mode_flags&DE_MODEFLAG_NONEXE) &&!(f->fi_copy->mode_flags&DE_MODEFLAG_EXE)) return;
212 uactx->stat_ret = stat(f->name, &uactx->stbuf);
213 uactx->tried_stat = 1;
214 if(uactx->stat_ret != 0) {
215 return;
218 oldmode = uactx->stbuf.st_mode;
219 newmode = oldmode;
221 // Start by turning off the executable bits in the tentative new mode.
222 newmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
224 if(f->fi_copy->mode_flags&DE_MODEFLAG_EXE) {
225 // Set an Executable bit if its corresponding Read bit is set.
226 if(oldmode & S_IRUSR) newmode |= S_IXUSR;
227 if(oldmode & S_IRGRP) newmode |= S_IXGRP;
228 if(oldmode & S_IROTH) newmode |= S_IXOTH;
231 if(newmode != oldmode) {
232 de_dbg2(f->c, "changing file mode from %03o to %03o",
233 (unsigned int)oldmode, (unsigned int)newmode);
234 chmod(f->name, newmode);
238 static void update_file_time(struct upd_attr_ctx *uactx, dbuf *f)
240 const struct de_timestamp *ts;
241 struct timeval times[2];
243 if(f->btype!=DBUF_TYPE_OFILE) return;
244 if(!f->fi_copy) return;
245 ts = &f->fi_copy->timestamp[DE_TIMESTAMPIDX_MODIFY];
246 if(!ts->is_valid) return;
247 if(!f->name) return;
249 if(!uactx->tried_stat) {
250 uactx->stat_ret = stat(f->name, &uactx->stbuf);
251 uactx->tried_stat = 1;
254 // I know that this code is not Y2038-compliant, if sizeof(time_t)==4.
255 // But it's not likely to be a serious problem, and I'd rather not replace
256 // it with code that's less portable.
258 de_zeromem(&times, sizeof(times));
259 // times[0] = access time
260 // times[1] = mod time
261 times[1].tv_sec = (long)de_timestamp_to_unix_time(ts);
262 if(ts->precision>DE_TSPREC_1SEC) {
263 times[1].tv_usec = (long)(de_timestamp_get_subsec(ts)/10);
266 // We don't want to set the access time, but unfortunately the utimes()
267 // function forces us to.
268 if(uactx->tried_stat && (uactx->stat_ret==0)) {
269 // If we have the file's current access time recorded, use that.
270 // (Though this may lose precision. Which could be fixed at the cost of
271 // portability.)
272 times[0].tv_sec = (long)uactx->stbuf.st_atime;
273 times[0].tv_usec = 0;
275 else {
276 // Otherwise use the mod time.
277 times[0] = times[1];
279 utimes(f->name, times);
282 void de_update_file_attribs(dbuf *f, u8 preserve_file_times)
284 struct upd_attr_ctx uactx;
286 de_zeromem(&uactx, sizeof(struct upd_attr_ctx));
288 update_file_perms(&uactx, f);
289 if(preserve_file_times) {
290 update_file_time(&uactx, f);
294 // Note: Need to keep this function in sync with the implementation in deark-win.c.
295 void de_current_time_to_timestamp(struct de_timestamp *ts)
297 struct timeval tv;
298 int ret;
300 de_zeromem(&tv, sizeof(struct timeval));
301 ret = gettimeofday(&tv, NULL);
302 if(ret!=0) {
303 de_zeromem(ts, sizeof(struct de_timestamp));
304 return;
307 de_unix_time_to_timestamp((i64)tv.tv_sec, ts, 0x1);
308 de_timestamp_set_subsec(ts, ((double)tv.tv_usec)/1000000.0);
311 void de_exitprocess(int s)
313 exit(s);
316 struct de_platform_data *de_platformdata_create(void)
318 struct de_platform_data *plctx;
320 plctx = de_malloc(NULL, sizeof(struct de_platform_data));
321 return plctx;
324 void de_platformdata_destroy(struct de_platform_data *plctx)
326 if(!plctx) return;
327 de_free(NULL, plctx);
330 #endif // DE_UNIX