Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1 | /**************************************************************************** |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 2 | * Copyright 2018-2020,2021 Thomas E. Dickey * |
| 3 | * Copyright 1998-2016,2017 Free Software Foundation, Inc. * |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 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 | /* |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 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 |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 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 | * |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 46 | * It has just one entry point: |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 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 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 101 | MODULE_ID("$Id: captoinfo.c,v 1.102 2021/09/04 10:29:15 tom Exp $") |
| 102 | |
| 103 | #if 0 |
| 104 | #define DEBUG_THIS(p) DEBUG(9, p) |
| 105 | #else |
| 106 | #define DEBUG_THIS(p) /* nothing */ |
| 107 | #endif |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 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) |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 128 | TYPE_MALLOC(char, my_length = 256, my_string); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 129 | |
| 130 | *my_string = '\0'; |
| 131 | return my_string; |
| 132 | } |
| 133 | |
| 134 | static char * |
| 135 | save_string(char *d, const char *const s) |
| 136 | { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 137 | size_t have = (size_t) (d - my_string); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 138 | size_t need = have + strlen(s) + 2; |
| 139 | if (need > my_length) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 140 | my_string = (char *) _nc_doalloc(my_string, my_length = (need + need)); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 141 | if (my_string == 0) |
| 142 | _nc_err_abort(MSG_NO_MEMORY); |
| 143 | d = my_string + have; |
| 144 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 145 | _nc_STRCPY(d, s, my_length - have); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 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 '%': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 195 | c = UChar(*sp); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 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))) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 208 | c = UChar(8 * c + (*sp++ - '0')); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 209 | len++; |
| 210 | } |
| 211 | break; |
| 212 | default: |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 213 | c = UChar(*sp); |
| 214 | len = (c != '\0') ? 2 : 1; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 215 | break; |
| 216 | } |
| 217 | break; |
| 218 | case '^': |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 219 | len = 2; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 220 | c = UChar(*++sp); |
| 221 | if (c == '?') { |
| 222 | c = 127; |
| 223 | } else if (c == '\0') { |
| 224 | len = 1; |
| 225 | } else { |
| 226 | c &= 0x1f; |
| 227 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 228 | break; |
| 229 | default: |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 230 | c = UChar(*sp); |
| 231 | len = (c != '\0') ? 1 : 0; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 232 | } |
| 233 | if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') { |
| 234 | dp = save_string(dp, "%\'"); |
| 235 | dp = save_char(dp, c); |
| 236 | dp = save_char(dp, '\''); |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 237 | } else if (c != '\0') { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 238 | dp = save_string(dp, "%{"); |
| 239 | if (c > 99) |
| 240 | dp = save_char(dp, c / 100 + '0'); |
| 241 | if (c > 9) |
| 242 | dp = save_char(dp, ((int) (c / 10)) % 10 + '0'); |
| 243 | dp = save_char(dp, c % 10 + '0'); |
| 244 | dp = save_char(dp, '}'); |
| 245 | } |
| 246 | return len; |
| 247 | } |
| 248 | |
| 249 | static void |
| 250 | getparm(int parm, int n) |
| 251 | /* push n copies of param on the terminfo stack if not already there */ |
| 252 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 253 | int nn; |
| 254 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 255 | if (seenr) { |
| 256 | if (parm == 1) |
| 257 | parm = 2; |
| 258 | else if (parm == 2) |
| 259 | parm = 1; |
| 260 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 261 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 262 | for (nn = 0; nn < n; ++nn) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 263 | dp = save_string(dp, "%p"); |
| 264 | dp = save_char(dp, '0' + parm); |
| 265 | } |
| 266 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 267 | if (onstack == parm) { |
| 268 | if (n > 1) { |
| 269 | _nc_warning("string may not be optimal"); |
| 270 | dp = save_string(dp, "%Pa"); |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 271 | while (n-- > 0) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 272 | dp = save_string(dp, "%ga"); |
| 273 | } |
| 274 | } |
| 275 | return; |
| 276 | } |
| 277 | if (onstack != 0) |
| 278 | push(); |
| 279 | |
| 280 | onstack = parm; |
| 281 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 282 | if (seenn && parm < 3) { |
| 283 | dp = save_string(dp, "%{96}%^"); |
| 284 | } |
| 285 | |
| 286 | if (seenm && parm < 3) { |
| 287 | dp = save_string(dp, "%{127}%^"); |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | /* |
| 292 | * Convert a termcap string to terminfo format. |
| 293 | * 'cap' is the relevant terminfo capability index. |
| 294 | * 's' is the string value of the capability. |
| 295 | * 'parameterized' tells what type of translations to do: |
| 296 | * % translations if 1 |
| 297 | * pad translations if >=0 |
| 298 | */ |
| 299 | NCURSES_EXPORT(char *) |
| 300 | _nc_captoinfo(const char *cap, const char *s, int const parameterized) |
| 301 | { |
| 302 | const char *capstart; |
| 303 | |
| 304 | stackptr = 0; |
| 305 | onstack = 0; |
| 306 | seenm = 0; |
| 307 | seenn = 0; |
| 308 | seenr = 0; |
| 309 | param = 1; |
| 310 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 311 | DEBUG_THIS(("_nc_captoinfo params %d, %s", parameterized, s)); |
| 312 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 313 | dp = init_string(); |
| 314 | |
| 315 | /* skip the initial padding (if we haven't been told not to) */ |
| 316 | capstart = 0; |
| 317 | if (s == 0) |
| 318 | s = ""; |
| 319 | if (parameterized >= 0 && isdigit(UChar(*s))) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 320 | for (capstart = s; *s != '\0'; s++) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 321 | if (!(isdigit(UChar(*s)) || *s == '*' || *s == '.')) |
| 322 | break; |
| 323 | |
| 324 | while (*s != '\0') { |
| 325 | switch (*s) { |
| 326 | case '%': |
| 327 | s++; |
| 328 | if (parameterized < 1) { |
| 329 | dp = save_char(dp, '%'); |
| 330 | break; |
| 331 | } |
| 332 | switch (*s++) { |
| 333 | case '%': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 334 | dp = save_string(dp, "%%"); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 335 | break; |
| 336 | case 'r': |
| 337 | if (seenr++ == 1) { |
| 338 | _nc_warning("saw %%r twice in %s", cap); |
| 339 | } |
| 340 | break; |
| 341 | case 'm': |
| 342 | if (seenm++ == 1) { |
| 343 | _nc_warning("saw %%m twice in %s", cap); |
| 344 | } |
| 345 | break; |
| 346 | case 'n': |
| 347 | if (seenn++ == 1) { |
| 348 | _nc_warning("saw %%n twice in %s", cap); |
| 349 | } |
| 350 | break; |
| 351 | case 'i': |
| 352 | dp = save_string(dp, "%i"); |
| 353 | break; |
| 354 | case '6': |
| 355 | case 'B': |
| 356 | getparm(param, 1); |
| 357 | dp = save_string(dp, "%{10}%/%{16}%*"); |
| 358 | getparm(param, 1); |
| 359 | dp = save_string(dp, "%{10}%m%+"); |
| 360 | break; |
| 361 | case '8': |
| 362 | case 'D': |
| 363 | getparm(param, 2); |
| 364 | dp = save_string(dp, "%{2}%*%-"); |
| 365 | break; |
| 366 | case '>': |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 367 | /* %?%{x}%>%t%{y}%+%; */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 368 | if (s[0] && s[1]) { |
| 369 | getparm(param, 2); |
| 370 | dp = save_string(dp, "%?"); |
| 371 | s += cvtchar(s); |
| 372 | dp = save_string(dp, "%>%t"); |
| 373 | s += cvtchar(s); |
| 374 | dp = save_string(dp, "%+%;"); |
| 375 | } else { |
| 376 | _nc_warning("expected two characters after %%>"); |
| 377 | dp = save_string(dp, "%>"); |
| 378 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 379 | break; |
| 380 | case 'a': |
| 381 | if ((*s == '=' || *s == '+' || *s == '-' |
| 382 | || *s == '*' || *s == '/') |
| 383 | && (s[1] == 'p' || s[1] == 'c') |
| 384 | && s[2] != '\0') { |
| 385 | int l; |
| 386 | l = 2; |
| 387 | if (*s != '=') |
| 388 | getparm(param, 1); |
| 389 | if (s[1] == 'p') { |
| 390 | getparm(param + s[2] - '@', 1); |
| 391 | if (param != onstack) { |
| 392 | pop(); |
| 393 | param--; |
| 394 | } |
| 395 | l++; |
| 396 | } else |
| 397 | l += cvtchar(s + 2); |
| 398 | switch (*s) { |
| 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 | dp = save_string(dp, "%/"); |
| 410 | break; |
| 411 | case '=': |
| 412 | if (seenr) { |
| 413 | if (param == 1) |
| 414 | onstack = 2; |
| 415 | else if (param == 2) |
| 416 | onstack = 1; |
| 417 | else |
| 418 | onstack = param; |
| 419 | } else |
| 420 | onstack = param; |
| 421 | break; |
| 422 | } |
| 423 | s += l; |
| 424 | break; |
| 425 | } |
| 426 | getparm(param, 1); |
| 427 | s += cvtchar(s); |
| 428 | dp = save_string(dp, "%+"); |
| 429 | break; |
| 430 | case '+': |
| 431 | getparm(param, 1); |
| 432 | s += cvtchar(s); |
| 433 | dp = save_string(dp, "%+%c"); |
| 434 | pop(); |
| 435 | break; |
| 436 | case 's': |
| 437 | #ifdef WATERLOO |
| 438 | s += cvtchar(s); |
| 439 | getparm(param, 1); |
| 440 | dp = save_string(dp, "%-"); |
| 441 | #else |
| 442 | getparm(param, 1); |
| 443 | dp = save_string(dp, "%s"); |
| 444 | pop(); |
| 445 | #endif /* WATERLOO */ |
| 446 | break; |
| 447 | case '-': |
| 448 | s += cvtchar(s); |
| 449 | getparm(param, 1); |
| 450 | dp = save_string(dp, "%-%c"); |
| 451 | pop(); |
| 452 | break; |
| 453 | case '.': |
| 454 | getparm(param, 1); |
| 455 | dp = save_string(dp, "%c"); |
| 456 | pop(); |
| 457 | break; |
| 458 | case '0': /* not clear any of the historical termcaps did this */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 459 | if (*s == '3') { |
| 460 | ++s; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 461 | goto see03; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 462 | } |
| 463 | if (*s == '2') { |
| 464 | ++s; |
| 465 | goto see02; |
| 466 | } |
| 467 | goto invalid; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 468 | case '2': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 469 | see02: |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 470 | getparm(param, 1); |
| 471 | dp = save_string(dp, "%2d"); |
| 472 | pop(); |
| 473 | break; |
| 474 | case '3': |
| 475 | see03: |
| 476 | getparm(param, 1); |
| 477 | dp = save_string(dp, "%3d"); |
| 478 | pop(); |
| 479 | break; |
| 480 | case 'd': |
| 481 | getparm(param, 1); |
| 482 | dp = save_string(dp, "%d"); |
| 483 | pop(); |
| 484 | break; |
| 485 | case 'f': |
| 486 | param++; |
| 487 | break; |
| 488 | case 'b': |
| 489 | param--; |
| 490 | break; |
| 491 | case '\\': |
| 492 | dp = save_string(dp, "%\\"); |
| 493 | break; |
| 494 | default: |
| 495 | invalid: |
| 496 | dp = save_char(dp, '%'); |
| 497 | s--; |
| 498 | _nc_warning("unknown %% code %s (%#x) in %s", |
| 499 | unctrl((chtype) *s), UChar(*s), cap); |
| 500 | break; |
| 501 | } |
| 502 | break; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 503 | default: |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 504 | if (*s != '\0') |
| 505 | dp = save_char(dp, *s++); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 506 | break; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 507 | } |
| 508 | } |
| 509 | |
| 510 | /* |
| 511 | * Now, if we stripped off some leading padding, add it at the end |
| 512 | * of the string as mandatory padding. |
| 513 | */ |
| 514 | if (capstart) { |
| 515 | dp = save_string(dp, "$<"); |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 516 | for (s = capstart; *s != '\0'; s++) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 517 | if (isdigit(UChar(*s)) || *s == '*' || *s == '.') |
| 518 | dp = save_char(dp, *s); |
| 519 | else |
| 520 | break; |
| 521 | dp = save_string(dp, "/>"); |
| 522 | } |
| 523 | |
| 524 | (void) save_char(dp, '\0'); |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 525 | |
| 526 | DEBUG_THIS(("... _nc_captoinfo %s", NonNull(my_string))); |
| 527 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 528 | return (my_string); |
| 529 | } |
| 530 | |
| 531 | /* |
| 532 | * Check for an expression that corresponds to "%B" (BCD): |
| 533 | * (parameter / 10) * 16 + (parameter % 10) |
| 534 | */ |
| 535 | static int |
| 536 | bcd_expression(const char *str) |
| 537 | { |
| 538 | /* leave this non-const for HPUX */ |
| 539 | static char fmt[] = "%%p%c%%{10}%%/%%{16}%%*%%p%c%%{10}%%m%%+"; |
| 540 | int len = 0; |
| 541 | char ch1, ch2; |
| 542 | |
| 543 | if (sscanf(str, fmt, &ch1, &ch2) == 2 |
| 544 | && isdigit(UChar(ch1)) |
| 545 | && isdigit(UChar(ch2)) |
| 546 | && (ch1 == ch2)) { |
| 547 | len = 28; |
| 548 | #ifndef NDEBUG |
| 549 | { |
| 550 | char buffer[80]; |
| 551 | int tst; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 552 | _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) fmt, ch1, ch2); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 553 | tst = strlen(buffer) - 1; |
| 554 | assert(len == tst); |
| 555 | } |
| 556 | #endif |
| 557 | } |
| 558 | return len; |
| 559 | } |
| 560 | |
| 561 | static char * |
| 562 | save_tc_char(char *bufptr, int c1) |
| 563 | { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 564 | if (is7bits(c1) && isprint(c1)) { |
| 565 | if (c1 == ':' || c1 == '\\') |
| 566 | bufptr = save_char(bufptr, '\\'); |
| 567 | bufptr = save_char(bufptr, c1); |
| 568 | } else { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 569 | char temp[80]; |
| 570 | |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 571 | if (c1 == (c1 & 0x1f)) { /* iscntrl() returns T on 255 */ |
| 572 | _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) |
| 573 | "%.20s", unctrl((chtype) c1)); |
| 574 | } else { |
| 575 | _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) |
| 576 | "\\%03o", c1); |
| 577 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 578 | bufptr = save_string(bufptr, temp); |
| 579 | } |
| 580 | return bufptr; |
| 581 | } |
| 582 | |
| 583 | static char * |
| 584 | save_tc_inequality(char *bufptr, int c1, int c2) |
| 585 | { |
| 586 | bufptr = save_string(bufptr, "%>"); |
| 587 | bufptr = save_tc_char(bufptr, c1); |
| 588 | bufptr = save_tc_char(bufptr, c2); |
| 589 | return bufptr; |
| 590 | } |
| 591 | |
| 592 | /* |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 593 | * info-to-cap --- conversion between terminfo and termcap formats |
| 594 | * |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 595 | * Here are the capabilities infotocap assumes it can translate to: |
| 596 | * |
| 597 | * %% output `%' |
| 598 | * %d output value as in printf %d |
| 599 | * %2 output value as in printf %2d |
| 600 | * %3 output value as in printf %3d |
| 601 | * %. output value as in printf %c |
| 602 | * %+c add character c to value, then do %. |
| 603 | * %>xy if value > x then add y, no output |
| 604 | * %r reverse order of two parameters, no output |
| 605 | * %i increment by one, no output |
| 606 | * %n exclusive-or all parameters with 0140 (Datamedia 2500) |
| 607 | * %B BCD (16*(value/10)) + (value%10), no output |
| 608 | * %D Reverse coding (value - 2*(value%16)), no output (Delta Data). |
| 609 | * %m exclusive-or all parameters with 0177 (not in 4.4BSD) |
| 610 | */ |
| 611 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 612 | #define octal_fixup(n, c) fixups[n].ch = ((fixups[n].ch << 3) | ((c) - '0')) |
| 613 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 614 | /* |
| 615 | * Convert a terminfo string to termcap format. Parameters are as in |
| 616 | * _nc_captoinfo(). |
| 617 | */ |
| 618 | NCURSES_EXPORT(char *) |
| 619 | _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameterized) |
| 620 | { |
| 621 | int seenone = 0, seentwo = 0, saw_m = 0, saw_n = 0; |
| 622 | const char *padding; |
| 623 | const char *trimmed = 0; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 624 | int in0, in1, in2; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 625 | char ch1 = 0, ch2 = 0; |
| 626 | char *bufptr = init_string(); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 627 | char octal[4]; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 628 | int len; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 629 | int digits; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 630 | bool syntax_error = FALSE; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 631 | int myfix = 0; |
| 632 | struct { |
| 633 | int ch; |
| 634 | int offset; |
| 635 | } fixups[MAX_TC_FIXUPS]; |
| 636 | |
| 637 | DEBUG_THIS(("_nc_infotocap %s params %d, %s", |
| 638 | _nc_strict_bsd ? "strict" : "loose", |
| 639 | parameterized, |
| 640 | _nc_visbuf(str))); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 641 | |
| 642 | /* we may have to move some trailing mandatory padding up front */ |
| 643 | padding = str + strlen(str) - 1; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 644 | if (padding > str && *padding == '>') { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 645 | if (padding > (str + 1) && *--padding == '/') |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 646 | --padding; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 647 | while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*') |
| 648 | padding--; |
| 649 | if (padding > str && *padding == '<' && *--padding == '$') |
| 650 | trimmed = padding; |
| 651 | padding += 2; |
| 652 | |
| 653 | while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*') |
| 654 | bufptr = save_char(bufptr, *padding++); |
| 655 | } |
| 656 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 657 | for (; !syntax_error && |
| 658 | *str && |
| 659 | ((trimmed == 0) || (str < trimmed)); str++) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 660 | int c1, c2; |
| 661 | char *cp = 0; |
| 662 | |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 663 | if (str[0] == '^') { |
| 664 | if (str[1] == '\0' || (str + 1) == trimmed) { |
| 665 | bufptr = save_string(bufptr, "\\136"); |
| 666 | ++str; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 667 | } else if (str[1] == '?') { |
| 668 | /* |
| 669 | * Although the 4.3BSD termcap file has an instance of "kb=^?", |
| 670 | * that appears to be just cut/paste since neither 4.3BSD nor |
| 671 | * 4.4BSD termcap interprets "^?" as DEL. |
| 672 | */ |
| 673 | bufptr = save_string(bufptr, "\\177"); |
| 674 | ++str; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 675 | } else { |
| 676 | bufptr = save_char(bufptr, *str++); |
| 677 | bufptr = save_char(bufptr, *str); |
| 678 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 679 | } else if (str[0] == ':') { |
| 680 | bufptr = save_char(bufptr, '\\'); |
| 681 | bufptr = save_char(bufptr, '0'); |
| 682 | bufptr = save_char(bufptr, '7'); |
| 683 | bufptr = save_char(bufptr, '2'); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 684 | } else if (str[0] == '\\') { |
| 685 | if (str[1] == '\0' || (str + 1) == trimmed) { |
| 686 | bufptr = save_string(bufptr, "\\134"); |
| 687 | ++str; |
| 688 | } else if (str[1] == '^') { |
| 689 | bufptr = save_string(bufptr, "\\136"); |
| 690 | ++str; |
| 691 | } else if (str[1] == ',') { |
| 692 | bufptr = save_char(bufptr, *++str); |
| 693 | } else { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 694 | int xx1; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 695 | |
| 696 | bufptr = save_char(bufptr, *str++); |
| 697 | xx1 = *str; |
| 698 | if (_nc_strict_bsd) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 699 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 700 | if (isoctal(UChar(xx1))) { |
| 701 | int pad = 0; |
| 702 | int xx2; |
| 703 | int fix = 0; |
| 704 | |
| 705 | if (!isoctal(UChar(str[1]))) |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 706 | pad = 2; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 707 | else if (str[1] && !isoctal(UChar(str[2]))) |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 708 | pad = 1; |
| 709 | |
| 710 | /* |
| 711 | * Test for "\0", "\00" or "\000" and transform those |
| 712 | * into "\200". |
| 713 | */ |
| 714 | if (xx1 == '0' |
| 715 | && ((pad == 2) || (str[1] == '0')) |
| 716 | && ((pad >= 1) || (str[2] == '0'))) { |
| 717 | xx2 = '2'; |
| 718 | } else { |
| 719 | xx2 = '0'; |
| 720 | pad = 0; /* FIXME - optionally pad to 3 digits */ |
| 721 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 722 | if (myfix < MAX_TC_FIXUPS) { |
| 723 | fix = 3 - pad; |
| 724 | fixups[myfix].ch = 0; |
| 725 | fixups[myfix].offset = (int) (bufptr |
| 726 | - my_string |
| 727 | - 1); |
| 728 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 729 | while (pad-- > 0) { |
| 730 | bufptr = save_char(bufptr, xx2); |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 731 | if (myfix < MAX_TC_FIXUPS) { |
| 732 | fixups[myfix].ch <<= 3; |
| 733 | fixups[myfix].ch |= (xx2 - '0'); |
| 734 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 735 | xx2 = '0'; |
| 736 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 737 | if (myfix < MAX_TC_FIXUPS) { |
| 738 | int n; |
| 739 | for (n = 0; n < fix; ++n) { |
| 740 | fixups[myfix].ch <<= 3; |
| 741 | fixups[myfix].ch |= (str[n] - '0'); |
| 742 | } |
| 743 | if (fixups[myfix].ch < 32) { |
| 744 | ++myfix; |
| 745 | } |
| 746 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 747 | } else if (strchr("E\\nrtbf", xx1) == 0) { |
| 748 | switch (xx1) { |
| 749 | case 'e': |
| 750 | xx1 = 'E'; |
| 751 | break; |
| 752 | case 'l': |
| 753 | xx1 = 'n'; |
| 754 | break; |
| 755 | case 's': |
| 756 | bufptr = save_char(bufptr, '0'); |
| 757 | bufptr = save_char(bufptr, '4'); |
| 758 | xx1 = '0'; |
| 759 | break; |
| 760 | case ':': |
| 761 | /* |
| 762 | * Note: termcap documentation claims that ":" |
| 763 | * must be escaped as "\072", however the |
| 764 | * documentation is incorrect - read the code. |
| 765 | * The replacement does not work reliably, |
| 766 | * so the advice is not helpful. |
| 767 | */ |
| 768 | bufptr = save_char(bufptr, '0'); |
| 769 | bufptr = save_char(bufptr, '7'); |
| 770 | xx1 = '2'; |
| 771 | break; |
| 772 | default: |
| 773 | /* should not happen, but handle this anyway */ |
| 774 | _nc_SPRINTF(octal, _nc_SLIMIT(sizeof(octal)) |
| 775 | "%03o", UChar(xx1)); |
| 776 | bufptr = save_char(bufptr, octal[0]); |
| 777 | bufptr = save_char(bufptr, octal[1]); |
| 778 | xx1 = octal[2]; |
| 779 | break; |
| 780 | } |
| 781 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 782 | } else { |
| 783 | if (myfix < MAX_TC_FIXUPS && isoctal(UChar(xx1))) { |
| 784 | bool will_fix = TRUE; |
| 785 | int n; |
| 786 | |
| 787 | fixups[myfix].ch = 0; |
| 788 | fixups[myfix].offset = (int) (bufptr - my_string - 1); |
| 789 | for (n = 0; n < 3; ++n) { |
| 790 | if (isoctal(str[n])) { |
| 791 | octal_fixup(myfix, str[n]); |
| 792 | } else { |
| 793 | will_fix = FALSE; |
| 794 | break; |
| 795 | } |
| 796 | } |
| 797 | if (will_fix && (fixups[myfix].ch < 32)) |
| 798 | ++myfix; |
| 799 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 800 | } |
| 801 | bufptr = save_char(bufptr, xx1); |
| 802 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 803 | } else if (str[0] == '$' && str[1] == '<') { /* discard padding */ |
| 804 | str += 2; |
| 805 | while (isdigit(UChar(*str)) |
| 806 | || *str == '.' |
| 807 | || *str == '*' |
| 808 | || *str == '/' |
| 809 | || *str == '>') |
| 810 | str++; |
| 811 | --str; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 812 | } else if (sscanf(str, |
| 813 | "[%%?%%p1%%{8}%%<%%t%d%%p1%%d%%e%%p1%%{16}%%<%%t%d%%p1%%{8}%%-%%d%%e%d;5;%%p1%%d%%;m", |
| 814 | &in0, &in1, &in2) == 3 |
| 815 | && ((in0 == 4 && in1 == 10 && in2 == 48) |
| 816 | || (in0 == 3 && in1 == 9 && in2 == 38))) { |
| 817 | /* dumb-down an optimized case from xterm-256color for termcap */ |
| 818 | if ((str = strstr(str, ";m")) == 0) |
| 819 | break; /* cannot happen */ |
| 820 | ++str; |
| 821 | if (in2 == 48) { |
| 822 | bufptr = save_string(bufptr, "[48;5;%dm"); |
| 823 | } else { |
| 824 | bufptr = save_string(bufptr, "[38;5;%dm"); |
| 825 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 826 | } else if (str[0] == '%' && str[1] == '%') { /* escaped '%' */ |
| 827 | bufptr = save_string(bufptr, "%%"); |
| 828 | ++str; |
| 829 | } else if (*str != '%' || (parameterized < 1)) { |
| 830 | bufptr = save_char(bufptr, *str); |
| 831 | } else if (sscanf(str, "%%?%%{%d}%%>%%t%%{%d}%%+%%;", &c1, &c2) == 2) { |
| 832 | str = strchr(str, ';'); |
| 833 | bufptr = save_tc_inequality(bufptr, c1, c2); |
| 834 | } else if (sscanf(str, "%%?%%{%d}%%>%%t%%'%c'%%+%%;", &c1, &ch2) == 2) { |
| 835 | str = strchr(str, ';'); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 836 | bufptr = save_tc_inequality(bufptr, c1, ch2); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 837 | } else if (sscanf(str, "%%?%%'%c'%%>%%t%%{%d}%%+%%;", &ch1, &c2) == 2) { |
| 838 | str = strchr(str, ';'); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 839 | bufptr = save_tc_inequality(bufptr, ch1, c2); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 840 | } else if (sscanf(str, "%%?%%'%c'%%>%%t%%'%c'%%+%%;", &ch1, &ch2) == 2) { |
| 841 | str = strchr(str, ';'); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 842 | bufptr = save_tc_inequality(bufptr, ch1, ch2); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 843 | } else if ((len = bcd_expression(str)) != 0) { |
| 844 | str += len; |
| 845 | bufptr = save_string(bufptr, "%B"); |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 846 | } else if ((sscanf(str, "%%{%d}%%+%%%c", &c1, &ch2) == 2 |
| 847 | || sscanf(str, "%%'%c'%%+%%%c", &ch1, &ch2) == 2) |
| 848 | && ch2 == 'c' |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 849 | && (cp = strchr(str, '+'))) { |
| 850 | str = cp + 2; |
| 851 | bufptr = save_string(bufptr, "%+"); |
| 852 | |
| 853 | if (ch1) |
| 854 | c1 = ch1; |
| 855 | bufptr = save_tc_char(bufptr, c1); |
| 856 | } |
| 857 | /* FIXME: this "works" for 'delta' */ |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 858 | else if (strncmp(str, "%{2}%*%-", (size_t) 8) == 0) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 859 | str += 7; |
| 860 | bufptr = save_string(bufptr, "%D"); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 861 | } else if (strncmp(str, "%{96}%^", (size_t) 7) == 0) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 862 | str += 6; |
| 863 | if (saw_m++ == 0) { |
| 864 | bufptr = save_string(bufptr, "%n"); |
| 865 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 866 | } else if (strncmp(str, "%{127}%^", (size_t) 8) == 0) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 867 | str += 7; |
| 868 | if (saw_n++ == 0) { |
| 869 | bufptr = save_string(bufptr, "%m"); |
| 870 | } |
| 871 | } else { /* cm-style format element */ |
| 872 | str++; |
| 873 | switch (*str) { |
| 874 | case '%': |
| 875 | bufptr = save_char(bufptr, '%'); |
| 876 | break; |
| 877 | |
| 878 | case '0': |
| 879 | case '1': |
| 880 | case '2': |
| 881 | case '3': |
| 882 | case '4': |
| 883 | case '5': |
| 884 | case '6': |
| 885 | case '7': |
| 886 | case '8': |
| 887 | case '9': |
| 888 | bufptr = save_char(bufptr, '%'); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 889 | ch1 = 0; |
| 890 | ch2 = 0; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 891 | digits = 0; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 892 | while (isdigit(UChar(*str))) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 893 | if (++digits > 2) { |
| 894 | syntax_error = TRUE; |
| 895 | break; |
| 896 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 897 | ch2 = ch1; |
| 898 | ch1 = *str++; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 899 | if (digits == 2 && ch2 != '0') { |
| 900 | syntax_error = TRUE; |
| 901 | break; |
| 902 | } else if (_nc_strict_bsd) { |
| 903 | if (ch1 > '3') { |
| 904 | syntax_error = TRUE; |
| 905 | break; |
| 906 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 907 | } else { |
| 908 | bufptr = save_char(bufptr, ch1); |
| 909 | } |
| 910 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 911 | if (syntax_error) |
| 912 | break; |
| 913 | /* |
| 914 | * Convert %02 to %2 and %03 to %3 |
| 915 | */ |
| 916 | if (ch2 == '0' && !_nc_strict_bsd) { |
| 917 | ch2 = 0; |
| 918 | bufptr[-2] = bufptr[-1]; |
| 919 | *--bufptr = 0; |
| 920 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 921 | if (_nc_strict_bsd) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 922 | if (ch2 != 0 && ch2 != '0') { |
| 923 | syntax_error = TRUE; |
| 924 | } else if (ch1 < '2') { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 925 | ch1 = 'd'; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 926 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 927 | bufptr = save_char(bufptr, ch1); |
| 928 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 929 | if (strchr("oxX.", *str)) { |
| 930 | syntax_error = TRUE; /* termcap doesn't have octal, hex */ |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 931 | } |
| 932 | break; |
| 933 | |
| 934 | case 'd': |
| 935 | bufptr = save_string(bufptr, "%d"); |
| 936 | break; |
| 937 | |
| 938 | case 'c': |
| 939 | bufptr = save_string(bufptr, "%."); |
| 940 | break; |
| 941 | |
| 942 | /* |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 943 | * %s isn't in termcap, but it is convenient to pass it through |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 944 | * so we can represent things like terminfo pfkey strings in |
| 945 | * termcap notation. |
| 946 | */ |
| 947 | case 's': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 948 | if (_nc_strict_bsd) { |
| 949 | syntax_error = TRUE; |
| 950 | } else { |
| 951 | bufptr = save_string(bufptr, "%s"); |
| 952 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 953 | break; |
| 954 | |
| 955 | case 'p': |
| 956 | str++; |
| 957 | if (*str == '1') |
| 958 | seenone = 1; |
| 959 | else if (*str == '2') { |
| 960 | if (!seenone && !seentwo) { |
| 961 | bufptr = save_string(bufptr, "%r"); |
| 962 | seentwo++; |
| 963 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 964 | } else if (*str >= '3') { |
| 965 | syntax_error = TRUE; |
| 966 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 967 | break; |
| 968 | |
| 969 | case 'i': |
| 970 | bufptr = save_string(bufptr, "%i"); |
| 971 | break; |
| 972 | |
| 973 | default: |
| 974 | bufptr = save_char(bufptr, *str); |
| 975 | syntax_error = TRUE; |
| 976 | break; |
| 977 | } /* endswitch (*str) */ |
| 978 | } /* endelse (*str == '%') */ |
| 979 | |
| 980 | /* |
| 981 | * 'str' always points to the end of what was scanned in this step, |
| 982 | * but that may not be the end of the string. |
| 983 | */ |
| 984 | assert(str != 0); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 985 | if (str == 0 || *str == '\0') |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 986 | break; |
| 987 | |
| 988 | } /* endwhile (*str) */ |
| 989 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 990 | if (!syntax_error && |
| 991 | myfix > 0 && |
| 992 | ((int) strlen(my_string) - (4 * myfix)) < MIN_TC_FIXUPS) { |
| 993 | while (--myfix >= 0) { |
| 994 | char *p = fixups[myfix].offset + my_string; |
| 995 | *p++ = '^'; |
| 996 | *p++ = (char) (fixups[myfix].ch | '@'); |
| 997 | while ((p[0] = p[2]) != '\0') { |
| 998 | ++p; |
| 999 | } |
| 1000 | } |
| 1001 | } |
| 1002 | |
| 1003 | DEBUG_THIS(("... _nc_infotocap %s", |
| 1004 | syntax_error |
| 1005 | ? "<ERR>" |
| 1006 | : _nc_visbuf(my_string))); |
| 1007 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1008 | return (syntax_error ? NULL : my_string); |
| 1009 | } |
| 1010 | |
| 1011 | #ifdef MAIN |
| 1012 | |
| 1013 | int curr_line; |
| 1014 | |
| 1015 | int |
| 1016 | main(int argc, char *argv[]) |
| 1017 | { |
| 1018 | int c, tc = FALSE; |
| 1019 | |
| 1020 | while ((c = getopt(argc, argv, "c")) != EOF) |
| 1021 | switch (c) { |
| 1022 | case 'c': |
| 1023 | tc = TRUE; |
| 1024 | break; |
| 1025 | } |
| 1026 | |
| 1027 | curr_line = 0; |
| 1028 | for (;;) { |
| 1029 | char buf[BUFSIZ]; |
| 1030 | |
| 1031 | ++curr_line; |
| 1032 | if (fgets(buf, sizeof(buf), stdin) == 0) |
| 1033 | break; |
| 1034 | buf[strlen(buf) - 1] = '\0'; |
| 1035 | _nc_set_source(buf); |
| 1036 | |
| 1037 | if (tc) { |
| 1038 | char *cp = _nc_infotocap("to termcap", buf, 1); |
| 1039 | |
| 1040 | if (cp) |
| 1041 | (void) fputs(cp, stdout); |
| 1042 | } else |
| 1043 | (void) fputs(_nc_captoinfo("to terminfo", buf, 1), stdout); |
| 1044 | (void) putchar('\n'); |
| 1045 | } |
| 1046 | return (0); |
| 1047 | } |
| 1048 | #endif /* MAIN */ |
| 1049 | |
| 1050 | #if NO_LEAKS |
| 1051 | NCURSES_EXPORT(void) |
| 1052 | _nc_captoinfo_leaks(void) |
| 1053 | { |
| 1054 | if (my_string != 0) { |
| 1055 | FreeAndNull(my_string); |
| 1056 | } |
| 1057 | my_length = 0; |
| 1058 | } |
| 1059 | #endif |