336793b039546cc3fce2d2a1a99b4a80e7d2e6c3
[firefly-linux-kernel-4.4.55.git] / scripts / kconfig / lxdialog / textbox.c
1 /*
2  *  textbox.c -- implements the text box
3  *
4  *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5  *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
6  *
7  *  This program is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU General Public License
9  *  as published by the Free Software Foundation; either version 2
10  *  of the License, or (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #include "dialog.h"
23
24 static void back_lines(int n);
25 static void print_page(WINDOW * win, int height, int width);
26 static void print_line(WINDOW * win, int row, int width);
27 static char *get_line(void);
28 static void print_position(WINDOW * win, int height, int width);
29
30 static int hscroll, fd, file_size, bytes_read;
31 static int begin_reached = 1, end_reached, page_length;
32 static char *buf, *page;
33
34 /*
35  * Display text from a file in a dialog box.
36  */
37 int dialog_textbox(const char *title, const char *file, int height, int width)
38 {
39         int i, x, y, cur_x, cur_y, fpos, key = 0;
40         int passed_end;
41         char search_term[MAX_LEN + 1];
42         WINDOW *dialog, *text;
43
44         search_term[0] = '\0';  /* no search term entered yet */
45
46         /* Open input file for reading */
47         if ((fd = open(file, O_RDONLY)) == -1) {
48                 endwin();
49                 fprintf(stderr, "\nCan't open input file in dialog_textbox().\n");
50                 exit(-1);
51         }
52         /* Get file size. Actually, 'file_size' is the real file size - 1,
53            since it's only the last byte offset from the beginning */
54         if ((file_size = lseek(fd, 0, SEEK_END)) == -1) {
55                 endwin();
56                 fprintf(stderr, "\nError getting file size in dialog_textbox().\n");
57                 exit(-1);
58         }
59         /* Restore file pointer to beginning of file after getting file size */
60         if (lseek(fd, 0, SEEK_SET) == -1) {
61                 endwin();
62                 fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
63                 exit(-1);
64         }
65         /* Allocate space for read buffer */
66         if ((buf = malloc(BUF_SIZE + 1)) == NULL) {
67                 endwin();
68                 fprintf(stderr, "\nCan't allocate memory in dialog_textbox().\n");
69                 exit(-1);
70         }
71         if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) {
72                 endwin();
73                 fprintf(stderr, "\nError reading file in dialog_textbox().\n");
74                 exit(-1);
75         }
76         buf[bytes_read] = '\0'; /* mark end of valid data */
77         page = buf;             /* page is pointer to start of page to be displayed */
78
79         /* center dialog box on screen */
80         x = (COLS - width) / 2;
81         y = (LINES - height) / 2;
82
83         draw_shadow(stdscr, y, x, height, width);
84
85         dialog = newwin(height, width, y, x);
86         keypad(dialog, TRUE);
87
88         /* Create window for text region, used for scrolling text */
89         text = subwin(dialog, height - 4, width - 2, y + 1, x + 1);
90         wattrset(text, dlg.dialog.atr);
91         wbkgdset(text, dlg.dialog.atr & A_COLOR);
92
93         keypad(text, TRUE);
94
95         /* register the new window, along with its borders */
96         draw_box(dialog, 0, 0, height, width,
97                  dlg.dialog.atr, dlg.border.atr);
98
99         wattrset(dialog, dlg.border.atr);
100         mvwaddch(dialog, height - 3, 0, ACS_LTEE);
101         for (i = 0; i < width - 2; i++)
102                 waddch(dialog, ACS_HLINE);
103         wattrset(dialog, dlg.dialog.atr);
104         wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
105         waddch(dialog, ACS_RTEE);
106
107         print_title(dialog, title, width);
108
109         print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
110         wnoutrefresh(dialog);
111         getyx(dialog, cur_y, cur_x);    /* Save cursor position */
112
113         /* Print first page of text */
114         attr_clear(text, height - 4, width - 2, dlg.dialog.atr);
115         print_page(text, height - 4, width - 2);
116         print_position(dialog, height, width);
117         wmove(dialog, cur_y, cur_x);    /* Restore cursor position */
118         wrefresh(dialog);
119
120         while ((key != ESC) && (key != '\n')) {
121                 key = wgetch(dialog);
122                 switch (key) {
123                 case 'E':       /* Exit */
124                 case 'e':
125                 case 'X':
126                 case 'x':
127                         delwin(dialog);
128                         free(buf);
129                         close(fd);
130                         return 0;
131                 case 'g':       /* First page */
132                 case KEY_HOME:
133                         if (!begin_reached) {
134                                 begin_reached = 1;
135                                 /* First page not in buffer? */
136                                 if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
137                                         endwin();
138                                         fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
139                                         exit(-1);
140                                 }
141                                 if (fpos > bytes_read) {        /* Yes, we have to read it in */
142                                         if (lseek(fd, 0, SEEK_SET) == -1) {
143                                                 endwin();
144                                                 fprintf(stderr, "\nError moving file pointer in "
145                                                                 "dialog_textbox().\n");
146                                                 exit(-1);
147                                         }
148                                         if ((bytes_read =
149                                              read(fd, buf, BUF_SIZE)) == -1) {
150                                                 endwin();
151                                                 fprintf(stderr, "\nError reading file in dialog_textbox().\n");
152                                                 exit(-1);
153                                         }
154                                         buf[bytes_read] = '\0';
155                                 }
156                                 page = buf;
157                                 print_page(text, height - 4, width - 2);
158                                 print_position(dialog, height, width);
159                                 wmove(dialog, cur_y, cur_x);    /* Restore cursor position */
160                                 wrefresh(dialog);
161                         }
162                         break;
163                 case 'G':       /* Last page */
164                 case KEY_END:
165
166                         end_reached = 1;
167                         /* Last page not in buffer? */
168                         if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
169                                 endwin();
170                                 fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
171                                 exit(-1);
172                         }
173                         if (fpos < file_size) { /* Yes, we have to read it in */
174                                 if (lseek(fd, -BUF_SIZE, SEEK_END) == -1) {
175                                         endwin();
176                                         fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
177                                         exit(-1);
178                                 }
179                                 if ((bytes_read =
180                                      read(fd, buf, BUF_SIZE)) == -1) {
181                                         endwin();
182                                         fprintf(stderr, "\nError reading file in dialog_textbox().\n");
183                                         exit(-1);
184                                 }
185                                 buf[bytes_read] = '\0';
186                         }
187                         page = buf + bytes_read;
188                         back_lines(height - 4);
189                         print_page(text, height - 4, width - 2);
190                         print_position(dialog, height, width);
191                         wmove(dialog, cur_y, cur_x);    /* Restore cursor position */
192                         wrefresh(dialog);
193                         break;
194                 case 'K':       /* Previous line */
195                 case 'k':
196                 case KEY_UP:
197                         if (!begin_reached) {
198                                 back_lines(page_length + 1);
199
200                                 /* We don't call print_page() here but use scrolling to ensure
201                                    faster screen update. However, 'end_reached' and
202                                    'page_length' should still be updated, and 'page' should
203                                    point to start of next page. This is done by calling
204                                    get_line() in the following 'for' loop. */
205                                 scrollok(text, TRUE);
206                                 wscrl(text, -1);        /* Scroll text region down one line */
207                                 scrollok(text, FALSE);
208                                 page_length = 0;
209                                 passed_end = 0;
210                                 for (i = 0; i < height - 4; i++) {
211                                         if (!i) {
212                                                 /* print first line of page */
213                                                 print_line(text, 0, width - 2);
214                                                 wnoutrefresh(text);
215                                         } else
216                                                 /* Called to update 'end_reached' and 'page' */
217                                                 get_line();
218                                         if (!passed_end)
219                                                 page_length++;
220                                         if (end_reached && !passed_end)
221                                                 passed_end = 1;
222                                 }
223
224                                 print_position(dialog, height, width);
225                                 wmove(dialog, cur_y, cur_x);    /* Restore cursor position */
226                                 wrefresh(dialog);
227                         }
228                         break;
229                 case 'B':       /* Previous page */
230                 case 'b':
231                 case KEY_PPAGE:
232                         if (begin_reached)
233                                 break;
234                         back_lines(page_length + height - 4);
235                         print_page(text, height - 4, width - 2);
236                         print_position(dialog, height, width);
237                         wmove(dialog, cur_y, cur_x);
238                         wrefresh(dialog);
239                         break;
240                 case 'J':       /* Next line */
241                 case 'j':
242                 case KEY_DOWN:
243                         if (!end_reached) {
244                                 begin_reached = 0;
245                                 scrollok(text, TRUE);
246                                 scroll(text);   /* Scroll text region up one line */
247                                 scrollok(text, FALSE);
248                                 print_line(text, height - 5, width - 2);
249                                 wnoutrefresh(text);
250                                 print_position(dialog, height, width);
251                                 wmove(dialog, cur_y, cur_x);    /* Restore cursor position */
252                                 wrefresh(dialog);
253                         }
254                         break;
255                 case KEY_NPAGE: /* Next page */
256                 case ' ':
257                         if (end_reached)
258                                 break;
259
260                         begin_reached = 0;
261                         print_page(text, height - 4, width - 2);
262                         print_position(dialog, height, width);
263                         wmove(dialog, cur_y, cur_x);
264                         wrefresh(dialog);
265                         break;
266                 case '0':       /* Beginning of line */
267                 case 'H':       /* Scroll left */
268                 case 'h':
269                 case KEY_LEFT:
270                         if (hscroll <= 0)
271                                 break;
272
273                         if (key == '0')
274                                 hscroll = 0;
275                         else
276                                 hscroll--;
277                         /* Reprint current page to scroll horizontally */
278                         back_lines(page_length);
279                         print_page(text, height - 4, width - 2);
280                         wmove(dialog, cur_y, cur_x);
281                         wrefresh(dialog);
282                         break;
283                 case 'L':       /* Scroll right */
284                 case 'l':
285                 case KEY_RIGHT:
286                         if (hscroll >= MAX_LEN)
287                                 break;
288                         hscroll++;
289                         /* Reprint current page to scroll horizontally */
290                         back_lines(page_length);
291                         print_page(text, height - 4, width - 2);
292                         wmove(dialog, cur_y, cur_x);
293                         wrefresh(dialog);
294                         break;
295                 case ESC:
296                         break;
297                 }
298         }
299
300         delwin(dialog);
301         free(buf);
302         close(fd);
303         return -1;              /* ESC pressed */
304 }
305
306 /*
307  * Go back 'n' lines in text file. Called by dialog_textbox().
308  * 'page' will be updated to point to the desired line in 'buf'.
309  */
310 static void back_lines(int n)
311 {
312         int i, fpos;
313
314         begin_reached = 0;
315         /* We have to distinguish between end_reached and !end_reached
316            since at end of file, the line is not ended by a '\n'.
317            The code inside 'if' basically does a '--page' to move one
318            character backward so as to skip '\n' of the previous line */
319         if (!end_reached) {
320                 /* Either beginning of buffer or beginning of file reached? */
321                 if (page == buf) {
322                         if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
323                                 endwin();
324                                 fprintf(stderr, "\nError moving file pointer in "
325                                                 "back_lines().\n");
326                                 exit(-1);
327                         }
328                         if (fpos > bytes_read) {        /* Not beginning of file yet */
329                                 /* We've reached beginning of buffer, but not beginning of
330                                    file yet, so read previous part of file into buffer.
331                                    Note that we only move backward for BUF_SIZE/2 bytes,
332                                    but not BUF_SIZE bytes to avoid re-reading again in
333                                    print_page() later */
334                                 /* Really possible to move backward BUF_SIZE/2 bytes? */
335                                 if (fpos < BUF_SIZE / 2 + bytes_read) {
336                                         /* No, move less then */
337                                         if (lseek(fd, 0, SEEK_SET) == -1) {
338                                                 endwin();
339                                                 fprintf(stderr, "\nError moving file pointer in "
340                                                                 "back_lines().\n");
341                                                 exit(-1);
342                                         }
343                                         page = buf + fpos - bytes_read;
344                                 } else {        /* Move backward BUF_SIZE/2 bytes */
345                                         if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) == -1) {
346                                                 endwin();
347                                                 fprintf(stderr, "\nError moving file pointer "
348                                                                 "in back_lines().\n");
349                                                 exit(-1);
350                                         }
351                                         page = buf + BUF_SIZE / 2;
352                                 }
353                                 if ((bytes_read =
354                                      read(fd, buf, BUF_SIZE)) == -1) {
355                                         endwin();
356                                         fprintf(stderr, "\nError reading file in back_lines().\n");
357                                         exit(-1);
358                                 }
359                                 buf[bytes_read] = '\0';
360                         } else {        /* Beginning of file reached */
361                                 begin_reached = 1;
362                                 return;
363                         }
364                 }
365                 if (*(--page) != '\n') {        /* '--page' here */
366                         /* Something's wrong... */
367                         endwin();
368                         fprintf(stderr, "\nInternal error in back_lines().\n");
369                         exit(-1);
370                 }
371         }
372         /* Go back 'n' lines */
373         for (i = 0; i < n; i++)
374                 do {
375                         if (page == buf) {
376                                 if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
377                                         endwin();
378                                         fprintf(stderr, "\nError moving file pointer in back_lines().\n");
379                                         exit(-1);
380                                 }
381                                 if (fpos > bytes_read) {
382                                         /* Really possible to move backward BUF_SIZE/2 bytes? */
383                                         if (fpos < BUF_SIZE / 2 + bytes_read) {
384                                                 /* No, move less then */
385                                                 if (lseek(fd, 0, SEEK_SET) == -1) {
386                                                         endwin();
387                                                         fprintf(stderr, "\nError moving file pointer "
388                                                                         "in back_lines().\n");
389                                                         exit(-1);
390                                                 }
391                                                 page = buf + fpos - bytes_read;
392                                         } else {        /* Move backward BUF_SIZE/2 bytes */
393                                                 if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) == -1) {
394                                                         endwin();
395                                                         fprintf(stderr, "\nError moving file pointer"
396                                                                         " in back_lines().\n");
397                                                         exit(-1);
398                                                 }
399                                                 page = buf + BUF_SIZE / 2;
400                                         }
401                                         if ((bytes_read =
402                                              read(fd, buf, BUF_SIZE)) == -1) {
403                                                 endwin();
404                                                 fprintf(stderr, "\nError reading file in "
405                                                                 "back_lines().\n");
406                                                 exit(-1);
407                                         }
408                                         buf[bytes_read] = '\0';
409                                 } else {        /* Beginning of file reached */
410                                         begin_reached = 1;
411                                         return;
412                                 }
413                         }
414                 } while (*(--page) != '\n');
415         page++;
416 }
417
418 /*
419  * Print a new page of text. Called by dialog_textbox().
420  */
421 static void print_page(WINDOW * win, int height, int width)
422 {
423         int i, passed_end = 0;
424
425         page_length = 0;
426         for (i = 0; i < height; i++) {
427                 print_line(win, i, width);
428                 if (!passed_end)
429                         page_length++;
430                 if (end_reached && !passed_end)
431                         passed_end = 1;
432         }
433         wnoutrefresh(win);
434 }
435
436 /*
437  * Print a new line of text. Called by dialog_textbox() and print_page().
438  */
439 static void print_line(WINDOW * win, int row, int width)
440 {
441         int y, x;
442         char *line;
443
444         line = get_line();
445         line += MIN(strlen(line), hscroll);     /* Scroll horizontally */
446         wmove(win, row, 0);     /* move cursor to correct line */
447         waddch(win, ' ');
448         waddnstr(win, line, MIN(strlen(line), width - 2));
449
450         getyx(win, y, x);
451         /* Clear 'residue' of previous line */
452 #if OLD_NCURSES
453         {
454                 int i;
455                 for (i = 0; i < width - x; i++)
456                         waddch(win, ' ');
457         }
458 #else
459         wclrtoeol(win);
460 #endif
461 }
462
463 /*
464  * Return current line of text. Called by dialog_textbox() and print_line().
465  * 'page' should point to start of current line before calling, and will be
466  * updated to point to start of next line.
467  */
468 static char *get_line(void)
469 {
470         int i = 0, fpos;
471         static char line[MAX_LEN + 1];
472
473         end_reached = 0;
474         while (*page != '\n') {
475                 if (*page == '\0') {
476                         /* Either end of file or end of buffer reached */
477                         if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
478                                 endwin();
479                                 fprintf(stderr, "\nError moving file pointer in "
480                                                 "get_line().\n");
481                                 exit(-1);
482                         }
483                         if (fpos < file_size) { /* Not end of file yet */
484                                 /* We've reached end of buffer, but not end of file yet,
485                                    so read next part of file into buffer */
486                                 if ((bytes_read =
487                                      read(fd, buf, BUF_SIZE)) == -1) {
488                                         endwin();
489                                         fprintf(stderr, "\nError reading file in get_line().\n");
490                                         exit(-1);
491                                 }
492                                 buf[bytes_read] = '\0';
493                                 page = buf;
494                         } else {
495                                 if (!end_reached)
496                                         end_reached = 1;
497                                 break;
498                         }
499                 } else if (i < MAX_LEN)
500                         line[i++] = *(page++);
501                 else {
502                         /* Truncate lines longer than MAX_LEN characters */
503                         if (i == MAX_LEN)
504                                 line[i++] = '\0';
505                         page++;
506                 }
507         }
508         if (i <= MAX_LEN)
509                 line[i] = '\0';
510         if (!end_reached)
511                 page++;         /* move pass '\n' */
512
513         return line;
514 }
515
516 /*
517  * Print current position
518  */
519 static void print_position(WINDOW * win, int height, int width)
520 {
521         int fpos, percent;
522
523         if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
524                 endwin();
525                 fprintf(stderr, "\nError moving file pointer in print_position().\n");
526                 exit(-1);
527         }
528         wattrset(win, dlg.position_indicator.atr);
529         wbkgdset(win, dlg.position_indicator.atr & A_COLOR);
530         percent = !file_size ?
531             100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
532         wmove(win, height - 3, width - 9);
533         wprintw(win, "(%3d%%)", percent);
534 }