Readline interface: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|REXX}}: fixed a few bugs, added a "user" command. -- ~~~~)
(→‎{{header|REXX}}: reduced the size of the program, added more commands. -- ~~~~)
Line 147: Line 147:
trace off /*suppress echoing of non-zero RC*/
trace off /*suppress echoing of non-zero RC*/
signal on syntax; signal on novalue /*handle REXX program errors. */
signal on syntax; signal on novalue /*handle REXX program errors. */
cmds='?|Help DIR ECHO CALendar HISTory Kedit LLL PROMPT Quit Rexx REDO Type'
cmds='?|Help|MANual CALendar DIR ECHO FC|FILECOMPAre FIND HISTory Kedit',
'LLL MEM MORE PROMPT Quit REMark Rexx REDO Type VER'
cmdX='CAL DIR ECHO FC FIND KEDIT LLL MEM MORE REM REXX TYPE VER'
cls='CLS' /*define the pgm to clear screen.*/
cls='CLS' /*define the pgm to clear screen.*/
@hist.='*** command not defined. ***' /*initialize the history database*/
@hist.='*** command not defined. ***' /*initialize the history database*/
Line 153: Line 155:
prompt='Enter command ──or── ? ──or── Quit' /*default PROMPT msg.*/
prompt='Enter command ──or── ? ──or── Quit' /*default PROMPT msg.*/
sw=linesize() /*some REXX don't have this BIF. */
sw=linesize() /*some REXX don't have this BIF. */
call .CLS /*start with a clean slate. */
cls /*start with a clean slate. */
redoing=0 /*flag for executing naked ReDO.*/
redoing=0


do forever
do forever
call prompter; if xxx=='' then iterate
call prompter; if xxx=='' then iterate
select /*now then, let's rock & roll. */
select /*now then, let's rock & roll. */
when xxxF=='CAL' then call .cal
when wordpos(xxxF,cmdX)\==0 then call .cmdX
when xxxF=='CLS' then call .cls
when xxxF=='HISTORY' then call .history
when xxxF=='DIR' then call .dir
when xxxF=='HELP' then call .help
when xxxF=='ECHO' then call .echo
when xxxF=='PROMPT' then call .prompt
when xxxF=='HISTORY' then call .history
when xxxF=='QUIT' then leave
when xxxF=='HELP' then call .help
when xxxF=='REDO' then call .redo
when xxxF=='KEDIT' then call .kedit
otherwise call er 'unknown command:' xxx yyy
when xxxF=='LLL' then call .lll
when xxxF=='PROMPT' then call .prompt
when xxxF=='QUIT' then leave
when xxxF=='REDO' then call .redo
when xxxF=='REXX' then call .rexx
when xxxF=='TYPE' then call .type
otherwise call er 'unknown command:' xxx yyy /*oops-say.*/
end /*select*/
end /*select*/
end /*forever*/
end /*forever*/
Line 186: Line 181:
condition('D'),'REXX source statement (line' sigl"):",,
condition('D'),'REXX source statement (line' sigl"):",,
sourceline(sigl)
sourceline(sigl)
/*──────────────────────────────────.CAL subroutine─────────────────────*/
/*──────────────────────────────────.CMDX subroutine────────────────────*/
.cal: 'CAL' yyy; return
.cmdX: xxxF yyy; return
/*──────────────────────────────────.CLS subroutine─────────────────────*/
.cls: cls; return
/*──────────────────────────────────.DIR subroutine─────────────────────*/
.dir: 'DIR' yyy; return
/*──────────────────────────────────.ECHO subroutine────────────────────*/
.echo: 'ECHO' yyy; return
/*──────────────────────────────────.HELP subroutine────────────────────*/
/*──────────────────────────────────.HELP subroutine────────────────────*/
.help: say center(strip(xxx yyy),sw-1,'═'); cmds_=cmds
.help: say center(strip(xxx yyy),sw-1,'═'); cmds_=cmds
cmdsH='CLS DIR ECHO TYPE'
cmdsH='CLS DIR ECHO FC FIND MEM MORE REM TYPE VER'
help. = ' No help is available for the' yyy "command."
help. = ' No help is available for the' yyy "command."
help.cal = 'shows a calendar for the current month or specified month.'
help.cal = 'shows a calendar for the current month or specified month.'
Line 223: Line 212:
end
end
return
return
/*──────────────────────────────────.KEDIT subroutine───────────────────*/
/*──────────────────────────────────.PROMPT subroutine──────────────────*/
.kedit: 'KEDIT' yyy; return
.prompt: if yyyU\=='' then prompt=yyy; return
/*──────────────────────────────────.LLL subroutine─────────────────────*/
.lll: 'LLL' yyy; return
/*──────────────────────────────────PROMPTER subroutine─────────────────*/
/*──────────────────────────────────PROMPTER subroutine─────────────────*/
prompter: if redoing then do /*special case for naked REDO */
prompter: if redoing then do /*special case for naked REDO */
Line 242: Line 229:
xxxF=unAbbrev(xxx) /*expand the abbreviation (maybe)*/
xxxF=unAbbrev(xxx) /*expand the abbreviation (maybe)*/
return
return
/*──────────────────────────────────.PROMPT subroutine──────────────────*/
.prompt: if yyyU\=='' then prompt=yyy; return
/*──────────────────────────────────.redo subroutine────────────────────*/
/*──────────────────────────────────.redo subroutine────────────────────*/
.redo:
.redo:
Line 257: Line 242:
@hist.yyy
@hist.yyy
return
return
/*──────────────────────────────────.REXX subroutine────────────────────*/
.rexx: 'REXX' yyy; return
/*──────────────────────────────────.TYPE subroutine────────────────────*/
.type: 'TYPE' yyy; return
/*──────────────────────────────────UNABBREV subroutine─────────────────*/
/*──────────────────────────────────UNABBREV subroutine─────────────────*/
unabbrev: procedure; arg ccc
unabbrev: procedure; arg ccc
Line 266: Line 247:
when abbrev('CALENDAR',ccc,3) then return 'CAL'
when abbrev('CALENDAR',ccc,3) then return 'CAL'
when abbrev('CLEARSCREEN',ccc,5) then return 'CLS'
when abbrev('CLEARSCREEN',ccc,5) then return 'CLS'
then return 'CLS'
when abbrev('FILECOMPARE',ccc,9) then return 'FC'
when abbrev('HISTORY',ccc,4) then return 'HISTORY'
when abbrev('HISTORY',ccc,4) then return 'HISTORY'
when abbrev('HELP',ccc,1) |,
when abbrev('HELP',ccc,1) |,
abbrev('MANUAL',ccc,3) |,
ccc=='?' then return 'HELP'
ccc=='?' then return 'HELP'
when abbrev('KEDIT',ccc,1) then return 'KEDIT'
when abbrev('KEDIT',ccc,1) then return 'KEDIT'
when abbrev('QUIT',ccc,1) then return 'QUIT'
when abbrev('QUIT',ccc,1) then return 'QUIT'
when abbrev('REMARK',ccc,3) then return 'REM'
when abbrev('REXX',ccc,1) then return 'REXX'
when abbrev('REXX',ccc,1) then return 'REXX'
when abbrev('TYPE',ccc,1) then return 'TYPE'
when abbrev('TYPE',ccc,1) then return 'TYPE'

Revision as of 23:12, 31 May 2012

Readline interface is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Build a simple application with a readline interface.

The interface should provide

  • a history
  • commandline editing
  • application specific commands

The application could be based on Simple database or something else.

If the language already provides a readline interface, then it should be demonstrated how build an application specific readline interface (one that only understands the applications commands) and also show how to customize the builtin readline to provide the above features and especially add application specific commands.

See also: Input loop

C

A program that does absolutely nothing. Type 'help' for help. <lang c>#include <readline/readline.h>

  1. include <readline/history.h>
  2. include <string.h>

int main() { char *s; using_history(); while (1) { s = readline("This be a prompt> ");

if (!s || !strcmp(s, "quit")) { puts("bye."); return 0; }

if (!strcmp(s, "help")) puts("commands: ls, cat, quit"); else if (!strcmp(s, "ls") || !strcmp(s, "cat")) { printf("command `%s' not implemented yet.\n", s); add_history(s); } else puts("Yes...?"); } }</lang>

Pike

watch.pike is the solution from Simple database#Pike. this solution demonstrates how to retrofit an application with a readline interface by inheriting the original and overriding specific functions.

<lang pike>#!/usr/bin/pike

inherit "watch.pike";

Stdio.Readline readln = Stdio.Readline();

void print_help() {

   write("The following commands are available: \n");
   write(sort(indices(functions))*", ");
   write("\n");

}

void watch_add() {

   ::watch_add(db);

}

void watch_list() {

   ::watch_list(db);

}

void do_exit() {

   exit(0);

}

mapping functions = ([ "add":watch_add,

                      "list":watch_list,
                      "load":watch_load,
                      "save":watch_save,
                      "help":print_help,
                      "quit":do_exit ]);

string prompt_read(string prompt) {

   return readln->read(prompt+": ");

}

void main() {

 Stdio.Readline.History readline_history = Stdio.Readline.History(512);
 readln->enable_history(readline_history);
 string prompt="> ";


 print_help();
 while(1)
 {
   string input=readln->read(prompt);
   if(!input)
     exit(0);
   if(input != "")
   {
      if (functions[input])
          functions[input]();
      else
      {
          write("unknown command\n");
          print_help();
      }
   }
 }

}</lang>

Sample session:

pike watch_rl.pike
The following commands are available:
add, help, list, load, quit, save
> load
> list
  Rosetta Code                            2 Simple Database                (27.10.2011.)
> add
Series: Rdm asks!
Title: Balanced ternary
Episode: 1
Date watched: 1.11.2011
Add new series? [y/n]: : y
> list
  Rdm asks!                               1 Balanced ternary               (1.11.2011.)
  Rosetta Code                            2 Simple Database                (27.10.2011.)
> hello
unknown command
The following commands are available: 
add, help, list, load, quit, save
> save
> quit

REXX

This REXX programs supports a REDO (re-do) with very limited editing to keep the program simple.
It has a history (which can be interrogated) and a few simple (DOS) commands.
The HELP (?), REDO, error checking, and abbreviations took up most of the program.
"User" commands (subroutines) are identified with a leading period (.) to make it easier to understand what's what. <lang rexx>/*REXX program to implement a simple "readline" shell. */ trace off /*suppress echoing of non-zero RC*/ signal on syntax; signal on novalue /*handle REXX program errors. */ cmds='?|Help|MANual CALendar DIR ECHO FC|FILECOMPAre FIND HISTory Kedit',

                   'LLL MEM MORE PROMPT Quit REMark Rexx REDO Type VER'

cmdX='CAL DIR ECHO FC FIND KEDIT LLL MEM MORE REM REXX TYPE VER' cls='CLS' /*define the pgm to clear screen.*/ @hist.='*** command not defined. ***' /*initialize the history database*/ hist#=0 /*number of commands in history. */ prompt='Enter command ──or──  ? ──or── Quit' /*default PROMPT msg.*/ sw=linesize() /*some REXX don't have this BIF. */ cls /*start with a clean slate. */ redoing=0 /*flag for executing naked ReDO.*/

  do forever
  call prompter;  if xxx== then iterate
    select                            /*now then, let's rock & roll.   */
    when wordpos(xxxF,cmdX)\==0 then call .cmdX
    when xxxF=='HISTORY'  then call .history
    when xxxF=='HELP'     then call .help
    when xxxF=='PROMPT'   then call .prompt
    when xxxF=='QUIT'     then leave
    when xxxF=='REDO'     then call .redo
    otherwise                  call er 'unknown command:' xxx yyy
    end   /*select*/
  end     /*forever*/

say 'Quitting...' /*say goodbye, 'cause it's polite*/ exit /*stick a fork in it, we're done.*/ /*───────────────────────────────error handling subroutines and others.─*/ er: say; say; say '****error!****'; say; say arg(1); say; say; return err: say; say; say center(' error! ',max(40,linesize()%2),"*"); say

              do j=1 for arg(); say arg(j); say; end; say; exit 13

novalue: syntax: call err 'REXX program' condition('C') "error",,

            condition('D'),'REXX source statement (line' sigl"):",,
            sourceline(sigl)

/*──────────────────────────────────.CMDX subroutine────────────────────*/ .cmdX: xxxF yyy; return /*──────────────────────────────────.HELP subroutine────────────────────*/ .help: say center(strip(xxx yyy),sw-1,'═'); cmds_=cmds cmdsH='CLS DIR ECHO FC FIND MEM MORE REM TYPE VER' help. = ' No help is available for the' yyy "command." help.cal = 'shows a calendar for the current month or specified month.' help.kedit = 'KEDITs the file specified.' help.lll = 'shows a formatted listing of files in the current directory.' help.prompt= 'sets the PROMPT message to the specified text.' help.q = 'exits this program.' help.redo = 're-does the a command # specified (or the last command).' help.rexx = 'executes the REXX program specified.' yyyF=unabbrev(yyy) if yyy== then do j=1 while cmds_\==

               parse var cmds_ x cmds_
               say left(,sw%2) changestr('|',x,"  |  ")
               end
          else select
               when wordpos(yyyF,cmdsH)\==0 then yyyF '/?'
               otherwise cmd?=yyyF
               if left(help.yyyF,1)\==' ' then say yyyF ' ' help.yyyF
                                          else say help.yyyF
               end

return /*──────────────────────────────────.HISTORY subroutine─────────────────*/ .history: say center('history',sw-1,'═'); w=length(hist#)

            do j=1 for hist#
            say right(j,w) '===>' @hist.j
            end

return /*──────────────────────────────────.PROMPT subroutine──────────────────*/ .prompt: if yyyU\== then prompt=yyy; return /*──────────────────────────────────PROMPTER subroutine─────────────────*/ prompter: if redoing then do /*special case for naked REDO */

                         redoing=0;       z=hist#-1
                         parse var @hist.z xxx yyy
                         end
                    else do
                         if prompt\== then do;   say;  say prompt;  end
                         parse pull xxx yyy
                         end

xxxU=xxx; upper xxxU; if xxx== then return yyyU=yyy; upper yyyU; yyyU=strip(yyyU) hist#=hist#+1; /*bump the history counter. */ @hist.hist#=strip(xxx yyy) /*assign to history. */ xxxF=unAbbrev(xxx) /*expand the abbreviation (maybe)*/ return /*──────────────────────────────────.redo subroutine────────────────────*/ .redo:

 select
 when yyyU== then redoing=1         /*assume they want the last cmd. */
 when words(yyy)\==1     then call er 'too many args specified for' xxx
 when \datatype(yyy,'W') then call er "2nd arg isn't numeric for" xxx
 otherwise nop
 end

if redoing then return /*handle with kid gloves. */ yyy=yyy/1 /*normalize it: +7 7. 1e1 007 7.0*/ say 'Re-doing:' @hist.yyy @hist.yyy return /*──────────────────────────────────UNABBREV subroutine─────────────────*/ unabbrev: procedure; arg ccc

  select
  when abbrev('CALENDAR',ccc,3)                 then return 'CAL'
  when abbrev('CLEARSCREEN',ccc,5)              then return 'CLS'
  when abbrev('FILECOMPARE',ccc,9)              then return 'FC'
  when abbrev('HISTORY',ccc,4)                  then return 'HISTORY'
  when abbrev('HELP',ccc,1) |,
       abbrev('MANUAL',ccc,3) |,
         ccc=='?'                               then return 'HELP'
  when abbrev('KEDIT',ccc,1)                    then return 'KEDIT'
  when abbrev('QUIT',ccc,1)                     then return 'QUIT'
  when abbrev('REMARK',ccc,3)                   then return 'REM'
  when abbrev('REXX',ccc,1)                     then return 'REXX'
  when abbrev('TYPE',ccc,1)                     then return 'TYPE'
  otherwise nop
  end

return ccc</lang>