srw / srw.c

Evan Gates 2a1b78c 
Evan Gates 18a44b3 
Evan Gates 9389d6c 
Evan Gates 5e2fb40 

Evan Gates 9389d6c 



Evan Gates 5e2fb40 
Evan Gates 9389d6c 

Evan Gates 839a387 
Evan Gates 9389d6c 
Evan Gates 839a387 
Evan Gates 9389d6c 
Evan Gates 839a387 
Evan Gates 5e2fb40 




Evan Gates 672738c 
Evan Gates c7d33f0 

Evan Gates b03a21d 
Evan Gates be86b01 




Evan Gates 5e2fb40 
Evan Gates 44e9aa3 


Evan Gates be86b01 



Evan Gates dc5ba6b 

Evan Gates be86b01 



Evan Gates 845b3a6 
Evan Gates 5e2fb40 
Evan Gates dc5ba6b 

Evan Gates be86b01 
Evan Gates 18a44b3 




Evan Gates 845b3a6 








Evan Gates dc5ba6b 

Evan Gates 2a1b78c 
Evan Gates 5e2fb40 







Evan Gates be86b01 
Evan Gates 5e2fb40 
Evan Gates be86b01 

Evan Gates 44e9aa3 
Evan Gates 5e2fb40 

Evan Gates be86b01 
Evan Gates 5e2fb40 
Evan Gates fde6445 
Evan Gates be86b01 
Evan Gates fde6445 




Evan Gates be86b01 
Evan Gates fde6445 
Evan Gates 5e2fb40 

Evan Gates dc5ba6b 
Evan Gates 5e2fb40 
Evan Gates be86b01 
Evan Gates 5e2fb40 

Evan Gates 2d61da7 
Evan Gates 5e2fb40 




Evan Gates be86b01 





Evan Gates 672738c 
Evan Gates fde6445 
Evan Gates 44e9aa3 
Evan Gates fde6445 

Evan Gates be86b01 
Evan Gates 44e9aa3 
Evan Gates be86b01 


Evan Gates fde6445 


Evan Gates be86b01 

Evan Gates 44e9aa3 
Evan Gates be86b01 

Evan Gates 44e9aa3 
Evan Gates be86b01 


Evan Gates 44e9aa3 
Evan Gates be86b01 



Evan Gates fde6445 
Evan Gates be86b01 
Evan Gates fde6445 

Evan Gates be86b01 



Evan Gates fde6445 



Evan Gates be86b01 
Evan Gates fde6445 



Evan Gates be86b01 
Evan Gates fde6445 
Evan Gates be86b01 
Evan Gates fde6445 


Evan Gates be86b01 
Evan Gates fde6445 

Evan Gates 0ac3b78 

Evan Gates 0dc4d81 
Evan Gates c7d33f0 
Evan Gates 0dc4d81 



Evan Gates c7d33f0 



Evan Gates 0ac3b78 
Evan Gates fde6445 
Evan Gates c7d33f0 
Evan Gates 0ac3b78 

Evan Gates 0dc4d81 
Evan Gates fde6445 


















Evan Gates bacc7d7 





Evan Gates fde6445 
Evan Gates 0dc4d81 








Evan Gates fde6445 
Evan Gates 0dc4d81 




Evan Gates fde6445 


Evan Gates be86b01 
Evan Gates fde6445 



Evan Gates be86b01 
Evan Gates fde6445 

Evan Gates be86b01 

Evan Gates 5e2fb40 



Evan Gates fde6445 

Evan Gates 5e2fb40 
Evan Gates fde6445 









Evan Gates dc5ba6b 

Evan Gates fde6445 

Evan Gates 18a44b3 
Evan Gates fde6445 

Evan Gates 5e2fb40 
Evan Gates fde6445 

Evan Gates 18a44b3 
Evan Gates fde6445 



Evan Gates 5e2fb40 
Evan Gates fde6445 




Evan Gates 5e2fb40 
Evan Gates fde6445 

Evan Gates 5e2fb40 
Evan Gates fde6445 
Evan Gates 5e2fb40 
#include <err.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/wait.h>

#ifdef __linux__
 #include <pty.h>
#else  // __linux__
 #include <util.h>
#endif // __linux__

#define STDIN  0
#define STDOUT 1
#define STDERR 2

// make sure not to use anything that can't be evaluated twice
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))

#define BACKSPACE   CTRL('h')
#define LINEFEED    CTRL('j')
#define RETURN      CTRL('m')
#define ESC         0x1b
#define DEL         0x7f

#define cursor_clr()  printf("\033[J")
#define cursor_beg()  printf("\033[1G")
#define cursor_fwd(n) printf("\033[%dC", MAX(1, (n)))

char  buf[4096], usrbuf[4096];
char  scrl_prompt_left[]  = "<";
char  scrl_prompt_right[] = ">";
char  nrml_prompt_default[] = ">";
char *nrml_prompt = nrml_prompt_default;
char  word_separators[] = " \t~!@#$%^&*-+=\\|/?.,><";

int start = 0, cur = 0, len = 0;
int mfd;
pid_t child;

void usage(void)
{
	printf("Usage: srw [-p prompt] cmd [args]...\n");
	exit(1);
}

void sigchld(int unused)
{
	pid_t pid;

	while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
		if (pid == child) {
			printf("\n");
			warnx("subprocess died");
			kill(getpid(), SIGINT);
		}
	}
}

void my_write(int fd, const char *buf, size_t count)
{
	int i, wrlen;

	for (i = wrlen = 0; i < count; i += wrlen)
		if ((wrlen = write(fd, buf + i, count - i)) < 0)
			warn("failed to write fd = %d", fd);
}

void erase_usr(void)
{
	cursor_beg();
	cursor_clr();
	fflush(stdout);
}

int start_pos(int start, int cur, int len, int cols)
{
	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 cols, left_prompt_len, right_prompt_len = 0;
	struct winsize ws;

	if (ioctl(STDOUT, TIOCGWINSZ, &ws) < 0)
		err(1, "failed ioctl");

	cols = ws.ws_col - 1;

	erase_usr();

	left_prompt_len = strlen(nrml_prompt);
	cols -= left_prompt_len;

	start = start_pos(start, cur, len, cols);

	if (start > 0 || len > cols) {
		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 'O' :
					case '[' :
						continue;
					case '3' : // Delete
						if (cur != len)
							shift_buf(usrbuf, cur + 1, len--, -1);
						escape++;
						break;
					case '7' : cur = 0;   escape++;     break; // Home
					case '8' : cur = len; escape++;     break; // End
					case 'C' : cur = MIN(cur + 1, len); break; // Left  Arrow
					case 'D' : cur = MAX(cur - 1, 0  ); break; // Right Arrow
					default  : escape++;
				}
				escape--;
				// I get some really weird behavior from the numpad on this computer...
				if (strchr("ABMUVWXYZfijklmo][\\^~_", c)) // end of one, or unsupported
					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('a') : cur = 0;                 break;
					case CTRL('b') : cur = MAX(0, cur - 1);   break;
					case CTRL('e') : cur = len;               break;
					case CTRL('f') : cur = MIN(len, cur + 1); break;
					case CTRL('k') : len = cur;               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++;
						 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)
{
	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 'v' : printf("srw-"VERSION" © Evan Gates\n"); exit(1);
			default  : usage();
		}
	}

	if (i >= argc)
		usage();

	tcgetattr(0, &set);
	set.c_lflag &= ~ECHO;

	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]);
	}

	// 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();
	run();

	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.