2 Copyright (c) 2001 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 #include <sys/types.h>
37 #include <sys/ioctl.h>
46 static int pipe_option = 0;
47 static int p2c_waitpipe[2];
48 static int c2p_waitpipe[2];
50 static Iso2022Ptr inputState = NULL, outputState = NULL;
52 static char *child_argv0 = NULL;
53 static const char *locale_name = NULL;
54 static int exitOnChild = 0;
55 static int converter = 0;
57 const char *locale_alias = LOCALE_ALIAS_FILE;
63 static volatile int sigwinch_queued = 0;
64 static volatile int sigchld_queued = 0;
66 static int convert(int, int);
67 static int condom(int, char **);
70 ErrorF(const char *f,...)
74 vfprintf(stderr, f, args);
79 FatalError(const char *f,...)
83 vfprintf(stderr, f, args);
93 " [ -V ] [ -h ] [ -list ] [ -v ] [ -argv0 name ]\n"
94 " [ -gl gn ] [-gr gk] "
95 "[ -g0 set ] [ -g1 set ] "
96 "[ -g2 set ] [ -g3 set ]\n"
97 " [ -encoding encoding ] "
98 "[ +oss ] [ +ols ] [ +osl ] [ +ot ]\n"
99 " [ -kgl gn ] [-kgr gk] "
100 "[ -kg0 set ] [ -kg1 set ] "
101 "[ -kg2 set ] [ -kg3 set ]\n"
102 " [ -k7 ] [ +kss ] [ +kssgr ] [ -kls ]\n"
106 "[ -ilog filename ] "
107 "[ -olog filename ] "
108 "[ -alias filename ] "
110 " [ program [ args ] ]\n");
114 parseOptions(int argc, char **argv)
118 if (argv[i][0] != '-' && argv[i][0] != '+') {
120 } else if (!strcmp(argv[i], "--")) {
123 } else if (!strcmp(argv[i], "-v")) {
126 } else if (!strcmp(argv[i], "-V")) {
127 printf("%s - %s\n", argv[0], VERSION);
129 } else if (!strcmp(argv[i], "-h")) {
132 } else if (!strcmp(argv[i], "-list")) {
135 } else if (!strcmp(argv[i], "+oss")) {
136 outputState->outputFlags &= ~OF_SS;
138 } else if (!strcmp(argv[i], "+ols")) {
139 outputState->outputFlags &= ~OF_LS;
141 } else if (!strcmp(argv[i], "+osl")) {
142 outputState->outputFlags &= ~OF_SELECT;
144 } else if (!strcmp(argv[i], "+ot")) {
145 outputState->outputFlags = OF_PASSTHRU;
147 } else if (!strcmp(argv[i], "-k7")) {
148 inputState->inputFlags &= ~IF_EIGHTBIT;
150 } else if (!strcmp(argv[i], "+kss")) {
151 inputState->inputFlags &= ~IF_SS;
153 } else if (!strcmp(argv[1], "+kssgr")) {
154 inputState->inputFlags &= ~IF_SSGR;
156 } else if (!strcmp(argv[i], "-kls")) {
157 inputState->inputFlags |= IF_LS;
159 } else if (!strcmp(argv[i], "-g0")) {
161 FatalError("-g0 requires an argument\n");
162 G0(outputState) = getCharsetByName(argv[i + 1]);
164 } else if (!strcmp(argv[i], "-g1")) {
166 FatalError("-g1 requires an argument\n");
167 G1(outputState) = getCharsetByName(argv[i + 1]);
169 } else if (!strcmp(argv[i], "-g2")) {
171 FatalError("-g2 requires an argument\n");
172 G2(outputState) = getCharsetByName(argv[i + 1]);
174 } else if (!strcmp(argv[i], "-g3")) {
176 FatalError("-g3 requires an argument\n");
177 G3(outputState) = getCharsetByName(argv[i + 1]);
180 } else if (!strcmp(argv[i], "-gl")) {
183 FatalError("-gl requires an argument\n");
184 if (strlen(argv[i + 1]) != 2 ||
185 argv[i + 1][0] != 'g')
188 j = argv[i + 1][1] - '0';
190 FatalError("The argument of -gl "
191 "should be one of g0 through g3,\n"
192 "not %s\n", argv[i + 1]);
194 outputState->glp = &outputState->g[j];
196 } else if (!strcmp(argv[i], "-gr")) {
199 FatalError("-gr requires an argument\n");
200 if (strlen(argv[i + 1]) != 2 ||
201 argv[i + 1][0] != 'g')
204 j = argv[i + 1][1] - '0';
206 FatalError("The argument of -gl "
207 "should be one of g0 through g3,\n"
208 "not %s\n", argv[i + 1]);
210 outputState->grp = &outputState->g[j];
212 } else if (!strcmp(argv[i], "-kg0")) {
214 FatalError("-kg0 requires an argument\n");
215 G0(inputState) = getCharsetByName(argv[i + 1]);
217 } else if (!strcmp(argv[i], "-kg1")) {
219 FatalError("-kg1 requires an argument\n");
220 G1(inputState) = getCharsetByName(argv[i + 1]);
222 } else if (!strcmp(argv[i], "-kg2")) {
224 FatalError("-kg2 requires an argument\n");
225 G2(inputState) = getCharsetByName(argv[i + 1]);
227 } else if (!strcmp(argv[i], "-kg3")) {
229 FatalError("-kg3 requires an argument\n");
230 G3(inputState) = getCharsetByName(argv[i + 1]);
233 } else if (!strcmp(argv[i], "-kgl")) {
236 FatalError("-kgl requires an argument\n");
237 if (strlen(argv[i + 1]) != 2 ||
238 argv[i + 1][0] != 'g')
241 j = argv[i + 1][1] - '0';
243 FatalError("The argument of -kgl "
244 "should be one of g0 through g3,\n"
245 "not %s\n", argv[i + 1]);
247 inputState->glp = &inputState->g[j];
249 } else if (!strcmp(argv[i], "-kgr")) {
252 FatalError("-kgl requires an argument\n");
253 if (strlen(argv[i + 1]) != 2 ||
254 argv[i + 1][0] != 'g')
257 j = argv[i + 1][1] - '0';
259 FatalError("The argument of -kgl "
260 "should be one of g0 through g3,\n"
261 "not %s\n", argv[i + 1]);
263 inputState->grp = &inputState->g[j];
265 } else if (!strcmp(argv[i], "-argv0")) {
267 FatalError("-argv0 requires an argument\n");
268 child_argv0 = argv[i + 1];
270 } else if (!strcmp(argv[i], "-x")) {
273 } else if (!strcmp(argv[i], "-c")) {
276 } else if (!strcmp(argv[i], "-ilog")) {
278 FatalError("-ilog requires an argument\n");
279 ilog = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0777);
281 perror("Couldn't open input log");
285 } else if (!strcmp(argv[i], "-olog")) {
287 FatalError("-olog requires an argument\n");
288 olog = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0777);
290 perror("Couldn't open output log");
294 } else if (!strcmp(argv[i], "-alias")) {
296 FatalError("-alias requires an argument\n");
297 locale_alias = argv[i + 1];
299 } else if (!strcmp(argv[i], "-encoding")) {
301 FatalError("-encoding requires an argument\n");
302 locale_name = argv[i + 1];
304 } else if (!strcmp(argv[i], "-p")) {
308 FatalError("Unknown option %s\n", argv[i]);
315 parseArgs(int argc, char **argv,
321 char **child_argv = NULL;
325 shell = getenv("SHELL");
327 path = strmalloc(shell);
331 path = strmalloc("/bin/sh");
335 child_argv = malloc(2 * sizeof(char *));
339 child_argv[0] = argv0;
341 child_argv[0] = my_basename(path);
342 child_argv[1] = NULL;
344 path = strmalloc(argv[0]);
347 child_argv = malloc((unsigned) (argc + 1) * sizeof(char *));
352 child_argv[0] = argv0;
354 child_argv[0] = my_basename(argv[0]);
355 memcpy(child_argv + 1, argv + 1, (unsigned) (argc - 1) * sizeof(char *));
356 child_argv[argc] = NULL;
360 *argv_return = child_argv;
372 main(int argc, char **argv)
379 if ((l = strmalloc("NCURSES_NO_UTF8_ACS=1")) != 0)
383 l = setlocale(LC_ALL, "");
385 ErrorF("Warning: couldn't set locale.\n");
387 inputState = allocIso2022();
389 FatalError("Couldn't create input state\n");
391 outputState = allocIso2022();
393 FatalError("Couldn't create output state\n");
396 locale_name = setlocale(LC_CTYPE, NULL);
398 locale_name = getenv("LC_ALL");
399 if (locale_name == NULL) {
400 locale_name = getenv("LC_CTYPE");
401 if (locale_name == NULL) {
402 locale_name = getenv("LANG");
407 if (locale_name == NULL) {
408 ErrorF("Couldn't get locale name -- using C\n");
412 i = parseOptions(argc, argv);
414 FatalError("Couldn't parse options\n");
416 rc = initIso2022(locale_name, NULL, outputState);
418 FatalError("Couldn't init output state\n");
420 rc = mergeIso2022(inputState, outputState);
422 FatalError("Couldn't init input state\n");
427 rc = condom(argc - i, argv + i);
436 convert(int ifd, int ofd)
439 unsigned char buf[BUFFER_SIZE];
443 perror("Couldn't drop privileges");
448 i = (int) read(ifd, buf, (size_t) BUFFER_SIZE);
451 perror("Read error");
456 copyOut(outputState, ofd, buf, (unsigned) i);
463 sigwinchHandler(int sig GCC_UNUSED)
470 sigchldHandler(int sig GCC_UNUSED)
482 installHandler(SIGWINCH, sigwinchHandler);
484 installHandler(SIGCHLD, sigchldHandler);
486 rc = copyTermios(0, pty);
488 FatalError("Couldn't copy terminal settings\n");
490 rc = setRawTermios();
492 FatalError("Couldn't set terminal to raw\n");
494 val = fcntl(0, F_GETFL, 0);
496 fcntl(0, F_SETFL, val | O_NONBLOCK);
498 val = fcntl(pty, F_GETFL, 0);
500 fcntl(pty, F_SETFL, val | O_NONBLOCK);
503 setWindowSize(0, pty);
514 installHandler(SIGWINCH, SIG_DFL);
516 installHandler(SIGCHLD, SIG_DFL);
518 val = fcntl(0, F_GETFL, 0);
520 fcntl(0, F_SETFL, val & ~O_NONBLOCK);
522 val = fcntl(pty, F_GETFL, 0);
524 fcntl(pty, F_SETFL, val & ~O_NONBLOCK);
529 close_waitpipe(int which)
531 close(p2c_waitpipe[which]);
532 close(c2p_waitpipe[!which]);
536 write_waitpipe(int fds[2])
538 IGNORE_RC(write(fds[1], "1", (size_t) 1));
542 read_waitpipe(int fds[2])
545 IGNORE_RC(read(fds[0], tmp, (size_t) 1));
549 condom(int argc, char **argv)
555 char **child_argv = 0;
558 rc = parseArgs(argc, argv, child_argv0,
561 FatalError("Couldn't parse arguments\n");
563 rc = allocatePty(&pty, &line);
565 perror("Couldn't allocate pty");
571 perror("Couldn't drop privileges");
576 IGNORE_RC(pipe(p2c_waitpipe));
577 IGNORE_RC(pipe(c2p_waitpipe));
582 perror("Couldn't fork");
591 child(line, path, child_argv);
606 child(char *line, char *path, char *const argv[])
617 kill(getppid(), SIGABRT);
623 kill(getppid(), SIGABRT);
627 write_waitpipe(c2p_waitpipe);
641 read_waitpipe(p2c_waitpipe);
646 perror("Couldn't exec");
651 parent(int pid GCC_UNUSED, int pty)
653 unsigned char buf[BUFFER_SIZE];
658 read_waitpipe(c2p_waitpipe);
662 reportIso2022(outputState);
667 write_waitpipe(p2c_waitpipe);
672 rc = waitForInput(0, pty);
674 if (sigwinch_queued) {
676 setWindowSize(0, pty);
679 if (sigchld_queued && exitOnChild)
684 i = (int) read(pty, buf, (size_t) BUFFER_SIZE);
685 if ((i == 0) || ((i < 0) && (errno != EAGAIN)))
688 copyOut(outputState, 0, buf, (unsigned) i);
691 i = (int) read(0, buf, (size_t) BUFFER_SIZE);
692 if ((i == 0) || ((i < 0) && (errno != EAGAIN)))
695 copyIn(inputState, pty, buf, i);
708 destroyIso2022(inputState);
709 destroyIso2022(outputState);