[BACK]Return to infocmp.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / infocmp

Annotation of src/usr.bin/infocmp/infocmp.c, Revision 1.10

1.10    ! millert     1: /*     $OpenBSD: infocmp.c,v 1.9 2000/03/13 23:53:41 millert Exp $     */
1.1       millert     2:
                      3: /****************************************************************************
1.9       millert     4:  * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc.              *
1.1       millert     5:  *                                                                          *
                      6:  * Permission is hereby granted, free of charge, to any person obtaining a  *
                      7:  * copy of this software and associated documentation files (the            *
                      8:  * "Software"), to deal in the Software without restriction, including      *
                      9:  * without limitation the rights to use, copy, modify, merge, publish,      *
                     10:  * distribute, distribute with modifications, sublicense, and/or sell       *
                     11:  * copies of the Software, and to permit persons to whom the Software is    *
                     12:  * furnished to do so, subject to the following conditions:                 *
                     13:  *                                                                          *
                     14:  * The above copyright notice and this permission notice shall be included  *
                     15:  * in all copies or substantial portions of the Software.                   *
                     16:  *                                                                          *
                     17:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
                     18:  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
                     19:  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
                     20:  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
                     21:  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
                     22:  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
                     23:  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
                     24:  *                                                                          *
                     25:  * Except as contained in this notice, the name(s) of the above copyright   *
                     26:  * holders shall not be used in advertising or otherwise to promote the     *
                     27:  * sale, use or other dealings in this Software without prior written       *
                     28:  * authorization.                                                           *
                     29:  ****************************************************************************/
                     30:
                     31: /****************************************************************************
                     32:  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
                     33:  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
                     34:  ****************************************************************************/
                     35:
                     36: /*
                     37:  *     infocmp.c -- decompile an entry, or compare two entries
                     38:  *             written by Eric S. Raymond
                     39:  */
                     40:
                     41: #include <progs.priv.h>
                     42:
                     43: #include <term_entry.h>
                     44: #include <dump_entry.h>
                     45:
1.10    ! millert    46: MODULE_ID("$From: infocmp.c,v 1.54 2000/03/19 02:56:14 tom Exp $")
1.1       millert    47:
                     48: #define L_CURL "{"
                     49: #define R_CURL "}"
                     50:
                     51: #define MAXTERMS       32      /* max # terminal arguments we can handle */
1.9       millert    52: #define MAX_STRING     1024    /* maximum formatted string */
1.1       millert    53:
                     54: const char *_nc_progname = "infocmp";
                     55:
1.8       millert    56: typedef char path[PATH_MAX];
1.1       millert    57:
                     58: /***************************************************************************
                     59:  *
                     60:  * The following control variables, together with the contents of the
                     61:  * terminfo entries, completely determine the actions of the program.
                     62:  *
                     63:  ***************************************************************************/
                     64:
                     65: static char *tname[MAXTERMS];  /* terminal type names */
1.9       millert    66: static ENTRY entries[MAXTERMS];        /* terminfo entries */
1.1       millert    67: static int termcount;          /* count of terminal entries */
                     68:
1.9       millert    69: static bool limited = TRUE;    /* "-r" option is not set */
                     70: static bool quiet = FALSE;
1.10    ! millert    71: static const char *bool_sep = ":";
        !            72: static const char *s_absent = "NULL";
        !            73: static const char *s_cancel = "NULL";
1.1       millert    74: static const char *tversion;   /* terminfo version selected */
1.9       millert    75: static int itrace;             /* trace flag for debugging */
                     76: static int mwidth = 60;
1.3       millert    77: static int numbers = 0;                /* format "%'char'" to/from "%{number}" */
1.9       millert    78: static int outform = F_TERMINFO;/* output format */
1.1       millert    79: static int sortmode;           /* sort_mode */
                     80:
                     81: /* main comparison mode */
                     82: static int compare;
                     83: #define C_DEFAULT      0       /* don't force comparison mode */
                     84: #define C_DIFFERENCE   1       /* list differences between two terminals */
                     85: #define C_COMMON       2       /* list common capabilities */
                     86: #define C_NAND         3       /* list capabilities in neither terminal */
                     87: #define C_USEALL       4       /* generate relative use-form entry */
                     88: static bool ignorepads;                /* ignore pad prefixes when diffing */
                     89:
                     90: #if NO_LEAKS
                     91: #undef ExitProgram
1.8       millert    92: static void
                     93: ExitProgram(int code) GCC_NORETURN;
                     94: /* prototype is to get gcc to accept the noreturn attribute */
                     95: static void
                     96: ExitProgram(int code)
1.1       millert    97: {
1.8       millert    98:     while (termcount-- > 0)
1.9       millert    99:        _nc_free_termtype(&entries[termcount].tterm);
1.8       millert   100:     _nc_leaks_dump_entry();
                    101:     _nc_free_and_exit(code);
1.1       millert   102: }
                    103: #endif
                    104:
1.8       millert   105: static char *
                    106: canonical_name(char *ptr, char *buf)
1.1       millert   107: /* extract the terminal type's primary name */
                    108: {
1.8       millert   109:     char *bp;
1.1       millert   110:
                    111:     (void) strcpy(buf, ptr);
1.9       millert   112:     if ((bp = strchr(buf, '|')) != 0)
1.1       millert   113:        *bp = '\0';
                    114:
1.8       millert   115:     return (buf);
1.1       millert   116: }
                    117:
                    118: /***************************************************************************
                    119:  *
                    120:  * Predicates for dump function
                    121:  *
                    122:  ***************************************************************************/
                    123:
1.8       millert   124: static int
1.9       millert   125: capcmp(int idx, const char *s, const char *t)
1.1       millert   126: /* capability comparison function */
                    127: {
                    128:     if (!VALID_STRING(s) && !VALID_STRING(t))
1.9       millert   129:        return (s != t);
1.1       millert   130:     else if (!VALID_STRING(s) || !VALID_STRING(t))
1.8       millert   131:        return (1);
1.1       millert   132:
1.9       millert   133:     if ((idx == acs_chars_index) || !ignorepads)
                    134:        return (strcmp(s, t));
                    135:     else
1.8       millert   136:        return (_nc_capcmp(s, t));
1.1       millert   137: }
                    138:
1.8       millert   139: static int
                    140: use_predicate(int type, int idx)
1.1       millert   141: /* predicate function to use for use decompilation */
                    142: {
1.9       millert   143:     ENTRY *ep;
1.1       millert   144:
1.8       millert   145:     switch (type) {
1.9       millert   146:     case BOOLEAN:
                    147:        {
1.8       millert   148:            int is_set = FALSE;
1.1       millert   149:
1.8       millert   150:            /*
                    151:             * This assumes that multiple use entries are supposed
                    152:             * to contribute the logical or of their boolean capabilities.
                    153:             * This is true if we take the semantics of multiple uses to
                    154:             * be 'each capability gets the first non-default value found
                    155:             * in the sequence of use entries'.
1.9       millert   156:             *
                    157:             * Note that cancelled or absent booleans are stored as FALSE,
                    158:             * unlike numbers and strings, whose cancelled/absent state is
                    159:             * recorded in the terminfo database.
1.8       millert   160:             */
1.9       millert   161:            for (ep = &entries[1]; ep < entries + termcount; ep++)
                    162:                if (ep->tterm.Booleans[idx] == TRUE) {
                    163:                    is_set = entries[0].tterm.Booleans[idx];
1.8       millert   164:                    break;
1.1       millert   165:                }
1.9       millert   166:            if (is_set != entries[0].tterm.Booleans[idx])
1.8       millert   167:                return (!is_set);
                    168:            else
                    169:                return (FAIL);
                    170:        }
1.1       millert   171:
1.9       millert   172:     case NUMBER:
                    173:        {
1.8       millert   174:            int value = ABSENT_NUMERIC;
1.1       millert   175:
1.8       millert   176:            /*
                    177:             * We take the semantics of multiple uses to be 'each
                    178:             * capability gets the first non-default value found
                    179:             * in the sequence of use entries'.
                    180:             */
1.9       millert   181:            for (ep = &entries[1]; ep < entries + termcount; ep++)
                    182:                if (VALID_NUMERIC(ep->tterm.Numbers[idx])) {
                    183:                    value = ep->tterm.Numbers[idx];
1.8       millert   184:                    break;
1.1       millert   185:                }
                    186:
1.9       millert   187:            if (value != entries[0].tterm.Numbers[idx])
1.8       millert   188:                return (value != ABSENT_NUMERIC);
                    189:            else
                    190:                return (FAIL);
                    191:        }
                    192:
1.9       millert   193:     case STRING:
                    194:        {
1.8       millert   195:            char *termstr, *usestr = ABSENT_STRING;
1.1       millert   196:
1.9       millert   197:            termstr = entries[0].tterm.Strings[idx];
1.1       millert   198:
1.8       millert   199:            /*
                    200:             * We take the semantics of multiple uses to be 'each
                    201:             * capability gets the first non-default value found
                    202:             * in the sequence of use entries'.
                    203:             */
1.9       millert   204:            for (ep = &entries[1]; ep < entries + termcount; ep++)
                    205:                if (ep->tterm.Strings[idx]) {
                    206:                    usestr = ep->tterm.Strings[idx];
1.8       millert   207:                    break;
                    208:                }
                    209:
                    210:            if (usestr == ABSENT_STRING && termstr == ABSENT_STRING)
                    211:                return (FAIL);
1.9       millert   212:            else if (!usestr || !termstr || capcmp(idx, usestr, termstr))
1.8       millert   213:                return (TRUE);
                    214:            else
                    215:                return (FAIL);
1.1       millert   216:        }
1.8       millert   217:     }
1.1       millert   218:
1.8       millert   219:     return (FALSE);            /* pacify compiler */
1.1       millert   220: }
                    221:
1.8       millert   222: static bool
1.9       millert   223: useeq(ENTRY * e1, ENTRY * e2)
                    224: /* are the use references in two entries equivalent? */
                    225: {
                    226:     int i, j;
                    227:
                    228:     if (e1->nuses != e2->nuses)
                    229:        return (FALSE);
                    230:
                    231:     /* Ugh...this is quadratic again */
                    232:     for (i = 0; i < e1->nuses; i++) {
                    233:        bool foundmatch = FALSE;
                    234:
                    235:        /* search second entry for given use reference */
                    236:        for (j = 0; j < e2->nuses; j++)
                    237:            if (!strcmp(e1->uses[i].name, e2->uses[j].name)) {
                    238:                foundmatch = TRUE;
                    239:                break;
                    240:            }
                    241:
                    242:        if (!foundmatch)
                    243:            return (FALSE);
                    244:     }
                    245:
                    246:     return (TRUE);
                    247: }
                    248:
                    249: static bool
1.8       millert   250: entryeq(TERMTYPE * t1, TERMTYPE * t2)
1.9       millert   251: /* are two entries equivalent? */
1.1       millert   252: {
1.8       millert   253:     int i;
1.1       millert   254:
1.2       millert   255:     for (i = 0; i < NUM_BOOLEANS(t1); i++)
1.1       millert   256:        if (t1->Booleans[i] != t2->Booleans[i])
1.8       millert   257:            return (FALSE);
1.1       millert   258:
1.2       millert   259:     for (i = 0; i < NUM_NUMBERS(t1); i++)
1.1       millert   260:        if (t1->Numbers[i] != t2->Numbers[i])
1.8       millert   261:            return (FALSE);
1.1       millert   262:
1.2       millert   263:     for (i = 0; i < NUM_STRINGS(t1); i++)
1.9       millert   264:        if (capcmp(i, t1->Strings[i], t2->Strings[i]))
1.8       millert   265:            return (FALSE);
1.1       millert   266:
1.8       millert   267:     return (TRUE);
1.1       millert   268: }
                    269:
                    270: #define TIC_EXPAND(result) _nc_tic_expand(result, outform==F_TERMINFO, numbers)
                    271:
1.8       millert   272: static void
1.9       millert   273: print_uses(ENTRY * ep, FILE * fp)
                    274: /* print an entry's use references */
                    275: {
                    276:     int i;
                    277:
                    278:     if (!ep->nuses)
                    279:        fputs("NULL", fp);
                    280:     else
                    281:        for (i = 0; i < ep->nuses; i++) {
                    282:            fputs(ep->uses[i].name, fp);
                    283:            if (i < ep->nuses - 1)
                    284:                fputs(" ", fp);
                    285:        }
                    286: }
                    287:
1.10    ! millert   288: static const char *
1.9       millert   289: dump_boolean(int val)
                    290: /* display the value of a boolean capability */
                    291: {
                    292:     switch (val) {
                    293:     case ABSENT_BOOLEAN:
                    294:        return (s_absent);
                    295:     case CANCELLED_BOOLEAN:
                    296:        return (s_cancel);
                    297:     case FALSE:
                    298:        return ("F");
                    299:     case TRUE:
                    300:        return ("T");
                    301:     default:
                    302:        return ("?");
                    303:     }
                    304: }
                    305:
                    306: static void
                    307: dump_numeric(int val, char *buf)
                    308: /* display the value of a boolean capability */
                    309: {
                    310:     switch (val) {
                    311:     case ABSENT_NUMERIC:
                    312:        strcpy(buf, s_absent);
                    313:        break;
                    314:     case CANCELLED_NUMERIC:
                    315:        strcpy(buf, s_cancel);
                    316:        break;
                    317:     default:
                    318:        sprintf(buf, "%d", val);
                    319:        break;
                    320:     }
                    321: }
                    322:
                    323: static void
                    324: dump_string(char *val, char *buf)
                    325: /* display the value of a string capability */
                    326: {
                    327:     if (val == ABSENT_STRING)
                    328:        strcpy(buf, s_absent);
                    329:     else if (val == CANCELLED_STRING)
                    330:        strcpy(buf, s_cancel);
                    331:     else {
                    332:        sprintf(buf, "'%.*s'", MAX_STRING - 3, TIC_EXPAND(val));
                    333:     }
                    334: }
                    335:
                    336: static void
1.8       millert   337: compare_predicate(int type, int idx, const char *name)
1.1       millert   338: /* predicate function to use for entry difference reports */
                    339: {
1.9       millert   340:     register ENTRY *e1 = &entries[0];
                    341:     register ENTRY *e2 = &entries[1];
                    342:     char buf1[MAX_STRING], buf2[MAX_STRING];
                    343:     int b1, b2;
                    344:     int n1, n2;
1.8       millert   345:     char *s1, *s2;
                    346:
                    347:     switch (type) {
1.9       millert   348:     case CMP_BOOLEAN:
                    349:        b1 = e1->tterm.Booleans[idx];
                    350:        b2 = e2->tterm.Booleans[idx];
1.8       millert   351:        switch (compare) {
                    352:        case C_DIFFERENCE:
1.9       millert   353:            if (!(b1 == ABSENT_BOOLEAN && b2 == ABSENT_BOOLEAN) && b1 != b2)
                    354:                (void) printf("\t%s: %s%s%s.\n",
1.8       millert   355:                    name,
1.9       millert   356:                    dump_boolean(b1),
                    357:                    bool_sep,
                    358:                    dump_boolean(b2));
1.8       millert   359:            break;
                    360:
                    361:        case C_COMMON:
1.9       millert   362:            if (b1 == b2 && b1 != ABSENT_BOOLEAN)
                    363:                (void) printf("\t%s= %s.\n", name, dump_boolean(b1));
1.8       millert   364:            break;
                    365:
                    366:        case C_NAND:
1.9       millert   367:            if (b1 == ABSENT_BOOLEAN && b2 == ABSENT_BOOLEAN)
1.8       millert   368:                (void) printf("\t!%s.\n", name);
                    369:            break;
                    370:        }
                    371:        break;
1.1       millert   372:
1.9       millert   373:     case CMP_NUMBER:
                    374:        n1 = e1->tterm.Numbers[idx];
                    375:        n2 = e2->tterm.Numbers[idx];
                    376:        dump_numeric(n1, buf1);
                    377:        dump_numeric(n2, buf2);
1.8       millert   378:        switch (compare) {
                    379:        case C_DIFFERENCE:
1.9       millert   380:            if (!((n1 == ABSENT_NUMERIC && n2 == ABSENT_NUMERIC)) && n1 != n2)
                    381:                (void) printf("\t%s: %s, %s.\n", name, buf1, buf2);
1.8       millert   382:            break;
1.1       millert   383:
1.8       millert   384:        case C_COMMON:
1.9       millert   385:            if (n1 != ABSENT_NUMERIC && n2 != ABSENT_NUMERIC && n1 == n2)
                    386:                (void) printf("\t%s= %s.\n", name, buf1);
1.8       millert   387:            break;
1.1       millert   388:
1.8       millert   389:        case C_NAND:
1.9       millert   390:            if (n1 == ABSENT_NUMERIC && n2 == ABSENT_NUMERIC)
1.8       millert   391:                (void) printf("\t!%s.\n", name);
                    392:            break;
                    393:        }
                    394:        break;
1.1       millert   395:
1.9       millert   396:     case CMP_STRING:
                    397:        s1 = e1->tterm.Strings[idx];
                    398:        s2 = e2->tterm.Strings[idx];
1.8       millert   399:        switch (compare) {
                    400:        case C_DIFFERENCE:
1.9       millert   401:            if (capcmp(idx, s1, s2)) {
                    402:                dump_string(s1, buf1);
                    403:                dump_string(s2, buf2);
1.8       millert   404:                if (strcmp(buf1, buf2))
1.9       millert   405:                    (void) printf("\t%s: %s, %s.\n", name, buf1, buf2);
1.8       millert   406:            }
                    407:            break;
1.1       millert   408:
1.8       millert   409:        case C_COMMON:
1.9       millert   410:            if (s1 && s2 && !capcmp(idx, s1, s2))
1.8       millert   411:                (void) printf("\t%s= '%s'.\n", name, TIC_EXPAND(s1));
                    412:            break;
1.1       millert   413:
1.8       millert   414:        case C_NAND:
                    415:            if (!s1 && !s2)
                    416:                (void) printf("\t!%s.\n", name);
                    417:            break;
1.1       millert   418:        }
1.8       millert   419:        break;
1.9       millert   420:
                    421:     case CMP_USE:
                    422:        /* unlike the other modes, this compares *all* use entries */
                    423:        switch (compare) {
                    424:        case C_DIFFERENCE:
                    425:            if (!useeq(e1, e2)) {
                    426:                (void) fputs("\tuse: ", stdout);
                    427:                print_uses(e1, stdout);
                    428:                fputs(", ", stdout);
                    429:                print_uses(e2, stdout);
                    430:                fputs(".\n", stdout);
                    431:            }
                    432:            break;
                    433:
                    434:        case C_COMMON:
                    435:            if (e1->nuses && e2->nuses && useeq(e1, e2)) {
                    436:                (void) fputs("\tuse: ", stdout);
                    437:                print_uses(e1, stdout);
                    438:                fputs(".\n", stdout);
                    439:            }
                    440:            break;
                    441:
                    442:        case C_NAND:
                    443:            if (!e1->nuses && !e2->nuses)
                    444:                (void) printf("\t!use.\n");
                    445:            break;
                    446:        }
1.8       millert   447:     }
1.1       millert   448: }
                    449:
                    450: /***************************************************************************
                    451:  *
                    452:  * Init string analysis
                    453:  *
                    454:  ***************************************************************************/
                    455:
1.8       millert   456: typedef struct {
                    457:     const char *from;
                    458:     const char *to;
                    459: } assoc;
1.1       millert   460:
                    461: static const assoc std_caps[] =
                    462: {
                    463:     /* these are specified by X.364 and iBCS2 */
1.8       millert   464:     {"\033c", "RIS"},          /* full reset */
                    465:     {"\0337", "SC"},           /* save cursor */
                    466:     {"\0338", "RC"},           /* restore cursor */
                    467:     {"\033[r", "RSR"},         /* not an X.364 mnemonic */
                    468:     {"\033[m", "SGR0"},                /* not an X.364 mnemonic */
                    469:     {"\033[2J", "ED2"},                /* clear page */
1.1       millert   470:
                    471:     /* this group is specified by ISO 2022 */
1.8       millert   472:     {"\033(0", "ISO DEC G0"},  /* enable DEC graphics for G0 */
                    473:     {"\033(A", "ISO UK G0"},   /* enable UK chars for G0 */
                    474:     {"\033(B", "ISO US G0"},   /* enable US chars for G0 */
                    475:     {"\033)0", "ISO DEC G1"},  /* enable DEC graphics for G1 */
                    476:     {"\033)A", "ISO UK G1"},   /* enable UK chars for G1 */
                    477:     {"\033)B", "ISO US G1"},   /* enable US chars for G1 */
1.1       millert   478:
                    479:     /* these are DEC private modes widely supported by emulators */
1.8       millert   480:     {"\033=", "DECPAM"},       /* application keypad mode */
                    481:     {"\033>", "DECPNM"},       /* normal keypad mode */
                    482:     {"\033<", "DECANSI"},      /* enter ANSI mode */
1.1       millert   483:
1.8       millert   484:     {(char *) 0, (char *) 0}
1.1       millert   485: };
                    486:
                    487: static const assoc private_modes[] =
                    488: /* DEC \E[ ... [hl] modes recognized by many emulators */
                    489: {
1.8       millert   490:     {"1", "CKM"},              /* application cursor keys */
                    491:     {"2", "ANM"},              /* set VT52 mode */
                    492:     {"3", "COLM"},             /* 132-column mode */
                    493:     {"4", "SCLM"},             /* smooth scroll */
                    494:     {"5", "SCNM"},             /* reverse video mode */
                    495:     {"6", "OM"},               /* origin mode */
                    496:     {"7", "AWM"},              /* wraparound mode */
                    497:     {"8", "ARM"},              /* auto-repeat mode */
                    498:     {(char *) 0, (char *) 0}
1.1       millert   499: };
                    500:
                    501: static const assoc ecma_highlights[] =
                    502: /* recognize ECMA attribute sequences */
                    503: {
1.8       millert   504:     {"0", "NORMAL"},           /* normal */
                    505:     {"1", "+BOLD"},            /* bold on */
                    506:     {"2", "+DIM"},             /* dim on */
                    507:     {"3", "+ITALIC"},          /* italic on */
                    508:     {"4", "+UNDERLINE"},       /* underline on */
                    509:     {"5", "+BLINK"},           /* blink on */
                    510:     {"6", "+FASTBLINK"},       /* fastblink on */
                    511:     {"7", "+REVERSE"},         /* reverse on */
                    512:     {"8", "+INVISIBLE"},       /* invisible on */
                    513:     {"9", "+DELETED"},         /* deleted on */
                    514:     {"10", "MAIN-FONT"},       /* select primary font */
                    515:     {"11", "ALT-FONT-1"},      /* select alternate font 1 */
                    516:     {"12", "ALT-FONT-2"},      /* select alternate font 2 */
                    517:     {"13", "ALT-FONT-3"},      /* select alternate font 3 */
                    518:     {"14", "ALT-FONT-4"},      /* select alternate font 4 */
                    519:     {"15", "ALT-FONT-5"},      /* select alternate font 5 */
                    520:     {"16", "ALT-FONT-6"},      /* select alternate font 6 */
                    521:     {"17", "ALT-FONT-7"},      /* select alternate font 7 */
                    522:     {"18", "ALT-FONT-1"},      /* select alternate font 1 */
                    523:     {"19", "ALT-FONT-1"},      /* select alternate font 1 */
                    524:     {"20", "FRAKTUR"},         /* Fraktur font */
                    525:     {"21", "DOUBLEUNDER"},     /* double underline */
                    526:     {"22", "-DIM"},            /* dim off */
                    527:     {"23", "-ITALIC"},         /* italic off */
                    528:     {"24", "-UNDERLINE"},      /* underline off */
                    529:     {"25", "-BLINK"},          /* blink off */
                    530:     {"26", "-FASTBLINK"},      /* fastblink off */
                    531:     {"27", "-REVERSE"},                /* reverse off */
                    532:     {"28", "-INVISIBLE"},      /* invisible off */
                    533:     {"29", "-DELETED"},                /* deleted off */
                    534:     {(char *) 0, (char *) 0}
1.1       millert   535: };
                    536:
1.8       millert   537: static void
                    538: analyze_string(const char *name, const char *cap, TERMTYPE * tp)
1.1       millert   539: {
1.8       millert   540:     char buf[MAX_TERMINFO_LENGTH];
                    541:     char buf2[MAX_TERMINFO_LENGTH];
                    542:     const char *sp, *ep;
                    543:     const assoc *ap;
1.1       millert   544:
                    545:     if (cap == ABSENT_STRING || cap == CANCELLED_STRING)
                    546:        return;
                    547:     (void) printf("%s: ", name);
                    548:
                    549:     buf[0] = '\0';
1.8       millert   550:     for (sp = cap; *sp; sp++) {
                    551:        int i;
                    552:        size_t len = 0;
1.1       millert   553:        const char *expansion = 0;
                    554:
                    555:        /* first, check other capabilities in this entry */
1.8       millert   556:        for (i = 0; i < STRCOUNT; i++) {
                    557:            char *cp = tp->Strings[i];
1.1       millert   558:
                    559:            /* don't use soft-key capabilities */
                    560:            if (strnames[i][0] == 'k' && strnames[i][0] == 'f')
                    561:                continue;
                    562:
1.8       millert   563:            if (cp != ABSENT_STRING && cp != CANCELLED_STRING && cp[0] && cp
                    564:                != cap) {
1.1       millert   565:                len = strlen(cp);
                    566:                (void) strncpy(buf2, sp, len);
                    567:                buf2[len] = '\0';
                    568:
                    569:                if (_nc_capcmp(cp, buf2))
                    570:                    continue;
                    571:
                    572: #define ISRS(s)        (!strncmp((s), "is", 2) || !strncmp((s), "rs", 2))
                    573:                /*
                    574:                 * Theoretically we just passed the test for translation
                    575:                 * (equality once the padding is stripped).  However, there
                    576:                 * are a few more hoops that need to be jumped so that
                    577:                 * identical pairs of initialization and reset strings
                    578:                 * don't just refer to each other.
                    579:                 */
                    580:                if (ISRS(name) || ISRS(strnames[i]))
                    581:                    if (cap < cp)
                    582:                        continue;
                    583: #undef ISRS
                    584:
                    585:                expansion = strnames[i];
                    586:                break;
                    587:            }
                    588:        }
                    589:
                    590:        /* now check the standard capabilities */
                    591:        if (!expansion)
1.8       millert   592:            for (ap = std_caps; ap->from; ap++) {
1.1       millert   593:                len = strlen(ap->from);
                    594:
1.8       millert   595:                if (strncmp(ap->from, sp, len) == 0) {
1.1       millert   596:                    expansion = ap->to;
                    597:                    break;
                    598:                }
                    599:            }
                    600:
                    601:        /* now check for private-mode sequences */
                    602:        if (!expansion
1.8       millert   603:            && sp[0] == '\033' && sp[1] == '[' && sp[2] == '?'
                    604:            && (len = strspn(sp + 3, "0123456789;"))
                    605:            && ((sp[3 + len] == 'h') || (sp[3 + len] == 'l'))) {
                    606:            char buf3[MAX_TERMINFO_LENGTH];
1.1       millert   607:
                    608:            (void) strcpy(buf2, (sp[3 + len] == 'h') ? "DEC+" : "DEC-");
                    609:            (void) strncpy(buf3, sp + 3, len);
                    610:            len += 4;
                    611:            buf3[len] = '\0';
                    612:
                    613:            ep = strtok(buf3, ";");
                    614:            do {
1.8       millert   615:                bool found = FALSE;
                    616:
                    617:                for (ap = private_modes; ap->from; ap++) {
                    618:                    size_t tlen = strlen(ap->from);
1.1       millert   619:
1.8       millert   620:                    if (strncmp(ap->from, ep, tlen) == 0) {
                    621:                        (void) strcat(buf2, ap->to);
                    622:                        found = TRUE;
                    623:                        break;
                    624:                    }
                    625:                }
                    626:
                    627:                if (!found)
                    628:                    (void) strcat(buf2, ep);
                    629:                (void) strcat(buf2, ";");
                    630:            } while
1.9       millert   631:                ((ep = strtok((char *) 0, ";")));
1.1       millert   632:            buf2[strlen(buf2) - 1] = '\0';
                    633:            expansion = buf2;
                    634:        }
                    635:
                    636:        /* now check for ECMA highlight sequences */
                    637:        if (!expansion
1.8       millert   638:            && sp[0] == '\033' && sp[1] == '['
                    639:            && (len = strspn(sp + 2, "0123456789;"))
                    640:            && sp[2 + len] == 'm') {
                    641:            char buf3[MAX_TERMINFO_LENGTH];
1.1       millert   642:
                    643:            (void) strcpy(buf2, "SGR:");
                    644:            (void) strncpy(buf3, sp + 2, len);
                    645:            len += 3;
                    646:            buf3[len] = '\0';
                    647:
                    648:            ep = strtok(buf3, ";");
                    649:            do {
1.8       millert   650:                bool found = FALSE;
                    651:
                    652:                for (ap = ecma_highlights; ap->from; ap++) {
                    653:                    size_t tlen = strlen(ap->from);
                    654:
                    655:                    if (strncmp(ap->from, ep, tlen) == 0) {
                    656:                        (void) strcat(buf2, ap->to);
                    657:                        found = TRUE;
                    658:                        break;
                    659:                    }
                    660:                }
1.1       millert   661:
1.8       millert   662:                if (!found)
                    663:                    (void) strcat(buf2, ep);
                    664:                (void) strcat(buf2, ";");
                    665:            } while
1.9       millert   666:                ((ep = strtok((char *) 0, ";")));
1.1       millert   667:
                    668:            buf2[strlen(buf2) - 1] = '\0';
                    669:            expansion = buf2;
                    670:        }
                    671:        /* now check for scroll region reset */
1.8       millert   672:        if (!expansion) {
1.1       millert   673:            (void) sprintf(buf2, "\033[1;%dr", tp->Numbers[2]);
                    674:            len = strlen(buf2);
                    675:            if (strncmp(buf2, sp, len) == 0)
                    676:                expansion = "RSR";
                    677:        }
                    678:
                    679:        /* now check for home-down */
1.8       millert   680:        if (!expansion) {
1.1       millert   681:            (void) sprintf(buf2, "\033[%d;1H", tp->Numbers[2]);
                    682:            len = strlen(buf2);
                    683:            if (strncmp(buf2, sp, len) == 0)
1.8       millert   684:                expansion = "LL";
1.1       millert   685:        }
                    686:
                    687:        /* now look at the expansion we got, if any */
1.8       millert   688:        if (expansion) {
1.1       millert   689:            (void) sprintf(buf + strlen(buf), "{%s}", expansion);
                    690:            sp += len - 1;
                    691:            continue;
1.8       millert   692:        } else {
1.1       millert   693:            /* couldn't match anything */
                    694:            buf2[0] = *sp;
                    695:            buf2[1] = '\0';
                    696:            (void) strcat(buf, TIC_EXPAND(buf2));
                    697:        }
                    698:     }
                    699:     (void) printf("%s\n", buf);
                    700: }
                    701:
                    702: /***************************************************************************
                    703:  *
                    704:  * File comparison
                    705:  *
                    706:  ***************************************************************************/
                    707:
1.8       millert   708: static void
                    709: file_comparison(int argc, char *argv[])
1.1       millert   710: {
                    711: #define MAXCOMPARE     2
                    712:     /* someday we may allow comparisons on more files */
1.8       millert   713:     int filecount = 0;
                    714:     ENTRY *heads[MAXCOMPARE];
                    715:     ENTRY *tails[MAXCOMPARE];
                    716:     ENTRY *qp, *rp;
                    717:     int i, n;
1.1       millert   718:
1.9       millert   719:     dump_init((char *) 0, F_LITERAL, S_TERMINFO, 0, itrace, FALSE);
1.1       millert   720:
1.8       millert   721:     for (n = 0; n < argc && n < MAXCOMPARE; n++) {
1.9       millert   722:        if (freopen(argv[n], "r", stdin) == 0)
1.1       millert   723:            _nc_err_abort("Can't open %s", argv[n]);
                    724:
1.9       millert   725:        _nc_head = _nc_tail = 0;
1.1       millert   726:
                    727:        /* parse entries out of the source file */
                    728:        _nc_set_source(argv[n]);
                    729:        _nc_read_entry_source(stdin, NULL, TRUE, FALSE, NULLHOOK);
                    730:
                    731:        if (itrace)
1.8       millert   732:            (void) fprintf(stderr, "Resolving file %d...\n", n - 0);
1.1       millert   733:
1.9       millert   734:        /* maybe do use resolution */
                    735:        if (!_nc_resolve_uses(!limited)) {
1.1       millert   736:            (void) fprintf(stderr,
1.8       millert   737:                "There are unresolved use entries in %s:\n",
                    738:                argv[n]);
1.9       millert   739:            for_entry_list(qp) {
1.8       millert   740:                if (qp->nuses) {
1.9       millert   741:                    (void) fputs(qp->tterm.term_names, stderr);
                    742:                    (void) fputc('\n', stderr);
                    743:                }
1.8       millert   744:            }
1.1       millert   745:            exit(EXIT_FAILURE);
                    746:        }
                    747:
                    748:        heads[filecount] = _nc_head;
                    749:        tails[filecount] = _nc_tail;
                    750:        filecount++;
                    751:     }
                    752:
                    753:     /* OK, all entries are in core.  Ready to do the comparison */
                    754:     if (itrace)
                    755:        (void) fprintf(stderr, "Entries are now in core...\n");
                    756:
1.9       millert   757:     /* The entry-matching loop. Sigh, this is intrinsically quadratic. */
1.8       millert   758:     for (qp = heads[0]; qp; qp = qp->next) {
1.1       millert   759:        for (rp = heads[1]; rp; rp = rp->next)
1.8       millert   760:            if (_nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) {
1.9       millert   761:                if (qp->ncrosslinks < MAX_CROSSLINKS)
                    762:                    qp->crosslinks[qp->ncrosslinks] = rp;
                    763:                qp->ncrosslinks++;
                    764:
                    765:                if (rp->ncrosslinks < MAX_CROSSLINKS)
                    766:                    rp->crosslinks[rp->ncrosslinks] = qp;
                    767:                rp->ncrosslinks++;
1.1       millert   768:            }
                    769:     }
                    770:
                    771:     /* now we have two circular lists with crosslinks */
                    772:     if (itrace)
                    773:        (void) fprintf(stderr, "Name matches are done...\n");
                    774:
1.9       millert   775:     for (qp = heads[0]; qp; qp = qp->next) {
                    776:        if (qp->ncrosslinks > 1) {
1.1       millert   777:            (void) fprintf(stderr,
1.8       millert   778:                "%s in file 1 (%s) has %d matches in file 2 (%s):\n",
                    779:                _nc_first_name(qp->tterm.term_names),
                    780:                argv[0],
1.9       millert   781:                qp->ncrosslinks,
1.8       millert   782:                argv[1]);
1.9       millert   783:            for (i = 0; i < qp->ncrosslinks; i++)
1.1       millert   784:                (void) fprintf(stderr,
1.8       millert   785:                    "\t%s\n",
1.9       millert   786:                    _nc_first_name((qp->crosslinks[i])->tterm.term_names));
1.1       millert   787:        }
1.9       millert   788:     }
                    789:
                    790:     for (rp = heads[1]; rp; rp = rp->next) {
                    791:        if (rp->ncrosslinks > 1) {
1.1       millert   792:            (void) fprintf(stderr,
1.8       millert   793:                "%s in file 2 (%s) has %d matches in file 1 (%s):\n",
                    794:                _nc_first_name(rp->tterm.term_names),
                    795:                argv[1],
1.9       millert   796:                rp->ncrosslinks,
1.8       millert   797:                argv[0]);
1.9       millert   798:            for (i = 0; i < rp->ncrosslinks; i++)
1.1       millert   799:                (void) fprintf(stderr,
1.8       millert   800:                    "\t%s\n",
1.9       millert   801:                    _nc_first_name((rp->crosslinks[i])->tterm.term_names));
1.1       millert   802:        }
1.9       millert   803:     }
1.1       millert   804:
                    805:     (void) printf("In file 1 (%s) only:\n", argv[0]);
                    806:     for (qp = heads[0]; qp; qp = qp->next)
1.9       millert   807:        if (qp->ncrosslinks == 0)
1.1       millert   808:            (void) printf("\t%s\n",
1.8       millert   809:                _nc_first_name(qp->tterm.term_names));
1.1       millert   810:
                    811:     (void) printf("In file 2 (%s) only:\n", argv[1]);
                    812:     for (rp = heads[1]; rp; rp = rp->next)
1.9       millert   813:        if (rp->ncrosslinks == 0)
1.1       millert   814:            (void) printf("\t%s\n",
1.8       millert   815:                _nc_first_name(rp->tterm.term_names));
1.1       millert   816:
                    817:     (void) printf("The following entries are equivalent:\n");
1.8       millert   818:     for (qp = heads[0]; qp; qp = qp->next) {
1.9       millert   819:        rp = qp->crosslinks[0];
                    820:
                    821:        if (qp->ncrosslinks == 1) {
                    822:            rp = qp->crosslinks[0];
1.1       millert   823:
1.9       millert   824:            repair_acsc(&qp->tterm);
                    825:            repair_acsc(&rp->tterm);
                    826: #if NCURSES_XNAMES
                    827:            _nc_align_termtype(&qp->tterm, &rp->tterm);
                    828: #endif
                    829:            if (entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp)) {
                    830:                char name1[NAMESIZE], name2[NAMESIZE];
1.1       millert   831:
1.9       millert   832:                (void) canonical_name(qp->tterm.term_names, name1);
                    833:                (void) canonical_name(rp->tterm.term_names, name2);
1.1       millert   834:
1.9       millert   835:                (void) printf("%s = %s\n", name1, name2);
                    836:            }
1.1       millert   837:        }
                    838:     }
                    839:
                    840:     (void) printf("Differing entries:\n");
                    841:     termcount = 2;
1.8       millert   842:     for (qp = heads[0]; qp; qp = qp->next) {
1.1       millert   843:
1.9       millert   844:        if (qp->ncrosslinks == 1) {
                    845:            rp = qp->crosslinks[0];
1.2       millert   846: #if NCURSES_XNAMES
1.9       millert   847:            /* sorry - we have to do this on each pass */
1.2       millert   848:            _nc_align_termtype(&qp->tterm, &rp->tterm);
                    849: #endif
1.9       millert   850:            if (!(entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp))) {
                    851:                char name1[NAMESIZE], name2[NAMESIZE];
1.1       millert   852:
1.9       millert   853:                entries[0] = *qp;
                    854:                entries[1] = *rp;
1.1       millert   855:
1.9       millert   856:                (void) canonical_name(qp->tterm.term_names, name1);
                    857:                (void) canonical_name(rp->tterm.term_names, name2);
1.1       millert   858:
1.9       millert   859:                switch (compare) {
                    860:                case C_DIFFERENCE:
                    861:                    if (itrace)
                    862:                        (void) fprintf(stderr,
                    863:                            "infocmp: dumping differences\n");
                    864:                    (void) printf("comparing %s to %s.\n", name1, name2);
                    865:                    compare_entry(compare_predicate, &entries->tterm, quiet);
                    866:                    break;
1.1       millert   867:
1.9       millert   868:                case C_COMMON:
                    869:                    if (itrace)
                    870:                        (void) fprintf(stderr,
                    871:                            "infocmp: dumping common capabilities\n");
                    872:                    (void) printf("comparing %s to %s.\n", name1, name2);
                    873:                    compare_entry(compare_predicate, &entries->tterm, quiet);
                    874:                    break;
1.1       millert   875:
1.9       millert   876:                case C_NAND:
                    877:                    if (itrace)
                    878:                        (void) fprintf(stderr,
                    879:                            "infocmp: dumping differences\n");
                    880:                    (void) printf("comparing %s to %s.\n", name1, name2);
                    881:                    compare_entry(compare_predicate, &entries->tterm, quiet);
                    882:                    break;
1.1       millert   883:
1.9       millert   884:                }
1.1       millert   885:            }
                    886:        }
                    887:     }
                    888: }
                    889:
1.8       millert   890: static void
                    891: usage(void)
1.1       millert   892: {
1.8       millert   893:     static const char *tbl[] =
                    894:     {
                    895:        "Usage: infocmp [options] [-A directory] [-B directory] [termname...]"
                    896:        ,""
                    897:        ,"Options:"
                    898:        ,"  -1    print single-column"
                    899:        ,"  -C    use termcap-names"
                    900:        ,"  -F    compare terminfo-files"
                    901:        ,"  -I    use terminfo-names"
                    902:        ,"  -L    use long names"
                    903:        ,"  -R subset (see manpage)"
                    904:        ,"  -T    eliminate size limits (test)"
                    905:        ,"  -V    print version"
1.10    ! millert   906: #if NCURSES_XNAMES
        !           907:        ,"  -a    with -F, list commented-out caps"
        !           908: #endif
1.8       millert   909:        ,"  -c    list common capabilities"
                    910:        ,"  -d    list different capabilities"
                    911:        ,"  -e    format output for C initializer"
                    912:        ,"  -E    format output as C tables"
                    913:        ,"  -f    with -1, format complex strings"
                    914:        ,"  -G    format %{number} to %'char'"
                    915:        ,"  -g    format %'char' to %{number}"
                    916:        ,"  -i    analyze initialization/reset"
                    917:        ,"  -l    output terminfo names"
                    918:        ,"  -n    list capabilities in neither"
                    919:        ,"  -p    ignore padding specifiers"
1.9       millert   920:        ,"  -q    brief listing, removes headers"
1.8       millert   921:        ,"  -r    with -C, output in termcap form"
1.9       millert   922:        ,"  -r    with -F, resolve use-references"
1.8       millert   923:        ,"  -s [d|i|l|c] sort fields"
                    924:        ,"  -u    produce source with 'use='"
                    925:        ,"  -v number  (verbose)"
                    926:        ,"  -w number  (width)"
                    927:     };
                    928:     const size_t first = 3;
                    929:     const size_t last = sizeof(tbl) / sizeof(tbl[0]);
                    930:     const size_t left = (last - first + 1) / 2 + first;
                    931:     size_t n;
                    932:
                    933:     for (n = 0; n < left; n++) {
                    934:        size_t m = (n < first) ? last : n + left - first;
                    935:        if (m < last)
                    936:            fprintf(stderr, "%-40.40s%s\n", tbl[n], tbl[m]);
                    937:        else
                    938:            fprintf(stderr, "%s\n", tbl[n]);
                    939:     }
                    940:     exit(EXIT_FAILURE);
1.1       millert   941: }
                    942:
1.8       millert   943: static char *
                    944: name_initializer(const char *type)
1.5       millert   945: {
                    946:     static char *initializer;
                    947:     char *s;
                    948:
                    949:     if (initializer == 0)
1.9       millert   950:        initializer = (char *) malloc(strlen(entries->tterm.term_names) + 20);
1.5       millert   951:
1.9       millert   952:     (void) sprintf(initializer, "%s_data_%s", type, entries->tterm.term_names);
1.8       millert   953:     for (s = initializer; *s != 0 && *s != '|'; s++) {
1.5       millert   954:        if (!isalnum(*s))
                    955:            *s = '_';
                    956:     }
                    957:     *s = 0;
                    958:     return initializer;
                    959: }
                    960:
                    961: /* dump C initializers for the terminal type */
1.8       millert   962: static void
1.9       millert   963: dump_initializers(TERMTYPE * term)
1.5       millert   964: {
1.8       millert   965:     int n;
1.5       millert   966:     const char *str = 0;
1.8       millert   967:     int size;
1.5       millert   968:
1.6       millert   969:     (void) printf("static char %s[] = %s\n", name_initializer("bool"), L_CURL);
1.5       millert   970:
1.8       millert   971:     for_each_boolean(n, term) {
                    972:        switch ((int) (term->Booleans[n])) {
1.5       millert   973:        case TRUE:
                    974:            str = "TRUE";
                    975:            break;
                    976:
                    977:        case FALSE:
                    978:            str = "FALSE";
                    979:            break;
                    980:
                    981:        case ABSENT_BOOLEAN:
                    982:            str = "ABSENT_BOOLEAN";
                    983:            break;
                    984:
                    985:        case CANCELLED_BOOLEAN:
                    986:            str = "CANCELLED_BOOLEAN";
                    987:            break;
                    988:        }
                    989:        (void) printf("\t/* %3d: %-8s */\t%s,\n",
1.8       millert   990:            n, ExtBoolname(term, n, boolnames), str);
1.5       millert   991:     }
                    992:     (void) printf("%s;\n", R_CURL);
                    993:
                    994:     (void) printf("static short %s[] = %s\n", name_initializer("number"), L_CURL);
                    995:
1.8       millert   996:     for_each_number(n, term) {
                    997:        char buf[BUFSIZ];
                    998:        switch (term->Numbers[n]) {
1.5       millert   999:        case ABSENT_NUMERIC:
                   1000:            str = "ABSENT_NUMERIC";
                   1001:            break;
                   1002:        case CANCELLED_NUMERIC:
                   1003:            str = "CANCELLED_NUMERIC";
                   1004:            break;
                   1005:        default:
                   1006:            sprintf(buf, "%d", term->Numbers[n]);
                   1007:            str = buf;
                   1008:            break;
                   1009:        }
1.8       millert  1010:        (void) printf("\t/* %3d: %-8s */\t%s,\n", n, ExtNumname(term, n,
                   1011:                numnames), str);
1.5       millert  1012:     }
                   1013:     (void) printf("%s;\n", R_CURL);
                   1014:
                   1015:     size = sizeof(TERMTYPE)
                   1016:        + (NUM_BOOLEANS(term) * sizeof(term->Booleans[0]))
                   1017:        + (NUM_NUMBERS(term) * sizeof(term->Numbers[0]));
                   1018:
                   1019:     (void) printf("static char * %s[] = %s\n", name_initializer("string"), L_CURL);
                   1020:
1.8       millert  1021:     for_each_string(n, term) {
1.9       millert  1022:        char buf[MAX_STRING], *sp, *tp;
1.5       millert  1023:
                   1024:        if (term->Strings[n] == ABSENT_STRING)
                   1025:            str = "ABSENT_STRING";
                   1026:        else if (term->Strings[n] == CANCELLED_STRING)
                   1027:            str = "CANCELLED_STRING";
1.8       millert  1028:        else {
1.5       millert  1029:            tp = buf;
                   1030:            *tp++ = '"';
1.9       millert  1031:            for (sp = term->Strings[n];
                   1032:                *sp != 0 && (tp - buf) < MAX_STRING - 6;
                   1033:                sp++) {
1.8       millert  1034:                if (isascii(*sp) && isprint(*sp) && *sp != '\\' && *sp != '"')
1.5       millert  1035:                    *tp++ = *sp;
1.8       millert  1036:                else {
1.5       millert  1037:                    (void) sprintf(tp, "\\%03o", *sp & 0xff);
                   1038:                    tp += 4;
                   1039:                }
                   1040:            }
                   1041:            *tp++ = '"';
                   1042:            *tp = '\0';
                   1043:            size += (strlen(term->Strings[n]) + 1);
                   1044:            str = buf;
                   1045:        }
                   1046: #if NCURSES_XNAMES
1.8       millert  1047:        if (n == STRCOUNT) {
1.5       millert  1048:            (void) printf("%s;\n", R_CURL);
                   1049:
1.8       millert  1050:            (void) printf("static char * %s[] = %s\n",
                   1051:                name_initializer("string_ext"), L_CURL);
1.5       millert  1052:        }
                   1053: #endif
1.8       millert  1054:        (void) printf("\t/* %3d: %-8s */\t%s,\n", n, ExtStrname(term, n,
                   1055:                strnames), str);
1.5       millert  1056:     }
                   1057:     (void) printf("%s;\n", R_CURL);
                   1058: }
                   1059:
                   1060: /* dump C initializers for the terminal type */
1.8       millert  1061: static void
1.9       millert  1062: dump_termtype(TERMTYPE * term)
1.5       millert  1063: {
                   1064:     (void) printf("\t%s\n\t\t\"%s\",\n", L_CURL, term->term_names);
                   1065:     (void) printf("\t\t(char *)0,\t/* pointer to string table */\n");
                   1066:
                   1067:     (void) printf("\t\t%s,\n", name_initializer("bool"));
                   1068:     (void) printf("\t\t%s,\n", name_initializer("number"));
                   1069:
                   1070:     (void) printf("\t\t%s,\n", name_initializer("string"));
                   1071:
                   1072: #if NCURSES_XNAMES
                   1073:     (void) printf("#if NCURSES_XNAMES\n");
                   1074:     (void) printf("\t\t(char *)0,\t/* pointer to extended string table */\n");
                   1075:     (void) printf("\t\t%s,\t/* ...corresponding names */\n",
                   1076:        (NUM_STRINGS(term) != STRCOUNT)
1.8       millert  1077:        ? name_initializer("string_ext")
                   1078:        : "(char **)0");
1.5       millert  1079:
                   1080:     (void) printf("\t\t%d,\t\t/* count total Booleans */\n", NUM_BOOLEANS(term));
1.8       millert  1081:     (void) printf("\t\t%d,\t\t/* count total Numbers */\n", NUM_NUMBERS(term));
                   1082:     (void) printf("\t\t%d,\t\t/* count total Strings */\n", NUM_STRINGS(term));
1.5       millert  1083:
1.8       millert  1084:     (void) printf("\t\t%d,\t\t/* count extensions to Booleans */\n",
                   1085:        NUM_BOOLEANS(term) - BOOLCOUNT);
                   1086:     (void) printf("\t\t%d,\t\t/* count extensions to Numbers */\n",
                   1087:        NUM_NUMBERS(term) - NUMCOUNT);
                   1088:     (void) printf("\t\t%d,\t\t/* count extensions to Strings */\n",
                   1089:        NUM_STRINGS(term) - STRCOUNT);
1.5       millert  1090:
                   1091:     (void) printf("#endif /* NCURSES_XNAMES */\n");
                   1092: #endif /* NCURSES_XNAMES */
                   1093:     (void) printf("\t%s\n", R_CURL);
                   1094: }
                   1095:
1.1       millert  1096: /***************************************************************************
                   1097:  *
                   1098:  * Main sequence
                   1099:  *
                   1100:  ***************************************************************************/
                   1101:
1.8       millert  1102: int
                   1103: main(int argc, char *argv[])
1.1       millert  1104: {
1.8       millert  1105:     char *terminal, *firstdir, *restdir;
                   1106:     /* Avoid "local data >32k" error with mwcc */
                   1107:     /* Also avoid overflowing smaller stacks on systems like AmigaOS */
1.9       millert  1108:     path *tfile = (path *) malloc(sizeof(path) * MAXTERMS);
1.8       millert  1109:     int c, i, len;
                   1110:     bool formatted = FALSE;
                   1111:     bool filecompare = FALSE;
                   1112:     int initdump = 0;
                   1113:     bool init_analyze = FALSE;
                   1114:
1.9       millert  1115:     if ((terminal = getenv("TERM")) == 0) {
1.8       millert  1116:        (void) fprintf(stderr,
                   1117:            "infocmp: environment variable TERM not set\n");
                   1118:        return EXIT_FAILURE;
                   1119:     }
1.1       millert  1120:
1.8       millert  1121:     /* where is the terminfo database location going to default to? */
                   1122:     restdir = firstdir = 0;
                   1123:
1.10    ! millert  1124:     while ((c = getopt(argc, argv, "adeEcCfFGgIinlLpqrR:s:uv:Vw:A:B:1T")) != EOF)
1.8       millert  1125:        switch (c) {
1.10    ! millert  1126: #if NCURSES_XNAMES
        !          1127:        case 'a':
        !          1128:            _nc_disable_period = TRUE;
        !          1129:            use_extended_names(TRUE);
        !          1130:            break;
        !          1131: #endif
1.8       millert  1132:        case 'd':
                   1133:            compare = C_DIFFERENCE;
                   1134:            break;
1.1       millert  1135:
1.8       millert  1136:        case 'e':
                   1137:            initdump |= 1;
                   1138:            break;
1.1       millert  1139:
1.8       millert  1140:        case 'E':
                   1141:            initdump |= 2;
                   1142:            break;
1.1       millert  1143:
1.8       millert  1144:        case 'c':
                   1145:            compare = C_COMMON;
                   1146:            break;
1.5       millert  1147:
1.8       millert  1148:        case 'C':
                   1149:            outform = F_TERMCAP;
                   1150:            tversion = "BSD";
                   1151:            if (sortmode == S_DEFAULT)
                   1152:                sortmode = S_TERMCAP;
                   1153:            break;
1.1       millert  1154:
1.8       millert  1155:        case 'f':
                   1156:            formatted = TRUE;
                   1157:            break;
1.1       millert  1158:
1.8       millert  1159:        case 'G':
                   1160:            numbers = 1;
                   1161:            break;
1.1       millert  1162:
1.8       millert  1163:        case 'g':
                   1164:            numbers = -1;
                   1165:            break;
1.1       millert  1166:
1.8       millert  1167:        case 'F':
                   1168:            filecompare = TRUE;
                   1169:            break;
1.3       millert  1170:
1.8       millert  1171:        case 'I':
                   1172:            outform = F_TERMINFO;
                   1173:            if (sortmode == S_DEFAULT)
                   1174:                sortmode = S_VARIABLE;
                   1175:            tversion = 0;
                   1176:            break;
1.1       millert  1177:
1.8       millert  1178:        case 'i':
                   1179:            init_analyze = TRUE;
                   1180:            break;
1.1       millert  1181:
1.8       millert  1182:        case 'l':
                   1183:            outform = F_TERMINFO;
                   1184:            break;
1.1       millert  1185:
1.8       millert  1186:        case 'L':
                   1187:            outform = F_VARIABLE;
                   1188:            if (sortmode == S_DEFAULT)
                   1189:                sortmode = S_VARIABLE;
                   1190:            break;
1.1       millert  1191:
1.8       millert  1192:        case 'n':
                   1193:            compare = C_NAND;
                   1194:            break;
1.1       millert  1195:
1.8       millert  1196:        case 'p':
                   1197:            ignorepads = TRUE;
                   1198:            break;
1.1       millert  1199:
1.9       millert  1200:        case 'q':
                   1201:            quiet = TRUE;
                   1202:            s_absent = "-";
                   1203:            s_cancel = "@";
                   1204:            bool_sep = ", ";
                   1205:            break;
                   1206:
1.8       millert  1207:        case 'r':
                   1208:            tversion = 0;
                   1209:            limited = FALSE;
                   1210:            break;
1.1       millert  1211:
1.8       millert  1212:        case 'R':
                   1213:            tversion = optarg;
                   1214:            break;
1.1       millert  1215:
1.8       millert  1216:        case 's':
                   1217:            if (*optarg == 'd')
                   1218:                sortmode = S_NOSORT;
                   1219:            else if (*optarg == 'i')
                   1220:                sortmode = S_TERMINFO;
                   1221:            else if (*optarg == 'l')
                   1222:                sortmode = S_VARIABLE;
                   1223:            else if (*optarg == 'c')
                   1224:                sortmode = S_TERMCAP;
                   1225:            else {
                   1226:                (void) fprintf(stderr,
                   1227:                    "infocmp: unknown sort mode\n");
                   1228:                return EXIT_FAILURE;
                   1229:            }
                   1230:            break;
1.1       millert  1231:
1.8       millert  1232:        case 'u':
                   1233:            compare = C_USEALL;
                   1234:            break;
1.1       millert  1235:
1.8       millert  1236:        case 'v':
                   1237:            itrace = atoi(optarg);
                   1238:            set_trace_level(itrace);
                   1239:            break;
1.1       millert  1240:
1.8       millert  1241:        case 'V':
                   1242:            (void) fputs(NCURSES_VERSION, stdout);
                   1243:            putchar('\n');
                   1244:            ExitProgram(EXIT_SUCCESS);
1.1       millert  1245:
1.8       millert  1246:        case 'w':
                   1247:            mwidth = atoi(optarg);
                   1248:            break;
1.1       millert  1249:
1.8       millert  1250:        case 'A':
                   1251:            firstdir = optarg;
                   1252:            break;
1.1       millert  1253:
1.8       millert  1254:        case 'B':
                   1255:            restdir = optarg;
                   1256:            break;
1.1       millert  1257:
1.8       millert  1258:        case '1':
                   1259:            mwidth = 0;
                   1260:            break;
1.1       millert  1261:
1.8       millert  1262:        case 'T':
                   1263:            limited = FALSE;
                   1264:            break;
                   1265:        default:
                   1266:            usage();
                   1267:        }
1.1       millert  1268:
1.8       millert  1269:     /* by default, sort by terminfo name */
                   1270:     if (sortmode == S_DEFAULT)
                   1271:        sortmode = S_TERMINFO;
1.1       millert  1272:
1.8       millert  1273:     /* set up for display */
                   1274:     dump_init(tversion, outform, sortmode, mwidth, itrace, formatted);
1.1       millert  1275:
1.8       millert  1276:     /* make sure we have at least one terminal name to work with */
                   1277:     if (optind >= argc)
                   1278:        argv[argc++] = terminal;
1.1       millert  1279:
1.8       millert  1280:     /* if user is after a comparison, make sure we have two entries */
                   1281:     if (compare != C_DEFAULT && optind >= argc - 1)
                   1282:        argv[argc++] = terminal;
1.1       millert  1283:
1.8       millert  1284:     /* exactly two terminal names with no options means do -d */
                   1285:     if (argc - optind == 2 && compare == C_DEFAULT)
                   1286:        compare = C_DIFFERENCE;
1.1       millert  1287:
1.8       millert  1288:     if (!filecompare) {
                   1289:        /* grab the entries */
                   1290:        termcount = 0;
                   1291:        for (; optind < argc; optind++) {
                   1292:            if (termcount >= MAXTERMS) {
                   1293:                (void) fprintf(stderr,
                   1294:                    "infocmp: too many terminal type arguments\n");
                   1295:                return EXIT_FAILURE;
                   1296:            } else {
                   1297:                const char *directory = termcount ? restdir : firstdir;
                   1298:                int status;
                   1299:
                   1300:                tname[termcount] = argv[optind];
                   1301:
                   1302:                if (directory) {
                   1303:                    (void) sprintf(tfile[termcount], "%s/%c/%s",
                   1304:                        directory,
                   1305:                        *argv[optind], argv[optind]);
                   1306:                    if (itrace)
                   1307:                        (void) fprintf(stderr,
                   1308:                            "infocmp: reading entry %s from file %s\n",
                   1309:                            argv[optind], tfile[termcount]);
1.1       millert  1310:
1.8       millert  1311:                    status = _nc_read_file_entry(tfile[termcount],
1.9       millert  1312:                        &entries[termcount].tterm);
1.8       millert  1313:                } else {
                   1314:                    if (itrace)
1.1       millert  1315:                        (void) fprintf(stderr,
1.8       millert  1316:                            "infocmp: reading entry %s from system directories %s\n",
                   1317:                            argv[optind], tname[termcount]);
                   1318:
                   1319:                    status = _nc_read_entry(tname[termcount],
                   1320:                        tfile[termcount],
1.9       millert  1321:                        &entries[termcount].tterm);
1.8       millert  1322:                    directory = TERMINFO;       /* for error message */
                   1323:                }
                   1324:
                   1325:                if (status <= 0) {
                   1326:                    (void) fprintf(stderr,
                   1327:                        "infocmp: couldn't open terminfo file %s.\n",
                   1328:                        tfile[termcount]);
                   1329:                    return EXIT_FAILURE;
1.1       millert  1330:                }
1.9       millert  1331:                repair_acsc(&entries[termcount].tterm);
1.8       millert  1332:                termcount++;
1.1       millert  1333:            }
1.8       millert  1334:        }
1.1       millert  1335:
1.2       millert  1336: #if NCURSES_XNAMES
1.8       millert  1337:        if (termcount > 1)
1.9       millert  1338:            _nc_align_termtype(&entries[0].tterm, &entries[1].tterm);
1.2       millert  1339: #endif
                   1340:
1.8       millert  1341:        /* dump as C initializer for the terminal type */
                   1342:        if (initdump) {
                   1343:            if (initdump & 1)
1.9       millert  1344:                dump_termtype(&entries[0].tterm);
1.8       millert  1345:            if (initdump & 2)
1.9       millert  1346:                dump_initializers(&entries[0].tterm);
1.8       millert  1347:            ExitProgram(EXIT_SUCCESS);
                   1348:        }
1.1       millert  1349:
1.8       millert  1350:        /* analyze the init strings */
                   1351:        if (init_analyze) {
1.1       millert  1352: #undef CUR
1.9       millert  1353: #define CUR    entries[0].tterm.
                   1354:            analyze_string("is1", init_1string, &entries[0].tterm);
                   1355:            analyze_string("is2", init_2string, &entries[0].tterm);
                   1356:            analyze_string("is3", init_3string, &entries[0].tterm);
                   1357:            analyze_string("rs1", reset_1string, &entries[0].tterm);
                   1358:            analyze_string("rs2", reset_2string, &entries[0].tterm);
                   1359:            analyze_string("rs3", reset_3string, &entries[0].tterm);
                   1360:            analyze_string("smcup", enter_ca_mode, &entries[0].tterm);
                   1361:            analyze_string("rmcup", exit_ca_mode, &entries[0].tterm);
1.1       millert  1362: #undef CUR
1.8       millert  1363:            ExitProgram(EXIT_SUCCESS);
                   1364:        }
1.1       millert  1365:
1.8       millert  1366:        /*
                   1367:         * Here's where the real work gets done
                   1368:         */
                   1369:        switch (compare) {
                   1370:        case C_DEFAULT:
                   1371:            if (itrace)
                   1372:                (void) fprintf(stderr,
                   1373:                    "infocmp: about to dump %s\n",
                   1374:                    tname[0]);
                   1375:            (void) printf("#\tReconstructed via infocmp from file: %s\n",
                   1376:                tfile[0]);
1.9       millert  1377:            len = dump_entry(&entries[0].tterm, limited, numbers, NULL);
1.8       millert  1378:            putchar('\n');
                   1379:            if (itrace)
                   1380:                (void) fprintf(stderr, "infocmp: length %d\n", len);
                   1381:            break;
1.1       millert  1382:
1.8       millert  1383:        case C_DIFFERENCE:
                   1384:            if (itrace)
                   1385:                (void) fprintf(stderr, "infocmp: dumping differences\n");
                   1386:            (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1.9       millert  1387:            compare_entry(compare_predicate, &entries->tterm, quiet);
1.8       millert  1388:            break;
1.1       millert  1389:
1.8       millert  1390:        case C_COMMON:
                   1391:            if (itrace)
                   1392:                (void) fprintf(stderr,
                   1393:                    "infocmp: dumping common capabilities\n");
                   1394:            (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1.9       millert  1395:            compare_entry(compare_predicate, &entries->tterm, quiet);
1.8       millert  1396:            break;
1.1       millert  1397:
1.8       millert  1398:        case C_NAND:
                   1399:            if (itrace)
                   1400:                (void) fprintf(stderr,
                   1401:                    "infocmp: dumping differences\n");
                   1402:            (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1.9       millert  1403:            compare_entry(compare_predicate, &entries->tterm, quiet);
1.8       millert  1404:            break;
1.1       millert  1405:
1.8       millert  1406:        case C_USEALL:
                   1407:            if (itrace)
                   1408:                (void) fprintf(stderr, "infocmp: dumping use entry\n");
1.9       millert  1409:            len = dump_entry(&entries[0].tterm, limited, numbers, use_predicate);
1.8       millert  1410:            for (i = 1; i < termcount; i++)
                   1411:                len += dump_uses(tname[i], !(outform == F_TERMCAP || outform
                   1412:                        == F_TCONVERR));
                   1413:            putchar('\n');
                   1414:            if (itrace)
                   1415:                (void) fprintf(stderr, "infocmp: length %d\n", len);
                   1416:            break;
1.1       millert  1417:        }
1.8       millert  1418:     } else if (compare == C_USEALL)
                   1419:        (void) fprintf(stderr, "Sorry, -u doesn't work with -F\n");
                   1420:     else if (compare == C_DEFAULT)
                   1421:        (void) fprintf(stderr, "Use `tic -[CI] <file>' for this.\n");
                   1422:     else if (argc - optind != 2)
                   1423:        (void) fprintf(stderr,
                   1424:            "File comparison needs exactly two file arguments.\n");
                   1425:     else
                   1426:        file_comparison(argc - optind, argv + optind);
1.1       millert  1427:
1.8       millert  1428:     ExitProgram(EXIT_SUCCESS);
1.1       millert  1429: }
                   1430:
                   1431: /* infocmp.c ends here */