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  * Copyright (c) 2019, Joyent, Inc.
  31  */
  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 
 193         if ((ibool == NULL) || (num == NULL) || (str == NULL) ||
 194             (use && (used == NULL)))
 195                 badmalloc();
 196 
 197         /* Fill in the names and initialize the structures. */
 198         nullseen = FALSE;
 199         for (i = 0; i < numbools; i++) {
 200                 ibool[i].infoname = boolnames[i];
 201                 ibool[i].capname = boolcodes[i];
 202                 /* This is necessary until fnames.c is */
 203                 /* incorporated into standard curses. */
 204                 if (nullseen || (boolfnames[i] == NULL)) {
 205                         ibool[i].fullname = "unknown_boolean";
 206                         nullseen = TRUE;
 207                 } else {
 208                         ibool[i].fullname = boolfnames[i];
 209                 }
 210                 ibool[i].changed = FALSE;
 211                 ibool[i].seenagain = FALSE;
 212         }
 213         nullseen = 0;
 214         for (i = 0; i < numnums; i++) {
 215                 num[i].infoname = numnames[i];
 216                 num[i].capname = numcodes[i];
 217                 if (nullseen || (numfnames[i] == NULL)) {
 218                         ibool[i].fullname = "unknown_number";
 219                         nullseen = TRUE;
 220                 } else {
 221                         num[i].fullname = numfnames[i];
 222                 }
 223                 num[i].changed = FALSE;
 224                 num[i].seenagain = FALSE;
 225         }
 226         nullseen = 0;
 227         for (i = 0; i < numstrs; i++) {
 228                 str[i].infoname = strnames[i];
 229                 str[i].capname = strcodes[i];
 230                 if (nullseen || (strfnames[i] == NULL)) {
 231                         str[i].fullname = "unknown_string";
 232                         nullseen = TRUE;
 233                 } else {
 234                         str[i].fullname = strfnames[i];
 235                 }
 236                 str[i].changed = FALSE;
 237                 str[i].seenagain = FALSE;
 238         }
 239 }
 240 
 241 /*
 242         Routines to be passed to qsort(3) for comparison of the structures.
 243 */
 244 int
 245 boolcompare(const void *x, const void *y)
 246 {
 247         struct boolstruct *a;
 248         struct boolstruct *b;
 249 
 250         a = (struct boolstruct *)x;
 251         b = (struct boolstruct *)y;
 252 
 253         switch ((int) sortorder) {
 254                 case (int) by_terminfo:
 255                         return (strcmp(a->infoname, b->infoname));
 256                 case (int) by_cap:
 257                         return (strcmp(a->capname, b->capname));
 258                 case (int) by_longnames:
 259                         return (strcmp(a->fullname, b->fullname));
 260                 default:
 261                         return (0);
 262         }
 263 }
 264 
 265 int
 266 numcompare(const void *x, const void *y)
 267 {
 268         struct numstruct *a;
 269         struct numstruct *b;
 270 
 271         a = (struct numstruct *)x;
 272         b = (struct numstruct *)y;
 273         switch ((int) sortorder) {
 274                 case (int) by_terminfo:
 275                         return (strcmp(a->infoname, b->infoname));
 276                 case (int) by_cap:
 277                         return (strcmp(a->capname, b->capname));
 278                 case (int) by_longnames:
 279                         return (strcmp(a->fullname, b->fullname));
 280                 default:
 281                         return (0);
 282         }
 283 }
 284 
 285 int
 286 strcompare(const void *x, const void *y)
 287 {
 288         struct strstruct *a;
 289         struct strstruct *b;
 290 
 291         a = (struct strstruct *)x;
 292         b = (struct strstruct *)y;
 293 
 294         switch ((int) sortorder) {
 295                 case (int) by_terminfo:
 296                         return (strcmp(a->infoname, b->infoname));
 297                 case (int) by_cap:
 298                         return (strcmp(a->capname, b->capname));
 299                 case (int) by_longnames:
 300                         return (strcmp(a->fullname, b->fullname));
 301                 default:
 302                         return (0);
 303         }
 304 }
 305 
 306 /*
 307         Sort the entries by their terminfo name.
 308 */
 309 static void
 310 sortnames()
 311 {
 312         if (sortorder != by_database) {
 313                 qsort((char *) ibool, (unsigned) numbools,
 314                         sizeof (struct boolstruct), boolcompare);
 315                 qsort((char *) num, (unsigned) numnums,
 316                         sizeof (struct numstruct), numcompare);
 317                 qsort((char *) str, (unsigned) numstrs,
 318                         sizeof (struct strstruct), strcompare);
 319         }
 320         return;
 321 }
 322 
 323 /*
 324         Print out a string, or "NULL" if it's not defined.
 325 */
 326 void
 327 PR(FILE *stream, char *string)
 328 {
 329         if (string == NULL)
 330                 (void) fprintf(stream, "NULL");
 331         else
 332                 tpr(stream, string);
 333 }
 334 
 335 /*
 336         Output the 'ko' termcap string. This is a list of all of the input
 337         keys that input the same thing as the corresponding output strings.
 338 */
 339 int kncounter;
 340 char kobuffer[512];
 341 
 342 char
 343 *addko(char *output, char *input, char *koptr)
 344 {
 345         char *inptr, *outptr, padbuffer[512];
 346         inptr = tgetstr(input, (char **)0);
 347         if (inptr == NULL)
 348                 return (koptr);
 349         outptr = tgetstr(output, (char **)0);
 350         if (outptr == NULL)
 351                 return (koptr);
 352         outptr = rmpadding(outptr, padbuffer, (int *) 0);
 353         if (strcmp(inptr, outptr) == 0) {
 354                 *koptr++ = *output++;
 355                 *koptr++ = *output++;
 356                 *koptr++ = ',';
 357                 kncounter++;
 358         }
 359         return (koptr);
 360 }
 361 
 362 void
 363 setupknko()
 364 {
 365         char *koptr;
 366 
 367         kncounter = 0;
 368         koptr = kobuffer;
 369 
 370         koptr = addko("bs", "kb", koptr);       /* key_backspace */
 371         koptr = addko("bt", "kB", koptr);       /* key_btab */
 372         koptr = addko("cl", "kC", koptr);       /* key_clear */
 373         koptr = addko("le", "kl", koptr);       /* key_left */
 374         koptr = addko("do", "kd", koptr);       /* key_down */
 375         koptr = addko("nd", "kr", koptr);       /* key_right */
 376         koptr = addko("up", "ku", koptr);       /* key_up */
 377         koptr = addko("dc", "kD", koptr);       /* key_dc */
 378         koptr = addko("dl", "kL", koptr);       /* key_dl */
 379         koptr = addko("cd", "kS", koptr);       /* key_eos */
 380         koptr = addko("ce", "kE", koptr);       /* key_eol */
 381         koptr = addko("ho", "kh", koptr);       /* key_home */
 382         koptr = addko("st", "kT", koptr);       /* key_stab */
 383         koptr = addko("ic", "kI", koptr);       /* key_ic */
 384         koptr = addko("im", "kI", koptr);       /* key_ic */
 385         koptr = addko("al", "kA", koptr);       /* key_il */
 386         koptr = addko("sf", "kF", koptr);       /* key_sf */
 387         koptr = addko("ll", "kH", koptr);       /* key_ll */
 388         koptr = addko("sr", "kR", koptr);       /* key_sr */
 389         koptr = addko("ei", "kM", koptr);       /* key_eic */
 390         koptr = addko("ct", "ka", koptr);       /* key_catab */
 391 
 392         /* get rid of comma */
 393         if (koptr != kobuffer)
 394                 *(--koptr) = '\0';
 395 }
 396 
 397 void
 398 pr_kn()
 399 {
 400         if (kncounter > 0)
 401                 pr_number((char *)0, "kn", (char *)0, kncounter);
 402 }
 403 
 404 void
 405 pr_ko()
 406 {
 407         if (kncounter > 0)
 408                 pr_string((char *)0, "ko", (char *)0, kobuffer);
 409 }
 410 
 411 void
 412 pr_bcaps()
 413 {
 414         char *retptr;
 415         char padbuffer[512];
 416 
 417         if (verbose)
 418                 (void) fprintf(trace, "looking at 'bs'\n");
 419         retptr = cconvert(rmpadding(cursor_left, padbuffer, (int *) 0));
 420         if (strcmp("\\b", retptr) == 0)
 421                 pr_boolean((char *)0, "bs", (char *)0, 1);
 422 
 423         if (verbose)
 424                 (void) fprintf(trace, "looking at 'pt'\n");
 425         retptr = cconvert(rmpadding(tab, padbuffer, (int *) 0));
 426         if (strcmp("\\t", retptr) == 0)
 427                 pr_boolean((char *)0, "pt", (char *)0, 1);
 428 
 429         if (verbose)
 430                 (void) fprintf(trace, "looking at 'nc'\n");
 431         retptr = cconvert(rmpadding(carriage_return, padbuffer, (int *) 0));
 432         if (strcmp("\\r", retptr) != 0)
 433                 pr_boolean((char *)0, "nc", (char *)0, 1);
 434 
 435         if (verbose)
 436                 (void) fprintf(trace, "looking at 'ns'\n");
 437         if (scroll_forward == NULL)
 438                 pr_boolean((char *)0, "ns", (char *)0, 1);
 439 
 440         /* Ignore "xr": Return acts like ce \r \n (Delta Data) */
 441 }
 442 
 443 void
 444 pr_ncaps()
 445 {
 446         char padbuffer[512];
 447         int padding;
 448 
 449         if (verbose)
 450                 (void) fprintf(trace, "looking at 'ug'\n");
 451         /* Duplicate sg for ug: Number of blank chars left by us or ue */
 452         if (magic_cookie_glitch > -1)
 453                 pr_number((char *)0, "ug", (char *)0, magic_cookie_glitch);
 454 
 455         if (verbose)
 456                 (void) fprintf(trace, "looking at 'dB'\n");
 457         /* Number of millisec of bs delay needed */
 458         (void) rmpadding(cursor_left, padbuffer, &padding);
 459         if (padding > 0)
 460                 pr_number((char *)0, "dB", (char *)0, padding);
 461 
 462         if (verbose)
 463                 (void) fprintf(trace, "looking at 'dC'\n");
 464         /* Number of millisec of cr delay needed */
 465         (void) rmpadding(carriage_return, padbuffer, &padding);
 466         if (padding > 0)
 467                 pr_number((char *)0, "dC", (char *)0, padding);
 468 
 469         if (verbose)
 470                 (void) fprintf(trace, "looking at 'dF'\n");
 471         /* Number of millisec of ff delay needed */
 472         (void) rmpadding(form_feed, padbuffer, &padding);
 473         if (padding > 0)
 474                 pr_number((char *)0, "dF", (char *)0, padding);
 475 
 476         if (verbose)
 477                 (void) fprintf(trace, "looking at 'dN'\n");
 478         /* Number of millisec of nl delay needed */
 479         (void) rmpadding(cursor_down, padbuffer, &padding);
 480         if (padding > 0)
 481                 pr_number((char *)0, "dN", (char *)0, padding);
 482 
 483         if (verbose)
 484                 (void) fprintf(trace, "looking at 'dT'\n");
 485         /* Number of millisec of tab delay needed */
 486         (void) rmpadding(tab, padbuffer, &padding);
 487         if (padding > 0)
 488                 pr_number((char *)0, "dT", (char *)0, padding);
 489 
 490         /* Handle "kn": Number of "other" keys */
 491         setupknko();
 492         pr_kn();
 493 }
 494 
 495 void
 496 pr_scaps()
 497 {
 498         char *retptr;
 499         char padbuffer[512];
 500 
 501         /* Backspace if not "^H" */
 502         if (verbose)
 503                 (void) fprintf(trace, "looking at 'bc'\n");
 504         retptr = cconvert(rmpadding(cursor_left, padbuffer, (int *) 0));
 505         if (strcmp("\\b", retptr) != 0)
 506                 pr_string((char *)0, "bc", (char *)0, cursor_left);
 507 
 508         /* Newline character (default "\n") */
 509         if (verbose)
 510                 (void) fprintf(trace, "looking at 'nl'\n");
 511         retptr = cconvert(rmpadding(cursor_down, padbuffer, (int *) 0));
 512         if (strcmp("\\n", retptr) != 0)
 513                 pr_string((char *)0, "nl", (char *)0, cursor_down);
 514 
 515         /* Handle "ko" here: Termcap entries for other non-function keys */
 516         pr_ko();
 517 
 518         /* Ignore "ma": Arrow key map, used by vi version 2 only */
 519 }
 520 
 521 /*
 522         Set up the first terminal and save the values from it.
 523 */
 524 void
 525 initfirstterm(char *term)
 526 {
 527         register int i;
 528 
 529         if (verbose) {
 530                 (void) fprintf(trace, "setting up terminal type '%s'.\n",
 531                     term);
 532         }
 533 
 534         (void) setupterm(term, devnull, (int *) 0);
 535 
 536         /* Save the name for later use. */
 537         if (use) {
 538                 register unsigned int length;
 539                 savettytype = _savettytype;
 540                 if ((length = strlen(ttytype)) >= TTYLEN) {
 541                         savettytype = malloc(length);
 542                         if (savettytype == NULL) {
 543                                 (void) fprintf(stderr, "%s: malloc is out "
 544                                     "of space\n", progname);
 545                                 (void) strncpy(_savettytype, ttytype,
 546                                     TTYLEN-1);
 547                                 _savettytype[TTYLEN] = '\0';
 548                                 savettytype = _savettytype;
 549                         }
 550                 } else {
 551                         (void) strcpy(_savettytype, ttytype);
 552                 }
 553         }
 554 
 555         if (printing != pr_none) {
 556                 pr_heading(term, ttytype);
 557                 pr_bheading();
 558         }
 559 
 560         /* Save the values for the first terminal. */
 561         for (i = 0; i < numbools; i++) {
 562                 if ((ibool[i].val = tgetflag(ibool[i].capname)) &&
 563                     printing != pr_none) {
 564                         pr_boolean(ibool[i].infoname, ibool[i].capname,
 565                             ibool[i].fullname, 1);
 566                 }
 567 
 568                 if (verbose) {
 569                         (void) fprintf(trace, "%s=%d.\n", ibool[i].infoname,
 570                             ibool[i].val);
 571                 }
 572         }
 573 
 574         if (printing != pr_none) {
 575                 if (printing == pr_cap)
 576                         pr_bcaps();
 577                 pr_bfooting();
 578                 pr_nheading();
 579         }
 580 
 581         for (i = 0; i < numnums; i++) {
 582                 if (((num[i].val = tgetnum(num[i].capname)) > -1) &&
 583                     printing != pr_none) {
 584                         pr_number(num[i].infoname, num[i].capname,
 585                             num[i].fullname, num[i].val);
 586                 }
 587 
 588                 if (verbose) {
 589                         (void) fprintf(trace, "%s=%d.\n", num[i].infoname,
 590                             num[i].val);
 591                 }
 592         }
 593 
 594         if (printing != pr_none) {
 595                 if (printing == pr_cap)
 596                         pr_ncaps();
 597                 pr_nfooting();
 598                 pr_sheading();
 599         }
 600 
 601         for (i = 0; i < numstrs; i++) {
 602                 str[i].val = tgetstr(str[i].capname, (char **)0);
 603                 if ((str[i].val != NULL) && printing != pr_none) {
 604                         pr_string(str[i].infoname, str[i].capname,
 605                             str[i].fullname, str[i].val);
 606                 }
 607 
 608                 if (verbose) {
 609                         (void) fprintf(trace, "%s='", str[i].infoname);
 610                         PR(trace, str[i].val);
 611                         (void) fprintf(trace, "'.\n");
 612                 }
 613         }
 614 
 615         if (printing == pr_cap)
 616                 pr_scaps();
 617 
 618         if (printing != pr_none)
 619                 pr_sfooting();
 620 }
 621 
 622 /*
 623         Set up the n'th terminal.
 624 */
 625 static void
 626 check_nth_terminal(char *nterm, int n)
 627 {
 628         register char boolval;
 629         register short numval;
 630         register char *strval;
 631         register int i;
 632 
 633         if (use)
 634                 used[n] = FALSE;
 635 
 636         if (verbose) {
 637                 (void) fprintf(trace, "adding in terminal type '%s'.\n",
 638                     nterm);
 639         }
 640 
 641         (void) setupterm(nterm, devnull, (int *) 0);
 642 
 643         if (printing != pr_none) {
 644                 pr_heading(nterm, ttytype);
 645                 pr_bheading();
 646         }
 647 
 648         if (diff || common || neither) {
 649                 if (Aflag && Bflag)
 650                         (void) printf("comparing %s (TERMINFO=%s) to %s "
 651                             "(TERMINFO=%s).\n",
 652                         firstterm, term1info, nterm, term2info);
 653                 else if (Aflag)
 654                         (void) printf("comparing %s (TERMINFO=%s) to %s.\n",
 655                             firstterm, term1info, nterm);
 656                 else if (Bflag)
 657                         (void) printf("comparing %s to %s (TERMINFO=%s).\n",
 658                             firstterm, nterm, term2info);
 659                 else
 660                         (void) printf("comparing %s to %s.\n",
 661                             firstterm, nterm);
 662                 (void) printf("    comparing booleans.\n");
 663         }
 664 
 665         /* save away the values for the nth terminal */
 666         for (i = 0; i < numbools; i++) {
 667                 boolval = tgetflag(ibool[i].capname);
 668                 if (use) {
 669                         if (ibool[i].seenagain) {
 670                         /*
 671                         ** We do not have to worry about this impossible case
 672                         ** since booleans can have only two values: true and
 673                         ** false.
 674                         ** if (boolval && (boolval != ibool[i].secondval))
 675                         **  {
 676                         **  (void) fprintf(trace, "use= order dependency"
 677                         **  "found:\n");
 678                         **  (void) fprintf(trace, "    %s: %s has %d, %s has"
 679                         **   " %d.\n",
 680                         **      ibool[i].capname, ibool[i].secondname,
 681                         **      ibool[i].secondval, nterm, boolval);
 682                         **  }
 683                         */
 684                         } else {
 685                                 if (boolval == TRUE) {
 686                                         ibool[i].seenagain = TRUE;
 687                                         ibool[i].secondval = boolval;
 688                                         ibool[i].secondname = nterm;
 689                                         if (ibool[i].val != boolval)
 690                                                 ibool[i].changed = TRUE;
 691                                         else
 692                                                 used[n] = TRUE;
 693                                 }
 694                         }
 695                 }
 696                 if (boolval) {
 697                         if (printing != pr_none) {
 698                                 pr_boolean(ibool[i].infoname, ibool[i].capname,
 699                                     ibool[i].fullname, 1);
 700                         }
 701 
 702                         if (common && (ibool[i].val == boolval))
 703                                 (void) printf("\t%s= T.\n", ibool[i].infoname);
 704                 } else if (neither && !ibool[i].val) {
 705                         (void) printf("\t!%s.\n", ibool[i].infoname);
 706                 }
 707                 if (diff && (ibool[i].val != boolval))
 708                         (void) printf("\t%s: %c:%c.\n", ibool[i].infoname,
 709                             ibool[i].val?'T':'F', boolval?'T':'F');
 710                 if (verbose) {
 711                         (void) fprintf(trace, "%s: %d:%d, changed=%d, "
 712                             "seen=%d.\n", ibool[i].infoname, ibool[i].val,
 713                             boolval, ibool[i].changed, ibool[i].seenagain);
 714                 }
 715         }
 716 
 717         if (printing != pr_none) {
 718                 if (printing == pr_cap)
 719                         pr_bcaps();
 720                 pr_bfooting();
 721                 pr_nheading();
 722         }
 723 
 724         if (diff || common || neither)
 725                 (void) printf("    comparing numbers.\n");
 726 
 727         for (i = 0; i < numnums; i++) {
 728                 numval = tgetnum(num[i].capname);
 729                 if (use) {
 730                         if (num[i].seenagain) {
 731                                 if ((numval > -1) &&
 732                                     (numval != num[i].secondval)) {
 733                                         (void) fprintf(stderr,
 734                                             "%s: use = order dependency "
 735                                             "found:\n", progname);
 736                                         (void) fprintf(stderr, "    %s: %s "
 737                                             "has %d, %s has %d.\n",
 738                                             num[i].capname, num[i].secondname,
 739                                             num[i].secondval, nterm, numval);
 740                                 }
 741                         } else {
 742                                 if (numval > -1) {
 743                                         num[i].seenagain = TRUE;
 744                                         num[i].secondval = numval;
 745                                         num[i].secondname = nterm;
 746                                         if ((numval > -1) &&
 747                                             (num[i].val != numval))
 748                                                 num[i].changed = TRUE;
 749                                         else
 750                                                 used[n] = TRUE;
 751                                 }
 752                         }
 753                 }
 754                 if (numval > -1) {
 755                         if (printing != pr_none) {
 756                                 pr_number(num[i].infoname, num[i].capname,
 757                                     num[i].fullname, numval);
 758                         }
 759 
 760                         if (common && (num[i].val == numval)) {
 761                                 (void) printf("\t%s= %d.\n", num[i].infoname,
 762                                     numval);
 763                         }
 764 
 765                 } else if (neither && (num[i].val == -1)) {
 766                         (void) printf("\t!%s.\n", num[i].infoname);
 767                 }
 768 
 769                 if (diff && (num[i].val != numval)) {
 770                         (void) printf("\t%s: %d:%d.\n",
 771                             num[i].infoname, num[i].val, numval);
 772                 }
 773 
 774                 if (verbose) {
 775                         (void) fprintf(trace, "%s: %d:%d, "
 776                             "changed = %d, seen = %d.\n",
 777                             num[i].infoname, num[i].val, numval,
 778                             num[i].changed, num[i].seenagain);
 779                 }
 780         }
 781 
 782         if (printing != pr_none) {
 783                 if (printing == pr_cap)
 784                         pr_ncaps();
 785                 pr_nfooting();
 786                 pr_sheading();
 787         }
 788 
 789         if (diff || common || neither)
 790                 (void) printf("    comparing strings.\n");
 791 
 792         for (i = 0; i < numstrs; i++) {
 793                 strval = tgetstr(str[i].capname, (char **)0);
 794                 if (use) {
 795                         if (str[i].seenagain && (strval != NULL)) {
 796                                 if (!EQUAL(strval, str[i].secondval)) {
 797                                         (void) fprintf(stderr,
 798                                             "use= order dependency"
 799                                             "  found:\n");
 800                                         (void) fprintf(stderr,
 801                                             "    %s: %s has '",
 802                                             str[i].capname, str[i].secondname);
 803                                         PR(stderr, str[i].secondval);
 804                                         (void) fprintf(stderr,
 805                                             "', %s has '", nterm);
 806                                         PR(stderr, strval);
 807                                         (void) fprintf(stderr, "'.\n");
 808                                 }
 809                         } else {
 810                                 if (strval != NULL) {
 811                                         str[i].seenagain = TRUE;
 812                                         str[i].secondval = strval;
 813                                         str[i].secondname = nterm;
 814                                         if (!EQUAL(str[i].val, strval))
 815                                                 str[i].changed = TRUE;
 816                                         else
 817                                                 used[n] = TRUE;
 818                                 }
 819                         }
 820                 }
 821                 if (strval != NULL) {
 822                         if (printing != pr_none) {
 823                                 pr_string(str[i].infoname, str[i].capname,
 824                                     str[i].fullname, strval);
 825                         }
 826 
 827                         if (common && EQUAL(str[i].val, strval)) {
 828                                 (void) printf("\t%s= '", str[i].infoname);
 829                                 PR(stdout, strval);
 830                                 (void) printf("'.\n");
 831                         }
 832                 } else if (neither && (str[i].val == NULL))
 833                         (void) printf("\t!%s.\n", str[i].infoname);
 834                 if (diff && !EQUAL(str[i].val, strval)) {
 835                         (void) printf("\t%s: '", str[i].infoname);
 836                         PR(stdout, str[i].val);
 837                         (void) printf("','");
 838                         PR(stdout, strval);
 839                         (void) printf("'.\n");
 840                 }
 841                 if (verbose) {
 842                         (void) fprintf(trace, "%s: '", str[i].infoname);
 843                         PR(trace, str[i].val);
 844                         (void) fprintf(trace, "':'");
 845                         PR(trace, strval);
 846                         (void) fprintf(trace, "',changed=%d,seen=%d.\n",
 847                             str[i].changed, str[i].seenagain);
 848                 }
 849         }
 850 
 851         if (printing == pr_cap)
 852                 pr_scaps();
 853 
 854         if (printing != pr_none)
 855                 pr_sfooting();
 856 
 857         return;
 858 }
 859 
 860 /*
 861         A capability gets an at-sign if it no longer exists, but
 862         one of the relative entries contains a value for it.
 863         It gets printed if the original value is not seen in ANY
 864         of the relative entries, or if the FIRST relative entry that has
 865         the capability gives a DIFFERENT value for the capability.
 866 */
 867 void
 868 dorelative(int firstoptind, int argc, char **argv)
 869 {
 870         register int i;
 871 
 872         /* turn off printing of termcap and long names */
 873         pr_init(pr_terminfo);
 874 
 875         /* print out the entry name */
 876         pr_heading((char *)0, savettytype);
 877 
 878         pr_bheading();
 879 
 880         /* Print out all bools that are different. */
 881         for (i = 0; i < numbools; i++) {
 882                 if (!ibool[i].val && ibool[i].changed) {
 883                         pr_boolean(ibool[i].infoname, (char *)0,
 884                             (char *)0, -1);
 885                 } else if (ibool[i].val && (ibool[i].changed ||
 886                     !ibool[i].seenagain)) {
 887                         pr_boolean(ibool[i].infoname, (char *)0, (char *)0, 1);
 888                 }
 889         }
 890 
 891         pr_bfooting();
 892         pr_nheading();
 893 
 894         /* Print out all nums that are different. */
 895         for (i = 0; i < numnums; i++) {
 896                 if (num[i].val < 0 && num[i].changed) {
 897                         pr_number(num[i].infoname, (char *)0, (char *)0, -1);
 898                 } else if (num[i].val >= 0 && (num[i].changed ||
 899                     !num[i].seenagain)) {
 900                         pr_number(num[i].infoname, (char *)0,
 901                             (char *)0, num[i].val);
 902                 }
 903         }
 904 
 905         pr_nfooting();
 906         pr_sheading();
 907 
 908         /* Print out all strs that are different. */
 909         for (i = 0; i < numstrs; i++) {
 910                 if (str[i].val == NULL && str[i].changed) {
 911                         pr_string(str[i].infoname, (char *)0, (char *)0,
 912                             (char *)0);
 913                 } else if ((str[i].val != NULL) &&
 914                     (str[i].changed || !str[i].seenagain)) {
 915                         pr_string(str[i].infoname,
 916                             (char *)0, (char *)0, str[i].val);
 917                 }
 918         }
 919 
 920         pr_sfooting();
 921 
 922         /* Finish it up. */
 923         for (i = firstoptind; i < argc; i++) {
 924                 if (used[i - firstoptind]) {
 925                         (void) printf("\tuse=%s,\n", argv[i]);
 926                 } else {
 927                         (void) fprintf(stderr,
 928                             "%s: 'use=%s' did not add anything to the "
 929                             "description.\n", progname, argv[i]);
 930                 }
 931         }
 932 }
 933 
 934 void
 935 local_setenv(char *termNinfo)
 936 {
 937         extern char **environ;
 938         static char *newenviron[2] = { 0, 0 };
 939         static unsigned int termsize = BUFSIZ;
 940         static char _terminfo[BUFSIZ];
 941         static char *terminfo = &_terminfo[0];
 942         register int termlen;
 943 
 944         if (termNinfo && *termNinfo) {
 945                 if (verbose) {
 946                         (void) fprintf(trace, "setting TERMINFO=%s.\n",
 947                             termNinfo);
 948                 }
 949 
 950                 termlen = strlen(termNinfo);
 951                 if (termlen + 10 > termsize) {
 952                         termsize = termlen + 20;
 953                         terminfo = (char *) malloc(termsize * sizeof (char));
 954                 }
 955                 if (terminfo == (char *) NULL)
 956                         badmalloc();
 957                 (void) sprintf(terminfo, "TERMINFO=%s", termNinfo);
 958                 newenviron[0] = terminfo;
 959         } else
 960                 newenviron[0] = (char *) 0;
 961         environ = newenviron;
 962 }
 963 
 964 int
 965 main(int argc, char **argv)
 966 {
 967         int i, c, firstoptind;
 968         char *tempargv[2];
 969         char *term = getenv("TERM");
 970 
 971         term1info = term2info = getenv("TERMINFO");
 972         progname = argv[0];
 973 
 974         /* parse options */
 975         while ((c = getopt(argc, argv, "ducnILCvV1rw:s:A:B:")) != EOF)
 976                 switch (c) {
 977                         case 'v':       verbose++;
 978                                         break;
 979                         case '1':       pr_onecolumn(1);
 980                                         break;
 981                         case 'w':       pr_width(atoi(optarg));
 982                                         break;
 983                         case 'd':       diff++;
 984                                         break;
 985                         case 'c':       common++;
 986                                         break;
 987                         case 'n':       neither++;
 988                                         break;
 989                         case 'u':       use++;
 990                                         break;
 991                         case 'L':       pr_init(printing = pr_longnames);
 992                                         break;
 993                         case 'I':       pr_init(printing = pr_terminfo);
 994                                         break;
 995                         case 'C':       pr_init(printing = pr_cap);
 996                                         break;
 997                         case 'A':       term1info = optarg; Aflag++;
 998                                         break;
 999                         case 'B':       term2info = optarg; Bflag++;
1000                                         break;
1001                         case 'r':       pr_caprestrict(0);
1002                                         break;
1003                         case 's':
1004                                 if (strcmp(optarg, "d") == 0)
1005                                         sortorder = by_database;
1006                                 else if (strcmp(optarg, "i") == 0)
1007                                         sortorder = by_terminfo;
1008                                 else if (strcmp(optarg, "l") == 0)
1009                                         sortorder = by_longnames;
1010                                 else if (strcmp(optarg, "c") == 0)
1011                                         sortorder = by_cap;
1012                                 else
1013                                         goto usage;
1014                                 break;
1015                         case 'V':
1016                                 (void) printf("%s: version %s\n", progname,
1017                                     "@(#)curses:screen/infocmp.c        1.13");
1018                                 exit(0);
1019                         case '?':
1020                                 usage:
1021                                 (void) fprintf(stderr,
1022                                     "usage: %s [-ducn] [-ILC] [-1Vv] "
1023                                     "[-s d|i|l|c] [-A directory] "
1024                                     "[-B directory] term-names ...\n",
1025                                     progname);
1026                                 (void) fprintf(stderr, "\t-d\tprint "
1027                                     "differences (the default for >1 "
1028                                     "term-name)\n");
1029                                 (void) fprintf(stderr, "\t-u\tproduce "
1030                                     "relative description\n");
1031                                 (void) fprintf(stderr, "\t-c\tprint common "
1032                                     "entries\n");
1033                                 (void) fprintf(stderr, "\t-n\tprint entries "
1034                                     "in neither\n");
1035                                 (void) fprintf(stderr, "\t-I\tprint terminfo "
1036                                     "entries (the default for 1 term-name)\n");
1037                                 (void) fprintf(stderr, "\t-C\tprint termcap "
1038                                     "entries\n");
1039                                 (void) fprintf(stderr, "\t-L\tprint long C "
1040                                     "variable names\n");
1041                                 (void) fprintf(stderr, "\t-1\tsingle column "
1042                                     "output\n");
1043                                 (void) fprintf(stderr, "\t-V\tprint program "
1044                                     "version\n");
1045                                 (void) fprintf(stderr, "\t-v\tverbose "
1046                                     "debugging output\n");
1047                                 (void) fprintf(stderr, "\t-s\tchange sort "
1048                                     "order\n");
1049                                 (void) fprintf(stderr, "\t-A\tset $TERMINFO "
1050                                     "for first term-name\n");
1051                                 (void) fprintf(stderr, "\t-B\tset $TERMINFO "
1052                                     "for other term-names\n");
1053                                 exit(-1);
1054                 }
1055 
1056         argc -= optind;
1057         argv += optind;
1058         optind = 0;
1059 
1060         /* Default to $TERM for -n, -I, -C and -L options. */
1061         /* This is done by faking argv[][], argc and optind. */
1062         if (neither && (argc == 0 || argc == 1)) {
1063                 if (argc == 0)
1064                         tempargv[0] = term;
1065                 else
1066                         tempargv[0] = argv[optind];
1067                 tempargv[1] = term;
1068                 argc = 2;
1069                 argv = tempargv;
1070                 optind = 0;
1071         } else if ((printing != pr_none) && (argc == 0)) {
1072                 tempargv[0] = term;
1073                 argc = 1;
1074                 argv = tempargv;
1075                 optind = 0;
1076         }
1077 
1078         /* Check for enough names. */
1079         if ((use || diff || common) && (argc <= 1)) {
1080                 (void) fprintf(stderr,
1081                     "%s: must have at least two terminal names for a "
1082                     "comparison to be done.\n", progname);
1083                 goto usage;
1084         }
1085 
1086         /* Set the default of diff -d or print -I */
1087         if (!use && (printing == pr_none) && !common && !neither) {
1088                 if (argc == 0 || argc == 1) {
1089                         if (argc == 0) {
1090                                 tempargv[0] = term;
1091                                 argc = 1;
1092                                 argv = tempargv;
1093                                 optind = 0;
1094                         }
1095                         pr_init(printing = pr_terminfo);
1096                 } else {
1097                         diff++;
1098                 }
1099         }
1100 
1101         /* Set the default sorting order. */
1102         if (sortorder == none) {
1103                 switch ((int) printing) {
1104                         case (int) pr_cap:
1105                                 sortorder = by_cap; break;
1106                         case (int) pr_longnames:
1107                                 sortorder = by_longnames; break;
1108                         case (int) pr_terminfo:
1109                         case (int) pr_none:
1110                                 sortorder = by_terminfo; break;
1111                 }
1112         }
1113 
1114         firstterm = argv[optind++];
1115         firstoptind = optind;
1116 
1117         allocvariables(argc, firstoptind);
1118         sortnames();
1119 
1120         devnull = open("/dev/null", O_RDWR);
1121         local_setenv(term1info);
1122         initfirstterm(firstterm);
1123         local_setenv(term2info);
1124         for (i = 0; optind < argc; optind++, i++)
1125                 check_nth_terminal(argv[optind], i);
1126 
1127         if (use)
1128                 dorelative(firstoptind, argc, argv);
1129 
1130         return (0);
1131 }