1 /* $Id: mapchan.c,v 1.17 2008/09/11 20:47:59 yuk Exp $ */
2
3 //#define SYSLOG
4 #include <stdio.h>
5 #include <ctype.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <sys/time.h>
9 #include <sys/ioctl.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <errno.h>
13 #include <termios.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <signal.h>
17 #include <libgen.h>
18 #if defined(__FreeBSD__) || defined(__DragonFly__)
19 #include <libutil.h>
20 #include <sys/ioctl.h>
21 #else
22 #include <pty.h>
23 #endif
24 #include <utmpx.h>
25 #ifdef SYSLOG
26 #include <syslog.h>
27 #endif
28
29 #define MAXBYTES 4
30 #ifdef UTF
31 #include <iconv.h>
32 #define ICONV_MAX 1024
33 #define utf_name "UTF8"
34
35 #ifndef TRUE
36 # define TRUE 1
37 #endif
38
39 #ifndef FALSE
40 # define FALSE 0
41 #endif
42
43 static iconv_t incode_table = NULL;
44 static iconv_t utf_table = NULL;
45 static unsigned int badchar = '*';
46 #endif
47
48 char *DEFAULT_MAP = "trivial.map";
49 char *DEFAULT_PTY = "-";
50 char *DEFAULT_SHELL = "/bin/sh";
51 char *DEFAULT_LOGFILE = "mapchan.log";
52
53 #ifndef MAX_SHELL_ARGS
54 #define MAX_SHELL_ARGS 16
55 #endif
56
57 #ifndef CPATH
58 #define CPATH "/usr/local/etc/mapchan"
59 #endif
60
61 #define BUFSIZE 32*1024
62 #define MAXPTYNAME 16
63 #define MAXPTYS 416
64
65 #define inputF 1
66 #define outputF 2
67 #define deadF 4
68 #define composeF 8
69 #define bellF 16
70 #define controlF 32
71 #define escCF 64
72
73 #ifdef DEBUG
74 FILE *dbg;
75 #endif
76
77 // Global parameters
78 char *tbl_file, *log_file;
79 char *shell_prog;
80 char ttytable[MAXPTYS];
81 int autoprobe98 = 1;
82 int quiet = 0;
83 int debug = 0;
84 int pause_open = 0;
85
86 // Global variables
87 #define kw_num 7
88 char* kw[kw_num] = { "input", "output", "dead", "compose", "bell", "control", "esc(" } ;
89 unsigned char deadC = '\0';
90 unsigned char composeC = '\0';
91 unsigned char mapon = '\0';
92 unsigned char mapoff = '\0';
93 int was_dead = 0;
94 unsigned char **outtbl = NULL;
95 unsigned char **intbl = NULL;
96 unsigned char **deadtbl = NULL;
97 unsigned char **escCtbl = NULL;
98 int clear_xonxoff = 1;
99
100 int tables_size = 0;
101 int fast_quit = 0;
102 int not_tty = 0;
103 int pty_fd; /* descriptor master */
104 int tty_fd; /* descriptor slave */
105 pid_t pid; /* child's PID */
106
107 fd_set readfds; /* select flags */
108
109
110 extern int optind, opterr, optopt;
111 extern char *optarg;
112
113 /*
114 mapchan -q -s shell -f file -h -?
115 */
116
117 #ifdef UTF
118 char usage[] = { "\nUsage:\n\nmapchan [options]\
119 \n\nwhere options are :\n\
120 \t-s shell - shell, if not /bin/sh\n\
121 \t-f file - input map-file\n\
122 \t-p /dev/ttyXX[-YY] | auto - terminal device name(s) or \"auto\" for autoprobing\n\
123 \t-o /dev/ttyXX - allocate the terminal for output stream\n\
124 \t-x - don't change XON/XOFF settings for pseudoterminal\n\
125 (by default - do as stty -ixon -ixoff) \n\
126 \t-u codeset - convert the codeset into UTF-8\n\
127 \t-q - force and quiet\n\
128 \t-P - pause when open terminal\n\
129 \t-d [logfile] - debug mode on. If logfile is omitted - use $TMPDIR/mapchan.log\n\
130 \t-h - print this screen and exit.\n" };
131 #else
132 char usage[] = { "\nUsage:\n\nmapchan [options]\
133 \n\nwhere options are :\n\
134 \t-s shell - shell, if not /bin/sh\n\
135 \t-f file - input map-file\n\
136 \t-p /dev/ttyXX[-YY] | auto - terminal device name(s) or \"auto\" for autoprobing\n\
137 \t-o /dev/ttyXX - allocate the terminal for output stream\n\
138 \t-x - don't change XON/XOFF settings for pseudoterminal\n\
139 (by default - do as stty -ixon -ixoff) \n\
140 \t-q - force and quiet\n\
141 \t-P - pause when open terminal\n\
142 \t-d [logfile] - debug mode on. If logfile is omitted - use $TMPDIR/mapchan.log\n\
143 \t-h - print this screen and exit.\n" };
144 #endif
145
help()146 void help() {
147 if (!quiet) printf("\r\nmapchan started with %d bytes in tables\r\n", tables_size);
148 }
149
150 #ifdef NOBASENAME
basename(char * path)151 char *basename(char *path) {
152 char *p;
153 if (p=strrchr(path, '/')) p++;
154 else p=path;
155 return(p);
156 }
157 #endif
158
159 struct bytecell {
160 struct bytecell *addr;
161 unsigned int ch;
162 };
163
164 struct bytecell table0_utf2ch[256];
165 char table_ch2utf[128][MAXBYTES+1];
166
init_0tables()167 void init_0tables() {
168 int l, cod;
169
170 l = sizeof(char*) * 256;
171 tables_size += l;
172 if (outtbl == NULL) outtbl = calloc(256, sizeof(char*));
173 for (cod=0; cod < 256; cod++) {
174 table0_utf2ch[cod].ch = cod;
175 table0_utf2ch[cod].addr = NULL;
176 }
177 }
178
179 #ifdef UTF
init_iconv(char * incode_name)180 int init_iconv(char *incode_name) {
181 utf_table = iconv_open(utf_name, incode_name);
182 if (utf_table == (iconv_t) -1)
183 return 1;
184 incode_table = iconv_open(incode_name, utf_name);
185 if (incode_table == (iconv_t) -1)
186 return 1;
187 return 0;
188 }
189
190
191 /* this function create the chain of tables "utf -> 8bit char" */
init_utf_tables(char * codeset)192 int init_utf_tables(char *codeset) {
193 int l, ch, cod, codx;
194 char buf[1];
195 char obuf[MAXBYTES];
196 char *pi, *po;
197 size_t ii, io;
198 struct bytecell *current_table;
199
200 if (codeset == NULL) return(0);
201
202 if (init_iconv(codeset) !=0) {
203 perror("Can't init iconv");
204 return(1);
205 }
206 for (cod=0; cod < 128; cod++) {
207 codx = cod | 0x80;
208 l = (MAXBYTES+1) * sizeof(char);
209 tables_size += l;
210 outtbl[codx] = malloc(l);
211 buf[0] = codx;
212 ii = 1; io = MAXBYTES;
213 pi = buf; po = obuf;
214 if ((l = iconv(utf_table, &pi, &ii, &po, &io)) == -1) {
215 /* no such char in UTF-8 */
216 table0_utf2ch[codx].ch = badchar;
217 table0_utf2ch[codx].addr = NULL;
218 outtbl[codx][0] = '\0';
219 } else {
220 current_table = table0_utf2ch;
221 io = MAXBYTES - io;
222 while (ii<io) {
223 ch = obuf[ii] & 0xff;
224 outtbl[codx][ii++] = ch;
225 if (ii != io) {
226 /* continue the chain */
227 if (current_table[ch].addr == NULL) {
228 l = 256 * sizeof(struct bytecell);
229 tables_size += l;
230 current_table = current_table[ch].addr = malloc(l);
231 } else
232 current_table = current_table[ch].addr;
233 } else {
234 /* last char in the chain */
235 current_table[ch].addr = NULL;
236 current_table[ch].ch = cod | 0x80;
237 }
238 }
239 outtbl[codx][ii] = '\0';
240 }
241 }
242 iconv_close(utf_table);
243 iconv_close(incode_table);
244
245 return(0);
246 }
247
248 #endif
249
from_utf(char ch)250 int from_utf(char ch) {
251 static struct bytecell *current_table = table0_utf2ch;
252 int c;
253
254 // c = ch & 0x7f;
255 c = ch & 0xff;
256 if (current_table[c].addr == NULL) {
257 c = current_table[c].ch;
258 current_table = table0_utf2ch;
259 } else {
260 current_table = current_table[c].addr;
261 c = 0;
262 }
263 return(c);
264 }
265
266 /*
267 * Screen deInitialization
268 */
269 void
deinit_mscr()270 deinit_mscr()
271 {
272
273 #ifdef DEBUG
274 fprintf(dbg, "Closing pty\n");
275 #endif
276 kill(pid, SIGHUP);
277 close(tty_fd);
278 close(pty_fd);
279 }
280
281 void
create_child()282 create_child()
283 {
284 pid_t n;
285 char *shell_args[MAX_SHELL_ARGS];
286 int i = 0;
287
288 #ifdef EXEC_SHELL
289 shell_args[i++] = "sh";
290 if (strcmp(DEFAULT_SHELL, shell_prog) != 0) {
291 shell_args[i++] = "-c";
292 shell_args[i++] = shell_prog;
293 }
294 shell_args[i++] = NULL;
295 #else
296 char *p;
297
298 p=shell_prog;
299 while (*p) {
300 while (isspace(*p)) if (*p=='\0') break;
301 else p++;
302 if (*p!='\0') {
303 shell_args[i++] = p;
304 if (i > MAX_SHELL_ARGS - 1) {
305 fprintf(stderr, "mapchan: too many args for shell\n");
306 exit(1);
307 }
308 while (!isspace(*p)) if (*p=='\0') break;
309 else p++;
310 if (*p!='\0') *(p++) = '\0';
311 }
312 }
313 shell_args[i] = NULL;
314 #endif
315
316 n = fork();
317 if (n < 0) {
318 perror("fork");
319 exit(1);
320 }
321 if (!n) {
322 /* child */
323 close(pty_fd);
324
325 /* Set up the slave pty as a controlling terminal. */
326 setsid();
327
328 /* Create stdin, stdout, and stderr. */
329 dup2(tty_fd, 0);
330 dup2(tty_fd, 1);
331 dup2(tty_fd, 2);
332 if (tty_fd > 2)
333 close(tty_fd);
334
335
336 /* Now pass the file descriptors to the shell. */
337 #ifdef EXEC_SHELL
338 //printf("exec 2\n");
339 //for (i=0 ; shell_args[i] != NULL; i++) printf("%d) %s\n", i, shell_args[i]);
340
341 if (execvp(DEFAULT_SHELL, shell_args) == -1) {
342 perror(shell_prog);
343 exit(1);
344 }
345 #else
346 if (execvp(shell_prog, shell_args) == -1) {
347 perror(shell_prog);
348 exit(1);
349 }
350 #endif
351
352 /*
353 if (execl(shell_prog, (char*)basename(shell_prog), NULL) == -1) {
354 perror(shell_prog);
355 exit(1);
356 }
357 */
358 }
359
360 /* parent */
361 pid = n;
362 close(tty_fd);
363
364 FD_ZERO(&readfds);
365 FD_SET(0, &readfds);
366 FD_SET(pty_fd, &readfds);
367 }
368
set_tty_size()369 int set_tty_size() {
370 struct winsize ws;
371
372 if (not_tty == 0) {
373 if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws)) {
374 perror("Get window size");
375 return(1);
376 }
377
378 if (ioctl (pty_fd, TIOCSWINSZ, (char *)&ws)) {
379 perror("Set window size");
380 return(1);
381 }
382 }
383 return(0);
384 }
385
386 void
sigresize(sig)387 sigresize(sig)
388 int sig;
389 {
390 set_tty_size();
391 signal(SIGWINCH, sigresize);
392 }
393
394 void
sigchld(sig)395 sigchld(sig)
396 int sig;
397 {
398 pid_t pid;
399 int status;
400
401 pid = waitpid(WAIT_ANY, &status, WNOHANG);
402 #ifdef DEBUG
403 fprintf(dbg, "\nChild %d died with status %d \n", pid, status);
404 #endif
405 #ifdef SYSLOG
406 syslog(LOG_INFO, "\nSIGCHLD: Child %d died with status %d \n", pid, status);
407 #endif
408 fast_quit = 1;
409
410 signal(SIGCHLD, sigchld);
411 }
412
413 void
sighup(sig)414 sighup(sig)
415 {
416 int i;
417
418 fprintf(stderr, "mapchan is catching signal %d\n", sig);
419 #ifdef SYSLOG
420 syslog(LOG_INFO, "Signal %d\n", sig);
421 #endif
422
423 for (i=1; i<NSIG; i++) signal(i, SIG_DFL);
424 fast_quit = 1;
425
426 }
427
428 /*
429 * Translation
430 * from String in Esc-sequence definition language
431 * to character codes
432
433 The language definitions :
434 0777 - octal code
435 0xAA - hexadecimal code
436 99 - decimal code
437 'A' - character A
438 A - character A
439 */
440
parse_cod(char * ks,char * out)441 char *parse_cod /* Return address of next word or NULL */
442 (char *ks, /* Input string */
443 char *out) /* here we return character code */
444 {
445 int i;
446 unsigned int c;
447
448 l_start:
449 i = ks[0];
450
451 if ((i >= '1') && (i <= '9'))
452 /* decimal number */
453 *out=atoi(ks);
454 else
455 switch (i) {
456 case '0':
457 if (ks[1] == 'x')
458 /* hexadecimal number */
459 sscanf(&ks[2], "%x", &c);
460 else
461 /* octal number */
462 sscanf(&ks[1], "%o", &c);
463 *out = c & 0xff;
464 break;
465 case '\'': if (ks[1] == '\\') {
466 ks = &ks[2];
467 goto l_start;
468 }
469 else
470 *out = ks[1];
471 break;
472 case '#':
473 *out = 0;
474 return(NULL);
475 break;
476 default: *out = i;
477 break;
478 }
479
480 i = 0;
481 if (ks[0] != '\'')
482 while (!isspace(ks[i])) if (!ks[i]) return(NULL);
483 else i++;
484 else {
485 i++;
486 while (ks[i] != '\'') if (!ks[i]) return(NULL);
487 else i++;
488 i++;
489 }
490 while (isspace(ks[i])) if (!ks[i]) return(NULL);
491 else i++;
492
493 if (!ks[i]) return(NULL);
494
495 return(&ks[i]);
496 }
497
498 int
keyword(char * s)499 keyword(char *s) {
500 int i;
501
502 for (i=0; i<kw_num; i++) if (!strncmp(kw[i], s, strlen(kw[i]))) break;
503 if (i == kw_num) i = 0;
504 else i = 1 << i;
505 return(i);
506 }
507
508 /*
509 * Decode table loading
510 *
511 */
load_map(fn)512 int load_map(fn)
513 char *fn; /* Input file name */
514 {
515 register int i, j, l;
516 char bufin[BUFSIZE], buf[BUFSIZE];
517 int flag;
518 FILE *mf;
519 char *p;
520 unsigned char c;
521
522 if (!fn) {
523 for (i=0; i<128; i++) {
524 p = malloc(2);
525 p[0] = i;
526 p[1] = '\0';
527 outtbl[i] = (unsigned char*) p;
528 }
529 tables_size += 128*2;
530 return(0);
531 }
532
533 flag = 0;
534
535 for(i=0;isgraph(fn[i]); i++);
536 fn[i] = '\0';
537 if ((mf=fopen(fn, "r")) == NULL) { /* open input file */
538 /* no in current directory - let's find in CPATH */
539 strncpy(buf, CPATH, BUFSIZE-2);
540 strcat(buf, "/");
541 strncat(buf, fn, BUFSIZE-strlen(buf)-2);
542 if ((mf=fopen(buf, "r")) == NULL) {
543 /* and no here too - error */
544 perror(buf);
545 return(1);
546 }
547 }
548 /* Read definition string */
549 j = 0;
550 while (fgets(bufin, 100, mf) != NULL) {
551 j++;
552 if (isspace(bufin[0]) || (bufin[0] == '#') || (!strlen(bufin)))
553 continue;
554 i=keyword(bufin);
555 if (i != 0)
556 switch (i) {
557 case inputF:
558 flag = i;
559 intbl = (unsigned char**) -1;
560 break;
561 case outputF:
562 flag = i;
563 break;
564 case escCF:
565 flag = i;
566 l = sizeof(struct bytecell) * 256;
567 tables_size += l;
568 escCtbl = malloc(l);
569 break;
570 case controlF:
571 flag = i;
572 break;
573 case bellF:
574 case composeF:
575 break;
576 case deadF:
577 if (flag & inputF) flag |= i;
578 else {
579 fprintf(stderr, "error in file %s, str. %d\n",
580 fn, j);
581 return(1);
582 }
583 for (i=0; i<strlen(bufin); i++)
584 if (p=parse_cod(bufin, &c))
585 parse_cod(p, &deadC);
586 l = sizeof(struct bytecell) * 256;
587 tables_size += l;
588 table0_utf2ch[deadC].addr = malloc(l);
589
590 break;
591 }
592 else {
593 p = parse_cod(bufin, &c);
594 for (i=0; p != NULL; i++) {
595 if (i>=99) {
596 buf[i] = '\0';
597 break;
598 }
599 p=parse_cod(p, &buf[i]);
600 }
601 if (flag == outputF) {
602 outtbl[c] = malloc(i);
603 tables_size += i;
604 memcpy(outtbl[c], buf, i);
605 outtbl[c][i] = '\0';
606 }
607 else if (flag & inputF) {
608 if (i != 1) fprintf(stderr, "Warning: multibyte sequences in 'input' section are not supported\n");
609 if (flag & deadF) {
610 table0_utf2ch[deadC].ch = buf[0];
611 table0_utf2ch[deadC].addr = NULL;
612 }
613 else {
614 table0_utf2ch[c].ch = buf[0];
615 table0_utf2ch[c].addr = NULL;
616 }
617 }
618 else if (flag == controlF) {
619 switch (c) {
620 case '-': /* mapping off */
621 mapoff = buf[0];
622 break;
623 case '+': /* mapping on */
624 mapon = buf[0];
625 break;
626 default:
627 break;
628 }
629 }
630 else if (flag == escCF) {
631 escCtbl[c] = malloc(i);
632 tables_size += i;
633 memcpy(escCtbl[c], buf, i);
634 escCtbl[c][i] = '\0';
635 }
636 }
637 }
638 fclose(mf);
639 return(0);
640 }
641
642 /*
643 * Transforming of pseudo-terminal index into symbolic name:
644 *
645 * 0 -> /dev/ttya0, ... 415 -> /dev/ttyzf
646 *
647 */
648
idx2pty(int idx)649 char *idx2pty(int idx) {
650 static char ptyname[MAXPTYNAME];
651 char c1, c2;
652 // 0123456789
653 strcpy(ptyname, "/dev/ttya0");
654 c1 = (idx >> 4) & 0xFF;
655 if (c1 > 25) return(NULL);
656 c2 = idx & 0xF;
657 if (c2 > 9) c2 += 'a' - '9' - 1;
658 ptyname[8] += c1;
659 ptyname[9] += c2;
660 return(ptyname);
661 }
662
663 /*
664 * Getting pseudo-terminal index from its symbolic name
665 *
666 * /dev/ttya0 -> 0 ... /dev/ttyzf -> 415
667 *
668 */
669
pty2idx(char * pty)670 int pty2idx(char *pty) {
671 int c1, c2;
672 int i;
673 i=strlen(pty)-1;
674 c2 = (pty[i--] - '0');
675 if (c2 > 9) c2 -= 'a' - '9' - 1;
676 if (c2 > 0xF) return(-1);
677 c1 = (pty[i] - 'a') &0xFF;
678 if (c1 > 25) return(-1);
679 c1 <<= 4;
680 return(c1+c2);
681 }
682
683
parse_tty_name(char * tty)684 int parse_tty_name(char *tty) {
685 int i, j;
686 char buf[MAXPTYNAME];
687 int i1, i2;
688 int eol=0;
689 char *p;
690
691 i1 = 0;
692 while (!eol) {
693 i2 = i1;
694 while (tty[i2] != ',') {
695 if (tty[i2] == '\0') {
696 eol++;
697 break;
698 }
699 i2++;
700 }
701 tty[i2] = '\0';
702 p = &tty[i1];
703 if ((strncmp(p, "-", 1) == 0) || (strncmp(p, "unix98", 6) == 0)) { // autoprobe in Unix'98 format
704 autoprobe98 = 1;
705 break;
706 }
707 else {
708 autoprobe98 = 0;
709 if (strncmp(p, "auto", 4) == 0) { // autoprobe in traditional format
710 for (i=0; i<MAXPTYS; i++) ttytable[i] = 1;
711 break;
712 }
713 else if (strncmp(p, "/dev/tty", 8) == 0) {
714 if (strlen(p) == 10) { // exactly 1 pty name
715 if ((i = pty2idx(p)) == -1) return(1);
716 else ttytable[i] = 1;
717 }
718 else if (p[10] == '-') { // range of pty names
719 sprintf(buf, "%.10s", p);
720 i = pty2idx(buf);
721 j = pty2idx(&p[11]);
722 if ((i == -1) || (j == -1) || (j < i)) return(1);
723 for (;i <= j;i++) ttytable[i] = 1;
724 }
725 }
726 }
727
728 i1 = i2 + 1;
729 }
730
731
732 return(0);
733 }
734
735 /*
736 Select field number 'field_num' (starting from 1) from string str.
737 Field - alphanumeric sequence what limited by spaces or EOL
738 If field_num < 0 - then limited only by EOL
739 Field as string with length not more then 'maxlen'
740 is returned into array 'field'
741
742 If no the field with such number or it is "-",
743 then into 'field' returned default value 'fdefault' if it's not NULL.
744
745 If field is '+' - the function exit code is 1, else - 0.
746
747 */
get_field(char * str,char * field,char * fdefault,int field_num,int maxlen)748 int get_field(char *str, char *field, char *fdefault, int field_num, int maxlen) {
749 int i=0;
750 int si=0;
751 int c;
752 int toend = 0;
753 #define skip_spaces() while ( ((c=str[si]) != '\0') && (isspace(c)) ) si++;
754 #define skip_text() while ( ((c=str[si]) != '\0') && (!isspace(c)) ) si++;
755
756 if (field_num < 0) {
757 field_num = - field_num;
758 toend++;
759 }
760 skip_spaces();
761 i = 1;
762 while (i != field_num) {
763 skip_text();
764 skip_spaces();
765 i++;
766 }
767 i = 0;
768 while ((c=str[si]) != '\0') {
769 if (!toend && isspace(c) ) break;
770 if (i < maxlen)
771 field[i++] = c;
772 si++;
773 }
774 field[i] = '\0';
775 if ( ((strlen(field) == 0) || ((strlen(field) == 1) && field[0] == '-'))
776 && (fdefault != NULL) ) strncpy(field, fdefault, maxlen);
777
778 if ((strlen(field) == 1) && (field[0] == '+')) i = 1;
779 else i = 0;
780
781 return(i);
782 }
783
default_tbl()784 void default_tbl() {
785 /* 1 2 3 4 5 6 7
786 # Terminal Map PTY Quiet Debug LogFile Shell
787 /dev/tty1 dos2koi.map /dev/ttyc1 - - - /bin/sh
788 */
789 char buf[BUFSIZE], buf1[BUFSIZE];
790 char *p;
791 FILE *f;
792
793 if (p=(char*)ttyname(fileno(stdin))) {
794 p=(char*)basename(p);
795 strncpy(buf, CPATH, BUFSIZE-1);
796 strncat(buf, "/default", BUFSIZE-strlen(buf));
797 if (f=fopen(buf, "r")) {
798 while (fgets(buf, BUFSIZE, f)) {
799 /* Read file /etc/mapchan/default */
800
801 // field 1 - control terminal name
802 get_field(buf, buf1, NULL, 1, BUFSIZE);
803 if (strcmp(p, buf1) == 0) {
804 // found the current terminal name
805 // field 2 - map-file name
806 get_field(buf, buf1, DEFAULT_MAP, 2, BUFSIZE);
807 tbl_file = malloc(strlen(buf1)+1);
808 strcpy(tbl_file, buf1);
809 // field 3 - pseudo-tty name
810 get_field(buf, buf1, DEFAULT_PTY, 3, MAXPTYNAME);
811 parse_tty_name(buf1);
812 // field 4 - "quiet" mode
813 quiet = get_field(buf, buf1, NULL, 4, 1);
814 // field 5 - debug mode
815 debug = get_field(buf, buf1, NULL, 5, 1);
816 // field 6 - log file
817 get_field(buf, buf1, DEFAULT_LOGFILE, 6, BUFSIZE);
818 if (debug) {
819 log_file = malloc(strlen(buf1) + 1);
820 strcpy(log_file, buf1);
821 }
822 // field 7 - shell
823 get_field(buf, buf1, DEFAULT_SHELL, -7, BUFSIZE);
824 shell_prog = malloc(strlen(buf1)+1);
825 strcpy(shell_prog, buf1);
826 break;
827 }
828 }
829 fclose(f);
830 }
831 }
832 else not_tty = 1;
833 }
834
835 #define flush_in() while (j > 0) { \
836 m = write(pty_fd, p, j); \
837 p += m; j -= m; \
838 }
839
840 #define flush_out() while (j > 0) { \
841 m = write(1, p, j); \
842 p += m; j -= m; \
843 }
844
845 char *
pty_name(char * tty_name)846 pty_name(char *tty_name)
847 {
848 int l;
849 char c1, c2; // 0123456789
850 static char n[] = { "/dev/ptyXX" };
851
852 l = strlen(tty_name);
853 c1 = tty_name[l-2];
854 c2 = tty_name[l-1];
855 n[8] = c1;
856 n[9] = c2;
857 return(n);
858 }
859
main(argc,argv)860 int main(argc, argv)
861 int argc;
862 char **argv;
863
864 {
865 int i, m, n, j, k;
866 char buf[BUFSIZE], buf1[BUFSIZE];
867 unsigned char c;
868 char *p, *p1;
869 fd_set lreadfds; /* select flags */
870 struct termios tios, old_tios;
871 int errflg;
872 int domapping = 1;
873 int out_mapping = 0;
874 int in_mapping = 0;
875 int status = 0;
876 int tty_i;
877 char *tty_name_i;
878 int was_esc= 0;
879 int was_esc_C = 0;
880 char *output_tty = NULL;
881 #ifdef LINUX_GRAPHICS_COMPAT
882 int escm = 0;
883 int escm_i = 0;
884 char escm_arg[80];
885 #endif
886 #ifdef UTF
887 int do_utf = FALSE;
888 char *utf_incode = NULL;
889 #endif
890
891 #ifdef SYSLOG
892 openlog("mapchan", LOG_PID, LOG_USER);
893 syslog(LOG_INFO, "Started.");
894 #endif
895
896 #ifdef DEBUG
897 if ((dbg = fopen("/dev/tty", "w")) == NULL) {
898 perror("debug open /dev/tty");
899 exit(1);
900 }
901 #endif
902
903
904 /*
905 * Command string parsing
906 */
907
908 /* Defaults */
909 if ((p = getenv("TMPDIR")) == NULL) p = "/tmp";
910 i = strlen(p) + strlen(DEFAULT_LOGFILE) + 1;
911 p1 = malloc(i);
912 sprintf(p1, "%s%s", p, DEFAULT_LOGFILE);
913 DEFAULT_LOGFILE = log_file = p1;
914
915 shell_prog = DEFAULT_SHELL;
916 tbl_file = NULL;
917
918 default_tbl();
919
920 /* Keys scanning */
921 errflg = 0;
922 while ((i = getopt(argc, argv, "qPxd::p:A:Z:s:f:o:u:h?")) != EOF) {
923 switch (i) {
924
925 case 'p': /* Explicit terminal name */
926 parse_tty_name(optarg);
927 break;
928 case 's': /* what shell */
929 shell_prog = optarg;
930 break;
931 case 'f': /* map-table */
932 tbl_file = optarg;
933 break;
934 case 'q':
935 quiet = 1;
936 break;
937 case 'x':
938 clear_xonxoff = 0;
939 break;
940 case 'P':
941 pause_open = 1;
942 break;
943 case 'd':
944 debug = 1;
945 if (optarg != NULL) log_file = optarg;
946 break;
947 case 'o': if (optarg != NULL) output_tty = optarg;
948 break;
949 #ifdef UTF
950 case 'u': do_utf = TRUE;
951 utf_incode = optarg;
952 break;
953 #endif
954 case 'h':
955 case '?':
956 errflg++;
957 break;
958
959 default:
960 fprintf(stderr, "Unknown key: -%c\n", i);
961 errflg++;
962 break;
963 }
964 }
965 if (errflg) { /* If there were errors */
966 fprintf(stderr, usage); /* show help text */
967 exit(1); /* and exit with error status */
968 }
969
970 if (output_tty) {
971 if (freopen(output_tty, "r+", stdout) == NULL) {
972 perror(output_tty);
973 exit(1);
974 }
975 if (freopen(output_tty, "r+", stderr) == NULL) {
976 perror(output_tty);
977 exit(1);
978 }
979 }
980
981 init_0tables();
982
983 #ifdef UTF
984 if (init_utf_tables(utf_incode) != 0) {
985 fprintf(stderr, "Can't create UTF chain table for codeset %s\n", utf_incode);
986 exit(1);
987 }
988 #endif
989 if (load_map(tbl_file) ||
990 ((outtbl == NULL) && (intbl == NULL) && (deadtbl == NULL))) {
991 if (quiet) {
992 #ifdef EXEC_SHELL
993 //printf("exec 1\n");
994 if (execlp("/bin/sh", "sh", "-c", shell_prog, NULL) == -1) {
995 #else
996
997 if (execl(shell_prog, (char*)basename(shell_prog), NULL) == -1) {
998 #endif
999 perror(shell_prog);
1000 exit(1);
1001 }
1002 } else {
1003 fprintf(stderr, "You don't define no one table - mapchan not needed\n");
1004 fprintf(stderr, usage); /* Show help text */
1005 exit(1);
1006 }
1007 }
1008
1009 #ifdef DEBUG
1010 if (outtbl) {
1011 printf("output\n");
1012 for (i=0; i<256; i++) if (outtbl[i]) {
1013 printf(" %c=%#0x) ", i, i);
1014 for (j=0; j<strlen(outtbl[i]); j++)
1015 if (outtbl[i][j] <= '\040')
1016 printf("%o ", outtbl[i][j] & 0377);
1017 else
1018 printf("%c ", outtbl[i][j]);
1019 putchar('\n');
1020 }
1021 }
1022 if (deadtbl) {
1023 printf("dead\n");
1024 for (i=0; i<256; i++) if (deadtbl[i]) {
1025 printf(" %c=%#0x) ", i, i);
1026 for (j=0; j<strlen(deadtbl[i]); j++)
1027 if (deadtbl[i][j] <= '\040')
1028 printf("%o ", deadtbl[i][j] & 0377);
1029 else
1030 printf("%c ", deadtbl[i][j]);
1031 putchar('\n');
1032 }
1033 }
1034 #endif
1035 if (not_tty == 0) {
1036 tcgetattr(0, &old_tios);
1037 memcpy(&tios, &old_tios, sizeof(struct termios));
1038 // tios.c_iflag =
1039 if (clear_xonxoff)
1040 tios.c_iflag &= ~(ICRNL | IXON | IXOFF);
1041 else
1042 tios.c_iflag &= ~(ICRNL);
1043 tios.c_oflag = tios.c_lflag = 0;
1044 // tios.c_cflag |= CS8 ;
1045 // tios.c_cflag &= ~( PARENB | PARODD | CSTOPB );
1046
1047 tcsetattr(0, TCSANOW, &tios);
1048 if (pause_open) {
1049 sleep(1);
1050 putchar(' ');
1051 sleep(1);
1052 }
1053 }
1054 signal(SIGCHLD, sigchld);
1055 signal(SIGWINCH, sigresize);
1056 for (i=1; i<NSIG; i++) if ((i != SIGCHLD) && (i != SIGWINCH)) signal(i, sighup);
1057
1058 if ( autoprobe98 ) { /* autoprobing in format Unix98 */
1059 if (openpty(&pty_fd,&tty_fd,NULL,NULL,NULL)) {
1060 perror("openpty failed: ");
1061 goto quit ;
1062 }
1063 } else { /* exactly terminal name or traditional autoprobing */
1064 tty_i=0;
1065 while (tty_i < MAXPTYS) {
1066 if (ttytable[tty_i]) {
1067 tty_name_i = idx2pty(tty_i);
1068 #ifdef DEBUG
1069 fprintf(dbg, "\rTrying %s\n\r", tty_name_i);
1070 #endif
1071 pty_fd = open(pty_name(tty_name_i), O_RDWR);
1072 if (pty_fd >= 0) {
1073 i++;
1074 break;
1075 }
1076 }
1077 tty_i++;
1078 }
1079 if (tty_i == MAXPTYS) {
1080 fprintf(stderr, "%s ERROR: No free ptys.\n\r", argv[0]);
1081 status = 1;
1082 goto quit;
1083 }
1084
1085 tty_fd = open(tty_name_i, O_RDWR);
1086 if (tty_fd < 0) {
1087 perror(tty_name_i);
1088 status = 1;
1089 goto quit;
1090 }
1091 }
1092
1093 if (not_tty == 0) {
1094 tcsetattr(tty_fd, TCSANOW, &old_tios);
1095 set_tty_size();
1096 }
1097 /* create screens */
1098 create_child();
1099
1100 i = 0;
1101 help();
1102
1103 out_mapping = outtbl || escCtbl ;
1104 #ifdef UTF
1105 out_mapping = out_mapping || do_utf;
1106 #endif
1107 in_mapping = intbl || deadtbl;
1108 #ifdef UTF
1109 in_mapping = in_mapping || do_utf;
1110 #endif
1111
1112 while (!fast_quit) {
1113 lreadfds = readfds;
1114 j = 0;
1115 while (select(32, &lreadfds, (fd_set *) 0,
1116 (fd_set *) 0, (struct timeval *) 0) < 0) {
1117 sleep(1);
1118 if (j++ > 10) {
1119 perror("select");
1120 break;
1121 }
1122 }
1123 if (j>10) {
1124 break;
1125 }
1126 if (FD_ISSET(0, &lreadfds)) {
1127 n = read(0, buf, sizeof buf);
1128 #ifdef SYSLOG
1129 syslog(LOG_INFO, "read0 %d", n);
1130 #endif
1131 if (n <= 0) {
1132 perror("read 0");
1133 break;
1134 }
1135 else if (in_mapping) {
1136 p = buf1;
1137 for (i=0,j=0;i<n;i++) {
1138 c = buf[i];
1139 if ((c = from_utf(c)) == 0) continue;
1140 if (j+1 >= BUFSIZE)
1141 flush_in();
1142 buf1[j++] = c;
1143 }
1144 }
1145 else {
1146 j = n;
1147 p = buf;
1148 }
1149 flush_in();
1150 }
1151
1152 if (FD_ISSET(pty_fd, &lreadfds)) {
1153 n = read(pty_fd, buf, sizeof buf);
1154 #ifdef SYSLOG
1155 syslog(LOG_INFO, "read1 %d", n);
1156 #endif
1157 if (!n) break;
1158 if (n < 0) {
1159 if (errno != EIO)
1160 perror("read pty_fd");
1161 continue;/*break;*/
1162 }
1163 else if (out_mapping) {
1164 p = buf1;
1165 for (i=0, j=0;i<n;i++) {
1166 c = buf[i];
1167 #ifdef MAP_ONOFF
1168 if (domapping) {
1169 if (mapoff && (c == mapoff)) {
1170 domapping = 0;
1171 continue;
1172 }
1173 }
1174 else {
1175 if (mapon && (c == mapon)) {
1176 domapping = 1;
1177 continue;
1178 }
1179 }
1180 #endif
1181
1182 #ifdef ESC_C_TBL
1183 if (domapping && (escCtbl != NULL)) {
1184 if (was_esc) {
1185 was_esc = 0;
1186 if (c == '(') {
1187 was_esc_C++;
1188 continue;
1189 }
1190 else {
1191 if (j >= BUFSIZE-1) flush_out();
1192 buf1[j++] = '\033';
1193 }
1194 }
1195 if (was_esc_C) {
1196 was_esc_C = 0;
1197 if (escCtbl[c]) {
1198 if (j+strlen(escCtbl[c]) >= BUFSIZE) flush_out();
1199 k = 0;
1200 while (escCtbl[c][k]) buf1[j++] = escCtbl[c][k++];
1201 continue;
1202 }
1203 else {
1204 if (j >= BUFSIZE-2) flush_out();
1205 buf1[j++] = '\033';
1206 buf1[j++] = '(';
1207 }
1208 }
1209
1210 if (c == '\033') {
1211 was_esc++;
1212 continue;
1213 }
1214 }
1215 #endif
1216
1217 if (domapping && ((p1=outtbl[c]) != NULL)) {
1218 if (j+strlen(p1) >= BUFSIZE) {
1219 flush_out();
1220 }
1221 k = 0;
1222 while (p1[k]) buf1[j++] = p1[k++];
1223 }
1224 else {
1225 if (j+1 >= BUFSIZE) {
1226 flush_out();
1227 }
1228 buf1[j++] = buf[i];
1229 }
1230 #ifdef LINUX_GRAPHICS_COMPAT
1231 /*
1232 linux console pseudographics compatibility:
1233 \033 [ 11 m - turn off mapping
1234 \033 [ 12 m - turn off mapping
1235 \033 [ 10 m - turn on mapping
1236 TODO: control array overflow!!!
1237 */
1238 if (escm == 0) {
1239 if (c == '\033') escm++;
1240 } else if (escm == 1) {
1241 if (c == '[') { escm++; escm_i = 0; }
1242 else escm = 0;
1243 } else if (escm >= 2) {
1244 if (isdigit(c)) escm_arg[escm_i++] = c;
1245 else {
1246 escm_arg[escm_i] = '\0';
1247 escm_i = 0;
1248 if ((c == ';') || (c == 'm')) {
1249 switch (atoi(escm_arg)) {
1250 case 10: escm = 3;
1251 break;
1252 case 11:
1253 case 12:
1254 escm = 4;
1255 break;
1256 default:
1257 break;
1258 }
1259 if ((c == 'm') && (escm > 2)) {
1260 domapping = escm & 1;
1261 escm = 0;
1262 }
1263 escm_i = 0;
1264 escm_arg[0] = '\0';
1265 }
1266 else escm = 0;
1267 }
1268 }
1269 #endif
1270 }
1271 }
1272 else {
1273 p = buf;
1274 j = n;
1275 }
1276 flush_out();
1277 }
1278 }
1279
1280 deinit_mscr();
1281 quit:
1282 if (not_tty == 0)
1283 if (tcsetattr(0, TCSANOW, &old_tios) < 0)
1284 perror("tcsetattr oldtios");
1285
1286 #ifdef SYSLOG
1287 syslog(LOG_INFO, "Ended.");
1288 closelog();
1289 #endif
1290 if (!quiet) printf("\r\nmapchan ended.\r\n");
1291 return(status);
1292 }
1293
1294