Readline interface: Difference between revisions
→{{header|REXX}}: added the '''changestr''' bif. -- ~~~~ |
m →{{header|REXX}}: corrected a typo. -- ~~~~ |
||
Line 145:
<br>"User" commands (subroutines) are identified with a leading period (.) to make it easier to understand what's what.
<br><br>Some older REXXes don't have a '''changestr'' bif, so one is included here.
<lang rexx>/*REXX program to implement a simple "readline"
trace off /*suppress echoing of non-zero RC*/
signal on syntax; signal on novalue
cmdX='ATTRIB CAL CHDIR COPY DEL DIR ECHO EDIT FC FIND KEDIT LLL',
'MEM MKDIR MORE REM REXX RMDIR SET TYPE VER XCOPY'
Line 159:
do forever /*do it until the fat lady sings.*/
if prompter() then iterate
select /*now then, let's rock & roll. */
when wordpos(xxxF,cmdX)\==0 then call .cmdX
Line 176:
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
|
Revision as of 04:00, 26 November 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 some of the simple (MS) 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.
Some older REXXes don't have a 'changestr bif, so one is included here.
<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. */
cmdX='ATTRIB CAL CHDIR COPY DEL DIR ECHO EDIT FC FIND KEDIT LLL',
'MEM MKDIR MORE REM REXX RMDIR SET TYPE VER XCOPY'
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 /*do it until the fat lady sings.*/ if prompter() then iterate /*Nothing entered? So try again.*/ select /*now then, let's rock & roll. */ when wordpos(xxxF,cmdX)\==0 then call .cmdX when xxxF=='EXIT' | xxxF="QUIT" then leave /*the fat lady sung.*/ when xxxF=='HISTORY' then call .history when xxxF=='HELP' then call .help when xxxF=='PROMPT' then call .prompt when xxxF=='REDO' then call .redo otherwise call er 'unknown command:' xxx yyy end /*select*/ end /*forever*/
say xxxF'ting...' /*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: cmdsH='ATTRIB CHDIR CLS COPY DEL DIR ECHO EDIT FC FIND MEM',
'MKDIR MORE PRINT REM RMDIR SET TREE TYPE VER XCOPY' /*the above commands have their own help via /? */
cmds='?|Help|MANual ATTRIButes CALendar CD|CHDIR|CHANGEDir COPY DELete|ERASE',
'DELete|ERASE DIR ECHO EDIT FC|FILECOMPAre FIND HISTory Kedit LLL', 'MEM MD|MKDIR|MAKEDir MORE PRINT PROMPT Quit|EXIT', 'R4 RD|RMDIR|REMOVEDir REGINA REMark Rexx REDO SET TREE Type VER'
say center(strip(xxx yyy),sw-1,'═'); cmds_=cmds; yyyF=unabbrev(yyy)
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.quit = 'quits (exits) this program.' help.redo = 're-does the command # specified (or the last command).' help.rexx = 'executes the REXX program specified.'
if yyy== then do j=1 while cmds_\==
parse var cmds_ x cmds_ say left(,sw%2) changestr('|',x," | ") end /*j*/ 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 /*select*/
return /*──────────────────────────────────.HISTORY subroutine─────────────────*/ .history: say center('history',sw-1,'═'); w=length(hist#)
do j=1 for hist# say right(j,w) '===>' @hist.j end /*j*/
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 1 /*No input? Try again*/ 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 0 /*──────────────────────────────────.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 /*select*/
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('ATTRIBUTES',ccc,6) then return 'ATTRIB' when abbrev('CALENDAR',ccc,3) then return 'CAL' when abbrev('CHANGEDIR',ccc,7) |, ccc=='CHDIR' |, ccc=='CD' then return 'CHDIR' when abbrev('CLEARSCREEN',ccc,5) then return 'CLS' when abbrev('FILECOMPARE',ccc,9) then return 'FC' when abbrev('DELETE',ccc,3) |, ccc=='ERASE' then return 'DEL' when abbrev('HISTORY',ccc,4) then return 'HISTORY' when abbrev('HELP',ccc,1) |, abbrev('MANUAL',ccc,3) |, ccc=='?' then return 'HELP' when abbrev('MAKEDIR',ccc,5) |, ccc=='MKDIR' |, ccc=='MD' then return 'MKDIR' 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('REMOVEDIR',ccc,7) |, ccc=='RMDIR' |, ccc=='RD' then return 'RMDIR' when abbrev('REXX',ccc,1) then return 'REXX' when abbrev('TYPE',ccc,1) then return 'TYPE' otherwise nop end /*select*/
return ccc</lang> output showing a sample session
Enter command ──or── ? ──or── Quit hel ═══════════════════════════════════════════hel══════════════════════════════════ ? | Help | MANual ATTRIButes CALendar CD | CHDIR | CHANGEDir COPY DELete | ERASE DELete | ERASE DIR ECHO EDIT FC | FILECOMPAre FIND HISTory Kedit LLL MEM MD | MKDIR | MAKEDir MORE PRINT PROMPT Quit | EXIT R4 RD | RMDIR | REMOVEDir REGINA REMark Rexx REDO SET TREE Type VER Enter command ──or── ? ──or── Quit rexx $mayan 123,456,787,654,321 ╔════╗ ╔════╗ ╔════╗ ╔════╗ ╔════╗ ╔════╗ ╔════╗ ╔════╗ ╔════╗ ╔════╗ ╔════╗ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ∙ ║ ║ ║ ║ ∙ ║ ║ ║ ║ ∙∙ ║ ║ ║ ║ ║ ║ ║ ║ ∙∙ ║ ║ ║ ║ ║ ║────║ ║────║ ║────║ ║ ║ ║────║ ║ ║ ║ ║ ║────║ ║────║ ║ ∙ ║ ║ ║ ║────║ ║────║ ║────║ ║ ║ ║────║ ║ ∙ ║ ║ ∙∙ ║ ║────║ ║────║ ║────║ ║ ∙∙ ║ ║────║ ║────║ ║────║ ║ ∙ ║ ╚════╝ ╚════╝ ╚════╝ ╚════╝ ╚════╝ ╚════╝ ╚════╝ ╚════╝ ╚════╝ ╚════╝ ╚════╝ Enter command ──or── ? ──or── Quit cal ┌────────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ June 2012 │ │ │ │ │ │ Sunday Monday Tuesday Wednesday Thursday Friday Saturday │ ├──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ─ 1 ─ │ 2 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘ Enter command ──or── ? ──or── Quit LLL mm/dd/yyyy hh:mm ──filesize── ─────────────────filename D:\*.*───────────────── 06/01/2012 10:36 2,322 DIV_GRID.TXT 05/13/2012 20:39 1,044 MOD_EXP.REX 05/13/2012 19:39 694 MOD_EXP.TXT 06/01/2012 02:32 1,439 RCOMBN.RX_ 06/01/2012 02:24 7,181 READLINT.REX 05/29/2012 22:48 60 sub00.REX ──────────────────────12,740 <──────────────total bytes [6 files] Enter command ──or── ? ──or── Quit dir Volume in drive D is -----D----- Volume Serial Number is 68D0-6356 Directory of D:\ 05/09/2012 12:40 <DIR> Config.Msi 12/16/2005 19:54 <DIR> Documents and Settings 01/15/2006 10:33 <DIR> Program Files 04/14/2010 19:58 <DIR> Recycled 12/16/2005 20:29 <DIR> System Volume Information 01/26/2011 00:21 <DIR> temp 12/16/2005 19:42 <DIR> WINDOWS 01/06/2006 14:43 211 boot.ini 06/01/2012 10:36 2,322 DIV_GRID.TXT 12/16/2005 20:18 0 IO.SYS 05/13/2012 20:39 1,044 MOD_EXP.REX 05/13/2012 19:39 694 MOD_EXP.TXT 12/16/2005 20:18 0 MSDOS.SYS 08/04/2004 12:00 47,564 NTDETECT.COM 08/04/2004 12:00 250,032 ntldr 06/01/2012 02:32 1,439 RCOMBN.RX_ 06/01/2012 02:24 7,181 READLINT.REX 05/29/2012 22:48 60 sub00.REX 11 File(s) 310,547 bytes 7 Dir(s) 6,793,314,304 bytes free Enter command ──or── ? ──or── Quit help ver ════════════════════════════════════════help ver═════════════════════════════════════════ Displays the Windows XP version. VER Enter command ──or── ? ──or── Quit ver Microsoft Windows XP [Version 5.1.2600] Enter command ──or── ? ──or── Quit q QUITting...