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 <ncurses.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <regex.h>
26 #include "menu.h"
27 #include "display.h"
28 #include "settings.h"
29 #include "common.h"
30 #include "sf.h"
31 #include "colors.h"
32 #include "input.h"
33 
34 int c;
35 int * pc = &c;
36 
37 int abortinput = 0;
38 
39 bool findSet = false;
40 
41 menuDef *fileMenu;
42 int fileMenuSize = 0;
43 wchar_t *fileMenuLabel;
44 
45 menuDef *caseMenu;
46 int caseMenuSize = 0;
47 wchar_t *caseMenuLabel;
48 
49 extern menuDef *settingsMenu;
50 extern int settingsMenuSize;
51 extern wchar_t *settingsMenuLabel;
52 
53 extern char regexinput[1024];
54 extern FILE *file;
55 extern int topline;
56 extern int leftcol;
57 extern char fileName[4096];
58 extern int displaysize;
59 extern int totallines;
60 extern int longestlongline;
61 extern int viewmode;
62 
63 extern int wrap;
64 extern int wrapmode;
65 
66 extern FILE *stream;
67 extern char *line;
68 
69 extern long int *filePos;
70 extern wchar_t *longline;
71 
generateDefaultMenus()72 void generateDefaultMenus(){
73   // File Menu
74   addMenuItem(&fileMenu, &fileMenuSize, "f_01", L"<F1>-Down", 265);
75   addMenuItem(&fileMenu, &fileMenuSize, "f_02", L"<F2>-Up", 266);
76   addMenuItem(&fileMenu, &fileMenuSize, "f_03", L"<F3>-Top", 267);
77   addMenuItem(&fileMenu, &fileMenuSize, "f_04", L"<F4>-Bottom", 268);
78   addMenuItem(&fileMenu, &fileMenuSize, "f_config", L"!Config", 'c');
79   addMenuItem(&fileMenu, &fileMenuSize, "f_find", L"!Find", 'f');
80   addMenuItem(&fileMenu, &fileMenuSize, "f_help", L"!Help", 'h');
81   addMenuItem(&fileMenu, &fileMenuSize, "f_position", L"!Position", 'p');
82   addMenuItem(&fileMenu, &fileMenuSize, "f_quit", L"!Quit", 'q');
83   if (wrap){
84     addMenuItem(&fileMenu, &fileMenuSize, "f_wrap", L"!Wrap-off", 'w');
85   } else {
86     addMenuItem(&fileMenu, &fileMenuSize, "f_wrap", L"!Wrap-on", 'w');
87   }
88 
89   // Case Menu
90   addMenuItem(&caseMenu, &caseMenuSize, "c1_ignore", L"!Ignore-case", 'i');
91   addMenuItem(&caseMenu, &caseMenuSize, "c2_sensitive", L"!Case-sensitive", 'c');
92 
93   // Setings Menu
94   addMenuItem(&settingsMenu, &settingsMenuSize, "s_quit", L"!Quit", 'q');
95   addMenuItem(&settingsMenu, &settingsMenuSize, "s_revert", L"!Revert", 'r');
96   addMenuItem(&settingsMenu, &settingsMenuSize, "s_save", L"!Save", 's');
97 }
98 
refreshMenuLabels()99 void refreshMenuLabels(){
100   fileMenuLabel     = genMenuDisplayLabel(L"", fileMenu, fileMenuSize, L"", 1);
101   caseMenuLabel     = genMenuDisplayLabel(L"", caseMenu, caseMenuSize, L"(enter = I)", 0);
102   settingsMenuLabel = genMenuDisplayLabel(L"SF Settings Menu -", settingsMenu, settingsMenuSize, L"", 1);
103 }
104 
unloadMenuLabels()105 void unloadMenuLabels(){
106   free(fileMenuLabel);
107   free(caseMenuLabel);
108   free(settingsMenuLabel);
109 }
110 
show_file_find(bool charcase,bool useLast)111 void show_file_find(bool charcase, bool useLast)
112 {
113   int regexcase;
114   int result;
115   char inputmessage[32];
116   char errormessage[1024];
117   if (!useLast){
118     if (charcase){
119       regexcase = 0;
120       strcpy(inputmessage, "Match Case - Enter string:");
121     } else {
122       regexcase = REG_ICASE;
123       strcpy(inputmessage, "Ignore Case - Enter string:");
124     }
125     move(0,0);
126     clrtoeol();
127     mvprintw(0, 0, inputmessage);
128     curs_set(TRUE);
129     move(0, strlen(inputmessage) + 1);
130     curs_set(FALSE);
131     if (readline(regexinput, 1024, regexinput) == -1 ){
132       abortinput = 1;
133     }
134   }
135   if (abortinput != 1) {
136     findSet = true;
137     result = findInFile(fileName, regexinput, regexcase);
138     if ( result > 0 ){
139       topline = result;
140       updateView();
141     } else if ( result == -2 ){
142       // Not a feature in DF-EDIT 2.3d, but a nice to have
143       sprintf(errormessage, "No further references to '%s' found.", regexinput);
144       topLineMessage(errormessage);
145     }
146   }
147 }
148 
show_file_find_case_input()149 int show_file_find_case_input()
150 {
151   int result;
152   move(0,0);
153   clrtoeol();
154   wPrintMenu(0,0,caseMenuLabel);
155   while(1)
156     {
157       *pc = getch10th();
158       if (*pc == menuHotkeyLookup(caseMenu, "c1_ignore", caseMenuSize) || *pc == 10){
159         result = 0;
160         break;
161       } else if (*pc == menuHotkeyLookup(caseMenu, "c2_sensitive", caseMenuSize)){
162         result = 1;
163         break;
164       } else if (*pc == 27){
165         // ESC
166         result = -1;
167         break;
168       }
169     }
170   return(result);
171 }
172 
show_file_position_input(int currentpos)173 void show_file_position_input(int currentpos)
174 {
175   char newpos[11];
176   char *filePosText;
177   int filePosTextLen;
178   int status;
179   // Fun fact, in DF-EDIT 2.3d, the following text input typoed "absolute" as "absolue", this typo also exists in the Windows version from 1997 (2.3d-76), however, the 1986 documentation correctly writes it as "absolute".
180   filePosTextLen = snprintf(NULL, 0, "Position relative (<+num> || <-num>) or absolute (<num>):");
181   filePosText = malloc(sizeof(char) * (filePosTextLen + 1));
182   sprintf(filePosText, "Position relative (<+num> || <-num>) or absolute (<num>):");
183   viewmode = 2;
184   move(0,0);
185   clrtoeol();
186   printMenu(0,0,filePosText);
187   free(filePosText);
188   curs_set(TRUE);
189   move(0,52);
190   status = readline(newpos, 11, ""); // DF-EDIT defaulted to 0, but it also defaulted to overtype mode, so for ease of use, we'll leave the default blank.
191   curs_set(FALSE);
192   if ((status != -1) && (strcmp(newpos,"") != 0)){
193     if (check_first_char(newpos, "+")){
194       memmove(newpos, newpos+1, strlen(newpos));
195       if (check_numbers_only(newpos)){
196         topline = topline + atoi(newpos);
197       }
198     } else if (check_first_char(newpos, "-")) {
199       memmove(newpos, newpos+1, strlen(newpos));
200       if (check_numbers_only(newpos)){
201         topline = topline - atoi(newpos);
202       }
203     } else {
204       if (check_numbers_only(newpos)){
205         topline = atoi(newpos);
206       }
207     }
208   }
209   wPrintMenu(0, 0, fileMenuLabel);
210 }
211 
show_file_inputs()212 void show_file_inputs()
213 {
214   int e = 0;
215   wPrintMenu(0, 0, fileMenuLabel);
216   while(1)
217     {
218       *pc = getch10th();
219       if (*pc == menuHotkeyLookup(fileMenu,"f_find", fileMenuSize)){
220         e = show_file_find_case_input();
221         if (e != -1){
222           show_file_find(e, false);
223         } else {
224           abortinput = 0;
225         }
226         wPrintMenu(0, 0, fileMenuLabel);
227       } else if (*pc == 6){
228         if (findSet){
229           show_file_find(false, true);
230         }
231         wPrintMenu(0, 0, fileMenuLabel);
232       } else if (*pc == menuHotkeyLookup(fileMenu, "f_help", fileMenuSize)){
233         showManPage("sf");
234         wPrintMenu(0, 0, fileMenuLabel);
235         refreshScreen();
236       } else if (*pc == menuHotkeyLookup(fileMenu, "f_position", fileMenuSize)){
237         show_file_position_input(topline);
238         if (topline > totallines + 1){
239           topline = totallines + 1;
240         } else if (topline < 1){
241           topline = 1;
242         }
243         updateView();
244       } else if (*pc == menuHotkeyLookup(fileMenu, "f_config", fileMenuSize)){
245         settingsMenuView();
246         wPrintMenu(0, 0, fileMenuLabel);
247         if(wrap){
248           leftcol = 1;
249         }
250         refreshScreen();
251         // updateView();
252       } else if (*pc == menuHotkeyLookup(fileMenu, "f_quit", fileMenuSize)){
253         free(longline);
254         free(filePos);
255         fclose(stream);
256         exittoshell();
257       } else if (*pc == menuHotkeyLookup(fileMenu, "f_wrap", fileMenuSize)){
258         if (wrap){
259           updateMenuItem(&fileMenu, &fileMenuSize, "f_wrap", L"!Wrap-on");
260           wrap = 0;
261         } else {
262           updateMenuItem(&fileMenu, &fileMenuSize, "f_wrap", L"!Wrap-off");
263           leftcol = 1;
264           wrap = 1;
265         }
266         unloadMenuLabels();
267         refreshMenuLabels();
268         wPrintMenu(0,0,fileMenuLabel);
269         updateView();
270       } else if (*pc == menuHotkeyLookup(fileMenu, "f_01", fileMenuSize) || *pc == 338){
271         topline = topline + displaysize;
272         if (topline > totallines + 1){
273           topline = totallines + 1;
274         }
275         updateView();
276       } else if (*pc == menuHotkeyLookup(fileMenu, "f_02", fileMenuSize) || *pc == 339){
277         topline = topline - displaysize;
278         if (topline < 1){
279           topline = 1;
280         }
281         updateView();
282       } else if (*pc == menuHotkeyLookup(fileMenu, "f_03", fileMenuSize)){
283         topline = 1;
284         updateView();
285       } else if (*pc == menuHotkeyLookup(fileMenu, "f_04", fileMenuSize)){
286         topline = totallines + 1; // Show EOF
287         updateView();
288       } else if (*pc == 258){
289         // Down Arrow
290         if (topline < totallines + 1){
291           topline++;
292           //loadFile(fileName);
293           updateView();
294         }
295       } else if (*pc == 259){
296         // Up Arrow
297         if (topline > 1){
298           topline--;
299           updateView();
300         }
301       } else if (*pc == 260){
302         // Left Arrow
303         if ((leftcol > 1) && (wrap != 1)){
304           leftcol--;
305           updateView();
306         }
307       } else if (*pc == 261){
308         // Right Arrow
309         if ((leftcol < longestlongline) && (wrap != 1)){
310           leftcol++;
311           updateView();
312         }
313       } else if (*pc == 262){
314         // Home
315         // Let's not disable this key when Wrapping is on, just in case.
316         leftcol = 1;
317         updateView();
318       } else if (*pc == 360){
319         // End
320         if (wrap != 1){
321           leftcol = longestlongline;
322           updateView();
323         }
324       }
325    }
326 }
327 
show_file_file_input()328 void show_file_file_input()
329 {
330   char *rewrite;
331   move(0,0);
332   clrtoeol(); // Probably not needed as this is only ever displayed when launching without a file
333   mvprintw(0,0,"Show File - Enter pathname:");
334   curs_set(TRUE);
335   move(0,28);
336   readline(fileName, 4096, "");
337   curs_set(FALSE);
338   if (check_first_char(fileName, "~")){
339     rewrite = str_replace(fileName, "~", getenv("HOME"));
340     strcpy(fileName, rewrite);
341     free(rewrite);
342   }
343   file_view(fileName);
344 }
345