]> Zhao Yanbai Git Server - minix.git/commitdiff
Import infocmp from NetBSD 66/266/1
authorAntoine Leca <Antoine.Leca.1@gmail.com>
Fri, 25 Jan 2013 11:19:17 +0000 (11:19 +0000)
committerLionel Sambuc <lionel@minix3.org>
Fri, 25 Jan 2013 19:11:01 +0000 (20:11 +0100)
Convert the content of the terminfo database into human-readable
form, the same as used by the tic(1) command already present.

distrib/sets/lists/minix/mi
releasetools/nbsd_ports
usr.bin/Makefile
usr.bin/infocmp/Makefile [new file with mode: 0644]
usr.bin/infocmp/infocmp.1 [new file with mode: 0644]
usr.bin/infocmp/infocmp.c [new file with mode: 0644]

index 3ed14aff4849c0a2d4d5790db69931d84a3a5657..ea2d810683768f949903f04acad6bee1a3e351bf 100644 (file)
 ./usr/bin/ifconfig                     minix-sys
 ./usr/bin/ifdef                                minix-sys
 ./usr/bin/indent                       minix-sys
+./usr/bin/infocmp                      minix-sys
 ./usr/bin/in.fingerd                   minix-sys
 ./usr/bin/in.rshd                      minix-sys
 ./usr/bin/install                      minix-sys
 ./usr/man/man1/if.1                    minix-sys
 ./usr/man/man1/ifdef.1                 minix-sys
 ./usr/man/man1/indent.1                        minix-sys
+./usr/man/man1/infocmp.1               minix-sys
 ./usr/man/man1/install.1               minix-sys
 ./usr/man/man1/isodir.1                        minix-sys
 ./usr/man/man1/isoinfo.1               minix-sys
index aacdb457c017a35f28d2ab83438273f8f2a7a7ba..167cce6afd6a94b6d1370611b5357bc8e8a0716a 100644 (file)
 2012/10/17 12:00:00,usr.bin/genassym
 2012/10/17 12:00:00,usr.bin/gzip
 2012/10/17 12:00:00,usr.bin/indent
+2012/10/17 12:00:00,usr.bin/infocmp
 2012/10/17 12:00:00,usr.bin/join
 2011/01/17 18:11:10,usr.bin/ldd
 2012/02/10 16:16:12,usr.bin/login
index d720f82185a796b9b0536171566bca33f9029f31..8d8899612494a2aa95baf04550553858159b2b68 100644 (file)
@@ -12,7 +12,7 @@ SUBDIR= \
        \
        \
        genassym \
-       indent join \
+       indent infocmp join \
        ldd \
        login lorder m4 \
        make man \
diff --git a/usr.bin/infocmp/Makefile b/usr.bin/infocmp/Makefile
new file mode 100644 (file)
index 0000000..f2b5a4c
--- /dev/null
@@ -0,0 +1,10 @@
+#      $NetBSD: Makefile,v 1.1 2010/02/03 15:16:33 roy Exp $
+
+PROG=          infocmp
+WARNS=         4
+
+CPPFLAGS+=     -I${.CURDIR}/../../lib/libterminfo
+LDADD+=                -lterminfo
+DPADD+=                ${LIBTERMINFO}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/infocmp/infocmp.1 b/usr.bin/infocmp/infocmp.1
new file mode 100644 (file)
index 0000000..10132cf
--- /dev/null
@@ -0,0 +1,131 @@
+.\"    $NetBSD: infocmp.1,v 1.5 2011/01/14 14:21:36 wiz Exp $
+.\"
+.\" Copyright (c) 2009 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Roy Marples.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd February 5, 2010
+.Dt INFOCMP 1
+.Os
+.Sh NAME
+.Nm infocmp
+.Nd compare or print compiled terminfo descriptions
+.Sh SYNOPSIS
+.Nm infocmp
+.Op Fl 1acnqux
+.Op Fl A Ar database
+.Op Fl B Ar database
+.Op Fl w Ar cols
+.Op Ar term ...
+.Sh DESCRIPTION
+The
+.Nm
+reconstructs the first available
+.Xr terminfo 5
+definition found for
+.Ar term
+and prints the result in a
+.Xr terminfo 5
+format.
+Capability types are grouped together and new types start new lines,
+first flags, then numbers, then strings.
+Capabilities are sorted by their name.
+If a second
+.Ar term
+is given then the capabilities are compared against each other.
+.Pp
+The following options are available:
+.Bl -tag -width Ev
+.It Fl 1
+Print one capability per line.
+.It Fl A Ar database
+Use this database to load the first terminal definition.
+.It Fl B Ar database
+Use this database to load subsequent terminal definitions.
+.It Fl a
+Include commented out capabilities.
+This only works if the database was compiled with the
+.Fl a
+flag passed to
+.Xr tic 1 .
+This also sets the
+.Fl x
+flag as
+.Nm
+retains commented out capabilities as non standard.
+.It Fl c
+Print capabilities common to each definition.
+.It Fl n
+Print capabilities that do not exist in either definition.
+.It Fl q
+Make the comparison listing shorter by omitting subheadings and using
+- for absent capabilities, @ for canceled capabilities rather than
+.Dv NULL .
+.It Fl u
+Build a new terminal description for the first terminal description,
+using subsequent terminal descriptions.
+This also sets the
+.Fl a
+flag.
+.It Fl w Ar cols
+Limit the width to
+.Ar cols .
+.It Fl x
+Include non-standard capabilities.
+More
+.Fl x
+only handles non-standard capabilities.
+This only works if the database was compiled with the
+.Fl x
+flag passed to
+.Xr tic 1 .
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width Ev
+.It Ev COLUMNS
+Override columns returned by the output terminal.
+.Fl w
+.Ar cols
+supersedes this.
+.It Ev TERM
+.Nm
+uses the contents of the
+.Ev TERM
+environment variable if no terminal name is given on the command line.
+.El
+.Sh EXIT STATUS
+.Ex -std infocmp
+.Sh SEE ALSO
+.Xr tic 1 ,
+.Xr terminfo 5
+.Sh STANDARDS
+The
+.Nm
+utility outputs information that conforms to the
+.St -xcurses4.2
+standard.
+.Sh AUTHORS
+.An Roy Marples Aq roy@NetBSD.org
diff --git a/usr.bin/infocmp/infocmp.c b/usr.bin/infocmp/infocmp.c
new file mode 100644 (file)
index 0000000..581708e
--- /dev/null
@@ -0,0 +1,795 @@
+/* $NetBSD: infocmp.c,v 1.7 2010/02/22 23:05:39 roy Exp $ */
+
+/*
+ * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: infocmp.c,v 1.7 2010/02/22 23:05:39 roy Exp $");
+
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <term_private.h>
+#include <term.h>
+#include <unistd.h>
+
+/* Specifically needed on MINIX, as struct winsize in not defined elsewhere */
+#include <minix/termios.h>
+
+#define SW 8
+
+typedef struct tient {
+       char type;
+       const char *id;
+       signed char flag;
+       short num;
+       const char *str;
+} TIENT;
+
+static size_t cols;
+static int aflag, cflag, nflag, qflag, xflag;
+
+static size_t
+outstr(FILE *f, const char *str)
+{
+       unsigned char ch;
+       size_t r, l;
+
+       r = 0;
+       l = strlen(str);
+       while ((ch = (unsigned char)(*str++)) != '\0') {
+               switch (ch) {
+               case 128:
+                       ch = '0';
+                       break;
+               case '\033':
+                       ch = 'E';
+                       break;
+               case '\014':
+                       ch = 'f';
+                       break;
+               case '^': /* FALLTHROUGH */
+               case ',': /* escape these */
+                       break;
+               case ' ':
+                       ch = 's';
+                       break;
+               default:
+                       if (ch == '\177') {
+                               if (f != NULL)
+                                       fputc('^', f);
+                               ch = '?';
+                               r++;
+                       } else if (iscntrl(ch) &&
+                           ch < 128 &&
+                           ch != '\\' &&
+                           (l < 4 || isdigit((unsigned char)*str)))
+                       {
+                               if (f != NULL)
+                                       fputc('^', f);
+                               ch += '@';
+                               r++;
+                       } else if (!isprint(ch)) {
+                               if (f != NULL)
+                                       fprintf(f, "\\%03o", ch);
+                               r += 4;
+                               continue;
+                       }
+                       goto prnt;
+               }
+               
+               if (f != NULL)
+                       fputc('\\', f);
+               r++;
+prnt:
+               if (f != NULL)
+                       fputc(ch, f);
+               r++;
+       }
+       return r;
+}
+
+static int
+ent_compare(const void *a, const void *b)
+{
+       const TIENT *ta, *tb;
+
+       ta = (const TIENT *)a;
+       tb = (const TIENT *)b;
+       return strcmp(ta->id, tb->id);
+}
+
+static void
+setdb(char *db)
+{
+       size_t len;
+
+       len = strlen(db);
+       if (len > 3 &&
+           db[len - 3] == '.' &&
+           db[len - 2] == 'd' &&
+           db[len - 1] == 'b')
+               db[len - 3] = '\0';
+       setenv("TERMINFO", db, 1);
+}
+
+static void
+print_ent(const TIENT *ents, size_t nents)
+{
+       size_t col, i, l;
+       char nbuf[64];  
+
+       if (nents == 0)
+               return;
+       
+       col = SW;
+       printf("\t");
+       for (i = 0; i < nents; i++) {
+               if (*ents[i].id == '.' && aflag == 0)
+                       continue;
+               switch (ents[i].type) {
+               case 'f':
+                       if (ents[i].flag == ABSENT_BOOLEAN)
+                               continue;
+                       l = strlen(ents[i].id) + 2;
+                       if (ents[i].flag == CANCELLED_BOOLEAN)
+                               l++;
+                       break;
+               case 'n':
+                       if (ents[i].num == ABSENT_NUMERIC)
+                               continue;
+                       if (VALID_NUMERIC(ents[i].num))
+                               l = snprintf(nbuf, sizeof(nbuf), "%s#%d,",
+                                   ents[i].id, ents[i].num);
+                       else
+                               l = snprintf(nbuf, sizeof(nbuf), "%s@,",
+                                   ents[i].id);
+                       break;
+               case 's':
+                       if (ents[i].str == ABSENT_STRING)
+                               continue;
+                       if (VALID_STRING(ents[i].str))
+                               l = strlen(ents[i].id) +
+                                   outstr(NULL, ents[i].str) + 7;
+                       else
+                               l = strlen(ents[i].id) + 3;
+                       break;
+               default:
+                       errx(1, "invalid type");
+               }
+               if (col != SW) {
+                       if (col + l > cols) {
+                               printf("\n\t");
+                               col = SW;
+                       } else
+                               col += printf(" ");
+               }
+               switch (ents[i].type) {
+               case 'f':
+                       col += printf("%s", ents[i].id);
+                       if (ents[i].flag == ABSENT_BOOLEAN ||
+                           ents[i].flag == CANCELLED_BOOLEAN)
+                               col += printf("@");
+                       col += printf(",");
+                       break;
+               case 'n':
+                       col += printf("%s", nbuf);
+                       break;
+               case 's':
+                       col += printf("%s", ents[i].id);
+                       if (VALID_STRING(ents[i].str)) {
+                               col += printf("=");
+                               col += outstr(stdout, ents[i].str);
+                       } else
+                               col += printf("@");
+                       col += printf(",");
+                       break;
+               }
+       }
+       printf("\n");
+}
+
+static size_t
+load_ents(TIENT *ents, TERMINAL *t, char type)
+{
+       size_t i, n, max;
+       TERMUSERDEF *ud;
+
+       switch (type) {
+       case 'f':
+               max = TIFLAGMAX;
+               break;
+       case 'n':
+               max = TINUMMAX;
+               break;
+       default:
+               max = TISTRMAX;
+       }
+       
+       n = 0;
+       for (i = 0; i <= max; i++) {
+               switch (type) {
+               case 'f':
+                       if (t->flags[i] == 1 ||
+                           (aflag && t->flags[i] == CANCELLED_BOOLEAN))
+                       {
+                               ents[n].id = _ti_flagid(i);
+                               ents[n].type = 'f';
+                               ents[n++].flag = t->flags[i];
+                       }
+                       break;
+               case 'n':
+                       if (VALID_NUMERIC(t->nums[i]) ||
+                           (aflag && t->nums[i] == CANCELLED_NUMERIC))
+                       {
+                               ents[n].id = _ti_numid(i);
+                               ents[n].type = 'n';
+                               ents[n++].num = t->nums[i];
+                       }
+                       break;
+               default:
+                       if (VALID_STRING(t->strs[i]) ||
+                           (aflag && t->strs[i] == CANCELLED_STRING))
+                       {
+                               ents[n].id = _ti_strid(i);
+                               ents[n].type = 's';
+                               ents[n++].str = t->strs[i];
+                       }
+                       break;
+               }
+       }
+       
+       if (xflag != 0 && t->_nuserdefs != 0) {
+               for (i = 0; i < t->_nuserdefs; i++) {
+                       ud = &t->_userdefs[i];
+                       if (ud->type == type) {
+                               switch (type) {
+                               case 'f':
+                                       if (!aflag &&
+                                           !VALID_BOOLEAN(ud->flag))
+                                               continue;
+                                       break;
+                               case 'n':
+                                       if (!aflag &&
+                                           !VALID_NUMERIC(ud->num))
+                                               continue;
+                                       break;
+                               case 's':
+                                       if (!aflag &&
+                                           !VALID_STRING(ud->str))
+                                               continue;
+                                       break;
+                               }
+                               ents[n].id = ud->id;
+                               ents[n].type = ud->type;
+                               ents[n].flag = ud->flag;
+                               ents[n].num = ud->num;
+                               ents[n++].str = ud->str;
+                       }
+               }
+       }
+       
+       qsort(ents, n, sizeof(TIENT), ent_compare);
+       return n;
+}
+
+static void
+cprint_ent(TIENT *ent)
+{
+
+       if (ent == NULL) {
+               if (qflag == 0)
+                       printf("NULL");
+               else
+                       printf("-");
+       }
+       
+       switch (ent->type) {
+       case 'f':
+               if (VALID_BOOLEAN(ent->flag))
+                       printf(ent->flag == 1 ? "T" : "F");
+               else if (qflag == 0)
+                       printf("F");
+               else if (ent->flag == CANCELLED_BOOLEAN)
+                       printf("@");
+               else
+                       printf("-");
+               break;
+       case 'n':
+               if (VALID_NUMERIC(ent->num))
+                       printf("%d", ent->num);
+               else if (qflag == 0)
+                       printf("NULL");
+               else if (ent->num == CANCELLED_NUMERIC)
+                       printf("@");
+               else
+                       printf("-");
+               break;
+       case 's':
+               if (VALID_STRING(ent->str)) {
+                       printf("'");
+                       outstr(stdout, ent->str);
+                       printf("'");
+               } else if (qflag == 0)
+                       printf("NULL");
+               else if (ent->str == CANCELLED_STRING)
+                       printf("@");
+               else
+                       printf("-");
+               break;
+       }
+}
+
+static void
+compare_ents(TIENT *ents1, size_t n1, TIENT *ents2, size_t n2)
+{
+       size_t i1, i2;
+       TIENT *e1, *e2, ee;
+       int c;
+       
+       i1 = i2 = 0;
+       ee.type = 'f';
+       ee.flag = ABSENT_BOOLEAN;
+       ee.num = ABSENT_NUMERIC;
+       ee.str = ABSENT_STRING;
+       while (i1 != n1 || i2 != n2) {
+               if (i1 == n1)
+                       c = 1;
+               else if (i2 == n2)
+                       c = -1;
+               else
+                       c = strcmp(ents1[i1].id, ents2[i2].id);
+               if (c == 0) {
+                       e1 = &ents1[i1++];
+                       e2 = &ents2[i2++];
+               } else if (c < 0) {
+                       e1 = &ents1[i1++];
+                       e2 = &ee;
+                       ee.id = e1->id;
+                       ee.type = e1->type;
+               } else {
+                       e1 = &ee;
+                       e2 = &ents2[i2++];
+                       ee.id = e2->id;
+                       ee.type = e2->type;
+               }
+               switch (e1->type) {
+               case 'f':
+                       if (cflag != 0) {
+                               if (e1->flag == e2->flag)
+                                       printf("\t%s\n", ents1[i1].id);
+                               continue;
+                       }
+                       if (e1->flag == e2->flag)
+                               continue;
+                       break;
+               case 'n':
+                       if (cflag != 0) {
+                               if (e1->num == e2->num)
+                                       printf("\t%s#%d\n",
+                                           ents1[i1].id, ents1[i1].num);
+                               continue;
+                       }
+                       if (e1->num == e2->num)
+                               continue;
+                       break;
+               case 's':
+                       if (cflag != 0) {
+                               if (VALID_STRING(e1->str) &&
+                                   VALID_STRING(e2->str) &&
+                                   strcmp(e1->str, e2->str) == 0) {
+                                       printf("\t%s=", ents1[i1].id);
+                                       outstr(stdout, ents1[i1].str);
+                                       printf("\n");
+                               }
+                               continue;
+                       }
+                       if (VALID_STRING(e1->str) &&
+                           VALID_STRING(e2->str) &&
+                           strcmp(e1->str, e2->str) == 0)
+                               continue;
+                       break;
+               }
+               printf("\t%s: ", e1->id);
+               cprint_ent(e1);
+               if (e1->type == 'f')
+                       printf(":");
+               else
+                       printf(", ");
+               cprint_ent(e2);
+               printf(".\n");
+       }
+}
+
+static TERMINAL *
+load_term(const char *name)
+{
+       TERMINAL *t;
+
+       t = calloc(1, sizeof(*t));
+       if (t == NULL)
+               err(1, "calloc");
+       if (name == NULL)
+               name = getenv("TERM");
+       if (name == NULL)
+               name = "dumb";
+       if (_ti_getterm(t, name, 1) == 1)
+               return t;
+
+       if (_ti_database == NULL)
+               errx(1, "no terminal definition found in internal database");
+       else
+               errx(1, "no terminal definition found in %s.db", _ti_database);
+}
+
+static void
+show_missing(TERMINAL *t1, TERMINAL *t2, char type)
+{
+       ssize_t i, max;
+       const char *id;
+       
+       switch (type) {
+       case 'f':
+               max = TIFLAGMAX;
+               break;
+       case 'n':
+               max = TINUMMAX;
+               break;
+       default:
+               max = TISTRMAX;
+       }
+
+       for (i = 0; i <= max; i++) {
+               switch (type) {
+               case 'f':
+                       if (t1->flags[i] != ABSENT_BOOLEAN ||
+                           t2->flags[i] != ABSENT_BOOLEAN)
+                               continue;
+                       id = _ti_flagid(i);
+                       break;
+               case 'n':
+                       if (t1->nums[i] != ABSENT_NUMERIC ||
+                           t2->nums[i] != ABSENT_NUMERIC)
+                               continue;
+                       id = _ti_numid(i);
+                       break;
+               default:
+                       if (t1->strs[i] != ABSENT_STRING ||
+                           t2->strs[i] != ABSENT_STRING)
+                               continue;
+                       id = _ti_strid(i);
+                       break;
+               }
+               printf("\t!%s.\n", id);
+       }
+}
+
+static TERMUSERDEF *
+find_userdef(TERMINAL *term, const char *id)
+{
+       size_t i;
+
+       for (i = 0; i < term->_nuserdefs; i++)
+               if (strcmp(term->_userdefs[i].id, id) == 0)
+                       return &term->_userdefs[i];
+       return NULL;
+}
+
+static void
+use_terms(TERMINAL *term, size_t nuse, char **uterms)
+{
+       TERMINAL **terms;
+       TERMUSERDEF *ud, *tud;
+       size_t i, j, agree, absent, data;
+
+       terms = malloc(sizeof(**terms) * nuse);
+       if (terms == NULL)
+               err(1, "malloc");
+       for (i = 0; i < nuse; i++) {
+               if (strcmp(term->name, *uterms) == 0)
+                       errx(1, "cannot use same terminal");
+               for (j = 0; j < i; j++)
+                       if (strcmp(terms[j]->name, *uterms) == 0)
+                               errx(1, "cannot use same terminal");
+               terms[i] = load_term(*uterms++);
+       }
+       
+       for (i = 0; i < TIFLAGMAX + 1; i++) {
+               agree = absent = data = 0;
+               for (j = 0; j < nuse; j++) {
+                       if (terms[j]->flags[i] == ABSENT_BOOLEAN ||
+                           terms[j]->flags[i] == CANCELLED_BOOLEAN)
+                               absent++;
+                       else {
+                               data++;
+                               if (term->flags[i] == terms[j]->flags[i])
+                                       agree++;
+                       }
+               }
+               if (data == 0)
+                       continue;
+               if (agree > 0 && agree + absent == nuse)
+                       term->flags[i] = ABSENT_BOOLEAN;
+               else if (term->flags[i] == ABSENT_BOOLEAN)
+                       term->flags[i] = CANCELLED_BOOLEAN;
+       }
+       
+       for (i = 0; i < TINUMMAX + 1; i++) {
+               agree = absent = data = 0;
+               for (j = 0; j < nuse; j++) {
+                       if (terms[j]->nums[i] == ABSENT_NUMERIC ||
+                           terms[j]->nums[i] == CANCELLED_NUMERIC)
+                               absent++;
+                       else {
+                               data++;
+                               if (term->nums[i] == terms[j]->nums[i])
+                                       agree++;
+                       }
+               }
+               if (data == 0)
+                       continue;
+               if (agree > 0 && agree + absent == nuse)
+                       term->nums[i] = ABSENT_NUMERIC;
+               else if (term->nums[i] == ABSENT_NUMERIC)
+                       term->nums[i] = CANCELLED_NUMERIC;
+       }
+       
+       for (i = 0; i < TISTRMAX + 1; i++) {
+               agree = absent = data = 0;
+               for (j = 0; j < nuse; j++) {
+                       if (terms[j]->strs[i] == ABSENT_STRING ||
+                           terms[j]->strs[i] == CANCELLED_STRING)
+                               absent++;
+                       else {
+                               data++;
+                               if (VALID_STRING(term->strs[i]) &&
+                                   strcmp(term->strs[i],
+                                       terms[j]->strs[i]) == 0)
+                                       agree++;
+                       }
+               }
+               if (data == 0)
+                       continue;
+               if (agree > 0 && agree + absent == nuse)
+                       term->strs[i] = ABSENT_STRING;
+               else if (term->strs[i] == ABSENT_STRING)
+                       term->strs[i] = CANCELLED_STRING;
+       }
+
+       /* User defined caps are more tricky.
+          First we set any to absent that agree. */
+       for (i = 0; i < term->_nuserdefs; i++) {
+               agree = absent = data = 0;
+               ud = &term->_userdefs[i];
+               for (j = 0; j < nuse; j++) {
+                       tud = find_userdef(terms[j], ud->id);
+                       if (tud == NULL)
+                               absent++;
+                       else {
+                               data++;
+                               switch (ud->type) {
+                               case 'f':
+                                       if (tud->type == 'f' &&
+                                           tud->flag == ud->flag)
+                                               agree++;
+                                       break;
+                               case 'n':
+                                       if (tud->type == 'n' &&
+                                           tud->num == ud->num)
+                                               agree++;
+                                       break;
+                               case 's':
+                                       if (tud->type == 's' &&
+                                           VALID_STRING(tud->str) &&
+                                           VALID_STRING(ud->str) &&
+                                           strcmp(ud->str, tud->str) == 0)
+                                               agree++;
+                                       break;
+                               }
+                       }
+               }
+               if (data == 0)
+                       continue;
+               if (agree > 0 && agree + absent == nuse) {
+                       ud->flag = ABSENT_BOOLEAN;
+                       ud->num = ABSENT_NUMERIC;
+                       ud->str = ABSENT_STRING;
+               }
+       }
+
+       /* Now add any that we don't have as cancelled */
+       for (i = 0; i < nuse; i++) {
+               for (j = 0; j < terms[i]->_nuserdefs; j++) {
+                       ud = find_userdef(term, terms[i]->_userdefs[j].id);
+                       if (ud != NULL)
+                               continue; /* We have handled this */
+                       term->_userdefs = realloc(term->_userdefs,
+                           sizeof(*term->_userdefs) * (term->_nuserdefs + 1));
+                       if (term->_userdefs == NULL)
+                               err(1, "malloc");
+                       tud = &term->_userdefs[term->_nuserdefs++];
+                       tud->id = terms[i]->_userdefs[j].id;
+                       tud->type = terms[i]->_userdefs[j].flag;
+                       tud->flag = CANCELLED_BOOLEAN;
+                       tud->num = CANCELLED_NUMERIC;
+                       tud->str = CANCELLED_STRING;
+               }
+       }
+}
+
+int
+main(int argc, char **argv)
+{
+       char *term, *Barg;
+       int ch, uflag;
+       TERMINAL *t, *t2;
+       size_t n, n2;
+       struct winsize ws;
+       TIENT ents[TISTRMAX + 1], ents2[TISTRMAX + 1];
+
+       cols = 80; /* default */
+       term = getenv("COLUMNS");
+       if (term != NULL)
+               cols = strtoul(term, NULL, 10);
+       else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0)
+               cols = ws.ws_col;
+
+       uflag = xflag = 0;
+       Barg = NULL;
+       while ((ch = getopt(argc, argv, "1A:B:acnquw:x")) != -1)
+               switch (ch) {
+               case '1':
+                       cols = 1;
+                       break;
+               case 'A':
+                       setdb(optarg);
+                       break;
+               case 'B':
+                       Barg = optarg;
+                       break;
+               case 'a':
+                       aflag = 1;
+                       break;
+               case 'c':
+                       cflag = 1;
+                       break;
+               case 'n':
+                       nflag = 1;
+                       break;
+               case 'q':
+                       qflag = 1;
+                       break;
+               case 'u':
+                       uflag = 1;
+                       aflag = 1;
+                       break;
+               case 'w':
+                       cols = strtoul(optarg, NULL, 10);
+                       break;
+               case 'x':
+                       xflag = 1;
+                       break;
+               case '?':
+               default:
+                       fprintf(stderr,
+                           "usage: %s [-1acnqux] [-A database] [-B database] "
+                           "[-w cols] [term]\n",
+                           getprogname());
+                       return EXIT_FAILURE;
+               }
+       cols--;
+
+       if (optind + 1 < argc)
+               aflag = 1;
+
+       if (optind < argc)
+               term = argv[optind++];
+       else
+               term = NULL;
+       t = load_term(term);
+
+       if (uflag != 0)
+               use_terms(t, argc - optind, argv + optind);
+
+       if ((optind + 1 != argc && nflag == 0) || uflag != 0) {
+               if (uflag == 0) {
+                       printf("# Reconstructed from ");
+                       if (_ti_database == NULL)
+                               printf("internal database\n");
+                       else
+                               printf("%s%s\n", _ti_database,
+                                   *_ti_database == '/' ? ".db" : "");
+               }
+               printf("%s", t->name);
+               if (t->_alias != NULL && *t->_alias != '\0')
+                       printf("|%s", t->_alias);
+               if (t->desc != NULL && *t->desc != '\0')
+                       printf("|%s", t->desc);
+               printf(",\n");
+
+               n = load_ents(ents, t, 'f');
+               print_ent(ents, n);
+               n = load_ents(ents, t, 'n');
+               print_ent(ents, n);
+               n = load_ents(ents, t, 's');
+               print_ent(ents, n);
+
+               if (uflag != 0) {
+                       printf("\t");
+                       n = SW;
+                       for (; optind < argc; optind++) {
+                               n2 = 5 + strlen(argv[optind]);
+                               if (n != SW) {
+                                       if (n + n2 > cols) {
+                                               printf("\n\t");
+                                               n = SW;
+                                       } else
+                                               n += printf(" ");
+                               }
+                               n += printf("use=%s,", argv[optind]);
+                       }
+                       printf("\n");
+               }
+               return EXIT_SUCCESS;
+       }
+
+       if (Barg == NULL)
+               unsetenv("TERMINFO");
+       else
+               setdb(Barg);
+       t2 = load_term(argv[optind++]);
+       printf("comparing %s to %s.\n", t->name, t2->name);
+       if (qflag == 0)
+               printf("    comparing booleans.\n");
+       if (nflag == 0) {
+               n = load_ents(ents, t, 'f');
+               n2 = load_ents(ents2, t2, 'f');
+               compare_ents(ents, n, ents2, n2);
+       } else
+               show_missing(t, t2, 'f');
+       if (qflag == 0)
+               printf("    comparing numbers.\n");
+       if (nflag == 0) {
+               n = load_ents(ents, t, 'n');
+               n2 = load_ents(ents2, t2, 'n');
+               compare_ents(ents, n, ents2, n2);
+       } else
+               show_missing(t, t2, 'n');
+       if (qflag == 0)
+               printf("    comparing strings.\n");
+       if (nflag == 0) {
+               n = load_ents(ents, t, 's');
+               n2 = load_ents(ents2, t2, 's');
+               compare_ents(ents, n, ents2, n2);
+       } else
+               show_missing(t, t2, 's');
+       return EXIT_SUCCESS;
+}