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 c7d33f0 










Evan Gates b03a21d 
Evan Gates be86b01 






Evan Gates 5e2fb40 
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 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 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 be86b01 
Evan Gates fde6445 



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


Evan Gates be86b01 
Evan Gates fde6445 


Evan Gates c7d33f0 








Evan Gates ebf708a 

Evan Gates fde6445 
Evan Gates c7d33f0 
Evan Gates fde6445 


















Evan Gates bacc7d7 





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

/*
 * I like these because they make sure you never evaluate something twice in the macro,
 * think function call or increment, but they aren't part of C99 so I have to get rid
 * of the -pedantic when I compile, but I like what that catches for me. Until I find
 * a better way to use these, I'll just have to be safe with the unsafe versions.
 *#define MIN(x,y) ({ typeof(x) _x = (x); typeof(y) _y = (y); (_x < _y) ? _x : _y; })
 *#define MAX(x,y) ({ typeof(x) _x = (x); typeof(y) _y = (y); (_x > _y) ? _x : _y; })
 */

#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))

#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

#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  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(1, 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) {
		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 '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
					case '~' : break;    // keep going until we get ~
					default  : escape++; // if we don't recognize it
				}
				escape--;
			} 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.