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