2 * Copyright (C) 2007, 2008, 2009
3 * pancake <youterm.com>
5 * dietline is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * dietline is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with dietline; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 /* dietline is a lighweight and portable library similar to GNU readline */
28 static void cons_set_raw(int b);
29 static int cons_get_real_columns();
39 #include <sys/ioctl.h>
46 const char *dl_prompt = "> ";
47 const char *dl_clipboard = NULL;
48 static char *dl_nullstr = "";
49 static char dl_buffer[DL_BUFSIZE];
50 static int dl_buffer_len = 0;
51 static int dl_buffer_idx = 0;
53 /* autocompletion callback */
54 char **(*dl_callback)(const char *text, int start, int end) = NULL;
57 char **dl_history = NULL;
58 int dl_histsize = DL_HISTSIZE;
60 int dl_autosave = 0; // TODO
61 int dl_disable = 0; // TODO use fgets..no autocompletion
63 // TODO : FULL READLINE COMPATIBILITY
64 // rl_attempted_completion_function = rad_autocompletion;
65 // char **rad_autocompletion(const char *text, int start, int end)
66 // return matches = rl_completion_matches (text, rad_offset_matches);
75 HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
77 GetConsoleMode(h, &mode);
78 SetConsoleMode(h, 0); // RAW
79 ret = ReadConsole(h, buf,1, &out, NULL);
82 if (read(0,buf,1) == 1)
86 SetConsoleMode(h, mode);
88 int ret = read(0,buf,1);
98 static char *labels = NULL;
99 static unsigned int size = 0;
100 static unsigned int lsize = 0;
102 static int label_get(const char *name)
105 for(i=0;i<size;i++) {
106 if (!strcmp(name, labels+i+4)) {
107 memcpy(&n, labels+i, 4);
110 i+=strlen(labels+i+4)+4;
115 static void label_add (const char *str) {
116 unsigned int size = dl_histidx;
117 unsigned int len = strlen(str)-1;
119 //eprintf("New label(%s)\n",str);
120 memset(labels+lsize+4, '\0', BLOCK-((lsize+len+4)%BLOCK));
121 memcpy(labels+lsize, &size, 4);
122 memcpy(labels+lsize+4, str, len);
128 unsigned int i, p, n = 0;
129 for(i=0;i<lsize;i++,n++) {
130 memcpy(&p, labels+i, 4);
131 printf(" %03d %03d %s\n", i, p, labels+i+4);
132 i+=strlen(labels+i+4)+4;
136 static void label_reset()
143 static int is_label(const char *str)
150 if (str[strlen(str)-1]==':') {
162 int dl_hist_label(const char *label, void (*cb)(const char*))
166 /* labelling stuff */
168 if (!is_label(label+1))
171 switch(is_label(label)) {
178 i = label_get(label);
184 if (dl_history != NULL)
185 for(i=0; i<dl_histsize; i++) {
186 if (dl_history[i] == NULL)
188 fprintf(stderr, "%s\n", dl_history[i]);
191 else fprintf(stderr, "%s\n", dl_history[i]);
197 int dl_hist_add(const char *line)
199 #if HAVE_LIB_READLINE
202 if (dl_histidx>=dl_histsize)
203 dl_histidx = 0; // workaround
204 if (*line) { // && dl_histidx < dl_histsize) {
205 dl_history[dl_histidx++] = strdup(line);
215 strncpy(dl_buffer, dl_history[--dl_histidx], DL_BUFSIZE-1);
217 dl_buffer_len = strlen(dl_buffer);
226 if (dl_histidx<dl_histsize) {
227 if (dl_history[dl_histidx] == NULL) {
229 dl_buffer_idx = dl_buffer_len = 0;
232 strncpy(dl_buffer, dl_history[dl_histidx++], DL_BUFSIZE-1);
234 dl_buffer_len = strlen(dl_buffer);
244 if (dl_history != NULL)
245 for(i=0;i<dl_histsize; i++) {
246 if (dl_history[i] == NULL)
248 printf("%.3d %s\n", i, dl_history[i]);
257 if (dl_history != NULL)
258 for(i=0;i<dl_histsize; i++) {
260 dl_history[i] = NULL;
262 return dl_histidx=0, dl_histsize;
273 /* load history from file. if file == NULL load from ~/.<prg>.history or so */
274 int dl_hist_load(const char *file)
276 #if HAVE_LIB_READLINE
283 snprintf(buf, 1023, "%s/%s", get_home_directory(), file);
284 fd = fopen(buf, "r");
288 fgets(buf, 1023, fd);
290 buf[strlen(buf)-1]='\0';
292 fgets(buf, 1023, fd);
299 int dl_hist_save(const char *file)
301 #if HAVE_LIB_READLINE
302 return rad_readline_finish();
308 snprintf(buf, 1023, "%s/%s", get_home_directory(), file);
309 fd = fopen(buf, "w");
312 for(i=0;i<dl_histidx;i++) {
313 fputs(dl_history[i], fd);
322 int dl_hist_chop(const char *file, int limit)
328 /* initialize history stuff */
331 #if HAVE_LIB_READLINE
335 labels = malloc(BLOCK);
336 dl_history = (char **)malloc(dl_histsize*sizeof(char *));
337 if (dl_history==NULL)
339 memset(dl_history, '\0', dl_histsize*sizeof(char *));
341 dl_histsize = DL_HISTSIZE;
351 unsigned char buf[10];
354 buf[0]=dl_readchar();
361 buf[0] = dl_readchar();
362 printf("unicode-%02x-%02x\n", buf[0],buf[1]);
364 case 8: // wtf is 127?
365 case 127: printf("backspace\n"); break;
366 case 32: printf("space\n"); break;
369 printf("esc-%02x-%02x-%02x-%02x\n",
370 buf[0],buf[1],buf[2],buf[3]);
372 case 12: printf("^L\n"); break;
373 case 13: printf("intro\n"); break;
374 case 18: printf("^R\n"); break;
375 case 9: printf("tab\n"); break;
376 case 3: printf("control-c\n"); break;
377 case 0: printf("control-space\n"); break;
379 printf("(code:%d)\n", buf[0]);
388 /* main readline function */
389 char *dl_readline(int argc, const char **argv)
394 int columns = cons_get_real_columns()-2;
396 dl_buffer_idx = dl_buffer_len = 0;
400 dl_echo = config.verbose;
405 fgets(dl_buffer, DL_BUFSIZE-1, stdin);
406 dl_buffer[strlen(dl_buffer)] = '\0';
407 return (*dl_buffer)? dl_buffer : NULL;
410 memset(&buf,0,sizeof buf);
414 printf("%s", dl_prompt);
427 for(i=1;i<argc;i++) {
428 if (dl_buffer_len==0||!strncmp(argv[i], dl_buffer, dl_buffer_len)) {
429 len+=strlen(argv[i])+1;
430 if (len+dl_buffer_len+4 >= columns) break;
431 printf("%s ", argv[i]);
439 dl_buffer[dl_buffer_len]='\0';
440 buf[0] = dl_readchar();
442 // printf("\x1b[K\r");
443 columns = cons_get_real_columns()-2;
447 printf("\r%*c\r", columns, ' ');
452 case 0: // control-space
459 dl_buffer_idx = dl_buffer_len;
464 dl_buffer[dl_buffer_idx = dl_buffer_len = 0] = '\0';
469 if (!dl_buffer[0]) { /* eof */
474 case 10: // ^J -- ignore
476 case 11: // ^K -- ignore
478 case 19: // ^S -- backspace
479 dl_buffer_idx = dl_buffer_idx?dl_buffer_idx-1:0;
481 case 12: // ^L -- right
482 dl_buffer_idx = dl_buffer_idx<dl_buffer_len?dl_buffer_idx+1:dl_buffer_len;
484 printf("\x1b[2J\x1b[0;0H");
488 dl_clipboard = strdup(dl_buffer);
494 if (dl_buffer_idx>0) {
495 for(i=dl_buffer_idx-1;i&&dl_buffer[i]==' ';i--);
496 for(;i&&dl_buffer[i]!=' ';i--);
497 for(;i>0&&dl_buffer[i]==' ';i--);
499 if (dl_buffer[i+1]==' ')
502 strcpy(dl_buffer+i, dl_buffer+dl_buffer_idx);
503 dl_buffer_len = strlen(dl_buffer);
507 case 25: // ^Y - paste
508 if (dl_clipboard != NULL) {
509 dl_buffer_len += strlen(dl_clipboard);
510 // TODO: support endless strings
511 if (dl_buffer_len < DL_BUFSIZE) {
512 dl_buffer_idx = dl_buffer_len;
513 strcat(dl_buffer, dl_clipboard);
514 } else dl_buffer_len -= strlen(dl_clipboard);
523 case 27: //esc-5b-41-00-00
524 buf[0] = dl_readchar();
525 buf[1] = dl_readchar();
529 if (dl_buffer_idx<dl_buffer_len)
530 strcpy(dl_buffer, dl_buffer+1);
540 dl_buffer_idx = dl_buffer_idx<dl_buffer_len?dl_buffer_idx+1:dl_buffer_len;
543 dl_buffer_idx = dl_buffer_idx?dl_buffer_idx-1:0;
551 if (dl_buffer_idx < dl_buffer_len) {
552 if (dl_buffer_idx>0) {
554 memcpy(dl_buffer+dl_buffer_idx, dl_buffer+dl_buffer_idx+1,strlen(dl_buffer+dl_buffer_idx));
557 dl_buffer_idx = --dl_buffer_len;
558 if (dl_buffer_len<0) dl_buffer_len=0;
559 dl_buffer[dl_buffer_len]='\0';
566 // XXX does not autocompletes correctly
567 // XXX needs to check if valid results have the same prefix (from 1 to N)
568 if (dl_callback != NULL) {
569 //const char *from = strrchr(dl_buffer, ' ');
570 //char **res = dl_callback(dl_buffer, (from==NULL)?dl_buffer_idx:from-dl_buffer, dl_buffer_len);
571 /* TODO: manage res */
574 for(i=1,opt=0;i<argc;i++)
575 if (!strncmp(argv[i], dl_buffer, dl_buffer_idx))
578 if (dl_buffer_len>0&&opt==1)
579 for(i=1;i<argc;i++) {
580 if (!strncmp(dl_buffer, argv[i], dl_buffer_len)) {
581 strcpy(dl_buffer, argv[i]);
582 dl_buffer_idx = dl_buffer_len = strlen(dl_buffer);
583 // TODO: if only 1 keyword hits:
584 // if (argv[i][dl_buffer_len]=='\0') {
585 // strcat(dl_buffer, " ");
593 if (dl_buffer_idx==0 || opt>1) {
595 printf("%s%s\n",dl_prompt,dl_buffer);
596 for(i=1;i<argc;i++) {
597 if (dl_buffer_len==0||!strncmp(argv[i], dl_buffer, dl_buffer_len)) {
598 len+=strlen(argv[i]);
599 // if (len+dl_buffer_len+4 >= columns) break;
601 printf("%s ", argv[i]);
611 // TODO: SUPPORT FOR ^R (search command in history)
617 for(i=1;i<argc;i++) {
618 if (dl_buffer_len==0 || !strncmp(argv[i], dl_buffer, dl_buffer_len)) {
619 printf("%*c", columns, ' ');
621 printf("\n\n(%s)\n\n", dl_buffer);
628 /* XXX use ^A & ^E */
629 if (dl_buffer_idx<dl_buffer_len) {
630 for(i = ++dl_buffer_len;i>dl_buffer_idx;i--)
631 dl_buffer[i] = dl_buffer[i-1];
632 dl_buffer[dl_buffer_idx] = buf[0];
634 dl_buffer[dl_buffer_len]=buf[0];
636 if (dl_buffer_len>1000)
638 dl_buffer[dl_buffer_len]='\0';
644 printf("\r%s%s", dl_prompt, dl_buffer);
645 printf("\r%s", dl_prompt);
647 for(i=0;i<dl_buffer_idx;i++)
648 printf("%c", dl_buffer[i]);
656 printf("\r%s%s\n", dl_prompt, dl_buffer);
660 if (dl_buffer[0]=='!' && dl_buffer[1]=='\0') {
672 static struct termios tio_old, tio_new;
676 #include <sys/ioctl.h>
677 #include <sys/wait.h>
678 #include <sys/socket.h>
681 static void cons_set_raw(int b)
685 tcgetattr(0, &tio_old);
686 memcpy ((char *)&tio_new, (char *)&tio_old, sizeof(struct termios));
687 tio_new.c_iflag &= ~(BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
688 tio_new.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
689 tio_new.c_cflag &= ~(CSIZE|PARENB);
690 tio_new.c_cflag |= CS8;
691 tio_new.c_cc[VMIN]=1; // Solaris stuff hehe
692 tcsetattr(0, TCSANOW, &tio_new);
697 tcsetattr(0, TCSANOW, &tio_old);
702 static int cons_get_real_columns()
709 if (ioctl(1, TIOCGWINSZ, &win)) {
718 int main(int argc, const char **argv)
729 ret = dl_readline(argc, argv);
731 printf(" [line] '%s'\n", ret);