/*
 *  lccvt -- legacy code converter for vt
 *
 *  revision history
 *	0.0: Feb.  5, 2003 by Dai ISHIJIMA as ek.c
 *	1.0: Apr. 12, 2018 (lccvt.c)
 *	1.1: Apr. 15, 2018 (Linux, SJIS)
 *	1.2: Sep. 10, 2022 (MacOS X)
 *
 *  see also:
 *	/usr/src/usr.bin/script/script.c
 *	/usr/src/usr.sbin/sysinstall/system.c
 */

#ifdef LINUX
#define NEED_PTY_H
#define NEED_UTMP_H
#define NEED_WAIT_H
#else
#define NEED_LIBUTIL_H
#endif
#ifdef MACOSX
#define NEED_UTIL.H
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>	/* bzero() <- FDZERO() */
#include <termios.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/ioctl.h>
#ifdef NEED_LIBUTIL_H
#include <libutil.h>	/* openpty() */ /* -lutil */
#endif
#ifdef NEED_UTIL_H
#include <util.h>	/* openpty() */ /* -lutil */
#endif
#ifdef NEED_PTY_H
#include <pty.h>	/* openpty() */ /* -lutil */
#endif
#ifdef NEED_UTMP_H
#include <utmp.h>	/* login_tty() */ /* -lutil */
#endif
#ifdef NEED_WAIT_H
#include <wait.h>	/* wait3(), WNOHANG */
#endif


#include "codeconv.h"

int (*mbtojis)(int code);


#ifdef LINUX
#define DEFAULT_SHELL "/bin/sh"
#else
#define DEFAULT_SHELL "/bin/csh"
#endif

#define SHELL_ENV "SHELL"

#define YES 1
#define NO  0


char *prog;
#define shift --argc; ++argv


static pid_t child;

#define CHILD_RUNNING 1
#define CHILD_EOF    -1
#define CHILD_SIGNAL -2
#define CHILD_KILLED -3

static int childstatus = CHILD_RUNNING;



/* 子プロセスを起動 */
void subshell(int master, int slave, char *shell, char **args)
{
    close(master);
    login_tty(slave);
    execvp(shell, args);	/* 子プロセスの起動 */
    /* ここから下は実行されないはず… */
    fprintf(stderr, "%s: can't start subshell (%s)\r\n", prog, shell);
    kill(0, SIGTERM);
    exit(1);
}


#ifdef LINUX
/* SIGCHILD で飛んでくる。exitした場合 */
void chk_child(int dummy)
{
    int status;
    pid_t pid;

    childstatus = CHILD_KILLED;
    while ((pid = wait3(&status, WNOHANG, NULL)) > 0) {
	if (pid == child) {
	    childstatus = CHILD_SIGNAL;
	}
    }
}
#endif


void putstdout(FILE *fp, char buf[], int len)
{
    static unsigned char obuf[BUFSIZ];
    static int opos = 0;
    int i, j;
    int mbchar, jis, u16, utf8, olen;
    char ch;
    int mask;

    for (i = 0; i < len; i++) {
	obuf[opos] = (unsigned char)buf[i];
	++opos;
    }
    obuf[opos] = 0;
    for (j = 0; j < opos; j++) {
	if (obuf[0] > 0x80) {
	    if (opos >= 2) {
		mbchar = (obuf[0] << 8) | obuf[1];
		jis = (*mbtojis)(mbchar);
		u16 = jistou16[jis];
		utf8 = u16toutf8(u16, &olen);
#ifdef DEBUG
		printf("%04x %04x %04x %06x\n", euc, jis, u16, utf8);
#endif
		mask = (0x00ff) << (8 * (olen - 1));
		for (i = 0; i < olen; i++) {
		    ch = (utf8 & mask) >> (8 * (olen - 1 - i));
		    mask = mask >> 8;
		    write(fileno(fp), &ch, 1);
		}
		for (i = 0; i < opos - 2; i++) {
		    obuf[i] = obuf[i + 2];
		}
		obuf[i] = 0;
		opos = opos - 2;
	    }
	}
	else {
	    write(fileno(fp), &obuf[0], 1);
	    for (i = 0; i < opos - 1; i++) {
		obuf[i] = obuf[i + 1];
	    }
	    opos = opos - 1;
	    obuf[opos] = 0;
	}
    }
}

int convloop(int master)
{
    int nfds;
    fd_set readfds;
    struct timeval timeout;
    int n;	/* 読み込んだ文字の数 */
    char buf[BUFSIZ];

    timeout.tv_sec = 0;
    timeout.tv_usec = 1000;
    childstatus = CHILD_RUNNING;
    while (childstatus == CHILD_RUNNING) {
	FD_ZERO(&readfds);
	FD_SET(master, &readfds); /* 子プロセスの出力を調べる */
	FD_SET(fileno(stdin), &readfds); /* 標準入力を調べる */
	FD_SET(fileno(stdout), &readfds); /* 標準入力を調べる */
	nfds = select(master + 1, &readfds, NULL, NULL, &timeout);
	if ((nfds < 0) && (errno != EINTR)) {
	    fprintf(stderr, ">>>if ((nfds < 0) && (errno != EINTR)) {\n");
	    break;
	}
	if ((nfds > 0) && (FD_ISSET(fileno(stdin), &readfds))) {
	    /* 標準入力からのデータを子プロセスへ書き出す */
	    n = read(fileno(stdin), buf, BUFSIZ);
	    if (n > 0) {
		(void)write(master, buf, n);
	    }
	}
	if ((nfds > 0) && (FD_ISSET(master, &readfds))) {
	    /* 子プロセスからのデータを標準出力へ書き出す */
	    /* n = read(master, buf, BUFSIZ); */
	    n = read(master, buf, 1);
	    if (n <= 0) {
		childstatus = CHILD_EOF;
	    }
	    if (n > 0) {
		putstdout(stdout, buf, n);
	    }
	}
    } /* while */
    if (childstatus == CHILD_EOF) {
	fprintf(stderr, "%s: EOF encountered, child process finished\r\n",
		prog);
    }
    else if (childstatus == CHILD_SIGNAL) {
	fprintf(stderr, "%s: caught SIGCHLD, child process finished\r\n",
		prog);
    }
    else {
	fprintf(stderr, "%s: child process finished (may be killed)\r\n", prog);
    }
    return(0);
}


/* 仮想端末を開く */
void pseudoterm(char *shell, char **args)
{
    struct termios tsave;	/* 端末の設定 保存版 */
    struct termios t;		/* 端末の設定 */
    struct winsize w;		/* 端末の画面サイズ */
    int master, slave;
    int status;
   
    tcgetattr(fileno(stdin), &t);
    tcgetattr(fileno(stdin), &tsave);
    ioctl(fileno(stdin), TIOCGWINSZ, &w);
    if (openpty(&master, &slave, NULL, &t, &w) != 0) {
	fprintf(stderr, "%s: can't open pty\n", prog);
	exit(1);
    }
    cfmakeraw(&t);
    t.c_lflag &= ~ECHO;
    (void)tcsetattr(fileno(stdin), TCSAFLUSH, &t);
    child = fork();
    if (child < 0) {			/* fork失敗 */
	fprintf(stderr, "%s: can't fork\n", prog);
	exit(1);
    }
    else if (child == 0) {		/* 子プロセス */
	subshell(master, slave, shell, args);
    }
    else {				/* 元々のプロセス */
#ifdef LINUX
	signal(SIGCHLD, chk_child);	/* 子プロセスが死んだかチェック */
#endif
#if 0
	signal(SIGWINCH, winch);	/* ウィンドウサイズ変更 */
#endif
	status = convloop(master);
    }
    (void)tcsetattr(fileno(stdin), TCSAFLUSH, &tsave);
}


void usage()
{
    fprintf(stderr, "Usage: %s [-e|-s] [child [args]...]\n", prog);
}


int main(int argc, char *argv[])
{
    int i;
    char *shell;
    char **args;
    
    prog = *argv;
    shift;
    mbtojis = euctojis;
    while ((argc > 0) && (argv[0][0] == '-')) {
	if (argv[0][1] == 'h') {
	    usage();
	    exit(1);
	}
	else if (argv[0][1] == 'e') {
	    mbtojis = euctojis;
	}
	else if (argv[0][1] == 's') {
	    mbtojis = sjistojis;
	}
	else {
	    usage();
	    exit(1);
	}
	shift;
    }
    args = (char **)calloc(argc + 3, sizeof(char *));
    if (argc < 1) {	/* オプションなし→シェル */
	shell = getenv(SHELL_ENV);
	if (shell == NULL) {
	    shell = DEFAULT_SHELL;
	}
	args[0] = "-sh";
	args[1] = "-i";
	args[2] = NULL;
    }
    else {
	shell = argv[0];
	for (i = 0; i < argc; i++) {
	    args[i] = argv[i];
	}
	args[i] = NULL;
    }
#ifdef EBUG
    fprintf(stderr, "%s: shell = %s\n", prog, shell);
    i = 0;
    while (args[i] != NULL) {
	fprintf(stderr, "%s\n", args[i]);
	++i;
    }
#endif
    tblinit();
    pseudoterm(shell, args);
    exit(0);
}

/* Local Variables: */
/* compile-command:"cc -g -Wall -o lccvt lccvt.c codeconv.c tbl.c -lutil" */
/* End: */

