| 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 | |
| 13 | int 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 | |
| 238 | finished: |
| 239 | free(home); |
| 240 | dir_free(&dir); |
| 241 | ui_deinit(); |
| 242 | dir_deinit(); |
| 243 | } |
| 244 | |