Annotation of src/usr.bin/tic/dump_entry.c, Revision 1.21
1.21 ! nicm 1: /* $OpenBSD: dump_entry.c,v 1.20 2017/05/11 19:13:17 millert Exp $ */
1.2 millert 2:
1.1 millert 3: /****************************************************************************
1.21 ! nicm 4: * Copyright 2018-2022,2023 Thomas E. Dickey *
! 5: * Copyright 1998-2016,2017 Free Software Foundation, Inc. *
1.1 millert 6: * *
7: * Permission is hereby granted, free of charge, to any person obtaining a *
8: * copy of this software and associated documentation files (the *
9: * "Software"), to deal in the Software without restriction, including *
10: * without limitation the rights to use, copy, modify, merge, publish, *
11: * distribute, distribute with modifications, sublicense, and/or sell *
12: * copies of the Software, and to permit persons to whom the Software is *
13: * furnished to do so, subject to the following conditions: *
14: * *
15: * The above copyright notice and this permission notice shall be included *
16: * in all copies or substantial portions of the Software. *
17: * *
18: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
19: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
20: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
21: * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
22: * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
23: * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
24: * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
25: * *
26: * Except as contained in this notice, the name(s) of the above copyright *
27: * holders shall not be used in advertising or otherwise to promote the *
28: * sale, use or other dealings in this Software without prior written *
29: * authorization. *
30: ****************************************************************************/
31:
32: /****************************************************************************
33: * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
34: * and: Eric S. Raymond <esr@snark.thyrsus.com> *
1.19 nicm 35: * and: Thomas E. Dickey 1996 on *
1.1 millert 36: ****************************************************************************/
37:
38: #define __INTERNAL_CAPS_VISIBLE
39: #include <progs.priv.h>
40:
1.21 ! nicm 41: #include <dump_entry.h>
! 42: #include <termsort.h> /* this C file is generated */
1.1 millert 43: #include <parametrized.h> /* so is this */
44:
1.21 ! nicm 45: MODULE_ID("$Id: dump_entry.c,v 1.20 2017/05/11 19:13:17 millert Exp $")
1.11 millert 46:
1.2 millert 47: #define DISCARD(string) string = ABSENT_STRING
1.11 millert 48: #define PRINTF (void) printf
1.21 ! nicm 49: #define WRAPPED 32
1.2 millert 50:
1.19 nicm 51: #define OkIndex(index,array) ((int)(index) >= 0 && (int)(index) < (int) SIZEOF(array))
1.21 ! nicm 52: #define TcOutput() (outform == F_TERMCAP || outform == F_TCONVERR)
1.19 nicm 53:
1.15 millert 54: typedef struct {
55: char *text;
56: size_t used;
57: size_t size;
58: } DYNBUF;
59:
1.11 millert 60: static int tversion; /* terminfo version */
61: static int outform; /* output format to use */
62: static int sortmode; /* sort mode to use */
63: static int width = 60; /* max line width for listings */
1.21 ! nicm 64: static int height = 65535; /* max number of lines for listings */
1.11 millert 65: static int column; /* current column, limited by 'width' */
66: static int oldcol; /* last value of column before wrap */
67: static bool pretty; /* true if we format if-then-else strings */
1.21 ! nicm 68: static bool wrapped; /* true if we wrap too-long strings */
! 69: static bool did_wrap; /* true if last wrap_concat did wrapping */
! 70: static bool checking; /* true if we are checking for tic */
! 71: static int quickdump; /* true if we are dumping compiled data */
1.11 millert 72:
1.19 nicm 73: static char *save_sgr;
74:
1.15 millert 75: static DYNBUF outbuf;
76: static DYNBUF tmpbuf;
1.1 millert 77:
78: /* indirection pointers for implementing sort and display modes */
1.19 nicm 79: static const PredIdx *bool_indirect, *num_indirect, *str_indirect;
1.11 millert 80: static NCURSES_CONST char *const *bool_names;
81: static NCURSES_CONST char *const *num_names;
82: static NCURSES_CONST char *const *str_names;
1.1 millert 83:
1.21 ! nicm 84: static const char *separator = "", *trailer = "";
! 85: static int indent = 8;
1.1 millert 86:
87: /* cover various ports and variants of terminfo */
88: #define V_ALLCAPS 0 /* all capabilities (SVr4, XSI, ncurses) */
89: #define V_SVR1 1 /* SVR1, Ultrix */
1.21 ! nicm 90: #define V_HPUX 2 /* HP-UX */
1.1 millert 91: #define V_AIX 3 /* AIX */
92: #define V_BSD 4 /* BSD */
93:
1.9 millert 94: #if NCURSES_XNAMES
95: #define OBSOLETE(n) (!_nc_user_definable && (n[0] == 'O' && n[1] == 'T'))
96: #else
1.1 millert 97: #define OBSOLETE(n) (n[0] == 'O' && n[1] == 'T')
1.9 millert 98: #endif
99:
1.21 ! nicm 100: #define isObsolete(f,n) ((f == F_TERMINFO || f == F_VARIABLE) && (sortmode != S_VARIABLE) && OBSOLETE(n))
1.1 millert 101:
1.4 millert 102: #if NCURSES_XNAMES
103: #define BoolIndirect(j) ((j >= BOOLCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : bool_indirect[j]))
104: #define NumIndirect(j) ((j >= NUMCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : num_indirect[j]))
105: #define StrIndirect(j) ((j >= STRCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : str_indirect[j]))
106: #else
107: #define BoolIndirect(j) ((sortmode == S_NOSORT) ? (j) : bool_indirect[j])
108: #define NumIndirect(j) ((sortmode == S_NOSORT) ? (j) : num_indirect[j])
109: #define StrIndirect(j) ((sortmode == S_NOSORT) ? (j) : str_indirect[j])
110: #endif
111:
1.21 ! nicm 112: static GCC_NORETURN void
! 113: failed(const char *s)
! 114: {
! 115: perror(s);
! 116: ExitProgram(EXIT_FAILURE);
! 117: }
! 118:
1.15 millert 119: static void
120: strncpy_DYN(DYNBUF * dst, const char *src, size_t need)
121: {
122: size_t want = need + dst->used + 1;
123: if (want > dst->size) {
124: dst->size += (want + 1024); /* be generous */
125: dst->text = typeRealloc(char, dst->size, dst->text);
1.21 ! nicm 126: if (dst->text == 0)
! 127: failed("strncpy_DYN");
1.15 millert 128: }
1.21 ! nicm 129: _nc_STRNCPY(dst->text + dst->used, src, need + 1);
1.15 millert 130: dst->used += need;
131: dst->text[dst->used] = 0;
132: }
133:
134: static void
135: strcpy_DYN(DYNBUF * dst, const char *src)
136: {
137: if (src == 0) {
138: dst->used = 0;
139: strcpy_DYN(dst, "");
140: } else {
141: strncpy_DYN(dst, src, strlen(src));
142: }
143: }
144:
1.1 millert 145: #if NO_LEAKS
1.15 millert 146: static void
147: free_DYN(DYNBUF * p)
148: {
149: if (p->text != 0)
150: free(p->text);
151: p->text = 0;
152: p->size = 0;
153: p->used = 0;
154: }
155:
1.11 millert 156: void
157: _nc_leaks_dump_entry(void)
1.1 millert 158: {
1.15 millert 159: free_DYN(&outbuf);
160: free_DYN(&tmpbuf);
1.1 millert 161: }
162: #endif
163:
1.19 nicm 164: #define NameTrans(check,result) \
1.21 ! nicm 165: if ((np->nte_index <= OK_ ## check) \
1.19 nicm 166: && check[np->nte_index]) \
167: return (result[np->nte_index])
168:
1.10 millert 169: NCURSES_CONST char *
170: nametrans(const char *name)
1.21 ! nicm 171: /* translate a capability name to termcap from terminfo */
1.1 millert 172: {
1.10 millert 173: const struct name_table_entry *np;
1.1 millert 174:
1.21 ! nicm 175: if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0) {
1.10 millert 176: switch (np->nte_type) {
1.1 millert 177: case BOOLEAN:
1.19 nicm 178: NameTrans(bool_from_termcap, boolcodes);
1.1 millert 179: break;
180:
181: case NUMBER:
1.19 nicm 182: NameTrans(num_from_termcap, numcodes);
1.1 millert 183: break;
184:
185: case STRING:
1.19 nicm 186: NameTrans(str_from_termcap, strcodes);
1.1 millert 187: break;
188: }
1.21 ! nicm 189: }
1.1 millert 190:
1.10 millert 191: return (0);
1.1 millert 192: }
193:
1.10 millert 194: void
1.21 ! nicm 195: dump_init(const char *version,
! 196: int mode,
! 197: int sort,
! 198: bool wrap_strings,
! 199: int twidth,
! 200: int theight,
! 201: unsigned traceval,
! 202: bool formatted,
! 203: bool check,
! 204: int quick)
1.1 millert 205: /* set up for entry display */
206: {
207: width = twidth;
1.21 ! nicm 208: height = theight;
1.1 millert 209: pretty = formatted;
1.21 ! nicm 210: wrapped = wrap_strings;
! 211: checking = check;
! 212: quickdump = (quick & 3);
! 213:
! 214: did_wrap = (width <= 0);
1.1 millert 215:
216: /* versions */
217: if (version == 0)
218: tversion = V_ALLCAPS;
219: else if (!strcmp(version, "SVr1") || !strcmp(version, "SVR1")
1.19 nicm 220: || !strcmp(version, "Ultrix"))
1.1 millert 221: tversion = V_SVR1;
222: else if (!strcmp(version, "HP"))
223: tversion = V_HPUX;
224: else if (!strcmp(version, "AIX"))
225: tversion = V_AIX;
226: else if (!strcmp(version, "BSD"))
227: tversion = V_BSD;
228: else
229: tversion = V_ALLCAPS;
230:
231: /* implement display modes */
1.10 millert 232: switch (outform = mode) {
1.1 millert 233: case F_LITERAL:
234: case F_TERMINFO:
235: bool_names = boolnames;
236: num_names = numnames;
237: str_names = strnames;
1.21 ! nicm 238: separator = (twidth > 0 && theight > 1) ? ", " : ",";
1.1 millert 239: trailer = "\n\t";
240: break;
241:
242: case F_VARIABLE:
243: bool_names = boolfnames;
244: num_names = numfnames;
245: str_names = strfnames;
1.21 ! nicm 246: separator = (twidth > 0 && theight > 1) ? ", " : ",";
1.1 millert 247: trailer = "\n\t";
248: break;
249:
250: case F_TERMCAP:
251: case F_TCONVERR:
252: bool_names = boolcodes;
253: num_names = numcodes;
254: str_names = strcodes;
255: separator = ":";
256: trailer = "\\\n\t:";
257: break;
258: }
1.21 ! nicm 259: indent = 8;
1.1 millert 260:
261: /* implement sort modes */
1.10 millert 262: switch (sortmode = sort) {
1.1 millert 263: case S_NOSORT:
264: if (traceval)
265: (void) fprintf(stderr,
1.19 nicm 266: "%s: sorting by term structure order\n", _nc_progname);
1.1 millert 267: break;
268:
269: case S_TERMINFO:
270: if (traceval)
271: (void) fprintf(stderr,
1.19 nicm 272: "%s: sorting by terminfo name order\n", _nc_progname);
1.1 millert 273: bool_indirect = bool_terminfo_sort;
274: num_indirect = num_terminfo_sort;
275: str_indirect = str_terminfo_sort;
276: break;
277:
278: case S_VARIABLE:
279: if (traceval)
280: (void) fprintf(stderr,
1.19 nicm 281: "%s: sorting by C variable order\n", _nc_progname);
1.1 millert 282: bool_indirect = bool_variable_sort;
283: num_indirect = num_variable_sort;
284: str_indirect = str_variable_sort;
285: break;
286:
287: case S_TERMCAP:
288: if (traceval)
289: (void) fprintf(stderr,
1.19 nicm 290: "%s: sorting by termcap name order\n", _nc_progname);
1.1 millert 291: bool_indirect = bool_termcap_sort;
292: num_indirect = num_termcap_sort;
293: str_indirect = str_termcap_sort;
294: break;
295: }
296:
297: if (traceval)
298: (void) fprintf(stderr,
1.19 nicm 299: "%s: width = %d, tversion = %d, outform = %d\n",
300: _nc_progname, width, tversion, outform);
1.1 millert 301: }
302:
1.21 ! nicm 303: static TERMTYPE2 *cur_type;
1.1 millert 304:
1.10 millert 305: static int
1.19 nicm 306: dump_predicate(PredType type, PredIdx idx)
1.1 millert 307: /* predicate function to use for ordinary decompilation */
308: {
1.10 millert 309: switch (type) {
310: case BOOLEAN:
311: return (cur_type->Booleans[idx] == FALSE)
312: ? FAIL : cur_type->Booleans[idx];
1.1 millert 313:
1.10 millert 314: case NUMBER:
315: return (cur_type->Numbers[idx] == ABSENT_NUMERIC)
316: ? FAIL : cur_type->Numbers[idx];
1.1 millert 317:
1.10 millert 318: case STRING:
319: return (cur_type->Strings[idx] != ABSENT_STRING)
320: ? (int) TRUE : FAIL;
321: }
1.1 millert 322:
1.10 millert 323: return (FALSE); /* pacify compiler */
1.1 millert 324: }
325:
1.21 ! nicm 326: static void set_obsolete_termcaps(TERMTYPE2 *tp);
1.1 millert 327:
328: /* is this the index of a function key string? */
1.21 ! nicm 329: #define FNKEY(i) \
! 330: (((i) >= STR_IDX(key_f0) && \
! 331: (i) <= STR_IDX(key_f9)) || \
! 332: ((i) >= STR_IDX(key_f11) && \
! 333: (i) <= STR_IDX(key_f63)))
1.1 millert 334:
1.19 nicm 335: /*
336: * If we configure with a different Caps file, the offsets into the arrays
337: * will change. So we use an address expression.
338: */
339: #define BOOL_IDX(name) (PredType) (&(name) - &(CUR Booleans[0]))
340: #define NUM_IDX(name) (PredType) (&(name) - &(CUR Numbers[0]))
341: #define STR_IDX(name) (PredType) (&(name) - &(CUR Strings[0]))
342:
1.10 millert 343: static bool
1.19 nicm 344: version_filter(PredType type, PredIdx idx)
1.1 millert 345: /* filter out capabilities we may want to suppress */
346: {
1.10 millert 347: switch (tversion) {
348: case V_ALLCAPS: /* SVr4, XSI Curses */
349: return (TRUE);
350:
351: case V_SVR1: /* System V Release 1, Ultrix */
352: switch (type) {
1.1 millert 353: case BOOLEAN:
1.19 nicm 354: return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
1.1 millert 355: case NUMBER:
1.19 nicm 356: return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE);
1.1 millert 357: case STRING:
1.19 nicm 358: return ((idx <= STR_IDX(prtr_non)) ? TRUE : FALSE);
1.1 millert 359: }
360: break;
361:
362: case V_HPUX: /* Hewlett-Packard */
1.10 millert 363: switch (type) {
1.1 millert 364: case BOOLEAN:
1.19 nicm 365: return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
1.1 millert 366: case NUMBER:
1.19 nicm 367: return ((idx <= NUM_IDX(label_width)) ? TRUE : FALSE);
1.1 millert 368: case STRING:
1.19 nicm 369: if (idx <= STR_IDX(prtr_non))
1.10 millert 370: return (TRUE);
1.1 millert 371: else if (FNKEY(idx)) /* function keys */
1.10 millert 372: return (TRUE);
1.19 nicm 373: else if (idx == STR_IDX(plab_norm)
374: || idx == STR_IDX(label_on)
375: || idx == STR_IDX(label_off))
1.10 millert 376: return (TRUE);
1.1 millert 377: else
1.10 millert 378: return (FALSE);
1.1 millert 379: }
380: break;
381:
382: case V_AIX: /* AIX */
1.10 millert 383: switch (type) {
1.1 millert 384: case BOOLEAN:
1.19 nicm 385: return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
1.1 millert 386: case NUMBER:
1.19 nicm 387: return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE);
1.1 millert 388: case STRING:
1.19 nicm 389: if (idx <= STR_IDX(prtr_non))
1.10 millert 390: return (TRUE);
1.1 millert 391: else if (FNKEY(idx)) /* function keys */
1.10 millert 392: return (TRUE);
1.1 millert 393: else
1.10 millert 394: return (FALSE);
1.1 millert 395: }
396: break;
397:
1.19 nicm 398: #define is_termcap(type) (OkIndex(idx, type##_from_termcap) && \
399: type##_from_termcap[idx])
400:
1.1 millert 401: case V_BSD: /* BSD */
1.10 millert 402: switch (type) {
1.1 millert 403: case BOOLEAN:
1.19 nicm 404: return is_termcap(bool);
1.1 millert 405: case NUMBER:
1.19 nicm 406: return is_termcap(num);
1.1 millert 407: case STRING:
1.19 nicm 408: return is_termcap(str);
1.1 millert 409: }
410: break;
411: }
412:
1.10 millert 413: return (FALSE); /* pacify the compiler */
1.1 millert 414: }
415:
1.11 millert 416: static void
1.19 nicm 417: trim_trailing(void)
418: {
419: while (outbuf.used > 0 && outbuf.text[outbuf.used - 1] == ' ')
420: outbuf.text[--outbuf.used] = '\0';
421: }
422:
423: static void
1.10 millert 424: force_wrap(void)
1.1 millert 425: {
1.10 millert 426: oldcol = column;
1.19 nicm 427: trim_trailing();
1.15 millert 428: strcpy_DYN(&outbuf, trailer);
1.21 ! nicm 429: column = indent;
! 430: }
! 431:
! 432: static int
! 433: op_length(const char *src, int offset)
! 434: {
! 435: int result = 0;
! 436:
! 437: if (offset > 0 && src[offset - 1] == '\\') {
! 438: result = 0;
! 439: } else {
! 440: int ch;
! 441:
! 442: result++; /* for '%' mark */
! 443: ch = src[offset + result];
! 444: if (TcOutput()) {
! 445: if (ch == '>') {
! 446: result += 3;
! 447: } else if (ch == '+') {
! 448: result += 2;
! 449: } else {
! 450: result++;
! 451: }
! 452: } else if (ch == '\'') {
! 453: result += 3;
! 454: } else if (ch == L_CURL[0]) {
! 455: int n = result;
! 456: while ((ch = src[offset + n]) != '\0') {
! 457: if (ch == R_CURL[0]) {
! 458: result = ++n;
! 459: break;
! 460: }
! 461: n++;
! 462: }
! 463: } else if (strchr("pPg", ch) != 0) {
! 464: result += 2;
! 465: } else {
! 466: result++; /* ordinary operator */
! 467: }
! 468: }
! 469: return result;
! 470: }
! 471:
! 472: /*
! 473: * When wrapping too-long strings, avoid splitting a backslash sequence, or
! 474: * a terminfo '%' operator. That will leave things a little ragged, but avoids
! 475: * a stray backslash at the end of the line, as well as making the result a
! 476: * little more readable.
! 477: */
! 478: static int
! 479: find_split(const char *src, int step, int size)
! 480: {
! 481: int result = size;
! 482:
! 483: if (size > 0) {
! 484: /* check if that would split a backslash-sequence */
! 485: int mark = size;
! 486: int n;
! 487:
! 488: for (n = size - 1; n > 0; --n) {
! 489: int ch = UChar(src[step + n]);
! 490: if (ch == '\\') {
! 491: if (n > 0 && src[step + n - 1] == ch)
! 492: --n;
! 493: mark = n;
! 494: break;
! 495: } else if (!isalnum(ch)) {
! 496: break;
! 497: }
! 498: }
! 499: if (mark < size) {
! 500: result = mark;
! 501: } else {
! 502: /* check if that would split a backslash-sequence */
! 503: for (n = size - 1; n > 0; --n) {
! 504: int ch = UChar(src[step + n]);
! 505: if (ch == '%') {
! 506: int need = op_length(src, step + n);
! 507: if ((n + need) > size) {
! 508: mark = n;
! 509: }
! 510: break;
! 511: }
! 512: }
! 513: if (mark < size) {
! 514: result = mark;
! 515: }
! 516: }
! 517: }
! 518: return result;
1.1 millert 519: }
520:
1.21 ! nicm 521: /*
! 522: * If we are going to wrap lines, we cannot leave literal spaces because that
! 523: * would be ambiguous if we split on that space.
! 524: */
! 525: static char *
! 526: fill_spaces(const char *src)
! 527: {
! 528: const char *fill = "\\s";
! 529: size_t need = strlen(src);
! 530: size_t size = strlen(fill);
! 531: char *result = 0;
! 532: int pass;
! 533: size_t s, d;
! 534: for (pass = 0; pass < 2; ++pass) {
! 535: for (s = d = 0; src[s] != '\0'; ++s) {
! 536: if (src[s] == ' ') {
! 537: if (pass) {
! 538: _nc_STRCPY(&result[d], fill, need + 1 - d);
! 539: d += size;
! 540: } else {
! 541: need += size;
! 542: }
! 543: } else {
! 544: if (pass) {
! 545: result[d++] = src[s];
! 546: } else {
! 547: ++d;
! 548: }
! 549: }
! 550: }
! 551: if (pass) {
! 552: result[d] = '\0';
! 553: } else {
! 554: result = calloc(need + 1, sizeof(char));
! 555: if (result == 0)
! 556: failed("fill_spaces");
! 557: }
! 558: }
! 559: return result;
! 560: }
! 561:
! 562: typedef enum {
! 563: wOFF = 0
! 564: ,w1ST = 1
! 565: ,w2ND = 2
! 566: ,wEND = 4
! 567: ,wERR = 8
! 568: } WRAPMODE;
! 569:
! 570: #define wrap_1ST(mode) ((mode)&w1ST)
! 571: #define wrap_END(mode) ((mode)&wEND)
! 572: #define wrap_ERR(mode) ((mode)&wERR)
! 573:
1.11 millert 574: static void
1.21 ! nicm 575: wrap_concat(const char *src, int need, unsigned mode)
1.1 millert 576: {
1.21 ! nicm 577: int gaps = (int) strlen(separator);
! 578: int want = gaps + need;
1.1 millert 579:
1.21 ! nicm 580: did_wrap = (width <= 0);
! 581: if (wrap_1ST(mode)
! 582: && column > indent
! 583: && column + want > width) {
1.10 millert 584: force_wrap();
585: }
1.21 ! nicm 586: if ((wrap_END(mode) && !wrap_ERR(mode)) &&
! 587: wrapped &&
! 588: (width >= 0) &&
! 589: (column + want) > width) {
! 590: int step = 0;
! 591: int used = width > WRAPPED ? width : WRAPPED;
! 592: int base = 0;
! 593: char *p, align[9];
! 594: const char *my_t = trailer;
! 595: char *fill = fill_spaces(src);
! 596: int last = (int) strlen(fill);
! 597:
! 598: need = last;
! 599:
! 600: if (TcOutput())
! 601: trailer = "\\\n\t ";
! 602:
! 603: if (!TcOutput() && (p = strchr(fill, '=')) != 0) {
! 604: base = (int) (p + 1 - fill);
! 605: if (base > 8)
! 606: base = 8;
! 607: _nc_SPRINTF(align, _nc_SLIMIT(align) "%*s", base, " ");
! 608: } else if (column > 8) {
! 609: base = column - 8;
! 610: if (base > 8)
! 611: base = 8;
! 612: _nc_SPRINTF(align, _nc_SLIMIT(align) "%*s", base, " ");
! 613: } else {
! 614: align[base] = '\0';
! 615: }
! 616: /* "pretty" overrides wrapping if it already split the line */
! 617: if (!pretty || strchr(fill, '\n') == 0) {
! 618: int tag = 0;
! 619:
! 620: if (TcOutput() && outbuf.used && !wrap_1ST(mode)) {
! 621: tag = 3;
! 622: }
! 623:
! 624: while ((column + (need + gaps)) > used) {
! 625: int size = used - tag;
! 626: if (step) {
! 627: strcpy_DYN(&outbuf, align);
! 628: size -= base;
! 629: }
! 630: if (size > (last - step)) {
! 631: size = (last - step);
! 632: }
! 633: size = find_split(fill, step, size);
! 634: strncpy_DYN(&outbuf, fill + step, (size_t) size);
! 635: step += size;
! 636: need -= size;
! 637: if (need > 0) {
! 638: force_wrap();
! 639: did_wrap = TRUE;
! 640: tag = 0;
! 641: }
! 642: }
! 643: }
! 644: if (need > 0) {
! 645: if (step)
! 646: strcpy_DYN(&outbuf, align);
! 647: strcpy_DYN(&outbuf, fill + step);
! 648: }
! 649: if (wrap_END(mode))
! 650: strcpy_DYN(&outbuf, separator);
! 651: trailer = my_t;
! 652: force_wrap();
! 653:
! 654: free(fill);
! 655: } else {
! 656: strcpy_DYN(&outbuf, src);
! 657: if (wrap_END(mode))
! 658: strcpy_DYN(&outbuf, separator);
! 659: column += (int) strlen(src);
! 660: }
! 661: }
! 662:
! 663: static void
! 664: wrap_concat1(const char *src)
! 665: {
! 666: int need = (int) strlen(src);
! 667: wrap_concat(src, need, w1ST | wEND);
! 668: }
! 669:
! 670: static void
! 671: wrap_concat3(const char *name, const char *eqls, const char *value)
! 672: {
! 673: int nlen = (int) strlen(name);
! 674: int elen = (int) strlen(eqls);
! 675: int vlen = (int) strlen(value);
! 676:
! 677: wrap_concat(name, nlen + elen + vlen, w1ST);
! 678: wrap_concat(eqls, elen + vlen, w2ND);
! 679: wrap_concat(value, vlen, wEND);
1.1 millert 680: }
681:
682: #define IGNORE_SEP_TRAIL(first,last,sep_trail) \
683: if ((size_t)(last - first) > sizeof(sep_trail)-1 \
684: && !strncmp(first, sep_trail, sizeof(sep_trail)-1)) \
1.19 nicm 685: first += sizeof(sep_trail)-2
1.1 millert 686:
687: /* Returns the nominal length of the buffer assuming it is termcap format,
688: * i.e., the continuation sequence is treated as a single character ":".
689: *
690: * There are several implementations of termcap which read the text into a
691: * fixed-size buffer. Generally they strip the newlines from the text, but may
692: * not do it until after the buffer is read. Also, "tc=" resolution may be
693: * expanded in the same buffer. This function is useful for measuring the size
694: * of the best fixed-buffer implementation; the worst case may be much worse.
695: */
696: #ifdef TEST_TERMCAP_LENGTH
1.10 millert 697: static int
698: termcap_length(const char *src)
1.1 millert 699: {
1.10 millert 700: static const char pattern[] = ":\\\n\t:";
1.1 millert 701:
1.10 millert 702: int len = 0;
703: const char *const t = src + strlen(src);
1.1 millert 704:
1.10 millert 705: while (*src != '\0') {
706: IGNORE_SEP_TRAIL(src, t, pattern);
707: src++;
708: len++;
709: }
710: return len;
1.1 millert 711: }
712: #else
713: #define termcap_length(src) strlen(src)
714: #endif
715:
1.19 nicm 716: static void
717: indent_DYN(DYNBUF * buffer, int level)
718: {
719: int n;
720:
721: for (n = 0; n < level; n++)
1.21 ! nicm 722: strncpy_DYN(buffer, "\t", (size_t) 1);
1.19 nicm 723: }
724:
1.21 ! nicm 725: /*
! 726: * Check if the current line which was begun consists only of a tab and the
! 727: * given leading text.
! 728: */
1.19 nicm 729: static bool
1.21 ! nicm 730: leading_DYN(DYNBUF * buffer, const char *leading)
! 731: {
! 732: bool result = FALSE;
! 733: size_t need = strlen(leading);
! 734: if (buffer->used > need) {
! 735: need = buffer->used - need;
! 736: if (!strcmp(buffer->text + need, leading)) {
! 737: result = TRUE;
! 738: while (--need != 0) {
! 739: if (buffer->text[need] == '\n') {
! 740: break;
! 741: }
! 742: if (buffer->text[need] != '\t') {
! 743: result = FALSE;
! 744: break;
! 745: }
! 746: }
! 747: }
! 748: }
! 749: return result;
! 750: }
! 751:
! 752: bool
! 753: has_params(const char *src, bool formatting)
1.19 nicm 754: {
755: bool result = FALSE;
756: int len = (int) strlen(src);
757: int n;
758: bool ifthen = FALSE;
759: bool params = FALSE;
760:
761: for (n = 0; n < len - 1; ++n) {
1.21 ! nicm 762: if (!strncmp(src + n, "%p", (size_t) 2)) {
1.19 nicm 763: params = TRUE;
1.21 ! nicm 764: } else if (!strncmp(src + n, "%;", (size_t) 2)) {
1.19 nicm 765: ifthen = TRUE;
766: result = params;
767: break;
768: }
769: }
770: if (!ifthen) {
1.21 ! nicm 771: if (formatting) {
! 772: result = ((len > 50) && params);
! 773: } else {
! 774: result = params;
! 775: }
1.19 nicm 776: }
777: return result;
778: }
779:
1.10 millert 780: static char *
1.21 ! nicm 781: fmt_complex(TERMTYPE2 *tterm, const char *capability, char *src, int level)
1.1 millert 782: {
1.19 nicm 783: bool percent = FALSE;
1.21 ! nicm 784: bool params = has_params(src, TRUE);
1.10 millert 785:
786: while (*src != '\0') {
787: switch (*src) {
1.21 ! nicm 788: case '^':
! 789: percent = FALSE;
! 790: strncpy_DYN(&tmpbuf, src++, (size_t) 1);
! 791: break;
1.10 millert 792: case '\\':
1.19 nicm 793: percent = FALSE;
1.21 ! nicm 794: strncpy_DYN(&tmpbuf, src++, (size_t) 1);
1.10 millert 795: break;
796: case '%':
1.19 nicm 797: percent = TRUE;
1.10 millert 798: break;
799: case '?': /* "if" */
800: case 't': /* "then" */
801: case 'e': /* "else" */
802: if (percent) {
1.19 nicm 803: percent = FALSE;
1.15 millert 804: tmpbuf.text[tmpbuf.used - 1] = '\n';
1.19 nicm 805: /* treat a "%e" as else-if, on the same level */
806: if (*src == 'e') {
807: indent_DYN(&tmpbuf, level);
1.21 ! nicm 808: strncpy_DYN(&tmpbuf, "%", (size_t) 1);
! 809: strncpy_DYN(&tmpbuf, src, (size_t) 1);
1.19 nicm 810: src++;
1.21 ! nicm 811: params = has_params(src, TRUE);
1.19 nicm 812: if (!params && *src != '\0' && *src != '%') {
1.21 ! nicm 813: strncpy_DYN(&tmpbuf, "\n", (size_t) 1);
1.19 nicm 814: indent_DYN(&tmpbuf, level + 1);
815: }
1.15 millert 816: } else {
1.19 nicm 817: indent_DYN(&tmpbuf, level + 1);
1.21 ! nicm 818: strncpy_DYN(&tmpbuf, "%", (size_t) 1);
! 819: strncpy_DYN(&tmpbuf, src, (size_t) 1);
1.15 millert 820: if (*src++ == '?') {
1.21 ! nicm 821: src = fmt_complex(tterm, capability, src, level + 1);
1.19 nicm 822: if (*src != '\0' && *src != '%') {
1.21 ! nicm 823: strncpy_DYN(&tmpbuf, "\n", (size_t) 1);
1.19 nicm 824: indent_DYN(&tmpbuf, level + 1);
825: }
1.15 millert 826: } else if (level == 1) {
1.21 ! nicm 827: if (checking)
! 828: _nc_warning("%s: %%%c without %%? in %s",
! 829: _nc_first_name(tterm->term_names),
! 830: *src, capability);
1.15 millert 831: }
1.10 millert 832: }
833: continue;
834: }
835: break;
836: case ';': /* "endif" */
837: if (percent) {
1.19 nicm 838: percent = FALSE;
1.10 millert 839: if (level > 1) {
1.15 millert 840: tmpbuf.text[tmpbuf.used - 1] = '\n';
1.19 nicm 841: indent_DYN(&tmpbuf, level);
1.21 ! nicm 842: strncpy_DYN(&tmpbuf, "%", (size_t) 1);
! 843: strncpy_DYN(&tmpbuf, src++, (size_t) 1);
! 844: if (src[0] == '%'
! 845: && src[1] != '\0'
! 846: && (strchr("?e;", src[1])) == 0) {
! 847: tmpbuf.text[tmpbuf.used++] = '\n';
! 848: indent_DYN(&tmpbuf, level);
! 849: }
1.10 millert 850: return src;
1.1 millert 851: }
1.21 ! nicm 852: if (checking)
! 853: _nc_warning("%s: %%; without %%? in %s",
! 854: _nc_first_name(tterm->term_names),
! 855: capability);
1.10 millert 856: }
857: break;
858: case 'p':
1.21 ! nicm 859: if (percent && params && !leading_DYN(&tmpbuf, "%")) {
1.15 millert 860: tmpbuf.text[tmpbuf.used - 1] = '\n';
1.19 nicm 861: indent_DYN(&tmpbuf, level + 1);
1.21 ! nicm 862: strncpy_DYN(&tmpbuf, "%", (size_t) 1);
1.10 millert 863: }
1.19 nicm 864: percent = FALSE;
1.10 millert 865: break;
1.19 nicm 866: case ' ':
1.21 ! nicm 867: strncpy_DYN(&tmpbuf, "\\s", (size_t) 2);
1.19 nicm 868: ++src;
869: continue;
1.10 millert 870: default:
1.19 nicm 871: percent = FALSE;
1.10 millert 872: break;
1.1 millert 873: }
1.21 ! nicm 874: strncpy_DYN(&tmpbuf, src++, (size_t) 1);
1.10 millert 875: }
876: return src;
1.1 millert 877: }
878:
1.21 ! nicm 879: /*
! 880: * Make "large" numbers a little easier to read by showing them in hexadecimal
! 881: * if they are "close" to a power of two.
! 882: */
! 883: static const char *
! 884: number_format(int value)
! 885: {
! 886: const char *result = "%d";
! 887:
! 888: if ((outform != F_TERMCAP) && (value > 255)) {
! 889: unsigned long lv = (unsigned long) value;
! 890: int bits = sizeof(unsigned long) * 8;
! 891: int nn;
! 892:
! 893: for (nn = 8; nn < bits; ++nn) {
! 894: unsigned long mm;
! 895:
! 896: mm = 1UL << nn;
! 897: if ((mm - 16) <= lv && (mm + 16) > lv) {
! 898: result = "%#x";
! 899: break;
! 900: }
! 901: }
! 902: }
! 903: return result;
! 904: }
! 905:
1.19 nicm 906: #define SAME_CAP(n,cap) (&tterm->Strings[n] == &cap)
907: #define EXTRA_CAP 20
908:
1.10 millert 909: int
1.21 ! nicm 910: fmt_entry(TERMTYPE2 *tterm,
1.19 nicm 911: PredFunc pred,
1.21 ! nicm 912: int content_only,
! 913: int suppress_untranslatable,
! 914: int infodump,
1.19 nicm 915: int numbers)
1.10 millert 916: {
1.19 nicm 917: PredIdx i, j;
918: char buffer[MAX_TERMINFO_LENGTH + EXTRA_CAP];
1.10 millert 919: NCURSES_CONST char *name;
920: int predval, len;
1.19 nicm 921: PredIdx num_bools = 0;
922: PredIdx num_values = 0;
923: PredIdx num_strings = 0;
1.10 millert 924: bool outcount = 0;
1.1 millert 925:
1.21 ! nicm 926: #define WRAP_CONCAT1(s) wrap_concat1(s); outcount = TRUE
! 927: #define WRAP_CONCAT WRAP_CONCAT1(buffer)
1.1 millert 928:
929: len = 12; /* terminfo file-header */
930:
931: if (pred == 0) {
932: cur_type = tterm;
933: pred = dump_predicate;
934: }
935:
1.15 millert 936: strcpy_DYN(&outbuf, 0);
1.19 nicm 937: if (content_only) {
1.21 ! nicm 938: column = indent; /* workaround to prevent empty lines */
1.19 nicm 939: } else {
940: strcpy_DYN(&outbuf, tterm->term_names);
1.21 ! nicm 941:
! 942: /*
! 943: * Colon is legal in terminfo descriptions, but not in termcap.
! 944: */
! 945: if (!infodump) {
! 946: char *p = outbuf.text;
! 947: while (*p) {
! 948: if (*p == ':') {
! 949: *p = '=';
! 950: }
! 951: ++p;
! 952: }
! 953: }
1.19 nicm 954: strcpy_DYN(&outbuf, separator);
955: column = (int) outbuf.used;
1.21 ! nicm 956: if (height > 1)
! 957: force_wrap();
1.19 nicm 958: }
1.1 millert 959:
1.10 millert 960: for_each_boolean(j, tterm) {
1.4 millert 961: i = BoolIndirect(j);
1.21 ! nicm 962: name = ExtBoolname(tterm, (int) i, bool_names);
1.19 nicm 963: assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);
1.1 millert 964:
965: if (!version_filter(BOOLEAN, i))
966: continue;
1.10 millert 967: else if (isObsolete(outform, name))
1.1 millert 968: continue;
969:
970: predval = pred(BOOLEAN, i);
971: if (predval != FAIL) {
1.21 ! nicm 972: _nc_STRCPY(buffer, name, sizeof(buffer));
1.1 millert 973: if (predval <= 0)
1.21 ! nicm 974: _nc_STRCAT(buffer, "@", sizeof(buffer));
1.1 millert 975: else if (i + 1 > num_bools)
976: num_bools = i + 1;
977: WRAP_CONCAT;
978: }
979: }
980:
1.21 ! nicm 981: if (column != indent && height > 1)
1.1 millert 982: force_wrap();
983:
1.10 millert 984: for_each_number(j, tterm) {
1.4 millert 985: i = NumIndirect(j);
1.21 ! nicm 986: name = ExtNumname(tterm, (int) i, num_names);
1.19 nicm 987: assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);
1.1 millert 988:
989: if (!version_filter(NUMBER, i))
990: continue;
1.10 millert 991: else if (isObsolete(outform, name))
1.1 millert 992: continue;
993:
994: predval = pred(NUMBER, i);
995: if (predval != FAIL) {
996: if (tterm->Numbers[i] < 0) {
1.21 ! nicm 997: _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
! 998: "%s@", name);
1.1 millert 999: } else {
1.21 ! nicm 1000: size_t nn;
! 1001: _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
! 1002: "%s#", name);
! 1003: nn = strlen(buffer);
! 1004: _nc_SPRINTF(buffer + nn, _nc_SLIMIT(sizeof(buffer) - nn)
! 1005: number_format(tterm->Numbers[i]),
! 1006: tterm->Numbers[i]);
1.1 millert 1007: if (i + 1 > num_values)
1008: num_values = i + 1;
1009: }
1010: WRAP_CONCAT;
1011: }
1012: }
1013:
1.21 ! nicm 1014: if (column != indent && height > 1)
1.1 millert 1015: force_wrap();
1016:
1.19 nicm 1017: len += (int) (num_bools
1018: + num_values * 2
1019: + strlen(tterm->term_names) + 1);
1.1 millert 1020: if (len & 1)
1.10 millert 1021: len++;
1.1 millert 1022:
1.15 millert 1023: #undef CUR
1024: #define CUR tterm->
1025: if (outform == F_TERMCAP) {
1.21 ! nicm 1026: if (VALID_STRING(termcap_reset)) {
! 1027: if (VALID_STRING(init_3string)
1.15 millert 1028: && !strcmp(init_3string, termcap_reset))
1029: DISCARD(init_3string);
1030:
1.21 ! nicm 1031: if (VALID_STRING(reset_2string)
1.15 millert 1032: && !strcmp(reset_2string, termcap_reset))
1033: DISCARD(reset_2string);
1034: }
1035: }
1036:
1.4 millert 1037: for_each_string(j, tterm) {
1.21 ! nicm 1038: char *capability;
1.4 millert 1039: i = StrIndirect(j);
1.21 ! nicm 1040: name = ExtStrname(tterm, (int) i, str_names);
1.19 nicm 1041: assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);
1042:
1043: capability = tterm->Strings[i];
1.1 millert 1044:
1045: if (!version_filter(STRING, i))
1046: continue;
1.10 millert 1047: else if (isObsolete(outform, name))
1.1 millert 1048: continue;
1049:
1.19 nicm 1050: #if NCURSES_XNAMES
1.1 millert 1051: /*
1.19 nicm 1052: * Extended names can be longer than 2 characters, but termcap programs
1053: * cannot read those (filter them out).
1.1 millert 1054: */
1.19 nicm 1055: if (outform == F_TERMCAP && (strlen(name) > 2))
1056: continue;
1057: #endif
1058:
1.10 millert 1059: if (outform == F_TERMCAP) {
1.19 nicm 1060: /*
1061: * Some older versions of vi want rmir/smir to be defined
1062: * for ich/ich1 to work. If they're not defined, force
1063: * them to be output as defined and empty.
1064: */
1065: if (PRESENT(insert_character) || PRESENT(parm_ich)) {
1066: if (SAME_CAP(i, enter_insert_mode)
1.10 millert 1067: && enter_insert_mode == ABSENT_STRING) {
1.21 ! nicm 1068: _nc_STRCPY(buffer, "im=", sizeof(buffer));
1.15 millert 1069: WRAP_CONCAT;
1070: continue;
1.1 millert 1071: }
1072:
1.19 nicm 1073: if (SAME_CAP(i, exit_insert_mode)
1.10 millert 1074: && exit_insert_mode == ABSENT_STRING) {
1.21 ! nicm 1075: _nc_STRCPY(buffer, "ei=", sizeof(buffer));
1.15 millert 1076: WRAP_CONCAT;
1077: continue;
1.1 millert 1078: }
1079: }
1.19 nicm 1080: /*
1081: * termcap applications such as screen will be confused if sgr0
1082: * is translated to a string containing rmacs. Filter that out.
1083: */
1084: if (PRESENT(exit_attribute_mode)) {
1085: if (SAME_CAP(i, exit_attribute_mode)) {
1086: char *trimmed_sgr0;
1087: char *my_sgr = set_attributes;
1088:
1089: set_attributes = save_sgr;
1090:
1091: trimmed_sgr0 = _nc_trim_sgr0(tterm);
1.21 ! nicm 1092: if (strcmp(capability, trimmed_sgr0)) {
1.19 nicm 1093: capability = trimmed_sgr0;
1.21 ! nicm 1094: } else {
! 1095: if (trimmed_sgr0 != exit_attribute_mode)
! 1096: free(trimmed_sgr0);
! 1097: }
1.19 nicm 1098:
1099: set_attributes = my_sgr;
1100: }
1101: }
1.2 millert 1102: }
1103:
1.1 millert 1104: predval = pred(STRING, i);
1105: buffer[0] = '\0';
1.15 millert 1106:
1.1 millert 1107: if (predval != FAIL) {
1.21 ! nicm 1108: if (VALID_STRING(capability)
1.10 millert 1109: && i + 1 > num_strings)
1.1 millert 1110: num_strings = i + 1;
1.15 millert 1111:
1.19 nicm 1112: if (!VALID_STRING(capability)) {
1.21 ! nicm 1113: _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
! 1114: "%s@", name);
1.15 millert 1115: WRAP_CONCAT;
1.21 ! nicm 1116: } else if (TcOutput()) {
! 1117: char *srccap = _nc_tic_expand(capability, TRUE, numbers);
1.19 nicm 1118: int params = ((i < (int) SIZEOF(parametrized))
1119: ? parametrized[i]
1.21 ! nicm 1120: : ((*srccap == 'k')
! 1121: ? 0
! 1122: : has_params(srccap, FALSE)));
1.16 millert 1123: char *cv = _nc_infotocap(name, srccap, params);
1.1 millert 1124:
1.10 millert 1125: if (cv == 0) {
1.15 millert 1126: if (outform == F_TCONVERR) {
1.21 ! nicm 1127: _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
! 1128: "%s=!!! %s WILL NOT CONVERT !!!",
! 1129: name, srccap);
! 1130: WRAP_CONCAT;
1.15 millert 1131: } else if (suppress_untranslatable) {
1.1 millert 1132: continue;
1.15 millert 1133: } else {
1.21 ! nicm 1134: char *s = srccap, *d = buffer;
! 1135: int need = 3 + (int) strlen(name);
! 1136: while ((*d = *s++) != 0) {
! 1137: if ((d - buffer + 2) >= (int) sizeof(buffer)) {
! 1138: fprintf(stderr,
! 1139: "%s: value for %s is too long\n",
! 1140: _nc_progname,
! 1141: name);
! 1142: *d = '\0';
! 1143: break;
! 1144: }
1.10 millert 1145: if (*d == ':') {
1146: *d++ = '\\';
1147: *d = ':';
1148: } else if (*d == '\\') {
1.21 ! nicm 1149: if ((*++d = *s++) == '\0')
! 1150: break;
1.10 millert 1151: }
1152: d++;
1.21 ! nicm 1153: *d = '\0';
1.10 millert 1154: }
1.21 ! nicm 1155: need += (int) (d - buffer);
! 1156: wrap_concat("..", need, w1ST | wERR);
! 1157: need -= 2;
! 1158: wrap_concat(name, need, wOFF | wERR);
! 1159: need -= (int) strlen(name);
! 1160: wrap_concat("=", need, w2ND | wERR);
! 1161: need -= 1;
! 1162: wrap_concat(buffer, need, wEND | wERR);
! 1163: outcount = TRUE;
1.10 millert 1164: }
1.15 millert 1165: } else {
1.21 ! nicm 1166: wrap_concat3(name, "=", cv);
1.15 millert 1167: }
1.19 nicm 1168: len += (int) strlen(capability) + 1;
1.10 millert 1169: } else {
1.19 nicm 1170: char *src = _nc_tic_expand(capability,
1171: outform == F_TERMINFO, numbers);
1.15 millert 1172:
1173: strcpy_DYN(&tmpbuf, 0);
1174: strcpy_DYN(&tmpbuf, name);
1175: strcpy_DYN(&tmpbuf, "=");
1.8 millert 1176: if (pretty
1.10 millert 1177: && (outform == F_TERMINFO
1.15 millert 1178: || outform == F_VARIABLE)) {
1.21 ! nicm 1179: fmt_complex(tterm, name, src, 1);
1.15 millert 1180: } else {
1181: strcpy_DYN(&tmpbuf, src);
1182: }
1.19 nicm 1183: len += (int) strlen(capability) + 1;
1.21 ! nicm 1184: WRAP_CONCAT1(tmpbuf.text);
1.1 millert 1185: }
1186: }
1.19 nicm 1187: /* e.g., trimmed_sgr0 */
1.21 ! nicm 1188: if (VALID_STRING(capability) &&
! 1189: capability != tterm->Strings[i])
1.19 nicm 1190: free(capability);
1.1 millert 1191: }
1.19 nicm 1192: len += (int) (num_strings * 2);
1.1 millert 1193:
1194: /*
1195: * This piece of code should be an effective inverse of the functions
1.19 nicm 1196: * postprocess_terminfo() and postprocess_terminfo() in parse_entry.c.
1.1 millert 1197: * Much more work should be done on this to support dumping termcaps.
1198: */
1.10 millert 1199: if (tversion == V_HPUX) {
1.19 nicm 1200: if (VALID_STRING(memory_lock)) {
1.21 ! nicm 1201: _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
! 1202: "meml=%s", memory_lock);
1.1 millert 1203: WRAP_CONCAT;
1204: }
1.19 nicm 1205: if (VALID_STRING(memory_unlock)) {
1.21 ! nicm 1206: _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
! 1207: "memu=%s", memory_unlock);
1.1 millert 1208: WRAP_CONCAT;
1209: }
1.10 millert 1210: } else if (tversion == V_AIX) {
1211: if (VALID_STRING(acs_chars)) {
1212: bool box_ok = TRUE;
1213: const char *acstrans = "lqkxjmwuvtn";
1214: const char *cp;
1215: char *tp, *sp, boxchars[11];
1.1 millert 1216:
1217: tp = boxchars;
1.10 millert 1218: for (cp = acstrans; *cp; cp++) {
1.21 ! nicm 1219: sp = (strchr) (acs_chars, *cp);
1.1 millert 1220: if (sp)
1221: *tp++ = sp[1];
1.10 millert 1222: else {
1.1 millert 1223: box_ok = FALSE;
1224: break;
1225: }
1226: }
1227: tp[0] = '\0';
1228:
1.10 millert 1229: if (box_ok) {
1.21 ! nicm 1230: char *tmp = _nc_tic_expand(boxchars,
! 1231: (outform == F_TERMINFO),
! 1232: numbers);
! 1233: _nc_STRCPY(buffer, "box1=", sizeof(buffer));
! 1234: while (*tmp != '\0') {
! 1235: size_t have = strlen(buffer);
! 1236: size_t next = strlen(tmp);
! 1237: size_t want = have + next + 1;
! 1238: size_t last = next;
! 1239: char save = '\0';
! 1240:
! 1241: /*
! 1242: * If the expanded string is too long for the buffer,
! 1243: * chop it off and save the location where we chopped it.
! 1244: */
! 1245: if (want >= sizeof(buffer)) {
! 1246: save = tmp[last];
! 1247: tmp[last] = '\0';
! 1248: }
! 1249: _nc_STRCAT(buffer, tmp, sizeof(buffer));
! 1250:
! 1251: /*
! 1252: * If we chopped the buffer, replace the missing piece and
! 1253: * shift everything to append the remainder.
! 1254: */
! 1255: if (save != '\0') {
! 1256: next = 0;
! 1257: tmp[last] = save;
! 1258: while ((tmp[next] = tmp[last + next]) != '\0') {
! 1259: ++next;
! 1260: }
! 1261: } else {
! 1262: break;
! 1263: }
! 1264: }
1.1 millert 1265: WRAP_CONCAT;
1266: }
1267: }
1268: }
1269:
1270: /*
1271: * kludge: trim off trailer to avoid an extra blank line
1272: * in infocmp -u output when there are no string differences
1273: */
1.10 millert 1274: if (outcount) {
1.15 millert 1275: bool trimmed = FALSE;
1.21 ! nicm 1276: j = (PredIdx) outbuf.used;
! 1277: if (wrapped && did_wrap) {
! 1278: /* EMPTY */ ;
! 1279: } else if (j >= 2
! 1280: && outbuf.text[j - 1] == '\t'
! 1281: && outbuf.text[j - 2] == '\n') {
1.15 millert 1282: outbuf.used -= 2;
1283: trimmed = TRUE;
1.1 millert 1284: } else if (j >= 4
1.19 nicm 1285: && outbuf.text[j - 1] == ':'
1286: && outbuf.text[j - 2] == '\t'
1287: && outbuf.text[j - 3] == '\n'
1288: && outbuf.text[j - 4] == '\\') {
1.15 millert 1289: outbuf.used -= 4;
1290: trimmed = TRUE;
1291: }
1292: if (trimmed) {
1293: outbuf.text[outbuf.used] = '\0';
1294: column = oldcol;
1.19 nicm 1295: strcpy_DYN(&outbuf, " ");
1.1 millert 1296: }
1297: }
1298: #if 0
1299: fprintf(stderr, "num_bools = %d\n", num_bools);
1300: fprintf(stderr, "num_values = %d\n", num_values);
1301: fprintf(stderr, "num_strings = %d\n", num_strings);
1302: fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n",
1.19 nicm 1303: tterm->term_names, len, outbuf.used, outbuf.text);
1.1 millert 1304: #endif
1305: /*
1306: * Here's where we use infodump to trigger a more stringent length check
1307: * for termcap-translation purposes.
1308: * Return the length of the raw entry, without tc= expansions,
1309: * It gives an idea of which entries are deadly to even *scan past*,
1310: * as opposed to *use*.
1311: */
1.19 nicm 1312: return (infodump ? len : (int) termcap_length(outbuf.text));
1313: }
1314:
1315: static bool
1.21 ! nicm 1316: kill_string(TERMTYPE2 *tterm, const char *const cap)
1.19 nicm 1317: {
1318: unsigned n;
1319: for (n = 0; n < NUM_STRINGS(tterm); ++n) {
1320: if (cap == tterm->Strings[n]) {
1321: tterm->Strings[n] = ABSENT_STRING;
1322: return TRUE;
1323: }
1324: }
1325: return FALSE;
1.1 millert 1326: }
1327:
1.19 nicm 1328: static char *
1.21 ! nicm 1329: find_string(TERMTYPE2 *tterm, char *name)
1.19 nicm 1330: {
1331: PredIdx n;
1332: for (n = 0; n < NUM_STRINGS(tterm); ++n) {
1333: if (version_filter(STRING, n)
1334: && !strcmp(name, strnames[n])) {
1335: char *cap = tterm->Strings[n];
1336: if (VALID_STRING(cap)) {
1337: return cap;
1338: }
1339: break;
1340: }
1341: }
1342: return ABSENT_STRING;
1343: }
1344:
1345: /*
1346: * This is used to remove function-key labels from a termcap entry to
1347: * make it smaller.
1348: */
1349: static int
1.21 ! nicm 1350: kill_labels(TERMTYPE2 *tterm, int target)
1.19 nicm 1351: {
1352: int n;
1353: int result = 0;
1.21 ! nicm 1354: char name[20];
1.19 nicm 1355:
1356: for (n = 0; n <= 10; ++n) {
1.21 ! nicm 1357: char *cap;
! 1358:
! 1359: _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "lf%d", n);
! 1360: cap = find_string(tterm, name);
! 1361: if (VALID_STRING(cap)
1.19 nicm 1362: && kill_string(tterm, cap)) {
1363: target -= (int) (strlen(cap) + 5);
1364: ++result;
1365: if (target < 0)
1366: break;
1367: }
1368: }
1369: return result;
1370: }
1371:
1372: /*
1373: * This is used to remove function-key definitions from a termcap entry to
1374: * make it smaller.
1375: */
1376: static int
1.21 ! nicm 1377: kill_fkeys(TERMTYPE2 *tterm, int target)
1.19 nicm 1378: {
1379: int n;
1380: int result = 0;
1.21 ! nicm 1381: char name[20];
1.19 nicm 1382:
1383: for (n = 60; n >= 0; --n) {
1.21 ! nicm 1384: char *cap;
! 1385:
! 1386: _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "kf%d", n);
! 1387: cap = find_string(tterm, name);
! 1388: if (VALID_STRING(cap)
1.19 nicm 1389: && kill_string(tterm, cap)) {
1390: target -= (int) (strlen(cap) + 5);
1391: ++result;
1392: if (target < 0)
1393: break;
1394: }
1395: }
1396: return result;
1397: }
1398:
1399: /*
1400: * Check if the given acsc string is a 1-1 mapping, i.e., just-like-vt100.
1401: * Also, since this is for termcap, we only care about the line-drawing map.
1402: */
1403: #define isLine(c) (strchr("lmkjtuvwqxn", c) != 0)
1404:
1405: static bool
1406: one_one_mapping(const char *mapping)
1407: {
1408: bool result = TRUE;
1409:
1.21 ! nicm 1410: if (VALID_STRING(mapping)) {
1.19 nicm 1411: int n = 0;
1.21 ! nicm 1412: while (mapping[n] != '\0' && mapping[n + 1] != '\0') {
1.19 nicm 1413: if (isLine(mapping[n]) &&
1414: mapping[n] != mapping[n + 1]) {
1415: result = FALSE;
1416: break;
1417: }
1418: n += 2;
1419: }
1420: }
1421: return result;
1422: }
1423:
1424: #define FMT_ENTRY() \
1425: fmt_entry(tterm, pred, \
1426: 0, \
1427: suppress_untranslatable, \
1428: infodump, numbers)
1429:
1430: #define SHOW_WHY PRINTF
1431:
1432: static bool
1.21 ! nicm 1433: purged_acs(TERMTYPE2 *tterm)
1.19 nicm 1434: {
1435: bool result = FALSE;
1436:
1437: if (VALID_STRING(acs_chars)) {
1438: if (!one_one_mapping(acs_chars)) {
1439: enter_alt_charset_mode = ABSENT_STRING;
1440: exit_alt_charset_mode = ABSENT_STRING;
1441: SHOW_WHY("# (rmacs/smacs removed for consistency)\n");
1442: }
1443: result = TRUE;
1444: }
1445: return result;
1446: }
1447:
1.21 ! nicm 1448: static void
! 1449: encode_b64(char *target, char *source, unsigned state, int *saved)
! 1450: {
! 1451: /* RFC-4648 */
! 1452: static const char data[] =
! 1453: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
! 1454: "abcdefghijklmnopqrstuvwxyz"
! 1455: "0123456789" "-_";
! 1456: int ch = UChar(source[state]);
! 1457:
! 1458: switch (state % 3) {
! 1459: case 0:
! 1460: *target++ = data[(ch >> 2) & 077];
! 1461: *saved = (ch << 4);
! 1462: break;
! 1463: case 1:
! 1464: *target++ = data[((ch >> 4) | *saved) & 077];
! 1465: *saved = (ch << 2);
! 1466: break;
! 1467: case 2:
! 1468: *target++ = data[((ch >> 6) | *saved) & 077];
! 1469: *target++ = data[ch & 077];
! 1470: *saved = 0;
! 1471: break;
! 1472: }
! 1473: *target = '\0';
! 1474: }
! 1475:
1.19 nicm 1476: /*
1477: * Dump a single entry.
1478: */
1479: void
1.21 ! nicm 1480: dump_entry(TERMTYPE2 *tterm,
! 1481: int suppress_untranslatable,
! 1482: int limited,
1.19 nicm 1483: int numbers,
1484: PredFunc pred)
1.1 millert 1485: {
1.21 ! nicm 1486: TERMTYPE2 save_tterm;
! 1487: int critlen;
1.10 millert 1488: const char *legend;
1489: bool infodump;
1.1 millert 1490:
1.21 ! nicm 1491: if (quickdump) {
! 1492: char bigbuf[65536];
! 1493: unsigned offset = 0;
! 1494:
! 1495: separator = "";
! 1496: trailer = "\n";
! 1497: indent = 0;
! 1498:
! 1499: if (_nc_write_object(tterm, bigbuf, &offset, sizeof(bigbuf)) == OK) {
! 1500: char numbuf[80];
! 1501: unsigned n;
! 1502:
! 1503: if (quickdump & 1) {
! 1504: if (outbuf.used)
! 1505: wrap_concat1("\n");
! 1506: wrap_concat1("hex:");
! 1507: for (n = 0; n < offset; ++n) {
! 1508: _nc_SPRINTF(numbuf, _nc_SLIMIT(sizeof(numbuf))
! 1509: "%02X", UChar(bigbuf[n]));
! 1510: wrap_concat1(numbuf);
! 1511: }
! 1512: }
! 1513: if (quickdump & 2) {
! 1514: static char padding[] =
! 1515: {0, 0};
! 1516: int value = 0;
! 1517:
! 1518: if (outbuf.used)
! 1519: wrap_concat1("\n");
! 1520: wrap_concat1("b64:");
! 1521: for (n = 0; n < offset; ++n) {
! 1522: encode_b64(numbuf, bigbuf, n, &value);
! 1523: wrap_concat1(numbuf);
! 1524: }
! 1525: switch (n % 3) {
! 1526: case 0:
! 1527: break;
! 1528: case 1:
! 1529: encode_b64(numbuf, padding, 1, &value);
! 1530: wrap_concat1(numbuf);
! 1531: wrap_concat1("==");
! 1532: break;
! 1533: case 2:
! 1534: encode_b64(numbuf, padding, 1, &value);
! 1535: wrap_concat1(numbuf);
! 1536: wrap_concat1("=");
! 1537: break;
! 1538: }
! 1539: }
! 1540: }
! 1541: return;
! 1542: }
! 1543:
! 1544: if (TcOutput()) {
1.1 millert 1545: critlen = MAX_TERMCAP_LENGTH;
1546: legend = "older termcap";
1547: infodump = FALSE;
1548: set_obsolete_termcaps(tterm);
1.10 millert 1549: } else {
1.1 millert 1550: critlen = MAX_TERMINFO_LENGTH;
1551: legend = "terminfo";
1552: infodump = TRUE;
1553: }
1554:
1.19 nicm 1555: save_sgr = set_attributes;
1556:
1.21 ! nicm 1557: if ((FMT_ENTRY() > critlen)
! 1558: && TcOutput()
1.10 millert 1559: && limited) {
1.19 nicm 1560:
1561: save_tterm = *tterm;
1562: if (!suppress_untranslatable) {
1563: SHOW_WHY("# (untranslatable capabilities removed to fit entry within %d bytes)\n",
1564: critlen);
1565: suppress_untranslatable = TRUE;
1566: }
1.21 ! nicm 1567: if (FMT_ENTRY() > critlen) {
1.1 millert 1568: /*
1.21 ! nicm 1569: * We pick on sgr because it is a nice long string capability that
1.12 millert 1570: * is really just an optimization hack. Another good candidate is
1571: * acsc since it is both long and unused by BSD termcap.
1.1 millert 1572: */
1.19 nicm 1573: bool changed = FALSE;
1574:
1575: #if NCURSES_XNAMES
1576: /*
1577: * Extended names are most likely function-key definitions. Drop
1578: * those first.
1579: */
1580: unsigned n;
1581: for (n = STRCOUNT; n < NUM_STRINGS(tterm); n++) {
1.21 ! nicm 1582: const char *name = ExtStrname(tterm, (int) n, strnames);
1.19 nicm 1583:
1584: if (VALID_STRING(tterm->Strings[n])) {
1585: set_attributes = ABSENT_STRING;
1586: /* we remove long names anyway - only report the short */
1587: if (strlen(name) <= 2) {
1588: SHOW_WHY("# (%s removed to fit entry within %d bytes)\n",
1589: name,
1590: critlen);
1591: }
1592: changed = TRUE;
1.21 ! nicm 1593: if (FMT_ENTRY() <= critlen)
1.19 nicm 1594: break;
1595: }
1.12 millert 1596: }
1.19 nicm 1597: #endif
1598: if (VALID_STRING(set_attributes)) {
1599: set_attributes = ABSENT_STRING;
1600: SHOW_WHY("# (sgr removed to fit entry within %d bytes)\n",
1601: critlen);
1602: changed = TRUE;
1603: }
1.21 ! nicm 1604: if (!changed || (FMT_ENTRY() > critlen)) {
1.19 nicm 1605: if (purged_acs(tterm)) {
1606: acs_chars = ABSENT_STRING;
1607: SHOW_WHY("# (acsc removed to fit entry within %d bytes)\n",
1608: critlen);
1609: changed = TRUE;
1610: }
1611: }
1.21 ! nicm 1612: if (!changed || (FMT_ENTRY() > critlen)) {
1.1 millert 1613: int oldversion = tversion;
1.21 ! nicm 1614: int len;
1.1 millert 1615:
1616: tversion = V_BSD;
1.19 nicm 1617: SHOW_WHY("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n",
1618: critlen);
1.1 millert 1619:
1.19 nicm 1620: len = FMT_ENTRY();
1621: if (len > critlen
1622: && kill_labels(tterm, len - critlen)) {
1623: SHOW_WHY("# (some labels capabilities suppressed to fit entry within %d bytes)\n",
1624: critlen);
1625: len = FMT_ENTRY();
1626: }
1627: if (len > critlen
1628: && kill_fkeys(tterm, len - critlen)) {
1629: SHOW_WHY("# (some function-key capabilities suppressed to fit entry within %d bytes)\n",
1630: critlen);
1631: len = FMT_ENTRY();
1632: }
1633: if (len > critlen) {
1.1 millert 1634: (void) fprintf(stderr,
1.21 ! nicm 1635: "%s: %s entry is %d bytes long\n",
! 1636: _nc_progname,
1.19 nicm 1637: _nc_first_name(tterm->term_names),
1638: len);
1639: SHOW_WHY("# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n",
1640: len, legend);
1.1 millert 1641: }
1642: tversion = oldversion;
1643: }
1.19 nicm 1644: set_attributes = save_sgr;
1645: *tterm = save_tterm;
1646: }
1647: } else if (!version_filter(STRING, STR_IDX(acs_chars))) {
1648: save_tterm = *tterm;
1649: if (purged_acs(tterm)) {
1.21 ! nicm 1650: (void) FMT_ENTRY();
1.1 millert 1651: }
1.19 nicm 1652: *tterm = save_tterm;
1.1 millert 1653: }
1654: }
1655:
1.19 nicm 1656: void
1.21 ! nicm 1657: dump_uses(const char *value, bool infodump)
1.1 millert 1658: /* dump "use=" clauses in the appropriate format */
1659: {
1.21 ! nicm 1660: char buffer[MAX_TERMINFO_LENGTH + EXTRA_CAP];
! 1661: int limit = (VALID_STRING(value) ? (int) strlen(value) : 0);
! 1662: const char *cap = infodump ? "use" : "tc";
1.1 millert 1663:
1.21 ! nicm 1664: if (TcOutput())
1.19 nicm 1665: trim_trailing();
1.21 ! nicm 1666: if (limit == 0) {
! 1667: _nc_warning("empty \"%s\" field", cap);
! 1668: value = "";
! 1669: } else if (limit > MAX_ALIAS) {
! 1670: _nc_warning("\"%s\" field too long (%d), limit to %d",
! 1671: cap, limit, MAX_ALIAS);
! 1672: limit = MAX_ALIAS;
! 1673: }
! 1674: _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
! 1675: "%s=%.*s", cap, limit, value);
! 1676: wrap_concat1(buffer);
1.19 nicm 1677: }
1678:
1679: int
1680: show_entry(void)
1681: {
1.21 ! nicm 1682: /*
! 1683: * Trim any remaining whitespace.
! 1684: */
! 1685: if (outbuf.used != 0) {
! 1686: bool infodump = !TcOutput();
! 1687: char delim = (char) (infodump ? ',' : ':');
! 1688: int j;
! 1689:
! 1690: for (j = (int) outbuf.used - 1; j > 0; --j) {
! 1691: char ch = outbuf.text[j];
! 1692: if (ch == '\n') {
! 1693: ;
! 1694: } else if (isspace(UChar(ch))) {
! 1695: outbuf.used = (size_t) j;
! 1696: } else if (!infodump && ch == '\\') {
! 1697: outbuf.used = (size_t) j;
! 1698: } else if (ch == delim && (outbuf.text[j - 1] != '\\')) {
! 1699: outbuf.used = (size_t) (j + 1);
! 1700: } else {
! 1701: break;
! 1702: }
! 1703: }
! 1704: outbuf.text[outbuf.used] = '\0';
! 1705: }
! 1706: if (outbuf.text != 0) {
! 1707: (void) fputs(outbuf.text, stdout);
! 1708: putchar('\n');
! 1709: }
1.19 nicm 1710: return (int) outbuf.used;
1.1 millert 1711: }
1712:
1.10 millert 1713: void
1.21 ! nicm 1714: compare_entry(PredHook hook,
! 1715: TERMTYPE2 *tp GCC_UNUSED,
1.19 nicm 1716: bool quiet)
1.1 millert 1717: /* compare two entries */
1718: {
1.19 nicm 1719: PredIdx i, j;
1.10 millert 1720: NCURSES_CONST char *name;
1.1 millert 1721:
1.15 millert 1722: if (!quiet)
1723: fputs(" comparing booleans.\n", stdout);
1.10 millert 1724: for_each_boolean(j, tp) {
1.4 millert 1725: i = BoolIndirect(j);
1.21 ! nicm 1726: name = ExtBoolname(tp, (int) i, bool_names);
1.1 millert 1727:
1.10 millert 1728: if (isObsolete(outform, name))
1.1 millert 1729: continue;
1730:
1.14 millert 1731: (*hook) (CMP_BOOLEAN, i, name);
1.1 millert 1732: }
1733:
1.15 millert 1734: if (!quiet)
1735: fputs(" comparing numbers.\n", stdout);
1.10 millert 1736: for_each_number(j, tp) {
1.4 millert 1737: i = NumIndirect(j);
1.21 ! nicm 1738: name = ExtNumname(tp, (int) i, num_names);
1.1 millert 1739:
1.10 millert 1740: if (isObsolete(outform, name))
1.1 millert 1741: continue;
1742:
1.14 millert 1743: (*hook) (CMP_NUMBER, i, name);
1.1 millert 1744: }
1745:
1.15 millert 1746: if (!quiet)
1747: fputs(" comparing strings.\n", stdout);
1.10 millert 1748: for_each_string(j, tp) {
1.4 millert 1749: i = StrIndirect(j);
1.21 ! nicm 1750: name = ExtStrname(tp, (int) i, str_names);
1.1 millert 1751:
1.10 millert 1752: if (isObsolete(outform, name))
1.1 millert 1753: continue;
1754:
1.14 millert 1755: (*hook) (CMP_STRING, i, name);
1.1 millert 1756: }
1.14 millert 1757:
1758: /* (void) fputs(" comparing use entries.\n", stdout); */
1759: (*hook) (CMP_USE, 0, "use");
1760:
1.1 millert 1761: }
1762:
1763: #define NOTSET(s) ((s) == 0)
1764:
1765: /*
1766: * This bit of legerdemain turns all the terminfo variable names into
1767: * references to locations in the arrays Booleans, Numbers, and Strings ---
1768: * precisely what's needed.
1769: */
1770: #undef CUR
1771: #define CUR tp->
1772:
1.10 millert 1773: static void
1.21 ! nicm 1774: set_obsolete_termcaps(TERMTYPE2 *tp)
1.1 millert 1775: {
1776: #include "capdefaults.c"
1777: }
1778:
1779: /*
1780: * Convert an alternate-character-set string to canonical form: sorted and
1781: * unique.
1782: */
1.14 millert 1783: void
1.21 ! nicm 1784: repair_acsc(TERMTYPE2 *tp)
1.1 millert 1785: {
1.10 millert 1786: if (VALID_STRING(acs_chars)) {
1.21 ! nicm 1787: size_t n;
1.10 millert 1788: char mapped[256];
1789: unsigned source;
1790: unsigned target;
1791: bool fix_needed = FALSE;
1792:
1793: for (n = 0, source = 0; acs_chars[n] != 0; n++) {
1.19 nicm 1794: target = UChar(acs_chars[n]);
1.10 millert 1795: if (source >= target) {
1796: fix_needed = TRUE;
1797: break;
1798: }
1799: source = target;
1800: if (acs_chars[n + 1])
1801: n++;
1802: }
1.21 ! nicm 1803:
1.10 millert 1804: if (fix_needed) {
1.21 ! nicm 1805: size_t m;
! 1806: char extra = 0;
! 1807:
1.10 millert 1808: memset(mapped, 0, sizeof(mapped));
1809: for (n = 0; acs_chars[n] != 0; n++) {
1.19 nicm 1810: source = UChar(acs_chars[n]);
1.10 millert 1811: if ((target = (unsigned char) acs_chars[n + 1]) != 0) {
1.19 nicm 1812: mapped[source] = (char) target;
1.10 millert 1813: n++;
1814: } else {
1.19 nicm 1815: extra = (char) source;
1.1 millert 1816: }
1817: }
1.10 millert 1818: for (n = m = 0; n < sizeof(mapped); n++) {
1819: if (mapped[n]) {
1.19 nicm 1820: acs_chars[m++] = (char) n;
1.10 millert 1821: acs_chars[m++] = mapped[n];
1.1 millert 1822: }
1823: }
1.10 millert 1824: if (extra)
1825: acs_chars[m++] = extra; /* garbage in, garbage out */
1826: acs_chars[m] = 0;
1.1 millert 1827: }
1.10 millert 1828: }
1.1 millert 1829: }