#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <signal.h>
#ifdef SVR4
#include <stropts.h>
#endif
#include <X11/fonts/fontenc.h>
#include "luit.h"
#include "sys.h"
#include "other.h"
#include "charset.h"
#include "iso2022.h"
static Iso2022Ptr inputState = NULL, outputState = NULL;
static char *child_argv0 = NULL;
static char *locale_name = NULL;
int ilog = -1;
int olog = -1;
int verbose = 0;
int converter = 0;
int exitOnChild = 0;
volatile int sigwinch_queued = 0;
volatile int sigchld_queued = 0;
static int convert(int, int);
static int condom(int, char**);
static void
ErrorF(char *f, ...)
{
va_list args;
va_start(args, f);
vfprintf(stderr, f, args);
va_end(args);
}
static void
FatalError(char *f, ...)
{
va_list args;
va_start(args, f);
vfprintf(stderr, f, args);
va_end(args);
exit(1);
}
static void
help(void)
{
fprintf(stderr,
"luit\n"
" [ -h ] [ -list ] [ -v ] [ -argv0 name ]\n"
" [ -gl gn ] [-gr gk] "
"[ -g0 set ] [ -g1 set ] "
"[ -g2 set ] [ -g3 set ]\n"
" [ -encoding encoding ] "
"[ +oss ] [ +ols ] [ +osl ] [ +ot ]\n"
" [ -kgl gn ] [-kgr gk] "
"[ -kg0 set ] [ -kg1 set ] "
"[ -kg2 set ] [ -kg3 set ]\n"
" [ -k7 ] [ +kss ] [ +kssgr ] [ -kls ]\n"
" [ -c ] [ -x ] [ -ilog filename ] [ -olog filename ] [ -- ]\n"
" [ program [ args ] ]\n");
}
static int
parseOptions(int argc, char **argv)
{
int i = 1;
while(i < argc) {
if(argv[i][0] != '-' && argv[i][0] != '+') {
break;
} else if(!strcmp(argv[i], "--")) {
i++;
break;
} else if(!strcmp(argv[i], "-v")) {
verbose++;
i++;
} else if(!strcmp(argv[i], "-h")) {
help();
exit(0);
} else if(!strcmp(argv[i], "-list")) {
reportCharsets();
exit(0);
} else if(!strcmp(argv[i], "+oss")) {
outputState->outputFlags &= ~OF_SS;
i++;
} else if(!strcmp(argv[i], "+ols")) {
outputState->outputFlags &= ~OF_LS;
i++;
} else if(!strcmp(argv[i], "+osl")) {
outputState->outputFlags &= ~OF_SELECT;
i++;
} else if(!strcmp(argv[i], "+ot")) {
outputState->outputFlags = OF_PASSTHRU;
i++;
} else if(!strcmp(argv[i], "-k7")) {
inputState->inputFlags &= ~IF_EIGHTBIT;
i++;
} else if(!strcmp(argv[i], "+kss")) {
inputState->inputFlags &= ~IF_SS;
i++;
} else if(!strcmp(argv[1], "+kssgr")) {
inputState->inputFlags &= ~IF_SSGR;
i++;
} else if(!strcmp(argv[i], "-kls")) {
inputState->inputFlags |= IF_LS;
i++;
} else if(!strcmp(argv[i], "-g0")) {
if(i + 1 >= argc)
FatalError("-g0 requires an argument\n");
G0(outputState) = getCharsetByName(argv[i + 1]);
i += 2;
} else if(!strcmp(argv[i], "-g1")) {
if(i + 1 >= argc)
FatalError("-g1 requires an argument\n");
G1(outputState) = getCharsetByName(argv[i + 1]);
i += 2;
} else if(!strcmp(argv[i], "-g2")) {
if(i + 1 >= argc)
FatalError("-g2 requires an argument\n");
G2(outputState) = getCharsetByName(argv[i + 1]);
i += 2;
} else if(!strcmp(argv[i], "-g3")) {
if(i + 1 >= argc)
FatalError("-g3 requires an argument\n");
G3(outputState) = getCharsetByName(argv[i + 1]);
i += 2;
} else if(!strcmp(argv[i], "-gl")) {
int j;
if(i + 1 >= argc)
FatalError("-gl requires an argument\n");
if(strlen(argv[i + 1]) != 2 ||
argv[i + 1][0] != 'g')
j = -1;
else
j = argv[i + 1][1] - '0';
if(j < 0 || j > 3)
FatalError("The argument of -gl "
"should be one of g0 through g3,\n"
"not %s\n", argv[i + 1]);
else
outputState->glp = &outputState->g[j];
i += 2;
} else if(!strcmp(argv[i], "-gr")) {
int j;
if(i + 1 >= argc)
FatalError("-gr requires an argument\n");
if(strlen(argv[i + 1]) != 2 ||
argv[i + 1][0] != 'g')
j = -1;
else
j = argv[i + 1][1] - '0';
if(j < 0 || j > 3)
FatalError("The argument of -gl "
"should be one of g0 through g3,\n"
"not %s\n", argv[i + 1]);
else
outputState->grp = &outputState->g[j];
i += 2;
} else if(!strcmp(argv[i], "-kg0")) {
if(i + 1 >= argc)
FatalError("-kg0 requires an argument\n");
G0(inputState) = getCharsetByName(argv[i + 1]);
i += 2;
} else if(!strcmp(argv[i], "-kg1")) {
if(i + 1 >= argc)
FatalError("-kg1 requires an argument\n");
G1(inputState) = getCharsetByName(argv[i + 1]);
i += 2;
} else if(!strcmp(argv[i], "-kg2")) {
if(i + 1 >= argc)
FatalError("-kg2 requires an argument\n");
G2(inputState) = getCharsetByName(argv[i + 1]);
i += 2;
} else if(!strcmp(argv[i], "-kg3")) {
if(i + 1 >= argc)
FatalError("-kg3 requires an argument\n");
G3(inputState) = getCharsetByName(argv[i + 1]);
i += 2;
} else if(!strcmp(argv[i], "-kgl")) {
int j;
if(i + 1 >= argc)
FatalError("-kgl requires an argument\n");
if(strlen(argv[i + 1]) != 2 ||
argv[i + 1][0] != 'g')
j = -1;
else
j = argv[i + 1][1] - '0';
if(j < 0 || j > 3)
FatalError("The argument of -kgl "
"should be one of g0 through g3,\n"
"not %s\n", argv[i + 1]);
else
inputState->glp = &inputState->g[j];
i += 2;
} else if(!strcmp(argv[i], "-kgr")) {
int j;
if(i + 1 >= argc)
FatalError("-kgl requires an argument\n");
if(strlen(argv[i + 1]) != 2 ||
argv[i + 1][0] != 'g')
j = -1;
else
j = argv[i + 1][1] - '0';
if(j < 0 || j > 3)
FatalError("The argument of -kgl "
"should be one of g0 through g3,\n"
"not %s\n", argv[i + 1]);
else
inputState->grp = &inputState->g[j];
i += 2;
} else if(!strcmp(argv[i], "-argv0")) {
if(i + 1 >= argc)
FatalError("-argv0 requires an argument\n");
child_argv0 = argv[i + 1];
i += 2;
} else if(!strcmp(argv[i], "-x")) {
exitOnChild = 1;
i++;
} else if(!strcmp(argv[i], "-c")) {
converter = 1;
i++;
} else if(!strcmp(argv[i], "-ilog")) {
if(i + 1 >= argc)
FatalError("-ilog requires an argument\n");
ilog = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0777);
if(ilog < 0) {
perror("Couldn't open input log");
exit(1);
}
i += 2;
} else if(!strcmp(argv[i], "-olog")) {
if(i + 1 >= argc)
FatalError("-olog requires an argument\n");
olog = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0777);
if(olog < 0) {
perror("Couldn't open output log");
exit(1);
}
i += 2;
} else if(!strcmp(argv[i], "-encoding")) {
int rc;
if(i + 1 >= argc)
FatalError("-encoding requires an argument\n");
rc = initIso2022(NULL, argv[i + 1], outputState);
if(rc < 0)
FatalError("Couldn't init output state\n");
i += 2;
} else {
FatalError("Unknown option %s\n", argv[i]);
}
}
return i;
}
static int
parseArgs(int argc, char **argv, char *argv0,
char **path_return, char ***argv_return)
{
char *path = NULL;
char **child_argv = NULL;
if(argc <= 0) {
char *shell;
shell = getenv("SHELL");
if(shell) {
path = malloc(strlen(shell) + 1);
if(!path)
goto bail;
strcpy(path, shell);
} else {
path = malloc(strlen("/bin/sh") + 1);
if(!path)
goto bail;
strcpy(path, "/bin/sh");
}
child_argv = malloc(2 * sizeof(char*));
if(!child_argv)
goto bail;
if(argv0)
child_argv[0] = argv0;
else
child_argv[0] = my_basename(path);
child_argv[1] = NULL;
} else {
path = malloc(strlen(argv[0]) + 1);
if(!path)
goto bail;
strcpy(path, argv[0]);
child_argv = malloc((argc + 1) * sizeof(char*));
if(!child_argv) {
goto bail;
}
if(child_argv0)
child_argv[0] = argv0;
else
child_argv[0] = my_basename(argv[0]);
memcpy(child_argv + 1, argv + 1, (argc - 1) * sizeof(char*));
child_argv[argc] = NULL;
}
*path_return = path;
*argv_return = child_argv;
return 0;
bail:
if(path)
free(path);
if(argv)
free(argv);
return -1;
}
int
main(int argc, char **argv)
{
int rc;
int i;
char *l;
l = setlocale(LC_ALL, "");
if(!l)
ErrorF("Warning: couldn't set locale.\n");
inputState = allocIso2022();
if(!inputState)
FatalError("Couldn't create input state\n");
outputState = allocIso2022();
if(!outputState)
FatalError("Couldn't create output state\n");
if(l) {
locale_name = setlocale(LC_CTYPE, NULL);
} else {
locale_name = getenv("LC_ALL");
if(locale_name == NULL) {
locale_name = getenv("LC_CTYPE");
if(locale_name == NULL) {
locale_name = getenv("LANG");
}
}
}
if(locale_name == NULL) {
ErrorF("Couldn't get locale name -- using C\n");
locale_name = "C";
}
rc = initIso2022(locale_name, NULL, outputState);
if(rc < 0)
FatalError("Couldn't init output state\n");
i = parseOptions(argc, argv);
if(i < 0)
FatalError("Couldn't parse options\n");
rc = mergeIso2022(inputState, outputState);
if(rc < 0)
FatalError("Couldn't init input state\n");
if(converter)
return convert(0, 1);
else
return condom(argc - i, argv + i);
}
static int
convert(int ifd, int ofd)
{
int rc, i;
unsigned char buf[BUFFER_SIZE];
rc = droppriv();
if(rc < 0) {
perror("Couldn't drop priviledges");
exit(1);
}
while(1) {
i = read(ifd, buf, BUFFER_SIZE);
if(i <= 0) {
if(i < 0) {
perror("Read error");
exit(1);
}
break;
}
copyOut(outputState, ofd, buf, i);
}
return 0;
}
static int
condom(int argc, char **argv)
{
int pty;
int pid;
char *line;
char *path;
char **child_argv;
int rc;
rc = parseArgs(argc, argv, child_argv0,
&path, &child_argv);
if(rc < 0)
FatalError("Couldn't parse arguments\n");
rc = allocatePty(&pty, &line);
if(rc < 0) {
perror("Couldn't allocate pty");
exit(1);
}
rc = droppriv();
if(rc < 0) {
perror("Couldn't drop priviledges");
exit(1);
}
pid = fork();
if(pid < 0) {
perror("Couldn't fork");
exit(1);
}
if(pid == 0) {
close(pty);
child(line, path, child_argv);
} else {
free(child_argv);
free(path);
free(line);
parent(pid, pty);
}
return 0;
}
void
child(char *line, char *path, char **argv)
{
int tty;
int pgrp;
close(0);
close(1);
close(2);
pgrp = setsid();
if(pgrp < 0) {
kill(getppid(), SIGABRT);
exit(1);
}
tty = openTty(line);
if(tty < 0) {
kill(getppid(), SIGABRT);
exit(1);
}
if(tty != 0)
dup2(tty, 0);
if(tty != 1)
dup2(tty, 1);
if(tty != 2)
dup2(tty, 2);
if(tty > 2)
close(tty);
execvp(path, argv);
perror("Couldn't exec");
exit(1);
}
static void
sigwinchHandler(int sig) {
sigwinch_queued = 1;
}
static void
sigchldHandler(int sig)
{
sigchld_queued = 1;
}
void
parent(int pid, int pty)
{
unsigned char buf[BUFFER_SIZE];
int i;
int val;
int rc;
if(verbose) {
reportIso2022(outputState);
}
#ifdef SIGWINCH
installHandler(SIGWINCH, sigwinchHandler);
#endif
installHandler(SIGCHLD, sigchldHandler);
rc = copyTermios(0, pty);
if(rc < 0)
FatalError("Couldn't copy terminal settings\n");
rc = setRawTermios();
if(rc < 0)
FatalError("Couldn't set terminal to raw\n");
val = fcntl(0, F_GETFL, 0);
if(val >= 0) {
fcntl(0, F_SETFL, val | O_NONBLOCK);
}
val = fcntl(pty, F_GETFL, 0);
if(val >= 0) {
fcntl(pty, F_SETFL, val | O_NONBLOCK);
}
setWindowSize(0, pty);
for(;;) {
rc = waitForInput(0, pty);
if(sigwinch_queued) {
sigwinch_queued = 0;
setWindowSize(0, pty);
}
if(sigchld_queued && exitOnChild)
break;
if(rc > 0) {
if(rc & 2) {
i = read(pty, buf, BUFFER_SIZE);
if((i == 0) || ((i < 0) && (errno != EAGAIN)))
break;
if(i > 0)
copyOut(outputState, 0, buf, i);
}
if(rc & 1) {
i = read(0, buf, BUFFER_SIZE);
if((i == 0) || ((i < 0) && (errno != EAGAIN)))
break;
if(i > 0)
copyIn(inputState, pty, buf, i);
}
}
}
restoreTermios();
}