1/*
2 *  dialog - Display simple dialog boxes from shell scripts
3 *
4 *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5 *
6 *  This program is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU General Public License
8 *  as published by the Free Software Foundation; either version 2
9 *  of the License, or (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, write to the Free Software
18 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 *
21 *  HISTORY:
22 *
23 *  17/12/93 - Version 0.1 released.
24 *
25 *  19/12/93 - menu will now scroll if there are more items than can fit
26 *             on the screen.
27 *           - added 'checklist', a dialog box with a list of options that
28 *             can be turned on or off. A list of options that are on is
29 *             returned on exit.
30 *
31 *  20/12/93 - Version 0.15 released.
32 *
33 *  29/12/93 - Incorporated patch from Patrick J. Volkerding
34 *             (volkerdi@mhd1.moorhead.msus.edu) that made these changes:
35 *             - increased MAX_LEN to 2048
36 *             - added 'infobox', equivalent to a message box without pausing
37 *             - added option '--clear' that will clear the screen
38 *             - Explicit line breaking when printing prompt text can be
39 *               invoked by real newline '\n' besides the string "\n"
40 *           - an optional parameter '--title <string>' can be used to
41 *             specify a title string for the dialog box
42 *
43 *  03/01/94 - added 'textbox', a dialog box for displaying text from a file.
44 *           - Version 0.2 released.
45 *
46 *  04/01/94 - some fixes and improvements for 'textbox':
47 *             - fixed a bug that will cause a segmentation violation when a
48 *               line is longer than MAX_LEN characters. Lines will now be
49 *               truncated if they are longer than MAX_LEN characters.
50 *             - removed wrefresh() from print_line(). This will increase
51 *               efficiency of print_page() which calls print_line().
52 *             - display current position in the form of percentage into file.
53 *           - Version 0.21 released.
54 *
55 *  05/01/94 - some changes for faster screen update.
56 *
57 *  07/01/94 - much more flexible color settings. Can use all 16 colors
58 *             (8 normal, 8 highlight) of the Linux console.
59 *
60 *  08/01/94 - added run-time configuration using configuration file.
61 *
62 *  09/01/94 - some minor bug fixes and cleanups for menubox, checklist and
63 *             textbox.
64 *
65 *  11/01/94 - added a man page.
66 *
67 *  13/01/94 - some changes for easier porting to other Unix systems (tested
68 *             on Ultrix, SunOS and HPUX)
69 *           - Version 0.3 released.
70 *
71 *  08/06/94 - Patches by Stuart Herbert - S.Herbert@shef.ac.uk
72 * 	       Fixed attr_clear and the textbox stuff to work with ncurses 1.8.5
73 * 	       Fixed the wordwrap routine - it'll actually wrap properly now
74 *	       Added a more 3D look to everything - having your own rc file could
75 *	         prove 'interesting' to say the least :-)
76 *             Added radiolist option
77 *	     - Version 0.4 released.
78 */
79
80#include <sys/cdefs.h>
81__FBSDID("$FreeBSD$");
82
83#define __DIALOG_MAIN__
84
85#include <dialog.h>
86#include <err.h>
87#include "dialog.priv.h"
88#ifdef HAVE_NCURSES
89#include "colors.h"
90#endif
91
92/* These are two "secret" globals that can be fiddled to make a dialog
93 * come up someplace other than a "centered" calculation for X,Y
94 */
95int DialogX, DialogY;
96
97/* This "secret" global allows you to change the behavior of an input field */
98int DialogInputAttrs;
99
100/*
101 * Do some initialization for dialog
102 */
103void init_dialog(void)
104{
105
106  if (issetugid()) {
107	errx(1, "libdialog is unsafe to use in setugid applications");
108  }
109
110#if defined(LOCALE)
111  (void) setlocale(LC_ALL, "");
112#endif
113
114#ifdef HAVE_NCURSES
115  if (parse_rc() == -1)    /* Read the configuration file */
116    exit(-1);
117#endif
118
119  if (initscr() == NULL) { /* Init curses */
120    fprintf(stderr, "\nCurses initialization error.\n");
121    exit(-1);
122  }
123  keypad(stdscr, TRUE);
124  cbreak();
125  noecho();
126
127#ifdef HAVE_NCURSES
128  if (use_colors || use_shadow)    /* Set up colors */
129    color_setup();
130#endif
131
132  /* Set screen to screen attribute */
133  dialog_clear_norefresh();
134  DialogX = DialogY = 0;
135}
136/* End of init_dialog() */
137
138
139#ifdef HAVE_NCURSES
140/*
141 * Setup for color display
142 */
143void color_setup(void)
144{
145  int i;
146
147  if (has_colors()) {    /* Terminal supports color? */
148    start_color();
149
150    /* Initialize color pairs */
151    for (i = 0; i < ATTRIBUTE_COUNT; i++)
152      init_pair(i+1, color_table[i][0], color_table[i][1]);
153
154    /* Setup color attributes */
155    for (i = 0; i < ATTRIBUTE_COUNT; i++)
156      attributes[i] = C_ATTR(color_table[i][2], i+1);
157  }
158}
159/* End of color_setup() */
160#endif
161
162
163/*
164 * Set window to attribute 'attr'
165 */
166void attr_clear(WINDOW *win, int height, int width, chtype attr)
167{
168  int i, j;
169
170  wattrset(win, attr);    /* Set window to attribute 'attr' */
171  for (i = 0; i < height; i++) {
172    wmove(win, i, 0);
173    for (j = 0; j < width; j++)
174      waddch(win, ' ');
175  }
176}
177/* End of attr_clear() */
178
179
180/*
181 * Print a string of text in a window, automatically wrap around to the
182 * next line if the string is too long to fit on one line. Note that the
183 * string may contain "\n" to represent a newline character or the real
184 * newline '\n', but in that case, auto wrap around will be disabled.
185 */
186void print_autowrap(WINDOW *win, unsigned char *prompt, int height, int width, int maxwidth, int y, int x, int center, int rawmode)
187{
188  int cur_x, cur_y, i;
189  unsigned char tempstr[MAX_LEN+1], *word, *tempptr, *tempptr1;
190  chtype ostuff[132], attrs = 0, init_bottom = 0;
191
192  wsetscrreg(win, y, height);
193  getyx(win, cur_y, cur_x);
194
195  strncpy(tempstr, prompt, MAX_LEN);
196  tempstr[MAX_LEN] = '\0';
197  if ((!rawmode && strstr(tempstr, "\\n") != NULL) ||
198      (strchr(tempstr, '\n') != NULL)) {    /* Prompt contains "\n" or '\n' */
199    word = tempstr;
200    while (1) {
201      tempptr = rawmode ? NULL : strstr(word, "\\n");
202      tempptr1 = strchr(word, '\n');
203      if (tempptr == NULL && tempptr1 == NULL)
204        break;
205      else if (tempptr == NULL) {    /* No more "\n" */
206        tempptr = tempptr1;
207        tempptr[0] = '\0';
208      }
209      else if (tempptr1 == NULL) {    /* No more '\n' */
210        tempptr[0] = '\0';
211        tempptr++;
212      }
213      else {    /* Prompt contains both "\n" and '\n' */
214        if (strlen(tempptr)-2 < strlen(tempptr1)-1) {
215          tempptr = tempptr1;
216          tempptr[0] = '\0';
217        }
218        else {
219          tempptr[0] = '\0';
220          tempptr++;
221        }
222      }
223
224      waddstr(win, word);
225      word = tempptr + 1;
226      if (++cur_y > height) {
227	cur_y--;
228	if (!init_bottom) {
229	  for (i = 0; i < x; i++)
230	    ostuff[i] = mvwinch(win, cur_y, i);
231	  for (i = width; i < maxwidth; i++)
232	    ostuff[i] = mvwinch(win, cur_y, i);
233	  attrs = getattrs(win);
234	  init_bottom = 1;
235	}
236	scrollok(win, TRUE);
237	scroll(win);
238	scrollok(win, FALSE);
239	wmove(win, cur_y, 0);
240	for (i = 0; i < x; i++) {
241	  wattrset(win, ostuff[i]&A_ATTRIBUTES);
242	  waddch(win, ostuff[i]);
243	}
244	wattrset(win, attrs);
245	for ( ; i < width; i++)
246	  waddch(win, ' ');
247	for ( ; i < maxwidth; i++) {
248	  wattrset(win, ostuff[i]&A_ATTRIBUTES);
249	  waddch(win, ostuff[i]);
250	}
251	wattrset(win, attrs);
252	wrefresh(win);
253      }
254      wmove(win, cur_y, cur_x = x);
255    }
256    waddstr(win, word);
257  }
258  else if (center && strlen(tempstr) <= width-x*2) {    /* If prompt is short */
259    wmove(win, cur_y, (width - strlen(tempstr)) / 2);
260    waddstr(win, tempstr);
261  }
262  else if (!center && strlen(tempstr) <= width-cur_x) {    /* If prompt is short */
263    waddstr(win, tempstr);
264  }
265  else {
266    char *p = tempstr;
267
268    /* Print prompt word by word, wrap around if necessary */
269    while ((word = strsep(&p, "\t\n ")) != NULL) {
270      int loop;
271      unsigned char sc;
272
273      if (*word == '\0')
274	continue;
275      do {
276	loop = 0;
277	if (cur_x+strlen(word) >= width+1) {    /* wrap around to next line */
278	  if (x+strlen(word) >= width+1) {
279	    sc = word[width-cur_x-1];
280	    word[width-cur_x-1] = '\0';
281	    wmove(win, cur_y, cur_x);
282	    waddstr(win, word);
283	    word[width-cur_x-1] = sc;
284	    word += width-cur_x-1;
285	    getyx(win, cur_y, cur_x);
286	    loop = 1;
287	  }
288	  cur_y++;
289	  cur_x = x;
290	  if (cur_y > height) {
291	    cur_y--;
292	    if (!init_bottom) {
293	      for (i = 0; i < x; i++)
294		ostuff[i] = mvwinch(win, cur_y, i);
295	      for (i = width; i < maxwidth; i++)
296		ostuff[i] = mvwinch(win, cur_y, i);
297	      attrs = getattrs(win);
298	      init_bottom = 1;
299	    }
300	    scrollok(win, TRUE);
301	    scroll(win);
302	    scrollok(win, FALSE);
303	    wmove(win, cur_y, 0);
304	    for (i = 0; i < x; i++) {
305	      wattrset(win, ostuff[i]&A_ATTRIBUTES);
306	      waddch(win, ostuff[i]);
307	    }
308	    wattrset(win, attrs);
309	    for ( ; i < width; i++)
310	      waddch(win, ' ');
311	    for ( ; i < maxwidth; i++) {
312	      wattrset(win, ostuff[i]&A_ATTRIBUTES);
313	      waddch(win, ostuff[i]);
314	    }
315	    wattrset(win, attrs);
316	    wrefresh(win);
317	  }
318	}
319      }
320      while(loop);
321      wmove(win, cur_y, cur_x);
322      waddstr(win, word);
323      getyx(win, cur_y, cur_x);
324      cur_x++;
325    }
326  }
327}
328/* End of print_autowrap() */
329
330
331/*
332 * Print a button
333 */
334void print_button(WINDOW *win, unsigned char *label, int y, int x, int selected)
335{
336  int i, temp;
337
338  wmove(win, y, x);
339  wattrset(win, selected ? button_active_attr : button_inactive_attr);
340  waddstr(win, selected ? "[" : " ");
341  temp = strspn(label, " ");
342  label += temp;
343  for (i = 0; i < temp; i++)
344    waddch(win, ' ');
345  wattrset(win, selected ? button_key_active_attr : button_key_inactive_attr);
346  waddch(win, label[0]);
347  wattrset(win, selected ? button_active_attr : button_inactive_attr);
348  waddstr(win, label+1);
349  waddstr(win, selected ? "]" : " ");
350  wmove(win, y, x+temp+1);
351}
352/* End of print_button() */
353
354
355/*
356 * Draw a rectangular box with line drawing characters
357 */
358void draw_box(WINDOW *win, int y, int x, int height, int width, chtype box, chtype border)
359{
360  int i, j;
361
362  wattrset(win, 0);
363  for (i = 0; i < height; i++) {
364    wmove(win, y + i, x);
365    for (j = 0; j < width; j++)
366      if (!i && !j)
367        waddch(win, border | ACS_ULCORNER);
368      else if (i == height-1 && !j)
369        waddch(win, border | ACS_LLCORNER);
370      else if (!i && j == width-1)
371        waddch(win, box | ACS_URCORNER);
372      else if (i == height-1 && j == width-1)
373        waddch(win, box | ACS_LRCORNER);
374      else if (!i)
375        waddch(win, border | ACS_HLINE);
376      else if (i == height-1)
377        waddch(win, box | ACS_HLINE);
378      else if (!j)
379        waddch(win, border | ACS_VLINE);
380      else if (j == width-1)
381        waddch(win, box | ACS_VLINE);
382      else
383        waddch(win, box | ' ');
384  }
385}
386/* End of draw_box() */
387
388
389#ifdef HAVE_NCURSES
390/*
391 * Draw shadows along the right and bottom edge to give a more 3D look
392 * to the boxes
393 */
394void draw_shadow(WINDOW *win, int y, int x, int height, int width)
395{
396  int i,sx,sy;
397  chtype attrs;
398
399  if (has_colors()) {    /* Whether terminal supports color? */
400    getbegyx(win,sy,sx);
401    attrs = getattrs(win);
402    if (y+height < getmaxy(win)) {
403	/* small touch */
404	wattrset(win, A_INVIS);
405	wmove(win, y + height, x + 2);
406	for (i = 0; i < width; i++)
407	    if (i+x+2 < getmaxx(win))
408	       waddch(win, ' ');
409	/* end touch */
410	wattrset(win, shadow_attr);
411	wmove(win, y + height, x + 2);
412	for (i = 0; i < width; i++)
413	    if (i+x+2 < getmaxx(win))
414	       waddch(win, mvwinch(newscr, sy+y+height, sx+x+2+i) & A_CHARTEXT);
415    }
416    if (x+width < getmaxx(win)) {
417	for (i = y + 1; i < y + height + 1; i++) {
418	  if (i < getmaxy(win)) {
419	      /* small touch */
420	      wattrset(win, A_INVIS);
421	      wmove(win, i, x + width);
422	      waddch(win, ' ');
423	      if (x+width+1 < getmaxx(win))
424		    waddch(win, ' ');
425	      /* end touch */
426	      wattrset(win, shadow_attr);
427	      wmove(win, i, x + width);
428	      waddch(win, mvwinch(newscr, sy+i, sx+x+width) & A_CHARTEXT);
429	      if (x+width+1 < getmaxx(win))
430		    waddch(win, mvwinch(newscr, sy+i, sx+x+width+1) & A_CHARTEXT);
431	  }
432	}
433    }
434    wattrset(win, attrs);
435    wnoutrefresh(win);
436  }
437}
438/* End of draw_shadow() */
439#endif
440
441void dialog_clear_norefresh(void)
442{
443    attr_clear(stdscr, LINES, COLS, screen_attr);
444    touchwin(stdscr);
445    wnoutrefresh(stdscr);
446}
447
448void dialog_clear(void)
449{
450    dialog_clear_norefresh();
451    doupdate();
452}
453
454void dialog_update(void)
455{
456    refresh();
457}
458
459void end_dialog(void)
460{
461    endwin();
462}
463
464int strwidth(const char *p)
465{
466	int i = 0, len, incr;
467	const char *start, *s, *s1, *s2;
468
469	for (start = s = p; ; start = (s += incr)) {
470		s1 = strchr(s, '\n');
471		s2 = strstr(s, "\\n");
472		if (s2 == NULL)
473			s = s1;
474		else if (s1 == NULL)
475			s = s2;
476		else
477			s = MIN(s1, s2);
478		if (s == NULL)
479			break;
480		incr = 1 + (s == s2);
481		len = s - start;
482		if (len > i)
483			i = len;
484	}
485	len = strlen(start);
486	if (len > i)
487		i = len;
488	return i;
489}
490
491int strheight(const char *p)
492{
493	int i = 1, incr;
494	const char *s, *s1, *s2;
495
496	for (s = p; ; s += incr) {
497		s1 = strchr(s, '\n');
498		s2 = strstr(s, "\\n");
499		if (s2 == NULL)
500			s = s1;
501		else if (s1 == NULL)
502			s = s2;
503		else
504			s = MIN(s1, s2);
505		if (s == NULL)
506			break;
507		incr = 1 + (s == s2);
508		i++;
509	}
510	return i;
511}
512
513void print_arrows(WINDOW *dialog, int scroll, int menu_height, int item_no,
514		  int box_x, int box_y, int tag_x, int cur_x, int cur_y)
515{
516    wmove(dialog, box_y, box_x + tag_x + 1);
517    wattrset(dialog, scroll ? uarrow_attr : menubox_attr);
518    waddch(dialog, scroll ? ACS_UARROW : ACS_HLINE);
519    wmove(dialog, box_y, box_x + tag_x + 2);
520    waddch(dialog, scroll ? '(' : ACS_HLINE);
521    wmove(dialog, box_y, box_x + tag_x + 3);
522    waddch(dialog, scroll ? '-' : ACS_HLINE);
523    wmove(dialog, box_y, box_x + tag_x + 4);
524    waddch(dialog, scroll ? ')' : ACS_HLINE);
525    wmove(dialog, box_y + menu_height + 1, box_x + tag_x + 1);
526    wattrset(dialog, scroll+menu_height < item_no ? darrow_attr : menubox_border_attr);
527    waddch(dialog, scroll+menu_height < item_no ? ACS_DARROW : ACS_HLINE);
528    wmove(dialog, box_y + menu_height + 1, box_x + tag_x + 2);
529    waddch(dialog, scroll+menu_height < item_no ? '(' : ACS_HLINE);
530    wmove(dialog, box_y + menu_height + 1, box_x + tag_x + 3);
531    waddch(dialog, scroll+menu_height < item_no ? '+' : ACS_HLINE);
532    wmove(dialog, box_y + menu_height + 1, box_x + tag_x + 4);
533    waddch(dialog, scroll+menu_height < item_no ? ')' : ACS_HLINE);
534    wmove(dialog, cur_y, cur_x);  /* Restore cursor position */
535}
536
537