[alt.sources] DMM

rob@mtdiablo.Concord.CA.US (Rob Bernardo) (06/18/91)

From the README file:

DMM is a set of C library functions that provide a fairly object-oriented
front end to curses for the management of data entry and menu screens.
It requires the System V version of curses.

The sources for these functions are in dmm.c and dmm.h.  A make file is
provided.  The make file and sources compile as is under SUN 0S 4.03, 4.1
and 4.1.1.  They may need minor modifications for compilation on other
flavors of UNIX.

A section 3 manual page is included; it details the use of the functions
as well as the look and feel of the screens they produce.

A demo program is included and can be compiled using the make file.
Using DMM effectively can be complex and the demo program demonstrates
various DMM facilities and points out highlights of the demo sources.

#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
# This is part 1 of a multipart archive                                    
# do not concatenate these parts, unpack them in order with /bin/sh        
#
#	Run the following text with /bin/sh to create:
#	  Makefile
#	  README
#	  dmm.3
#	  dmm.c
#	  dmm.h
#	  dmmdemo.h
#	  dmmdemo1.c
#	  dmmdemo2.c
#	  dmmdemo3.c
#	  dmmdemo4.c
#	  dmmdemo5.c
#	  dmmdemo6.c
#
if test -r s2_seq_.tmp
then echo "Must unpack archives in sequence!"
     next=`cat s2_seq_.tmp`; echo "Please unpack part $next next"
     exit 1; fi
echo "x - extracting Makefile (Text)"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
X# @(#)Makefile	1.1 Copyright (c) 1991 Robert Bernardo, rob@mtdiablo.Concord.CA.US,
X# an employee of Pande, Inc.  Permission to use granted only if this
X# notice is not removed from this make file produced from it.
X
XCC = /usr/5bin/cc
X
X
XDEMOOBJS =  dmmdemo1.o dmmdemo2.o dmmdemo3.o dmmdemo4.o dmmdemo5.o dmmdemo6.o
Xdmmdemo: $(DEMOOBJS) dmm.o
X	$(CC) $(DEMOOBJS) dmm.o -lcurses -o dmmdemo
X
Xdmm:	dmm.o
X
Xclean:
X	rm -f *.o dmmdemo core
X
Xlint:
X	lint -I/usr/5include *.c
SHAR_EOF
chmod 0444 Makefile || echo "restore of Makefile fails"
set `wc -c Makefile`;Sum=$1
if test "$Sum" != "462"
then echo original size 462, current size $Sum;fi
echo "x - extracting README (Text)"
sed 's/^X//' << 'SHAR_EOF' > README &&
XThis is the 1.1 version of DMM.  It is a beta version.  Bugs, fixes,
Xcomments and suggestions should be sent to Robert Bernardo, at
Xrob@mtdiablo.Concord.CA.US.
X
XDMM is a set of C library functions that provide a fairly object-oriented
Xfront end to curses for the management of data entry and menu screens.
XIt requires the System V version of curses.
X
XThe sources for these functions are in dmm.c and dmm.h.  A make file is
Xprovided.  The make file and sources compile as is under SUN 0S 4.03, 4.1
Xand 4.1.1.  They may need minor modifications for compilation on other
Xflavors of UNIX.
X
XA section 3 manual page is included; it details the use of the functions
Xas well as the look and feel of the screens they produce.
X
XA demo program is included and can be compiled using the make file.
XUsing DMM effectively can be complex and the demo program demonstrates
Xvarious DMM facilities and points out highlights of the demo sources.
X
X@(#)README	1.1 Copyright (c) 1991 Robert Bernardo,
Xrob@mtdiablo.Concord.CA.US, an employee of Pande, Inc.  Permission to
Xuse DMM granted only if the copyright notice is not removed from any of
Xthe component files nor from any binaries produced from them.
SHAR_EOF
chmod 0444 README || echo "restore of README fails"
set `wc -c README`;Sum=$1
if test "$Sum" != "1183"
then echo original size 1183, current size $Sum;fi
echo "x - extracting dmm.3 (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmm.3 &&
X.\ @(#)dmm.3	1.1
X.TH DMM 3 6/13/91
X.SH NAME
XdmmRun, dmmReview, dmmInit, dmmClose, dmmPutMsgs, dmmPutMsgsTimer, dmmPutMsgsHit, dmmPutMsgsNext, dmmClear, dmmRmMsgs, dmmGetNumLines - manage data entry and menu screens
X.SH SYNOPSIS
X.nf
X.B #include <dmm.h>
X.PP
X.B extern DMMRETTYPE dmmRetType;
X.PP
X.B int dmmRun(startField, screen, menu, drawFlag, valFlag)
X.B int startField;
X.B DMMSCREEN screen;
X.B DMMMENU menu;
X.B DMMDRAWFLAG drawFlag;
X.B DMMVALFLAG valFlag;
X.PP
X.B int dmmReview(screen)
X.B DMMSCREEN screen;
X.PP
X.B void dmmInit()
X.PP
X.B void dmmClose()
X.PP
X.B void dmmPutMsgs(mesgblock)
X.B DMMMESGBLK mesgblock;
X.PP
X.B void dmmPutMsgsTimer(mesgblock, duration)
X.B DMMMESGBLK mesgblock;
X.B unsigned duration;
X.PP
X.B void dmmPutMsgsHit(mesgblock)
X.B DMMMESGBLK mesgblock;
X.PP
X.B void dmmPutMsgsNext(mesgblock)
X.B DMMMESGBLK mesgblock;
X.PP
X.B void dmmClear()
X.PP
X.B void dmmRmMsgs(removetype)
X.B DMMREMOVETYPE removetype;
X.PP
X.B int dmmGetNumLines()
X.fi
X.SH DESCRIPTION
X.IR "Data entry and Menu Manager" ", " DMM ,
Xis a set of functions that
Xprovide a fairly object-oriented interface to
X.I curses
Xand enforce a particular look and feel in managing data entry and 
Xmenu screens.
XThe next two subsections describe this look and feel; subsequent subsections
Xdescribe the use of the functions.
X.SS Screen appearance
X.PP
XThe screen consists of three
X.I curses
Xwindows.
X.PP
XThe top line is an instruction window that displays a general user instruction
Xthat varies with the state that the screen is in.
X.PP
XThe next two lines are used by the menu.
XThe menu is presented as a ring menu.
XA scrollable list of typically one-word menu items is 
Xdisplayed horizontally on the first of the two lines.
XWhen the menu is active, one menu item is considered current and is highlighted.
XThe second line of the menu window is used to display a longer description
Xof the current menu item.
XThrough input keystrokes, the user can make a particular menu item current,
Xand then with a particular keystroke can confirm the
Xcurrently selected menu item as the choice made.
X.PP
XThe rest of the screen is the data entry window. Visually, this window appears
Xto consist of two sorts of items: editable fields and static, non-editable 
Xphrases (used for headings and for labels of the editable fields).
XTypically the two are displayed with different attributes (e.g. editable
Xfields displayed highlighted and the static fields displayed plain).
XWhen the data entry window is active, one editable field is considered
Xcurrent, and the cursor is placed inside it.
XTypically the current editable field is displayed with a different
Xhighlighting attribute from the other editable fields.
XThe user can edit the current field, or move to another field,
Xmaking it current.
XWhen the user enters a keystroke to move to another field, the value of
Xthe then-current field may be audited and,
Xif found in error, an error message displayed and the cursor
Xis moved to its first character position;
Xotherwise the cursor is moved to first character position of that other field,
Xwhich is made current.
X.PP
XWhile the screen is interactive,
Xeither the menu or the data entry window will be active.
XThrough a particular keystroke, the user can switch between these two states.
XTypically, a screen consists of both a menu and a data entry window (though
Xonly one of the two are required) and starts with the data entry window
Xactive and a program-specified field current.
XThe user may enter data in the editable fields and then go to the menu
Xto select an action to happen, such as to go forward with the data as entered,
Xor perhaps to ignore the data and just pop back up to a higher menu.
X.PP
XIn either menu or data entry state, the user can enter a keystroke that
Xdisplays help information.
X.SS User input
X.PP
XThe following user input keystrokes have similar effects 
Xin both menu and data entry states:
X.PD 0
X.TP 20
Xcontrol-g
XToggles between the menu and data entry states.
X.TP 20
Xcontrol-h
XDisplays a screen giving this keystroke-effect information.
X.TP 20
Xcontrol-r
XRedraws the entire screen.
X.TP 20
Xtab, control-n
XMakes the next editable field or menu item current.
X.TP 20
Xcontrol-p
XMakes the preceding editable field or menu item current.
X.PD
X.PP
XThe following user input keystrokes are particular to the menu state:
X.PD 0
X.TP 20
Xcarriage return
XChoses the current menu item.
X.TP 20
Xalpha character
XMakes current the single menu item that begins with the alpha character.
XIf there is not exactly one such menu item, the screen is flashed.
X.PD
X.PP
XThe following user input keystrokes are particular to the data entry state:
X.PD 0
X.TP 20
Xcontrol-c
XClears current editable field from current position to end of field and makes
Xthe next editable field current.
X.TP 20
Xcarriage return
XMakes the the next editable field current.
X.TP 20
Xarrow key
XMoves the cursor in the indicated direction within the current editable field
Xif possible.
X.TP 20
Xdelete
XErases the character to the left of the cursor, i.e. 
Xreplaces it with a blank and moves the cursor left,
Xif not in the first character position in the current editable field.
XIf it is in the last character position, and the last character is not blank,
Xit erases the character under the cursor and leaves the cursor there.
X.TP 20
Xprinting character
XInserts the character in the current editable field at the cursor position
X(and advances the cursor within the field
Xif not at the last character position of the field)
X.PD
X.PP
XAny other keystroke will flash the screen.
X.SS Screen specification
X.PP
XAn invocation of the function
X.B dmmRun
Xmanages the display and operation of a screen
Xand engages the user in interaction with the screen.
X.PP
XThe layout of the data entry window is specified by
X.IR screen ,
Xa null terminated array of pointers to DMMFIELD structures, defined in
X.IR dmm.h .
X.PP
XEach DMMFIELD describes one data entry field. Its elements describe
Xtwo parts of the data entry field. The label elements describe the
Xconstant part of the field, which may be used for display of headings,
Xdata or a label for the editable part. The input elements describe the editable
Xpart, a field on the screen where the user can move the cursor and input data.
XThe editable part of a field is realized as a (nearly) rectangular area within
Xthe window.
X.TP 20
XlabelValue
XA non-editable string to be displayed.
XA NULL value indicates no string to display.
X.TP 20
X.PD 0
XlabelCol,
X.TP 20
X.PD
XlabelLine
XThe coordinates in the data entry window where the first character of
X.I labelValue
Xis displayed.
XA negative line number represents lines from the bottom
Xof the window, the bottom line represented with -1.
X.TP 20
XlabelAttrib
XThe attributes with which the label is displayed, given as the logical
X.IR or -ing
Xof the attribute constants given in <curses.h>, or  0L for no attributes.
X.TP 20
XinputLength
XThe number of data characters to accept from the user for editable part of
Xthe field.
XThis governs the total size of the rectangular input area.
XIf 0,
Xthe field is considered to have no editable part;
Xthis is useful for the presentation of display-only data on the screen,
Xe.g. headings.
X.TP 20
XinputEditValue
XA buffer of length 
X.I inputLength
X+ 1 (for the terminating null) used to store the data as entered or 
Xedited by the user.
X.TP
XinputInitValue
XA string with an initial value to be copied to inputEditValue before user
Xinteraction.
X(This is dependent on the value of 
X.I valFlag
X- see below.)
XThe string must no be longer than
X.I inputLength.
XA NULL value is treated the same as a zero-length string.
X.TP 20
X.PD 0
XinputCol,
X.TP 20
X.PD
XinputLine
XThe coordinates in the data entry window where the upper left corner
Xof the input area is displayed.
XA negative column value designates a column position that
Xis that many intervening columns to the right of the last character of
X.IR labelValue .
XA negative line number represents lines from the bottom
Xof the window, the bottom line represented with -1.
X.TP 20
XinputMaxCols
XThe width of the rectangular input area. If smaller than
X.I inputLength
Xthe rectangular area will be more than one line in height.
XIf 0,
Xthe width is taken from
X.IR inputLength ,
Xi.e. the rectangular area will be exactly one line.
X.TP
X.PD 0
XinputFullLines,
X.TP 20
X.PD
XinputShortCols
XTo be left unset by the calling function and calculated by
X.BR dmmRun .
XThey represent the number of lines of full width in the rectangular area
Xand the number of columns in the last ``remainder'' line of the rectangular
Xarea, respectively.
XActually, because of the ``remainder'' line, the rectangular area is not
Xalways an exact rectangle.
X.TP 20
XinputAttrib
XThe attributes with which the rectangular area is displayed when 
Xthe field is not current.
XSee
X.I labelAttrib
Xfor a description of possible values.
X.TP 20
XinputEditAttrib
XThe attributes with which the rectangular area is displayed when the field
Xis current.
XSee
X.I labelAttrib
Xfor a description of possible values.
XThe value A_INVIS can be used for such things as password fields.
XDespite the warning in <curses.h> that this value is not supported,
Xit is safe to use because its realization within
X.I DMM
Xis independent of that of
X.IR curses .
X.TP 20
XinputInstructVec
XA null terminated array of string pointers which should contain detailed
Xinstructions to the user on how to edit the field.
XThey are displayed, when the field is current,
Xat the bottom of the screen, one string per line.
XA NULL value indicates no instructions to display.
X.TP
XinputNumInstructs
XTo be left unset by the calling function and calculated by
X.B dmmRun
Xas the number of strings in
X.IR inputInstructVec .
X.TP 20
XinputAudit
XA pointer to a function that returns an
X.IR int .
XIt is invoked with 
X.I inputEditValue
Xas its single argument
Xwhen the user enters a keystroke that would make the field non-current.
XThis function is useful for auditing the value of
X.I inputEditValue
Xand displaying audit error messages to the user (using the message display
Xfunctions described below).
XIf NULL, it is not invoked.
XThe function must accept a single 
X.I char *
Xargument and must return one of the following DMMAUDITRETURN values, defined in 
X.IR dmm.h :
X.RS 20
X.TP 20
XdmmAuditOkay
XWhen returned the cursor is advanced to the appropriate editable field.
XUseful when the value of 
X.I inputEditValue
Xis okay.
X.TP 20
XdmmAuditBad
XWhen returned the cursor is moved to the first position of the current input
Xfield.
XUseful when the value of 
X.I inputEditValue
Xis needs correction by the user.
X.TP 20
X.PD 0
XdmmAuditReturnOkay,
X.TP 20
X.PD 
XdmmAuditReturnBad
XUseful when an inter-field audit needs to be performed.
XThese two values cause
X.B dmmRun
Xto return with
X.B dmmRetType
Xset to
X.I dmmOkayField
Xor
X.IR dmmBadField ,
Xrespectively, and the return value of
X.B dmmRun
Xset to the number of the current editable field.
XThe calling function can then do its inter-field audit, such as might be 
Xnecessary when the two fields are month-of-year and day-of-month, where
Xlegal values are interdependent and then reinvoke
X.BR dmmRun .
XSee the discussion of
X.I startField
Xbelow for further details on reinvocations of
X.B dmmRun
Xafter an inter-field audit.
X.RE
X.TP 20
X.PD 0
XnextInField,
X.TP 20
X.PD
XprevInField
XTo be left unset by the calling function and calculated by
X.BR dmmRun .
X.PP
XSome user keystrokes cause the next or preceding editable field to 
Xbecome current.
XThe order of editable fields is taken from the order of the DMMFIELD
Xstructures in
X.IR screen ;
Xthis list is considered circular,
Xe.g. the editable field that comes next after the
Xlast editable field is the first editable field in the
X.I screen
Xarray.
X.PP
XIf there are no fields to display, i.e. if this is a menu-only screen,
X.I screen
Xshould be NULL.
X.PP
XThe menu window is specified by
X.IR menu ,
Xa null terminated array of pointers to DMMENUITEM structures, defined in
X.IR dmm.h .
X.PP
XEach DMMMENUITEM describes one menu item, the elements of which are as follows:
X.TP 20
Xlabel
XA short string to be displayed in the menu item list.
X.TP 20
Xdescript
XA longer, more explanatory string that is displayed on the
Xsecond line of the menu window when
Xthe menu item is current.
X.TP 20
X.PD 0
Xcol,
X.TP 20
X.PD
Xpage
XTo be left unset by the calling function and calculated by
X.BR dmmRun .
X.PP
XIf there is to be no menu for the screen,
X.I menu
Xshould be NULL.
X.PP
XThe value of
X.I drawFlag
Xdetermines whether internal window drawing and calculations need to
Xtake place.
XPermissible values are:
X.TP 20
XdmmNew
XUsed when a screen is displayed for the first time
Xor when a screen is redisplayed after intervening call to
X.B dmmRun
Xhas displayed another screen.
X.TP 20
XdmmOld
XUsed when 
X.B dmmRun
Xis to display the same screen as when it was last called.
XNothing about the 
X.I screen
Xor
X.I menu
Xstructures nor the values that they point to may have been changed
Xsince the last call because the screen in this case is repainted
Xby a simple
X.I wrefresh
Xwith no internal window redrawing or recalculations.
X.PP
XThe value of
X.I valFlag
Xdetermines the initial values of the editable fields. 
XPermissible values are:
X.TP 20
XdmmInitVal
Xcauses the value of
X.I inputInitValue
Xto be copied to
X.I inputEditValue
Xbefore the screen is displayed and user interaction begun.
XThis is useful when a certain screen is to be displayed more than oncer
Xper execution of the program and some of those times it should
Xstart off with certain default values instead of with the edited values.
XThe default value of each field should be placed in the
X.I inputInitVal
Xparameter for the field.
X.TP 20
XdmmEdVal
Xcauses no such copying.
XThe values displayed are those that are already in the
X.I inputEditValue
Xparameters for each field.
X.PP
XThe value of
X.I startField
Xdetermines which field is made current when user interaction is begun;
Xit must be the index in
X.I screen
Xof a DMMFIELD that is editable, i.e. one that has a non-zero
X.IR inputLength .
XThe special value of -1 has a significance according to the value of
X.IR drawFlag .
XIf 
X.I drawFlag
Xis
X.IR dmmNew ,
Xthe starting editable field will be the 
Xfirst editable field (not necessarily the first field) in
X.IR screen .
XIf 
X.I drawFlag is
X.IR dmmOld ,
Xthe starting editable field depends upon the state of affairs when
Xlast invocation of
X.B dmmRun
Xreturned.
XIf it returned due to a menu selection
Xthe starting editable field will be the last current field.
XIf it returned because an
X.I inputAudit
Xfunction returned
X.I dmmAuditReturnOkay 
Xor
X.IR dmmAuditReturnBad ,
Xthe starting editable field will be either the next or preceding editable
Xfield (depending upon whether the user keystroke that invoked the audit
Xwas for moving to the next or preceding field).
XAs described above, this is useful after an inter-field edit has taken place
Xfor a seamless appearance to the separate invocations of
X.BR dmmRun .
XIf the inter-field audit showed valid editable field values, it is useful
Xto reinvoke
X.B dmmRun
Xwith an
X.I drawFlag
Xvalue of 
X.I dmmOld
Xand a
X.I startField
Xvalue of -1.
XIf the inter-field audit indicates that an editable field value needs
Xcorrection by the user,
Xan error message should be displayed and
X.B dmmRun
Xshould be reinvoked with an
X.I drawFlag
Xvalue of
X.I dmmOld
Xand
X.I startField
Xset to the index of the offending field.
X.PP
X.B dmmRun
Xreturns typically when a user choses a menu item (i.e. enters a carriage
Xreturn while in the menu state), but also when an
X.I inputAudit
Xfunction returns
X.I dmmAuditReturnOKay
Xor
X.IR dmmAuditReturnBad .
XIn the former case,
X.B dmmRun
Xreturns the index of the menu item chosen, and sets
X.B dmmRetType
Xto
X.IR dmmMenu .
XIn the latter cases, it returns the index of the associated editable field;
X.B dmmRetType
Xis set to
X.I dmmOkayField
Xor
X.I dmmBadField
Xdepending upon whether the audit function returned
X.I dmmAuditReturnOKay
Xor
X.IR dmmAuditReturnBad ,
Xrespectively.
XA menuless screen must therefore have an
X.I inputAudit
Xfunction that unconditionally returns either
X.I dmmAuditReturnOkay
Xor
X.IR dmmAuditReturnBad ;
Xotherwise there will be nothing the user can input that will cause
X.B dmmRun
Xto return!
X.SS Terminal initialization and cleanup
X.PP
XThe function
X.B dmmRun
Xprovides normal 
X.I curses
Xinitialization (e.g. puts the terminal in raw mode) and creates the various
Xwindows needed for its work.
XWhen all 
X.I curses
Xinteraction is finished in the program, whether through
X.I DMM
Xfunctions
Xor though other functions, the program should invoke
X.B dmmClose
Xto clean up and return the terminal to its previous state.
XThe function
X.B dmmInit
Xprovides the same initialization functionality as
X.B dmmRun
Xwithout the presentation of a screen.
XThis is useful when the program needs to do
X.I curses
Xwork (such as display of data on the screen using any of the message
Xdisplay function described below) prior to the first call of
X.BR dmmRun .
XIf initialization has already been done,
X.B dmmRun
Xwill not do it again (unless, of course, there has been an intervening
Xinvocation of
X.BR dmmClose ).
X.SS Non-interactive display of data
X.PP
XData, such as error messages, may be displayed in the data entry window
Xwithout the user-interaction of a screen.
XThis is useful not only between invocations of
X.B dmmRun 
X(or
X.B dmmInit
Xand
X.BR dmmRun ),
Xbut also within the
X.I inputAudit
Xfunctions to display messages to the user when the user needs to correct
Xan editable field.
X.B dmmPutMsgs
Xis the simplest of the message display functions; it adds a block of
Xmessages to the existing data entry window and leaves them there.
X.I mesgblock
Xis a null-terminated array of DMMMESSAGE structures, defined in
X.IR dmm.h .
XEach DMMMESSAGE describes one message string,
Xthe elements of which are as follows:
X.TP 20
Xvalue
XThe string to be displayed.
X.TP 20
X.PD 0
Xcol,
X.TP 20
X.PD
Xline
XThe coordinates in the data entry window where the first character of
X.I value
Xis displayed.
XA negative value for
X.I line
Xrepresents lines from the bottom
Xof the window, the bottom line represented with -1.
X.TP 20
Xattrib
XThe attributes with which 
X.I value
Xis displayed.
XSee
X.IR labelAttrib ,
Xabove, for a description of possible values.
X.PP
XMessages are cleared from the window with the next invocation of
X.B dmmRun
Xor with an invocation of
X.BR dmmRmMsgs .
XIf
X.I removetype
Xis
X.IR dmmRemoveAll ,
Xall message displayed since the last invocation of
X.B dmmRun
Xor 
X.B dmmInit
Xare removed from the window.
XIf
X.I removetype
Xis
X.IR dmmRemoveLast
Xonly the messages displayed with the last invocation of
X.B dmmPutMsgs
Xare removed.
X.PP
X.B dmmPutMsgsTimer
Xdisplays the messages described by
X.IR mesgblock ,
Xsleeps for
X.I duration
Xseconds, removes them and returns.
X.PP
X.B dmmPutMsgsHit
Xdisplays the messages described by
X.IR mesgblock ,
Xdisplays an instruction in the instruction window for the user to press
Xany key to continue, and when the user enters a keystroke, the messages
Xare removed and the function returns.
X.PP
X.B dmmPutMsgsNext
Xdisplays the messages described by
X.I mesgblock
Xand removes them upon the next user keystroke entered under an invocation of 
X.B dmmRun
X(unless removed first by a call to
X.IR dmmRmMsgs ).
XThe keystroke is interpreted by
X.B dmmRun
Xas it would normally rather than discard it as it is under an invocation of
X.BR dmmPutMsgsHit .
X.PP
X.B dmmClear
Xclears everything from the data entry window,
Xall messages and any display from the last call of
X.BR dmmRun .
XThis is useful prior to using
X.RI non- DMM
Xfunctions for screen manipulation and after a call to 
X.B dmmRun
Xwhen you don't want the data entry portion of the screen displayed any longer.
X.PP
X.B dmmGetNumLines
Xreturns the number of lines in the data entry window.
XThis is useful, for example, when displaying tabular data as a set of messages,
Xand one needs to know how many lines of the table can be displayed in a single
Xscreenful.
X.SS Non-interactive auditing of editable field values
XAs mentioned above, a field's input value is audited under
X.B dmmRun
Xonly when the user input a keystroke that would change the field from current
Xto non-current.
XConsequently, fields that the user doesn't traverse go unaudited under
X.BR dmmRun ,
Xa problem with fields whose initial input value (see discussion
Xof
X.I valFlag
Xabove) is not a valid value;
X(this is often the case,
Xe.g. when there is no default value to give for a field's
X.I initVal
Xbut a value for the field is required nontheless.)
XThe solution is to call
X.BR dmmReview ,
Xwhich invokes the
X.I inputAudit
Xfunction of each editable field and 
Xreturning the index of the first editable field whose
X.I inputAudit
Xreturns either
X.I dmmAuditBad
Xor
X.I dmmAuditReturnBad
Xand by setting 
X.B dmmRetType
Xto
X.IR dmmBadField .
XIf no editable fields have invalid values, it returns -1 and sets
X.B dmmRetType
Xto
X.IR dmmOkayField .
XTherefore, if a screen has initial editable field values that are not valid,
Xit is recommended that the call to 
X.B dmmRun
Xthat presents the screen be followed by a call to
X.B dmmReview
X(given an appropriate return value of
X.BR dmmRun ).
X.SS Data structures as function arguments
X.I dmm.h
X.I typedefs 
Xa number of data structures for use with these functions:
X.PD 0
X.PP
X.nf
X    typedef struct dmmField DMMFIELD, *DMMSCREEN[], **DMMSCREEN2;
X    typedef struct dmmMenuItem DMMMENUITEM, *DMMMENU[], **DMMMENU2;
X    typedef struct dmmMessage DMMMESSAGE, *DMMMESGBLK[], **DMMMESGBLK2;
X.fi
X.PP
XThe first of each triplet are the basic structures used, and the second
Xare the array-of-pointers data types used above as arguments to the 
X.I DMM
Xfunctions.
XHowever, sometimes it is useful (and syntactically equivalent in some contextss)
Xto use a pointer pointer instead of an array of pointers for the argument of
Xthe called function, as are the third of each triplet.
XAn example of a situation in which the pointer pointers are
Xuseful is as follows:
X.PD
X.PP
X.nf
X    int		startField = -1;
X    DMMDRAWFLAG	drawFlag = dmmNew;
X    DMMVALFLAG	valFlag = dmmInitVal;
X		/* initialization of the DMMSCREENs and DMMMENUs
X		 * omitted in this example for brevity
X		 */
X    DMMSCREEN	normal_screen, root_screen;
X    DMMSCREEN2	screen_to_use;
X    DMMMENU	normal_menu, root_menu;
X    DMMMENU2	menu_to_use;
X    ...
X    if(getuid() == 0) {
X	...
X	screen_to_use = root_screen;
X	menu_to_use = root_menu;
X	...
X    } else {
X	...
X	screen_to_use = normal_screen;
X	menu_to_use = normal_menu;
X	...
X    }
X    ...
X    dmmRun(startField, screen_to_use, menu_to_use, drawFlag, valFlag);
X.fi
X.SH EXAMPLE
X.PP
XThe sources for a demo program that illustrates good use of
X.I DMM
Xare included with the
X.I DMM
Xsources.
XNotice how the declaration of the various
X.I DMM
Xdata structures as static variables enables easy initialization.
X.SH DIAGNOSTICS
X.PP
X.B dmmRun
Xreturns -1 if the screen is faulty,
X(e.g. a label or input portion of a field or a menu item
Xdoes not fit on the screen,
Xeach menu item label does not begin with a unique alphabetic character,
X.I startField
Xis not the index of an editable field,
Xthere is no menu and the data entry portion has no editable fields,
Xetc.), and
Xa diagnostic message is displayed on the screen.
XNon-error return values for
X.B dmmRun
Xand
X.B dmmReview
Xare described above.
X.SH SEE ALSO
X.PP
X.IR curses (3V)
X.SH AUTHOR
X.PP
XCopyright \(co 1991, Robert Bernardo, rob@mtdiablo.Concord.CA.US,
Xan employee of Pande, Inc.
XPermission to use granted only if this 
Xnotice is not removed from this manual page.
SHAR_EOF
chmod 0444 dmm.3 || echo "restore of dmm.3 fails"
set `wc -c dmm.3`;Sum=$1
if test "$Sum" != "23153"
then echo original size 23153, current size $Sum;fi
echo "x - extracting dmm.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmm.c &&
X#ifndef LINT
Xstatic char     dmm_c_id[] = {"@(#)dmm.c	1.1 \
XCopyright (c) 1991 Robert Bernardo, rob@mtdiablo.Concord.CA.US, \
Xan employee of Pande, Inc. \
XPermission to use granted only if this notice is not removed from \
Xthis source file nor from any binaries produced from it."};
X#endif
X
X#include <ctype.h>
X#include <curses.h>
X#include <string.h>
X#include <signal.h>
X#include <term.h>
X#include <sys/ttychars.h>
X#include "dmm.h"
X
X/* see man page for description */
XDMMRETTYPE      dmmRetType;
X
X/* enum for type of cursor change */
Xenum change {
X    previous, stayput, movecursor, next
X};
X
X/* error message data structures */
Xstatic char     		errorMsgBuf[BUFSIZ];
Xstatic DMMMESSAGE 		errorMsg = {errorMsgBuf, 0, 0, A_REVERSE};
Xstatic DMMMESGBLK 		errorMsgBlk = {&errorMsg, 0};
X
X#define CNVRT_LN_NUM(lnnum)	(lnnum < 0 ? LFLINE+lnnum+1 : lnnum)
X#define DISP_ERROR_0(mesg)	{strcpy(errorMsg.value,mesg);	\
X				dmmPutMsgsHit(errorMsgBlk);}
X#define DISP_ERROR_1(fmt,arg)	{sprintf(errorMsg.value,fmt,arg);	\
X				dmmPutMsgsHit(errorMsgBlk);}
X#define DISP_ERROR_2(fmt,a,b)	{sprintf(errorMsg.value,fmt,a,b);	\
X				dmmPutMsgsHit(errorMsgBlk);}
X#define DISP_ERROR_3(fmt,a,b,c) {sprintf(errorMsg.value,fmt,a,b,c); \
X				  dmmPutMsgsHit(errorMsgBlk);}
X
X/* special command input character symbols */
X#define TAB	'\t'
X#define RETURN	'\r'
X#define DELETE	CERASE
X
X/* menu scroll symbols */
X#define LEFT_SCROLL_SYMBOL	"<<"
X#define RIGHT_SCROLL_SYMBOL	">>"
X
X/* parameters of the windows */
X/* 	- number of lines in each window */
X#define MLINES	2		/* menu window */
X#define ILINES	1		/* instruct window */
X#define FLINES	(LINES - MLINES - ILINES)	/* field window */
X
X/*	- menu line functions */
X#define MENU_LABEL_LINE		0
X#define MENU_DESCRIPT_LINE	1
X
X/* 	- line number of first line of windows */
X
X#define FILINE	0		/* instruct window */
X#define FMLINE	(FILINE + ILINES)	/* menu window */
X#define FFLINE	(FMLINE + MLINES)	/* field window */
X
X/* 	- line number of last line of windows in window (not in screen) */
X#define LFLINE	(FLINES - 1)	/* field window */
X
X#define MENUI_SPACING 2		/* number of spaces between */
X
X/* string constants */
X/* 	- general instruction messages */
X#define MENU_INSTRUCT		"Cursor to desired item and enter RETURN. Press control-h for help."
X#define GOTO_MENU_INSTRUCT	"Press control-g to enter menu. Press control-h for help."
X#define NULL_MENU_INSTRUCT	"Press control-h for help."
X#define ANY_KEY_INSTRUCT	"Press any key to continue."
X
X/* 	- diagnostic error messages */
X#define BAD_FIELDNUM	"Program error! Target field %d beyond max %d.\n"
X#define NO_INPUT_MSG	"Program error! No input fields nor menu items.\n"
X#define NOT_IFIELD	"Program error! Target field %d not an input field.\n"
X#define WIDE_LABEL	"Program error! Menu label %d too wide for screen.\n"
X#define NON_ALPHA	"Program error! Menu item %d doesn't begin with an alphabetic character."
X#define LABEL_NO_FIT	"Program error! Label for field %d won't fit on screen.\n"
X#define IP_NO_FIT	"Program error! Input part of field %d won't fit on screen.\n"
X
X#define DMMWATTRSET(win, attrib)	wattrset(win, attrib&~A_INVIS)
X#define DMMWATTRON(win, attrib)	wattron(win, attrib&~A_INVIS)
X#define DMMWATTROFF(win, attrib)	wattroff(win, attrib&~A_INVIS)
X
X
X/* global variables */
X/* general information discoverd about a screen */
Xstatic int      numFields,	/* number of fields */
X                numInputParts,	/* number of fields with input parts */
X                numMenuItems,	/* number of menu items */
X                numMenuPages,	/* number of menu pages */
X                curMenuItem,	/* current menu item */
X                firstInputFld;	/* first field with input part */
X/* flags needed to manage the removal of messages displayed with
X * dmmPutMsgsNext() when not called under a call to dmmRun(), e.g. not
X * under an inputAudit function.
X */
Xstatic int      interactive,	/* set when dmm is interactive */
X                wantNextClear,	/* set when next keystroke should clear
X				 * messages from screen */
X                needAddMsgs;	/* set when need to add messages from a
X				 * dmmPutMsgsNext() called when dmm was not
X				 * interactive */
XPMNmsgsWereLast;		/* set when the last messages added were
X				 * added by dmmPutMsgsNext(). This is needed
X				 * because if dmmRmMsgs() is called to remove
X				 * just the last messages, the need to clear
X				 * the dmmPutMsgsNext() is removed and need
X				 * not be done on the next user keystroke
X				 * under dmmRun(). */
X
X/* global variables for parameters so they don't have to be passed all over */
Xstatic DMMSCREEN2 dupeScreen;	/* field vector */
Xstatic DMMMENU2 dupeMenu;	/* menu vector */
X/* windows */
Xstatic WINDOW  *fieldWin,	/* field window */
X               *fieldWinCopy,	/* saved window between dmmRun() calls */
X               *fieldWinPreMsg,	/* saved window before messages */
X               *mesgWin,	/* overlay window of messages */
X               *instructWin,	/* general instruction window */
X               *instructWinCopy,/* copy of instruction window */
X               *menuWin,	/* menu window */
X               *menuWinCopy;	/* copy menu window */
X/* miscellaneous */
Xstatic int      isOpen;		/* 0 if dmmInit needs to be performed */
X
Xstatic int      menuIndex[(int) ('z' - 'a' + 1)];	/* index by letter into
X							 * menu by initial
X							 * letters of menu
X							 * labels. */
X/* structures for help screens */
X#define KEY_COLUMN	5
X#define	EFFECT_COLUMN	22
X#define	INTRO_LINE	0
X#define	HELP_LINE	(INTRO_LINE + 2)
X#define	REDRAW_LINE	(HELP_LINE + 1)
X#define NEXT_LINE	(REDRAW_LINE + 1)
X#define PREV_LINE	(NEXT_LINE + 1)
X#define RETURNS_LINE	(PREV_LINE + 1)
X#define RETURNM_LINE	(RETURNS_LINE + 1)
X#define DELETE_LINE	(RETURNM_LINE + 1)
X#define ARROW_LINE	(DELETE_LINE + 1)
X#define CLEAR_LINE	(ARROW_LINE + 1)
X#define GOTO_LINE	(CLEAR_LINE + 1)
X#define NORMALS_LINE	(GOTO_LINE + 1)
X#define NORMALM_LINE	(NORMALS_LINE + 1)
X
Xstatic DMMMESSAGE introHelpKey =
X{"Key", KEY_COLUMN, INTRO_LINE, A_REVERSE};
Xstatic DMMMESSAGE helpHelpKey =
X{"control-h", KEY_COLUMN, HELP_LINE};
Xstatic DMMMESSAGE redrawHelpKey =
X{"control-r", KEY_COLUMN, REDRAW_LINE};
Xstatic DMMMESSAGE nextHelpKey =
X{"control-n/tab", KEY_COLUMN, NEXT_LINE};
Xstatic DMMMESSAGE prevHelpKey = {"control-p", KEY_COLUMN, PREV_LINE};
Xstatic DMMMESSAGE returnHelpKey = {"return", KEY_COLUMN, RETURNS_LINE};
Xstatic DMMMESSAGE deleteHelpKey = {"delete", KEY_COLUMN, DELETE_LINE};
Xstatic DMMMESSAGE arrowHelpKey = {"arrow key", KEY_COLUMN, ARROW_LINE};
Xstatic DMMMESSAGE clearHelpKey = {"control-c", KEY_COLUMN, CLEAR_LINE};
Xstatic DMMMESSAGE gotoHelpKey = {"control-g", KEY_COLUMN, GOTO_LINE};
Xstatic DMMMESSAGE normalHelpKey =
X{"`normal' keys", KEY_COLUMN, NORMALS_LINE};
X
Xstatic DMMMESSAGE introEffectKey =
X{"Effect", EFFECT_COLUMN, INTRO_LINE, A_REVERSE};
Xstatic DMMMESSAGE helpEffectKey =
X{"display help (this screen)", EFFECT_COLUMN, HELP_LINE};
Xstatic DMMMESSAGE redrawEffectKey =
X{"redraw screen", EFFECT_COLUMN, REDRAW_LINE};
Xstatic DMMMESSAGE nextEffectKey =
X{"go to next data field or menu item", EFFECT_COLUMN, NEXT_LINE};
Xstatic DMMMESSAGE prevEffectKey =
X{"go to previous data field or menu item", EFFECT_COLUMN, PREV_LINE};
Xstatic DMMMESSAGE returnScreenEffectKey =
X{"if in data entry, go to next data field or,", EFFECT_COLUMN, RETURNS_LINE};
Xstatic DMMMESSAGE returnMenuEffectKey =
X{"if in menu, accept menu choice", EFFECT_COLUMN, RETURNM_LINE};
Xstatic DMMMESSAGE deleteEffectKey =
X{"erase preceding character in current data field", EFFECT_COLUMN, DELETE_LINE};
Xstatic DMMMESSAGE arrowEffectKey =
X{"move within current data field", EFFECT_COLUMN, ARROW_LINE};
Xstatic DMMMESSAGE clearEffectKey =
X{"clear to end of current data field and go to next",
XEFFECT_COLUMN, CLEAR_LINE};
Xstatic DMMMESSAGE gotoEffectKey =
X{"move from data entry to menu or vice versa", EFFECT_COLUMN, GOTO_LINE};
Xstatic DMMMESSAGE normalScreenEffectKey =
X{"if in data entry, input letter into data field or,",
XEFFECT_COLUMN, NORMALS_LINE};
Xstatic DMMMESSAGE normalMenuEffectKey =
X{"if in menu, highlight menu item with same first letter",
XEFFECT_COLUMN, NORMALM_LINE};
X
Xstatic DMMMESGBLK helpMsgVec =
X{&introHelpKey, &helpHelpKey, &redrawHelpKey, &nextHelpKey,
X    &prevHelpKey, &returnHelpKey, &deleteHelpKey, &arrowHelpKey, &clearHelpKey,
X    &gotoHelpKey, &normalHelpKey, &introEffectKey,
X    &helpEffectKey, &redrawEffectKey, &nextEffectKey, &prevEffectKey,
X    &returnScreenEffectKey, &deleteEffectKey, &returnMenuEffectKey,
X    &arrowEffectKey, &clearEffectKey, &gotoEffectKey, &normalScreenEffectKey,
X&normalMenuEffectKey, 0};
X/* dmmInit()	put the terminal in the right mode and set up curses with
X *		the right windows.
X */
Xvoid
XdmmInit ()
X{
X    initscr ();
X    raw ();
X    nonl ();
X    noecho ();
X
X    /* get terminal in right mode */
X    putp (init_1string);
X    putp (init_2string);
X    putp (init_3string);
X    putp (cursor_visible);	/* blinking cursor */
X
X    /* create windows */
X    fieldWin = newwin (FLINES, COLS, FFLINE, 0);
X    fieldWinCopy = newwin (FLINES, COLS, FFLINE, 0);
X    fieldWinPreMsg = newwin (FLINES, COLS, FFLINE, 0);
X    mesgWin = newwin (FLINES, COLS, FFLINE, 0);
X    instructWin = newwin (ILINES, COLS, FILINE, 0);
X    instructWinCopy = newwin (ILINES, COLS, FILINE, 0);
X    menuWin = newwin (MLINES, COLS, FMLINE, 0);
X    menuWinCopy = newwin (MLINES, COLS, FMLINE, 0);
X
X    /* leave cursor where code says to */
X    leaveok (instructWin, FALSE);
X    leaveok (fieldWin, FALSE);
X    leaveok (menuWin, FALSE);
X
X    /* treat escape sequences as single characters */
X    keypad (fieldWin, TRUE);
X    keypad (menuWin, TRUE);
X
X    /* set attributes of windows fixed at a single attribute */
X    DMMWATTRSET (instructWin, A_REVERSE);
X
X    isOpen = 1;
X
X    return;
X}
X
X
X/* redraw()	unconditionally redraw the entirety of each window.
X */
Xstatic void
Xredraw ()
X{
X    clearok (fieldWin, TRUE);
X    clearok (instructWin, TRUE);
X    clearok (menuWin, TRUE);
X    wnoutrefresh (fieldWin);
X    wnoutrefresh (instructWin);
X    wnoutrefresh (menuWin);
X    doupdate ();
X    return;
X}
X
X
X/* dmmClose()	cleanup dmm and close down curses */
Xvoid
XdmmClose ()
X{
X    delwin (fieldWin);
X    delwin (fieldWinCopy);
X    delwin (fieldWinPreMsg);
X    delwin (mesgWin);
X    delwin (instructWin);
X    delwin (instructWinCopy);
X    delwin (menuWin);
X    delwin (menuWinCopy);
X    isOpen = 0;
X    clear ();
X    refresh ();
X    endwin ();
X    putp (cursor_normal);
X    return;
X}
X
X
X/* dmmGetNumLines	return the number of lines in fieldWin */
Xint
XdmmGetNumLines ()
X{
X    return FLINES;
X}
X
X/* outOfTheWay		put the cursor in a place where it's not distracting */
Xvoid
XoutOfTheWay()
X{
X    wmove (instructWin, 0, COLS - 1);
X    wnoutrefresh (instructWin);
X    return;
X}
X
X
X/* dmmRmMsgs()	remove messages displayed */
Xvoid
XdmmRmMsgs (removeType)
XDMMREMOVETYPE   removeType;
X{
X    /*
X     * remove residue of dmmPutMsgsNext type of messages as appropriate to
X     * whether they were last or wether we are clearing all messages not just
X     * the last messages
X     */
X    if ((removeType == dmmRemoveAll) || (PMNmsgsWereLast)) {
X	wclear (mesgWin);
X	needAddMsgs = 0;
X	wantNextClear = 0;
X    }
X    /* remove proper "layer" of messages */
X    overwrite ((removeType == dmmRemoveAll ? fieldWinCopy : fieldWinPreMsg),
X	       fieldWin);
X    wnoutrefresh (fieldWin);
X    outOfTheWay();
X    doupdate();
X    return;
X}
X
X
X/* wGetCh()		like wgetch(), but clears any messages displayed by
X *			dmmPutMesgsNext()
X */
Xstatic int
XwGetCh (win)
XWINDOW         *win;
X{
X    int             keystroke;
X    /* get keystroke */
X    keystroke = wgetch (win);
X
X    /* if need to clear messages, do so */
X    if (wantNextClear) {
X	dmmRmMsgs (dmmRemoveLast);
X	wantNextClear = 0;
X    }
X    /* if mesgWin was used, clear it so stale messages don't get reused */
X    if (needAddMsgs) {
X	werase (mesgWin);
X	needAddMsgs = 0;
X    }
X    return keystroke;
X}
X
X
X/* addMsgs()	add messages to the indicated window */
Xstatic void
XaddMsgs (win, mesgBlock)
XWINDOW         *win;
XDMMMESGBLK      mesgBlock;
X{
X    /* display each item in the message array */
X    while (*mesgBlock) {
X	DMMWATTRON (win, (*mesgBlock)->attrib);
X	mvwaddstr (win, CNVRT_LN_NUM ((*mesgBlock)->line),
X		   (*mesgBlock)->col, (*mesgBlock)->value);
X	DMMWATTROFF (win, (*mesgBlock)->attrib);
X	mesgBlock++;
X    }
X    wnoutrefresh (win);
X    return;
X}
X
X
X/* doPutMsgs()	see comments for dmmPutMsgs, if save is set, write
X * 			messages as well to mesgWin
X */
Xstatic void
XdoPutMsgs (mesgBlock, save)
XDMMMESGBLK      mesgBlock;
Xint             save;
X{
X
X    /* save copy of window as it currently is */
X    overwrite (fieldWin, fieldWinPreMsg);
X
X    /* display messages on fieldWin, and on mesgWin as needed */
X    addMsgs (fieldWin, mesgBlock);
X    if (save)
X	addMsgs (mesgWin, mesgBlock);
X    PMNmsgsWereLast = 0;	/* may be set by outer call to
X				 * dmmPutMsgsNext() */
X    outOfTheWay();
X    doupdate ();
X}
X
X
X/* dmmPutMsgsNext()	remove messages displayed  until a key is input
X * 			under dmmRun or until explicitly removed
X */
Xvoid
XdmmPutMsgsNext (mesgBlock)
XDMMMESGBLK      mesgBlock;
X{
X    wantNextClear = mesgBlock ? 1 : 0;
X    if (!interactive)
X	needAddMsgs = 1;
X    doPutMsgs (mesgBlock, needAddMsgs);
X    PMNmsgsWereLast = 1;
X    return;
X}
X
X
X/* dmmPutMsgs()		display messages */
Xvoid
XdmmPutMsgs (mesgBlock)
XDMMMESGBLK      mesgBlock;
X{
X    doPutMsgs (mesgBlock, 0);
X    return;
X}
X
X/* dmmClear()		clear everything irretrievably from fieldWin */
Xvoid
XdmmClear()
X{
X    wclear(mesgWin);
X    wnoutrefresh(mesgWin);
X    wclear(fieldWin);
X    wnoutrefresh(fieldWin);
X    wclear(fieldWinCopy);
X    wnoutrefresh(fieldWinCopy);
X    outOfTheWay();
X    doupdate();
X    return;
X}
X
X
X/* dmmPutMsgsTimer()		display messages for a certain number of secs */
Xvoid
XdmmPutMsgsTimer (mesgBlock, duration)
XDMMMESGBLK      mesgBlock;
Xunsigned        duration;
X{
X    /* display messages */
X    dmmPutMsgs (mesgBlock);
X
X    /* sleep and then redisplay the original window without the messages */
X    (void) sleep (duration);
X    dmmRmMsgs (dmmRemoveLast);
X
X    return;
X}
X
X
X/* dispInstruct()	display a message in the general instruction window */
Xstatic void
XdispInstruct (msg)
Xchar           *msg;
X{
X    mvwaddstr (instructWin, 0, 0, msg);
X    wclrtoeol (instructWin);
X    wnoutrefresh (instructWin);
X    return;
X}
X
X
X/* doPutMsgsHit()	display the messages indicated by mesgBlock
X *			until the user enters a keystroke
X */
Xvoid static
XdoPutMsgsHit (mesgBlock, restoreThem)
XDMMMESGBLK      mesgBlock;
Xint             restoreThem;
X{
X    register int 	x;
X    overwrite (instructWin, instructWinCopy);
X    if (restoreThem) {
X	overwrite (fieldWin, fieldWinCopy);
X	overwrite (menuWin, menuWinCopy);
X	wclear (fieldWin);
X	wclear (menuWin);
X	wnoutrefresh (menuWin);
X    }
X    dispInstruct (ANY_KEY_INSTRUCT);
X    flushinp ();
X    dmmPutMsgs (mesgBlock);
X    wgetch (fieldWin);
X    flushinp ();
X    if (restoreThem) {
X	overwrite (fieldWinCopy, fieldWin);
X	overwrite (menuWinCopy, menuWin);
X	wnoutrefresh (fieldWin);
X	wnoutrefresh (menuWin);
X    } else
X	dmmRmMsgs (dmmRemoveLast);
X    overwrite (instructWinCopy, instructWin);
X
X    /* That last overwrite makes the whole window inverse video including
X     * trailing blanks, not just the message part, so we have to clear
X     * the trailing spaces while in normal attributes.
X     */
X    DMMWATTROFF (instructWin, A_REVERSE);
X    x = instructWin->_maxx;
X    while(x--) {
X	if((mvwinch(instructWin, 0, x) & A_CHARTEXT) != ' ') {
X	    x++;
X	    break;
X	}
X    }
X    wmove(instructWin, 0, x);
X    wclrtoeol(instructWin);
X    DMMWATTRON (instructWin, A_REVERSE);
X    wnoutrefresh (instructWin);
X    doupdate ();
X    return;
X}
X
X
X/* dmmPutMsgsHit()	display a message until the user presses any key */
Xvoid
XdmmPutMsgsHit (mesgBlock)
XDMMMESGBLK      mesgBlock;
X{
X    doPutMsgsHit (mesgBlock, 0);
X    return;
X}
X
X
X/* dispInField()	display the input part of the specified field
X *			with the specified attributes.
X */
Xstatic void
XdispInField (fldPtr, attrib)
Xregister DMMFIELD *fldPtr;
Xlong            attrib;
X{
X    register int    charNum;	/* counter of characters output */
X    int             doNulls,	/* set if characters run out */
X                    len;	/* length of field */
X    if (fldPtr->inputLength) {
X	DMMWATTRON (fieldWin, attrib);
X	charNum = 0;
X	doNulls = (attrib & A_INVIS) ? 1 : 0;
X	for (charNum = 0, len = fldPtr->inputLength; charNum < len; charNum++) {
X	    if (fldPtr->inputEditValue[charNum] == '\0')
X		doNulls = 1;	/* we got to end of string, from here to end
X				 * output blanks */
X	    mvwaddch (fieldWin,
X		      fldPtr->inputLine + (charNum / fldPtr->inputMaxCols),
X		      fldPtr->inputCol + (charNum % fldPtr->inputMaxCols),
X		      doNulls ? ' ' : fldPtr->inputEditValue[charNum]);
X	}
X	DMMWATTROFF (fieldWin, attrib);
X    }
X    return;
X}
X
X
X/* calcDflts()	calculate line and column numbers for the label
X * 		and input part of data fields; a negative line
X *		number represents lines from the window bottom,
X *		with -1 being the bottom; a negative column for
X *		the input part of the field indicates the number
X *		of columns separating it and the end of the label part;
X *		calculate the number of full length lines and
X *		the length of the last line (if not full length)
X *		of the input part.
X */
Xstatic void
XcalcDflts (fldPtr)
Xregister DMMFIELD *fldPtr;
X{
X
X    /* calculate line numbers */
X    fldPtr->labelLine = CNVRT_LN_NUM (fldPtr->labelLine);
X    fldPtr->inputLine = CNVRT_LN_NUM (fldPtr->inputLine);
X
X    /* calculate input part column number */
X    if (fldPtr->inputCol < 0)
X	fldPtr->inputCol = fldPtr->labelCol
X	    + strlen (fldPtr->labelValue) - fldPtr->inputCol - 1;
X
X    /* calculate dimensions of lines of input part from length; if
X     * inputMaxCols is 0, field will be one line of inputLength chars
X     */
X    if (fldPtr->inputMaxCols) {
X	fldPtr->inputFullLines = fldPtr->inputLength / fldPtr->inputMaxCols;
X	fldPtr->inputShortCols = fldPtr->inputLength % fldPtr->inputMaxCols;
X    } else {
X	fldPtr->inputMaxCols = fldPtr->inputLength;
X	fldPtr->inputFullLines = 1;
X	fldPtr->inputShortCols = 0;
X    }
X
X    return;
X}
X
X
X/* calcMenu()	calculate page and column for each menu item
X *		and create index for menu; return -1 if
X *		more than one item label begin with the same
X *		letter, else 0.
X */
Xstatic int
XcalcMenu ()
X{
X    int             letter,
X                    thisPage,
X                    nextCol,
X                    scrollSymSize = strlen (LEFT_SCROLL_SYMBOL),
X                    numScrollSymbols,
X                    itemLen,
X                    itemNum = 0;
X    /* initialize menu index */
X    for (letter = 0; letter < (int) ('z' - 'a' + 1); letter++)
X	menuIndex[letter] = -1;
X
X    /* get count of menu items */
X    while (dupeMenu[itemNum++]);
X    numMenuItems = itemNum - 1;
X
X    /* start on first column of first page */
X    thisPage = 0;
X    nextCol = 0;
X    itemNum = 0;
X    while (dupeMenu[itemNum]) {
X
X	/* register item in index */
X	letter = (int) *(dupeMenu[itemNum]->label);
X	if (!(isascii (letter) && isalpha (letter))) {
X	    DISP_ERROR_1 (NON_ALPHA, itemNum);
X	    return -1;
X	}
X	if (isupper (letter))
X	    letter = tolower (letter);
X	letter -= (int) 'a';	/* start from 0, not 'a' */
X	if (menuIndex[letter] == -1)
X	    menuIndex[letter] = itemNum;
X	else
X	    menuIndex[letter] = -2;;
X
X	itemLen = strlen (dupeMenu[itemNum]->label);
X
X	/* check to see that label isn't wider than the window itself if this
X	 * menu has only one item, then we don't need to worry about fitting
X	 * in any scroll symbols; if we are already beyond the first page and
X	 * there are no more items, we need to worry about only one scroll
X	 * symbol also fitting; if we are already beyond the first page and
X	 * there are more items, we need to worry about two scroll symbols
X	 * also fitting.
X	 */
X	if (numMenuItems == 1)
X	    numScrollSymbols = 0;
X	else if ((thisPage > 0) && ((itemNum + 1) == numMenuItems))
X	    numScrollSymbols = 1;
X	else
X	    numScrollSymbols = 2;
X	if ((itemLen +
X	     (numScrollSymbols * (scrollSymSize + MENUI_SPACING))) > COLS) {
X	    DISP_ERROR_1 (WIDE_LABEL, itemNum);
X	    return -1;
X	}
X
X	/* check to see if it will fit on this page; if not the last item we
X	 * need to make sure on scrolling symbol will fit as well.
X	 */
X	numScrollSymbols = ((itemNum + 1) == numMenuItems) ? 0 : 1;
X	if ((itemLen + nextCol +
X	     (numScrollSymbols * (scrollSymSize + MENUI_SPACING))) > COLS) {
X
X	    /* won't fit on this page, start new page */
X	    thisPage++;
X	    nextCol = scrollSymSize + MENUI_SPACING;
X	}
X
X	/* assign its col and page */
X	dupeMenu[itemNum]->col = nextCol;
X	dupeMenu[itemNum]->page = thisPage;
X	nextCol += (itemLen + MENUI_SPACING);
X
X	itemNum++;
X    }
X
X    /* Set global menu variables; hmm ... numMenuItems already set above */
X    numMenuPages = thisPage + 1;
X    return 0;
X}
X
X
X/* dispMenuItem()	display menu item highlighted or not
X *			and display description field if highlighted
X */
Xstatic void
XdispMenuItem (mitemPtr, highlight)
XDMMMENUITEM    *mitemPtr;
Xint             highlight;
X{
X    if (mitemPtr) {
X	if (highlight)
X	    DMMWATTRON (menuWin, A_REVERSE);
X	mvwaddstr (menuWin, MENU_LABEL_LINE, mitemPtr->col,
X		   mitemPtr->label);
X	if (highlight) {
X	    DMMWATTROFF (menuWin, A_REVERSE);
X	    mvwaddstr (menuWin, MENU_DESCRIPT_LINE, 0, mitemPtr->descript);
X	    wclrtoeol (menuWin);
X	}
X    }
X    return;
X}
X
X
X/* dispMenuPage()	display page of menu items containing the
X *			item indicated; if highlight is TRUE
X *			highlight the menu item; if force is TRUE,
X *			redisplay from scratch.
X */
Xstatic void
XdispMenuPage (itemNum, highlight, dmmRenew)
Xint             itemNum,
X                highlight,
X                dmmRenew;
X{
X    static int      lastPageDisplayed = -1,
X                    lastItemHighlighted = -1;
X    int             itemIndex,
X                    pageNum;
X
X    /* if page is current only redisplay old item unhighlighted and new item
X     * highlighted, else display whole page from scratch adding right and
X     * left scrolling symbols as needed
X     */
X
X    if (!dmmRenew && (dupeMenu[itemNum]->page == lastPageDisplayed)) {
X
X	if (itemNum != lastItemHighlighted)
X	    if (lastItemHighlighted != -1)
X		dispMenuItem (dupeMenu[lastItemHighlighted], 0);
X	dispMenuItem (dupeMenu[itemNum], highlight);
X
X    } else {
X
X	werase (menuWin);
X	pageNum = dupeMenu[itemNum]->page;
X	for (itemIndex = 0; itemIndex < numMenuItems; itemIndex++)
X	    if (dupeMenu[itemIndex]->page == pageNum)
X		dispMenuItem (dupeMenu[itemIndex],
X			      ((itemIndex == itemNum) && highlight));
X
X	if (pageNum)
X	    mvwaddstr (menuWin, MENU_LABEL_LINE, 0, LEFT_SCROLL_SYMBOL);
X	if (pageNum + 1 < numMenuPages)
X	    mvwaddstr (menuWin, MENU_LABEL_LINE,
X		  COLS - sizeof (RIGHT_SCROLL_SYMBOL), RIGHT_SCROLL_SYMBOL);
X
X	lastPageDisplayed = pageNum;
X    }
X    lastItemHighlighted = itemNum;
X    wnoutrefresh (menuWin);
X    return;
X}
X
X
X/* layout()		do layout and calculations for the first display */
Xstatic int
Xlayout ()
X{
X    register int    fldNum = 0;	/* number of current field */
X    register DMMFIELD *fieldPtr;
X    int             inputPartCnt = 0,	/* number of input fields so far */
X                    lastInFieldNum = -1,
X                    firstInFieldNum;
X
X    /* indicate first input field not yet discovered */
X    firstInputFld = -1;
X
X    /* erase previous contents of windows */
X    werase (fieldWin);
X    werase (instructWin);
X    werase (menuWin);
X
X    /* draw field window */
X    if (dupeScreen) {
X	while (dupeScreen[fldNum]) {
X	    fieldPtr = dupeScreen[fldNum];
X
X	    /* check that label fits on screen */
X	    if (fieldPtr->labelValue &&
X	    ((fieldPtr->labelCol + strlen (fieldPtr->labelValue) > COLS)) ||
X		((fieldPtr->labelLine + 1> FLINES))) {
X		DISP_ERROR_1 (LABEL_NO_FIT, fldNum);
X		return -1;
X	    }
X
X	    /* display label, if any */
X	    if (fieldPtr->labelValue && *fieldPtr->labelValue) {
X		DMMWATTRON (fieldWin, fieldPtr->labelAttrib);
X		mvwaddstr (fieldWin, fieldPtr->labelLine,
X			   fieldPtr->labelCol, fieldPtr->labelValue);
X		DMMWATTROFF (fieldWin, fieldPtr->labelAttrib);
X	    }
X
X	    /* if there is an input part, display it */
X	    if (fieldPtr->inputLength) {
X
X		/* calculate number of instruction lines */
X		fieldPtr->inputNumInstructs =
X		    cntInstructs (fieldPtr);
X
X		/* set the first input field if not yet found */
X		if (firstInputFld == -1)
X		    firstInputFld = fldNum;
X
X		/* calculate defaults */
X		calcDflts (fieldPtr);
X
X		/* check that input part of field fits on screen */
X		if ((fieldPtr->inputCol + fieldPtr->inputMaxCols > COLS) ||
X		    (fieldPtr->inputLine + fieldPtr->inputFullLines +
X		     (fieldPtr->inputShortCols ? 1 : 0) > FLINES)) {
X		    DISP_ERROR_1 (IP_NO_FIT, fldNum);
X		    return -1;
X		}
X
X		/* display edited value */
X		dispInField (fieldPtr, fieldPtr->inputAttrib);
X
X		/* set last input field's next pointer to this field and this
X		 * field's prev pointer to last input field
X		 */
X		if (lastInFieldNum != -1) {
X		    dupeScreen[lastInFieldNum]->nextInField = fldNum;
X		    fieldPtr->prevInField = lastInFieldNum;
X		} else
X		    firstInFieldNum = fldNum;
X
X		lastInFieldNum = fldNum;
X		inputPartCnt++;
X	    }
X	    fldNum++;
X	}
X	if ((firstInFieldNum != -1) && (lastInFieldNum != -1)) {
X	    dupeScreen[firstInFieldNum]->prevInField = lastInFieldNum;
X	    fieldPtr->nextInField = firstInFieldNum;
X	}
X    }
X
X    /* set globals to values of local register variables */
X    numFields = fldNum;
X    numInputParts = inputPartCnt;
X
X    /* display menu items and determine menu instruction */
X    if (dupeMenu) {
X
X	/* do menu calculations */
X	if (calcMenu ())
X	    return -1;
X
X	/* display menu page that contains first item; no item highlighted */
X	dispMenuPage (curMenuItem = 0, 0, 1);
X    }
X    return 0;
X}
X
X
X/* copyInFields()	copy all initial values to edited values of
X *			input parts of fields and display; a null
X *			initial value is equivalent to a zero-length
X *			string.
X */
Xstatic void
XcopyInFields ()
X{
X    int             fldNum;	/* counter of field */
X    DMMFIELD       *fldPtr;
X    for (fldNum = 0; fldNum < numFields; fldNum++) {
X	fldPtr = dupeScreen[fldNum];
X	if (fldPtr->inputLength) {
X	    strncpy (fldPtr->inputEditValue,
X		     fldPtr->inputInitValue ? fldPtr->inputInitValue : "",
X		     fldPtr->inputLength);
X	    fldPtr->inputEditValue[fldPtr->inputLength] = '\0';
X	    dispInField (fldPtr, fldPtr->inputAttrib);
X	}
X    }
X    return;
X}
X
X
X/* insane()	check the sanity of things, where fldNum is the number
X *		of the selected starting input field,
X *		returning 0 if okay, else writing a diagnostic message
X *		to stderr and returning non-zero.
X */
Xstatic int
Xinsane (fldNum)
Xint             fldNum;
X{
X    /* is the starting field within the proper range? */
X    if (fldNum > numFields - 1) {
X	DISP_ERROR_2 (BAD_FIELDNUM, fldNum + 1, numFields);
X	return 1;
X    }
X
X    /* does the starting field have an input part */
X    if (numInputParts) {
X	if (dupeScreen[fldNum]->inputLength < 1) {
X	    DISP_ERROR_1 (NOT_IFIELD, fldNum + 1);
X	    return 1;
X	}
X    }
X
X    /* is there at least one field with an input part or one menu item */
X    if (!(numInputParts || numMenuItems)) {
X	DISP_ERROR_0 (NO_INPUT_MSG);
X	return 1;
X    }
X
X    return 0;
X}
X
X
X/* dmmReview()		audit all input fields */
Xint
XdmmReview (screen)
XDMMSCREEN       screen;
X{
X    register int    fldNum;	/* field index */
X    register DMMFIELD *fldPtr;
X    DMMAUDITRETURN  auditReturnVal;	/* return value of the audit function */
X
X    for (fldNum = 0; screen[fldNum]; fldNum++) {
X	fldPtr = screen[fldNum];
X	if (fldPtr->inputLength) {
X	    auditReturnVal = fldPtr->inputAudit (fldPtr->inputEditValue);
X	    if ((auditReturnVal == dmmAuditBad)
X	        || (auditReturnVal == dmmAuditReturnBad)) {
X		dmmRetType = dmmBadField;
X		return fldNum;
X	    }
X	}
X    }
X    dmmRetType = dmmOkayField;
X    return -1;
X}
X
X
X/* cntInstructs(fldPtr)	return the number of instruction lines for
X *			the specified field number
X */
Xstatic int
XcntInstructs (fldPtr)
XDMMFIELD       *fldPtr;
X{
X    register int    numInstruct = 0,	/* number of instruction lines */
X                    instructNum = 0;	/* index in inputInstructVec */
X
X    if (fldPtr->inputInstructVec) {
X	while (fldPtr->inputInstructVec[instructNum++]) {
X	    numInstruct++;
X	}
X    }
X    return numInstruct;
X}
X
X
X/* arriveInField()		display the input field of the specified
X *				number with the edit attributes and display the
X *				instructions for that input field
X */
Xstatic void
XarriveInField (fldPtr)
XDMMFIELD       *fldPtr;
X{
X    register int    numInstructs,	/* number of instructions left to
X					 * display */
X                    instructLine;	/* line number for displaying next
X					 * instr */
X
X    /* display field in appropriate attributes */
X    dispInField (fldPtr, fldPtr->inputEditAttrib);
X
X    /* display instruction lines in reverse order, bottom up */
X    numInstructs = fldPtr->inputNumInstructs;
X    instructLine = LFLINE;
X    while (numInstructs--)
X	mvwaddstr (fieldWin,
X		 instructLine--, 0, fldPtr->inputInstructVec[numInstructs]);
X    return;
X}
X
X
X/* leaveInField()	display the input field of the specified
X *				number with the normal attributes and erase
X *				the instructions for that input field
X */
Xstatic void
XleaveInField (fldPtr)
XDMMFIELD       *fldPtr;
X{
X    register int    numInstructs,	/* number of instructions left to
X					 * display */
X                    instructLine;	/* line number for displaying next
X					 * instr */
X    /* display field in appropriate attributes */
X    dispInField (fldPtr, fldPtr->inputAttrib);
X
X    /* erase instruction lines starting with the bottom */
X    numInstructs = fldPtr->inputNumInstructs;
X    instructLine = LFLINE;
X    while (numInstructs--) {
X	wmove (fieldWin, instructLine--, 0);
X	wclrtoeol (fieldWin);
X    }
X    return;
X}
X
X
X/* chgInField()	change to the first position of a (perhaps
X *			input field; old is the last input field,
X *			and delta specifies what the change is to the
X *			new input field; return the number of the new
X *			input field.
X */
Xstatic int
XchgInField (old, delta)
Xint             old;
Xenum change     delta;
X{
X    int             new;	/* number of the new input field */
X
X
X    /* determine new field */
X    new = ((delta != stayput) ?
X	   (delta == next ?
X	    dupeScreen[old]->nextInField : dupeScreen[old]->prevInField) 
X	   : old);
X
X    /* if field actually changed, leave old field and arrive at new field */
X    if (new != old)
X	leaveInField (dupeScreen[old]);
X    arriveInField (dupeScreen[new]);
X
X    /* move cursor to first position in new field */
X    wmove (fieldWin, dupeScreen[new]->inputLine, dupeScreen[new]->inputCol);
X
X    return new;
X}
X
X
X/* adjustFld()	convert trailing blanks of an edited
X *		field value to nulls and intermediate nulls to blanks.
X */
Xstatic void
XadjustFld (fldPtr)
Xregister DMMFIELD *fldPtr;
X{
X    register int    charCnt = fldPtr->inputLength;	/* counter for
X							 * characters output */
X    enum location {
X	internal, external
X    };				/* whether before or after the terminating
X				 * null of inputEditValue */
X    register enum location state;
X
X    state = external;
X    while (charCnt--) {
X	switch (state) {
X	case external:
X
X	    switch (fldPtr->inputEditValue[charCnt]) {
X	    case '\0':
X		break;
X	    case ' ':
X		fldPtr->inputEditValue[charCnt] = '\0';
X		break;
X	    default:
X		state = internal;
X		break;
X	    }
X	    break;
X
X	case internal:
X	    if (fldPtr->inputEditValue[charCnt] == '\0')
X		fldPtr->inputEditValue[charCnt] = ' ';
X	    break;
X	}
X    }
SHAR_EOF
echo "End of part 1"
echo "File dmm.c is continued in part 2"
echo "2" > s2_seq_.tmp
exit 0
-- 
Rob Bernardo					Mt. Diablo Software Solutions
email: rob@mtdiablo.Concord.CA.US		phone: (415) 827-4301

rob@mtdiablo.Concord.CA.US (Rob Bernardo) (06/18/91)

#!/bin/sh
# this is part 2 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file dmm.c continued
#
CurArch=2
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file dmm.c"
sed 's/^X//' << 'SHAR_EOF' >> dmm.c
X    return;
X}
X
X
X/* This replaces the standard curses overlay(), which is broken:
X * it does not preserve the attributes of the copied characters
X */
Xstatic void
XoverLay (srcWin, dstWin)
XWINDOW         *srcWin,
X               *dstWin;
X{
X    register int    x,
X                    y;
X    register chtype mychar;
X    for (y = 0; y < srcWin->_maxy; y++) {
X	for (x = 0; x < srcWin->_maxx; x++) {
X	    mychar = mvwinch (srcWin, y, x);
X	    if (((mychar & A_CHARTEXT) != ' ')
X		|| ((mychar & A_ATTRIBUTES) != A_NORMAL))
X		mvwinsch (dstWin, y, x, mychar);
X	}
X    }
X    return;
X}
X
X
X
X/* fieldMgr()	manage user interactions with the field window;
X *		start by first positioning in the infput field
X *		designated by fldNum; if fldNum is -1, use the
X *		default input field, determined from the last
X *		call to this function;
X *		set the default field for use in the next call to
X *		this function before returning.
X *		return -1 if the user wants to leave for the menu;
X *		else return the field number of a field if the
X *		audit routine for that field returns dmmAuditReturnOkay
X *		or dmmAuditReturnBad, which means that the screen 
X *		handler must return to the application software;
X */
Xstatic int
XfieldMgr (fldNum)
Xregister int    fldNum;
X{
X    register int    curCol,	/* current column number in field */
X                    curLine,	/* current line number in field */
X                    curChar;	/* current char position in field */
X    enum change     delta;	/* what to do upon user input keystroke */
X    int             key,	/* user's input keystroke */
X                    len;	/* length of field being manipulated */
X    static          dfltInputFld;	/* default field for next call */
X    char           *instructMesg;	/* message for instruct line */
X
X    /* display general instructions about how to go to menu */
X    if (numMenuItems)
X	instructMesg = GOTO_MENU_INSTRUCT;
X    else
X	instructMesg = NULL_MENU_INSTRUCT;
X    dispInstruct (instructMesg);
X
X    /* first field: instruction and attribute */
X    fldNum = (fldNum == -1) ? dfltInputFld : fldNum;
X    arriveInField (dupeScreen[fldNum]);
X
X    /* separate parts of refresh for sake of dispInstruct() above */
X    wnoutrefresh (fieldWin);
X    doupdate ();
X
X    /* add messages as needed */
X    if (needAddMsgs) {
X	overwrite (fieldWin, fieldWinPreMsg);
X	overLay (mesgWin, fieldWin);
X    }
X    /* position cursor in appropriate field */
X    (void) chgInField (fldNum, stayput);
X    curCol = curLine = curChar = 0;
X
X    /* process user input */
X    while (1) {
X	delta = stayput;	/* default is no chg in field/cursor pos. Any
X				 * of the below choices that change the
X				 * screen but stayput need to do their own
X				 * wrefresh() */
X
X	switch (key = wGetCh (fieldWin)) {
X
X	case CTRL (h):
X	    /* display help */
X	    doPutMsgsHit (helpMsgVec, 1);
X	    break;
X
X	case CTRL (g):
X	    /* submit screen as is; set default field to current field and go
X	     * to menu
X	     */
X	    adjustFld (dupeScreen[fldNum]);
X	    leaveInField (dupeScreen[dfltInputFld = fldNum]);
X	    return -1;
X
X	case CTRL (c):
X	    /* clear to end of field, audit, move to next field */
X	    for (len = dupeScreen[fldNum]->inputLength;
X		 curChar < len; curChar++)
X		dupeScreen[fldNum]->inputEditValue[curChar] = '\0';
X	    dispInField (dupeScreen[fldNum],
X			 dupeScreen[fldNum]->inputEditAttrib);
X	    delta = next;
X	    break;
X
X	case RETURN:
X	case TAB:
X	case CTRL (n):
X	    /* audit and move to next field */
X	    delta = next;
X	    break;
X
X	case CTRL (p):
X	    /* audit and move to previous field */
X	    delta = previous;
X	    break;
X
X	case CTRL (r):
X	    /* redraw screen */
X	    redraw ();
X	    break;
X
X	case KEY_LEFT:
X	    /* move cursor to left if possible */
X	    if (curCol) {
X		curCol--;
X		curChar--;
X		delta = movecursor;
X	    } else
X		flash();
X	    break;
X
X	case KEY_RIGHT:
X	    /* move cursor to right if possible */
X	    if (((curLine < dupeScreen[fldNum]->inputFullLines) &&
X		 (curCol + 1 < dupeScreen[fldNum]->inputMaxCols)) ||
X		(curCol + 1 < dupeScreen[fldNum]->inputShortCols)) {
X		curCol++;
X		curChar++;
X		delta = movecursor;
X	    } else
X		flash();
X	    break;
X
X	case KEY_UP:
X	    /* move cursor up if possible */
X	    if (curLine != 0) {
X		curLine--;
X		curChar -= dupeScreen[fldNum]->inputMaxCols;
X		delta = movecursor;
X	    } else
X		flash();
X	    break;
X
X	case KEY_DOWN:
X	    /* move cursor down if possible */
X	    if ((curLine + 1 < dupeScreen[fldNum]->inputFullLines) ||
X		((curLine + 1 == dupeScreen[fldNum]->inputFullLines) &&
X		 (curCol < dupeScreen[fldNum]->inputShortCols))) {
X		curLine++;
X		curChar += dupeScreen[fldNum]->inputMaxCols;
X		delta = movecursor;
X	    } else
X		flash();
X	    break;
X
X	case DELETE:
X	    /* erase character to left of cursor and move cursor left,
X	     * except if we are in last character position and last
X	     * character is not a space, then erase last character
X	     * and don't move cursor.
X	     * Obviously, erase nothing if we are in the first character
X	     * position last is not also a last character position.
X	     */
X	    if((curChar + 1 == dupeScreen[fldNum]->inputLength) &&
X	       (dupeScreen[fldNum]->inputEditValue[curChar] != ' ')) {
X
X	       /* erase last character internally */
X		dupeScreen[fldNum]->inputEditValue[curChar] = ' ';
X
X	    } else {
X
X		/* do nothing if we are in the first character position
X		 * which is not also the last character position */
X		if (!curChar) {
X		    flash();
X		    break;
X		}
X
X		/* erase last character internally */
X		dupeScreen[fldNum]->inputEditValue[--curChar] = ' ';
X
X		/* retreat the cursor */
X		if (curCol)
X		    curCol--;
X		else {
X		    curLine--;
X		    curCol = dupeScreen[fldNum]->inputMaxCols - 1;
X		}
X	    }
X
X	    /* display space on screen */
X	    DMMWATTRON (fieldWin, dupeScreen[fldNum]->inputEditAttrib);
X	    mvwaddch (fieldWin,
X		      dupeScreen[fldNum]->inputLine + curLine,
X		      dupeScreen[fldNum]->inputCol + curCol, ' ');
X	    DMMWATTROFF (fieldWin, dupeScreen[fldNum]->inputEditAttrib);
X
X	    delta = movecursor;
X	    break;
X
X	default:
X	    /* process only if a printing character */
X	    if (isascii (key) && isprint (key)) {
X
X		/* display character, or if A_INVIS  display space */
X		DMMWATTRON (fieldWin, dupeScreen[fldNum]->inputEditAttrib);
X		mvwaddch (fieldWin,
X			  dupeScreen[fldNum]->inputLine + curLine,
X			  dupeScreen[fldNum]->inputCol + curCol,
X			  (dupeScreen[fldNum]->inputEditAttrib & A_INVIS) ?
X			  ' ' : (char) key);
X		DMMWATTROFF (fieldWin, dupeScreen[fldNum]->inputEditAttrib);
X
X		/* store internally */
X		dupeScreen[fldNum]->inputEditValue[curChar] = (char) key;
X
X		/* if not at end of field, move cursor */
X		if (curChar + 1 < dupeScreen[fldNum]->inputLength) {
X		    /* either move to next col in same line or first col of
X		     * next line
X		     */
X		    curChar++;
X		    if (((curLine < dupeScreen[fldNum]->inputFullLines) &&
X			 (curCol + 1 < dupeScreen[fldNum]->inputMaxCols)) ||
X			(curCol + 1 < dupeScreen[fldNum]->inputShortCols))
X			curCol++;
X		    else {
X			curCol = 0;
X			curLine++;
X		    }
X		    delta = movecursor;
X		}
X	    } else
X		flash ();
X	    break;
X	}
X
X	/* now check if cursor position of field is to be changed */
X	switch (delta) {
X
X	case next:
X	case previous:
X	    /* advance or retreat a field */
X
X	    /* remove trailing blanks and internal nulls */
X	    adjustFld (dupeScreen[fldNum]);
X
X	    /* audit field */
X	    if (dupeScreen[fldNum]->inputAudit) {
X
X		switch (dupeScreen[fldNum]->inputAudit 
X		        (dupeScreen[fldNum]->inputEditValue)) {
X
X		case dmmAuditReturnOkay:
X		    /* must leave screen handler; first show any changes to
X		     * field value and set default field according to user's
X		     * last keystroke
X		     */
X		    dmmRetType = dmmOkayField;
X		    leaveInField (dupeScreen[fldNum]);
X		    wrefresh (fieldWin);
X		    dfltInputFld = delta == next ?
X			dupeScreen[fldNum]->nextInField :
X			dupeScreen[fldNum]->prevInField;
X		    return fldNum;
X
X		case dmmAuditReturnBad:
X		    /* must leave screen handler; first show any changes to
X		     * field value and set default field according to user's
X		     * last keystroke
X		     */
X		    dmmRetType = dmmBadField;
X		    leaveInField (dupeScreen[fldNum]);
X		    wrefresh (fieldWin);
X		    dfltInputFld = delta == next ?
X			dupeScreen[fldNum]->nextInField :
X			dupeScreen[fldNum]->prevInField;
X		    return fldNum;
X
X		case dmmAuditBad:
X		    /* failed; stay in this field */
X		    delta = stayput;
X		    break;
X
X		case dmmAuditOkay:
X		    /* audit okay; go to another field */
X		    break;
X		}
X	    }
X
X	    /* show any changes to field value */
X	    fldNum = chgInField (fldNum, delta);
X	    curCol = curLine = curChar = 0;
X	    wrefresh (fieldWin);
X	    break;
X
X	case stayput:
X	case movecursor:
X	    /* move cursor to current column and line position in current
X	     * input field as just set or left as was
X	     */
X	    wmove (fieldWin, dupeScreen[fldNum]->inputLine + curLine,
X		   dupeScreen[fldNum]->inputCol + curCol);
X	    wrefresh (fieldWin);
X	    break;
X	}
X    }
X}
X 
X
X/* leaveMenu()	leave menu;
X *		if menuitem is < 0, erase whole menu,
X * 		else unhighlight indicated item
X */
Xstatic void
XleaveMenu (itemNum)
Xint             itemNum;
X{
X    if (itemNum < 0) {
X	wclear (menuWin);
X	wnoutrefresh (menuWin);
X    } else
X	dispMenuPage (itemNum, 0, 0);
X    return;
X}
X
X
X/* menuMgr		manage user interactions with the menu window;
X *			return the number of the menu item selected
X *			or -1 if the user wants to leave the menu without
X *			a selection.
X */
Xstatic int
XmenuMgr ()
X{
X    int             key;
X
X    /* display the current page of the menu with the current item highlighted */
X    dispMenuPage (curMenuItem, 1, 1);
X
X    /* display general menu instruction */
X    dispInstruct (MENU_INSTRUCT);
X
X    /* add messages as needed */
X    if (needAddMsgs) {
X	overwrite (fieldWin, fieldWinPreMsg);
X	overLay (mesgWin, fieldWin);
X	wnoutrefresh (fieldWin);
X    }
X
X    /* process user input */
X    while (1) {
X
X	/* refresh instruct and menu page on screen, as needed */
X	outOfTheWay();
X	doupdate ();
X
X	switch (key = wGetCh (menuWin)) {
X
X	case CTRL (h):
X	    /* display help */
X	    doPutMsgsHit (helpMsgVec, 1);
X	    break;
X
X	case CTRL (r):
X	    /* redraw screen */
X	    redraw ();
X	    break;
X
X	case CTRL (g):
X	    /* leave menu for input part of screen */
X	    leaveMenu (curMenuItem);
X	    return -1;
X
X	case RETURN:
X	    /* submit this choice */
X	    leaveMenu (-1);
X	    return curMenuItem;
X
X	case TAB:
X	case CTRL (n):
X	    /* move to next item */
X	    if (++curMenuItem >= numMenuItems)
X		curMenuItem = 0;
X	    dispMenuPage (curMenuItem, 1, 0);
X	    break;
X
X	case CTRL (p):
X	    /* move to previous item */
X	    if (--curMenuItem < 0)
X		curMenuItem = numMenuItems - 1;
X	    dispMenuPage (curMenuItem, 1, 0);
X	    break;
X
X	default:
X	    /* find single menu item with matching first character */
X	    if (isascii (key) && isalpha (key)) {
X		if (isupper (key))
X		    key = tolower (key);
X		if (menuIndex[key - 'a'] >= 0) {
X		    dispMenuPage (curMenuItem = menuIndex[key - 'a'], 1, 0);
X		    break;
X		}
X	    }
X	    flash ();
X	    break;
X	}
X    }
X}
X
X
X/* dmmRun	manage menu and data entry screen - see man page for fuller
X *		description
X */
Xint
XdmmRun (startField, screen, menu, dmmDrawFlag, dmmValFlag)
Xint             startField;
XDMMDRAWFLAG     dmmDrawFlag;
XDMMVALFLAG      dmmValFlag;
XDMMSCREEN       screen;
XDMMMENU         menu;
X{
X    int             itemNum;	/* menu or field number */
X    void            (*oldSignalValue) ();	/* current state for signal
X						 * SIGALRM */
X
X    /* copy parameters to global variables */
X    dupeScreen = screen;
X    dupeMenu = menu;
X
X    /* initialize windows if needed */
X    if (!isOpen)
X	dmmInit ();
X
X    /* get rid of type-ahead input */
X    flushinp ();
X
X    /* process dmmDrawFlag */
X    switch (dmmDrawFlag) {
X
X    case dmmNew:
X
X	/* the first display of a screen - do initial layout calculations */
X	if (layout ())
X	    return -1;
X
X	/* determine field for cursor */
X	if (startField == -1)
X	    startField = firstInputFld;
X
X	/* do sanity checking */
X	if (insane (startField))
X	    return -1;
X
X	/* display screen */
X	redraw ();
X
X	break;
X
X
X    case dmmOld:
X	/* restore saved screen and redisplay menu with first item
X	 * highlighted; let a -1 startField stand - fieldMgr() will
X	 * interpret it to mean the last input field saved on the last call
X	 * of dmmRun().
X	 */
X
X	/* This overwrite gets rid of any messages displayed by
X	 * dmmPutMsgsNext(), and so the needAddMsgs flag is used to get them
X	 * reinstated in fieldMgr() and menuMgr().
X	 */
X	overwrite (fieldWinCopy, fieldWin);
X	redraw ();
X	if (dupeMenu)
X	    dispMenuPage (0, 0, 1);
X	break;
X
X    }
X
X    /* copy inputInitValue's if indicated by dmmValFlag */
X    if (dmmValFlag == dmmInitVal)
X	copyInFields ();
X
X    /* process user input */
X
X    /* ignore SIGALRM to avoid a bug in wgetch() that fails to cancel an
X     * alarm() it sets after unsetting the catching of SIGALRM; this must be
X     * reset to current value before returning.
X     */
X    oldSignalValue = signal (SIGALRM, SIG_IGN);
X
X    interactive = 1;
X
X    while (1) {
X
X	/* manage fields with input parts, if any */
X	if (numInputParts) {
X	    if ((itemNum = fieldMgr (startField)) != -1)
X		break;
X	    startField = itemNum;
X	}
X
X	/* manage menu items, if any */
X	if (numMenuItems) {
X	    if ((itemNum = menuMgr ()) != -1) {
X		dmmRetType = dmmMenu;
X		break;
X	    }
X	    doupdate ();
X	}
X    }
X
X    /* remove instruction and update screen */
X    dispInstruct ("");
X    doupdate ();
X
X    /* save copy of screen for restoral with dmmOld option */
X    overwrite (fieldWin, fieldWinCopy);
X
X    /* undo ignoring of alarm signal */
X    (void) signal (SIGALRM, oldSignalValue);
X
X    interactive = 0;
X
X    return itemNum;
X}
SHAR_EOF
echo "File dmm.c is complete"
chmod 0444 dmm.c || echo "restore of dmm.c fails"
set `wc -c dmm.c`;Sum=$1
if test "$Sum" != "44980"
then echo original size 44980, current size $Sum;fi
echo "x - extracting dmm.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmm.h &&
X#ifndef LINT
Xstatic char dmm_h_id[] = {"@(#)dmm.h	1.1 \
XCopyright (c) 1991 Robert Bernardo, rob@mtdiablo.Concord.CA.US, \
Xan employee of Pande, Inc. \
XPermission  to use granted only if this notice is not removed from \
Xthis source file nor from any binaries produced from it."};
X
X#endif
X
X/* data display and entry field descriptor */
Xtypedef struct dmmField {
X    char                   *labelValue;
X    int                     labelCol,
X                            labelLine;
X    long                    labelAttrib;
X    char                   *inputEditValue,
X                           *inputInitValue;
X    int                     inputCol,
X                            inputLine,
X                            inputLength,
X                            inputMaxCols,
X                            inputFullLines,
X                            inputShortCols;
X    long                    inputAttrib,
X                            inputEditAttrib;
X    char                  **inputInstructVec;
X    int                     inputNumInstructs;
X    enum dmmAuditReturn     (*inputAudit) ();
X    int                     nextInField,
X                            prevInField;
X}                       DMMFIELD, *DMMSCREEN[], **DMMSCREEN2;
X
X/* menu item descriptor */
Xtypedef struct dmmMenuItem {
X    char                   *label,
X                           *descript;
X    int                     col,
X                            page;
X}                       DMMMENUITEM, *DMMMENU[], **DMMMENU2;
X
X/* message descriptor */
Xtypedef struct dmmMessage {
X    char                   *value;
X    int                     col,
X                            line;
X    long                    attrib;
X}                       DMMMESSAGE, *DMMMESGBLK[], **DMMMESGBLK2;
X
X/* return values for inputAudit() */
Xtypedef enum dmmAuditReturn {
X    dmmAuditOkay, dmmAuditBad, dmmAuditReturnOkay, dmmAuditReturnBad
X}                       DMMAUDITRETURN;
X
X/* DrawFlag values */
Xtypedef enum dmmDrawFlag {
X    dmmNew, dmmOld
X}                       DMMDRAWFLAG;
X
X/* ValFlag values */
Xtypedef enum dmmValFlag {
X    dmmInitVal, dmmEdVal
X}                       DMMVALFLAG;
X
X/* dmmRetType values */
Xtypedef enum dmmRetType {
X    dmmOkayField, dmmBadField, dmmMenu
X}                       DMMRETTYPE;
X
X/* dmmRemoveType values */
Xtypedef enum dmmRemoveType {
X    dmmRemoveAll, dmmRemoveLast
X}                       DMMREMOVETYPE;
X
Xextern enum dmmRetType  dmmRetType;
Xextern void             dmmPutMsgs(),
X                        dmmPutMsgsTimer(),
X                        dmmPutMsgsHit(),
X                        dmmPutMsgsNext(),
X                        dmmRmMsgs(),
X                        dmmInit(),
X                        dmmClear(),
X                        dmmClose();
X
X#ifndef A_INVIS
X#define A_INVIS         000020000000L
X#endif
SHAR_EOF
chmod 0444 dmm.h || echo "restore of dmm.h fails"
set `wc -c dmm.h`;Sum=$1
if test "$Sum" != "2793"
then echo original size 2793, current size $Sum;fi
echo "x - extracting dmmdemo.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmmdemo.h &&
X#ifndef LINT
Xstatic char dmmdemo_h_id[] = {"@(#)dmmdemo.h	1.1 \
XCopyright (c) 1991 Robert Bernardo, rob@mtdiablo.Concord.CA.US, \
Xan employee of Pande, Inc. \
XPermission  to use granted only if this notice is not removed from \
Xthis source file nor from any binaries produced from it."};
X#endif
X
X#define NUMBERCOL 5
X#define BODYCOL (NUMBERCOL + 3)
X
Xextern void             calcdate(),
X			msgdemo(),
X			getcomment(),
X			monthmenu();
SHAR_EOF
chmod 0444 dmmdemo.h || echo "restore of dmmdemo.h fails"
set `wc -c dmmdemo.h`;Sum=$1
if test "$Sum" != "432"
then echo original size 432, current size $Sum;fi
echo "x - extracting dmmdemo1.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmmdemo1.c &&
X#ifndef LINT
Xstatic char     dmmdemo_1_c_id[] = {"@(#)dmmdemo1.c	1.1 \
XCopyright (c) 1991 Robert Bernardo, rob@mtdiablo.Concord.CA.US, \
Xan employee of Pande, Inc. \
XPermission to use granted only if this notice is not removed from \
Xthis source file nor from any binaries produced from it."};
X#endif
X#include <curses.h>
X#include "dmm.h"
X#include "dmmdemo.h"
X
X/* messages */
Xstatic DMMMESSAGE       intro1 =
X{"Welcome to the DMM demonstration program. This program will use DMM",
X0, 0, 0},
X
X                        intro2 =
X{"functions to present a variety of screens. For each screen, it will",
X0, 1, 0},
X
X                        intro3 =
X{"state the associated source file and list its salient features.",
X0, 2, 0},
X
X                        intro4 =
X{"The source for this screen is in dmmdemo1.c.",
X0, 4, 0},
X
X                        intro5 =
X{"1. It has just called dmmInit() to start up curses (putting the",
XNUMBERCOL, 5, 0},
X
X                        intro6 =
X{"terminal into raw mode, etc.) and to initialize the DMM windows.",
XBODYCOL, 6, 0},
X
X                        intro7 =
X{"2. This block of messages is displayed with a call do dmmPutMsgsHit().",
XNUMBERCOL, 7, 0},
X
X                        intro8 =
X{"Notice the top line if this screen. After the next message, you",
XBODYCOL, 8, 0},
X
X                        intro9 =
X{"will be presented with a menuless screen. The top line always has",
XBODYCOL, 9, 0},
X
X                        intro10 =
X{"an appropriate general instruction when a call to dmmRun() presents",
XBODYCOL, 10, 0},
X
X                        intro11 =
X{"a screen. Pay attention to it throughout this demo. Remember that",
XBODYCOL, 11, 0},
X
X                        intro12 =
X{"control-H displays a useful help screen during a dmmRun() call.",
XBODYCOL, 12, 0},
X
X                        intro13 =
X{"The source for the menuless screen is in dmmdemo2.c Enter any log name",
X0, 0, 0},
X
X                        intro14 =
X{"and any password; it doesn't matter. The simulated login procedure is",
X0, 1, 0},
X
X                        intro15 =
X{"hardcoded to reject them the first time and succeed the second, so you",
X0, 2, 0},
X
X                        intro16 =
X{"will need to try a second time. Next time you run this program, enter 'y'",
X0, 3, 0},
X
X                        intro17 =
X{"to the \"give up\" question and see what happens.",
X0, 4, 0},
X
X                        intro18 =
X{"Note the following:",
X0, 6, 0},
X
X                        intro19 =
X{"1. Negative column numbers are used to separate the labels from the",
XNUMBERCOL, 7, 0},
X
X                        intro20 =
X{"input areas by a certain number of blank spaces.",
XBODYCOL, 8, 0},
X
X                        intro21 =
X{"2. The audit functions for the \"give up\" (if you enter 'y') and ",
XNUMBERCOL, 9, 0},
X
X                        intro22 =
X{"\"password\" fields return values that cause dmmRun() to return. Because ",
XBODYCOL, 10, 0},
X
X                        intro23 =
X{"this is a menuless screen and  no menu selection can be made, such is",
XBODYCOL, 11, 0},
X
X                        intro24 =
X{"necessary so that dmmRun() doesn't just go on indefinitely.",
XBODYCOL, 12, 0},
X
X                        intro25 =
X{"3. See how the data type DMMSCREEN2 is used for a pointer.",
XNUMBERCOL, 13, 0},
X
X                        intro26 =
X{"4. Data entered into the password field is invisible.",
XNUMBERCOL, 14, 0},
X			sizemsg1 =
X{"The DMM demo requires your screen (or window) to have at least 24",
X0, 0, A_REVERSE},
X			sizemsg2 =
X{"lines and 80 columns.",
X0, 1, A_REVERSE},
X			byemsg =
X{"End of the DMM demonstration. Good bye.",
X0, 0, A_REVERSE};
X
X
X
X
Xstatic DMMMESGBLK       introA = {&intro1, &intro2, &intro3, &intro4, &intro5,
X				  &intro6, &intro7, &intro8, &intro9, &intro10,
X			          &intro11, &intro12, 0},
X
X                        introB = {&intro13, &intro14, &intro15, &intro16,
X				  &intro17, &intro18, &intro19, &intro20,
X				  &intro21, &intro22, &intro23, &intro24,
X				  &intro25, &intro26, 0},
X
X			requirements = {&sizemsg1, &sizemsg2, 0},
X
X			byemsgblk = {&byemsg, 0};
X
Xmain()
X{
X    dmmInit();				/* start up curses; init DMM windows  */
X    if((COLS < 80) || (LINES < 24)) { 	/* make sure screen is large enough */
X	dmmPutMsgsHit(requirements);
X	dmmClose();
X	exit(1);
X    }
X    
X    dmmPutMsgsHit(introA);		/* display a message until user hits
X					 * a key */
X    dmmPutMsgsHit(introB);
X    if (login() == 0) {			/* if login goes okay, do rest of
X					 * demo screens
X					 */
X	monthmenu();
X	msgdemo();
X	calcdate();
X	getcomment();
X    }
X    dmmPutMsgsTimer(byemsgblk, 2);
X    dmmClose();				/* shut down curses and DMM windows */
X    exit(0);
X}
SHAR_EOF
chmod 0444 dmmdemo1.c || echo "restore of dmmdemo1.c fails"
set `wc -c dmmdemo1.c`;Sum=$1
if test "$Sum" != "4642"
then echo original size 4642, current size $Sum;fi
echo "x - extracting dmmdemo2.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmmdemo2.c &&
X#ifndef LINT
Xstatic char     dmmdemo_2_c_id[] = {"@(#)dmmdemo2.c	1.1 \
XCopyright (c) 1991 Robert Bernardo, rob@mtdiablo.Concord.CA.US, \
Xan employee of Pande, Inc. \
XPermission to use granted only if this notice is not removed from \
Xthis source file nor from any binaries produced from it."};
X#endif
X#include <strings.h>
X#include <curses.h>
X#include "dmm.h"
X
X/* field sizes */
X#define USERNAMELEN 10
X#define PASSWDLEN 8
X#define YESNOLEN 1
X
X/* editable buffers */
Xstatic char             username[USERNAMELEN + 1],
X                        giveup[YESNOLEN + 1],
X                        passwd[PASSWDLEN + 1],
X                        auditbuf[80];
X
X/* instruction fields */
Xstatic char            *usernameinstr[] = {"Enter your user name", 0},
X                       *giveupinstr[] = {"Enter y or n", 0},
X                       *passwdinstr[] = {"Enter your password", 0};
X
X/* data fields */
X#define GIVEUPLINE	2
X#define LNSPACING	2
X#define LABELCOL	10
X#define USERNAMELINE	(GIVEUPLINE + LNSPACING)
X#define PASSWDLINE	(USERNAMELINE + LNSPACING)
X#define MSGLINE		(PASSWDLINE + LNSPACING)
Xextern DMMAUDITRETURN   giveupaudit(),
X			auditUsername(),
X			auditPasswd();
X
Xstatic DMMFIELD         giveupfld = {"Give up logging in?", LABELCOL,
X			    GIVEUPLINE, 0, giveup, "n", -2, GIVEUPLINE,
X			    YESNOLEN, 0, 0, 0, A_REVERSE, A_UNDERLINE,
X			    giveupinstr, 0, giveupaudit, 0},
X
X                        usernamefld = {"User name", LABELCOL,
X			    USERNAMELINE, 0, username, 0, -2,
X			    USERNAMELINE, USERNAMELEN, 0, 0, 0,
X			    A_REVERSE, A_UNDERLINE, usernameinstr,
X			    0, auditUsername, 0},
X
X                        passwdfld = {"Password", LABELCOL, PASSWDLINE, 0,
X			    passwd, 0, -2, PASSWDLINE,
X			    PASSWDLEN, 0, 0, 0, A_REVERSE | A_INVIS,
X			    A_UNDERLINE | A_INVIS, passwdinstr, 0,
X			    auditPasswd, 0};
X
X/* data field arrays */
Xstatic DMMSCREEN        loginscreen1 = {&usernamefld, &passwdfld, 0},
X                        loginscreen2 = {&giveupfld, &usernamefld, &passwdfld, 0};
X
X/* messages */
Xstatic DMMMESSAGE       plswaitmsg = {"Please wait", 0, 0, A_REVERSE},
X                        auditmsg = {auditbuf, 0, -2, A_REVERSE};
X
Xstatic DMMMESGBLK       plswaitmsgblk = {&plswaitmsg, 0},
X                        auditmsgblk = {&auditmsg, 0};
X
X
XDMMAUDITRETURN
Xgiveupaudit(value)
Xchar                   *value;
X{
X    if (*value == 'y' || *value == 'Y')
X	return dmmAuditReturnOkay;
X    if (*value != 'n' && *value == 'N')
X	return dmmAuditBad;
X    return dmmAuditOkay;
X}
X
XDMMAUDITRETURN
XauditPasswd(value)
Xchar                   *value;		/* unused but required */
X{
X    return dmmAuditReturnOkay;
X}
X
XDMMAUDITRETURN
XauditUsername(username)
Xchar                   *username;
X{
X    extern int              errno;
X
X    if (!*username)
X	strcpy(auditbuf, "A user name must be specified");
X    else
X	return dmmAuditOkay;
X    dmmPutMsgsNext(auditmsgblk);
X    return dmmAuditBad;
X}
X
X/* return 0 if user logs in correctly, else -1 */
Xint
Xlogin()
X{
X
X    int                     retcode,
X                            tries = 0;
X    DMMSCREEN2              screen;
X
X    /* use the screen without give-up capabilities first time around */
X    screen = loginscreen1;
X    while (1) {
X
X	/* solicit login information */
X	retcode = dmmRun(-1, screen, (DMMMENU2) 0, dmmNew, dmmInitVal);
X	if (retcode == -1)
X	    return -1;
X
X	/* check if user wants to give up */
X	if (*giveup == 'y' || *giveup == 'Y')
X	    return -1;
X
X	/* process login information in dummy fashion */
X	dmmPutMsgs(plswaitmsgblk);
X	sleep(2);
X	if (tries++)
X	    return 0;
X
X	/* use the screen with give-up capabilities on  subsequent tries */
X	screen = loginscreen2;
X    }
X}
SHAR_EOF
chmod 0444 dmmdemo2.c || echo "restore of dmmdemo2.c fails"
set `wc -c dmmdemo2.c`;Sum=$1
if test "$Sum" != "3637"
then echo original size 3637, current size $Sum;fi
echo "x - extracting dmmdemo3.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmmdemo3.c &&
X#ifndef LINT
Xstatic char     dmmdemo_3_c_id[] = {"@(#)dmmdemo3.c	1.1 \
XCopyright (c) 1991 Robert Bernardo, rob@mtdiablo.Concord.CA.US, \
Xan employee of Pande, Inc. \
XPermission to use granted only if this notice is not removed from \
Xthis source file nor from any binaries produced from it."};
X#endif
X#include <curses.h>
X#include "dmm.h"
X#include "dmmdemo.h"
X
X/* messages */
Xstatic DMMMESSAGE       month = {"January", 10, 2, A_REVERSE},
X                        days = {"S   M   T   W   T   F   S ", 5, 3, A_UNDERLINE},
X                        week1 = {"    1   2   3   4   5   6", 5, 4, 0},
X                        week2 = {"7   8   9   10  11  12  13", 5, 5, 0},
X                        week3 = {"14  15  16  17  18  19  20", 5, 6, 0},
X                        week4 = {"21  22  23  24  25  26  27", 5, 7, 0},
X                        week5 = {"28  29  30  31", 5, 8, 0};
X
Xstatic DMMMESGBLK       calendar = {&month, &days, &week1, &week2,
X&week3, &week4, &week5, 0};
X
X/* menu */
Xstatic DMMMENUITEM      jan = {"January", "Display calendar for January"},
X                        feb = {"February", "Display calendar for February"},
X                        mar = {"March", "Display calendar for March"},
X                        apr = {"April", "Display calendar for April"},
X                        may = {"May", "Display calendar for May"},
X                        jun = {"June", "Display calendar for June"},
X                        jul = {"July", "Display calendar for July"},
X                        aug = {"August", "Display calendar for August"},
X                        sep = {"September", "Display calendar for September"},
X                        oct = {"October", "Display calendar for October"},
X                        nov = {"November", "Display calendar for November"},
X                        dec = {"December", "Display calendar for December"};
X
Xstatic DMMMENU          menu = {&jan, &feb, &mar, &apr, &may, &jun,
X				&jul, &aug, &sep, &oct, &nov, &dec, 0};
X
X
X/* data screen */
X/* Notice that on the following fields, since they have no input part, only the
X * first three elements need be specified, letting the rest default to
X * zero, a feature of their being static
X */
Xstatic DMMFIELD         msg1 =
X{"Select any menu item; it doesn't matter which. A dummy calendar will be",
X0, 1, 0},
X
X                        msg2 =
X{"displayed. The source for this screen is in dmmdemo3.c. Note the following:",
X0, 2, 0},
X
X                        msg3 =
X{"1. The is a menu-only screen because the data fields have only",
XNUMBERCOL, 3, 0},
X
X                        msg4 =
X{"label parts and no input parts.",
XBODYCOL, 4, 0},
X
X                        msg5 =
X{"2. The menu is long enough to scroll. Notice that the end scrolls to the",
XNUMBERCOL, 5, 0},
X
X                        msg6 =
X{"beginning and vice versa.",
XBODYCOL, 6, 0},
X
X                        msg7 =
X{"3. The menu has some items with shared first letters and other items with",
XNUMBERCOL, 7, 0},
X
X                        msg8 =
X{"unique first letters by which they are selectable.",
XBODYCOL, 8, 0},
X
X                        msg9 =
X{"4. Messages lines can have a variety of attributes; see the calendar.",
XNUMBERCOL, 9, 0};
X
Xstatic DMMSCREEN        msgscreen = {&msg1, &msg2, &msg3, &msg4, &msg5,
X&msg6, &msg7, &msg8, &msg9, 0};
X
X
Xvoid
Xmonthmenu()
X{
X    dmmRun(-1, msgscreen, menu, dmmNew, dmmInitVal);
X    /* normally we'd process the return code of dmmRun() and learn the month
X     * that was selected from the menu and then display the proper calendar.
X     * But this is just a demo and we'll display the same calendar no matter
X     * what month was chosen. */
X    dmmClear();				/* remove data entry/display part of
X					 * screen */
X    dmmPutMsgsHit(calendar);		/* display calendar */
X    return;
X}
SHAR_EOF
chmod 0444 dmmdemo3.c || echo "restore of dmmdemo3.c fails"
set `wc -c dmmdemo3.c`;Sum=$1
if test "$Sum" != "3773"
then echo original size 3773, current size $Sum;fi
echo "x - extracting dmmdemo4.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmmdemo4.c &&
X#ifndef LINT
Xstatic char     dmmdemo_4_c_id[] = {"@(#)dmmdemo4.c	1.1 \
XCopyright (c) 1991 Robert Bernardo, rob@mtdiablo.Concord.CA.US, \
Xan employee of Pande, Inc. \
XPermission to use granted only if this notice is not removed from \
Xthis source file nor from any binaries produced from it."};
X#endif
X#include "dmm.h"
X
X/* messages */
Xstatic DMMMESSAGE	msgA1 = 
X{"The source for this next set of messages is in dmmdemo4.c. It demonstrates",
X0, 0, 0},
X			msgA2 =
X{"various message display functions. This message is displayed with a call",
X0, 1, 0},
X			msgA3 =
X{"to dmmPutMsgsHit(), and will go away upon your next keystroke. After that",
X0, 2, 0},
X			msgA4 =
X{"a message will be displayed with dmmPutMsgsTimer for a few seconds. Then",
X0, 3, 0},
X			msgA5 =
X{"three short messages will be displayed. The last will be removed with",
X0, 4, 0},
X			msgA6 =
X{"a call to dmmRmMsgs(dmmRemoveLast), and then all messages will be removed",
X0, 5, 0},
X			msgA7 =
X{"with a call to dmmRmMsgs(dmmRemoveAll).",
X0, 6, 0},
X			msgB1 =
X{"This is the message displayed with dmmPutMsgsTimer() for a few seconds.",
X0, 1, 0},
X			msgC1 =
X{"This is the first of three messages.",
X0, 1, 0},
X			msgD1 =
X{"This is the second of three messages.",
X0, 3, 0},
X			msgE1 =
X{"This is the last of three messages.",
X0, 5, 0},
X			msgE2 =
X{"It will go away first, then all messages will be removed.",
X0, 6, 0};
X
Xstatic DMMMESGBLK	msgblkA = {&msgA1, &msgA2, &msgA3, &msgA4, &msgA5,
X				   &msgA6, &msgA7, 0},
X			msgblkB = {&msgB1, 0},
X			msgblkC = {&msgC1, 0},
X			msgblkD = {&msgD1, 0},
X			msgblkE = {&msgE1, &msgE2, 0};
X
Xvoid
Xmsgdemo()
X{
X    dmmPutMsgsHit(msgblkA);
X    dmmPutMsgsTimer(msgblkB, (unsigned)3);
X    dmmPutMsgs(msgblkC);
X    sleep(1);
X    dmmPutMsgs(msgblkD);
X    sleep(1);
X    dmmPutMsgs(msgblkE);
X    sleep(3);
X    dmmRmMsgs(dmmRemoveLast);
X    sleep(3);
X    dmmRmMsgs(dmmRemoveAll);
X    sleep(2);
X    return;
X}
SHAR_EOF
chmod 0444 dmmdemo4.c || echo "restore of dmmdemo4.c fails"
set `wc -c dmmdemo4.c`;Sum=$1
if test "$Sum" != "1886"
then echo original size 1886, current size $Sum;fi
echo "x - extracting dmmdemo5.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmmdemo5.c &&
X#ifndef LINT
Xstatic char     dmmdemo_5_c_id[] = {"@(#)dmmdemo5.c	1.1 \
XCopyright (c) 1991 Robert Bernardo, rob@mtdiablo.Concord.CA.US, \
Xan employee of Pande, Inc. \
XPermission to use granted only if this notice is not removed from \
Xthis source file nor from any binaries produced from it."};
X#endif
X#include <ctype.h>
X#include <curses.h>
X#include "dmm.h"
X#include "dmmdemo.h"
X
X#define			MONTHLEN 	2
X#define			DAYLEN 		2
X#define			YEARLEN 	2
X#define			MONTHCOL 	10
X#define			DAYCOL		(MONTHCOL + MONTHLEN)
X#define			YEARCOL		(DAYCOL + DAYLEN + 1)
X#define			DATELINE	10
X
X/* messages */
Xstatic char             dayofweek[80];
X
Xstatic DMMMESSAGE	msgA1 =
X{"The source for the next screen (and these messages) is in dmmdemo5.c.",
X0, 0, 0},
X			msgA2 =
X{"That screen shows a fairly prototypical logic in processing of various",
X0, 1, 0},
X			msgA3 =
X{"return values and conditions.  The screen solicits a date from the user",
X0, 2, 0},
X			msgA4 =
X{"and the rest of the code simulates calculation of the day of the week of",
X0, 3, 0},
X			msgA5 =
X{"the date.",
X0, 4, 0},
X			msgA6 =
X{"Note the following:",
X0, 6, 0},
X			msgA7 =
X{"1. Look at the overall logic of the function calcdate().  It is a",
XNUMBERCOL, 7, 0},
X			msgA8 =
X{"forever loop with a single call to dmmRun().  Notice that some of",
XBODYCOL, 8, 0},
X			msgA9 =
X{"various arguments to dmmRun(), namely startfield, drawflag and",
XBODYCOL, 9, 0},
X			msgA10 =
X{"valflag, get set to different values on different iterations of",
XBODYCOL, 10, 0},
X			msgA11 =
X{"the forever loop according to various conditions.",
XBODYCOL, 11, 0},
X			msgB1 =
X{"2. dmmReview() is used to check the input field values after dmmRun()",
XNUMBERCOL, 0, 0},
X			msgB2 =
X{"returns.  This is needed because the default initial values of the",
XBODYCOL, 1, 0},
X			msgB3 =
X{"input fields are not valid values.  Notice how if dmmReview()",
XBODYCOL, 2, 0},
X			msgB4 =
X{"indicates an invalid field value, startfield, drawflag and valflag",
XBODYCOL, 3, 0},
X			msgB5 =
X{"are set to re-present the screen with the cursor in the offending",
XBODYCOL, 4, 0},
X			msgB6 =
X{"field. If dmmReview() shows that all field values are valid, the",
XBODYCOL, 5, 0},
X			msgB7 =
X{"day of the week is displayed as a message alongside the input fields,",
XBODYCOL, 6, 0},
X			msgB8 =
X{"which are still on the screen, and then the screen is redisplayed",
XBODYCOL, 7, 0},
X			msgB9 =
X{"afresh.",
XBODYCOL, 8, 0},
X			msgC1 =
X{"3. The three input fields have audit functions that return",
XNUMBERCOL, 0, 0},
X			msgC2 =
X{"dmmAuditReturnOkay so that dmmRun() returns for the surrounding",
XBODYCOL, 1, 0},
X			msgC3 =
X{"code to perform an inter-field audit. (The inter-field audit is here",
XBODYCOL, 2, 0},
X			msgC4 =
X{"only as an example. In a real program, it would be better not to have",
XBODYCOL, 3, 0},
X			msgC5 =
X{"the audit functions return dmmAuditReturnOkay, and just perform an",
XBODYCOL, 4, 0},
X			msgC6 =
X{"inter-field audit after dmmReview returns from verifying the validity",
XBODYCOL, 5, 0},
X			msgC7 =
X{"of the fields separately.) Notice how startfield, drawflag and valflag",
XBODYCOL, 6, 0},
X			msgC8 =
X{"are set if the inter-field audit fails and if it succeeds.",
XBODYCOL, 7, 0},
X			msgD1 =
X{"4. A negative line number is used for the error messages to place the",
XNUMBERCOL, 0, 0},
X			msgD2 =
X{"message a certain number of lines from the bottom. Notice that it is ",
XBODYCOL, 1, 0},
X			msgD3 =
X{"placed so as not to conflict with the bottom lines used by the",
XBODYCOL, 2, 0},
X			msgD4 =
X{"instruction messages.",
XBODYCOL, 3, 0},
X			errormsg = {0, 0, -3, A_REVERSE},
X                        dayofweekmsg = {dayofweek, YEARCOL + 4, DATELINE, 0};
X
Xstatic DMMMESGBLK	msgblkA = {&msgA1, &msgA2, &msgA3, &msgA4, &msgA5,
X				   &msgA6, &msgA7, &msgA8, &msgA9, &msgA10,
X				   &msgA11, 0},
X			msgblkB = {&msgB1, &msgB2, &msgB3, &msgB4, &msgB5,
X				   &msgB6, &msgB7, &msgB8, &msgB9, 0},
X			msgblkC = {&msgC1, &msgC2, &msgC3, &msgC4, &msgC5,
X				   &msgC6, &msgC7, &msgC8, 0},
X			msgblkD = {&msgD1, &msgD2, &msgD3, &msgD4, 0},
X			errormsgblk = {&errormsg, 0},
X                        dayofweekmsgblk = {&dayofweekmsg, 0};
X
X/* menu */
Xstatic DMMMENUITEM      calcitem = {"Calculate", "Calculate day of week"},
X                        quititem = {"Quit", "Quit this part of demo program"};
X
Xstatic DMMMENU          menu = {&calcitem, &quititem, 0};
X
X/* fields and screen */
Xstatic char            *monthinstruct[] = {"Enter month as two digits", 
X			   "An example of a multiline instruction", 0},
X                       *dayinstruct[] =
X			   {"Enter day of months as two digits", 0},
X                       *yearinstruct[] = {"Enter year as two digits", 0};
X
Xextern DMMAUDITRETURN   monthaudit(),
X			dayaudit(),
X			yearaudit();
X
Xstatic char             monthbuf[MONTHLEN + 1],
X                        daybuf[DAYLEN + 1],
X                        yearbuf[YEARLEN + 1];
X
Xstatic DMMFIELD         month = {0, 0, 0, 0, monthbuf, 0, MONTHCOL, DATELINE,
X			    MONTHLEN, 0, 0, 0, A_REVERSE, A_UNDERLINE,
X			    monthinstruct, 0, monthaudit},
X
X                        day = {"/", DAYCOL, DATELINE, 0, daybuf, 0, -1,
X			    DATELINE, DAYLEN, 0, 0, 0, A_REVERSE,
X			    A_UNDERLINE, dayinstruct, 0, dayaudit},
X
X                        year = {"/", YEARCOL, DATELINE, 0, yearbuf, 0, -1,
X			    DATELINE, YEARLEN, 0, 0, 0, A_REVERSE,
X			    A_UNDERLINE, yearinstruct, 0, yearaudit};
X
Xstatic DMMSCREEN        screen = {&month, &day, &year, 0};
X
Xstatic int              daysinmonth[] =
X			    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
Xstatic char            *daysofweek[] = {"Monday", "Tuesday", "Wednesday",
X			    "Thursday", "Friday", "Saturday", "Sunday"};
X
Xvoid
Xcalcdate()
X{
X    int                     retcode,
X                            startfield = -1,
X                            dayint,
X                            monthint,
X                            yearint;
X    DMMDRAWFLAG             drawflag = dmmNew;
X    DMMVALFLAG              valflag = dmmInitVal;
X
X    dmmPutMsgsHit(msgblkA);
X    dmmPutMsgsHit(msgblkB);
X    dmmPutMsgsHit(msgblkC);
X    dmmPutMsgsHit(msgblkD);
X    while (1) {
X	if((retcode = dmmRun(startfield, screen, menu, drawflag, valflag))
X	   == -1)
X	    return;
X	dayint = atoi(daybuf);
X	monthint = atoi(monthbuf);
X	yearint = atoi(yearbuf);
X	if (dmmRetType == dmmMenu) {
X
X	    /* user selected a menu item; if it was 'quit', return, else user
X	     * wants day of week calculated */
X	    if (retcode == 1)
X		/* user selected 'quit' menu item */
X		return;
X
X	    /* audit input values before calculating day of week */
X	    if ((retcode = dmmReview(screen)) == -1) {
X		/* all individual values okay - display day of week */
X		sprintf(dayofweek, "falls on a %s",
X			daysofweek[(dayint + monthint + yearint) % 7]);
X		dmmPutMsgsHit(dayofweekmsgblk);
X
X		/* redisplay fresh screen */
X		startfield = -1;
X		drawflag = dmmNew;
X		valflag = dmmInitVal;
X	    } else {
X		/* bad input value -- go back to offending field of last
X		 * screen for user to correct it */
X		startfield = retcode;
X		drawflag = dmmOld;
X		valflag = dmmEdVal;
X	    }
X	} else {
X	    /* an audit function must have returned - do the interfield audit
X	     * that fits the input the user gave so far */
X	    if ((*monthbuf && *daybuf) &&
X	    ((*yearbuf && !validdayofmonthandyear(dayint, monthint, yearint))
X	     || !validdayofmonth(dayint, monthint))) {
X		/* bad day of month */
X		startfield = 1;		/* index of day field */
X		drawflag = dmmOld;
X		valflag = dmmEdVal;
X	    } else {
X		/* continue with appropriate field */
X		startfield = -1;
X		drawflag = dmmOld;
X		valflag = dmmEdVal;
X	    }
X	}
X    }
X}
X
Xvaliddayofmonthandyear(dayofmonth, month, year)
Xint                     dayofmonth,	/* presumed between 1 and 31 */
X                        month,		/* presumed between 1 and 12 */
X                        year;		/* presumed between 0 and 99 */
X{
X    if ((dayofmonth > daysinmonth[month]) ||
X	    ((dayofmonth == 29) && (month == 2) &&
X	     (year % 4 != 0) || (year % 100 == 0))) {
X	errormsg.value = "Not that many days in that month";
X	dmmPutMsgsNext(errormsgblk);
X	return 0;
X    }
X    return 1;
X}
X
Xvaliddayofmonth(dayofmonth, month)
Xint                     dayofmonth,	/* presumed between 1 and 31 */
X                        month;		/* presumed between 1 and 12 */
X{
X    if (dayofmonth > daysinmonth[month]) {
X	errormsg.value = "Not that many days in that month";
X	dmmPutMsgsNext(errormsgblk);
X	return 0;
X    }
X    return 1;
X}
X
XDMMAUDITRETURN
Xmonthaudit(month)
Xchar                   *month;
X{
X    int                     monthint = atoi(month);
X
X    /* check for valid month */
X    if ((strlen(month) != MONTHLEN) || (monthint < 1) || (monthint > 12)) {
X	errormsg.value = "Month must be given as two digits, 01 through 12";
X	dmmPutMsgsNext(errormsgblk);
X	return dmmAuditBad;
X    }
X    /* need to check month against day of month */
X    return dmmAuditReturnOkay;
X}
X
XDMMAUDITRETURN
Xdayaudit(day)
Xchar                   *day;
X{
X    int                     dayint = atoi(day);
X
X    /* check for valid day of month */
X    if ((strlen(day) != DAYLEN) || (dayint < 1) || (dayint > 31)) {
X	errormsg.value = "Day must be given as two digits, 01 through 31";
X	dmmPutMsgsHit(errormsgblk);
X	return dmmAuditBad;
X    }
X    /* need to check day of month against month */
X    return dmmAuditReturnOkay;
X}
X
XDMMAUDITRETURN
Xyearaudit(year)
Xchar                   *year;
X{
X    /* check for valid year */
X    if ((strlen(year) != YEARLEN) || (!isdigit(*year)) || (!isdigit(*(year + 1)))) {
X	errormsg.value = "Year must be given as two digits, 00 through 99";
X	dmmPutMsgsNext(errormsgblk);
X	return dmmAuditBad;
X    }
X    return dmmAuditReturnOkay;
X}
SHAR_EOF
chmod 0444 dmmdemo5.c || echo "restore of dmmdemo5.c fails"
set `wc -c dmmdemo5.c`;Sum=$1
if test "$Sum" != "9605"
then echo original size 9605, current size $Sum;fi
echo "x - extracting dmmdemo6.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmmdemo6.c &&
X#ifndef LINT
Xstatic char     dmmdemo_6_c_id[] = {"@(#)dmmdemo6.c	1.1 \
XCopyright (c) 1991 Robert Bernardo, rob@mtdiablo.Concord.CA.US, \
Xan employee of Pande, Inc. \
XPermission to use granted only if this notice is not removed from \
Xthis source file nor from any binaries produced from it."};
X#endif
X#include <curses.h>
X#include "dmm.h"
X#include "dmmdemo.h"
X
X/* messages */
Xstatic DMMMESSAGE	infomsg1 =
X{"The source for the next screen (and these messages) is in dmmdemo6.c.",
X0, 0, 0},
X			infomsg2 =
X{"Note the following:",
X0, 1, 0},
X			infomsg3 =
X{"1. The first screen has one input field that spans more than one line.",
XNUMBERCOL, 2, 0},
X			infomsg4 =
X{"This is achieved by having the inputMaxcols datum in the",
XBODYCOL, 3, 0},
X			infomsg5 =
X{"DMMFIELD structure be non-zero and less than the inputLength",
XBODYCOL, 4, 0},
X			infomsg6 =
X{"datum. Play around with the arrow keys.",
XBODYCOL, 5, 0},
X			infomsg7 =
X{"2. The second screen is like the first, except that a line number is",
XNUMBERCOL, 6, 0},
X			infomsg8 =
X{"put out of range. Since the screen cannot be displayed as indicated",
XBODYCOL, 7, 0},
X			infomsg9 =
X{"by the DMMSCREEN data, dmmRun() displays a diagnostic message and",
XBODYCOL, 8, 0},
X			infomsg10 =
X{"returns -1. Notice how dmmGetNumLines() can be used to ascertain",
XBODYCOL, 9, 0},
X			infomsg11 =
X{"the number of lines available in the data entry window.",
XBODYCOL, 10, 0},
X			infomsg12 =
X{"This is the last part of the demonstration.",
X0, 12, 0};
X
Xstatic DMMMESGBLK	infomsgblk = {&infomsg1, &infomsg2, &infomsg3,
X				      &infomsg4, &infomsg5, &infomsg6,
X				      &infomsg7, &infomsg8, &infomsg9,
X				      &infomsg10, &infomsg11, &infomsg12, 0};
X
X/* menu */
Xstatic DMMMENUITEM      mailit = {"Mail", "Mail comment to administrator"},
X                        dontmailit = {"Quit", "Quit this part of demo program"};
X
Xstatic DMMMENU          menu = {&mailit, &dontmailit, 0};
X
X/* fields and screen */
Xstatic char            *commentinstruct[] = {"Enter comment for administrator",
X			   0};
X
X#define COMMENTSIZE	700
Xstatic char             commentbuf[COMMENTSIZE + 1];
X
Xstatic DMMFIELD         comment = {"Comment", 10, 0, 0, commentbuf, 0, 10, 2,
X			    COMMENTSIZE, 60, 0, 0, A_REVERSE, A_REVERSE,
X			    commentinstruct, 0, 0};
X
Xstatic DMMSCREEN        screen = {&comment, 0};
X
Xvoid
Xgetcomment()
X{
X    int                     numlines;
X
X    dmmPutMsgsHit(infomsgblk);
X    dmmRun(-1, screen, menu, dmmNew, dmmInitVal);
X    numlines = dmmGetNumLines();
X    comment.labelLine = numlines;
X    dmmRun(-1, screen, menu, dmmNew, dmmInitVal);
X    return;
X}
SHAR_EOF
chmod 0444 dmmdemo6.c || echo "restore of dmmdemo6.c fails"
set `wc -c dmmdemo6.c`;Sum=$1
if test "$Sum" != "2582"
then echo original size 2582, current size $Sum;fi
rm -f s2_seq_.tmp
echo "You have unpacked the last part"
exit 0
-- 
Rob Bernardo					Mt. Diablo Software Solutions
email: rob@mtdiablo.Concord.CA.US		phone: (415) 827-4301