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