Anonymous avatar Anonymous committed be86b01

added cursor and arrow keys, working on ^w

Comments (0)

Files changed (1)

 #define STDERR 2
 
 #define MAX(a, b)   (((a) > (b)) ? (a) : (b))
-#define TO_LOWER(c) ((c) | 0x20)
-#define CTRL(c)     (TO_LOWER(c)-'a'+1)
+#define MIN(a, b)   (((a) < (b)) ? (a) : (b))
 
+#define BELL        CTRL('g')
+#define BACKSPACE   CTRL('h')
+#define TAB         CTRL('i')
+#define LINEFEED    CTRL('j')
+#define RETURN      CTRL('m')
+#define ESC         0x1b
+#define DEL         0x7f
 
-char csi[] = {0x1b, '[', 0};
-char buf[4096], usrbuf[4096];
+#define cursor_clr()  printf("\033[J")
+#define cursor_beg()  printf("\033[1G")
+#define cursor_fwd(n) printf("\033[%dC", MAX(1, (n)))
+#define cursor_bak(n) printf("\033[%dD", MAX(1, (n)))
+
+char  csi[] = {ESC, '[', 0};
+char  buf[4096], usrbuf[4096];
+char  scrl_prompt_left[]  = "<";
+char  scrl_prompt_right[] = ">";
 char  nrml_prompt_default[] = ">";
-char  scrl_prompt_default[] = "<";
 char *nrml_prompt = nrml_prompt_default;
-char *scrl_prompt = scrl_prompt_default;
-int end = 0;
+char  word_separators[] = " \t~!@#$%^&*-+=\\|/?.,><";
+
+int start = 0, cur = 0, len = 0;
+int mfd;
 pid_t child;
 
 void usage(void)
 {
-	printf("Usage: srw [-p prompt] [-P scroll_prompt] cmd [args]...\n");
+	printf("Usage: srw [-p prompt] cmd [args]...\n");
 	exit(1);
 }
 
 			warn("failed to write fd = %d", fd);
 }
 
-void prompt(int *start, int *cols, int end)
+void erase_usr(void)
 {
-	int prompt_len, scrl_len, nrml_len;
-	char *prompt;
-
-	nrml_len = strlen(nrml_prompt);
-	scrl_len = strlen(scrl_prompt);
-
-	*start = MAX(0, end - *cols + nrml_len);
-
-	prompt     = *start ? scrl_prompt : nrml_prompt;
-	prompt_len = *start ? scrl_len    : nrml_len;
-
-	*start = MAX(0, end - *cols + prompt_len);
-
-	my_write(STDOUT, prompt, prompt_len);
-	*cols -= prompt_len;
+	cursor_beg();
+	cursor_clr();
+	fflush(stdout);
 }
 
-void erase_usr(void)
+int start_pos(int start, int cur, int len, int cols)
 {
-	printf("%s1G", csi); //first column
-	printf("%sJ",  csi); //erase
-	fflush(stdout);
+    int ret;
+
+    ret = start;
+    ret = MIN(ret, len - cols);
+    ret = MAX(ret, 0);
+    ret = MAX(ret, cur - cols);
+    ret = MIN(ret, cur);
+
+    return ret;
 }
 
 void print_usr(void)
 {
-	int start, cols;
+	int cols, left_prompt_len, right_prompt_len = 0;
 	struct winsize ws;
 
 	if (ioctl(1, TIOCGWINSZ, &ws) < 0)
 	cols = ws.ws_col - 1;
 
 	erase_usr();
-	prompt(&start, &cols, end);
-	my_write(STDOUT, usrbuf + start, end - start);
+
+	left_prompt_len = strlen(nrml_prompt);
+	cols -= left_prompt_len;
+
+	start = start_pos(start, cur, len, cols);
+
+	if (start > 0) {
+	    left_prompt_len = strlen(scrl_prompt_left);
+	    my_write(STDOUT, scrl_prompt_left, left_prompt_len);
+	    cols = ws.ws_col - 1 - left_prompt_len;
+	    start = start_pos(start, cur, len, cols);
+	} else {
+	    my_write(STDOUT, nrml_prompt, left_prompt_len);
+	}
+
+	if (start + cols < len) {
+	    right_prompt_len = strlen(scrl_prompt_right);
+	    cols -= right_prompt_len;
+	    start = start_pos(start, cur, len, cols);
+	}
+
+	my_write(STDOUT, usrbuf + start, MIN(cols, len));
+
+	if (start + cols < len)
+	    my_write(STDOUT, scrl_prompt_right, right_prompt_len);
+
+	cursor_beg();
+	cursor_fwd(left_prompt_len + cur - start);
+	fflush(stdout);
+}
+
+void shift_buf(char *buf, int ind, int len, int delta)
+{
+    int i;
+
+	for (i = (delta > 0) ? len - 1 : ind ; i >= ind && i < len; i -= (delta > 0) ? 1 : -1)
+	    buf[i + delta] = buf[i];
+}
+
+void run()
+{
+    static int escape = 0;
+    int rdlen, i;
+    char c;
+    fd_set rd;
+
+    for (;;) {
+	FD_ZERO(&rd);
+	FD_SET(STDIN, &rd);
+	FD_SET(mfd,   &rd);
+
+	select(mfd + 1, &rd, NULL, NULL, NULL);
+
+	if (FD_ISSET(STDIN, &rd)) {
+	    if ((rdlen = read(STDIN, &c, 1)) < 0)
+		warn("failed to read from stdin");
+
+	    if (escape) {
+		switch (c) {
+		    case '[' : continue;
+		    case 'C' : cur = MIN(cur + 1, len); break;
+		    case 'D' : cur = MAX(cur - 1, 0  ); break;
+		}
+		escape = 0;
+	    } else {
+		switch (c) {
+		    case ESC :
+			escape = 1;
+			break;
+		    case RETURN :
+		    case LINEFEED :
+			erase_usr();
+			usrbuf[len++] = '\n';
+			my_write(mfd, usrbuf, len);
+			start = cur = len = 0;
+			break;
+		    case BACKSPACE :
+		    case DEL :
+			if (cur != len)
+			    shift_buf(usrbuf, cur, len, -1);
+			cur = MAX(0, cur - 1);
+			len = MAX(0, len - 1);
+			break;
+		    case CTRL('u') :
+			start = cur = len = 0;
+			break;
+		    case CTRL('w') :
+			for (i = cur - 1; i >= 0 && strchr(word_separators, usrbuf[i]); i--)
+			    ;
+			for (; i >=0 && !strchr(word_separators, usrbuf[i]); i--)
+			    ;
+			i = MAX(0, i);
+			shift_buf(usrbuf, cur, len, cur - i);
+			len -= cur - i;
+			cur = i;
+			break;
+		    default :
+			if (cur != len)
+			    shift_buf(usrbuf, cur, len, 1);
+			usrbuf[cur++] = c;
+			len++;
+			break;
+		}
+	    }
+	    print_usr();
+	}
+	if (FD_ISSET(mfd, &rd)) {
+	    if ((rdlen = read(mfd, buf, sizeof(buf))) < 0)
+		warn("failed to read from master");
+
+	    erase_usr();
+	    my_write(STDOUT, buf, rdlen);
+	    print_usr();
+	}
+    }
 }
 
 int main(int argc, char **argv)
 {
-	int mfd, rdlen, i;
-	char word_separators[] = " \t~!@#$%^&*-+=\\|/?.,><";
-	fd_set rd;
-	struct termios set;
+    struct termios set;
+    int i;
 
-	for (i = 1; i < argc; i++) {
-		if (argv[i][0] != '-')
-			break;
-		if (argv[i][2] != '\0')
-			usage();
-		switch (argv[i][1]) {
-			case 'p' : nrml_prompt = argv[++i]; break;
-			case 'P' : scrl_prompt = argv[++i]; break;
-			case 'v' : printf("srw-"VERSION" © Evan Gates\n"); exit(1);
-			default  : usage();
-		}
+    for (i = 1; i < argc; i++) {
+	if (argv[i][0] != '-')
+	    break;
+	if (argv[i][2] != '\0')
+	    usage();
+	switch (argv[i][1]) {
+	    case 'p' : nrml_prompt = argv[++i]; break;
+	    case 'v' : printf("srw-"VERSION" © Evan Gates\n"); exit(1);
+	    default  : usage();
 	}
+    }
 
-	if (i >= argc)
-		usage();
+    if (i >= argc)
+	usage();
 
-	tcgetattr(0, &set);
-	set.c_lflag &= ~ECHO;
+    tcgetattr(0, &set);
+    set.c_lflag &= ~ECHO;
 
-	if (signal(SIGCHLD, sigchld) == SIG_ERR)
-		err(1, "failed to install SIGCHLD handler");
+    if (signal(SIGCHLD, sigchld) == SIG_ERR)
+	err(1, "failed to install SIGCHLD handler");
 
-	if ((child = forkpty(&mfd, NULL, &set, NULL)) == 0) { //child
-		execvp(argv[i], &argv[i]);
-		err(1, "failed execvp %s", argv[i]);
-	}
+    if ((child = forkpty(&mfd, NULL, &set, NULL)) == 0) { //child
+	execvp(argv[i], &argv[i]);
+	err(1, "failed execvp %s", argv[i]);
+    }
 
-	// read one character at a time;
-	set.c_lflag    &= ~(ICANON | ECHO);
-	set.c_cc[VMIN ] = 1;
-	set.c_cc[VTIME] = 0;
-	tcsetattr(0, TCSANOW, &set);
+    // read one character at a time;
+    set.c_lflag    &= ~(ICANON | ECHO);
+    set.c_cc[VMIN ] = 1;
+    set.c_cc[VTIME] = 0;
+    tcsetattr(0, TCSANOW, &set);
 
-	print_usr();
+    print_usr();
+    run();
 
-	for (;;) {
-		FD_ZERO(&rd);
-		FD_SET(STDIN, &rd);
-		FD_SET(mfd,   &rd);
-
-		select(mfd + 1, &rd, NULL, NULL, NULL);
-
-		if (FD_ISSET(STDIN, &rd)) {
-			if ((rdlen = read(STDIN, usrbuf + end, 1)) < 0)
-				warn("failed to read from stdin");
-
-			switch (usrbuf[end++]) {
-				case '\n':
-					erase_usr();
-					my_write(mfd, usrbuf, end);
-					end = 0;
-					break;
-				case '\b' :
-				case 0x7f :
-					end -= (end > 1) ? 2 : 1;
-					break;
-				case 0x15 : // Control-u
-					end = 0;
-					break;
-				case 0x17 :
-					for (i = end - 2; i >= 0 && strchr(word_separators, usrbuf[i]); i--)
-						;
-					for (i = MAX(i, 0); i >=0 && !strchr(word_separators, usrbuf[i]); i--)
-						;
-					end = i + 1;
-					break;
-			}
-			print_usr();
-
-		}
-		if (FD_ISSET(mfd, &rd)) {
-			if ((rdlen = read(mfd, buf, sizeof(buf))) < 0)
-				warn("failed to read from master");
-
-			erase_usr();
-			my_write(STDOUT, buf, rdlen);
-			print_usr();
-		}
-	}
-	return 0;
+    return 0;
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.