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