Readline interface: Difference between revisions
(forced table of contents (with a _toc_ keyword). -- ~~~~) |
m (→{{header|REXX}}: simplified the unAbbrev subroutine. -- ~~~~) |
||
Line 262:
select
when abbrev('CALENDAR',ccc,3) then return 'CAL'
when abbrev('CLEARSCREEN',ccc,5)
when abbrev('HISTORY',ccc,4) then return 'HISTORY'
when abbrev('HELP',ccc,1) |,
ccc=='?' then return 'HELP'
when abbrev('KEDIT',ccc,1) then return 'KEDIT'
when abbrev('QUIT',ccc,1) then return 'QUIT'
when abbrev('REXX',ccc,1) then return 'REXX'
when abbrev('TYPE',ccc,1) then return 'TYPE'
otherwise nop
end
|
Revision as of 22:14, 31 May 2012
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>
- include <readline/history.h>
- 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.
<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. */
cmds1='? DIR CALendar HISTory Kedit LLL PROMPT Quit Rexx REDO Type'
cmds2='?|Help DIR CALendar HISTory Kedit LLL PROMPT Quit Rexx REDO Type'
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 one of:' cmds1 /*the default PROMPT message. */
sw=linesize() /*some REXX don't have this BIF. */
call .CLS /*start with a clean slate. */
redoing=0
do forever call prompter; if xxx== then iterate select /*now then, let's rock & roll. */ when xxxF=='CAL' then call .cal when xxxF=='CLS' then call .cls when xxxF=='DIR' then call .dir when xxxF=='HISTORY' then call .history when xxxF=='HELP' then call .help when xxxF=='KEDIT' then call .kedit 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 say 'unknown command:' xxx yyy /*oops-say.*/ 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 arg(1); 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)
/*──────────────────────────────────.CAL subroutine─────────────────────*/ .cal: 'CAL' yyy; return /*──────────────────────────────────.CLS subroutine─────────────────────*/ .cls: cls; return /*──────────────────────────────────.DIR subroutine─────────────────────*/ .dir: 'DIR' yyy; return /*──────────────────────────────────.HELP subroutine────────────────────*/ .help: say center(strip(xxx yyy),sw-1,'-'); cmds_=cmds2 cmdsH='CLS DIR TYPE' 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 day 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 /*──────────────────────────────────.KEDIT subroutine───────────────────*/ .kedit: 'KEDIT' yyy; return /*──────────────────────────────────.LLL subroutine─────────────────────*/ .lll: 'LLL' 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 /*──────────────────────────────────.PROMPT subroutine──────────────────*/ .prompt: if yyyU\== then prompt=yyy; 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 /*──────────────────────────────────.REXX subroutine────────────────────*/ .rexx: 'REXX' yyy; return /*──────────────────────────────────.TYPE subroutine────────────────────*/ .type: 'TYPE' 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('HISTORY',ccc,4) then return 'HISTORY' when abbrev('HELP',ccc,1) |, ccc=='?' then return 'HELP' when abbrev('KEDIT',ccc,1) then return 'KEDIT' when abbrev('QUIT',ccc,1) then return 'QUIT' when abbrev('REXX',ccc,1) then return 'REXX' when abbrev('TYPE',ccc,1) then return 'TYPE' otherwise nop end
return ccc</lang>