1 /*
2 DF-SHOW: An interactive directory/file browser written for Unix-like systems.
3 Based on the applications from the PC-DOS DF-EDIT suite by Larry Kroeker.
4 Copyright (C) 2018-2021 Robert Ian Hawdon
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #define _GNU_SOURCE
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <ncurses.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <wctype.h>
28 #include <dirent.h>
29 #include <sys/stat.h>
30 #include <libconfig.h>
31 #include <wchar.h>
32 #include <signal.h>
33 #include <math.h>
34 #include <sys/wait.h>
35 #include "menu.h"
36 #include "colors.h"
37 #include "config.h"
38 #include "common.h"
39
40 DIR *folder;
41 FILE *file;
42
43 int exitCode = 0;
44 int enableCtrlC = 0;
45
46 char globalConfLocation[128];
47 char homeConfLocation[128];
48
49 char themeName[128] = "default";
50
51 char errmessage[256];
52
53 extern int * pc;
54
55 extern int resized;
56
57 void refreshScreen(); // This reference needs to exist to allow getch10th to be common.
58
59 void unloadMenuLabels();
60
61 void freeSettingVars();
62
getch10th(void)63 int getch10th (void) {
64 int ch;
65 do {
66 if (resized) {
67 resized = 0;
68 refreshScreen();
69 }
70 halfdelay (1);
71 ch = getch();
72 } while (ch == ERR || ch == KEY_RESIZE);
73 return ch;
74 }
75
splitPath(pathDirs ** dirStruct,char * path)76 int splitPath(pathDirs **dirStruct, char *path){
77 int e, i, j, c;
78 pathDirs *tmp;
79
80 tmp = malloc(sizeof(pathDirs));
81 if (tmp){
82 *dirStruct = tmp;
83 }
84
85 e = -1;
86 j = 0;
87 c = strlen(path);
88
89 for(i = 0; i < c; i++){
90 if (path[i] == '/'){
91 if (e > -1){
92 (*dirStruct)[e].directories[j] = '\0';
93 if(!strcmp((*dirStruct)[e].directories, "..")){
94 // assmue .. and remove the element before
95 (*dirStruct)[e] = (*dirStruct)[e - 1];
96 (*dirStruct)[e - 1] = (*dirStruct)[e - 2];
97 e--;
98 (*dirStruct) = realloc((*dirStruct), sizeof(pathDirs) * (2 + e));
99 } else if (!strcmp((*dirStruct)[e].directories, ".")){
100 // strip single .
101 strcpy((*dirStruct)[e].directories, "\0");
102 } else {
103 // If element created is NOT ..
104 e++;
105 (*dirStruct) = realloc((*dirStruct), sizeof(pathDirs) * (2 + e));
106 }
107 } else {
108 e++;
109 (*dirStruct) = realloc((*dirStruct), sizeof(pathDirs) * (2 + e));
110 }
111 j=0;
112 } else {
113 (*dirStruct)[e].directories[j] = path[i];
114 j++;
115 }
116 }
117 (*dirStruct)[e].directories[j] = '\0';
118 if (!strcmp((*dirStruct)[e].directories, ".")){
119 strcpy((*dirStruct)[e].directories, "");
120 e--;
121 }
122
123 return(e);
124 }
125
createParentsInput(char * path)126 int createParentsInput(char *path)
127 {
128 int result = 0;
129 int messageLen;
130 wchar_t *message = malloc(sizeof(wchar_t) * 1);
131
132 messageLen = (strlen(path) + 64);
133
134 message = realloc(message, sizeof(wchar_t) * (messageLen + 1));
135
136 swprintf(message, messageLen, L"Directory [<%s>] does not exist. Create it? !Yes/!No (enter = no)", path);
137 wPrintMenu(0,0, message);
138 while(1)
139 {
140 *pc = getch10th();
141 if (*pc == 'y'){
142 result = 1;
143 break;
144 } else if ((*pc == 'n') || (*pc == 10)){
145 result = 0;
146 break;
147 } else if (*pc == 27){
148 result = -1;
149 break;
150 }
151 }
152 free(message);
153 return(result);
154 }
155
createParentDirs(char * path)156 void createParentDirs(char *path){
157 pathDirs *targetPath;
158 char *tempPath = malloc(sizeof(char) + 1);
159 int e, i = 0;
160
161 e = splitPath(&targetPath, path);
162
163 strcpy(tempPath, "");
164 for (i = 0; i < e; i++){
165 tempPath = realloc(tempPath, sizeof(char) * (strlen(tempPath) + strlen(targetPath[i].directories) + 2));
166 sprintf(tempPath, "%s/%s", tempPath, targetPath[i].directories);
167 if (!check_dir(tempPath)){
168 mk_dir(tempPath);
169 }
170 }
171
172 free(targetPath);
173 free(tempPath);
174 return;
175 }
176
mk_dir(char * path)177 void mk_dir(char *path)
178 {
179 struct stat st = {0};
180
181 if (stat(path, &st) == -1) {
182 mkdir(path, 0755);
183 }
184 }
185
setConfLocations()186 void setConfLocations()
187 {
188 sprintf(globalConfLocation, "%s/%s", SYSCONFIG, CONF_NAME);
189
190 sprintf(homeConfLocation, "%s/%s/%s", getenv("HOME"), HOME_CONF_DIR, CONF_NAME);
191 }
192
exittoshell()193 int exittoshell()
194 {
195 clear();
196 unloadMenuLabels();
197 freeSettingVars();
198 endwin();
199 exit(exitCode);
200 return exitCode;
201 }
202
dirFromPath(const char * myStr)203 char * dirFromPath(const char* myStr){
204 char *outStr;
205 int i = strlen(myStr);
206 int n = 0;
207
208 while(i <= strlen(myStr) && myStr[i] != '/'){
209 i--;
210 }
211
212 outStr = malloc(sizeof (char) * (i + 2));
213
214 if (i < 2){
215 strcpy(outStr, "/");
216 } else{
217 while(n <= i){
218 outStr[n] = myStr[n];
219 n++;
220 }
221
222 outStr[n - 1] = '\0';
223 }
224
225 return outStr;
226
227 }
228
objectFromPath(const char * myStr)229 char * objectFromPath(const char *myStr){
230 char *outStr;
231 int i = strlen(myStr);
232 int n = 0;
233 int c = 0;
234
235 while(i <= strlen(myStr) && myStr[i] != '/'){
236 i--;
237 n++;
238 }
239
240 outStr = malloc(sizeof (char) * n);
241
242 i++; // Removes the initial /
243
244 for(; i < strlen(myStr); c++){
245 outStr[c] = myStr[i];
246 i++;
247 }
248
249 outStr[n - 1] = '\0';
250 return outStr;
251
252 }
253
printVersion(char * programName)254 void printVersion(char* programName){
255 printf (("Directory File Show (DF-SHOW) - %s %s\n"), programName, VERSION);
256 fputs (("\
257 Copyright (C) 2021 Robert Ian Hawdon\n\
258 License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.\n\
259 This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n\
260 are welcome to redistribute it under certain conditions.\n"), stdout);
261 }
262
check_dir(char * pwd)263 int check_dir(char *pwd)
264 {
265 const char *path = pwd;
266 struct stat sb;
267 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)){
268 folder = opendir ( path );
269 if (access ( path, F_OK ) != -1 ){
270 if ( folder ){
271 closedir ( folder );
272 return 1;
273 } else {
274 return 0;
275 }
276 } else {
277 return 0;
278 }
279 }
280 return 0;
281 }
282
check_file(char * file)283 int check_file(char *file){
284 if( access( file, F_OK ) != -1 ) {
285 return 1;
286 } else {
287 return 0;
288 }
289 }
290
check_exec(const char * object)291 int check_exec(const char *object)
292 {
293 if (access(object, X_OK) == 0) {
294 return 1;
295 } else {
296 return 0;
297 }
298 }
299
check_last_char(const char * str,const char * chk)300 int check_last_char(const char *str, const char *chk)
301 {
302 if (!strcmp(&str[strlen(str) - 1], chk)){
303 return 1;
304 } else {
305 return 0;
306 }
307 }
308
check_first_char(const char * str,const char * chk)309 int check_first_char(const char *str, const char *chk)
310 {
311 if (str[0] == chk[0]){
312 return 1;
313 } else {
314 return 0;
315 }
316 }
317
check_numbers_only(const char * s)318 int check_numbers_only(const char *s)
319 {
320 while (*s) {
321 if (isdigit(*s++) == 0) return 0;
322 }
323
324 return 1;
325 }
326
327 // Credit for the following function must go to this guy:
328 // https://stackoverflow.com/a/779960
str_replace(char * orig,char * rep,char * with)329 char *str_replace(char *orig, char *rep, char *with) {
330 char *result; // the return string
331 char *ins; // the next insert point
332 char *tmp; // varies
333 int len_rep; // length of rep (the string to remove)
334 int len_with; // length of with (the string to replace rep with)
335 int len_front; // distance between rep and end of last rep
336 int count; // number of replacements
337
338 // sanity checks and initialization
339 if (!orig || !rep)
340 return NULL;
341 len_rep = strlen(rep);
342 if (len_rep == 0)
343 return NULL; // empty rep causes infinite loop during count
344 if (!with)
345 with = "";
346 len_with = strlen(with);
347
348 // count the number of replacements needed
349 ins = orig;
350 for (count = 0; (tmp = strstr(ins, rep)); ++count) {
351 ins = tmp + len_rep;
352 }
353
354 tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);
355
356 if (!result)
357 return NULL;
358
359 // first time through the loop, all the variable are set correctly
360 // from here on,
361 // tmp points to the end of the result string
362 // ins points to the next occurrence of rep in orig
363 // orig points to the remainder of orig after "end of rep"
364 while (count--) {
365 ins = strstr(orig, rep);
366 len_front = ins - orig;
367 tmp = strncpy(tmp, orig, len_front) + len_front;
368 tmp = strcpy(tmp, with) + len_with;
369 orig += len_front + len_rep; // move to next "end of rep"
370 }
371 strcpy(tmp, orig);
372 return result;
373 }
374
read_line(FILE * fin)375 char * read_line(FILE *fin) {
376 char *buffer;
377 char *tmp;
378 int read_chars = 0;
379 int bufsize = 8192;
380 char *line = malloc(bufsize);
381
382 if ( !line ) {
383 return NULL;
384 }
385
386 buffer = line;
387
388 while ( fgets(buffer, bufsize - read_chars, fin) ) {
389 read_chars = strlen(line);
390
391 if ( line[read_chars - 1] == '\n' ) {
392 line[read_chars - 1] = '\0';
393 return line;
394 }
395
396 else {
397 bufsize = 2 * bufsize;
398 tmp = realloc(line, bufsize);
399 if ( tmp ) {
400 line = tmp;
401 buffer = line + read_chars;
402 }
403 else {
404 free(line);
405 return NULL;
406 }
407 }
408 }
409 return NULL;
410 }
411
showManPage(const char * prog)412 void showManPage(const char * prog)
413 {
414 char mancmd[10];
415 int i;
416 sprintf(mancmd, "man %s", prog);
417 clear();
418 system("clear"); // Needed to ensure man pages display correctly
419 system(mancmd);
420 }
421
can_run_command(const char * cmd)422 int can_run_command(const char *cmd) {
423 const char *path = getenv("PATH");
424 char *buf;
425 char *p;
426 if(cmd[0] == '\0'){
427 return 0;
428 }
429 if(strchr(cmd, '/')) {
430 return access(cmd, X_OK)==0;
431 }
432 if(!path){
433 return -1; // something is horribly wrong...
434 }
435 buf = malloc(strlen(path)+strlen(cmd)+3);
436 for(; *path; ++path) {
437 p = buf;
438 for(; *path && *path!=':'; ++path,++p) {
439 *p = *path;
440 }
441 if(p==buf) *p++='.';
442 if(p[-1]!='/') *p++='/';
443 strcpy(p, cmd);
444 if(access(buf, X_OK)==0) {
445 free(buf);
446 return 1;
447 }
448 if(!*path) break;
449 }
450 free(buf);
451 return 0;
452 }
453
commandFromPath(const char * cmd)454 char * commandFromPath(const char *cmd) {
455 const char *path = getenv("PATH");
456 char *outStr;
457 char *p;
458 if (cmd[0] == '\0'){
459 outStr = malloc(sizeof(char) + 1);
460 outStr[0] = '\0';
461 return outStr;
462 }
463 if(strchr(cmd, '/')) {
464 free(outStr);
465 outStr = malloc(strlen(cmd)+1);
466 sprintf(outStr, "%s", cmd);
467 return outStr;
468 }
469 if(!path){
470 return NULL; // something is horribly wrong...
471 }
472 outStr = malloc(strlen(path)+strlen(cmd)+3);
473 for(; *path; ++path) {
474 p = outStr;
475 for(; *path && *path!=':'; ++path,++p) {
476 *p = *path;
477 }
478 if(p==outStr) *p++='.';
479 if(p[-1]!='/') *p++='/';
480 strcpy(p, cmd);
481 if(access(outStr, X_OK)==0) {
482 return outStr;
483 }
484 if(!*path) break;
485 }
486 free(outStr);
487 outStr = malloc(sizeof(char) + 1);
488 outStr[0] = '\0';
489 return outStr;
490 }
491
countArguments(const char * cmd)492 int countArguments(const char *cmd)
493 {
494 int i, cmdLen, countArgs;
495 bool reset = true;
496 bool quote = false;
497 bool doubleQuote = true;
498
499 // Getting the length of the input
500 cmdLen = strlen(cmd);
501
502 countArgs = 1;
503
504 // First sweep to get the number of args, and length
505 for (i = 0; i < (cmdLen); i++){
506 if (cmd[i] == '"'){
507 if (!doubleQuote){
508 doubleQuote = true;
509 } else {
510 doubleQuote = false;
511 }
512 }
513 if (cmd[i] == '\'' && !doubleQuote){
514 if (!quote){
515 quote = true;
516 } else {
517 quote = false;
518 }
519 }
520 if (cmd[i] == ' '){
521 if (!quote){
522 if (!reset){
523 countArgs++;
524 }
525 reset = true;
526 }
527 } else {
528 reset = false;
529 }
530 }
531
532 return countArgs;
533
534 }
535
buildCommandArguments(const char * cmd,char ** args,size_t items)536 void buildCommandArguments(const char *cmd, char **args, size_t items)
537 {
538 int j, k, cmdLen; // , countArgs;
539 int i, itemCount, argCharCount;
540 int cmdPos = 0;
541 int cmdOffset = 0;
542 int* itemLen = (int*) malloc(items * sizeof(int));
543 int* itemLen_copy = itemLen;
544 bool reset = true;
545 bool quote = false;
546 bool doubleQuote = false;
547 char *tempStr;
548
549 // Getting the length of the input
550 cmdLen = strlen(cmd);
551
552 // endwin();
553 // First sweep to get the number of args, and length
554 itemCount = 0;
555 argCharCount = 0;
556 itemLen[0] = 0;
557 for (i = 0; i < (cmdLen); i++){
558 if (cmd[i] == '"'){
559 if (!doubleQuote){
560 doubleQuote = true;
561 } else {
562 doubleQuote = false;
563 }
564 }
565 if (cmd[i] == '\'' && !doubleQuote){
566 if (!quote){
567 quote = true;
568 } else {
569 quote = false;
570 }
571 }
572 if (cmd[i] == ' '){
573 if (!quote){
574 if (!reset){
575 itemCount++;
576 argCharCount = 0;
577 }
578 reset = true;
579 } else {
580 argCharCount++;
581 }
582 } else {
583 argCharCount++;
584 itemLen[itemCount] = argCharCount;
585 reset = false;
586 }
587 }
588
589 // We need one more as the last argument MUST be NULL
590 // countArgs++;
591
592 for (i = 0; i < (itemCount + 1); i++){
593 tempStr = calloc(itemLen[i] + 1, sizeof(char));
594 args[i] = calloc(itemLen[i] + 1, sizeof(char));
595 for (k = 0; k < itemLen[i]; k++){
596 if ((cmdPos + cmdOffset + k) > cmdLen){
597 // Hacky workaround to avoid buffer overflow
598 break;
599 }
600 checkBlank:
601 if (cmd[cmdPos + cmdOffset + k] == ' ' && k == 0){
602 cmdOffset++;
603 goto checkBlank;
604 }
605 if (cmd[cmdPos + cmdOffset + k] == '\'' && !doubleQuote){
606 cmdOffset++;
607 }
608 if (cmd[cmdPos + cmdOffset + k] == '"'){
609 if (!doubleQuote){
610 doubleQuote = true;
611 } else {
612 doubleQuote = false;
613 cmdOffset++;
614 }
615 cmdOffset++;
616 }
617 tempStr[k] = cmd[cmdPos + cmdOffset + k];
618 if (k == (itemLen[i] - 1)){
619 tempStr[k + 1] = '\0';
620 }
621 }
622 strcpy(args[i], tempStr);
623 cmdPos += itemLen[i];
624 free(tempStr);
625 }
626
627 args[itemCount + 1] = NULL;
628
629 free(itemLen_copy);
630 return;
631
632 }
633
launchExternalCommand(char * cmd,char ** args,ushort_t mode)634 int launchExternalCommand(char *cmd, char **args, ushort_t mode)
635 {
636 sigset_t newMask, oldMask;
637 pid_t parent = getpid();
638 pid_t pid;
639 int i;
640
641 sigemptyset(&newMask);
642 sigemptyset(&oldMask);
643
644 // char *arguments[] = {cmd, *args, NULL};
645
646
647 if (mode == M_NORMAL){
648 curs_set(TRUE);
649 echo();
650 nocbreak();
651 keypad(stdscr, FALSE);
652 endwin();
653 } else if (mode == M_NONE) {
654 erase();
655 refresh();
656 }
657
658 pid = fork();
659 if (pid == -1){
660 // Catch error
661 return -1;
662 } else if ( pid == 0) {
663 execv(cmd, args);
664 _exit(EXIT_FAILURE);
665 } else if ( pid > 0 ) {
666 int status;
667 sigaddset(&newMask, SIGWINCH);
668 sigprocmask(SIG_BLOCK, &newMask, &oldMask);
669 waitpid(pid, &status, 0);
670 sigprocmask(SIG_SETMASK, &oldMask, NULL);
671 }
672 refreshScreen();
673 return 0;
674 }
675
sigintHandle(int sig)676 void sigintHandle(int sig){
677 // Does nothing
678 }
679
680