1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*      Copyright (c) 1988 AT&T     */
  23 /*        All Rights Reserved   */
  24 
  25 
  26 /*
  27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  28  * Use is subject to license terms.
  29  */
  30 
  31 #pragma ident   "%Z%%M% %I%     %E% SMI"        /* SVr4.0 1.13  */
  32 
  33 /*
  34     NAME
  35         infocmp - compare terminfo descriptions, or dump a terminfo
  36         description
  37 
  38     AUTHOR
  39         Tony Hansen, February 23, 1984.
  40 */
  41 
  42 #include "curses.h"
  43 #include "term.h"
  44 #include "print.h"
  45 #include <fcntl.h>
  46 #include <stdlib.h>
  47 
  48 /* externs from libcurses */
  49 extern char *boolnames[];
  50 extern char *boolcodes[];
  51 extern char *boolfnames[];
  52 extern char *numnames[];
  53 extern char *numcodes[];
  54 extern char *numfnames[];
  55 extern char *strnames[];
  56 extern char *strcodes[];
  57 extern char *strfnames[];
  58 extern char ttytype[];
  59 extern int tgetflag();
  60 extern int tgetnum();
  61 extern char *tgetstr();
  62 
  63 /* externs from libc */
  64 extern void exit();
  65 extern void qsort();
  66 extern char *getenv();
  67 extern int getopt();
  68 extern int optind;
  69 extern char *optarg;
  70 extern char *strncpy(), *strcpy();
  71 extern int strcmp(), strlen();
  72 
  73 /* data structures for this program */
  74 
  75 struct boolstruct {
  76     char *infoname;                     /* the terminfo capability name */
  77     char *capname;                      /* the termcap capability name */
  78     char *fullname;                     /* the long C variable name */
  79     char *secondname;                   /* the use= terminal w/ this value */
  80     char val;                           /* the value */
  81     char secondval;                     /* the value in the use= terminal */
  82     char changed;                       /* a use= terminal changed the value */
  83     char seenagain;                     /* a use= terminal had this entry */
  84     };
  85 
  86 struct numstruct {
  87     char *infoname;                     /* ditto from above */
  88     char *capname;
  89     char *fullname;
  90     char *secondname;
  91     short val;
  92     short secondval;
  93     char changed;
  94     char seenagain;
  95     };
  96 
  97 struct strstruct {
  98     char *infoname;                     /* ditto from above */
  99     char *capname;
 100     char *fullname;
 101     char *secondname;
 102     char *val;
 103     char *secondval;
 104     char changed;
 105     char seenagain;
 106     };
 107 
 108 /* globals for this file */
 109 char *progname;                 /* argv[0], the name of the program */
 110 static struct boolstruct *ibool; /* array of char information */
 111 static struct numstruct *num;   /* array of number information */
 112 static struct strstruct *str;   /* array of string information */
 113 static char *used;              /* usage statistics */
 114 static int numbools;            /* how many booleans there are */
 115 static int numnums;             /* how many numbers there are */
 116 static int numstrs;             /* how many strings there are */
 117 #define TTYLEN 255
 118 static char *firstterm;         /* the name of the first terminal */
 119 static char *savettytype;       /* the synonyms of the first terminal */
 120 static char _savettytype[TTYLEN+1]; /* the place to save those names */
 121 static int devnull;             /* open("/dev/null") for setupterm */
 122 #define trace stderr            /* send trace messages to stderr */
 123 
 124 /* options */
 125 static int verbose = 0;         /* debugging printing level */
 126 static int diff = 0;            /* produce diff listing, the default */
 127 static int common = 0;          /* produce common listing */
 128 static int neither = 0;         /* list caps in neither entry */
 129 static int use = 0;             /* produce use= comparison listing */
 130 static enum printtypes printing /* doing any of above printing at all */
 131         = pr_none;
 132 enum { none, by_database, by_terminfo, by_longnames, by_cap }
 133     sortorder = none;           /* sort the fields for printing */
 134 static char *term1info, *term2info;     /* $TERMINFO settings */
 135 static int Aflag = 0, Bflag = 0;        /* $TERMINFO was set with -A/-B */
 136 
 137 #define EQUAL(s1, s2)   (((s1 == NULL) && (s2 == NULL)) || \
 138                         ((s1 != NULL) && (s2 != NULL) && \
 139                         (strcmp(s1, s2) == 0)))
 140 
 141 static void sortnames();
 142 int numcompare(const void *, const void *);
 143 int boolcompare(const void *, const void *);
 144 int strcompare(const void *, const void *);
 145 static void check_nth_terminal(char *, int);
 146 
 147 void
 148 badmalloc()
 149 {
 150         (void) fprintf(stderr, "%s: malloc is out of space!\n", progname);
 151         exit(-1);
 152 }
 153 
 154 /*
 155     Allocate and initialize the global data structures and variables.
 156 */
 157 void
 158 allocvariables(int argc, int firstoptind)
 159 {
 160         register int i, nullseen;
 161 
 162         /* find out how many names we are dealing with */
 163         for (numbools = 0; boolnames[numbools]; numbools++)
 164                 ;
 165         for (numnums = 0; numnames[numnums]; numnums++)
 166                 ;
 167         for (numstrs = 0; strnames[numstrs]; numstrs++)
 168                 ;
 169 
 170         if (verbose) {
 171                 (void) fprintf(trace, "There are %d boolean capabilities.\n",
 172                     numbools);
 173                 (void) fprintf(trace, "There are %d numeric capabilities.\n",
 174                     numnums);
 175                 (void) fprintf(trace, "There are %d string capabilities.\n",
 176                     numstrs);
 177         }
 178 
 179         /* Allocate storage for the names and their values */
 180         ibool = (struct boolstruct  *) malloc((unsigned) numbools *
 181             sizeof (struct boolstruct));
 182         num = (struct numstruct *) malloc((unsigned) numnums *
 183             sizeof (struct numstruct));
 184         str = (struct strstruct *) malloc((unsigned) numstrs *
 185             sizeof (struct strstruct));
 186 
 187         /* Allocate array to keep track of which names have been used. */
 188         if (use)
 189                 used = (char *) malloc((unsigned) (argc - firstoptind) *
 190                     sizeof (char));
 191 
 192         if ((ibool == NULL) || (num == NULL) || (str == NULL) ||
 193             (use && (used == NULL)))
 194                 badmalloc();
 195 
 196         /* Fill in the names and initialize the structures. */
 197         nullseen = FALSE;
 198         for (i = 0; i < numbools; i++) {
 199                 ibool[i].infoname = boolnames[i];
 200                 ibool[i].capname = boolcodes[i];
 201                 /* This is necessary until fnames.c is */
 202                 /* incorporated into standard curses. */
 203                 if (nullseen || (boolfnames[i] == NULL)) {
 204                         ibool[i].fullname = "unknown_boolean";
 205                         nullseen = TRUE;
 206                 } else
 207                         ibool[i].fullname = boolfnames[i];
 208                 ibool[i].changed = FALSE;
 209                 ibool[i].seenagain = FALSE;
 210         }
 211         nullseen = 0;
 212         for (i = 0; i < numnums; i++) {
 213                 num[i].infoname = numnames[i];
 214                 num[i].capname = numcodes[i];
 215                 if (nullseen || (numfnames[i] == NULL)) {
 216                         ibool[i].fullname = "unknown_number";
 217                         nullseen = TRUE;
 218                 } else
 219                         num[i].fullname = numfnames[i];
 220                 num[i].changed = FALSE;
 221                 num[i].seenagain = FALSE;
 222         }
 223         nullseen = 0;
 224         for (i = 0; i < numstrs; i++) {
 225                 str[i].infoname = strnames[i];
 226                 str[i].capname = strcodes[i];
 227                 if (nullseen || (strfnames[i] == NULL)) {
 228                         str[i].fullname = "unknown_string";
 229                         nullseen = TRUE;
 230                 } else
 231                         str[i].fullname = strfnames[i];
 232                 str[i].changed = FALSE;
 233                 str[i].seenagain = FALSE;
 234         }
 235 }
 236 
 237 /*
 238         Routines to be passed to qsort(3) for comparison of the structures.
 239 */
 240 int
 241 boolcompare(const void *x, const void *y)
 242 {
 243         struct boolstruct *a;
 244         struct boolstruct *b;
 245 
 246         a = (struct boolstruct *)x;
 247         b = (struct boolstruct *)y;
 248 
 249         switch ((int) sortorder) {
 250                 case (int) by_terminfo:
 251                         return (strcmp(a->infoname, b->infoname));
 252                 case (int) by_cap:
 253                         return (strcmp(a->capname, b->capname));
 254                 case (int) by_longnames:
 255                         return (strcmp(a->fullname, b->fullname));
 256                 default:
 257                         return (0);
 258         }
 259 }
 260 
 261 int
 262 numcompare(const void *x, const void *y)
 263 {
 264         struct numstruct *a;
 265         struct numstruct *b;
 266 
 267         a = (struct numstruct *)x;
 268         b = (struct numstruct *)y;
 269         switch ((int) sortorder) {
 270                 case (int) by_terminfo:
 271                         return (strcmp(a->infoname, b->infoname));
 272                 case (int) by_cap:
 273                         return (strcmp(a->capname, b->capname));
 274                 case (int) by_longnames:
 275                         return (strcmp(a->fullname, b->fullname));
 276                 default:
 277                         return (0);
 278         }
 279 }
 280 
 281 int
 282 strcompare(const void *x, const void *y)
 283 {
 284         struct strstruct *a;
 285         struct strstruct *b;
 286 
 287         a = (struct strstruct *)x;
 288         b = (struct strstruct *)y;
 289 
 290         switch ((int) sortorder) {
 291                 case (int) by_terminfo:
 292                         return (strcmp(a->infoname, b->infoname));
 293                 case (int) by_cap:
 294                         return (strcmp(a->capname, b->capname));
 295                 case (int) by_longnames:
 296                         return (strcmp(a->fullname, b->fullname));
 297                 default:
 298                         return (0);
 299         }
 300 }
 301 
 302 /*
 303         Sort the entries by their terminfo name.
 304 */
 305 static void
 306 sortnames()
 307 {
 308         if (sortorder != by_database) {
 309                 qsort((char *) ibool, (unsigned) numbools,
 310                         sizeof (struct boolstruct), boolcompare);
 311                 qsort((char *) num, (unsigned) numnums,
 312                         sizeof (struct numstruct), numcompare);
 313                 qsort((char *) str, (unsigned) numstrs,
 314                         sizeof (struct strstruct), strcompare);
 315         }
 316         return;
 317 }
 318 
 319 /*
 320         Print out a string, or "NULL" if it's not defined.
 321 */
 322 void
 323 PR(FILE *stream, char *string)
 324 {
 325         if (string == NULL)
 326                 (void) fprintf(stream, "NULL");
 327         else
 328                 tpr(stream, string);
 329 }
 330 
 331 /*
 332         Output the 'ko' termcap string. This is a list of all of the input
 333         keys that input the same thing as the corresponding output strings.
 334 */
 335 int kncounter;
 336 char kobuffer[512];
 337 
 338 char
 339 *addko(char *output, char *input, char *koptr)
 340 {
 341         char *inptr, *outptr, padbuffer[512];
 342         inptr = tgetstr(input, (char **)0);
 343         if (inptr == NULL)
 344                 return (koptr);
 345         outptr = tgetstr(output, (char **)0);
 346         if (outptr == NULL)
 347                 return (koptr);
 348         outptr = rmpadding(outptr, padbuffer, (int *) 0);
 349         if (strcmp(inptr, outptr) == 0) {
 350                 *koptr++ = *output++;
 351                 *koptr++ = *output++;
 352                 *koptr++ = ',';
 353                 kncounter++;
 354         }
 355         return (koptr);
 356 }
 357 
 358 void
 359 setupknko()
 360 {
 361         char *koptr;
 362 
 363         kncounter = 0;
 364         koptr = kobuffer;
 365 
 366         koptr = addko("bs", "kb", koptr);       /* key_backspace */
 367         koptr = addko("bt", "kB", koptr);       /* key_btab */
 368         koptr = addko("cl", "kC", koptr);       /* key_clear */
 369         koptr = addko("le", "kl", koptr);       /* key_left */
 370         koptr = addko("do", "kd", koptr);       /* key_down */
 371         koptr = addko("nd", "kr", koptr);       /* key_right */
 372         koptr = addko("up", "ku", koptr);       /* key_up */
 373         koptr = addko("dc", "kD", koptr);       /* key_dc */
 374         koptr = addko("dl", "kL", koptr);       /* key_dl */
 375         koptr = addko("cd", "kS", koptr);       /* key_eos */
 376         koptr = addko("ce", "kE", koptr);       /* key_eol */
 377         koptr = addko("ho", "kh", koptr);       /* key_home */
 378         koptr = addko("st", "kT", koptr);       /* key_stab */
 379         koptr = addko("ic", "kI", koptr);       /* key_ic */
 380         koptr = addko("im", "kI", koptr);       /* key_ic */
 381         koptr = addko("al", "kA", koptr);       /* key_il */
 382         koptr = addko("sf", "kF", koptr);       /* key_sf */
 383         koptr = addko("ll", "kH", koptr);       /* key_ll */
 384         koptr = addko("sr", "kR", koptr);       /* key_sr */
 385         koptr = addko("ei", "kM", koptr);       /* key_eic */
 386         koptr = addko("ct", "ka", koptr);       /* key_catab */
 387 
 388         /* get rid of comma */
 389         if (koptr != kobuffer)
 390                 *(--koptr) = '\0';
 391 }
 392 
 393 void
 394 pr_kn()
 395 {
 396         if (kncounter > 0)
 397                 pr_number((char *)0, "kn", (char *)0, kncounter);
 398 }
 399 
 400 void
 401 pr_ko()
 402 {
 403         if (kncounter > 0)
 404                 pr_string((char *)0, "ko", (char *)0, kobuffer);
 405 }
 406 
 407 void
 408 pr_bcaps()
 409 {
 410         char *retptr;
 411         char padbuffer[512];
 412 
 413         if (verbose)
 414                 (void) fprintf(trace, "looking at 'bs'\n");
 415         retptr = cconvert(rmpadding(cursor_left, padbuffer, (int *) 0));
 416         if (strcmp("\\b", retptr) == 0)
 417                 pr_boolean((char *)0, "bs", (char *)0, 1);
 418 
 419         if (verbose)
 420                 (void) fprintf(trace, "looking at 'pt'\n");
 421         retptr = cconvert(rmpadding(tab, padbuffer, (int *) 0));
 422         if (strcmp("\\t", retptr) == 0)
 423                 pr_boolean((char *)0, "pt", (char *)0, 1);
 424 
 425         if (verbose)
 426                 (void) fprintf(trace, "looking at 'nc'\n");
 427         retptr = cconvert(rmpadding(carriage_return, padbuffer, (int *) 0));
 428         if (strcmp("\\r", retptr) != 0)
 429                 pr_boolean((char *)0, "nc", (char *)0, 1);
 430 
 431         if (verbose)
 432                 (void) fprintf(trace, "looking at 'ns'\n");
 433         if (scroll_forward == NULL)
 434                 pr_boolean((char *)0, "ns", (char *)0, 1);
 435 
 436         /* Ignore "xr": Return acts like ce \r \n (Delta Data) */
 437 }
 438 
 439 void
 440 pr_ncaps()
 441 {
 442         char padbuffer[512];
 443         int padding;
 444 
 445         if (verbose)
 446                 (void) fprintf(trace, "looking at 'ug'\n");
 447         /* Duplicate sg for ug: Number of blank chars left by us or ue */
 448         if (magic_cookie_glitch > -1)
 449                 pr_number((char *)0, "ug", (char *)0, magic_cookie_glitch);
 450 
 451         if (verbose)
 452                 (void) fprintf(trace, "looking at 'dB'\n");
 453         /* Number of millisec of bs delay needed */
 454         (void) rmpadding(cursor_left, padbuffer, &padding);
 455         if (padding > 0)
 456                 pr_number((char *)0, "dB", (char *)0, padding);
 457 
 458         if (verbose)
 459                 (void) fprintf(trace, "looking at 'dC'\n");
 460         /* Number of millisec of cr delay needed */
 461         (void) rmpadding(carriage_return, padbuffer, &padding);
 462         if (padding > 0)
 463                 pr_number((char *)0, "dC", (char *)0, padding);
 464 
 465         if (verbose)
 466                 (void) fprintf(trace, "looking at 'dF'\n");
 467         /* Number of millisec of ff delay needed */
 468         (void) rmpadding(form_feed, padbuffer, &padding);
 469         if (padding > 0)
 470                 pr_number((char *)0, "dF", (char *)0, padding);
 471 
 472         if (verbose)
 473                 (void) fprintf(trace, "looking at 'dN'\n");
 474         /* Number of millisec of nl delay needed */
 475         (void) rmpadding(cursor_down, padbuffer, &padding);
 476         if (padding > 0)
 477                 pr_number((char *)0, "dN", (char *)0, padding);
 478 
 479         if (verbose)
 480                 (void) fprintf(trace, "looking at 'dT'\n");
 481         /* Number of millisec of tab delay needed */
 482         (void) rmpadding(tab, padbuffer, &padding);
 483         if (padding > 0)
 484                 pr_number((char *)0, "dT", (char *)0, padding);
 485 
 486         /* Handle "kn": Number of "other" keys */
 487         setupknko();
 488         pr_kn();
 489 }
 490 
 491 void
 492 pr_scaps()
 493 {
 494         char *retptr;
 495         char padbuffer[512];
 496 
 497         /* Backspace if not "^H" */
 498         if (verbose)
 499                 (void) fprintf(trace, "looking at 'bc'\n");
 500         retptr = cconvert(rmpadding(cursor_left, padbuffer, (int *) 0));
 501         if (strcmp("\\b", retptr) != 0)
 502                 pr_string((char *)0, "bc", (char *)0, cursor_left);
 503 
 504         /* Newline character (default "\n") */
 505         if (verbose)
 506                 (void) fprintf(trace, "looking at 'nl'\n");
 507         retptr = cconvert(rmpadding(cursor_down, padbuffer, (int *) 0));
 508         if (strcmp("\\n", retptr) != 0)
 509                 pr_string((char *)0, "nl", (char *)0, cursor_down);
 510 
 511         /* Handle "ko" here: Termcap entries for other non-function keys */
 512         pr_ko();
 513 
 514         /* Ignore "ma": Arrow key map, used by vi version 2 only */
 515 }
 516 
 517 /*
 518         Set up the first terminal and save the values from it.
 519 */
 520 void
 521 initfirstterm(char *term)
 522 {
 523         register int i;
 524 
 525         if (verbose)
 526                 (void) fprintf(trace, "setting up terminal type '%s'.\n",
 527                     term);
 528 
 529         (void) setupterm(term, devnull, (int *) 0);
 530 
 531         /* Save the name for later use. */
 532         if (use) {
 533                 register unsigned int length;
 534                 savettytype = _savettytype;
 535                 if ((length = strlen(ttytype)) >= TTYLEN) {
 536                         savettytype = malloc(length);
 537                         if (savettytype == NULL) {
 538                                 (void) fprintf(stderr, "%s: malloc is out "
 539                                     "of space\n", progname);
 540                                 (void) strncpy(_savettytype, ttytype,
 541                                     TTYLEN-1);
 542                                 _savettytype[TTYLEN] = '\0';
 543                                 savettytype = _savettytype;
 544                         }
 545                 } else
 546                         (void) strcpy(_savettytype, ttytype);
 547         }
 548 
 549         if (printing != pr_none) {
 550                 pr_heading(term, ttytype);
 551                 pr_bheading();
 552         }
 553 
 554         /* Save the values for the first terminal. */
 555         for (i = 0; i < numbools; i++) {
 556                 if ((ibool[i].val = tgetflag(ibool[i].capname)) &&
 557                     printing != pr_none)
 558                         pr_boolean(ibool[i].infoname, ibool[i].capname,
 559                             ibool[i].fullname, 1);
 560                 if (verbose)
 561                         (void) fprintf(trace, "%s=%d.\n", ibool[i].infoname,
 562                             ibool[i].val);
 563         }
 564 
 565         if (printing != pr_none) {
 566                 if (printing == pr_cap)
 567                         pr_bcaps();
 568                 pr_bfooting();
 569                 pr_nheading();
 570         }
 571 
 572         for (i = 0; i < numnums; i++) {
 573                 if (((num[i].val = tgetnum(num[i].capname)) > -1) &&
 574                     printing != pr_none)
 575                         pr_number(num[i].infoname, num[i].capname,
 576                             num[i].fullname, num[i].val);
 577                 if (verbose)
 578                         (void) fprintf(trace, "%s=%d.\n", num[i].infoname,
 579                             num[i].val);
 580         }
 581 
 582         if (printing != pr_none) {
 583                 if (printing == pr_cap)
 584                         pr_ncaps();
 585                 pr_nfooting();
 586                 pr_sheading();
 587         }
 588 
 589         for (i = 0; i < numstrs; i++) {
 590                 str[i].val = tgetstr(str[i].capname, (char **)0);
 591                 if ((str[i].val != NULL) && printing != pr_none)
 592                         pr_string(str[i].infoname, str[i].capname,
 593                             str[i].fullname, str[i].val);
 594                 if (verbose) {
 595                         (void) fprintf(trace, "%s='", str[i].infoname);
 596                         PR(trace, str[i].val);
 597                         (void) fprintf(trace, "'.\n");
 598                 }
 599         }
 600 
 601         if (printing == pr_cap)
 602                 pr_scaps();
 603 
 604         if (printing != pr_none)
 605                 pr_sfooting();
 606 }
 607 
 608 /*
 609         Set up the n'th terminal.
 610 */
 611 static void
 612 check_nth_terminal(char *nterm, int n)
 613 {
 614         register char boolval;
 615         register short numval;
 616         register char *strval;
 617         register int i;
 618 
 619         if (use)
 620                 used[n] = FALSE;
 621 
 622         if (verbose)
 623                 (void) fprintf(trace, "adding in terminal type '%s'.\n",
 624                     nterm);
 625 
 626         (void) setupterm(nterm, devnull, (int *) 0);
 627 
 628         if (printing != pr_none) {
 629                 pr_heading(nterm, ttytype);
 630                 pr_bheading();
 631         }
 632 
 633         if (diff || common || neither) {
 634                 if (Aflag && Bflag)
 635                         (void) printf("comparing %s (TERMINFO=%s) to %s "
 636                             "(TERMINFO=%s).\n",
 637                         firstterm, term1info, nterm, term2info);
 638                 else if (Aflag)
 639                         (void) printf("comparing %s (TERMINFO=%s) to %s.\n",
 640                             firstterm, term1info, nterm);
 641                 else if (Bflag)
 642                         (void) printf("comparing %s to %s (TERMINFO=%s).\n",
 643                             firstterm, nterm, term2info);
 644                 else
 645                         (void) printf("comparing %s to %s.\n",
 646                             firstterm, nterm);
 647                 (void) printf("    comparing booleans.\n");
 648         }
 649 
 650         /* save away the values for the nth terminal */
 651         for (i = 0; i < numbools; i++) {
 652                 boolval = tgetflag(ibool[i].capname);
 653                 if (use) {
 654                         if (ibool[i].seenagain) {
 655                         /*
 656                         ** We do not have to worry about this impossible case
 657                         ** since booleans can have only two values: true and
 658                         ** false.
 659                         ** if (boolval && (boolval != ibool[i].secondval))
 660                         **  {
 661                         **  (void) fprintf(trace, "use= order dependency"
 662                         **  "found:\n");
 663                         **  (void) fprintf(trace, "    %s: %s has %d, %s has"
 664                         **   " %d.\n",
 665                         **      ibool[i].capname, ibool[i].secondname,
 666                         **      ibool[i].secondval, nterm, boolval);
 667                         **  }
 668                         */
 669                         } else {
 670                                 if (boolval == TRUE) {
 671                                         ibool[i].seenagain = TRUE;
 672                                         ibool[i].secondval = boolval;
 673                                         ibool[i].secondname = nterm;
 674                                         if (ibool[i].val != boolval)
 675                                                 ibool[i].changed = TRUE;
 676                                         else
 677                                                 used[n] = TRUE;
 678                                 }
 679                         }
 680                 }
 681                 if (boolval) {
 682                         if (printing != pr_none)
 683                                 pr_boolean(ibool[i].infoname, ibool[i].capname,
 684                                     ibool[i].fullname, 1);
 685                         if (common && (ibool[i].val == boolval))
 686                                 (void) printf("\t%s= T.\n", ibool[i].infoname);
 687                 } else if (neither && !ibool[i].val)
 688                         (void) printf("\t!%s.\n", ibool[i].infoname);
 689                 if (diff && (ibool[i].val != boolval))
 690                         (void) printf("\t%s: %c:%c.\n", ibool[i].infoname,
 691                             ibool[i].val?'T':'F', boolval?'T':'F');
 692                 if (verbose)
 693                         (void) fprintf(trace, "%s: %d:%d, changed=%d, "
 694                             "seen=%d.\n", ibool[i].infoname, ibool[i].val,
 695                             boolval, ibool[i].changed, ibool[i].seenagain);
 696         }
 697 
 698         if (printing != pr_none) {
 699                 if (printing == pr_cap)
 700                         pr_bcaps();
 701                 pr_bfooting();
 702                 pr_nheading();
 703         }
 704 
 705         if (diff || common || neither)
 706                 (void) printf("    comparing numbers.\n");
 707 
 708         for (i = 0; i < numnums; i++) {
 709                 numval = tgetnum(num[i].capname);
 710                 if (use) {
 711                         if (num[i].seenagain) {
 712                                 if ((numval > -1) &&
 713                                     (numval != num[i].secondval)) {
 714                                         (void) fprintf(stderr,
 715                                             "%s: use = order dependency "
 716                                             "found:\n", progname);
 717                                         (void) fprintf(stderr, "    %s: %s "
 718                                             "has %d, %s has %d.\n",
 719                                             num[i].capname, num[i].secondname,
 720                                             num[i].secondval, nterm, numval);
 721                                 }
 722                         } else {
 723                                 if (numval > -1) {
 724                                         num[i].seenagain = TRUE;
 725                                         num[i].secondval = numval;
 726                                         num[i].secondname = nterm;
 727                                         if ((numval > -1) &&
 728                                             (num[i].val != numval))
 729                                                 num[i].changed = TRUE;
 730                                         else
 731                                                 used[n] = TRUE;
 732                                 }
 733                         }
 734                 }
 735                 if (numval > -1) {
 736                         if (printing != pr_none)
 737                                 pr_number(num[i].infoname, num[i].capname,
 738                                     num[i].fullname, numval);
 739                         if (common && (num[i].val == numval))
 740                                 (void) printf("\t%s= %d.\n", num[i].infoname,
 741                                     numval);
 742                         } else if (neither && (num[i].val == -1))
 743                                 (void) printf("\t!%s.\n", num[i].infoname);
 744                         if (diff && (num[i].val != numval))
 745                                 (void) printf("\t%s: %d:%d.\n",
 746                                     num[i].infoname, num[i].val, numval);
 747                         if (verbose)
 748                                 (void) fprintf(trace, "%s: %d:%d, "
 749                                     "changed = %d, seen = %d.\n",
 750                                     num[i].infoname, num[i].val, numval,
 751                                     num[i].changed, num[i].seenagain);
 752         }
 753 
 754         if (printing != pr_none) {
 755                 if (printing == pr_cap)
 756                         pr_ncaps();
 757                 pr_nfooting();
 758                 pr_sheading();
 759         }
 760 
 761         if (diff || common || neither)
 762                 (void) printf("    comparing strings.\n");
 763 
 764         for (i = 0; i < numstrs; i++) {
 765                 strval = tgetstr(str[i].capname, (char **)0);
 766                 if (use) {
 767                         if (str[i].seenagain && (strval != NULL)) {
 768                                 if (!EQUAL(strval, str[i].secondval)) {
 769                                         (void) fprintf(stderr,
 770                                             "use= order dependency"
 771                                             "  found:\n");
 772                                         (void) fprintf(stderr,
 773                                             "    %s: %s has '",
 774                                             str[i].capname, str[i].secondname);
 775                                         PR(stderr, str[i].secondval);
 776                                         (void) fprintf(stderr,
 777                                             "', %s has '", nterm);
 778                                         PR(stderr, strval);
 779                                         (void) fprintf(stderr, "'.\n");
 780                                 }
 781                         } else {
 782                                 if (strval != NULL) {
 783                                         str[i].seenagain = TRUE;
 784                                         str[i].secondval = strval;
 785                                         str[i].secondname = nterm;
 786                                         if (!EQUAL(str[i].val, strval))
 787                                                 str[i].changed = TRUE;
 788                                         else
 789                                                 used[n] = TRUE;
 790                                 }
 791                         }
 792                 }
 793                 if (strval != NULL) {
 794                         if (printing != pr_none)
 795                                 pr_string(str[i].infoname, str[i].capname,
 796                                     str[i].fullname, strval);
 797                         if (common && EQUAL(str[i].val, strval)) {
 798                                 (void) printf("\t%s= '", str[i].infoname);
 799                                 PR(stdout, strval);
 800                                 (void) printf("'.\n");
 801                         }
 802                 } else if (neither && (str[i].val == NULL))
 803                         (void) printf("\t!%s.\n", str[i].infoname);
 804                 if (diff && !EQUAL(str[i].val, strval)) {
 805                         (void) printf("\t%s: '", str[i].infoname);
 806                         PR(stdout, str[i].val);
 807                         (void) printf("','");
 808                         PR(stdout, strval);
 809                         (void) printf("'.\n");
 810                 }
 811                 if (verbose) {
 812                         (void) fprintf(trace, "%s: '", str[i].infoname);
 813                         PR(trace, str[i].val);
 814                         (void) fprintf(trace, "':'");
 815                         PR(trace, strval);
 816                         (void) fprintf(trace, "',changed=%d,seen=%d.\n",
 817                             str[i].changed, str[i].seenagain);
 818                 }
 819         }
 820 
 821         if (printing == pr_cap)
 822                 pr_scaps();
 823 
 824         if (printing != pr_none)
 825                 pr_sfooting();
 826 
 827         return;
 828 }
 829 
 830 /*
 831         A capability gets an at-sign if it no longer exists, but
 832         one of the relative entries contains a value for it.
 833         It gets printed if the original value is not seen in ANY
 834         of the relative entries, or if the FIRST relative entry that has
 835         the capability gives a DIFFERENT value for the capability.
 836 */
 837 void
 838 dorelative(int firstoptind, int argc, char **argv)
 839 {
 840         register int i;
 841 
 842         /* turn off printing of termcap and long names */
 843         pr_init(pr_terminfo);
 844 
 845         /* print out the entry name */
 846         pr_heading((char *)0, savettytype);
 847 
 848         pr_bheading();
 849 
 850         /* Print out all bools that are different. */
 851         for (i = 0; i < numbools; i++)
 852                 if (!ibool[i].val && ibool[i].changed)
 853                         pr_boolean(ibool[i].infoname, (char *)0,
 854                             (char *)0, -1);
 855                 else if (ibool[i].val && (ibool[i].changed ||
 856                     !ibool[i].seenagain))
 857                         pr_boolean(ibool[i].infoname, (char *)0, (char *)0, 1);
 858 
 859         pr_bfooting();
 860         pr_nheading();
 861 
 862         /* Print out all nums that are different. */
 863         for (i = 0; i < numnums; i++)
 864                 if (num[i].val < 0 && num[i].changed)
 865                         pr_number(num[i].infoname, (char *)0, (char *)0, -1);
 866                 else if (num[i].val >= 0 && (num[i].changed ||
 867                     !num[i].seenagain))
 868                         pr_number(num[i].infoname, (char *)0,
 869                             (char *)0, num[i].val);
 870 
 871         pr_nfooting();
 872         pr_sheading();
 873 
 874         /* Print out all strs that are different. */
 875         for (i = 0; i < numstrs; i++)
 876                 if (str[i].val == NULL && str[i].changed)
 877                         pr_string(str[i].infoname, (char *)0, (char *)0,
 878                             (char *)0);
 879                 else if ((str[i].val != NULL) &&
 880                     (str[i].changed || !str[i].seenagain))
 881         pr_string(str[i].infoname, (char *)0, (char *)0, str[i].val);
 882 
 883         pr_sfooting();
 884 
 885         /* Finish it up. */
 886         for (i = firstoptind; i < argc; i++)
 887                 if (used[i - firstoptind])
 888                         (void) printf("\tuse=%s,\n", argv[i]);
 889                 else
 890                         (void) fprintf(stderr,
 891                             "%s: 'use=%s' did not add anything to the "
 892                             "description.\n", progname, argv[i]);
 893 }
 894 
 895 void
 896 local_setenv(char *termNinfo)
 897 {
 898         extern char **environ;
 899         static char *newenviron[2] = { 0, 0 };
 900         static unsigned int termsize = BUFSIZ;
 901         static char _terminfo[BUFSIZ];
 902         static char *terminfo = &_terminfo[0];
 903         register int termlen;
 904 
 905         if (termNinfo && *termNinfo) {
 906                 if (verbose)
 907                         (void) fprintf(trace, "setting TERMINFO=%s.\n",
 908                             termNinfo);
 909                 termlen = strlen(termNinfo);
 910                 if (termlen + 10 > termsize) {
 911                         termsize = termlen + 20;
 912                         terminfo = (char *) malloc(termsize * sizeof (char));
 913                 }
 914                 if (terminfo == (char *) NULL)
 915                         badmalloc();
 916                 (void) sprintf(terminfo, "TERMINFO=%s", termNinfo);
 917                 newenviron[0] = terminfo;
 918         } else
 919                 newenviron[0] = (char *) 0;
 920         environ = newenviron;
 921 }
 922 
 923 int
 924 main(int argc, char **argv)
 925 {
 926         int i, c, firstoptind;
 927         char *tempargv[2];
 928         char *term = getenv("TERM");
 929 
 930         term1info = term2info = getenv("TERMINFO");
 931         progname = argv[0];
 932 
 933         /* parse options */
 934         while ((c = getopt(argc, argv, "ducnILCvV1rw:s:A:B:")) != EOF)
 935                 switch (c) {
 936                         case 'v':       verbose++;
 937                                         break;
 938                         case '1':       pr_onecolumn(1);
 939                                         break;
 940                         case 'w':       pr_width(atoi(optarg));
 941                                         break;
 942                         case 'd':       diff++;
 943                                         break;
 944                         case 'c':       common++;
 945                                         break;
 946                         case 'n':       neither++;
 947                                         break;
 948                         case 'u':       use++;
 949                                         break;
 950                         case 'L':       pr_init(printing = pr_longnames);
 951                                         break;
 952                         case 'I':       pr_init(printing = pr_terminfo);
 953                                         break;
 954                         case 'C':       pr_init(printing = pr_cap);
 955                                         break;
 956                         case 'A':       term1info = optarg; Aflag++;
 957                                         break;
 958                         case 'B':       term2info = optarg; Bflag++;
 959                                         break;
 960                         case 'r':       pr_caprestrict(0);
 961                                         break;
 962                         case 's':
 963                                 if (strcmp(optarg, "d") == 0)
 964                                         sortorder = by_database;
 965                                 else if (strcmp(optarg, "i") == 0)
 966                                         sortorder = by_terminfo;
 967                                 else if (strcmp(optarg, "l") == 0)
 968                                         sortorder = by_longnames;
 969                                 else if (strcmp(optarg, "c") == 0)
 970                                         sortorder = by_cap;
 971                                 else
 972                                         goto usage;
 973                                 break;
 974                         case 'V':
 975                                 (void) printf("%s: version %s\n", progname,
 976                                     "@(#)curses:screen/infocmp.c        1.13");
 977                                 exit(0);
 978                         case '?':
 979                                 usage:
 980                                 (void) fprintf(stderr,
 981                                     "usage: %s [-ducn] [-ILC] [-1Vv] "
 982                                     "[-s d|i|l|c] [-A directory] "
 983                                     "[-B directory] term-names ...\n",
 984                                     progname);
 985                                 (void) fprintf(stderr, "\t-d\tprint "
 986                                     "differences (the default for >1 "
 987                                     "term-name)\n");
 988                                 (void) fprintf(stderr, "\t-u\tproduce "
 989                                     "relative description\n");
 990                                 (void) fprintf(stderr, "\t-c\tprint common "
 991                                     "entries\n");
 992                                 (void) fprintf(stderr, "\t-n\tprint entries "
 993                                     "in neither\n");
 994                                 (void) fprintf(stderr, "\t-I\tprint terminfo "
 995                                     "entries (the default for 1 term-name)\n");
 996                                 (void) fprintf(stderr, "\t-C\tprint termcap "
 997                                     "entries\n");
 998                                 (void) fprintf(stderr, "\t-L\tprint long C "
 999                                     "variable names\n");
1000                                 (void) fprintf(stderr, "\t-1\tsingle column "
1001                                     "output\n");
1002                                 (void) fprintf(stderr, "\t-V\tprint program "
1003                                     "version\n");
1004                                 (void) fprintf(stderr, "\t-v\tverbose "
1005                                     "debugging output\n");
1006                                 (void) fprintf(stderr, "\t-s\tchange sort "
1007                                     "order\n");
1008                                 (void) fprintf(stderr, "\t-A\tset $TERMINFO "
1009                                     "for first term-name\n");
1010                                 (void) fprintf(stderr, "\t-B\tset $TERMINFO "
1011                                     "for other term-names\n");
1012                                 exit(-1);
1013                 }
1014 
1015         argc -= optind;
1016         argv += optind;
1017         optind = 0;
1018 
1019         /* Default to $TERM for -n, -I, -C and -L options. */
1020         /* This is done by faking argv[][], argc and optind. */
1021         if (neither && (argc == 0 || argc == 1)) {
1022                 if (argc == 0)
1023                         tempargv[0] = term;
1024                 else
1025                         tempargv[0] = argv[optind];
1026                 tempargv[1] = term;
1027                 argc = 2;
1028                 argv = tempargv;
1029                 optind = 0;
1030         } else if ((printing != pr_none) && (argc == 0)) {
1031                 tempargv[0] = term;
1032                 argc = 1;
1033                 argv = tempargv;
1034                 optind = 0;
1035         }
1036 
1037         /* Check for enough names. */
1038         if ((use || diff || common) && (argc <= 1)) {
1039                 (void) fprintf(stderr,
1040                     "%s: must have at least two terminal names for a "
1041                     "comparison to be done.\n", progname);
1042                 goto usage;
1043         }
1044 
1045         /* Set the default of diff -d or print -I */
1046         if (!use && (printing == pr_none) && !common && !neither) {
1047                 if (argc == 0 || argc == 1) {
1048                         if (argc == 0) {
1049                                 tempargv[0] = term;
1050                                 argc = 1;
1051                                 argv = tempargv;
1052                                 optind = 0;
1053                         }
1054                         pr_init(printing = pr_terminfo);
1055                 } else
1056                         diff++;
1057         }
1058 
1059         /* Set the default sorting order. */
1060         if (sortorder == none)
1061                 switch ((int) printing) {
1062                         case (int) pr_cap:
1063                                 sortorder = by_cap; break;
1064                         case (int) pr_longnames:
1065                                 sortorder = by_longnames; break;
1066                         case (int) pr_terminfo:
1067                         case (int) pr_none:
1068                                 sortorder = by_terminfo; break;
1069                 }
1070 
1071         firstterm = argv[optind++];
1072         firstoptind = optind;
1073 
1074         allocvariables(argc, firstoptind);
1075         sortnames();
1076 
1077         devnull = open("/dev/null", O_RDWR);
1078         local_setenv(term1info);
1079         initfirstterm(firstterm);
1080         local_setenv(term2info);
1081         for (i = 0; optind < argc; optind++, i++)
1082                 check_nth_terminal(argv[optind], i);
1083 
1084         if (use)
1085                 dorelative(firstoptind, argc, argv);
1086 
1087         return (0);
1088 }