💾 Archived View for gemini.rmf-dev.com › repo › Vaati › mz › files › 13fc50b575d7d90289b537f3ca45763… captured on 2023-11-04 at 12:29:42. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-09-08)
-=-=-=-=-=-=-
0 /*
1 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>
2 *
3 * Permission to use, copy, modify, and distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 */
15 #ifdef __linux__
16 #define _GNU_SOURCE
17 #else
18 #define _BSD_SOURCE
19 #endif
20 #include <dirent.h>
21 #include <unistd.h>
22 #include <ctype.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdint.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <fts.h>
30 #include <stdio.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include "termbox.h"
34 #include "view.h"
35 #include "file.h"
36 #include "strlcpy.h"
37 #include "client.h"
38 #include "util.h"
39
40 int file_init(struct view *view, const char* path) {
41
42 PZERO(view);
43
44 if (path)
45 STRCPY(view->path, path);
46 else if (view->path != getcwd(V(view->path)))
47 return -1;
48
49 view->fd = open(view->path, O_DIRECTORY);
50 if (view->fd < 0)
51 return -1;
52
53 return 0;
54 }
55
56 int file_up(struct view *view) {
57 return file_cd(view, "..");
58 }
59
60 int file_cd(struct view *view, const char *path) {
61
62 char buf[PATH_MAX];
63 int fd, len, back;
64
65 if (!strcmp(path, ".")) return 0;
66
67 len = STRCPY(buf, view->path);
68 back = 0;
69 if (!strcmp(path, "..")) {
70 if (!strcmp(view->path, "/")) {
71 return 0;
72 }
73 len--;
74 while (len >= 0) {
75 if (buf[len] == '/') {
76 buf[AZ(len)] = '\0';
77 back = 1;
78 break;
79 }
80 len--;
81 }
82 if (!back) return 0;
83 } else {
84 if (buf[AZ(len) - 1] != '/') {
85 buf[len] = '/';
86 len++;
87 }
88 strlcpy(&buf[len], path, sizeof(buf) - len);
89 }
90
91 fd = open(buf, O_DIRECTORY);
92 if (fd < 0) return -1;
93 close(view->fd);
94 view->fd = fd;
95 fchdir(fd);
96 STRCPY(view->path, buf);
97 view->selected = 0;
98 return 0;
99 }
100
101 void file_free(struct view *view) {
102 size_t i;
103 for (i = 0; i < view->length; i++) {
104 free(view->entries[i].other);
105 view->entries[i].other = NULL;
106 }
107 free(view->entries);
108 view->length = 0;
109 view->entries = NULL;
110 }
111
112 int file_sort(const void* a, const void* b)
113 {
114 struct entry *first = (struct entry*)a;
115 struct entry *second = (struct entry*)b;
116 int i = 0, j = 0;
117
118 if (first->type != second->type) {
119 if (first->type == DT_DIR) return -1;
120 if (second->type == DT_DIR) return 1;
121 }
122 while (1) {
123 uint32_t c1, c2;
124 do {
125 if (!first->name[i]) return 0;
126 i += tb_utf8_char_to_unicode(&c1, &first->name[i]);
127 } while (c1 == ' ' || c1 == '\t');
128 do {
129 if (!second->name[j]) return 0;
130 j += tb_utf8_char_to_unicode(&c2, &second->name[j]);
131 } while (c2 == ' ' || c2 == '\t');
132 c1 = tolower(c1);
133 c2 = tolower(c2);
134 if (c1 != c2)
135 return c1 < c2 ? -1 : 1;
136 }
137 return 0;
138 }
139
140 int file_reload(struct view *view) {
141 close(view->fd);
142 view->fd = open(view->path, O_DIRECTORY);
143 if (view->fd < 0) return -1;
144 return file_ls(view);
145 }
146
147 int file_ls(struct view *view) {
148 struct dirent *entry;
149 DIR *dp;
150 int length, i, fd;
151
152 fd = dup(view->fd);
153 dp = fdopendir(fd);
154 if (dp == NULL)
155 return -1;
156
157 length = 0;
158 while ((entry = readdir(dp))) {
159 if (!view->showhidden && entry->d_name[0] == '.')
160 continue;
161 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
162 length++;
163 }
164 if (length == 0) {
165 closedir(dp);
166 file_free(view);
167 return 0;
168 }
169
170 rewinddir(dp);
171 file_free(view);
172 view->entries = calloc(length, sizeof(struct entry));
173 i = 0;
174 while ((entry = readdir(dp))) {
175 if (!strcmp(entry->d_name, ".") ||
176 !strcmp(entry->d_name, ".."))
177 continue;
178 if (!view->showhidden && entry->d_name[0] == '.')
179 continue;
180 view->entries[i].selected = 0;
181 strlcpy(view->entries[i].name, entry->d_name,
182 sizeof(view->entries[i].name));
183
184 #ifndef sun
185 if (entry->d_type == DT_LNK || entry->d_type == DT_UNKNOWN) {
186 #endif
187 struct stat buf;
188 if (!fstatat(view->fd, entry->d_name, &buf, 0)) {
189 view->entries[i].type = S_ISDIR(buf.st_mode) ?
190 DT_DIR : DT_REG;
191 } else {
192 view->entries[i].type = DT_REG;
193 }
194 #ifndef sun
195 } else
196 view->entries[i].type = entry->d_type;
197 #endif
198 i++;
199 }
200
201 qsort(view->entries, length, sizeof(struct entry), file_sort);
202
203 closedir(dp);
204 close(fd);
205 view->length = length;
206 view->scroll = 0;
207 lseek(view->fd, 0, SEEK_SET);
208 return 0;
209 }
210
211 int file_select(struct view *view, const char *path) {
212 size_t i = 0;
213 while (i < view->length) {
214 if (!strncmp(view->entries[i].name, path,
215 sizeof(view->entries[i].name))) {
216 view->selected = i;
217 return 0;
218 }
219 i++;
220 }
221 return -1;
222 }
223
224 int file_move_entry(struct view *view, struct entry *entry) {
225
226 int fd = open(client.copy_path, O_DIRECTORY);
227 if (fd < 0) return -1;
228 return file_move(client.copy_path, fd, entry->name,
229 view->fd, view->path, entry->name);
230 }
231
232 #ifdef __linux__
233 #define lseek lseek64
234 #define off_t off64_t
235 #endif
236
237 #if !defined(__linux__) && !defined(__FreeBSD__)
238 #define NO_COPY_FILE_RANGE
239 #endif
240
241 int file_copy_entry(struct view *view, struct entry *entry) {
242
243 struct stat st;
244 int fd, dstfd, srcfd;
245
246 fd = openat(view->fd, entry->name, 0);
247 if (fd > -1) {
248 close(fd);
249 errno = EEXIST;
250 return -1;
251 }
252
253 fd = open(client.copy_path, O_DIRECTORY);
254 if (fd < 0) return -1;
255 srcfd = openat(fd, entry->name, O_RDONLY);
256 close(fd);
257 if (srcfd < 0) return -1;
258
259 if (fstat(srcfd, &st)) {
260 close(srcfd);
261 return -1;
262 }
263
264 if (S_ISDIR(st.st_mode)) {
265 char buf[PATH_MAX];
266 snprintf(V(buf), "cp -r \"%s/%s\" \"%s\"", client.copy_path,
267 client.copy->name, view->path);
268 return system(buf);
269 }
270
271 dstfd = openat(view->fd, entry->name, O_WRONLY|O_CREAT, st.st_mode);
272 if (dstfd < 0) return -1;
273
274 return file_copy(srcfd, dstfd, 0);
275 }
276
277 int file_copy(int src, int dst, int usebuf) {
278
279 size_t length, ret;
280
281 #ifdef NO_COPY_FILE_RANGE
282 if (usebuf == -1) return -1;
283 #endif
284
285 length = lseek(src, 0, SEEK_END);
286 if (length == (size_t)-1 ||
287 lseek(src, 0, SEEK_SET) == (off_t)-1) {
288 close(dst);
289 close(src);
290 return -1;
291 }
292
293 ret = 0;
294 while (1) {
295 ssize_t i;
296 char buf[4096];
297 #ifndef NO_COPY_FILE_RANGE
298 if (usebuf) {
299 #endif
300 i = read(src, buf, sizeof(buf));
301 if (i <= 0) break;
302 write(dst, buf, i);
303 #ifndef NO_COPY_FILE_RANGE
304 } else {
305 i = copy_file_range(src, 0, dst, 0, length - ret, 0);
306 if (i <= 0) break;
307 }
308 #endif
309 ret += i;
310 }
311
312 #ifndef NO_COPY_FILE_RANGE
313 /* retry without copy_file_range if the operation failed */
314 if (ret != length && !usebuf) return file_copy(src, dst, 1);
315 #endif
316
317 close(dst);
318 close(src);
319 if (ret != length) {
320 return -1;
321 }
322
323 return 0;
324 }
325
326 int file_move(const char *oldpath, int srcdir, const char *oldname,
327 int dstdir, const char *newpath, const char *newname) {
328
329 int error, fd;
330
331 fd = openat(dstdir, newname, 0);
332 if (fd > -1) {
333 errno = EEXIST;
334 close(fd);
335 return -1;
336 }
337
338 error = renameat(srcdir, oldname, dstdir, newname);
339 /* EXDEV : when trying to move a file to another file system */
340 if (error && errno == EXDEV) {
341 int src, dst;
342 struct stat st;
343
344 if (fstatat(srcdir, oldname, &st, 0)) return -1;
345 /* use a shell command instead of recursively copying files */
346 if (S_ISDIR(st.st_mode)) {
347 char buf[PATH_MAX * 3];
348 snprintf(V(buf), "mv \"%s/%s\" \"%s/%s\"",
349 oldpath, oldname,
350 newpath, newname);
351 return system(buf);
352 }
353
354 src = openat(srcdir, oldname, O_RDONLY);
355 if (src < 0) return -1;
356 dst = openat(dstdir, newname, O_WRONLY|O_CREAT, st.st_mode);
357 if (dst < 0) {
358 close(src);
359 return -1;
360 }
361 if (!file_copy(src, dst, 1)) {
362 char buf[2048];
363 snprintf(V(buf), "%s/%s", oldpath, oldname);
364 if (!remove(buf)) error = 0;
365 }
366 close(src);
367 close(dst);
368 }
369 return error;
370 }
371