1 /*
2
3 Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. Neither the name of authors nor the names of its contributors
17 may be used to endorse or promote products derived from this software
18 without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 SUCH DAMAGE.
31
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37 #if (!defined(DEBUG) && !defined(NDEBUG))
38 #define NDEBUG
39 #endif
40 #include <stdio.h>
41 #ifdef HAVE_ASSERT_H
42 #include <assert.h>
43 #endif
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 #ifdef HAVE_SYS_TYPES_H
48 #include <sys/types.h>
49 #endif
50 #ifdef HAVE_SIGNAL_H
51 #include <signal.h>
52 #endif
53 #ifdef HAVE_STDLIB_H
54 #include <stdlib.h>
55 #endif
56 #ifdef HAVE_STRING_H
57 #include <string.h>
58 #endif
59 #ifdef HAVE_STRINGS_H
60 #include <strings.h>
61 #endif
62 /* solarisでは term.hの前にcurses.hが必要 */
63 #ifdef HAVE_CURSES_H
64 #include <curses.h>
65 #endif
66 #ifdef HAVE_TERM_H
67 #include <term.h>
68 #elif HAVE_NCURSES_TERM_H
69 #include <ncurses/term.h>
70 #endif
71 #ifdef HAVE_STDARG_H
72 #include <stdarg.h>
73 #endif
74 #ifdef HAVE_GETOPT_H
75 #include <getopt.h>
76 #endif
77 #ifdef HAVE_PWD_H
78 #include <pwd.h>
79 #endif
80 #ifdef HAVE_FCNTL_H
81 #include <fcntl.h>
82 #endif
83 #ifdef HAVE_CTYPE_H
84 #include <ctype.h>
85 #endif
86 #ifdef HAVE_SYS_STAT_H
87 #include <sys/stat.h>
88 #endif
89 #ifdef HAVE_SYS_TIME_H
90 #include <sys/time.h>
91 #endif
92 #ifdef HAVE_STROPTS_H
93 #include <stropts.h>
94 #endif
95 #ifdef HAVE_SYS_PARAM_H
96 #include <sys/param.h>
97 #endif
98
99 #ifdef HAVE_PTY_H
100 #include <pty.h>
101 #endif
102 #ifdef HAVE_UTMP_H
103 #include <utmp.h>
104 #endif
105 #ifdef HAVE_UTIL_H
106 #include <util.h>
107 #endif
108 #ifdef HAVE_LIBUTIL_H
109 #include <libutil.h>
110 #endif
111 #ifdef HAVE_TERMIOS_H
112 #include <termios.h>
113 #endif
114
115 #include <uim/uim.h>
116
117 #include "udsock.h"
118 #include "str.h"
119 #include "uim-fep.h"
120 #include "callbacks.h"
121 #include "draw.h"
122 #include "escseq.h"
123 #include "key.h"
124 #include "read.h"
125 #include "helper.h"
126
127 #define DEFAULT_STATUS LASTLINE
128
129 /* global variables */
130 struct opt_tag g_opt = {
131 DEFAULT_STATUS, /* status_type */
132 FALSE, /* ddskk */
133 FALSE, /* cursor_no_reverse */
134 FALSE, /* use_civis */
135 FALSE, /* on_the_spot */
136 UNDEFINED, /* statusline_width */
137 0, /* timeout */
138 FALSE, /* no_report_cursor */
139 FALSE /* print_key */
140 };
141 int g_win_in = STDIN_FILENO;
142 int g_win_out = STDOUT_FILENO;
143 struct winsize *g_win;
144 uim_context g_context;
145
146 /* 疑似端末のmasterのファイル記述子 */
147 static int s_master;
148 /* 起動時の端末状態 */
149 static struct termios s_save_tios;
150
151 static char s_path_setmode[MAXPATHLEN];
152 static char s_path_getmode[MAXPATHLEN];
153 static int s_setmode_fd = -1;
154 #ifndef HAVE_SIG_ATOMIC_T
155 typedef int sig_atomic_t;
156 #endif
157 static volatile sig_atomic_t s_signal_flag;
158 static sigset_t s_orig_sigmask;
159
160 #define SIG_FLAG_DONE 1
161 #define SIG_FLAG_RECOVER (1 << 1)
162 #define SIG_FLAG_WINCH (1 << 2)
163 #define SIG_FLAG_USR1 (1 << 3)
164 #define SIG_FLAG_USR2 (1 << 4)
165 #define SIG_FLAG_TSTP (1 << 5)
166
167 static void init_uim(const char *engine);
168 static const char *get_default_im_name(void);
169 static int make_color_escseq(const char *instr, struct attribute_tag *attr);
170 static int colorname2n(const char *name);
171 static pid_t my_forkpty(int *amaster, struct termios *termp, struct winsize *winp);
172 static void main_loop(void);
173 static void recover_loop(void);
174 static struct winsize *get_winsize(void);
175 static void set_signal_handler(void);
176 static void reset_signal_handler(void);
177 static void signal_handler(int sig_no);
178 static void recover(void);
179 static void sigtstp_handler(void);
180 static void sigwinch_handler(void);
181 static void sigusr1_handler(void);
182 static void sigusr2_handler(void);
183 static void usage(void);
184 static void version(void);
185
186
187 /*
188 * uimを初期化する
189 * engine 変換エンジンの名前
190 */
init_uim(const char * engine)191 static void init_uim(const char *engine)
192 {
193 int nr;
194 int i;
195 if (uim_init() == -1) {
196 printf("uim_init error\n");
197 exit(EXIT_FAILURE);
198 }
199 g_context = uim_create_context(NULL, get_enc(), NULL, engine, NULL, commit_cb);
200 nr = uim_get_nr_im(g_context);
201 for (i = 0; i < nr; i++) {
202 if (strcmp(engine, uim_get_im_name(g_context, i)) == 0) {
203 break;
204 }
205 }
206 if (i == nr) {
207 printf("%s is not an available input method\n\n", engine);
208 usage();
209 exit(EXIT_FAILURE);
210 }
211 }
212
get_default_im_name(void)213 static const char *get_default_im_name(void)
214 {
215 const char *engine;
216
217 /* uim_get_default_im_name() requires initialized Scheme interpreter */
218 if (uim_init() == -1) {
219 printf("uim_init error\n");
220 exit(EXIT_FAILURE);
221 }
222
223 engine = uim_get_default_im_name(""); /* instructs native locale */
224
225 /* the string is only valid until next uim API call */
226 return engine;
227 }
228
main(int argc,char ** argv)229 int main(int argc, char **argv)
230 {
231 /* command will be execed on pty */
232 const char **command = uim_malloc(sizeof(const char *) * (argc + 1));
233 char *engine;
234 char *sock_path = NULL; /* Socket for backtick */
235 int gnu_screen = FALSE;
236 char pid_str[30];
237 struct attribute_tag attr_uim = {
238 FALSE, /* underline */
239 FALSE, /* standout */
240 FALSE, /* bold */
241 FALSE, /* blink */
242 UNDEFINED, /* foreground */
243 UNDEFINED /* background */
244 };
245 FILE *fp;
246 const char *suffix = NULL;
247 char uim_dir[UNIX_PATH_MAX];
248 const char *sty_str;
249 const char *win_str;
250 struct stat stat_buf;
251 const char *errstr;
252
253 int op;
254
255 if ((command[0] = getenv("SHELL")) == NULL || *command[0] == '\0') {
256 struct passwd *pw;
257 if ((pw = getpwuid(getuid())) == NULL || *(command[0] = pw->pw_shell) == '\0') {
258 command[0] = "/bin/sh";
259 }
260 }
261 command[1] = NULL;
262
263 init_str();
264 engine = uim_strdup(get_default_im_name());
265
266 while ((op = getopt(argc, argv, "e:s:u:b:w:t:C:f:SXciodKvh")) != -1) {
267 int i;
268 switch (op) {
269 case 'e':
270 command[0] = optarg;
271 for (i = 1; i < 1 + argc - optind; i++) {
272 command[i] = argv[optind - 1 + i];
273 }
274 command[i] = NULL;
275 opterr = 0;
276 getopt(argc = optind, argv, "");
277 goto opt_end;
278
279 case 's':
280 if (strncmp(optarg, "none", strlen(optarg)) == 0) {
281 g_opt.status_type = NONE;
282 }
283 else if (strncmp(optarg, "backtick", strlen(optarg)) == 0) {
284 g_opt.status_type = BACKTICK;
285 }
286 else if (strncmp(optarg, "lastline", strlen(optarg)) == 0) {
287 g_opt.status_type = LASTLINE;
288 }
289 else {
290 usage();
291 return EXIT_FAILURE;
292 }
293 break;
294
295 case 'S':
296 gnu_screen = TRUE;
297 g_opt.no_report_cursor = TRUE;
298 break;
299
300 case 'X':
301 gnu_screen = TRUE;
302 break;
303
304 case 'd':
305 g_opt.ddskk = TRUE;
306 break;
307
308 case 'K':
309 g_opt.print_key = TRUE;
310 g_opt.status_type = NONE;
311 g_opt.no_report_cursor = TRUE;
312 break;
313
314 case 'u':
315 engine = uim_strdup(optarg);
316 break;
317
318 case 'c':
319 g_opt.cursor_no_reverse = TRUE;
320 break;
321
322 case 'i':
323 g_opt.use_civis = TRUE;
324 break;
325
326 case 'o':
327 g_opt.on_the_spot = TRUE;
328 break;
329
330 case 'b':
331 sock_path = optarg;
332 break;
333
334 case 'f':
335 suffix = optarg;
336 break;
337
338 case 'w':
339 g_opt.statusline_width = strtonum(optarg, 1, 10000, &errstr);
340 if (errstr) {
341 printf("'%s' is %s", optarg, errstr);
342 return EXIT_FAILURE;
343 }
344 if (g_opt.statusline_width <= 0) {
345 usage();
346 return EXIT_FAILURE;
347 }
348 break;
349
350 case 't':
351 g_opt.timeout = atof(optarg) * 1000000;
352 if (g_opt.timeout <= 0) {
353 usage();
354 return EXIT_FAILURE;
355 }
356 break;
357
358 case 'C':
359 if (make_color_escseq(optarg, &attr_uim) == EXIT_FAILURE) {
360 usage();
361 return EXIT_FAILURE;
362 }
363 break;
364
365 case 'v':
366 version();
367 return EXIT_SUCCESS;
368
369 case 'h':
370 usage();
371 return EXIT_SUCCESS;
372
373 case '?':
374 usage();
375 return EXIT_FAILURE;
376 }
377 }
378 opt_end:
379
380 if (optind != argc) {
381 usage();
382 return EXIT_FAILURE;
383 }
384
385 if (gnu_screen) {
386 g_opt.status_type = BACKTICK;
387 s_master = PROC_FILENO;
388 g_win_in = WIN_IN_FILENO;
389 g_win_out = WIN_OUT_FILENO;
390 }
391
392 if (g_opt.status_type == BACKTICK) {
393 init_sendsocket(sock_path);
394 }
395
396 if (getenv("UIM_FEP_PID")) {
397 if (gnu_screen) {
398 sendline("uim-fep is already running");
399 } else {
400 puts("uim-fep is already running");
401 }
402 return EXIT_FAILURE;
403 }
404
405 if (attr_uim.foreground == UNDEFINED) {
406 attr_uim.foreground = FALSE;
407 }
408 if (attr_uim.background == UNDEFINED) {
409 attr_uim.background = FALSE;
410 }
411
412 if (!isatty(g_win_in)) {
413 g_win_in = open("/dev/tty", O_RDONLY);
414 }
415
416 tcgetattr(g_win_in, &s_save_tios);
417 setupterm(NULL, g_win_out, NULL);
418
419 if (!get_ud_path(uim_dir, sizeof(uim_dir))) {
420 sendline("uim-fep cannot make directory");
421 /* return EXIT_FAILURE; */
422 }
423
424 sty_str = getenv("STY");
425 win_str = getenv("WINDOW");
426 if (gnu_screen) {
427 if (!(sty_str != NULL && win_str != NULL)) {
428 puts("STY and WINDOW are not defined");
429 return EXIT_FAILURE;
430 }
431 snprintf(s_path_getmode, sizeof(s_path_getmode), "%s/getmode-%s-%s", uim_dir, sty_str, win_str);
432 snprintf(s_path_setmode, sizeof(s_path_setmode), "%s/setmode-%s-%s", uim_dir, sty_str, win_str);
433 if (stat(s_path_getmode, &stat_buf) == 0 || stat(s_path_setmode, &stat_buf) == 0) {
434 char msg[100];
435 snprintf(msg, sizeof(msg), "uim-fep is already running on window %s", win_str);
436 sendline(msg);
437 return EXIT_FAILURE;
438 }
439 if (suffix != NULL) {
440 snprintf(s_path_getmode, sizeof(s_path_getmode), "%s/getmode-%s", uim_dir, suffix);
441 snprintf(s_path_setmode, sizeof(s_path_setmode), "%s/setmode-%s", uim_dir, suffix);
442 } else {
443 snprintf(s_path_getmode, sizeof(s_path_getmode), "%s/getmode-%s-%s-screen", uim_dir, sty_str, win_str);
444 snprintf(s_path_setmode, sizeof(s_path_setmode), "%s/setmode-%s-%s-screen", uim_dir, sty_str, win_str);
445 }
446 } else {
447 if (sty_str != NULL && win_str != NULL) {
448 snprintf(s_path_getmode, sizeof(s_path_getmode), "%s/getmode-%s-%s-screen", uim_dir, sty_str, win_str);
449 snprintf(s_path_setmode, sizeof(s_path_setmode), "%s/setmode-%s-%s-screen", uim_dir, sty_str, win_str);
450 if (stat(s_path_getmode, &stat_buf) == 0 || stat(s_path_setmode, &stat_buf) == 0) {
451 printf("uim-fep is already running on window %s as filter\n", win_str);
452 return EXIT_FAILURE;
453 }
454 if (suffix != NULL) {
455 snprintf(s_path_getmode, sizeof(s_path_getmode), "%s/getmode-%s", uim_dir, suffix);
456 snprintf(s_path_setmode, sizeof(s_path_setmode), "%s/setmode-%s", uim_dir, suffix);
457 } else {
458 snprintf(s_path_getmode, sizeof(s_path_getmode), "%s/getmode-%s-%s", uim_dir, sty_str, win_str);
459 snprintf(s_path_setmode, sizeof(s_path_setmode), "%s/setmode-%s-%s", uim_dir, sty_str, win_str);
460 }
461 } else {
462 if (suffix != NULL) {
463 snprintf(s_path_getmode, sizeof(s_path_getmode), "%s/getmode-%s", uim_dir, suffix);
464 snprintf(s_path_setmode, sizeof(s_path_setmode), "%s/setmode-%s", uim_dir, suffix);
465 } else {
466 int file_suffix = 1;
467 int pid = getpid();
468
469 snprintf(s_path_getmode, sizeof(s_path_getmode), "%s/getmode-%d", uim_dir, pid);
470 snprintf(s_path_setmode, sizeof(s_path_setmode), "%s/setmode-%d", uim_dir, pid);
471 while (stat(s_path_getmode, &stat_buf) == 0 || stat(s_path_setmode, &stat_buf) == 0) {
472 snprintf(s_path_getmode, sizeof(s_path_getmode), "%s/getmode-%d-%d", uim_dir, pid, file_suffix);
473 snprintf(s_path_setmode, sizeof(s_path_setmode), "%s/setmode-%d-%d", uim_dir, pid, file_suffix);
474 file_suffix++;
475 }
476 }
477 }
478 }
479
480 snprintf(pid_str, sizeof(pid_str), "%d", getpid());
481 setenv("UIM_FEP_PID", pid_str, 1);
482
483 if ((fp = fopen(s_path_getmode, "wt")) != NULL) {
484 fclose(fp);
485 unlink(s_path_getmode);
486 setenv("UIM_FEP_GETMODE", s_path_getmode, 1);
487 } else {
488 s_path_getmode[0] = '\0';
489 }
490
491 unlink(s_path_setmode);
492 if (mkfifo(s_path_setmode, 0600) != -1) {
493 unlink(s_path_setmode);
494 setenv("UIM_FEP_SETMODE", s_path_setmode, 1);
495 } else {
496 s_path_setmode[0] = '\0';
497 s_setmode_fd = -1;
498 }
499
500 g_win = get_winsize();
501 if (!gnu_screen && !g_opt.print_key) {
502 pid_t child = my_forkpty(&s_master, &s_save_tios, g_win);
503
504 if (child < 0) {
505 perror("fork");
506 return EXIT_FAILURE;
507 }
508 if (child == 0) {
509 /* 子プロセス */
510 if (g_win_in != STDIN_FILENO) {
511 close(g_win_in);
512 }
513 execvp(command[0], (char *const *)command);
514 perror(command[0]);
515 done(EXIT_FAILURE);
516 }
517 }
518
519 free(command);
520
521 if (g_opt.status_type == BACKTICK && g_opt.statusline_width > CANDSIZE / 2) {
522 g_opt.statusline_width = CANDSIZE / 2;
523 }
524
525 init_uim(engine);
526 free(engine);
527 if (gnu_screen) {
528 uim_set_mode(g_context, 1);
529 }
530 init_helper();
531 init_callbacks();
532 focus_in();
533 init_draw(s_master, s_path_getmode);
534 init_escseq(&attr_uim);
535 set_signal_handler();
536
537 if (s_path_setmode[0] != '\0' && mkfifo(s_path_setmode, 0600) != -1) {
538 s_setmode_fd = open(s_path_setmode, O_RDONLY | O_NONBLOCK);
539 #ifndef __CYGWIN32__
540 open(s_path_setmode, O_WRONLY);
541 #endif
542 } else {
543 s_path_setmode[0] = '\0';
544 s_setmode_fd = -1;
545 }
546
547 if (g_opt.print_key) {
548 printf("Press any key.\r\n");
549 printf("To exit the program, press 'q' key.\r\n");
550 }
551 main_loop();
552 done(EXIT_SUCCESS);
553 return EXIT_SUCCESS;
554 }
555
make_color_escseq(const char * instr,struct attribute_tag * attr)556 static int make_color_escseq(const char *instr, struct attribute_tag *attr)
557 {
558 char *colon;
559 if ((colon = strchr(instr, ':')) == NULL) {
560 return EXIT_FAILURE;
561 }
562 if (instr < colon) {
563 colon[0] = '\0';
564 if ((attr->foreground = colorname2n(instr)) == UNDEFINED) {
565 colon[0] = ':';
566 return EXIT_FAILURE;
567 }
568 colon[0] = ':';
569 }
570 instr = colon + 1;
571 if (instr[0] != '\0') {
572 if ((attr->background = colorname2n(instr)) == UNDEFINED) {
573 return EXIT_FAILURE;
574 }
575 }
576 if (attr->foreground != UNDEFINED) {
577 attr->bold = (attr->foreground & 8) == 8;
578 attr->foreground = (attr->foreground & 7) + 30;
579 } else {
580 attr->foreground = FALSE;
581 }
582 if (attr->background != UNDEFINED) {
583 attr->blink = (attr->background & 8) == 8;
584 attr->background = (attr->background & 7) + 40;
585 } else {
586 attr->background = FALSE;
587 }
588 return EXIT_SUCCESS;
589 }
590
colorname2n(const char * name)591 static int colorname2n(const char *name)
592 {
593 if (strcasecmp(name, "black") == 0 || strcasecmp(name, "k") == 0) {
594 return 0;
595 }
596 if (strcasecmp(name, "red") == 0 || strcasecmp(name, "r") == 0) {
597 return 1;
598 }
599 if (strcasecmp(name, "green") == 0 || strcasecmp(name, "g") == 0) {
600 return 2;
601 }
602 if (strcasecmp(name, "yellow") == 0 || strcasecmp(name, "y") == 0) {
603 return 3;
604 }
605 if (strcasecmp(name, "blue") == 0 || strcasecmp(name, "b") == 0) {
606 return 4;
607 }
608 if (strcasecmp(name, "magenta") == 0 || strcasecmp(name, "m") == 0) {
609 return 5;
610 }
611 if (strcasecmp(name, "cyan") == 0 || strcasecmp(name, "c") == 0) {
612 return 6;
613 }
614 if (strcasecmp(name, "white") == 0 || strcasecmp(name, "w") == 0) {
615 return 7;
616 }
617 if (strcasecmp(name, "lightblack") == 0 || strcasecmp(name, "lk") == 0) {
618 return 8;
619 }
620 if (strcasecmp(name, "lightred") == 0 || strcasecmp(name, "lr") == 0) {
621 return 9;
622 }
623 if (strcasecmp(name, "lightgreen") == 0 || strcasecmp(name, "lg") == 0) {
624 return 10;
625 }
626 if (strcasecmp(name, "lightyellow") == 0 || strcasecmp(name, "ly") == 0) {
627 return 11;
628 }
629 if (strcasecmp(name, "lightblue") == 0 || strcasecmp(name, "lb") == 0) {
630 return 12;
631 }
632 if (strcasecmp(name, "lightmagenta") == 0 || strcasecmp(name, "lm") == 0) {
633 return 13;
634 }
635 if (strcasecmp(name, "lightcyan") == 0 || strcasecmp(name, "lc") == 0) {
636 return 14;
637 }
638 if (strcasecmp(name, "lightwhite") == 0 || strcasecmp(name, "lw") == 0) {
639 return 15;
640 }
641 return UNDEFINED;
642 }
643
644 #if defined(HAVE_FORKPTY)
my_forkpty(int * amaster,struct termios * termp,struct winsize * winp)645 static pid_t my_forkpty(int *amaster, struct termios *termp, struct winsize *winp)
646 {
647 pid_t pid;
648 int slave;
649
650 tcflag_t save_iflag = termp->c_iflag;
651 termp->c_iflag &= ~ISTRIP;
652
653 if (openpty(amaster, &slave, NULL, termp, g_win) == -1) {
654 perror("openpty");
655 return -1;
656 }
657
658 termp->c_iflag = save_iflag;
659
660 if ((pid = fork()) < 0) {
661 return -2;
662 }
663 if (pid == 0) {
664 /* 子プロセス */
665 int redirected_stdin = dup(STDIN_FILENO);
666 close(*amaster);
667 login_tty(slave);
668 if (g_win_in != STDIN_FILENO) {
669 dup2(redirected_stdin, STDIN_FILENO);
670 }
671 close(redirected_stdin);
672 return 0;
673 } else {
674 close(slave);
675 return pid;
676 }
677 }
678 #elif defined(__svr4__) || defined(__sgi__) || defined(__SVR4)
my_forkpty(int * amaster,struct termios * termp,struct winsize * winp)679 static pid_t my_forkpty(int *amaster, struct termios *termp, struct winsize *winp)
680 {
681 pid_t pid;
682 int slave;
683 if ((*amaster = open("/dev/ptmx", O_RDWR)) < 0) {
684 return -1;
685 }
686 if ((pid = fork()) < 0) {
687 return -2;
688 }
689 if (pid == 0) {
690 setsid();
691 grantpt(*amaster);
692 unlockpt(*amaster);
693 if ((slave = open(ptsname(*amaster), O_RDWR)) < 0) {
694 return -3;
695 }
696 if (ioctl(slave, I_PUSH, "ptem") < 0) {
697 return -4;
698 }
699 if (ioctl(slave, I_PUSH, "ldterm") < 0) {
700 return -5;
701 }
702 if (ioctl(slave, I_PUSH, "ttcompat") < 0) {
703 return -6;
704 }
705 if (termp != NULL) {
706 tcflag_t save_iflag = termp->c_iflag;
707 termp->c_iflag &= ~ISTRIP;
708 tcsetattr(slave, TCSAFLUSH, termp);
709 termp->c_iflag = save_iflag;
710 }
711 if (winp != NULL) {
712 ioctl(slave, TIOCSWINSZ, winp);
713 }
714 close(*amaster);
715 if (g_win_in == STDIN_FILENO) {
716 dup2(slave, STDIN_FILENO);
717 }
718 dup2(slave, STDOUT_FILENO);
719 dup2(slave, STDERR_FILENO);
720 close(slave);
721 return 0;
722 } else {
723 return pid;
724 }
725 }
726 #endif
727
728 #define BUFSIZE 4096
main_loop(void)729 static void main_loop(void)
730 {
731 char buf[BUFSIZE];
732 ssize_t len;
733 fd_set fds;
734 int nfd;
735 char *_clear_screen = cut_padding(clear_screen);
736 char *_clr_eos = cut_padding(clr_eos);
737 const char *errstr;
738
739 if (g_win_in > s_master) {
740 if (g_win_in > s_setmode_fd) {
741 nfd = g_win_in;
742 } else {
743 nfd = s_setmode_fd;
744 }
745 } else {
746 if (s_master > s_setmode_fd) {
747 nfd = s_master;
748 } else {
749 nfd = s_setmode_fd;
750 }
751 }
752 if (g_helper_fd > nfd) {
753 nfd = g_helper_fd;
754 }
755 nfd++;
756
757 while (TRUE) {
758 /* コミットされたときにプリエディットがあるか */
759 if (is_commit_and_preedit()) {
760 struct timeval t;
761 FD_ZERO(&fds);
762 FD_SET(g_win_in, &fds);
763 FD_SET(s_master, &fds);
764 if (s_setmode_fd >= 0) {
765 FD_SET(s_setmode_fd, &fds);
766 }
767 t.tv_sec = 0;
768 /* 0.1秒 */
769 t.tv_usec = 100000;
770 if (my_select(nfd, &fds, &t) == 0) {
771 /* タイムアウトした */
772 draw_commit_and_preedit();
773 debug2(("<end draw_commit_and_preedit>"));
774 }
775 }
776
777 FD_ZERO(&fds);
778 FD_SET(g_win_in, &fds);
779 FD_SET(s_master, &fds);
780 if (s_setmode_fd >= 0) {
781 FD_SET(s_setmode_fd, &fds);
782 }
783 if (g_helper_fd >= 0) {
784 FD_SET(g_helper_fd, &fds);
785 }
786
787 if (my_pselect(nfd, &fds, &s_orig_sigmask) <= 0) {
788 /* signalで割り込まれたときにくる。selectの返り値は-1でerrno==EINTR */
789 debug(("signal flag = %x\n", s_signal_flag));
790 if ((s_signal_flag & SIG_FLAG_DONE ) != 0) {
791 s_signal_flag &= ~SIG_FLAG_DONE;
792 done(1);
793 }
794 if ((s_signal_flag & SIG_FLAG_RECOVER) != 0) {
795 s_signal_flag &= ~SIG_FLAG_RECOVER;
796 recover();
797 }
798 if ((s_signal_flag & SIG_FLAG_WINCH ) != 0) {
799 s_signal_flag &= ~SIG_FLAG_WINCH;
800 sigwinch_handler();
801 }
802 if ((s_signal_flag & SIG_FLAG_USR1 ) != 0) {
803 s_signal_flag &= ~SIG_FLAG_USR1;
804 sigusr1_handler();
805 }
806 if ((s_signal_flag & SIG_FLAG_USR2 ) != 0) {
807 s_signal_flag &= ~SIG_FLAG_USR2;
808 sigusr2_handler();
809 }
810 if ((s_signal_flag & SIG_FLAG_TSTP ) != 0) {
811 s_signal_flag &= ~SIG_FLAG_TSTP;
812 sigtstp_handler();
813 }
814 continue;
815 }
816
817 /* モードを変更する */
818 if (s_setmode_fd >= 0 && FD_ISSET(s_setmode_fd, &fds)) {
819 int start, end;
820
821 #ifdef __CYGWIN32__
822 if ((len = read(s_setmode_fd, buf, sizeof(buf) - 1)) == -1 || len == 0) {
823 debug2(("pipe closed\n"));
824 close(s_setmode_fd);
825 s_setmode_fd = open(s_path_setmode, O_RDONLY | O_NONBLOCK);
826 }
827 #else
828 if ((len = read(s_setmode_fd, buf, sizeof(buf) - 1)) == -1 || len == 0) {
829 /* XXX: fatal */
830 debug2(("cannot read setmode file\n"));
831 return;
832 }
833 #endif
834
835 for (end = len - 1; end >= 0 && !isdigit((unsigned char)buf[end]); --end);
836 /* プリエディットを編集中でなければモードを変更する */
837 if (end >= 0 && !g_start_preedit) {
838 int mode;
839
840 for (start = end; start > 0 && isdigit((unsigned char)buf[start - 1]); --start);
841 buf[end + 1] = '\0';
842 mode = strtonum(&buf[start], 0, uim_get_nr_modes(g_context) - 1, &errstr);
843 if (errstr) {
844 debug2(("error: '%s' is %s\n", &buf[start], errstr));
845 } else if (mode != uim_get_current_mode(g_context)) {
846 debug2(("mode change %d\n", mode));
847 uim_set_mode(g_context, mode);
848 /* callbacks_set_mode(uim_get_current_mode(g_context)); */
849 draw_statusline_restore();
850 }
851 }
852 }
853
854
855 /* キーボード(stdin)からの入力 */
856 if (FD_ISSET(g_win_in, &fds)) {
857 int key_state = 0;
858 if (!g_focus_in) {
859 focus_in();
860 }
861
862 if ((len = read_stdin(buf, sizeof(buf) - 1)) == -1 || len == 0) {
863 /* ここにはこないと思う */
864 return;
865 }
866 buf[len] = '\0';
867 debug(("read \"%s\"\n", buf));
868
869 #define LARGE_INPUT_THRESHOLD 10
870 if (len >= LARGE_INPUT_THRESHOLD && !g_opt.print_key) {
871 /* ペーストなどで大量に入力されたときは変換しない */
872 if (!g_start_preedit) {
873 write(s_master, buf, len);
874 }
875 } else {
876
877 int i;
878 char master_buf[LARGE_INPUT_THRESHOLD];
879 int master_buf_len = 0;
880 for (i = 0; i < len; i++) {
881 int key_len;
882 int *key_and_key_len = escape_sequence2key(buf + i, len - i);
883 int key = key_and_key_len[0];
884
885 if (key == UKey_Other) {
886
887 int not_enough;
888 key = tty2key(buf[i]);
889 key_state |= tty2key_state(buf[i]);
890 not_enough = key_and_key_len[1];
891
892 if (buf[i] == ESCAPE_CODE && i == len - 1) {
893 not_enough = TRUE;
894 }
895
896 if (not_enough && g_opt.timeout > 0) {
897 /* 入力が足らないので再び読み出す */
898 struct timeval t;
899 fd_set fds;
900 FD_ZERO(&fds);
901 FD_SET(g_win_in, &fds);
902 t.tv_sec = 0;
903 t.tv_usec = g_opt.timeout;
904 if (my_select(g_win_in + 1, &fds, &t) > 0) {
905 ssize_t nr;
906
907 if ((nr = read_stdin(buf + len, sizeof(buf) - len - 1)) != -1) {
908 len += nr;
909 buf[len] = '\0';
910 debug(("read_again \"%s\"\n", buf));
911 i--;
912 continue;
913 } else {
914 /* XXX: fatal */
915 return;
916 }
917 }
918 }
919
920 if (buf[i] == ESCAPE_CODE && i < len - 1) {
921 key_state = UMod_Alt;
922 continue;
923 }
924 key_len = 1;
925
926 } else { /* key != UKey_Other */
927 key_len = key_and_key_len[1];
928 }
929
930 if (g_opt.print_key) {
931 print_key(key, key_state);
932 } else {
933 int raw = press_key(key, key_state);
934 if (!draw()) {
935 if (g_opt.status_type == BACKTICK) {
936 update_backtick();
937 }
938 }
939 if (raw && !g_start_preedit) {
940 if (key_state & UMod_Alt) {
941 memcpy(master_buf + master_buf_len, buf + i - 1, key_len + 1);
942 master_buf_len += key_len + 1;
943 } else {
944 memcpy(master_buf + master_buf_len, buf + i, key_len);
945 master_buf_len += key_len;
946 }
947 }
948 }
949
950 key_state = 0;
951 i += (key_len - 1);
952 }
953 write(s_master, master_buf, master_buf_len);
954 }
955 }
956 #undef LARGE_INPUT_THRESHOLD
957
958
959 /* input from pty (child process) */
960 if (!g_opt.print_key && FD_ISSET(s_master, &fds)) {
961 if ((len = read(s_master, buf, sizeof(buf) - 1)) == -1 || len == 0) {
962 /* 子プロセスが終了した */
963 return;
964 }
965 buf[len] = '\0';
966
967 /* クリアされた時にモードを再描画する */
968 if (g_opt.status_type == LASTLINE) {
969 char *str1 = rstrstr_len(buf, _clear_screen, len);
970 char *str2 = rstrstr_len(buf, _clr_eos, len);
971 if (str1 != NULL || str2 != NULL) {
972 int str1_len;
973 if (str2 > str1) {
974 str1 = str2;
975 }
976 str1_len = len - (str1 - buf);
977 /* str1はclear_screenかclr_eosの次の文字列を指している */
978 put_pty_str(buf, len - str1_len);
979 draw_statusline_force_restore();
980 put_pty_str(str1, str1_len);
981 } else {
982 put_pty_str(buf, len);
983 }
984 } else {
985 put_pty_str(buf, len);
986 }
987 }
988
989 if (g_helper_fd >= 0 && FD_ISSET(g_helper_fd, &fds)) {
990 helper_handler();
991 draw();
992 }
993 }
994 }
995
996 /*
997 * 何もしないフィルタ
998 */
recover_loop(void)999 static void recover_loop(void)
1000 {
1001 char buf[BUFSIZE];
1002 ssize_t len;
1003 fd_set fds;
1004
1005 while (TRUE) {
1006 FD_ZERO(&fds);
1007 FD_SET(g_win_in, &fds);
1008 FD_SET(s_master, &fds);
1009 if (select(s_master + 1, &fds, NULL, NULL, NULL) <= 0) {
1010 /* signalで割り込まれたときにくる。selectの返り値は-1でerrno==EINTR */
1011 continue;
1012 }
1013 if (FD_ISSET(g_win_in, &fds)) {
1014 if ((len = read(g_win_in, buf, sizeof(buf))) == -1 || len == 0) {
1015 /* ここにはこないと思う */
1016 return;
1017 }
1018 write(s_master, buf, len);
1019 }
1020 if (FD_ISSET(s_master, &fds)) {
1021 if ((len = read(s_master, buf, sizeof(buf))) == -1 || len == 0) {
1022 /* 子プロセスが終了した */
1023 return;
1024 }
1025 write(g_win_out, buf, len);
1026 }
1027 }
1028 }
1029
1030 /*
1031 * 現在のウィンドウサイズを返す
1032 * 返り値はfreeする
1033 */
get_winsize(void)1034 static struct winsize *get_winsize(void)
1035 {
1036 struct winsize *win = uim_malloc(sizeof(struct winsize));
1037 ioctl(g_win_in, TIOCGWINSZ, win);
1038 if (g_opt.status_type == LASTLINE) {
1039 win->ws_row--;
1040 }
1041 #ifdef PTY_TEST
1042 win->ws_col = 140;
1043 win->ws_row = 50;
1044 #endif
1045 return win;
1046 }
1047
1048 /*
1049 * シグナルハンドラを設定する
1050 */
set_signal_handler(void)1051 static void set_signal_handler(void)
1052 {
1053 struct sigaction act;
1054 sigset_t sigmask;
1055
1056 sigemptyset(&sigmask);
1057 sigaddset(&sigmask, SIGHUP);
1058 sigaddset(&sigmask, SIGTERM);
1059 sigaddset(&sigmask, SIGQUIT);
1060 sigaddset(&sigmask, SIGINT);
1061 sigaddset(&sigmask, SIGWINCH);
1062 sigaddset(&sigmask, SIGUSR1);
1063 sigaddset(&sigmask, SIGUSR2);
1064 sigaddset(&sigmask, SIGTSTP);
1065 sigaddset(&sigmask, SIGCONT);
1066 sigprocmask(SIG_BLOCK, &sigmask, &s_orig_sigmask);
1067
1068 /* シグナルをブロックしない */
1069 sigemptyset(&act.sa_mask);
1070 act.sa_flags = 0;
1071 /* システムコールを再実行する(実装依存) */
1072 /* act.sa_flags = SA_RESTART; */
1073
1074 act.sa_handler = signal_handler;
1075 sigaction(SIGHUP, &act, NULL);
1076 sigaction(SIGTERM, &act, NULL);
1077 sigaction(SIGQUIT, &act, NULL);
1078 sigaction(SIGINT, &act, NULL);
1079 sigaction(SIGWINCH, &act, NULL);
1080 sigaction(SIGUSR1, &act, NULL);
1081 sigaction(SIGUSR2, &act, NULL);
1082 sigaction(SIGTSTP, &act, NULL);
1083 sigaction(SIGCONT, &act, NULL);
1084 }
1085
reset_signal_handler(void)1086 static void reset_signal_handler(void)
1087 {
1088 struct sigaction act;
1089
1090 sigprocmask(SIG_SETMASK, &s_orig_sigmask, NULL);
1091
1092 sigemptyset(&act.sa_mask);
1093 act.sa_flags = 0;
1094 act.sa_handler = SIG_DFL;
1095 sigaction(SIGHUP, &act, NULL);
1096 sigaction(SIGTERM, &act, NULL);
1097 sigaction(SIGQUIT, &act, NULL);
1098 sigaction(SIGINT, &act, NULL);
1099 sigaction(SIGWINCH, &act, NULL);
1100 sigaction(SIGUSR1, &act, NULL);
1101 sigaction(SIGUSR2, &act, NULL);
1102 sigaction(SIGTSTP, &act, NULL);
1103 sigaction(SIGCONT, &act, NULL);
1104 }
1105
signal_handler(int sig_no)1106 static void signal_handler(int sig_no)
1107 {
1108 switch (sig_no) {
1109 case SIGHUP:
1110 case SIGTERM:
1111 case SIGQUIT:
1112 s_signal_flag |= SIG_FLAG_DONE;
1113 break;
1114 case SIGINT:
1115 s_signal_flag |= SIG_FLAG_RECOVER;
1116 break;
1117 case SIGWINCH:
1118 s_signal_flag |= SIG_FLAG_WINCH;
1119 break;
1120 case SIGUSR1:
1121 s_signal_flag |= SIG_FLAG_USR1;
1122 break;
1123 case SIGUSR2:
1124 s_signal_flag |= SIG_FLAG_USR2;
1125 break;
1126 case SIGTSTP:
1127 s_signal_flag |= SIG_FLAG_TSTP;
1128 break;
1129 }
1130 }
1131
recover(void)1132 static void recover(void)
1133 {
1134 reset_signal_handler();
1135 put_exit_attribute_mode();
1136 put_restore_cursor();
1137 put_cursor_normal();
1138 recover_loop();
1139 done(EXIT_SUCCESS);
1140 }
1141
1142 /*
1143 * 休止シグナル
1144 */
sigtstp_handler(void)1145 static void sigtstp_handler(void)
1146 {
1147 struct sigaction act;
1148 sigset_t sigmask;
1149
1150 quit_escseq();
1151 put_save_cursor();
1152 tcsetattr(g_win_in, TCSAFLUSH, &s_save_tios);
1153
1154 sigemptyset(&act.sa_mask);
1155 act.sa_flags = 0;
1156 act.sa_handler = SIG_DFL;
1157 sigaction(SIGTSTP, &act, NULL);
1158 sigaction(SIGCONT, &act, NULL);
1159
1160 sigemptyset(&sigmask);
1161 sigaddset(&sigmask, SIGTSTP);
1162 sigaddset(&sigmask, SIGCONT);
1163 sigprocmask(SIG_UNBLOCK, &sigmask, NULL);
1164
1165 kill(getpid(), SIGTSTP);
1166
1167 sigprocmask(SIG_BLOCK, &sigmask, NULL);
1168
1169 act.sa_handler = signal_handler;
1170 sigaction(SIGTSTP, &act, NULL);
1171 sigaction(SIGCONT, &act, NULL);
1172 fixtty();
1173 }
1174
1175 /*
1176 * 端末のサイズが変わったときに仮想端末の大きさを合わせる。
1177 */
sigwinch_handler(void)1178 static void sigwinch_handler(void)
1179 {
1180 struct winsize *prev_win = g_win;
1181 g_win = get_winsize();
1182
1183 debug2(("g_win->ws_row = %d g_win->ws_col = %d\n", g_win->ws_row, g_win->ws_col));
1184
1185 escseq_winch();
1186 callbacks_winch();
1187 draw_winch(prev_win);
1188
1189 free(prev_win);
1190 ioctl(s_master, TIOCSWINSZ, g_win);
1191 }
1192
done(int exit_value)1193 void done(int exit_value)
1194 {
1195 uim_quit();
1196 quit_escseq();
1197 quit_helper();
1198 if (g_opt.status_type == BACKTICK) {
1199 clear_backtick();
1200 }
1201 tcsetattr(g_win_in, TCSAFLUSH, &s_save_tios);
1202 if (s_setmode_fd >= 0) {
1203 close(s_setmode_fd);
1204 }
1205 if (s_path_setmode[0] != '\0') {
1206 unlink(s_path_setmode);
1207 }
1208 if (s_path_getmode[0] != '\0') {
1209 unlink(s_path_getmode);
1210 }
1211 exit(exit_value);
1212 }
1213
sigusr1_handler(void)1214 static void sigusr1_handler(void)
1215 {
1216 press_key(UKey_Private1, 0);
1217 draw();
1218 }
1219
sigusr2_handler(void)1220 static void sigusr2_handler(void)
1221 {
1222 press_key(UKey_Private2, 0);
1223 draw();
1224 }
1225
1226 /*
1227 * helpを表示する。
1228 */
usage(void)1229 static void usage(void)
1230 {
1231
1232 uim_context context;
1233 int nr;
1234 int i;
1235 int max_im_name_len = 0;
1236
1237 uim_init();
1238 context = uim_create_context(NULL, get_enc(), NULL, NULL, NULL, commit_cb);
1239
1240 printf("uim-fep version %s\n", PACKAGE_VERSION);
1241 printf("Usage: uim-fep [OPTIONS]\n");
1242 printf(" or uim-fep [OPTIONS] -e command arg1 arg2 ...\n");
1243 printf(" or uim-fep [OPTIONS] -S\n");
1244 printf(" or uim-fep [-t <sec>] -K\n");
1245 printf("\n");
1246 printf("Options\n"
1247 "-u <input method> input method [default=%s]\n"
1248 "-s <lastline/backtick/none> statusline type [default=%s]\n"
1249 "-b <file> socket file [default=%s]\n"
1250 "-w <width> statusline width\n"
1251 "%s"
1252 "%s"
1253 "-e command arg1 arg2 ... executed command [default=%s]\n"
1254 "%s",
1255 get_default_im_name(),
1256 DEFAULT_STATUS == LASTLINE ? "lastline" :
1257 DEFAULT_STATUS == BACKTICK ? "backtick" : "none",
1258 usersockname(NULL),
1259 "-t <sec> key timeout\n"
1260 "-C [<foreground color>]:[<background color>]\n"
1261 "-c reverse cursor\n"
1262 "-i use cursor_invisible(civis)\n"
1263 "-o on the spot\n"
1264 "-d ddskk like candidate style\n",
1265 "-f file name suffix of $UIM_FEP_SETMODE and $UIM_FEP_GETMODE\n"
1266 "-S GNU screen mode\n"
1267 "-K show key code\n",
1268 getenv("SHELL") != NULL ? getenv("SHELL") : "/bin/sh",
1269 "-h display this help\n"
1270 "-v display version\n"
1271 );
1272
1273 printf("\n[input method]\n");
1274 nr = uim_get_nr_im(context);
1275 for (i = 0; i < nr; i++) {
1276 int im_name_len = strlen(uim_get_im_name(context, i));
1277 im_name_len += strlen(uim_get_im_language(context, i));
1278 im_name_len += strlen(" ()");
1279 #ifdef DEBUG
1280 im_name_len += strlen(" ");
1281 im_name_len += strlen(uim_get_im_encoding(context, i));
1282 #endif
1283 if (im_name_len > max_im_name_len) {
1284 max_im_name_len = im_name_len;
1285 }
1286 }
1287
1288 for (i = 0; i < nr; i++) {
1289 int j;
1290 const char *im_name = uim_get_im_name(context, i);
1291 int im_name_len = strlen(im_name);
1292 printf("%s", im_name);
1293
1294 im_name = uim_get_im_language(context, i);
1295 im_name_len += strlen(im_name);
1296 printf(" (%s)", im_name);
1297 im_name_len += strlen(" ()");
1298 #ifdef DEBUG
1299 im_name = uim_get_im_encoding(context, i);
1300 im_name_len += strlen(im_name);
1301 printf(" %s", im_name);
1302 im_name_len += strlen(" ");
1303 #endif
1304 for (j = 0; j < max_im_name_len - im_name_len + 2; j++) {
1305 putchar(' ');
1306 }
1307 printf("%s", uim_get_im_short_desc(context, i));
1308 printf("\n");
1309 }
1310
1311 uim_quit();
1312 }
1313
1314 /*
1315 * versionを表示する
1316 */
version(void)1317 static void version(void)
1318 {
1319 printf("uim-fep version %s\n", PACKAGE_VERSION);
1320 }
1321
1322 #if defined(DEBUG) && DEBUG > 1
1323 static FILE *_log = NULL;
_debug(const char * fmt,...)1324 void _debug(const char *fmt, ...)
1325 {
1326 va_list ap;
1327 if (_log == NULL) {
1328 _log = fopen("uim-fep-log", "w");
1329 }
1330 va_start(ap, fmt);
1331 vfprintf(_log, fmt, ap);
1332 va_end(ap);
1333 fflush(_log);
1334 }
1335
_debug_write(const char * str,int len)1336 void _debug_write(const char *str, int len)
1337 {
1338 if (_log == NULL) {
1339 _log = fopen("uim-fep-log", "w");
1340 }
1341 fwrite(str, 1, len, _log);
1342 fflush(_log);
1343 }
1344 #endif
1345