Source

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 5e2fb40 








Evan Gates dc5ba6b 



Evan Gates 5e2fb40 
Evan Gates 845b3a6 
Evan Gates 5e2fb40 
Evan Gates dc5ba6b 


Evan Gates 18a44b3 




Evan Gates 845b3a6 








Evan Gates dc5ba6b 

Evan Gates 2a1b78c 
Evan Gates 5e2fb40 


























Evan Gates dc5ba6b 
Evan Gates 5e2fb40 





Evan Gates dc5ba6b 
Evan Gates 5e2fb40 















Evan Gates dc5ba6b 
Evan Gates 5e2fb40 


Evan Gates dc5ba6b 







Evan Gates 2a1b78c 
Evan Gates dc5ba6b 



Evan Gates 18a44b3 


Evan Gates 5e2fb40 


Evan Gates 18a44b3 
Evan Gates 2a1b78c 
Evan Gates 18a44b3 
Evan Gates 845b3a6 
Evan Gates dc5ba6b 
Evan Gates 2a1b78c 
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

#define MAX(a, b) (((a) > (b)) ? (a) : (b))

char csi[] = {0x1b, '[', 0};
char buf[4096], usrbuf[4096];
char  nrml_prompt_default[] = ">";
char  scrl_prompt_default[] = "<";
char *nrml_prompt = nrml_prompt_default;
char *scrl_prompt = scrl_prompt_default;
int pos = 0;
pid_t child;

void usage(void)
{
	printf("Usage: srw [-p prompt] [-P scroll_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 prompt(int *start, int *cols, int pos)
{
	int prompt_len, scrl_len, nrml_len;
	char *prompt;

	nrml_len = strlen(nrml_prompt);
	scrl_len = strlen(scrl_prompt);

	*start = MAX(0, pos - *cols + nrml_len);

	prompt     = *start ? scrl_prompt : nrml_prompt;
	prompt_len = *start ? scrl_len    : nrml_len;

	*start = MAX(0, pos - *cols + prompt_len);

	my_write(STDOUT, prompt, prompt_len);
	*cols -= prompt_len;
}

void erase_usr(void)
{
	printf("%s1G", csi); //first column
	printf("%sJ",  csi); //erase
	fflush(stdout);
}

void print_usr(void)
{
	int start, cols;
	struct winsize ws;

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

	cols = ws.ws_col - 1;

	erase_usr();
	prompt(&start, &cols, pos);
	my_write(STDOUT, usrbuf + start, pos - start);
}

int main(int argc, char **argv)
{
	int mfd, rdlen, i;
	fd_set rd;
	struct termios set;

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

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

	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 + pos, 1)) < 0)
				warn("failed to read from stdin");

			switch (usrbuf[pos++]) {
				case '\n':
					erase_usr();
					my_write(mfd, usrbuf, pos);
					pos = 0;
					break;
				case '\b':
				case 127 :
					pos -= (pos > 1) ? 2 : 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;
}