[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.9

1.9     ! millert     1: /*     $OpenBSD: infocmp.c,v 1.8 2000/01/16 01:35:19 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.9     ! millert    46: MODULE_ID("$From: infocmp.c,v 1.52 2000/03/12 02:34:09 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;
        !            71: static char *bool_sep = ":";
        !            72: static char *s_absent = "NULL";
        !            73: static 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:
        !           288: static char *
        !           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"
                    906:        ,"  -c    list common capabilities"
                    907:        ,"  -d    list different capabilities"
                    908:        ,"  -e    format output for C initializer"
                    909:        ,"  -E    format output as C tables"
                    910:        ,"  -f    with -1, format complex strings"
                    911:        ,"  -G    format %{number} to %'char'"
                    912:        ,"  -g    format %'char' to %{number}"
                    913:        ,"  -i    analyze initialization/reset"
                    914:        ,"  -l    output terminfo names"
                    915:        ,"  -n    list capabilities in neither"
                    916:        ,"  -p    ignore padding specifiers"
1.9     ! millert   917:        ,"  -q    brief listing, removes headers"
1.8       millert   918:        ,"  -r    with -C, output in termcap form"
1.9     ! millert   919:        ,"  -r    with -F, resolve use-references"
1.8       millert   920:        ,"  -s [d|i|l|c] sort fields"
                    921:        ,"  -u    produce source with 'use='"
                    922:        ,"  -v number  (verbose)"
                    923:        ,"  -w number  (width)"
                    924:     };
                    925:     const size_t first = 3;
                    926:     const size_t last = sizeof(tbl) / sizeof(tbl[0]);
                    927:     const size_t left = (last - first + 1) / 2 + first;
                    928:     size_t n;
                    929:
                    930:     for (n = 0; n < left; n++) {
                    931:        size_t m = (n < first) ? last : n + left - first;
                    932:        if (m < last)
                    933:            fprintf(stderr, "%-40.40s%s\n", tbl[n], tbl[m]);
                    934:        else
                    935:            fprintf(stderr, "%s\n", tbl[n]);
                    936:     }
                    937:     exit(EXIT_FAILURE);
1.1       millert   938: }
                    939:
1.8       millert   940: static char *
                    941: name_initializer(const char *type)
1.5       millert   942: {
                    943:     static char *initializer;
                    944:     char *s;
                    945:
                    946:     if (initializer == 0)
1.9     ! millert   947:        initializer = (char *) malloc(strlen(entries->tterm.term_names) + 20);
1.5       millert   948:
1.9     ! millert   949:     (void) sprintf(initializer, "%s_data_%s", type, entries->tterm.term_names);
1.8       millert   950:     for (s = initializer; *s != 0 && *s != '|'; s++) {
1.5       millert   951:        if (!isalnum(*s))
                    952:            *s = '_';
                    953:     }
                    954:     *s = 0;
                    955:     return initializer;
                    956: }
                    957:
                    958: /* dump C initializers for the terminal type */
1.8       millert   959: static void
1.9     ! millert   960: dump_initializers(TERMTYPE * term)
1.5       millert   961: {
1.8       millert   962:     int n;
1.5       millert   963:     const char *str = 0;
1.8       millert   964:     int size;
1.5       millert   965:
1.6       millert   966:     (void) printf("static char %s[] = %s\n", name_initializer("bool"), L_CURL);
1.5       millert   967:
1.8       millert   968:     for_each_boolean(n, term) {
                    969:        switch ((int) (term->Booleans[n])) {
1.5       millert   970:        case TRUE:
                    971:            str = "TRUE";
                    972:            break;
                    973:
                    974:        case FALSE:
                    975:            str = "FALSE";
                    976:            break;
                    977:
                    978:        case ABSENT_BOOLEAN:
                    979:            str = "ABSENT_BOOLEAN";
                    980:            break;
                    981:
                    982:        case CANCELLED_BOOLEAN:
                    983:            str = "CANCELLED_BOOLEAN";
                    984:            break;
                    985:        }
                    986:        (void) printf("\t/* %3d: %-8s */\t%s,\n",
1.8       millert   987:            n, ExtBoolname(term, n, boolnames), str);
1.5       millert   988:     }
                    989:     (void) printf("%s;\n", R_CURL);
                    990:
                    991:     (void) printf("static short %s[] = %s\n", name_initializer("number"), L_CURL);
                    992:
1.8       millert   993:     for_each_number(n, term) {
                    994:        char buf[BUFSIZ];
                    995:        switch (term->Numbers[n]) {
1.5       millert   996:        case ABSENT_NUMERIC:
                    997:            str = "ABSENT_NUMERIC";
                    998:            break;
                    999:        case CANCELLED_NUMERIC:
                   1000:            str = "CANCELLED_NUMERIC";
                   1001:            break;
                   1002:        default:
                   1003:            sprintf(buf, "%d", term->Numbers[n]);
                   1004:            str = buf;
                   1005:            break;
                   1006:        }
1.8       millert  1007:        (void) printf("\t/* %3d: %-8s */\t%s,\n", n, ExtNumname(term, n,
                   1008:                numnames), str);
1.5       millert  1009:     }
                   1010:     (void) printf("%s;\n", R_CURL);
                   1011:
                   1012:     size = sizeof(TERMTYPE)
                   1013:        + (NUM_BOOLEANS(term) * sizeof(term->Booleans[0]))
                   1014:        + (NUM_NUMBERS(term) * sizeof(term->Numbers[0]));
                   1015:
                   1016:     (void) printf("static char * %s[] = %s\n", name_initializer("string"), L_CURL);
                   1017:
1.8       millert  1018:     for_each_string(n, term) {
1.9     ! millert  1019:        char buf[MAX_STRING], *sp, *tp;
1.5       millert  1020:
                   1021:        if (term->Strings[n] == ABSENT_STRING)
                   1022:            str = "ABSENT_STRING";
                   1023:        else if (term->Strings[n] == CANCELLED_STRING)
                   1024:            str = "CANCELLED_STRING";
1.8       millert  1025:        else {
1.5       millert  1026:            tp = buf;
                   1027:            *tp++ = '"';
1.9     ! millert  1028:            for (sp = term->Strings[n];
        !          1029:                *sp != 0 && (tp - buf) < MAX_STRING - 6;
        !          1030:                sp++) {
1.8       millert  1031:                if (isascii(*sp) && isprint(*sp) && *sp != '\\' && *sp != '"')
1.5       millert  1032:                    *tp++ = *sp;
1.8       millert  1033:                else {
1.5       millert  1034:                    (void) sprintf(tp, "\\%03o", *sp & 0xff);
                   1035:                    tp += 4;
                   1036:                }
                   1037:            }
                   1038:            *tp++ = '"';
                   1039:            *tp = '\0';
                   1040:            size += (strlen(term->Strings[n]) + 1);
                   1041:            str = buf;
                   1042:        }
                   1043: #if NCURSES_XNAMES
1.8       millert  1044:        if (n == STRCOUNT) {
1.5       millert  1045:            (void) printf("%s;\n", R_CURL);
                   1046:
1.8       millert  1047:            (void) printf("static char * %s[] = %s\n",
                   1048:                name_initializer("string_ext"), L_CURL);
1.5       millert  1049:        }
                   1050: #endif
1.8       millert  1051:        (void) printf("\t/* %3d: %-8s */\t%s,\n", n, ExtStrname(term, n,
                   1052:                strnames), str);
1.5       millert  1053:     }
                   1054:     (void) printf("%s;\n", R_CURL);
                   1055: }
                   1056:
                   1057: /* dump C initializers for the terminal type */
1.8       millert  1058: static void
1.9     ! millert  1059: dump_termtype(TERMTYPE * term)
1.5       millert  1060: {
                   1061:     (void) printf("\t%s\n\t\t\"%s\",\n", L_CURL, term->term_names);
                   1062:     (void) printf("\t\t(char *)0,\t/* pointer to string table */\n");
                   1063:
                   1064:     (void) printf("\t\t%s,\n", name_initializer("bool"));
                   1065:     (void) printf("\t\t%s,\n", name_initializer("number"));
                   1066:
                   1067:     (void) printf("\t\t%s,\n", name_initializer("string"));
                   1068:
                   1069: #if NCURSES_XNAMES
                   1070:     (void) printf("#if NCURSES_XNAMES\n");
                   1071:     (void) printf("\t\t(char *)0,\t/* pointer to extended string table */\n");
                   1072:     (void) printf("\t\t%s,\t/* ...corresponding names */\n",
                   1073:        (NUM_STRINGS(term) != STRCOUNT)
1.8       millert  1074:        ? name_initializer("string_ext")
                   1075:        : "(char **)0");
1.5       millert  1076:
                   1077:     (void) printf("\t\t%d,\t\t/* count total Booleans */\n", NUM_BOOLEANS(term));
1.8       millert  1078:     (void) printf("\t\t%d,\t\t/* count total Numbers */\n", NUM_NUMBERS(term));
                   1079:     (void) printf("\t\t%d,\t\t/* count total Strings */\n", NUM_STRINGS(term));
1.5       millert  1080:
1.8       millert  1081:     (void) printf("\t\t%d,\t\t/* count extensions to Booleans */\n",
                   1082:        NUM_BOOLEANS(term) - BOOLCOUNT);
                   1083:     (void) printf("\t\t%d,\t\t/* count extensions to Numbers */\n",
                   1084:        NUM_NUMBERS(term) - NUMCOUNT);
                   1085:     (void) printf("\t\t%d,\t\t/* count extensions to Strings */\n",
                   1086:        NUM_STRINGS(term) - STRCOUNT);
1.5       millert  1087:
                   1088:     (void) printf("#endif /* NCURSES_XNAMES */\n");
                   1089: #endif /* NCURSES_XNAMES */
                   1090:     (void) printf("\t%s\n", R_CURL);
                   1091: }
                   1092:
1.1       millert  1093: /***************************************************************************
                   1094:  *
                   1095:  * Main sequence
                   1096:  *
                   1097:  ***************************************************************************/
                   1098:
1.8       millert  1099: int
                   1100: main(int argc, char *argv[])
1.1       millert  1101: {
1.8       millert  1102:     char *terminal, *firstdir, *restdir;
                   1103:     /* Avoid "local data >32k" error with mwcc */
                   1104:     /* Also avoid overflowing smaller stacks on systems like AmigaOS */
1.9     ! millert  1105:     path *tfile = (path *) malloc(sizeof(path) * MAXTERMS);
1.8       millert  1106:     int c, i, len;
                   1107:     bool formatted = FALSE;
                   1108:     bool filecompare = FALSE;
                   1109:     int initdump = 0;
                   1110:     bool init_analyze = FALSE;
                   1111:
1.9     ! millert  1112:     if ((terminal = getenv("TERM")) == 0) {
1.8       millert  1113:        (void) fprintf(stderr,
                   1114:            "infocmp: environment variable TERM not set\n");
                   1115:        return EXIT_FAILURE;
                   1116:     }
1.1       millert  1117:
1.8       millert  1118:     /* where is the terminfo database location going to default to? */
                   1119:     restdir = firstdir = 0;
                   1120:
1.9     ! millert  1121:     while ((c = getopt(argc, argv, "deEcCfFGgIinlLpqrR:s:uv:Vw:A:B:1T")) != EOF)
1.8       millert  1122:        switch (c) {
                   1123:        case 'd':
                   1124:            compare = C_DIFFERENCE;
                   1125:            break;
1.1       millert  1126:
1.8       millert  1127:        case 'e':
                   1128:            initdump |= 1;
                   1129:            break;
1.1       millert  1130:
1.8       millert  1131:        case 'E':
                   1132:            initdump |= 2;
                   1133:            break;
1.1       millert  1134:
1.8       millert  1135:        case 'c':
                   1136:            compare = C_COMMON;
                   1137:            break;
1.5       millert  1138:
1.8       millert  1139:        case 'C':
                   1140:            outform = F_TERMCAP;
                   1141:            tversion = "BSD";
                   1142:            if (sortmode == S_DEFAULT)
                   1143:                sortmode = S_TERMCAP;
                   1144:            break;
1.1       millert  1145:
1.8       millert  1146:        case 'f':
                   1147:            formatted = TRUE;
                   1148:            break;
1.1       millert  1149:
1.8       millert  1150:        case 'G':
                   1151:            numbers = 1;
                   1152:            break;
1.1       millert  1153:
1.8       millert  1154:        case 'g':
                   1155:            numbers = -1;
                   1156:            break;
1.1       millert  1157:
1.8       millert  1158:        case 'F':
                   1159:            filecompare = TRUE;
                   1160:            break;
1.3       millert  1161:
1.8       millert  1162:        case 'I':
                   1163:            outform = F_TERMINFO;
                   1164:            if (sortmode == S_DEFAULT)
                   1165:                sortmode = S_VARIABLE;
                   1166:            tversion = 0;
                   1167:            break;
1.1       millert  1168:
1.8       millert  1169:        case 'i':
                   1170:            init_analyze = TRUE;
                   1171:            break;
1.1       millert  1172:
1.8       millert  1173:        case 'l':
                   1174:            outform = F_TERMINFO;
                   1175:            break;
1.1       millert  1176:
1.8       millert  1177:        case 'L':
                   1178:            outform = F_VARIABLE;
                   1179:            if (sortmode == S_DEFAULT)
                   1180:                sortmode = S_VARIABLE;
                   1181:            break;
1.1       millert  1182:
1.8       millert  1183:        case 'n':
                   1184:            compare = C_NAND;
                   1185:            break;
1.1       millert  1186:
1.8       millert  1187:        case 'p':
                   1188:            ignorepads = TRUE;
                   1189:            break;
1.1       millert  1190:
1.9     ! millert  1191:        case 'q':
        !          1192:            quiet = TRUE;
        !          1193:            s_absent = "-";
        !          1194:            s_cancel = "@";
        !          1195:            bool_sep = ", ";
        !          1196:            break;
        !          1197:
1.8       millert  1198:        case 'r':
                   1199:            tversion = 0;
                   1200:            limited = FALSE;
                   1201:            break;
1.1       millert  1202:
1.8       millert  1203:        case 'R':
                   1204:            tversion = optarg;
                   1205:            break;
1.1       millert  1206:
1.8       millert  1207:        case 's':
                   1208:            if (*optarg == 'd')
                   1209:                sortmode = S_NOSORT;
                   1210:            else if (*optarg == 'i')
                   1211:                sortmode = S_TERMINFO;
                   1212:            else if (*optarg == 'l')
                   1213:                sortmode = S_VARIABLE;
                   1214:            else if (*optarg == 'c')
                   1215:                sortmode = S_TERMCAP;
                   1216:            else {
                   1217:                (void) fprintf(stderr,
                   1218:                    "infocmp: unknown sort mode\n");
                   1219:                return EXIT_FAILURE;
                   1220:            }
                   1221:            break;
1.1       millert  1222:
1.8       millert  1223:        case 'u':
                   1224:            compare = C_USEALL;
                   1225:            break;
1.1       millert  1226:
1.8       millert  1227:        case 'v':
                   1228:            itrace = atoi(optarg);
                   1229:            set_trace_level(itrace);
                   1230:            break;
1.1       millert  1231:
1.8       millert  1232:        case 'V':
                   1233:            (void) fputs(NCURSES_VERSION, stdout);
                   1234:            putchar('\n');
                   1235:            ExitProgram(EXIT_SUCCESS);
1.1       millert  1236:
1.8       millert  1237:        case 'w':
                   1238:            mwidth = atoi(optarg);
                   1239:            break;
1.1       millert  1240:
1.8       millert  1241:        case 'A':
                   1242:            firstdir = optarg;
                   1243:            break;
1.1       millert  1244:
1.8       millert  1245:        case 'B':
                   1246:            restdir = optarg;
                   1247:            break;
1.1       millert  1248:
1.8       millert  1249:        case '1':
                   1250:            mwidth = 0;
                   1251:            break;
1.1       millert  1252:
1.8       millert  1253:        case 'T':
                   1254:            limited = FALSE;
                   1255:            break;
                   1256:        default:
                   1257:            usage();
                   1258:        }
1.1       millert  1259:
1.8       millert  1260:     /* by default, sort by terminfo name */
                   1261:     if (sortmode == S_DEFAULT)
                   1262:        sortmode = S_TERMINFO;
1.1       millert  1263:
1.8       millert  1264:     /* set up for display */
                   1265:     dump_init(tversion, outform, sortmode, mwidth, itrace, formatted);
1.1       millert  1266:
1.8       millert  1267:     /* make sure we have at least one terminal name to work with */
                   1268:     if (optind >= argc)
                   1269:        argv[argc++] = terminal;
1.1       millert  1270:
1.8       millert  1271:     /* if user is after a comparison, make sure we have two entries */
                   1272:     if (compare != C_DEFAULT && optind >= argc - 1)
                   1273:        argv[argc++] = terminal;
1.1       millert  1274:
1.8       millert  1275:     /* exactly two terminal names with no options means do -d */
                   1276:     if (argc - optind == 2 && compare == C_DEFAULT)
                   1277:        compare = C_DIFFERENCE;
1.1       millert  1278:
1.8       millert  1279:     if (!filecompare) {
                   1280:        /* grab the entries */
                   1281:        termcount = 0;
                   1282:        for (; optind < argc; optind++) {
                   1283:            if (termcount >= MAXTERMS) {
                   1284:                (void) fprintf(stderr,
                   1285:                    "infocmp: too many terminal type arguments\n");
                   1286:                return EXIT_FAILURE;
                   1287:            } else {
                   1288:                const char *directory = termcount ? restdir : firstdir;
                   1289:                int status;
                   1290:
                   1291:                tname[termcount] = argv[optind];
                   1292:
                   1293:                if (directory) {
                   1294:                    (void) sprintf(tfile[termcount], "%s/%c/%s",
                   1295:                        directory,
                   1296:                        *argv[optind], argv[optind]);
                   1297:                    if (itrace)
                   1298:                        (void) fprintf(stderr,
                   1299:                            "infocmp: reading entry %s from file %s\n",
                   1300:                            argv[optind], tfile[termcount]);
1.1       millert  1301:
1.8       millert  1302:                    status = _nc_read_file_entry(tfile[termcount],
1.9     ! millert  1303:                        &entries[termcount].tterm);
1.8       millert  1304:                } else {
                   1305:                    if (itrace)
1.1       millert  1306:                        (void) fprintf(stderr,
1.8       millert  1307:                            "infocmp: reading entry %s from system directories %s\n",
                   1308:                            argv[optind], tname[termcount]);
                   1309:
                   1310:                    status = _nc_read_entry(tname[termcount],
                   1311:                        tfile[termcount],
1.9     ! millert  1312:                        &entries[termcount].tterm);
1.8       millert  1313:                    directory = TERMINFO;       /* for error message */
                   1314:                }
                   1315:
                   1316:                if (status <= 0) {
                   1317:                    (void) fprintf(stderr,
                   1318:                        "infocmp: couldn't open terminfo file %s.\n",
                   1319:                        tfile[termcount]);
                   1320:                    return EXIT_FAILURE;
1.1       millert  1321:                }
1.9     ! millert  1322:                repair_acsc(&entries[termcount].tterm);
1.8       millert  1323:                termcount++;
1.1       millert  1324:            }
1.8       millert  1325:        }
1.1       millert  1326:
1.2       millert  1327: #if NCURSES_XNAMES
1.8       millert  1328:        if (termcount > 1)
1.9     ! millert  1329:            _nc_align_termtype(&entries[0].tterm, &entries[1].tterm);
1.2       millert  1330: #endif
                   1331:
1.8       millert  1332:        /* dump as C initializer for the terminal type */
                   1333:        if (initdump) {
                   1334:            if (initdump & 1)
1.9     ! millert  1335:                dump_termtype(&entries[0].tterm);
1.8       millert  1336:            if (initdump & 2)
1.9     ! millert  1337:                dump_initializers(&entries[0].tterm);
1.8       millert  1338:            ExitProgram(EXIT_SUCCESS);
                   1339:        }
1.1       millert  1340:
1.8       millert  1341:        /* analyze the init strings */
                   1342:        if (init_analyze) {
1.1       millert  1343: #undef CUR
1.9     ! millert  1344: #define CUR    entries[0].tterm.
        !          1345:            analyze_string("is1", init_1string, &entries[0].tterm);
        !          1346:            analyze_string("is2", init_2string, &entries[0].tterm);
        !          1347:            analyze_string("is3", init_3string, &entries[0].tterm);
        !          1348:            analyze_string("rs1", reset_1string, &entries[0].tterm);
        !          1349:            analyze_string("rs2", reset_2string, &entries[0].tterm);
        !          1350:            analyze_string("rs3", reset_3string, &entries[0].tterm);
        !          1351:            analyze_string("smcup", enter_ca_mode, &entries[0].tterm);
        !          1352:            analyze_string("rmcup", exit_ca_mode, &entries[0].tterm);
1.1       millert  1353: #undef CUR
1.8       millert  1354:            ExitProgram(EXIT_SUCCESS);
                   1355:        }
1.1       millert  1356:
1.8       millert  1357:        /*
                   1358:         * Here's where the real work gets done
                   1359:         */
                   1360:        switch (compare) {
                   1361:        case C_DEFAULT:
                   1362:            if (itrace)
                   1363:                (void) fprintf(stderr,
                   1364:                    "infocmp: about to dump %s\n",
                   1365:                    tname[0]);
                   1366:            (void) printf("#\tReconstructed via infocmp from file: %s\n",
                   1367:                tfile[0]);
1.9     ! millert  1368:            len = dump_entry(&entries[0].tterm, limited, numbers, NULL);
1.8       millert  1369:            putchar('\n');
                   1370:            if (itrace)
                   1371:                (void) fprintf(stderr, "infocmp: length %d\n", len);
                   1372:            break;
1.1       millert  1373:
1.8       millert  1374:        case C_DIFFERENCE:
                   1375:            if (itrace)
                   1376:                (void) fprintf(stderr, "infocmp: dumping differences\n");
                   1377:            (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1.9     ! millert  1378:            compare_entry(compare_predicate, &entries->tterm, quiet);
1.8       millert  1379:            break;
1.1       millert  1380:
1.8       millert  1381:        case C_COMMON:
                   1382:            if (itrace)
                   1383:                (void) fprintf(stderr,
                   1384:                    "infocmp: dumping common capabilities\n");
                   1385:            (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1.9     ! millert  1386:            compare_entry(compare_predicate, &entries->tterm, quiet);
1.8       millert  1387:            break;
1.1       millert  1388:
1.8       millert  1389:        case C_NAND:
                   1390:            if (itrace)
                   1391:                (void) fprintf(stderr,
                   1392:                    "infocmp: dumping differences\n");
                   1393:            (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1.9     ! millert  1394:            compare_entry(compare_predicate, &entries->tterm, quiet);
1.8       millert  1395:            break;
1.1       millert  1396:
1.8       millert  1397:        case C_USEALL:
                   1398:            if (itrace)
                   1399:                (void) fprintf(stderr, "infocmp: dumping use entry\n");
1.9     ! millert  1400:            len = dump_entry(&entries[0].tterm, limited, numbers, use_predicate);
1.8       millert  1401:            for (i = 1; i < termcount; i++)
                   1402:                len += dump_uses(tname[i], !(outform == F_TERMCAP || outform
                   1403:                        == F_TCONVERR));
                   1404:            putchar('\n');
                   1405:            if (itrace)
                   1406:                (void) fprintf(stderr, "infocmp: length %d\n", len);
                   1407:            break;
1.1       millert  1408:        }
1.8       millert  1409:     } else if (compare == C_USEALL)
                   1410:        (void) fprintf(stderr, "Sorry, -u doesn't work with -F\n");
                   1411:     else if (compare == C_DEFAULT)
                   1412:        (void) fprintf(stderr, "Use `tic -[CI] <file>' for this.\n");
                   1413:     else if (argc - optind != 2)
                   1414:        (void) fprintf(stderr,
                   1415:            "File comparison needs exactly two file arguments.\n");
                   1416:     else
                   1417:        file_comparison(argc - optind, argv + optind);
1.1       millert  1418:
1.8       millert  1419:     ExitProgram(EXIT_SUCCESS);
1.1       millert  1420: }
                   1421:
                   1422: /* infocmp.c ends here */