#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>


/* "Undocumented" ioctls */
#define KDGKBTYPE	0x4B33	/* get keyboard type */
#define VT_OPENQRY	0x5600	/* find available vt */
#define VT_ACTIVATE	0x5606	/* make vt active */
#define VT_WAITACTIVE	0x5607	/* wait for vt active */


void bail(const char* s) {
    perror(s);
    exit(errno); }


void disconnect(int tty_fd) {
    int rc = ioctl(tty_fd, TIOCNOTTY);
    if(rc < 0) bail("disconnect"); }


void reopen(int fd, char* f, int flags) {
    int nfd = -1;
    int rc = close(fd);
    if(rc < 0) bail("reopen:close");
    nfd = open(f, flags);
    if(nfd < 0) bail("reopen:open");
    if(nfd != fd) {
	printf("reopened %d on %d\n", fd, nfd);
	exit(nfd); }}


int ctty_fd = -1;
int cons_fd = -1;

int get_tty() 
{
    int arg = 0;
    int fd = cons_fd = ctty_fd = open("/dev/vc/0", O_RDONLY);
    if(fd < 0) bail("get_tty:open");
    if(ioctl(fd, KDGKBTYPE, &arg) < 0) {
	close(fd);
	fd = cons_fd = open("/dev/vc/0", O_RDONLY);
	if(fd < 0) bail("get_tty:open0");
	if(ioctl(fd, KDGKBTYPE, &arg) < 0) bail("get_tty:KDGKBTYPE"); }
    return fd; 
}


void setup_vt(unsigned vt) 
{
    char tty[32];
    int rc = 0;
    int fd = get_tty();
    if(!vt) {
	static int get_vt = 0;
	rc = ioctl(fd, VT_OPENQRY, &get_vt);
	if(rc < 0) bail("setup_vt:VT_OPENQRY");
	vt = get_vt; }
    printf("changing to vt %d\n", vt);
    sprintf(tty, "/dev/vc/%d", vt);
/*    disconnect(ctty_fd);*/
    reopen(0, tty, O_RDONLY);
    reopen(1, tty, O_WRONLY);
    reopen(2, tty, O_WRONLY);
    if(ioctl(fd, VT_ACTIVATE, vt) < 0) bail("setup_vt:VT_ACTIVATE");
    if(ioctl(fd, VT_WAITACTIVE, vt) < 0) bail("setup_vt:VT_WAITACTIVE"); 
}


void usage() {
    puts("usage: execvt [-t <vt>] program [arg...]");
    exit(1); }


int main(int c, char**v) {
    unsigned vt_number = 0;
    char **exec_args = 0;

    if(c < 2) usage();
    if(c > 3 && !strcmp(v[1], "-t")) {
	sscanf(v[2], "%d", &vt_number);
	exec_args = v + 3; }
      else exec_args = v + 1;
    setup_vt(vt_number);
    execvp(exec_args[0], exec_args); 
}
