dslinux/user/dialog CHANGES COPYING README VERSION aclocal.m4 arrows.c buttons.c calendar.c checklist.c config.guess config.hin config.sub configure configure.in dialog.1 dialog.3 dialog.c dialog.h dialog.lsm dialog.pl dlg_colors.h dlg_keys.c dlg_keys.h formbox.c fselect.c guage.c headers.sh inputbox.c inputstr.c install-sh makefile.in menubox.c mkdirs.sh mouse.c mousewget.c msgbox.c pause.c progressbox.c rc.c tailbox.c textbox.c timebox.c ui_getc.c util.c version.c yesno.c

stsp stsp at user.in-berlin.de
Mon Oct 23 22:59:30 CEST 2006


Update of /cvsroot/dslinux/dslinux/user/dialog
In directory antilope:/tmp/cvs-serv17426

Added Files:
	CHANGES COPYING README VERSION aclocal.m4 arrows.c buttons.c 
	calendar.c checklist.c config.guess config.hin config.sub 
	configure configure.in dialog.1 dialog.3 dialog.c dialog.h 
	dialog.lsm dialog.pl dlg_colors.h dlg_keys.c dlg_keys.h 
	formbox.c fselect.c guage.c headers.sh inputbox.c inputstr.c 
	install-sh makefile.in menubox.c mkdirs.sh mouse.c mousewget.c 
	msgbox.c pause.c progressbox.c rc.c tailbox.c textbox.c 
	timebox.c ui_getc.c util.c version.c yesno.c 
Log Message:
Adding pristine copy of dialog-1.0-20060221 so I can branch from it.


--- NEW FILE: mousewget.c ---
/*
 * $Id: mousewget.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 * mousewget.c -- mouse/wgetch support for dialog
 *
 * Copyright 2000-2003,2005   Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>
#include <dlg_keys.h>

static int
mouse_wgetch(WINDOW *win, int *fkey, bool ignore_errs)
{
    int key;

#if USE_MOUSE

    do {

	key = dlg_getc(win, fkey);
	if (fkey && (key == KEY_MOUSE)) {
	    MEVENT event;
	    mseRegion *p;

	    if (getmouse(&event) != ERR) {
		if ((p = dlg_mouse_region(event.y, event.x)) != 0) {
		    key = DLGK_MOUSE(p->code);
		} else if ((p = dlg_mouse_bigregion(event.y, event.x)) != 0) {
		    int x = event.x - p->x;
		    int y = event.y - p->y;
		    int row = (p->X - p->x) / p->step_x;

		    key = -(p->code);
		    switch (p->mode) {
		    case 1:	/* index by lines */
			key += y;
			break;
		    case 2:	/* index by columns */
			key += (x / p->step_x);
			break;
		    default:
		    case 3:	/* index by cells */
			key += (x / p->step_x) + (y * row);
			break;
		    }
		} else {
		    (void) beep();
		    key = ERR;
		}
	    } else {
		(void) beep();
		key = ERR;
	    }
	}

    } while (ignore_errs && (key == ERR));

#else

    do {
	key = dlg_getc(win, fkey);
    } while (ignore_errs && (key == ERR));

#endif

    return key;
}

int
dlg_mouse_wgetch(WINDOW *win, int *fkey)
{
    return mouse_wgetch(win, fkey, TRUE);
}

int
dlg_mouse_wgetch_nowait(WINDOW *win, int *fkey)
{
    return mouse_wgetch(win, fkey, FALSE);
}

--- NEW FILE: configure.in ---
dnl $Id: configure.in,v 1.1 2006-10-23 20:59:26 stsp Exp $
dnl Process this file with autoconf to produce a configure script.
dnl
AC_PREREQ(2.13.20020210)
AC_INIT(dialog.h)
AC_CONFIG_HEADER(dlg_config.h:config.hin)

CF_VERSION_INFO(dialog)

DESTDIR=
AC_SUBST(DESTDIR)

dnl
dnl Checks for programs.
dnl
AC_PROG_CC
AC_PROG_CPP
AC_PROG_GCC_TRADITIONAL
AC_PROG_MAKE_SET
AC_PROG_RANLIB
AC_PROG_INSTALL

dnl needed for CF_WITH_LIBTOOL
AC_CHECK_TOOL(AR, ar, ar)

AC_ISC_POSIX
AC_C_CONST

CF_MAKEFLAGS
CF_MAKE_TAGS
CF_DISABLE_ECHO
CF_PROG_EXT
CF_LIB_PREFIX
CF_XOPEN_SOURCE
CF_LARGEFILE

AC_ARG_WITH(warnings,
[  --with-warnings         turn on gcc warnings, for debugging],[
	CF_GCC_ATTRIBUTES
	CF_GCC_WARNINGS
])

dnl
dnl Checks for libraries.
dnl
CF_BUNDLED_INTL(makefile,enable)
CF_MATH_LIB(,sqrt(x))

CF_WITH_DBMALLOC
CF_WITH_DMALLOC

LIBTOOL_MAKE="#"
CF_WITH_LIBTOOL
if test "$with_libtool" = "yes" ; then
	OBJEXT="lo"
	LIBTOOL_MAKE=
fi
AC_SUBST(LIBTOOL_MAKE)

use_ncurses=no
AC_ARG_WITH(ncurses,
	[  --with-ncurses          compile/link with ncurses library],
	[use_ncurses=ncurses])
AC_ARG_WITH(ncursesw,
	[  --with-ncursesw         compile/link with wide-char ncurses library],
	[use_ncurses=ncursesw])
if test $use_ncurses != no ; then
	cf_wide_curses=yes
	if test $use_ncurses = ncursesw ; then
		CF_UTF8_LIB
	fi
	CF_NCURSES_CPPFLAGS($use_ncurses)
	CF_NCURSES_LIBS($use_ncurses)
else
	cf_wide_curses=no
	CF_CURSES_CPPFLAGS
	CF_NCURSES_VERSION
	CF_CURSES_LIBS
fi

EXTRAOBJS=""
cf_all_widgets=yes

CF_ARG_MSG_ENABLE([if you want config-file support],
	rc-file,
	[  --disable-rc-file       do not include config-file support],
	[EXTRAOBJS="$EXTRAOBJS rc\$o"
	 AC_DEFINE(HAVE_RC_FILE)],,$cf_all_widgets)

CF_ARG_MSG_ENABLE([if you want Xdialog-style dialogs],
	Xdialog,
	[  --disable-Xdialog       do not include Xdialog-style dialogs],
	[EXTRAOBJS="$EXTRAOBJS calendar\$o fselect\$o timebox\$o"
	 AC_DEFINE(HAVE_XDIALOG)],,$cf_all_widgets)

CF_ARG_MSG_ENABLE([if you want the form dialog],
	form,
	[  --disable-form          do not include the form dialog],
	[EXTRAOBJS="$EXTRAOBJS formbox\$o"
	 AC_DEFINE(HAVE_FORMBOX)],,$cf_all_widgets)

CF_ARG_MSG_ENABLE([if you want the gauge dialog],
	gauge,
	[  --disable-gauge         do not include the gauge dialogs],
	[EXTRAOBJS="$EXTRAOBJS guage\$o pause\$o progressbox\$o"
	 AC_DEFINE(HAVE_GAUGE)],,$cf_all_widgets)

CF_ARG_MSG_ENABLE([if you want the tailbox dialog],
	tailbox,
	[  --disable-tailbox       do not include the tailbox dialog],
	[EXTRAOBJS="$EXTRAOBJS tailbox\$o"
	 AC_DEFINE(HAVE_TAILBOX)],,$cf_all_widgets)

CF_ARG_MSG_ENABLE([if you want the wide-curses features],
	widec,
	[  --enable-widec          enable wide-curses features],
	[AC_DEFINE(USE_WIDE_CURSES)],,$cf_wide_curses)

AC_SUBST(EXTRAOBJS)

dnl
dnl Checks for header files.
dnl
AC_HEADER_STDC
AC_HEADER_TIME
AC_HEADER_DIRENT
AC_CHECK_HEADERS(search.h unistd.h)
CF_CURSES_TERM_H

dnl
dnl Checks for library functions.
dnl
AC_TYPE_SIGNAL
AC_CHECK_FUNCS(\
strcasecmp \
tsearch \
waitpid \
)

CF_CURSES_FUNCS(\
_nc_free_and_exit \
flushinp \
getbegx \
getbegy \
getbegyx \
getcurx \
getcury \
getmaxx \
getmaxy \
getmaxyx \
getparx \
getpary \
getparyx \
wget_wch \
)

AC_CHECK_FUNC(start_color,[AC_DEFINE(HAVE_COLOR)])
CF_CURSES_CHTYPE
CF_FUNC_WAIT

AC_TRY_LINK([#include <locale.h>],[setlocale(LC_ALL, "")],[AC_DEFINE(HAVE_SETLOCALE)])

AC_OUTPUT(makefile $SUB_MAKEFILE samples/install/makefile,,,sort -u)

--- NEW FILE: inputbox.c ---
/*
 *  $Id: inputbox.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  inputbox.c -- implements the input box
 *
 *  Copyright 2000-2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors:
 *	Savio Lam (lam836 at cs.cuhk.hk)
 */

#include <dialog.h>
#include <dlg_keys.h>

#define sTEXT -1

/*
 * Display a dialog box for entering a string
 */
int
dialog_inputbox(const char *title, const char *cprompt, int height, int width,
		const char *init, const int password)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	INPUTSTR_BINDINGS,
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV,	KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV,	KEY_LEFT ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV,	KEY_UP ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif
    int x, y, box_y, box_x, box_width;
    int show_buttons;
    int col_offset = 0;
    int chr_offset = 0;
    int key, fkey, code;
    int result = DLG_EXIT_UNKNOWN;
    int state;
    int first;
    char *input;
    WINDOW *dialog;
    char *prompt = dlg_strclone(cprompt);
    const char **buttons = dlg_ok_labels();

    dlg_does_output();

    dlg_tab_correct_str(prompt);

    /* Set up the initial value */
    input = dlg_set_result(init);

#ifdef KEY_RESIZE
  retry:
#endif
    show_buttons = TRUE;
    state = dialog_vars.defaultno ? dlg_defaultno_button() : sTEXT;
    first = (state == sTEXT);
    key = fkey = 0;

    if (init != NULL) {
	dlg_auto_size(title, prompt, &height, &width, 5,
		      MIN(MAX(dlg_count_columns(init) + 7, 26),
			  SCOLS - (dialog_vars.begin_set ?
				   dialog_vars.begin_x : 0)));
    } else {
	dlg_auto_size(title, prompt, &height, &width, 5, 26);
    }
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);

    dialog = dlg_new_window(height, width, y, x);
    dlg_register_window(dialog, "inputbox", binding);
    dlg_register_buttons(dialog, "inputbox", buttons);

    dlg_mouse_setbase(x, y);

    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_bottom_box(dialog);
    dlg_draw_title(dialog, title);

    wattrset(dialog, dialog_attr);
    dlg_print_autowrap(dialog, prompt, height, width);

    /* Draw the input field box */
    box_width = width - 6;
    getyx(dialog, y, x);
    box_y = y + 2;
    box_x = (width - box_width) / 2;
    dlg_mouse_mkregion(y + 1, box_x - 1, 3, box_width + 2, 'i');
    dlg_draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2,
		 border_attr, dialog_attr);

    while (result == DLG_EXIT_UNKNOWN) {
	int edit = 0;

	/*
	 * The last field drawn determines where the cursor is shown:
	 */
	if (show_buttons) {
	    show_buttons = FALSE;
	    col_offset = dlg_edit_offset(input, chr_offset, box_width);
	    (void) wmove(dialog, box_y, box_x + col_offset);
	    dlg_draw_buttons(dialog, height - 2, 0, buttons, state, FALSE, width);
	}

	if (!first) {
	    key = dlg_mouse_wgetch(dialog, &fkey);
	    if (dlg_result_key(key, fkey, &result))
		break;
	}

	/*
	 * Handle mouse clicks first, since we want to know if this is a button,
	 * or something that dlg_edit_string() should handle.
	 */
	if (fkey
	    && is_DLGK_MOUSE(key)
	    && (code = dlg_ok_buttoncode(key - M_EVENT)) >= 0) {
	    result = code;
	    continue;
	}

	if (state == sTEXT) {	/* Input box selected */
	    edit = dlg_edit_string(input, &chr_offset, key, fkey, first);

	    if (edit) {
		dlg_show_string(dialog, input, chr_offset, inputbox_attr,
				box_y, box_x, box_width, password, first);
		first = FALSE;
		continue;
	    } else if (first) {
		first = FALSE;
		continue;
	    }
	}

	/* handle non-functionkeys */
	if (!fkey && (code = dlg_char_to_button(key, buttons)) >= 0) {
	    dlg_del_window(dialog);
	    result = dlg_ok_buttoncode(code);
	    continue;
	}

	/* handle functionkeys */
	if (fkey) {
	    switch (key) {
	    case DLGK_MOUSE('i'):	/* mouse enter events */
		state = 0;
		/* FALLTHRU */
	    case DLGK_FIELD_PREV:
		show_buttons = TRUE;
		state = dlg_prev_ok_buttonindex(state, sTEXT);
		break;
	    case DLGK_FIELD_NEXT:
		show_buttons = TRUE;
		state = dlg_next_ok_buttonindex(state, sTEXT);
		break;
	    case ' ':		/* FIXME: conflict with inputstr.c */
	    case DLGK_ENTER:
		dlg_del_window(dialog);
		result = (state >= 0) ? dlg_ok_buttoncode(state) : DLG_EXIT_OK;
		break;
#ifdef KEY_RESIZE
	    case KEY_RESIZE:
		/* reset data */
		height = old_height;
		width = old_width;
		/* repaint */
		dlg_clear();
		dlg_del_window(dialog);
		refresh();
		dlg_mouse_free_regions();
		goto retry;
#endif
	    default:
		beep();
		break;
	    }
	} else {
	    beep();
	}
    }

    dlg_del_window(dialog);
    dlg_mouse_free_regions();
    free(prompt);
    return result;
}

--- NEW FILE: dialog.c ---
/*
 * $Id: dialog.c,v 1.1 2006-10-23 20:59:26 stsp Exp $
 *
 *  cdialog - Display simple dialog boxes from shell scripts
 *
 *  Copyright 2000-2004,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
[...1483 lines suppressed...]
		    /* if we got a cancel, etc., stop chaining */
		    if (retval != DLG_EXIT_OK)
			esc_pressed = TRUE;
		    else
			dialog_vars.dlg_clear_screen = TRUE;
		    break;
		}
	    }
	    if (dialog_vars.dlg_clear_screen)
		dlg_clear();
	}
    }

    dlg_killall_bg(&retval);
    if (dialog_state.screen_initialized) {
	(void) refresh();
	end_dialog();
    }
    dlg_exit(retval);
}

--- NEW FILE: mkdirs.sh ---
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman at prep.ai.mit.edu>
# Created: 1993-05-16
# Last modified: 1994-03-25
# Public domain
#

errstatus=0
umask 022

for file in ${1+"$@"} ; do
   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
   shift

   pathcomp=
   for d in ${1+"$@"} ; do
     pathcomp="$pathcomp$d"
     case "$pathcomp" in
       -* ) pathcomp=./$pathcomp ;;
     esac

     if test ! -d "$pathcomp"; then
        echo "mkdir $pathcomp" 1>&2
        case "$pathcomp" in
          [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]: )
                ;;               # DOSISH systems
          * )          mkdir "$pathcomp" || errstatus=$? ;;
        esac
     fi

     pathcomp="$pathcomp/"
   done
done

exit $errstatus

# mkinstalldirs ends here

--- NEW FILE: util.c ---
/*
 *  $Id: util.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  util.c -- miscellaneous utilities for dialog
 *
 *  Copyright 2000-2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
[...1751 lines suppressed...]
    return y;
}
#endif

#if !(defined(HAVE_GETPARX) && defined(HAVE_GETPARY))
int
getparx(WINDOW *win)
{
    int y, x;
    getparyx(win, y, x);
    return x;
}
int
getpary(WINDOW *win)
{
    int y, x;
    getparyx(win, y, x);
    return y;
}
#endif

--- NEW FILE: COPYING ---
		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

			    NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

		     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!



--- NEW FILE: aclocal.m4 ---
dnl macros used for DIALOG configure script
dnl Copyright 1999-2004,2005 -- Thomas E. Dickey
dnl $Id: aclocal.m4,v 1.1 2006-10-23 20:59:25 stsp Exp $
dnl ---------------------------------------------------------------------------
dnl ---------------------------------------------------------------------------
dnl AM_GNU_GETTEXT version: 11 updated: 2004/01/26 20:58:40
dnl --------------
dnl Usage: Just like AM_WITH_NLS, which see.
AC_DEFUN([AM_GNU_GETTEXT],
  [AC_REQUIRE([AC_PROG_MAKE_SET])dnl
   AC_REQUIRE([AC_PROG_CC])dnl
   AC_REQUIRE([AC_CANONICAL_HOST])dnl
   AC_REQUIRE([AC_PROG_RANLIB])dnl
   AC_REQUIRE([AC_ISC_POSIX])dnl
   AC_REQUIRE([AC_HEADER_STDC])dnl
   AC_REQUIRE([AC_C_CONST])dnl
   AC_REQUIRE([AC_C_INLINE])dnl
   AC_REQUIRE([AC_TYPE_OFF_T])dnl
   AC_REQUIRE([AC_TYPE_SIZE_T])dnl
[...2949 lines suppressed...]
  [
    AC_CACHE_CHECK(whether we are using the GNU C Library 2.1 or newer,
      ac_cv_gnu_library_2_1,
      [AC_EGREP_CPP([Lucky GNU user],
	[
#include <features.h>
#ifdef __GNU_LIBRARY__
 #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2)
  Lucky GNU user
 #endif
#endif
	],
	ac_cv_gnu_library_2_1=yes,
	ac_cv_gnu_library_2_1=no)
      ]
    )
    AC_SUBST(GLIBC21)
    GLIBC21="$ac_cv_gnu_library_2_1"
  ]
)

--- NEW FILE: dialog.h ---
/*
 *  $Id: dialog.h,v 1.1 2006-10-23 20:59:26 stsp Exp $
 *
 *  dialog.h -- common declarations for all dialog modules
 *
 *  Copyright 2000-2004,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors
 *	Savio Lam (lam836 at cs.cuhk.hk)
 */

#ifndef DIALOG_H_included
#define DIALOG_H_included 1

#include <dlg_config.h>

#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>	/* fork() etc. */
#include <math.h>	/* sqrt() */

#if defined(HAVE_NCURSESW_NCURSES_H)
#include <ncursesw/ncurses.h>
#elif defined(HAVE_NCURSES_NCURSES_H)
#include <ncurses/ncurses.h>
#elif defined(HAVE_NCURSES_CURSES_H)
#include <ncurses/curses.h>
#elif defined(HAVE_NCURSES_H)
#include <ncurses.h>
#elif defined(ultrix)
#include <cursesX.h>
#else
#include <curses.h>
#endif

/* possible conflicts with <term.h> which may be included in <curses.h> */
#ifdef color_names
#undef color_names
#endif

#ifdef buttons
#undef buttons
#endif

#ifdef ENABLE_NLS
#include <libintl.h>
#include <langinfo.h>
#define _(s) gettext(s)
#else
#undef _
#define _(s) s
#endif

#ifndef GCC_NORETURN
#define GCC_NORETURN /*nothing*/
#endif

#ifndef GCC_UNUSED
#define GCC_UNUSED /*nothing*/
#endif

#ifndef HAVE_WGET_WCH
#undef USE_WIDE_CURSES
#endif

/*
 * FIXME: a configure check would be useful
 */
#ifdef __hpux
#undef ACS_UARROW
#undef ACS_DARROW
#endif

/*
 * Change these if you want
 */
#define USE_SHADOW TRUE
#define USE_COLORS TRUE

#ifdef HAVE_COLOR
#define SCOLS	(COLS - (dialog_state.use_shadow ? 2 : 0))
#define SLINES	(LINES - (dialog_state.use_shadow ? 1 : 0))
#else
#define SCOLS	COLS
#define SLINES	LINES
#endif

#define DLG_EXIT_ESC		255
#define DLG_EXIT_UNKNOWN	-2	/* never return this (internal use) */
#define DLG_EXIT_ERROR		-1	/* the shell sees this as 255 */
#define DLG_EXIT_OK		0
#define DLG_EXIT_CANCEL		1
#define DLG_EXIT_HELP		2
#define DLG_EXIT_EXTRA		3
#define DLG_EXIT_ITEM_HELP	4	/* actually DLG_EXIT_HELP */

#define CHR_BACKSPACE	8
#define CHR_REPAINT	12	/* control/L */
#define CHR_DELETE	127
#define CHR_NEXT	('n' & 0x1f)
#define CHR_PREVIOUS	('p' & 0x1f)

#define ESC 27
#define TAB 9

#define MARGIN 1
#define GUTTER 2
#define SHADOW_ROWS 1
#define SHADOW_COLS 2

#define MAX_LEN 2048
#define BUF_SIZE (10*1024)

#undef  MIN
#define MIN(x,y) ((x) < (y) ? (x) : (y))

#undef  MAX
#define MAX(x,y) ((x) > (y) ? (x) : (y))

#define DEFAULT_SEPARATE_STR "\t"
#define DEFAULT_ASPECT_RATIO 9
/* how many spaces is a tab long (default)? */
#define TAB_LEN 8
#define WTIMEOUT_VAL        10

#ifndef A_CHARTEXT
#define A_CHARTEXT 0xff
#endif

#define CharOf(ch)  ((ch) & A_CHARTEXT)

#ifndef ACS_ULCORNER
#define ACS_ULCORNER '+'
#endif
#ifndef ACS_LLCORNER
#define ACS_LLCORNER '+'
#endif
#ifndef ACS_URCORNER
#define ACS_URCORNER '+'
#endif
#ifndef ACS_LRCORNER
#define ACS_LRCORNER '+'
#endif
#ifndef ACS_HLINE
#define ACS_HLINE '-'
#endif
#ifndef ACS_VLINE
#define ACS_VLINE '|'
#endif
#ifndef ACS_LTEE
#define ACS_LTEE '+'
#endif
#ifndef ACS_RTEE
#define ACS_RTEE '+'
#endif
#ifndef ACS_UARROW
#define ACS_UARROW '^'
#endif
#ifndef ACS_DARROW
#define ACS_DARROW 'v'
#endif

/* these definitions may work for antique versions of curses */
#ifndef HAVE_GETBEGYX
#undef  getbegyx
#define getbegyx(win,y,x)	(y = (win)?(win)->_begy:ERR, x = (win)?(win)->_begx:ERR)
#endif

#ifndef HAVE_GETMAXYX
#undef  getmaxyx
#define getmaxyx(win,y,x)	(y = (win)?(win)->_maxy:ERR, x = (win)?(win)->_maxx:ERR)
#endif

#ifndef HAVE_GETPARYX
#undef  getparyx
#define getparyx(win,y,x)	(y = (win)?(win)->_pary:ERR, x = (win)?(win)->_parx:ERR)
#endif

#ifdef __cplusplus
extern "C" {
#endif

/* these definitions may be needed for bleeding-edge curses implementations */
#if !(defined(HAVE_GETBEGX) && defined(HAVE_GETBEGY))
#undef getbegx
#undef getbegy
#define getbegx(win) dlg_getbegx(win)
#define getbegy(win) dlg_getbegy(win)
extern int dlg_getbegx(WINDOW * /*win*/);
extern int dlg_getbegy(WINDOW * /*win*/);
#endif

#if !(defined(HAVE_GETCURX) && defined(HAVE_GETCURY))
#undef getcurx
#undef getcury
#define getcurx(win) dlg_getcurx(win)
#define getcury(win) dlg_getcury(win)
extern int dlg_getcurx(WINDOW * /*win*/);
extern int dlg_getcury(WINDOW * /*win*/);
#endif

#if !(defined(HAVE_GETMAXX) && defined(HAVE_GETMAXY))
#undef getmaxx
#undef getmaxy
#define getmaxx(win) dlg_getmaxx(win)
#define getmaxy(win) dlg_getmaxy(win)
extern int dlg_getmaxx(WINDOW * /*win*/);
extern int dlg_getmaxy(WINDOW * /*win*/);
#endif

#if !(defined(HAVE_GETPARX) && defined(HAVE_GETPARY))
#undef getparx
#undef getpary
#define getparx(win) dlg_getparx(win)
#define getpary(win) dlg_getpary(win)
extern int dlg_getparx(WINDOW * /*win*/);
extern int dlg_getpary(WINDOW * /*win*/);
#endif

/*
 * This is a list of "old" names, which should be helpful in updating
 * applications that use libdialog.  Starting with 2003/11/26, all exported
 * symbols from libdialog have "dlg_" prefix, or "dialog_" prefix or "_dialog"
 * suffix.
 */
#ifdef __DIALOG_OLD_NAMES__
#define color_table                       dlg_color_table
#define attr_clear(win,h,w,a)             dlg_attr_clear(win,h,w,a)
#define auto_size(t,s,h,w,xl,mc)          dlg_auto_size(t,s,h,w,xl,mc)
#define auto_sizefile(t,f,h,w,xl,mc)      dlg_auto_sizefile(t,f,h,w,xl,mc)
#define beeping()                         dlg_beeping()
#define box_x_ordinate(w)                 dlg_box_x_ordinate(w)
#define box_y_ordinate(h)                 dlg_box_y_ordinate(h)
#define calc_listh(h,lh,in)               dlg_calc_listh(h,lh,in)
#define calc_listw(in,items,group)        dlg_calc_listw(in,items,group)
#define color_setup()                     dlg_color_setup()
#define create_rc(f)                      dlg_create_rc(f)
#define ctl_size(h,w)                     dlg_ctl_size(h,w)
#define del_window(win)                   dlg_del_window(win)
#define dialog_clear()                    dlg_clear()
#define draw_bottom_box(win)              dlg_draw_bottom_box(win)
#define draw_box(win,y,x,h,w,xc,bc)       dlg_draw_box(win,y,x,h,w,xc,bc)
#define draw_shadow(win,h,w,y,x)          dlg_draw_shadow(win,h,w,y,x)
#define draw_title(win,t)                 dlg_draw_title(win,t)
#define exiterr                           dlg_exiterr
#define killall_bg(n)                     dlg_killall_bg(n)
#define mouse_bigregion(y,x)              dlg_mouse_bigregion(y,x)
#define mouse_free_regions()              dlg_mouse_free_regions()
#define mouse_mkbigregion(y,x,h,w,n,ix,iy,m) dlg_mouse_mkbigregion(y,x,h,w,n,ix,iy,m)
#define mouse_mkregion(y,x,h,w,n)         dlg_mouse_mkregion(y,x,h,w,n)
#define mouse_region(y,x)                 dlg_mouse_region(y,x)
#define mouse_setbase(x,y)                dlg_mouse_setbase(x,y)
#define mouse_wgetch(w,c)                 dlg_mouse_wgetch(w,c)
#define new_window(h,w,y,x)               dlg_new_window(h,w,y,x)
#define parse_rc()                        dlg_parse_rc()
#define print_autowrap(win,s,h,w)         dlg_print_autowrap(win,s,h,w)
#define print_size(h,w)                   dlg_print_size(h,w)
#define put_backtitle()                   dlg_put_backtitle()
#define strclone(cprompt)                 dlg_strclone(cprompt)
#define sub_window(win,h,w,y,x)           dlg_sub_window(win,h,w,y,x)
#define tab_correct_str(s)                dlg_tab_correct_str(s)
#endif

/*
 * Attribute names
 */
#define DIALOG_ATR(n)                 dlg_color_table[n].atr

#define screen_attr                   DIALOG_ATR(0)
#define shadow_attr                   DIALOG_ATR(1)
#define dialog_attr                   DIALOG_ATR(2)
#define title_attr                    DIALOG_ATR(3)
#define border_attr                   DIALOG_ATR(4)
#define button_active_attr            DIALOG_ATR(5)
#define button_inactive_attr          DIALOG_ATR(6)
#define button_key_active_attr        DIALOG_ATR(7)
#define button_key_inactive_attr      DIALOG_ATR(8)
#define button_label_active_attr      DIALOG_ATR(9)
#define button_label_inactive_attr    DIALOG_ATR(10)
#define inputbox_attr                 DIALOG_ATR(11)
#define inputbox_border_attr          DIALOG_ATR(12)
#define searchbox_attr                DIALOG_ATR(13)
#define searchbox_title_attr          DIALOG_ATR(14)
#define searchbox_border_attr         DIALOG_ATR(15)
#define position_indicator_attr       DIALOG_ATR(16)
#define menubox_attr                  DIALOG_ATR(17)
#define menubox_border_attr           DIALOG_ATR(18)
#define item_attr                     DIALOG_ATR(19)
#define item_selected_attr            DIALOG_ATR(20)
#define tag_attr                      DIALOG_ATR(21)
#define tag_selected_attr             DIALOG_ATR(22)
#define tag_key_attr                  DIALOG_ATR(23)
#define tag_key_selected_attr         DIALOG_ATR(24)
#define check_attr                    DIALOG_ATR(25)
#define check_selected_attr           DIALOG_ATR(26)
#define uarrow_attr                   DIALOG_ATR(27)
#define darrow_attr                   DIALOG_ATR(28)
#define itemhelp_attr                 DIALOG_ATR(29)
#define form_active_text_attr         DIALOG_ATR(30)
#define form_text_attr                DIALOG_ATR(31)

#define DLGK_max (KEY_MAX + 256)

/*
 * Callbacks are used to implement the "background" tailbox.
 */
typedef struct _dlg_callback {
    struct _dlg_callback *next;
    FILE *input;
    WINDOW *win;
    bool keep_bg;	/* keep in background, on exit */
    bool bg_task;	/* true if this is background task */
    bool (*handle_getc)(struct _dlg_callback *p, int ch, int fkey, int *result);
} DIALOG_CALLBACK;

typedef struct _dlg_windows {
    struct _dlg_windows *next;
    WINDOW *normal;
    WINDOW *shadow;
} DIALOG_WINDOWS;

/*
 * Global variables, which are initialized as needed
 */
typedef struct {
    DIALOG_CALLBACK *getc_callbacks;
    DIALOG_CALLBACK *getc_redirect;
    DIALOG_WINDOWS *all_windows;
    FILE *output;		/* option "--output-fd fd" */
    FILE *pipe_input;		/* used for gauge widget */
    FILE *screen_output;	/* newterm(), etc. */
    bool screen_initialized;
    bool use_colors;		/* use colors by default? */
    bool use_scrollbar;		/* RESERVED */
    bool use_shadow;		/* shadow dialog boxes by default? */
    bool visit_items;		/* option "--visit-items" */
    char *separate_str;		/* option "--separate-widget string" */
    int aspect_ratio;		/* option "--aspect ratio" */
    int output_count;		/* # of widgets that may have done output */
    int tab_len;		/* option "--tab-len n" */
} DIALOG_STATE;

extern DIALOG_STATE dialog_state;

/*
 * Global variables, which dialog resets before each widget
 */
typedef struct {
    bool beep_after_signal;	/* option "--beep-after" */
    bool beep_signal;		/* option "--beep" */
    bool begin_set;		/* option "--begin y x" was used */
    bool cant_kill;		/* option "--no-kill" */
    bool colors;		/* option "--colors" */
    bool cr_wrap;		/* option "--cr-wrap" */
    bool defaultno;		/* option "--defaultno" */
    bool dlg_clear_screen;	/* option "--clear" */
    bool extra_button;		/* option "--extra-button" */
    bool help_button;		/* option "--help-button" */
    bool help_status;		/* option "--help-status" */
    bool input_menu;		/* menu vs inputmenu widget */
    bool insecure;		/* option "--insecure" */
    bool item_help;		/* option "--item-help" */
    bool keep_window;		/* option "--keep-window" */
    bool nocancel;		/* option "--no-cancel" */
    bool nocollapse;		/* option "--no-collapse" */
    bool print_siz;		/* option "--print-size" */
    bool separate_output;	/* option "--separate-output" */
    bool single_quoted;		/* option "--single-quoted" */
    bool size_err;		/* option "--size-err" */
    bool tab_correct;		/* option "--tab-correct" */
    bool trim_whitespace;	/* option "--trim" */
    char *backtitle;		/* option "--backtitle backtitle" */
    char *cancel_label;		/* option "--cancel-label string" */
    char *default_item;		/* option "--default-item string" */
    char *exit_label;		/* option "--exit-label string" */
    char *extra_label;		/* option "--extra-label string" */
    char *help_label;		/* option "--help-label string" */
    char *input_result;
    char *no_label;		/* option "--no-label string" */
    char *ok_label;		/* option "--ok-label string" */
    char *title;		/* option "--title title" */
    char *yes_label;		/* option "--yes-label string" */
    int begin_x;		/* option "--begin y x" (second value) */
    int begin_y;		/* option "--begin y x" (first value) */
    int max_input;		/* option "--max-input size" */
    int scale_factor;		/* RESERVED */
    int sleep_secs;		/* option "--sleep secs" */
    int timeout_secs;		/* option "--timeout secs" */
    unsigned input_length;	/* nonzero if input_result is allocated */
    /* 1.0-20051207 */
    unsigned formitem_type;	/* DIALOG_FORMITEM.type in dialog_form() */
} DIALOG_VARS;

#define USE_ITEM_HELP(s)        (dialog_vars.item_help && (s) != 0)
#define CHECKBOX_TAGS           (dialog_vars.item_help ? 4 : 3)
#define MENUBOX_TAGS            (dialog_vars.item_help ? 3 : 2)
#define FORMBOX_TAGS            (dialog_vars.item_help ? 9 : 8)

extern DIALOG_VARS dialog_vars;

#ifndef HAVE_TYPE_CHTYPE
#define chtype long
#endif

#define UCH(ch)			((unsigned char)(ch))

#define assert_ptr(ptr,msg) if ((ptr) == 0) dlg_exiterr("cannot allocate memory in " msg)

/*
 * Table for attribute- and color-values.
 */
typedef struct {
    chtype atr;
#ifdef HAVE_COLOR
    int fg;
    int bg;
    int hilite;
#endif
#ifdef HAVE_RC_FILE
    char *name;
    char *comment;
#endif
} DIALOG_COLORS;

extern DIALOG_COLORS dlg_color_table[];

/*
 * Function prototypes
 */
extern char *dialog_version(void);

/* widgets, each in separate files */
extern int dialog_calendar(const char * /*title*/, const char * /*subtitle*/, int /*height*/, int /*width*/, int /*day*/, int /*month*/, int /*year*/);
extern int dialog_checklist(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/, int /*list_height*/, int /*item_no*/, char ** /*items*/, int /*flag*/);
extern int dialog_form(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/, int /*form_height*/, int /*item_no*/, char ** /*items*/);
extern int dialog_fselect(const char * /*title*/, const char * /*path*/, int /*height*/, int /*width*/);
extern int dialog_gauge(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/, int /*percent*/);
extern int dialog_inputbox(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/, const char * /*init*/, const int /*password*/);
extern int dialog_menu(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/, int /*menu_height*/, int /*item_no*/, char ** /*items*/);
extern int dialog_msgbox(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/, int /*pauseopt*/);
extern int dialog_pause(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/, int /*seconds*/);
extern int dialog_progressbox(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/);
extern int dialog_tailbox(const char * /*title*/, const char * /*file*/, int /*height*/, int /*width*/, int /*bg_task*/);
extern int dialog_textbox(const char * /*title*/, const char * /*file*/, int /*height*/, int /*width*/);
extern int dialog_timebox(const char * /*title*/, const char * /*subtitle*/, int /*height*/, int /*width*/, int /*hour*/, int /*minute*/, int /*second*/);
extern int dialog_yesno(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/);

/* some widgets have alternate entrypoints, to allow list manipulation */
typedef struct {
    char *name;
    char *text;
    char *help;
    int state;
} DIALOG_LISTITEM;

typedef struct {
    unsigned type;		/* the field type (0=input, 1=password) */
    char *name;			/* the field label */
    int name_len;		/* ...its length */
    int name_y;			/* ...its y-ordinate */
    int name_x;			/* ...its x-ordinate */
    bool name_free;		/* ...true if .name can be freed */
    char *text;			/* the field contents */
    int text_len;		/* ...its length on the screen */
    int text_y;			/* ...its y-ordinate */
    int text_x;			/* ...its x-ordinate */
    int text_flen;		/* ...its length on screen, or 0 if no input allowed */
    int text_ilen;		/* ...its limit on amount to be entered */
    bool text_free;		/* ...true if .text can be freed */
    char *help;			/* help-message, if any */
    bool help_free;		/* ...true if .help can be freed */
} DIALOG_FORMITEM;

typedef	int (DIALOG_INPUTMENU) (DIALOG_LISTITEM * /*items*/, int /*current*/, char * /*newtext*/);

extern int dlg_checklist(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/, int /*list_height*/, int /*item_no*/, DIALOG_LISTITEM * /*items*/, const char * /*states*/, int /*flag*/, int * /*current_item*/);
extern int dlg_form(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/, int /*form_height*/, int /*item_no*/, DIALOG_FORMITEM * /*items*/, int * /*current_item*/);
extern int dlg_menu(const char * /*title*/, const char * /*cprompt*/, int /*height*/, int /*width*/, int /*menu_height*/, int /*item_no*/, DIALOG_LISTITEM * /*items*/, int * /*current_item*/, DIALOG_INPUTMENU /*rename_menu*/);

/* arrows.c */
extern void dlg_draw_arrows(WINDOW * /*dialog*/, int /*top_arrow*/, int /*bottom_arrow*/, int /*x*/, int /*top*/, int /*bottom*/);
extern void dlg_draw_arrows2(WINDOW * /*dialog*/, int /*top_arrow*/, int /*bottom_arrow*/, int /*x*/, int /*top*/, int /*bottom*/, chtype /*attr*/, chtype /*borderattr*/);

/* buttons.c */
extern const char ** dlg_exit_label(void);
extern const char ** dlg_ok_label(void);
extern const char ** dlg_ok_labels(void);
extern const char ** dlg_yes_labels(void);
extern int dlg_button_count(const char ** /*labels*/);
extern int dlg_button_to_char(const char * /*label*/);
extern int dlg_button_x_step(const char ** /*labels*/, int /*limit*/, int * /*gap*/, int * /*margin*/, int * /*step*/);
extern int dlg_char_to_button(int /*ch*/, const char ** /*labels*/);
extern int dlg_exit_buttoncode(int /*button*/);
extern int dlg_match_char(int /*ch*/, const char * /*string*/);
extern int dlg_next_button(const char ** /*labels*/, int /*button*/);
extern int dlg_next_ok_buttonindex(int /*current*/, int /*extra*/);
extern int dlg_ok_buttoncode(int /*button*/);
extern int dlg_prev_button(const char ** /*labels*/, int /*button*/);
extern int dlg_prev_ok_buttonindex(int /*current*/, int /*extra*/);
extern int dlg_yes_buttoncode(int /*button*/);
extern void dlg_button_layout(const char ** /*labels*/, int * /*limit*/);
extern void dlg_button_sizes(const char ** /*labels*/, int /*vertical*/, int * /*longest*/, int * /*length*/);
extern void dlg_draw_buttons(WINDOW * /*win*/, int /*y*/, int /*x*/, const char ** /*labels*/, int /*selected*/, int /*vertical*/, int /*limit*/);

/* formbox.c */
extern int dlg_default_formitem(DIALOG_FORMITEM * /*items*/);
extern void dlg_free_formitems(DIALOG_FORMITEM * /*items*/);

/* inputstr.c */
extern bool dlg_edit_string(char * /*string*/, int * /*offset*/, int /*key*/, int /*fkey*/, bool /*force*/);
extern const int * dlg_index_columns(const char * /*string*/);
extern const int * dlg_index_wchars(const char * /*string*/);
extern int dlg_count_columns(const char * /*string*/);
extern int dlg_count_wchars(const char * /*string*/);
extern int dlg_edit_offset(char * /*string*/, int /*offset*/, int /*x_last*/);
extern int dlg_limit_columns(const char * /*string*/, int /*limit*/, int /*offset*/);
extern void dlg_show_string(WINDOW * /*win*/, const char * /*string*/, int /*offset*/, chtype /*attr*/, int /*y_base*/, int /*x_base*/, int /*x_last*/, bool /*hidden*/, bool /*force*/);

/* rc.c */
#ifdef HAVE_RC_FILE
extern int dlg_parse_rc(void);
extern void dlg_create_rc(const char * /*filename*/);
#endif

/* ui_getc.c */
extern int dlg_getc(WINDOW * /*win*/, int * /*fkey*/);
extern int dlg_getc_callbacks(int /*ch*/, int /*fkey*/, int * /*result*/);
extern int dlg_last_getc(void);
extern void dlg_add_callback(DIALOG_CALLBACK * /*p*/);
extern void dlg_flush_getc(void);
extern void dlg_remove_callback(DIALOG_CALLBACK * /*p*/);
extern void dlg_killall_bg(int *retval);

/* util.c */
extern WINDOW * dlg_new_window(int /*height*/, int /*width*/, int /*y*/, int /*x*/);
extern WINDOW * dlg_sub_window(WINDOW * /*win*/, int /*height*/, int /*width*/, int /*y*/, int /*x*/);
extern char * dlg_set_result(const char * /*string*/);
extern char * dlg_strclone(const char * /*cprompt*/);
extern int dlg_box_x_ordinate(int /*width*/);
extern int dlg_box_y_ordinate(int /*height*/);
extern int dlg_calc_list_width(int /*item_no*/, DIALOG_LISTITEM * /*items*/);
extern int dlg_calc_listw(int /*item_no*/, char ** /*items*/, int /*group*/);
extern int dlg_default_item(char ** /*items*/, int /*llen*/);
extern int dlg_default_listitem(DIALOG_LISTITEM * /*items*/);
extern int dlg_defaultno_button(void);
extern void dlg_add_quoted(char * /*string*/);
extern void dlg_add_result(char * /*string*/);
extern void dlg_attr_clear(WINDOW * /*win*/, int /*height*/, int /*width*/, chtype /*attr*/);
extern void dlg_auto_size(const char * /*title*/, const char * /*prompt*/, int * /*height*/, int * /*width*/, int /*boxlines*/, int /*mincols*/);
extern void dlg_auto_sizefile(const char * /*title*/, const char * /*file*/, int * /*height*/, int * /*width*/, int /*boxlines*/, int /*mincols*/);
extern void dlg_beeping(void);
extern void dlg_calc_listh(int * /*height*/, int * /*list_height*/, int /*item_no*/);
extern void dlg_clear(void);
extern void dlg_clr_result(void);
extern void dlg_ctl_size(int /*height*/, int /*width*/);
extern void dlg_del_window(WINDOW * /*win*/);
extern void dlg_does_output(void);
extern void dlg_draw_bottom_box(WINDOW * /*win*/);
extern void dlg_draw_box(WINDOW * /*win*/, int /*y*/, int /*x*/, int /*height*/, int /*width*/, chtype /*boxchar*/, chtype /*borderchar*/);
extern void dlg_draw_title(WINDOW *win, const char *title);
extern void dlg_exit(int /*code*/) GCC_NORETURN;
extern void dlg_item_help(char * /*txt*/);
extern void dlg_print_autowrap(WINDOW * /*win*/, const char * /*prompt*/, int /*height*/, int /*width*/);
extern void dlg_print_size(int /*height*/, int /*width*/);
extern void dlg_print_text(WINDOW * /*win*/, const char * /*txt*/, int /*len*/, chtype * /*attr*/);
extern void dlg_put_backtitle(void);
extern void dlg_set_focus(WINDOW * /*parent*/, WINDOW * /*win*/);
extern void dlg_tab_correct_str(char * /*prompt*/);
extern void dlg_trim_string(char * /*src*/);
extern void end_dialog(void);
extern void init_dialog(FILE * /*input*/, FILE * /*output*/);

extern void dlg_exiterr(const char *, ...)
#if defined(__GNUC__) && !defined(printf)
__attribute__((format(printf,1,2)))
#endif
;

#ifdef HAVE_COLOR
extern chtype dlg_color_pair(int /*foreground*/, int /*background*/);
extern int dlg_color_count(void);
extern void dlg_color_setup(void);
extern void dlg_draw_shadow(WINDOW * /*win*/, int /*height*/, int /*width*/, int /*y*/, int /*x*/);
#endif

#ifdef HAVE_STRCASECMP
#define dlg_strcmp(a,b) strcasecmp(a,b)
#else
extern int dlg_strcmp(const char * /*a*/, const char * /*b*/);
#endif

/*
 * The following stuff is needed for mouse support
 */
typedef struct mseRegion {
    int x, y, X, Y, code;
    int mode, step_x, step_y;
    struct mseRegion *next;
} mseRegion;

#if defined(NCURSES_MOUSE_VERSION)

#define	mouse_open() mousemask(BUTTON1_CLICKED, (mmask_t *) 0)
#define	mouse_close() mousemask(0, (mmask_t *) 0)

extern mseRegion * dlg_mouse_mkregion (int /*y*/, int /*x*/, int /*height*/, int /*width*/, int /*code*/);
extern void dlg_mouse_free_regions (void);
extern void dlg_mouse_mkbigregion (int /*y*/, int /*x*/, int /*height*/, int /*width*/, int /*code*/, int /*step_x*/, int /*step_y*/, int /*mode*/);
extern void dlg_mouse_setbase (int /*x*/, int /*y*/);

#define USE_MOUSE 1

#else

#define	mouse_open() /*nothing*/
#define	mouse_close() /*nothing*/
#define dlg_mouse_free_regions() /* nothing */
#define	dlg_mouse_mkregion(y, x, height, width, code) /*nothing*/
#define	dlg_mouse_mkbigregion(y, x, height, width, code, step_x, step_y, mode) /*nothing*/
#define	dlg_mouse_setbase(x, y) /*nothing*/

#define USE_MOUSE 0

#endif

extern mseRegion *dlg_mouse_region (int /*y*/, int /*x*/);
extern mseRegion *dlg_mouse_bigregion (int /*y*/, int /*x*/);
extern int dlg_mouse_wgetch (WINDOW * /*win*/, int * /*fkey*/);
extern int dlg_mouse_wgetch_nowait (WINDOW * /*win*/, int * /*fkey*/);

#define mouse_mkbutton(y,x,len,code) dlg_mouse_mkregion(y,x,1,len,code);

/*
 * This is the base for fictitious keys, which activate
 * the buttons.
 *
 * Mouse-generated keys are the following:
 *   -- the first 32 are used as numbers, in addition to '0'-'9'
 *   -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
 *   -- uppercase chars are used to invoke the button (M_EVENT + 'O')
 */
#define M_EVENT (DLGK_max + 1)

/*
 * The `flag' parameter in checklist is used to select between
 * radiolist and checklist
 */
#define FLAG_CHECK 1
#define FLAG_RADIO 0

/*
 * This is used only for debugging (FIXME: should have a separate header).
 */
#ifdef NO_LEAKS
extern void _dlg_inputstr_leaks(void);
#if defined(NCURSES_VERSION) && defined(HAVE__NC_FREE_AND_EXIT)
extern void _nc_free_and_exit(int);	/* nc_alloc.h normally not installed */
#endif
#endif

#ifdef __cplusplus
}
#endif

#endif /* DIALOG_H_included */

--- NEW FILE: CHANGES ---
-- $Id: CHANGES,v 1.1 2006-10-23 20:59:25 stsp Exp $
-- Thomas E. Dickey <dickey at invisible-island.net>

This version of dialog was originally from a Debian snapshot.  I've done this
to it:

2006/02/21
	+ fix logic in split-out dlg_menu() to separate inputmenu and menu
	  handling (report by Auke Kok).

2006/01/26
	+ fix fselect.c to compile properly with Intel compiler and largefile
	  option.
	+ improve configure script checks for curses headers to work around
	  breakage in some packages, e.g., cygwin.
	+ amend correction to menubox, fixes normal menus (Debian #349969).

2006/01/19
	+ completed dialog.3 manpage
[...1193 lines suppressed...]

	+ corrected spelling of "gauge"

The relevant portions of the Debian change log for the original version
(dialog-0.9a-12) are abstracted here, omitting details of their packaging:

1998/05/24

	+ Replaced guage.c by the one in dialog 0.6z, which is known to work.
	  Fixes Bug #18284: unstable dialog.

1997/12/16

	+ dialog.c:  dialog_input_result printed with "%s" format.  This was
	  Bug #9913, fixed by Bill Mitchell, but the change was lost.
	+ Pristine source, .depend is not removed in clean target.  Instead, it
	  is made zero lenght (otherwise it would not work *without* fakeroot).
	+ Added '^U' support in input box (Bug #9915, patch by joey at debian.org).
	+ Wrote patch to fix core-dumping problem (Bug #13170).  Sven Rudolph
	  <sr1 at inf.tu-dresden.de>:

--- NEW FILE: buttons.c ---
/*
 *  $Id: buttons.c,v 1.1 2006-10-23 20:59:25 stsp Exp $
 *
 *  buttons.c -- draw buttons, e.g., OK/Cancel
 *
 * Copyright 2000-2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>
#include <dlg_keys.h>

#define MIN_BUTTON (dialog_state.visit_items ? -1 : 0)

static void
center_label(char *buffer, int longest, const char *label)
{
    int len = dlg_count_columns(label);
    int left = 0, right = 0;

    *buffer = 0;
    if (len < longest) {
	left = (longest - len) / 2;
	right = (longest - len - left);
	if (left > 0)
	    sprintf(buffer, "%*s", left, " ");
    }
    strcat(buffer, label);
    if (right > 0)
	sprintf(buffer + strlen(buffer), "%*s", right, " ");
}

/*
 * Parse a multibyte character out of the string, set it past the parsed
 * character.
 */
static int
string_to_char(const char **stringp)
{
    int result;
#ifdef USE_WIDE_CURSES
    const char *string = *stringp;
    int have = strlen(string);
    int check;
    int len;
    wchar_t cmp2;
    mbstate_t state;

    memset(&state, 0, sizeof(state));
    len = mbrlen(string, have, &state);
    if (len > 0 && len <= have) {
	memset(&state, 0, sizeof(state));
	check = mbrtowc(&cmp2, string, len, &state);
	if (check <= 0)
	    cmp2 = 0;
	*stringp += len;
    } else {
	cmp2 = UCH(*string);
	*stringp += 1;
    }
    result = cmp2;
#else
    const char *string = *stringp;
    result = UCH(*string);
    *stringp += 1;
#endif
    return result;
}

/*
 * Print a button
 */
static void
print_button(WINDOW *win, const char *label, int y, int x, int selected)
{
    int i;
    int state = 0;
    const int *indx = dlg_index_wchars(label);
    int limit = dlg_count_wchars(label);
    chtype key_attr = (selected
		       ? button_key_active_attr
		       : button_key_inactive_attr);
    chtype label_attr = (selected
			 ? button_label_active_attr
			 : button_label_inactive_attr);

    (void) wmove(win, y, x);
    wattrset(win, selected
	     ? button_active_attr
	     : button_inactive_attr);
    (void) waddstr(win, "<");
    wattrset(win, label_attr);
    for (i = 0; i < limit; ++i) {
	int first = indx[i];
	int last = indx[i + 1];

	switch (state) {
	case 0:
#ifdef USE_WIDE_CURSES
	    if ((last - first) != 1) {
		const char *temp = (label + first);
		int cmp = string_to_char(&temp);
		if (dlg_isupper(cmp)) {
		    wattrset(win, key_attr);
		    state = 1;
		}
		break;
	    }
#endif
	    if (dlg_isupper(UCH(label[first]))) {
		wattrset(win, key_attr);
		state = 1;
	    }
	    break;
	case 1:
	    wattrset(win, label_attr);
	    state = 2;
	    break;
	}
	waddnstr(win, label + first, last - first);
    }
    wattrset(win, selected
	     ? button_active_attr
	     : button_inactive_attr);
    (void) waddstr(win, ">");
    (void) wmove(win, y, x + strspn(label, " ") + 1);
}

/*
 * Count the buttons in the list.
 */
int
dlg_button_count(const char **labels)
{
    int result = 0;
    while (*labels++ != 0)
	++result;
    return result;
}

/*
 * Compute the size of the button array in columns.  Return the total number of
 * columns in *length, and the longest button's columns in *longest
 */
void
dlg_button_sizes(const char **labels,
		 int vertical,
		 int *longest,
		 int *length)
{
    int n;

    *length = 0;
    *longest = 0;
    for (n = 0; labels[n] != 0; n++) {
	if (vertical) {
	    *length += 1;
	    *longest = 1;
	} else {
	    int len = dlg_count_columns(labels[n]);
	    if (len > *longest)
		*longest = len;
	    *length += len;
	}
    }
    /*
     * If we can, make all of the buttons the same size.  This is only optional
     * for buttons laid out horizontally.
     */
    if (*longest < 6 - (*longest & 1))
	*longest = 6 - (*longest & 1);
    if (!vertical)
	*length = *longest * n;
}

/*
 * Compute the size of the button array.
 */
int
dlg_button_x_step(const char **labels, int limit, int *gap, int *margin, int *step)
{
    int count = dlg_button_count(labels);
    int longest;
    int length;
    int unused;
    int used;

    dlg_button_sizes(labels, FALSE, &longest, &length);
    used = (length + (count * 2));
    unused = limit - used;

    if ((*gap = unused / (count + 3)) <= 0) {
	if ((*gap = unused / (count + 1)) <= 0)
	    *gap = 1;
	*margin = *gap;
    } else {
	*margin = *gap * 2;
    }
    *step = *gap + (used + count - 1) / count;
    return (*gap > 0) && (unused >= 0);
}

/*
 * Make sure there is enough space for the buttons
 */
void
dlg_button_layout(const char **labels, int *limit)
{
    int width = 1;
    int gap, margin, step;

    while (!dlg_button_x_step(labels, width, &gap, &margin, &step))
	++width;
    width += (4 * MARGIN);
    if (width > COLS)
	width = COLS;
    if (width > *limit)
	*limit = width;
}

/*
 * Print a list of buttons at the given position.
 */
void
dlg_draw_buttons(WINDOW *win,
		 int y, int x,
		 const char **labels,
		 int selected,
		 int vertical,
		 int limit)
{
    chtype save = getattrs(win);
    int n;
    int step = 0;
    int length;
    int longest;
    int final_x;
    int final_y;
    int gap;
    int margin;
    unsigned need;
    char *buffer;

    dlg_mouse_setbase(getbegx(win), getbegy(win));

    getyx(win, final_y, final_x);

    dlg_button_sizes(labels, vertical, &longest, &length);

    if (vertical) {
	y += 1;
	step = 1;
    } else {
	dlg_button_x_step(labels, limit, &gap, &margin, &step);
	x += margin;
    }

    /*
     * Allocate a buffer big enough for any label.
     */
    need = longest;
    for (n = 0; labels[n] != 0; ++n) {
	need += strlen(labels[n]) + 1;
    }
    buffer = malloc(need);
    assert_ptr(buffer, "dlg_draw_buttons");

    /*
     * Draw the labels.
     */
    for (n = 0; labels[n] != 0; n++) {
	center_label(buffer, longest, labels[n]);
	mouse_mkbutton(y, x, dlg_count_columns(buffer), n);
	print_button(win, buffer, y, x,
		     (selected == n) || (n == 0 && selected < 0));
	if (selected == n)
	    getyx(win, final_y, final_x);

	if (vertical) {
	    if ((y += step) > limit)
		break;
	} else {
	    if ((x += step) > limit)
		break;
	}
    }
    (void) wmove(win, final_y, final_x);
    wrefresh(win);
    free(buffer);
    wattrset(win, save);
}

/*
 * Match a given character against the beginning of the string, ignoring case
 * of the given character.  The matching string must begin with an uppercase
 * character.
 */
int
dlg_match_char(int ch, const char *string)
{
    if (string != 0) {
	int cmp2 = string_to_char(&string);
#ifdef USE_WIDE_CURSES
	wint_t cmp1 = dlg_toupper(ch);
	if (cmp2 != 0 && (wchar_t) cmp1 == (wchar_t) dlg_toupper(cmp2)) {
	    return TRUE;
	}
#else
	if (ch > 0 && ch < 256) {
	    if (dlg_toupper(ch) == dlg_toupper(cmp2))
		return TRUE;
	}
#endif
    }
    return FALSE;
}

/*
 * Find the first uppercase character in the label, which we may use for an
 * abbreviation.
 */
int
dlg_button_to_char(const char *label)
{
    int cmp = -1;

    while (*label != 0) {
	cmp = string_to_char(&label);
	if (dlg_isupper(cmp)) {
	    break;
	}
    }
    return cmp;
}

/*
 * Given a list of button labels, and a character which may be the abbreviation
 * for one, find it, if it exists.  An abbreviation will be the first character
 * which happens to be capitalized in the label.
 */
int
dlg_char_to_button(int ch, const char **labels)
{
    if (labels != 0) {
	int j;

	ch = dlg_toupper(dlg_last_getc());
	for (j = 0; labels[j] != 0; ++j) {
	    int cmp = dlg_button_to_char(labels[j]);
	    if (ch == cmp) {
		dlg_flush_getc();
		return j;
	    }
	}
    }
    return DLG_EXIT_UNKNOWN;
}

static const char *
my_yes_label(void)
{
    return (dialog_vars.yes_label != NULL)
	? dialog_vars.yes_label
	: _("Yes");
}

static const char *
my_no_label(void)
{
    return (dialog_vars.no_label != NULL)
	? dialog_vars.no_label
	: _("No");
}

static const char *
my_ok_label(void)
{
    return (dialog_vars.ok_label != NULL)
	? dialog_vars.ok_label
	: _("OK");
}

static const char *
my_cancel_label(void)
{
    return (dialog_vars.cancel_label != NULL)
	? dialog_vars.cancel_label
	: _("Cancel");
}

static const char *
my_exit_label(void)
{
    return (dialog_vars.exit_label != NULL)
	? dialog_vars.exit_label
	: _("EXIT");
}

static const char *
my_extra_label(void)
{
    return (dialog_vars.extra_label != NULL)
	? dialog_vars.extra_label
	: _("Extra");
}

static const char *
my_help_label(void)
{
    return (dialog_vars.help_label != NULL)
	? dialog_vars.help_label
	: _("Help");
}

/*
 * Return a list of button labels.
 */
const char **
dlg_exit_label(void)
{
    const char **result;

    if (dialog_vars.extra_button) {
	result = dlg_ok_labels();
    } else {
	static const char *labels[3];
	int n = 0;

	labels[n++] = my_exit_label();
	if (dialog_vars.help_button)
	    labels[n++] = my_help_label();
	labels[n] = 0;

	result = labels;
    }
    return result;
}

/*
 * Map the given button index for dlg_exit_label() into our exit-code.
 */
int
dlg_exit_buttoncode(int button)
{
    int result = DLG_EXIT_ERROR;

    if (dialog_vars.extra_button) {
	result = dlg_ok_buttoncode(button);
    } else if (button == 0) {
	result = DLG_EXIT_OK;
    } else if (button == 1 && dialog_vars.help_button) {
	result = DLG_EXIT_HELP;
    }
    return result;
}

const char **
dlg_ok_label(void)
{
    static const char *labels[3];
    int n = 0;

    labels[n++] = my_ok_label();
    if (dialog_vars.help_button)
	labels[n++] = my_help_label();
    labels[n] = 0;
    return labels;
}

/*
 * Return a list of button labels for the OK/Cancel group.
 */
const char **
dlg_ok_labels(void)
{
    static const char *labels[5];
    int n = 0;

    labels[n++] = my_ok_label();
    if (dialog_vars.extra_button)
	labels[n++] = my_extra_label();
    if (!dialog_vars.nocancel)
	labels[n++] = my_cancel_label();
    if (dialog_vars.help_button)
	labels[n++] = my_help_label();
    labels[n] = 0;
    return labels;
}

/*
 * Map the given button index for dlg_ok_labels() into our exit-code
 */
int
dlg_ok_buttoncode(int button)
{
    int result = DLG_EXIT_ERROR;
    int n = 1;

    if (button <= 0) {
	result = DLG_EXIT_OK;
    } else if (dialog_vars.extra_button && (button == n++)) {
	result = DLG_EXIT_EXTRA;
    } else if (!dialog_vars.nocancel && (button == n++)) {
	result = DLG_EXIT_CANCEL;
    } else if (dialog_vars.help_button && (button == n)) {
	result = DLG_EXIT_HELP;
    }
    return result;
}

/*
 * Given that we're using dlg_ok_labels() to list buttons, find the next index
 * in the list of buttons.  The 'extra' parameter if negative provides a way to
 * enumerate extra active areas on the widget.
 */
int
dlg_next_ok_buttonindex(int current, int extra)
{
    int result = current + 1;

    if (current >= 0
	&& dlg_ok_buttoncode(result) < 0)
	result = extra;
    return result;
}

/*
 * Similarly, find the previous button index.
 */
int
dlg_prev_ok_buttonindex(int current, int extra)
{
    int result = current - 1;

    if (result < extra) {
	for (result = 0; dlg_ok_buttoncode(result + 1) >= 0; ++result) {
	    ;
	}
    }
    return result;
}

/*
 * Find the button-index for the "OK" or "Cancel" button, according to
 * whether --defaultno is given.  If --nocancel was given, we always return
 * the index for "OK".
 */
int
dlg_defaultno_button(void)
{
    int result = 0;

    if (dialog_vars.defaultno && !dialog_vars.nocancel) {
	while (dlg_ok_buttoncode(result) != DLG_EXIT_CANCEL)
	    ++result;
    }
    return result;
}

/*
 * Return a list of buttons for Yes/No labels.
 */
const char **
dlg_yes_labels(void)
{
    const char **result;

    if (dialog_vars.extra_button) {
	result = dlg_ok_labels();
    } else {
	static const char *labels[4];
	int n = 0;

	labels[n++] = my_yes_label();
	labels[n++] = my_no_label();
	if (dialog_vars.help_button)
	    labels[n++] = my_help_label();
	labels[n] = 0;

	result = labels;
    }

    return result;
}

/*
 * Map the given button index for dlg_yes_labels() into our exit-code.
 */
int
dlg_yes_buttoncode(int button)
{
    int result = DLG_EXIT_ERROR;

    if (dialog_vars.extra_button) {
	result = dlg_ok_buttoncode(button);
    } else if (button == 0) {
	result = DLG_EXIT_OK;
    } else if (button == 1) {
	result = DLG_EXIT_CANCEL;
    } else if (button == 2 && dialog_vars.help_button) {
	result = DLG_EXIT_HELP;
    }

    return result;
}

/*
 * Return the next index in labels[];
 */
int
dlg_next_button(const char **labels, int button)
{
    if (labels[button + 1] != 0)
	++button;
    else
	button = MIN_BUTTON;
    return button;
}

/*
 * Return the previous index in labels[];
 */
int
dlg_prev_button(const char **labels, int button)
{
    if (button > MIN_BUTTON)
	--button;
    else {
	while (labels[button + 1] != 0)
	    ++button;
    }
    return button;
}

--- NEW FILE: calendar.c ---
/*
 * $Id: calendar.c,v 1.1 2006-10-23 20:59:26 stsp Exp $
 *
 *  calendar.c -- implements the calendar box
 *
 * Copyright 2001-2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>
#include <dlg_keys.h>

#include <time.h>

#define ONE_DAY  (60 * 60 * 24)

#define MON_WIDE 4		/* width of a month-name */
#define DAY_HIGH 6		/* maximum lines in day-grid */
#define DAY_WIDE (8 * MON_WIDE)	/* width of the day-grid */
#define HDR_HIGH 1		/* height of cells with month/year */
#define BTN_HIGH 1		/* height of button-row excluding margin */

/* two more lines: titles for day-of-week and month/year boxes */
#define MIN_HIGH (DAY_HIGH + 2 + HDR_HIGH + BTN_HIGH + (7 * MARGIN))
#define MIN_WIDE (DAY_WIDE + (4 * MARGIN))

typedef enum {
    sMONTH = -3
    ,sYEAR = -2
    ,sDAY = -1
} STATES;

struct _box;

typedef int (*BOX_DRAW) (struct _box *, struct tm *);

typedef struct _box {
    WINDOW *parent;
    WINDOW *window;
    int x;
    int y;
    int width;
    int height;
    BOX_DRAW box_draw;
} BOX;

static int
days_in_month(struct tm *current, int offset /* -1, 0, 1 */ )
{
    static const int nominal[] =
    {
	31, 28, 31, 30, 31, 30,
	31, 31, 30, 31, 30, 31
    };
    int year = current->tm_year;
    int month = current->tm_mon + offset;
    int result;

    while (month < 0) {
	month += 12;
	year -= 1;
    }
    while (month >= 12) {
	month -= 12;
	year += 1;
    }
    result = nominal[month];
    if (month == 1)
	result += ((year % 4) == 0);
    return result;
}

static int
days_in_year(struct tm *current, int offset /* -1, 0, 1 */ )
{
    int year = current->tm_year + 1900 + offset;

    return ((year % 4) == 0) ? 366 : 365;
}

static int
day_cell_number(struct tm *current)
{
    int cell;
    cell = current->tm_mday - ((6 + current->tm_mday - current->tm_wday) % 7);
    if ((current->tm_mday - 1) % 7 != current->tm_wday)
	cell += 6;
    else
	cell--;
    return cell;
}

static int
next_or_previous(int key, int two_d)
{
    int result = 0;

    switch (key) {
    case DLGK_GRID_UP:
	result = two_d ? -7 : -1;
	break;
    case DLGK_GRID_LEFT:
	result = -1;
	break;
    case DLGK_GRID_DOWN:
	result = two_d ? 7 : 1;
	break;
    case DLGK_GRID_RIGHT:
	result = 1;
	break;
    default:
	beep();
	break;
    }
    return result;
}

/*
 * Draw the day-of-month selection box
 */
static int
draw_day(BOX * data, struct tm *current)
{
#ifdef ENABLE_NLS
    char *of_week[] =
    {
	nl_langinfo(ABDAY_1),
	nl_langinfo(ABDAY_2),
	nl_langinfo(ABDAY_3),
	nl_langinfo(ABDAY_4),
	nl_langinfo(ABDAY_5),
	nl_langinfo(ABDAY_6),
	nl_langinfo(ABDAY_7)
    };
#else
    static const char *const of_week[] =
    {
	"Sunday",
	"Monday",
	"Tuesday",
	"Wednesday",
	"Thursday",
	"Friday",
	"Saturday"
    };
#endif
    int cell_wide = MON_WIDE;
    int y, x, this_x = 0;
    int save_y = 0, save_x = 0;
    int day = current->tm_mday;
    int mday;
    int week;
    int last = days_in_month(current, 0);
    int prev = days_in_month(current, -1);

    werase(data->window);
    dlg_draw_box(data->parent,
		 data->y - MARGIN, data->x - MARGIN,
		 data->height + (2 * MARGIN), data->width + (2 * MARGIN),
		 menubox_border_attr, menubox_attr);	/* border of daybox */

    wattrset(data->window, menubox_attr);	/* daynames headline */
    for (x = 0; x < 7; x++) {
	mvwprintw(data->window,
		  0, (x + 1) * cell_wide, "%*.*s ",
		  cell_wide - 1,
		  cell_wide - 1,
		  of_week[x]);
    }

    mday = ((6 + current->tm_mday - current->tm_wday) % 7) - 7;
    if (mday <= -7)
	mday += 7;
    /* mday is now in the range -6 to 0. */
    week = (current->tm_yday + 6 + mday - current->tm_mday) / 7;

    for (y = 1; mday < last; y++) {
	wattrset(data->window, menubox_attr);	/* weeknumbers headline */
	mvwprintw(data->window,
		  y, 0,
		  "%*d ",
		  cell_wide - 1,
		  ++week);
	for (x = 0; x < 7; x++) {
	    this_x = 1 + (x + 1) * cell_wide;
	    ++mday;
	    if (wmove(data->window, y, this_x) == ERR)
		continue;
	    wattrset(data->window, item_attr);	/* not selected days */
	    if (mday == day) {
		wattrset(data->window, item_selected_attr);	/* selected day */
		save_y = y;
		save_x = this_x;
	    }
	    if (mday > 0) {
		if (mday <= last) {
		    wprintw(data->window, "%*d", cell_wide - 2, mday);
		} else if (mday == day) {
		    wprintw(data->window, "%*d", cell_wide - 2, mday - last);
		}
	    } else if (mday == day) {
		wprintw(data->window, "%*d", cell_wide - 2, mday + prev);
	    }
	}
	wmove(data->window, save_y, save_x);
    }
    dlg_draw_arrows(data->parent, TRUE, TRUE,
		    data->x + 5,
		    data->y - 1,
		    data->y + data->height);

    return 0;
}

/*
 * Draw the month-of-year selection box
 */
static int
draw_month(BOX * data, struct tm *current)
{
#ifdef ENABLE_NLS
    char *months[] =
    {
	nl_langinfo(MON_1),
	nl_langinfo(MON_2),
	nl_langinfo(MON_3),
	nl_langinfo(MON_4),
	nl_langinfo(MON_5),
	nl_langinfo(MON_6),
	nl_langinfo(MON_7),
	nl_langinfo(MON_8),
	nl_langinfo(MON_9),
	nl_langinfo(MON_10),
	nl_langinfo(MON_11),
	nl_langinfo(MON_12)
    };
#else
    static const char *const months[] =
    {
	"January",
	"February",
	"March",
	"April",
	"May",
	"June",
	"July",
	"August",
	"September",
	"October",
	"November",
	"December"
    };
#endif
    int month;

    month = current->tm_mon + 1;

    wattrset(data->parent, dialog_attr);	/* Headline "Month" */
    (void) mvwprintw(data->parent, data->y - 2, data->x - 1, _("Month"));
    dlg_draw_box(data->parent,
		 data->y - 1, data->x - 1,
		 data->height + 2, data->width + 2,
		 menubox_border_attr, menubox_attr);	/* borders of monthbox */
    wattrset(data->window, item_attr);	/* color the month selection */
    mvwprintw(data->window, 0, 0, "%s", months[month - 1]);
    wmove(data->window, 0, 0);
    return 0;
}

/*
 * Draw the year selection box
 */
static int
draw_year(BOX * data, struct tm *current)
{
    int year = current->tm_year + 1900;

    wattrset(data->parent, dialog_attr);	/* Headline "Year" */
    (void) mvwprintw(data->parent, data->y - 2, data->x - 1, _("Year"));
    dlg_draw_box(data->parent,
		 data->y - 1, data->x - 1,
		 data->height + 2, data->width + 2,
		 menubox_border_attr, menubox_attr);	/* borders of yearbox */
    wattrset(data->window, item_attr);	/* color the year selection */
    mvwprintw(data->window, 0, 0, "%4d", year);
    wmove(data->window, 0, 0);
    return 0;
}

static int
init_object(BOX * data,
	    WINDOW *parent,
	    int x, int y,
	    int width, int height,
	    BOX_DRAW box_draw,
	    int code)
{
    data->parent = parent;
    data->x = x;
    data->y = y;
    data->width = width;
    data->height = height;
    data->box_draw = box_draw;

    data->window = derwin(data->parent,
			  data->height, data->width,
			  data->y, data->x);
    if (data->window == 0)
	return -1;
    (void) keypad(data->window, TRUE);

    dlg_mouse_setbase(getbegx(parent), getbegy(parent));
    if (code == 'D') {
	dlg_mouse_mkbigregion(y + 1, x + MON_WIDE, height - 1, width - MON_WIDE,
			      KEY_MAX, 1, MON_WIDE, 3);
    } else {
	dlg_mouse_mkregion(y, x, height, width, code);
    }

    return 0;
}

#define DrawObject(data) (data)->box_draw(data, &current)

/*
 * Display a dialog box for entering a date
 */
int
dialog_calendar(const char *title,
		const char *subtitle,
		int height,
		int width,
		int day,
		int month,
		int year)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_ENTER,	' ' ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_GRID_DOWN,	'j' ),
	DLG_KEYS_DATA( DLGK_GRID_DOWN,	KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_GRID_DOWN,	KEY_NPAGE ),
	DLG_KEYS_DATA( DLGK_GRID_DOWN,	DLGK_MOUSE(KEY_NPAGE) ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,  'h' ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,  CHR_BACKSPACE ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,  CHR_PREVIOUS ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,  KEY_LEFT ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'l' ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT, CHR_NEXT ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_NEXT ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_GRID_UP,	'k' ),
	DLG_KEYS_DATA( DLGK_GRID_UP,	KEY_PPAGE ),
	DLG_KEYS_DATA( DLGK_GRID_UP,	KEY_PREVIOUS ),
	DLG_KEYS_DATA( DLGK_GRID_UP,	KEY_UP ),
	DLG_KEYS_DATA( DLGK_GRID_UP,  	DLGK_MOUSE(KEY_PPAGE) ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif
    BOX dy_box, mn_box, yr_box;
    int fkey;
    int key = 0;
    int key2;
    int step;
    int button;
    int result = DLG_EXIT_UNKNOWN;
    WINDOW *dialog;
    time_t now_time = time((time_t *) 0);
    struct tm current;
    STATES state = dlg_defaultno_button();
    const char **buttons = dlg_ok_labels();
    char *prompt = dlg_strclone(subtitle);
    int longest;
    int mincols;
    char buffer[MAX_LEN];

    dlg_does_output();

    now_time = time((time_t *) 0);
    current = *localtime(&now_time);
    if (day < 0)
	day = current.tm_mday;
    if (month < 0)
	month = current.tm_mon + 1;
    if (year < 0)
	year = current.tm_year + 1900;

    /* compute a struct tm that matches the day/month/year parameters */
    if (((year -= 1900) > 0) && (year < 200)) {
	/* ugly, but I'd like to run this on older machines w/o mktime -TD */
	for (;;) {
	    if (year > current.tm_year) {
		now_time += ONE_DAY * days_in_year(&current, 0);
	    } else if (year < current.tm_year) {
		now_time -= ONE_DAY * days_in_year(&current, -1);
	    } else if (month > current.tm_mon + 1) {
		now_time += ONE_DAY * days_in_month(&current, 0);
	    } else if (month < current.tm_mon + 1) {
		now_time -= ONE_DAY * days_in_month(&current, -1);
	    } else if (day > current.tm_mday) {
		now_time += ONE_DAY;
	    } else if (day < current.tm_mday) {
		now_time -= ONE_DAY;
	    } else {
		break;
	    }
	    current = *localtime(&now_time);
	}
    }

    dlg_button_sizes(buttons, FALSE, &longest, &mincols);
    mincols += (0 * MARGIN) + (dlg_button_count(buttons) * 3) - 1;
    if (mincols < MIN_WIDE)
	mincols = MIN_WIDE;

#ifdef KEY_RESIZE
  retry:
#endif

    dlg_auto_size(title, prompt, &height, &width, 0, mincols);
    height += MIN_HIGH - 1;
    if (width < MIN_WIDE)
	width = MIN_WIDE;
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    dialog = dlg_new_window(height, width,
			    dlg_box_y_ordinate(height),
			    dlg_box_x_ordinate(width));
    dlg_register_window(dialog, "calendar", binding);
    dlg_register_buttons(dialog, "calendar", buttons);

    /* mainbox */
    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_bottom_box(dialog);
    dlg_draw_title(dialog, title);

    wattrset(dialog, dialog_attr);	/* text mainbox */
    dlg_print_autowrap(dialog, prompt, height, width);

    /* compute positions of day, month and year boxes */
    memset(&dy_box, 0, sizeof(dy_box));
    memset(&mn_box, 0, sizeof(mn_box));
    memset(&yr_box, 0, sizeof(yr_box));

    if (init_object(&dy_box,
		    dialog,
		    (width - DAY_WIDE) / 2,
		    1 + (height - (DAY_HIGH + BTN_HIGH + (5 * MARGIN))),
		    DAY_WIDE,
		    DAY_HIGH + 1,
		    draw_day,
		    'D') < 0
	|| DrawObject(&dy_box) < 0)
	return DLG_EXIT_ERROR;

    if (init_object(&mn_box,
		    dialog,
		    dy_box.x,
		    dy_box.y - (HDR_HIGH + 2 * MARGIN),
		    (DAY_WIDE / 2) - MARGIN,
		    HDR_HIGH,
		    draw_month,
		    'M') < 0
	|| DrawObject(&mn_box) < 0)
	return DLG_EXIT_ERROR;

    if (init_object(&yr_box,
		    dialog,
		    dy_box.x + mn_box.width + 2,
		    mn_box.y,
		    mn_box.width,
		    mn_box.height,
		    draw_year,
		    'Y') < 0
	|| DrawObject(&yr_box) < 0)
	return DLG_EXIT_ERROR;

    while (result == DLG_EXIT_UNKNOWN) {
	BOX *obj = (state == sDAY ? &dy_box
		    : (state == sMONTH ? &mn_box :
		       (state == sYEAR ? &yr_box : 0)));

	button = (state < 0) ? 0 : state;
	dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
	if (obj != 0)
	    dlg_set_focus(dialog, obj->window);

	key = dlg_mouse_wgetch(dialog, &fkey);
	if (dlg_result_key(key, fkey, &result))
	    break;

	if (fkey && (key >= DLGK_MOUSE(KEY_MIN) && key <= DLGK_MOUSE(KEY_MAX))) {
	    key = dlg_lookup_key(dialog, key - M_EVENT, &fkey);
	}

	if ((key2 = dlg_char_to_button(key, buttons)) >= 0) {
	    result = key2;
	} else if (fkey) {
	    /* handle function-keys */
	    switch (key) {
	    case DLGK_MOUSE('D'):
		state = sDAY;
		break;
	    case DLGK_MOUSE('M'):
		state = sMONTH;
		break;
	    case DLGK_MOUSE('Y'):
		state = sYEAR;
		break;
	    case DLGK_ENTER:
		result = dlg_ok_buttoncode(button);
		break;
	    case DLGK_FIELD_PREV:
		state = dlg_prev_ok_buttonindex(state, sMONTH);
		break;
	    case DLGK_FIELD_NEXT:
		state = dlg_next_ok_buttonindex(state, sMONTH);
		break;
#ifdef KEY_RESIZE
	    case KEY_RESIZE:
		/* reset data */
		height = old_height;
		width = old_width;
		/* repaint */
		dlg_clear();
		dlg_del_window(dialog);
		refresh();
		dlg_mouse_free_regions();
		goto retry;
#endif
	    default:
		step = 0;
		key2 = -1;
		if (is_DLGK_MOUSE(key)) {
		    if ((key2 = dlg_ok_buttoncode(key - M_EVENT)) >= 0) {
			result = key2;
			break;
		    } else if (key >= DLGK_MOUSE(KEY_MAX)) {
			state = sDAY;
			obj = &dy_box;
			key2 = 1;
			step = (key
				- DLGK_MOUSE(KEY_MAX)
				- day_cell_number(&current));
		    }
		}
		if (obj != 0) {
		    if (key2 < 0)
			step = next_or_previous(key, (obj == &dy_box));
		    if (step != 0) {
			struct tm old = current;

			/* see comment regarding mktime -TD */
			if (obj == &dy_box) {
			    now_time += ONE_DAY * step;
			} else if (obj == &mn_box) {
			    if (step > 0)
				now_time += ONE_DAY *
				    days_in_month(&current, 0);
			    else
				now_time -= ONE_DAY *
				    days_in_month(&current, -1);
			} else if (obj == &yr_box) {
			    if (step > 0)
				now_time += (ONE_DAY
					     * days_in_year(&current, 0));
			    else
				now_time -= (ONE_DAY
					     * days_in_year(&current, -1));
			}

			current = *localtime(&now_time);

			if (obj != &dy_box
			    && (current.tm_mday != old.tm_mday
				|| current.tm_mon != old.tm_mon
				|| current.tm_year != old.tm_year))
			    DrawObject(&dy_box);
			if (obj != &mn_box && current.tm_mon != old.tm_mon)
			    DrawObject(&mn_box);
			if (obj != &yr_box && current.tm_year != old.tm_year)
			    DrawObject(&yr_box);
			(void) DrawObject(obj);
		    }
		} else if (state >= 0) {
		    if (next_or_previous(key, FALSE) < 0)
			state = dlg_prev_ok_buttonindex(state, sMONTH);
		    else if (next_or_previous(key, FALSE) > 0)
			state = dlg_next_ok_buttonindex(state, sMONTH);
		}
		break;
	    }
	}
    }

    dlg_del_window(dialog);
    sprintf(buffer, "%02d/%02d/%0d\n",
	    current.tm_mday, current.tm_mon + 1, current.tm_year + 1900);
    dlg_add_result(buffer);
    dlg_mouse_free_regions();
    free(prompt);
    return result;
}

--- NEW FILE: checklist.c ---
/*
 *  $Id: checklist.c,v 1.1 2006-10-23 20:59:26 stsp Exp $
 *
 *  checklist.c -- implements the checklist box
 *
 *  Copyright 2000-2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors:
 *	Savio Lam (lam836 at cs.cuhk.hk)
 *	Stuart Herbert - S.Herbert at sheffield.ac.uk: radiolist extension
 *	Alessandro Rubini - rubini at ipvvis.unipv.it: merged the two
 */

#include <dialog.h>
#include <dlg_keys.h>

static int list_width, check_x, item_x, checkflag;

#define LLEN(n) ((n) * CHECKBOX_TAGS)
#define ItemData(i)    &items[LLEN(i)]
#define ItemName(i)    items[LLEN(i)]
#define ItemText(i)    items[LLEN(i) + 1]
#define ItemStatus(i)  items[LLEN(i) + 2]
#define ItemHelp(i)    items[LLEN(i) + 3]

static void
print_arrows(WINDOW *win,
	     int box_x,
	     int box_y,
	     int scrollamt,
	     int choice,
	     int item_no,
	     int list_height)
{
    dlg_draw_arrows2(win, scrollamt,
		     scrollamt + choice < item_no,
		     box_x + check_x + 5,
		     box_y,
		     box_y + list_height + 1,
		     menubox_attr,
		     menubox_border_attr);
}

/*
 * Print list item.  The 'selected' parameter is true if 'choice' is the
 * current item.  That one is colored differently from the other items.
 */
static void
print_item(WINDOW *win,
	   DIALOG_LISTITEM * item,
	   const char *states,
	   int choice,
	   int selected)
{
    chtype save = getattrs(win);
    int i;
    chtype attr = A_NORMAL;
    const int *cols;
    const int *indx;
    int limit;

    /* Clear 'residue' of last item */
    wattrset(win, menubox_attr);
    (void) wmove(win, choice, 0);
    for (i = 0; i < list_width; i++)
	(void) waddch(win, ' ');

    (void) wmove(win, choice, check_x);
    wattrset(win, selected ? check_selected_attr : check_attr);
    (void) wprintw(win,
		   (checkflag == FLAG_CHECK) ? "[%c]" : "(%c)",
		   states[item->state]);
    wattrset(win, menubox_attr);
    (void) waddch(win, ' ');

    if (strlen(item->name) != 0) {

	indx = dlg_index_wchars(item->name);

	wattrset(win, selected ? tag_key_selected_attr : tag_key_attr);
	(void) waddnstr(win, item->name, indx[1]);

	if ((int) strlen(item->name) > indx[1]) {
	    limit = dlg_limit_columns(item->name, (item_x - check_x - 6), 1);
	    if (limit > 1) {
		wattrset(win, selected ? tag_selected_attr : tag_attr);
		(void) waddnstr(win,
				item->name + indx[1],
				indx[limit] - indx[1]);
	    }
	}
    }

    if (strlen(item->text) != 0) {
	cols = dlg_index_columns(item->text);
	limit = dlg_limit_columns(item->text, (getmaxx(win) - item_x + 1), 0);

	if (limit > 0) {
	    (void) wmove(win, choice, item_x);
	    wattrset(win, selected ? item_selected_attr : item_attr);
	    dlg_print_text(win, item->text, cols[limit], &attr);
	}
    }

    if (selected) {
	dlg_item_help(item->help);
    }
    wattrset(win, save);
}

/*
 * This is an alternate interface to 'checklist' which allows the application
 * to read the list item states back directly without putting them in the
 * output buffer.  It also provides for more than two states over which the
 * check/radio box can display.
 */
int
dlg_checklist(const char *title,
	      const char *cprompt,
	      int height,
	      int width,
	      int list_height,
	      int item_no,
	      DIALOG_LISTITEM * items,
	      const char *states,
	      int flag,
	      int *current_item)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
	DLG_KEYS_DATA( DLGK_ITEM_FIRST, KEY_HOME ),
	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_END ),
	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_LL ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	'+' ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,	'-' ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,	KEY_UP ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	KEY_NPAGE ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE) ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,	KEY_PPAGE ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE) ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif
    int i, j, key2, found, x, y, cur_x, cur_y, box_x, box_y;
    int key = 0, fkey;
    int button = dialog_state.visit_items ? -1 : dlg_defaultno_button();
    int choice = dlg_default_listitem(items);
    int scrollamt = 0;
    int max_choice;
    int was_mouse;
    int use_width, name_width, text_width;
    int result = DLG_EXIT_UNKNOWN;
    int num_states;
    WINDOW *dialog, *list;
    char *prompt = dlg_strclone(cprompt);
    const char **buttons = dlg_ok_labels();

    dlg_does_output();
    dlg_tab_correct_str(prompt);

#ifdef KEY_RESIZE
  retry:
#endif

    if (list_height == 0) {
	use_width = dlg_calc_list_width(item_no, items) + 10;
	/* calculate height without items (4) */
	dlg_auto_size(title, prompt, &height, &width, 4, MAX(26, use_width));
	dlg_calc_listh(&height, &list_height, item_no);
    } else {
	dlg_auto_size(title, prompt, &height, &width, 4 + list_height, 26);
    }
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    /* we need at least two states */
    if (states == 0 || strlen(states) < 2)
	states = " *";
    num_states = strlen(states);

    checkflag = flag;

    max_choice = MIN(list_height, item_no);

    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);

    dialog = dlg_new_window(height, width, y, x);
    dlg_register_window(dialog, "checklist", binding);
    dlg_register_buttons(dialog, "checklist", buttons);

    dlg_mouse_setbase(x, y);

    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_bottom_box(dialog);
    dlg_draw_title(dialog, title);

    wattrset(dialog, dialog_attr);
    dlg_print_autowrap(dialog, prompt, height, width);

    list_width = width - 6;
    getyx(dialog, cur_y, cur_x);
    box_y = cur_y + 1;
    box_x = (width - list_width) / 2 - 1;

    /* create new window for the list */
    list = dlg_sub_window(dialog, list_height, list_width,
			  y + box_y + 1, x + box_x + 1);

    /* draw a box around the list items */
    dlg_draw_box(dialog, box_y, box_x,
		 list_height + 2 * MARGIN,
		 list_width + 2 * MARGIN,
		 menubox_border_attr, menubox_attr);

    text_width = 0;
    name_width = 0;
    /* Find length of longest item to center checklist */
    for (i = 0; i < item_no; i++) {
	text_width = MAX(text_width, dlg_count_columns(items[i].text));
	name_width = MAX(name_width, dlg_count_columns(items[i].name));
    }

    /* If the name+text is wider than the list is allowed, then truncate
     * one or both of them.  If the name is no wider than 1/4 of the list,
     * leave it intact.
     */
    use_width = (list_width - 6);
    if (text_width + name_width > use_width) {
	int need = 0.25 * use_width;
	if (name_width > need) {
	    int want = use_width * ((double) name_width) / (text_width + name_width);
	    name_width = (want > need) ? want : need;
	}
	text_width = use_width - name_width;
    }

    check_x = (use_width - (text_width + name_width)) / 2;
    item_x = name_width + check_x + 6;

    /* ensure we are scrolled to show the current choice */
    if (choice >= (max_choice + scrollamt)) {
	scrollamt = choice - max_choice + 1;
	choice = max_choice - 1;
    }
    /* Print the list */
    for (i = 0; i < max_choice; i++)
	print_item(list,
		   &items[i + scrollamt],
		   states,
		   i, i == choice);
    (void) wnoutrefresh(list);

    /* register the new window, along with its borders */
    dlg_mouse_mkbigregion(box_y + 1, box_x, list_height, list_width + 2,
			  KEY_MAX, 1, 1, 1 /* by lines */ );

    print_arrows(dialog,
		 box_x, box_y,
		 scrollamt, max_choice, item_no, list_height);

    dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);

    while (result == DLG_EXIT_UNKNOWN) {
	if (button < 0)		/* --visit-items */
	    wmove(dialog, box_y + choice + 1, box_x + check_x + 2);

	key = dlg_mouse_wgetch(dialog, &fkey);
	if (dlg_result_key(key, fkey, &result))
	    break;

	was_mouse = (fkey && is_DLGK_MOUSE(key));
	if (was_mouse)
	    key -= M_EVENT;

	if (was_mouse && (key >= KEY_MAX)) {
	    getyx(dialog, cur_y, cur_x);
	    i = (key - KEY_MAX);
	    if (i < max_choice) {
		/* De-highlight current item */
		print_item(list,
			   &items[scrollamt + choice],
			   states,
			   choice, FALSE);
		/* Highlight new item */
		choice = (key - KEY_MAX);
		print_item(list,
			   &items[scrollamt + choice],
			   states,
			   choice, TRUE);
		(void) wnoutrefresh(list);
		(void) wmove(dialog, cur_y, cur_x);

		key = ' ';	/* force the selected item to toggle */
	    } else {
		beep();
		continue;
	    }
	    fkey = FALSE;
	} else if (was_mouse && key >= KEY_MIN) {
	    key = dlg_lookup_key(dialog, key, &fkey);
	}

	/*
	 * A space toggles the item status.  We handle either a checklist
	 * (any number of items can be selected) or radio list (zero or one
	 * items can be selected).
	 */
	if (key == ' ') {
	    int current = scrollamt + choice;
	    int next = items[current].state + 1;

	    if (next >= num_states)
		next = 0;

	    getyx(dialog, cur_y, cur_x);
	    if (flag == FLAG_CHECK) {	/* checklist? */
		items[current].state = next;
		print_item(list,
			   &items[scrollamt + choice],
			   states,
			   choice, TRUE);
	    } else {		/* radiolist */
		for (i = 0; i < item_no; i++) {
		    if (i != current) {
			items[i].state = 0;
		    }
		}
		if (items[current].state) {
		    items[current].state = next ? next : 1;
		    print_item(list,
			       &items[current],
			       states,
			       choice, TRUE);
		} else {
		    items[current].state = 1;
		    for (i = 0; i < max_choice; i++)
			print_item(list,
				   &items[scrollamt + i],
				   states,
				   i, i == choice);
		}
	    }
	    (void) wnoutrefresh(list);
	    (void) wmove(dialog, cur_y, cur_x);
	    wrefresh(dialog);
	    continue;		/* wait for another key press */
	}

	/*
	 * Check if key pressed matches first character of any item tag in
	 * list.  If there is more than one match, we will cycle through
	 * each one as the same key is pressed repeatedly.
	 */
	found = FALSE;
	if (!fkey) {
	    if (button < 0 || !dialog_state.visit_items) {
		for (j = scrollamt + choice + 1; j < item_no; j++) {
		    if (dlg_match_char(dlg_last_getc(), items[j].name)) {
			found = TRUE;
			i = j - scrollamt;
			break;
		    }
		}
		if (!found) {
		    for (j = 0; j <= scrollamt + choice; j++) {
			if (dlg_match_char(dlg_last_getc(), items[j].name)) {
			    found = TRUE;
			    i = j - scrollamt;
			    break;
			}
		    }
		}
		if (found)
		    dlg_flush_getc();
	    } else if ((j = dlg_char_to_button(key, buttons)) >= 0) {
		button = j;
		ungetch('\n');
		continue;
	    }
	}

	/*
	 * A single digit (1-9) positions the selection to that line in the
	 * current screen.
	 */
	if (!found
	    && (key <= '9')
	    && (key > '0')
	    && (key - '1' < max_choice)) {
	    found = TRUE;
	    i = key - '1';
	}

	if (!found) {
	    if (fkey) {
		found = TRUE;
		switch (key) {
		case DLGK_ITEM_FIRST:
		    i = -scrollamt;
		    break;
		case DLGK_ITEM_LAST:
		    i = item_no - 1 - scrollamt;
		    break;
		case DLGK_PAGE_PREV:
		    if (choice)
			i = 0;
		    else if (scrollamt != 0)
			i = -MIN(scrollamt, max_choice);
		    else
			continue;
		    break;
		case DLGK_PAGE_NEXT:
		    i = MIN(choice + max_choice, item_no - scrollamt - 1);
		    break;
		case DLGK_ITEM_PREV:
		    i = choice - 1;
		    if (choice == 0 && scrollamt == 0)
			continue;
		    break;
		case DLGK_ITEM_NEXT:
		    i = choice + 1;
		    if (scrollamt + choice >= item_no - 1)
			continue;
		    break;
		default:
		    found = FALSE;
		    break;
		}
	    }
	}

	if (found) {
	    if (i != choice) {
		getyx(dialog, cur_y, cur_x);
		if (i < 0 || i >= max_choice) {
#if defined(NCURSES_VERSION_MAJOR) && NCURSES_VERSION_MAJOR < 5
		    /*
		     * Using wscrl to assist ncurses scrolling is not needed
		     * in version 5.x
		     */
		    if (i == -1) {
			if (list_height > 1) {
			    /* De-highlight current first item */
			    print_item(list,
				       &items[scrollamt],
				       states,
				       0, FALSE);
			    scrollok(list, TRUE);
			    wscrl(list, -1);
			    scrollok(list, FALSE);
			}
			scrollamt--;
			print_item(list,
				   &items[scrollamt],
				   states,
				   0, TRUE);
		    } else if (i == max_choice) {
			if (list_height > 1) {
			    /* De-highlight current last item before scrolling up */
			    print_item(list,
				       &items[scrollamt + max_choice - 1],
				       states,
				       max_choice - 1, FALSE);
			    scrollok(list, TRUE);
			    wscrl(list, 1);
			    scrollok(list, FALSE);
			}
			scrollamt++;
			print_item(list,
				   &items[scrollamt + max_choice - 1],
				   states,
				   max_choice - 1, TRUE);
		    } else
#endif
		    {
			if (i < 0) {
			    scrollamt += i;
			    choice = 0;
			} else {
			    choice = max_choice - 1;
			    scrollamt += (i - max_choice + 1);
			}
			for (i = 0; i < max_choice; i++) {
			    print_item(list,
				       &items[scrollamt + i],
				       states,
				       i, i == choice);
			}
		    }
		    (void) wnoutrefresh(list);
		    print_arrows(dialog,
				 box_x, box_y,
				 scrollamt, max_choice, item_no, list_height);
		} else {
		    /* De-highlight current item */
		    print_item(list,
			       &items[scrollamt + choice],
			       states,
			       choice, FALSE);
		    /* Highlight new item */
		    choice = i;
		    print_item(list,
			       &items[scrollamt + choice],
			       states,
			       choice, TRUE);
		    (void) wnoutrefresh(list);
		    (void) wmove(dialog, cur_y, cur_x);
		    wrefresh(dialog);
		}
	    }
	    continue;		/* wait for another key press */
	}

	if (fkey) {
	    switch (key) {
	    case DLGK_ENTER:
		result = dlg_ok_buttoncode(button);
		break;
	    case DLGK_FIELD_PREV:
		button = dlg_prev_button(buttons, button);
		dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
				 FALSE, width);
		break;
	    case DLGK_FIELD_NEXT:
		button = dlg_next_button(buttons, button);
		dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
				 FALSE, width);
		break;
#ifdef KEY_RESIZE
	    case KEY_RESIZE:
		/* reset data */
		height = old_height;
		width = old_width;
		/* repaint */
		dlg_clear();
		dlg_del_window(dialog);
		refresh();
		dlg_mouse_free_regions();
		goto retry;
#endif
	    default:
		if (was_mouse) {
		    if ((key2 = dlg_ok_buttoncode(key)) >= 0) {
			result = key2;
			break;
		    }
		    beep();
		}
	    }
	} else {
	    beep();
	}
    }

    dlg_del_window(dialog);
    dlg_mouse_free_regions();
    free(prompt);
    *current_item = (scrollamt + choice);
    return result;
}

/*
 * Display a dialog box with a list of options that can be turned on or off
 * The `flag' parameter is used to select between radiolist and checklist.
 */
int
dialog_checklist(const char *title,
		 const char *cprompt,
		 int height,
		 int width,
		 int list_height,
		 int item_no,
		 char **items,
		 int flag)
{
    int result;
    int i;
    DIALOG_LISTITEM *listitems;
    bool separate_output = ((flag == FLAG_CHECK)
			    && (dialog_vars.separate_output));
    bool show_status = FALSE;
    int current = 0;

    listitems = calloc(item_no + 1, sizeof(*listitems));
    assert_ptr(listitems, "dialog_checklist");

    for (i = 0; i < item_no; ++i) {
	listitems[i].name = ItemName(i);
	listitems[i].text = ItemText(i);
	listitems[i].help = (dialog_vars.item_help) ? ItemHelp(i) : "";
	listitems[i].state = !dlg_strcmp(ItemStatus(i), "on");
    }

    result = dlg_checklist(title,
			   cprompt,
			   height,
			   width,
			   list_height,
			   item_no,
			   listitems,
			   NULL,
			   flag,
			   &current);

    switch (result) {
    case DLG_EXIT_OK:		/* FALLTHRU */
    case DLG_EXIT_EXTRA:
	show_status = TRUE;
	break;
    case DLG_EXIT_HELP:
	dlg_add_result("HELP ");
	show_status = dialog_vars.help_status;
	if (USE_ITEM_HELP(listitems[current].help)) {
	    if (show_status) {
		if (separate_output) {
		    dlg_add_result(listitems[current].help);
		    dlg_add_result("\n");
		} else {
		    dlg_add_quoted(listitems[current].help);
		}
	    } else {
		dlg_add_result(listitems[current].help);
	    }
	    result = DLG_EXIT_ITEM_HELP;
	} else {
	    if (show_status) {
		if (separate_output) {
		    dlg_add_result(listitems[current].name);
		    dlg_add_result("\n");
		} else {
		    dlg_add_quoted(listitems[current].name);
		}
	    } else {
		dlg_add_result(listitems[current].name);
	    }
	}
	break;
    }

    if (show_status) {
	for (i = 0; i < item_no; i++) {
	    if (listitems[i].state) {
		if (separate_output) {
		    dlg_add_result(listitems[i].name);
		    dlg_add_result("\n");
		} else {
		    if (dialog_vars.input_result && *(dialog_vars.input_result))
			dlg_add_result(" ");
		    if (flag == FLAG_CHECK) {
			dlg_add_quoted(listitems[i].name);
		    } else {
			dlg_add_result(listitems[i].name);
		    }
		}
	    }
	}
    }

    free(listitems);
    return result;
}

--- NEW FILE: arrows.c ---
/*
 *  $Id: arrows.c,v 1.1 2006-10-23 20:59:25 stsp Exp $
 *
 *  arrows.c -- draw arrows to indicate end-of-range for lists
 *
 * Copyright 2000-2004,2005   Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>

#ifdef USE_WIDE_CURSES
#define add_acs(win, code) wadd_wch(win, W ## code)
#else
#define add_acs(win, code) waddch(win, code)
#endif

#ifdef HAVE_COLOR
static chtype
merge_colors(chtype foreground, chtype background)
{
    chtype result = foreground;
    if ((foreground & A_COLOR) != (background & A_COLOR)) {
	short fg_f, bg_f;
	short fg_b, bg_b;
	if (pair_content(PAIR_NUMBER(foreground), &fg_f, &bg_f) != ERR
	    && pair_content(PAIR_NUMBER(background), &fg_b, &bg_b) != ERR) {
	    result &= ~A_COLOR;
	    result |= dlg_color_pair(fg_f, bg_b);
	}
    }
    return result;
}
#else
#define merge_colors(f,b) (f)
#endif

void
dlg_draw_arrows2(WINDOW *dialog,
		 int top_arrow,
		 int bottom_arrow,
		 int x,
		 int top,
		 int bottom,
		 chtype attr,
		 chtype borderattr)
{
    chtype save = getattrs(dialog);
    int cur_x, cur_y;

    getyx(dialog, cur_y, cur_x);

    (void) wmove(dialog, top, x);
    if (top_arrow) {
	wattrset(dialog, merge_colors(uarrow_attr, attr));
	(void) add_acs(dialog, ACS_UARROW);
	(void) waddstr(dialog, "(-)");
    } else {
	wattrset(dialog, attr);
	(void) whline(dialog, ACS_HLINE, 4);
    }
    mouse_mkbutton(top, x - 1, 6, KEY_PPAGE);

    (void) wmove(dialog, bottom, x);
    if (bottom_arrow) {
	wattrset(dialog, merge_colors(darrow_attr, attr));
	(void) add_acs(dialog, ACS_DARROW);
	(void) waddstr(dialog, "(+)");
    } else {
	wattrset(dialog, borderattr);
	(void) whline(dialog, ACS_HLINE, 4);
    }
    mouse_mkbutton(bottom, x - 1, 6, KEY_NPAGE);

    (void) wmove(dialog, cur_y, cur_x);
    wrefresh(dialog);

    wattrset(dialog, save);
}

void
dlg_draw_arrows(WINDOW *dialog,
		int top_arrow,
		int bottom_arrow,
		int x,
		int top,
		int bottom)
{
    dlg_draw_arrows2(dialog,
		     top_arrow,
		     bottom_arrow,
		     x,
		     top,
		     bottom,
		     menubox_attr,
		     menubox_border_attr);
}

--- NEW FILE: makefile.in ---
# $Id: makefile.in,v 1.1 2006-10-23 20:59:27 stsp Exp $
# template makefile for DIALOG
#
SHELL		= /bin/sh

srcdir		= @srcdir@
VPATH		= @srcdir@

prefix		= @prefix@
exec_prefix	= @exec_prefix@

top_builddir	= .

x		= @EXEEXT@
o		= . at OBJEXT@
a		= @LIB_SUFFIX@

DESTDIR		=
bindir		= $(DESTDIR)@bindir@
includedir	= $(DESTDIR)@includedir@
libdir		= $(DESTDIR)@libdir@
mandir		= $(DESTDIR)@mandir@
MAN1DIR		= $(mandir)/man1
MAN3DIR		= $(mandir)/man3

# see po/makefile
localedir       = $(prefix)/@DATADIRNAME@/locale

CFLAGS		= @CFLAGS@
CPPFLAGS	= @CPPFLAGS@ @DEFS@ -I. -I$(srcdir) -DLOCALEDIR=\"$(localedir)\"
EXTRA_CFLAGS	= @EXTRA_CFLAGS@
CC		= @CC@
AR		= @AR@
LDFLAGS		= @LDFLAGS@
LIBS		= @LIBS@ @INTLDIR_MAKE@ @INTLLIBS@
RANLIB		= @LIB_PREP@

RM		= rm -f
LINT		= lint

LIBTOOL		= @LIBTOOL@ @ECHO_LT@
LIBTOOL_CLEAN	= @LIB_CLEAN@
LIBTOOL_COMPILE	= @LIB_COMPILE@
LIBTOOL_CREATE	= @LIB_CREATE@
LIBTOOL_LINK	= @LIB_LINK@
LIBTOOL_INSTALL	= @LIB_INSTALL@
LIBTOOL_UNINSTALL = @LIB_UNINSTALL@

INSTALL		= @INSTALL@
INSTALL_PROGRAM	= $(LIBTOOL_INSTALL) @INSTALL_PROGRAM@
INSTALL_DATA	= @INSTALL_DATA@

LINK		= $(LIBTOOL_LINK) $(CC)

#
# Standard .c to .o compile line.
#
.SUFFIXES: .c $o
.c$o :
@RULE_CC@
	@ECHO_CC@$(LIBTOOL_COMPILE) $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CPPFLAGS) -c $<

EXTRAOBJS = @EXTRAOBJS@
OBJECTS = $(EXTRAOBJS) \
	arrows$o \
	buttons$o \
	checklist$o \
	dlg_keys$o \
	inputbox$o \
	inputstr$o \
	menubox$o \
	mouse$o \
	mousewget$o \
	msgbox$o \
	textbox$o \
	ui_getc$o \
	util$o \
	version$o \
	yesno$o

LIB_OBJECT = @LIB_OBJECT@

SRCS = $(OBJECTS:$o=.c)
HDRS = \
	dialog.h \
	dlg_colors.h \
	dlg_config.h \
	dlg_keys.h

LIB	= @LIB_PREFIX at dialog$a
PROG	= dialog$x
ALL	= $(LIB) $(PROG)

BIN_DIRS	= $(bindir) $(MAN1DIR)
LIB_DIRS	= $(libdir) $(includedir) $(MAN3DIR)

all	:: $(ALL)

@LIBTOOL_MAKE at install	:: install-lib
@LIBTOOL_MAKE at uninstall	:: uninstall-lib

@INTLDIR_MAKE@@INTLLIBS@ :
@INTLDIR_MAKE@	@echo "Building GNU gettext library..."
@INTLDIR_MAKE@	@cd intl && $(MAKE) @cf_cv_makeflags@

dialog$o \
$(OBJECTS) : $(srcdir)/dialog.h $(srcdir)/dlg_keys.h dlg_config.h VERSION

$(LIB) : $(LIB_OBJECT)
	$(LIBTOOL_CREATE) $(LIB) $(LIB_OBJECT)
	$(RANLIB) $@

dialog$x : $(LIB) dialog$o @INTLDIR_MAKE@ @INTLLIBS@
	$(LINK) -o $@ dialog$o -L. -ldialog $(LDFLAGS) $(LIBS)

clean	\
distclean \
install \
install-strip \
uninstall ::
	@echo making $@
@INTLDIR_MAKE@	@-test -f intl/makefile && cd intl && $(MAKE) @cf_cv_makeflags@ $@
@MSG_DIR_MAKE@	@-test -f po/makefile && cd po && $(MAKE) @cf_cv_makeflags@ $@

clean	::
	@- $(RM) -r autom4te.cache
	- $(RM) *.*cov *.da *.bb *.bbg
	- $(LIBTOOL_CLEAN) $(RM) *$o $(ALL)
	- $(RM) headers.sed
	- $(RM) core *~ tags TAGS

distclean :: clean
	$(RM) intl/libintl.h intl/po2tbl.sed
	$(RM) makefile dlg_config.h config.cache config.log config.status
	$(RM) samples/install/makefile

install :: $(PROG) $(bindir)
	$(INSTALL_PROGRAM) $(PROG) $(bindir)

install-strip :: $(PROG) $(bindir)
	$(INSTALL_PROGRAM) -s $(PROG) $(bindir)

install-strip \
install :: $(MAN1DIR)
	$(INSTALL_DATA) $(srcdir)/dialog.1 $(MAN1DIR)

uninstall ::
	$(RM) $(bindir)/$(PROG)
	$(RM) $(MAN1DIR)/dialog.1

# most users do not want/need the library, so the install rules are distinct.
install-lib :: $(LIB_DIRS) headers.sed
	@ echo "** installing library in $(libdir)"
	$(LIBTOOL_INSTALL) $(INSTALL_DATA) $(LIB) $(libdir)
	@ echo "** installing headers in $(includedir)"
	@ $(SHELL) $(srcdir)/headers.sh $(INSTALL_DATA) $(includedir) $(srcdir) dialog.h
	@ $(SHELL) $(srcdir)/headers.sh $(INSTALL_DATA) $(includedir) $(srcdir) dlg_colors.h
	@ $(SHELL) $(srcdir)/headers.sh $(INSTALL_DATA) $(includedir) $(srcdir) dlg_keys.h
	@ $(SHELL) $(srcdir)/headers.sh $(INSTALL_DATA) $(includedir) .         dlg_config.h
	@ echo "** installing manpage"
	$(INSTALL_DATA) $(srcdir)/dialog.3 $(MAN3DIR)

uninstall-lib :: $(LIB_DIRS)
	- $(LIBTOOL_UNINSTALL) $(RM) $(libdir)/$(LIB)
	$(RM) $(includedir)/dialog.h
	$(RM) $(includedir)/dlg_colors.h
	$(RM) $(includedir)/dlg_keys.h
	$(RM) $(includedir)/dlg_config.h
	$(RM) $(MAN3DIR)/dialog.3

headers.sed : $(srcdir)/headers.sh
	$(SHELL) $(srcdir)/headers.sh $(includedir) $(srcdir)

$(MAN1DIR) \
$(MAN3DIR) \
$(bindir) \
$(includedir) \
$(libdir) : ; $(srcdir)/mkdirs.sh $@

@MAKE_LOWER_TAGS at tags :
@MAKE_LOWER_TAGS@	ctags $(SRCS) $(HDRS)

@MAKE_LOWER_TAGS at TAGS :
@MAKE_LOWER_TAGS@	etags $(SRCS) $(HDRS)

lint:
	$(LINT) $(CPPFLAGS) *.c

--- NEW FILE: config.guess ---
#! /bin/sh
# Attempt to guess a canonical system name.
#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
#   2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.

timestamp='2005-11-11'

# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
[...1426 lines suppressed...]
/bin/universe          = `(/bin/universe) 2>/dev/null`
/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
/bin/arch              = `(/bin/arch) 2>/dev/null`
/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`

UNAME_MACHINE = ${UNAME_MACHINE}
UNAME_RELEASE = ${UNAME_RELEASE}
UNAME_SYSTEM  = ${UNAME_SYSTEM}
UNAME_VERSION = ${UNAME_VERSION}
EOF

exit 1

# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:

--- NEW FILE: config.sub ---
#! /bin/sh
# Configuration validation subroutine script.
#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
#   2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.

timestamp='2005-11-13'

# This file is (in principle) common to ALL GNU software.
# The presence of a machine in this file suggests that SOME GNU software
# can handle that machine.  It does not imply ALL GNU software can.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
[...1548 lines suppressed...]
			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
				vendor=atari
				;;
			-vos*)
				vendor=stratus
				;;
		esac
		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
		;;
esac

echo $basic_machine$os
exit

# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:

--- NEW FILE: progressbox.c ---
/*
 *  $Id: progressbox.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  progressbox.c -- implements the progress box
 *
 *  Copyright 2005	Valery Reznic
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>
#include <dlg_keys.h>

#define MIN_HIGH (4)
#define MIN_WIDE (10 + 2 * (2 + MARGIN))

typedef struct {
    DIALOG_CALLBACK obj;
    WINDOW *text;
    char line[MAX_LEN + 1];
    int is_eof;
} MY_OBJ;

/*
 * Return current line of text.
 */
static char *
get_line(MY_OBJ * obj)
{
    FILE *fp = obj->obj.input;
    int col = 0;
    int j, tmpint, ch;

    while (1) {
	if ((ch = getc(fp)) == EOF) {
	    obj->is_eof = 1;
	    if (col) {
		break;
	    } else {
		return NULL;
	    }
	}
	if (ch == '\n')
	    break;
	if (ch == '\r')
	    break;
	if ((ch == TAB) && (dialog_vars.tab_correct)) {
	    tmpint = dialog_state.tab_len
		- (col % dialog_state.tab_len);
	    for (j = 0; j < tmpint; j++) {
		if (col < MAX_LEN)
		    obj->line[col] = ' ';
		++col;
	    }
	} else {
	    obj->line[col] = ch;
	    ++col;
	}
	if (col >= MAX_LEN)
	    break;
    }

    obj->line[col] = '\0';

    return obj->line;
}

/*
 * Print a new line of text.
 */
static void
print_line(MY_OBJ * obj, WINDOW *win, int row, int width)
{
    int i, y, x;
    char *line = obj->line;

    (void) wmove(win, row, 0);	/* move cursor to correct line */
    (void) waddch(win, ' ');
#ifdef NCURSES_VERSION
    (void) waddnstr(win, line, MIN((int) strlen(line), width - 2));
#else
    line[MIN((int) strlen(line), width - 2)] = '\0';
    waddstr(win, line);
#endif

    getyx(win, y, x);
    /* Clear 'residue' of previous line */
    for (i = 0; i < width - x; i++)
	(void) waddch(win, ' ');
}

/*
 * Display text from a stdin in a scrolling window.
 */
int
dialog_progressbox(const char *title, const char *cprompt, int height, int width)
{
    int i;
    int x, y, thigh;
    WINDOW *dialog, *text;
    MY_OBJ *obj;
    FILE *fd = dialog_state.pipe_input;
    char *prompt = dlg_strclone(cprompt);

    dlg_tab_correct_str(prompt);
    dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, MIN_WIDE);
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);
    thigh = height - (2 * MARGIN);

    dialog = dlg_new_window(height, width, y, x);

    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_title(dialog, title);

    if (*prompt != '\0') {
	int y2, x2;

	wattrset(dialog, dialog_attr);
	dlg_print_autowrap(dialog, prompt, height, width);
	getyx(dialog, y2, x2);
	++y2;
	wmove(dialog, y2, MARGIN);
	for (i = 0; i < getmaxx(dialog) - 2 * MARGIN; i++)
	    (void) waddch(dialog, ACS_HLINE);
	y += y2;
	thigh -= y2;
    }

    /* Create window for text region, used for scrolling text */
    text = dlg_sub_window(dialog,
			  thigh,
			  width - (2 * MARGIN),
			  y + MARGIN,
			  x + MARGIN);

    (void) wrefresh(dialog);

    (void) wmove(dialog, thigh, (MARGIN + 1));
    (void) wnoutrefresh(dialog);

    obj = (MY_OBJ *) calloc(1, sizeof(MY_OBJ));
    assert_ptr(obj, "dialog_progressbox");

    obj->obj.input = fd;
    obj->obj.win = dialog;
    obj->text = text;

    dlg_attr_clear(text, thigh, getmaxx(text), dialog_attr);
    for (i = 0; get_line(obj); i++) {
	if (i < thigh) {
	    print_line(obj, text, i, width - (2 * MARGIN));
	} else {
	    scrollok(text, TRUE);
	    scroll(text);
	    scrollok(text, FALSE);
	    print_line(obj, text, thigh - 1, width - (2 * MARGIN));
	}
	(void) wnoutrefresh(text);
	(void) wrefresh(text);
	if (obj->is_eof)
	    break;
    }
    dlg_unregister_window(text);
    dlg_del_window(dialog);
    free(prompt);

    return DLG_EXIT_OK;
}

--- NEW FILE: install-sh ---
#! /bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission.  M.I.T. makes no representations about the
# suitability of this software for any purpose.  It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.  It can only install one file at a time, a restriction
# shared with many OS's install programs.


# set DOITPROG to echo to test this script

# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"


# put in absolute paths if you don't have them in your path; or use env. vars.

mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"

transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""

while [ x"$1" != x ]; do
    case $1 in
	-c) instcmd="$cpprog"
	    shift
	    continue;;

	-d) dir_arg=true
	    shift
	    continue;;

	-m) chmodcmd="$chmodprog $2"
	    shift
	    shift
	    continue;;

	-o) chowncmd="$chownprog $2"
	    shift
	    shift
	    continue;;

	-g) chgrpcmd="$chgrpprog $2"
	    shift
	    shift
	    continue;;

	-s) stripcmd="$stripprog"
	    shift
	    continue;;

	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
	    shift
	    continue;;

	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
	    shift
	    continue;;

	*)  if [ x"$src" = x ]
	    then
		src=$1
	    else
		# this colon is to work around a 386BSD /bin/sh bug
		:
		dst=$1
	    fi
	    shift
	    continue;;
    esac
done

if [ x"$src" = x ]
then
	echo "install:	no input file specified"
	exit 1
else
	:
fi

if [ x"$dir_arg" != x ]; then
	dst=$src
	src=""

	if [ -d $dst ]; then
		instcmd=:
		chmodcmd=""
	else
		instcmd=$mkdirprog
	fi
else

# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.

	if [ -f $src -o -d $src ]
	then
		:
	else
		echo "install:  $src does not exist"
		exit 1
	fi

	if [ x"$dst" = x ]
	then
		echo "install:	no destination specified"
		exit 1
	else
		:
	fi

# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic

	if [ -d $dst ]
	then
		dst="$dst"/`basename $src`
	else
		:
	fi
fi

## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`

# Make sure that the destination directory exists.
#  this part is taken from Noah Friedman's mkinstalldirs script

# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
	'
IFS="${IFS-${defaultIFS}}"

oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"

pathcomp=''

while [ $# -ne 0 ] ; do
	pathcomp="${pathcomp}${1}"
	shift

	if [ ! -d "${pathcomp}" ] ;
        then
		$mkdirprog "${pathcomp}"
	else
		:
	fi

	pathcomp="${pathcomp}/"
done
fi

if [ x"$dir_arg" != x ]
then
	$doit $instcmd $dst &&

	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi &&
	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi &&
	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi &&
	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi
else

# If we're going to rename the final executable, determine the name now.

	if [ x"$transformarg" = x ]
	then
		dstfile=`basename $dst`
	else
		dstfile=`basename $dst $transformbasename |
			sed $transformarg`$transformbasename
	fi

# don't allow the sed command to completely eliminate the filename

	if [ x"$dstfile" = x ]
	then
		dstfile=`basename $dst`
	else
		:
	fi

# Make a temp file name in the proper directory.

	dsttmp=$dstdir/#inst.$$#

# Move or copy the file name to the temp name

	$doit $instcmd $src $dsttmp &&

	trap "rm -f ${dsttmp}" 0 &&

# and set any options; do chmod last to preserve setuid bits

# If any of these fail, we abort the whole thing.  If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.

	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi &&
	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi &&
	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi &&
	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi &&

# Now rename the file to the real destination.

	$doit $rmcmd -f $dstdir/$dstfile &&
	$doit $mvcmd $dsttmp $dstdir/$dstfile

fi &&


exit 0

--- NEW FILE: dialog.lsm ---
Begin3
Title:          dialog - Display dialog boxes in scripts
Version:        1.0-20060221
Entered-date:   21FEB06
Description:    Dialog is a program that will let you present a variety
                of questions or display messages in nice looking color
                non-graphical dialog boxes from a shell or perl script.
                Various dialog boxes can be presented such as yes/no, menu,
                input, message, checklist, radiolist, and more.

                This program is based on the no longer maintained cdialog, but
                contains bug fixes and more features.
Keywords:       dialog, cdialog, shell, script, window
Author:         Originally Savio Lam for the ancient version
Maintained-by:  Thomas E. Dickey  <dickey at invisible-island.net>
                Vila, Santiago  <sanvila at debian.org>
Primary-site:   http://invisible-island.net/dialog/
                http://packages.debian.org/
Alternate-site: ftp://ftp.us.debian.org/debian/pool/main/d/dialog/
Original-site:  ftp://sunsite.unc.edu/pub/Linux/utils/shell
Platforms:      Linux *nix, OS/2 EMX
Copying-policy: LGPL
End

--- NEW FILE: README ---
-(1999-12-25)-------------------------------------------------------------------
This version of dialog, formerly known as cdialog is based on the Debian
package for dialog 0.9a (see CHANGES for recent modifications)
- T.Dickey <dickey at invisible-island.net>

-(1996-01-15)-------------------------------------------------------------------
I have uploaded cdialog-0.9a.tar.gz to sunsite. It should be moved
to pub/Linux/utils/shell, I think.

Starting point for cdialog v.0.9a was dialog-0.6c. Many new features
like as Resolution-independence, Auto-sizing, Maximizing, more widget
on the same screen (multi-widget), etc. were added. New widget are 
tailbox and tailbox-in-background. Here are the options:

   Usage: dialog <Common options> <Box options>
          { --and-widget <Common options> <Box options> }

   Common options: <Global options>
       [--backtitle <backtitle>] [--sleep <secs>] [--beep] [--beep-after]
       [--clear] [--begin <y> <x>] [--aspect <ratio>] [--print-size]
       [--print-maxsize] [--size-err] [--separate-output] [--cr-wrap]
       [--tab-len <n>] [--tab-correct] [--print-version] [--no-kill]
       [--title <title>]

   Global options: [--shadow] [--no-shadow] [--separate-widget "<str>"]

At the moment, mouse support with libgpm can't be added because it
does't implement the wtimeout() function of ncurses. Wtimeout() is
needed to have more widgets (es.tailbox) cooperating on the same
screen... I don't know if with newer versions of libgpm it's possible.

I have no more time to write docs for this new version...Is there
anyone, that looking at the code, can do it??? Ouch! :-)
Don't flame me!

For the future, if any volunteer want, the way to evolve cdialog is to
1) make a daemon for a better support of multi-tasking or implementing
   multithreading.
2) add an option that could permit to read commands (--options) from a
   file, like as in a normal programming language, but maintaining 
   compatiblity with older version of dialog.

I no longer could maintain cdialog...
Executable and library name of cdialog are the same of dialog, for
compatiblity.

I think that only one directive should be follow: don't use a resource
like stdin, stdout when you'll write new options for cdialog; these 
resources have to be shared from all widgets on the command line.
Guage uses stdin :-/ so that can't be mixed for example with an inputbox,
but it was made before of multi-widget. However this is not a big problem!

THERE ARE NO *KNOWN* BUGS. If anyone has much time and can find the way
to add wtimeout() support to libgpm, also mouse could be supported.

Please use ncurses-1.9.4 or newer.

|  __   |  demarco_p at abramo.it:~$ make Linux | more > UserFriendly;
| /__)  |  /~~  _   _ _   _   /~\  _     /  .  _          |
|/  ako | (___ (_) | ) ) (-' (__/ | )   /__ | | ) (_| ><  .  coordinator.


--- NEW FILE: dlg_colors.h ---
/*
 *  $Id: dlg_colors.h,v 1.1 2006-10-23 20:59:26 stsp Exp $
 *
 *  colors.h -- color attribute definitions
 *
 *  Copyright 2000-2004,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors
 *	Savio Lam (lam836 at cs.cuhk.hk)
 */

#ifndef COLORS_H_included
#define COLORS_H_included 1

#include <dialog.h>

/*
 *   Default color definitions (DLGC means "Dialog Color")
 *
 *   DLGC_FG_xxx = foreground for "xxx"
 *   DLGC_BG_xxx = background for "xxx"
 *   DLGC_HL_xxx = highlight for "xxx"
 */
#define DLGC_FG_SCREEN                 COLOR_CYAN
#define DLGC_BG_SCREEN                 COLOR_BLUE
#define DLGC_HL_SCREEN                 TRUE

#define DLGC_FG_SHADOW                 COLOR_BLACK
#define DLGC_BG_SHADOW                 COLOR_BLACK
#define DLGC_HL_SHADOW                 TRUE

#define DLGC_FG_DIALOG                 COLOR_BLACK
#define DLGC_BG_DIALOG                 COLOR_WHITE
#define DLGC_HL_DIALOG                 FALSE

#define DLGC_FG_TITLE                  COLOR_BLUE
#define DLGC_BG_TITLE                  COLOR_WHITE
#define DLGC_HL_TITLE                  TRUE

#define DLGC_FG_BORDER                 COLOR_WHITE
#define DLGC_BG_BORDER                 COLOR_WHITE
#define DLGC_HL_BORDER                 TRUE

#define DLGC_FG_BUTTON_ACTIVE          COLOR_WHITE
#define DLGC_BG_BUTTON_ACTIVE          COLOR_BLUE
#define DLGC_HL_BUTTON_ACTIVE          TRUE

#define DLGC_FG_BUTTON_INACTIVE        COLOR_BLACK
#define DLGC_BG_BUTTON_INACTIVE        COLOR_WHITE
#define DLGC_HL_BUTTON_INACTIVE        FALSE

#define DLGC_FG_BUTTON_KEY_ACTIVE      COLOR_WHITE
#define DLGC_BG_BUTTON_KEY_ACTIVE      COLOR_BLUE
#define DLGC_HL_BUTTON_KEY_ACTIVE      TRUE

#define DLGC_FG_BUTTON_KEY_INACTIVE    COLOR_RED
#define DLGC_BG_BUTTON_KEY_INACTIVE    COLOR_WHITE
#define DLGC_HL_BUTTON_KEY_INACTIVE    FALSE

#define DLGC_FG_BUTTON_LABEL_ACTIVE    COLOR_YELLOW
#define DLGC_BG_BUTTON_LABEL_ACTIVE    COLOR_BLUE
#define DLGC_HL_BUTTON_LABEL_ACTIVE    TRUE

#define DLGC_FG_BUTTON_LABEL_INACTIVE  COLOR_BLACK
#define DLGC_BG_BUTTON_LABEL_INACTIVE  COLOR_WHITE
#define DLGC_HL_BUTTON_LABEL_INACTIVE  TRUE

#define DLGC_FG_INPUTBOX               COLOR_BLACK
#define DLGC_BG_INPUTBOX               COLOR_WHITE
#define DLGC_HL_INPUTBOX               FALSE

#define DLGC_FG_INPUTBOX_BORDER        COLOR_BLACK
#define DLGC_BG_INPUTBOX_BORDER        COLOR_WHITE
#define DLGC_HL_INPUTBOX_BORDER        FALSE

#define DLGC_FG_SEARCHBOX              COLOR_BLACK
#define DLGC_BG_SEARCHBOX              COLOR_WHITE
#define DLGC_HL_SEARCHBOX              FALSE

#define DLGC_FG_SEARCHBOX_TITLE        COLOR_BLUE
#define DLGC_BG_SEARCHBOX_TITLE        COLOR_WHITE
#define DLGC_HL_SEARCHBOX_TITLE        TRUE

#define DLGC_FG_SEARCHBOX_BORDER       COLOR_WHITE
#define DLGC_BG_SEARCHBOX_BORDER       COLOR_WHITE
#define DLGC_HL_SEARCHBOX_BORDER       TRUE

#define DLGC_FG_POSITION_INDICATOR     COLOR_BLUE
#define DLGC_BG_POSITION_INDICATOR     COLOR_WHITE
#define DLGC_HL_POSITION_INDICATOR     TRUE

#define DLGC_FG_MENUBOX                COLOR_BLACK
#define DLGC_BG_MENUBOX                COLOR_WHITE
#define DLGC_HL_MENUBOX                FALSE

#define DLGC_FG_MENUBOX_BORDER         COLOR_WHITE
#define DLGC_BG_MENUBOX_BORDER         COLOR_WHITE
#define DLGC_HL_MENUBOX_BORDER         TRUE

#define DLGC_FG_ITEM                   COLOR_BLACK
#define DLGC_BG_ITEM                   COLOR_WHITE
#define DLGC_HL_ITEM                   FALSE

#define DLGC_FG_ITEM_SELECTED          COLOR_WHITE
#define DLGC_BG_ITEM_SELECTED          COLOR_BLUE
#define DLGC_HL_ITEM_SELECTED          TRUE

#define DLGC_FG_TAG                    COLOR_BLUE
#define DLGC_BG_TAG                    COLOR_WHITE
#define DLGC_HL_TAG                    TRUE

#define DLGC_FG_TAG_SELECTED           COLOR_YELLOW
#define DLGC_BG_TAG_SELECTED           COLOR_BLUE
#define DLGC_HL_TAG_SELECTED           TRUE

#define DLGC_FG_TAG_KEY                COLOR_RED
#define DLGC_BG_TAG_KEY                COLOR_WHITE
#define DLGC_HL_TAG_KEY                FALSE

#define DLGC_FG_TAG_KEY_SELECTED       COLOR_RED
#define DLGC_BG_TAG_KEY_SELECTED       COLOR_BLUE
#define DLGC_HL_TAG_KEY_SELECTED       TRUE

#define DLGC_FG_CHECK                  COLOR_BLACK
#define DLGC_BG_CHECK                  COLOR_WHITE
#define DLGC_HL_CHECK                  FALSE

#define DLGC_FG_CHECK_SELECTED         COLOR_WHITE
#define DLGC_BG_CHECK_SELECTED         COLOR_BLUE
#define DLGC_HL_CHECK_SELECTED         TRUE

#define DLGC_FG_UARROW                 COLOR_GREEN
#define DLGC_BG_UARROW                 COLOR_WHITE
#define DLGC_HL_UARROW                 TRUE

#define DLGC_FG_DARROW                 COLOR_GREEN
#define DLGC_BG_DARROW                 COLOR_WHITE
#define DLGC_HL_DARROW                 TRUE

#define DLGC_FG_ITEMHELP               COLOR_WHITE
#define DLGC_BG_ITEMHELP               COLOR_BLACK
#define DLGC_HL_ITEMHELP               FALSE

#define DLGC_FG_FORM_ACTIVE_TEXT       COLOR_WHITE
#define DLGC_BG_FORM_ACTIVE_TEXT       COLOR_BLUE
#define DLGC_HL_FORM_ACTIVE_TEXT       TRUE

#define DLGC_FG_FORM_TEXT              COLOR_WHITE
#define DLGC_BG_FORM_TEXT              COLOR_CYAN
#define DLGC_HL_FORM_TEXT              TRUE

/* End of default color definitions */

/*
 * Global variables
 */

typedef struct {
    const char *name;
    int value;
} color_names_st;

#endif /* COLORS_H_included */

--- NEW FILE: inputstr.c ---
/*
 * $Id: inputstr.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  inputstr.c -- functions for input/display of a string
 *
 * Copyright 2000-2004,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>
#include <dlg_keys.h>

#include <errno.h>

#ifdef HAVE_SETLOCALE
#include <locale.h>
#endif

#if defined(HAVE_SEARCH_H) && defined(HAVE_TSEARCH)
#include <search.h>
#else
#undef HAVE_TSEARCH
#endif

typedef struct _cache {
    struct _cache *next;
#ifdef USE_WIDE_CURSES
    struct _cache *cache_at;	/* unique: associate caches by CACHE */
    const char *string_at;	/* unique: associate caches by char* */
#endif
    unsigned s_len;		/* strlen(string) - we add 1 for EOS */
    unsigned i_len;		/* length(list) - we add 1 for EOS */
    char *string;		/* a copy of the last-processed string */
    int *list;			/* indices into the string */
} CACHE;

#ifdef USE_WIDE_CURSES
#define SAME_CACHE(c,s,l) (c->string != 0 && memcmp(c->string,s,l) == 0)

static CACHE *cache_list;

#ifdef HAVE_TSEARCH
static void *sorted_cache;
#endif

#ifdef USE_WIDE_CURSES
static int
have_locale(void)
{
    static int result = -1;
    if (result < 0) {
	char *test = setlocale(LC_ALL, 0);
	if (test == 0 || *test == 0) {
	    result = FALSE;
	} else if (strcmp(test, "C") && strcmp(test, "POSIX")) {
	    result = TRUE;
	} else {
	    result = FALSE;
	}
    }
    return result;
}
#endif

#ifdef HAVE_TSEARCH
static int
compare_cache(const void *a, const void *b)
{
    const CACHE *p = (const CACHE *) a;
    const CACHE *q = (const CACHE *) b;
    int result = 0;
    result = p->cache_at - q->cache_at;
    if (result == 0)
	result = p->string_at - q->string_at;
    return result;
}
#endif

static CACHE *
find_cache(CACHE * cache, const char *string)
{
    CACHE *p;

#ifdef HAVE_TSEARCH
    void *pp;
    CACHE find;

    memset(&find, 0, sizeof(find));
    find.cache_at = cache;
    find.string_at = string;

    if ((pp = tfind(&find, &sorted_cache, compare_cache)) != 0) {
	p = *(CACHE **) pp;
    } else {
	p = 0;
    }
#else
    for (p = cache_list; p != 0; p = p->next) {
	if (p->cache_at == cache
	    && p->string_at == string) {
	    break;
	}
    }
#endif
    return p;
}

static void
make_cache(CACHE * cache, const char *string)
{
    CACHE *p;

    p = (CACHE *) calloc(1, sizeof(CACHE));
    assert_ptr(p, "load_cache");
    p->next = cache_list;
    cache_list = p;

    p->cache_at = cache;
    p->string_at = string;

    *cache = *p;
#ifdef HAVE_TSEARCH
    (void) tsearch(p, &sorted_cache, compare_cache);
#endif
}

static void
load_cache(CACHE * cache, const char *string)
{
    CACHE *p;

    if ((p = find_cache(cache, string)) != 0) {
	*cache = *p;
    } else {
	make_cache(cache, string);
    }
}

static void
save_cache(CACHE * cache, const char *string)
{
    CACHE *p;

    if ((p = find_cache(cache, string)) != 0) {
	CACHE *q = p->next;
	*p = *cache;
	p->next = q;
    }
}
#else
#define SAME_CACHE(c,s,l) (c->string != 0)
#define load_cache(cache, string)	/* nothing */
#define save_cache(cache, string)	/* nothing */
#endif /* USE_WIDE_CURSES */

/*
 * If the given string has not changed, we do not need to update the index.
 * If we need to update the index, allocate enough memory for it.
 */
static bool
same_cache2(CACHE * cache, const char *string, unsigned i_len)
{
    unsigned need;
    unsigned s_len = strlen(string);

    if (cache->s_len != 0
	&& cache->s_len >= s_len
	&& cache->list != 0
	&& SAME_CACHE(cache, string, s_len)) {
	return TRUE;
    }

    need = sizeof(int *) * (i_len + 1);
    if (cache->list == 0) {
	cache->list = malloc(need);
    } else if (cache->i_len < i_len) {
	cache->list = realloc(cache->list, need);
    }
    cache->i_len = i_len;

    if (cache->s_len >= s_len && cache->string != 0) {
	strcpy(cache->string, string);
    } else {
	if (cache->string != 0)
	    free(cache->string);
	cache->string = dlg_strclone(string);
    }
    cache->s_len = s_len;

    return FALSE;
}

#ifdef USE_WIDE_CURSES
/*
 * Like same_cache2(), but we are only concerned about caching a copy of the
 * string and its associated length.
 */
static bool
same_cache1(CACHE * cache, const char *string, unsigned i_len)
{
    unsigned s_len = strlen(string);

    if (cache->s_len == s_len
	&& SAME_CACHE(cache, string, s_len)) {
	return TRUE;
    }

    if (cache->s_len >= s_len && cache->string != 0) {
	strcpy(cache->string, string);
    } else {
	if (cache->string != 0)
	    free(cache->string);
	cache->string = dlg_strclone(string);
    }
    cache->s_len = s_len;
    cache->i_len = i_len;

    return FALSE;
}
#endif /* USE_WIDE_CURSES */

/*
 * Counts the number of bytes that make up complete wide-characters, up to byte
 * 'len'.  If there is no locale set, simply return the original length.
 */
#ifdef USE_WIDE_CURSES
static int
dlg_count_wcbytes(const char *string, size_t len)
{
    int result;

    if (have_locale()) {
	static CACHE cache;

	load_cache(&cache, string);
	if (!same_cache1(&cache, string, len)) {
	    while (len != 0) {
		int part = 0;
		int code = 0;
		const char *src = cache.string;
		mbstate_t state;
		int save = cache.string[len];

		cache.string[len] = '\0';
		memset(&state, 0, sizeof(state));
		code = mbsrtowcs((wchar_t *) 0, &src, len, &state);
		cache.string[len] = save;
		if (code >= 0) {
		    break;
		}
		++part;
		--len;
	    }
	    cache.i_len = len;
	    save_cache(&cache, string);
	}
	result = cache.i_len;
    } else {
	result = len;
    }
    return result;
}
#endif /* USE_WIDE_CURSES */

/*
 * Counts the number of wide-characters in the string.
 */
int
dlg_count_wchars(const char *string)
{
    int result;

#ifdef USE_WIDE_CURSES
    if (have_locale()) {
	static CACHE cache;
	size_t len = strlen(string);

	load_cache(&cache, string);
	if (!same_cache1(&cache, string, len)) {
	    const char *src = cache.string;
	    mbstate_t state;
	    int part = dlg_count_wcbytes(cache.string, len);
	    int save = cache.string[part];
	    int code;
	    wchar_t *temp = calloc(len + 1, sizeof(wchar_t));

	    cache.string[part] = '\0';
	    memset(&state, 0, sizeof(state));
	    code = mbsrtowcs(temp, &src, part, &state);
	    cache.i_len = (code >= 0) ? wcslen(temp) : 0;
	    cache.string[part] = save;
	    free(temp);
	    save_cache(&cache, string);
	}
	result = cache.i_len;
    } else
#endif /* USE_WIDE_CURSES */
    {
	result = strlen(string);
    }
    return result;
}

/*
 * Build an index of the wide-characters in the string, so we can easily tell
 * which byte-offset begins a given wide-character.
 */
const int *
dlg_index_wchars(const char *string)
{
    static CACHE cache;
    unsigned len = dlg_count_wchars(string);
    unsigned inx;

    load_cache(&cache, string);
    if (!same_cache2(&cache, string, len)) {
	const char *current = string;

	cache.list[0] = 0;
	for (inx = 1; inx <= len; ++inx) {
#ifdef USE_WIDE_CURSES
	    if (have_locale()) {
		mbstate_t state;
		int width;
		memset(&state, 0, sizeof(state));
		width = mbrlen(current, strlen(current), &state);
		if (width <= 0)
		    width = 1;	/* FIXME: what if we have a control-char? */
		current += width;
		cache.list[inx] = cache.list[inx - 1] + width;
	    } else
#endif /* USE_WIDE_CURSES */
	    {
		(void) current;
		cache.list[inx] = inx;
	    }
	}
	save_cache(&cache, string);
    }
    return cache.list;
}

/*
 * Given the character-offset to find in the list, return the corresponding
 * array index.
 */
static int
dlg_find_index(const int *list, int limit, int to_find)
{
    int result;
    for (result = 0; result <= limit; ++result) {
	if (to_find == list[result]
	    || result == limit
	    || to_find < list[result + 1])
	    break;
    }
    return result;
}

/*
 * Build a list of the display-columns for the given string's characters.
 */
const int *
dlg_index_columns(const char *string)
{
    static CACHE cache;
    unsigned len = dlg_count_wchars(string);
    unsigned inx;

    load_cache(&cache, string);
    if (!same_cache2(&cache, string, len)) {
	cache.list[0] = 0;
#ifdef USE_WIDE_CURSES
	if (have_locale()) {
	    size_t num_bytes = strlen(string);
	    const int *inx_wchars = dlg_index_wchars(string);
	    mbstate_t state;

	    for (inx = 0; inx < len; ++inx) {
		wchar_t temp;
		int check;
		int result;

		memset(&state, 0, sizeof(state));
		check = mbrtowc(&temp, string + inx_wchars[inx], num_bytes -
				inx_wchars[inx], &state);
		if (check <= 0)
		    result = 1;
		else
		    result = wcwidth(temp);
		if (result < 0) {
		    cchar_t temp2;
		    setcchar(&temp2, &temp, 0, 0, 0);
		    result = wcslen(wunctrl(&temp2));
		}
		cache.list[inx + 1] = result;
		if (inx > 0)
		    cache.list[inx + 1] += cache.list[inx];
	    }
	} else
#endif /* USE_WIDE_CURSES */
	{
	    for (inx = 0; inx < len; ++inx) {
		cache.list[inx + 1] = (isprint(UCH(string[inx]))
				       ? 1
				       : strlen(unctrl(UCH(string[inx]))));
		if (string[inx] == '\n')
		    cache.list[inx + 1] = 1;
		if (inx != 0)
		    cache.list[inx + 1] += cache.list[inx];
	    }
	}
	save_cache(&cache, string);
    }
    return cache.list;
}

/*
 * Returns the number of columns used for a string.  That happens to be the
 * end-value of the cols[] array.
 */
int
dlg_count_columns(const char *string)
{
    int result = 0;
    int limit = dlg_count_wchars(string);
    if (limit > 0) {
	const int *cols = dlg_index_columns(string);
	result = cols[limit];
    } else {
	result = strlen(string);
    }
    return result;
}

/*
 * Given a column limit, count the number of wide characters that can fit
 * into that limit.  The offset is used to skip over a leading character
 * that was already written.
 */
int
dlg_limit_columns(const char *string, int limit, int offset)
{
    const int *cols = dlg_index_columns(string);
    int result = dlg_count_wchars(string);

    while (result > 0 && (cols[result] - cols[offset]) > limit)
	--result;
    return result;
}

/*
 * Updates the string and character-offset, given various editing characters
 * or literal characters which are inserted at the character-offset.
 */
bool
dlg_edit_string(char *string, int *chr_offset, int key, int fkey, bool force)
{
    int i;
    int len = strlen(string);
    int limit = dlg_count_wchars(string);
    const int *indx = dlg_index_wchars(string);
    int offset = dlg_find_index(indx, limit, *chr_offset);
    int max_len = MAX_LEN;
    bool edit = TRUE;

    if (dialog_vars.max_input != 0 && dialog_vars.max_input < MAX_LEN)
	max_len = dialog_vars.max_input;

    /* transform editing characters into equivalent function-keys */
    if (!fkey) {
	fkey = TRUE;		/* assume we transform */
	switch (key) {
	case 0:
	    break;
	case ESC:
	case TAB:
	    fkey = FALSE;	/* this is used for navigation */
	    break;
	default:
	    fkey = FALSE;	/* ...no, we did not transform */
	    break;
	}
    }

    if (fkey) {
	switch (key) {
	case 0:		/* special case for loop entry */
	    edit = force;
	    break;
	case DLGK_GRID_LEFT:
	    if (*chr_offset)
		*chr_offset = indx[offset - 1];
	    break;
	case DLGK_GRID_RIGHT:
	    if (offset < limit)
		*chr_offset = indx[offset + 1];
	    break;
	case DLGK_BEGIN:
	    if (*chr_offset)
		*chr_offset = 0;
	    break;
	case DLGK_FINAL:
	    if (offset < limit)
		*chr_offset = indx[limit];
	    break;
	case DLGK_DELETE_LEFT:
	    if (offset) {
		int gap = indx[offset] - indx[offset - 1];
		*chr_offset = indx[offset - 1];
		if (gap > 0) {
		    for (i = *chr_offset;
			 (string[i] = string[i + gap]) != '\0';
			 i++) {
			;
		    }
		}
	    }
	    break;
	case DLGK_DELETE_RIGHT:
	    if (limit) {
		if (--limit == 0) {
		    string[*chr_offset = 0] = '\0';
		} else {
		    int gap = ((offset <= limit)
			       ? (indx[offset + 1] - indx[offset])
			       : 0);
		    if (gap > 0) {
			for (i = indx[offset];
			     (string[i] = string[i + gap]) != '\0';
			     i++) {
			    ;
			}
		    } else if (offset > 0) {
			string[indx[offset - 1]] = '\0';
		    }
		    if (*chr_offset > indx[limit])
			*chr_offset = indx[limit];
		}
	    }
	    break;
	case DLGK_DELETE_ALL:
	    string[*chr_offset = 0] = '\0';
	    break;
	case DLGK_ENTER:
	    edit = 0;
	    break;
#ifdef KEY_RESIZE
	case KEY_RESIZE:
	    edit = 0;
	    break;
#endif
	case DLGK_FIELD_NEXT:
	case DLGK_FIELD_PREV:
	    edit = 0;
	    break;
	default:
	    beep();
	    break;
	}
    } else {
	if (key == ESC || key == TAB) {
	    edit = 0;
	} else {
	    if (len < max_len) {
		for (i = ++len; i > *chr_offset; i--)
		    string[i] = string[i - 1];
		string[*chr_offset] = key;
		*chr_offset += 1;
	    } else {
		(void) beep();
	    }
	}
    }
    return edit;
}

static void
compute_edit_offset(const char *string,
		    int chr_offset,
		    int x_last,
		    int *p_dpy_column,
		    int *p_scroll_amt)
{
    const int *cols = dlg_index_columns(string);
    const int *indx = dlg_index_wchars(string);
    int limit = dlg_count_wchars(string);
    int offset = dlg_find_index(indx, limit, chr_offset);
    int offset2;
    int dpy_column;
    int n;

    for (n = offset2 = 0; n <= offset; ++n) {
	if ((cols[offset] - cols[n]) < x_last
	    && (offset == limit || (cols[offset + 1] - cols[n]) < x_last)) {
	    offset2 = n;
	    break;
	}
    }

    dpy_column = cols[offset] - cols[offset2];

    if (p_dpy_column != 0)
	*p_dpy_column = dpy_column;
    if (p_scroll_amt != 0)
	*p_scroll_amt = offset2;
}

/*
 * Given the character-offset in the string, returns the display-offset where
 * we will position the cursor.
 */
int
dlg_edit_offset(char *string, int chr_offset, int x_last)
{
    int result;

    compute_edit_offset(string, chr_offset, x_last, &result, 0);

    return result;
}

/*
 * Displays the string, shifted as necessary, to fit within the box and show
 * the current character-offset.
 */
void
dlg_show_string(WINDOW *win,
		const char *string,	/* string to display (may be multibyte) */
		int chr_offset,	/* character (not bytes) offset */
		chtype attr,	/* window-attributes */
		int y_base,	/* beginning row on screen */
		int x_base,	/* beginning column on screen */
		int x_last,	/* number of columns on screen */
		bool hidden,	/* if true, do not echo */
		bool force)	/* if true, force repaint */
{
    x_last = MIN(x_last + x_base, getmaxx(win)) - x_base;

    if (hidden && !dialog_vars.insecure) {
	if (force) {
	    (void) wmove(win, y_base, x_base);
	    wrefresh(win);
	}
    } else {
	const int *cols = dlg_index_columns(string);
	const int *indx = dlg_index_wchars(string);
	int limit = dlg_count_wchars(string);

	int i, j, k;
	int input_x;
	int scrollamt;

	compute_edit_offset(string, chr_offset, x_last, &input_x, &scrollamt);

	wattrset(win, attr);
	(void) wmove(win, y_base, x_base);
	for (i = scrollamt, k = 0; i < limit && k < x_last; ++i) {
	    int check = cols[i + 1] - cols[scrollamt];
	    if (check <= x_last) {
		for (j = indx[i]; j < indx[i + 1]; ++j) {
		    if (hidden && dialog_vars.insecure)
			waddch(win, '*');
		    else
			waddch(win, CharOf(string[j]));
		}
		k = check;
	    } else {
		break;
	    }
	}
	while (k++ < x_last)
	    waddch(win, ' ');
	(void) wmove(win, y_base, x_base + input_x);
	wrefresh(win);
    }
}

#ifdef NO_LEAKS
void
_dlg_inputstr_leaks(void)
{
    while (cache_list != 0) {
	CACHE *next = cache_list->next;
#ifdef HAVE_TSEARCH
	tdelete(cache_list, &sorted_cache, compare_cache);
#endif
	if (cache_list->string != 0)
	    free(cache_list->string);
	if (cache_list->list != 0)
	    free(cache_list->list);
	free(cache_list);
	cache_list = next;
    }
}
#endif

--- NEW FILE: dialog.3 ---
.\" $Id: dialog.3,v 1.1 2006-10-23 20:59:26 stsp Exp $
.\" Copyright 2005  Thomas E. Dickey
.\"
.\" This program is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU Lesser General Public License as
.\" published by the Free Software Foundation; either version 2.1 of the
.\" License, or (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful, but
.\" WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
.\" Lesser General Public License for more details.
.\"
.\" You should have received a copy of the GNU Lesser General Public
.\" License along with this program; if not, write to
.\"	Free Software Foundation, Inc.
.\"	51 Franklin St., Fifth Floor
.\"	Boston, MA 02110, USA.
.TH DIALOG 3 "" "$Date: 2006-10-23 20:59:26 $"
[...2186 lines suppressed...]
.TP 5
.B dlg_yes_buttoncode
Map the given button index for \fBdlg_yes_labels\fP into \fBdialog\fP's exit-code.
.RS
.TP 5
.B int \fIbutton
is the button index
.RE
.
.TP 5
.B dlg_yes_labels
Return a list of buttons for Yes/No labels.
.
.\" ************************************************************************
.SH SEE ALSO
dialog (1).
.
.\" ************************************************************************
.SH AUTHOR
Thomas E. Dickey

--- NEW FILE: dialog.1 ---
.\" $Id: dialog.1,v 1.1 2006-10-23 20:59:26 stsp Exp $
.\" Copyright 2005,2006  Thomas E. Dickey
.\"
.\" This program is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU Lesser General Public License as
.\" published by the Free Software Foundation; either version 2.1 of the
.\" License, or (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful, but
.\" WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
.\" Lesser General Public License for more details.
.\"
.\" You should have received a copy of the GNU Lesser General Public
.\" License along with this program; if not, write to
.\"	Free Software Foundation, Inc.
.\"	51 Franklin St., Fifth Floor
.\"	Boston, MA 02110, USA.
.de EX
[...968 lines suppressed...]
.SH CONTRIBUTORS
Tobias C. Rittweiler
.LP
Valery Reznic - the form and progressbox widgets.
.LP
Yura Kalinichenko adapted the gauge widget as "pause".
.PP
This is a rewrite (except as needed to provide compatibility)
of the earlier version of \fBdialog 0.9a\fP,
which lists as authors:
.RS
.LP
Savio Lam - version 0.3, "dialog"
.LP
Stuart Herbert - patch for version 0.4
.LP
Marc Ewing - the gauge widget.
.LP
Pasquale De Marco "Pako" - version 0.9a, "cdialog"
.RE

--- NEW FILE: configure ---
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by Autoconf 2.52.20030208.
#
# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001
# Free Software Foundation, Inc.
# This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it.

# Avoid depending upon Character Ranges.
as_cr_letters='abcdefghijklmnopqrstuvwxyz'
as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
as_cr_Letters=$as_cr_letters$as_cr_LETTERS
as_cr_digits='0123456789'
as_cr_alnum=$as_cr_Letters$as_cr_digits

# Sed expression to map a string onto a valid variable name.
as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g"

[...10825 lines suppressed...]
ac_clean_files=$ac_clean_files_save

# configure is writing to config.log, and then calls config.status.
# config.status does its own redirection, appending to config.log.
# Unfortunately, on DOS this fails, as config.log is still kept open
# by configure, so config.status won't be able to write to it; its
# output is simply discarded.  So we exec the FD to /dev/null,
# effectively closing config.log, so it can be properly (re)opened and
# appended to by config.status.  When coming back to configure, we
# need to make the FD available again.
if test "$no_create" != yes; then
  ac_cs_success=:
  exec 5>/dev/null
  $SHELL $CONFIG_STATUS || ac_cs_success=false
  exec 5>>config.log
  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
  # would make configure fail if this is the last instruction.
  $ac_cs_success || { (exit 1); exit 1; }
fi


--- NEW FILE: fselect.c ---
/*
 * $Id: fselect.c,v 1.1 2006-10-23 20:59:26 stsp Exp $
 *
 *  fselect.c -- implements the file-selector box
 *
 * Copyright 2000-2005,2006   Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>
#include <dlg_keys.h>

#include <sys/types.h>
#include <sys/stat.h>

#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# if HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif

# if defined(_FILE_OFFSET_BITS) && defined(HAVE_STRUCT_DIRENT64)
#  if !defined(_LP64) && (_FILE_OFFSET_BITS == 64)
#   define      DIRENT  struct dirent64
#  else
#   define      DIRENT  struct dirent
#  endif
# else
#  define       DIRENT  struct dirent
# endif

#define EXT_WIDE 1
#define HDR_HIGH 1
#define BTN_HIGH (1 + 2 * MARGIN)	/* Ok/Cancel, also input-box */
#define MIN_HIGH (HDR_HIGH - MARGIN + (BTN_HIGH * 2) + 4 * MARGIN)
#define MIN_WIDE (2 * MAX(dlg_count_columns(d_label), dlg_count_columns(f_label)) + 6 * MARGIN + 2 * EXT_WIDE)

#define MOUSE_D (KEY_MAX + 0)
#define MOUSE_F (KEY_MAX + 10000)
#define MOUSE_T (KEY_MAX + 20000)

typedef enum {
    sDIRS = -3
    ,sFILES = -2
    ,sTEXT = -1
} STATES;

typedef struct {
    WINDOW *par;		/* parent window */
    WINDOW *win;		/* this window */
    int length;			/* length of the data[] array */
    int offset;			/* index of first item on screen */
    int choice;			/* index of the selection */
    int mousex;			/* base of mouse-code return-values */
    unsigned allocd;
    char **data;
} LIST;

static void
init_list(LIST * list, WINDOW *par, WINDOW *win, int mousex)
{
    list->par = par;
    list->win = win;
    list->length = 0;
    list->offset = 0;
    list->choice = 0;
    list->mousex = mousex;
    list->allocd = 0;
    list->data = 0;
    dlg_mouse_mkbigregion(getbegy(win), getbegx(win),
			  getmaxy(win), getmaxx(win),
			  mousex, 1, 1, 1 /* by lines */ );
}

static char *
leaf_of(char *path)
{
    char *leaf = strrchr(path, '/');
    if (leaf != 0)
	leaf++;
    else
	leaf = path;
    return leaf;
}

static char *
data_of(LIST * list)
{
    if (list != 0
	&& list->data != 0)
	return list->data[list->choice];
    return 0;
}

static void
free_list(LIST * list, int reinit)
{
    int n;

    if (list->data != 0) {
	for (n = 0; list->data[n] != 0; n++)
	    free(list->data[n]);
	free(list->data);
	list->data = 0;
    }
    if (reinit)
	init_list(list, list->par, list->win, list->mousex);
}

static void
add_to_list(LIST * list, char *text)
{
    unsigned need;

    need = list->length + 1;
    if (need + 1 > list->allocd) {
	list->allocd = 2 * (need + 1);
	if (list->data == 0) {
	    list->data = (char **) malloc(sizeof(char *) * list->allocd);
	} else {
	    list->data = (char **) realloc(list->data, sizeof(char *) * list->allocd);
	}
	assert_ptr(list->data, "add_to_list");
    }
    list->data[list->length++] = dlg_strclone(text);
    list->data[list->length] = 0;
}

static void
keep_visible(LIST * list)
{
    int high = getmaxy(list->win);

    if (list->choice < list->offset) {
	list->offset = list->choice;
    }
    if (list->choice - list->offset >= high)
	list->offset = list->choice - high + 1;
}

#define Value(c) (int)((c) & 0xff)

static int
find_choice(char *target, LIST * list)
{
    int n;
    int choice = list->choice;
    int len_1, len_2, cmp_1, cmp_2;

    if (*target == 0) {
	list->choice = 0;
    } else {
	/* find the match with the longest length.  If more than one has the
	 * same length, choose the one with the closest match of the final
	 * character.
	 */
	len_1 = 0;
	cmp_1 = 256;
	for (n = 0; n < list->length; n++) {
	    char *a = target;
	    char *b = list->data[n];

	    len_2 = 0;
	    while ((*a != 0) && (*b != 0) && (*a == *b)) {
		a++;
		b++;
		len_2++;
	    }
	    cmp_2 = Value(*a) - Value(*b);
	    if (cmp_2 < 0)
		cmp_2 = -cmp_2;
	    if ((len_2 > len_1)
		|| (len_1 == len_2 && cmp_2 < cmp_1)) {
		len_1 = len_2;
		cmp_1 = cmp_2;
		list->choice = n;
	    }
	}
    }
    if (choice != list->choice) {
	keep_visible(list);
    }
    return (choice != list->choice);
}

static void
display_list(LIST * list)
{
    int n;
    int x;
    int y;
    int top;
    int bottom;

    dlg_attr_clear(list->win, getmaxy(list->win), getmaxx(list->win), item_attr);
    for (n = list->offset; n < list->length && list->data[n]; n++) {
	y = n - list->offset;
	if (y >= getmaxy(list->win))
	    break;
	(void) wmove(list->win, y, 0);
	if (n == list->choice)
	    wattrset(list->win, item_selected_attr);
	(void) waddstr(list->win, list->data[n]);
	wattrset(list->win, item_attr);
    }
    wattrset(list->win, item_attr);

    getparyx(list->win, y, x);

    top = y - 1;
    bottom = y + getmaxy(list->win);
    dlg_draw_arrows(list->par, list->offset,
		    list->length - list->offset > getmaxy(list->win),
		    x + 1,
		    top,
		    bottom);

    (void) wmove(list->win, list->choice - list->offset, 0);
    (void) wnoutrefresh(list->win);
}

/* FIXME: see arrows.c
 * This workaround is used to allow two lists to have scroll-tabs at the same
 * time, by reassigning their return-values to be different.  Just for
 * readability, we use the names of keys with similar connotations, though all
 * that is really required is that they're distinct, so we can put them in a
 * switch statement.
 */
static void
fix_arrows(LIST * list)
{
    int x;
    int y;
    int top;
    int bottom;

    getparyx(list->win, y, x);
    top = y - 1;
    bottom = y + getmaxy(list->win);

    mouse_mkbutton(top, x, 6,
		   ((list->mousex == MOUSE_D)
		    ? KEY_PREVIOUS
		    : KEY_PPAGE));
    mouse_mkbutton(bottom, x, 6,
		   ((list->mousex == MOUSE_D)
		    ? KEY_NEXT
		    : KEY_NPAGE));
}

static int
show_list(char *target, LIST * list, bool keep)
{
    int changed = keep || find_choice(target, list);
    display_list(list);
    return changed;
}

/*
 * Highlight the closest match to 'target' in the given list, setting offset
 * to match.
 */
static int
show_both_lists(char *input, LIST * d_list, LIST * f_list, bool keep)
{
    char *leaf = leaf_of(input);

    return show_list(leaf, d_list, keep) | show_list(leaf, f_list, keep);
}

/*
 * Move up/down in the given list
 */
static bool
change_list(int choice, LIST * list)
{
    if (data_of(list) != 0) {
	int last = list->length - 1;

	choice += list->choice;
	if (choice < 0)
	    choice = 0;
	if (choice > last)
	    choice = last;
	list->choice = choice;
	keep_visible(list);
	display_list(list);
	return TRUE;
    }
    return FALSE;
}

static void
scroll_list(int direction, LIST * list)
{
    if (data_of(list) != 0) {
	int length = getmaxy(list->win);
	if (change_list(direction * length, list))
	    return;
    }
    beep();
}

static int
compar(const void *a, const void *b)
{
    return strcmp(*(const char *const *) a, *(const char *const *) b);
}

static bool
fill_lists(char *current, char *input, LIST * d_list, LIST * f_list, bool keep)
{
    DIR *dp;
    DIRENT *de;
    struct stat sb;
    int n;
    char path[MAX_LEN + 1];
    char *leaf;

    /* check if we've updated the lists */
    for (n = 0; current[n] && input[n]; n++) {
	if (current[n] != input[n])
	    break;
    }
    if (current[n] == input[n])
	return FALSE;
    if (strchr(current + n, '/') == 0
	&& strchr(input + n, '/') == 0) {
	return show_both_lists(input, d_list, f_list, keep);
    }

    strcpy(current, input);

    /* refill the lists */
    free_list(d_list, TRUE);
    free_list(f_list, TRUE);
    strcpy(path, current);
    if ((leaf = strrchr(path, '/')) != 0) {
	*++leaf = 0;
    } else {
	strcpy(path, "./");
	leaf = path + strlen(path);
    }
    if ((dp = opendir(path)) != 0) {
	while ((de = readdir(dp)) != 0) {
	    strncpy(leaf, de->d_name, NAMLEN(de))[NAMLEN(de)] = 0;
	    if (stat(path, &sb) == 0) {
		if ((sb.st_mode & S_IFMT) == S_IFDIR)
		    add_to_list(d_list, leaf);
		else
		    add_to_list(f_list, leaf);
	    }
	}
	(void) closedir(dp);
	/* sort the lists */
	qsort(d_list->data, d_list->length, sizeof(d_list->data[0]), compar);
	qsort(f_list->data, f_list->length, sizeof(f_list->data[0]), compar);
    }

    (void) show_both_lists(input, d_list, f_list, FALSE);
    d_list->offset = d_list->choice;
    f_list->offset = f_list->choice;
    return TRUE;
}

static bool
usable_state(STATES state, LIST * dirs, LIST * files)
{
    bool result;

    switch (state) {
    case sDIRS:
	result = (data_of(dirs) != 0);
	break;
    case sFILES:
	result = (data_of(files) != 0);
	break;
    default:
	result = TRUE;
	break;
    }
    return result;
}

#define which_list() ((state == sFILES) \
			? &f_list \
			: ((state == sDIRS) \
			  ? &d_list \
			  : 0))
/*
 * Display a dialog box for entering a filename
 */
int
dialog_fselect(const char *title, const char *path, int height, int width)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),	/* override inputstr */
	INPUTSTR_BINDINGS,
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_DOWN),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_NEXT ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_UP ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,  KEY_NPAGE ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,  KEY_PPAGE ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
    bool resized = FALSE;
#endif
    int tbox_y, tbox_x, tbox_width, tbox_height;
    int dbox_y, dbox_x, dbox_width, dbox_height;
    int fbox_y, fbox_x, fbox_width, fbox_height;
    int show_buttons = TRUE;
    int offset = 0;
    int key = 0;
    int fkey = FALSE;
    int code;
    int result = DLG_EXIT_UNKNOWN;
    int state = dialog_vars.defaultno ? dlg_defaultno_button() : sTEXT;
    int button = state;
    int first = (state == sTEXT);
    char *input;
    char *completed;
    char current[MAX_LEN + 1];
    WINDOW *dialog, *w_text, *w_dir, *w_file;
    const char **buttons = dlg_ok_labels();
    char *d_label = _("Directories");
    char *f_label = _("Files");
    int min_wide = MIN_WIDE;
    int min_items = height ? 0 : 4;
    LIST d_list, f_list;

    dlg_does_output();

    /* Set up the initial value */
    input = dlg_set_result(path);
    offset = strlen(input);
    *current = 0;

#ifdef KEY_RESIZE
  retry:
#endif
    dlg_auto_size(title, (char *) 0, &height, &width, 6, 25);
    height += MIN_HIGH + min_items;
    if (width < min_wide)
	width = min_wide;
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    dialog = dlg_new_window(height, width,
			    dlg_box_y_ordinate(height),
			    dlg_box_x_ordinate(width));
    dlg_register_window(dialog, "fselect", binding);
    dlg_register_buttons(dialog, "fselect", buttons);

    dlg_mouse_setbase(0, 0);

    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_bottom_box(dialog);
    dlg_draw_title(dialog, title);

    wattrset(dialog, dialog_attr);

    /* Draw the input field box */
    tbox_height = 1;
    tbox_width = width - (4 * MARGIN + 2);
    tbox_y = height - (BTN_HIGH * 2) + MARGIN;
    tbox_x = (width - tbox_width) / 2;

    w_text = derwin(dialog, tbox_height, tbox_width, tbox_y, tbox_x);
    if (w_text == 0)
	return DLG_EXIT_ERROR;

    (void) keypad(w_text, TRUE);
    dlg_draw_box(dialog, tbox_y - MARGIN, tbox_x - MARGIN,
		 (2 * MARGIN + 1), tbox_width + (MARGIN + EXT_WIDE),
		 menubox_border_attr, menubox_attr);
    dlg_mouse_mkbigregion(getbegy(dialog) + tbox_y - MARGIN,
			  getbegx(dialog) + tbox_x - MARGIN,
			  1 + (2 * MARGIN),
			  tbox_width + (MARGIN + EXT_WIDE),
			  MOUSE_T, 1, 1, 3 /* doesn't matter */ );

    /* Draw the directory listing box */
    dbox_height = height - MIN_HIGH;
    dbox_width = (width - (6 * MARGIN + 2 * EXT_WIDE)) / 2;
    dbox_y = (2 * MARGIN + 1);
    dbox_x = tbox_x;

    w_dir = derwin(dialog, dbox_height, dbox_width, dbox_y, dbox_x);
    if (w_dir == 0)
	return DLG_EXIT_ERROR;

    (void) keypad(w_dir, TRUE);
    (void) mvwprintw(dialog, dbox_y - (MARGIN + 1), dbox_x - MARGIN, d_label);
    dlg_draw_box(dialog,
		 dbox_y - MARGIN, dbox_x - MARGIN,
		 dbox_height + (MARGIN + 1), dbox_width + (MARGIN + 1),
		 menubox_border_attr, menubox_attr);
    init_list(&d_list, dialog, w_dir, MOUSE_D);

    /* Draw the filename listing box */
    fbox_height = dbox_height;
    fbox_width = dbox_width;
    fbox_y = dbox_y;
    fbox_x = tbox_x + dbox_width + (2 * MARGIN);

    w_file = derwin(dialog, fbox_height, fbox_width, fbox_y, fbox_x);
    if (w_file == 0)
	return DLG_EXIT_ERROR;

    (void) keypad(w_file, TRUE);
    (void) mvwprintw(dialog, fbox_y - (MARGIN + 1), fbox_x - MARGIN, f_label);
    dlg_draw_box(dialog,
		 fbox_y - MARGIN, fbox_x - MARGIN,
		 fbox_height + (MARGIN + 1), fbox_width + (MARGIN + 1),
		 menubox_border_attr, menubox_attr);
    init_list(&f_list, dialog, w_file, MOUSE_F);

    while (result == DLG_EXIT_UNKNOWN) {

	if (fill_lists(current, input, &d_list, &f_list, state < sTEXT))
	    show_buttons = TRUE;

#ifdef KEY_RESIZE
	if (resized) {
	    resized = FALSE;
	    dlg_show_string(w_text, input, offset, inputbox_attr,
			    0, 0, tbox_width, 0, first);
	}
#endif

	/*
	 * The last field drawn determines where the cursor is shown:
	 */
	if (show_buttons) {
	    show_buttons = FALSE;
	    button = (state < 0) ? 0 : state;
	    dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
	}
	if (state < 0) {
	    switch (state) {
	    case sTEXT:
		dlg_set_focus(dialog, w_text);
		break;
	    case sFILES:
		dlg_set_focus(dialog, w_file);
		break;
	    case sDIRS:
		dlg_set_focus(dialog, w_dir);
		break;
	    }
	}

	if (first) {
	    (void) wrefresh(dialog);
	} else {
	    fix_arrows(&d_list);
	    fix_arrows(&f_list);
	    key = dlg_mouse_wgetch(dialog, &fkey);
	    if (dlg_result_key(key, fkey, &result))
		break;
	}

	if (!fkey && key == ' ') {
	    key = DLGK_SELECT;
	    fkey = TRUE;
	}

	if (fkey) {
	    switch (key) {
	    case DLGK_MOUSE(KEY_PREVIOUS):
		state = sDIRS;
		scroll_list(-1, which_list());
		continue;
	    case DLGK_MOUSE(KEY_NEXT):
		state = sDIRS;
		scroll_list(1, which_list());
		continue;
	    case DLGK_MOUSE(KEY_PPAGE):
		state = sFILES;
		scroll_list(-1, which_list());
		continue;
	    case DLGK_MOUSE(KEY_NPAGE):
		state = sFILES;
		scroll_list(1, which_list());
		continue;
	    case DLGK_PAGE_PREV:
		scroll_list(-1, which_list());
		continue;
	    case DLGK_PAGE_NEXT:
		scroll_list(1, which_list());
		continue;
	    case DLGK_ITEM_PREV:
		if (change_list(-1, which_list()))
		    continue;
		/* FALLTHRU */
	    case DLGK_FIELD_PREV:
		show_buttons = TRUE;
		do {
		    state = dlg_prev_ok_buttonindex(state, sDIRS);
		} while (!usable_state(state, &d_list, &f_list));
		continue;
	    case DLGK_ITEM_NEXT:
		if (change_list(1, which_list()))
		    continue;
		/* FALLTHRU */
	    case DLGK_FIELD_NEXT:
		show_buttons = TRUE;
		do {
		    state = dlg_next_ok_buttonindex(state, sDIRS);
		} while (!usable_state(state, &d_list, &f_list));
		continue;
	    case DLGK_SELECT:
		completed = 0;
		if (state == sFILES) {
		    completed = data_of(&f_list);
		} else if (state == sDIRS) {
		    completed = data_of(&d_list);
		}
		if (completed != 0) {
		    state = sTEXT;
		    show_buttons = TRUE;
		    strcpy(leaf_of(input), completed);
		    offset = strlen(input);
		    dlg_show_string(w_text, input, offset, inputbox_attr,
				    0, 0, tbox_width, 0, first);
		    continue;
		} else if (state < sTEXT) {
		    (void) beep();
		    continue;
		}
		/* FALLTHRU */
	    case DLGK_ENTER:
		result = (state > 0) ? dlg_ok_buttoncode(state) : DLG_EXIT_OK;
		continue;
#ifdef KEY_RESIZE
	    case KEY_RESIZE:
		/* reset data */
		height = old_height;
		width = old_width;
		show_buttons = TRUE;
		*current = 0;
		resized = TRUE;
		/* repaint */
		dlg_clear();
		dlg_del_window(dialog);
		refresh();
		dlg_mouse_free_regions();
		goto retry;
#endif
	    default:
		if (key >= DLGK_MOUSE(MOUSE_T)) {
		    state = sTEXT;
		    continue;
		} else if (key >= DLGK_MOUSE(MOUSE_F)) {
		    state = sFILES;
		    f_list.choice = (key - DLGK_MOUSE(MOUSE_F)) + f_list.offset;
		    display_list(&f_list);
		    continue;
		} else if (key >= DLGK_MOUSE(MOUSE_D)) {
		    state = sDIRS;
		    d_list.choice = (key - DLGK_MOUSE(MOUSE_D)) + d_list.offset;
		    display_list(&d_list);
		    continue;
		} else if (is_DLGK_MOUSE(key)
			   && (code = dlg_ok_buttoncode(key - M_EVENT)) >= 0) {
		    result = code;
		    continue;
		}
		break;
	    }
	}

	if (state < 0) {	/* Input box selected if we're editing */
	    int edit = dlg_edit_string(input, &offset, key, fkey, first);

	    if (edit) {
		dlg_show_string(w_text, input, offset, inputbox_attr,
				0, 0, tbox_width, 0, first);
		first = FALSE;
		state = sTEXT;
	    }
	} else if (state >= 0 &&
		   (code = dlg_char_to_button(key, buttons)) >= 0) {
	    result = dlg_ok_buttoncode(code);
	    break;
	}
    }

    dlg_del_window(dialog);
    dlg_mouse_free_regions();
    free_list(&d_list, FALSE);
    free_list(&f_list, FALSE);
    return result;
}

--- NEW FILE: mouse.c ---
/*
 * $Id: mouse.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 * mouse.c -- mouse support for dialog
 *
 * Copyright 2002-2003,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>
#include <dlg_keys.h>

#if USE_MOUSE

static int basex, basey;

static mseRegion *regionList = NULL;

/*=========== region related functions =============*/

static mseRegion *
find_region_by_code(int code)
{
    mseRegion *butPtr;

    for (butPtr = regionList; butPtr; butPtr = butPtr->next) {
	if (code == butPtr->code)
	    break;
    }
    return butPtr;
}

void
dlg_mouse_setbase(int x, int y)
{
    basex = x;
    basey = y;
}

void
dlg_mouse_mkbigregion(int y, int x,
		      int height, int width,
		      int code,
		      int step_y, int step_x,
		      int mode)
{
    mseRegion *butPtr = dlg_mouse_mkregion(y, x, height, width, -DLGK_MOUSE(code));
    butPtr->mode = mode;
    butPtr->step_x = MAX(1, step_x);
    butPtr->step_y = MAX(1, step_y);
}

void
dlg_mouse_free_regions(void)
{
    while (regionList != 0) {
	mseRegion *butPtr = regionList->next;
	free(regionList);
	regionList = butPtr;
    }
}

mseRegion *
dlg_mouse_mkregion(int y, int x, int height, int width, int code)
{
    mseRegion *butPtr;

    if ((butPtr = find_region_by_code(code)) == 0) {
	butPtr = malloc(sizeof(mseRegion));
	assert_ptr(butPtr, "dlg_mouse_mkregion");
	butPtr->next = regionList;
	regionList = butPtr;
    }
    if (butPtr != 0) {
	butPtr->mode = -1;
	butPtr->step_x = 0;
	butPtr->step_y = 0;
	butPtr->y = basey + y;
	butPtr->Y = basey + y + height;
	butPtr->x = basex + x;
	butPtr->X = basex + x + width;
	butPtr->code = code;
    }
    return butPtr;
}

/* retrieve the frame under the pointer */
static mseRegion *
any_mouse_region(int y, int x, int small)
{
    mseRegion *butPtr;

    for (butPtr = regionList; butPtr; butPtr = butPtr->next) {
	if (small ^ (butPtr->code >= 0))
	    continue;
	if (y < butPtr->y || y >= butPtr->Y)
	    continue;
	if (x < butPtr->x || x >= butPtr->X)
	    continue;
	break;			/* found */
    }
    return butPtr;
}

/* retrieve the frame under the pointer */
mseRegion *
dlg_mouse_region(int y, int x)
{
    return any_mouse_region(y, x, TRUE);
}

/* retrieve the bigframe under the pointer */
mseRegion *
dlg_mouse_bigregion(int y, int x)
{
    return any_mouse_region(y, x, FALSE);
}

#else
void mouse_dummy(void);
void
mouse_dummy(void)
{
}
#endif /* USE_MOUSE */

--- NEW FILE: ui_getc.c ---
/*
 *  $Id: ui_getc.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  ui_getc.c - user interface glue for getc()
 *
 * Copyright 2001-2004,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>
#include <dlg_keys.h>

#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

#ifdef __QNX__
#include <sys/select.h>
#endif

#ifndef WEXITSTATUS
# ifdef HAVE_TYPE_UNIONWAIT
#  define	WEXITSTATUS(status)	(status.w_retcode)
# else
#  define	WEXITSTATUS(status)	(((status) & 0xff00) >> 8)
# endif
#endif

#ifndef WTERMSIG
# ifdef HAVE_TYPE_UNIONWAIT
#  define	WTERMSIG(status)	(status.w_termsig)
# else
#  define	WTERMSIG(status)	((status) & 0x7f)
# endif
#endif

void
dlg_add_callback(DIALOG_CALLBACK * p)
{
    p->next = dialog_state.getc_callbacks;
    dialog_state.getc_callbacks = p;
    wtimeout(p->win, WTIMEOUT_VAL);
}

void
dlg_remove_callback(DIALOG_CALLBACK * p)
{
    DIALOG_CALLBACK *q;

    if (p->input != 0) {
	fclose(p->input);
	p->input = 0;
    }

    dlg_del_window(p->win);
    if ((q = dialog_state.getc_callbacks) == p) {
	dialog_state.getc_callbacks = p->next;
    } else {
	while (q != 0) {
	    if (q->next == p) {
		q->next = p->next;
		break;
	    }
	    q = q->next;
	}
    }
    free(p);
}

/*
 * FIXME: this could be replaced by a select/poll on several file descriptors
 */
static int
dlg_getc_ready(DIALOG_CALLBACK * p)
{
    fd_set read_fds;
    int fd = fileno(p->input);
    struct timeval test;

    FD_ZERO(&read_fds);
    FD_SET(fd, &read_fds);

    test.tv_sec = 0;		/* Seconds.  */
    test.tv_usec = WTIMEOUT_VAL * 1000;		/* Microseconds.  */
    return (select(fd + 1, &read_fds, (fd_set *) 0, (fd_set *) 0, &test) == 1)
	&& (FD_ISSET(fd, &read_fds));
}

int
dlg_getc_callbacks(int ch, int fkey, int *result)
{
    DIALOG_CALLBACK *p, *q;

    if ((p = dialog_state.getc_callbacks) != 0) {
	do {
	    q = p->next;
	    if (dlg_getc_ready(p)) {
		if (!(p->handle_getc(p, ch, fkey, result))) {
		    dlg_remove_callback(p);
		}
	    }
	} while ((p = q) != 0);
	return TRUE;
    }
    return FALSE;
}

static void
dlg_raise_window(WINDOW *win)
{
    touchwin(win);
    wmove(win, getcury(win), getcurx(win));
    wnoutrefresh(win);
    doupdate();
}

/*
 * This is a work-around for the case where we actually need the wide-character
 * code versus a byte stream.
 */
static int last_getc = ERR;

#ifdef USE_WIDE_CURSES
static char last_getc_bytes[80];
static int have_last_getc;
static int used_last_getc;
#endif

int
dlg_last_getc(void)
{
#ifdef USE_WIDE_CURSES
    if (used_last_getc != 1)
	return ERR;		/* not really an error... */
#endif
    return last_getc;
}

void
dlg_flush_getc(void)
{
    last_getc = ERR;
#ifdef USE_WIDE_CURSES
    have_last_getc = 0;
    used_last_getc = 0;
#endif
}

/*
 * Read a character from the given window.  Handle repainting here (to simplify
 * things in the calling application).  Also, if input-callback(s) are set up,
 * poll the corresponding files and handle the updates, e.g., for displaying a
 * tailbox.
 */
int
dlg_getc(WINDOW *win, int *fkey)
{
    WINDOW *save_win = win;
    int ch = ERR;
    int result;
    bool done = FALSE;
    DIALOG_CALLBACK *p;
    int interval = dialog_vars.timeout_secs;
    time_t expired = time((time_t *) 0) + dialog_vars.timeout_secs;
    time_t current;

    if (dialog_state.getc_callbacks != 0)
	wtimeout(win, WTIMEOUT_VAL);
    else if (interval > 0)
	wtimeout(win, interval);

    while (!done) {
#ifdef USE_WIDE_CURSES
	int code;
	mbstate_t state;
	wchar_t my_wchar;
	wint_t my_wint;

	/*
	 * We get a wide character, translate it to multibyte form to avoid
	 * having to change the rest of the code to use wide-characters.
	 */
	if (used_last_getc >= have_last_getc) {
	    used_last_getc = 0;
	    have_last_getc = 0;
	    ch = ERR;
	    *fkey = 0;
	    code = wget_wch(win, &my_wint);
	    my_wchar = my_wint;
	    switch (code) {
	    case KEY_CODE_YES:
		ch = *fkey = my_wchar;
		last_getc = my_wchar;
		break;
	    case OK:
		memset(&state, 0, sizeof(state));
		have_last_getc = wcrtomb(last_getc_bytes, my_wchar, &state);
		if (have_last_getc < 0) {
		    have_last_getc = used_last_getc = 0;
		    last_getc_bytes[0] = my_wchar;
		}
		ch = CharOf(last_getc_bytes[used_last_getc++]);
		last_getc = my_wchar;
		break;
	    case ERR:
		ch = ERR;
		last_getc = ERR;
		break;
	    default:
		break;
	    }
	} else {
	    ch = CharOf(last_getc_bytes[used_last_getc++]);
	}
#else
	ch = wgetch(win);
	last_getc = ch;
	*fkey = (ch > KEY_MIN && ch < KEY_MAX);
#endif
	ch = dlg_lookup_key(win, ch, fkey);
	current = time((time_t *) 0);

	switch (ch) {
	case CHR_REPAINT:
	    (void) touchwin(win);
	    (void) wrefresh(curscr);
	    break;
	case ERR:		/* wtimeout() in effect; check for file I/O */
	    if (interval > 0
		&& current >= expired) {
		dlg_exiterr("timeout");
	    }
	    if (dlg_getc_callbacks(ch, *fkey, &result)) {
		dlg_raise_window(win);
	    } else {
		done = (interval <= 0);
	    }
	    break;
	case TAB:
	    /* Handle tab as a special case for traversing between the nominal
	     * "current" window, and other windows having callbacks.  If the
	     * nominal (control) window closes, we'll close the windows with
	     * callbacks.
	     */
	    if (dialog_state.getc_callbacks != 0) {
		if ((p = dialog_state.getc_redirect) != 0) {
		    p = p->next;
		} else {
		    p = dialog_state.getc_callbacks;
		}
		if ((dialog_state.getc_redirect = p) != 0) {
		    win = p->win;
		} else {
		    win = save_win;
		}
		dlg_raise_window(win);
		break;
	    }
	    /* FALLTHRU */
	default:
	    if ((p = dialog_state.getc_redirect) != 0) {
		if (!(p->handle_getc(p, ch, *fkey, &result))) {
		    dlg_remove_callback(p);
		    dialog_state.getc_redirect = 0;
		    win = save_win;
		}
		break;
	    } else {
		done = TRUE;
	    }
	}
    }
    return ch;
}

static void
finish_bg(int sig GCC_UNUSED)
{
    end_dialog();
    dlg_exit(DLG_EXIT_ERROR);
}

/*
 * If we have callbacks active, purge the list of all that are not marked
 * to keep in the background.  If any remain, run those in a background
 * process.
 */
void
dlg_killall_bg(int *retval)
{
    DIALOG_CALLBACK *cb;
    int pid;
#ifdef HAVE_TYPE_UNIONWAIT
    union wait wstatus;
#else
    int wstatus;
#endif

    if ((cb = dialog_state.getc_callbacks) != 0) {
	while (cb != 0) {
	    if (cb->keep_bg) {
		cb = cb->next;
	    } else {
		dlg_remove_callback(cb);
		cb = dialog_state.getc_callbacks;
	    }
	}
	if (dialog_state.getc_callbacks != 0) {

	    refresh();
	    fflush(stdout);
	    fflush(stderr);
	    reset_shell_mode();
	    if ((pid = fork()) != 0) {
		_exit(pid > 0 ? DLG_EXIT_OK : DLG_EXIT_ERROR);
	    } else if (pid == 0) {	/* child */
		if ((pid = fork()) != 0) {
		    /*
		     * Echo the process-id of the grandchild so a shell script
		     * can read that, and kill that process.  We'll wait around
		     * until then.  Our parent has already left, leaving us
		     * temporarily orphaned.
		     */
		    if (pid > 0) {	/* parent */
			fprintf(stderr, "%d\n", pid);
			fflush(stderr);
		    }
		    /* wait for child */
#ifdef HAVE_WAITPID
		    while (-1 == waitpid(pid, &wstatus, 0)) {
#ifdef EINTR
			if (errno == EINTR)
			    continue;
#endif /* EINTR */
#ifdef ERESTARTSYS
			if (errno == ERESTARTSYS)
			    continue;
#endif /* ERESTARTSYS */
			break;
		    }
#else
		    while (wait(&wstatus) != pid)	/* do nothing */
			;
#endif
		    _exit(WEXITSTATUS(wstatus));
		} else if (pid == 0) {
		    if (!dialog_vars.cant_kill)
			(void) signal(SIGHUP, finish_bg);
		    (void) signal(SIGINT, finish_bg);
		    (void) signal(SIGQUIT, finish_bg);
		    while (dialog_state.getc_callbacks != 0) {
			int fkey = 0;
			dlg_getc_callbacks(ERR, fkey, retval);
			napms(1000);
		    }
		}
	    }
	}
    }
}

--- NEW FILE: VERSION ---
4:2:0	1.0	20060221

--- NEW FILE: config.hin ---
/*
 * The configure script expands this as a set of definitions
 */
@DEFS@

--- NEW FILE: textbox.c ---
/*
 *  $Id: textbox.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  textbox.c -- implements the text box
 *
 *  Copyright 2000-2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors:
 *	Savio Lam (lam836 at cs.cuhk.hk)
 */

#include <dialog.h>
#include <dlg_keys.h>

#define PAGE_LENGTH	(height - 4)
#define PAGE_WIDTH	(width - 2)

typedef struct {
    DIALOG_CALLBACK obj;
    WINDOW *text;
    const char **buttons;
    int hscroll;
    char line[MAX_LEN + 1];
    int fd;
    long file_size;
    long fd_bytes_read;
    long bytes_read;
    long buffer_len;
    bool begin_reached;
    bool buffer_first;
    bool end_reached;
    long page_length;		/* lines on the page which is shown */
    long in_buf;		/* ending index into buf[] for page */
    char *buf;
} MY_OBJ;

static long
lseek_obj(MY_OBJ * obj, long offset, int mode)
{
    long fpos;
    if ((fpos = lseek(obj->fd, offset, mode)) == -1) {
	switch (mode) {
	case SEEK_CUR:
	    dlg_exiterr("Cannot get file position");
	    break;
	case SEEK_END:
	    dlg_exiterr("Cannot seek to end of file");
	    break;
	case SEEK_SET:
	    dlg_exiterr("Cannot set file position to %ld", offset);
	    break;
	}
    }
    return fpos;
}

static long
ftell_obj(MY_OBJ * obj)
{
    return lseek_obj(obj, 0, SEEK_CUR);
}

static char *
xalloc(long size)
{
    char *result = malloc(size);
    assert_ptr(result, "xalloc");
    return result;
}

/*
 * read_high() substitutes read() for tab->spaces conversion
 *
 * buffer_len, fd_bytes_read, bytes_read are modified
 * buf is allocated
 *
 * fd_bytes_read is the effective number of bytes read from file
 * bytes_read is the length of buf, that can be different if tab_correct
 */
static void
read_high(MY_OBJ * obj, size_t size_read)
{
    char *buftab, ch;
    int i = 0, j, n, tmpint;
    long begin_line;

    /* Allocate space for read buffer */
    buftab = xalloc(size_read + 1);

    if ((obj->fd_bytes_read = read(obj->fd, buftab, size_read)) != -1) {

	buftab[obj->fd_bytes_read] = '\0';	/* mark end of valid data */

	if (dialog_vars.tab_correct) {

	    /* calculate bytes_read by buftab and fd_bytes_read */
	    obj->bytes_read = begin_line = 0;
	    for (j = 0; j < obj->fd_bytes_read; j++)
		if (buftab[j] == TAB)
		    obj->bytes_read += dialog_state.tab_len
			- ((obj->bytes_read - begin_line)
			   % dialog_state.tab_len);
		else if (buftab[j] == '\n') {
		    obj->bytes_read++;
		    begin_line = obj->bytes_read;
		} else
		    obj->bytes_read++;

	    if (obj->bytes_read > obj->buffer_len) {
		if (obj->buffer_first)
		    obj->buffer_first = FALSE;	/* disp = 0 */
		else {
		    free(obj->buf);
		}

		obj->buffer_len = obj->bytes_read;

		/* Allocate space for read buffer */
		obj->buf = xalloc(obj->buffer_len + 1);
	    }

	} else {
	    if (obj->buffer_first) {
		obj->buffer_first = FALSE;

		/* Allocate space for read buffer */
		obj->buf = xalloc(size_read + 1);
	    }

	    obj->bytes_read = obj->fd_bytes_read;
	}

	j = 0;
	begin_line = 0;
	while (j < obj->fd_bytes_read)
	    if (((ch = buftab[j++]) == TAB) && (dialog_vars.tab_correct != 0)) {
		tmpint = (dialog_state.tab_len
			  - ((int) ((long) i - begin_line) % dialog_state.tab_len));
		for (n = 0; n < tmpint; n++)
		    obj->buf[i++] = ' ';
	    } else {
		if (ch == '\n')
		    begin_line = i + 1;
		obj->buf[i++] = ch;
	    }

	obj->buf[i] = '\0';	/* mark end of valid data */

    }
    if (obj->bytes_read == -1)
	dlg_exiterr("Error reading file");
    free(buftab);
}

static long
find_first(MY_OBJ * obj, char *buffer, long length)
{
    long recount = obj->page_length;
    long result = 0;

    while (length > 0) {
	if (buffer[length] == '\n') {
	    if (--recount < 0) {
		result = length;
		break;
	    }
	}
	--length;
    }
    return result;
}

static long
tabize(MY_OBJ * obj, long val, long *first_pos)
{
    long fpos;
    long i, count, begin_line;
    char *buftab;

    if (!dialog_vars.tab_correct)
	return val;

    fpos = ftell_obj(obj);

    lseek_obj(obj, fpos - obj->fd_bytes_read, SEEK_SET);

    /* Allocate space for read buffer */
    buftab = xalloc(val + 1);

    if ((read(obj->fd, buftab, val)) == -1)
	dlg_exiterr("Error reading file in tabize().");

    begin_line = count = 0;
    if (first_pos != 0)
	*first_pos = 0;

    for (i = 0; i < val; i++) {
	if ((first_pos != 0) && (count >= val)) {
	    *first_pos = find_first(obj, buftab, i);
	    break;
	}
	if (buftab[i] == TAB)
	    count += dialog_state.tab_len
		- ((count - begin_line) % dialog_state.tab_len);
	else if (buftab[i] == '\n') {
	    count++;
	    begin_line = count;
	} else
	    count++;
    }

    lseek_obj(obj, fpos, SEEK_SET);
    free(buftab);
    return count;
}
/*
 * Return current line of text.
 * 'page' should point to start of current line before calling, and will be
 * updated to point to start of next line.
 */
static char *
get_line(MY_OBJ * obj)
{
    int i = 0;
    long fpos;

    obj->end_reached = FALSE;
    while (obj->buf[obj->in_buf] != '\n') {
	if (obj->buf[obj->in_buf] == '\0') {	/* Either end of file or end of buffer reached */
	    fpos = ftell_obj(obj);

	    if (fpos < obj->file_size) {	/* Not end of file yet */
		/* We've reached end of buffer, but not end of file yet, so
		 * read next part of file into buffer
		 */
		read_high(obj, BUF_SIZE);
		obj->in_buf = 0;
	    } else {
		if (!obj->end_reached)
		    obj->end_reached = TRUE;
		break;
	    }
	} else if (i < MAX_LEN)
	    obj->line[i++] = obj->buf[obj->in_buf++];
	else {
	    if (i == MAX_LEN)	/* Truncate lines longer than MAX_LEN characters */
		obj->line[i++] = '\0';
	    obj->in_buf++;
	}
    }
    if (i <= MAX_LEN)
	obj->line[i] = '\0';
    if (!obj->end_reached)
	obj->in_buf++;		/* move past '\n' */

    return obj->line;
}

static bool
match_string(MY_OBJ * obj, char *string)
{
    char *match = get_line(obj);
    return strstr(match, string) != 0;
}

/*
 * Go back 'n' lines in text file. Called by dialog_textbox().
 * 'in_buf' will be updated to point to the desired line in 'buf'.
 */
static void
back_lines(MY_OBJ * obj, long n)
{
    int i;
    long fpos;
    long val_to_tabize;

    obj->begin_reached = FALSE;
    /* We have to distinguish between end_reached and !end_reached since at end
       * of file, the line is not ended by a '\n'.  The code inside 'if'
       * basically does a '--in_buf' to move one character backward so as to
       * skip '\n' of the previous line */
    if (!obj->end_reached) {
	/* Either beginning of buffer or beginning of file reached? */

	if (obj->in_buf == 0) {
	    fpos = ftell_obj(obj);

	    if (fpos > obj->fd_bytes_read) {	/* Not beginning of file yet */
		/* We've reached beginning of buffer, but not beginning of file
		 * yet, so read previous part of file into buffer.  Note that
		 * we only move backward for BUF_SIZE/2 bytes, but not BUF_SIZE
		 * bytes to avoid re-reading again in print_page() later
		 */
		/* Really possible to move backward BUF_SIZE/2 bytes? */
		if (fpos < BUF_SIZE / 2 + obj->fd_bytes_read) {
		    /* No, move less than */
		    lseek_obj(obj, 0, SEEK_SET);
		    val_to_tabize = fpos - obj->fd_bytes_read;
		} else {	/* Move backward BUF_SIZE/2 bytes */
		    lseek_obj(obj, -(BUF_SIZE / 2 + obj->fd_bytes_read), SEEK_CUR);
		    val_to_tabize = BUF_SIZE / 2;
		}
		read_high(obj, BUF_SIZE);

		obj->in_buf = tabize(obj, val_to_tabize, (long *) 0);

	    } else {		/* Beginning of file reached */
		obj->begin_reached = TRUE;
		return;
	    }
	}
	obj->in_buf--;
	if (obj->buf[obj->in_buf] != '\n')
	    /* Something's wrong... */
	    dlg_exiterr("Internal error in back_lines().");
    }

    /* Go back 'n' lines */
    for (i = 0; i < n; i++) {
	do {
	    if (obj->in_buf == 0) {
		fpos = ftell_obj(obj);

		if (fpos > obj->fd_bytes_read) {
		    /* Really possible to move backward BUF_SIZE/2 bytes? */
		    if (fpos < BUF_SIZE / 2 + obj->fd_bytes_read) {
			/* No, move less than */
			lseek_obj(obj, 0, SEEK_SET);
			val_to_tabize = fpos - obj->fd_bytes_read;
		    } else {	/* Move backward BUF_SIZE/2 bytes */
			lseek_obj(obj, -(BUF_SIZE / 2 + obj->fd_bytes_read), SEEK_CUR);
			val_to_tabize = BUF_SIZE / 2;
		    }
		    read_high(obj, BUF_SIZE);

		    obj->in_buf = tabize(obj, val_to_tabize, (long *) 0);

		} else {	/* Beginning of file reached */
		    obj->begin_reached = TRUE;
		    return;
		}
	    }
	} while (obj->buf[--(obj->in_buf)] != '\n');
    }
    obj->in_buf++;
}

/*
 * Print a new line of text.
 */
static void
print_line(MY_OBJ * obj, int row, int width)
{
    int i, y, x;
    char *line = get_line(obj);
    const int *cols = dlg_index_columns(line);
    const int *indx = dlg_index_wchars(line);
    int limit = dlg_count_wchars(line);
    int first = 0;
    int last = limit;

    for (i = 0; i <= limit && cols[i] < obj->hscroll; ++i)
	first = i;

    for (i = first; i <= limit && cols[i] - cols[first] <= PAGE_WIDTH; ++i)
	last = i;

    (void) wmove(obj->text, row, 0);	/* move cursor to correct line */
    (void) waddch(obj->text, ' ');
    (void) waddnstr(obj->text, line + indx[first], indx[last] - indx[first]);

    getyx(obj->text, y, x);
    /* Clear 'residue' of previous line */
    for (i = 0; i < width - x; i++)
	(void) waddch(obj->text, ' ');
}

/*
 * Print a new page of text.
 */
static void
print_page(MY_OBJ * obj, int height, int width)
{
    int i, passed_end = 0;

    obj->page_length = 0;
    for (i = 0; i < height; i++) {
	print_line(obj, i, width);
	if (!passed_end)
	    obj->page_length++;
	if (obj->end_reached && !passed_end)
	    passed_end = 1;
    }
    (void) wnoutrefresh(obj->text);
}

/*
 * Print current position
 */
static void
print_position(MY_OBJ * obj, WINDOW *win, int height, int width)
{
    chtype save = getattrs(win);
    long fpos;
    long size;
    int percent;
    int len;
    long first = -1;
    char buffer[80];

    fpos = ftell_obj(obj);
    if (dialog_vars.tab_correct)
	size = tabize(obj, obj->in_buf, &first);
    else
	first = find_first(obj, obj->buf, size = obj->in_buf);

    wattrset(win, position_indicator_attr);
    percent = (!obj->file_size
	       ? 100
	       : (int) (((fpos - obj->fd_bytes_read + size) * 100)
			/ obj->file_size));

    if (percent < 0)
	percent = 0;
    if (percent > 100)
	percent = 100;

    (void) sprintf(buffer, "%d%%", percent);
    (void) wmove(win, PAGE_LENGTH + 1, PAGE_WIDTH - 7);
    (void) waddstr(win, buffer);
    if ((len = dlg_count_columns(buffer)) < 4) {
	wattrset(win, border_attr);
	whline(win, ACS_HLINE, 4 - len);
    }

    wattrset(win, save);
    dlg_draw_arrows2(win,
		     first != 0,
		     size < obj->fd_bytes_read,
		     5, 0, PAGE_LENGTH + 1,
		     border_attr,
		     border_attr);
}

/*
 * Display a dialog box and get the search term from user.
 */
static int
get_search_term(WINDOW *dialog, char *input, int height, int width)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	INPUTSTR_BINDINGS,
	ENTERKEY_BINDINGS,
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

    int old_x, old_y;
    int box_x, box_y;
    int box_height, box_width;
    int offset = 0;
    int key = 0;
    int fkey = 0;
    bool first = TRUE;
    int result = DLG_EXIT_UNKNOWN;
    char *caption = _("Search");
    int len_caption = dlg_count_columns(caption);
    const int *indx;
    int limit;
    WINDOW *widget;

    getbegyx(dialog, old_y, old_x);

    box_height = 1 + (2 * MARGIN);
    box_width = len_caption + (2 * (MARGIN + 2));
    box_width = MAX(box_width, 30);
    box_width = MIN(box_width, getmaxx(dialog) - 2 * MARGIN);
    len_caption = MIN(len_caption, box_width - (2 * (MARGIN + 1)));

    box_x = (width - box_width) / 2;
    box_y = (height - box_height) / 2;
#ifdef HAVE_COLOR
    if (dialog_state.use_shadow)
	dlg_draw_shadow(dialog, box_y, box_x, box_height, box_width);
#endif
    widget = newwin(box_height, box_width, old_y + box_y, old_x + box_x);
    keypad(widget, TRUE);
    dlg_register_window(widget, "searchbox", binding);

    dlg_draw_box(widget, 0, 0, box_height, box_width,
		 searchbox_attr,
		 searchbox_border_attr);
    wattrset(widget, searchbox_title_attr);
    (void) wmove(widget, 0, (box_width - len_caption) / 2);

    indx = dlg_index_wchars(caption);
    limit = dlg_limit_columns(caption, len_caption, 0);
    (void) waddnstr(widget, caption + indx[0], indx[limit] - indx[0]);

    box_y++;
    box_x++;
    box_width -= 2;
    input[0] = '\0';

    for (;;) {
	if (!first) {
	    key = dlg_getc(widget, &fkey);
	    if (key == ESC) {
		result = DLG_EXIT_ESC;
		break;
	    } else if (key == DLGK_ENTER) {
		result = DLG_EXIT_OK;
		break;
	    }
	}
	if (dlg_edit_string(input, &offset, key, fkey, first)) {
	    dlg_show_string(widget, input, offset, searchbox_attr,
			    1, 1, box_width, FALSE, first);
	    first = FALSE;
	}
    }
    delwin(widget);
    return result;
}

static bool
perform_search(MY_OBJ * obj, int height, int width, int key, char *search_term)
{
    int dir;
    long tempinx;
    long fpos;
    bool found;
    bool temp, temp1;
    bool moved = FALSE;

    /* set search direction */
    dir = (key == '/' || key == 'n') ? 1 : 0;
    if (dir ? !obj->end_reached : !obj->begin_reached) {
	if (key == 'n' || key == 'N') {
	    if (search_term[0] == '\0') {	/* No search term yet */
		(void) beep();
		return FALSE;
	    }
	    /* Get search term from user */
	} else if (get_search_term(obj->text, search_term,
				   PAGE_LENGTH,
				   PAGE_WIDTH) == DLG_EXIT_ESC
		   || search_term[0] == '\0') {
	    /* ESC pressed, or no search term, reprint page to clear box */
	    wattrset(obj->text, dialog_attr);
	    back_lines(obj, obj->page_length);
	    return TRUE;
	}
	/* Save variables for restoring in case search term can't be found */
	tempinx = obj->in_buf;
	temp = obj->begin_reached;
	temp1 = obj->end_reached;
	fpos = ftell_obj(obj) - obj->fd_bytes_read;
	/* update 'in_buf' to point to next (previous) line before
	   forward (backward) searching */
	back_lines(obj, (dir
			 ? obj->page_length - 1
			 : obj->page_length + 1));
	found = FALSE;
	if (dir) {		/* Forward search */
	    while ((found = match_string(obj, search_term)) == FALSE) {
		if (obj->end_reached)
		    break;
	    }
	} else {		/* Backward search */
	    while ((found = match_string(obj, search_term)) == FALSE) {
		if (obj->begin_reached)
		    break;
		back_lines(obj, 2);
	    }
	}
	if (found == FALSE) {	/* not found */
	    (void) beep();
	    /* Restore program state to that before searching */
	    lseek_obj(obj, fpos, SEEK_SET);

	    read_high(obj, BUF_SIZE);

	    obj->in_buf = tempinx;
	    obj->begin_reached = temp;
	    obj->end_reached = temp1;
	    /* move 'in_buf' to point to start of current page to
	     * re-print current page.  Note that 'in_buf' always points
	     * to start of next page, so this is necessary
	     */
	    back_lines(obj, obj->page_length);
	} else {		/* Search term found */
	    back_lines(obj, 1);
	}
	/* Reprint page */
	wattrset(obj->text, dialog_attr);
	moved = TRUE;
    } else {			/* no need to find */
	(void) beep();
    }
    return moved;
}

/*
 * Display text from a file in a dialog box.
 */
int
dialog_textbox(const char *title, const char *file, int height, int width)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_GRID_DOWN,  'J' ),
	DLG_KEYS_DATA( DLGK_GRID_DOWN,  'j' ),
	DLG_KEYS_DATA( DLGK_GRID_DOWN,  KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,  'H' ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,  'h' ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,  KEY_LEFT ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'L' ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'l' ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_GRID_UP,    'K' ),
	DLG_KEYS_DATA( DLGK_GRID_UP,    'k' ),
	DLG_KEYS_DATA( DLGK_GRID_UP,    KEY_UP ),
	DLG_KEYS_DATA( DLGK_PAGE_FIRST, 'g' ),
	DLG_KEYS_DATA( DLGK_PAGE_FIRST, KEY_HOME ),
	DLG_KEYS_DATA( DLGK_PAGE_LAST,  'G' ),
	DLG_KEYS_DATA( DLGK_PAGE_LAST,  KEY_END ),
	DLG_KEYS_DATA( DLGK_PAGE_LAST,  KEY_LL ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,  ' ' ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,  KEY_NPAGE ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,  'B' ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,  'b' ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,  KEY_PPAGE ),
	DLG_KEYS_DATA( DLGK_BEGIN,	'0' ),
	DLG_KEYS_DATA( DLGK_BEGIN,	KEY_BEG ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif
    long fpos;
    int x, y, cur_x, cur_y;
    int key = 0, fkey;
    int next = 0;
    int i, code, passed_end;
    char search_term[MAX_LEN + 1];
    MY_OBJ obj;
    WINDOW *dialog;
    bool moved;
    int result = DLG_EXIT_UNKNOWN;
    int button = dialog_vars.extra_button ? dlg_defaultno_button() : 0;

    search_term[0] = '\0';	/* no search term entered yet */

    memset(&obj, 0, sizeof(obj));

    obj.begin_reached = TRUE;
    obj.buffer_first = TRUE;
    obj.end_reached = FALSE;
    obj.buttons = dlg_exit_label();

    /* Open input file for reading */
    if ((obj.fd = open(file, O_RDONLY)) == -1)
	dlg_exiterr("Can't open input file %s", file);

    /* Get file size. Actually, 'file_size' is the real file size - 1,
       since it's only the last byte offset from the beginning */
    obj.file_size = lseek_obj(&obj, 0, SEEK_END);

    /* Restore file pointer to beginning of file after getting file size */
    lseek_obj(&obj, 0, SEEK_SET);

    read_high(&obj, BUF_SIZE);

#ifdef KEY_RESIZE
  retry:
#endif
    moved = TRUE;

    dlg_auto_sizefile(title, file, &height, &width, 2, 12);
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);

    dialog = dlg_new_window(height, width, y, x);
    dlg_register_window(dialog, "textbox", binding);
    dlg_register_buttons(dialog, "textbox", obj.buttons);

    dlg_mouse_setbase(x, y);

    /* Create window for text region, used for scrolling text */
    obj.text = dlg_sub_window(dialog, PAGE_LENGTH, PAGE_WIDTH, y + 1, x + 1);

    /* register the new window, along with its borders */
    dlg_mouse_mkbigregion(0, 0, PAGE_LENGTH + 2, width, KEY_MAX, 1, 1, 1 /* lines */ );
    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_bottom_box(dialog);
    dlg_draw_title(dialog, title);

    dlg_draw_buttons(dialog, PAGE_LENGTH + 2, 0, obj.buttons, button, FALSE, width);
    (void) wnoutrefresh(dialog);
    getyx(dialog, cur_y, cur_x);	/* Save cursor position */

    dlg_attr_clear(obj.text, PAGE_LENGTH, PAGE_WIDTH, dialog_attr);

    while (result == DLG_EXIT_UNKNOWN) {

	/*
	 * Update the screen according to whether we shifted up/down by a line
	 * or not.
	 */
	if (moved) {
	    if (next < 0) {
		(void) scrollok(obj.text, TRUE);
		(void) scroll(obj.text);	/* Scroll text region up one line */
		(void) scrollok(obj.text, FALSE);
		print_line(&obj, PAGE_LENGTH - 1, PAGE_WIDTH);
		(void) wnoutrefresh(obj.text);
	    } else if (next > 0) {
		/*
		 * We don't call print_page() here but use scrolling to ensure
		 * faster screen update.  However, 'end_reached' and
		 * 'page_length' should still be updated, and 'in_buf' should
		 * point to start of next page.  This is done by calling
		 * get_line() in the following 'for' loop.
		 */
		(void) scrollok(obj.text, TRUE);
		(void) wscrl(obj.text, -1);	/* Scroll text region down one line */
		(void) scrollok(obj.text, FALSE);
		obj.page_length = 0;
		passed_end = 0;
		for (i = 0; i < PAGE_LENGTH; i++) {
		    if (!i) {
			print_line(&obj, 0, PAGE_WIDTH);	/* print first line of page */
			(void) wnoutrefresh(obj.text);
		    } else
			(void) get_line(&obj);	/* Called to update 'end_reached' and 'in_buf' */
		    if (!passed_end)
			obj.page_length++;
		    if (obj.end_reached && !passed_end)
			passed_end = 1;
		}
	    } else {
		print_page(&obj, PAGE_LENGTH, PAGE_WIDTH);
	    }
	    print_position(&obj, dialog, height, width);
	    (void) wmove(dialog, cur_y, cur_x);		/* Restore cursor position */
	    wrefresh(dialog);
	}
	moved = FALSE;		/* assume we'll not move */
	next = 0;		/* ...but not scroll by a line */

	key = dlg_mouse_wgetch(dialog, &fkey);
	if (dlg_result_key(key, fkey, &result))
	    break;

	if (!fkey && (code = dlg_char_to_button(key, obj.buttons)) >= 0) {
	    result = dlg_ok_buttoncode(code);
	    break;
	}

	if (fkey) {
	    switch (key) {
	    default:
		if (is_DLGK_MOUSE(key)) {
		    result = dlg_exit_buttoncode(key - M_EVENT);
		    if (result < 0)
			result = DLG_EXIT_OK;
		} else {
		    beep();
		}
		break;
	    case DLGK_FIELD_NEXT:
		button = dlg_next_button(obj.buttons, button);
		if (button < 0)
		    button = 0;
		dlg_draw_buttons(dialog,
				 height - 2, 0,
				 obj.buttons, button,
				 FALSE, width);
		break;
	    case DLGK_FIELD_PREV:
		button = dlg_prev_button(obj.buttons, button);
		if (button < 0)
		    button = 0;
		dlg_draw_buttons(dialog,
				 height - 2, 0,
				 obj.buttons, button,
				 FALSE, width);
		break;
	    case DLGK_ENTER:
		result = dlg_exit_buttoncode(button);
		break;
	    case DLGK_PAGE_FIRST:
		if (!obj.begin_reached) {
		    obj.begin_reached = 1;
		    /* First page not in buffer? */
		    fpos = ftell_obj(&obj);

		    if (fpos > obj.fd_bytes_read) {
			/* Yes, we have to read it in */
			lseek_obj(&obj, 0, SEEK_SET);

			read_high(&obj, BUF_SIZE);
		    }
		    obj.in_buf = 0;
		    moved = TRUE;
		}
		break;
	    case DLGK_PAGE_LAST:
		obj.end_reached = TRUE;
		/* Last page not in buffer? */
		fpos = ftell_obj(&obj);

		if (fpos < obj.file_size) {
		    /* Yes, we have to read it in */
		    lseek_obj(&obj, -BUF_SIZE, SEEK_END);

		    read_high(&obj, BUF_SIZE);
		}
		obj.in_buf = obj.bytes_read;
		back_lines(&obj, PAGE_LENGTH);
		moved = TRUE;
		break;
	    case DLGK_GRID_UP:	/* Previous line */
		if (!obj.begin_reached) {
		    back_lines(&obj, obj.page_length + 1);
		    next = 1;
		    moved = TRUE;
		}
		break;
	    case DLGK_PAGE_PREV:	/* Previous page */
	    case DLGK_MOUSE(KEY_PPAGE):
		if (!obj.begin_reached) {
		    back_lines(&obj, obj.page_length + PAGE_LENGTH);
		    moved = TRUE;
		}
		break;
	    case DLGK_GRID_DOWN:	/* Next line */
		if (!obj.end_reached) {
		    obj.begin_reached = 0;
		    next = -1;
		    moved = TRUE;
		}
		break;
	    case DLGK_PAGE_NEXT:	/* Next page */
	    case DLGK_MOUSE(KEY_NPAGE):
		if (!obj.end_reached) {
		    obj.begin_reached = 0;
		    moved = TRUE;
		}
		break;
	    case DLGK_BEGIN:	/* Beginning of line */
		if (obj.hscroll > 0) {
		    obj.hscroll = 0;
		    /* Reprint current page to scroll horizontally */
		    back_lines(&obj, obj.page_length);
		    moved = TRUE;
		}
		break;
	    case DLGK_GRID_LEFT:	/* Scroll left */
		if (obj.hscroll > 0) {
		    obj.hscroll--;
		    /* Reprint current page to scroll horizontally */
		    back_lines(&obj, obj.page_length);
		    moved = TRUE;
		}
		break;
	    case DLGK_GRID_RIGHT:	/* Scroll right */
		if (obj.hscroll < MAX_LEN) {
		    obj.hscroll++;
		    /* Reprint current page to scroll horizontally */
		    back_lines(&obj, obj.page_length);
		    moved = TRUE;
		}
		break;
#ifdef KEY_RESIZE
	    case KEY_RESIZE:
		/* reset data */
		height = old_height;
		width = old_width;
		back_lines(&obj, obj.page_length);
		moved = TRUE;
		/* repaint */
		dlg_clear();
		dlg_del_window(dialog);
		refresh();
		dlg_mouse_free_regions();
		goto retry;
#endif
	    }
	} else {
	    switch (key) {
	    case '/':		/* Forward search */
	    case 'n':		/* Repeat forward search */
	    case '?':		/* Backward search */
	    case 'N':		/* Repeat backward search */
		moved = perform_search(&obj, height, width, key, search_term);
		fkey = FALSE;
		break;
	    default:
		beep();
		break;
	    }
	}
    }

    dlg_del_window(dialog);
    free(obj.buf);
    (void) close(obj.fd);
    dlg_mouse_free_regions();
    return result;
}

--- NEW FILE: guage.c ---
/*
 *  $Id: guage.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  guage.c -- implements the gauge dialog
 *
 *  Copyright 2000-2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors
 *	Marc Ewing, Red Hat Software
 */

#include <dialog.h>
#include <errno.h>

#define MY_LEN (MAX_LEN)/2

#define MIN_HIGH (4)
#define MIN_WIDE (10 + 2 * (2 + MARGIN))

#define isMarker(buf) !strncmp(buf, "XXX", 3)

#define if_FINISH(status,statement) if ((status) == 0) statement

#ifdef KEY_RESIZE
#define if_RESIZE(status,statement) if ((status) < 0) statement
#else
#define if_RESIZE(status,statement)	/* nothing */
#endif

static int
read_data(char *buffer, FILE *fp)
{
    int result;

    if (feof(fp)) {
	result = 0;
    } else if (fgets(buffer, MY_LEN, fp) != 0) {
	dlg_trim_string(buffer);
	result = 1;
    } else {
	result = -1;
#ifdef KEY_RESIZE
	/*
	 * Since we weren't doing getch's before, we won't necessarily
	 * have a KEY_RESIZE returned.  Call getch() anyway to force it
	 * to call resizeterm as-needed.
	 */
	if (errno == EINTR) {
	    (void) getch();	/* allow it to call resizeterm() */
	    refresh();
	    dlg_clear();
	    (void) getch();
	} else {
	    result = 0;
	}
#endif
    }
    return result;
}

static int
decode_percent(char *buffer)
{
    char *tmp = 0;
    long value = strtol(buffer, &tmp, 10);

    if (tmp != 0 && (*tmp == 0 || isspace(UCH(*tmp))) && value >= 0) {
	return TRUE;
    }
    return FALSE;
}

/*
 * Display a gauge, or progress meter.  Starts at percent% and reads stdin.  If
 * stdin is not XXX, then it is interpreted as a percentage, and the display is
 * updated accordingly.  Otherwise the next line is the percentage, and
 * subsequent lines up to another XXX are used for the new prompt.  Note that
 * the size of the window never changes, so the prompt can not get any larger
 * than the height and width specified.
 */
int
dialog_gauge(const char *title,
	     const char *cprompt,
	     int height,
	     int width,
	     int percent)
{
#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif
    int i, x, y;
    int status;
    char buf[MY_LEN];
    char prompt_buf[MY_LEN];
    char *prompt = dlg_strclone(cprompt);
    WINDOW *dialog;

    curs_set(0);

    dlg_tab_correct_str(prompt);

#ifdef KEY_RESIZE
    nodelay(stdscr, TRUE);
    goto first_try;
  retry:
    dlg_del_window(dialog);
    height = old_height;
    width = old_width;
  first_try:
#endif

    dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, MIN_WIDE);
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    /* center dialog box on screen */
    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);

    dialog = dlg_new_window(height, width, y, x);

    do {
	(void) werase(dialog);
	dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);

	dlg_draw_title(dialog, title);

	wattrset(dialog, dialog_attr);
	dlg_print_autowrap(dialog, prompt, height, width);

	dlg_draw_box(dialog,
		     height - 4, 2 + MARGIN,
		     2 + MARGIN, width - 2 * (2 + MARGIN),
		     dialog_attr,
		     border_attr);

	/*
	 * Clear the area for the progress bar by filling it with spaces
	 * in the title-attribute, and write the percentage with that
	 * attribute.
	 */
	(void) wmove(dialog, height - 3, 4);
	wattrset(dialog, title_attr);

	for (i = 0; i < (width - 2 * (3 + MARGIN)); i++)
	    (void) waddch(dialog, ' ');

	(void) wmove(dialog, height - 3, (width / 2) - 2);
	(void) wprintw(dialog, "%3d%%", percent);

	/*
	 * Now draw a bar in reverse, relative to the background.
	 * The window attribute was useful for painting the background,
	 * but requires some tweaks to reverse it.
	 */
	x = (percent * (width - 2 * (3 + MARGIN))) / 100;
	if ((title_attr & A_REVERSE) != 0) {
	    wattroff(dialog, A_REVERSE);
	} else {
	    wattrset(dialog, A_REVERSE);
	}
	(void) wmove(dialog, height - 3, 4);
	for (i = 0; i < x; i++) {
	    chtype ch = winch(dialog);
	    if (title_attr & A_REVERSE) {
		ch &= ~A_REVERSE;
	    }
	    (void) waddch(dialog, ch);
	}

	(void) wrefresh(dialog);

	status = read_data(buf, dialog_state.pipe_input);
	if_FINISH(status, break);
	if_RESIZE(status, goto retry);

	if (isMarker(buf)) {
	    /*
	     * Historically, next line should be percentage, but one of the
	     * worse-written clones of 'dialog' assumes the number is missing.
	     * (Gresham's Law applied to software).
	     */
	    status = read_data(buf, dialog_state.pipe_input);
	    if_FINISH(status, break);
	    if_RESIZE(status, goto retry);

	    prompt_buf[0] = '\0';
	    if (decode_percent(buf))
		percent = atoi(buf);
	    else
		strcpy(prompt_buf, buf);

	    /* Rest is message text */
	    while ((status = read_data(buf, dialog_state.pipe_input)) > 0
		   && !isMarker(buf)) {
		if (strlen(prompt_buf) + strlen(buf) < sizeof(prompt_buf) - 1) {
		    strcat(prompt_buf, buf);
		}
	    }
	    if_FINISH(status, break);
	    if_RESIZE(status, goto retry);

	    if (prompt != prompt_buf)
		free(prompt);
	    prompt = prompt_buf;
	} else if (decode_percent(buf)) {
	    percent = atoi(buf);
	}
    } while (1);

#ifdef KEY_RESIZE
    nodelay(stdscr, FALSE);
#endif
    fclose(dialog_state.pipe_input);
    curs_set(1);
    dlg_del_window(dialog);
    if (prompt != prompt_buf)
	free(prompt);
    return (DLG_EXIT_OK);
}

--- NEW FILE: formbox.c ---
/*
 *  $Id: formbox.c,v 1.1 2006-10-23 20:59:26 stsp Exp $
 *
 *  formbox.c -- implements the form (i.e, some pairs label/editbox)
 *
 *  Copyright 2003-2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  This is adapted from source contributed by
 *	Valery Reznic (valery_reznic at users.sourceforge.net)
 */

#include <dialog.h>
#include <dlg_keys.h>

#define LLEN(n) ((n) * FORMBOX_TAGS)

#define ItemName(i)     items[LLEN(i) + 0]
#define ItemNameY(i)    items[LLEN(i) + 1]
#define ItemNameX(i)    items[LLEN(i) + 2]
#define ItemText(i)     items[LLEN(i) + 3]
#define ItemTextY(i)    items[LLEN(i) + 4]
#define ItemTextX(i)    items[LLEN(i) + 5]
#define ItemTextFLen(i) items[LLEN(i) + 6]
#define ItemTextILen(i) items[LLEN(i) + 7]
#define ItemHelp(i)     (dialog_vars.item_help ? items[LLEN(i) + 8] : "")

#define isPassword(item) ((item)->type == 1)

static bool
in_window(WINDOW *win, int scrollamt, int y)
{
    return (y >= scrollamt && y - scrollamt < getmaxy(win));
}

static bool
ok_move(WINDOW *win, int scrollamt, int y, int x)
{
    return in_window(win, scrollamt, y)
	&& (wmove(win, y - scrollamt, x) != ERR);
}

static void
move_past(WINDOW *win, int y, int x)
{
    if (wmove(win, y, x) == ERR)
	wmove(win, y, getmaxx(win) - 1);
}

/*
 * Print form item
 */
static int
print_item(WINDOW *win, DIALOG_FORMITEM * item, int scrollamt, bool choice)
{
    int count = 0;
    int len;

    if (ok_move(win, scrollamt, item->name_y, item->name_x)) {
	len = item->name_len;
	len = MIN(len, getmaxx(win) - item->name_x);
	if (len > 0) {
	    dlg_show_string(win,
			    item->name,
			    0,
			    menubox_attr,
			    item->name_y - scrollamt,
			    item->name_x,
			    len,
			    FALSE,
			    FALSE);
	    move_past(win, item->name_y - scrollamt, item->name_x + len);
	    count = 1;
	}
    }
    if (item->text_len && ok_move(win, scrollamt, item->text_y, item->text_x)) {
	len = item->text_len;
	len = MIN(len, getmaxx(win) - item->text_x);
	if (len > 0) {
	    dlg_show_string(win,
			    item->text,
			    0,
			    choice ? form_active_text_attr : form_text_attr,
			    item->text_y - scrollamt,
			    item->text_x,
			    len,
			    isPassword(item),
			    FALSE);
	    move_past(win, item->text_y - scrollamt, item->text_x + len);
	    count = 1;
	}
    }
    return count;
}

/*
 * Print the entire form.
 */
static void
print_form(WINDOW *win, DIALOG_FORMITEM * item, int total, int scrollamt, int choice)
{
    int n;
    int count = 0;

    for (n = 0; n < total; ++n) {
	count += print_item(win, item + n, scrollamt, n == choice);
    }
    if (count) {
	wbkgdset(win, menubox_attr | ' ');
	wclrtobot(win);
	(void) wnoutrefresh(win);
    }
}

static int
set_choice(DIALOG_FORMITEM item[], int choice, int item_no)
{
    int i;
    if (item[choice].text_flen > 0)
	return choice;
    for (i = 0; i < item_no; i++) {
	if (item[i].text_flen > 0)
	    return i;
    }
    dlg_exiterr("No field has flen > 0\n");
    return -1;			/* Dummy, to make compiler happy */
}

/*
 * Find the last y-value in the form.
 */
static int
form_limit(DIALOG_FORMITEM item[])
{
    int n;
    int limit = 0;
    for (n = 0; item[n].name != 0; ++n) {
	if (limit < item[n].name_y)
	    limit = item[n].name_y;
	if (limit < item[n].text_y)
	    limit = item[n].text_y;
    }
    return limit;
}

/*
 * Tab to the next field.
 */
static bool
tab_next(WINDOW *win,
	 DIALOG_FORMITEM item[],
	 int item_no,
	 int stepsize,
	 int *choice,
	 int *scrollamt)
{
    int old_choice = *choice;
    int old_scroll = *scrollamt;
    bool wrapped = FALSE;

    do {
	*choice += stepsize;
	if (*choice < 0) {
	    *choice = item_no - 1;
	    wrapped = TRUE;
	}
	if (*choice >= item_no) {
	    *choice = 0;
	    wrapped = TRUE;
	}
	if (item[*choice].text_flen > 0) {
	    int lo = MIN(item[*choice].name_y, item[*choice].text_y);
	    int hi = MAX(item[*choice].name_y, item[*choice].text_y);

	    if (old_choice == *choice)
		break;
	    print_item(win, item + old_choice, *scrollamt, FALSE);

	    if (*scrollamt < lo + 1 - getmaxy(win))
		*scrollamt = lo + 1 - getmaxy(win);
	    if (*scrollamt > hi)
		*scrollamt = hi;
	    /*
	     * If we have to scroll to show a wrap-around, it does get
	     * confusing.  Just give up rather than scroll.  Tab'ing to the
	     * next field in a multi-column form is a different matter.  Scroll
	     * for that.
	     */
	    if (*scrollamt != old_scroll) {
		if (wrapped) {
		    beep();
		    *scrollamt = old_scroll;
		    *choice = old_choice;
		} else {
		    scrollok(win, TRUE);
		    wscrl(win, *scrollamt - old_scroll);
		    scrollok(win, FALSE);
		}
	    }
	    break;
	}
    } while (*choice != old_choice);

    return (old_choice != *choice) || (old_scroll != *scrollamt);
}

/*
 * Scroll to the next page, putting the choice at the first editable field
 * in that page.  Note that fields are not necessarily in top-to-bottom order,
 * nor is there necessarily a field on each row of the window.
 */
static bool
scroll_next(WINDOW *win, DIALOG_FORMITEM item[], int stepsize, int *choice, int *scrollamt)
{
    int old_choice = *choice;
    int old_scroll = *scrollamt;
    int old_row = MIN(item[old_choice].text_y, item[old_choice].name_y);
    int target = old_scroll + stepsize;
    int n;

    if (stepsize < 0) {
	if (old_row != old_scroll)
	    target = old_scroll;
	else
	    target = old_scroll + stepsize;
	if (target < 0)
	    target = 0;
    } else {
	int limit = form_limit(item);
	if (target > limit)
	    target = limit;
    }

    for (n = 0; item[n].name != 0; ++n) {
	if (item[n].text_flen > 0) {
	    int new_row = MIN(item[n].text_y, item[n].name_y);
	    if (abs(new_row - target) < abs(old_row - target)) {
		old_row = new_row;
		*choice = n;
	    }
	}
    }

    if (old_choice != *choice)
	print_item(win, item + old_choice, *scrollamt, FALSE);

    *scrollamt = *choice;
    if (*scrollamt != old_scroll) {
	scrollok(win, TRUE);
	wscrl(win, *scrollamt - old_scroll);
	scrollok(win, FALSE);
    }
    return (old_choice != *choice) || (old_scroll != *scrollamt);
}

/*
 * Do a sanity check on the field length, and return the "right" value.
 */
static int
real_length(DIALOG_FORMITEM * item)
{
    return (item->text_flen > 0
	    ? item->text_flen
	    : (item->text_flen < 0
	       ? -item->text_flen
	       : item->text_len));
}

/*
 * Compute the form size, setup field buffers.
 */
static void
make_FORM_ELTs(DIALOG_FORMITEM * item,
	       int item_no,
	       int *min_height,
	       int *min_width)
{
    int i;
    int min_w = 0;
    int min_h = 0;

    for (i = 0; i < item_no; ++i) {
	int real_len = real_length(item + i);

	/*
	 * Special value '0' for text_flen: no input allowed
	 * Special value '0' for text_ilen: 'be the same as text_flen'
	 */
	if (item[i].text_ilen == 0)
	    item[i].text_ilen = real_len;

	min_h = MAX(min_h, item[i].name_y + 1);
	min_h = MAX(min_h, item[i].text_y + 1);
	min_w = MAX(min_w, item[i].name_x + 1 + item[i].name_len);
	min_w = MAX(min_w, item[i].text_x + 1 + real_len);

	item[i].text_len = real_length(item + i);

	/*
	 * We do not know the actual length of .text, so we allocate it here
	 * to ensure it is big enough.
	 */
	if (item[i].text_flen > 0) {
	    int max_len = MAX(item[i].text_ilen + 1, MAX_LEN);
	    char *old_text = item[i].text;

	    if (dialog_vars.max_input != 0 && dialog_vars.max_input < MAX_LEN)
		max_len = dialog_vars.max_input;

	    item[i].text = malloc(max_len + 1);
	    assert_ptr(item[i].text, "dialog_form");

	    sprintf(item[i].text, "%.*s", item[i].text_ilen, old_text);

	    if (item[i].text_free) {
		item[i].text_free = FALSE;
		free(old_text);
	    }
	    item[i].text_free = TRUE;
	}
    }

    *min_height = min_h;
    *min_width = min_w;
}

int
dlg_default_formitem(DIALOG_FORMITEM * items)
{
    int result = 0;

    if (dialog_vars.default_item != 0) {
	int count = 0;
	while (items->name != 0) {
	    if (!strcmp(dialog_vars.default_item, items->name)) {
		result = count;
		break;
	    }
	    ++items;
	    count++;
	}
    }
    return result;
}

/*
 * Display a form for fulfill a number of fields
 */
int
dlg_form(const char *title,
	 const char *cprompt,
	 int height,
	 int width,
	 int form_height,
	 int item_no,
	 DIALOG_FORMITEM * items,
	 int *current_item)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	INPUTSTR_BINDINGS,
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  CHR_NEXT ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_NEXT ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,  CHR_PREVIOUS ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_PREVIOUS ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_UP ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,  KEY_NPAGE ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,  KEY_PPAGE ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif

#define sTEXT -1

    int form_width;
    int first = TRUE;
    int chr_offset = 0;
    int state = dialog_vars.defaultno ? dlg_defaultno_button() : sTEXT;
    int x, y, cur_x, cur_y, box_x, box_y;
    int code;
    int key = 0;
    int fkey;
    int choice = dlg_default_formitem(items);
    int new_choice, new_scroll;
    int scrollamt = 0;
    int result = DLG_EXIT_UNKNOWN;
    int min_width = 0, min_height = 0;
    bool was_autosize = (height == 0 || width == 0);
    bool show_buttons = FALSE;
    bool scroll_changed = FALSE;
    bool field_changed = FALSE;
    WINDOW *dialog, *form;
    char *prompt = dlg_strclone(cprompt);
    const char **buttons = dlg_ok_labels();
    DIALOG_FORMITEM *current;

    make_FORM_ELTs(items, item_no, &min_height, &min_width);
    dlg_does_output();
    dlg_tab_correct_str(prompt);

#ifdef KEY_RESIZE
  retry:
#endif

    dlg_auto_size(title, prompt, &height, &width,
		  1 + 3 * MARGIN,
		  MAX(26, 2 + min_width));

    if (form_height == 0)
	form_height = min_height;

    if (was_autosize) {
	form_height = MIN(SLINES - height, form_height);
	height += form_height;
    } else {
	int thigh = 0;
	int twide = 0;
	dlg_auto_size(title, prompt, &thigh, &twide, 0, width);
	thigh = SLINES - (height - (thigh + 1 + 3 * MARGIN));
	form_height = MIN(thigh, form_height);
    }

    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);

    dialog = dlg_new_window(height, width, y, x);
    dlg_register_window(dialog, "formbox", binding);
    dlg_register_buttons(dialog, "formbox", buttons);

    dlg_mouse_setbase(x, y);

    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_bottom_box(dialog);
    dlg_draw_title(dialog, title);

    wattrset(dialog, dialog_attr);
    dlg_print_autowrap(dialog, prompt, height, width);

    form_width = width - 6;
    getyx(dialog, cur_y, cur_x);
    box_y = cur_y + 1;
    box_x = (width - form_width) / 2 - 1;

    /* create new window for the form */
    form = dlg_sub_window(dialog, form_height, form_width, y + box_y + 1,
			  x + box_x + 1);

    /* draw a box around the form items */
    dlg_draw_box(dialog, box_y, box_x, form_height + 2, form_width + 2,
		 menubox_border_attr, menubox_attr);

    /* register the new window, along with its borders */
    dlg_mouse_mkbigregion(getbegy(form) - getbegy(dialog),
			  getbegx(form) - getbegx(dialog),
			  getmaxy(form),
			  getmaxx(form),
			  KEY_MAX, 1, 1, 3 /* by cells */ );

    show_buttons = TRUE;
    scroll_changed = TRUE;

    choice = set_choice(items, choice, item_no);
    current = &items[choice];

    while (result == DLG_EXIT_UNKNOWN) {
	int edit = FALSE;

	if (scroll_changed) {
	    print_form(form, items, item_no, scrollamt, choice);
	    dlg_draw_arrows(dialog, scrollamt,
			    scrollamt + form_height < item_no,
			    box_x + 1,
			    box_y,
			    box_y + form_height + 1);
	    scroll_changed = FALSE;
	}

	if (show_buttons) {
	    dlg_item_help("");
	    dlg_draw_buttons(dialog, height - 2, 0, buttons,
			     ((state < 0)
			      ? 1000	/* no such button, not highlighted */
			      : state),
			     FALSE, width);
	    show_buttons = FALSE;
	}

	if (field_changed || state == sTEXT) {
	    if (field_changed)
		chr_offset = 0;
	    current = &items[choice];
	    dialog_vars.max_input = current->text_ilen;
	    dlg_item_help(current->help);
	    dlg_show_string(form, current->text, chr_offset,
			    form_active_text_attr,
			    current->text_y - scrollamt,
			    current->text_x,
			    current->text_len,
			    isPassword(current), first);
	    field_changed = FALSE;
	}

	key = dlg_mouse_wgetch(dialog, &fkey);
	if (dlg_result_key(key, fkey, &result))
	    break;

	/* handle non-functionkeys */
	if (!fkey) {
	    if (state != sTEXT) {
		code = dlg_char_to_button(key, buttons);
		if (code >= 0) {
		    dlg_del_window(dialog);
		    result = dlg_ok_buttoncode(code);
		    continue;
		}
		if (key == ' ') {
		    fkey = TRUE;
		    key = DLGK_ENTER;
		}
	    }
	}

	/* handle functionkeys */
	if (fkey) {
	    bool do_scroll = FALSE;
	    bool do_tab = FALSE;
	    int move_by = 0;

	    switch (key) {
	    case DLGK_MOUSE(KEY_PPAGE):
	    case DLGK_PAGE_PREV:
		do_scroll = TRUE;
		move_by = -form_height;
		break;

	    case DLGK_MOUSE(KEY_NPAGE):
	    case DLGK_PAGE_NEXT:
		do_scroll = TRUE;
		move_by = form_height;
		break;

	    case DLGK_ENTER:
		dlg_del_window(dialog);
		result = (state >= 0) ? dlg_ok_buttoncode(state) : DLG_EXIT_OK;
		continue;

	    case DLGK_GRID_LEFT:
		if (state == sTEXT)
		    break;
		/* FALLTHRU */
	    case DLGK_ITEM_PREV:
		if (state == sTEXT) {
		    do_tab = TRUE;
		    move_by = -1;
		    break;
		} else {
		    state = dlg_prev_ok_buttonindex(state, 0);
		    show_buttons = TRUE;
		    continue;
		}

	    case DLGK_FIELD_PREV:
		state = dlg_prev_ok_buttonindex(state, sTEXT);
		show_buttons = TRUE;
		continue;

	    case DLGK_FIELD_NEXT:
		state = dlg_next_ok_buttonindex(state, sTEXT);
		show_buttons = TRUE;
		continue;

	    case DLGK_GRID_RIGHT:
		if (state == sTEXT)
		    break;
		/* FALLTHRU */

	    case DLGK_ITEM_NEXT:
		if (state == sTEXT) {
		    do_tab = TRUE;
		    move_by = 1;
		    break;
		} else {
		    state = dlg_next_ok_buttonindex(state, 0);
		    show_buttons = TRUE;
		    continue;
		}

#ifdef KEY_RESIZE
	    case KEY_RESIZE:
		/* reset data */
		height = old_height;
		width = old_width;
		/* repaint */
		dlg_clear();
		dlg_del_window(dialog);
		refresh();
		dlg_mouse_free_regions();
		goto retry;
#endif
	    default:
#if USE_MOUSE
		if (is_DLGK_MOUSE(key)) {
		    if (key >= DLGK_MOUSE(KEY_MAX)) {
			int cell = key - DLGK_MOUSE(KEY_MAX);
			int row = (cell / getmaxx(form)) + scrollamt;
			int col = (cell % getmaxx(form));
			int n;

			for (n = 0; n < item_no; ++n) {
			    if (items[n].name_y == row
				&& items[n].name_x <= col
				&& (items[n].name_x + items[n].name_len > col
				    || (items[n].name_y == items[n].text_y
					&& items[n].text_x > col))) {
				field_changed = TRUE;
				break;
			    }
			    if (items[n].text_y == row
				&& items[n].text_x <= col
				&& items[n].text_x + items[n].text_ilen > col) {
				field_changed = TRUE;
				break;
			    }
			}
			if (field_changed) {
			    print_item(form, items + choice, scrollamt, FALSE);
			    choice = n;
			    continue;
			}
			beep();
		    } else if ((code = dlg_ok_buttoncode(key - M_EVENT)) >= 0) {
			result = code;
		    }
		    continue;
		}
#endif
		break;
	    }

	    new_scroll = scrollamt;
	    new_choice = choice;
	    if (do_scroll) {
		if (scroll_next(form, items, move_by, &new_choice, &new_scroll)) {
		    if (choice != new_choice) {
			choice = new_choice;
			field_changed = TRUE;
		    }
		    if (scrollamt != new_scroll) {
			scrollamt = new_scroll;
			scroll_changed = TRUE;
		    }
		}
		continue;
	    }
	    if (do_tab) {
		if (tab_next(form, items, item_no, move_by, &new_choice, &new_scroll)) {
		    if (choice != new_choice) {
			choice = new_choice;
			field_changed = TRUE;
		    }
		    if (scrollamt != new_scroll) {
			scrollamt = new_scroll;
			scroll_changed = TRUE;
		    }
		}
		continue;
	    }
	}

	if (state == sTEXT) {	/* Input box selected */
	    edit = dlg_edit_string(current->text, &chr_offset, key, fkey, first);
	    if (edit) {
		dlg_show_string(form, current->text, chr_offset,
				form_active_text_attr,
				current->text_y - scrollamt,
				current->text_x,
				current->text_len,
				isPassword(current), first);
		continue;
	    }
	}

    }

    dlg_mouse_free_regions();
    dlg_del_window(dialog);
    free(prompt);

    *current_item = scrollamt + choice;
    return result;
}

/*
 * Free memory owned by a list of DIALOG_FORMITEM's.
 */
void
dlg_free_formitems(DIALOG_FORMITEM * items)
{
    int n;
    for (n = 0; items[n].name != 0; ++n) {
	if (items[n].name_free)
	    free(items[n].name);
	if (items[n].text_free)
	    free(items[n].text);
	if (items[n].help_free)
	    free(items[n].help);
    }
    free(items);
}

/*
 * The script accepts values beginning at 1, while curses starts at 0.
 */
static int
ordinate(const char *s)
{
    int result = atoi(s);
    if (result > 0)
	--result;
    else
	result = 0;
    return result;
}

int
dialog_form(const char *title,
	    const char *cprompt,
	    int height,
	    int width,
	    int form_height,
	    int item_no,
	    char **items)
{
    int result;
    int choice;
    int i;
    DIALOG_FORMITEM *listitems;
    bool show_status = FALSE;

    listitems = calloc(item_no + 1, sizeof(*listitems));
    assert_ptr(listitems, "dialog_menu");

    for (i = 0; i < item_no; ++i) {
	listitems[i].type = dialog_vars.formitem_type;
	listitems[i].name = ItemName(i);
	listitems[i].name_len = strlen(ItemName(i));
	listitems[i].name_y = ordinate(ItemNameY(i));
	listitems[i].name_x = ordinate(ItemNameX(i));
	listitems[i].text = ItemText(i);
	listitems[i].text_len = strlen(ItemText(i));
	listitems[i].text_y = ordinate(ItemTextY(i));
	listitems[i].text_x = ordinate(ItemTextX(i));
	listitems[i].text_flen = atoi(ItemTextFLen(i));
	listitems[i].text_ilen = atoi(ItemTextILen(i));
	listitems[i].help = (dialog_vars.item_help) ? ItemHelp(i) : "";

	listitems[i].text_flen = real_length(&listitems[i]);
    }

    result = dlg_form(title,
		      cprompt,
		      height,
		      width,
		      form_height,
		      item_no,
		      listitems,
		      &choice);

    switch (result) {
    case DLG_EXIT_OK:		/* FALLTHRU */
    case DLG_EXIT_EXTRA:
	show_status = TRUE;
	break;
    case DLG_EXIT_HELP:
	dlg_add_result("HELP ");
	show_status = dialog_vars.help_status;
	if (USE_ITEM_HELP(listitems[choice].help)) {
	    dlg_add_result(listitems[choice].help);
	    result = DLG_EXIT_ITEM_HELP;
	} else {
	    dlg_add_result(listitems[choice].name);
	}
	if (show_status)
	    dlg_add_result("\n");
	break;
    }
    if (show_status) {
	for (i = 0; i < item_no; i++) {
	    if (listitems[i].text_flen > 0) {
		dlg_add_result(listitems[i].text);
		dlg_add_result("\n");
	    }
	}
    }

    dlg_free_formitems(listitems);

    return result;
}

--- NEW FILE: dlg_keys.h ---
/*
 *  $Id: dlg_keys.h,v 1.1 2006-10-23 20:59:26 stsp Exp $
 *
 *  dlg_keys.h -- runtime binding support for dialog
 *
 *  Copyright 2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#ifndef DLG_KEYS_H_included
#define DLG_KEYS_H_included 1

#include <dialog.h>

#ifdef USE_WIDE_CURSES
#include <wctype.h>
#define dlg_toupper(ch) towupper(ch)
#define dlg_isupper(ch) iswupper(ch)
#else
#define dlg_toupper(ch) toupper(ch)
#define dlg_isupper(ch) (isalpha(ch) && isupper(ch))
#endif

typedef struct {
    int is_function_key;
    int	curses_key;
    int dialog_key;
} DLG_KEYS_BINDING;

#define DLG_KEYS_DATA(dialog, curses)  { curses >= KEY_MIN, curses, dialog }

#define END_KEYS_BINDING { -1, 0, 0 }

/*
 * Define dialog's internal function-keys past the range used by curses.
 */
typedef enum {
    DLGK_MIN = KEY_MAX + 1,
    /* predefined buttons */
    DLGK_OK,
    DLGK_CANCEL,
    DLGK_EXTRA,
    DLGK_HELP,
    DLGK_ESC,
    /* moving from screen to screen (pages) */
    DLGK_PAGE_FIRST,
    DLGK_PAGE_LAST,
    DLGK_PAGE_NEXT,
    DLGK_PAGE_PREV,
    /* moving within a list */
    DLGK_ITEM_FIRST,
    DLGK_ITEM_LAST,
    DLGK_ITEM_NEXT,
    DLGK_ITEM_PREV,
    /* moving from field to field (or buttons) */
    DLGK_FIELD_FIRST,
    DLGK_FIELD_LAST,
    DLGK_FIELD_NEXT,
    DLGK_FIELD_PREV,
    /* moving within a grid */
    DLGK_GRID_UP,
    DLGK_GRID_DOWN,
    DLGK_GRID_LEFT,
    DLGK_GRID_RIGHT,
    /* delete */
    DLGK_DELETE_LEFT,
    DLGK_DELETE_RIGHT,
    DLGK_DELETE_ALL,
    /* special */
    DLGK_ENTER,
    DLGK_BEGIN,
    DLGK_FINAL,
    DLGK_SELECT
} DLG_KEYS_ENUM;

#define is_DLGK_MOUSE(code)	((code) >= M_EVENT)
#define DLGK_MOUSE(code)	((code) + M_EVENT)

#define ENTERKEY_BINDINGS \
	DLG_KEYS_DATA( DLGK_ENTER,	   '\n' ), \
	DLG_KEYS_DATA( DLGK_ENTER,	   '\r' ), \
	DLG_KEYS_DATA( DLGK_ENTER,	   KEY_ENTER )

/* ^U == 21 */
#define INPUTSTR_BINDINGS \
	DLG_KEYS_DATA( DLGK_BEGIN,	   KEY_HOME ), \
	DLG_KEYS_DATA( DLGK_DELETE_ALL,    21 ), \
	DLG_KEYS_DATA( DLGK_DELETE_LEFT,   CHR_BACKSPACE ), \
	DLG_KEYS_DATA( DLGK_DELETE_LEFT,   KEY_BACKSPACE ), \
	DLG_KEYS_DATA( DLGK_DELETE_RIGHT,  CHR_DELETE ), \
	DLG_KEYS_DATA( DLGK_DELETE_RIGHT,  KEY_DC ), \
	DLG_KEYS_DATA( DLGK_FINAL,	   KEY_END ), \
	DLG_KEYS_DATA( DLGK_GRID_LEFT,	   KEY_LEFT ), \
	DLG_KEYS_DATA( DLGK_GRID_RIGHT,	   KEY_RIGHT )

extern int dlg_lookup_key(WINDOW * /*win*/, int /*curses_key*/, int * /*dialog_key*/);
extern int dlg_result_key(int /*dialog_key*/, int /*fkey*/, int * /*resultp*/);
extern void dlg_register_buttons(WINDOW * /*win*/, const char * /*name*/, const char ** /*buttons*/);
extern void dlg_register_window(WINDOW * /*win*/, const char * /*name*/, DLG_KEYS_BINDING * /*binding*/);
extern void dlg_unregister_window(WINDOW * /*win*/);

#ifdef HAVE_RC_FILE
extern int dlg_parse_bindkey(char * /*params*/);
extern void dlg_dump_keys(FILE * /*fp*/);
#endif

#endif /* DLG_KEYS_H_included */

--- NEW FILE: headers.sh ---
#! /bin/sh
# $Id: headers.sh,v 1.1 2006-10-23 20:59:27 stsp Exp $
#
# Adjust includes for header files that reside in a subdirectory of
# /usr/include, etc.
#
# Parameters (the first case creates the sed script):
#	$1 is the target directory
#	$2 is the source directory
# or (the second case does the install, using the sed script):
#	$1 is the script to use for installing
#	$2 is the target directory
#	$3 is the source directory
#	$4 is the file to install, editing source/target/etc.

PACKAGE=DIALOG
PKGNAME=DLG
CONFIGH=dlg_config.h

TMPSED=headers.sed

if test $# = 2 ; then
	rm -f $TMPSED
	DST=$1
	REF=$2
	LEAF=`basename $DST`
	case $DST in
	/*/include/$LEAF)
		END=`basename $DST`
		for i in $REF/*.h
		do
			NAME=`basename $i`
			echo "s/<$NAME>/<$END\/$NAME>/g" >> $TMPSED
		done
		;;
	*)
		echo "" >> $TMPSED
		;;
	esac
	for name in `
	egrep "#define[ 	][ 	]*[A-Z]" $REF/$CONFIGH \
		| sed	-e 's/^#define[ 	][ 	]*//' \
			-e 's/[ 	].*//' \
		| egrep -v "^GCC_" \
		| egrep -v "^${PACKAGE}_" \
		| sort -u \
		| egrep -v "^${PKGNAME}_"`
	do
		echo "s/\\<$name\\>/${PKGNAME}_$name/g" >>$TMPSED
	done
else
	PRG=""
	while test $# != 3
	do
		PRG="$PRG $1"; shift
	done

	DST=$1
	REF=$2
	SRC=$3

	SHOW=`basename $SRC`
	TMPSRC=${TMPDIR-/tmp}/${SHOW}$$

	echo "	... $SHOW"
	test -f $REF/$SRC && SRC="$REF/$SRC"

	rm -f $TMPSRC
	sed -f $TMPSED $SRC > $TMPSRC
	NAME=`basename $SRC`

	# Just in case someone gzip'd manpages, remove the conflicting copy.
	test -f $DST/$NAME.gz && rm -f $DST/$NAME.gz

	eval $PRG $TMPSRC $DST/$NAME
	rm -f $TMPSRC
fi

--- NEW FILE: msgbox.c ---
/*
 *  $Id: msgbox.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  msgbox.c -- implements the message box and info box
 *
 *  Copyright 2000-2004,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors:
 *	Savio Lam (lam836 at cs.cuhk.hk)
 */

#include <dialog.h>
#include <dlg_keys.h>

/*
 * Display the message in a scrollable window.  Actually the way it works is
 * that we create a "tall" window of the proper width, let the text wrap within
 * that, and copy a slice of the result to the dialog.
 *
 * It works for ncurses.  Other curses implementations show only blanks (Tru64)
 * or garbage (NetBSD).
 */
static int
show_message(WINDOW *dialog,
	     const char *prompt,
	     int offset,
	     int page,
	     int width,
	     int pauseopt)
{
#ifdef NCURSES_VERSION
    if (pauseopt) {
	int wide = width - (2 * MARGIN);
	int high = LINES;
	int y, x;
	int len;
	int percent;
	WINDOW *dummy;
	char buffer[5];

#if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH >= 20040417
	/*
	 * If we're not limited by the screensize, allow text to possibly be
	 * one character per line.
	 */
	if ((len = dlg_count_columns(prompt)) > high)
	    high = len;
#endif
	dummy = newwin(high, width, 0, 0);
	wbkgdset(dummy, dialog_attr | ' ');
	wattrset(dummy, dialog_attr);
	werase(dummy);
	dlg_print_autowrap(dummy, prompt, high, width);
	getyx(dummy, y, x);

	copywin(dummy,		/* srcwin */
		dialog,		/* dstwin */
		offset + MARGIN,	/* sminrow */
		MARGIN,		/* smincol */
		MARGIN,		/* dminrow */
		MARGIN,		/* dmincol */
		page,		/* dmaxrow */
		wide,		/* dmaxcol */
		FALSE);

	delwin(dummy);

	/* if the text is incomplete, or we have scrolled, show the percentage */
	if (y > 0 && wide > 4) {
	    percent = ((page + offset) * 100.0 / y);
	    if (percent < 0)
		percent = 0;
	    if (percent > 100)
		percent = 100;
	    if (offset != 0 || percent != 100) {
		(void) wattrset(dialog, position_indicator_attr);
		(void) wmove(dialog, MARGIN + page, wide - 4);
		(void) sprintf(buffer, "%d%%", percent);
		(void) waddstr(dialog, buffer);
		if ((len = strlen(buffer)) < 4) {
		    wattrset(dialog, border_attr);
		    whline(dialog, ACS_HLINE, 4 - len);
		}
	    }
	}
	return (y - page);
    }
#endif
    (void) offset;
    wattrset(dialog, dialog_attr);
    dlg_print_autowrap(dialog, prompt, page + 1 + (3 * MARGIN), width);
    return 0;
}

/*
 * Display a message box. Program will pause and display an "OK" button
 * if the parameter 'pauseopt' is non-zero.
 */
int
dialog_msgbox(const char *title, const char *cprompt, int height, int width,
	      int pauseopt)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_ENTER,	' ' ),
	DLG_KEYS_DATA( DLGK_GRID_DOWN,	'J' ),
	DLG_KEYS_DATA( DLGK_GRID_DOWN,	'j' ),
	DLG_KEYS_DATA( DLGK_GRID_DOWN,	KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_GRID_UP,	'K' ),
	DLG_KEYS_DATA( DLGK_GRID_UP,	'k' ),
	DLG_KEYS_DATA( DLGK_GRID_UP,	KEY_UP ),
	DLG_KEYS_DATA( DLGK_PAGE_FIRST,	'g' ),
	DLG_KEYS_DATA( DLGK_PAGE_FIRST,	KEY_HOME ),
	DLG_KEYS_DATA( DLGK_PAGE_LAST,	'G' ),
	DLG_KEYS_DATA( DLGK_PAGE_LAST,	KEY_END ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	'F' ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	'f' ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	KEY_NPAGE ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,	'B' ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,	'b' ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,	KEY_PPAGE ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV,	KEY_UP ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

    int x, y, last = 0, page;
    int button = 0;
    int key = 0, fkey;
    int result = DLG_EXIT_UNKNOWN;
    WINDOW *dialog = 0;
    char *prompt = dlg_strclone(cprompt);
    const char **buttons = dlg_ok_label();
    int offset = 0;
    int check;
    bool show = TRUE;

#ifdef KEY_RESIZE
    int req_high = height;
    int req_wide = width;
  restart:
#endif

    dlg_tab_correct_str(prompt);
    dlg_auto_size(title, prompt, &height, &width,
		  (pauseopt == 1 ? 2 : 0),
		  (pauseopt == 1 ? 12 : 0));
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);

#ifdef KEY_RESIZE
    if (dialog != 0) {
	(void) wresize(dialog, height, width);
	(void) mvwin(dialog, y, x);
	(void) refresh();
    } else
#endif
    {
	dialog = dlg_new_window(height, width, y, x);
	dlg_register_window(dialog, "msgbox", binding);
	dlg_register_buttons(dialog, "msgbox", buttons);
    }
    page = height - (1 + 3 * MARGIN);

    dlg_mouse_setbase(x, y);

    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_title(dialog, title);

    wattrset(dialog, dialog_attr);

    if (pauseopt) {
	dlg_draw_bottom_box(dialog);
	mouse_mkbutton(height - 2, width / 2 - 4, 6, '\n');
	dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);

	while (result == DLG_EXIT_UNKNOWN) {
	    if (show) {
		getyx(dialog, y, x);
		last = show_message(dialog, prompt, offset, page, width, pauseopt);
		wmove(dialog, y, x);
		wrefresh(dialog);
		show = FALSE;
	    }
	    key = dlg_mouse_wgetch(dialog, &fkey);
	    if (dlg_result_key(key, fkey, &result))
		break;

	    if (!fkey && (check = dlg_char_to_button(key, buttons)) >= 0) {
		result = check ? DLG_EXIT_HELP : DLG_EXIT_OK;
		break;
	    }

	    if (fkey) {
		switch (key) {
#ifdef KEY_RESIZE
		case KEY_RESIZE:
		    dlg_clear();
		    height = req_high;
		    width = req_wide;
		    goto restart;
#endif
		case DLGK_FIELD_NEXT:
		    button = dlg_next_button(buttons, button);
		    if (button < 0)
			button = 0;
		    dlg_draw_buttons(dialog,
				     height - 2, 0,
				     buttons, button,
				     FALSE, width);
		    break;
		case DLGK_FIELD_PREV:
		    button = dlg_prev_button(buttons, button);
		    if (button < 0)
			button = 0;
		    dlg_draw_buttons(dialog,
				     height - 2, 0,
				     buttons, button,
				     FALSE, width);
		    break;
		case DLGK_ENTER:
		    result = button ? DLG_EXIT_HELP : DLG_EXIT_OK;
		    break;
		case DLGK_PAGE_FIRST:
		    if (offset > 0) {
			offset = 0;
			show = TRUE;
		    }
		    break;
		case DLGK_PAGE_LAST:
		    if (offset < last) {
			offset = last;
			show = TRUE;
		    }
		    break;
		case DLGK_GRID_UP:
		    if (offset > 0) {
			--offset;
			show = TRUE;
		    }
		    break;
		case DLGK_GRID_DOWN:
		    if (offset < last) {
			++offset;
			show = TRUE;
		    }
		    break;
		case DLGK_PAGE_PREV:
		    if (offset > 0) {
			offset -= page;
			if (offset < 0)
			    offset = 0;
			show = TRUE;
		    }
		    break;
		case DLGK_PAGE_NEXT:
		    if (offset < last) {
			offset += page;
			if (offset > last)
			    offset = last;
			show = TRUE;
		    }
		    break;
		case DLGK_MOUSE(0):
		    result = DLG_EXIT_OK;
		    break;
		case DLGK_MOUSE(1):
		    result = DLG_EXIT_HELP;
		    break;
		default:
		    beep();
		    break;
		}
	    } else {
		beep();
	    }
	}
    } else {
	show_message(dialog, prompt, offset, page, width, pauseopt);
	wrefresh(dialog);
	result = DLG_EXIT_OK;
    }

    dlg_del_window(dialog);
    dlg_mouse_free_regions();
    free(prompt);
    return result;
}

--- NEW FILE: dlg_keys.c ---
/*
 *  $Id: dlg_keys.c,v 1.1 2006-10-23 20:59:26 stsp Exp $
 *
 *  dlg_keys.c -- runtime binding support for dialog
 *
 *  Copyright 2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>
#include <dlg_keys.h>

#define LIST_BINDINGS struct _list_bindings

LIST_BINDINGS {
    LIST_BINDINGS *link;
    WINDOW *win;		/* window on which widget gets input */
    const char *name;		/* widget name */
    bool buttons;		/* true only for dlg_register_buttons() */
    DLG_KEYS_BINDING *binding;	/* list of bindings */
};

static LIST_BINDINGS *all_bindings;
static const DLG_KEYS_BINDING end_keys_binding = END_KEYS_BINDING;

/*
 * For a given named widget's window, associate a binding table.
 */
void
dlg_register_window(WINDOW *win, const char *name, DLG_KEYS_BINDING * binding)
{
    LIST_BINDINGS *p, *q;

    for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
	if (p->win == win && !strcmp(p->name, name)) {
	    p->binding = binding;
	    return;
	}
    }
    /* add built-in bindings at the end of the list (see compare_bindings). */
    if ((p = (LIST_BINDINGS *) calloc(1, sizeof(*p))) != 0) {
	p->win = win;
	p->name = name;
	p->binding = binding;
	if (q != 0)
	    q->link = p;
	else
	    all_bindings = p;
    }
}

/*
 * Unlike dlg_lookup_key(), this looks for either widget-builtin or rc-file
 * definitions, depending on whether 'win' is null.
 */
static int
key_is_bound(WINDOW *win, const char *name, int curses_key, int function_key)
{
    LIST_BINDINGS *p;

    for (p = all_bindings; p != 0; p = p->link) {
	if (p->win == win && !dlg_strcmp(p->name, name)) {
	    int n;
	    for (n = 0; p->binding[n].is_function_key >= 0; ++n) {
		if (p->binding[n].curses_key == curses_key
		    && p->binding[n].is_function_key == function_key) {
		    return TRUE;
		}
	    }
	}
    }
    return FALSE;
}

/*
 * Call this function after dlg_register_window(), for the list of button
 * labels associated with the widget.
 *
 * Ensure that dlg_lookup_key() will not accidentally translate a key that
 * we would like to use for a button abbreviation to some other key, e.g.,
 * h/j/k/l for navigation into a cursor key.  Do this by binding the key
 * to itself.
 *
 * See dlg_char_to_button().
 */
void
dlg_register_buttons(WINDOW *win, const char *name, const char **buttons)
{
    int n;
    LIST_BINDINGS *p;
    DLG_KEYS_BINDING *q;

    if (buttons == 0)
	return;

    for (n = 0; buttons[n] != 0; ++n) {
	int curses_key = dlg_button_to_char(buttons[n]);

	/* ignore multibyte characters */
	if (curses_key >= KEY_MIN)
	    continue;

	/* if it is not bound in the widget, skip it (no conflicts) */
	if (!key_is_bound(win, name, curses_key, FALSE))
	    continue;

#ifdef HAVE_RC_FILE
	/* if it is bound in the rc-file, skip it */
	if (key_is_bound(0, name, curses_key, FALSE))
	    continue;
#endif

	if ((p = (LIST_BINDINGS *) calloc(1, sizeof(*p))) != 0) {
	    if ((q = (DLG_KEYS_BINDING *) calloc(2, sizeof(*q))) != 0) {
		q[0].is_function_key = 0;
		q[0].curses_key = curses_key;
		q[0].dialog_key = curses_key;
		q[1] = end_keys_binding;

		p->win = win;
		p->name = name;
		p->buttons = TRUE;
		p->binding = q;

		/* put these at the beginning, to override the widget's table */
		p->link = all_bindings;
		all_bindings = p;
	    } else {
		free(p);
	    }
	}
    }
}

/*
 * Remove the bindings for a given window.
 */
void
dlg_unregister_window(WINDOW *win)
{
    LIST_BINDINGS *p, *q;

    for (p = all_bindings, q = 0; p != 0; p = p->link) {
	if (p->win == win) {
	    if (q != 0) {
		q->link = p->link;
	    } else {
		all_bindings = p->link;
	    }
	    /* the user-defined and buttons-bindings all are length=1 */
	    if (p->binding[1].is_function_key < 0)
		free(p->binding);
	    free(p);
	    dlg_unregister_window(win);
	    break;
	}
	q = p;
    }
}

/*
 * Call this after wgetch(), using the same window pointer and passing
 * the curses-key.
 *
 * If there is no binding associated with the widget, it simply returns
 * the given curses-key.
 *
 * Parameters:
 *	win is the window on which the wgetch() was done.
 *	curses_key is the value returned by wgetch().
 *	fkey in/out (on input, it is true if curses_key is a function key,
 *		and on output, it is true if the result is a function key).
 */
int
dlg_lookup_key(WINDOW *win, int curses_key, int *fkey)
{
    LIST_BINDINGS *p;
    int n;

    /*
     * Ignore mouse clicks, since they are already encoded properly.
     */
#ifdef KEY_MOUSE
    if (*fkey != 0 && curses_key == KEY_MOUSE) {
	;
    } else
#endif
	/*
	 * Ignore resize events, since they are already encoded properly.
	 */
#ifdef KEY_RESIZE
    if (*fkey != 0 && curses_key == KEY_RESIZE) {
	;
    } else
#endif
    if (*fkey == 0 || curses_key < KEY_MAX) {
	for (p = all_bindings; p != 0; p = p->link) {
	    if (p->win == win || p->win == 0) {
		int function_key = (*fkey != 0);
		for (n = 0; p->binding[n].is_function_key >= 0; ++n) {
		    if (p->buttons
			&& !function_key
			&& p->binding[n].curses_key == (int) dlg_toupper(curses_key)) {
			*fkey = 0;
			return p->binding[n].dialog_key;
		    }
		    if (p->binding[n].curses_key == curses_key
			&& p->binding[n].is_function_key == function_key) {
			*fkey = p->binding[n].dialog_key;
			return *fkey;
		    }
		}
	    }
	}
    }
    return curses_key;
}

/*
 * Test a dialog internal keycode to see if it corresponds to one of the push
 * buttons on the widget such as "OK".
 *
 * This is only useful if there are user-defined key bindings, since there are
 * no built-in bindings that map directly to DLGK_OK, etc.
 *
 * See also dlg_ok_buttoncode().
 */
int
dlg_result_key(int dialog_key, int fkey GCC_UNUSED, int *resultp)
{
    int done = FALSE;

#ifdef HAVE_RC_FILE
    if (fkey) {
	switch ((DLG_KEYS_ENUM) dialog_key) {
	case DLGK_OK:
	    *resultp = DLG_EXIT_OK;
	    done = TRUE;
	    break;
	case DLGK_CANCEL:
	    if (!dialog_vars.nocancel) {
		*resultp = DLG_EXIT_CANCEL;
		done = TRUE;
	    }
	    break;
	case DLGK_EXTRA:
	    if (dialog_vars.extra_button) {
		*resultp = DLG_EXIT_EXTRA;
		done = TRUE;
	    }
	    break;
	case DLGK_HELP:
	    if (dialog_vars.help_button) {
		*resultp = DLG_EXIT_HELP;
		done = TRUE;
	    }
	    break;
	case DLGK_ESC:
	    *resultp = DLG_EXIT_ESC;
	    done = TRUE;
	    break;
	default:
	    break;
	}
    } else
#endif
    if (dialog_key == ESC) {
	*resultp = DLG_EXIT_ESC;
	done = TRUE;
    }
    return done;
}

#ifdef HAVE_RC_FILE
typedef struct {
    const char *name;
    int code;
} CODENAME;

#define CURSES_NAME(upper) { #upper, KEY_ ## upper }
#define COUNT_CURSES  sizeof(curses_names)/sizeof(curses_names[0])
static const CODENAME curses_names[] =
{
    CURSES_NAME(DOWN),
    CURSES_NAME(UP),
    CURSES_NAME(LEFT),
    CURSES_NAME(RIGHT),
    CURSES_NAME(HOME),
    CURSES_NAME(BACKSPACE),
    CURSES_NAME(F0),
    CURSES_NAME(DL),
    CURSES_NAME(IL),
    CURSES_NAME(DC),
    CURSES_NAME(IC),
    CURSES_NAME(EIC),
    CURSES_NAME(CLEAR),
    CURSES_NAME(EOS),
    CURSES_NAME(EOL),
    CURSES_NAME(SF),
    CURSES_NAME(SR),
    CURSES_NAME(NPAGE),
    CURSES_NAME(PPAGE),
    CURSES_NAME(STAB),
    CURSES_NAME(CTAB),
    CURSES_NAME(CATAB),
    CURSES_NAME(ENTER),
    CURSES_NAME(PRINT),
    CURSES_NAME(LL),
    CURSES_NAME(A1),
    CURSES_NAME(A3),
    CURSES_NAME(B2),
    CURSES_NAME(C1),
    CURSES_NAME(C3),
    CURSES_NAME(BTAB),
    CURSES_NAME(BEG),
    CURSES_NAME(CANCEL),
    CURSES_NAME(CLOSE),
    CURSES_NAME(COMMAND),
    CURSES_NAME(COPY),
    CURSES_NAME(CREATE),
    CURSES_NAME(END),
    CURSES_NAME(EXIT),
    CURSES_NAME(FIND),
    CURSES_NAME(HELP),
    CURSES_NAME(MARK),
    CURSES_NAME(MESSAGE),
    CURSES_NAME(MOVE),
    CURSES_NAME(NEXT),
    CURSES_NAME(OPEN),
    CURSES_NAME(OPTIONS),
    CURSES_NAME(PREVIOUS),
    CURSES_NAME(REDO),
    CURSES_NAME(REFERENCE),
    CURSES_NAME(REFRESH),
    CURSES_NAME(REPLACE),
    CURSES_NAME(RESTART),
    CURSES_NAME(RESUME),
    CURSES_NAME(SAVE),
    CURSES_NAME(SBEG),
    CURSES_NAME(SCANCEL),
    CURSES_NAME(SCOMMAND),
    CURSES_NAME(SCOPY),
    CURSES_NAME(SCREATE),
    CURSES_NAME(SDC),
    CURSES_NAME(SDL),
    CURSES_NAME(SELECT),
    CURSES_NAME(SEND),
    CURSES_NAME(SEOL),
    CURSES_NAME(SEXIT),
    CURSES_NAME(SFIND),
    CURSES_NAME(SHELP),
    CURSES_NAME(SHOME),
    CURSES_NAME(SIC),
    CURSES_NAME(SLEFT),
    CURSES_NAME(SMESSAGE),
    CURSES_NAME(SMOVE),
    CURSES_NAME(SNEXT),
    CURSES_NAME(SOPTIONS),
    CURSES_NAME(SPREVIOUS),
    CURSES_NAME(SPRINT),
    CURSES_NAME(SREDO),
    CURSES_NAME(SREPLACE),
    CURSES_NAME(SRIGHT),
    CURSES_NAME(SRSUME),
    CURSES_NAME(SSAVE),
    CURSES_NAME(SSUSPEND),
    CURSES_NAME(SUNDO),
    CURSES_NAME(SUSPEND),
    CURSES_NAME(UNDO),
};

#define DIALOG_NAME(upper) { #upper, DLGK_ ## upper }
#define COUNT_DIALOG  sizeof(dialog_names)/sizeof(dialog_names[0])
static const CODENAME dialog_names[] =
{
    DIALOG_NAME(OK),
    DIALOG_NAME(CANCEL),
    DIALOG_NAME(EXTRA),
    DIALOG_NAME(HELP),
    DIALOG_NAME(ESC),
    DIALOG_NAME(PAGE_FIRST),
    DIALOG_NAME(PAGE_LAST),
    DIALOG_NAME(PAGE_NEXT),
    DIALOG_NAME(PAGE_PREV),
    DIALOG_NAME(ITEM_FIRST),
    DIALOG_NAME(ITEM_LAST),
    DIALOG_NAME(ITEM_NEXT),
    DIALOG_NAME(ITEM_PREV),
    DIALOG_NAME(FIELD_FIRST),
    DIALOG_NAME(FIELD_LAST),
    DIALOG_NAME(FIELD_NEXT),
    DIALOG_NAME(FIELD_PREV),
    DIALOG_NAME(GRID_UP),
    DIALOG_NAME(GRID_DOWN),
    DIALOG_NAME(GRID_LEFT),
    DIALOG_NAME(GRID_RIGHT),
    DIALOG_NAME(DELETE_LEFT),
    DIALOG_NAME(DELETE_RIGHT),
    DIALOG_NAME(DELETE_ALL),
    DIALOG_NAME(ENTER),
    DIALOG_NAME(BEGIN),
    DIALOG_NAME(FINAL),
    DIALOG_NAME(SELECT)
};

static char *
skip_white(char *s)
{
    while (*s != '\0' && isspace(CharOf(*s)))
	++s;
    return s;
}

static char *
skip_black(char *s)
{
    while (*s != '\0' && !isspace(CharOf(*s)))
	++s;
    return s;
}

/*
 * Find a user-defined binding, given the curses key code.
 */
static DLG_KEYS_BINDING *
find_binding(char *widget, int curses_key)
{
    LIST_BINDINGS *p;
    DLG_KEYS_BINDING *result = 0;

    for (p = all_bindings; p != 0; p = p->link) {
	if (p->win == 0
	    && !dlg_strcmp(p->name, widget)
	    && p->binding->curses_key == curses_key) {
	    result = p->binding;
	    break;
	}
    }
    return result;
}

/*
 * Built-in bindings have a nonzero "win" member, and the associated binding
 * table can have more than one entry.  We keep those last, since lookups will
 * find the user-defined bindings first and use those.
 *
 * Sort "*" (all-widgets) entries past named widgets, since those are less
 * specific.
 */
static int
compare_bindings(LIST_BINDINGS * a, LIST_BINDINGS * b)
{
    int result = 0;
    if (a->win == b->win) {
	if (!strcmp(a->name, b->name)) {
	    result = a->binding[0].curses_key - b->binding[0].curses_key;
	} else if (!strcmp(b->name, "*")) {
	    result = -1;
	} else if (!strcmp(a->name, "*")) {
	    result = 1;
	} else {
	    result = dlg_strcmp(a->name, b->name);
	}
    } else if (b->win) {
	result = -1;
    } else {
	result = 1;
    }
    return result;
}

/*
 * Find a user-defined binding, given the curses key code.  If it does not
 * exist, create a new one, inserting it into the linked list, keeping it
 * sorted to simplify lookups for user-defined bindings that can override
 * the built-in bindings.
 */
static DLG_KEYS_BINDING *
make_binding(char *widget, int curses_key, int is_function, int dialog_key)
{
    LIST_BINDINGS *entry;
    DLG_KEYS_BINDING *data;
    char *name;
    LIST_BINDINGS *p, *q;
    DLG_KEYS_BINDING *result = find_binding(widget, curses_key);

    if (result == 0
	&& (entry = calloc(1, sizeof(LIST_BINDINGS))) != 0
	&& (data = calloc(2, sizeof(DLG_KEYS_BINDING))) != 0
	&& (name = dlg_strclone(widget)) != 0) {

	entry->name = name;
	entry->binding = data;

	data[0].is_function_key = is_function;
	data[0].curses_key = curses_key;
	data[0].dialog_key = dialog_key;

	data[1] = end_keys_binding;

	for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
	    if (compare_bindings(entry, p) < 0) {
		break;
	    }
	}
	if (q != 0) {
	    q->link = entry;
	} else {
	    all_bindings = entry;
	}
	if (p != 0) {
	    entry->link = p;
	}
	result = data;
    }

    return result;
}

/*
 * Parse the parameters of the "bindkeys" configuration-file entry.  This
 * expects widget name which may be "*", followed by curses key definition and
 * then dialog key definition.
 *
 * The curses key "should" be one of the names (ignoring case) from
 * curses_names[], but may also be a single control character (prefix "^" or
 * "~" depending on whether it is C0 or C1), or an escaped single character. 
 * Binding a printable character with dialog is possible but not useful.
 *
 * The dialog key must be one of the names from dialog_names[].
 */
int
dlg_parse_bindkey(char *params)
{
    char *p = skip_white(params);
    char *q;
    bool escaped = FALSE;
    int modified = 0;
    int result = FALSE;
    unsigned xx;
    char *widget;
    int is_function = FALSE;
    int curses_key;
    int dialog_key;

    curses_key = -1;
    dialog_key = -1;
    widget = p;

    p = skip_black(p);
    if (p != widget && *p != '\0') {
	*p++ = '\0';
	q = p;
	while (*p != '\0' && curses_key < 0) {
	    if (escaped) {
		escaped = FALSE;
		curses_key = *p;
	    } else if (*p == '\\') {
		escaped = TRUE;
	    } else if (modified) {
		if (*p == '?') {
		    curses_key = ((modified == '^')
				  ? 127
				  : 255);
		} else {
		    curses_key = ((modified == '^')
				  ? (*p & 0x1f)
				  : ((*p & 0x1f) | 0x80));
		}
	    } else if (*p == '^') {
		modified = *p;
	    } else if (*p == '~') {
		modified = *p;
	    } else if (isspace(CharOf(*p))) {
		break;
	    }
	    ++p;
	}
	if (!isspace(CharOf(*p))) {
	    ;
	} else {
	    *p++ = '\0';
	    if (curses_key < 0) {
		char fprefix[2];
		char check[2];
		int keynumber;
		if (sscanf(q, "%[Ff]%d%c", fprefix, &keynumber, check) == 2) {
		    curses_key = KEY_F(keynumber);
		    is_function = TRUE;
		} else {
		    for (xx = 0; xx < COUNT_CURSES; ++xx) {
			if (!dlg_strcmp(curses_names[xx].name, q)) {
			    curses_key = curses_names[xx].code;
			    is_function = TRUE;
			    break;
			}
		    }
		}
	    }
	}
	q = skip_white(p);
	p = skip_black(q);
	if (p != q) {
	    for (xx = 0; xx < COUNT_DIALOG; ++xx) {
		if (!dlg_strcmp(dialog_names[xx].name, q)) {
		    dialog_key = dialog_names[xx].code;
		    break;
		}
	    }
	}
	if (*widget != '\0'
	    && curses_key >= 0
	    && dialog_key >= 0
	    && make_binding(widget, curses_key, is_function, dialog_key) != 0) {
	    result = TRUE;
	}
    }
    return result;
}

static void
dump_curses_key(FILE *fp, int curses_key)
{
    if (curses_key > KEY_MIN) {
	unsigned n;
	bool found = FALSE;
	for (n = 0; n < COUNT_CURSES; ++n) {
	    if (curses_names[n].code == curses_key) {
		fprintf(fp, "%s", curses_names[n].name);
		found = TRUE;
		break;
	    }
	}
	if (!found) {
	    if (curses_key >= KEY_F(0)) {
		fprintf(fp, "F%d", curses_key - KEY_F(0));
	    } else {
		fprintf(fp, "curses%d", curses_key);
	    }
	}
    } else if (curses_key >= 0 && curses_key < 32) {
	fprintf(fp, "^%c", curses_key + 64);
    } else if (curses_key == 127) {
	fprintf(fp, "^?");
    } else if (curses_key >= 128 && curses_key < 160) {
	fprintf(fp, "~%c", curses_key - 64);
    } else if (curses_key == 255) {
	fprintf(fp, "~?");
    } else {
	fprintf(fp, "\\%c", curses_key);
    }
}

static void
dump_dialog_key(FILE *fp, int dialog_key)
{
    unsigned n;
    bool found = FALSE;
    for (n = 0; n < COUNT_DIALOG; ++n) {
	if (dialog_names[n].code == dialog_key) {
	    fputs(dialog_names[n].name, fp);
	    found = TRUE;
	    break;
	}
    }
    if (!found) {
	fprintf(fp, "dialog%d", dialog_key);
    }
}

static void
dump_one_binding(FILE *fp, const char *widget, DLG_KEYS_BINDING * binding)
{
    fprintf(fp, "bindkey %s ", widget);
    dump_curses_key(fp, binding->curses_key);
    fputc(' ', fp);
    dump_dialog_key(fp, binding->dialog_key);
    fputc('\n', fp);
}

void
dlg_dump_keys(FILE *fp)
{
    LIST_BINDINGS *p;
    const char *last = "";
    unsigned n;
    unsigned count = 0;

    for (p = all_bindings; p != 0; p = p->link) {
	if (p->win == 0) {
	    ++count;
	}
    }
    if (count != 0) {
	for (p = all_bindings, n = 0; p != 0; p = p->link) {
	    if (p->win == 0) {
		if (dlg_strcmp(last, p->name)) {
		    fprintf(fp, "\n# key bindings for %s widgets\n",
			    !strcmp(p->name, "*") ? "all" : p->name);
		    last = p->name;
		}
		dump_one_binding(fp, p->name, p->binding);
	    }
	}
    }
}
#endif /* HAVE_RC_FILE */

--- NEW FILE: yesno.c ---
/*
 *  $Id: yesno.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  yesno.c -- implements the yes/no box
 *
 *  Copyright 1999-2004,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors
 *	Savio Lam (lam836 at cs.cuhk.hk)
 */

#include <dialog.h>
#include <dlg_keys.h>

/*
 * Display a dialog box with two buttons - Yes and No.
 */
int
dialog_yesno(const char *title, const char *cprompt, int height, int width)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_ENTER,	' ' ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV,	KEY_UP ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

    int x, y;
    int key = 0, fkey;
    int code;
    int button = dlg_defaultno_button();
    WINDOW *dialog = 0;
    int result = DLG_EXIT_UNKNOWN;
    char *prompt = dlg_strclone(cprompt);
    const char **buttons = dlg_yes_labels();

#ifdef KEY_RESIZE
    int req_high = height;
    int req_wide = width;
  restart:
#endif

    dlg_tab_correct_str(prompt);
    dlg_auto_size(title, prompt, &height, &width, 2, 25);
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);

#ifdef KEY_RESIZE
    if (dialog != 0) {
	(void) wresize(dialog, height, width);
	(void) mvwin(dialog, y, x);
	(void) refresh();
    } else
#endif
    {
	dialog = dlg_new_window(height, width, y, x);
	dlg_register_window(dialog, "yesno", binding);
	dlg_register_buttons(dialog, "yesno", buttons);
    }

    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_bottom_box(dialog);
    dlg_draw_title(dialog, title);

    wattrset(dialog, dialog_attr);
    dlg_print_autowrap(dialog, prompt, height, width);

    dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);

    while (result == DLG_EXIT_UNKNOWN) {
	key = dlg_mouse_wgetch(dialog, &fkey);
	if (dlg_result_key(key, fkey, &result))
	    break;
	if ((code = dlg_char_to_button(key, buttons)) >= 0) {
	    result = dlg_ok_buttoncode(code);
	    break;
	}
	/* handle function keys */
	if (fkey) {
	    switch (key) {
	    case DLGK_FIELD_NEXT:
		button = dlg_next_button(buttons, button);
		if (button < 0)
		    button = 0;
		dlg_draw_buttons(dialog,
				 height - 2, 0,
				 buttons, button,
				 FALSE, width);
		break;
	    case DLGK_FIELD_PREV:
		button = dlg_prev_button(buttons, button);
		if (button < 0)
		    button = 0;
		dlg_draw_buttons(dialog,
				 height - 2, 0,
				 buttons, button,
				 FALSE, width);
		break;
	    case DLGK_ENTER:
		result = dlg_yes_buttoncode(button);
		break;
#ifdef KEY_RESIZE
	    case KEY_RESIZE:
		dlg_clear();
		height = req_high;
		width = req_wide;
		goto restart;
#endif
	    default:
		if (is_DLGK_MOUSE(key)) {
		    result = dlg_yes_buttoncode(key - M_EVENT);
		    if (result < 0)
			result = DLG_EXIT_OK;
		} else {
		    beep();
		}
		break;
	    }
	} else {
	    beep();
	}
    }

    dlg_del_window(dialog);
    dlg_mouse_free_regions();
    free(prompt);
    return result;
}

--- NEW FILE: pause.c ---
/*
 *  $Id: pause.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  pause.c -- implements the pause dialog
 *
 *  Copyright 2004-2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  This is adapted from source contributed by
 *	Yura Kalinichenko
 */

#include <dialog.h>
#include <dlg_keys.h>

#define MY_TIMEOUT 50

/*
 * This is like gauge, but can be interrupted.
 *
 * A pause box displays a meter along the bottom of the box.  The meter
 * indicates how many seconds remain until the end of the pause.  The pause
 * exits when timeout is reached (status OK) or the user presses the Exit
 * button (status CANCEL).
 */
int
dialog_pause(const char *title,
	     const char *cprompt,
	     int height,
	     int width,
	     int seconds)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_ENTER,	' ' ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV,	KEY_UP ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif

    int i, x, y, step;
    int button = 0;
    int seconds_orig;
    WINDOW *dialog;
    const char **buttons = dlg_exit_label();
    int key = 0, fkey;
    int result = DLG_EXIT_UNKNOWN;
    char *prompt = dlg_strclone(cprompt);

    curs_set(0);

    dlg_tab_correct_str(prompt);

    seconds_orig = (seconds > 0) ? seconds : 1;

#ifdef KEY_RESIZE
  retry:
    height = old_height;
    width = old_width;
#endif

    dlg_auto_size(title, prompt, &height, &width, 0, 0);
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    /* center dialog box on screen */
    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);

    dialog = dlg_new_window(height, width, y, x);
    dlg_register_window(dialog, "pause", binding);
    dlg_register_buttons(dialog, "pause", buttons);

    dlg_mouse_setbase(x, y);
    nodelay(dialog, TRUE);

    do {
	(void) werase(dialog);
	dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);

	dlg_draw_title(dialog, title);

	wattrset(dialog, dialog_attr);
	dlg_print_autowrap(dialog, prompt, height, width);

	dlg_draw_box(dialog,
		     height - 6, 2 + MARGIN,
		     2 + MARGIN, width - 2 * (2 + MARGIN),
		     dialog_attr,
		     border_attr);

	/*
	 * Clear the area for the progress bar by filling it with spaces
	 * in the title-attribute, and write the percentage with that
	 * attribute.
	 */
	(void) wmove(dialog, height - 5, 4);
	wattrset(dialog, title_attr);

	for (i = 0; i < (width - 2 * (3 + MARGIN)); i++)
	    (void) waddch(dialog, ' ');

	(void) wmove(dialog, height - 5, (width / 2) - 2);
	(void) wprintw(dialog, "%3d", seconds);

	/*
	 * Now draw a bar in reverse, relative to the background.
	 * The window attribute was useful for painting the background,
	 * but requires some tweaks to reverse it.
	 */
	x = (seconds * (width - 2 * (3 + MARGIN))) / seconds_orig;
	if ((title_attr & A_REVERSE) != 0) {
	    wattroff(dialog, A_REVERSE);
	} else {
	    wattrset(dialog, A_REVERSE);
	}
	(void) wmove(dialog, height - 5, 4);
	for (i = 0; i < x; i++) {
	    chtype ch = winch(dialog);
	    if (title_attr & A_REVERSE) {
		ch &= ~A_REVERSE;
	    }
	    (void) waddch(dialog, ch);
	}

	dlg_draw_bottom_box(dialog);
	mouse_mkbutton(height - 2, width / 2 - 4, 6, '\n');
	dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
	(void) wrefresh(dialog);

	for (step = 0;
	     (result == DLG_EXIT_UNKNOWN) && (step < 1000);
	     step += MY_TIMEOUT) {

	    napms(MY_TIMEOUT);
	    key = dlg_mouse_wgetch_nowait(dialog, &fkey);
	    if (dlg_result_key(key, fkey, &result))
		break;

	    switch (key) {
#ifdef KEY_RESIZE
	    case KEY_RESIZE:
		dlg_clear();	/* fill the background */
		dlg_del_window(dialog);		/* delete this window */
		refresh();	/* get it all onto the terminal */
		goto retry;
#endif
	    case DLGK_FIELD_NEXT:
		button = dlg_next_button(buttons, button);
		if (button < 0)
		    button = 0;
		dlg_draw_buttons(dialog,
				 height - 2, 0,
				 buttons, button,
				 FALSE, width);
		break;
	    case DLGK_FIELD_PREV:
		button = dlg_prev_button(buttons, button);
		if (button < 0)
		    button = 0;
		dlg_draw_buttons(dialog,
				 height - 2, 0,
				 buttons, button,
				 FALSE, width);
		break;
	    case DLGK_ENTER:
		/* Do not use dlg_exit_buttoncode() since we want to return
		 * a cancel rather than ok if the timeout has not expired.
		 */
		result = button ? DLG_EXIT_HELP : DLG_EXIT_CANCEL;
		break;
	    case DLGK_MOUSE(0):
		result = DLG_EXIT_CANCEL;
		break;
	    case DLGK_MOUSE(1):
		result = DLG_EXIT_HELP;
		break;
	    case ERR:
		break;
	    default:
		result = DLG_EXIT_CANCEL;
		break;
	    }
	}
    } while ((result == DLG_EXIT_UNKNOWN) && (seconds-- > 0));

    curs_set(1);
    dlg_mouse_free_regions();
    dlg_del_window(dialog);
    free(prompt);
    return (result);
}

--- NEW FILE: timebox.c ---
/*
 * $Id: timebox.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  timebox.c -- implements the timebox dialog
 *
 * Copyright 2001-2005,2006   Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */

#include <dialog.h>
#include <dlg_keys.h>

#include <time.h>

#define ONE_HIGH 1
#define ONE_WIDE 2
#define BTN_HIGH 2

#define MIN_HIGH (ONE_HIGH + BTN_HIGH + (4 * MARGIN))
#define MIN_WIDE ((3 * (ONE_WIDE + 2 * MARGIN)) + 2 + (2 * MARGIN))

typedef enum {
    sHR = -3
    ,sMN = -2
    ,sSC = -1
} STATES;

struct _box;

typedef int (*BOX_DRAW) (struct _box *, struct tm *);

typedef struct _box {
    WINDOW *parent;
    WINDOW *window;
    int x;
    int y;
    int width;
    int height;
    int period;
    int value;
} BOX;

static int
next_or_previous(int key)
{
    int result = 0;

    switch (key) {
    case DLGK_ITEM_PREV:
	result = -1;
	break;
    case DLGK_ITEM_NEXT:
	result = 1;
	break;
    default:
	beep();
	break;
    }
    return result;
}
/*
 * Draw the hour-of-month selection box
 */
static int
draw_cell(BOX * data)
{
    werase(data->window);
    dlg_draw_box(data->parent,
		 data->y - MARGIN, data->x - MARGIN,
		 data->height + (2 * MARGIN), data->width + (2 * MARGIN),
		 menubox_border_attr, menubox_attr);

    wattrset(data->window, item_attr);
    wprintw(data->window, "%02d", data->value);
    return 0;
}

static int
init_object(BOX * data,
	    WINDOW *parent,
	    int x, int y,
	    int width, int height,
	    int period, int value,
	    int code)
{
    data->parent = parent;
    data->x = x;
    data->y = y;
    data->width = width;
    data->height = height;
    data->period = period;
    data->value = value % period;

    data->window = derwin(data->parent,
			  data->height, data->width,
			  data->y, data->x);
    if (data->window == 0)
	return -1;
    (void) keypad(data->window, TRUE);

    dlg_mouse_setbase(getbegx(parent), getbegy(parent));
    dlg_mouse_mkregion(y, x, height, width, code);

    return 0;
}

#define DrawObject(data) draw_cell(data)

/*
 * Display a dialog box for entering a date
 */
int
dialog_timebox(const char *title,
	       const char *subtitle,
	       int height,
	       int width,
	       int hour,
	       int minute,
	       int second)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	DLG_KEYS_DATA( DLGK_DELETE_RIGHT,KEY_DC ),
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_ENTER,	' ' ),
	DLG_KEYS_DATA( DLGK_FIELD_FIRST,KEY_HOME ),
	DLG_KEYS_DATA( DLGK_FIELD_LAST, KEY_END ),
	DLG_KEYS_DATA( DLGK_FIELD_LAST, KEY_LL ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, CHR_NEXT ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, CHR_BACKSPACE ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, CHR_PREVIOUS ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_DOWN),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_NEXT),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  KEY_NPAGE),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_PPAGE ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_PREVIOUS ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,  KEY_UP ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif
    BOX hr_box, mn_box, sc_box;
    int key = 0, key2, fkey;
    int button;
    int result = DLG_EXIT_UNKNOWN;
    WINDOW *dialog;
    time_t now_time = time((time_t *) 0);
    struct tm current;
    STATES state = dlg_defaultno_button();
    const char **buttons = dlg_ok_labels();
    char *prompt = dlg_strclone(subtitle);
    char buffer[MAX_LEN];

    now_time = time((time_t *) 0);
    current = *localtime(&now_time);

    dlg_does_output();

#ifdef KEY_RESIZE
  retry:
#endif

    dlg_auto_size(title, prompt, &height, &width, 0, 0);
    height += MIN_HIGH;
    if (width < MIN_WIDE)
	width = MIN_WIDE;
    dlg_button_layout(buttons, &width);
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    dialog = dlg_new_window(height, width,
			    dlg_box_y_ordinate(height),
			    dlg_box_x_ordinate(width));
    dlg_register_window(dialog, "timebox", binding);
    dlg_register_buttons(dialog, "timebox", buttons);

    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_bottom_box(dialog);
    dlg_draw_title(dialog, title);

    wattrset(dialog, dialog_attr);
    dlg_print_autowrap(dialog, prompt, height, width);

    /* compute positions of hour, month and year boxes */
    memset(&hr_box, 0, sizeof(hr_box));
    memset(&mn_box, 0, sizeof(mn_box));
    memset(&sc_box, 0, sizeof(sc_box));

    if (init_object(&hr_box,
		    dialog,
		    (width - MIN_WIDE + 1) / 2 + MARGIN,
		    (height - MIN_HIGH + MARGIN),
		    ONE_WIDE,
		    ONE_HIGH,
		    24,
		    hour >= 0 ? hour : current.tm_hour,
		    'H') < 0
	|| DrawObject(&hr_box) < 0)
	return -1;

    mvwprintw(dialog, hr_box.y, hr_box.x + ONE_WIDE + MARGIN, ":");
    if (init_object(&mn_box,
		    dialog,
		    hr_box.x + (ONE_WIDE + 2 * MARGIN + 1),
		    hr_box.y,
		    hr_box.width,
		    hr_box.height,
		    60,
		    minute >= 0 ? minute : current.tm_min,
		    'M') < 0
	|| DrawObject(&mn_box) < 0)
	return -1;

    mvwprintw(dialog, mn_box.y, mn_box.x + ONE_WIDE + MARGIN, ":");
    if (init_object(&sc_box,
		    dialog,
		    mn_box.x + (ONE_WIDE + 2 * MARGIN + 1),
		    mn_box.y,
		    mn_box.width,
		    mn_box.height,
		    60,
		    second >= 0 ? second : current.tm_sec,
		    'S') < 0
	|| DrawObject(&sc_box) < 0)
	return -1;

    while (result == DLG_EXIT_UNKNOWN) {
	BOX *obj = (state == sHR ? &hr_box
		    : (state == sMN ? &mn_box :
		       (state == sSC ? &sc_box : 0)));

	button = (state < 0) ? 0 : state;
	dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
	if (obj != 0)
	    dlg_set_focus(dialog, obj->window);

	key = dlg_mouse_wgetch(dialog, &fkey);
	if (dlg_result_key(key, fkey, &result))
	    break;

	if ((key2 = dlg_char_to_button(key, buttons)) >= 0) {
	    result = key2;
	} else {
	    /* handle function-keys */
	    if (fkey) {
		switch (key) {
		case DLGK_MOUSE(0):
		    result = DLG_EXIT_OK;
		    break;
		case DLGK_MOUSE(1):
		    result = DLG_EXIT_CANCEL;
		    break;
		case DLGK_MOUSE('H'):
		    state = sHR;
		    break;
		case DLGK_MOUSE('M'):
		    state = sMN;
		    break;
		case DLGK_MOUSE('S'):
		    state = sSC;
		    break;
		case DLGK_ENTER:
		    result = button;
		    break;
		case DLGK_FIELD_PREV:
		    state = dlg_prev_ok_buttonindex(state, sHR);
		    break;
		case DLGK_FIELD_NEXT:
		    state = dlg_next_ok_buttonindex(state, sHR);
		    break;
		case DLGK_FIELD_FIRST:
		    if (obj != 0) {
			obj->value = 0;
			(void) DrawObject(obj);
		    }
		    break;
		case DLGK_FIELD_LAST:
		    if (obj != 0) {
			switch (state) {
			case sHR:
			    obj->value = 23;
			    break;
			case sMN:
			case sSC:
			    obj->value = 59;
			    break;
			}
			(void) DrawObject(obj);
		    }
		    break;
		case DLGK_DELETE_RIGHT:
		    if (obj != 0) {
			obj->value /= 10;
			(void) DrawObject(obj);
		    }
		    break;
#ifdef KEY_RESIZE
		case KEY_RESIZE:
		    /* reset data */
		    height = old_height;
		    width = old_width;
		    hour = hr_box.value;
		    minute = mn_box.value;
		    second = sc_box.value;
		    /* repaint */
		    dlg_clear();
		    dlg_del_window(dialog);
		    refresh();
		    dlg_mouse_free_regions();
		    goto retry;
#endif
		default:
		    if (obj != 0) {
			int step = next_or_previous(key);
			if (step != 0) {
			    obj->value += step;
			    while (obj->value < 0)
				obj->value += obj->period;
			    obj->value %= obj->period;
			    (void) DrawObject(obj);
			}
		    }
		    break;
		}
	    } else if (isdigit(key)) {
		if (obj != 0) {
		    int digit = (key - '0');
		    int value = (obj->value * 10) + digit;
		    if (value < obj->period) {
			obj->value = value;
			(void) DrawObject(obj);
		    } else {
			beep();
		    }
		}
	    } else {
		beep();
	    }
	}
    }

    dlg_del_window(dialog);
    sprintf(buffer, "%02d:%02d:%02d\n",
	    hr_box.value, mn_box.value, sc_box.value);
    dlg_add_result(buffer);
    dlg_mouse_free_regions();
    free(prompt);
    return result;
}

--- NEW FILE: rc.c ---
/*
 *  $Id: rc.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  rc.c -- routines for processing the configuration file
 *
 *  Copyright 2000-2004,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors
 *	Savio Lam (lam836 at cs.cuhk.hk)
 */

#include <dialog.h>

#include <dlg_keys.h>

#ifdef HAVE_COLOR
#include <dlg_colors.h>

/*
 * For matching color names with color values
 */
static const color_names_st color_names[] =
{
    {"BLACK", COLOR_BLACK},
    {"RED", COLOR_RED},
    {"GREEN", COLOR_GREEN},
    {"YELLOW", COLOR_YELLOW},
    {"BLUE", COLOR_BLUE},
    {"MAGENTA", COLOR_MAGENTA},
    {"CYAN", COLOR_CYAN},
    {"WHITE", COLOR_WHITE},
};				/* color names */
#define COLOR_COUNT	(sizeof(color_names) / sizeof(color_names[0]))
#endif /* HAVE_COLOR */

#define GLOBALRC "/etc/dialogrc"
#define DIALOGRC ".dialogrc"

/* Types of values */
#define VAL_INT  0
#define VAL_STR  1
#define VAL_BOOL 2

/* Type of line in configuration file */
typedef enum {
    LINE_ERROR = -1,
    LINE_EQUALS,
    LINE_EMPTY
} PARSE_LINE;

/* number of configuration variables */
#define VAR_COUNT        (sizeof(vars) / sizeof(vars_st))

/* check if character is white space */
#define whitespace(c)    (c == ' ' || c == '\t')

/* check if character is string quoting characters */
#define isquote(c)       (c == '"' || c == '\'')

/* get last character of string */
#define lastch(str)      str[strlen(str)-1]

/*
 * Configuration variables
 */
typedef struct {
    const char *name;		/* name of configuration variable as in DIALOGRC */
    void *var;			/* address of actual variable to change */
    int type;			/* type of value */
    const char *comment;	/* comment to put in "rc" file */
} vars_st;

/*
 * This table should contain only references to dialog_state, since dialog_vars
 * is reset specially in dialog.c before each widget.
 */
static const vars_st vars[] =
{
    {"aspect",
     &dialog_state.aspect_ratio,
     VAL_INT,
     "Set aspect-ration."},

    {"separate_widget",
     &dialog_state.separate_str,
     VAL_STR,
     "Set separator (for multiple widgets output)."},

    {"tab_len",
     &dialog_state.tab_len,
     VAL_INT,
     "Set tab-length (for textbox tab-conversion)."},

    {"visit_items",
     &dialog_state.visit_items,
     VAL_BOOL,
     "Make tab-traversal for checklist, etc., include the list."},

#ifdef HAVE_COLOR
    {"use_shadow",
     &dialog_state.use_shadow,
     VAL_BOOL,
     "Shadow dialog boxes? This also turns on color."},

    {"use_colors",
     &dialog_state.use_colors,
     VAL_BOOL,
     "Turn color support ON or OFF"},
#endif				/* HAVE_COLOR */
};				/* vars */

static int
skip_whitespace(char *str, int n)
{
    while (whitespace(str[n]) && str[n] != '\0')
	n++;
    return n;
}

static int
skip_keyword(char *str, int n)
{
    while (isalnum(CharOf(str[n])) && str[n] != '\0')
	n++;
    return n;
}

static int
find_vars(char *name)
{
    int result = -1;
    unsigned i;

    for (i = 0; i < VAR_COUNT; i++) {
	if (dlg_strcmp(vars[i].name, name) == 0) {
	    result = i;
	    break;
	}
    }
    return result;
}

#ifdef HAVE_COLOR
static int
find_color(char *name)
{
    int result = -1;
    int i;
    int limit = dlg_color_count();

    for (i = 0; i < limit; i++) {
	if (dlg_strcmp(dlg_color_table[i].name, name) == 0) {
	    result = i;
	    break;
	}
    }
    return result;
}

/*
 * Convert an attribute to a string representation like this:
 *
 * "(foreground,background,highlight)"
 */
static char *
attr_to_str(char *str, int fg, int bg, int hl)
{
    int i;

    strcpy(str, "(");
    /* foreground */
    for (i = 0; fg != color_names[i].value; i++) ;
    strcat(str, color_names[i].name);
    strcat(str, ",");

    /* background */
    for (i = 0; bg != color_names[i].value; i++) ;
    strcat(str, color_names[i].name);

    /* highlight */
    strcat(str, hl ? ",ON)" : ",OFF)");

    return str;
}

/*
 * Extract the foreground, background and highlight values from an attribute
 * represented as a string in this form:
 *
 * "(foreground,background,highlight)"
 */
static int
str_to_attr(char *str, int *fg, int *bg, int *hl)
{
    int i = 0, get_fg = 1;
    unsigned j;
    char tempstr[MAX_LEN + 1], *part;

    if (str[0] != '(' || lastch(str) != ')')
	return -1;		/* invalid representation */

    /* remove the parenthesis */
    strcpy(tempstr, str + 1);
    lastch(tempstr) = '\0';

    /* get foreground and background */

    while (1) {
	/* skip white space before fg/bg string */
	i = skip_whitespace(tempstr, i);
	if (tempstr[i] == '\0')
	    return -1;		/* invalid representation */
	part = tempstr + i;	/* set 'part' to start of fg/bg string */

	/* find end of fg/bg string */
	while (!whitespace(tempstr[i]) && tempstr[i] != ','
	       && tempstr[i] != '\0')
	    i++;

	if (tempstr[i] == '\0')
	    return -1;		/* invalid representation */
	else if (whitespace(tempstr[i])) {	/* not yet ',' */
	    tempstr[i++] = '\0';

	    /* skip white space before ',' */
	    i = skip_whitespace(tempstr, i);
	    if (tempstr[i] != ',')
		return -1;	/* invalid representation */
	}
	tempstr[i++] = '\0';	/* skip the ',' */
	for (j = 0; j < COLOR_COUNT && dlg_strcmp(part, color_names[j].name);
	     j++) ;
	if (j == COLOR_COUNT)	/* invalid color name */
	    return -1;
	if (get_fg) {
	    *fg = color_names[j].value;
	    get_fg = 0;		/* next we have to get the background */
	} else {
	    *bg = color_names[j].value;
	    break;
	}
    }				/* got foreground and background */

    /* get highlight */

    /* skip white space before highlight string */
    i = skip_whitespace(tempstr, i);
    if (tempstr[i] == '\0')
	return -1;		/* invalid representation */
    part = tempstr + i;		/* set 'part' to start of highlight string */

    /* trim trailing white space from highlight string */
    i = strlen(part) - 1;
    while (whitespace(part[i]) && i > 0)
	i--;
    part[i + 1] = '\0';

    if (!dlg_strcmp(part, "ON"))
	*hl = TRUE;
    else if (!dlg_strcmp(part, "OFF"))
	*hl = FALSE;
    else
	return -1;		/* invalid highlight value */

    return 0;
}
#endif /* HAVE_COLOR */

/*
 * Check if the line begins with a special keyword; if so, return true while
 * pointing params to its parameters.
 */
static int
begins_with(char *line, const char *keyword, char **params)
{
    int i = skip_whitespace(line, 0);
    int j = skip_keyword(line, i);

    if ((j - i) == (int) strlen(keyword)) {
	char save = line[j];
	line[j] = 0;
	if (!dlg_strcmp(keyword, line + i)) {
	    *params = line + skip_whitespace(line, j + 1);
	    return 1;
	}
	line[j] = save;
    }

    return 0;
}

/*
 * Parse a line in the configuration file
 *
 * Each line is of the form:  "variable = value". On exit, 'var' will contain
 * the variable name, and 'value' will contain the value string.
 *
 * Return values:
 *
 * LINE_EMPTY   - line is blank or comment
 * LINE_EQUALS  - line contains "variable = value"
 * LINE_ERROR   - syntax error in line
 */
static PARSE_LINE
parse_line(char *line, char **var, char **value)
{
    int i = 0;

    /* ignore white space at beginning of line */
    i = skip_whitespace(line, i);

    if (line[i] == '\0')	/* line is blank */
	return LINE_EMPTY;
    else if (line[i] == '#')	/* line is comment */
	return LINE_EMPTY;
    else if (line[i] == '=')	/* variable names cannot start with a '=' */
	return LINE_ERROR;

    /* set 'var' to variable name */
    *var = line + i++;		/* skip to next character */

    /* find end of variable name */
    while (!whitespace(line[i]) && line[i] != '=' && line[i] != '\0')
	i++;

    if (line[i] == '\0')	/* syntax error */
	return LINE_ERROR;
    else if (line[i] == '=')
	line[i++] = '\0';
    else {
	line[i++] = '\0';

	/* skip white space before '=' */
	i = skip_whitespace(line, i);

	if (line[i] != '=')	/* syntax error */
	    return LINE_ERROR;
	else
	    i++;		/* skip the '=' */
    }

    /* skip white space after '=' */
    i = skip_whitespace(line, i);

    if (line[i] == '\0')
	return LINE_ERROR;
    else
	*value = line + i;	/* set 'value' to value string */

    /* trim trailing white space from 'value' */
    i = strlen(*value) - 1;
    while (whitespace((*value)[i]) && i > 0)
	i--;
    (*value)[i + 1] = '\0';

    return LINE_EQUALS;		/* no syntax error in line */
}

/*
 * Create the configuration file
 */
void
dlg_create_rc(const char *filename)
{
    unsigned i;
    FILE *rc_file;

    if ((rc_file = fopen(filename, "wt")) == NULL)
	dlg_exiterr("Error opening file for writing in dlg_create_rc().");

    fprintf(rc_file, "#\n\
# Run-time configuration file for dialog\n\
#\n\
# Automatically generated by \"dialog --create-rc <file>\"\n\
#\n\
#\n\
# Types of values:\n\
#\n\
# Number     -  <number>\n\
# String     -  \"string\"\n\
# Boolean    -  <ON|OFF>\n"
#ifdef HAVE_COLOR
	    "\
# Attribute  -  (foreground,background,highlight?)\n"
#endif
	);

    /* Print an entry for each configuration variable */
    for (i = 0; i < VAR_COUNT; i++) {
	fprintf(rc_file, "\n# %s\n", vars[i].comment);
	switch (vars[i].type) {
	case VAL_INT:
	    fprintf(rc_file, "%s = %d\n", vars[i].name,
		    *((int *) vars[i].var));
	    break;
	case VAL_STR:
	    fprintf(rc_file, "%s = \"%s\"\n", vars[i].name,
		    (char *) vars[i].var);
	    break;
	case VAL_BOOL:
	    fprintf(rc_file, "%s = %s\n", vars[i].name,
		    *((bool *) vars[i].var) ? "ON" : "OFF");
	    break;
	}
    }
#ifdef HAVE_COLOR
    for (i = 0; i < (unsigned) dlg_color_count(); ++i) {
	char buffer[MAX_LEN + 1];

	fprintf(rc_file, "\n# %s\n", dlg_color_table[i].comment);
	fprintf(rc_file, "%s = %s\n", dlg_color_table[i].name,
		attr_to_str(buffer,
			    dlg_color_table[i].fg,
			    dlg_color_table[i].bg,
			    dlg_color_table[i].hilite));
    }
#endif /* HAVE_COLOR */
#if 1
    dlg_dump_keys(rc_file);
#endif

    (void) fclose(rc_file);
}

/*
 * Parse the configuration file and set up variables
 */
int
dlg_parse_rc(void)
{
    int i;
    int l = 1;
    PARSE_LINE parse;
    char str[MAX_LEN + 1];
    char *var;
    char *value;
    char *tempptr;
    int result = 0;
    FILE *rc_file = 0;
#if 1
    char *params;
#endif

    /*
     *  At startup, dialog determines the settings to use as follows:
     *
     *  a) if the environment variable $DIALOGRC is set, its value determines
     *     the name of the configuration file.
     *
     *  b) if the file in (a) can't be found, use the file $HOME/.dialogrc
     *     as the configuration file.
     *
     *  c) if the file in (b) can't be found, try using the GLOBALRC file.
     *     Usually this will be /etc/dialogrc.
     *
     *  d) if the file in (c) cannot be found, use the compiled-in defaults.
     */

    /* try step (a) */
    if ((tempptr = getenv("DIALOGRC")) != NULL)
	rc_file = fopen(tempptr, "rt");

    if (tempptr == NULL || rc_file == NULL) {	/* step (a) failed? */
	/* try step (b) */
	if ((tempptr = getenv("HOME")) != NULL
	    && strlen(tempptr) < MAX_LEN - 20) {
	    if (tempptr[0] == '\0' || lastch(tempptr) == '/')
		sprintf(str, "%s%s", tempptr, DIALOGRC);
	    else
		sprintf(str, "%s/%s", tempptr, DIALOGRC);
	    rc_file = fopen(str, "rt");
	}
    }

    if (tempptr == NULL || rc_file == NULL) {	/* step (b) failed? */
	/* try step (c) */
	sprintf(str, "%s", GLOBALRC);
	if ((rc_file = fopen(str, "rt")) == NULL)
	    return 0;		/* step (c) failed, use default values */
    }

    /* Scan each line and set variables */
    while ((result == 0) && (fgets(str, MAX_LEN, rc_file) != NULL)) {
	if (*str == '\0' || lastch(str) != '\n') {
	    /* ignore rest of file if line too long */
	    fprintf(stderr, "\nParse error: line %d of configuration"
		    " file too long.\n", l);
	    result = -1;	/* parse aborted */
	    break;
	}

	lastch(str) = '\0';
	if (begins_with(str, "bindkey", &params)) {
	    dlg_parse_bindkey(params);
	    continue;
	}
	parse = parse_line(str, &var, &value);	/* parse current line */

	switch (parse) {
	case LINE_EMPTY:	/* ignore blank lines and comments */
	    break;
	case LINE_EQUALS:
	    /* search table for matching config variable name */
	    if ((i = find_vars(var)) >= 0) {
		switch (vars[i].type) {
		case VAL_INT:
		    *((int *) vars[i].var) = atoi(value);
		    break;
		case VAL_STR:
		    if (!isquote(value[0]) || !isquote(lastch(value))
			|| strlen(value) < 2) {
			fprintf(stderr, "\nParse error: string value "
				"expected at line %d of configuration "
				"file.\n", l);
			result = -1;	/* parse aborted */
		    } else {
			/* remove the (") quotes */
			value++;
			lastch(value) = '\0';
			strcpy((char *) vars[i].var, value);
		    }
		    break;
		case VAL_BOOL:
		    if (!dlg_strcmp(value, "ON"))
			*((bool *) vars[i].var) = TRUE;
		    else if (!dlg_strcmp(value, "OFF"))
			*((bool *) vars[i].var) = FALSE;
		    else {
			fprintf(stderr, "\nParse error: boolean value "
				"expected at line %d of configuration "
				"file (found %s).\n", l, value);
			result = -1;	/* parse aborted */
		    }
		    break;
		}
#ifdef HAVE_COLOR
	    } else if ((i = find_color(var)) >= 0) {
		int fg = 0;
		int bg = 0;
		int hl = 0;
		if (str_to_attr(value, &fg, &bg, &hl) == -1) {
		    fprintf(stderr, "\nParse error: attribute "
			    "value expected at line %d of configuration "
			    "file.\n", l);
		    result = -1;	/* parse aborted */
		} else {
		    dlg_color_table[i].fg = fg;
		    dlg_color_table[i].bg = bg;
		    dlg_color_table[i].hilite = hl;
		}
	    } else {
#endif /* HAVE_COLOR */
		fprintf(stderr, "\nParse error: unknown variable "
			"at line %d of configuration file:\n\t%s\n", l, var);
		result = -1;	/* parse aborted */
	    }
	    break;
	case LINE_ERROR:
	    fprintf(stderr, "\nParse error: syntax error at line %d of "
		    "configuration file.\n", l);
	    result = -1;	/* parse aborted */
	    break;
	}
	l++;			/* next line */
    }

    (void) fclose(rc_file);
    return result;
}

--- NEW FILE: menubox.c ---
/*
 *  $Id: menubox.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  menubox.c -- implements the menu box
 *
 *  Copyright 2000-2005,2006	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors
 *	Savio Lam (lam836 at cs.cuhk.hk)
 */

#include <dialog.h>
#include <dlg_keys.h>

static int menu_width, tag_x, item_x;

typedef enum {
    Unselected = 0,
    Selected,
    Editing
} Mode;

#define INPUT_ROWS     3	/* rows per inputmenu entry */

#define LLEN(n) ((n) * MENUBOX_TAGS)
#define ItemName(i)    items[LLEN(i)]
#define ItemText(i)    items[LLEN(i) + 1]
#define ItemHelp(i)    items[LLEN(i) + 2]

#define RowHeight(i) (is_inputmenu ? ((i) * INPUT_ROWS) : ((i) * 1))
#define ItemToRow(i) (is_inputmenu ? ((i) * INPUT_ROWS + 1) : (i))
#define RowToItem(i) (is_inputmenu ? ((i) / INPUT_ROWS + 0) : (i))

static void
print_arrows(WINDOW *win,
	     int box_x,
	     int box_y,
	     int scrollamt,
	     int max_choice,
	     int item_no,
	     int menu_height)
{
    dlg_draw_arrows(win, scrollamt,
		    scrollamt + max_choice < item_no,
		    box_x + tag_x + 1,
		    box_y,
		    box_y + menu_height + 1);
}

/*
 * Print the tag of a menu-item
 */
static void
print_tag(WINDOW *win,
	  DIALOG_LISTITEM * item,
	  int choice,
	  Mode selected,
	  bool is_inputmenu)
{
    int my_x = item_x;
    int my_y = ItemToRow(choice);
    int tag_width = (my_x - tag_x - GUTTER);
    const int *cols;
    const int *indx;
    int limit;
    unsigned prefix;

    cols = dlg_index_columns(item->name);
    indx = dlg_index_wchars(item->name);
    limit = dlg_count_wchars(item->name);
    prefix = indx[1] - indx[0];

    /* highlight first char of the tag to be special */
    (void) wmove(win, my_y, tag_x);
    wattrset(win, selected ? tag_key_selected_attr : tag_key_attr);
    if (strlen(item->name) != 0)
	(void) waddnstr(win, item->name, prefix);
    /* print rest of the string */
    wattrset(win, selected ? tag_selected_attr : tag_attr);
    if (strlen(item->name) > prefix) {
	limit = dlg_limit_columns(item->name, tag_width, 1);
	if (limit > 0)
	    (void) waddnstr(win, item->name + indx[1], indx[limit] - indx[1]);
    }
}

/*
 * Print menu item
 */
static void
print_item(WINDOW *win,
	   DIALOG_LISTITEM * items,
	   int choice,
	   Mode selected,
	   bool is_inputmenu)
{
    chtype save = getattrs(win);
    int n;
    int my_width = menu_width;
    int my_x = item_x;
    int my_y = ItemToRow(choice);
    chtype attr = A_NORMAL;
    chtype textchar;
    chtype bordchar;

    switch (selected) {
    default:
    case Unselected:
	textchar = item_attr;
	bordchar = item_attr;
	break;
    case Selected:
	textchar = item_selected_attr;
	bordchar = item_selected_attr;
	break;
    case Editing:
	textchar = inputbox_attr;
	bordchar = dialog_attr;
	break;
    }

    /* Clear 'residue' of last item and mark current current item */
    if (is_inputmenu) {
	wattrset(win, (selected != Unselected) ? item_selected_attr : item_attr);
	for (n = my_y - 1; n < my_y + INPUT_ROWS - 1; n++) {
	    wmove(win, n, 0);
	    wprintw(win, "%*s", my_width, " ");
	}
    } else {
	wattrset(win, menubox_attr);
	wmove(win, my_y, 0);
	wprintw(win, "%*s", my_width, " ");
    }

    print_tag(win, items, choice, selected, is_inputmenu);

    /* Draw the input field box (only for inputmenu) */
    (void) wmove(win, my_y, my_x);
    if (is_inputmenu) {
	my_width -= 1;
	dlg_draw_box(win, my_y - 1, my_x, INPUT_ROWS, my_width - my_x - tag_x,
		     bordchar,
		     bordchar);
	my_width -= 1;
	++my_x;
    }

    /* print actual item */
    wmove(win, my_y, my_x);
    wattrset(win, textchar);
    dlg_print_text(win, items->text, my_width - my_x, &attr);

    if (selected) {
	dlg_item_help(items->help);
    }
    wattrset(win, save);
}

/*
 * Allow the user to edit the text of a menu entry.
 */
static int
input_menu_edit(WINDOW *win,
		DIALOG_LISTITEM * items,
		int choice,
		char **resultp)
{
    chtype save = getattrs(win);
    char *result;
    int offset = 0;
    int key = 0, fkey = 0;
    int first = TRUE;
    /* see above */
    bool is_inputmenu = TRUE;
    int y = ItemToRow(choice);
    int code = TRUE;
    int max_len = MAX(strlen(items->text) + 1, MAX_LEN);

    if (dialog_vars.max_input != 0 && dialog_vars.max_input < MAX_LEN)
	max_len = dialog_vars.max_input;

    result = malloc(max_len);
    assert_ptr(result, "input_menu_edit");

    /* original item is used to initialize the input string. */
    result[0] = '\0';
    strcpy(result, items->text);

    print_item(win, items, choice, Editing, TRUE);

    /* taken out of inputbox.c - but somewhat modified */
    for (;;) {
	if (!first)
	    key = dlg_mouse_wgetch(win, &fkey);
	if (dlg_edit_string(result, &offset, key, fkey, first)) {
	    dlg_show_string(win, result, offset, inputbox_attr,
			    y, item_x + 1, menu_width - item_x - 3,
			    FALSE, first);
	    first = FALSE;
	} else if (key == ESC || key == TAB) {
	    code = FALSE;
	    break;
	} else {
	    break;
	}
    }
    print_item(win, items, choice, Selected, TRUE);
    wattrset(win, save);

    *resultp = result;
    return code;
}

static int
handle_button(int code, DIALOG_LISTITEM * items, int choice)
{
    switch (code) {
    case DLG_EXIT_OK:		/* FALLTHRU */
    case DLG_EXIT_EXTRA:
	dlg_add_result(items[choice].name);
	break;
    case DLG_EXIT_HELP:
	dlg_add_result("HELP ");
	if (USE_ITEM_HELP(items[choice].help)) {
	    dlg_add_result(items[choice].help);
	    code = DLG_EXIT_ITEM_HELP;
	} else {
	    dlg_add_result(items[choice].name);
	}
	break;
    }
    return code;
}

static int
dlg_renamed_menutext(DIALOG_LISTITEM * items, int current, char *newtext)
{
    if (dialog_vars.input_result)
	dialog_vars.input_result[0] = '\0';
    dlg_add_result("RENAMED ");
    dlg_add_result(items[current].name);
    dlg_add_result(" ");
    dlg_add_result(newtext);
    return DLG_EXIT_EXTRA;
}

static int
dlg_dummy_menutext(DIALOG_LISTITEM * items, int current, char *newtext)
{
    (void) items;
    (void) current;
    (void) newtext;
    return DLG_EXIT_ERROR;
}

/*
 * This is an alternate interface to 'menu' which allows the application
 * to read the list item states back directly without putting them in the
 * output buffer.
 */
int
dlg_menu(const char *title,
	 const char *cprompt,
	 int height,
	 int width,
	 int menu_height,
	 int item_no,
	 DIALOG_LISTITEM * items,
	 int *current_item,
	 DIALOG_INPUTMENU rename_menutext)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	' ' ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	KEY_RIGHT ),
	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	TAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV,	KEY_BTAB ),
	DLG_KEYS_DATA( DLGK_FIELD_PREV,	KEY_LEFT ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	'+' ),
	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	KEY_DOWN ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,	'-' ),
	DLG_KEYS_DATA( DLGK_ITEM_PREV,	KEY_UP ),
	DLG_KEYS_DATA( DLGK_PAGE_FIRST,	KEY_HOME ),
	DLG_KEYS_DATA( DLGK_PAGE_LAST,	KEY_END ),
	DLG_KEYS_DATA( DLGK_PAGE_LAST,	KEY_LL ),
	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	KEY_NPAGE ),
	DLG_KEYS_DATA( DLGK_PAGE_PREV,	KEY_PPAGE ),
	END_KEYS_BINDING
    };
    static DLG_KEYS_BINDING binding2[] = {
	INPUTSTR_BINDINGS,
	ENTERKEY_BINDINGS,
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif
    int i, j, x, y, cur_x, cur_y, box_x, box_y;
    int key = 0, fkey;
    int button = dialog_state.visit_items ? -1 : dlg_defaultno_button();
    int choice = dlg_default_listitem(items);
    int result = DLG_EXIT_UNKNOWN;
    int scrollamt = 0;
    int max_choice, min_width;
    int found;
    int use_width, name_width, text_width;
    WINDOW *dialog, *menu;
    char *prompt = dlg_strclone(cprompt);
    const char **buttons = dlg_ok_labels();
    bool is_inputmenu = (rename_menutext == dlg_renamed_menutext);

    dlg_does_output();
    dlg_tab_correct_str(prompt);

#ifdef KEY_RESIZE
  retry:
#endif

    if (menu_height == 0) {
	min_width = dlg_calc_list_width(item_no, items) + 10;
	/* calculate height without items (4) */
	dlg_auto_size(title, prompt, &height, &width, 4, MAX(26, min_width));
	dlg_calc_listh(&height, &menu_height, item_no);
    } else {
	dlg_auto_size(title, prompt, &height, &width, 4 + menu_height, 26);
    }
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    /* Find out maximal number of displayable items at once. */
    max_choice = MIN(menu_height,
		     RowHeight(item_no));
    if (is_inputmenu)
	max_choice /= INPUT_ROWS;

    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);

    dialog = dlg_new_window(height, width, y, x);
    dlg_register_window(dialog, "menubox", binding);
    dlg_register_buttons(dialog, "menubox", buttons);

    dlg_mouse_setbase(x, y);

    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_bottom_box(dialog);
    dlg_draw_title(dialog, title);

    wattrset(dialog, dialog_attr);
    dlg_print_autowrap(dialog, prompt, height, width);

    menu_width = width - 6;
    getyx(dialog, cur_y, cur_x);
    box_y = cur_y + 1;
    box_x = (width - menu_width) / 2 - 1;

    /* create new window for the menu */
    menu = dlg_sub_window(dialog, menu_height, menu_width,
			  y + box_y + 1,
			  x + box_x + 1);
    dlg_register_window(menu, "menu", binding2);
    dlg_register_buttons(menu, "menu", buttons);

    /* draw a box around the menu items */
    dlg_draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2,
		 menubox_border_attr, menubox_attr);

    name_width = 0;
    text_width = 0;

    /* Find length of longest item to center menu  *
     * only if --menu was given, using --inputmenu *
     * won't be centered.                         */
    for (i = 0; i < item_no; i++) {
	name_width = MAX(name_width, dlg_count_columns(items[i].name));
	text_width = MAX(text_width, dlg_count_columns(items[i].text));
    }

    /* If the name+text is wider than the list is allowed, then truncate
     * one or both of them.  If the name is no wider than 30% of the list,
     * leave it intact.
     *
     * FIXME: the gutter width and name/list ratio should be configurable.
     */
    use_width = (menu_width - GUTTER);
    if (text_width + name_width > use_width) {
	int need = (int) (0.30 * use_width);
	if (name_width > need) {
	    int want = (int) (use_width
			      * ((double) name_width)
			      / (text_width + name_width));
	    name_width = (want > need) ? want : need;
	}
	text_width = use_width - name_width;
    }

    tag_x = (is_inputmenu
	     ? 0
	     : (use_width - text_width - name_width) / 2);
    item_x = name_width + tag_x + GUTTER;

    if (choice - scrollamt >= max_choice) {
	scrollamt = choice - (max_choice - 1);
	choice = max_choice - 1;
    }

    /* Print the menu */
    for (i = 0; i < max_choice; i++) {
	print_item(menu,
		   &items[i + scrollamt],
		   i,
		   (i == choice) ? Selected : Unselected,
		   is_inputmenu);
    }
    (void) wnoutrefresh(menu);

    /* register the new window, along with its borders */
    dlg_mouse_mkbigregion(box_y + 1, box_x, menu_height + 2, menu_width + 2,
			  KEY_MAX, 1, 1, 1 /* by lines */ );

    print_arrows(dialog, box_x, box_y, scrollamt, max_choice, item_no, menu_height);

    dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);

    while (result == DLG_EXIT_UNKNOWN) {
	if (button < 0)		/* --visit-items */
	    wmove(dialog, box_y + ItemToRow(choice) + 1, box_x + tag_x + 1);

	key = dlg_mouse_wgetch(dialog, &fkey);
	if (dlg_result_key(key, fkey, &result))
	    break;

	found = FALSE;
	if (fkey) {
	    /*
	     * Allow a mouse-click on a box to switch selection to that box.
	     * Handling a button click is a little more complicated, since we
	     * push a KEY_ENTER back onto the input stream so we'll put the
	     * cursor at the right place before handling the "keypress".
	     */
	    if (key >= DLGK_MOUSE(KEY_MAX)) {
		key -= DLGK_MOUSE(KEY_MAX);
		i = RowToItem(key);
		if (i < max_choice) {
		    found = TRUE;
		} else {
		    beep();
		    continue;
		}
	    } else if (is_DLGK_MOUSE(key)
		       && dlg_ok_buttoncode(key - M_EVENT) >= 0) {
		button = (key - M_EVENT);
		ungetch('\n');
		continue;
	    }
	} else {
	    /*
	     * Check if key pressed matches first character of any item tag in
	     * list.  If there is more than one match, we will cycle through
	     * each one as the same key is pressed repeatedly.
	     */
	    if (button < 0 || !dialog_state.visit_items) {
		for (j = scrollamt + choice + 1; j < item_no; j++) {
		    if (dlg_match_char(dlg_last_getc(), items[j].name)) {
			found = TRUE;
			i = j - scrollamt;
			break;
		    }
		}
		if (!found) {
		    for (j = 0; j <= scrollamt + choice; j++) {
			if (dlg_match_char(dlg_last_getc(), items[j].name)) {
			    found = TRUE;
			    i = j - scrollamt;
			    break;
			}
		    }
		}
		if (found)
		    dlg_flush_getc();
	    } else if ((j = dlg_char_to_button(key, buttons)) >= 0) {
		button = j;
		ungetch('\n');
		continue;
	    }

	    /*
	     * A single digit (1-9) positions the selection to that line in the
	     * current screen.
	     */
	    if (!found
		&& (key <= '9')
		&& (key > '0')
		&& (key - '1' < max_choice)) {
		found = TRUE;
		i = key - '1';
	    }
	}

	if (!found && fkey) {
	    found = TRUE;
	    switch (key) {
	    case DLGK_PAGE_FIRST:
		i = -scrollamt;
		break;
	    case DLGK_PAGE_LAST:
		i = item_no - 1 - scrollamt;
		break;
	    case DLGK_MOUSE(KEY_PPAGE):
	    case DLGK_PAGE_PREV:
		if (choice)
		    i = 0;
		else if (scrollamt != 0)
		    i = -MIN(scrollamt, max_choice);
		else
		    continue;
		break;
	    case DLGK_MOUSE(KEY_NPAGE):
	    case DLGK_PAGE_NEXT:
		i = MIN(choice + max_choice, item_no - scrollamt - 1);
		break;
	    case DLGK_ITEM_PREV:
		i = choice - 1;
		if (choice == 0 && scrollamt == 0)
		    continue;
		break;
	    case DLGK_ITEM_NEXT:
		i = choice + 1;
		if (scrollamt + choice >= item_no - 1)
		    continue;
		break;
	    default:
		found = FALSE;
		break;
	    }
	}

	if (found) {
	    if (i != choice) {
		getyx(dialog, cur_y, cur_x);
		if (i < 0 || i >= max_choice) {
#if defined(NCURSES_VERSION_MAJOR) && NCURSES_VERSION_MAJOR < 5
		    /*
		     * Using wscrl to assist ncurses scrolling is not needed
		     * in version 5.x
		     */
		    if (i == -1) {
			if (menu_height > 1) {
			    /* De-highlight current first item */
			    print_item(menu,
				       &items[scrollamt],
				       0, Unselected, is_inputmenu);
			    scrollok(menu, TRUE);
			    wscrl(menu, -RowHeight(1));
			    scrollok(menu, FALSE);
			}
			scrollamt--;
			print_item(menu,
				   &items[scrollamt],
				   0, Selected, is_inputmenu);
		    } else if (i == max_choice) {
			if (menu_height > 1) {
			    /* De-highlight current last item before scrolling up */
			    print_item(menu,
				       &items[scrollamt + max_choice - 1],
				       max_choice - 1,
				       Unselected,
				       is_inputmenu);
			    scrollok(menu, TRUE);
			    wscrl(menu, RowHeight(1));
			    scrollok(menu, FALSE);
			}
			scrollamt++;
			print_item(menu,
				   &items[scrollamt + max_choice - 1],
				   max_choice - 1, TRUE,
				   is_inputmenu);
		    } else
#endif
		    {
			if (i < 0) {
			    scrollamt += i;
			    choice = 0;
			} else {
			    choice = max_choice - 1;
			    scrollamt += (i - max_choice + 1);
			}
			for (i = 0; i < max_choice; i++) {
			    print_item(menu,
				       &items[scrollamt + i],
				       i,
				       (i == choice) ? Selected : Unselected,
				       is_inputmenu);
			}
		    }
		    /* Clean bottom lines */
		    if (is_inputmenu) {
			int spare_lines, x_count;
			spare_lines = menu_height % INPUT_ROWS;
			wattrset(menu, menubox_attr);
			for (; spare_lines; spare_lines--) {
			    wmove(menu, menu_height - spare_lines, 0);
			    for (x_count = 0; x_count < menu_width;
				 x_count++) {
				waddch(menu, ' ');
			    }
			}
		    }
		    (void) wnoutrefresh(menu);
		    print_arrows(dialog,
				 box_x, box_y,
				 scrollamt, max_choice, item_no, menu_height);
		} else {
		    /* De-highlight current item */
		    print_item(menu,
			       &items[scrollamt + choice],
			       choice,
			       Unselected,
			       is_inputmenu);
		    /* Highlight new item */
		    choice = i;
		    print_item(menu,
			       &items[scrollamt + choice],
			       choice,
			       Selected,
			       is_inputmenu);
		    (void) wnoutrefresh(menu);
		    (void) wmove(dialog, cur_y, cur_x);
		    wrefresh(dialog);
		}
	    }
	    continue;		/* wait for another key press */
	}

	if (fkey) {
	    switch (key) {
	    case DLGK_FIELD_PREV:
		button = dlg_prev_button(buttons, button);
		dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
				 FALSE, width);
		break;
	    case DLGK_FIELD_NEXT:
		button = dlg_next_button(buttons, button);
		dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
				 FALSE, width);
		break;
	    case DLGK_ENTER:
		result = dlg_ok_buttoncode(button);

		/*
		 * If dlg_menu() is called from dialog_menu(), we want to
		 * capture the results into dialog_vars.input_result, but not
		 * if dlg_menu() is called directly from an application.  We
		 * can check this by testing if rename_menutext is the function
		 * pointer owned by dialog_menu().  It would be nicer to have
		 * this logic inside dialog_menu(), but that cannot be done
		 * since we would lose compatibility for the results reported
		 * after input_menu_edit().
		 */
		if (result == DLG_EXIT_ERROR) {
		    result = DLG_EXIT_UNKNOWN;
		} else if (is_inputmenu
			   || rename_menutext == dlg_dummy_menutext) {
		    result = handle_button(result,
					   items,
					   scrollamt + choice);
		}

		/*
		 * If we have a rename_menutext function, interpret the Extra
		 * button as a request to rename the menu's text.  If that
		 * function doesn't return "Unknown", we will exit from this
		 * function.  Usually that is done for dialog_menu(), so the
		 * shell script can use the updated value.  If it does return
		 * "Unknown", update the list item only.  A direct caller of
		 * dlg_menu() can free the renamed value - we cannot.
		 */
		if (is_inputmenu && result == DLG_EXIT_EXTRA) {
		    char *tmp;

		    if (input_menu_edit(menu,
					&items[scrollamt + choice],
					choice,
					&tmp)) {
			result = rename_menutext(items, scrollamt + choice, tmp);
			if (result == DLG_EXIT_UNKNOWN) {
			    items[scrollamt + choice].text = tmp;
			} else {
			    free(tmp);
			}
		    } else {
			result = DLG_EXIT_UNKNOWN;
			print_item(menu,
				   &items[scrollamt + choice],
				   choice,
				   Selected,
				   is_inputmenu);
			(void) wnoutrefresh(menu);
			free(tmp);
		    }

		    if (result == DLG_EXIT_UNKNOWN) {
			dlg_draw_buttons(dialog, height - 2, 0,
					 buttons, button, FALSE, width);
		    }
		}
		break;
#ifdef KEY_RESIZE
	    case KEY_RESIZE:
		/* reset data */
		height = old_height;
		width = old_width;
		/* repaint */
		dlg_clear();
		dlg_del_window(dialog);
		refresh();
		dlg_mouse_free_regions();
		goto retry;
#endif
	    default:
		flash();
		break;
	    }
	}
    }

    dlg_mouse_free_regions();
    dlg_unregister_window(menu);
    dlg_del_window(dialog);
    free(prompt);

    *current_item = scrollamt + choice;
    return result;
}

/*
 * Display a menu for choosing among a number of options
 */
int
dialog_menu(const char *title,
	    const char *cprompt,
	    int height,
	    int width,
	    int menu_height,
	    int item_no,
	    char **items)
{
    int result;
    int choice;
    int i;
    DIALOG_LISTITEM *listitems;

    listitems = calloc(item_no + 1, sizeof(*listitems));
    assert_ptr(listitems, "dialog_menu");

    for (i = 0; i < item_no; ++i) {
	listitems[i].name = ItemName(i);
	listitems[i].text = ItemText(i);
	listitems[i].help = (dialog_vars.item_help) ? ItemHelp(i) : "";
    }

    result = dlg_menu(title,
		      cprompt,
		      height,
		      width,
		      menu_height,
		      item_no,
		      listitems,
		      &choice,
		      dialog_vars.input_menu ? dlg_renamed_menutext : dlg_dummy_menutext);

    free(listitems);
    return result;
}

--- NEW FILE: tailbox.c ---
/*
 *  $Id: tailbox.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  tailbox.c -- implements the tail box
 *
 *  Copyright 2000-2004,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 *
 *  An earlier version of this program lists as authors
 *	Pasquale De Marco (demarco_p at abramo.it)
 */

#include <dialog.h>
#include <dlg_keys.h>

typedef struct {
    DIALOG_CALLBACK obj;
    WINDOW *text;
    const char **buttons;
    int hscroll;
    int old_hscroll;
    char line[MAX_LEN + 1];
} MY_OBJ;

/*
 * Return current line of text.
 */
static char *
get_line(MY_OBJ * obj)
{
    FILE *fp = obj->obj.input;
    int col = -(obj->hscroll);
    int j, tmpint, ch;

    do {
	if (((ch = getc(fp)) == EOF) && !feof(fp))
	    dlg_exiterr("Error moving file pointer in get_line().");
	else if (!feof(fp) && (ch != '\n')) {
	    if ((ch == TAB) && (dialog_vars.tab_correct)) {
		tmpint = dialog_state.tab_len
		    - ((col + obj->hscroll) % dialog_state.tab_len);
		for (j = 0; j < tmpint; j++) {
		    if (col >= 0 && col < MAX_LEN)
			obj->line[col] = ' ';
		    ++col;
		}
	    } else {
		if (col >= 0)
		    obj->line[col] = ch;
		++col;
	    }
	    if (col >= MAX_LEN)
		break;
	}
    } while (!feof(fp) && (ch != '\n'));

    if (col < 0)
	col = 0;
    obj->line[col] = '\0';

    return obj->line;
}

/*
 * Print a new line of text.
 */
static void
print_line(MY_OBJ * obj, WINDOW *win, int row, int width)
{
    int i, y, x;
    char *line = get_line(obj);

    (void) wmove(win, row, 0);	/* move cursor to correct line */
    (void) waddch(win, ' ');
#ifdef NCURSES_VERSION
    (void) waddnstr(win, line, MIN((int) strlen(line), width - 2));
#else
    line[MIN((int) strlen(line), width - 2)] = '\0';
    waddstr(win, line);
#endif

    getyx(win, y, x);
    /* Clear 'residue' of previous line */
    for (i = 0; i < width - x; i++)
	(void) waddch(win, ' ');
}

/*
 * Go back 'target' lines in text file.  BUFSIZ has to be in 'size_t' range.
 */
static void
last_lines(MY_OBJ * obj, int target)
{
    FILE *fp = obj->obj.input;
    int inx;
    int count = 0;
    char buf[BUFSIZ + 1];
    size_t size_to_read;
    size_t offset = 0;
    long fpos = 0;

    if (fseek(fp, 0, SEEK_END) == -1
	|| (fpos = ftell(fp)) < 0)
	dlg_exiterr("Error moving file pointer in last_lines().");

    if (fpos != 0) {
	++target;
	for (;;) {
	    if (fpos >= BUFSIZ) {
		size_to_read = (size_t) BUFSIZ;
	    } else {
		size_to_read = ((size_t) fpos);
	    }
	    fpos -= size_to_read;
	    if (fseek(fp, fpos, SEEK_SET) == -1)
		dlg_exiterr("Error moving file pointer in last_lines().");
	    (void) fread(buf, size_to_read, 1, fp);
	    if (ferror(fp))
		dlg_exiterr("Error reading file in last_lines().");

	    offset += size_to_read;
	    for (inx = size_to_read - 1; inx >= 0; --inx) {
		if (buf[inx] == '\n') {
		    if (++count > target)
			break;
		    offset = inx + 1;
		}
	    }

	    if (count > target) {
		break;
	    } else if (fpos == 0) {
		offset = 0;
		break;
	    }
	}

	if (fseek(fp, fpos + offset, SEEK_SET) == -1)
	    dlg_exiterr("Error moving file pointer in last_lines().");
    }
}

/*
 * Print a new page of text.
 */
static void
print_page(MY_OBJ * obj, int height, int width)
{
    int i;

    for (i = 0; i < height; i++) {
	print_line(obj, obj->text, i, width);
    }
    (void) wnoutrefresh(obj->text);
}

static void
print_last_page(MY_OBJ * obj)
{
    int high = getmaxy(obj->obj.win) - (2 * MARGIN + (obj->obj.bg_task ? 1 : 3));
    int wide = getmaxx(obj->text);

    last_lines(obj, high);
    print_page(obj, high, wide);
}

static void
repaint_text(MY_OBJ * obj)
{
    int cur_y, cur_x;

    getyx(obj->obj.win, cur_y, cur_x);
    obj->old_hscroll = obj->hscroll;
    print_last_page(obj);
    (void) wmove(obj->obj.win, cur_y, cur_x);	/* Restore cursor position */
    wrefresh(obj->obj.win);
}

static bool
handle_my_getc(DIALOG_CALLBACK * cb, int ch, int fkey, int *result)
{
    MY_OBJ *obj = (MY_OBJ *) cb;
    bool done = FALSE;

    if (!fkey && dlg_char_to_button(ch, obj->buttons) == 0) {
	ch = DLGK_ENTER;
	fkey = TRUE;
    }

    if (fkey) {
	switch (ch) {
	case DLGK_ENTER:
	    *result = DLG_EXIT_OK;
	    done = TRUE;
	    break;
	case DLGK_BEGIN:	/* Beginning of line */
	    obj->hscroll = 0;
	    break;
	case DLGK_GRID_LEFT:	/* Scroll left */
	    if (obj->hscroll > 0) {
		obj->hscroll -= 1;
	    }
	    break;
	case DLGK_GRID_RIGHT:	/* Scroll right */
	    if (obj->hscroll < MAX_LEN)
		obj->hscroll += 1;
	    break;
	default:
	    beep();
	    break;
	}
    } else {
	switch (ch) {
	case ERR:
	    ch = getc(cb->input);
	    (void) ungetc(ch, cb->input);
	    if ((ch != EOF) || (obj->hscroll != obj->old_hscroll)) {
		repaint_text(obj);
	    }
	    break;
	case ESC:
	    done = TRUE;
	    *result = DLG_EXIT_ESC;
	    break;
	default:
	    beep();
	    break;
	}
    }

    return !done;
}

/*
 * Display text from a file in a dialog box, like in a "tail -f".
 */
int
dialog_tailbox(const char *title, const char *file, int height, int width, int bg_task)
{
    /* *INDENT-OFF* */
    static DLG_KEYS_BINDING binding[] = {
	ENTERKEY_BINDINGS,
	DLG_KEYS_DATA( DLGK_BEGIN,      '0' ),
	DLG_KEYS_DATA( DLGK_BEGIN,      KEY_BEG ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,  'H' ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,  'h' ),
	DLG_KEYS_DATA( DLGK_GRID_LEFT,  KEY_LEFT ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'L' ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'l' ),
	DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHT ),
	END_KEYS_BINDING
    };
    /* *INDENT-ON* */

#ifdef KEY_RESIZE
    int old_height = height;
    int old_width = width;
#endif
    int fkey;
    int x, y, result, thigh;
    WINDOW *dialog, *text;
    const char **buttons = 0;
    MY_OBJ *obj;
    FILE *fd;

    /* Open input file for reading */
    if ((fd = fopen(file, "rb")) == NULL)
	dlg_exiterr("Can't open input file in dialog_tailbox().");

#ifdef KEY_RESIZE
  retry:
#endif
    dlg_auto_sizefile(title, file, &height, &width, 2, 12);
    dlg_print_size(height, width);
    dlg_ctl_size(height, width);

    x = dlg_box_x_ordinate(width);
    y = dlg_box_y_ordinate(height);
    thigh = height - ((2 * MARGIN) + (bg_task ? 0 : 2));

    dialog = dlg_new_window(height, width, y, x);

    dlg_mouse_setbase(x, y);

    /* Create window for text region, used for scrolling text */
    text = dlg_sub_window(dialog,
			  thigh,
			  width - (2 * MARGIN),
			  y + MARGIN,
			  x + MARGIN);

    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
    dlg_draw_bottom_box(dialog);
    dlg_draw_title(dialog, title);

    if (!bg_task) {
	buttons = dlg_exit_label();
	dlg_draw_buttons(dialog, height - (2 * MARGIN), 0, buttons, FALSE,
			 FALSE, width);
    }

    (void) wmove(dialog, thigh, (MARGIN + 1));
    (void) wnoutrefresh(dialog);

    obj = (MY_OBJ *) calloc(1, sizeof(MY_OBJ));
    assert_ptr(obj, "dialog_tailbox");

    obj->obj.input = fd;
    obj->obj.win = dialog;
    obj->obj.handle_getc = handle_my_getc;
    obj->obj.keep_bg = bg_task && dialog_vars.cant_kill;
    obj->obj.bg_task = bg_task;
    obj->text = text;
    obj->buttons = buttons;
    dlg_add_callback(&(obj->obj));

    dlg_register_window(dialog, "tailbox", binding);
    dlg_register_buttons(dialog, "tailbox", buttons);

    /* Print last page of text */
    dlg_attr_clear(text, thigh, getmaxx(text), dialog_attr);
    repaint_text(obj);

    if (bg_task) {
	result = DLG_EXIT_OK;
    } else {
	int ch;
	do {
	    ch = dlg_getc(dialog, &fkey);
#ifdef KEY_RESIZE
	    if (fkey && ch == KEY_RESIZE) {
		/* reset data */
		height = old_height;
		width = old_width;
		/* repaint */
		dlg_clear();
		dlg_del_window(dialog);
		refresh();
		dlg_mouse_free_regions();
		goto retry;
	    }
#endif
	}
	while (handle_my_getc(&(obj->obj), ch, fkey, &result));
    }
    dlg_mouse_free_regions();
    return result;
}

--- NEW FILE: dialog.pl ---
# Functions that handle calling dialog(1) -*-perl-*-
# $Id: dialog.pl,v 1.1 2006-10-23 20:59:26 stsp Exp $

# Return values are 1 for success and 0 for failure (or cancel)
# Resultant text (if any) is in dialog_result

# Unfortunately, the gauge requires use of /bin/sh to get going.
# I didn't bother to make the others shell-free, although it
# would be simple to do.

# Note that dialog generally returns 0 for success, so I invert the
# sense of the return code for more readable boolean expressions.

$scr_lines = 24;

require "flush.pl";

sub rhs_clear {
    return system("dialog --clear");
}

sub rhs_textbox {
    local ( $title, $file, $width, $height ) = @_;

    system("dialog --title \"$title\" --textbox $file $height $width");

    return 1;
}

sub rhs_msgbox {
    local ( $title, $message, $width ) = @_;
    local ( $tmp, $height, $message_len );

    $message = &rhs_wordwrap($message, $width);
    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }
    $height = 4 + $message_len;

    $tmp = system("dialog --title \"$title\" --msgbox \"$message\" $height $width");
    if ($tmp) {
	return 0;
    } else {
	return 1;
    }
}

sub rhs_infobox {
    local ( $title, $message, $width ) = @_;
    local ( $tmp, $height, $message_len );

    $message = &rhs_wordwrap($message, $width);
    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }
    $height = 2 + $message_len;

    return system("dialog --title \"$title\" --infobox \"$message\" $height $width");
}

sub rhs_yesno {
    local ( $title, $message, $width ) = @_;
    local ( $tmp, $height, $message_len );

    $message = &rhs_wordwrap($message, $width);
    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }
    $height = 4 + $message_len;

    $tmp = system("dialog --title \"$title\" --yesno \"$message\" $height $width");
    # Dumb: dialog returns 0 for "yes" and 1 for "no"
    if (! $tmp) {
	return 1;
    } else {
	return 0;
    }
}

sub rhs_gauge {
    local ( $title, $message, $width, $percent ) = @_;
    local ( $tmp, $height, $message_len );

    $gauge_width = $width;

    $message = &rhs_wordwrap($message, $width);
    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }
    $height = 5 + $message_len;

    open(GAUGE, "|dialog --title \"$title\" --gauge \"$message\" $height $width $percent");
}

sub rhs_update_gauge {
    local ( $percent ) = @_;

    &printflush(GAUGE, "$percent\n");
}

sub rhs_update_gauge_and_message {
    local ( $message, $percent ) = @_;

    $message = &rhs_wordwrap($message, $gauge_width);
    $message =~ s/\n/\\n/g;
    &printflush(GAUGE, "XXX\n$percent\n$message\nXXX\n");
}

sub rhs_stop_gauge {
    close GAUGE;
}

sub rhs_inputbox {
    local ( $title, $message, $width, $instr ) = @_;
    local ( $tmp, $height, $message_len );

    $message = &rhs_wordwrap($message, $width);
    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }
    $height = 7 + $message_len;

    return &return_output(0, "dialog --title \"$title\" --inputbox \"$message\" $height $width \"$instr\"");
}

sub rhs_menu {
    local ( $title, $message, $width, $numitems ) = @_;
    local ( $i, $tmp, $ent, $height, $menuheight, @list, $message_len );

    shift; shift; shift; shift;

    @list = ();
    for ($i = 0; $i < $numitems; $i++) {
        $ent = shift;
        $list[@list] = "\"$ent\"";
	$ent = shift;
        $list[@list] = "\"$ent\"";
    }

    $message = &rhs_wordwrap($message, $width);

    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }

    $height = $message_len + 6 + $numitems;
    if ($height <= $scr_lines) {
        $menuheight = $numitems;
    } else {
        $height = $scr_lines;
        $menuheight = $scr_lines - $message_len - 6;
    }

    return &return_output(0, "dialog --title \"$title\" --menu \"$message\" $height $width $menuheight @list");
}

sub rhs_menul {
    local ( $title, $message, $width, $numitems ) = @_;
    local ( $i, $tmp, $ent, $height, $menuheight, @list, $message_len );

    shift; shift; shift; shift;

    @list = ();
    for ($i = 0; $i < $numitems; $i++) {
        $ent = shift;
        $list[@list] = "\"$ent\"";
        $list[@list] = "\"\"";
    }

    $message = &rhs_wordwrap($message, $width);

    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }

    $height = $message_len + 6 + $numitems;
    if ($height <= $scr_lines) {
        $menuheight = $numitems;
    } else {
        $height = $scr_lines;
        $menuheight = $scr_lines - $message_len - 6;
    }

    return &return_output(0, "dialog --title \"$title\" --menu \"$message\" $height $width $menuheight @list");
}

sub rhs_menua {
    local ( $title, $message, $width, %items ) = @_;
    local ( $tmp, $ent, $height, $menuheight, @list, $message_len );

    @list = ();
    foreach $ent (sort keys (%items)) {
        $list[@list] = "\"$ent\"";
        $list[@list] = "\"$items{$ent}\"";
    }

    $message = &rhs_wordwrap($message, $width);

    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }

    $numitems = keys(%items);
    $height = $message_len + 6 + $numitems;
    if ($height <= $scr_lines) {
        $menuheight = $numitems;
    } else {
        $height = $scr_lines;
        $menuheight = $scr_lines - $message_len - 6;
    }

    return &return_output(0, "dialog --title \"$title\" --menu \"$message\" $height $width $menuheight @list");
}

sub rhs_checklist {
    local ( $title, $message, $width, $numitems ) = @_;
    local ( $i, $tmp, $ent, $height, $menuheight, @list, $message_len );

    shift; shift; shift; shift;

    @list = ();
    for ($i = 0; $i < $numitems; $i++) {
        $ent = shift;
        $list[@list] = "\"$ent\"";
        $ent = shift;
        $list[@list] = "\"$ent\"";
        $ent = shift;
	if ($ent) {
	    $list[@list] = "ON";
	} else {
	    $list[@list] = "OFF";
	}
    }

    $message = &rhs_wordwrap($message, $width);

    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }

    $height = $message_len + 6 + $numitems;
    if ($height <= $scr_lines) {
        $menuheight = $numitems;
    } else {
        $height = $scr_lines;
        $menuheight = $scr_lines - $message_len - 6;
    }

    return &return_output("list", "dialog --title \"$title\" --separate-output --checklist \"$message\" $height $width $menuheight @list");
}

sub rhs_checklistl {
    local ( $title, $message, $width, $numitems ) = @_;
    local ( $i, $tmp, $ent, $height, $menuheight, @list, $message_len );

    shift; shift; shift; shift;

    @list = ();
    for ($i = 0; $i < $numitems; $i++) {
        $ent = shift;
        $list[@list] = "\"$ent\"";
        $list[@list] = "\"\"";
	$list[@list] = "OFF";
    }

    $message = &rhs_wordwrap($message, $width);

    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }

    $height = $message_len + 6 + $numitems;
    if ($height <= $scr_lines) {
        $menuheight = $numitems;
    } else {
        $height = $scr_lines;
        $menuheight = $scr_lines - $message_len - 6;
    }
    return &return_output("list", "dialog --title \"$title\" --separate-output --checklist \"$message\" $height $width $menuheight @list");
}

sub rhs_checklista {
    local ( $title, $message, $width, %items ) = @_;
    local ( $tmp, $ent, $height, $menuheight, @list, $message_len );

    shift; shift; shift; shift;

    @list = ();
    foreach $ent (sort keys (%items)) {
	$list[@list] = "\"$ent\"";
	$list[@list] = "\"$items{$ent}\"";
	$list[@list] = "OFF";
    }

    $message = &rhs_wordwrap($message, $width);

    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }

    $numitems = keys(%items);
    $height = $message_len + 6 + $numitems;
    if ($height <= $scr_lines) {
        $menuheight = $numitems;
    } else {
        $height = $scr_lines;
        $menuheight = $scr_lines - $message_len - 6;
    }

    return &return_output("list", "dialog --title \"$title\" --separate-output --checklist \"$message\" $height $width $menuheight @list");
}

sub rhs_radiolist {
    local ( $title, $message, $width, $numitems ) = @_;
    local ( $i, $tmp, $ent, $height, $menuheight, @list, $message_len );

    shift; shift; shift; shift;

    @list = ();
    for ($i = 0; $i < $numitems; $i++) {
        $ent = shift;
        $list[@list] = "\"$ent\"";
        $ent = shift;
        $list[@list] = "\"$ent\"";
        $ent = shift;
	if ($ent) {
	    $list[@list] = "ON";
	} else {
	    $list[@list] = "OFF";
	}
    }

    $message = &rhs_wordwrap($message, $width);

    $message_len = split(/^/, $message);
    $tmp = $message;
    if (chop($tmp) eq "\n") {
	$message_len++;
    }

    $height = $message_len + 6 + $numitems;
    if ($height <= $scr_lines) {
        $menuheight = $numitems;
    } else {
        $height = $scr_lines;
        $menuheight = $scr_lines - $message_len - 6;
    }

    return &return_output(0 , "dialog --title \"$title\" --radiolist \"$message\" $height $width $menuheight @list");
}

sub return_output {
    local ( $listp, $command ) = @_;
    local ( $res ) = 1;

    pipe(PARENT_READER, CHILD_WRITER);
    # We have to fork (as opposed to using "system") so that the parent
    # process can read from the pipe to avoid deadlock.
    my ($pid) = fork;
    if ($pid == 0) {	# child
	close(PARENT_READER);
	open(STDERR, ">&CHILD_WRITER");
	exec($command);
	die("no exec");
    }
    if ($pid > 0) {	# parent
	close( CHILD_WRITER );
    	if ($listp) {
	    @dialog_result = ();
	    while (<PARENT_READER>) {
		chop;
		$dialog_result[@dialog_result] = $_;
	    }
	}
	else { $dialog_result = <PARENT_READER>; }
	close(PARENT_READER);
	waitpid($pid,0);
	$res = $?;
    }

    # Again, dialog returns results backwards
    if (! $res) {
	return 1;
    } else {
	return 0;
    }
}

sub rhs_wordwrap {
    local ( $intext, $width ) = @_;
    local ( $outtext, $i, $j, @lines, $wrap, @words, $pos, $pad );

    $outtext = "";
    $pad = 3;			# leave 3 spaces around each line
    $pos = $pad;		# current insert position
    $wrap = 0;			# 1 if we have been auto wraping
    $insert_nl = 0;		# 1 if we just did an absolute
				# and we should preface any new text
				# with a new line
    @lines = split(/\n/, $intext);
    for ($i = 0; $i <= $#lines; $i++) {
        if ($lines[$i] =~ /^>/) {
	    $outtext .= "\n" if ($insert_nl);
            $outtext .= "\n" if ($wrap);
	    $lines[$i] =~ /^>(.*)$/;
            $outtext .= $1;
	    $insert_nl = 1;
            $wrap = 0;
            $pos = $pad;
        } else {
            $wrap = 1;
            @words = split(/\s+/,$lines[$i]);
            for ($j = 0; $j <= $#words; $j++) {
		if ($insert_nl) {
		    $outtext .= "\n";
		    $insert_nl = 0;
		}
                if ((length($words[$j]) + $pos) > $width - $pad) {
                    $outtext .= "\n";
                    $pos = $pad;
                }
                $outtext .= $words[$j] . " ";
                $pos += length($words[$j]) + 1;
            }
        }
    }

    return $outtext;
}

############
1;

--- NEW FILE: version.c ---
/*
 *  $Id: version.c,v 1.1 2006-10-23 20:59:27 stsp Exp $
 *
 *  version.c -- dialog's version string
 *
 *  Copyright 2003,2005	Thomas E. Dickey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to
 *	Free Software Foundation, Inc.
 *	51 Franklin St., Fifth Floor
 *	Boston, MA 02110, USA.
 */
#include <dialog.h>

#define quoted(a)	#a
#define concat(a,b)	a "-" quoted(b)
#define DLG_VERSION	concat(DIALOG_VERSION,DIALOG_PATCHDATE)

char *
dialog_version(void)
{
    return DLG_VERSION;
}




More information about the dslinux-commit mailing list