Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / tconv / tconv.c
1 /*
2  * tconv.c
3  *
4  * Ross Ridge
5  * Public Domain
6  * 92/02/01 07:30:23
7  *
8  * tconv [-b] [-c [-OUGd]] [-i] [-B [-D dir]] [-I] [-k] [-V] [-t term] [file]
9  *
10  * -c           convert from termcap
11  * -i           convert from terminfo source
12  * -b           convert from terminfo binary
13  * -B           convert to terminfo binary
14  * -I           convert to terminfo source
15  * -V           print version info
16  *
17  * The following switches are available when converting from termcap:
18  * -d           don't supply any defaults for missing capabilities
19  * -O           include obsolete termcap capabilities
20  * -G           include GNU capabilities
21  * -U           include UW capabilities
22  *
23  * -k           keep comments
24  * -D dir       directory to put terminfo binaries in
25  *
26  * -t term      name of terminal to translate
27  * file         filename of termcap/terminfo database to use
28  *
29  * If a file is specifed and no terminal is given the entire file we be
30  * translated.
31  * If no terminal and no file is specified then the terminal name will be
32  * taken from the environment variable TERM.
33  * Unless compiling to a terminfo binary, output is to stdout.
34  *
35  */
36
37 #define NOTLIB
38 #include "defs.h"
39 #define SINGLE
40 #include <term.h>
41
42 #include <ctype.h>
43 #include <fcntl.h>
44 #ifdef USE_STDDEF
45 #include <sys/types.h>
46 #endif
47 #include <sys/stat.h>
48 #ifdef __FreeBSD__
49 #include <unistd.h>
50 #endif
51
52 #ifndef __FreeBSD__
53 #include "strtok.c"
54 #include "mkdir.c"
55 #endif
56
57 #ifndef lint
58 static const char SCCSid[] =
59         "@(#) mytinfo tconv.c 3.2 92/02/01 public domain, By Ross Ridge";
60 static const char rcsid[] =
61   "$FreeBSD: src/usr.bin/tconv/tconv.c,v 1.5.2.1 2001/03/04 09:07:50 kris Exp $";
62 #endif
63
64 /* the right margin of the output */
65 #define LINELEN 76
66
67 struct term_path *path; /* returned from _buildpath */
68
69 TERMINAL _term_buf;
70 char buf[MAX_BUF+1];    /* buffer for the termcap entry */
71
72 int noOT = 1;           /* -O */
73 int noGNU = 1;          /* -G */
74 int noUW = 1;           /* -W */
75 int dodefault = 1;      /* -d */
76 int keepcomments = 0;   /* -k */
77 int compile = 0;        /* -B */
78 int from_tcap = 0;      /* -c */
79 int from_tinfo = 0;     /* -i */
80 int from_tbin = 0;      /* -b */
81 char *directory = NULL; /* -D */
82
83 int continued = 0;
84 int termcap = 1;
85
86 int lineno = 0;         /* current line number */
87
88 /* print the first part of a warning message */
89 void
90 warn() {
91         if (lineno == 0)
92                 fprintf(stderr, "warning: ");
93         else
94                 fprintf(stderr, "(%s)%d: warning: ",
95                         _term_buf.name_long, lineno);
96 }
97
98 /* output a string indenting at the beginning of a line, and wraping
99  * at the right margin.
100  */
101 void
102 putstr(s)
103 char *s; {
104         static pos = 0;
105         int l;
106
107         if (s == NULL) {
108                 if (pos != 0) {
109                         pos = 0;
110                         putchar('\n');
111                 }
112                 return;
113         }
114
115         if (termcap && noOT && *s == 'O')
116                 return;
117         if (termcap && noGNU && *s == 'G')
118                 return;
119         if (termcap && noUW && *s == 'U')
120                 return;
121
122         l = strlen(s) + 2;
123
124         if (l + pos > LINELEN && pos != 0) {
125                 putchar('\n');
126                 pos = 0;
127         }
128
129         if (pos == 0) {
130                 putchar('\t');
131                 pos = 8;
132         } else
133                 putchar(' ');
134
135         printf("%s,", s);
136
137         pos += l;
138 }
139
140 #ifndef MAX_PUSHED
141 /* maximum # of parameters that can be pushed onto the stack */
142 #define MAX_PUSHED 16
143 #endif
144
145 int stack[MAX_PUSHED];  /* the stack */
146 int stackptr;           /* the next empty place on the stack */
147 int onstack;            /* the top of stack */
148 int seenm;              /* seen a %m */
149 int seenn;              /* seen a %n */
150 int seenr;              /* seen a %r */
151 int param;              /* current parameter */
152 char *dp;               /* pointer to the end of the converted string */
153
154 /* push onstack on to the stack */
155 void
156 push() {
157         if (stackptr > MAX_PUSHED) {
158                 warn();
159                 fprintf(stderr, "string to complex to convert\n");
160         } else
161                 stack[stackptr++] = onstack;
162 }
163
164 /* pop the top of the stack into onstack */
165 void
166 pop() {
167         if (stackptr == 0)
168                 if (onstack == 0) {
169                         warn();
170                         fprintf(stderr, "I'm confused\n");
171                 } else
172                         onstack = 0;
173         else
174                 onstack = stack[--stackptr];
175         param++;
176 }
177
178 /* convert a character to a terminfo push */
179 static int
180 cvtchar(sp)
181 register char *sp; {
182         char c;
183         int l;
184
185         switch(*sp) {
186         case '\\':
187                 switch(*++sp) {
188                 case '\'':
189                 case '$':
190                 case '\\':
191                 case '%':
192                         c = *sp;
193                         l = 2;
194                         break;
195                 case '\0':
196                         c = '\\';
197                         l = 1;
198                         break;
199                 case '0':
200                         if (sp[1] == '0' && sp[2] == '0') {
201                                 c = '\0';
202                                 l = 4;
203                         } else {
204                                 c = '\200'; /* '\0' ???? */
205                                 l = 2;
206                         }
207                         break;
208                 default:
209                         c = *sp;
210                         l = 2;
211                         break;
212                 }
213                 break;
214         default:
215                 c = *sp;
216                 l = 1;
217         }
218         c &= 0177;
219         if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') {
220                 *dp++ = '%'; *dp++ = '\''; *dp++ = c; *dp++ = '\'';
221         } else {
222                 *dp++ = '%'; *dp++ = '{';
223                 if (c > 99)
224                         *dp++ = c / 100 + '0';
225                 if (c > 9)
226                         *dp++ = (c / 10) % 10 + '0';
227                 *dp++ = c % 10 + '0';
228                 *dp++ = '}';
229         }
230         return l;
231 }
232
233 /* push n copies of param on the terminfo stack if not already there */
234 void
235 getparm(parm, n)
236 int parm;
237 int n; {
238         if (seenr)  {
239                 if (parm == 1)
240                         parm = 2;
241                 else if (parm == 2)
242                         parm = 1;
243         }
244         if (onstack == parm) {
245                 if (n > 1) {
246                         warn();
247                         fprintf(stderr, "string may not be optimal");
248                         *dp++ = '%'; *dp++ = 'P'; *dp++ = 'a';
249                         while(n--) {
250                                 *dp++ = '%'; *dp++ = 'g'; *dp++ = 'a';
251                         }
252                 }
253                 return;
254         }
255         if (onstack != 0)
256                 push();
257
258         onstack = parm;
259
260         while(n--) {            /* %p0 */
261                 *dp++ = '%'; *dp++ = 'p'; *dp++ = '0' + parm;
262         }
263
264         if (seenn && parm < 3) { /* %{96}%^ */
265                 *dp++ = '%'; *dp++ = '{'; *dp++ = '9'; *dp++ = '6'; *dp++ = '}';
266                 *dp++ = '%'; *dp++ = '^';
267         }
268
269         if (seenm && parm < 3) { /* %{127}%^ */
270                 *dp++ = '%'; *dp++ = '{'; *dp++ = '1'; *dp++ = '2'; *dp++ = '7';
271                 *dp++ = '}'; *dp++ = '%'; *dp++ = '^';
272         }
273 }
274
275 /* convert a string to terminfo format */
276 char *
277 convstr(s, i)
278 register char *s;
279 int i; {
280         static char line[MAX_LINE];
281         register char *cap;
282         int nocode = 0;
283
284         stackptr = 0;
285         onstack = 0;
286         seenm = 0;
287         seenn = 0;
288         seenr = 0;
289         param = 1;
290
291         dp = line;
292         cap = strnames[i];
293 #if 0
294         if (cap[0] == 'k'
295             || ((cap[0] == 'i' || cap[0] == 'r') && cap[1] == 's'
296                 && (cap[2] == '1' || cap[2] == '2' || cap[2] == '3')))
297         /* if (k.* || [ir]s[123]) */
298                 nocode = 1;
299 #else
300         if (_strflags[i] != 'G')
301                 nocode = 1;
302 #endif
303         if (!nocode) {
304                 char *d = s;
305                 while(*s != '\0') {
306                         if (s[0] == '\\' && s[1] != '\0')
307                                 s++;
308                         else if (s[0] == '%' && s[1] != '\0') {
309                                 if (s[1] == 'p') {
310                                         if (termcap) {
311                                                 warn();
312                                                 fprintf(stderr,
313 "string '%s' already in terminfo format\n", strcodes[i]);
314                                                 nocode = 1;
315                                                 break;
316                                         } else
317                                                 nocode = 1;
318                                 }
319                                 s++;
320                         }
321                         s++;
322                 }
323                 if (!nocode && !termcap) {
324                         warn();
325                         fprintf(stderr,
326 "string '%s' not in terminfo format, converting...\n", cap);
327                 }
328                 s = d;
329         }
330         while(*s != '\0') {
331                 switch(*s) {
332                 case '%':
333                         s++;
334                         if (nocode) {
335                                 *dp++ = '%';
336                                 break;
337                         }
338                         switch(*s++) {
339                         case '%': *dp++ = '%'; break;
340                         case 'r':
341                                 if (seenr++ == 1) {
342                                         warn();
343                                         fprintf(stderr, "seen %%r twice\n");
344                                 }
345                                 break;
346                         case 'm':
347                                 if (seenm++ == 1) {
348                                         warn();
349                                         fprintf(stderr, "seen %%m twice\n");
350                                 }
351                                 break;
352                         case 'n':
353                                 if (seenn++ == 1) {
354                                         warn();
355                                         fprintf(stderr, "seen %%n twice\n");
356                                 }
357                                 break;
358                         case 'i': *dp++ = '%'; *dp++ = 'i'; break;
359                         case '6':
360                         case 'B':
361                                 getparm(param, 2);
362                                 /* %{6}%*%+ */
363                                 *dp++ = '%'; *dp++ = '{'; *dp++ = '6';
364                                 *dp++ = '}'; *dp++ = '%'; *dp++ = '*';
365                                 *dp++ = '%'; *dp++ = '+';
366                                 break;
367                         case '8':
368                         case 'D':
369                                 getparm(param, 2);
370                                 /* %{2}%*%- */
371                                 *dp++ = '%'; *dp++ = '{'; *dp++ = '2';
372                                 *dp++ = '}'; *dp++ = '%'; *dp++ = '*';
373                                 *dp++ = '%'; *dp++ = '-';
374                                 break;
375                         case '>':
376                                 getparm(param, 2);
377                                 /* %?%{x}%>%t%{y}%+%; */
378                                 *dp++ = '%'; *dp++ = '?';
379                                 s += cvtchar(s);
380                                 *dp++ = '%'; *dp++ = '>';
381                                 *dp++ = '%'; *dp++ = 't';
382                                 s += cvtchar(s);
383                                 *dp++ = '%'; *dp++ = '+';
384                                 *dp++ = '%'; *dp++ = ';';
385                                 break;
386                         case 'a':
387                                 if ((*s == '=' || *s == '+' || *s == '-'
388                                      || *s == '*' || *s == '/')
389                                     && (s[1] == 'p' || s[1] == 'c')
390                                     && s[2] != '\0') {
391                                         int l;
392                                         l = 2;
393                                         if (*s != '=')
394                                                 getparm(param, 1);
395                                         if (s[1] == 'p') {
396                                                 getparm(param + s[2] - '@', 1);
397                                                 if (param != onstack) {
398                                                         pop();
399                                                         param--;
400                                                 }
401                                                 l++;
402                                         } else
403                                                 l += cvtchar(s + 2);
404                                         switch(*s) {
405                                         case '+':
406                                                 *dp++ = '%'; *dp++ = '+';
407                                                 break;
408                                         case '-':
409                                                 *dp++ = '%'; *dp++ = '-';
410                                                 break;
411                                         case '*':
412                                                 *dp++ = '%'; *dp++ = '*';
413                                                 break;
414                                         case '/':
415                                                 *dp++ = '%'; *dp++ = '/';
416                                                 break;
417                                         case '=':
418                                                 if (seenr)
419                                                         if (param == 1)
420                                                                 onstack = 2;
421                                                         else if (param == 2)
422                                                                 onstack = 1;
423                                                         else
424                                                                 onstack = param;
425                                                 else
426                                                         onstack = param;
427                                                 break;
428                                         }
429                                         s += l;
430                                         break;
431                                 }
432                                 getparm(param, 1);
433                                 s += cvtchar(s);
434                                 *dp++ = '%'; *dp++ = '+';
435                                 break;
436                         case '+':
437                                 getparm(param, 1);
438                                 s += cvtchar(s);
439                                 *dp++ = '%'; *dp++ = '+';
440                                 *dp++ = '%'; *dp++ = 'c';
441                                 pop();
442                                 break;
443                         case 's':
444                                 s += cvtchar(s);
445                                 getparm(param, 1);
446                                 *dp++ = '%'; *dp++ = '-';
447                                 break;
448                         case '-':
449                                 s += cvtchar(s);
450                                 getparm(param, 1);
451                                 *dp++ = '%'; *dp++ = '-';
452                                 *dp++ = '%'; *dp++ = 'c';
453                                 pop();
454                                 break;
455                         case '.':
456                                 getparm(param, 1);
457                                 *dp++ = '%'; *dp++ = 'c';
458                                 pop();
459                                 break;
460                         case '2':
461                                 getparm(param, 1);
462                                 *dp++ = '%'; *dp++ = '0';
463                                 *dp++ = '2'; *dp++ = 'd';
464                                 pop();
465                                 break;
466                         case '3':
467                                 getparm(param, 1);
468                                 *dp++ = '%'; *dp++ = '0';
469                                 *dp++ = '3'; *dp++ = 'd';
470                                 pop();
471                                 break;
472                         case 'd':
473                                 getparm(param, 1);
474                                 *dp++ = '%'; *dp++ = 'd';
475                                 pop();
476                                 break;
477                         case 'f':
478                                 param++;
479                                 break;
480                         case 'b':
481                                 param--;
482                                 break;
483                         default:
484                                 warn();
485                                 *dp++ = '%';
486                                 s--;
487                                 fprintf(stderr, "'%s' unknown %% code %c",
488                                         strcodes[i], *s);
489                                 if (*s >= 0 && *s < 32)
490                                         fprintf(stderr, "^%c\n", *s + '@');
491                                 else if (*s < 0 || *s >= 127)
492                                         fprintf(stderr, "\\%03o\n", *s & 0377);
493                                 else
494                                         fprintf(stderr, "%c\n", *s);
495                                 break;
496                         }
497                         break;
498                 case '\\':
499                         if (!compile) {*dp++ = *s++; *dp++ = *s++; break;}
500                         /* FALLTHROUGH */
501                 case '\n':
502                         if (!compile) {*dp++ = '\\'; *dp++ = 'n'; s++; break;}
503                         /* FALLTHROUGH */
504                 case '\t':
505                         if (!compile) {*dp++ = '\\'; *dp++ = 't'; s++; break;}
506                         /* FALLTHROUGH */
507                 case '\r':
508                         if (!compile) {*dp++ = '\\'; *dp++ = 'r'; s++; break;}
509                         /* FALLTHROUGH */
510                 case '\200':
511                         if (!compile) {*dp++ = '\\'; *dp++ = '0'; s++; break;}
512                         /* FALLTHROUGH */
513                 case '\f':
514                         if (!compile) {*dp++ = '\\'; *dp++ = 'f'; s++; break;}
515                         /* FALLTHROUGH */
516                 case '\b':
517                         if (!compile) {*dp++ = '\\'; *dp++ = 'b'; s++; break;}
518                         /* FALLTHROUGH */
519                 case ' ':
520                         if (!compile) {*dp++ = '\\'; *dp++ = 's'; s++; break;}
521                         /* FALLTHROUGH */
522                 case '^':
523                         if (!compile) {*dp++ = '\\'; *dp++ = '^'; s++; break;}
524                         /* FALLTHROUGH */
525                 case ':':
526                         if (!compile) {*dp++ = '\\'; *dp++ = ':'; s++; break;}
527                         /* FALLTHROUGH */
528                 case ',':
529                         if (!compile) {*dp++ = '\\'; *dp++ = ','; s++; break;}
530                         /* FALLTHROUGH */
531 #if 0
532                 case '\'':
533                         if (!compile) {*dp++ = '\\'; *dp++ = '\''; s++; break;}
534                         /* FALLTHROUGH */
535 #endif
536                 default:
537                         if (compile)
538                                 *dp++ = *s++;
539                         else if (*s > 0 && *s < 32) {
540                                 *dp++ = '^';
541                                 *dp++ = *s + '@';
542                                 s++;
543                         } else if (*s <= 0 || *s >= 127) {
544                                 *dp++ = '\\';
545                                 *dp++ = ((*s & 0300) >> 6) + '0';
546                                 *dp++ = ((*s & 0070) >> 3) + '0';
547                                 *dp++ = (*s & 0007) + '0';
548                                 s++;
549                         } else
550                                 *dp++ = *s++;
551                         break;
552                 }
553         }
554         *dp = '\0';
555         return line;
556 }
557
558 #define LSB(n) ((unsigned) (n) & 0377)
559 #define MSB(n) (((unsigned) (n) >> 8) & 0377)
560
561 void
562 writebin(fd, name)
563 int fd;
564 char *name; {
565         static char bin[MAX_BUF + 1];
566         register char *s;
567         register char *d;
568         register int i;
569         register char *t;
570         register int n;
571         char *strtbl;
572         int sz_name, n_bools, n_nums, n_offs, sz_strs;
573         extern int _boolorder[], _numorder[], _strorder[];
574
575         strncpy(bin + 12, name, 127);
576         bin[12 + 128] = '\0';
577         sz_name = strlen(name) + 1;
578         if (sz_name > 128)
579                 sz_name = 128;
580
581         s = bin + 12 + sz_name;
582         for(i = 0; _boolorder[i] != -1; i++) {
583                 switch(_term_buf.bools[i]) {
584                 case -1: *s++ = 0; break;
585                 case  0: *s++ = 0377; break;
586                 default: *s++ = 1; break;
587                 }
588         }
589         n_bools = i;
590         if ((sz_name + n_bools) & 1)
591                 n_bools++;
592
593         s = bin + 12 + sz_name + n_bools;
594         for(i = 0; _numorder[i] != -1; i++) {
595                 n = _term_buf.nums[_numorder[i]];
596                 switch(n) {
597                 case -2: *s++ = 0377; *s++ = 0377; break;
598                 case -1: *s++ = 0376; *s++ = 0377; break;
599                 default:
600                         *s++ = LSB(n);
601                         *s++ = MSB(n);
602                 }
603         }
604         n_nums = i;
605
606         s = bin + 12 + sz_name + n_bools + n_nums * 2;
607         for(i = 0; _strorder[i] != -1; i++) {
608                 if (_term_buf.strs[_strorder[i]] == (char *) 0) {
609                         *s++ = 0376; *s++ = 0377;
610                 } else {
611                         *s++ = 0377; *s++ = 0377;
612                 }
613         }
614         n_offs = i;
615
616         s = bin + 12 + sz_name + n_bools + n_nums * 2;
617         strtbl = d = s + n_offs * 2;
618         for(i = 0; _strorder[i] != -1; i++) {
619                 t = _term_buf.strs[_strorder[i]];
620                 if (t == (char *) -1 || t == (char *)  0)
621                         s += 2;
622                 else {
623                         n = d - strtbl;
624                         *s++ = LSB(n);
625                         *s++ = MSB(n);
626                         t = convstr(t, _strorder[i]);
627                         while(*t != '\0') {
628                                 *d++ = *t++;
629                                 if (d >= bin + MAX_BUF - 1) {
630                                         warn();
631                                         fprintf(stderr,
632                                         "compiled entry to big\n");
633                                         *d++ = '\0';
634                                         goto toobig;
635                                 }
636                         }
637                         *d++ = '\0';
638                 }
639         }
640
641 toobig:
642         sz_strs = d - strtbl;
643
644         bin[0] = 032;
645         bin[1] = 01;
646         bin[2] = LSB(sz_name);
647         bin[3] = MSB(sz_name);
648         bin[4] = LSB(n_bools);
649         bin[5] = MSB(n_bools);
650         bin[6] = LSB(n_nums);
651         bin[7] = MSB(n_nums);
652         bin[8] = LSB(n_offs);
653         bin[9] = MSB(n_offs);
654         bin[10] = LSB(sz_strs);
655         bin[11] = MSB(sz_strs);
656
657         if (write(fd, bin, d - bin) == -1)
658                 quit(errno, "can't write binary file");
659
660         return;
661 }
662
663 void
664 find_directory() {
665         struct term_path *p;
666         struct stat st;
667
668         if (directory != NULL)
669                 return;
670
671         p = path;
672         while(p->type != -1 && p->file != NULL) {
673                 if (stat(p->file, &st) == 0) {
674                         if ((st.st_mode & 0170000) == 0040000) {
675                                 directory = p->file;
676                                 return;
677                         }
678                 }
679                 p++;
680         }
681         quit(-1, "can't find a terminfo directory");
682 }
683
684 /* convert a terminal name to a binary filename */
685 char *
686 binfile(name)
687 char *name; {
688         static char line[MAX_LINE+1];
689
690         sprintf(line, "%s/%c/%s", directory, *name, name);
691         return line;
692 }
693
694 char *
695 bindir(name)
696 char *name; {
697         static char line[MAX_LINE+1];
698
699         sprintf(line, "%s/%c", directory, *name);
700         return line;
701 }
702
703 int
704 badname(name)
705 char *name; {
706         while(*name) {
707                 if (*name == '/' || !isgraph(*name))
708                         return 1;
709                 name++;
710         }
711         return 0;
712 }
713
714 /* output a terminfo binary */
715 void
716 outputbin(name)
717 char *name; {
718         register char *s, *d, *last;
719         char tmp[MAX_LINE+1];
720         char line[MAX_LINE+1];
721         int fd;
722
723         find_directory();
724
725         s = name;
726         d = line;
727         while(*s != '\0' && d < line + MAX_LINE) {
728                 *d++ = *s++;
729         }
730
731         while(d > line && d[-1] == '|') {
732                 d--;
733         }
734
735         *d = '\0';
736
737         s = strtok(line, "|");
738         last = NULL;
739
740         while(s != NULL && last == NULL) {
741                 if (*s == '\0') {
742                         ;
743                 } else if (badname(s)) {
744                         if (lineno)
745                                 warn();
746                         fprintf(stderr, "bad terminal name '%s', ignored.\n",
747                                 s);
748                 } else {
749                         if (access(bindir(s), 2) == -1) {
750                                 if (errno != ENOENT)
751                                         quit(errno,
752                                              "can't access directory '%s'",
753                                              bindir(s));
754                                 if (mkdir(bindir(s), 0777) == -1)
755                                         quit(errno, "can't make directory '%s'",
756                                              bindir(s));
757                         }
758                         fd = open(binfile(s), O_WRONLY | O_CREAT | O_EXCL,
759                                   0666);
760                         if (fd == -1) {
761                                 if (errno != EEXIST)
762                                         quit(errno, "can't open file '%s'",
763                                              binfile(s));
764                                 if (unlink(binfile(s)) == -1)
765                                         quit(errno, "can't unlink file '%s'",
766                                              binfile(s));
767                                 fd = open(binfile(s),
768                                           O_WRONLY | O_CREAT | O_EXCL, 0666);
769                                 if (fd == -1)
770                                         quit(errno, "can't create file '%s'",
771                                              binfile(s));
772                         }
773                         writebin(fd, name);
774                         close(fd);
775                         last = s;
776                 }
777                 s = strtok(NULL, "|");
778         }
779
780         if (last == NULL) {
781                 if (lineno)
782                         warn();
783                 fprintf(stderr, "no terminal name, entry ignored.\n");
784                 return;
785         }
786
787         while(s != NULL && s + strlen(s) != d) {
788                 if (*s == '\0' || strcmp(s, last) == 0) {
789                         ;
790                 } else if (badname(s)) {
791                         if (lineno)
792                                 warn();
793                         fprintf(stderr, "bad terminal name '%s', ignored.\n",
794                                 s);
795                 } else {
796                         if (access(bindir(s), 2) == -1) {
797                                 if (errno != ENOENT)
798                                         quit(errno,
799                                              "can't access directory '%s'",
800                                              bindir(s));
801                                 if (mkdir(bindir(s), 0777) == -1)
802                                         quit(errno, "can't make directory '%s'",
803                                              bindir(s));
804                         }
805                         if (access(binfile(s), 0) == -1) {
806                                 if (errno != ENOENT)
807                                         quit(errno, "can't access file '%s'",
808                                              binfile(s));
809                         } else if (unlink(binfile(s)) == -1) {
810                                         quit(errno, "can't unlink file '%s'",
811                                              binfile(s));
812                         }
813                         strcpy(tmp, binfile(last));
814                         if (link(tmp, binfile(s)) == -1) {
815                                 quit(errno, "can't link '%s' to '%s'",
816                                      last, binfile(s));
817                         }
818                 }
819                 s = strtok(NULL, "|");
820         }
821         return;
822 }
823
824 /* output an entry in terminfo source format */
825 void
826 outputinfo(name)
827 char *name; {
828         int i;
829         char line[MAX_LINE];
830
831         printf("%s,\n", name);
832
833         for(i = 0; i < NUM_OF_BOOLS; i++)
834                 if (_term_buf.bools[i] == 0) {
835                         sprintf(line, "%s@", boolnames[i]);
836                         putstr(line);
837                 } else if (_term_buf.bools[i] != -1)
838                         putstr(boolnames[i]);
839
840         for(i = 0; i < NUM_OF_NUMS; i++)
841                 if (_term_buf.nums[i] == -1) {
842                         sprintf(line, "%s@", numnames[i]);
843                         putstr(line);
844                 } else if (_term_buf.nums[i] != -2) {
845                         sprintf(line, "%s#%d", numnames[i], _term_buf.nums[i]);
846                         putstr(line);
847                 }
848
849         for(i = 0; i < NUM_OF_STRS; i++)
850                 if (_term_buf.strs[i] == NULL) {
851                         sprintf(line, "%s@", strnames[i]);
852                         putstr(line);
853                 } else if (_term_buf.strs[i] != (char *) -1) {
854                         sprintf(line, "%s=%s", strnames[i],
855                                 convstr(_term_buf.strs[i], i));
856                         putstr(line);
857                 }
858         putstr(NULL);
859 }
860
861 /* convert a terminfo entry to binary format */
862 void
863 convtinfo() {
864         int i, r;
865
866         termcap = 0;
867
868         for(i = 0; i < NUM_OF_BOOLS; i++)
869                 _term_buf.bools[i] = -1;
870         for(i = 0; i < NUM_OF_NUMS; i++)
871                 _term_buf.nums[i] = -2;
872         for(i = 0; i < NUM_OF_STRS; i++)
873                 _term_buf.strs[i] = (char *) -1;
874
875         _term_buf.name_all = NULL;
876
877         r = _gettinfo(buf, &_term_buf, path);
878         if (r != 0) {
879                 if (lineno == 0)
880                         quit(-1, "problem reading entry");
881                 else {
882                         warn();
883                         fprintf(stderr, "problem reading entry\n");
884                 }
885         }
886
887         if (compile)
888                 outputbin(_term_buf.name_all);
889         else
890                 outputinfo(_term_buf.name_all);
891         return;
892 }
893
894 /* convert a terminfo binary to terminfo source */
895 void
896 convtbin() {
897         int i, r;
898
899         termcap = 0;
900
901         for(i = 0; i < NUM_OF_BOOLS; i++)
902                 _term_buf.bools[i] = -1;
903         for(i = 0; i < NUM_OF_NUMS; i++)
904                 _term_buf.nums[i] = -2;
905         for(i = 0; i < NUM_OF_STRS; i++)
906                 _term_buf.strs[i] = (char *) -1;
907
908         _term_buf.name_all = NULL;
909
910         r = _gettbin(buf, &_term_buf);
911         if (r != 0) {
912                 if (lineno == 0)
913                         quit(-1, "problem reading entry");
914                 else {
915                         warn();
916                         fprintf(stderr, "problem reading entry\n");
917                 }
918         }
919
920         outputinfo(_term_buf.name_all);
921
922         return;
923 }
924
925 /* convert a termcap entry to terminfo format */
926 void
927 convtcap() {
928         int i, r;
929         char *name;
930
931         termcap = 1;
932
933         for(i = 0; i < NUM_OF_BOOLS; i++)
934                 _term_buf.bools[i] = -1;
935         for(i = 0; i < NUM_OF_NUMS; i++)
936                 _term_buf.nums[i] = -2;
937         for(i = 0; i < NUM_OF_STRS; i++)
938                 _term_buf.strs[i] = (char *) -1;
939
940         _term_buf.name_all = NULL;
941
942
943 #if DEBUG
944         printf("%s\n", buf);
945 #endif
946         r = _gettcap(buf, &_term_buf, path);
947         if (r != 0) {
948                 if (lineno == 0)
949                         quit(-1, "problem reading entry");
950                 else {
951                         warn();
952                         fprintf(stderr, "problem reading entry\n");
953                 }
954         }
955
956         if (dodefault && !continued)
957                 _tcapdefault();
958
959         _tcapconv();
960
961         name = _term_buf.name_all;
962 #if DEBUG
963         printf("...%s\n", name);
964 #endif
965         if (name[0] != '\0' && name[1] != '\0' && name[2] == '|')
966                 name += 3;      /* skip the 2 letter code */
967
968         if (compile)
969                 outputbin(name);
970         else
971                 outputinfo(name);
972 }
973
974 void
975 convbinfile(file)
976 char *file; {
977         register FILE *f;
978         int r;
979
980         f = fopen(file, "r");
981
982         if (f == NULL)
983                 quit(errno, "can't open '%s'", file);
984
985         r = fread(buf, sizeof(char), MAX_BUF, f);
986         if (r < 12 || buf[0] != 032 || buf[1] != 01)
987                 quit(-1, "file '%s' corrupted", file);
988
989         convtbin();
990 }
991
992 /* convert a termcap file to terminfo format */
993 void
994 convtcfile(file)
995 char *file; {
996         int nocolon;
997         register int c;
998         register char *d;
999         register FILE *f;
1000
1001         f = fopen(file, "r");
1002
1003         if (f == NULL)
1004                 quit(errno, "can't open '%s'", file);
1005
1006         d = buf;
1007         c = getc(f);
1008         while(c != EOF) {
1009                 lineno++;
1010                 if (c == '#') {
1011                         if (keepcomments) {
1012                                 do {
1013                                         putchar(c);
1014                                         c = getc(f);
1015                                 } while(c != '\n' && c != EOF);
1016                                 putchar('\n');
1017                         } else
1018                                 do
1019                                         c = getc(f);
1020                                 while(c != '\n' && c != EOF);
1021                         if (c != EOF)
1022                                 c = getc(f);
1023                         continue;
1024                 }
1025                 while(isspace(c) && c != '\n')
1026                         c = getc(f);
1027                 if (c == '\n' && buf == d) {
1028                         c = getc(f);
1029                         continue;
1030                 }
1031
1032                 while(c != EOF) {
1033                         if (c == '\\') {
1034                                 c = getc(f);
1035                                 if (c == EOF)
1036                                         break;
1037                                 if (c == '\n') {
1038                                         c = getc(f);
1039                                         break;
1040                                 }
1041                                 *d++ = '\\';
1042                                 *d++ = c;
1043                         } else if (c == '\n') {
1044                                 *d = '\0';
1045                                 if (*--d == ':') {
1046                                         nocolon = 0;
1047                                         *d-- = '\0';
1048                                 } else {
1049                                         nocolon = 1;
1050                                 }
1051                                 while(d > buf && *d != ':')
1052                                         d--;
1053                                 if (d[1] == 't' && d[2] == 'c' && d[3] == '=') {
1054                                         continued = 1;
1055                                         d[1] = '\0';
1056                                 } else
1057                                         continued = 0;
1058                                 convtcap();
1059                                 if (nocolon) {
1060                                         warn();
1061                                         fprintf(stderr,
1062                                                 "entry doesn't end with :\n");
1063                                 }
1064                                 _term_buf.strbuf = _endstr();
1065                                 _del_strs(&_term_buf);
1066                                 if (continued) {
1067                                         printf("\tuse=%s,\n", d + 4);
1068                                 }
1069                                 d = buf;
1070                                 c = getc(f);
1071                                 break;
1072                         } else
1073                                 *d++ = c;
1074                         c = getc(f);
1075                 }
1076         }
1077 }
1078
1079 static int
1080 getln(f, buf, len)
1081 FILE *f;
1082 register char *buf;
1083 int len; {
1084         register int c, i = 0;
1085
1086         while((c = getc(f)) == '#') {
1087                 lineno++;
1088                 if (keepcomments) {
1089                         putchar('#');
1090                         while((c = getc(f)) != '\n') {
1091                                 if (c == EOF)
1092                                         return -1;
1093                                 putchar(c);
1094                         }
1095                         putchar('\n');
1096                 } else {
1097                         while((c = getc(f)) != '\n')
1098                                 if (c == EOF)
1099                                         return -1;
1100                 }
1101         }
1102
1103         lineno++;
1104         while(c != '\n') {
1105                 if (c == EOF)
1106                         return -1;
1107                 if (i < len) {
1108                         i++;
1109                         *buf++ = c;
1110                 }
1111                 c = getc(f);
1112         }
1113
1114         while(isspace(*(buf-1))) {
1115                 buf--;
1116                 i--;
1117         }
1118
1119         *buf = '\0';
1120         return i;
1121 }
1122
1123 void
1124 convtifile(file)
1125 char *file; {
1126         static char line[MAX_LINE+1];
1127         int l;
1128         int n;
1129         register FILE *f;
1130
1131         f = fopen(file, "r");
1132
1133         if (f == NULL)
1134                 quit(errno, "can't open '%s'", file);
1135
1136         lineno = 0;
1137
1138         l = getln(f, line, MAX_LINE);
1139         while(l != -1) {
1140                 if (line[l-1] == ':') {
1141                         strncpy(buf, line, MAX_BUF);
1142                         convtcap();
1143                 } else if (line[l-1] == '\\') {
1144                         n = MAX_BUF;
1145                         do {
1146                                 line[--l] = '\0';
1147                                 if (n > 0)
1148                                         strncpy(buf + MAX_BUF - n, line, n);
1149                                 n -= l;
1150                                 l = getln(f, line, MAX_LINE);
1151                         } while(l != -1 && line[l-1] == '\\');
1152                         if (n > 0 && l != -1)
1153                                 strncpy(buf + MAX_BUF - n, line, n);
1154                         convtcap();
1155                 } else if (line[l-1] == ',') {
1156                         n = MAX_BUF;
1157                         do {
1158                                 if (n > 0)
1159                                         strncpy(buf + MAX_BUF - n, line, n);
1160                                 n -= l;
1161                                 l = getln(f, line, MAX_LINE);
1162                         } while(l != -1 && isspace(line[0]));
1163 #if 0
1164                         printf("buf = '%s'\n", buf);
1165 #endif
1166                         convtinfo();
1167                         continue;
1168                 } else if (line[0] != '\0') {
1169                         warn();
1170                         fprintf(stderr, "malformed line\n");
1171                         if (keepcomments) {
1172                                 printf("%s\n", line);
1173                         }
1174                 }
1175                 l = getln(f, line, MAX_LINE);
1176         }
1177         return;
1178 }
1179
1180 /* dummy routine for quit */
1181 /* ARGSUSED */
1182 void
1183 do_cleanup(e)
1184 int e; {
1185         return;
1186 }
1187
1188 /* print out usage, called by quit */
1189 /* ARGSUSED */  
1190 void
1191 usage(e)
1192 int e; {
1193         fprintf(stderr, "%s\n%s\n%s\n%s\n", 
1194                 "usage: tconv [-b] [-c [-OUGd]] [-i] [-B [-D dir]] [-I] [-k] [-V]",
1195                 "             [-t term] [file]",
1196                 "       tic [file]",
1197                 "       captoinfo [-t term] [-OUGdk] [file]");
1198         return;
1199 }
1200
1201 int
1202 main(argc, argv)
1203 int argc;
1204 char **argv; {
1205         char *term = NULL;
1206         char *file = NULL;
1207         int r;
1208         char c;
1209         int pversion = 0;
1210
1211         prg_name = strrchr(argv[0], '/');
1212         if (prg_name == NULL)
1213                 prg_name = argv[0];
1214         else
1215                 prg_name++;
1216
1217         cleanup = usage;
1218
1219         opterr = 0;
1220
1221         if (strcmp(prg_name, "tic") == 0)
1222                 compile = 1;
1223
1224         while ((c = getopt(argc, argv, "bciBIOGUdkVD:t:")) != -1) {
1225                 switch(c) {
1226                 case 'O':
1227                         noOT = 0;
1228                         break;
1229                 case 'G':
1230                         noGNU = 0;
1231                         break;
1232                 case 'U':
1233                         noUW = 0;
1234                         break;
1235                 case 'D':
1236                         if (directory != NULL)
1237                                 quit(-1, "more than one directory specified");
1238                         directory = optarg;
1239                         break;
1240                 case 't':
1241                         if (term != NULL)
1242                                 quit(-1, "more than one terminal specified");
1243                         term = optarg;
1244                         break;
1245                 case 'd': dodefault = 0; break;
1246                 case 'k': keepcomments = 1; break;
1247                 case 'b': from_tbin = 1; break;
1248                 case 'c': from_tcap = 1; break;
1249                 case 'i': from_tinfo = 1; break;
1250                 case 'B': compile = 1; break;
1251                 case 'I': compile = 0; break;
1252                 case 'V': pversion = 1; break;
1253                 case '?':
1254                 default:
1255                         quit(-1, "bad or missing command line argument");
1256                 }
1257         }
1258
1259         if (pversion) {
1260                 quit(0, "%s\n%s", _mytinfo_version, SCCSid);
1261         }
1262
1263         if (optind == argc - 1)
1264                 file = argv[optind];
1265         else if (optind != argc)
1266                 quit(-1, "wrong number of arguments");
1267
1268         if (from_tbin + from_tcap + from_tinfo > 1)
1269                 quit(-1, "more than one input file type specified");
1270
1271         if (!from_tcap && !from_tinfo && !from_tbin && file != NULL) {
1272                 if (strcmp(prg_name, "cap2info") == 0
1273                     || strcmp(prg_name, "captoinfo") == 0)
1274                         from_tcap = 1;
1275                 else if (strcmp(prg_name, "tic") == 0)
1276                         from_tinfo = 1;
1277                 else
1278                         quit(-1, "no input file type specified");
1279         }
1280
1281         if (from_tbin && compile)
1282                 quit(-1, "can't convert from binary to binary");
1283
1284         if (file != NULL) {
1285                 if (from_tbin) {
1286                         cleanup = do_cleanup;
1287                         convbinfile(file);
1288                         exit(0);
1289                 }
1290                 if (!compile)
1291                         path = _buildpath(file, 0, NULL, -1);
1292                 else {
1293                         path = _buildpath(file, 0,
1294                                           "$TERMINFO", 2,
1295                                           "$MYTERMINFO", 2,
1296 #ifdef TERMINFODIR
1297                                           TERMINFODIR, 0,
1298 #endif
1299                                           NULL, -1);
1300                 }
1301                 if (path == NULL)
1302                         quit(-1, "can't build path");
1303                 if (term == NULL) {
1304                         cleanup = do_cleanup;
1305                         if (from_tcap && !compile)
1306                                 convtcfile(file);
1307                         else
1308                                 convtifile(file);
1309                         exit(0);
1310                 }
1311         } else if (from_tcap && !compile)
1312                 path = _buildpath("$TERMCAP", 1,
1313 #ifdef TERMCAPFILE
1314                                   TERMCAPFILE, 0,
1315 #endif
1316                                   NULL, -1);
1317         else if (from_tinfo || from_tbin)
1318                 path = _buildpath("$TERMINFO", 2,
1319                                   "$MYTERMINFO", 2,
1320 #ifdef TERMINFODIR
1321                                   TERMINFODIR, 0,
1322 #endif
1323 #ifdef TERMINFOSRC
1324                                   TERMINFOSRC, 0,
1325 #endif
1326                                   NULL, -1);
1327         else if (from_tcap)
1328                 path = _buildpath("$TERMCAP", 1,
1329 #ifdef TERMCAPFILE
1330                                   TERMCAPFILE, 0,
1331 #endif
1332                                   "$TERMINFO", 2,
1333                                   "$MYTERMINFO", 2,
1334 #ifdef TERMINFODIR
1335                                   TERMINFODIR, 0,
1336 #endif
1337                                   NULL, -1);
1338         else
1339                 path = _buildpath(
1340 #ifdef USE_TERMINFO
1341                                   "$MYTERMINFO", 2,
1342                                   "$TERMINFO", 2,
1343 #ifdef TERMINFODIR
1344                                   TERMINFODIR, 0,
1345 #endif
1346 #ifdef TERMINFOSRC
1347                                   TERMINFOSRC, 0,
1348 #endif
1349 #endif
1350 #ifdef USE_TERMCAP
1351                                   "$TERMCAP", 1,
1352 #ifdef TERMCAPFILE
1353                                   TERMCAPFILE, 0,
1354 #endif
1355 #endif
1356                                   NULL, -1);
1357         if (term == NULL) {
1358                 term = getenv("TERM");
1359                 if (term == NULL)
1360                         quit(-1, "no terminal type given");
1361         }
1362
1363         cleanup = do_cleanup;
1364
1365         r = _findterm(term, path, buf);
1366         switch(r) {
1367         case 1:
1368                 convtcap();
1369                 break;
1370         case 2:
1371                 convtinfo();
1372                 break;
1373         case 3:
1374                 if (compile)
1375                         quit(-1, "entry is already compiled");
1376                 convtbin();
1377                 break;
1378         default:
1379                 quit(-1, "can't find a terminal entry for '%s'", term);
1380         }
1381
1382         exit(0);
1383         return 0;
1384 }