1 /**************************************************************************** 2 * Copyright 2018-2019,2020 Thomas E. Dickey * 3 * Copyright 1998-2016,2017 Free Software Foundation, Inc. * 4 * * 5 * Permission is hereby granted, free of charge, to any person obtaining a * 6 * copy of this software and associated documentation files (the * 7 * "Software"), to deal in the Software without restriction, including * 8 * without limitation the rights to use, copy, modify, merge, publish, * 9 * distribute, distribute with modifications, sublicense, and/or sell * 10 * copies of the Software, and to permit persons to whom the Software is * 11 * furnished to do so, subject to the following conditions: * 12 * * 13 * The above copyright notice and this permission notice shall be included * 14 * in all copies or substantial portions of the Software. * 15 * * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 23 * * 24 * Except as contained in this notice, the name(s) of the above copyright * 25 * holders shall not be used in advertising or otherwise to promote the * 26 * sale, use or other dealings in this Software without prior written * 27 * authorization. * 28 ****************************************************************************/ 29 30 /**************************************************************************** 31 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 32 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 33 * and: Thomas E. Dickey 1996-on * 34 ****************************************************************************/ 35 36 /* 37 * captoinfo.c 38 * 39 * Provide conversion in both directions between termcap and terminfo. 40 * 41 * cap-to-info --- conversion between termcap and terminfo formats 42 * 43 * The captoinfo() code was swiped from Ross Ridge's mytinfo package, 44 * adapted to fit ncurses by Eric S. Raymond <esr@snark.thyrsus.com>. 45 * 46 * It has just one entry point: 47 * 48 * char *_nc_captoinfo(n, s, parameterized) 49 * 50 * Convert value s for termcap string capability named n into terminfo 51 * format. 52 * 53 * This code recognizes all the standard 4.4BSD %-escapes: 54 * 55 * %% output `%' 56 * %d output value as in printf %d 57 * %2 output value as in printf %2d 58 * %3 output value as in printf %3d 59 * %. output value as in printf %c 60 * %+x add x to value, then do %. 61 * %>xy if value > x then add y, no output 62 * %r reverse order of two parameters, no output 63 * %i increment by one, no output 64 * %n exclusive-or all parameters with 0140 (Datamedia 2500) 65 * %B BCD (16*(value/10)) + (value%10), no output 66 * %D Reverse coding (value - 2*(value%16)), no output (Delta Data). 67 * 68 * Also, %02 and %03 are accepted as synonyms for %2 and %3. 69 * 70 * Besides all the standard termcap escapes, this translator understands 71 * the following extended escapes: 72 * 73 * used by GNU Emacs termcap libraries 74 * %a[+*-/=][cp]x GNU arithmetic. 75 * %m xor the first two parameters by 0177 76 * %b backup to previous parameter 77 * %f skip this parameter 78 * 79 * used by the University of Waterloo (MFCF) termcap libraries 80 * %-x subtract parameter FROM char x and output it as a char 81 * %ax add the character x to parameter 82 * 83 * If #define WATERLOO is on, also enable these translations: 84 * 85 * %sx subtract parameter FROM the character x 86 * 87 * By default, this Waterloo translations are not compiled in, because 88 * the Waterloo %s conflicts with the way terminfo uses %s in strings for 89 * function programming. 90 * 91 * Note the two definitions of %a: the GNU definition is translated if the 92 * characters after the 'a' are valid for it, otherwise the UW definition 93 * is translated. 94 */ 95 96 #include <curses.priv.h> 97 98 #include <ctype.h> 99 #include <tic.h> 100 101 MODULE_ID("$Id: captoinfo.c,v 1.98 2020/02/02 23:34:34 tom Exp $") 102 103 #if 0 104 #define DEBUG_THIS(p) DEBUG(9, p) 105 #else 106 #define DEBUG_THIS(p) /* nothing */ 107 #endif 108 109 #define MAX_PUSHED 16 /* max # args we can push onto the stack */ 110 111 static int stack[MAX_PUSHED]; /* the stack */ 112 static int stackptr; /* the next empty place on the stack */ 113 static int onstack; /* the top of stack */ 114 static int seenm; /* seen a %m */ 115 static int seenn; /* seen a %n */ 116 static int seenr; /* seen a %r */ 117 static int param; /* current parameter */ 118 static char *dp; /* pointer to end of the converted string */ 119 120 static char *my_string; 121 static size_t my_length; 122 123 static char * 124 init_string(void) 125 /* initialize 'my_string', 'my_length' */ 126 { 127 if (my_string == 0) 128 TYPE_MALLOC(char, my_length = 256, my_string); 129 130 *my_string = '\0'; 131 return my_string; 132 } 133 134 static char * 135 save_string(char *d, const char *const s) 136 { 137 size_t have = (size_t) (d - my_string); 138 size_t need = have + strlen(s) + 2; 139 if (need > my_length) { 140 my_string = (char *) _nc_doalloc(my_string, my_length = (need + need)); 141 if (my_string == 0) 142 _nc_err_abort(MSG_NO_MEMORY); 143 d = my_string + have; 144 } 145 _nc_STRCPY(d, s, my_length - have); 146 return d + strlen(d); 147 } 148 149 static NCURSES_INLINE char * 150 save_char(char *s, int c) 151 { 152 static char temp[2]; 153 temp[0] = (char) c; 154 return save_string(s, temp); 155 } 156 157 static void 158 push(void) 159 /* push onstack on to the stack */ 160 { 161 if (stackptr >= MAX_PUSHED) 162 _nc_warning("string too complex to convert"); 163 else 164 stack[stackptr++] = onstack; 165 } 166 167 static void 168 pop(void) 169 /* pop the top of the stack into onstack */ 170 { 171 if (stackptr == 0) { 172 if (onstack == 0) 173 _nc_warning("I'm confused"); 174 else 175 onstack = 0; 176 } else 177 onstack = stack[--stackptr]; 178 param++; 179 } 180 181 static int 182 cvtchar(register const char *sp) 183 /* convert a character to a terminfo push */ 184 { 185 unsigned char c = 0; 186 int len; 187 188 switch (*sp) { 189 case '\\': 190 switch (*++sp) { 191 case '\'': 192 case '$': 193 case '\\': 194 case '%': 195 c = UChar(*sp); 196 len = 2; 197 break; 198 case '\0': 199 c = '\\'; 200 len = 1; 201 break; 202 case '0': 203 case '1': 204 case '2': 205 case '3': 206 len = 1; 207 while (isdigit(UChar(*sp))) { 208 c = UChar(8 * c + (*sp++ - '0')); 209 len++; 210 } 211 break; 212 default: 213 c = UChar(*sp); 214 len = (c != '\0') ? 2 : 1; 215 break; 216 } 217 break; 218 case '^': 219 c = UChar(*++sp); 220 if (c == '?') 221 c = 127; 222 else 223 c &= 0x1f; 224 len = 2; 225 break; 226 default: 227 c = UChar(*sp); 228 len = (c != '\0') ? 1 : 0; 229 } 230 if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') { 231 dp = save_string(dp, "%\'"); 232 dp = save_char(dp, c); 233 dp = save_char(dp, '\''); 234 } else if (c != '\0') { 235 dp = save_string(dp, "%{"); 236 if (c > 99) 237 dp = save_char(dp, c / 100 + '0'); 238 if (c > 9) 239 dp = save_char(dp, ((int) (c / 10)) % 10 + '0'); 240 dp = save_char(dp, c % 10 + '0'); 241 dp = save_char(dp, '}'); 242 } 243 return len; 244 } 245 246 static void 247 getparm(int parm, int n) 248 /* push n copies of param on the terminfo stack if not already there */ 249 { 250 int nn; 251 252 if (seenr) { 253 if (parm == 1) 254 parm = 2; 255 else if (parm == 2) 256 parm = 1; 257 } 258 259 for (nn = 0; nn < n; ++nn) { 260 dp = save_string(dp, "%p"); 261 dp = save_char(dp, '0' + parm); 262 } 263 264 if (onstack == parm) { 265 if (n > 1) { 266 _nc_warning("string may not be optimal"); 267 dp = save_string(dp, "%Pa"); 268 while (n-- > 0) { 269 dp = save_string(dp, "%ga"); 270 } 271 } 272 return; 273 } 274 if (onstack != 0) 275 push(); 276 277 onstack = parm; 278 279 if (seenn && parm < 3) { 280 dp = save_string(dp, "%{96}%^"); 281 } 282 283 if (seenm && parm < 3) { 284 dp = save_string(dp, "%{127}%^"); 285 } 286 } 287 288 /* 289 * Convert a termcap string to terminfo format. 290 * 'cap' is the relevant terminfo capability index. 291 * 's' is the string value of the capability. 292 * 'parameterized' tells what type of translations to do: 293 * % translations if 1 294 * pad translations if >=0 295 */ 296 NCURSES_EXPORT(char *) 297 _nc_captoinfo(const char *cap, const char *s, int const parameterized) 298 { 299 const char *capstart; 300 301 stackptr = 0; 302 onstack = 0; 303 seenm = 0; 304 seenn = 0; 305 seenr = 0; 306 param = 1; 307 308 DEBUG_THIS(("_nc_captoinfo params %d, %s", parameterized, s)); 309 310 dp = init_string(); 311 312 /* skip the initial padding (if we haven't been told not to) */ 313 capstart = 0; 314 if (s == 0) 315 s = ""; 316 if (parameterized >= 0 && isdigit(UChar(*s))) 317 for (capstart = s; *s != '\0'; s++) 318 if (!(isdigit(UChar(*s)) || *s == '*' || *s == '.')) 319 break; 320 321 while (*s != '\0') { 322 switch (*s) { 323 case '%': 324 s++; 325 if (parameterized < 1) { 326 dp = save_char(dp, '%'); 327 break; 328 } 329 switch (*s++) { 330 case '%': 331 dp = save_string(dp, "%%"); 332 break; 333 case 'r': 334 if (seenr++ == 1) { 335 _nc_warning("saw %%r twice in %s", cap); 336 } 337 break; 338 case 'm': 339 if (seenm++ == 1) { 340 _nc_warning("saw %%m twice in %s", cap); 341 } 342 break; 343 case 'n': 344 if (seenn++ == 1) { 345 _nc_warning("saw %%n twice in %s", cap); 346 } 347 break; 348 case 'i': 349 dp = save_string(dp, "%i"); 350 break; 351 case '6': 352 case 'B': 353 getparm(param, 1); 354 dp = save_string(dp, "%{10}%/%{16}%*"); 355 getparm(param, 1); 356 dp = save_string(dp, "%{10}%m%+"); 357 break; 358 case '8': 359 case 'D': 360 getparm(param, 2); 361 dp = save_string(dp, "%{2}%*%-"); 362 break; 363 case '>': 364 /* %?%{x}%>%t%{y}%+%; */ 365 if (s[0] && s[1]) { 366 getparm(param, 2); 367 dp = save_string(dp, "%?"); 368 s += cvtchar(s); 369 dp = save_string(dp, "%>%t"); 370 s += cvtchar(s); 371 dp = save_string(dp, "%+%;"); 372 } else { 373 _nc_warning("expected two characters after %%>"); 374 dp = save_string(dp, "%>"); 375 } 376 break; 377 case 'a': 378 if ((*s == '=' || *s == '+' || *s == '-' 379 || *s == '*' || *s == '/') 380 && (s[1] == 'p' || s[1] == 'c') 381 && s[2] != '\0') { 382 int l; 383 l = 2; 384 if (*s != '=') 385 getparm(param, 1); 386 if (s[1] == 'p') { 387 getparm(param + s[2] - '@', 1); 388 if (param != onstack) { 389 pop(); 390 param--; 391 } 392 l++; 393 } else 394 l += cvtchar(s + 2); 395 switch (*s) { 396 case '+': 397 dp = save_string(dp, "%+"); 398 break; 399 case '-': 400 dp = save_string(dp, "%-"); 401 break; 402 case '*': 403 dp = save_string(dp, "%*"); 404 break; 405 case '/': 406 dp = save_string(dp, "%/"); 407 break; 408 case '=': 409 if (seenr) { 410 if (param == 1) 411 onstack = 2; 412 else if (param == 2) 413 onstack = 1; 414 else 415 onstack = param; 416 } else 417 onstack = param; 418 break; 419 } 420 s += l; 421 break; 422 } 423 getparm(param, 1); 424 s += cvtchar(s); 425 dp = save_string(dp, "%+"); 426 break; 427 case '+': 428 getparm(param, 1); 429 s += cvtchar(s); 430 dp = save_string(dp, "%+%c"); 431 pop(); 432 break; 433 case 's': 434 #ifdef WATERLOO 435 s += cvtchar(s); 436 getparm(param, 1); 437 dp = save_string(dp, "%-"); 438 #else 439 getparm(param, 1); 440 dp = save_string(dp, "%s"); 441 pop(); 442 #endif /* WATERLOO */ 443 break; 444 case '-': 445 s += cvtchar(s); 446 getparm(param, 1); 447 dp = save_string(dp, "%-%c"); 448 pop(); 449 break; 450 case '.': 451 getparm(param, 1); 452 dp = save_string(dp, "%c"); 453 pop(); 454 break; 455 case '0': /* not clear any of the historical termcaps did this */ 456 if (*s == '3') { 457 ++s; 458 goto see03; 459 } 460 if (*s == '2') { 461 ++s; 462 goto see02; 463 } 464 goto invalid; 465 case '2': 466 see02: 467 getparm(param, 1); 468 dp = save_string(dp, "%2d"); 469 pop(); 470 break; 471 case '3': 472 see03: 473 getparm(param, 1); 474 dp = save_string(dp, "%3d"); 475 pop(); 476 break; 477 case 'd': 478 getparm(param, 1); 479 dp = save_string(dp, "%d"); 480 pop(); 481 break; 482 case 'f': 483 param++; 484 break; 485 case 'b': 486 param--; 487 break; 488 case '\\': 489 dp = save_string(dp, "%\\"); 490 break; 491 default: 492 invalid: 493 dp = save_char(dp, '%'); 494 s--; 495 _nc_warning("unknown %% code %s (%#x) in %s", 496 unctrl((chtype) *s), UChar(*s), cap); 497 break; 498 } 499 break; 500 default: 501 if (*s != '\0') 502 dp = save_char(dp, *s++); 503 break; 504 } 505 } 506 507 /* 508 * Now, if we stripped off some leading padding, add it at the end 509 * of the string as mandatory padding. 510 */ 511 if (capstart) { 512 dp = save_string(dp, "$<"); 513 for (s = capstart; *s != '\0'; s++) 514 if (isdigit(UChar(*s)) || *s == '*' || *s == '.') 515 dp = save_char(dp, *s); 516 else 517 break; 518 dp = save_string(dp, "/>"); 519 } 520 521 (void) save_char(dp, '\0'); 522 523 DEBUG_THIS(("... _nc_captoinfo %s", NonNull(my_string))); 524 525 return (my_string); 526 } 527 528 /* 529 * Check for an expression that corresponds to "%B" (BCD): 530 * (parameter / 10) * 16 + (parameter % 10) 531 */ 532 static int 533 bcd_expression(const char *str) 534 { 535 /* leave this non-const for HPUX */ 536 static char fmt[] = "%%p%c%%{10}%%/%%{16}%%*%%p%c%%{10}%%m%%+"; 537 int len = 0; 538 char ch1, ch2; 539 540 if (sscanf(str, fmt, &ch1, &ch2) == 2 541 && isdigit(UChar(ch1)) 542 && isdigit(UChar(ch2)) 543 && (ch1 == ch2)) { 544 len = 28; 545 #ifndef NDEBUG 546 { 547 char buffer[80]; 548 int tst; 549 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) fmt, ch1, ch2); 550 tst = strlen(buffer) - 1; 551 assert(len == tst); 552 } 553 #endif 554 } 555 return len; 556 } 557 558 static char * 559 save_tc_char(char *bufptr, int c1) 560 { 561 if (is7bits(c1) && isprint(c1)) { 562 if (c1 == ':' || c1 == '\\') 563 bufptr = save_char(bufptr, '\\'); 564 bufptr = save_char(bufptr, c1); 565 } else { 566 char temp[80]; 567 568 if (c1 == (c1 & 0x1f)) { /* iscntrl() returns T on 255 */ 569 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) 570 "%.20s", unctrl((chtype) c1)); 571 } else { 572 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) 573 "\\%03o", c1); 574 } 575 bufptr = save_string(bufptr, temp); 576 } 577 return bufptr; 578 } 579 580 static char * 581 save_tc_inequality(char *bufptr, int c1, int c2) 582 { 583 bufptr = save_string(bufptr, "%>"); 584 bufptr = save_tc_char(bufptr, c1); 585 bufptr = save_tc_char(bufptr, c2); 586 return bufptr; 587 } 588 589 /* 590 * info-to-cap --- conversion between terminfo and termcap formats 591 * 592 * Here are the capabilities infotocap assumes it can translate to: 593 * 594 * %% output `%' 595 * %d output value as in printf %d 596 * %2 output value as in printf %2d 597 * %3 output value as in printf %3d 598 * %. output value as in printf %c 599 * %+c add character c to value, then do %. 600 * %>xy if value > x then add y, no output 601 * %r reverse order of two parameters, no output 602 * %i increment by one, no output 603 * %n exclusive-or all parameters with 0140 (Datamedia 2500) 604 * %B BCD (16*(value/10)) + (value%10), no output 605 * %D Reverse coding (value - 2*(value%16)), no output (Delta Data). 606 * %m exclusive-or all parameters with 0177 (not in 4.4BSD) 607 */ 608 609 #define octal_fixup(n, c) fixups[n].ch = ((fixups[n].ch << 3) | ((c) - '0')) 610 611 /* 612 * Convert a terminfo string to termcap format. Parameters are as in 613 * _nc_captoinfo(). 614 */ 615 NCURSES_EXPORT(char *) 616 _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameterized) 617 { 618 int seenone = 0, seentwo = 0, saw_m = 0, saw_n = 0; 619 const char *padding; 620 const char *trimmed = 0; 621 int in0, in1, in2; 622 char ch1 = 0, ch2 = 0; 623 char *bufptr = init_string(); 624 char octal[4]; 625 int len; 626 int digits; 627 bool syntax_error = FALSE; 628 int myfix = 0; 629 struct { 630 int ch; 631 int offset; 632 } fixups[MAX_TC_FIXUPS]; 633 634 DEBUG_THIS(("_nc_infotocap params %d, %s", parameterized, str)); 635 636 /* we may have to move some trailing mandatory padding up front */ 637 padding = str + strlen(str) - 1; 638 if (padding > str && *padding == '>') { 639 if (*--padding == '/') 640 --padding; 641 while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*') 642 padding--; 643 if (padding > str && *padding == '<' && *--padding == '$') 644 trimmed = padding; 645 padding += 2; 646 647 while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*') 648 bufptr = save_char(bufptr, *padding++); 649 } 650 651 for (; !syntax_error && 652 *str && 653 ((trimmed == 0) || (str < trimmed)); str++) { 654 int c1, c2; 655 char *cp = 0; 656 657 if (str[0] == '^') { 658 if (str[1] == '\0' || (str + 1) == trimmed) { 659 bufptr = save_string(bufptr, "\\136"); 660 ++str; 661 } else if (str[1] == '?') { 662 /* 663 * Although the 4.3BSD termcap file has an instance of "kb=^?", 664 * that appears to be just cut/paste since neither 4.3BSD nor 665 * 4.4BSD termcap interprets "^?" as DEL. 666 */ 667 bufptr = save_string(bufptr, "\\177"); 668 ++str; 669 } else { 670 bufptr = save_char(bufptr, *str++); 671 bufptr = save_char(bufptr, *str); 672 } 673 } else if (str[0] == '\\') { 674 if (str[1] == '\0' || (str + 1) == trimmed) { 675 bufptr = save_string(bufptr, "\\134"); 676 ++str; 677 } else if (str[1] == '^') { 678 bufptr = save_string(bufptr, "\\136"); 679 ++str; 680 } else if (str[1] == ',') { 681 bufptr = save_char(bufptr, *++str); 682 } else { 683 int xx1; 684 685 bufptr = save_char(bufptr, *str++); 686 xx1 = *str; 687 if (_nc_strict_bsd) { 688 689 if (isoctal(UChar(xx1))) { 690 int pad = 0; 691 int xx2; 692 int fix = 0; 693 694 if (!isoctal(UChar(str[1]))) 695 pad = 2; 696 else if (str[1] && !isoctal(UChar(str[2]))) 697 pad = 1; 698 699 /* 700 * Test for "\0", "\00" or "\000" and transform those 701 * into "\200". 702 */ 703 if (xx1 == '0' 704 && ((pad == 2) || (str[1] == '0')) 705 && ((pad >= 1) || (str[2] == '0'))) { 706 xx2 = '2'; 707 } else { 708 xx2 = '0'; 709 pad = 0; /* FIXME - optionally pad to 3 digits */ 710 } 711 if (myfix < MAX_TC_FIXUPS) { 712 fix = 3 - pad; 713 fixups[myfix].ch = 0; 714 fixups[myfix].offset = (int) (bufptr 715 - my_string 716 - 1); 717 } 718 while (pad-- > 0) { 719 bufptr = save_char(bufptr, xx2); 720 if (myfix < MAX_TC_FIXUPS) { 721 fixups[myfix].ch <<= 3; 722 fixups[myfix].ch |= (xx2 - '0'); 723 } 724 xx2 = '0'; 725 } 726 if (myfix < MAX_TC_FIXUPS) { 727 int n; 728 for (n = 0; n < fix; ++n) { 729 fixups[myfix].ch <<= 3; 730 fixups[myfix].ch |= (str[n] - '0'); 731 } 732 if (fixups[myfix].ch < 32) { 733 ++myfix; 734 } 735 } 736 } else if (strchr("E\\nrtbf", xx1) == 0) { 737 switch (xx1) { 738 case 'e': 739 xx1 = 'E'; 740 break; 741 case 'l': 742 xx1 = 'n'; 743 break; 744 case 's': 745 bufptr = save_char(bufptr, '0'); 746 bufptr = save_char(bufptr, '4'); 747 xx1 = '0'; 748 break; 749 case ':': 750 /* 751 * Note: termcap documentation claims that ":" 752 * must be escaped as "\072", however the 753 * documentation is incorrect - read the code. 754 * The replacement does not work reliably, 755 * so the advice is not helpful. 756 */ 757 bufptr = save_char(bufptr, '0'); 758 bufptr = save_char(bufptr, '7'); 759 xx1 = '2'; 760 break; 761 default: 762 /* should not happen, but handle this anyway */ 763 _nc_SPRINTF(octal, _nc_SLIMIT(sizeof(octal)) 764 "%03o", UChar(xx1)); 765 bufptr = save_char(bufptr, octal[0]); 766 bufptr = save_char(bufptr, octal[1]); 767 xx1 = octal[2]; 768 break; 769 } 770 } 771 } else { 772 if (myfix < MAX_TC_FIXUPS && isoctal(UChar(xx1))) { 773 bool will_fix = TRUE; 774 int n; 775 776 fixups[myfix].ch = 0; 777 fixups[myfix].offset = (int) (bufptr - my_string - 1); 778 for (n = 0; n < 3; ++n) { 779 if (isoctal(str[n])) { 780 octal_fixup(myfix, str[n]); 781 } else { 782 will_fix = FALSE; 783 break; 784 } 785 } 786 if (will_fix && (fixups[myfix].ch < 32)) 787 ++myfix; 788 } 789 } 790 bufptr = save_char(bufptr, xx1); 791 } 792 } else if (str[0] == '$' && str[1] == '<') { /* discard padding */ 793 str += 2; 794 while (isdigit(UChar(*str)) 795 || *str == '.' 796 || *str == '*' 797 || *str == '/' 798 || *str == '>') 799 str++; 800 --str; 801 } else if (sscanf(str, 802 "[%%?%%p1%%{8}%%<%%t%d%%p1%%d%%e%%p1%%{16}%%<%%t%d%%p1%%{8}%%-%%d%%e%d;5;%%p1%%d%%;m", 803 &in0, &in1, &in2) == 3 804 && ((in0 == 4 && in1 == 10 && in2 == 48) 805 || (in0 == 3 && in1 == 9 && in2 == 38))) { 806 /* dumb-down an optimized case from xterm-256color for termcap */ 807 if ((str = strstr(str, ";m")) == 0) 808 break; /* cannot happen */ 809 ++str; 810 if (in2 == 48) { 811 bufptr = save_string(bufptr, "[48;5;%dm"); 812 } else { 813 bufptr = save_string(bufptr, "[38;5;%dm"); 814 } 815 } else if (str[0] == '%' && str[1] == '%') { /* escaped '%' */ 816 bufptr = save_string(bufptr, "%%"); 817 ++str; 818 } else if (*str != '%' || (parameterized < 1)) { 819 bufptr = save_char(bufptr, *str); 820 } else if (sscanf(str, "%%?%%{%d}%%>%%t%%{%d}%%+%%;", &c1, &c2) == 2) { 821 str = strchr(str, ';'); 822 bufptr = save_tc_inequality(bufptr, c1, c2); 823 } else if (sscanf(str, "%%?%%{%d}%%>%%t%%'%c'%%+%%;", &c1, &ch2) == 2) { 824 str = strchr(str, ';'); 825 bufptr = save_tc_inequality(bufptr, c1, ch2); 826 } else if (sscanf(str, "%%?%%'%c'%%>%%t%%{%d}%%+%%;", &ch1, &c2) == 2) { 827 str = strchr(str, ';'); 828 bufptr = save_tc_inequality(bufptr, ch1, c2); 829 } else if (sscanf(str, "%%?%%'%c'%%>%%t%%'%c'%%+%%;", &ch1, &ch2) == 2) { 830 str = strchr(str, ';'); 831 bufptr = save_tc_inequality(bufptr, ch1, ch2); 832 } else if ((len = bcd_expression(str)) != 0) { 833 str += len; 834 bufptr = save_string(bufptr, "%B"); 835 } else if ((sscanf(str, "%%{%d}%%+%%%c", &c1, &ch2) == 2 836 || sscanf(str, "%%'%c'%%+%%%c", &ch1, &ch2) == 2) 837 && ch2 == 'c' 838 && (cp = strchr(str, '+'))) { 839 str = cp + 2; 840 bufptr = save_string(bufptr, "%+"); 841 842 if (ch1) 843 c1 = ch1; 844 bufptr = save_tc_char(bufptr, c1); 845 } 846 /* FIXME: this "works" for 'delta' */ 847 else if (strncmp(str, "%{2}%*%-", (size_t) 8) == 0) { 848 str += 7; 849 bufptr = save_string(bufptr, "%D"); 850 } else if (strncmp(str, "%{96}%^", (size_t) 7) == 0) { 851 str += 6; 852 if (saw_m++ == 0) { 853 bufptr = save_string(bufptr, "%n"); 854 } 855 } else if (strncmp(str, "%{127}%^", (size_t) 8) == 0) { 856 str += 7; 857 if (saw_n++ == 0) { 858 bufptr = save_string(bufptr, "%m"); 859 } 860 } else { /* cm-style format element */ 861 str++; 862 switch (*str) { 863 case '%': 864 bufptr = save_char(bufptr, '%'); 865 break; 866 867 case '0': 868 case '1': 869 case '2': 870 case '3': 871 case '4': 872 case '5': 873 case '6': 874 case '7': 875 case '8': 876 case '9': 877 bufptr = save_char(bufptr, '%'); 878 ch1 = 0; 879 ch2 = 0; 880 digits = 0; 881 while (isdigit(UChar(*str))) { 882 if (++digits > 2) { 883 syntax_error = TRUE; 884 break; 885 } 886 ch2 = ch1; 887 ch1 = *str++; 888 if (digits == 2 && ch2 != '0') { 889 syntax_error = TRUE; 890 break; 891 } else if (_nc_strict_bsd) { 892 if (ch1 > '3') { 893 syntax_error = TRUE; 894 break; 895 } 896 } else { 897 bufptr = save_char(bufptr, ch1); 898 } 899 } 900 if (syntax_error) 901 break; 902 /* 903 * Convert %02 to %2 and %03 to %3 904 */ 905 if (ch2 == '0' && !_nc_strict_bsd) { 906 ch2 = 0; 907 bufptr[-2] = bufptr[-1]; 908 *--bufptr = 0; 909 } 910 if (_nc_strict_bsd) { 911 if (ch2 != 0 && ch2 != '0') { 912 syntax_error = TRUE; 913 } else if (ch1 < '2') { 914 ch1 = 'd'; 915 } 916 bufptr = save_char(bufptr, ch1); 917 } 918 if (strchr("oxX.", *str)) { 919 syntax_error = TRUE; /* termcap doesn't have octal, hex */ 920 } 921 break; 922 923 case 'd': 924 bufptr = save_string(bufptr, "%d"); 925 break; 926 927 case 'c': 928 bufptr = save_string(bufptr, "%."); 929 break; 930 931 /* 932 * %s isn't in termcap, but it's convenient to pass it through 933 * so we can represent things like terminfo pfkey strings in 934 * termcap notation. 935 */ 936 case 's': 937 if (_nc_strict_bsd) { 938 syntax_error = TRUE; 939 } else { 940 bufptr = save_string(bufptr, "%s"); 941 } 942 break; 943 944 case 'p': 945 str++; 946 if (*str == '1') 947 seenone = 1; 948 else if (*str == '2') { 949 if (!seenone && !seentwo) { 950 bufptr = save_string(bufptr, "%r"); 951 seentwo++; 952 } 953 } else if (*str >= '3') { 954 syntax_error = TRUE; 955 } 956 break; 957 958 case 'i': 959 bufptr = save_string(bufptr, "%i"); 960 break; 961 962 default: 963 bufptr = save_char(bufptr, *str); 964 syntax_error = TRUE; 965 break; 966 } /* endswitch (*str) */ 967 } /* endelse (*str == '%') */ 968 969 /* 970 * 'str' always points to the end of what was scanned in this step, 971 * but that may not be the end of the string. 972 */ 973 assert(str != 0); 974 if (str == 0 || *str == '\0') 975 break; 976 977 } /* endwhile (*str) */ 978 979 if (!syntax_error && 980 myfix > 0 && 981 ((int) strlen(my_string) - (4 * myfix)) < MIN_TC_FIXUPS) { 982 while (--myfix >= 0) { 983 char *p = fixups[myfix].offset + my_string; 984 *p++ = '^'; 985 *p++ = (char) (fixups[myfix].ch | '@'); 986 while ((p[0] = p[2]) != '\0') { 987 ++p; 988 } 989 } 990 } 991 992 DEBUG_THIS(("... _nc_infotocap %s", 993 syntax_error 994 ? "<ERR>" 995 : _nc_visbuf(my_string))); 996 997 return (syntax_error ? NULL : my_string); 998 } 999 1000 #ifdef MAIN 1001 1002 int curr_line; 1003 1004 int 1005 main(int argc, char *argv[]) 1006 { 1007 int c, tc = FALSE; 1008 1009 while ((c = getopt(argc, argv, "c")) != EOF) 1010 switch (c) { 1011 case 'c': 1012 tc = TRUE; 1013 break; 1014 } 1015 1016 curr_line = 0; 1017 for (;;) { 1018 char buf[BUFSIZ]; 1019 1020 ++curr_line; 1021 if (fgets(buf, sizeof(buf), stdin) == 0) 1022 break; 1023 buf[strlen(buf) - 1] = '\0'; 1024 _nc_set_source(buf); 1025 1026 if (tc) { 1027 char *cp = _nc_infotocap("to termcap", buf, 1); 1028 1029 if (cp) 1030 (void) fputs(cp, stdout); 1031 } else 1032 (void) fputs(_nc_captoinfo("to terminfo", buf, 1), stdout); 1033 (void) putchar('\n'); 1034 } 1035 return (0); 1036 } 1037 #endif /* MAIN */ 1038 1039 #if NO_LEAKS 1040 NCURSES_EXPORT(void) 1041 _nc_captoinfo_leaks(void) 1042 { 1043 if (my_string != 0) { 1044 FreeAndNull(my_string); 1045 } 1046 my_length = 0; 1047 } 1048 #endif 1049