Thumbnail

rani/cscroll.git

Clone URL: https://git.buni.party/rani/cscroll.git

Viewing file on branch master

1#include <errno.h>
2#include <stdio.h>
3#include <string.h>
4#include <stddef.h>
5#include <ncurses.h>
6#include <stdbool.h>
7
8#include "ui.h"
9#include "dir.h"
10#include "config.h"
11#include "cvector.h"
12
13int main(int argc, char ** argv) {
14 dir_init();
15 const char * cwd = dir_get_cwd();
16 // dir_get_home may return a static field so it must be copied here
17 char * home = (char*)dir_get_home();
18 if (home) home = strdup(home);
19
20 dir_t dir;
21 dir_list(cwd, &dir);
22
23 ui_init();
24 enum prog_mode mode = MODE_NORMAL;
25 size_t cursor = 0;
26 ui_status_info("");
27 for (;;) {
28 ui_erase();
29
30 size_t dirlen = dir_len(&dir);
31
32 ui_set_title(cwd, home);
33 ui_print_dir(&dir, cursor);
34 ui_print_cursor(cursor, dirlen, mode, dir_get_total_marked());
35
36 ui_refresh();
37
38 dirent_t * cur_de = dir_get_entry(&dir, cursor);
39
40 int inputc = getch();
41 if (inputc != KEY_RESIZE) ui_status_info("");
42 switch (inputc) {
43 UP_KEYS:
44 if (cursor > 0) cursor--;
45 break;
46 DOWN_KEYS:
47 if (cursor < dirlen - 1) cursor++;
48 break;
49 LEFT_KEYS: {
50 // the string from dir_basename(cwd) may be a static field
51 // so it might not be safe to use it after changing cwd
52 const char * truebasename = dir_basename(cwd);
53 char * basename = NULL;
54 if (truebasename) basename = strdup(truebasename);
55
56 int ret = dir_cd_back(cwd);
57 if (ret >= 0) {
58 cwd = dir_get_cwd();
59 dir_free(&dir);
60 ret = dir_list(cwd, &dir);
61 if (ret < 0) {
62 ui_status_error(strerror(-ret));
63 } else if (basename) {
64 size_t idx;
65 int i = dir_search_name(&dir, basename, &idx);
66 if (i >= 0) cursor = idx;
67 }
68 } else {
69 ui_status_error(strerror(-ret));
70 }
71 free(basename);
72 break;
73 }
74 RIGHT_KEYS: {
75 if (!cur_de) break;
76 if (cur_de->type == DE_DIR || (cur_de->type == DE_LINK
77 && cur_de->linktype == DE_DIR)) {
78 int ret = dir_cd(cwd, cur_de->name);
79 if (ret >= 0) {
80 cursor = 0;
81 cwd = dir_get_cwd();
82 dir_free(&dir);
83 dir_list(cwd, &dir);
84 if (dir_len(&dir) == 0) ui_status_info("Empty Directory");
85 } else {
86 ui_status_error(strerror(-ret));
87 }
88 } else {
89 ui_deinit();
90 int ret = dirent_open(cur_de);
91 if (ret < 0) ui_status_error(strerror(-ret));
92 ui_reinit();
93 }
94 break;
95 }
96 case KEY_HOME:
97 case 'g':
98 cursor = 0;
99 break;
100 case KEY_END:
101 case 'G':
102 if (dirlen == 0) cursor = 0;
103 else cursor = dirlen - 1;
104 break;
105 case '.': {
106 config.dots = !config.dots;
107 if (!cur_de) cursor = 0;
108 else {
109 size_t idx;
110 int ret = dir_search_name(&dir, cur_de->name, &idx);
111 if (ret < 0) cursor = 0;
112 else cursor = idx;
113 }
114 break;
115 }
116 case 'o':
117 // cycle through long mode options
118 if (config.longmode) {
119 if (config.longinline) config.longinline = false;
120 else config.longmode = false;
121 } else {
122 config.longmode = true;
123 config.longinline = true;
124 }
125 break;
126 case '/': {
127 const char * input = ui_readline("/");
128 if (!input) break;
129 size_t idx;
130 int i = dir_search_regex(&dir, input, &idx);
131 if (i >= 0) {
132 cursor = idx;
133 } else switch (-i) {
134 case REGSEARCH_BAD_REGEX:
135 ui_status_error("Bad RegEx");
136 break;
137 case REGSEARCH_NOT_FOUND:
138 ui_status_info("No Matches Found");
139 break;
140 default:
141 ui_status_error("Unhandled Case");
142 break;
143 }
144 break;
145 }
146 case 'd': {
147 if (mode == MODE_CUT) {
148 ui_status_info("Cannot Delete While Cutting");
149 break;
150 }
151 ui_refresh();
152 bool del = ui_prompt_deletion(cur_de, dir_get_total_marked());
153 if (!del) {
154 ui_status_info("Not Deleting");
155 ui_refresh();
156 break;
157 }
158
159 ui_status_info("Deleting");
160 ui_refresh();
161
162 int ret;
163 if (dir_get_total_marked() == 0) ret = dirent_delete(cur_de);
164 else ret = dir_marked_delete();
165 if (ret < 0) {
166 ui_status_error("Deletion Failed");
167 } else {
168 ui_status_info("Deleted");
169 }
170
171 dir_free(&dir);
172 dir_list(cwd, &dir);
173 dirlen = dir_len(&dir);
174 if (dirlen == 0) cursor = 0;
175 else if (cursor > dirlen - 1) cursor = dirlen - 1;
176 break;
177 }
178 case 'm': {
179 if (!cur_de) break;
180 if (mode == MODE_CUT) {
181 ui_status_info("Cannot Mark While Cutting");
182 break;
183 }
184 int ret = dirent_togglemark(cur_de);
185 if (ret < 0) switch (-ret) {
186 case TOGGLEMARK_PARENT_MARKED:
187 ui_status_info("Ancestor Already Marked");
188 break;
189 case TOGGLEMARK_DIRENT_UNKNOWN:
190 ui_status_error("Cannot Mark Unknown Dirent");
191 break;
192 default:
193 ui_status_error("Mark Failed");
194 break;
195 }
196 break;
197 }
198 case 'c':
199 if (mode == MODE_NORMAL && dir_get_total_marked() > 0) mode = MODE_CUT;
200 else if (mode == MODE_CUT) mode = MODE_NORMAL;
201 break;
202 case 'p': {
203 if (mode != MODE_CUT) {
204 ui_status_info("Cannot Paste if Not Cutting");
205 break;
206 }
207 bool paste = ui_prompt_paste(dir_get_total_marked());
208 if (!paste) {
209 ui_status_info("Not Pasting");
210 break;
211 }
212 mode = MODE_NORMAL;
213 ui_status_info("Pasting");
214 ui_refresh();
215
216 size_t total_marks, total_pastes;
217 total_marks = dir_get_total_marked();
218 int ret = dir_paste_marks(cwd, &total_pastes);
219 if (ret < 0) ui_status_error(strerror(-ret));
220 else if (total_pastes == 0)
221 ui_status_error("Nothing Pasted");
222 else if (total_marks != total_pastes)
223 ui_status_error("Not All Marks Pasted");
224 else ui_status_info("Pasted");
225 dir_free(&dir);
226 dir_list(cwd, &dir);
227 break;
228 }
229 case 'q':
230 goto finished;
231 case KEY_RESIZE:
232 ui_resize();
233 break;
234 default: break;
235 }
236 }
237
238finished:
239 free(home);
240 dir_free(&dir);
241 ui_deinit();
242 dir_deinit();
243}
244