[comp.sys.handhelds] Repost of PHONE V1.4

ervin@pinbot.enet.dec.com (Joseph James Ervin) (03/18/91)

Hi folks,

It's been a while since I posted my phone number manager, so I 
thought I'd repost it for the new users in this conference.

>>>Joe Ervin



*************
   This program is a basic database program designed to keep a list of 
   phone numbers and addresses, and provide access to them, editing, etc..

   To run it just download the following code and evaluate the program
   object. 

   The first time you run it, you'll be informed that no database exists
   and that you should create it.    

   When creating the database, which happens automatically the first time
   you run the program, you'll be prompted alternately for a name, phone
   number, and address.  If you don't want to enter a phone number or an
   address, just hit ENTER at these prompts.  Just keep entering the data
   at the prompts, and when your through just hit ENTER at the name prompt
   without entering any data.  This will  terminate the input mode.  

   Note: This method of input termination works any time you are prompted
   for a  name and decide not to enter one.  Remember this as it will come
   in handy at  other times when the program prompts you for a name.

   Once you have created a database, running the program will prompt you as
   to  whether you want to search the list or edit the list.  

1. SEARCHING THE LIST. 
 
   The way to search the list is by pressing SRCH when prompted. The
   program will search each name in the  list for any occurance of the
   string you enter (when prompted).  This way,  you can search for people
   by last name, or whatever.  For example, if you  have the phone numbers
   of all the local movie theaters, then entering  CINEMA at the prompt
   will give you a list of all the movie theaters  (assuming you were smart
   enough to use the string "CINEMA" as part of the name for each movie
   theater).  You get the idea.  The program will display a list of entries
   that matched the search string input by the user.  The default display
   mode shows the name/phone number, with a menu item (ADDR) by which
   the user can choose to display addresses.  Note that the same menu
   button toggles between ADDR mode (displaying addresses) and NUM
   mode (displaying phone numbers).

   If you enter an asterisk ("*") at the prompt when searching the
   list, the program will output the entire list.  This is useful for
   going through the list (in edit mode, say) and deleting entries
   that are no longer needed.

2. EDITING THE LIST.  

   Basically, just follow the prompts and you'll do well. I  tried to make
   this fairly straightforward, and I have put in some degree of error
   checking of input data to make sure it makes sense, but I'm sure you can
   get it to crash if you try.   Some simple rules to follow:  Avoid typing 
   over the ":Name:", ":Number:", or ":ADDR:"  strings when entering data
   at a prompt.  The program uses these strings internally to find your
   input, so if you type  over them, the program will get confused trying
   to isolate the actual input from within the input string.

   The program uses the SRCH algorithm described above to locate the exact
   item you wish to edit.  When you have the exact item you wish to edit on
   the  screen, press PICK and you will have a chance to make your changes.
   Again, the entire list can be viewed by specifying an asterisk as the
   search string.   

   Note:  When you press PICK, you will be prompted to edit either the
   name/number or name/address, depending on whether the number or address
   was being displayed when you pressed PICK.  This  goes for deleting
   entries as well.  

   Any time you see an EXIT option on the menu bar, you can press it to
   back  out of the program.  Sometimes it backs you out one menu level,
   and sometimes you exit the program entirely.  I tried to handle each
   case in the most natural way, rather than always doing the same thing. 
   I hope you agree that this was the way to go.

   Some usage notes:  I intentionally had the output of the SEARCH
   algorithm appear above the bottom of the display so there would be room
   for multiple lines of text in the :NUMBER: field.  This facilitates
   including home and work phone numbers for people in this field on two
   lines.  Just insert a line feed when entering/editing the entries.  The
   same goes for the ":ADDR:" field; use line feeds to separate lines of
   text in the address.

   Anyway, here's version 1.4 of the program.  I hope you like it.  The
   comments will automatically strip out when you download to your
   calculator. 

   Enjoy, and please give me your comments, and any ideas you have for
   new features.

   Regards,

   >>>Joe Ervin

**********************   CUT HERE  **************************************


%%HP: T(3)A(R)F(.);
\<<  @ Start of Program @
@ PHONE V1.4 by Joe Ervin.  18-JAN-1991 @
@ NEW FEATURES FOR V1.4.
@ Added handling of addresses, in addition to names and numbers.  
@ Included YN menu definition, rather than using an external reference.
@ Added RCLF and STOF to preserve the state of the system.  Also enforce
@ STD display mode during execution.  Ensures a nice display during PHSEARCH.
@
@ NEW FEATURES FOR V1.3.   
@ Added a PREV menu choice in the PHSEARCH routine to allow the user to
@ move backwards through the list of matches.

CLEAR
{} @ Dummy value to hold place for PHNAMES. @
{} @ Dummy value to hold place for PHNUMBERS. @
{}
0 
0
\-> PHNAMES PHNUMBERS ADDRESSES PHSIZE FLAGS
   @ Creates local storage for names and numbers. @

\<< @ Surounds the procedures as well as main routine. @

\<<  @Start definition of PHSEARCH routine.@ 
CLEAR

0 0 0 {} 0 


\-> 
        SRCHNAME  @ Local variable to hold the search name. @
        DONE      @ Local variable to detect when search has finished. @
        MATCHNUM  @ Local variable to store the number of matches.  @
        MATCHLIST     @ Stores list of matches. @
        MATCHINDEX    @ Used to index into the match list. @


@ What we are going to do now is to step through each name in the list, and @
@ for each name, we will do a pattern match of the search name within each@ 
@ name in the list. If the searchname is found within a given name, then @
@ the index corresponding to the matched name will be added to MATCHLIST. @
@ This routine uses flag 1 to indicate success or failure to the calling @
@ routine. (SET = success, CLEAR = failure - i.e. no matches.  @ 
@ Flag 2 is also used to indicate whether the main routine should exit @
@ or prompt the user for another choice whether to search or edit the @
@ phone list. @
                                                                            
\<< @This delimiter surrounds PHSEARCH's local variables@
@ The following are definitions for four different menus which
@ are used by the PHSEARCH facility.  These definitions are defined
@ here as local variables for reasons of performace;  it ran
@ kinda slow when I built these menus in real time.  Defining them
@ here and then just calling them speeds things up considerably.

@ Here is the menu definition for MENADR @
        {{"Next" \<< CONT \>> } {"Prev" \<<  \-> X
        \<< CASE
        'X==1' 
          THEN 
            IF 'MATCHNUM>1' @ Do nothing if only 1 match.  
            THEN 
              'MATCHNUM-1' EVAL CONT 
            ELSE X CONT
            END 
          END 
        'X==2' THEN MATCHNUM CONT END @ So next index will be 1.
        'X-2' EVAL CONT  @ Do this by default to back up one index.
        END  \>>  @ end of case statement 1.
        \>> }                                
        {} { "Addr" \<< 6 SF \-> X
        \<< CASE
        'X==1' THEN IF 'MATCHNUM>1' @ Do nothing if only 1 match.  
          THEN 
            'MATCHNUM' EVAL CONT 
          ELSE X CONT
          END 
        END 
        'X-1' EVAL CONT @ Do this by default to replay last index.
        END \>> @ end of case statement 2.
        \>>}
        {} {"Exit" 
        \<< 1 'DONE' STO CONT \>>} }

@ Here is the menu definition for MENNUM
        {{"Next" \<< CONT \>> } {"Prev" \<<  \-> X
        \<< CASE
          'X==1' THEN IF 'MATCHNUM>1' @ Do nothing if only 1 match.  
          THEN 
            'MATCHNUM-1' EVAL CONT 
          ELSE X CONT
          END 
        END 
        @So next index will be MATCHNUM.
        'X==2' THEN MATCHNUM CONT END @ So next index will be 1.
        'X-2' EVAL CONT @ Do this by default to back up one index.
        END \>> @ end of case statement 3.
        \>> }                                
        {} { "Num" \<< 6 CF \-> X
        \<< CASE
        'X==1' THEN IF 'MATCHNUM>1' @ Do nothing if only 1 match.  
          THEN 
            'MATCHNUM' EVAL CONT 
          ELSE X CONT
          END 
        END 
        'X-1' EVAL CONT @ Do this by default to replay last index.
        END \>> @ end of case statement 4.
        \>> } {} {"Exit" 
        \<< 1 'DONE' STO CONT \>>} }

@ Here is the menu definition for MENPICKADR
        {{"Next" \<< CONT \>> } {"Prev" \<<  \-> X
        \<< CASE
          'X==1' THEN IF 'MATCHNUM>1' @ Do nothing if only 1 match.  
          THEN 
            'MATCHNUM-1' EVAL CONT 
          ELSE X CONT
          END 
        END 
        'X==2' THEN MATCHNUM CONT END @ So next index will be 1.
        'X-2' EVAL CONT @ Do this by default to back up one index.
        END \>> @ end of case statement 5.
        \>> }                   
        {"Pick" 
        \<<IF DUP 1 == THEN DROP MATCHNUM ELSE 1 - END GET 1 'DONE' STO 
        4 CF CONT \>>} 
        { "Addr" \<< 6 SF \-> X
        \<< CASE
        'X==1' THEN IF 'MATCHNUM>1' @ Do nothing if only 1 match.  
          THEN 
            'MATCHNUM' EVAL CONT 
          ELSE X CONT
          END 
        END 
        'X-1' EVAL CONT @ Do this by default to replay last index.
        END \>> @ end of case statement 6.
        \>>}
        {} {"Exit" 
        \<< 1 'DONE' STO 1 CF 2 SF CONT \>>} } 


@ Here is the menu definition for MENPICKNUM
        {{"Next" \<< CONT \>> } {"Prev" \<<  \-> X
        \<< CASE
          'X==1' THEN IF 'MATCHNUM>1' @ Do nothing if only 1 match.  
          THEN 
            'MATCHNUM-1' EVAL CONT 
          ELSE X CONT
          END 
        END 
        'X==2' THEN MATCHNUM CONT END @ So next index will be 1.
        'X-2' EVAL CONT @ Do this by default to back up one index.
        END \>> @ end of case statement 7.
        \>> }                   
        {"Pick" 
        \<<IF DUP 1 == THEN DROP MATCHNUM ELSE 1 - END GET 1 'DONE' STO 
        4 CF CONT \>>} 
        { "Num" \<< 6 CF \-> X
        \<< CASE
        'X==1' THEN IF 'MATCHNUM>1' @ Do nothing if only 1 match.  
          THEN 
            'MATCHNUM' EVAL CONT 
          ELSE X CONT
          END 
        END 
        'X-1' EVAL CONT @ Do this by default to replay last index.
        END \>> @ end of case statement 8.
        \>> } {} {"Exit" 
        \<< 1 'DONE' STO 1 CF 2 SF CONT \>>} } 

\->     MENADR       @ Menu definitions used within PHSEARCH.
        MENNUM
        MENPICKADR
        MENPICKNUM


\<< @ This delimeter surrounds the defining procedure for the above
    @ menu definitions.

{} TMENU  @ Just to clear the menu from the main routine. @
"Enter name..." {":Name:" \Ga} INPUT @Prompt user for name to search.@

IF DUP SIZE 6 \<=   @ If no name was entered. @ 
THEN 
 1 SF  @ To indicate successfull completion of search routine. @
 2 SF  @ To cause the calling routine to ask again: SEARCH or EDIT.  @
 4 CF @ Just in case this routine was called from PHEDIT. @
ELSE
  DUP SIZE 7 SWAP SUB 'SRCHNAME' STO
  PHNAMES  OBJ\->@ Puts the list of names on the stack.@
  IF SRCHNAME "*" SAME THEN 3 SF END
  1 SWAP FOR I  @ Step through the whole list of names. @
    SRCHNAME  @ Put the string to search for on the stack. @
    IF POS 3 FS? OR 
    @ If SRCHNAME exists anywhere within the name...@  
    THEN
      'MATCHNUM'  INCR DROP @ Add one to the number of matches. @
      'PHSIZE - I + 1' EVAL 
      'MATCHLIST' STO+  @ Add the match index to list of match indices. @
    END  @ End this match check. @
  NEXT   @ End loop checking for matches. @
  3 CF

  IF MATCHNUM 0 \=/   @ IF there were at least some matches...@
  THEN 
    1 SF  2 CF   @ To indicate success to the calling routine. @
    MATCHLIST 
    1  @ To initialize index into matchlist. @
    DO   @ Display list of matches until done. @
      GETI  @ Fetch the next item pointed to by the matchindex. @ 
      'MATCHINDEX' STO DUP 
      IF 1 == THEN MATCHNUM ELSE DUP 1 - END
      @ Now display which match we are looking at. @
      CLLCD
      "Match " SWAP + " of "  + MATCHNUM + 
      1 DISP
      ":Name:" PHNAMES MATCHINDEX GET + 
      @ Concatenates label to name. @
      "
"
      +  @ Adds a LF to the end of the NAME string.
      IF 6 FC? @ If we are in "numbers" mode...
      THEN
        ":Number:" PHNUMBERS MATCHINDEX GET +  
        @ Concatenates label to number. @
        + @ Concatenates the NUMBER string to the NAME string.
        4 DISP
      ELSE 
        "
"
        ADDRESSES MATCHINDEX GET +   
        @ Concatenates label to address. @
        + @ Concatenates the ADDRESS string to the NAME string.
        3 DISP
      END
      3 FREEZE 

@ Now we need to call in the proper menu definition depending on what 
@ display mode we are in (flag 6) and whether PHSEARCH was called from
@ PHEDIT or from the main routine.
      IF 4 FC?  @ PHSEARCH was called from main routine.
      THEN 
        IF 6 FC? @ If in "numbers" mode...
        THEN  @ We need to add a menu item to switch to "addresses" mode.
          MENADR
        ELSE  @ We are in "address" mode...  
          MENNUM
        END
      ELSE  @ PHSEARCH was called from PHEDIT.
        IF 6 FC? @ If in "numbers" mode...
        THEN  @ We need to add a menu item to switch to "addresses" mode.
          MENPICKADR
        ELSE     @ We are in "address" mode...  
          MENPICKNUM
        END
      END

      TMENU HALT  @ Display the menu and stop for input.
    UNTIL DONE
    END
  ELSE  @  If there were no matches...@
    "The specified name
was not found" @ To indicate search failure. @
    1 CF 2 CF @ To indicate failure to the calling routine. @
    CLLCD 1200 .1 BEEP 2 DISP 1 WAIT
  END
END
\>> @ This delimeter surrounds the defining procedure for the 
    @ menu definitions.

\>>
\>> @Ends definition of PHSEARCH routine.@ 

\-> PHSEARCH @ Creates the subroutine PHSEARCH. @
\<<
\<< @ Definition of the PHEDIT routine starts here. @
\-> P1 @ Take the parameter passed in on the stack. @
\<< IF P1 "INT" SAME 
THEN 

@ At this point, the local variables "PHNAMES" and "PHNUMBERS" contain the @
@ names and numbers of the list to be edited.  Now we need to present the @
@ user with a meaningful display from which to choose actions.  The actions @
@ allowed include "MODIFY" and "ADD".  The following code prompts the user @
@ to choose between these two options, and then performs the simple editing @
@ function. @

    "Do you want to add
to the list, modify
an existing entry,
or delete an entry?" 
    CLLCD 1 DISP 3
    FREEZE {{"Add" \<<"ADD" CONT\>>} {"Mdfy" \<<"MODIFY" 5 CF CONT\>>} 
    {"Del"  \<<"MODIFY" 5 SF CONT \>>} {} {} 
    {"EXIT" \<<2 SF CONT\>>}} TMENU HALT

ELSE CLEAR "ADD" @ This is for when PHEDIT was called by main with @
                 @ P1 of "NEW". @
END 

@ This is for the case when PHEDIT was called by the main routine for  @
@ the case where there was no phone list. @
 
IF 2 FC?C 
THEN 
     
{} TMENU  @ Clear the menu from what was previously there. @
0 0 0 0 \-> X TEMP LEN LEN2 DONE
\<< CASE  
@ Note the creation of the scratch variables 'TEMP', 'LEN', 'LEN2'.@
@ 'TEMP' is scratch for the name, LEN is scratch for the name's length, @
@ and 'LEN2' is scratch for the length of the phone number. @
@ DONE is used for the repeat loop. @
@ X gets the "ADD" or "MODIFY" string from the above prompt. @

  X "ADD" SAME @ Here we are adding to the list of names. @
      THEN
      DO 
        "Enter name..." {":Name:" \Ga} INPUT 
        @ Prompts the user to enter the name for this entry. @
        @ Now we need to build up the command line for the INPUT command @
        @ which will pull in the number for this entry.  We want to @
        @ display the name above where the user is going to enter the @
        @ number. @
        DUP SIZE 'LEN' STO 'TEMP' STO 
        IF LEN 6 > 
        THEN 
          "Enter number..." TEMP EVAL "
:Number:" + { 2 9 }
          \Ga 3 \->LIST INPUT 
       
          @ The current prompt shows the name and prompts for @
          @ the phone number. @
       
          @ Now save the new information and update PHSIZE.
          DUP DUP SIZE 'LEN2' STO 7 LEN SUB PHNAMES + 'PHNAMES' STO 
          LEN 10 +
          LEN2 SUB PHNUMBERS + 'PHNUMBERS' STO 
          PHNAMES SIZE 'PHSIZE' STO

          @ Now we need to prompt for the address information...
          "Enter address..." TEMP EVAL "
:Addr:" + { 2 7 }
          \Ga 3 \->LIST INPUT 
       
          @ The current prompt shows the name and prompts for @
          @ the address. @
       
          @ Now save the new ADDRESS information.
          DUP SIZE 'LEN2' STO 
          LEN 8 +
          LEN2 SUB ADDRESSES + 'ADDRESSES' STO 

        ELSE 
          1 'DONE' STO
        END
      UNTIL DONE 
      END
    END  @"ADD"@

    X "MODIFY" SAME 
      THEN         @ User wants to edit the list @

      CLEAR
      0 0 ""  \-> INDEX  @ Stores the index of the entry to be edited. @
                DONE     @ Used to terminate DO UNTIL loop. @
                EDITSTRING   @ Stores the modified string. @

\<<
      @ Basically what I want to do here is to call the PHSEARCH routine @
      @ in a way that it will get the user to pick a specific entry.  @
      @ I want to use the PHSEARCH routine to do this since so much of @
      @ the code needed to do this is alread done.  The result of the @
      @ PHSEARCH routine is to return an index indicating the exact @
      @ entry which is to be edited.  This routine then needs only @
      @ display it in an input statement in such a way that the user @
      @ can modify it as it sits on the display.  When the user presses @
      @ ENTER, the modified entries (name and number) will be written @
      @ back into the database. @
      @ This routine also checks flag 5 to determine whether the user @
      @ wants to delete the record or simply modify it. @
     
      DO 
        2 CF 4 SF PHSEARCH EVAL @ Get the index of entry to be modified. @
      UNTIL 1 FS?C @ Repeat search routine until user selects an entry @
                   @ or gives up. @
      END  
      4 CF   @ Flag 4 was set only to indicate to PHSEARCH to use "pick" in @
                @ the menu so user could choose the item to edit.
      IF 2 FC?C  @ If flag 2 is set, then the user must have given up @
                 @ trying to select an entry to edit. @
      THEN  @ Let's edit the selected entry @
      'INDEX' STO 
        IF 5 FC?C 
        THEN 
          DO 
  @ This section of code was modified in v1.4 to handle the editing of 
  @ addresses.  For this reason, I use several IFTE commands below to 
  @ differentiate between editing the PHNUMBERS field and the 
  @ ADDRESSES field. 

            INDEX DUP 
            PHNAMES SWAP GET @ Gets the name to be modified. @
            SWAP 
            6 FC? 'PHNUMBERS' 'ADDRESSES' IFTE 
            @ Gets PHNUMBERS or ADDRESSES, depending on the 
            @ state of flag 6.

            SWAP GET   @ Gets number/address to be modified. @

            @ The stack now has
            @           2: name
            @           1: number/addr

            @ Now reformat the information for the INPUT command.
        
            6 FC? ":Number:" ":Addr:" IFTE 
            SWAP +
            SWAP ":Name:" SWAP +   @ Adds tags to the name and num/addr.
            "
"
            + SWAP +  @ Creates one long string with a carraige return @
            @ to separate the name and num/addr strings. @
        

            @ Now display this information using INPUT so the user can @
            @ edit the strings. @


            "Edit entry..."  SWAP 0
            2 \->LIST {} TMENU INPUT

            @ At this point, the user has been prompted with the name and @
            @ number to be modified.  The keyboard is in overstrike mode @
            @ and the cursor was left at the end of the number. @

            @ Now we must recover the new name and number from the @
            @ string on the stack. @
 
            DUP 'EDITSTRING' STO   @ Save a copy of the string.
            IF DUP ":Name:" POS 1 ==   @ Test for existence of the name...
            SWAP DUP 
            6 FC? ":Number:" ":Addr:" IFTE 
            POS                   @ ...and number tags within the string.
            DUP 'TEMP' STO  @ Save location of num/addr tag.
            ROT AND    
            THEN    @ The user entered valid data. @

              @ Update the database with new name.@
              DUP 7 TEMP 2 - SUB @ Extract the modified name. @
              1 \->LIST PHNAMES SWAP INDEX SWAP REPL
              'PHNAMES' STO    @ Save modified name in PHNAMES. 

              @ Update the database with the new number/address.
              TEMP 
              6 FC? 8 6 IFTE 
              + EDITSTRING SIZE SUB @ Extract the new name/addr.
              1 \->LIST
              6 FC? 'PHNUMBERS' 'ADDRESSES' IFTE 
              SWAP INDEX SWAP REPL 
              6 FC? "'PHNUMBERS'" "'ADDRESSES'" IFTE 
              OBJ\-> STO 

              1 'DONE' STO 
              CLEAR
            ELSE CLLCD 1200 .1 BEEP "Invalid Entry" 2 DISP 2 WAIT
            END
          UNTIL DONE
          END
        ELSE    @ The user wants to delete the entry.  @
          CLEAR INDEX DUP 
          PHNAMES SWAP GET @ Gets the name to be modified. @
          SWAP PHNUMBERS SWAP GET   @ Gets number to be modified. @

          @ The stack now has           @
          @                   2: name   @
          @                   1: number @

          @ Now reformat the information for the INPUT command. @
        
          ":Number:" SWAP +
          SWAP ":Name:" SWAP +   @ Adds tags to the name and number. @
          "
"
          + SWAP +  @ Creates one long string with a carraige return @
          @ to separate the name and number strings. @
        

          @ Now display this information and prompt user to verify that @
          @ this record should be deleted.   

          CLLCD  "Delete entry?"  1 DISP
          4 DISP 1200 .1 BEEP 3 FREEZE 
          { { "Yes" \<< 1 CONT \>> } { } { } { } { } 
          { "No" \<< 0 CONT \>> } }
          TMENU HALT
          IF   @ yes @
          THEN 
            PHNAMES  OBJ\->  DROP @ Put the list of names on the stack. @
            PHSIZE INDEX 1 - - ROLL 
            DROP   @ Deletes the entry pointed to by INDEX. @
            PHSIZE 1 - \->LIST 'PHNAMES' STO  

            PHNUMBERS  OBJ\->  DROP @ Put the list of names on the stack. @
            PHSIZE INDEX 1 - - ROLL 
            DROP  @ Deletes the entry pointed to by INDEX. @
            PHSIZE 1 - \->LIST 'PHNUMBERS' STO  

            ADDRESSES  OBJ\->  DROP @ Put the list of names on the stack. @
            PHSIZE INDEX 1 - - ROLL 
            DROP  @ Deletes the entry pointed to by INDEX. @
            'PHSIZE'  DECR \->LIST 'ADDRESSES' STO  
          END
        END
      ELSE 5 CF 
      END
\>>
    END
  END  
\>>    @end case@
END  @IF 2 FC?C

ADDRESSES PHNUMBERS PHNAMES 3 \->LIST 'PHDAT' STO


\>> @ Surrounds PHEDIT's parameters. @
\>> @ Ends the definition of PHEDIT and pushes PHEDIT on the stack @
    @ to be defined later.@





 \-> PHEDIT @ Defines the subroutine "PHEDIT", which edits the database. @
           @ PHEDIT takes as parameters a single string which must have the @
           @ value "NEW", indicating a new list should be created, @
           @ or "INT", indicating the old list should be prompted as to 
           @ whether the current list is to be added to or edited. @
 
\<< @ The main routine starts here. @
64 STWS  @ Sets the word size to 64.  Needed for RCLF
RCLF DUP 'FLAGS' STO
2 {#0d} REPL STOF  @ Clear user flags.
STD  @ Set display to STD mode.  Looks nicer.
IF 'PHDAT' VTYPE -1 ==   
  THEN
  CLLCD 
  "Phone Database does" 2 DISP "not exist.  Do you"
  3 DISP "want to create it?"
  4 DISP 3 FREEZE 
  { { "Yes" \<< 1 CONT \>> } { } { } { } { } 
  { "No" \<< 0 CONT \>> } }
  TMENU HALT  @ Prompt the user for yes/no @
  IF 
    THEN  
    {} DUP 'PHNAMES' STO 'PHNUMBERS' STO 
    "NEW" PHEDIT  EVAL @ tells PHEDIT to go directly to the @
                             @ edit input mode on a new list @
  @ After the user exits PHEDIT, control falls down to main routine below.@
    ELSE

"You must create a
phone database before
any further action can
be taken."
      2 DISP CLEAR 2 MENU  1200 .1 BEEP 3 FREEZE  3 WAIT
      0 DOERR         @ Obviously, the user is a pinhead. @
    END 

  END 

@ We need to start the main routine.@
0 \-> DONE \<<
  @ First we need to suck in the database. @
     {} DUP 'PHNAMES' STO 'PHNUMBERS' STO 
  CLEAR  @ Clear the stack, so the following DEPTH command will work.
  PHDAT EVAL

@ At this point, we need to determine if the current database is in
@ the new format used with V1.4 and later.  If the database does not 
@ contain the list of ADDRESSES, then we need to add them and write it back
@ out.
  IF  DEPTH 2 == 
  THEN @ We must update the database structure.
    CLLCD "Reformating PHDAT.
Please wait..." 2 DISP 1 WAIT
    DUP @ Create a spare copy of PHNAMES.
    {}  @ Put a null list on the stack.
    SWAP SIZE 1 SWAP FOR I "" + NEXT  @ Creates a list of null strings.
    3 ROLLD 3 \->LIST
    'PHDAT' STO @ Adds the empty address list to the database.
    CLEAR PHDAT EVAL
  END    

  'PHNAMES' STO  'PHNUMBERS' STO @ copies the database in the local  @
                                 @ variables 'PHNAMES' , 'PHNUMBERS' @
  'ADDRESSES' STO                @ and 'ADDRESSES'.
  PHNAMES SIZE 'PHSIZE' STO  @ Indicates the number of names in the list. @

  DO 
  @ Here we want to ask the user whether we are searching or editing. @
"Do you want to 
search the list or
edit the list?" 
  CLLCD 1 DISP 3 FREEZE
  {{"SRCH" \<< DO PHSEARCH EVAL UNTIL 1 FS?C END CONT \>>}
  {"EDIT" \<< "INT" PHEDIT  EVAL CONT \>>} 
  {} 
  {} {} {"Exit" \<<1 'DONE' STO CONT \>>}} TMENU HALT
  @ "INT" for PHEDIT here to tell PHEDIT to prompt the user as to @
  @ whether he wants to edit (modify) the phone list or simply add to it. @

  UNTIL DONE @ Keep on doing this loop until DONE. @
  END
  \>> @ DO @

  CLEAR 2 MENU 
  FLAGS STOF  @ Resets the state of the system and user flags.
\>> @ Defines bounds for which PHSEARCH is defined. 
\>> @ Main Routine. 
\>> @ Main routine and procedures. 
\>> @ End of Program.