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

1.18    ! ray         1: /*     $OpenBSD: infocmp.c,v 1.17 2003/04/08 19:08:58 deraadt 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.13      millert    46: MODULE_ID("$From: infocmp.c,v 1.60 2001/02/24 22:03:12 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.11      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 */
1.11      millert    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 *
1.16      deraadt   106: canonical_name(char *ptr, char *buf, size_t bufl)
1.1       millert   107: /* extract the terminal type's primary name */
                    108: {
1.8       millert   109:     char *bp;
1.1       millert   110:
1.16      deraadt   111:     (void) strlcpy(buf, ptr, bufl);
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
1.16      deraadt   307: dump_numeric(int val, char *buf, size_t bufl)
1.9       millert   308: /* display the value of a boolean capability */
                    309: {
                    310:     switch (val) {
                    311:     case ABSENT_NUMERIC:
1.16      deraadt   312:        strlcpy(buf, s_absent, bufl);
1.9       millert   313:        break;
                    314:     case CANCELLED_NUMERIC:
1.16      deraadt   315:        strlcpy(buf, s_cancel, bufl);
1.9       millert   316:        break;
                    317:     default:
1.16      deraadt   318:        snprintf(buf, bufl, "%d", val);
1.9       millert   319:        break;
                    320:     }
                    321: }
                    322:
                    323: static void
1.16      deraadt   324: dump_string(char *val, char *buf, size_t bufl)
1.9       millert   325: /* display the value of a string capability */
                    326: {
                    327:     if (val == ABSENT_STRING)
1.16      deraadt   328:        strlcpy(buf, s_absent, bufl);
1.9       millert   329:     else if (val == CANCELLED_STRING)
1.16      deraadt   330:        strlcpy(buf, s_cancel, bufl);
1.9       millert   331:     else {
1.16      deraadt   332:        snprintf(buf, bufl, "'%.*s'", MAX_STRING - 3, TIC_EXPAND(val));
1.9       millert   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.15      mpech     340:     ENTRY *e1 = &entries[0];
                    341:     ENTRY *e2 = &entries[1];
1.9       millert   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.11      millert   355:                              name,
                    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];
1.16      deraadt   376:        dump_numeric(n1, buf1, sizeof buf1);
                    377:        dump_numeric(n2, buf2, sizeof 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)) {
1.16      deraadt   402:                dump_string(s1, buf1, sizeof buf1);
                    403:                dump_string(s2, buf2, sizeof 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:
1.16      deraadt   608:            (void) strlcpy(buf2, (sp[3 + len] == 'h') ? "DEC+" : "DEC-",
                    609:                sizeof buf2);
1.1       millert   610:            (void) strncpy(buf3, sp + 3, len);
                    611:            len += 4;
                    612:            buf3[len] = '\0';
                    613:
                    614:            ep = strtok(buf3, ";");
                    615:            do {
1.8       millert   616:                bool found = FALSE;
                    617:
                    618:                for (ap = private_modes; ap->from; ap++) {
                    619:                    size_t tlen = strlen(ap->from);
1.1       millert   620:
1.8       millert   621:                    if (strncmp(ap->from, ep, tlen) == 0) {
1.16      deraadt   622:                        (void) strlcat(buf2, ap->to, sizeof buf2);
1.8       millert   623:                        found = TRUE;
                    624:                        break;
                    625:                    }
                    626:                }
                    627:
                    628:                if (!found)
1.16      deraadt   629:                    (void) strlcat(buf2, ep, sizeof buf2);
                    630:                (void) strlcat(buf2, ";", sizeof buf2);
1.8       millert   631:            } while
1.9       millert   632:                ((ep = strtok((char *) 0, ";")));
1.18    ! ray       633:            if (buf2[0] != '\0' && buf2[strlen(buf2) - 1] == ';')
        !           634:                    buf2[strlen(buf2) - 1] = '\0';
1.1       millert   635:            expansion = buf2;
                    636:        }
                    637:
                    638:        /* now check for ECMA highlight sequences */
                    639:        if (!expansion
1.8       millert   640:            && sp[0] == '\033' && sp[1] == '['
                    641:            && (len = strspn(sp + 2, "0123456789;"))
                    642:            && sp[2 + len] == 'm') {
                    643:            char buf3[MAX_TERMINFO_LENGTH];
1.1       millert   644:
1.16      deraadt   645:            (void) strlcpy(buf2, "SGR:", sizeof buf2);
1.1       millert   646:            (void) strncpy(buf3, sp + 2, len);
                    647:            len += 3;
                    648:            buf3[len] = '\0';
                    649:
                    650:            ep = strtok(buf3, ";");
                    651:            do {
1.8       millert   652:                bool found = FALSE;
                    653:
                    654:                for (ap = ecma_highlights; ap->from; ap++) {
                    655:                    size_t tlen = strlen(ap->from);
                    656:
                    657:                    if (strncmp(ap->from, ep, tlen) == 0) {
1.16      deraadt   658:                        (void) strlcat(buf2, ap->to, sizeof buf2);
1.8       millert   659:                        found = TRUE;
                    660:                        break;
                    661:                    }
                    662:                }
1.1       millert   663:
1.8       millert   664:                if (!found)
1.16      deraadt   665:                    (void) strlcat(buf2, ep, sizeof buf2);
                    666:                (void) strlcat(buf2, ";", sizeof buf2);
1.8       millert   667:            } while
1.9       millert   668:                ((ep = strtok((char *) 0, ";")));
1.1       millert   669:
1.18    ! ray       670:            if (buf2[0] != '\0' && buf2[strlen(buf2) - 1] == ';')
        !           671:                    buf2[strlen(buf2) - 1] = '\0';
1.1       millert   672:            expansion = buf2;
                    673:        }
                    674:        /* now check for scroll region reset */
1.8       millert   675:        if (!expansion) {
1.16      deraadt   676:            (void) snprintf(buf2, sizeof buf2, "\033[1;%dr", tp->Numbers[2]);
1.1       millert   677:            len = strlen(buf2);
                    678:            if (strncmp(buf2, sp, len) == 0)
                    679:                expansion = "RSR";
                    680:        }
                    681:
                    682:        /* now check for home-down */
1.8       millert   683:        if (!expansion) {
1.16      deraadt   684:            (void) snprintf(buf2, sizeof buf2, "\033[%d;1H", tp->Numbers[2]);
1.1       millert   685:            len = strlen(buf2);
                    686:            if (strncmp(buf2, sp, len) == 0)
1.8       millert   687:                expansion = "LL";
1.1       millert   688:        }
                    689:
                    690:        /* now look at the expansion we got, if any */
1.8       millert   691:        if (expansion) {
1.17      deraadt   692:            (void) snprintf(buf + strlen(buf), sizeof buf - strlen(buf),
                    693:                "{%s}", expansion);
1.1       millert   694:            sp += len - 1;
                    695:            continue;
1.8       millert   696:        } else {
1.1       millert   697:            /* couldn't match anything */
                    698:            buf2[0] = *sp;
                    699:            buf2[1] = '\0';
1.16      deraadt   700:            (void) strlcat(buf, TIC_EXPAND(buf2), sizeof buf);
1.1       millert   701:        }
                    702:     }
                    703:     (void) printf("%s\n", buf);
                    704: }
                    705:
                    706: /***************************************************************************
                    707:  *
                    708:  * File comparison
                    709:  *
                    710:  ***************************************************************************/
                    711:
1.8       millert   712: static void
                    713: file_comparison(int argc, char *argv[])
1.1       millert   714: {
                    715: #define MAXCOMPARE     2
                    716:     /* someday we may allow comparisons on more files */
1.8       millert   717:     int filecount = 0;
                    718:     ENTRY *heads[MAXCOMPARE];
                    719:     ENTRY *qp, *rp;
                    720:     int i, n;
1.1       millert   721:
1.9       millert   722:     dump_init((char *) 0, F_LITERAL, S_TERMINFO, 0, itrace, FALSE);
1.1       millert   723:
1.8       millert   724:     for (n = 0; n < argc && n < MAXCOMPARE; n++) {
1.9       millert   725:        if (freopen(argv[n], "r", stdin) == 0)
1.1       millert   726:            _nc_err_abort("Can't open %s", argv[n]);
                    727:
1.9       millert   728:        _nc_head = _nc_tail = 0;
1.1       millert   729:
                    730:        /* parse entries out of the source file */
                    731:        _nc_set_source(argv[n]);
                    732:        _nc_read_entry_source(stdin, NULL, TRUE, FALSE, NULLHOOK);
                    733:
                    734:        if (itrace)
1.8       millert   735:            (void) fprintf(stderr, "Resolving file %d...\n", n - 0);
1.1       millert   736:
1.9       millert   737:        /* maybe do use resolution */
                    738:        if (!_nc_resolve_uses(!limited)) {
1.1       millert   739:            (void) fprintf(stderr,
1.11      millert   740:                           "There are unresolved use entries in %s:\n",
                    741:                           argv[n]);
1.9       millert   742:            for_entry_list(qp) {
1.8       millert   743:                if (qp->nuses) {
1.9       millert   744:                    (void) fputs(qp->tterm.term_names, stderr);
                    745:                    (void) fputc('\n', stderr);
                    746:                }
1.8       millert   747:            }
1.1       millert   748:            exit(EXIT_FAILURE);
                    749:        }
                    750:
                    751:        heads[filecount] = _nc_head;
                    752:        filecount++;
                    753:     }
                    754:
                    755:     /* OK, all entries are in core.  Ready to do the comparison */
                    756:     if (itrace)
                    757:        (void) fprintf(stderr, "Entries are now in core...\n");
                    758:
1.9       millert   759:     /* The entry-matching loop. Sigh, this is intrinsically quadratic. */
1.8       millert   760:     for (qp = heads[0]; qp; qp = qp->next) {
1.1       millert   761:        for (rp = heads[1]; rp; rp = rp->next)
1.8       millert   762:            if (_nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) {
1.9       millert   763:                if (qp->ncrosslinks < MAX_CROSSLINKS)
                    764:                    qp->crosslinks[qp->ncrosslinks] = rp;
                    765:                qp->ncrosslinks++;
                    766:
                    767:                if (rp->ncrosslinks < MAX_CROSSLINKS)
                    768:                    rp->crosslinks[rp->ncrosslinks] = qp;
                    769:                rp->ncrosslinks++;
1.1       millert   770:            }
                    771:     }
                    772:
                    773:     /* now we have two circular lists with crosslinks */
                    774:     if (itrace)
                    775:        (void) fprintf(stderr, "Name matches are done...\n");
                    776:
1.9       millert   777:     for (qp = heads[0]; qp; qp = qp->next) {
                    778:        if (qp->ncrosslinks > 1) {
1.1       millert   779:            (void) fprintf(stderr,
1.11      millert   780:                           "%s in file 1 (%s) has %d matches in file 2 (%s):\n",
                    781:                           _nc_first_name(qp->tterm.term_names),
                    782:                           argv[0],
                    783:                           qp->ncrosslinks,
                    784:                           argv[1]);
1.9       millert   785:            for (i = 0; i < qp->ncrosslinks; i++)
1.1       millert   786:                (void) fprintf(stderr,
1.11      millert   787:                               "\t%s\n",
                    788:                               _nc_first_name((qp->crosslinks[i])->tterm.term_names));
1.1       millert   789:        }
1.9       millert   790:     }
                    791:
                    792:     for (rp = heads[1]; rp; rp = rp->next) {
                    793:        if (rp->ncrosslinks > 1) {
1.1       millert   794:            (void) fprintf(stderr,
1.11      millert   795:                           "%s in file 2 (%s) has %d matches in file 1 (%s):\n",
                    796:                           _nc_first_name(rp->tterm.term_names),
                    797:                           argv[1],
                    798:                           rp->ncrosslinks,
                    799:                           argv[0]);
1.9       millert   800:            for (i = 0; i < rp->ncrosslinks; i++)
1.1       millert   801:                (void) fprintf(stderr,
1.11      millert   802:                               "\t%s\n",
                    803:                               _nc_first_name((rp->crosslinks[i])->tterm.term_names));
1.1       millert   804:        }
1.9       millert   805:     }
1.1       millert   806:
                    807:     (void) printf("In file 1 (%s) only:\n", argv[0]);
                    808:     for (qp = heads[0]; qp; qp = qp->next)
1.9       millert   809:        if (qp->ncrosslinks == 0)
1.1       millert   810:            (void) printf("\t%s\n",
1.11      millert   811:                          _nc_first_name(qp->tterm.term_names));
1.1       millert   812:
                    813:     (void) printf("In file 2 (%s) only:\n", argv[1]);
                    814:     for (rp = heads[1]; rp; rp = rp->next)
1.9       millert   815:        if (rp->ncrosslinks == 0)
1.1       millert   816:            (void) printf("\t%s\n",
1.11      millert   817:                          _nc_first_name(rp->tterm.term_names));
1.1       millert   818:
                    819:     (void) printf("The following entries are equivalent:\n");
1.8       millert   820:     for (qp = heads[0]; qp; qp = qp->next) {
1.9       millert   821:        rp = qp->crosslinks[0];
                    822:
                    823:        if (qp->ncrosslinks == 1) {
                    824:            rp = qp->crosslinks[0];
1.1       millert   825:
1.9       millert   826:            repair_acsc(&qp->tterm);
                    827:            repair_acsc(&rp->tterm);
                    828: #if NCURSES_XNAMES
                    829:            _nc_align_termtype(&qp->tterm, &rp->tterm);
                    830: #endif
                    831:            if (entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp)) {
                    832:                char name1[NAMESIZE], name2[NAMESIZE];
1.1       millert   833:
1.16      deraadt   834:                (void) canonical_name(qp->tterm.term_names, name1, sizeof name1);
                    835:                (void) canonical_name(rp->tterm.term_names, name2, sizeof name2);
1.1       millert   836:
1.9       millert   837:                (void) printf("%s = %s\n", name1, name2);
                    838:            }
1.1       millert   839:        }
                    840:     }
                    841:
                    842:     (void) printf("Differing entries:\n");
                    843:     termcount = 2;
1.8       millert   844:     for (qp = heads[0]; qp; qp = qp->next) {
1.1       millert   845:
1.9       millert   846:        if (qp->ncrosslinks == 1) {
                    847:            rp = qp->crosslinks[0];
1.2       millert   848: #if NCURSES_XNAMES
1.9       millert   849:            /* sorry - we have to do this on each pass */
1.2       millert   850:            _nc_align_termtype(&qp->tterm, &rp->tterm);
                    851: #endif
1.9       millert   852:            if (!(entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp))) {
                    853:                char name1[NAMESIZE], name2[NAMESIZE];
1.1       millert   854:
1.9       millert   855:                entries[0] = *qp;
                    856:                entries[1] = *rp;
1.1       millert   857:
1.16      deraadt   858:                (void) canonical_name(qp->tterm.term_names, name1, sizeof name1);
                    859:                (void) canonical_name(rp->tterm.term_names, name2, sizeof name2);
1.1       millert   860:
1.9       millert   861:                switch (compare) {
                    862:                case C_DIFFERENCE:
                    863:                    if (itrace)
                    864:                        (void) fprintf(stderr,
1.11      millert   865:                                       "infocmp: dumping differences\n");
1.9       millert   866:                    (void) printf("comparing %s to %s.\n", name1, name2);
                    867:                    compare_entry(compare_predicate, &entries->tterm, quiet);
                    868:                    break;
1.1       millert   869:
1.9       millert   870:                case C_COMMON:
                    871:                    if (itrace)
                    872:                        (void) fprintf(stderr,
1.11      millert   873:                                       "infocmp: dumping common capabilities\n");
1.9       millert   874:                    (void) printf("comparing %s to %s.\n", name1, name2);
                    875:                    compare_entry(compare_predicate, &entries->tterm, quiet);
                    876:                    break;
1.1       millert   877:
1.9       millert   878:                case C_NAND:
                    879:                    if (itrace)
                    880:                        (void) fprintf(stderr,
1.11      millert   881:                                       "infocmp: dumping differences\n");
1.9       millert   882:                    (void) printf("comparing %s to %s.\n", name1, name2);
                    883:                    compare_entry(compare_predicate, &entries->tterm, quiet);
                    884:                    break;
1.1       millert   885:
1.9       millert   886:                }
1.1       millert   887:            }
                    888:        }
                    889:     }
                    890: }
                    891:
1.8       millert   892: static void
                    893: usage(void)
1.1       millert   894: {
1.8       millert   895:     static const char *tbl[] =
                    896:     {
                    897:        "Usage: infocmp [options] [-A directory] [-B directory] [termname...]"
                    898:        ,""
                    899:        ,"Options:"
                    900:        ,"  -1    print single-column"
                    901:        ,"  -C    use termcap-names"
                    902:        ,"  -F    compare terminfo-files"
                    903:        ,"  -I    use terminfo-names"
                    904:        ,"  -L    use long names"
                    905:        ,"  -R subset (see manpage)"
                    906:        ,"  -T    eliminate size limits (test)"
                    907:        ,"  -V    print version"
1.10      millert   908: #if NCURSES_XNAMES
                    909:        ,"  -a    with -F, list commented-out caps"
                    910: #endif
1.8       millert   911:        ,"  -c    list common capabilities"
                    912:        ,"  -d    list different capabilities"
                    913:        ,"  -e    format output for C initializer"
                    914:        ,"  -E    format output as C tables"
                    915:        ,"  -f    with -1, format complex strings"
                    916:        ,"  -G    format %{number} to %'char'"
                    917:        ,"  -g    format %'char' to %{number}"
                    918:        ,"  -i    analyze initialization/reset"
                    919:        ,"  -l    output terminfo names"
                    920:        ,"  -n    list capabilities in neither"
                    921:        ,"  -p    ignore padding specifiers"
1.9       millert   922:        ,"  -q    brief listing, removes headers"
1.8       millert   923:        ,"  -r    with -C, output in termcap form"
1.9       millert   924:        ,"  -r    with -F, resolve use-references"
1.8       millert   925:        ,"  -s [d|i|l|c] sort fields"
                    926:        ,"  -u    produce source with 'use='"
                    927:        ,"  -v number  (verbose)"
                    928:        ,"  -w number  (width)"
                    929:     };
                    930:     const size_t first = 3;
1.11      millert   931:     const size_t last = SIZEOF(tbl);
1.8       millert   932:     const size_t left = (last - first + 1) / 2 + first;
                    933:     size_t n;
                    934:
                    935:     for (n = 0; n < left; n++) {
                    936:        size_t m = (n < first) ? last : n + left - first;
                    937:        if (m < last)
                    938:            fprintf(stderr, "%-40.40s%s\n", tbl[n], tbl[m]);
                    939:        else
                    940:            fprintf(stderr, "%s\n", tbl[n]);
                    941:     }
                    942:     exit(EXIT_FAILURE);
1.1       millert   943: }
                    944:
1.8       millert   945: static char *
                    946: name_initializer(const char *type)
1.5       millert   947: {
                    948:     static char *initializer;
1.16      deraadt   949:     static size_t len;
1.5       millert   950:     char *s;
                    951:
1.16      deraadt   952:     if (initializer == 0) {
                    953:        len = strlen(entries->tterm.term_names) + 20;
                    954:        initializer = (char *) malloc(len);
                    955:     }
1.5       millert   956:
1.16      deraadt   957:     (void) snprintf(initializer, len, "%s_data_%s", type, entries->tterm.term_names);
1.8       millert   958:     for (s = initializer; *s != 0 && *s != '|'; s++) {
1.12      millert   959:        if (!isalnum(CharOf(*s)))
1.5       millert   960:            *s = '_';
                    961:     }
                    962:     *s = 0;
                    963:     return initializer;
                    964: }
                    965:
                    966: /* dump C initializers for the terminal type */
1.8       millert   967: static void
1.9       millert   968: dump_initializers(TERMTYPE * term)
1.5       millert   969: {
1.8       millert   970:     int n;
1.5       millert   971:     const char *str = 0;
1.8       millert   972:     int size;
1.5       millert   973:
1.6       millert   974:     (void) printf("static char %s[] = %s\n", name_initializer("bool"), L_CURL);
1.5       millert   975:
1.8       millert   976:     for_each_boolean(n, term) {
                    977:        switch ((int) (term->Booleans[n])) {
1.5       millert   978:        case TRUE:
                    979:            str = "TRUE";
                    980:            break;
                    981:
                    982:        case FALSE:
                    983:            str = "FALSE";
                    984:            break;
                    985:
                    986:        case ABSENT_BOOLEAN:
                    987:            str = "ABSENT_BOOLEAN";
                    988:            break;
                    989:
                    990:        case CANCELLED_BOOLEAN:
                    991:            str = "CANCELLED_BOOLEAN";
                    992:            break;
                    993:        }
                    994:        (void) printf("\t/* %3d: %-8s */\t%s,\n",
1.11      millert   995:                      n, ExtBoolname(term, n, boolnames), str);
1.5       millert   996:     }
                    997:     (void) printf("%s;\n", R_CURL);
                    998:
                    999:     (void) printf("static short %s[] = %s\n", name_initializer("number"), L_CURL);
                   1000:
1.8       millert  1001:     for_each_number(n, term) {
                   1002:        char buf[BUFSIZ];
                   1003:        switch (term->Numbers[n]) {
1.5       millert  1004:        case ABSENT_NUMERIC:
                   1005:            str = "ABSENT_NUMERIC";
                   1006:            break;
                   1007:        case CANCELLED_NUMERIC:
                   1008:            str = "CANCELLED_NUMERIC";
                   1009:            break;
                   1010:        default:
1.16      deraadt  1011:            snprintf(buf, sizeof buf, "%d", term->Numbers[n]);
1.5       millert  1012:            str = buf;
                   1013:            break;
                   1014:        }
1.11      millert  1015:        (void) printf("\t/* %3d: %-8s */\t%s,\n", n,
                   1016:                      ExtNumname(term, n, numnames), str);
1.5       millert  1017:     }
                   1018:     (void) printf("%s;\n", R_CURL);
                   1019:
                   1020:     size = sizeof(TERMTYPE)
                   1021:        + (NUM_BOOLEANS(term) * sizeof(term->Booleans[0]))
                   1022:        + (NUM_NUMBERS(term) * sizeof(term->Numbers[0]));
                   1023:
                   1024:     (void) printf("static char * %s[] = %s\n", name_initializer("string"), L_CURL);
                   1025:
1.8       millert  1026:     for_each_string(n, term) {
1.9       millert  1027:        char buf[MAX_STRING], *sp, *tp;
1.5       millert  1028:
                   1029:        if (term->Strings[n] == ABSENT_STRING)
                   1030:            str = "ABSENT_STRING";
                   1031:        else if (term->Strings[n] == CANCELLED_STRING)
                   1032:            str = "CANCELLED_STRING";
1.8       millert  1033:        else {
1.5       millert  1034:            tp = buf;
                   1035:            *tp++ = '"';
1.9       millert  1036:            for (sp = term->Strings[n];
1.11      millert  1037:                 *sp != 0 && (tp - buf) < MAX_STRING - 6;
                   1038:                 sp++) {
1.12      millert  1039:                if (isascii(CharOf(*sp))
                   1040:                    && isprint(CharOf(*sp))
                   1041:                    && *sp != '\\'
                   1042:                    && *sp != '"')
1.5       millert  1043:                    *tp++ = *sp;
1.8       millert  1044:                else {
1.17      deraadt  1045:                    (void) snprintf(tp, buf + sizeof buf - tp, "\\%03o", CharOf(*sp));
1.5       millert  1046:                    tp += 4;
                   1047:                }
                   1048:            }
                   1049:            *tp++ = '"';
                   1050:            *tp = '\0';
                   1051:            size += (strlen(term->Strings[n]) + 1);
                   1052:            str = buf;
                   1053:        }
                   1054: #if NCURSES_XNAMES
1.8       millert  1055:        if (n == STRCOUNT) {
1.5       millert  1056:            (void) printf("%s;\n", R_CURL);
                   1057:
1.8       millert  1058:            (void) printf("static char * %s[] = %s\n",
1.11      millert  1059:                          name_initializer("string_ext"), L_CURL);
1.5       millert  1060:        }
                   1061: #endif
1.11      millert  1062:        (void) printf("\t/* %3d: %-8s */\t%s,\n", n,
                   1063:                      ExtStrname(term, n, strnames), str);
1.5       millert  1064:     }
                   1065:     (void) printf("%s;\n", R_CURL);
                   1066: }
                   1067:
                   1068: /* dump C initializers for the terminal type */
1.8       millert  1069: static void
1.9       millert  1070: dump_termtype(TERMTYPE * term)
1.5       millert  1071: {
                   1072:     (void) printf("\t%s\n\t\t\"%s\",\n", L_CURL, term->term_names);
                   1073:     (void) printf("\t\t(char *)0,\t/* pointer to string table */\n");
                   1074:
                   1075:     (void) printf("\t\t%s,\n", name_initializer("bool"));
                   1076:     (void) printf("\t\t%s,\n", name_initializer("number"));
                   1077:
                   1078:     (void) printf("\t\t%s,\n", name_initializer("string"));
                   1079:
                   1080: #if NCURSES_XNAMES
                   1081:     (void) printf("#if NCURSES_XNAMES\n");
                   1082:     (void) printf("\t\t(char *)0,\t/* pointer to extended string table */\n");
                   1083:     (void) printf("\t\t%s,\t/* ...corresponding names */\n",
1.11      millert  1084:                  (NUM_STRINGS(term) != STRCOUNT)
                   1085:                  ? name_initializer("string_ext")
                   1086:                  : "(char **)0");
1.5       millert  1087:
                   1088:     (void) printf("\t\t%d,\t\t/* count total Booleans */\n", NUM_BOOLEANS(term));
1.8       millert  1089:     (void) printf("\t\t%d,\t\t/* count total Numbers */\n", NUM_NUMBERS(term));
                   1090:     (void) printf("\t\t%d,\t\t/* count total Strings */\n", NUM_STRINGS(term));
1.5       millert  1091:
1.8       millert  1092:     (void) printf("\t\t%d,\t\t/* count extensions to Booleans */\n",
1.11      millert  1093:                  NUM_BOOLEANS(term) - BOOLCOUNT);
1.8       millert  1094:     (void) printf("\t\t%d,\t\t/* count extensions to Numbers */\n",
1.11      millert  1095:                  NUM_NUMBERS(term) - NUMCOUNT);
1.8       millert  1096:     (void) printf("\t\t%d,\t\t/* count extensions to Strings */\n",
1.11      millert  1097:                  NUM_STRINGS(term) - STRCOUNT);
1.5       millert  1098:
                   1099:     (void) printf("#endif /* NCURSES_XNAMES */\n");
                   1100: #endif /* NCURSES_XNAMES */
                   1101:     (void) printf("\t%s\n", R_CURL);
                   1102: }
                   1103:
1.11      millert  1104: static int
                   1105: optarg_to_number(void)
                   1106: {
                   1107:     char *temp = 0;
                   1108:     long value = strtol(optarg, &temp, 0);
                   1109:
                   1110:     if (temp == 0 || temp == optarg || *temp != 0) {
                   1111:        fprintf(stderr, "Expected a number, not \"%s\"\n", optarg);
                   1112:        exit(EXIT_FAILURE);
                   1113:     }
                   1114:     return (int) value;
                   1115: }
                   1116:
1.1       millert  1117: /***************************************************************************
                   1118:  *
                   1119:  * Main sequence
                   1120:  *
                   1121:  ***************************************************************************/
                   1122:
1.8       millert  1123: int
                   1124: main(int argc, char *argv[])
1.1       millert  1125: {
1.8       millert  1126:     char *terminal, *firstdir, *restdir;
                   1127:     /* Avoid "local data >32k" error with mwcc */
                   1128:     /* Also avoid overflowing smaller stacks on systems like AmigaOS */
1.17      deraadt  1129:     size_t tlen = sizeof(path) * MAXTERMS;
                   1130:     path *tfile = (path *) malloc(tlen);
1.8       millert  1131:     int c, i, len;
                   1132:     bool formatted = FALSE;
                   1133:     bool filecompare = FALSE;
                   1134:     int initdump = 0;
                   1135:     bool init_analyze = FALSE;
                   1136:
1.9       millert  1137:     if ((terminal = getenv("TERM")) == 0) {
1.8       millert  1138:        (void) fprintf(stderr,
1.11      millert  1139:                       "infocmp: environment variable TERM not set\n");
1.8       millert  1140:        return EXIT_FAILURE;
                   1141:     }
1.1       millert  1142:
1.8       millert  1143:     /* where is the terminfo database location going to default to? */
                   1144:     restdir = firstdir = 0;
                   1145:
1.14      mpech    1146:     while ((c = getopt(argc, argv, "adeEcCfFGgIinlLpqrR:s:uv:Vw:A:B:1T")) != -1)
1.8       millert  1147:        switch (c) {
1.10      millert  1148: #if NCURSES_XNAMES
                   1149:        case 'a':
                   1150:            _nc_disable_period = TRUE;
                   1151:            use_extended_names(TRUE);
                   1152:            break;
                   1153: #endif
1.8       millert  1154:        case 'd':
                   1155:            compare = C_DIFFERENCE;
                   1156:            break;
1.1       millert  1157:
1.8       millert  1158:        case 'e':
                   1159:            initdump |= 1;
                   1160:            break;
1.1       millert  1161:
1.8       millert  1162:        case 'E':
                   1163:            initdump |= 2;
                   1164:            break;
1.1       millert  1165:
1.8       millert  1166:        case 'c':
                   1167:            compare = C_COMMON;
                   1168:            break;
1.5       millert  1169:
1.8       millert  1170:        case 'C':
                   1171:            outform = F_TERMCAP;
                   1172:            tversion = "BSD";
                   1173:            if (sortmode == S_DEFAULT)
                   1174:                sortmode = S_TERMCAP;
                   1175:            break;
1.1       millert  1176:
1.8       millert  1177:        case 'f':
                   1178:            formatted = TRUE;
                   1179:            break;
1.1       millert  1180:
1.8       millert  1181:        case 'G':
                   1182:            numbers = 1;
                   1183:            break;
1.1       millert  1184:
1.8       millert  1185:        case 'g':
                   1186:            numbers = -1;
                   1187:            break;
1.1       millert  1188:
1.8       millert  1189:        case 'F':
                   1190:            filecompare = TRUE;
                   1191:            break;
1.3       millert  1192:
1.8       millert  1193:        case 'I':
                   1194:            outform = F_TERMINFO;
                   1195:            if (sortmode == S_DEFAULT)
                   1196:                sortmode = S_VARIABLE;
                   1197:            tversion = 0;
                   1198:            break;
1.1       millert  1199:
1.8       millert  1200:        case 'i':
                   1201:            init_analyze = TRUE;
                   1202:            break;
1.1       millert  1203:
1.8       millert  1204:        case 'l':
                   1205:            outform = F_TERMINFO;
                   1206:            break;
1.1       millert  1207:
1.8       millert  1208:        case 'L':
                   1209:            outform = F_VARIABLE;
                   1210:            if (sortmode == S_DEFAULT)
                   1211:                sortmode = S_VARIABLE;
                   1212:            break;
1.1       millert  1213:
1.8       millert  1214:        case 'n':
                   1215:            compare = C_NAND;
                   1216:            break;
1.1       millert  1217:
1.8       millert  1218:        case 'p':
                   1219:            ignorepads = TRUE;
                   1220:            break;
1.1       millert  1221:
1.9       millert  1222:        case 'q':
                   1223:            quiet = TRUE;
                   1224:            s_absent = "-";
                   1225:            s_cancel = "@";
                   1226:            bool_sep = ", ";
                   1227:            break;
                   1228:
1.8       millert  1229:        case 'r':
                   1230:            tversion = 0;
                   1231:            limited = FALSE;
                   1232:            break;
1.1       millert  1233:
1.8       millert  1234:        case 'R':
                   1235:            tversion = optarg;
                   1236:            break;
1.1       millert  1237:
1.8       millert  1238:        case 's':
                   1239:            if (*optarg == 'd')
                   1240:                sortmode = S_NOSORT;
                   1241:            else if (*optarg == 'i')
                   1242:                sortmode = S_TERMINFO;
                   1243:            else if (*optarg == 'l')
                   1244:                sortmode = S_VARIABLE;
                   1245:            else if (*optarg == 'c')
                   1246:                sortmode = S_TERMCAP;
                   1247:            else {
                   1248:                (void) fprintf(stderr,
1.11      millert  1249:                               "infocmp: unknown sort mode\n");
1.8       millert  1250:                return EXIT_FAILURE;
                   1251:            }
                   1252:            break;
1.1       millert  1253:
1.8       millert  1254:        case 'u':
                   1255:            compare = C_USEALL;
                   1256:            break;
1.1       millert  1257:
1.8       millert  1258:        case 'v':
1.11      millert  1259:            itrace = optarg_to_number();
1.8       millert  1260:            set_trace_level(itrace);
                   1261:            break;
1.1       millert  1262:
1.8       millert  1263:        case 'V':
1.11      millert  1264:            puts(curses_version());
1.8       millert  1265:            ExitProgram(EXIT_SUCCESS);
1.1       millert  1266:
1.8       millert  1267:        case 'w':
1.11      millert  1268:            mwidth = optarg_to_number();
1.8       millert  1269:            break;
1.1       millert  1270:
1.8       millert  1271:        case 'A':
                   1272:            firstdir = optarg;
                   1273:            break;
1.1       millert  1274:
1.8       millert  1275:        case 'B':
                   1276:            restdir = optarg;
                   1277:            break;
1.1       millert  1278:
1.8       millert  1279:        case '1':
                   1280:            mwidth = 0;
                   1281:            break;
1.1       millert  1282:
1.8       millert  1283:        case 'T':
                   1284:            limited = FALSE;
                   1285:            break;
                   1286:        default:
                   1287:            usage();
                   1288:        }
1.1       millert  1289:
1.8       millert  1290:     /* by default, sort by terminfo name */
                   1291:     if (sortmode == S_DEFAULT)
                   1292:        sortmode = S_TERMINFO;
1.1       millert  1293:
1.8       millert  1294:     /* set up for display */
                   1295:     dump_init(tversion, outform, sortmode, mwidth, itrace, formatted);
1.1       millert  1296:
1.8       millert  1297:     /* make sure we have at least one terminal name to work with */
                   1298:     if (optind >= argc)
                   1299:        argv[argc++] = terminal;
1.1       millert  1300:
1.8       millert  1301:     /* if user is after a comparison, make sure we have two entries */
                   1302:     if (compare != C_DEFAULT && optind >= argc - 1)
                   1303:        argv[argc++] = terminal;
1.1       millert  1304:
1.8       millert  1305:     /* exactly two terminal names with no options means do -d */
                   1306:     if (argc - optind == 2 && compare == C_DEFAULT)
                   1307:        compare = C_DIFFERENCE;
1.1       millert  1308:
1.8       millert  1309:     if (!filecompare) {
                   1310:        /* grab the entries */
                   1311:        termcount = 0;
                   1312:        for (; optind < argc; optind++) {
                   1313:            if (termcount >= MAXTERMS) {
                   1314:                (void) fprintf(stderr,
1.11      millert  1315:                               "infocmp: too many terminal type arguments\n");
1.8       millert  1316:                return EXIT_FAILURE;
                   1317:            } else {
                   1318:                const char *directory = termcount ? restdir : firstdir;
                   1319:                int status;
                   1320:
                   1321:                tname[termcount] = argv[optind];
                   1322:
                   1323:                if (directory) {
1.17      deraadt  1324:                    (void) snprintf(tfile[termcount],
                   1325:                                    tlen - termcount,
                   1326:                                   "%s/%c/%s",
1.11      millert  1327:                                   directory,
                   1328:                                   *argv[optind], argv[optind]);
1.8       millert  1329:                    if (itrace)
                   1330:                        (void) fprintf(stderr,
1.11      millert  1331:                                       "infocmp: reading entry %s from file %s\n",
                   1332:                                       argv[optind], tfile[termcount]);
1.1       millert  1333:
1.8       millert  1334:                    status = _nc_read_file_entry(tfile[termcount],
1.11      millert  1335:                                                 &entries[termcount].tterm);
1.8       millert  1336:                } else {
                   1337:                    if (itrace)
1.1       millert  1338:                        (void) fprintf(stderr,
1.11      millert  1339:                                       "infocmp: reading entry %s from system directories %s\n",
                   1340:                                       argv[optind], tname[termcount]);
1.8       millert  1341:
                   1342:                    status = _nc_read_entry(tname[termcount],
1.11      millert  1343:                                            tfile[termcount],
                   1344:                                            &entries[termcount].tterm);
1.8       millert  1345:                    directory = TERMINFO;       /* for error message */
                   1346:                }
                   1347:
                   1348:                if (status <= 0) {
                   1349:                    (void) fprintf(stderr,
1.11      millert  1350:                                   "infocmp: couldn't open terminfo file %s.\n",
                   1351:                                   tfile[termcount]);
1.8       millert  1352:                    return EXIT_FAILURE;
1.1       millert  1353:                }
1.9       millert  1354:                repair_acsc(&entries[termcount].tterm);
1.8       millert  1355:                termcount++;
1.1       millert  1356:            }
1.8       millert  1357:        }
1.1       millert  1358:
1.2       millert  1359: #if NCURSES_XNAMES
1.8       millert  1360:        if (termcount > 1)
1.9       millert  1361:            _nc_align_termtype(&entries[0].tterm, &entries[1].tterm);
1.2       millert  1362: #endif
                   1363:
1.8       millert  1364:        /* dump as C initializer for the terminal type */
                   1365:        if (initdump) {
                   1366:            if (initdump & 1)
1.9       millert  1367:                dump_termtype(&entries[0].tterm);
1.8       millert  1368:            if (initdump & 2)
1.9       millert  1369:                dump_initializers(&entries[0].tterm);
1.8       millert  1370:            ExitProgram(EXIT_SUCCESS);
                   1371:        }
1.1       millert  1372:
1.8       millert  1373:        /* analyze the init strings */
                   1374:        if (init_analyze) {
1.1       millert  1375: #undef CUR
1.9       millert  1376: #define CUR    entries[0].tterm.
                   1377:            analyze_string("is1", init_1string, &entries[0].tterm);
                   1378:            analyze_string("is2", init_2string, &entries[0].tterm);
                   1379:            analyze_string("is3", init_3string, &entries[0].tterm);
                   1380:            analyze_string("rs1", reset_1string, &entries[0].tterm);
                   1381:            analyze_string("rs2", reset_2string, &entries[0].tterm);
                   1382:            analyze_string("rs3", reset_3string, &entries[0].tterm);
                   1383:            analyze_string("smcup", enter_ca_mode, &entries[0].tterm);
                   1384:            analyze_string("rmcup", exit_ca_mode, &entries[0].tterm);
1.1       millert  1385: #undef CUR
1.8       millert  1386:            ExitProgram(EXIT_SUCCESS);
                   1387:        }
1.1       millert  1388:
1.8       millert  1389:        /*
                   1390:         * Here's where the real work gets done
                   1391:         */
                   1392:        switch (compare) {
                   1393:        case C_DEFAULT:
                   1394:            if (itrace)
                   1395:                (void) fprintf(stderr,
1.11      millert  1396:                               "infocmp: about to dump %s\n",
                   1397:                               tname[0]);
1.8       millert  1398:            (void) printf("#\tReconstructed via infocmp from file: %s\n",
1.11      millert  1399:                          tfile[0]);
1.9       millert  1400:            len = dump_entry(&entries[0].tterm, limited, numbers, NULL);
1.8       millert  1401:            putchar('\n');
                   1402:            if (itrace)
                   1403:                (void) fprintf(stderr, "infocmp: length %d\n", len);
                   1404:            break;
1.1       millert  1405:
1.8       millert  1406:        case C_DIFFERENCE:
                   1407:            if (itrace)
                   1408:                (void) fprintf(stderr, "infocmp: dumping differences\n");
                   1409:            (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1.9       millert  1410:            compare_entry(compare_predicate, &entries->tterm, quiet);
1.8       millert  1411:            break;
1.1       millert  1412:
1.8       millert  1413:        case C_COMMON:
                   1414:            if (itrace)
                   1415:                (void) fprintf(stderr,
1.11      millert  1416:                               "infocmp: dumping common capabilities\n");
1.8       millert  1417:            (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1.9       millert  1418:            compare_entry(compare_predicate, &entries->tterm, quiet);
1.8       millert  1419:            break;
1.1       millert  1420:
1.8       millert  1421:        case C_NAND:
                   1422:            if (itrace)
                   1423:                (void) fprintf(stderr,
1.11      millert  1424:                               "infocmp: dumping differences\n");
1.8       millert  1425:            (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1.9       millert  1426:            compare_entry(compare_predicate, &entries->tterm, quiet);
1.8       millert  1427:            break;
1.1       millert  1428:
1.8       millert  1429:        case C_USEALL:
                   1430:            if (itrace)
                   1431:                (void) fprintf(stderr, "infocmp: dumping use entry\n");
1.9       millert  1432:            len = dump_entry(&entries[0].tterm, limited, numbers, use_predicate);
1.8       millert  1433:            for (i = 1; i < termcount; i++)
                   1434:                len += dump_uses(tname[i], !(outform == F_TERMCAP || outform
1.11      millert  1435:                                             == F_TCONVERR));
1.8       millert  1436:            putchar('\n');
                   1437:            if (itrace)
                   1438:                (void) fprintf(stderr, "infocmp: length %d\n", len);
                   1439:            break;
1.1       millert  1440:        }
1.8       millert  1441:     } else if (compare == C_USEALL)
                   1442:        (void) fprintf(stderr, "Sorry, -u doesn't work with -F\n");
                   1443:     else if (compare == C_DEFAULT)
                   1444:        (void) fprintf(stderr, "Use `tic -[CI] <file>' for this.\n");
                   1445:     else if (argc - optind != 2)
                   1446:        (void) fprintf(stderr,
1.11      millert  1447:                       "File comparison needs exactly two file arguments.\n");
1.8       millert  1448:     else
                   1449:        file_comparison(argc - optind, argv + optind);
1.1       millert  1450:
1.8       millert  1451:     ExitProgram(EXIT_SUCCESS);
1.1       millert  1452: }
                   1453:
                   1454: /* infocmp.c ends here */