├── .gitignore ├── .gitmodules ├── README.md ├── makefile ├── sceenshots ├── home.jpg ├── selection.jpg └── visual.jpg └── src ├── action.c ├── action.h ├── actionPlumbing.c ├── actionPlumbing.h ├── core.c ├── core.h ├── info.c ├── info.h ├── main.c ├── types.c └── types.h /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /a.out 3 | /test 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/deps/libgit2"] 2 | path = src/deps/libgit2 3 | url = https://github.com/libgit2/libgit2 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Minifm 2 | ## About 3 | Minifm ie MiniFileManager is a visual file manager inspired by program such as Ranger, Vifm, NNN, etc; as well as fzf, and fzy in the way they use term codes instead of ncurses to avoid making the program a full screen process. 4 | 5 | **WARNING**: this program is in it's early stages use at your own risk, and if it doesn't work for you be sure to let me know, I'd really like to have it running well on other machines. 6 | **REWRITE**: the code for the project isn't maintainable, so I'm working on some better foundations that'll show up elsewhere on my github, before they're implemented here 7 | 8 | ## Usage 9 | this program is ment to preform small action on files in the current directory, such as changing directory, opening a file, deleting, copying. 10 | 11 | As far as I know changing the current directory of your shell has to be done by said shell, and not a child process. In-order to do this I use a shell function like so: 12 | 13 | ```bash 14 | function mfm() 15 | { 16 | mfm_path="$1" 17 | mfm_backup_path="" 18 | while [ -n "$mfm_path" ]; do 19 | mfm_path=`minifm $mfm_path 2 | #include "action.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "string.h" 8 | #include "info.h" 9 | #include "actionPlumbing.h" 10 | #include "core.h" 11 | 12 | int toggleHidden(t_state * state) 13 | { 14 | float newsel; 15 | if(*state->selected != 0) 16 | newsel = (float)*state->selected / (float)*state->dirCount; 17 | else 18 | newsel = 0; 19 | 20 | state->config->viewHidden = !state->config->viewHidden; 21 | *state->dirCount = countDir(state); 22 | 23 | free(state->selected); 24 | state->selected = malloc(sizeof(int) * *state->dirCount + 1); 25 | for(int i = 1; i < *state->dirCount + 1; i++) 26 | { 27 | state->selected[i] = -1; 28 | } 29 | *state->selected = (int)(newsel * *state->dirCount); 30 | 31 | updateDirList(state); 32 | return 0; 33 | } 34 | 35 | int enterVisual(t_state * state) 36 | { 37 | for(int i = 1; i < *state->dirCount; i++) 38 | state->selected[i] = -1; 39 | state->mode = VISUAL; 40 | return 0; 41 | } 42 | 43 | int printSelected(t_state * state) 44 | { 45 | for(int i = 0; i < *state->dirCount; i++) 46 | printf("%d", state->selected[i]); 47 | return 0; 48 | } 49 | 50 | int visualMoveDown(t_state * state) 51 | { 52 | int * selected = state->selected; 53 | int * dirCount = state->dirCount; 54 | int topOfSelection = state->topOfSelection; 55 | int i = 0; 56 | for(; selected[i] != -1; i++); 57 | 58 | if(topOfSelection) 59 | { 60 | if(selected[1] != -1) 61 | { 62 | int i = 1; 63 | for(; selected[i] != -1 ; i++) 64 | selected[i - 1] = selected[i]; 65 | selected[i - 1] = -1; 66 | } else { 67 | state->topOfSelection = !state->topOfSelection; 68 | if(selected[i - 1] < *dirCount - 1) 69 | { 70 | visualMoveDown(state); 71 | } 72 | } 73 | } else { 74 | if(selected[i - 1] < *dirCount - 1) 75 | { 76 | selected[i] = selected[i - 1] + 1; 77 | } 78 | } 79 | return 0; 80 | } 81 | 82 | int visualMoveUp(t_state * state) 83 | { 84 | int * selected = state->selected; 85 | int * topOfSelection = &state->topOfSelection; 86 | int i = 0; 87 | for(; selected[i] != -1; i++); 88 | 89 | 90 | if(*topOfSelection) 91 | { 92 | if(*selected > 0) { 93 | int tmp = selected[0]; 94 | int tmp2; 95 | selected[0] = selected[0] - 1; 96 | int i = 1; 97 | for(; selected[i] != -1; i++) 98 | { 99 | tmp2 = selected[i]; 100 | selected[i] = tmp; 101 | tmp = tmp2; 102 | } 103 | tmp2 = selected[i]; 104 | selected[i] = tmp; 105 | } 106 | 107 | } else { 108 | if(selected[i-1] == *selected) 109 | { 110 | *topOfSelection = !(*topOfSelection); 111 | if(*selected > 0) { 112 | visualMoveUp(state); 113 | } 114 | } else { 115 | int i = 0; 116 | for(; selected[i] != -1; i++); 117 | selected[i - 1] = -1; 118 | } 119 | } 120 | return 0; 121 | } 122 | 123 | int changeSelectionPos(t_state * state) 124 | { 125 | state->topOfSelection = !state->topOfSelection; 126 | return 0; 127 | } 128 | 129 | int selectOne(t_state * state) 130 | { 131 | int i = 1; 132 | for(; state->selected[i] != -1; i++){ 133 | if(state->selected[i] == state->selected[0]) 134 | break; 135 | } 136 | state->selected[i] = state->selected[0]; 137 | return 0; 138 | } 139 | 140 | //Match user input against file list. output file name if user hits enter 141 | //TODO: highlight matches, and posible remove the files that do not 142 | //COULD: make into a mode 143 | int Search(t_state * state) 144 | { 145 | t_fileAttrib ** fileAtrribArray = state->fileAttribArray; 146 | int * selected = state->selected; 147 | int * dirCount = state->dirCount; 148 | char * cwd = state->cwd; 149 | FILE * tty = state->tty; 150 | char tmp[2] = {' ', '\0'}; 151 | char search[256]; 152 | search[0] = '\0'; 153 | int bestScore = 0; 154 | int bestMatchIndex = 0; 155 | int currentScore = 0; 156 | int numMatch = 0; 157 | while(1) 158 | { 159 | tmp[0] = getchar(); 160 | if(tmp[0] == 27) 161 | return 0; 162 | else if(tmp[0] == '\r') 163 | { 164 | enter(state); 165 | return 1; 166 | } 167 | 168 | strcat(search, tmp); 169 | for(int i = 0; i < *dirCount; i++) 170 | { 171 | for(int j = 0; j < strlen(fileAtrribArray[i]->name); j++) 172 | { 173 | if(fileAtrribArray[i]->name[j] == search[0]) 174 | { 175 | currentScore = matchScore(&search[j], &fileAtrribArray[i]->name[j]); 176 | 177 | if (currentScore > bestScore) 178 | { 179 | bestScore = currentScore; 180 | bestMatchIndex = i; 181 | *selected = bestMatchIndex; 182 | draw(state); 183 | numMatch++; 184 | } else if (currentScore == bestScore) 185 | numMatch++; 186 | } 187 | } 188 | } 189 | numMatch = 0; 190 | } 191 | } 192 | 193 | int MkDir(t_state * state) 194 | { 195 | char * cwd = state->cwd; 196 | char tmp[2] = {' ', '\0'}; 197 | char newName[256]; 198 | newName[0] = '\0'; 199 | char mkdirCommand[PATH_MAX]; 200 | 201 | state->msg[0] = '\0'; 202 | draw(state); 203 | 204 | while(1) 205 | { 206 | tmp[0] = getchar(); 207 | if(tmp[0] == 27) 208 | return 0; 209 | else if(tmp[0] == '\r') 210 | { 211 | sprintf(mkdirCommand ,"mkdir %s/%s", cwd, newName); 212 | system(mkdirCommand); 213 | printf("%s/%s", cwd, newName); 214 | return 1; 215 | } 216 | 217 | strcat(newName, tmp); 218 | strcpy(state->msg, newName); 219 | draw(state); 220 | } 221 | } 222 | 223 | int MkFile(t_state * state) 224 | { 225 | char * cwd = state->cwd; 226 | char tmp[2] = {' ', '\0'}; 227 | char newName[256]; 228 | newName[0] = '\0'; 229 | char mkFileCommand[PATH_MAX]; 230 | 231 | state->msg[0] = '\0'; 232 | draw(state); 233 | 234 | while(1) 235 | { 236 | tmp[0] = getchar(); 237 | if(tmp[0] == 27) 238 | return 0; 239 | else if(tmp[0] == '\r') 240 | { 241 | sprintf(mkFileCommand ,"touch %s/%s", cwd, newName); 242 | system(mkFileCommand); 243 | printf("%s/%s", cwd, newName); 244 | return 1; 245 | } 246 | 247 | strcat(newName, tmp); 248 | strcpy(state->msg, newName); 249 | draw(state); 250 | } 251 | } 252 | 253 | //output name of selected file and exit program 254 | int enter(t_state * state) 255 | { 256 | t_fileAttrib ** fileAttribArray = state->fileAttribArray; 257 | int * selected = state->selected; 258 | int * dirCount = state->dirCount; 259 | char * cwd = state->cwd; 260 | FILE * tty = state->tty; 261 | 262 | if(*dirCount > 0) 263 | { 264 | char * sel = malloc(sizeof(char) * (strlen(fileAttribArray[*selected]->name) + 1)); 265 | strcpy(sel, fileAttribArray[*selected]->name); 266 | *selected = 0; 267 | strcat(cwd, "/"); 268 | strcat(cwd, sel); 269 | } 270 | 271 | for(int i = 0; i < *dirCount; i++) 272 | freeFileAttrib(fileAttribArray[i]); 273 | 274 | fprintf(tty, "\033[J"); 275 | fprintf(tty, "\e[?25h"); 276 | printf("%s", cwd); 277 | return 1; 278 | } 279 | 280 | int moveDown(t_state * state) 281 | { 282 | int * selected = state->selected; 283 | int * dirCount = state->dirCount; 284 | *selected = *selected < *dirCount - 1 ? *selected + 1 : *selected; 285 | return 0; 286 | } 287 | 288 | int moveUp(t_state * state) 289 | { 290 | int * selected = state->selected; 291 | *selected = *selected > 0 ? *selected - 1 : *selected; 292 | return 0; 293 | } 294 | 295 | int halfPageDown(t_state * state) 296 | { 297 | for(int i = 0; i < state->config->viewRange/2; i++) 298 | moveDown(state); 299 | return 0; 300 | } 301 | 302 | int halfPageUp(t_state * state) 303 | { 304 | for(int i = 0; i < state->config->viewRange/2; i++) 305 | moveUp(state); 306 | return 0; 307 | } 308 | 309 | int escape(t_state * state) 310 | { 311 | if(state->selected[1] != -1 && *state->dirCount > 0) 312 | { 313 | for(int i = 1; i < *state->dirCount; i++) 314 | state->selected[i] = -1; 315 | state->mode = NORMAL; 316 | state->topOfSelection = 1; 317 | return 0; 318 | } 319 | return 1; 320 | } 321 | 322 | void freeAction(t_action * action) 323 | { 324 | free(action->combo); 325 | free(action->function); 326 | } 327 | 328 | int gotoTop(t_state * state) 329 | { 330 | int * selected = state->selected; 331 | *selected = 0; 332 | return 0; 333 | } 334 | 335 | int gotoBottom(t_state * state){ 336 | int * selected = state->selected; 337 | int * dirCount = state->dirCount; 338 | *selected = *dirCount - 1; 339 | return 0; 340 | } 341 | 342 | int backDir(t_state * state) 343 | { 344 | *strrchr(state->cwd, '/') = '\0'; 345 | printf("%s", state->cwd); 346 | return 1; 347 | } 348 | 349 | int removeFile(t_state * state) 350 | { 351 | for(int i = 0; state->selected[i] != -1; i++) 352 | remove(state->fileAttribArray[state->selected[i]]->name); 353 | updateDirList(state); 354 | return 1; 355 | } 356 | 357 | int yank(t_state * state) 358 | { 359 | FILE * yankList = fopen("/tmp/yanklist", "w"); 360 | 361 | int i = state->mode != VISUAL && state->selected[1] != -1 ? 1 : 0; 362 | for(; state->selected[i] != -1; i++) 363 | fprintf(yankList, "%s/%s\n", state->cwd, state->fileAttribArray[state->selected[i]]->name); 364 | 365 | fclose(yankList); 366 | return 1; 367 | } 368 | 369 | //TODO: error checking 370 | int put(t_state * state) 371 | { 372 | FILE * yankList = fopen("/tmp/yanklist", "r"); 373 | char putFile[PATH_MAX]; 374 | char command[2*PATH_MAX + 1]; 375 | char * newline; 376 | 377 | while(fgets(putFile, PATH_MAX, yankList)) 378 | { 379 | newline = strchr(putFile, '\n'); 380 | *newline = '\0'; 381 | sprintf(command, "cp -r %s %s", putFile, state->cwd); 382 | system(command); 383 | } 384 | 385 | fclose(yankList); 386 | return 1; 387 | } 388 | -------------------------------------------------------------------------------- /src/action.h: -------------------------------------------------------------------------------- 1 | #ifndef __ACTION_H__ 2 | #define __ACTION_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | #include 8 | #include "types.h" 9 | 10 | int toggleHidden(t_state * state); 11 | int Search(t_state * state); 12 | int enter(t_state * state); 13 | int moveDown(t_state * state); 14 | int moveUp(t_state * state); 15 | int escape(t_state * state); 16 | void freeAction(t_action * action); 17 | int gotoTop(t_state * state); 18 | int gotoBottom(t_state * state); 19 | int backDir(t_state * state); 20 | int removeFile(t_state * state); 21 | int yank(t_state * state); 22 | int put(t_state * state); 23 | int Visual(t_state * state); 24 | int enterVisual(t_state * state); 25 | int visualMoveDown(t_state * state); 26 | int visualMoveUp(t_state * state); 27 | int changeSelectionPos(t_state * state); 28 | int printSelected(t_state * state); 29 | int selectOne(t_state * state); 30 | int halfPageUp(t_state * state); 31 | int halfPageDown(t_state * state); 32 | int MkDir(t_state * state); 33 | int MkFile(t_state * state); 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/actionPlumbing.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "actionPlumbing.h" 3 | #include 4 | 5 | //assings a score based on how many letters match in strings 6 | int matchScore(char * search, char * check) 7 | { 8 | int score = 0; 9 | for(int i = 0; i < strlen(check); i++) 10 | { 11 | if(search[i] == check[i]) 12 | { 13 | score++; 14 | } else { 15 | break; 16 | } 17 | } 18 | return score; 19 | } 20 | 21 | t_action * initAction(mode mode, char * combo, int (*function)(t_state *) ) 22 | { 23 | t_action * newAction; 24 | 25 | newAction = malloc(sizeof(t_action)); 26 | newAction->combo = malloc(sizeof(int)); 27 | newAction->combo = combo; 28 | int (*fp)(t_state * state) = function; 29 | newAction->function = malloc(sizeof(&function)); 30 | newAction->function = function; 31 | newAction->mode = mode; 32 | 33 | return newAction; 34 | } 35 | 36 | void listQueue(struct actionNode * commands, t_action * action) 37 | { 38 | commands->tail->nextNode = malloc(sizeof(struct actionNode)); 39 | commands->tail = commands->tail->nextNode; 40 | commands->tail->action = action; 41 | commands->tail->nextNode = NULL; 42 | } 43 | 44 | struct actionNode * initList(t_action * action) 45 | { 46 | struct actionNode * commands = malloc(sizeof(struct actionNode)); 47 | commands->action = malloc(sizeof(t_action)); 48 | commands->action = action; 49 | commands->tail = commands; 50 | commands->nextNode = NULL; 51 | return commands; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/actionPlumbing.h: -------------------------------------------------------------------------------- 1 | #ifndef __ACTIONPLUMBING_H__ 2 | #define __ACTIONPLUMBING_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | #include "types.h" 8 | 9 | int matchScore(char * search, char * check); 10 | t_action * initAction(mode mode, char * combo, int (*function)(t_state *) ); 11 | int matchScore(char * search, char * check); 12 | int Search(t_state * state); 13 | void listQueue(struct actionNode * commands, t_action * action); 14 | struct actionNode * initList(t_action * action); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/core.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "action.h" 8 | #include "actionPlumbing.h" 9 | #include "info.h" 10 | #include 11 | #include 12 | #include 13 | 14 | int isCursorLine(t_state * state, int line) 15 | { 16 | int * selected = state->selected; 17 | if(state->topOfSelection) 18 | { 19 | if(line == *selected) 20 | return 1; 21 | } else { 22 | int j = 0; 23 | for(; selected[j] != -1; j++); 24 | 25 | if(line == selected[j - 1]) 26 | return 1; 27 | } 28 | return 0; 29 | } 30 | 31 | enum statusFlags { 32 | IX_MODIFIED = 1, 33 | IX_MODIFIED_B = 1 << 1, 34 | WT_MODIFIED = 1 << 8, 35 | }; 36 | 37 | char * PrintStatus(char * statusString, unsigned int status) 38 | { 39 | char wtChar = ' '; 40 | char ixChar = ' '; 41 | 42 | if((status & WT_MODIFIED) > 0) 43 | wtChar = 'M'; 44 | 45 | if((status & IX_MODIFIED) > 0) 46 | ixChar = 'M'; 47 | 48 | //HACK: idk why this is how it is 49 | if((status & IX_MODIFIED_B) > 0) 50 | ixChar = 'M'; 51 | 52 | char tmp[256]; 53 | tmp[0] = '\0'; 54 | 55 | sprintf(tmp, "\e[31m%c\e[32m%c", wtChar, ixChar); 56 | strcat(statusString, tmp); 57 | return statusString; 58 | } 59 | 60 | void printLine(t_state * state, t_termLine * line) 61 | { 62 | int invertNum = line->invertNum ? 7 : 0; 63 | int invertText = line->invertText ? 7 : 0; 64 | char * statusString; 65 | statusString = malloc(sizeof(char) * 255); 66 | statusString[0] = '\0'; 67 | 68 | fprintf(state->tty, 69 | "\e[%dm%s%*d \e[%dm%s%-*.*s\e[0m\n\r", 70 | invertNum, 71 | line->numEscCode, 72 | line->numPadding, 73 | line->lineNum, 74 | invertText, 75 | line->textEscCode, 76 | line->nameLength, 77 | line->nameLength, 78 | line->text); 79 | 80 | free(statusString); 81 | } 82 | 83 | int isSelected(t_state * state, int lineNum) 84 | { 85 | for(int j = 0; state->selected[j] != -1; j++){ 86 | if(lineNum == state->selected[j]) 87 | { 88 | return 1; 89 | } 90 | } 91 | return 0; 92 | } 93 | 94 | //draws to terminal 95 | void draw(t_state * state) 96 | { 97 | t_termLine * line = malloc(sizeof(t_termLine)); 98 | t_theme * theme = state->theme; 99 | line->numPadding = (int)ceil(log10((double)*state->dirCount + 1)); 100 | fprintf(state->tty, "\033[J"); 101 | line->nameLength = state->config->nameLength; 102 | line->numEscCode = theme->numberLine; 103 | 104 | for(int i = getStart(state); 105 | i < getEnd(state); 106 | i++) 107 | { 108 | line->invertText = 0; 109 | line->invertNum = 0; 110 | line->textEscCode = theme->normal; 111 | 112 | line->lineNum = *state->selected - i > 0 ? *state->selected - i : (*state->selected - i) * -1; 113 | 114 | if(isCursorLine(state, i)) 115 | { 116 | line->lineNum = i + 1; 117 | line->invertNum = 1; 118 | } 119 | 120 | if(access(state->fileAttribArray[i]->name, X_OK) != -1) 121 | line->textEscCode = theme->executable; 122 | 123 | switch(state->fileAttribArray[i]->fileMode) { 124 | case REGULAR: 125 | break; 126 | case FIFO: 127 | line->textEscCode = theme->pipe; 128 | break; 129 | case DIRECTORY: 130 | line->textEscCode = theme->directory; 131 | break; 132 | case EXACUTABLE: 133 | line->textEscCode = theme->executable; 134 | break; 135 | } 136 | 137 | line->invertText = isSelected(state, i); 138 | line->text = state->fileAttribArray[i]->name; 139 | line->gitStatus = state->fileAttribArray[i]->gitStatus; 140 | printLine(state, line); 141 | } 142 | fprintf(state->tty, "%s\n\r", state->msg); 143 | fprintf(state->tty, "\033[%dA", getEnd(state) - getStart(state) + 1); 144 | free(line); 145 | } 146 | 147 | int CheckFlag(t_state * state, char arg) { 148 | switch (arg) { 149 | case 'a': 150 | toggleHidden(state); 151 | break; 152 | case 'h': 153 | printf( 154 | "Minifm help\n\r" 155 | " -a show hidden files\n\r" 156 | " -h show this\n\r" 157 | "Controls:\n\r" 158 | "j move down\n\r" 159 | "k move up\n\r" 160 | "/ search\n\r" 161 | "enter output file path\n\r" 162 | "escape close program \n\r" 163 | "G goto bottom\n\r" 164 | "gg goto top\n\r" 165 | "b output parent dir\n\r" 166 | "v visual mode\n\r" 167 | "^H toggle hidden\n\r" 168 | "dd delete file\n\r" 169 | "yy yank file(s)\n\r" 170 | "p put file(s)\n\r" 171 | "V select one\n\r" 172 | "o swap visual position\n\r" 173 | ); 174 | return 1; 175 | break; 176 | default: 177 | break; 178 | } 179 | return 0; 180 | } 181 | 182 | //TODO: remove trailing / if exists 183 | int CheckArgs(t_state * state, int argc, char * argv[]) { 184 | int line; 185 | for(int i = 0; i < argc; i++) 186 | { 187 | if(*argv[i] == '-') { 188 | if(strlen(argv[i]) > 1) { 189 | for(int j = 1; j < strlen(argv[i]); j++) { 190 | if(sscanf(&argv[i][j], "%d", &line) == 1) { 191 | for(int k = 0; k < line - 1; k++) 192 | moveDown(state); 193 | break; 194 | } 195 | if(CheckFlag(state, argv[i][j]) == 1) { 196 | return 1; 197 | } 198 | } 199 | } 200 | } else { 201 | if(isDir(argv[i])) 202 | { 203 | state->cwd = argv[1]; 204 | strcpy(state->cwd, argv[1]); 205 | *state->dirCount = countDir(state); 206 | } 207 | } 208 | } 209 | return 0; 210 | } 211 | 212 | int canMatch(mode mode, char * combo, struct actionNode * head) 213 | { 214 | int matchable = 0; 215 | char * cmpCombo; 216 | 217 | for(int i = 0; combo[i] > 48 && combo[i] < 47; i++); 218 | 219 | while (head) 220 | { 221 | if(strlen(combo) <= strlen(head->action->combo) && (head->action->mode & mode) > 0) 222 | { 223 | for(int j = 0; j < strlen(combo); j++) 224 | { 225 | if (combo[j] != head->action->combo[j]) 226 | break; 227 | if(j == strlen(combo) - 1) 228 | matchable = 1; 229 | } 230 | } 231 | head = head->nextNode; 232 | } 233 | return matchable; 234 | } 235 | 236 | //TODO: insert mode to rename files 237 | struct actionNode * initDefaultMappings() 238 | { 239 | struct actionNode * commands; 240 | 241 | commands = initList(initAction(NORMAL | VISUAL, "\x1b", escape)); 242 | listQueue(commands, initAction(NORMAL, "\r", enter)); 243 | listQueue(commands, initAction(NORMAL, "/", Search)); 244 | listQueue(commands, initAction(NORMAL, "j", moveDown)); 245 | listQueue(commands, initAction(NORMAL, "k", moveUp)); 246 | listQueue(commands, initAction(NORMAL, "gg", gotoTop)); 247 | listQueue(commands, initAction(NORMAL, "G", gotoBottom)); 248 | listQueue(commands, initAction(NORMAL, "b", backDir)); 249 | listQueue(commands, initAction(NORMAL, "\x8", toggleHidden)); 250 | listQueue(commands, initAction(NORMAL, "V", selectOne)); 251 | listQueue(commands, initAction(NORMAL | VISUAL, "dd", removeFile)); 252 | listQueue(commands, initAction(NORMAL | VISUAL, "yy", yank)); 253 | listQueue(commands, initAction(NORMAL, "p", put)); 254 | listQueue(commands, initAction(NORMAL, "\4", halfPageDown)); //ctrl-D 255 | listQueue(commands, initAction(NORMAL, "\x15", halfPageUp)); //ctrl-U 256 | listQueue(commands, initAction(NORMAL, "mkd", MkDir)); 257 | listQueue(commands, initAction(NORMAL, "mkf", MkFile)); 258 | 259 | listQueue(commands, initAction(NORMAL, "v", enterVisual)); 260 | listQueue(commands, initAction(VISUAL, "j", visualMoveDown)); 261 | listQueue(commands, initAction(VISUAL, "k", visualMoveUp)); 262 | listQueue(commands, initAction(VISUAL, "o", changeSelectionPos)); 263 | listQueue(commands, initAction(VISUAL, "\r", printSelected)); 264 | 265 | return commands; 266 | } 267 | 268 | int input(t_state * state, struct actionNode * commands) 269 | { 270 | char tmp[2] = {' ', '\0'}; 271 | char combo[256]; 272 | combo[0] = '\0'; 273 | char countStr[256]; 274 | char msg[256]; 275 | int countInt; 276 | 277 | countStr[0] = '\0'; 278 | combo[0] = '\0'; 279 | 280 | struct actionNode * commandPointer; 281 | commandPointer = commands; 282 | int containsNonNum = 0; 283 | while(1) 284 | { 285 | tmp[0] = getchar(); 286 | if(tmp[0] == 27 && (strlen(combo) > 1 || strlen(countStr))) 287 | { 288 | countStr[0] = '\0'; 289 | combo[0] = '\0'; 290 | countInt = 0; 291 | sprintf(state->msg, ""); 292 | draw(state); 293 | continue; 294 | } 295 | 296 | if(containsNonNum) 297 | { 298 | if (tmp[0] >= 48 && tmp[0] <= 57) 299 | { 300 | combo[0] = '\0'; 301 | countStr[0] = '\0'; 302 | countInt = 0; 303 | continue; 304 | } 305 | } else { 306 | if (tmp[0] >= 48 && tmp[0] <= 57) 307 | { 308 | strcat(countStr, tmp); 309 | sprintf(state->msg, "%s%s", countStr, combo); 310 | draw(state); 311 | continue; 312 | } 313 | } 314 | containsNonNum = 1; 315 | strcat(combo, tmp); 316 | 317 | if (!canMatch(state->mode, combo, commandPointer)) 318 | { 319 | combo[0] = '\0'; 320 | countStr[0] = '\0'; 321 | return 0; 322 | } 323 | sprintf(state->msg, "%s%s", countStr, combo); 324 | draw(state); 325 | 326 | while(commandPointer != NULL) 327 | { 328 | if (strcmp(combo, commandPointer->action->combo) == 0 && (commandPointer->action->mode & state->mode) > 0) 329 | { 330 | countInt = strtol(countStr, NULL, 10); 331 | countInt = countInt > 0 ? countInt : 1; 332 | for(int i = 0; i < countInt; i++) 333 | { 334 | if(1 == commandPointer->action->function(state)) 335 | { 336 | countStr[0] = '\0'; 337 | combo[0] = '\0'; 338 | sprintf(state->msg, ""); 339 | return 1; 340 | } 341 | } 342 | 343 | countStr[0] = '\0'; 344 | combo[0] = '\0'; 345 | sprintf(state->msg, ""); 346 | return 0; 347 | } 348 | commandPointer = commandPointer->nextNode; 349 | } 350 | commandPointer = commands; 351 | } 352 | return 0; 353 | } 354 | -------------------------------------------------------------------------------- /src/core.h: -------------------------------------------------------------------------------- 1 | #ifndef __CORE_H__ 2 | #define __CORE_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | #include "types.h" 8 | 9 | char * GetRepoRoot(); 10 | int enter(t_state * state); 11 | void draw(t_state * state); 12 | int input(t_state * state, struct actionNode * commands); 13 | struct actionNode * initDefaultMappings(); 14 | int CheckArgs(t_state * state, int argc, char * argv[]); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/info.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "info.h" 8 | #include 9 | #include 10 | 11 | //callback for comparing file names 12 | int compFunc(const void * a, const void * b) 13 | { 14 | t_fileAttrib ** aval = (t_fileAttrib **) a; 15 | int ascore = 0; 16 | ascore -= (*aval)->fileMode * 10; 17 | 18 | t_fileAttrib ** bval = (t_fileAttrib **) b; 19 | int bscore = 0; 20 | bscore -= (*bval)->fileMode * 10; 21 | 22 | if(strcmp((*aval)->name, (*bval)->name) >= 0) 23 | ascore++; 24 | else 25 | bscore++; 26 | 27 | return ascore - bscore; 28 | } 29 | 30 | file_mode CheckMode(char * fileName) { 31 | struct stat st; 32 | stat(fileName, &st); 33 | 34 | if (S_ISREG(st.st_mode)) 35 | return REGULAR; 36 | if (S_ISFIFO(st.st_mode)) 37 | return FIFO; 38 | if (S_ISDIR(st.st_mode)) 39 | return DIRECTORY; 40 | if (access(fileName, X_OK)) 41 | return DIRECTORY; 42 | return -1; 43 | } 44 | 45 | //change proccess dir 46 | //possible do this by having an update string array so it can keep 47 | //track of everything on its own 48 | void updateDirList(t_state * state) 49 | { 50 | int viewHidden = state->config->viewHidden; 51 | t_fileAttrib ** fileAttribArray = state->fileAttribArray; 52 | char fileName[PATH_MAX]; 53 | 54 | int dircount = countDir(state); 55 | DIR * dir; 56 | dir = opendir(state->cwd); 57 | if(!dir) 58 | exit(1); 59 | 60 | int tmp = *state->selected; 61 | free(state->selected); 62 | state->selected = malloc(sizeof(int) * dircount + 1); 63 | state->selected[0] = tmp; 64 | for(int i = 1; i <= countDir(state); i++) 65 | state->selected[i] = -1; 66 | 67 | struct dirent * ent; 68 | ent = readdir(dir); 69 | int ind = 0; 70 | while(ent != NULL){ 71 | 72 | if(ent->d_name[0] != '.' || viewHidden) 73 | { 74 | free(fileAttribArray[ind]); 75 | fileAttribArray[ind] = malloc(sizeof(t_fileAttrib)); 76 | fileAttribArray[ind]->name = malloc(sizeof(ent->d_name)); 77 | memset(fileAttribArray[ind]->name, '\0', sizeof(ent->d_name)); 78 | strcpy(fileAttribArray[ind]->name, ent->d_name); 79 | fileAttribArray[ind]->gitStatus = 0; 80 | sprintf(fileName, "%s/%s", state->cwd, fileAttribArray[ind]->name); 81 | fileAttribArray[ind]->fileMode = CheckMode(fileName); 82 | ind++; 83 | } 84 | ent = readdir(dir); 85 | } 86 | closedir(dir); 87 | qsort(fileAttribArray, dircount, sizeof(char *), compFunc); 88 | } 89 | 90 | //determin how many remaining files can be drawn 91 | int getEnd(t_state * state) 92 | { 93 | if (*state->dirCount < state->config->viewRange || 94 | *state->selected + state->config->viewRange/2 > *state->dirCount) 95 | return *state->dirCount; 96 | else if(*state->selected < state->config->viewRange/2) 97 | { 98 | return state->config->viewRange; 99 | } else { 100 | return *state->selected + state->config->viewRange/2; 101 | } 102 | } 103 | 104 | //determin how many previous files can be drawn 105 | int getStart(t_state * state) 106 | { 107 | if (*state->selected > state->config->viewRange/2) 108 | { 109 | if (*state->dirCount - *state->selected >= state->config->viewRange/2) 110 | return *state->selected - state->config->viewRange/2; 111 | if(*state->dirCount - state->config->viewRange < 0) 112 | return 0; 113 | else 114 | return *state->dirCount - state->config->viewRange; 115 | } 116 | else 117 | return 0; 118 | } 119 | 120 | #include 121 | #include 122 | 123 | //count files in dir 124 | int countDir(t_state * state) 125 | { 126 | int viewHidden = state->config->viewHidden; 127 | static int count = 0; 128 | count++; 129 | DIR * dir; 130 | dir = opendir(state->cwd); 131 | 132 | struct dirent * ent; 133 | 134 | int dirCount = 0; 135 | while ((ent = readdir(dir))) { 136 | if(ent->d_name[0] != '.' || viewHidden) 137 | dirCount++; 138 | } 139 | closedir(dir); 140 | return dirCount; 141 | } 142 | 143 | int isDir(char * folder) 144 | { 145 | struct stat sb; 146 | 147 | if (stat(folder, &sb) == 0 && S_ISDIR(sb.st_mode)) { 148 | return 1; 149 | } else { 150 | return 0; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/info.h: -------------------------------------------------------------------------------- 1 | #ifndef __INFO_H__ 2 | #define __INFO_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | #include "types.h" 8 | 9 | int countDir(t_state * state); 10 | void updateDirList(t_state * state); 11 | int isDir(char * folder); 12 | int getEnd(t_state * state); 13 | int getStart(t_state * state); 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "info.h" 10 | #include "core.h" 11 | 12 | //TODO: convert bool nums to use true and false vars 13 | 14 | /*FIXME: make all actions work while in an empty directory 15 | unchecked actions: enterVisual, printSelected, 16 | visualMoveDown, visualMoveUp, changeSelectionPos, selectOne, Search 17 | moveDown, moveUp, halfPageDown, halfPageUp, freeAction 18 | gotoTop, gotoBottom, backDir, yank, put */ 19 | 20 | int main(int argc, char * argv[]) { 21 | t_config * config = DefaultConfig(); 22 | t_theme * theme = DefaultTheme(); 23 | t_state * state = InitState(config, theme); 24 | 25 | struct actionNode * commands = initDefaultMappings(); 26 | updateDirList(state); 27 | int done; 28 | done = CheckArgs(state, argc, argv); 29 | 30 | // program loop 31 | while(!done){ 32 | draw(state); 33 | done = input(state, commands); 34 | } 35 | 36 | FreeState(state); 37 | } 38 | -------------------------------------------------------------------------------- /src/types.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include 3 | #include 4 | #include "info.h" 5 | 6 | void freeFileAttrib(t_fileAttrib * fileAttrib) 7 | { 8 | free(fileAttrib->name); 9 | free(fileAttrib); 10 | } 11 | 12 | t_theme * DefaultTheme() { 13 | t_theme * theme = malloc(sizeof(t_theme)); 14 | theme->normal = "\033[49;39m"; 15 | theme->numberLine = "\033[49;90m"; 16 | theme->directory = "\033[49;34m"; 17 | theme->gitWarn = "\033[49;31m"; 18 | theme->gitAdd = "\033[49;32m"; 19 | theme->executable = "\033[49;92m"; 20 | theme->pipe = "\033[49;33m"; 21 | return theme; 22 | } 23 | 24 | t_config * DefaultConfig() { 25 | t_config * config = malloc(sizeof(t_config)); 26 | 27 | config->viewHidden = 0; 28 | config->viewRange = 12; 29 | config->nameLength = 15; 30 | 31 | return config; 32 | } 33 | 34 | #include 35 | #include 36 | char * GetRepoRoot() 37 | { 38 | char cwd[PATH_MAX]; 39 | char tmpPath[PATH_MAX]; 40 | strcpy(cwd, getenv("PWD")); 41 | int error = 1; 42 | 43 | while(error) 44 | { 45 | sprintf(tmpPath, "%s/.git", cwd); 46 | if( access( tmpPath, F_OK ) == 0 ) { 47 | error = 0; 48 | } else { 49 | error = 1; 50 | } 51 | 52 | if(error) 53 | *strrchr(cwd, '/') = '\0'; 54 | } 55 | 56 | if(strcmp(getenv("HOME"), cwd) == 0) 57 | cwd[0] = '\0'; 58 | 59 | char * outString = malloc(sizeof(char) * strlen(cwd)); 60 | strcpy(outString, cwd); 61 | return outString; 62 | } 63 | 64 | void SetSelected(t_state * state) { 65 | for(int i = 1; i < *state->dirCount + 1; i++) 66 | { 67 | state->selected[i] = -1; 68 | } 69 | state->selected[0] = 0; 70 | } 71 | 72 | t_state * InitState(t_config * config, t_theme * theme) { 73 | t_state * state = malloc(sizeof(t_state)); 74 | int * dirCount = malloc(sizeof(int)); 75 | state->theme = theme; 76 | state->config = config; 77 | 78 | state->cwd = malloc(sizeof(char) * PATH_MAX); 79 | getcwd(state->cwd, sizeof(char) * PATH_MAX); 80 | *dirCount = countDir(state); 81 | state->dirCount = dirCount; 82 | state->tty = fopen("/dev/tty", "w"); 83 | state->fileAttribArray = malloc(sizeof(t_fileAttrib) * 10000); 84 | state->selected = malloc(sizeof(int) * *dirCount + 1); 85 | state->mode = NORMAL; 86 | state->topOfSelection = 1; 87 | state->msg = malloc(sizeof(char) * PATH_MAX); 88 | state->msg[0] = '\0'; 89 | state->flags = 0; 90 | SetSelected(state); 91 | 92 | tcgetattr( STDIN_FILENO, &state->oldt); 93 | cfmakeraw(&state->newt); 94 | tcsetattr( STDIN_FILENO, TCSANOW, &state->newt); 95 | fprintf(state->tty, "\e[?25l"); 96 | 97 | return state; 98 | } 99 | 100 | void FreeState(t_state * state) { 101 | fprintf(state->tty, "\033[J"); 102 | tcsetattr(STDIN_FILENO, TCSANOW, &state->oldt); 103 | fprintf(state->tty, "\e[?25h"); 104 | 105 | // HACK: crashes when i free why??? 106 | // free(state->cwd); 107 | free(state->dirCount); 108 | free(state->config); 109 | free(state->msg); 110 | free(state->selected); 111 | free(state); 112 | } 113 | -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | #ifndef __TYPES_H__ 2 | #define __TYPES_H__ 3 | 4 | #include 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | #include 9 | #include 10 | 11 | #define true 1 12 | #define false 0 13 | 14 | typedef enum{ 15 | NORMAL = 1, 16 | INSERT = 2, 17 | VISUAL = 4, 18 | } mode; 19 | 20 | typedef enum { 21 | FIFO, 22 | REGULAR, 23 | EXACUTABLE, 24 | DIRECTORY, 25 | } file_mode; 26 | 27 | typedef struct s_fileAttrib { 28 | char * name; 29 | file_mode fileMode; 30 | unsigned int gitStatus; 31 | } t_fileAttrib; 32 | 33 | typedef struct theme { 34 | char * normal; 35 | char * numberLine; 36 | char * gitWarn; 37 | char * gitAdd; 38 | char * directory; 39 | char * link; 40 | char * pipe; 41 | char * executable; 42 | } t_theme; 43 | 44 | typedef struct config { 45 | int viewHidden; 46 | int viewRange; 47 | int nameLength; 48 | } t_config; 49 | 50 | typedef enum { 51 | OUTPUT_LINE_NUM = 1 52 | } t_flags; 53 | 54 | 55 | typedef struct state{ 56 | //data 57 | mode mode; 58 | int * dirCount; 59 | int * selected; 60 | char * msg; 61 | char * cwd; 62 | t_fileAttrib ** fileAttribArray; 63 | FILE * tty; 64 | int topOfSelection; 65 | struct termios oldt; 66 | struct termios newt; 67 | t_flags flags; 68 | 69 | //config 70 | t_config * config; 71 | t_theme * theme; 72 | } t_state; 73 | 74 | typedef struct termLine { 75 | int invertText; 76 | char * textEscCode; 77 | int textColourBg; 78 | int textColourFg; 79 | char * text; 80 | int invertNum; 81 | char * numEscCode; 82 | int numFg; 83 | int numBg; 84 | int lineNum; 85 | int numPadding; 86 | int nameLength; 87 | int gitStatus; 88 | } t_termLine; 89 | 90 | typedef struct { 91 | mode mode; 92 | char * combo; 93 | int (*function)(t_state * state); 94 | } t_action; 95 | 96 | struct actionNode { 97 | t_action * action; 98 | struct actionNode * nextNode; 99 | struct actionNode * tail; 100 | }; 101 | 102 | void freeFileAttrib(t_fileAttrib * fileAttrib); 103 | t_theme * DefaultTheme(); 104 | t_config * DefaultConfig(); 105 | t_state * InitState(t_config * config, t_theme * theme); 106 | void FreeState(t_state * state); 107 | 108 | #ifdef __cplusplus 109 | } 110 | #endif 111 | 112 | #endif 113 | --------------------------------------------------------------------------------