[alt.sources] listfile, listdir, select - use arrow keys to select file name

flak@mcgp1.UUCP (Dan Flak) (03/05/91)

listfile, listdir and select came about from the need to allow a
user to select the name of a file from a reports directory and do
something with it (usually print or view it).

For example:

listfile /usr/users/fred/reports 1 0 more

will list all of the regular files in the directory
/usr/users/fred/reports, newest file first (last report created),
excluding files beginning with dot (.), in three columns. Fred
can then use the arrow keys to move the cursor from file name to
file name, scrolling or paging until he highlights the desired
file. When he presses <RETURN>, the file is confirmed, and
displayed using more. Fred could also select a file by typing the
number sign (#) and typing in the file number, or by typing
<SPACE> and typing in the file name.


This is my first attempt at using curses, and probably my last. I
took the easy way out and used the 'system' call to execute
whatever action was specified. 'exec' and its ilk might have been
cleaner.

listfile is simply a "front end" to link the list directory
function (listdir) with the select function. select was
deliberately written to be modular and flexible enough to display
any array of strings.

To make, modify the Makefile for BSD or for SYSV and type 'make
all'.

man pages tell all.

#----- cut here -----
#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 03/04/1991 23:38 UTC by flak@mcgp1
# Source directory /usr/users/flak/tmp
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   1333 -rw-rw-rw- listfile.roff
#    972 -rw-rw-rw- listdir.roff
#   2906 -rw-rw-rw- select.roff
#   3332 -rw-rw-rw- listdir.BSD
#   3416 -rw-rw-rw- listdir.SYSV
#    532 -rw-rw-rw- Makefile
#   2279 -rw-rw-rw- main.c
#  15789 -rw-rw-rw- select.c
#
# ============= listfile.roff ==============
if test -f 'listfile.roff' -a X"$1" != X"-c"; then
	echo 'x - skipping listfile.roff (File already exists)'
else
echo 'x - extracting listfile.roff (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'listfile.roff' &&
.  SCCS @(#) listfile.el 1.1 11/20/89 15:06:59
.TH listfile l local
.SH NAME
listfile \- select a file from a directory and display it.
.SH SYNOPSIS
listfile <directory> <sortkey> <showdot> <command> [options]
.SH DESCRIPTION
.I listfile
gets the names for the regular files in the specified directory 
and displays them using 'select'. File names are loaded in alphabetical
order if sortkey is zero (0) or in inverse chronological order (latest
file first) if sortkey is non-zero. File names with a dot "." prefix are
shown if showdot is non-zero, and are suppressed if showdot is zero.
Once a file name is selected, it is displayed using the specified
command.
.SH EXAMPLES
listfile /usr/users/reports 1 0 more
.sp
will bring up a list of file names in the directory, /usr/users/reports,
in inverse chronological order. Once a file name is selected, the file
will be viewed using 'more'.
.sp
listfile /usr/local/src 0 0 print4
.sp
will bring up a list of file names in the directory, /usr/local/src,
in alphabetical order and print the selected file using print4.
.SH SEE ALSO
ls (1)
.br
select(l)
.SH DIAGNOSTICS
Program terminates quietly on error detection or abort.
.SH AUTHOR
Dan Flak - McCaw Cellular Communications Inc., 
.br
201 Elliott Ave W., Suite 105, Seattle, Wa 98119, 
.br
206-283-2658, (usenet: nwnexus!mcgp1!flak)
SHAR_EOF
chmod 0666 listfile.roff ||
echo 'restore of listfile.roff failed'
Wc_c="`wc -c < 'listfile.roff'`"
test 1333 -eq "$Wc_c" ||
	echo 'listfile.roff: original size 1333, current size' "$Wc_c"
fi
# ============= listdir.roff ==============
if test -f 'listdir.roff' -a X"$1" != X"-c"; then
	echo 'x - skipping listdir.roff (File already exists)'
else
echo 'x - extracting listdir.roff (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'listdir.roff' &&
.  SCCS = @(#) listdir.el 1.1 11/20/89 15:06:28
.TH listdir l local
.SH NAME
listdir \- list directory contents
.SH SYNOPSIS
int listdir (dirname, array, sortkey, showdot)
.br
char *dirname;
.br
char *array[];
.br
int  sortkey;
.br
int  showdot;
.SH DESCRIPTION
.I Listdir
lists the regular file contents of dirname. The file names will be loaded into 
the array according to the value selected for sortkey. A zero (0) value
for sortkey will
load the file names in alphabetical order. A non-zero value for sortkey
will load the
file names in inverse chronological order (latest file first). File
names with a dot "." prefix will be loaded if showdot is non-zero.
Otherwise they are suppressed. 
.SH SEE ALSO
ls (1)
.SH DIAGNOSTICS
Listdir returns the number of elements in the array or -1 upon error
detection.
.SH AUTHOR
Dan Flak - McCaw Cellular Communications Inc., 
.br
201 Elliott Ave W., Suite 105, Seattle, Wa 98119, 
.br
206-283-2658, (usenet: nwnexus!mcgp1!flak)
SHAR_EOF
chmod 0666 listdir.roff ||
echo 'restore of listdir.roff failed'
Wc_c="`wc -c < 'listdir.roff'`"
test 972 -eq "$Wc_c" ||
	echo 'listdir.roff: original size 972, current size' "$Wc_c"
fi
# ============= select.roff ==============
if test -f 'select.roff' -a X"$1" != X"-c"; then
	echo 'x - skipping select.roff (File already exists)'
else
echo 'x - extracting select.roff (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'select.roff' &&
.  SCCS = @(#) select.el 1.2 11/20/89 15:05:53
.TH select l local
.SH NAME
select - select an item from a list of items
.SH SYNOPSIS
char *select (array, narray, width)
.br
char *array[];
.br
int  narray;
.br
int  width;
.SH DESCRIPTION
.I Select
displays the items of the array on the screen for selection
using arrow keys, item number, or item name.
.sp
.I Array 
is the array of items to be displayed.
.sp
.I Narray 
is the number of elements in the array.
.sp
.I Width 
is the desired width of the elements in the array. The minimum
width is 1, the maximum is 74. Item names will be padded or truncated to
fit the specified width for display.
.SH DISPLAY
The display will be in multiple columns depending on the width specified. 
Items will be evenly distributed among the columns and the items are
displayed sequentially down the columns. The scrolling window
is 20 lines long. If each column contains 25 items, then on "Page 1",
column 1 will contain items 1 through 20, column 2 will contain items 26 
through 45, etc. You can either scroll or page to subsequent pages.
The current selection is displayed at the bottom of the page.
.SH COMMANDS
The following keystrokes are used to select items from the display.
.TP
.B UP ARROW
will move the highlighted bar up one selection. If scrolling to the
previous page is required, it will occur.
.TP
.B DOWN ARROW
will move the highlighted bar down one selection. If scrolling to the
following page is required, it will occur.
.TP
.B RIGHT ARROW
will move the highlighted bar right one column. If it is in the last
column, the bar will "wrap" to the first column in the same row.
.TP
.B LEFT ARROW
will move the highlighted bar left one column. If it is in the first
column, the bar will "wrap" to the last column in the same row.
.TP
.B <ESC>B
will page the display to the bottom page.
.TP
.B <ESC>N
will page the screen to the next page.
.TP
.B <ESC>P
will page the screen to the previous page.
.TP
.B <ESC>Q
will abort the process and return a NULL.
.TP
.B <ESC>R
redraws the scrolling window.
.TP
.B <ESC>T
will page the screen to the top page.
.TP
.B <RETURN>
will select the item currently highlighted.
.TP
.B <SPACE>
will allow you to select an item manually by typing in its name.
.TP
.B #
.br
will allow you to select an item manually by typing in its number.
.SH DIAGNOSTICS
Select returns a pointer to the array element containing the selected
item. If the process is aborted by <ESC>Q or if an internal error is
detected, a NULL is returned.
.SH AUTHOR
Dan Flak, McCaw Cellular Communications Inc., Suite 105, 201 Elliott Ave W,
Seattle, Wa 98119, 206-283-2658 (usenet: nwnexus!mcgp1!flak)
.SH BUGS
None yet, however, the following improvements could be made:
.sp
Include h, j, k, l for cursor movement to supplement arrow keys.
.sp
Include <ESC> UP ARROW and <ESC> DOWN ARROW to move cursor to top or
bottom of the currently displayed column.
SHAR_EOF
chmod 0666 select.roff ||
echo 'restore of select.roff failed'
Wc_c="`wc -c < 'select.roff'`"
test 2906 -eq "$Wc_c" ||
	echo 'select.roff: original size 2906, current size' "$Wc_c"
fi
# ============= listdir.BSD ==============
if test -f 'listdir.BSD' -a X"$1" != X"-c"; then
	echo 'x - skipping listdir.BSD (File already exists)'
else
echo 'x - extracting listdir.BSD (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'listdir.BSD' &&
X
/*+
X * File: listdir.c
X *
X * Description:
X *   List directory contents.
X *
X * This module is the proprietary property of Cottege Computing
X * copyright 1989, all rights reserved.
X *
X * Audit Trail:
X *   SCCS = @(#) listdir.BSD 1.2 11/20/89 16:22:42
X *
-*/
X
/* #includes                            */
#include <sys/types.h>
#include <sys/stat.h>
#include <dir.h>
X
/* #defines                             */
#define WIDTH    14						/* file name width				   */
#define MAXFILES 255					/* maximum number of files		   */
#define SAY printf
X
/* external variables                   */
X
/* referenced external functions        */
X
/* referenced internal functions        */
X
/* global variables                     */
X
DIR    *dirp;
struct direct *dp;
struct stat   statbuf;
X
struct record
X	{
X	char filename[WIDTH + 1];			/* filename						   */
X	long timestamp;						/* time stamp					   */
X	int  idx1;							/* index 1 for sort algorithm      */
X	int  idx2;							/* index 2 for sort algorithm      */
X	} files [MAXFILES];
X
/* static variables                     */
X
/*<
X * Function: listdir (dirname, array, sortkey, showdot)
X *
X * Description:
X *   read a directory and sort its contents.
X *
X * Data Type: int
X *
X * Arguments:
X *   dirname  = directory name
X *   array    = array to which filenames are passed
X *   sortkey  = sort key (0 = by name, 1 = by date)
X *   showdot  = show .* files (0 = no show, 1 = show)
X *
X * Returns:
X *   number of files found.
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
int
listdir (dirname, array, sortkey, showdot)
char *dirname;
char *array[];
int  sortkey;
int  showdot;
{
int  nfiles = 0;						/* number of files found		   */
int  cc;							    /* stat call return value		   */
int  idx2;
int  i;
X
cc = stat (dirname, &statbuf);			/* check to see if dirname is	   */
X										/* really a directory			   */
X
if (cc == -1)							/* first, it has to exist		   */
X	{
X	return (-1);
X	}
X
if (!(statbuf.st_mode & S_IFDIR))		/* and it must be the right mode   */
X	{
X	return (-1);
X	}
X
chdir (dirname);
X
dirp = opendir (dirname);				/* open the directory			   */
X
for (dp = readdir (dirp); dp != NULL; dp = readdir (dirp))
X	{
X	if (!showdot &&  *(dp->d_name) == '.')
X		{
X		continue;
X		}
X	cc = stat (dp->d_name, &statbuf);
X	if (statbuf.st_mode & S_IFREG)		/* if it's a regular file		   */
X		{								/* get its stats				   */
X		sprintf (files[nfiles].filename, "%s", dp->d_name);
X		files[nfiles].timestamp = statbuf.st_ctime;
X
X		files[nfiles].idx1 = 0;			/* assign an index				   */
X
X		if (sortkey)					/* sort by date descending		   */
X			{
X			for (i = 0; i < nfiles; i++)
X				{
X				if (files[nfiles].timestamp < files[i].timestamp)
X					{
X					files[nfiles].idx1++;
X					}
X				else
X					{
X					files[i].idx1++;
X					}
X				}
X			}
X		else							/* sort by name ascending		   */
X			{
X			for (i = 0; i < nfiles; i++)
X				{
X				if (strcmp (files[nfiles].filename, files[i].filename) > 0)
X					{
X					files[nfiles].idx1++;
X					}
X				else
X					{
X					files[i].idx1++;
X					}
X				}
X			}
X		nfiles++;						/* increment number of reg files   */
X		}
X	}
X
closedir (dirp);
X
for (i = 0; i < nfiles; i++)			/* assign actual index			   */
X	{
X	files[files[i].idx1].idx2 = i;
X	}
X
for (i = 0; i < nfiles; i++)			/* fill the array				   */
X	{
X	strcpy (array[i], files[files[i].idx2].filename);
X	}
X
return (nfiles);
}
SHAR_EOF
chmod 0666 listdir.BSD ||
echo 'restore of listdir.BSD failed'
Wc_c="`wc -c < 'listdir.BSD'`"
test 3332 -eq "$Wc_c" ||
	echo 'listdir.BSD: original size 3332, current size' "$Wc_c"
fi
# ============= listdir.SYSV ==============
if test -f 'listdir.SYSV' -a X"$1" != X"-c"; then
	echo 'x - skipping listdir.SYSV (File already exists)'
else
echo 'x - extracting listdir.SYSV (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'listdir.SYSV' &&
X
/*+
X * File: listdir.c
X *
X * Description:
X *   Read a directory.
X *
X * This module is the property of McCaw Cellular Communications, Inc
X * Copyright 1989, all rights reserved
X *
X * Audit Trail:
X *   Original Author and Date:
X *      Dan Flak - 11/8/89
X *
-*/
X
#ifdef VERSION
static char *SCCS = "@(#) listdir.SYSV 1.1 11/20/89 16:21:30";
#endif
X
/* #includes */
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
X
/* #defines  */
#define WIDTH     14					/* file name width				   */
#define MAXFILES  255					/* maximum number of file names	   */
#define SAY printf
X
/* external variables */
X
/* referenced external functions */
X
/* internal functions */
X
/* global variables   */
X
struct direct entry;
struct stat statbuf;
X
struct record
X	{
X	char filename[WIDTH+1];				/* filename						   */
X	long timestamp;						/* time stamp					   */
X	int  idx1;							/* index1 for sort algorithm       */
X	int  idx2;							/* index2 for sort algorithm	   */
X	} files [MAXFILES];
X
/* static variables   */
X
X
/*<
X * Function: listdir (dirname, array, sortkey, showdot)
X *
X * Description:
X *   Fill the array with the contents of dirname
X *
X * Data Type: int
X *
X * Arguments:
X *    dirname - directory name
X *    array   - array of structures containing directory entries
X *    sortkey - sort key (0 = by name, 1 = by date)
X *    showdot - show .* files (0 = no show, 1 =show)
X *
X * Returns:
X *    -1  =  can't open directory
X *     n  =  number of files found
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL
X *
>*/
X
int
listdir (dirname, array, sortkey, showdot)
char *dirname;
char *array[];
int  sortkey;
int  showdot;
{
int  nfiles = 0;						/* number of files read       */
int  fid;								/* file ID                    	  */
long bytesread = 1;
int  shortsize;							/* number of bytes for inode  */
int  cc;								/* stat call return value		   */
int  i;
X
shortsize = sizeof (short);
X
cc = stat (dirname, &statbuf);
X
if (cc == -1)							/* first the name has to exist	   */
X	{
X	return (-1);
X	}
X
if (!(statbuf.st_mode & S_IFDIR))		/* and it has to be a directory    */
X	{
X	return (-1);
X	}
X
if ((fid = open (dirname, O_RDONLY)) == -1)
X    {									/* and it has to be readable	   */
X    return (-1);
X    }
X
chdir (dirname);
X
while ((read (fid, &entry, DIRSIZ + shortsize)) > 0)
X    {
X	if (entry.d_ino)					/* if inode isn't zero it's a good */
X		{								/* file							   */
X		if (!showdot && *entry.d_name == '.')
X			{
X			continue;
X			}
X		if (stat (entry.d_name, &statbuf) == 0)
X			{
X			if (statbuf.st_mode & S_IFREG)
X				{
X				sprintf (files[nfiles].filename, "%s", entry.d_name);
X				files[nfiles].timestamp = statbuf.st_ctime;
X				files[nfiles].idx1 = 0;
X				if (sortkey)
X					{
X					for (i = 0; i < nfiles; i++)
X						{
X						if (files[nfiles].timestamp < files[i].timestamp)
X							{
X							files[nfiles].idx1++;
X							}
X						else
X							{
X							files[i].idx1++;
X							}
X						}
X					}
X				else
X					{
X					for (i = 0 ; i < nfiles; i++)
X						{
X						if (strcmp(files[nfiles].filename, files[i].filename) 
X							> 0)
X							{
X							files[nfiles].idx1++;
X							}
X						else
X							{
X							files[i].idx1++;
X							}
X						}
X					}
X				nfiles++;
X				}			
X			}
X		}
X    }
close (fid);
X
for (i = 0; i < nfiles; i++)
X	{
X	files[files[i].idx1].idx2 = i;
X	}
X
for (i = 0 ; i < nfiles; i++)
X	{
X	strcpy (array[i], files[files[i].idx2].filename);
X	}
X
return (nfiles);
}
SHAR_EOF
chmod 0666 listdir.SYSV ||
echo 'restore of listdir.SYSV failed'
Wc_c="`wc -c < 'listdir.SYSV'`"
test 3416 -eq "$Wc_c" ||
	echo 'listdir.SYSV: original size 3416, current size' "$Wc_c"
fi
# ============= Makefile ==============
if test -f 'Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping Makefile (File already exists)'
else
echo 'x - extracting Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
# SCCS = @(#) Makefile 1.6 12/18/89 15:08:28
# SYSTEM = BSD
SYSTEM = SYSV
X
CFLAGS = -O
X
OBJS = listfile atest
X
SRCS = select.c listdir.$(SYSTEM) main.c select.roff listdir.roff \
X		listfile.roff 
X
.SUFFIXES:	.roff .man
X
.roff.man:	
X		nroff -man $*.roff > $*.man
X
all:	listfile select.man listdir.man listfile.man 
X		touch all
X
clean:
X		rm -f $(OBJS) *.man *.o all core
X
listfile:		main.o select.o listdir.o
X	cc -o listfile main.o listdir.o select.o -lcurses -ltermcap
X
listdir.c:		listdir.$(SYSTEM)
X		cp listdir.$(SYSTEM) listdir.c
SHAR_EOF
chmod 0666 Makefile ||
echo 'restore of Makefile failed'
Wc_c="`wc -c < 'Makefile'`"
test 532 -eq "$Wc_c" ||
	echo 'Makefile: original size 532, current size' "$Wc_c"
fi
# ============= main.c ==============
if test -f 'main.c' -a X"$1" != X"-c"; then
	echo 'x - skipping main.c (File already exists)'
else
echo 'x - extracting main.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'main.c' &&
X
/*+
X * File: main.c
X *
X * Description:
X *    List a directory, display it for selection and execute an action on
X *    the selected file.
X *
X * This module is the proprietary property of Cottege Computing
X * copyright 1989, all rights reserved.
X *
X * Audit Trail:
X *    SCCS = @(#) main.c 1.2 11/20/89 16:45:33
X *    Dan Flak - 11/18/89
X *
-*/
X
/* #includes                            */
#include <stdio.h>
X
/* #defines                             */
#define SAY printf
#define WIDTH 14
#define MAXFILES 255
X
/* external variables                   */
X
/* referenced external functions        */
int  listdir();							/* Prepares a list of files        */
char *select();							/* shows the list of files for slec-
X										 * tion							   */
X
/* referenced internal functions        */
X
/* global variables                     */
char xarray[MAXFILES][WIDTH + 1];		/* Array of filenames			   */
char *aptr[MAXFILES];				    /* Array of pointers to filenames  */
X
int  narray;							/* Number of elements in array     */
X
/* static variables                     */
X
/*<
X * Function: main (argc, argv)
X *
X * Description:
X *   Call listdir to fill the array with filenames. Call select to display
X *   them. Exec a command on the selected file.
X *
X * Data Type: int
X *
X * Arguments:
X *    argv[1] = directory name
X *    argv[2] = sort key (0 = alphabetical, 1 = by date)
X *    argv[3] = showdot (0 = don't show .*, 1 = show .*)
X *    argv[4] = command to execute
X *      .          .
X *      .          .
X *      .          .
X *    argv[n] = options to the command to execute.
X *
X * Returns:
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
int
main (argc, argv)
int  argc;
char *argv[];
{
int i;
char *ptr;
char cmdstr[128];
X
for (i = 0; i < MAXFILES; i++)			/* initalize the pointers to the   */
X	{									/* the array					   */
X	aptr[i] = xarray[i];
X	}
X
narray = listdir (argv[1], aptr, atoi (argv[2]), atoi (argv[3]));
X										/* get directory contents		   */
X
ptr = select (aptr, narray, WIDTH);     /* select a file				   */
X
if (*ptr != NULL)						/* display it					   */
X	{
X	chdir (argv[1]);
X	strcpy (cmdstr, argv[4]);
X	for (i = 5; i < argc; i++)
X		{
X		sprintf (cmdstr, "%s %s", cmdstr, argv[i]);
X		}
X	sprintf (cmdstr,"%s %s", cmdstr, ptr);
X	system (cmdstr);
X	}
}
SHAR_EOF
chmod 0666 main.c ||
echo 'restore of main.c failed'
Wc_c="`wc -c < 'main.c'`"
test 2279 -eq "$Wc_c" ||
	echo 'main.c: original size 2279, current size' "$Wc_c"
fi
# ============= select.c ==============
if test -f 'select.c' -a X"$1" != X"-c"; then
	echo 'x - skipping select.c (File already exists)'
else
echo 'x - extracting select.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'select.c' &&
X
/*+
X * File: select.c
X *
X * Description:
X *   Display a list of items for selection.
X *
X * This module is the proprietary property of Cottege Computing
X * copyright 1989, all rights reserved.
X *
X * Audit Trail:
X *   SCCSID = @(#) select.c 1.2 11/20/89 15:05:06
X *   Dan Flak - 11/7/89
X *
-*/
X
/* #includes                            */
#include <curses.h>
X
/* #defines                             */
#define SAY    printf
X
#define RETURN    '\015'
#define ESC       '\033'
#define BACKSPACE '\010'
X
#define MAX(x,y) (((x) < (y)) ? y : x)
#define MIN(x,y) (((x) > (y)) ? y : x)
#define WINLEN 20
#define ARRAYPOS linenum + irow + lastline * icol
X
#define UP    1
#define DOWN  2
#define LEFT  3
#define RIGHT 4
X
#define ON    1
#define OFF   0
X
/* external variables                   */
X
/* referenced external functions        */
X
/* referenced internal functions        */
char *closewin();
char *select();
int  do_esc();
void errormsg();
void newpage();
void homecursor();
void putcursor();
void mvcursor();
void scroll();
void redraw();
void wrap();
X
/* global variables                     */
X
char *rtnstr = NULL;					/* return string (item selected)   */
char cmdstr[32];						/* printf format string            */
X
int  arraypos = 0;						/* item position in array          */
int  center;							/* center of the screen			   */
int  column = 0;						/* column number				   */
int  lastline;							/* last line in the array          */
int  linenum = 0;						/* line number                     */
int  ncol = 1;                          /* number of columns               */
int  screenx = 0, screeny = 0;			/* x and y screen positions        */
X
WINDOW *win1, *win2, *win3;				/* information, command and work-  */
X										/* ing windows                     */
X
X
/* static variables                     */
X
X
/*<
X * Function: select (array, narray, width)
X *
X * Description:
X *   Select an item from an array.
X *
X * Data Type: char *
X *
X * Arguments:
X *   array  - the array containing the selection items
X *  narray  - number of items in the array.
X *   width  - string length of items in the array.
X *
X * Returns:
X *   pointer to a string containing the item name.
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
char *
select (array, narray, width)
char *array[];
int  narray;
int  width;
{
int i;
X
ncol = 80 / (6 + width);				/* determine number of columns     */
center = 40 - (6 + width) / 2;
X
if (ncol > 10 || ncol < 1)				/* will display fit on screen?     */
X	{
X	return (closewin (-2));
X	}
X
lastline = narray / ncol;				/* compute total number of lines   */
if (narray % ncol)
X	{
X	lastline++;
X	}
X
sprintf (cmdstr, "%c3d %c-%d.%ds", '%', '%', width, width);
X
initscr();
crmode ();
noecho ();
nonl();
X
win1 = newwin ( 2, 80,  0,  0);			/* top information window          */
win2 = newwin (WINLEN, 80,  2,  0);		/* working window                  */
win3 = newwin ( 2, 80, 22,  0);         /* command window                  */
X
wmove (win1, 0, 0);						/* print instructions              */
waddstr (win1, "Use arrow keys and <RETURN> to select from menu or");
wmove (win1, 1, 0);
waddstr (win1, "'#' to enter number or <SPACE> to enter name. <ESC>Q quits");
wrefresh (win1);
X
wclear (win2);
redraw (array, narray);					/* show intial set of items		   */
homecursor(array);
X
while (getcmds(array, narray))			/* get commands                    */
X	{
X	homecursor(array);					/* put cursor at bottom of screen  */
X	}
X
return (closewin(0));
}
X
X
/*<
X * Function: getcmds (array narray)
X *
X * Description:
X *   Get cursor commands
X *
X * Data Type: int
X *
X * Arguments:
X *   array - the array containing the items
X *  narray - the number of elements in the array
X *
X * Returns:
X *    0 - upon <ESC>q.
X *    0 - upon confirmation of selection.
X *    1 - all other cases.
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
int
getcmds (array, narray)
char *array[];
int  narray;
{
int c;
X
c = wgetch (win3);
X
switch (c)
X	{
X	case ESC:
X		return (do_esc(array, narray));
X	break;
X	case ' ':
X		return (do_string (array, narray));
X	break;
X	case '#':
X		return (do_number(array, narray));
X	break;
X	case RETURN:
X		rtnstr = array[arraypos];
X		return (confirm());
X	break;
X	default:
X		return (1);
X	break;
X	}
}
X
X
/*<
X * Function: do_esc (array, narray)
X *
X * Description:
X *   Evaluate ESC sequences for cursor moves, scroll, wrap and
X *      paging.
X *
X * Data Type: int
X *
X * Arguments:
X *   array - array of display items
X *  narray - number of elements in array
X *
X * Returns:
X *   0 for <ESC>Q
X *   1 for anything else
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
int
do_esc (array, narray)
char *array[];
int narray;
{
int c;
X
c = wgetch (win3);
X
if (c == '[')							/* it's an arrow key               */
X	{
X	c = wgetch (win3);
X	switch (c)
X		{
X		case 'A':						/* up arrow                       */
X			if (screeny == 0 && linenum > 0)
X				{						/* if at top with items above     */
X				scroll (UP, array, narray);
X				}
X			else if (screeny > 0) 		 /* cursor below top line		  */
X				{
X				mvcursor (UP, array, narray);
X				}
X		break;
X		case 'B':						/* down arrow                     */
X			if (screeny == WINLEN -1 && linenum + WINLEN < lastline)
X				{						/* if on bottom with items below  */
X				scroll (DOWN, array, narray);
X				}
X			else if (screeny < MIN (WINLEN - 1, lastline - 1))
X				{						/* cursor above bottom line		  */
X				mvcursor (DOWN, array, narray);
X				}
X		break;
X		case 'C':						/* right arrow                    */
X			if (screenx < (80 * (ncol - 1)) / ncol)
X				{						/* if not in right column		  */
X				mvcursor (RIGHT, array, narray);
X				}
X			else if (screenx == (80 * (ncol - 1)) / ncol)
X				{						/* if in right column			  */
X				wrap (RIGHT, array, narray);
X				}
X		break;
X		case 'D':						/* left arrow                     */
X			if (screenx > 0)
X				{						/* if not in left column          */
X				mvcursor (LEFT, array, narray);
X				}
X			else
X				{						/* in left column				  */
X				wrap (LEFT, array, narray);
X				}
X		break;
X		}
X	}									/* end case arrow                 */
else 
X	{
X	switch (c)
X		{
X		case 'N': case 'n':				/* Next page                       */
X			linenum = MIN (linenum + 2 * WINLEN, lastline) - WINLEN;
X			newpage ();
X			redraw (array, narray);
X		break;
X		case 'P': case 'p':				/* Previous page                   */
X			linenum = MAX (0, linenum - WINLEN);
X			newpage ();
X			redraw (array, narray);
X		break;
X		case 'T': case 't':				/* Top of scroll                   */
X			linenum = 0;
X			newpage ();
X			redraw (array, narray);
X		break;
X		case 'B': case 'b':				/* Bottom of scroll                */
X			linenum = MAX(0, lastline - WINLEN);
X			newpage ();
X			redraw (array, narray);
X		break;
X		case 'R': case 'r':
X			wclear (win2);
X			redraw (array, narray);
X		break;
X		case 'Q': case 'q':				/* Quit							   */
X			return (0);
X		break;
X		}
X	}									/* end case anything else          */
return (1);
}
X
X
/*<
X * Function: closewin (n)
X *
X * Description:
X *   Close the windows gracefully
X *
X * Data Type: char *
X *
X * Arguments:
X *    n = exit code
X *
X * Returns:
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
char *
closewin (n)
int n;
{
werase (win1);
werase (win2);
werase (win3);
X
wrefresh (win1);
wrefresh (win2);
wrefresh (win3);
X
endwin ();
X
if (n)									/* If error                        */
X	{
X	return (NULL);      				/* Return pointer to a null        */
X	}
else
X	{
X	return (rtnstr);					/* Return pointer to selected item */
X	}
}
X
X
/*<
X * Function: redraw (array, narray)
X *
X * Description:
X *    Redraw the working screen
X *
X * Data Type: void
X *
X * Arguments:
X *    array - array of items to display.
X *   narray - number of elements in the array
X *
X * Returns: None
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
void
redraw(array, narray)
char *array[];
int  narray;
{
int irow, icol;							/* screen row and column number    */
X
for (irow = 0; irow < MIN (lastline, WINLEN); irow++)
X	{
X	for (icol = 0; icol < ncol; icol++) /* for each row and column         */
X		{
X		wmove (win2, irow, (80 * icol) / ncol);
X		if (ARRAYPOS == arraypos)       /* highlight the selection         */
X			{
X			wstandout (win2);
X			}
X		else
X			{
X			wstandend (win2);
X			}
X		if ((irow + linenum < lastline) + ncol && icol == ncol -1)
X			{							/* clear old items in last column  */
X			wclrtoeol (win2);
X			}
X		if (ARRAYPOS < narray)			/* Print all if you can            */
X			{
X			wprintw (win2, cmdstr, ARRAYPOS + 1, array[ARRAYPOS]);
X			}
X		}
X	}
wrefresh (win2);
}
X
X
/*<
X * Function: mvcursor (n, array, narray)
X *
X * Description:
X *   Move the cursor in the indicated direction.
X *
X * Data Type: void
X *
X * Arguments:
X *       n = direction to move the cursor
X *   array = array of display items
X *  narray = number of items in the array
X *
X * Returns:
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
void
mvcursor (n, array, narray)
int  n;
char *array[];
int  narray;
{
putcursor (OFF, array);
X
switch (n)
X	{
X	case UP:
X		screeny--;
X		arraypos--;
X	break;
X	case DOWN:
X		if (arraypos + 1 < narray)
X			{
X			screeny++;
X			arraypos++;
X			}
X	break;
X	case LEFT:
X		column--;
X		screenx = (80 * column) / ncol;
X		arraypos -= lastline;
X	break;
X	case RIGHT:
X		if (arraypos +  lastline < narray)
X			{
X			column++;
X			screenx = (80 * column) / ncol;
X			arraypos += lastline;
X			}
X	break;
X	}
putcursor (ON, array);
}
X
X
/*<
X * Function: putcursor (n, array)
X *
X * Description:
X *   Print an item in normal or highlight mode.
X *
X * Data Type: void
X *
X * Arguments:
X *   n = highlight mode (0 = off, 1 = on)
X *   array = array of display items.
X *
X * Returns:
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
void
putcursor (n, array)
int  n;
char *array[];
{
wmove (win2, screeny, screenx);
if (n)
X	{
X	wstandout (win2);
X	}
else
X	{
X	wstandend (win2);
X	}
X
wprintw (win2, cmdstr, arraypos + 1, array[arraypos]);
wrefresh (win2);
}
X
X
/*<
X * Function: scroll (n, array, narray)
X *
X * Description:
X *   Scroll the page.
X *
X * Data Type: void
X *
X * Arguments:
X *        n = direction of scroll
X *    array = array of display items
X *   narray = number of items in the array
X *
X * Returns:
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
void
scroll (n, array, narray)
int  n;
char *array[];
int  narray;
{
switch (n)
X	{
X	case UP:
X		linenum--;
X		arraypos--;
X	break;
X	case DOWN:
X		linenum++;
X		arraypos++;
X	break;
X	}
redraw (array, narray);
}
X
X
/*<
X * Function: wrap (n, array, narray)
X *
X * Description:
X *   Wrap the cursor
X *
X * Data Type: void
X *
X * Arguments:
X *   n = direction of wrap
X *   array = display items
X *  narray = number of items in the array
X *
X * Returns:
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
void
wrap (n, array, narray)
int  n;
char *array[];
int narray;
{
putcursor (OFF, array);
X
switch (n)
X	{
X	case LEFT:
X		if (arraypos + lastline * (ncol - 1) < narray)
X			{
X			column = ncol - 1;
X			screenx = (80 * column) / ncol;
X			arraypos += lastline * (ncol - 1);
X			}
X	break;
X	case RIGHT:
X		column = 0;
X		screenx = 0;
X		arraypos -= lastline * (ncol - 1);
X	break;
X	}
X
putcursor (ON, array);
}
X
X
/*<
X * Function: do_number (array, narray)
X *
X * Description:
X *   Get a number from the keyboard and match it to an item selection.
X *
X * Data Type: int
X *
X * Arguments:
X *    array = array of display items
X *   narray = numbr of elements in the array.
X *
X * Returns:
X *   0 - if item is confirmed
X *   1 - if item is not confirmed
X *   1 - if item is not found.
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL
X *
>*/
X
int 
do_number (array, narray) 
char *array[];
int  narray;
{
char number[16];
int  n;
X
echo ();
nl();
X
werase (win3);							/* clear the window				   */
wmove (win3, 0, 0);						/* position the cursor			   */
waddstr (win3, "ENTER ITEM NUMBER: ");  /* write the prompt				   */
wmove (win3, 0, 19);    				/* position the cursor			   */
wrefresh (win3);						/* redraw the window			   */
wgetstr (win3, number);					/* get a string					   */
wrefresh (win3); werase (win3); wrefresh (win3);
X
noecho();
nonl();
X
wmove (win3, 0, center);				/* move the cursor				   */
X
n = atoi (number);
if (n > 0 && n <= narray)
X	{
X	wprintw (win3, cmdstr , n, array [n-1]);
X	rtnstr = array [n-1];
X	return (confirm ());
X	}
else
X	{
X	wprintw (win3, "%3d is not a good item number", n);
X	errormsg();
X	return (1);
X	}
}
X
X
/*<
X * Function: do_string (array, narray)
X *
X * Description:
X *   Get a name from the keyboard and match it to an item selection.
X *
X * Data Type: int
X *
X * Arguments:
X *    array = array of display items
X *   narray = numbr of elements in the array.
X *
X * Returns:
X *   0 - if item is confirmed
X *   1 - if item is not confirmed
X *   1 - if item is not found.
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL
X *
>*/
X
int 
do_string (array, narray)
char *array[];
int  narray;
{
int  i;
int  found = 0;
char name[128];
X
echo ();
nl();
X
werase (win3);							/* clear the window				   */
wmove (win3, 0, 0);						/* position the cursor			   */
waddstr (win3, "ENTER ITEM NAME:");		/* write the prompt				   */
wmove (win3, 0, 18);        			/* position the cursor			   */
wrefresh (win3);						/* redraw the window			   */
wgetstr (win3, name);					/* get a string					   */
wrefresh (win3); werase (win3); wrefresh (win3);
X
noecho();
nonl();
X
wmove (win3, 0, center); 				/* move the cursor				   */
X
for (i = 0; i < narray; i++)
X	{
X	if (strcmp (name, array[i]) == 0)
X		{
X		found = 1;
X		break;
X		}
X	}
X
if (found)
X	{
X	wprintw (win3, cmdstr , i + 1, array [i]);
X	rtnstr = array [i];
X	return (confirm ());
X	}
else
X	{
X	wprintw (win3, "%s is not a good item name", name);
X	errormsg();
X	return (1);
X	}
}
X
X
/*<
X * Function: homecursor (array)
X *
X * Description:
X *   Print the current slection in win3
X *
X * Data Type: void
X *
X * Arguments:
X *    array - the array containing the select items
X *
X * Returns:
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
void
homecursor (array)
char *array[];
{
wmove (win3, 0, center);
wprintw (win3, cmdstr, arraypos + 1, array[arraypos]);
wrefresh (win3);
}
X
X
/*<
X * Function: newpage ()
X *
X * Description:
X *    Set up fresh page on <ESC> T, B, N and P.
X *
X * Data Type: void
X *
X * Arguments: globals
X *
X * Returns: globals
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL:
X *
>*/
X
void 
newpage ()
{
if (isonpage ())
X	{									/* item still on page			   */
X	screeny = arraypos - linenum - column * lastline;
X	}
else
X	{									/* item scrolled off page		   */
X	arraypos = linenum;					/* "home" the item number		   */
X	column = 0;							/* and everthing else			   */
X	screenx = 0;
X	screeny = 0;
X	}
}
X
X
/*<
X * Function: isonpage ()
X *
X * Description:
X *   Determine if an item is still on the page after paging.
X *
X * Data Type: int
X *
X * Arguments: globals
X *
X * Returns: globals
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL
X *
>*/
X
int
isonpage ()
{
int i;
X
for (i = 0; i < ncol; i++)
X	{
X	if (arraypos >= linenum + i * lastline &&
X		arraypos <= linenum + WINLEN - 1 + i * lastline)
X		{
X		return (1);
X		}
X	}
return (0);
}
X
X
/*<
X * Function: confirm ()
X *
X * Description:
X *   Confirm a user's selection.
X *
X * Data Type: int
X *
X * Arguments:
X *
X * Returns:
X *   0 - if the item is confirmed
X *   1 - if the item is not confirmed
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL
X *
>*/
X
int
confirm ()
{
int c;
X
wmove (win3, 1, 20);
waddstr (win3, "Do you wish to select this item? (y/n) ");
wrefresh (win3);
X
c = wgetch (win3);
X
if (c == 'y' || c == 'Y')
X	{
X	return (0);
X	}
else
X	{
X	werase (win3);
X	wrefresh (win3);
X	rtnstr = NULL;
X	return (1);
X	}
}
X
X
/*<
X * Function: errormsg()
X *
X * Description:
X *   Pause the processing to let the user read an error message. Wait
X *   for a keystroke.
X *
X * Data Type: void
X *
X * Arguments:
X *
X * Returns:
X *
X * Side Effects:
X *
X * Calls:
X *
X * PDL
X *
>*/
X
void
errormsg()
{
wmove (win3, 1, 28);
waddstr (win3, "Press any key to continue");
wrefresh (win3);
wgetch (win3);
werase (win3); wrefresh (win3);
}
SHAR_EOF
chmod 0666 select.c ||
echo 'restore of select.c failed'
Wc_c="`wc -c < 'select.c'`"
test 15789 -eq "$Wc_c" ||
	echo 'select.c: original size 15789, current size' "$Wc_c"
fi
exit 0
-- 
       Dan Flak - McCaw Cellular Communications Inc., 201 Elliot Ave W.,
    Suite 105, Seattle, Wa 98119, 206-286-4355, (usenet: nwnexus!mcgp1!flak)