1 /* $NetBSD: infocmp.c,v 1.8 2013/10/01 09:01:49 roy Exp $ */
4 * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
6 * This code is derived from software contributed to The NetBSD Foundation
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: infocmp.c,v 1.8 2013/10/01 09:01:49 roy Exp $");
33 #include <sys/ioctl.h>
40 #include <term_private.h>
46 typedef struct tient
{
55 static int aflag
, cflag
, nflag
, qflag
, xflag
;
58 outstr(FILE *f
, const char *str
)
65 while ((ch
= (unsigned char)(*str
++)) != '\0') {
76 case '^': /* FALLTHROUGH */
77 case ',': /* escape these */
88 } else if (iscntrl(ch
) &&
91 (l
< 4 || isdigit((unsigned char)*str
)))
97 } else if (!isprint(ch
)) {
99 fprintf(f
, "\\%03o", ch
);
118 ent_compare(const void *a
, const void *b
)
120 const TIENT
*ta
, *tb
;
122 ta
= (const TIENT
*)a
;
123 tb
= (const TIENT
*)b
;
124 return strcmp(ta
->id
, tb
->id
);
134 db
[len
- 3] == '.' &&
135 db
[len
- 2] == 'd' &&
138 setenv("TERMINFO", db
, 1);
142 print_ent(const TIENT
*ents
, size_t nents
)
152 for (i
= 0; i
< nents
; i
++) {
153 if (*ents
[i
].id
== '.' && aflag
== 0)
155 switch (ents
[i
].type
) {
157 if (ents
[i
].flag
== ABSENT_BOOLEAN
)
159 l
= strlen(ents
[i
].id
) + 2;
160 if (ents
[i
].flag
== CANCELLED_BOOLEAN
)
164 if (ents
[i
].num
== ABSENT_NUMERIC
)
166 if (VALID_NUMERIC(ents
[i
].num
))
167 l
= snprintf(nbuf
, sizeof(nbuf
), "%s#%d,",
168 ents
[i
].id
, ents
[i
].num
);
170 l
= snprintf(nbuf
, sizeof(nbuf
), "%s@,",
174 if (ents
[i
].str
== ABSENT_STRING
)
176 if (VALID_STRING(ents
[i
].str
))
177 l
= strlen(ents
[i
].id
) +
178 outstr(NULL
, ents
[i
].str
) + 7;
180 l
= strlen(ents
[i
].id
) + 3;
183 errx(1, "invalid type");
186 if (col
+ l
> cols
) {
192 switch (ents
[i
].type
) {
194 col
+= printf("%s", ents
[i
].id
);
195 if (ents
[i
].flag
== ABSENT_BOOLEAN
||
196 ents
[i
].flag
== CANCELLED_BOOLEAN
)
201 col
+= printf("%s", nbuf
);
204 col
+= printf("%s", ents
[i
].id
);
205 if (VALID_STRING(ents
[i
].str
)) {
207 col
+= outstr(stdout
, ents
[i
].str
);
218 load_ents(TIENT
*ents
, TERMINAL
*t
, char type
)
235 for (i
= 0; i
<= max
; i
++) {
238 if (t
->flags
[i
] == 1 ||
239 (aflag
&& t
->flags
[i
] == CANCELLED_BOOLEAN
))
241 ents
[n
].id
= _ti_flagid(i
);
243 ents
[n
++].flag
= t
->flags
[i
];
247 if (VALID_NUMERIC(t
->nums
[i
]) ||
248 (aflag
&& t
->nums
[i
] == CANCELLED_NUMERIC
))
250 ents
[n
].id
= _ti_numid(i
);
252 ents
[n
++].num
= t
->nums
[i
];
256 if (VALID_STRING(t
->strs
[i
]) ||
257 (aflag
&& t
->strs
[i
] == CANCELLED_STRING
))
259 ents
[n
].id
= _ti_strid(i
);
261 ents
[n
++].str
= t
->strs
[i
];
267 if (xflag
!= 0 && t
->_nuserdefs
!= 0) {
268 for (i
= 0; i
< t
->_nuserdefs
; i
++) {
269 ud
= &t
->_userdefs
[i
];
270 if (ud
->type
== type
) {
274 !VALID_BOOLEAN(ud
->flag
))
279 !VALID_NUMERIC(ud
->num
))
284 !VALID_STRING(ud
->str
))
289 ents
[n
].type
= ud
->type
;
290 ents
[n
].flag
= ud
->flag
;
291 ents
[n
].num
= ud
->num
;
292 ents
[n
++].str
= ud
->str
;
297 qsort(ents
, n
, sizeof(TIENT
), ent_compare
);
302 cprint_ent(TIENT
*ent
)
314 if (VALID_BOOLEAN(ent
->flag
))
315 printf(ent
->flag
== 1 ? "T" : "F");
318 else if (ent
->flag
== CANCELLED_BOOLEAN
)
324 if (VALID_NUMERIC(ent
->num
))
325 printf("%d", ent
->num
);
328 else if (ent
->num
== CANCELLED_NUMERIC
)
334 if (VALID_STRING(ent
->str
)) {
336 outstr(stdout
, ent
->str
);
338 } else if (qflag
== 0)
340 else if (ent
->str
== CANCELLED_STRING
)
349 compare_ents(TIENT
*ents1
, size_t n1
, TIENT
*ents2
, size_t n2
)
357 ee
.flag
= ABSENT_BOOLEAN
;
358 ee
.num
= ABSENT_NUMERIC
;
359 ee
.str
= ABSENT_STRING
;
360 while (i1
!= n1
|| i2
!= n2
) {
366 c
= strcmp(ents1
[i1
].id
, ents2
[i2
].id
);
384 if (e1
->flag
== e2
->flag
)
385 printf("\t%s\n", ents1
[i1
].id
);
388 if (e1
->flag
== e2
->flag
)
393 if (e1
->num
== e2
->num
)
395 ents1
[i1
].id
, ents1
[i1
].num
);
398 if (e1
->num
== e2
->num
)
403 if (VALID_STRING(e1
->str
) &&
404 VALID_STRING(e2
->str
) &&
405 strcmp(e1
->str
, e2
->str
) == 0) {
406 printf("\t%s=", ents1
[i1
].id
);
407 outstr(stdout
, ents1
[i1
].str
);
412 if (VALID_STRING(e1
->str
) &&
413 VALID_STRING(e2
->str
) &&
414 strcmp(e1
->str
, e2
->str
) == 0)
418 printf("\t%s: ", e1
->id
);
430 load_term(const char *name
)
434 t
= calloc(1, sizeof(*t
));
438 name
= getenv("TERM");
441 if (_ti_getterm(t
, name
, 1) == 1)
444 if (_ti_database
== NULL
)
445 errx(1, "no terminal definition found in internal database");
447 errx(1, "no terminal definition found in %s.db", _ti_database
);
451 show_missing(TERMINAL
*t1
, TERMINAL
*t2
, char type
)
467 for (i
= 0; i
<= max
; i
++) {
470 if (t1
->flags
[i
] != ABSENT_BOOLEAN
||
471 t2
->flags
[i
] != ABSENT_BOOLEAN
)
476 if (t1
->nums
[i
] != ABSENT_NUMERIC
||
477 t2
->nums
[i
] != ABSENT_NUMERIC
)
482 if (t1
->strs
[i
] != ABSENT_STRING
||
483 t2
->strs
[i
] != ABSENT_STRING
)
488 printf("\t!%s.\n", id
);
493 find_userdef(TERMINAL
*term
, const char *id
)
497 for (i
= 0; i
< term
->_nuserdefs
; i
++)
498 if (strcmp(term
->_userdefs
[i
].id
, id
) == 0)
499 return &term
->_userdefs
[i
];
504 use_terms(TERMINAL
*term
, size_t nuse
, char **uterms
)
507 TERMUSERDEF
*ud
, *tud
;
508 size_t i
, j
, agree
, absent
, data
;
510 terms
= malloc(sizeof(**terms
) * nuse
);
513 for (i
= 0; i
< nuse
; i
++) {
514 if (strcmp(term
->name
, *uterms
) == 0)
515 errx(1, "cannot use same terminal");
516 for (j
= 0; j
< i
; j
++)
517 if (strcmp(terms
[j
]->name
, *uterms
) == 0)
518 errx(1, "cannot use same terminal");
519 terms
[i
] = load_term(*uterms
++);
522 for (i
= 0; i
< TIFLAGMAX
+ 1; i
++) {
523 agree
= absent
= data
= 0;
524 for (j
= 0; j
< nuse
; j
++) {
525 if (terms
[j
]->flags
[i
] == ABSENT_BOOLEAN
||
526 terms
[j
]->flags
[i
] == CANCELLED_BOOLEAN
)
530 if (term
->flags
[i
] == terms
[j
]->flags
[i
])
536 if (agree
> 0 && agree
+ absent
== nuse
)
537 term
->flags
[i
] = ABSENT_BOOLEAN
;
538 else if (term
->flags
[i
] == ABSENT_BOOLEAN
)
539 term
->flags
[i
] = CANCELLED_BOOLEAN
;
542 for (i
= 0; i
< TINUMMAX
+ 1; i
++) {
543 agree
= absent
= data
= 0;
544 for (j
= 0; j
< nuse
; j
++) {
545 if (terms
[j
]->nums
[i
] == ABSENT_NUMERIC
||
546 terms
[j
]->nums
[i
] == CANCELLED_NUMERIC
)
550 if (term
->nums
[i
] == terms
[j
]->nums
[i
])
556 if (agree
> 0 && agree
+ absent
== nuse
)
557 term
->nums
[i
] = ABSENT_NUMERIC
;
558 else if (term
->nums
[i
] == ABSENT_NUMERIC
)
559 term
->nums
[i
] = CANCELLED_NUMERIC
;
562 for (i
= 0; i
< TISTRMAX
+ 1; i
++) {
563 agree
= absent
= data
= 0;
564 for (j
= 0; j
< nuse
; j
++) {
565 if (terms
[j
]->strs
[i
] == ABSENT_STRING
||
566 terms
[j
]->strs
[i
] == CANCELLED_STRING
)
570 if (VALID_STRING(term
->strs
[i
]) &&
571 strcmp(term
->strs
[i
],
572 terms
[j
]->strs
[i
]) == 0)
578 if (agree
> 0 && agree
+ absent
== nuse
)
579 term
->strs
[i
] = ABSENT_STRING
;
580 else if (term
->strs
[i
] == ABSENT_STRING
)
581 term
->strs
[i
] = CANCELLED_STRING
;
584 /* User defined caps are more tricky.
585 First we set any to absent that agree. */
586 for (i
= 0; i
< term
->_nuserdefs
; i
++) {
587 agree
= absent
= data
= 0;
588 ud
= &term
->_userdefs
[i
];
589 for (j
= 0; j
< nuse
; j
++) {
590 tud
= find_userdef(terms
[j
], ud
->id
);
597 if (tud
->type
== 'f' &&
598 tud
->flag
== ud
->flag
)
602 if (tud
->type
== 'n' &&
607 if (tud
->type
== 's' &&
608 VALID_STRING(tud
->str
) &&
609 VALID_STRING(ud
->str
) &&
610 strcmp(ud
->str
, tud
->str
) == 0)
618 if (agree
> 0 && agree
+ absent
== nuse
) {
619 ud
->flag
= ABSENT_BOOLEAN
;
620 ud
->num
= ABSENT_NUMERIC
;
621 ud
->str
= ABSENT_STRING
;
625 /* Now add any that we don't have as cancelled */
626 for (i
= 0; i
< nuse
; i
++) {
627 for (j
= 0; j
< terms
[i
]->_nuserdefs
; j
++) {
628 ud
= find_userdef(term
, terms
[i
]->_userdefs
[j
].id
);
630 continue; /* We have handled this */
631 term
->_userdefs
= realloc(term
->_userdefs
,
632 sizeof(*term
->_userdefs
) * (term
->_nuserdefs
+ 1));
633 if (term
->_userdefs
== NULL
)
635 tud
= &term
->_userdefs
[term
->_nuserdefs
++];
636 tud
->id
= terms
[i
]->_userdefs
[j
].id
;
637 tud
->type
= terms
[i
]->_userdefs
[j
].flag
;
638 tud
->flag
= CANCELLED_BOOLEAN
;
639 tud
->num
= CANCELLED_NUMERIC
;
640 tud
->str
= CANCELLED_STRING
;
646 main(int argc
, char **argv
)
653 TIENT ents
[TISTRMAX
+ 1], ents2
[TISTRMAX
+ 1];
655 cols
= 80; /* default */
656 term
= getenv("COLUMNS");
658 cols
= strtoul(term
, NULL
, 10);
659 else if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) == 0)
664 while ((ch
= getopt(argc
, argv
, "1A:B:acnquw:x")) != -1)
692 cols
= strtoul(optarg
, NULL
, 10);
700 "usage: %s [-1acnqux] [-A database] [-B database] "
701 "[-w cols] [term]\n",
707 if (optind
+ 1 < argc
)
711 term
= argv
[optind
++];
717 use_terms(t
, argc
- optind
, argv
+ optind
);
719 if ((optind
+ 1 != argc
&& nflag
== 0) || uflag
!= 0) {
721 printf("# Reconstructed from ");
722 if (_ti_database
== NULL
)
723 printf("internal database\n");
725 printf("%s%s\n", _ti_database
,
726 *_ti_database
== '/' ? ".cdb" : "");
728 printf("%s", t
->name
);
729 if (t
->_alias
!= NULL
&& *t
->_alias
!= '\0')
730 printf("|%s", t
->_alias
);
731 if (t
->desc
!= NULL
&& *t
->desc
!= '\0')
732 printf("|%s", t
->desc
);
735 n
= load_ents(ents
, t
, 'f');
737 n
= load_ents(ents
, t
, 'n');
739 n
= load_ents(ents
, t
, 's');
745 for (; optind
< argc
; optind
++) {
746 n2
= 5 + strlen(argv
[optind
]);
754 n
+= printf("use=%s,", argv
[optind
]);
762 unsetenv("TERMINFO");
765 t2
= load_term(argv
[optind
++]);
766 printf("comparing %s to %s.\n", t
->name
, t2
->name
);
768 printf(" comparing booleans.\n");
770 n
= load_ents(ents
, t
, 'f');
771 n2
= load_ents(ents2
, t2
, 'f');
772 compare_ents(ents
, n
, ents2
, n2
);
774 show_missing(t
, t2
, 'f');
776 printf(" comparing numbers.\n");
778 n
= load_ents(ents
, t
, 'n');
779 n2
= load_ents(ents2
, t2
, 'n');
780 compare_ents(ents
, n
, ents2
, n2
);
782 show_missing(t
, t2
, 'n');
784 printf(" comparing strings.\n");
786 n
= load_ents(ents
, t
, 's');
787 n2
= load_ents(ents2
, t2
, 's');
788 compare_ents(ents
, n
, ents2
, n2
);
790 show_missing(t
, t2
, 's');