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-2021,2023 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 | /* |
| 37 | * tparm.c |
| 38 | * |
| 39 | */ |
| 40 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 41 | #define entry _ncu_entry |
| 42 | #define ENTRY _ncu_ENTRY |
| 43 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 44 | #include <curses.priv.h> |
| 45 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 46 | #undef entry |
| 47 | #undef ENTRY |
| 48 | |
| 49 | #if HAVE_TSEARCH |
| 50 | #include <search.h> |
| 51 | #endif |
| 52 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 53 | #include <ctype.h> |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 54 | #include <tic.h> |
| 55 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 56 | MODULE_ID("$Id: lib_tparm.c,v 1.153 2023/11/04 19:28:41 tom Exp $") |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 57 | |
| 58 | /* |
| 59 | * char * |
| 60 | * tparm(string, ...) |
| 61 | * |
| 62 | * Substitute the given parameters into the given string by the following |
| 63 | * rules (taken from terminfo(5)): |
| 64 | * |
| 65 | * Cursor addressing and other strings requiring parame- |
| 66 | * ters in the terminal are described by a parameterized string |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 67 | * capability, with escapes like %x in it. For example, to |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 68 | * address the cursor, the cup capability is given, using two |
| 69 | * parameters: the row and column to address to. (Rows and |
| 70 | * columns are numbered from zero and refer to the physical |
| 71 | * screen visible to the user, not to any unseen memory.) If |
| 72 | * the terminal has memory relative cursor addressing, that can |
| 73 | * be indicated by |
| 74 | * |
| 75 | * The parameter mechanism uses a stack and special % |
| 76 | * codes to manipulate it. Typically a sequence will push one |
| 77 | * of the parameters onto the stack and then print it in some |
| 78 | * format. Often more complex operations are necessary. |
| 79 | * |
| 80 | * The % encodings have the following meanings: |
| 81 | * |
| 82 | * %% outputs `%' |
| 83 | * %c print pop() like %c in printf() |
| 84 | * %s print pop() like %s in printf() |
| 85 | * %[[:]flags][width[.precision]][doxXs] |
| 86 | * as in printf, flags are [-+#] and space |
| 87 | * The ':' is used to avoid making %+ or %- |
| 88 | * patterns (see below). |
| 89 | * |
| 90 | * %p[1-9] push ith parm |
| 91 | * %P[a-z] set dynamic variable [a-z] to pop() |
| 92 | * %g[a-z] get dynamic variable [a-z] and push it |
| 93 | * %P[A-Z] set static variable [A-Z] to pop() |
| 94 | * %g[A-Z] get static variable [A-Z] and push it |
| 95 | * %l push strlen(pop) |
| 96 | * %'c' push char constant c |
| 97 | * %{nn} push integer constant nn |
| 98 | * |
| 99 | * %+ %- %* %/ %m |
| 100 | * arithmetic (%m is mod): push(pop() op pop()) |
| 101 | * %& %| %^ bit operations: push(pop() op pop()) |
| 102 | * %= %> %< logical operations: push(pop() op pop()) |
| 103 | * %A %O logical and & or operations for conditionals |
| 104 | * %! %~ unary operations push(op pop()) |
| 105 | * %i add 1 to first two parms (for ANSI terminals) |
| 106 | * |
| 107 | * %? expr %t thenpart %e elsepart %; |
| 108 | * if-then-else, %e elsepart is optional. |
| 109 | * else-if's are possible ala Algol 68: |
| 110 | * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %; |
| 111 | * |
| 112 | * For those of the above operators which are binary and not commutative, |
| 113 | * the stack works in the usual way, with |
| 114 | * %gx %gy %m |
| 115 | * resulting in x mod y, not the reverse. |
| 116 | */ |
| 117 | |
| 118 | NCURSES_EXPORT_VAR(int) _nc_tparm_err = 0; |
| 119 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 120 | #define TPS(var) tps->var |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 121 | #define popcount _nc_popcount /* workaround for NetBSD 6.0 defect */ |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 122 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 123 | #define get_tparm_state(term) \ |
| 124 | (term != NULL \ |
| 125 | ? &(term->tparm_state) \ |
| 126 | : &(_nc_prescreen.tparm_state)) |
| 127 | |
| 128 | #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z') |
| 129 | #define isLOWER(c) ((c) >= 'a' && (c) <= 'z') |
| 130 | #define tc_BUMP() if (level < 0 && number < 2) number++ |
| 131 | |
| 132 | typedef struct { |
| 133 | const char *format; /* format-string can be used as cache-key */ |
| 134 | int tparm_type; /* bit-set for each string-parameter */ |
| 135 | int num_actual; |
| 136 | int num_parsed; |
| 137 | int num_popped; |
| 138 | TPARM_ARG param[NUM_PARM]; |
| 139 | char *p_is_s[NUM_PARM]; |
| 140 | } TPARM_DATA; |
| 141 | |
| 142 | #if HAVE_TSEARCH |
| 143 | #define MyCache _nc_globals.cached_tparm |
| 144 | #define MyCount _nc_globals.count_tparm |
| 145 | static int which_tparm; |
| 146 | static TPARM_DATA **delete_tparm; |
| 147 | #endif /* HAVE_TSEARCH */ |
| 148 | |
| 149 | static char dummy[] = ""; /* avoid const-cast */ |
| 150 | |
| 151 | #if HAVE_TSEARCH |
| 152 | static int |
| 153 | cmp_format(const void *p, const void *q) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 154 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 155 | const char *a = *(char *const *) p; |
| 156 | const char *b = *(char *const *) q; |
| 157 | return strcmp(a, b); |
| 158 | } |
| 159 | #endif |
| 160 | |
| 161 | #if HAVE_TSEARCH |
| 162 | static void |
| 163 | visit_nodes(const void *nodep, VISIT which, int depth) |
| 164 | { |
| 165 | (void) depth; |
| 166 | if (which == preorder || which == leaf) { |
| 167 | delete_tparm[which_tparm] = *(TPARM_DATA **) nodep; |
| 168 | which_tparm++; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 169 | } |
| 170 | } |
| 171 | #endif |
| 172 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 173 | NCURSES_EXPORT(void) |
| 174 | _nc_free_tparm(TERMINAL *termp) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 175 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 176 | TPARM_STATE *tps = get_tparm_state(termp); |
| 177 | #if HAVE_TSEARCH |
| 178 | if (MyCount != 0) { |
| 179 | delete_tparm = typeCalloc(TPARM_DATA *, MyCount); |
| 180 | if (delete_tparm != NULL) { |
| 181 | which_tparm = 0; |
| 182 | twalk(MyCache, visit_nodes); |
| 183 | for (which_tparm = 0; which_tparm < MyCount; ++which_tparm) { |
| 184 | TPARM_DATA *ptr = delete_tparm[which_tparm]; |
| 185 | if (ptr != NULL) { |
| 186 | tdelete(ptr, &MyCache, cmp_format); |
| 187 | free((char *) ptr->format); |
| 188 | free(ptr); |
| 189 | } |
| 190 | } |
| 191 | which_tparm = 0; |
| 192 | twalk(MyCache, visit_nodes); |
| 193 | FreeAndNull(delete_tparm); |
| 194 | } |
| 195 | MyCount = 0; |
| 196 | which_tparm = 0; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 197 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 198 | #endif |
| 199 | FreeAndNull(TPS(out_buff)); |
| 200 | TPS(out_size) = 0; |
| 201 | TPS(out_used) = 0; |
| 202 | |
| 203 | FreeAndNull(TPS(fmt_buff)); |
| 204 | TPS(fmt_size) = 0; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 205 | } |
| 206 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 207 | static int |
| 208 | tparm_error(TPARM_STATE *tps, const char *message) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 209 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 210 | (void) tps; |
| 211 | (void) message; |
| 212 | DEBUG(2, ("%s: %s", message, _nc_visbuf(TPS(tparam_base)))); |
| 213 | return ++_nc_tparm_err; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 214 | } |
| 215 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 216 | #define get_space(tps, need) \ |
| 217 | { \ |
| 218 | size_t need2get = need + TPS(out_used); \ |
| 219 | if (need2get > TPS(out_size)) { \ |
| 220 | TPS(out_size) = need2get * 2; \ |
| 221 | TYPE_REALLOC(char, TPS(out_size), TPS(out_buff)); \ |
| 222 | } \ |
| 223 | } |
| 224 | |
| 225 | #if NCURSES_EXPANDED |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 226 | static NCURSES_INLINE void |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 227 | (get_space) (TPARM_STATE *tps, size_t need) { |
| 228 | get_space(tps, need); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 229 | } |
| 230 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 231 | #undef get_space |
| 232 | #endif |
| 233 | |
| 234 | #define save_text(tps, fmt, s, len) \ |
| 235 | { \ |
| 236 | size_t s_len = (size_t) len + strlen(s) + strlen(fmt); \ |
| 237 | get_space(tps, s_len + 1); \ |
| 238 | _nc_SPRINTF(TPS(out_buff) + TPS(out_used), \ |
| 239 | _nc_SLIMIT(TPS(out_size) - TPS(out_used)) \ |
| 240 | fmt, s); \ |
| 241 | TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used)); \ |
| 242 | } |
| 243 | |
| 244 | #if NCURSES_EXPANDED |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 245 | static NCURSES_INLINE void |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 246 | (save_text) (TPARM_STATE *tps, const char *fmt, const char *s, int len) { |
| 247 | save_text(tps, fmt, s, len); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 248 | } |
| 249 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 250 | #undef save_text |
| 251 | #endif |
| 252 | |
| 253 | #define save_number(tps, fmt, number, len) \ |
| 254 | { \ |
| 255 | size_t s_len = (size_t) len + 30 + strlen(fmt); \ |
| 256 | get_space(tps, s_len + 1); \ |
| 257 | _nc_SPRINTF(TPS(out_buff) + TPS(out_used), \ |
| 258 | _nc_SLIMIT(TPS(out_size) - TPS(out_used)) \ |
| 259 | fmt, number); \ |
| 260 | TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used)); \ |
| 261 | } |
| 262 | |
| 263 | #if NCURSES_EXPANDED |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 264 | static NCURSES_INLINE void |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 265 | (save_number) (TPARM_STATE *tps, const char *fmt, int number, int len) { |
| 266 | save_number(tps, fmt, number, len); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 267 | } |
| 268 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 269 | #undef save_number |
| 270 | #endif |
| 271 | |
| 272 | #define save_char(tps, c) \ |
| 273 | { \ |
| 274 | get_space(tps, (size_t) 1); \ |
| 275 | TPS(out_buff)[TPS(out_used)++] = (char) ((c == 0) ? 0200 : c); \ |
| 276 | } |
| 277 | |
| 278 | #if NCURSES_EXPANDED |
| 279 | static NCURSES_INLINE void |
| 280 | (save_char) (TPARM_STATE *tps, int c) { |
| 281 | save_char(tps, c); |
| 282 | } |
| 283 | |
| 284 | #undef save_char |
| 285 | #endif |
| 286 | |
| 287 | #define npush(tps, x) \ |
| 288 | { \ |
| 289 | if (TPS(stack_ptr) < STACKSIZE) { \ |
| 290 | TPS(stack)[TPS(stack_ptr)].num_type = TRUE; \ |
| 291 | TPS(stack)[TPS(stack_ptr)].data.num = x; \ |
| 292 | TPS(stack_ptr)++; \ |
| 293 | } else { \ |
| 294 | (void) tparm_error(tps, "npush: stack overflow"); \ |
| 295 | } \ |
| 296 | } |
| 297 | |
| 298 | #if NCURSES_EXPANDED |
| 299 | static NCURSES_INLINE void |
| 300 | (npush) (TPARM_STATE *tps, int x) { |
| 301 | npush(tps, x); |
| 302 | } |
| 303 | |
| 304 | #undef npush |
| 305 | #endif |
| 306 | |
| 307 | #define spush(tps, x) \ |
| 308 | { \ |
| 309 | if (TPS(stack_ptr) < STACKSIZE) { \ |
| 310 | TPS(stack)[TPS(stack_ptr)].num_type = FALSE; \ |
| 311 | TPS(stack)[TPS(stack_ptr)].data.str = x; \ |
| 312 | TPS(stack_ptr)++; \ |
| 313 | } else { \ |
| 314 | (void) tparm_error(tps, "spush: stack overflow"); \ |
| 315 | } \ |
| 316 | } |
| 317 | |
| 318 | #if NCURSES_EXPANDED |
| 319 | static NCURSES_INLINE void |
| 320 | (spush) (TPARM_STATE *tps, char *x) { |
| 321 | spush(tps, x); |
| 322 | } |
| 323 | |
| 324 | #undef spush |
| 325 | #endif |
| 326 | |
| 327 | #define npop(tps) \ |
| 328 | ((TPS(stack_ptr)-- > 0) \ |
| 329 | ? ((TPS(stack)[TPS(stack_ptr)].num_type) \ |
| 330 | ? TPS(stack)[TPS(stack_ptr)].data.num \ |
| 331 | : 0) \ |
| 332 | : (tparm_error(tps, "npop: stack underflow"), \ |
| 333 | TPS(stack_ptr) = 0)) |
| 334 | |
| 335 | #if NCURSES_EXPANDED |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 336 | static NCURSES_INLINE int |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 337 | (npop) (TPARM_STATE *tps) { |
| 338 | return npop(tps); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 339 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 340 | #undef npop |
| 341 | #endif |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 342 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 343 | #define spop(tps) \ |
| 344 | ((TPS(stack_ptr)-- > 0) \ |
| 345 | ? ((!TPS(stack)[TPS(stack_ptr)].num_type \ |
| 346 | && TPS(stack)[TPS(stack_ptr)].data.str != 0) \ |
| 347 | ? TPS(stack)[TPS(stack_ptr)].data.str \ |
| 348 | : dummy) \ |
| 349 | : (tparm_error(tps, "spop: stack underflow"), \ |
| 350 | dummy)) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 351 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 352 | #if NCURSES_EXPANDED |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 353 | static NCURSES_INLINE char * |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 354 | (spop) (TPARM_STATE *tps) { |
| 355 | return spop(tps); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 356 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 357 | #undef spop |
| 358 | #endif |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 359 | |
| 360 | static NCURSES_INLINE const char * |
| 361 | parse_format(const char *s, char *format, int *len) |
| 362 | { |
| 363 | *len = 0; |
| 364 | if (format != 0) { |
| 365 | bool done = FALSE; |
| 366 | bool allowminus = FALSE; |
| 367 | bool dot = FALSE; |
| 368 | bool err = FALSE; |
| 369 | char *fmt = format; |
| 370 | int my_width = 0; |
| 371 | int my_prec = 0; |
| 372 | int value = 0; |
| 373 | |
| 374 | *len = 0; |
| 375 | *format++ = '%'; |
| 376 | while (*s != '\0' && !done) { |
| 377 | switch (*s) { |
| 378 | case 'c': /* FALLTHRU */ |
| 379 | case 'd': /* FALLTHRU */ |
| 380 | case 'o': /* FALLTHRU */ |
| 381 | case 'x': /* FALLTHRU */ |
| 382 | case 'X': /* FALLTHRU */ |
| 383 | case 's': |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 384 | #ifdef EXP_XTERM_1005 |
| 385 | case 'u': |
| 386 | #endif |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 387 | *format++ = *s; |
| 388 | done = TRUE; |
| 389 | break; |
| 390 | case '.': |
| 391 | *format++ = *s++; |
| 392 | if (dot) { |
| 393 | err = TRUE; |
| 394 | } else { /* value before '.' is the width */ |
| 395 | dot = TRUE; |
| 396 | my_width = value; |
| 397 | } |
| 398 | value = 0; |
| 399 | break; |
| 400 | case '#': |
| 401 | *format++ = *s++; |
| 402 | break; |
| 403 | case ' ': |
| 404 | *format++ = *s++; |
| 405 | break; |
| 406 | case ':': |
| 407 | s++; |
| 408 | allowminus = TRUE; |
| 409 | break; |
| 410 | case '-': |
| 411 | if (allowminus) { |
| 412 | *format++ = *s++; |
| 413 | } else { |
| 414 | done = TRUE; |
| 415 | } |
| 416 | break; |
| 417 | default: |
| 418 | if (isdigit(UChar(*s))) { |
| 419 | value = (value * 10) + (*s - '0'); |
| 420 | if (value > 10000) |
| 421 | err = TRUE; |
| 422 | *format++ = *s++; |
| 423 | } else { |
| 424 | done = TRUE; |
| 425 | } |
| 426 | } |
| 427 | } |
| 428 | |
| 429 | /* |
| 430 | * If we found an error, ignore (and remove) the flags. |
| 431 | */ |
| 432 | if (err) { |
| 433 | my_width = my_prec = value = 0; |
| 434 | format = fmt; |
| 435 | *format++ = '%'; |
| 436 | *format++ = *s; |
| 437 | } |
| 438 | |
| 439 | /* |
| 440 | * Any value after '.' is the precision. If we did not see '.', then |
| 441 | * the value is the width. |
| 442 | */ |
| 443 | if (dot) |
| 444 | my_prec = value; |
| 445 | else |
| 446 | my_width = value; |
| 447 | |
| 448 | *format = '\0'; |
| 449 | /* return maximum string length in print */ |
| 450 | *len = (my_width > my_prec) ? my_width : my_prec; |
| 451 | } |
| 452 | return s; |
| 453 | } |
| 454 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 455 | /* |
| 456 | * Analyze the string to see how many parameters we need from the varargs list, |
| 457 | * and what their types are. We will only accept string parameters if they |
| 458 | * appear as a %l or %s format following an explicit parameter reference (e.g., |
| 459 | * %p2%s). All other parameters are numbers. |
| 460 | * |
| 461 | * 'number' counts coarsely the number of pop's we see in the string, and |
| 462 | * 'popcount' shows the highest parameter number in the string. We would like |
| 463 | * to simply use the latter count, but if we are reading termcap strings, there |
| 464 | * may be cases that we cannot see the explicit parameter numbers. |
| 465 | */ |
| 466 | NCURSES_EXPORT(int) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 467 | _nc_tparm_analyze(TERMINAL *term, const char *string, char **p_is_s, int *popcount) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 468 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 469 | TPARM_STATE *tps = get_tparm_state(term); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 470 | size_t len2; |
| 471 | int i; |
| 472 | int lastpop = -1; |
| 473 | int len; |
| 474 | int number = 0; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 475 | int level = -1; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 476 | const char *cp = string; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 477 | |
| 478 | if (cp == 0) |
| 479 | return 0; |
| 480 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 481 | if ((len2 = strlen(cp)) + 2 > TPS(fmt_size)) { |
| 482 | TPS(fmt_size) += len2 + 2; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 483 | TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff)); |
| 484 | if (TPS(fmt_buff) == 0) |
| 485 | return 0; |
| 486 | } |
| 487 | |
| 488 | memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM); |
| 489 | *popcount = 0; |
| 490 | |
| 491 | while ((cp - string) < (int) len2) { |
| 492 | if (*cp == '%') { |
| 493 | cp++; |
| 494 | cp = parse_format(cp, TPS(fmt_buff), &len); |
| 495 | switch (*cp) { |
| 496 | default: |
| 497 | break; |
| 498 | |
| 499 | case 'd': /* FALLTHRU */ |
| 500 | case 'o': /* FALLTHRU */ |
| 501 | case 'x': /* FALLTHRU */ |
| 502 | case 'X': /* FALLTHRU */ |
| 503 | case 'c': /* FALLTHRU */ |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 504 | #ifdef EXP_XTERM_1005 |
| 505 | case 'u': |
| 506 | #endif |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 507 | if (lastpop <= 0) { |
| 508 | tc_BUMP(); |
| 509 | } |
| 510 | level -= 1; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 511 | lastpop = -1; |
| 512 | break; |
| 513 | |
| 514 | case 'l': |
| 515 | case 's': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 516 | if (lastpop > 0) { |
| 517 | level -= 1; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 518 | p_is_s[lastpop - 1] = dummy; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 519 | } |
| 520 | tc_BUMP(); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 521 | break; |
| 522 | |
| 523 | case 'p': |
| 524 | cp++; |
| 525 | i = (UChar(*cp) - '0'); |
| 526 | if (i >= 0 && i <= NUM_PARM) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 527 | ++level; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 528 | lastpop = i; |
| 529 | if (lastpop > *popcount) |
| 530 | *popcount = lastpop; |
| 531 | } |
| 532 | break; |
| 533 | |
| 534 | case 'P': |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 535 | ++cp; |
| 536 | break; |
| 537 | |
| 538 | case 'g': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 539 | ++level; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 540 | cp++; |
| 541 | break; |
| 542 | |
| 543 | case S_QUOTE: |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 544 | ++level; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 545 | cp += 2; |
| 546 | lastpop = -1; |
| 547 | break; |
| 548 | |
| 549 | case L_BRACE: |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 550 | ++level; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 551 | cp++; |
| 552 | while (isdigit(UChar(*cp))) { |
| 553 | cp++; |
| 554 | } |
| 555 | break; |
| 556 | |
| 557 | case '+': |
| 558 | case '-': |
| 559 | case '*': |
| 560 | case '/': |
| 561 | case 'm': |
| 562 | case 'A': |
| 563 | case 'O': |
| 564 | case '&': |
| 565 | case '|': |
| 566 | case '^': |
| 567 | case '=': |
| 568 | case '<': |
| 569 | case '>': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 570 | tc_BUMP(); |
| 571 | level -= 1; /* pop 2, operate, push 1 */ |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 572 | lastpop = -1; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 573 | break; |
| 574 | |
| 575 | case '!': |
| 576 | case '~': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 577 | tc_BUMP(); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 578 | lastpop = -1; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 579 | break; |
| 580 | |
| 581 | case 'i': |
| 582 | /* will add 1 to first (usually two) parameters */ |
| 583 | break; |
| 584 | } |
| 585 | } |
| 586 | if (*cp != '\0') |
| 587 | cp++; |
| 588 | } |
| 589 | |
| 590 | if (number > NUM_PARM) |
| 591 | number = NUM_PARM; |
| 592 | return number; |
| 593 | } |
| 594 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 595 | /* |
| 596 | * Analyze the capability string, finding the number of parameters and their |
| 597 | * types. |
| 598 | * |
| 599 | * TODO: cache the result so that this is done once per capability per term. |
| 600 | */ |
| 601 | static int |
| 602 | tparm_setup(TERMINAL *term, const char *string, TPARM_DATA *result) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 603 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 604 | TPARM_STATE *tps = get_tparm_state(term); |
| 605 | int rc = OK; |
| 606 | |
| 607 | TPS(out_used) = 0; |
| 608 | memset(result, 0, sizeof(*result)); |
| 609 | |
| 610 | if (!VALID_STRING(string)) { |
| 611 | TR(TRACE_CALLS, ("%s: format is invalid", TPS(tname))); |
| 612 | rc = ERR; |
| 613 | } else { |
| 614 | #if HAVE_TSEARCH |
| 615 | TPARM_DATA *fs; |
| 616 | void *ft; |
| 617 | |
| 618 | result->format = string; |
| 619 | if ((ft = tfind(result, &MyCache, cmp_format)) != 0) { |
| 620 | size_t len2; |
| 621 | fs = *(TPARM_DATA **) ft; |
| 622 | *result = *fs; |
| 623 | if ((len2 = strlen(string)) + 2 > TPS(fmt_size)) { |
| 624 | TPS(fmt_size) += len2 + 2; |
| 625 | TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff)); |
| 626 | if (TPS(fmt_buff) == 0) |
| 627 | return ERR; |
| 628 | } |
| 629 | } else |
| 630 | #endif |
| 631 | { |
| 632 | /* |
| 633 | * Find the highest parameter-number referred to in the format |
| 634 | * string. Use this value to limit the number of arguments copied |
| 635 | * from the variable-length argument list. |
| 636 | */ |
| 637 | result->num_parsed = _nc_tparm_analyze(term, string, |
| 638 | result->p_is_s, |
| 639 | &(result->num_popped)); |
| 640 | if (TPS(fmt_buff) == 0) { |
| 641 | TR(TRACE_CALLS, ("%s: error in analysis", TPS(tname))); |
| 642 | rc = ERR; |
| 643 | } else { |
| 644 | int n; |
| 645 | |
| 646 | if (result->num_parsed > NUM_PARM) |
| 647 | result->num_parsed = NUM_PARM; |
| 648 | if (result->num_popped > NUM_PARM) |
| 649 | result->num_popped = NUM_PARM; |
| 650 | result->num_actual = Max(result->num_popped, result->num_parsed); |
| 651 | |
| 652 | for (n = 0; n < result->num_actual; ++n) { |
| 653 | if (result->p_is_s[n]) |
| 654 | result->tparm_type |= (1 << n); |
| 655 | } |
| 656 | #if HAVE_TSEARCH |
| 657 | if ((fs = typeCalloc(TPARM_DATA, 1)) != 0) { |
| 658 | *fs = *result; |
| 659 | if ((fs->format = strdup(string)) != 0) { |
| 660 | if (tsearch(fs, &MyCache, cmp_format) != 0) { |
| 661 | ++MyCount; |
| 662 | } else { |
| 663 | free(fs); |
| 664 | rc = ERR; |
| 665 | } |
| 666 | } else { |
| 667 | free(fs); |
| 668 | rc = ERR; |
| 669 | } |
| 670 | } else { |
| 671 | rc = ERR; |
| 672 | } |
| 673 | #endif |
| 674 | } |
| 675 | } |
| 676 | } |
| 677 | |
| 678 | return rc; |
| 679 | } |
| 680 | |
| 681 | /* |
| 682 | * A few caps (such as plab_norm) have string-valued parms. We'll have to |
| 683 | * assume that the caller knows the difference, since a char* and an int may |
| 684 | * not be the same size on the stack. The normal prototype for tparm uses 9 |
| 685 | * long's, which is consistent with our va_arg() usage. |
| 686 | */ |
| 687 | static void |
| 688 | tparm_copy_valist(TPARM_DATA *data, int use_TPARM_ARG, va_list ap) |
| 689 | { |
| 690 | int i; |
| 691 | |
| 692 | for (i = 0; i < data->num_actual; i++) { |
| 693 | if (data->p_is_s[i] != 0) { |
| 694 | char *value = va_arg(ap, char *); |
| 695 | if (value == 0) |
| 696 | value = dummy; |
| 697 | data->p_is_s[i] = value; |
| 698 | data->param[i] = 0; |
| 699 | } else if (use_TPARM_ARG) { |
| 700 | data->param[i] = va_arg(ap, TPARM_ARG); |
| 701 | } else { |
| 702 | data->param[i] = (TPARM_ARG) va_arg(ap, int); |
| 703 | } |
| 704 | } |
| 705 | } |
| 706 | |
| 707 | /* |
| 708 | * This is a termcap compatibility hack. If there are no explicit pop |
| 709 | * operations in the string, load the stack in such a way that successive pops |
| 710 | * will grab successive parameters. That will make the expansion of (for |
| 711 | * example) \E[%d;%dH work correctly in termcap style, which means tparam() |
| 712 | * will expand termcap strings OK. |
| 713 | */ |
| 714 | static bool |
| 715 | tparm_tc_compat(TPARM_STATE *tps, TPARM_DATA *data) |
| 716 | { |
| 717 | bool termcap_hack = FALSE; |
| 718 | |
| 719 | TPS(stack_ptr) = 0; |
| 720 | |
| 721 | if (data->num_popped == 0) { |
| 722 | int i; |
| 723 | |
| 724 | termcap_hack = TRUE; |
| 725 | for (i = data->num_parsed - 1; i >= 0; i--) { |
| 726 | if (data->p_is_s[i]) { |
| 727 | spush(tps, data->p_is_s[i]); |
| 728 | } else { |
| 729 | npush(tps, (int) data->param[i]); |
| 730 | } |
| 731 | } |
| 732 | } |
| 733 | return termcap_hack; |
| 734 | } |
| 735 | |
| 736 | #ifdef TRACE |
| 737 | static void |
| 738 | tparm_trace_call(TPARM_STATE *tps, const char *string, TPARM_DATA *data) |
| 739 | { |
| 740 | if (USE_TRACEF(TRACE_CALLS)) { |
| 741 | int i; |
| 742 | for (i = 0; i < data->num_actual; i++) { |
| 743 | if (data->p_is_s[i] != 0) { |
| 744 | save_text(tps, ", %s", _nc_visbuf(data->p_is_s[i]), 0); |
| 745 | } else if ((long) data->param[i] > MAX_OF_TYPE(NCURSES_INT2) || |
| 746 | (long) data->param[i] < 0) { |
| 747 | _tracef("BUG: problem with tparm parameter #%d of %d", |
| 748 | i + 1, data->num_actual); |
| 749 | break; |
| 750 | } else { |
| 751 | save_number(tps, ", %d", (int) data->param[i], 0); |
| 752 | } |
| 753 | } |
| 754 | _tracef(T_CALLED("%s(%s%s)"), TPS(tname), _nc_visbuf(string), TPS(out_buff)); |
| 755 | TPS(out_used) = 0; |
| 756 | _nc_unlock_global(tracef); |
| 757 | } |
| 758 | } |
| 759 | |
| 760 | #else |
| 761 | #define tparm_trace_call(tps, string, data) /* nothing */ |
| 762 | #endif /* TRACE */ |
| 763 | |
| 764 | #define init_vars(name) \ |
| 765 | if (!name##_used) { \ |
| 766 | name##_used = TRUE; \ |
| 767 | memset(name##_vars, 0, sizeof(name##_vars)); \ |
| 768 | } |
| 769 | |
| 770 | static NCURSES_INLINE char * |
| 771 | tparam_internal(TPARM_STATE *tps, const char *string, TPARM_DATA *data) |
| 772 | { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 773 | int number; |
| 774 | int len; |
| 775 | int level; |
| 776 | int x, y; |
| 777 | int i; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 778 | const char *s; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 779 | const char *cp = string; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 780 | size_t len2 = strlen(cp); |
| 781 | bool incremented_two = FALSE; |
| 782 | bool termcap_hack = tparm_tc_compat(tps, data); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 783 | /* |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 784 | * SVr4 curses stores variables 'A' to 'Z' in the TERMINAL structure (so |
| 785 | * they are initialized once to zero), and variables 'a' to 'z' on the |
| 786 | * stack in tparm, referring to the former as "static" and the latter as |
| 787 | * "dynamic". However, it makes no check to ensure that the "dynamic" |
| 788 | * variables are initialized. |
| 789 | * |
| 790 | * Solaris xpg4 curses makes no distinction between the upper/lower, and |
| 791 | * stores the common set of 26 variables on the stack, without initializing |
| 792 | * them. |
| 793 | * |
| 794 | * In ncurses, both sets of variables are initialized on the first use. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 795 | */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 796 | bool dynamic_used = FALSE; |
| 797 | int dynamic_vars[NUM_VARS]; |
| 798 | |
| 799 | tparm_trace_call(tps, string, data); |
| 800 | |
| 801 | if (TPS(fmt_buff) == NULL) { |
| 802 | T((T_RETURN("<null>"))); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 803 | return NULL; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 804 | } |
| 805 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 806 | while ((cp - string) < (int) len2) { |
| 807 | if (*cp != '%') { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 808 | save_char(tps, UChar(*cp)); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 809 | } else { |
| 810 | TPS(tparam_base) = cp++; |
| 811 | cp = parse_format(cp, TPS(fmt_buff), &len); |
| 812 | switch (*cp) { |
| 813 | default: |
| 814 | break; |
| 815 | case '%': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 816 | save_char(tps, '%'); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 817 | break; |
| 818 | |
| 819 | case 'd': /* FALLTHRU */ |
| 820 | case 'o': /* FALLTHRU */ |
| 821 | case 'x': /* FALLTHRU */ |
| 822 | case 'X': /* FALLTHRU */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 823 | x = npop(tps); |
| 824 | save_number(tps, TPS(fmt_buff), x, len); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 825 | break; |
| 826 | |
| 827 | case 'c': /* FALLTHRU */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 828 | x = npop(tps); |
| 829 | save_char(tps, x); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 830 | break; |
| 831 | |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 832 | #ifdef EXP_XTERM_1005 |
| 833 | case 'u': |
| 834 | { |
| 835 | unsigned char target[10]; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 836 | unsigned source = (unsigned) npop(tps); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 837 | int rc = _nc_conv_to_utf8(target, source, (unsigned) |
| 838 | sizeof(target)); |
| 839 | int n; |
| 840 | for (n = 0; n < rc; ++n) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 841 | save_char(tps, target[n]); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 842 | } |
| 843 | } |
| 844 | break; |
| 845 | #endif |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 846 | case 'l': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 847 | s = spop(tps); |
| 848 | npush(tps, (int) strlen(s)); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 849 | break; |
| 850 | |
| 851 | case 's': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 852 | s = spop(tps); |
| 853 | save_text(tps, TPS(fmt_buff), s, len); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 854 | break; |
| 855 | |
| 856 | case 'p': |
| 857 | cp++; |
| 858 | i = (UChar(*cp) - '1'); |
| 859 | if (i >= 0 && i < NUM_PARM) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 860 | if (data->p_is_s[i]) { |
| 861 | spush(tps, data->p_is_s[i]); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 862 | } else { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 863 | npush(tps, (int) data->param[i]); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 864 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 865 | } |
| 866 | break; |
| 867 | |
| 868 | case 'P': |
| 869 | cp++; |
| 870 | if (isUPPER(*cp)) { |
| 871 | i = (UChar(*cp) - 'A'); |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 872 | TPS(static_vars)[i] = npop(tps); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 873 | } else if (isLOWER(*cp)) { |
| 874 | i = (UChar(*cp) - 'a'); |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 875 | init_vars(dynamic); |
| 876 | dynamic_vars[i] = npop(tps); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 877 | } |
| 878 | break; |
| 879 | |
| 880 | case 'g': |
| 881 | cp++; |
| 882 | if (isUPPER(*cp)) { |
| 883 | i = (UChar(*cp) - 'A'); |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 884 | npush(tps, TPS(static_vars)[i]); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 885 | } else if (isLOWER(*cp)) { |
| 886 | i = (UChar(*cp) - 'a'); |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 887 | init_vars(dynamic); |
| 888 | npush(tps, dynamic_vars[i]); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 889 | } |
| 890 | break; |
| 891 | |
| 892 | case S_QUOTE: |
| 893 | cp++; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 894 | npush(tps, UChar(*cp)); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 895 | cp++; |
| 896 | break; |
| 897 | |
| 898 | case L_BRACE: |
| 899 | number = 0; |
| 900 | cp++; |
| 901 | while (isdigit(UChar(*cp))) { |
| 902 | number = (number * 10) + (UChar(*cp) - '0'); |
| 903 | cp++; |
| 904 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 905 | npush(tps, number); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 906 | break; |
| 907 | |
| 908 | case '+': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 909 | y = npop(tps); |
| 910 | x = npop(tps); |
| 911 | npush(tps, x + y); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 912 | break; |
| 913 | |
| 914 | case '-': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 915 | y = npop(tps); |
| 916 | x = npop(tps); |
| 917 | npush(tps, x - y); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 918 | break; |
| 919 | |
| 920 | case '*': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 921 | y = npop(tps); |
| 922 | x = npop(tps); |
| 923 | npush(tps, x * y); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 924 | break; |
| 925 | |
| 926 | case '/': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 927 | y = npop(tps); |
| 928 | x = npop(tps); |
| 929 | npush(tps, y ? (x / y) : 0); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 930 | break; |
| 931 | |
| 932 | case 'm': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 933 | y = npop(tps); |
| 934 | x = npop(tps); |
| 935 | npush(tps, y ? (x % y) : 0); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 936 | break; |
| 937 | |
| 938 | case 'A': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 939 | y = npop(tps); |
| 940 | x = npop(tps); |
| 941 | npush(tps, y && x); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 942 | break; |
| 943 | |
| 944 | case 'O': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 945 | y = npop(tps); |
| 946 | x = npop(tps); |
| 947 | npush(tps, y || x); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 948 | break; |
| 949 | |
| 950 | case '&': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 951 | y = npop(tps); |
| 952 | x = npop(tps); |
| 953 | npush(tps, x & y); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 954 | break; |
| 955 | |
| 956 | case '|': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 957 | y = npop(tps); |
| 958 | x = npop(tps); |
| 959 | npush(tps, x | y); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 960 | break; |
| 961 | |
| 962 | case '^': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 963 | y = npop(tps); |
| 964 | x = npop(tps); |
| 965 | npush(tps, x ^ y); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 966 | break; |
| 967 | |
| 968 | case '=': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 969 | y = npop(tps); |
| 970 | x = npop(tps); |
| 971 | npush(tps, x == y); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 972 | break; |
| 973 | |
| 974 | case '<': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 975 | y = npop(tps); |
| 976 | x = npop(tps); |
| 977 | npush(tps, x < y); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 978 | break; |
| 979 | |
| 980 | case '>': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 981 | y = npop(tps); |
| 982 | x = npop(tps); |
| 983 | npush(tps, x > y); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 984 | break; |
| 985 | |
| 986 | case '!': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 987 | x = npop(tps); |
| 988 | npush(tps, !x); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 989 | break; |
| 990 | |
| 991 | case '~': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 992 | x = npop(tps); |
| 993 | npush(tps, ~x); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 994 | break; |
| 995 | |
| 996 | case 'i': |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 997 | /* |
| 998 | * Increment the first two parameters -- if they are numbers |
| 999 | * rather than strings. As a side effect, assign into the |
| 1000 | * stack; if this is termcap, then the stack was populated |
| 1001 | * using the termcap hack above rather than via the terminfo |
| 1002 | * 'p' case. |
| 1003 | */ |
| 1004 | if (!incremented_two) { |
| 1005 | incremented_two = TRUE; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1006 | if (data->p_is_s[0] == 0) { |
| 1007 | data->param[0]++; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1008 | if (termcap_hack) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1009 | TPS(stack)[0].data.num = (int) data->param[0]; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1010 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1011 | if (data->p_is_s[1] == 0) { |
| 1012 | data->param[1]++; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1013 | if (termcap_hack) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1014 | TPS(stack)[1].data.num = (int) data->param[1]; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1015 | } |
| 1016 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1017 | break; |
| 1018 | |
| 1019 | case '?': |
| 1020 | break; |
| 1021 | |
| 1022 | case 't': |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1023 | x = npop(tps); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1024 | if (!x) { |
| 1025 | /* scan forward for %e or %; at level zero */ |
| 1026 | cp++; |
| 1027 | level = 0; |
| 1028 | while (*cp) { |
| 1029 | if (*cp == '%') { |
| 1030 | cp++; |
| 1031 | if (*cp == '?') |
| 1032 | level++; |
| 1033 | else if (*cp == ';') { |
| 1034 | if (level > 0) |
| 1035 | level--; |
| 1036 | else |
| 1037 | break; |
| 1038 | } else if (*cp == 'e' && level == 0) |
| 1039 | break; |
| 1040 | } |
| 1041 | |
| 1042 | if (*cp) |
| 1043 | cp++; |
| 1044 | } |
| 1045 | } |
| 1046 | break; |
| 1047 | |
| 1048 | case 'e': |
| 1049 | /* scan forward for a %; at level zero */ |
| 1050 | cp++; |
| 1051 | level = 0; |
| 1052 | while (*cp) { |
| 1053 | if (*cp == '%') { |
| 1054 | cp++; |
| 1055 | if (*cp == '?') |
| 1056 | level++; |
| 1057 | else if (*cp == ';') { |
| 1058 | if (level > 0) |
| 1059 | level--; |
| 1060 | else |
| 1061 | break; |
| 1062 | } |
| 1063 | } |
| 1064 | |
| 1065 | if (*cp) |
| 1066 | cp++; |
| 1067 | } |
| 1068 | break; |
| 1069 | |
| 1070 | case ';': |
| 1071 | break; |
| 1072 | |
| 1073 | } /* endswitch (*cp) */ |
| 1074 | } /* endelse (*cp == '%') */ |
| 1075 | |
| 1076 | if (*cp == '\0') |
| 1077 | break; |
| 1078 | |
| 1079 | cp++; |
| 1080 | } /* endwhile (*cp) */ |
| 1081 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1082 | get_space(tps, (size_t) 1); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1083 | TPS(out_buff)[TPS(out_used)] = '\0'; |
| 1084 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1085 | if (TPS(stack_ptr) && !_nc_tparm_err) { |
| 1086 | DEBUG(2, ("tparm: stack has %d item%s on return", |
| 1087 | TPS(stack_ptr), |
| 1088 | TPS(stack_ptr) == 1 ? "" : "s")); |
| 1089 | _nc_tparm_err++; |
| 1090 | } |
| 1091 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1092 | T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff)))); |
| 1093 | return (TPS(out_buff)); |
| 1094 | } |
| 1095 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1096 | #ifdef CUR |
| 1097 | /* |
| 1098 | * Only a few standard capabilities accept string parameters. The others that |
| 1099 | * are parameterized accept only numeric parameters. |
| 1100 | */ |
| 1101 | static bool |
| 1102 | check_string_caps(TPARM_DATA *data, const char *string) |
| 1103 | { |
| 1104 | bool result = FALSE; |
| 1105 | |
| 1106 | #define CHECK_CAP(name) (VALID_STRING(name) && !strcmp(name, string)) |
| 1107 | |
| 1108 | /* |
| 1109 | * Disallow string parameters unless we can check them against a terminal |
| 1110 | * description. |
| 1111 | */ |
| 1112 | if (cur_term != NULL) { |
| 1113 | int want_type = 0; |
| 1114 | |
| 1115 | if (CHECK_CAP(pkey_key)) |
| 1116 | want_type = 2; /* function key #1, type string #2 */ |
| 1117 | else if (CHECK_CAP(pkey_local)) |
| 1118 | want_type = 2; /* function key #1, execute string #2 */ |
| 1119 | else if (CHECK_CAP(pkey_xmit)) |
| 1120 | want_type = 2; /* function key #1, transmit string #2 */ |
| 1121 | else if (CHECK_CAP(plab_norm)) |
| 1122 | want_type = 2; /* label #1, show string #2 */ |
| 1123 | #ifdef pkey_plab |
| 1124 | else if (CHECK_CAP(pkey_plab)) |
| 1125 | want_type = 6; /* function key #1, type string #2, show string #3 */ |
| 1126 | #endif |
| 1127 | #if NCURSES_XNAMES |
| 1128 | else { |
| 1129 | char *check; |
| 1130 | |
| 1131 | check = tigetstr("Cs"); |
| 1132 | if (CHECK_CAP(check)) |
| 1133 | want_type = 1; /* style #1 */ |
| 1134 | |
| 1135 | check = tigetstr("Ms"); |
| 1136 | if (CHECK_CAP(check)) |
| 1137 | want_type = 3; /* storage unit #1, content #2 */ |
| 1138 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1139 | #endif |
| 1140 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1141 | if (want_type == data->tparm_type) { |
| 1142 | result = TRUE; |
| 1143 | } else { |
| 1144 | T(("unexpected string-parameter")); |
| 1145 | } |
| 1146 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1147 | return result; |
| 1148 | } |
| 1149 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1150 | #define ValidCap(allow_strings) (myData.tparm_type == 0 || \ |
| 1151 | (allow_strings && \ |
| 1152 | check_string_caps(&myData, string))) |
| 1153 | #else |
| 1154 | #define ValidCap(allow_strings) 1 |
| 1155 | #endif |
| 1156 | |
| 1157 | #if NCURSES_TPARM_VARARGS |
| 1158 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1159 | NCURSES_EXPORT(char *) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1160 | tparm(const char *string, ...) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1161 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1162 | TPARM_STATE *tps = get_tparm_state(cur_term); |
| 1163 | TPARM_DATA myData; |
| 1164 | char *result = NULL; |
| 1165 | |
| 1166 | _nc_tparm_err = 0; |
| 1167 | #ifdef TRACE |
| 1168 | tps->tname = "tparm"; |
| 1169 | #endif /* TRACE */ |
| 1170 | |
| 1171 | if (tparm_setup(cur_term, string, &myData) == OK && ValidCap(TRUE)) { |
| 1172 | va_list ap; |
| 1173 | |
| 1174 | va_start(ap, string); |
| 1175 | tparm_copy_valist(&myData, TRUE, ap); |
| 1176 | va_end(ap); |
| 1177 | |
| 1178 | result = tparam_internal(tps, string, &myData); |
| 1179 | } |
| 1180 | return result; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1181 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1182 | |
| 1183 | #else /* !NCURSES_TPARM_VARARGS */ |
| 1184 | |
| 1185 | NCURSES_EXPORT(char *) |
| 1186 | tparm(const char *string, |
| 1187 | TPARM_ARG a1, |
| 1188 | TPARM_ARG a2, |
| 1189 | TPARM_ARG a3, |
| 1190 | TPARM_ARG a4, |
| 1191 | TPARM_ARG a5, |
| 1192 | TPARM_ARG a6, |
| 1193 | TPARM_ARG a7, |
| 1194 | TPARM_ARG a8, |
| 1195 | TPARM_ARG a9) |
| 1196 | { |
| 1197 | TPARM_STATE *tps = get_tparm_state(cur_term); |
| 1198 | TPARM_DATA myData; |
| 1199 | char *result = NULL; |
| 1200 | |
| 1201 | _nc_tparm_err = 0; |
| 1202 | #ifdef TRACE |
| 1203 | tps->tname = "tparm"; |
| 1204 | #endif /* TRACE */ |
| 1205 | |
| 1206 | #define string_ok (sizeof(char*) <= sizeof(TPARM_ARG)) |
| 1207 | |
| 1208 | if (tparm_setup(cur_term, string, &myData) == OK && ValidCap(string_ok)) { |
| 1209 | |
| 1210 | myData.param[0] = a1; |
| 1211 | myData.param[1] = a2; |
| 1212 | myData.param[2] = a3; |
| 1213 | myData.param[3] = a4; |
| 1214 | myData.param[4] = a5; |
| 1215 | myData.param[5] = a6; |
| 1216 | myData.param[6] = a7; |
| 1217 | myData.param[7] = a8; |
| 1218 | myData.param[8] = a9; |
| 1219 | |
| 1220 | result = tparam_internal(tps, string, &myData); |
| 1221 | } |
| 1222 | return result; |
| 1223 | } |
| 1224 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1225 | #endif /* NCURSES_TPARM_VARARGS */ |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1226 | |
| 1227 | NCURSES_EXPORT(char *) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1228 | tiparm(const char *string, ...) |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1229 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1230 | TPARM_STATE *tps = get_tparm_state(cur_term); |
| 1231 | TPARM_DATA myData; |
| 1232 | char *result = NULL; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1233 | |
| 1234 | _nc_tparm_err = 0; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1235 | #ifdef TRACE |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1236 | tps->tname = "tiparm"; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1237 | #endif /* TRACE */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1238 | |
| 1239 | if (tparm_setup(cur_term, string, &myData) == OK && ValidCap(TRUE)) { |
| 1240 | va_list ap; |
| 1241 | |
| 1242 | va_start(ap, string); |
| 1243 | tparm_copy_valist(&myData, FALSE, ap); |
| 1244 | va_end(ap); |
| 1245 | |
| 1246 | result = tparam_internal(tps, string, &myData); |
| 1247 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 1248 | return result; |
| 1249 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1250 | |
| 1251 | /* |
| 1252 | * Use tparm if the formatting string matches the expected number of parameters |
| 1253 | * counting string-parameters. |
| 1254 | */ |
| 1255 | NCURSES_EXPORT(char *) |
| 1256 | tiparm_s(int num_expected, int tparm_type, const char *string, ...) |
| 1257 | { |
| 1258 | TPARM_STATE *tps = get_tparm_state(cur_term); |
| 1259 | TPARM_DATA myData; |
| 1260 | char *result = NULL; |
| 1261 | |
| 1262 | _nc_tparm_err = 0; |
| 1263 | #ifdef TRACE |
| 1264 | tps->tname = "tiparm_s"; |
| 1265 | #endif /* TRACE */ |
| 1266 | if (num_expected >= 0 && |
| 1267 | num_expected <= 9 && |
| 1268 | tparm_type >= 0 && |
| 1269 | tparm_type < 7 && /* limit to 2 string parameters */ |
| 1270 | tparm_setup(cur_term, string, &myData) == OK && |
| 1271 | myData.tparm_type == tparm_type && |
| 1272 | myData.num_actual == num_expected) { |
| 1273 | va_list ap; |
| 1274 | |
| 1275 | va_start(ap, string); |
| 1276 | tparm_copy_valist(&myData, FALSE, ap); |
| 1277 | va_end(ap); |
| 1278 | |
| 1279 | result = tparam_internal(tps, string, &myData); |
| 1280 | } |
| 1281 | return result; |
| 1282 | } |
| 1283 | |
| 1284 | /* |
| 1285 | * Analyze the formatting string, return the analysis. |
| 1286 | */ |
| 1287 | NCURSES_EXPORT(int) |
| 1288 | tiscan_s(int *num_expected, int *tparm_type, const char *string) |
| 1289 | { |
| 1290 | TPARM_DATA myData; |
| 1291 | int result = ERR; |
| 1292 | |
| 1293 | #ifdef TRACE |
| 1294 | TPARM_STATE *tps = get_tparm_state(cur_term); |
| 1295 | tps->tname = "tiscan_s"; |
| 1296 | #endif /* TRACE */ |
| 1297 | |
| 1298 | if (tparm_setup(cur_term, string, &myData) == OK) { |
| 1299 | *num_expected = myData.num_actual; |
| 1300 | *tparm_type = myData.tparm_type; |
| 1301 | result = OK; |
| 1302 | } |
| 1303 | return result; |
| 1304 | } |
| 1305 | |
| 1306 | /* |
| 1307 | * The internal-use flavor ensures that parameters are numbers, not strings. |
| 1308 | * In addition to ensuring that they are numbers, it ensures that the parameter |
| 1309 | * count is consistent with intended usage. |
| 1310 | * |
| 1311 | * Unlike the general-purpose tparm/tiparm, these internal calls are fairly |
| 1312 | * well defined: |
| 1313 | * |
| 1314 | * expected == 0 - not applicable |
| 1315 | * expected == 1 - set color, or vertical/horizontal addressing |
| 1316 | * expected == 2 - cursor addressing |
| 1317 | * expected == 4 - initialize color or color pair |
| 1318 | * expected == 9 - set attributes |
| 1319 | * |
| 1320 | * Only for the last case (set attributes) should a parameter be optional. |
| 1321 | * Also, a capability which calls for more parameters than expected should be |
| 1322 | * ignored. |
| 1323 | * |
| 1324 | * Return a null if the parameter-checks fail. Otherwise, return a pointer to |
| 1325 | * the formatted capability string. |
| 1326 | */ |
| 1327 | NCURSES_EXPORT(char *) |
| 1328 | _nc_tiparm(int expected, const char *string, ...) |
| 1329 | { |
| 1330 | TPARM_STATE *tps = get_tparm_state(cur_term); |
| 1331 | TPARM_DATA myData; |
| 1332 | char *result = NULL; |
| 1333 | |
| 1334 | _nc_tparm_err = 0; |
| 1335 | T((T_CALLED("_nc_tiparm(%d, %s, ...)"), expected, _nc_visbuf(string))); |
| 1336 | #ifdef TRACE |
| 1337 | tps->tname = "_nc_tiparm"; |
| 1338 | #endif /* TRACE */ |
| 1339 | |
| 1340 | if (tparm_setup(cur_term, string, &myData) == OK && ValidCap(FALSE)) { |
| 1341 | #ifdef CUR |
| 1342 | if (myData.num_actual != expected && cur_term != NULL) { |
| 1343 | int needed = expected; |
| 1344 | if (CHECK_CAP(to_status_line)) { |
| 1345 | needed = 0; /* allow for xterm's status line */ |
| 1346 | } else if (CHECK_CAP(set_a_background)) { |
| 1347 | needed = 0; /* allow for monochrome fakers */ |
| 1348 | } else if (CHECK_CAP(set_a_foreground)) { |
| 1349 | needed = 0; |
| 1350 | } else if (CHECK_CAP(set_background)) { |
| 1351 | needed = 0; |
| 1352 | } else if (CHECK_CAP(set_foreground)) { |
| 1353 | needed = 0; |
| 1354 | } |
| 1355 | #if NCURSES_XNAMES |
| 1356 | else { |
| 1357 | char *check; |
| 1358 | |
| 1359 | check = tigetstr("xm"); |
| 1360 | if (CHECK_CAP(check)) { |
| 1361 | needed = 3; |
| 1362 | } |
| 1363 | check = tigetstr("S0"); |
| 1364 | if (CHECK_CAP(check)) { |
| 1365 | needed = 0; /* used in screen-base */ |
| 1366 | } |
| 1367 | } |
| 1368 | #endif |
| 1369 | if (myData.num_actual >= needed && myData.num_actual <= expected) |
| 1370 | expected = myData.num_actual; |
| 1371 | } |
| 1372 | #endif |
| 1373 | if (myData.num_actual == 0 && expected) { |
| 1374 | T(("missing parameter%s, expected %s%d", |
| 1375 | expected > 1 ? "s" : "", |
| 1376 | expected == 9 ? "up to " : "", |
| 1377 | expected)); |
| 1378 | } else if (myData.num_actual > expected) { |
| 1379 | T(("too many parameters, have %d, expected %d", |
| 1380 | myData.num_actual, |
| 1381 | expected)); |
| 1382 | } else if (expected != 9 && myData.num_actual != expected) { |
| 1383 | T(("expected %d parameters, have %d", |
| 1384 | myData.num_actual, |
| 1385 | expected)); |
| 1386 | } else { |
| 1387 | va_list ap; |
| 1388 | |
| 1389 | va_start(ap, string); |
| 1390 | tparm_copy_valist(&myData, FALSE, ap); |
| 1391 | va_end(ap); |
| 1392 | |
| 1393 | result = tparam_internal(tps, string, &myData); |
| 1394 | } |
| 1395 | } |
| 1396 | returnPtr(result); |
| 1397 | } |
| 1398 | |
| 1399 | /* |
| 1400 | * Improve tic's checks by resetting the terminfo "static variables" before |
| 1401 | * calling functions which may update them. |
| 1402 | */ |
| 1403 | NCURSES_EXPORT(void) |
| 1404 | _nc_reset_tparm(TERMINAL *term) |
| 1405 | { |
| 1406 | TPARM_STATE *tps = get_tparm_state(term); |
| 1407 | memset(TPS(static_vars), 0, sizeof(TPS(static_vars))); |
| 1408 | } |