example.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <errno.h>
  5. #include <stdbool.h>
  6. #include <termios.h>
  7. #include <unistd.h>
  8. #include <fcntl.h>
  9. #include <assert.h>
  10. #include "chry_readline.h"
  11. struct termios orig_termios;
  12. static void disableRawMode(int fd)
  13. {
  14. tcsetattr(fd, TCSAFLUSH, &orig_termios);
  15. }
  16. static void AtExit(void)
  17. {
  18. disableRawMode(STDIN_FILENO);
  19. }
  20. /* Raw mode: 1960 magic shit. */
  21. static int enableRawMode(int fd)
  22. {
  23. struct termios raw;
  24. if (!isatty(STDIN_FILENO))
  25. goto fatal;
  26. atexit(AtExit);
  27. if (tcgetattr(fd, &orig_termios) == -1)
  28. goto fatal;
  29. raw = orig_termios; /* modify the original mode */
  30. /* input modes: no break, no CR to NL, no parity check, no strip char,
  31. * no start/stop output control. */
  32. raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  33. /* output modes - disable post processing */
  34. raw.c_oflag &= ~(OPOST);
  35. /* control modes - set 8 bit chars */
  36. raw.c_cflag |= (CS8);
  37. /* local modes - choing off, canonical off, no extended functions,
  38. * no signal chars (^Z,^C) */
  39. raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
  40. /* control chars - set return condition: min number of bytes and timer.
  41. * We want read to return every single byte, without timeout. */
  42. raw.c_cc[VMIN] = 1;
  43. raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
  44. /* put terminal in raw mode after flushing */
  45. if (tcsetattr(fd, TCSAFLUSH, &raw) < 0)
  46. goto fatal;
  47. return 0;
  48. fatal:
  49. errno = ENOTTY;
  50. return -1;
  51. }
  52. static uint16_t sput(chry_readline_t *rl, const void *data, uint16_t size)
  53. {
  54. uint16_t i;
  55. (void)rl;
  56. for (i = 0; i < size; i++) {
  57. if (1 != write(STDOUT_FILENO, (uint8_t *)data + i, 1)) {
  58. break;
  59. }
  60. }
  61. return i;
  62. }
  63. static uint16_t sget(chry_readline_t *rl, void *data, uint16_t size)
  64. {
  65. uint16_t i;
  66. (void)rl;
  67. for (i = 0; i < size; i++) {
  68. if (1 != read(STDIN_FILENO, (uint8_t *)data + i, 1)) {
  69. break;
  70. }
  71. }
  72. return i;
  73. }
  74. chry_readline_t rl;
  75. static int ucb(chry_readline_t *rl, uint8_t exec)
  76. {
  77. /*!< user event callback will not output newline automatically */
  78. chry_readline_newline(rl);
  79. switch (exec) {
  80. case CHRY_READLINE_EXEC_EOF:
  81. fprintf(stderr, "EOF\r\n");
  82. disableRawMode(STDIN_FILENO);
  83. exit(0);
  84. break;
  85. case CHRY_READLINE_EXEC_SIGINT:
  86. chry_readline_ignore(rl, false);
  87. fprintf(stderr, "SIGINT\r\n");
  88. break;
  89. case CHRY_READLINE_EXEC_SIGQUIT:
  90. fprintf(stderr, "SIGQUIT\r\n");
  91. break;
  92. case CHRY_READLINE_EXEC_SIGCONT:
  93. fprintf(stderr, "SIGCONT\r\n");
  94. break;
  95. case CHRY_READLINE_EXEC_SIGSTOP:
  96. fprintf(stderr, "SIGSTOP\r\n");
  97. break;
  98. case CHRY_READLINE_EXEC_SIGTSTP:
  99. fprintf(stderr, "SIGTSTP\r\n");
  100. break;
  101. case CHRY_READLINE_EXEC_F1 ... CHRY_READLINE_EXEC_F12:
  102. fprintf(stderr, "F%d event\r\n", exec - CHRY_READLINE_EXEC_F1 + 1);
  103. break;
  104. case CHRY_READLINE_EXEC_USER ... CHRY_READLINE_EXEC_END:
  105. fprintf(stderr, "U%d event\r\n", exec - CHRY_READLINE_EXEC_USER + 1);
  106. break;
  107. default:
  108. fprintf(stderr, "ERROR EXEC %d\r\n", exec);
  109. return -1;
  110. }
  111. /*!< return 1 will not refresh */
  112. /*!< return 0 to refresh whole line (include prompt) */
  113. /*!< return -1 to end readline (error) */
  114. return 0;
  115. }
  116. // clang-format off
  117. static const char *clist[] = {
  118. "hello", "world", "hell", "/exit", "/mask", "/unmask", "/prompt","/ignore",
  119. "hello0","hello1","hello2","hello3","hello4","hello5","hello6","hello7","hello8","hello9",
  120. "hello10","hello11","hello12","hello13","hello14","hello15","hello16","hello17","hello18","hello19",
  121. "hello20","hello21","hello22","hello23","hello24","hello25","hello26","hello27","hello28","hello29",
  122. "hello30","hello31","hello32","hello33","hello344444","hello35","hello36","hello37","hello38","hello39",
  123. "hello40","hello41","hello42","hello43","hello44","hello45","hello46","hello47","hello48","hello49",
  124. "hello50","hello51","hello52","hello53","hello54","hello55","hello56","hello57","hello58","hello59",
  125. "hello60","hello61","hello62","hello63","hello64","hello65","hello66","hello67","hello68","hello69",
  126. "hello70","hello71","hello72","hello73","hello74","hello75","hello76","hello77","hello78","hello79",
  127. "hello80","hello81","hello82","hello83","hello84","hello85","hello86","hello87","hello88","hello89",
  128. };
  129. // clang-format on
  130. static uint8_t acb(chry_readline_t *rl, char *pre, uint16_t *size, const char **argv, uint8_t *argl, uint8_t argcmax)
  131. {
  132. (void)rl;
  133. uint8_t argc = 0;
  134. for (uint32_t i = 0; i < sizeof(clist) / sizeof(char *); i++) {
  135. if (strncmp(pre, clist[i], *size) == 0) {
  136. argv[argc] = clist[i];
  137. argl[argc] = strlen(clist[i]);
  138. argc++;
  139. if (argc >= argcmax) {
  140. break;
  141. }
  142. }
  143. }
  144. return argc;
  145. }
  146. int main(int argc, char **argv)
  147. {
  148. char *prgname = argv[0];
  149. uint8_t keycode = 0;
  150. uint8_t xterm = 0;
  151. uint8_t repl = 0;
  152. (void)keycode;
  153. while (argc > 1) {
  154. argc--;
  155. argv++;
  156. if (!strcmp(*argv, "--keycodes")) {
  157. keycode = 1;
  158. } else if (!strcmp(*argv, "--xterm")) {
  159. xterm = 1;
  160. } else if (!strcmp(*argv, "--autosize")) {
  161. xterm = 1;
  162. } else if (!strcmp(*argv, "--repl")) {
  163. repl = 1;
  164. } else {
  165. fprintf(stderr, "Usage: %s [--keycodes] [--xterm] [--autosize] [--repl]\n", prgname);
  166. exit(1);
  167. }
  168. }
  169. enableRawMode(STDIN_FILENO);
  170. int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
  171. if (flags == -1) {
  172. perror("fcntl");
  173. return 1;
  174. }
  175. if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) {
  176. perror("fcntl");
  177. return 1;
  178. }
  179. char prompt[256];
  180. char history[256];
  181. char linebuf[128];
  182. char *line;
  183. uint16_t linesize;
  184. memset(prompt, 0xff, 256);
  185. chry_readline_init_t rl_init = {
  186. .prompt = prompt,
  187. .pptsize = 34 + 11,
  188. .history = history,
  189. .histsize = sizeof(history), /*!< size must be power of 2 !!! */
  190. .sget = sget,
  191. .sput = sput
  192. };
  193. assert(0 == chry_readline_init(&rl, &rl_init));
  194. /*!< the segidx must be incremented at first */
  195. assert(0 == chry_readline_prompt_edit(&rl, 0, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_GREEN, .bold = 1 }.raw, "cherry"));
  196. assert(0 == chry_readline_prompt_edit(&rl, 1, 0, ":"));
  197. assert(0 == chry_readline_prompt_edit(&rl, 2, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_BLUE, .bold = 1 }.raw, "~"));
  198. assert(0 == chry_readline_prompt_edit(&rl, 3, 0, "$ "));
  199. #if !CONFIG_READLINE_PROMPTEDIT
  200. memcpy(prompt, "nopptedit:~$ ", sizeof("nopptedit:~$ "));
  201. #endif
  202. #if CONFIG_READLINE_DEBUG
  203. if (keycode) {
  204. chry_readline_debug(&rl);
  205. disableRawMode(STDIN_FILENO);
  206. return 0;
  207. }
  208. #endif
  209. (void)xterm;
  210. if (xterm) {
  211. chry_readline_detect(&rl);
  212. }
  213. chry_readline_set_completion_cb(&rl, acb);
  214. chry_readline_set_user_cb(&rl, ucb);
  215. /*!< mapping ctrl+q to exec user event 1 */
  216. chry_readline_set_ctrlmap(&rl, CHRY_READLINE_CTRLMAP_X, CHRY_READLINE_EXEC_USER);
  217. /*!< mapping alt+q to exec user event 2 */
  218. chry_readline_set_altmap(&rl, CHRY_READLINE_ALTMAP_X, CHRY_READLINE_EXEC_USER + 1);
  219. if (repl) {
  220. goto repl;
  221. }
  222. uint8_t i = 0;
  223. uint32_t taskcnt = 0;
  224. while (1) {
  225. line = chry_readline(&rl, linebuf, sizeof(linebuf), &linesize);
  226. if (line == NULL) {
  227. printf("chry_readline error\r\n");
  228. break;
  229. } else if (line == (void *)-1) {
  230. if (taskcnt++ > 1000 * 10000) {
  231. chry_readline_erase_line(&rl);
  232. printf("other task\r\n");
  233. chry_readline_edit_refresh(&rl);
  234. taskcnt = 0;
  235. }
  236. } else if (linesize) {
  237. printf("len = %2d <'%s'>\r\n", linesize, line);
  238. if (strncmp(line, "/mask", linesize) == 0 && linesize == strlen("/mask")) {
  239. chry_readline_mask(&rl, 1);
  240. } else if (strncmp(line, "/unmask", linesize) == 0 && linesize == strlen("/unmask")) {
  241. chry_readline_mask(&rl, 0);
  242. } else if (strncmp(line, "/prompt", linesize) == 0 && linesize == strlen("/prompt")) {
  243. int ret = chry_readline_prompt_edit(&rl, 2, (chry_readline_sgr_t){ .foreground = CHRY_READLINE_SGR_BLUE, .bold = 1 }.raw, "~/readline/%d", i++);
  244. if (ret == 1) {
  245. printf("prompt not enouth space\r\n");
  246. } else if (ret == -1) {
  247. /*!< check if the segidx is legal */
  248. printf("prompt edit error\r\n");
  249. goto end;
  250. }
  251. } else if (strncmp(line, "/exit", linesize) == 0 && linesize == strlen("/exit")) {
  252. disableRawMode(STDIN_FILENO);
  253. return 0;
  254. } else if (strncmp(line, "/ignore", linesize) == 0 && linesize == strlen("/ignore")) {
  255. chry_readline_ignore(&rl, true);
  256. }
  257. }
  258. }
  259. goto end;
  260. repl:
  261. if (!CONFIG_READLINE_PROMPTEDIT) {
  262. printf("repl example, must enable CONFIG_READLINE_PROMPTEDIT\r\n");
  263. return 0;
  264. }
  265. printf("enter repl test mode, end with ':' to enter multiline mode\r\n");
  266. chry_readline_prompt_clear(&rl);
  267. assert(0 == chry_readline_prompt_edit(&rl, 0, 0, ">>> "));
  268. /*!< map ctrl+i(tab) to auto completion or space */
  269. chry_readline_set_ctrlmap(&rl, CHRY_READLINE_CTRLMAP_I, CHRY_READLINE_EXEC_ACPLT);
  270. bool multienable = false;
  271. int linecount = 0;
  272. char multibuff[32][128];
  273. while ((line = chry_readline(&rl, multibuff[linecount], 128, &linesize)) != NULL) {
  274. if (linesize) {
  275. if (line[linesize - 1] == ':') {
  276. multienable = true;
  277. assert(0 == chry_readline_prompt_edit(&rl, 0, 0, "... "));
  278. }
  279. if (++linecount > 32) {
  280. printf("too many line\r\n");
  281. break;
  282. }
  283. if (!multienable) {
  284. printf("len = %2d <'%s'>\r\n", linesize, line);
  285. if (strncmp(line, "/exit", linesize) == 0) {
  286. if (linesize == strlen("/exit")) {
  287. disableRawMode(STDIN_FILENO);
  288. return 0;
  289. }
  290. }
  291. }
  292. } else {
  293. /**
  294. * The first two bytes of the buffer store the linesize of type uint16_t,
  295. * followed by the input content and ending \0. \0 is not included in the linesize
  296. */
  297. if (multienable) {
  298. assert(0 == chry_readline_prompt_edit(&rl, 0, 0, ">>> "));
  299. for (int i = 0; i < linecount; i++) {
  300. printf("%3d %s\r\n", *((uint16_t *)multibuff[i]), &multibuff[i][2]);
  301. }
  302. multienable = false;
  303. linecount = 0;
  304. }
  305. }
  306. }
  307. end:
  308. disableRawMode(STDIN_FILENO);
  309. return -1;
  310. }