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