luit.c revision dbe7da2e
1/* 2Copyright (c) 2001 by Juliusz Chroboczek 3 4Permission is hereby granted, free of charge, to any person obtaining a copy 5of this software and associated documentation files (the "Software"), to deal 6in the Software without restriction, including without limitation the rights 7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8copies of the Software, and to permit persons to whom the Software is 9furnished to do so, subject to the following conditions: 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20THE SOFTWARE. 21*/ 22/* $XFree86: xc/programs/luit/luit.c,v 1.9 2002/10/17 01:06:09 dawes Exp $ */ 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <locale.h> 28#include <sys/types.h> 29#include <fcntl.h> 30#include <unistd.h> 31#include <errno.h> 32#include <assert.h> 33#include <stdarg.h> 34#include <sys/ioctl.h> 35#include <signal.h> 36 37#ifdef HAVE_CONFIG_H 38# include "config.h" 39#endif 40 41#ifdef HAVE_STROPTS_H 42# include <stropts.h> 43#endif 44 45#include <X11/fonts/fontenc.h> 46#include "luit.h" 47#include "sys.h" 48#include "other.h" 49#include "charset.h" 50#include "iso2022.h" 51 52static Iso2022Ptr inputState = NULL, outputState = NULL; 53 54static char *child_argv0 = NULL; 55static char *locale_name = NULL; 56int ilog = -1; 57int olog = -1; 58int verbose = 0; 59static int converter = 0; 60static int exitOnChild = 0; 61 62static volatile int sigwinch_queued = 0; 63static volatile int sigchld_queued = 0; 64 65static int convert(int, int); 66static int condom(int, char**); 67 68static void 69ErrorF(char *f, ...) 70{ 71 va_list args; 72 va_start(args, f); 73 vfprintf(stderr, f, args); 74 va_end(args); 75} 76 77static void 78FatalError(char *f, ...) 79{ 80 va_list args; 81 va_start(args, f); 82 vfprintf(stderr, f, args); 83 va_end(args); 84 exit(1); 85} 86 87 88static void 89help(void) 90{ 91 fprintf(stderr, 92 "luit\n" 93 " [ -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" 103 " [ -c ] [ -x ] [ -ilog filename ] [ -olog filename ] [ -- ]\n" 104 " [ program [ args ] ]\n"); 105 106} 107 108 109static int 110parseOptions(int argc, char **argv) 111{ 112 int i = 1; 113 while(i < argc) { 114 if(argv[i][0] != '-' && argv[i][0] != '+') { 115 break; 116 } else if(!strcmp(argv[i], "--")) { 117 i++; 118 break; 119 } else if(!strcmp(argv[i], "-v")) { 120 verbose++; 121 i++; 122 } else if(!strcmp(argv[i], "-h")) { 123 help(); 124 exit(0); 125 } else if(!strcmp(argv[i], "-list")) { 126 reportCharsets(); 127 exit(0); 128 } else if(!strcmp(argv[i], "+oss")) { 129 outputState->outputFlags &= ~OF_SS; 130 i++; 131 } else if(!strcmp(argv[i], "+ols")) { 132 outputState->outputFlags &= ~OF_LS; 133 i++; 134 } else if(!strcmp(argv[i], "+osl")) { 135 outputState->outputFlags &= ~OF_SELECT; 136 i++; 137 } else if(!strcmp(argv[i], "+ot")) { 138 outputState->outputFlags = OF_PASSTHRU; 139 i++; 140 } else if(!strcmp(argv[i], "-k7")) { 141 inputState->inputFlags &= ~IF_EIGHTBIT; 142 i++; 143 } else if(!strcmp(argv[i], "+kss")) { 144 inputState->inputFlags &= ~IF_SS; 145 i++; 146 } else if(!strcmp(argv[1], "+kssgr")) { 147 inputState->inputFlags &= ~IF_SSGR; 148 i++; 149 } else if(!strcmp(argv[i], "-kls")) { 150 inputState->inputFlags |= IF_LS; 151 i++; 152 } else if(!strcmp(argv[i], "-g0")) { 153 if(i + 1 >= argc) 154 FatalError("-g0 requires an argument\n"); 155 G0(outputState) = getCharsetByName(argv[i + 1]); 156 i += 2; 157 } else if(!strcmp(argv[i], "-g1")) { 158 if(i + 1 >= argc) 159 FatalError("-g1 requires an argument\n"); 160 G1(outputState) = getCharsetByName(argv[i + 1]); 161 i += 2; 162 } else if(!strcmp(argv[i], "-g2")) { 163 if(i + 1 >= argc) 164 FatalError("-g2 requires an argument\n"); 165 G2(outputState) = getCharsetByName(argv[i + 1]); 166 i += 2; 167 } else if(!strcmp(argv[i], "-g3")) { 168 if(i + 1 >= argc) 169 FatalError("-g3 requires an argument\n"); 170 G3(outputState) = getCharsetByName(argv[i + 1]); 171 172 i += 2; 173 } else if(!strcmp(argv[i], "-gl")) { 174 int j; 175 if(i + 1 >= argc) 176 FatalError("-gl requires an argument\n"); 177 if(strlen(argv[i + 1]) != 2 || 178 argv[i + 1][0] != 'g') 179 j = -1; 180 else 181 j = argv[i + 1][1] - '0'; 182 if(j < 0 || j > 3) 183 FatalError("The argument of -gl " 184 "should be one of g0 through g3,\n" 185 "not %s\n", argv[i + 1]); 186 else 187 outputState->glp = &outputState->g[j]; 188 i += 2; 189 } else if(!strcmp(argv[i], "-gr")) { 190 int j; 191 if(i + 1 >= argc) 192 FatalError("-gr requires an argument\n"); 193 if(strlen(argv[i + 1]) != 2 || 194 argv[i + 1][0] != 'g') 195 j = -1; 196 else 197 j = argv[i + 1][1] - '0'; 198 if(j < 0 || j > 3) 199 FatalError("The argument of -gl " 200 "should be one of g0 through g3,\n" 201 "not %s\n", argv[i + 1]); 202 else 203 outputState->grp = &outputState->g[j]; 204 i += 2; 205 } else if(!strcmp(argv[i], "-kg0")) { 206 if(i + 1 >= argc) 207 FatalError("-kg0 requires an argument\n"); 208 G0(inputState) = getCharsetByName(argv[i + 1]); 209 i += 2; 210 } else if(!strcmp(argv[i], "-kg1")) { 211 if(i + 1 >= argc) 212 FatalError("-kg1 requires an argument\n"); 213 G1(inputState) = getCharsetByName(argv[i + 1]); 214 i += 2; 215 } else if(!strcmp(argv[i], "-kg2")) { 216 if(i + 1 >= argc) 217 FatalError("-kg2 requires an argument\n"); 218 G2(inputState) = getCharsetByName(argv[i + 1]); 219 i += 2; 220 } else if(!strcmp(argv[i], "-kg3")) { 221 if(i + 1 >= argc) 222 FatalError("-kg3 requires an argument\n"); 223 G3(inputState) = getCharsetByName(argv[i + 1]); 224 225 i += 2; 226 } else if(!strcmp(argv[i], "-kgl")) { 227 int j; 228 if(i + 1 >= argc) 229 FatalError("-kgl requires an argument\n"); 230 if(strlen(argv[i + 1]) != 2 || 231 argv[i + 1][0] != 'g') 232 j = -1; 233 else 234 j = argv[i + 1][1] - '0'; 235 if(j < 0 || j > 3) 236 FatalError("The argument of -kgl " 237 "should be one of g0 through g3,\n" 238 "not %s\n", argv[i + 1]); 239 else 240 inputState->glp = &inputState->g[j]; 241 i += 2; 242 } else if(!strcmp(argv[i], "-kgr")) { 243 int j; 244 if(i + 1 >= argc) 245 FatalError("-kgl requires an argument\n"); 246 if(strlen(argv[i + 1]) != 2 || 247 argv[i + 1][0] != 'g') 248 j = -1; 249 else 250 j = argv[i + 1][1] - '0'; 251 if(j < 0 || j > 3) 252 FatalError("The argument of -kgl " 253 "should be one of g0 through g3,\n" 254 "not %s\n", argv[i + 1]); 255 else 256 inputState->grp = &inputState->g[j]; 257 i += 2; 258 } else if(!strcmp(argv[i], "-argv0")) { 259 if(i + 1 >= argc) 260 FatalError("-argv0 requires an argument\n"); 261 child_argv0 = argv[i + 1]; 262 i += 2; 263 } else if(!strcmp(argv[i], "-x")) { 264 exitOnChild = 1; 265 i++; 266 } else if(!strcmp(argv[i], "-c")) { 267 converter = 1; 268 i++; 269 } else if(!strcmp(argv[i], "-ilog")) { 270 if(i + 1 >= argc) 271 FatalError("-ilog requires an argument\n"); 272 ilog = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0777); 273 if(ilog < 0) { 274 perror("Couldn't open input log"); 275 exit(1); 276 } 277 i += 2; 278 } else if(!strcmp(argv[i], "-olog")) { 279 if(i + 1 >= argc) 280 FatalError("-olog requires an argument\n"); 281 olog = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0777); 282 if(olog < 0) { 283 perror("Couldn't open output log"); 284 exit(1); 285 } 286 i += 2; 287 } else if(!strcmp(argv[i], "-encoding")) { 288 int rc; 289 if(i + 1 >= argc) 290 FatalError("-encoding requires an argument\n"); 291 rc = initIso2022(NULL, argv[i + 1], outputState); 292 if(rc < 0) 293 FatalError("Couldn't init output state\n"); 294 i += 2; 295 } else { 296 FatalError("Unknown option %s\n", argv[i]); 297 } 298 } 299 return i; 300} 301 302static int 303parseArgs(int argc, char **argv, char *argv0, 304 char **path_return, char ***argv_return) 305{ 306 char *path = NULL; 307 char **child_argv = NULL; 308 309 if(argc <= 0) { 310 char *shell; 311 shell = getenv("SHELL"); 312 if(shell) { 313 path = strdup(shell); 314 } else { 315 path = strdup("/bin/sh"); 316 } 317 if(!path) 318 goto bail; 319 child_argv = malloc(2 * sizeof(char*)); 320 if(!child_argv) 321 goto bail; 322 if(argv0) 323 child_argv[0] = argv0; 324 else 325 child_argv[0] = my_basename(path); 326 child_argv[1] = NULL; 327 } else { 328 path = strdup(argv[0]); 329 if(!path) 330 goto bail; 331 child_argv = malloc((argc + 1) * sizeof(char*)); 332 if(!child_argv) { 333 goto bail; 334 } 335 if(child_argv0) 336 child_argv[0] = argv0; 337 else 338 child_argv[0] = my_basename(argv[0]); 339 memcpy(child_argv + 1, argv + 1, (argc - 1) * sizeof(char*)); 340 child_argv[argc] = NULL; 341 } 342 343 *path_return = path; 344 *argv_return = child_argv; 345 return 0; 346 347 bail: 348 if(path) 349 free(path); 350 if(argv) 351 free(argv); 352 return -1; 353} 354 355 356int 357main(int argc, char **argv) 358{ 359 int rc; 360 int i; 361 char *l; 362 363 l = setlocale(LC_ALL, ""); 364 if(!l) 365 ErrorF("Warning: couldn't set locale.\n"); 366 367 inputState = allocIso2022(); 368 if(!inputState) 369 FatalError("Couldn't create input state\n"); 370 371 outputState = allocIso2022(); 372 if(!outputState) 373 FatalError("Couldn't create output state\n"); 374 375 if(l) { 376 locale_name = setlocale(LC_CTYPE, NULL); 377 } else { 378 locale_name = getenv("LC_ALL"); 379 if(locale_name == NULL) { 380 locale_name = getenv("LC_CTYPE"); 381 if(locale_name == NULL) { 382 locale_name = getenv("LANG"); 383 } 384 } 385 } 386 387 if(locale_name == NULL) { 388 ErrorF("Couldn't get locale name -- using C\n"); 389 locale_name = "C"; 390 } 391 392 rc = initIso2022(locale_name, NULL, outputState); 393 if(rc < 0) 394 FatalError("Couldn't init output state\n"); 395 396 i = parseOptions(argc, argv); 397 if(i < 0) 398 FatalError("Couldn't parse options\n"); 399 400 rc = mergeIso2022(inputState, outputState); 401 if(rc < 0) 402 FatalError("Couldn't init input state\n"); 403 404 if(converter) 405 return convert(0, 1); 406 else 407 return condom(argc - i, argv + i); 408} 409 410static int 411convert(int ifd, int ofd) 412{ 413 int rc, i; 414 unsigned char buf[BUFFER_SIZE]; 415 416 rc = droppriv(); 417 if(rc < 0) { 418 perror("Couldn't drop priviledges"); 419 exit(1); 420 } 421 422 while(1) { 423 i = read(ifd, buf, BUFFER_SIZE); 424 if(i <= 0) { 425 if(i < 0) { 426 perror("Read error"); 427 exit(1); 428 } 429 break; 430 } 431 copyOut(outputState, ofd, buf, i); 432 } 433 return 0; 434} 435 436static void 437sigwinchHandler(int sig) 438{ 439 sigwinch_queued = 1; 440} 441 442static void 443sigchldHandler(int sig) 444{ 445 sigchld_queued = 1; 446} 447 448static int 449condom(int argc, char **argv) 450{ 451 int pty; 452 int pid; 453 char *line; 454 char *path; 455 char **child_argv; 456 int rc; 457 int val; 458 459 path = NULL; 460 child_argv = NULL; 461 rc = parseArgs(argc, argv, child_argv0, 462 &path, &child_argv); 463 if(rc < 0) 464 FatalError("Couldn't parse arguments\n"); 465 466 rc = allocatePty(&pty, &line); 467 if(rc < 0) { 468 perror("Couldn't allocate pty"); 469 exit(1); 470 } 471 472 rc = droppriv(); 473 if(rc < 0) { 474 perror("Couldn't drop priviledges"); 475 exit(1); 476 } 477#ifdef SIGWINCH 478 installHandler(SIGWINCH, sigwinchHandler); 479#endif 480 installHandler(SIGCHLD, sigchldHandler); 481 482 rc = copyTermios(0, pty); 483 if(rc < 0) 484 FatalError("Couldn't copy terminal settings\n"); 485 486 rc = setRawTermios(); 487 if(rc < 0) 488 FatalError("Couldn't set terminal to raw\n"); 489 490 val = fcntl(0, F_GETFL, 0); 491 if(val >= 0) { 492 fcntl(0, F_SETFL, val | O_NONBLOCK); 493 } 494 val = fcntl(pty, F_GETFL, 0); 495 if(val >= 0) { 496 fcntl(pty, F_SETFL, val | O_NONBLOCK); 497 } 498 499 setWindowSize(0, pty); 500 501 pid = fork(); 502 if(pid < 0) { 503 perror("Couldn't fork"); 504 exit(1); 505 } 506 507 if(pid == 0) { 508 close(pty); 509#ifdef SIGWINCH 510 installHandler(SIGWINCH, SIG_DFL); 511#endif 512 installHandler(SIGCHLD, SIG_DFL); 513 child(line, path, child_argv); 514 } else { 515 free(child_argv); 516 free(path); 517 free(line); 518 parent(pid, pty); 519 } 520 521 return 0; 522} 523 524void 525child(char *line, char *path, char **argv) 526{ 527 int tty; 528 int pgrp; 529 530 close(0); 531 close(1); 532 close(2); 533 534 pgrp = setsid(); 535 if(pgrp < 0) { 536 kill(getppid(), SIGABRT); 537 exit(1); 538 } 539 540 tty = openTty(line); 541 if(tty < 0) { 542 kill(getppid(), SIGABRT); 543 exit(1); 544 } 545 546 if(tty != 0) 547 dup2(tty, 0); 548 if(tty != 1) 549 dup2(tty, 1); 550 if(tty != 2) 551 dup2(tty, 2); 552 553 if(tty > 2) 554 close(tty); 555 556 execvp(path, argv); 557 perror("Couldn't exec"); 558 exit(1); 559} 560 561void 562parent(int pid, int pty) 563{ 564 unsigned char buf[BUFFER_SIZE]; 565 int i; 566 int rc; 567 568 if(verbose) { 569 reportIso2022(outputState); 570 } 571 572 for(;;) { 573 rc = waitForInput(0, pty); 574 575 if(sigwinch_queued) { 576 sigwinch_queued = 0; 577 setWindowSize(0, pty); 578 } 579 580 if(sigchld_queued && exitOnChild) 581 break; 582 583 if(rc > 0) { 584 if(rc & 2) { 585 i = read(pty, buf, BUFFER_SIZE); 586 if((i == 0) || ((i < 0) && (errno != EAGAIN))) 587 break; 588 if(i > 0) 589 copyOut(outputState, 0, buf, i); 590 } 591 if(rc & 1) { 592 i = read(0, buf, BUFFER_SIZE); 593 if((i == 0) || ((i < 0) && (errno != EAGAIN))) 594 break; 595 if(i > 0) 596 copyIn(inputState, pty, buf, i); 597 } 598 } 599 } 600 601 restoreTermios(); 602} 603