tinyrl.c 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061
  1. /*
  2. * tinyrl.c
  3. */
  4. #include <assert.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <ctype.h>
  9. #include <errno.h>
  10. #include <unistd.h>
  11. #include <faux/faux.h>
  12. #include <faux/str.h>
  13. #include "private.h"
  14. #define LINE_CHUNK 80
  15. tinyrl_t *tinyrl_new(FILE *istream, FILE *ostream,
  16. const char *hist_fname, size_t hist_stifle)
  17. {
  18. tinyrl_t *tinyrl = NULL;
  19. int i = 0;
  20. tinyrl = faux_zmalloc(sizeof(tinyrl_t));
  21. if (!tinyrl)
  22. return NULL;
  23. // Line
  24. faux_bzero(&tinyrl->line, sizeof(tinyrl->line));
  25. tinyrl_line_extend(tinyrl, LINE_CHUNK);
  26. // Last line
  27. tinyrl_reset_line_state(tinyrl);
  28. // Input processing vars
  29. tinyrl->utf8_cont = 0;
  30. tinyrl->esc_cont = BOOL_FALSE;
  31. tinyrl->esc_seq[0] = '\0';
  32. tinyrl->esc_p = tinyrl->esc_seq;
  33. // Prompt
  34. tinyrl_set_prompt(tinyrl, "> ");
  35. // Key handlers
  36. for (i = 0; i < NUM_HANDLERS; i++) {
  37. tinyrl->handlers[i] = tinyrl_key_default;
  38. }
  39. tinyrl->handlers[KEY_CR] = tinyrl_key_crlf;
  40. tinyrl->handlers[KEY_LF] = tinyrl_key_crlf;
  41. tinyrl->handlers[KEY_ETX] = tinyrl_key_interrupt;
  42. tinyrl->handlers[KEY_DEL] = tinyrl_key_backspace;
  43. tinyrl->handlers[KEY_BS] = tinyrl_key_backspace;
  44. tinyrl->handlers[KEY_EOT] = tinyrl_key_delete;
  45. tinyrl->handlers[KEY_FF] = tinyrl_key_clear_screen;
  46. tinyrl->handlers[KEY_NAK] = tinyrl_key_erase_line;
  47. tinyrl->handlers[KEY_SOH] = tinyrl_key_start_of_line;
  48. tinyrl->handlers[KEY_ENQ] = tinyrl_key_end_of_line;
  49. tinyrl->handlers[KEY_VT] = tinyrl_key_kill;
  50. tinyrl->handlers[KEY_EM] = tinyrl_key_yank;
  51. tinyrl->handlers[KEY_HT] = tinyrl_key_tab;
  52. tinyrl->handlers[KEY_ETB] = tinyrl_key_backword;
  53. tinyrl->max_line_length = 0;
  54. tinyrl->buffer = NULL;
  55. tinyrl->buffer_size = 0;
  56. tinyrl->done = BOOL_FALSE;
  57. tinyrl->completion_over = BOOL_FALSE;
  58. tinyrl->attempted_completion_function = NULL;
  59. tinyrl->hotkey_fn = NULL;
  60. tinyrl->state = 0;
  61. tinyrl->kill_string = NULL;
  62. tinyrl->echo_char = '\0';
  63. tinyrl->echo_enabled = BOOL_TRUE;
  64. tinyrl->utf8 = BOOL_TRUE;
  65. tinyrl->busy = BOOL_FALSE;
  66. // VT100 terminal
  67. tinyrl->term = vt100_new(istream, ostream);
  68. // To save terminal settings
  69. tinyrl_set_istream(tinyrl, istream);
  70. tinyrl->width = vt100_width(tinyrl->term);
  71. // History object
  72. tinyrl->hist = hist_new(hist_fname, hist_stifle);
  73. tinyrl_hist_restore(tinyrl);
  74. tty_raw_mode(tinyrl);
  75. return tinyrl;
  76. }
  77. void tinyrl_free(tinyrl_t *tinyrl)
  78. {
  79. assert(tinyrl);
  80. if (!tinyrl)
  81. return;
  82. tty_restore_mode(tinyrl);
  83. hist_free(tinyrl->hist);
  84. vt100_free(tinyrl->term);
  85. faux_str_free(tinyrl->prompt);
  86. tinyrl_reset_line_state(tinyrl);
  87. // faux_str_free(tinyrl->kill_string);
  88. faux_free(tinyrl);
  89. }
  90. void tty_raw_mode(tinyrl_t *tinyrl)
  91. {
  92. struct termios new_termios = {};
  93. FILE *istream = NULL;
  94. int fd = -1;
  95. if (!tinyrl)
  96. return;
  97. istream = vt100_istream(tinyrl->term);
  98. if (!istream)
  99. return;
  100. fd = fileno(istream);
  101. if (tcgetattr(fd, &new_termios) < 0)
  102. return;
  103. new_termios.c_iflag = 0;
  104. new_termios.c_oflag = OPOST | ONLCR;
  105. new_termios.c_lflag = 0;
  106. new_termios.c_cc[VMIN] = 1;
  107. new_termios.c_cc[VTIME] = 0;
  108. // Mode switch
  109. tcsetattr(fd, TCSADRAIN, &new_termios);
  110. }
  111. void tty_restore_mode(tinyrl_t *tinyrl)
  112. {
  113. FILE *istream = NULL;
  114. int fd = -1;
  115. istream = vt100_istream(tinyrl->term);
  116. if (!istream)
  117. return;
  118. fd = fileno(istream);
  119. // Do the mode switch
  120. tcsetattr(fd, TCSADRAIN, &tinyrl->default_termios);
  121. }
  122. bool_t tinyrl_bind_key(tinyrl_t *tinyrl, int key, tinyrl_key_func_t *fn)
  123. {
  124. assert(tinyrl);
  125. if (!tinyrl)
  126. return BOOL_FALSE;
  127. if ((key < 0) || (key > 255))
  128. return BOOL_FALSE;
  129. tinyrl->handlers[key] = fn;
  130. return BOOL_TRUE;
  131. }
  132. void tinyrl_set_hotkey_fn(tinyrl_t *tinyrl, tinyrl_key_func_t *fn)
  133. {
  134. tinyrl->hotkey_fn = fn;
  135. }
  136. void tinyrl_set_istream(tinyrl_t *tinyrl, FILE *istream)
  137. {
  138. assert(tinyrl);
  139. if (!tinyrl)
  140. return;
  141. vt100_set_istream(tinyrl->term, istream);
  142. // Save terminal settings to restore on exit
  143. if (istream)
  144. tcgetattr(fileno(istream), &tinyrl->default_termios);
  145. }
  146. FILE *tinyrl_istream(const tinyrl_t *tinyrl)
  147. {
  148. return vt100_istream(tinyrl->term);
  149. }
  150. void tinyrl_set_ostream(tinyrl_t *tinyrl, FILE *ostream)
  151. {
  152. assert(tinyrl);
  153. if (!tinyrl)
  154. return;
  155. vt100_set_ostream(tinyrl->term, ostream);
  156. }
  157. FILE *tinyrl_ostream(const tinyrl_t *tinyrl)
  158. {
  159. return vt100_ostream(tinyrl->term);
  160. }
  161. bool_t tinyrl_utf8(const tinyrl_t *tinyrl)
  162. {
  163. assert(tinyrl);
  164. if (!tinyrl)
  165. return BOOL_TRUE;
  166. return tinyrl->utf8;
  167. }
  168. void tinyrl_set_utf8(tinyrl_t *tinyrl, bool_t utf8)
  169. {
  170. assert(tinyrl);
  171. if (!tinyrl)
  172. return;
  173. tinyrl->utf8 = utf8;
  174. }
  175. bool_t tinyrl_busy(const tinyrl_t *tinyrl)
  176. {
  177. assert(tinyrl);
  178. if (!tinyrl)
  179. return BOOL_FALSE;
  180. return tinyrl->busy;
  181. }
  182. void tinyrl_set_busy(tinyrl_t *tinyrl, bool_t busy)
  183. {
  184. assert(tinyrl);
  185. if (!tinyrl)
  186. return;
  187. tinyrl->busy = busy;
  188. }
  189. const char *tinyrl_prompt(const tinyrl_t *tinyrl)
  190. {
  191. assert(tinyrl);
  192. if (!tinyrl)
  193. return NULL;
  194. return tinyrl->prompt;
  195. }
  196. void tinyrl_set_prompt(tinyrl_t *tinyrl, const char *prompt)
  197. {
  198. assert(tinyrl);
  199. if (!tinyrl)
  200. return;
  201. if (tinyrl->prompt)
  202. faux_str_free(tinyrl->prompt);
  203. tinyrl->prompt = faux_str_dup(prompt);
  204. tinyrl->prompt_len = strlen(tinyrl->prompt);
  205. tinyrl->prompt_chars = utf8_nsyms(tinyrl->prompt, tinyrl->prompt_len);
  206. }
  207. void *tinyrl_udata(const tinyrl_t *tinyrl)
  208. {
  209. assert(tinyrl);
  210. if (!tinyrl)
  211. return NULL;
  212. return tinyrl->udata;
  213. }
  214. void tinyrl_set_udata(tinyrl_t *tinyrl, void *udata)
  215. {
  216. assert(tinyrl);
  217. if (!tinyrl)
  218. return;
  219. tinyrl->udata = udata;
  220. }
  221. const char *tinyrl_line(const tinyrl_t *tinyrl)
  222. {
  223. return tinyrl->line.str;
  224. }
  225. bool_t tinyrl_hist_save(const tinyrl_t *tinyrl)
  226. {
  227. assert(tinyrl);
  228. if (!tinyrl)
  229. return BOOL_FALSE;
  230. return hist_save(tinyrl->hist);
  231. }
  232. bool_t tinyrl_hist_restore(tinyrl_t *tinyrl)
  233. {
  234. assert(tinyrl);
  235. if (!tinyrl)
  236. return BOOL_FALSE;
  237. return hist_restore(tinyrl->hist);
  238. }
  239. static bool_t process_char(tinyrl_t *tinyrl, char key)
  240. {
  241. // Begin of ESC sequence
  242. if (!tinyrl->esc_cont && (KEY_ESC == key)) {
  243. tinyrl->esc_cont = BOOL_TRUE; // Start ESC sequence
  244. tinyrl->esc_p = tinyrl->esc_seq;
  245. // Note: Don't put ESC symbol itself to buffer
  246. return BOOL_TRUE;
  247. }
  248. // Continue ESC sequence
  249. if (tinyrl->esc_cont) {
  250. // Broken sequence. Too long
  251. if ((tinyrl->esc_p - tinyrl->esc_seq) >= (sizeof(tinyrl->esc_seq) - 1)) {
  252. tinyrl->esc_cont = BOOL_FALSE;
  253. return BOOL_FALSE;
  254. }
  255. // Save the curren char to sequence buffer
  256. *tinyrl->esc_p = key;
  257. tinyrl->esc_p++;
  258. // ANSI standard control sequences will end
  259. // with a character between 64 - 126
  260. if ((key != '[') && (key > 63)) {
  261. *tinyrl->esc_p = '\0';
  262. tinyrl_esc_seq(tinyrl, tinyrl->esc_seq);
  263. tinyrl->esc_cont = BOOL_FALSE;
  264. //tinyrl_redisplay(tinyrl);
  265. }
  266. return BOOL_TRUE;
  267. }
  268. // Call the handler for key
  269. // Handler (that has no special meaning) will put new char to line buffer
  270. if (!tinyrl->handlers[(unsigned char)key](tinyrl, key))
  271. vt100_ding(tinyrl->term);
  272. // if (tinyrl->done) // Some handler set the done flag
  273. // continue; // It will break the loop
  274. if (tinyrl->utf8) {
  275. // ASCII char (one byte)
  276. if (!(UTF8_7BIT_MASK & key)) {
  277. tinyrl->utf8_cont = 0;
  278. // First byte of multibyte symbol
  279. } else if (UTF8_11 == (key & UTF8_MASK)) {
  280. // Find out number of symbol's bytes
  281. unsigned int b = (unsigned int)key;
  282. tinyrl->utf8_cont = 0;
  283. while ((tinyrl->utf8_cont < 6) && (UTF8_10 != (b & UTF8_MASK))) {
  284. tinyrl->utf8_cont++;
  285. b = b << 1;
  286. }
  287. // Continue of multibyte symbol
  288. } else if ((tinyrl->utf8_cont > 0) && (UTF8_10 == (key & UTF8_MASK))) {
  289. tinyrl->utf8_cont--;
  290. }
  291. }
  292. // For non UTF-8 encoding the utf8_cont is always 0.
  293. // For UTF-8 it's 0 when one-byte symbol or we get
  294. // all bytes for the current multibyte character
  295. // if (!tinyrl->utf8_cont) {
  296. // //tinyrl_redisplay(tinyrl);
  297. // printf("%s\n", tinyrl->line.str);
  298. // }
  299. return BOOL_TRUE;
  300. }
  301. int tinyrl_read(tinyrl_t *tinyrl)
  302. {
  303. int rc = 0;
  304. unsigned char key = 0;
  305. int count = 0;
  306. assert(tinyrl);
  307. tinyrl_set_busy(tinyrl, BOOL_FALSE);
  308. while ((rc = vt100_getchar(tinyrl->term, &key)) > 0) {
  309. count++;
  310. process_char(tinyrl, key);
  311. // Some commands can't be processed immediately by handlers and
  312. // need some network exchange for example. In this case we will
  313. // not execute redisplay() here.
  314. if (!tinyrl->utf8_cont && !tinyrl_busy(tinyrl)) {
  315. tinyrl_redisplay(tinyrl);
  316. // printf("%s\n", tinyrl->line.str);
  317. }
  318. //printf("key=%u, pos=%lu, len=%lu\n", key, tinyrl->line.pos, tinyrl->line.len);
  319. }
  320. if ((rc < 0) && (EAGAIN == errno))
  321. return count;
  322. return rc;
  323. }
  324. /*
  325. * Ensure that buffer has enough space to hold len characters,
  326. * possibly reallocating it if necessary. The function returns BOOL_TRUE
  327. * if the line is successfully extended, BOOL_FALSE if not.
  328. */
  329. bool_t tinyrl_line_extend(tinyrl_t *tinyrl, size_t len)
  330. {
  331. char *new_buf = NULL;
  332. size_t new_size = 0;
  333. size_t chunk_num = 0;
  334. if (tinyrl->line.len >= len)
  335. return BOOL_TRUE;
  336. chunk_num = len / LINE_CHUNK;
  337. if ((len % LINE_CHUNK) > 0)
  338. chunk_num++;
  339. new_size = chunk_num * LINE_CHUNK;
  340. // First initialization
  341. if (tinyrl->line.str == NULL) {
  342. tinyrl->line.str = faux_zmalloc(new_size);
  343. if (!tinyrl->line.str)
  344. return BOOL_FALSE;
  345. tinyrl->line.size = new_size;
  346. return BOOL_TRUE;
  347. }
  348. new_buf = realloc(tinyrl->line.str, new_size);
  349. if (!new_buf)
  350. return BOOL_FALSE;
  351. tinyrl->line.str = new_buf;
  352. tinyrl->line.size = new_size;
  353. return BOOL_TRUE;
  354. }
  355. bool_t tinyrl_esc_seq(tinyrl_t *tinyrl, const char *esc_seq)
  356. {
  357. bool_t result = BOOL_FALSE;
  358. switch (vt100_esc_decode(tinyrl->term, esc_seq)) {
  359. case VT100_CURSOR_UP:
  360. result = tinyrl_key_up(tinyrl, 0);
  361. break;
  362. case VT100_CURSOR_DOWN:
  363. result = tinyrl_key_down(tinyrl, 0);
  364. break;
  365. case VT100_CURSOR_LEFT:
  366. result = tinyrl_key_left(tinyrl, 0);
  367. break;
  368. case VT100_CURSOR_RIGHT:
  369. result = tinyrl_key_right(tinyrl, 0);
  370. break;
  371. case VT100_HOME:
  372. result = tinyrl_key_start_of_line(tinyrl, 0);
  373. break;
  374. case VT100_END:
  375. result = tinyrl_key_end_of_line(tinyrl, 0);
  376. break;
  377. case VT100_DELETE:
  378. result = tinyrl_key_delete(tinyrl, 0);
  379. break;
  380. case VT100_INSERT:
  381. case VT100_PGDOWN:
  382. case VT100_PGUP:
  383. case VT100_UNKNOWN:
  384. break;
  385. }
  386. return result;
  387. }
  388. bool_t tinyrl_line_insert(tinyrl_t *tinyrl, const char *text, size_t len)
  389. {
  390. size_t new_size = tinyrl->line.len + len + 1;
  391. if (len == 0)
  392. return BOOL_TRUE;
  393. tinyrl_line_extend(tinyrl, new_size);
  394. if (tinyrl->line.pos < tinyrl->line.len) {
  395. memmove(tinyrl->line.str + tinyrl->line.pos + len,
  396. tinyrl->line.str + tinyrl->line.pos,
  397. tinyrl->line.len - tinyrl->line.pos);
  398. }
  399. memcpy(tinyrl->line.str + tinyrl->line.pos, text, len);
  400. tinyrl->line.pos += len;
  401. tinyrl->line.len += len;
  402. tinyrl->line.str[tinyrl->line.len] = '\0';
  403. return BOOL_TRUE;
  404. }
  405. bool_t tinyrl_line_delete(tinyrl_t *tinyrl, off_t start, size_t len)
  406. {
  407. if (start >= tinyrl->line.len)
  408. return BOOL_TRUE;
  409. if ((start + len) >= tinyrl->line.len) {
  410. tinyrl->line.len = start;
  411. } else {
  412. memmove(tinyrl->line.str + start,
  413. tinyrl->line.str + start + len,
  414. tinyrl->line.len - (start + len));
  415. tinyrl->line.len -= len;
  416. }
  417. tinyrl->line.pos = start;
  418. tinyrl->line.str[tinyrl->line.len] = '\0';
  419. return BOOL_TRUE;
  420. }
  421. static void move_cursor(const tinyrl_t *tinyrl, size_t cur_pos, size_t target_pos)
  422. {
  423. int rows = 0;
  424. int cols = 0;
  425. // Note: The '/' is not real math division. It's integer part of division
  426. // so we need separate division for two values.
  427. rows = (target_pos / tinyrl->width) - (cur_pos / tinyrl->width);
  428. cols = (target_pos % tinyrl->width) - (cur_pos % tinyrl->width);
  429. if (cols > 0)
  430. vt100_cursor_forward(tinyrl->term, cols);
  431. else if (cols < 0)
  432. vt100_cursor_back(tinyrl->term, -cols);
  433. if (rows > 0)
  434. vt100_cursor_down(tinyrl->term, rows);
  435. else if (rows < 0)
  436. vt100_cursor_up(tinyrl->term, -rows);
  437. }
  438. static size_t str_equal_part(const tinyrl_t *tinyrl,
  439. const char *s1, const char *s2)
  440. {
  441. const char *str1 = s1;
  442. const char *str2 = s2;
  443. if (!str1 || !str2)
  444. return 0;
  445. while (*str1 && *str2) {
  446. if (*str1 != *str2)
  447. break;
  448. str1++;
  449. str2++;
  450. }
  451. if (!tinyrl->utf8)
  452. return str1 - s1;
  453. // UTF8
  454. // If UTF8_10 byte (intermediate byte of UTF-8 sequence) is not equal
  455. // then we need to find starting of this UTF-8 character because whole
  456. // UTF-8 character is not equal.
  457. if (UTF8_10 == (*str1 & UTF8_MASK)) {
  458. // Skip intermediate bytes
  459. while ((str1 > s1) && (UTF8_10 == (*str1 & UTF8_MASK)))
  460. str1--;
  461. }
  462. return str1 - s1;
  463. }
  464. void tinyrl_save_last(tinyrl_t *tinyrl)
  465. {
  466. faux_str_free(tinyrl->last.str);
  467. tinyrl->last = tinyrl->line;
  468. tinyrl->last.str = faux_str_dup(tinyrl->line.str);
  469. }
  470. void tinyrl_reset_line_state(tinyrl_t *tinyrl)
  471. {
  472. faux_str_free(tinyrl->last.str);
  473. faux_bzero(&tinyrl->last, sizeof(tinyrl->last));
  474. }
  475. void tinyrl_reset_line(tinyrl_t *tinyrl)
  476. {
  477. tinyrl_line_delete(tinyrl, 0, tinyrl->line.len);
  478. }
  479. void tinyrl_redisplay(tinyrl_t *tinyrl)
  480. {
  481. size_t width = vt100_width(tinyrl->term);
  482. // unsigned int line_size = strlen(tinyrl->line);
  483. unsigned int line_chars = utf8_nsyms(tinyrl->line.str, tinyrl->line.len);
  484. size_t cols = 0;
  485. size_t eq_bytes = 0;
  486. // Prepare print position
  487. if (tinyrl->last.str && (width == tinyrl->width)) {
  488. size_t eq_chars = 0; // Printable symbols
  489. size_t last_pos_chars = 0;
  490. // If line and last line have the equal chars at begining
  491. eq_bytes = str_equal_part(tinyrl, tinyrl->line.str, tinyrl->last.str);
  492. eq_chars = utf8_nsyms(tinyrl->last.str, eq_bytes);
  493. last_pos_chars = utf8_nsyms(tinyrl->last.str, tinyrl->last.pos);
  494. move_cursor(tinyrl, tinyrl->prompt_chars + last_pos_chars,
  495. tinyrl->prompt_chars + eq_chars);
  496. } else {
  497. // Prepare to resize
  498. if (width != tinyrl->width) {
  499. vt100_next_line(tinyrl->term);
  500. vt100_erase_down(tinyrl->term);
  501. }
  502. vt100_printf(tinyrl->term, "%s", tinyrl->prompt);
  503. }
  504. // Print current line
  505. vt100_printf(tinyrl->term, "%s", tinyrl->line.str + eq_bytes);
  506. cols = (tinyrl->prompt_chars + line_chars) % width;
  507. if ((cols == 0) && (tinyrl->line.len - eq_bytes))
  508. vt100_next_line(tinyrl->term);
  509. // Erase down if current line is shorter than previous one
  510. if (tinyrl->last.len > tinyrl->line.len)
  511. vt100_erase_down(tinyrl->term);
  512. // Move the cursor to the insertion point
  513. if (tinyrl->line.pos < tinyrl->line.len) {
  514. size_t pos_chars = utf8_nsyms(tinyrl->line.str, tinyrl->line.pos);
  515. move_cursor(tinyrl, tinyrl->prompt_chars + line_chars,
  516. tinyrl->prompt_chars + pos_chars);
  517. }
  518. // Update the display
  519. vt100_oflush(tinyrl->term);
  520. // Save the last line buffer
  521. tinyrl_save_last(tinyrl);
  522. tinyrl->width = width;
  523. }
  524. void tinyrl_crlf(const tinyrl_t *tinyrl)
  525. {
  526. vt100_printf(tinyrl->term, "\n");
  527. }
  528. // Jump to first free line after current multiline input
  529. void tinyrl_multi_crlf(const tinyrl_t *tinyrl)
  530. {
  531. size_t full_chars = utf8_nsyms(tinyrl->last.str, tinyrl->last.len);
  532. size_t pos_chars = utf8_nsyms(tinyrl->last.str, tinyrl->last.pos);
  533. move_cursor(tinyrl, tinyrl->prompt_chars + pos_chars,
  534. tinyrl->prompt_chars + full_chars);
  535. tinyrl_crlf(tinyrl);
  536. vt100_oflush(tinyrl->term);
  537. }
  538. #if 0
  539. /*----------------------------------------------------------------------- */
  540. /*
  541. tinyrl is called whenever a line is edited in any way.
  542. It signals that if we are currently viewing a history line we should transfer it
  543. to the current buffer
  544. */
  545. static void changed_line(tinyrl_t * tinyrl)
  546. {
  547. /* if the current line is not our buffer then make it so */
  548. if (tinyrl->line != tinyrl->buffer) {
  549. /* replace the current buffer with the new details */
  550. free(tinyrl->buffer);
  551. tinyrl->line = tinyrl->buffer = lub_string_dup(tinyrl->line);
  552. tinyrl->buffer_size = strlen(tinyrl->buffer);
  553. assert(tinyrl->line);
  554. }
  555. }
  556. /*----------------------------------------------------------------------- */
  557. /*
  558. * A convenience function for displaying a list of strings in columnar
  559. * format on Readline's output stream. matches is the list of strings,
  560. * in argv format, such as a list of completion matches. len is the number
  561. * of strings in matches, and max is the length of the longest string in matches.
  562. * tinyrl function uses the setting of print-completions-horizontally to select
  563. * how the matches are displayed
  564. */
  565. void tinyrl_display_matches(const tinyrl_t *tinyrl,
  566. char *const *matches, unsigned int len, size_t max)
  567. {
  568. unsigned int width = tinyrl_vt100__get_width(tinyrl->term);
  569. unsigned int cols, rows;
  570. /* Find out column and rows number */
  571. if (max < width)
  572. cols = (width + 1) / (max + 1); /* allow for a space between words */
  573. else
  574. cols = 1;
  575. rows = len / cols + 1;
  576. assert(matches);
  577. if (matches) {
  578. unsigned int r, c;
  579. len--, matches++; /* skip the subtitution string */
  580. /* Print out a table of completions */
  581. for (r = 0; r < rows && len; r++) {
  582. for (c = 0; c < cols && len; c++) {
  583. const char *match = *matches++;
  584. len--;
  585. if ((c + 1) == cols) /* Last str in row */
  586. tinyrl_vt100_printf(tinyrl->term, "%s",
  587. match);
  588. else
  589. tinyrl_vt100_printf(tinyrl->term, "%-*s ",
  590. max, match);
  591. }
  592. tinyrl_crlf(tinyrl);
  593. }
  594. }
  595. }
  596. /*-------------------------------------------------------- */
  597. /*
  598. * Returns an array of strings which is a list of completions for text.
  599. * If there are no completions, returns NULL. The first entry in the
  600. * returned array is the substitution for text. The remaining entries
  601. * are the possible completions. The array is terminated with a NULL pointer.
  602. *
  603. * entry_func is a function of two args, and returns a char *.
  604. * The first argument is text. The second is a state argument;
  605. * it is zero on the first call, and non-zero on subsequent calls.
  606. * entry_func returns a NULL pointer to the caller when there are no
  607. * more matches.
  608. */
  609. char **tinyrl_completion(tinyrl_t * tinyrl,
  610. const char *line, unsigned int start, unsigned int end,
  611. tinyrl_compentry_func_t * entry_func)
  612. {
  613. unsigned int state = 0;
  614. size_t size = 1;
  615. unsigned int offset = 1; /* Need at least one entry for the substitution */
  616. char **matches = NULL;
  617. char *match;
  618. /* duplicate the string upto the insertion point */
  619. char *text = lub_string_dupn(line, end);
  620. /* now try and find possible completions */
  621. while ((match = entry_func(tinyrl, text, start, state++))) {
  622. if (size == offset) {
  623. /* resize the buffer if needed - the +1 is for the NULL terminator */
  624. size += 10;
  625. matches =
  626. realloc(matches, (sizeof(char *) * (size + 1)));
  627. }
  628. /* not much we can do... */
  629. if (!matches)
  630. break;
  631. matches[offset] = match;
  632. /*
  633. * augment the substitute string with tinyrl entry
  634. */
  635. if (1 == offset) {
  636. /* let's be optimistic */
  637. matches[0] = lub_string_dup(match);
  638. } else {
  639. char *p = matches[0];
  640. size_t match_len = strlen(p);
  641. /* identify the common prefix */
  642. while ((tolower(*p) == tolower(*match)) && match_len--) {
  643. p++, match++;
  644. }
  645. /* terminate the prefix string */
  646. *p = '\0';
  647. }
  648. offset++;
  649. }
  650. /* be a good memory citizen */
  651. lub_string_free(text);
  652. if (matches)
  653. matches[offset] = NULL;
  654. return matches;
  655. }
  656. /*-------------------------------------------------------- */
  657. void tinyrl_delete_matches(char **tinyrl)
  658. {
  659. char **matches = tinyrl;
  660. while (*matches) {
  661. /* release the memory for each contained string */
  662. free(*matches++);
  663. }
  664. /* release the memory for the array */
  665. free(tinyrl);
  666. }
  667. /*-------------------------------------------------------- */
  668. /*-------------------------------------------------------- */
  669. /*
  670. * Ring the terminal bell, obeying the setting of bell-style.
  671. */
  672. void tinyrl_ding(const tinyrl_t * tinyrl)
  673. {
  674. tinyrl_vt100_ding(tinyrl->term);
  675. }
  676. /*-------------------------------------------------------- */
  677. void tinyrl_reset_line_state(tinyrl_t * tinyrl)
  678. {
  679. lub_string_free(tinyrl->last_buffer);
  680. tinyrl->last_buffer = NULL;
  681. tinyrl->last_line_size = 0;
  682. tinyrl_redisplay(tinyrl);
  683. }
  684. /*-------------------------------------------------------- */
  685. void tinyrl_replace_line(tinyrl_t * tinyrl, const char *text, int clear_undo)
  686. {
  687. size_t new_len = strlen(text);
  688. /* ignored for now */
  689. clear_undo = clear_undo;
  690. /* ensure there is sufficient space */
  691. if (tinyrl_extend_line_buffer(tinyrl, new_len)) {
  692. /* overwrite the current contents of the buffer */
  693. strcpy(tinyrl->buffer, text);
  694. /* set the insert point and end point */
  695. tinyrl->point = tinyrl->end = new_len;
  696. }
  697. tinyrl_redisplay(tinyrl);
  698. }
  699. /*-------------------------------------------------------- */
  700. static tinyrl_match_e
  701. tinyrl_do_complete(tinyrl_t * tinyrl, bool_t with_extensions)
  702. {
  703. tinyrl_match_e result = TINYRL_NO_MATCH;
  704. char **matches = NULL;
  705. unsigned int start, end;
  706. bool_t completion = BOOL_FALSE;
  707. bool_t prefix = BOOL_FALSE;
  708. int i = 0;
  709. /* find the start and end of the current word */
  710. start = end = tinyrl->point;
  711. while (start && !isspace(tinyrl->line[start - 1]))
  712. start--;
  713. if (tinyrl->attempted_completion_function) {
  714. tinyrl->completion_over = BOOL_FALSE;
  715. tinyrl->completion_error_over = BOOL_FALSE;
  716. /* try and complete the current line buffer */
  717. matches = tinyrl->attempted_completion_function(tinyrl,
  718. tinyrl->line, start, end);
  719. }
  720. if (!matches && (BOOL_FALSE == tinyrl->completion_over)) {
  721. /* insert default completion call here... */
  722. }
  723. if (!matches)
  724. return result;
  725. /* identify and insert a common prefix if there is one */
  726. if (0 != strncmp(matches[0], &tinyrl->line[start],
  727. strlen(matches[0]))) {
  728. /*
  729. * delete the original text not including
  730. * the current insertion point character
  731. */
  732. if (tinyrl->end != end)
  733. end--;
  734. tinyrl_delete_text(tinyrl, start, end);
  735. if (BOOL_FALSE == tinyrl_insert_text(tinyrl, matches[0]))
  736. return TINYRL_NO_MATCH;
  737. completion = BOOL_TRUE;
  738. }
  739. for (i = 1; matches[i]; i++) {
  740. /* tinyrl is just a prefix string */
  741. if (0 == lub_string_nocasecmp(matches[0], matches[i]))
  742. prefix = BOOL_TRUE;
  743. }
  744. /* is there more than one completion? */
  745. if (matches[2]) {
  746. char **tmp = matches;
  747. unsigned int max, len;
  748. max = len = 0;
  749. while (*tmp) {
  750. size_t size = strlen(*tmp++);
  751. len++;
  752. if (size > max)
  753. max = size;
  754. }
  755. if (completion)
  756. result = TINYRL_COMPLETED_AMBIGUOUS;
  757. else if (prefix)
  758. result = TINYRL_MATCH_WITH_EXTENSIONS;
  759. else
  760. result = TINYRL_AMBIGUOUS;
  761. if (with_extensions || !prefix) {
  762. /* Either we always want to show extensions or
  763. * we haven't been able to complete the current line
  764. * and there is just a prefix, so let the user see the options
  765. */
  766. tinyrl_crlf(tinyrl);
  767. tinyrl_display_matches(tinyrl, matches, len, max);
  768. tinyrl_reset_line_state(tinyrl);
  769. }
  770. } else {
  771. result = completion ?
  772. TINYRL_COMPLETED_MATCH : TINYRL_MATCH;
  773. }
  774. /* free the memory */
  775. tinyrl_delete_matches(matches);
  776. /* redisplay the line */
  777. tinyrl_redisplay(tinyrl);
  778. return result;
  779. }
  780. /*-------------------------------------------------------- */
  781. tinyrl_match_e tinyrl_complete_with_extensions(tinyrl_t * tinyrl)
  782. {
  783. return tinyrl_do_complete(tinyrl, BOOL_TRUE);
  784. }
  785. /*-------------------------------------------------------- */
  786. tinyrl_match_e tinyrl_complete(tinyrl_t * tinyrl)
  787. {
  788. return tinyrl_do_complete(tinyrl, BOOL_FALSE);
  789. }
  790. /*-------------------------------------------------------- */
  791. void *tinyrl__get_context(const tinyrl_t * tinyrl)
  792. {
  793. return tinyrl->context;
  794. }
  795. /*--------------------------------------------------------- */
  796. const char *tinyrl__get_line(const tinyrl_t * tinyrl)
  797. {
  798. return tinyrl->line;
  799. }
  800. /*--------------------------------------------------------- */
  801. tinyrl_history_t *tinyrl__get_history(const tinyrl_t * tinyrl)
  802. {
  803. return tinyrl->history;
  804. }
  805. /*--------------------------------------------------------- */
  806. void tinyrl_completion_over(tinyrl_t * tinyrl)
  807. {
  808. tinyrl->completion_over = BOOL_TRUE;
  809. }
  810. /*--------------------------------------------------------- */
  811. void tinyrl_completion_error_over(tinyrl_t * tinyrl)
  812. {
  813. tinyrl->completion_error_over = BOOL_TRUE;
  814. }
  815. /*--------------------------------------------------------- */
  816. bool_t tinyrl_is_completion_error_over(const tinyrl_t * tinyrl)
  817. {
  818. return tinyrl->completion_error_over;
  819. }
  820. /*--------------------------------------------------------- */
  821. void tinyrl_done(tinyrl_t * tinyrl)
  822. {
  823. tinyrl->done = BOOL_TRUE;
  824. }
  825. /*--------------------------------------------------------- */
  826. void tinyrl_enable_echo(tinyrl_t * tinyrl)
  827. {
  828. tinyrl->echo_enabled = BOOL_TRUE;
  829. }
  830. /*--------------------------------------------------------- */
  831. void tinyrl_disable_echo(tinyrl_t * tinyrl, char echo_char)
  832. {
  833. tinyrl->echo_enabled = BOOL_FALSE;
  834. tinyrl->echo_char = echo_char;
  835. }
  836. /*-------------------------------------------------------- */
  837. bool_t tinyrl_is_quoting(const tinyrl_t * tinyrl)
  838. {
  839. bool_t result = BOOL_FALSE;
  840. /* count the quotes upto the current insertion point */
  841. unsigned int i = 0;
  842. while (i < tinyrl->point) {
  843. if (result && (tinyrl->line[i] == '\\')) {
  844. i++;
  845. if (i >= tinyrl->point)
  846. break;
  847. i++;
  848. continue;
  849. }
  850. if (tinyrl->line[i++] == '"') {
  851. result = result ? BOOL_FALSE : BOOL_TRUE;
  852. }
  853. }
  854. return result;
  855. }
  856. /*-------------------------------------------------------- */
  857. bool_t tinyrl_is_empty(const tinyrl_t *tinyrl)
  858. {
  859. return (tinyrl->point == 0) ? BOOL_TRUE : BOOL_FALSE;
  860. }
  861. /*--------------------------------------------------------- */
  862. void tinyrl_limit_line_length(tinyrl_t * tinyrl, unsigned int length)
  863. {
  864. tinyrl->max_line_length = length;
  865. }
  866. #endif