PageRenderTime 62ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/linux265/lkcd/archive/lkcdutils/librl/rl.c

#
C | 661 lines | 472 code | 73 blank | 116 comment | 84 complexity | 60d9ee74a5a7ed8f8a2265287b6414c3 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, LGPL-2.1, LGPL-2.0
  1. /*
  2. * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
  3. */
  4. /*
  5. Leaner command input module.
  6. Support basic command line editing, history mechanism and completion
  7. mechanism.
  8. History mechanism supported:
  9. A command name 'h' or 'history' will not be returned by the
  10. rl utilities and will generate a list of the current history.
  11. An optional parameter can be passed along with the 'h' or
  12. 'history' command to set the number of command being remembered.
  13. !! Refer to the previous command. By itself, this substitution
  14. repeats the previous command.
  15. !n Refer to command line n .
  16. !-n Refer to the current command line minus n.
  17. !str Refer to the most recent command starting with str.
  18. The standard ^ keys are supported:
  19. ^W : delete to previous word
  20. ^D : delete current character
  21. ^A : goto start of line
  22. ^E : goto end of line
  23. ^F : forward one character
  24. ^B : backward one character
  25. ^H : delete previous character
  26. ^N : down history
  27. ^K : erase to eol (from cursor)
  28. ^L : clear screen and redisplay prompt
  29. ^P : up history
  30. ^U : erase to beginning of line (from cursor)
  31. ^R : redraw input line
  32. ESC-f : forward one word
  33. ESC-b : backward one word
  34. ESC-d : delete next word
  35. ESC-DEL : delete previous word
  36. Completion mechanism supported:
  37. When TAB is pressed while having inputted line, call the function
  38. registered beforehand to complete line.
  39. */
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <stdio.h>
  43. #include <unistd.h>
  44. #include <ctype.h>
  45. #include <curses.h>
  46. #include <term.h>
  47. #include <termio.h>
  48. #include "rl.h"
  49. #define ctrl(c) ((c) & 0x1f)
  50. static int maxh=DEF_HIST; /* number of buffered commands */
  51. static int maxl=DEF_LENGTH; /* maximum command length */
  52. static int in; /* input tty handle */
  53. static char *prompt=">"; /* prompt to be used */
  54. static char *bol, *leftN, *rightN, *upN, *downN, *home; /* cursor movement */
  55. static char *kup, *kdown, *kleft, *kright, *kdel; /* keyboard input sequences */
  56. static char *cod; /* display management */
  57. static char *bip; /* sound the bell */
  58. static int notty=0; /* no controling terminal or missing basic kbd functionality */
  59. static int width=80; /* width of the screen we are working from */
  60. static int xenl=0;
  61. static char *buf;
  62. static struct termio tio, stio;
  63. static char *kwb="\033\177";
  64. static char *kwf="\033d";
  65. static char *fw="\033f";
  66. static char *bw="\033b";
  67. static rl_complete_func_t rl_complete_func; /* function for completing line */
  68. /*
  69. setup terminal characteristics and allocate initial stuff
  70. */
  71. int
  72. rl_init(char *p, int bufsize, int histsize)
  73. {
  74. char *term;
  75. int ret;
  76. /* set prompt if specified */
  77. if(p) prompt=p;
  78. if(bufsize) maxl=bufsize;
  79. if(histsize) maxh=histsize;
  80. if(!(hist_init(maxh, maxl))) return 0;
  81. /* allocate a new buffer */
  82. if(!(buf=malloc(maxl))) return 0;
  83. /* setup terminal. If we do not have a terminal
  84. we'll use the gets() call. If we have a terminal
  85. but no TERM, we use the "dumb" terminal */
  86. in=fileno(stdin);
  87. if(!isatty(in))
  88. {
  89. notty=1;
  90. return 1;
  91. }
  92. printf("\n");
  93. bip="\007";
  94. if(!(term = getenv ("TERM"))) term="dumb";
  95. else
  96. {
  97. /* XXX force iris-ansi-net to iris-ansi
  98. Current Linux terminfo does'nt know iris-ansi-net */
  99. if(!strcmp(term,"iris-ansi-net")) term[9]='\0';
  100. }
  101. if(setupterm(term, in, &ret)!=ERR)
  102. {
  103. /* if any of these basics go back to fgets() */
  104. if(!(upN=tigetstr("cuu")) ||
  105. !(downN=tigetstr("cud")) ||
  106. !(leftN=tigetstr("cub")) ||
  107. !(bol=tigetstr("cr")) ||
  108. !(rightN=tigetstr("cuf")) ||
  109. !(cod=tigetstr("ed"))) { notty=1; return 1; }
  110. xenl=tigetflag("xenl");
  111. home=tigetstr("clear");
  112. kup=tigetstr("kcuu1");
  113. kdown=tigetstr("kcud1");
  114. kleft=tigetstr("kcub1");
  115. kright=tigetstr("kcuf1");
  116. kdel=tigetstr("kdch1");
  117. } else
  118. {
  119. if(!ret)
  120. printf("Unknown TERM : %s\n", term);
  121. notty=1;
  122. return 1;
  123. }
  124. /* get window size */
  125. {
  126. struct winsize w;
  127. if (ioctl (in, TIOCGWINSZ, &w) == 0)
  128. {
  129. width=w.ws_col;
  130. }
  131. else /* use ENV */
  132. {
  133. char *ewidth;
  134. if ((ewidth = getenv ("COLUMNS")))
  135. width = atoi (ewidth);
  136. /* use what's in terminfo */
  137. if (width <= 0)
  138. width = tigetnum ("co");
  139. }
  140. if (width <= 1) width = 80;
  141. }
  142. /* set ourselves in the proper mode */
  143. {
  144. if(ioctl(in, TCGETA, &tio)) { notty=1; return 1;}
  145. stio=tio;
  146. tio.c_lflag &= ~(ICANON | ECHO);
  147. tio.c_iflag &= ~(ICRNL | INLCR);
  148. tio.c_cc[VMIN] = 1;
  149. tio.c_cc[VTIME] = 0;
  150. }
  151. return 1;
  152. }
  153. #define UP_HISTORY 1001
  154. #define DOWN_HISTORY 1002
  155. #define CURSOR_LEFT 1003
  156. #define CURSOR_RIGHT 1004
  157. #define DELETE 1005
  158. #define BACKSPACE 1006
  159. #define KILLLINE 1007
  160. #define LINEDONE 1008
  161. #define KILLWORD 1009
  162. #define KILLTOBOL 1010
  163. #define KILLTOEOL 1011
  164. #define GOTOBOL 1012
  165. #define GOTOEOL 1013
  166. #define CLRSCR 1014
  167. #define REDRAW 1015
  168. #define KILL_WORD_FORWARD 1016
  169. #define WORD_BACKWARD 1017
  170. #define WORD_FORWARD 1018
  171. #define COMPLETE_LINE 1019 /* for completing line */
  172. #define DEL 1020
  173. #define NCTRL 16
  174. static int ctrls[NCTRL][2]=
  175. {
  176. {UP_HISTORY, ctrl('P')},
  177. {DOWN_HISTORY, ctrl('N')},
  178. {CURSOR_LEFT, ctrl('B')},
  179. {CURSOR_RIGHT, ctrl('F')},
  180. {DELETE , ctrl('D')},
  181. {BACKSPACE, ctrl('H')},
  182. {LINEDONE, ctrl('J')},
  183. {KILLWORD, ctrl('W')},
  184. {KILLTOBOL, ctrl('U')},
  185. {KILLTOEOL, ctrl('K')},
  186. {GOTOBOL, ctrl('A')},
  187. {GOTOEOL, ctrl('E')},
  188. {CLRSCR, ctrl('L')},
  189. {REDRAW, ctrl('R')},
  190. {DEL, '\177'},
  191. {LINEDONE, '\r'},
  192. };
  193. #define NBIND (sizeof(codes)/sizeof(codes[0]))
  194. static int codes[]={
  195. UP_HISTORY,DOWN_HISTORY,CURSOR_LEFT,CURSOR_RIGHT,DELETE,
  196. KILLWORD,KILL_WORD_FORWARD,WORD_BACKWARD,WORD_FORWARD,
  197. };
  198. static char **seqs[]={
  199. &kup,&kdown,&kleft,&kright,&kdel,
  200. &kwb,&kwf,&bw,&fw
  201. };
  202. #define buz() putp(bip)
  203. static int
  204. getinput(void)
  205. {
  206. int idx=0;
  207. char curseq[10];
  208. while(1)
  209. {
  210. int c=getc(stdin);
  211. int i;
  212. int found=0;
  213. if (c == '\t') {
  214. /* completing line */
  215. return COMPLETE_LINE;
  216. }
  217. /* check the control characters */
  218. for(i=0;i<NCTRL;i++)
  219. if(ctrls[i][1]==c) return ctrls[i][0];
  220. /* check the keyboard sequences */
  221. curseq[idx++]=c;
  222. for(i=0;i<NBIND;i++)
  223. {
  224. int j;
  225. if(!*(seqs[i])) continue;
  226. for(j=0;j<idx;j++)
  227. {
  228. if((*seqs[i])[j]==curseq[j])
  229. {
  230. /* set found if we match the entire current input */
  231. if(j==idx-1) found=1;
  232. if((*seqs[i])[j+1]=='\0') return codes[i];
  233. }
  234. }
  235. }
  236. if(!found) {
  237. if(isprint(c))
  238. return c;
  239. else {
  240. buz(); idx=0;
  241. }
  242. }
  243. }
  244. }
  245. static int curpos; /* position of cursor inside the input string */
  246. static int maxpos=0; /* current length of the input string */
  247. static void curboth(int n)
  248. {
  249. int curx=curpos%width;
  250. int cury=curpos/width;
  251. int newx=(curpos+n)%width;
  252. int newy=(curpos+n)/width;
  253. if(newy > cury) putp(tparm(downN, newy-cury));
  254. else if(cury > newy) putp(tparm(upN, cury-newy));
  255. if(newx > curx) putp(tparm(rightN, newx-curx));
  256. else if(curx > newx) putp(tparm(leftN, curx-newx));
  257. curpos+=n;
  258. }
  259. static void curleft(int n) { curboth(-n); }
  260. static void curright(int n) { curboth(n); }
  261. /*
  262. This function clears the screen button. Displays the current buffer and
  263. sets the cursor at the proper position.
  264. */
  265. static void
  266. showbuf(int repos)
  267. {
  268. int max, i;
  269. int pos=curpos;
  270. /* clear to end of display from where we are now */
  271. putp(cod);
  272. /* display the current buffer */
  273. for(i=curpos; i<maxpos; i+=max) {
  274. int c;
  275. max=width-(i%width);
  276. if(i+max > maxpos) max=maxpos-i;
  277. c=buf[i+max];
  278. buf[i+max]='\0';
  279. putp(buf+i);
  280. buf[i+max]=c;
  281. if(!((i+max)%width) && xenl) {
  282. putp("\n");
  283. putp(bol);
  284. }
  285. }
  286. curpos=maxpos;
  287. if(repos) curleft(maxpos-pos);
  288. }
  289. static char *
  290. getline(void)
  291. {
  292. int plen=strlen(prompt);
  293. int histoff=0;
  294. /* show the prompt */
  295. strcpy(buf, prompt);
  296. maxpos=plen;
  297. curpos=0;
  298. showbuf(0);
  299. curpos=plen;
  300. ioctl(in, TCSETA, &tio);
  301. {
  302. struct winsize w;
  303. int nw=0;
  304. if (ioctl (in, TIOCGWINSZ, &w) == 0) nw=w.ws_col;
  305. if(nw>1) width=nw;
  306. }
  307. while(1) {
  308. int i;
  309. switch((i=getinput())) {
  310. case UP_HISTORY: case DOWN_HISTORY:
  311. {
  312. int inc=(i==UP_HISTORY?1:-1);
  313. char *p;
  314. if((p=hist_getcmd(histoff+inc)))
  315. {
  316. curleft(curpos-plen);
  317. strcpy(buf+plen, p);
  318. maxpos=strlen(buf);
  319. showbuf(0);
  320. curpos=maxpos;
  321. histoff+=inc;
  322. }else buz();
  323. }
  324. break;
  325. case BACKSPACE: case DEL: case CURSOR_LEFT:
  326. {
  327. if(curpos==plen) buz();
  328. else
  329. {
  330. curleft(1);
  331. /* we need to reprint if backspace */
  332. if(i==BACKSPACE || i==DEL)
  333. {
  334. memmove(buf+curpos, buf+curpos+1, maxl-curpos);
  335. maxpos--;
  336. showbuf(1);
  337. }
  338. }
  339. }
  340. break;
  341. case DELETE:
  342. {
  343. if(curpos==maxpos) buz();
  344. else
  345. {
  346. memmove(buf+curpos, buf+curpos+1, maxl-curpos);
  347. maxpos--;
  348. showbuf(1);
  349. }
  350. }
  351. break;
  352. case CURSOR_RIGHT:
  353. {
  354. if(curpos==maxpos) buz();
  355. else { curright(1); }
  356. }
  357. break;
  358. case LINEDONE:
  359. {
  360. /* we're about to return, so set the cursor position */
  361. curright(maxpos-curpos);
  362. putp("\r\n");
  363. ioctl(in, TCSETA, &stio);
  364. return buf+plen;
  365. }
  366. /* erase entire line . Currently not linked to any keys */
  367. case KILLLINE:
  368. {
  369. curleft(curpos-plen);
  370. maxpos=plen;
  371. buf[plen]='\0';
  372. showbuf(1);
  373. }
  374. /* erase the current word */
  375. case KILLWORD:
  376. {
  377. /* if we are at the start of the line , bip */
  378. if(curpos==plen) buz();
  379. else
  380. {
  381. int i=curpos-1;
  382. /* if the cursor sits on a white character already
  383. find the first non white one */
  384. while(!isalnum(buf[i])&&i>plen) i--;
  385. /* skip back untill beginning of line or white again */
  386. while(isalnum(buf[i])&&i>plen) i--;
  387. if(i<maxpos && !isalnum(buf[i])) i++;
  388. /* move every backward */
  389. memmove(buf+i, buf+curpos, maxl-curpos);
  390. curleft(curpos-i);
  391. maxpos=strlen(buf);
  392. showbuf(1);
  393. }
  394. }
  395. break;
  396. case KILLTOBOL:
  397. {
  398. memmove(buf+plen, buf+curpos, maxl-curpos);
  399. curleft(curpos-plen);
  400. maxpos=strlen(buf);
  401. showbuf(1);
  402. }
  403. break;
  404. case KILLTOEOL:
  405. {
  406. buf[curpos]='\0';
  407. maxpos=strlen(buf);
  408. showbuf(1);
  409. }
  410. break;
  411. case GOTOBOL: { curleft(curpos-plen); } break;
  412. case GOTOEOL: { curright(maxpos-curpos); } break;
  413. case CLRSCR:
  414. {
  415. if(home) {
  416. int i=curpos;
  417. putp(home);
  418. curpos=0;
  419. showbuf(0);
  420. curpos=maxpos;
  421. curleft(maxpos-i);
  422. } else buz();
  423. } break;
  424. case REDRAW: { } break; /* do nothing */
  425. case WORD_FORWARD:
  426. /* if we are at the start of the line , bip */
  427. if(curpos==maxpos) buz();
  428. else
  429. {
  430. int i=curpos;
  431. /* if the cursor sits on a white character already
  432. find the first non white one */
  433. while(!isalnum(buf[i])&&i<maxpos) i++;
  434. /* scip back untill beginning of line or white again */
  435. while(isalnum(buf[i])&&i<maxpos) i++;
  436. curright(i-curpos);
  437. }
  438. break;
  439. case WORD_BACKWARD:
  440. /* if we are at the start of the line , bip */
  441. if(curpos==plen) buz();
  442. else
  443. {
  444. int i=curpos;
  445. /* if the cursor sits on a white character already
  446. find the first non white one */
  447. if(i>plen) i--;
  448. while(!isalnum(buf[i])&&i>plen) i--;
  449. /* scip back untill beginning of line or white again */
  450. while(isalnum(buf[i])&&i>plen) i--;
  451. while(!isalnum(buf[i])&&i>plen) i++;
  452. curleft(curpos-i);
  453. }
  454. break;
  455. case KILL_WORD_FORWARD:
  456. /* if we are at the start of the line , bip */
  457. if(curpos==maxpos) buz();
  458. else
  459. {
  460. int i=curpos;
  461. /* if the cursor sits on a white character already
  462. find the first non white one */
  463. while(!isalnum(buf[i])&&i<maxpos) i++;
  464. /* scip back untill beginning of line or white again */
  465. while(isalnum(buf[i])&&i<maxpos) i++;
  466. while(!isalnum(buf[i])&&i<maxpos) i++;
  467. /* keep one space */
  468. if(i<maxpos && isalnum(buf[i])) i--;
  469. /* move every backward */
  470. memmove(buf+curpos, buf+i, maxl-i);
  471. maxpos=strlen(buf);
  472. showbuf(1);
  473. }
  474. break;
  475. case COMPLETE_LINE: /* complete line */
  476. {
  477. char *ret;
  478. int retstr_len;
  479. int save_curpos;
  480. /* if rl_complete_func is not registered, bip */
  481. if (!rl_complete_func) {
  482. buz();
  483. } else {
  484. /* save current cursor position */
  485. save_curpos = curpos;
  486. /* since command list may be printed in rl_complete_func(),
  487. move cursor to max position */
  488. curright(maxpos - curpos);
  489. /* call line completion function */
  490. ret = rl_complete_func(buf+plen, save_curpos-plen);
  491. if (ret == DRAW_NEW_ENTIRE_LINE) {
  492. /* draw new entire line */
  493. curpos = 0;
  494. showbuf(0);
  495. /* resume cursor position */
  496. curleft(maxpos - save_curpos);
  497. } else if (ret == PRINT_BEEP) {
  498. /* resume cursor position */
  499. curleft(maxpos - save_curpos);
  500. /* print beep character */
  501. buz();
  502. } else if (ret > 0) {
  503. /* insert string that returned before position of cursor */
  504. retstr_len = strlen(ret);
  505. /* resume cursor position */
  506. curleft(maxpos - save_curpos);
  507. if (maxpos+retstr_len>maxl) {
  508. /* if we exceed maximum command length, bip */
  509. buz();
  510. } else {
  511. /* insert string that returned */
  512. memmove(buf+curpos+retstr_len, buf+curpos, maxl-curpos-retstr_len);
  513. strncpy(buf+curpos, ret, retstr_len);
  514. maxpos+=retstr_len;
  515. showbuf(1);
  516. curright(retstr_len);
  517. }
  518. }
  519. }
  520. }
  521. break;
  522. default:
  523. {
  524. if(maxpos==maxl) buz();
  525. else
  526. {
  527. memmove(buf+curpos+1, buf+curpos, maxl-curpos-1);
  528. buf[curpos]=i;
  529. maxpos++;
  530. showbuf(1);
  531. curright(1);
  532. }
  533. }
  534. break;
  535. }
  536. }
  537. }
  538. char *
  539. rl_getline(void)
  540. {
  541. char *p;
  542. char *command;
  543. while(1)
  544. {
  545. if(notty)
  546. {
  547. printf("%s", prompt);
  548. if(!(command=fgets(buf, maxl, stdin))) return NULL;
  549. command[strlen(command)-1]='\0';
  550. }
  551. else if(!(command=getline())) return NULL;
  552. for(p=command;*p==' '||*p=='\t';p++);
  553. /* if not empty, pass it thought history */
  554. if(*p && (command=hist_cmd(command)))
  555. {
  556. /* hist_cmd() return a pointer to the actual history
  557. entry, so make a copy to buf and return to user */
  558. strcpy(buf,command);
  559. return buf;
  560. }
  561. }
  562. }
  563. /* register function which complete line */
  564. void
  565. rl_register_complete_func(rl_complete_func_t complete_func)
  566. {
  567. rl_complete_func = complete_func;
  568. }
  569. #ifdef MAIN
  570. main()
  571. {
  572. if(!rl_init(">> ", 1024, 100)) exit(0);
  573. printf("notty=%d\n", notty);
  574. while(1) printf("command=(%s)\n", rl_getline());
  575. }
  576. #endif