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-2022,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 | * parse_entry.c -- compile one terminfo or termcap entry |
| 38 | * |
| 39 | * Get an exact in-core representation of an entry. Don't |
| 40 | * try to resolve use or tc capabilities, that is someone |
| 41 | * else's job. Depends on the lexical analyzer to get tokens |
| 42 | * from the input stream. |
| 43 | */ |
| 44 | |
| 45 | #define __INTERNAL_CAPS_VISIBLE |
| 46 | #include <curses.priv.h> |
| 47 | |
| 48 | #include <ctype.h> |
| 49 | #include <tic.h> |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 50 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 51 | MODULE_ID("$Id: parse_entry.c,v 1.108 2023/04/24 22:32:33 tom Exp $") |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 52 | |
| 53 | #ifdef LINT |
| 54 | static short const parametrized[] = |
| 55 | {0}; |
| 56 | #else |
| 57 | #include <parametrized.h> |
| 58 | #endif |
| 59 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 60 | static void postprocess_termcap(TERMTYPE2 *, bool); |
| 61 | static void postprocess_terminfo(TERMTYPE2 *); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 62 | static struct name_table_entry const *lookup_fullname(const char *name); |
| 63 | |
| 64 | #if NCURSES_XNAMES |
| 65 | |
| 66 | static struct name_table_entry const * |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 67 | _nc_extend_names(ENTRY * entryp, const char *name, int token_type) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 68 | { |
| 69 | static struct name_table_entry temp; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 70 | TERMTYPE2 *tp = &(entryp->tterm); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 71 | unsigned offset = 0; |
| 72 | unsigned actual; |
| 73 | unsigned tindex; |
| 74 | unsigned first, last, n; |
| 75 | bool found; |
| 76 | |
| 77 | switch (token_type) { |
| 78 | case BOOLEAN: |
| 79 | first = 0; |
| 80 | last = tp->ext_Booleans; |
| 81 | offset = tp->ext_Booleans; |
| 82 | tindex = tp->num_Booleans; |
| 83 | break; |
| 84 | case NUMBER: |
| 85 | first = tp->ext_Booleans; |
| 86 | last = tp->ext_Numbers + first; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 87 | offset = (unsigned) (tp->ext_Booleans + tp->ext_Numbers); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 88 | tindex = tp->num_Numbers; |
| 89 | break; |
| 90 | case STRING: |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 91 | first = (unsigned) (tp->ext_Booleans + tp->ext_Numbers); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 92 | last = tp->ext_Strings + first; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 93 | offset = (unsigned) (tp->ext_Booleans + tp->ext_Numbers + tp->ext_Strings); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 94 | tindex = tp->num_Strings; |
| 95 | break; |
| 96 | case CANCEL: |
| 97 | actual = NUM_EXT_NAMES(tp); |
| 98 | for (n = 0; n < actual; n++) { |
| 99 | if (!strcmp(name, tp->ext_Names[n])) { |
| 100 | if (n > (unsigned) (tp->ext_Booleans + tp->ext_Numbers)) { |
| 101 | token_type = STRING; |
| 102 | } else if (n > tp->ext_Booleans) { |
| 103 | token_type = NUMBER; |
| 104 | } else { |
| 105 | token_type = BOOLEAN; |
| 106 | } |
| 107 | return _nc_extend_names(entryp, name, token_type); |
| 108 | } |
| 109 | } |
| 110 | /* Well, we are given a cancel for a name that we don't recognize */ |
| 111 | return _nc_extend_names(entryp, name, STRING); |
| 112 | default: |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 113 | return NULL; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 114 | } |
| 115 | |
| 116 | /* Adjust the 'offset' (insertion-point) to keep the lists of extended |
| 117 | * names sorted. |
| 118 | */ |
| 119 | for (n = first, found = FALSE; n < last; n++) { |
| 120 | int cmp = strcmp(tp->ext_Names[n], name); |
| 121 | if (cmp == 0) |
| 122 | found = TRUE; |
| 123 | if (cmp >= 0) { |
| 124 | offset = n; |
| 125 | tindex = n - first; |
| 126 | switch (token_type) { |
| 127 | case BOOLEAN: |
| 128 | tindex += BOOLCOUNT; |
| 129 | break; |
| 130 | case NUMBER: |
| 131 | tindex += NUMCOUNT; |
| 132 | break; |
| 133 | case STRING: |
| 134 | tindex += STRCOUNT; |
| 135 | break; |
| 136 | } |
| 137 | break; |
| 138 | } |
| 139 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 140 | |
| 141 | #define for_each_value(max) \ |
| 142 | for (last = (unsigned) (max - 1); last > tindex; last--) |
| 143 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 144 | if (!found) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 145 | char *saved; |
| 146 | |
| 147 | if ((saved = _nc_save_str(name)) == NULL) |
| 148 | return NULL; |
| 149 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 150 | switch (token_type) { |
| 151 | case BOOLEAN: |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 152 | tp->ext_Booleans++; |
| 153 | tp->num_Booleans++; |
| 154 | TYPE_REALLOC(NCURSES_SBOOL, tp->num_Booleans, tp->Booleans); |
| 155 | for_each_value(tp->num_Booleans) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 156 | tp->Booleans[last] = tp->Booleans[last - 1]; |
| 157 | break; |
| 158 | case NUMBER: |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 159 | tp->ext_Numbers++; |
| 160 | tp->num_Numbers++; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 161 | TYPE_REALLOC(NCURSES_INT2, tp->num_Numbers, tp->Numbers); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 162 | for_each_value(tp->num_Numbers) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 163 | tp->Numbers[last] = tp->Numbers[last - 1]; |
| 164 | break; |
| 165 | case STRING: |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 166 | tp->ext_Strings++; |
| 167 | tp->num_Strings++; |
| 168 | TYPE_REALLOC(char *, tp->num_Strings, tp->Strings); |
| 169 | for_each_value(tp->num_Strings) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 170 | tp->Strings[last] = tp->Strings[last - 1]; |
| 171 | break; |
| 172 | } |
| 173 | actual = NUM_EXT_NAMES(tp); |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 174 | TYPE_REALLOC(char *, actual, tp->ext_Names); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 175 | while (--actual > offset) |
| 176 | tp->ext_Names[actual] = tp->ext_Names[actual - 1]; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 177 | tp->ext_Names[offset] = saved; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 178 | } |
| 179 | |
| 180 | temp.nte_name = tp->ext_Names[offset]; |
| 181 | temp.nte_type = token_type; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 182 | temp.nte_index = (short) tindex; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 183 | temp.nte_link = -1; |
| 184 | |
| 185 | return &temp; |
| 186 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 187 | |
| 188 | static const char * |
| 189 | usertype2s(int mask) |
| 190 | { |
| 191 | const char *result = "unknown"; |
| 192 | if (mask & (1 << BOOLEAN)) { |
| 193 | result = "boolean"; |
| 194 | } else if (mask & (1 << NUMBER)) { |
| 195 | result = "number"; |
| 196 | } else if (mask & (1 << STRING)) { |
| 197 | result = "string"; |
| 198 | } |
| 199 | return result; |
| 200 | } |
| 201 | |
| 202 | static bool |
| 203 | expected_type(const char *name, int token_type, bool silent) |
| 204 | { |
| 205 | struct user_table_entry const *entry = _nc_find_user_entry(name); |
| 206 | bool result = TRUE; |
| 207 | if ((entry != 0) && (token_type != CANCEL)) { |
| 208 | int have_type = (1 << token_type); |
| 209 | if (!(entry->ute_type & have_type)) { |
| 210 | if (!silent) |
| 211 | _nc_warning("expected %s-type for %s, have %s", |
| 212 | usertype2s(entry->ute_type), |
| 213 | name, |
| 214 | usertype2s(have_type)); |
| 215 | result = FALSE; |
| 216 | } |
| 217 | } |
| 218 | return result; |
| 219 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 220 | #endif /* NCURSES_XNAMES */ |
| 221 | |
| 222 | /* |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 223 | * A valid entry name uses characters from the "portable character set" |
| 224 | * (more commonly referred to as US-ASCII), and disallows some of the |
| 225 | * punctuation characters: |
| 226 | * |
| 227 | * '/' is a pathname separator |
| 228 | * '\' may be a pathname separator, but more important, is an escape |
| 229 | * '|' delimits names and description |
| 230 | * '#' denotes a numeric value |
| 231 | * '=' denotes a string value |
| 232 | * '@' denotes a cancelled symbol |
| 233 | * ',' separates terminfo capabilities |
| 234 | * ':' separates termcap capabilities |
| 235 | * |
| 236 | * Termcap capability names may begin with a '#' or '@' (since they have |
| 237 | * exactly two characters). |
| 238 | */ |
| 239 | static bool |
| 240 | valid_entryname(const char *name) |
| 241 | { |
| 242 | bool result = TRUE; |
| 243 | bool first = TRUE; |
| 244 | int ch; |
| 245 | while ((ch = UChar(*name++)) != '\0') { |
| 246 | if (ch <= ' ' || ch > '~' || strchr("/\\|=,:", ch) != NULL) { |
| 247 | result = FALSE; |
| 248 | break; |
| 249 | } |
| 250 | if (!first && strchr("#@", ch) != NULL) { |
| 251 | result = FALSE; |
| 252 | break; |
| 253 | } |
| 254 | first = FALSE; |
| 255 | } |
| 256 | return result; |
| 257 | } |
| 258 | |
| 259 | /* |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 260 | * int |
| 261 | * _nc_parse_entry(entry, literal, silent) |
| 262 | * |
| 263 | * Compile one entry. Doesn't try to resolve use or tc capabilities. |
| 264 | * |
| 265 | * found-forward-use = FALSE |
| 266 | * re-initialise internal arrays |
| 267 | * get_token(); |
| 268 | * if the token was not a name in column 1, complain and die |
| 269 | * save names in entry's string table |
| 270 | * while (get_token() is not EOF and not NAMES) |
| 271 | * check for existence and type-correctness |
| 272 | * enter cap into structure |
| 273 | * if STRING |
| 274 | * save string in entry's string table |
| 275 | * push back token |
| 276 | */ |
| 277 | |
| 278 | #define BAD_TC_USAGE if (!bad_tc_usage) \ |
| 279 | { bad_tc_usage = TRUE; \ |
| 280 | _nc_warning("Legacy termcap allows only a trailing tc= clause"); } |
| 281 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 282 | #define MAX_NUMBER MAX_OF_TYPE(NCURSES_INT2) |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 283 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 284 | NCURSES_EXPORT(int) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 285 | _nc_parse_entry(ENTRY * entryp, int literal, bool silent) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 286 | { |
| 287 | int token_type; |
| 288 | struct name_table_entry const *entry_ptr; |
| 289 | char *ptr, *base; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 290 | const char *name; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 291 | bool bad_tc_usage = FALSE; |
| 292 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 293 | TR(TRACE_DATABASE, |
| 294 | (T_CALLED("_nc_parse_entry(entry=%p, literal=%d, silent=%d)"), |
| 295 | (void *) entryp, literal, silent)); |
| 296 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 297 | token_type = _nc_get_token(silent); |
| 298 | |
| 299 | if (token_type == EOF) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 300 | returnDB(EOF); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 301 | if (token_type != NAMES) |
| 302 | _nc_err_abort("Entry does not start with terminal names in column one"); |
| 303 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 304 | _nc_init_entry(entryp); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 305 | |
| 306 | entryp->cstart = _nc_comment_start; |
| 307 | entryp->cend = _nc_comment_end; |
| 308 | entryp->startline = _nc_start_line; |
| 309 | DEBUG(2, ("Comment range is %ld to %ld", entryp->cstart, entryp->cend)); |
| 310 | |
| 311 | /* |
| 312 | * Strip off the 2-character termcap name, if present. Originally termcap |
| 313 | * used that as an indexing aid. We can retain 2-character terminfo names, |
| 314 | * but note that they would be lost if we translate to/from termcap. This |
| 315 | * feature is supposedly obsolete since "newer" BSD implementations do not |
| 316 | * use it; however our reference for this feature is SunOS 4.x, which |
| 317 | * implemented it. Note that the resulting terminal type was never the |
| 318 | * 2-character name, but was instead the first alias after that. |
| 319 | */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 320 | #define ok_TC2(s) (isgraph(UChar(s)) && (s) != '|') |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 321 | ptr = _nc_curr_token.tk_name; |
| 322 | if (_nc_syntax == SYN_TERMCAP |
| 323 | #if NCURSES_XNAMES |
| 324 | && !_nc_user_definable |
| 325 | #endif |
| 326 | ) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 327 | if (ok_TC2(ptr[0]) && ok_TC2(ptr[1]) && (ptr[2] == '|')) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 328 | ptr += 3; |
| 329 | _nc_curr_token.tk_name[2] = '\0'; |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | entryp->tterm.str_table = entryp->tterm.term_names = _nc_save_str(ptr); |
| 334 | |
| 335 | if (entryp->tterm.str_table == 0) |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 336 | returnDB(ERR); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 337 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 338 | DEBUG(2, ("Starting '%s'", ptr)); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 339 | |
| 340 | /* |
| 341 | * We do this because the one-token lookahead in the parse loop |
| 342 | * results in the terminal type getting prematurely set to correspond |
| 343 | * to that of the next entry. |
| 344 | */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 345 | name = _nc_first_name(entryp->tterm.term_names); |
| 346 | if (!valid_entryname(name)) { |
| 347 | _nc_warning("invalid entry name \"%s\"", name); |
| 348 | name = "invalid"; |
| 349 | } |
| 350 | _nc_set_type(name); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 351 | |
| 352 | /* check for overly-long names and aliases */ |
| 353 | for (base = entryp->tterm.term_names; (ptr = strchr(base, '|')) != 0; |
| 354 | base = ptr + 1) { |
| 355 | if (ptr - base > MAX_ALIAS) { |
| 356 | _nc_warning("%s `%.*s' may be too long", |
| 357 | (base == entryp->tterm.term_names) |
| 358 | ? "primary name" |
| 359 | : "alias", |
| 360 | (int) (ptr - base), base); |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | entryp->nuses = 0; |
| 365 | |
| 366 | for (token_type = _nc_get_token(silent); |
| 367 | token_type != EOF && token_type != NAMES; |
| 368 | token_type = _nc_get_token(silent)) { |
| 369 | bool is_use = (strcmp(_nc_curr_token.tk_name, "use") == 0); |
| 370 | bool is_tc = !is_use && (strcmp(_nc_curr_token.tk_name, "tc") == 0); |
| 371 | if (is_use || is_tc) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 372 | char *saved; |
| 373 | |
| 374 | if (!VALID_STRING(_nc_curr_token.tk_valstring) |
| 375 | || _nc_curr_token.tk_valstring[0] == '\0') { |
| 376 | _nc_warning("missing name for use-clause"); |
| 377 | continue; |
| 378 | } else if (!valid_entryname(_nc_curr_token.tk_valstring)) { |
| 379 | _nc_warning("invalid name for use-clause \"%s\"", |
| 380 | _nc_curr_token.tk_valstring); |
| 381 | continue; |
| 382 | } else if (entryp->nuses >= MAX_USES) { |
| 383 | _nc_warning("too many use-clauses, ignored \"%s\"", |
| 384 | _nc_curr_token.tk_valstring); |
| 385 | continue; |
| 386 | } |
| 387 | if ((saved = _nc_save_str(_nc_curr_token.tk_valstring)) != NULL) { |
| 388 | entryp->uses[entryp->nuses].name = saved; |
| 389 | entryp->uses[entryp->nuses].line = _nc_curr_line; |
| 390 | entryp->nuses++; |
| 391 | if (entryp->nuses > 1 && is_tc) { |
| 392 | BAD_TC_USAGE |
| 393 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 394 | } |
| 395 | } else { |
| 396 | /* normal token lookup */ |
| 397 | entry_ptr = _nc_find_entry(_nc_curr_token.tk_name, |
| 398 | _nc_get_hash_table(_nc_syntax)); |
| 399 | |
| 400 | /* |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 401 | * Our kluge to handle aliasing. The reason it is done |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 402 | * this ugly way, with a linear search, is so the hashing |
| 403 | * machinery doesn't have to be made really complicated |
| 404 | * (also we get better warnings this way). No point in |
| 405 | * making this case fast, aliased caps aren't common now |
| 406 | * and will get rarer. |
| 407 | */ |
| 408 | if (entry_ptr == NOTFOUND) { |
| 409 | const struct alias *ap; |
| 410 | |
| 411 | if (_nc_syntax == SYN_TERMCAP) { |
| 412 | if (entryp->nuses != 0) { |
| 413 | BAD_TC_USAGE |
| 414 | } |
| 415 | for (ap = _nc_get_alias_table(TRUE); ap->from; ap++) |
| 416 | if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) { |
| 417 | if (ap->to == (char *) 0) { |
| 418 | _nc_warning("%s (%s termcap extension) ignored", |
| 419 | ap->from, ap->source); |
| 420 | goto nexttok; |
| 421 | } |
| 422 | |
| 423 | entry_ptr = _nc_find_entry(ap->to, |
| 424 | _nc_get_hash_table(TRUE)); |
| 425 | if (entry_ptr && !silent) |
| 426 | _nc_warning("%s (%s termcap extension) aliased to %s", |
| 427 | ap->from, ap->source, ap->to); |
| 428 | break; |
| 429 | } |
| 430 | } else { /* if (_nc_syntax == SYN_TERMINFO) */ |
| 431 | for (ap = _nc_get_alias_table(FALSE); ap->from; ap++) |
| 432 | if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) { |
| 433 | if (ap->to == (char *) 0) { |
| 434 | _nc_warning("%s (%s terminfo extension) ignored", |
| 435 | ap->from, ap->source); |
| 436 | goto nexttok; |
| 437 | } |
| 438 | |
| 439 | entry_ptr = _nc_find_entry(ap->to, |
| 440 | _nc_get_hash_table(FALSE)); |
| 441 | if (entry_ptr && !silent) |
| 442 | _nc_warning("%s (%s terminfo extension) aliased to %s", |
| 443 | ap->from, ap->source, ap->to); |
| 444 | break; |
| 445 | } |
| 446 | |
| 447 | if (entry_ptr == NOTFOUND) { |
| 448 | entry_ptr = lookup_fullname(_nc_curr_token.tk_name); |
| 449 | } |
| 450 | } |
| 451 | } |
| 452 | #if NCURSES_XNAMES |
| 453 | /* |
| 454 | * If we have extended-names active, we will automatically |
| 455 | * define a name based on its context. |
| 456 | */ |
| 457 | if (entry_ptr == NOTFOUND |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 458 | && _nc_user_definable) { |
| 459 | if (expected_type(_nc_curr_token.tk_name, token_type, silent)) { |
| 460 | if ((entry_ptr = _nc_extend_names(entryp, |
| 461 | _nc_curr_token.tk_name, |
| 462 | token_type)) != 0) { |
| 463 | if (_nc_tracing >= DEBUG_LEVEL(1)) { |
| 464 | _nc_warning("extended capability '%s'", |
| 465 | _nc_curr_token.tk_name); |
| 466 | } |
| 467 | } |
| 468 | } else { |
| 469 | /* ignore it: we have already printed error message */ |
| 470 | continue; |
| 471 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 472 | } |
| 473 | #endif /* NCURSES_XNAMES */ |
| 474 | |
| 475 | /* can't find this cap name, not even as an alias */ |
| 476 | if (entry_ptr == NOTFOUND) { |
| 477 | if (!silent) |
| 478 | _nc_warning("unknown capability '%s'", |
| 479 | _nc_curr_token.tk_name); |
| 480 | continue; |
| 481 | } |
| 482 | |
| 483 | /* deal with bad type/value combinations. */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 484 | if (token_type == CANCEL) { |
| 485 | /* |
| 486 | * Prefer terminfo in this (long-obsolete) ambiguity: |
| 487 | */ |
| 488 | if (!strcmp("ma", _nc_curr_token.tk_name)) { |
| 489 | entry_ptr = _nc_find_type_entry("ma", NUMBER, |
| 490 | _nc_syntax != 0); |
| 491 | assert(entry_ptr != 0); |
| 492 | } |
| 493 | } else if (entry_ptr->nte_type != token_type) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 494 | /* |
| 495 | * Nasty special cases here handle situations in which type |
| 496 | * information can resolve name clashes. Normal lookup |
| 497 | * finds the last instance in the capability table of a |
| 498 | * given name, regardless of type. find_type_entry looks |
| 499 | * for a first matching instance with given type. So as |
| 500 | * long as all ambiguous names occur in pairs of distinct |
| 501 | * type, this will do the job. |
| 502 | */ |
| 503 | |
| 504 | if (token_type == NUMBER |
| 505 | && !strcmp("ma", _nc_curr_token.tk_name)) { |
| 506 | /* tell max_attributes from arrow_key_map */ |
| 507 | entry_ptr = _nc_find_type_entry("ma", NUMBER, |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 508 | _nc_syntax != 0); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 509 | assert(entry_ptr != 0); |
| 510 | |
| 511 | } else if (token_type == STRING |
| 512 | && !strcmp("MT", _nc_curr_token.tk_name)) { |
| 513 | /* map terminfo's string MT to MT */ |
| 514 | entry_ptr = _nc_find_type_entry("MT", STRING, |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 515 | _nc_syntax != 0); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 516 | assert(entry_ptr != 0); |
| 517 | |
| 518 | } else if (token_type == BOOLEAN |
| 519 | && entry_ptr->nte_type == STRING) { |
| 520 | /* treat strings without following "=" as empty strings */ |
| 521 | token_type = STRING; |
| 522 | } else { |
| 523 | /* we couldn't recover; skip this token */ |
| 524 | if (!silent) { |
| 525 | const char *type_name; |
| 526 | switch (entry_ptr->nte_type) { |
| 527 | case BOOLEAN: |
| 528 | type_name = "boolean"; |
| 529 | break; |
| 530 | case STRING: |
| 531 | type_name = "string"; |
| 532 | break; |
| 533 | case NUMBER: |
| 534 | type_name = "numeric"; |
| 535 | break; |
| 536 | default: |
| 537 | type_name = "unknown"; |
| 538 | break; |
| 539 | } |
| 540 | _nc_warning("wrong type used for %s capability '%s'", |
| 541 | type_name, _nc_curr_token.tk_name); |
| 542 | } |
| 543 | continue; |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | /* now we know that the type/value combination is OK */ |
| 548 | switch (token_type) { |
| 549 | case CANCEL: |
| 550 | switch (entry_ptr->nte_type) { |
| 551 | case BOOLEAN: |
| 552 | entryp->tterm.Booleans[entry_ptr->nte_index] = CANCELLED_BOOLEAN; |
| 553 | break; |
| 554 | |
| 555 | case NUMBER: |
| 556 | entryp->tterm.Numbers[entry_ptr->nte_index] = CANCELLED_NUMERIC; |
| 557 | break; |
| 558 | |
| 559 | case STRING: |
| 560 | entryp->tterm.Strings[entry_ptr->nte_index] = CANCELLED_STRING; |
| 561 | break; |
| 562 | } |
| 563 | break; |
| 564 | |
| 565 | case BOOLEAN: |
| 566 | entryp->tterm.Booleans[entry_ptr->nte_index] = TRUE; |
| 567 | break; |
| 568 | |
| 569 | case NUMBER: |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 570 | #if !NCURSES_EXT_NUMBERS |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 571 | if (_nc_curr_token.tk_valnumber > MAX_NUMBER) { |
| 572 | entryp->tterm.Numbers[entry_ptr->nte_index] = MAX_NUMBER; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 573 | } else |
| 574 | #endif |
| 575 | { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 576 | entryp->tterm.Numbers[entry_ptr->nte_index] = |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 577 | (NCURSES_INT2) _nc_curr_token.tk_valnumber; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 578 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 579 | break; |
| 580 | |
| 581 | case STRING: |
| 582 | ptr = _nc_curr_token.tk_valstring; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 583 | if (_nc_syntax == SYN_TERMCAP) { |
| 584 | int n = entry_ptr->nte_index; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 585 | ptr = _nc_captoinfo(_nc_curr_token.tk_name, |
| 586 | ptr, |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 587 | (n < (int) SIZEOF(parametrized)) |
| 588 | ? parametrized[n] |
| 589 | : 0); |
| 590 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 591 | entryp->tterm.Strings[entry_ptr->nte_index] = _nc_save_str(ptr); |
| 592 | break; |
| 593 | |
| 594 | default: |
| 595 | if (!silent) |
| 596 | _nc_warning("unknown token type"); |
| 597 | _nc_panic_mode((char) ((_nc_syntax == SYN_TERMCAP) ? ':' : ',')); |
| 598 | continue; |
| 599 | } |
| 600 | } /* end else cur_token.name != "use" */ |
| 601 | nexttok: |
| 602 | continue; /* cannot have a label w/o statement */ |
| 603 | } /* endwhile (not EOF and not NAMES) */ |
| 604 | |
| 605 | _nc_push_token(token_type); |
| 606 | _nc_set_type(_nc_first_name(entryp->tterm.term_names)); |
| 607 | |
| 608 | /* |
| 609 | * Try to deduce as much as possible from extension capabilities |
| 610 | * (this includes obsolete BSD capabilities). Sigh...it would be more |
| 611 | * space-efficient to call this after use resolution, but it has |
| 612 | * to be done before entry allocation is wrapped up. |
| 613 | */ |
| 614 | if (!literal) { |
| 615 | if (_nc_syntax == SYN_TERMCAP) { |
| 616 | bool has_base_entry = FALSE; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 617 | |
| 618 | /* |
| 619 | * Don't insert defaults if this is a `+' entry meant only |
| 620 | * for inclusion in other entries (not sure termcap ever |
| 621 | * had these, actually). |
| 622 | */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 623 | if (strchr(entryp->tterm.term_names, '+')) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 624 | has_base_entry = TRUE; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 625 | } else { |
| 626 | unsigned i; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 627 | /* |
| 628 | * Otherwise, look for a base entry that will already |
| 629 | * have picked up defaults via translation. |
| 630 | */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 631 | for (i = 0; i < entryp->nuses; i++) { |
| 632 | if (entryp->uses[i].name != 0 |
| 633 | && !strchr(entryp->uses[i].name, '+')) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 634 | has_base_entry = TRUE; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 635 | } |
| 636 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 637 | |
| 638 | postprocess_termcap(&entryp->tterm, has_base_entry); |
| 639 | } else |
| 640 | postprocess_terminfo(&entryp->tterm); |
| 641 | } |
| 642 | _nc_wrap_entry(entryp, FALSE); |
| 643 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 644 | returnDB(OK); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 645 | } |
| 646 | |
| 647 | NCURSES_EXPORT(int) |
| 648 | _nc_capcmp(const char *s, const char *t) |
| 649 | /* compare two string capabilities, stripping out padding */ |
| 650 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 651 | bool ok_s = VALID_STRING(s); |
| 652 | bool ok_t = VALID_STRING(t); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 653 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 654 | if (ok_s && ok_t) { |
| 655 | for (;;) { |
| 656 | if (s[0] == '$' && s[1] == '<') { |
| 657 | for (s += 2;; s++) { |
| 658 | if (!(isdigit(UChar(*s)) |
| 659 | || *s == '.' |
| 660 | || *s == '*' |
| 661 | || *s == '/' |
| 662 | || *s == '>')) { |
| 663 | break; |
| 664 | } |
| 665 | } |
| 666 | } |
| 667 | |
| 668 | if (t[0] == '$' && t[1] == '<') { |
| 669 | for (t += 2;; t++) { |
| 670 | if (!(isdigit(UChar(*t)) |
| 671 | || *t == '.' |
| 672 | || *t == '*' |
| 673 | || *t == '/' |
| 674 | || *t == '>')) { |
| 675 | break; |
| 676 | } |
| 677 | } |
| 678 | } |
| 679 | |
| 680 | /* we've now pushed s and t past any padding they pointed at */ |
| 681 | |
| 682 | if (*s == '\0' && *t == '\0') |
| 683 | return (0); |
| 684 | |
| 685 | if (*s != *t) |
| 686 | return (*t - *s); |
| 687 | |
| 688 | /* else *s == *t but one is not NUL, so continue */ |
| 689 | s++, t++; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 690 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 691 | } else if (ok_s || ok_t) { |
| 692 | return 1; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 693 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 694 | return 0; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 695 | } |
| 696 | |
| 697 | static void |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 698 | append_acs0(string_desc * dst, int code, char *src, size_t off) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 699 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 700 | if (src != 0 && off < strlen(src)) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 701 | char temp[3]; |
| 702 | temp[0] = (char) code; |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 703 | temp[1] = src[off]; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 704 | temp[2] = 0; |
| 705 | _nc_safe_strcat(dst, temp); |
| 706 | } |
| 707 | } |
| 708 | |
| 709 | static void |
| 710 | append_acs(string_desc * dst, int code, char *src) |
| 711 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 712 | if (VALID_STRING(src) && strlen(src) == 1) { |
| 713 | append_acs0(dst, code, src, 0); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 714 | } |
| 715 | } |
| 716 | |
| 717 | /* |
| 718 | * The ko capability, if present, consists of a comma-separated capability |
| 719 | * list. For each capability, we may assume there is a keycap that sends the |
| 720 | * string which is the value of that capability. |
| 721 | */ |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 722 | #define DATA(from, to) { { from }, { to } } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 723 | typedef struct { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 724 | const char from[3]; |
| 725 | const char to[6]; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 726 | } assoc; |
| 727 | static assoc const ko_xlate[] = |
| 728 | { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 729 | DATA("al", "kil1"), /* insert line key -> KEY_IL */ |
| 730 | DATA("bt", "kcbt"), /* back tab -> KEY_BTAB */ |
| 731 | DATA("cd", "ked"), /* clear-to-eos key -> KEY_EOL */ |
| 732 | DATA("ce", "kel"), /* clear-to-eol key -> KEY_EOS */ |
| 733 | DATA("cl", "kclr"), /* clear key -> KEY_CLEAR */ |
| 734 | DATA("ct", "tbc"), /* clear all tabs -> KEY_CATAB */ |
| 735 | DATA("dc", "kdch1"), /* delete char -> KEY_DC */ |
| 736 | DATA("dl", "kdl1"), /* delete line -> KEY_DL */ |
| 737 | DATA("do", "kcud1"), /* down key -> KEY_DOWN */ |
| 738 | DATA("ei", "krmir"), /* exit insert key -> KEY_EIC */ |
| 739 | DATA("ho", "khome"), /* home key -> KEY_HOME */ |
| 740 | DATA("ic", "kich1"), /* insert char key -> KEY_IC */ |
| 741 | DATA("im", "kIC"), /* insert-mode key -> KEY_SIC */ |
| 742 | DATA("le", "kcub1"), /* le key -> KEY_LEFT */ |
| 743 | DATA("nd", "kcuf1"), /* nd key -> KEY_RIGHT */ |
| 744 | DATA("nl", "kent"), /* new line key -> KEY_ENTER */ |
| 745 | DATA("st", "khts"), /* set-tab key -> KEY_STAB */ |
| 746 | DATA("ta", ""), |
| 747 | DATA("up", "kcuu1"), /* up-arrow key -> KEY_UP */ |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 748 | }; |
| 749 | |
| 750 | /* |
| 751 | * This routine fills in string caps that either had defaults under |
| 752 | * termcap or can be manufactured from obsolete termcap capabilities. |
| 753 | * It was lifted from Ross Ridge's mytinfo package. |
| 754 | */ |
| 755 | |
| 756 | static const char C_CR[] = "\r"; |
| 757 | static const char C_LF[] = "\n"; |
| 758 | static const char C_BS[] = "\b"; |
| 759 | static const char C_HT[] = "\t"; |
| 760 | |
| 761 | /* |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 762 | * This bit of legerdemain turns all the terminfo variable names into |
| 763 | * references to locations in the arrays Booleans, Numbers, and Strings --- |
| 764 | * precisely what's needed. |
| 765 | */ |
| 766 | |
| 767 | #undef CUR |
| 768 | #define CUR tp-> |
| 769 | |
| 770 | static void |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 771 | postprocess_termcap(TERMTYPE2 *tp, bool has_base) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 772 | { |
| 773 | char buf[MAX_LINE * 2 + 2]; |
| 774 | string_desc result; |
| 775 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 776 | TR(TRACE_DATABASE, |
| 777 | (T_CALLED("postprocess_termcap(tp=%p, has_base=%d)"), |
| 778 | (void *) tp, has_base)); |
| 779 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 780 | /* |
| 781 | * TERMCAP DEFAULTS AND OBSOLETE-CAPABILITY TRANSLATIONS |
| 782 | * |
| 783 | * This first part of the code is the functional inverse of the |
| 784 | * fragment in capdefaults.c. |
| 785 | * ---------------------------------------------------------------------- |
| 786 | */ |
| 787 | |
| 788 | /* if there was a tc entry, assume we picked up defaults via that */ |
| 789 | if (!has_base) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 790 | if (WANTED(init_3string) && PRESENT(termcap_init2)) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 791 | init_3string = _nc_save_str(termcap_init2); |
| 792 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 793 | if (WANTED(reset_2string) && PRESENT(termcap_reset)) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 794 | reset_2string = _nc_save_str(termcap_reset); |
| 795 | |
| 796 | if (WANTED(carriage_return)) { |
| 797 | if (carriage_return_delay > 0) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 798 | _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) |
| 799 | "%s$<%d>", C_CR, carriage_return_delay); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 800 | carriage_return = _nc_save_str(buf); |
| 801 | } else |
| 802 | carriage_return = _nc_save_str(C_CR); |
| 803 | } |
| 804 | if (WANTED(cursor_left)) { |
| 805 | if (backspace_delay > 0) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 806 | _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) |
| 807 | "%s$<%d>", C_BS, backspace_delay); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 808 | cursor_left = _nc_save_str(buf); |
| 809 | } else if (backspaces_with_bs == 1) |
| 810 | cursor_left = _nc_save_str(C_BS); |
| 811 | else if (PRESENT(backspace_if_not_bs)) |
| 812 | cursor_left = backspace_if_not_bs; |
| 813 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 814 | /* vi doesn't use "do", but it does seem to use nl (or '\n') instead */ |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 815 | if (WANTED(cursor_down)) { |
| 816 | if (PRESENT(linefeed_if_not_lf)) |
| 817 | cursor_down = linefeed_if_not_lf; |
| 818 | else if (linefeed_is_newline != 1) { |
| 819 | if (new_line_delay > 0) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 820 | _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) |
| 821 | "%s$<%d>", C_LF, new_line_delay); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 822 | cursor_down = _nc_save_str(buf); |
| 823 | } else |
| 824 | cursor_down = _nc_save_str(C_LF); |
| 825 | } |
| 826 | } |
| 827 | if (WANTED(scroll_forward) && crt_no_scrolling != 1) { |
| 828 | if (PRESENT(linefeed_if_not_lf)) |
| 829 | cursor_down = linefeed_if_not_lf; |
| 830 | else if (linefeed_is_newline != 1) { |
| 831 | if (new_line_delay > 0) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 832 | _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) |
| 833 | "%s$<%d>", C_LF, new_line_delay); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 834 | scroll_forward = _nc_save_str(buf); |
| 835 | } else |
| 836 | scroll_forward = _nc_save_str(C_LF); |
| 837 | } |
| 838 | } |
| 839 | if (WANTED(newline)) { |
| 840 | if (linefeed_is_newline == 1) { |
| 841 | if (new_line_delay > 0) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 842 | _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) |
| 843 | "%s$<%d>", C_LF, new_line_delay); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 844 | newline = _nc_save_str(buf); |
| 845 | } else |
| 846 | newline = _nc_save_str(C_LF); |
| 847 | } else if (PRESENT(carriage_return) && PRESENT(scroll_forward)) { |
| 848 | _nc_str_init(&result, buf, sizeof(buf)); |
| 849 | if (_nc_safe_strcat(&result, carriage_return) |
| 850 | && _nc_safe_strcat(&result, scroll_forward)) |
| 851 | newline = _nc_save_str(buf); |
| 852 | } else if (PRESENT(carriage_return) && PRESENT(cursor_down)) { |
| 853 | _nc_str_init(&result, buf, sizeof(buf)); |
| 854 | if (_nc_safe_strcat(&result, carriage_return) |
| 855 | && _nc_safe_strcat(&result, cursor_down)) |
| 856 | newline = _nc_save_str(buf); |
| 857 | } |
| 858 | } |
| 859 | } |
| 860 | |
| 861 | /* |
| 862 | * Inverse of capdefaults.c code ends here. |
| 863 | * ---------------------------------------------------------------------- |
| 864 | * |
| 865 | * TERMCAP-TO TERMINFO MAPPINGS FOR SOURCE TRANSLATION |
| 866 | * |
| 867 | * These translations will *not* be inverted by tgetent(). |
| 868 | */ |
| 869 | |
| 870 | if (!has_base) { |
| 871 | /* |
| 872 | * We wait until now to decide if we've got a working cr because even |
| 873 | * one that doesn't work can be used for newline. Unfortunately the |
| 874 | * space allocated for it is wasted. |
| 875 | */ |
| 876 | if (return_does_clr_eol == 1 || no_correctly_working_cr == 1) |
| 877 | carriage_return = ABSENT_STRING; |
| 878 | |
| 879 | /* |
| 880 | * Supposedly most termcap entries have ta now and '\t' is no longer a |
| 881 | * default, but it doesn't seem to be true... |
| 882 | */ |
| 883 | if (WANTED(tab)) { |
| 884 | if (horizontal_tab_delay > 0) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 885 | _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) |
| 886 | "%s$<%d>", C_HT, horizontal_tab_delay); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 887 | tab = _nc_save_str(buf); |
| 888 | } else |
| 889 | tab = _nc_save_str(C_HT); |
| 890 | } |
| 891 | if (init_tabs == ABSENT_NUMERIC && has_hardware_tabs == TRUE) |
| 892 | init_tabs = 8; |
| 893 | |
| 894 | /* |
| 895 | * Assume we can beep with ^G unless we're given bl@. |
| 896 | */ |
| 897 | if (WANTED(bell)) |
| 898 | bell = _nc_save_str("\007"); |
| 899 | } |
| 900 | |
| 901 | /* |
| 902 | * Translate the old termcap :pt: capability to it#8 + ht=\t |
| 903 | */ |
| 904 | if (has_hardware_tabs == TRUE) { |
| 905 | if (init_tabs != 8 && init_tabs != ABSENT_NUMERIC) |
| 906 | _nc_warning("hardware tabs with a width other than 8: %d", init_tabs); |
| 907 | else { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 908 | if (PRESENT(tab) && _nc_capcmp(tab, C_HT)) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 909 | _nc_warning("hardware tabs with a non-^I tab string %s", |
| 910 | _nc_visbuf(tab)); |
| 911 | else { |
| 912 | if (WANTED(tab)) |
| 913 | tab = _nc_save_str(C_HT); |
| 914 | init_tabs = 8; |
| 915 | } |
| 916 | } |
| 917 | } |
| 918 | /* |
| 919 | * Now translate the ko capability, if there is one. This |
| 920 | * isn't from mytinfo... |
| 921 | */ |
| 922 | if (PRESENT(other_non_function_keys)) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 923 | char *base; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 924 | char *bp, *cp, *dp; |
| 925 | struct name_table_entry const *from_ptr; |
| 926 | struct name_table_entry const *to_ptr; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 927 | char buf2[MAX_TERMINFO_LENGTH]; |
| 928 | bool foundim; |
| 929 | |
| 930 | /* we're going to use this for a special case later */ |
| 931 | dp = strchr(other_non_function_keys, 'i'); |
| 932 | foundim = (dp != 0) && (dp[1] == 'm'); |
| 933 | |
| 934 | /* look at each comma-separated capability in the ko string... */ |
| 935 | for (base = other_non_function_keys; |
| 936 | (cp = strchr(base, ',')) != 0; |
| 937 | base = cp + 1) { |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 938 | size_t len = (unsigned) (cp - base); |
| 939 | size_t n; |
| 940 | assoc const *ap = 0; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 941 | |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 942 | for (n = 0; n < SIZEOF(ko_xlate); ++n) { |
| 943 | if (len == strlen(ko_xlate[n].from) |
| 944 | && strncmp(ko_xlate[n].from, base, len) == 0) { |
| 945 | ap = ko_xlate + n; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 946 | break; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 947 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 948 | } |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 949 | if (ap == 0) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 950 | _nc_warning("unknown capability `%.*s' in ko string", |
| 951 | (int) len, base); |
| 952 | continue; |
Steve Kondik | ae271bc | 2015-11-15 02:50:53 +0100 | [diff] [blame] | 953 | } else if (ap->to[0] == '\0') /* ignore it */ |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 954 | continue; |
| 955 | |
| 956 | /* now we know we found a match in ko_table, so... */ |
| 957 | |
| 958 | from_ptr = _nc_find_entry(ap->from, _nc_get_hash_table(TRUE)); |
| 959 | to_ptr = _nc_find_entry(ap->to, _nc_get_hash_table(FALSE)); |
| 960 | |
| 961 | if (!from_ptr || !to_ptr) /* should never happen! */ |
| 962 | _nc_err_abort("ko translation table is invalid, I give up"); |
| 963 | |
| 964 | if (WANTED(tp->Strings[from_ptr->nte_index])) { |
| 965 | _nc_warning("no value for ko capability %s", ap->from); |
| 966 | continue; |
| 967 | } |
| 968 | |
| 969 | if (tp->Strings[to_ptr->nte_index]) { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 970 | const char *s = tp->Strings[from_ptr->nte_index]; |
| 971 | const char *t = tp->Strings[to_ptr->nte_index]; |
| 972 | /* There's no point in warning about it if it is the same |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 973 | * string; that's just an inefficiency. |
| 974 | */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 975 | if (VALID_STRING(s) && VALID_STRING(t) && strcmp(s, t) != 0) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 976 | _nc_warning("%s (%s) already has an explicit value %s, ignoring ko", |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 977 | ap->to, ap->from, t); |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 978 | continue; |
| 979 | } |
| 980 | |
| 981 | /* |
| 982 | * The magic moment -- copy the mapped key string over, |
| 983 | * stripping out padding. |
| 984 | */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 985 | bp = tp->Strings[from_ptr->nte_index]; |
| 986 | if (VALID_STRING(bp)) { |
| 987 | for (dp = buf2; *bp; bp++) { |
| 988 | if (bp[0] == '$' && bp[1] == '<') { |
| 989 | while (*bp && *bp != '>') { |
| 990 | ++bp; |
| 991 | } |
| 992 | } else |
| 993 | *dp++ = *bp; |
| 994 | } |
| 995 | *dp = '\0'; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 996 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 997 | tp->Strings[to_ptr->nte_index] = _nc_save_str(buf2); |
| 998 | } else { |
| 999 | tp->Strings[to_ptr->nte_index] = bp; |
| 1000 | } |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1001 | } |
| 1002 | |
| 1003 | /* |
| 1004 | * Note: ko=im and ko=ic both want to grab the `Insert' |
| 1005 | * keycap. There's a kich1 but no ksmir, so the ic capability |
| 1006 | * got mapped to kich1 and im to kIC to avoid a collision. |
| 1007 | * If the description has im but not ic, hack kIC back to kich1. |
| 1008 | */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1009 | if (foundim && WANTED(key_ic) && PRESENT(key_sic)) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1010 | key_ic = key_sic; |
| 1011 | key_sic = ABSENT_STRING; |
| 1012 | } |
| 1013 | } |
| 1014 | |
| 1015 | if (!has_base) { |
| 1016 | if (!hard_copy) { |
| 1017 | if (WANTED(key_backspace)) |
| 1018 | key_backspace = _nc_save_str(C_BS); |
| 1019 | if (WANTED(key_left)) |
| 1020 | key_left = _nc_save_str(C_BS); |
| 1021 | if (WANTED(key_down)) |
| 1022 | key_down = _nc_save_str(C_LF); |
| 1023 | } |
| 1024 | } |
| 1025 | |
| 1026 | /* |
| 1027 | * Translate XENIX forms characters. |
| 1028 | */ |
| 1029 | if (PRESENT(acs_ulcorner) || |
| 1030 | PRESENT(acs_llcorner) || |
| 1031 | PRESENT(acs_urcorner) || |
| 1032 | PRESENT(acs_lrcorner) || |
| 1033 | PRESENT(acs_ltee) || |
| 1034 | PRESENT(acs_rtee) || |
| 1035 | PRESENT(acs_btee) || |
| 1036 | PRESENT(acs_ttee) || |
| 1037 | PRESENT(acs_hline) || |
| 1038 | PRESENT(acs_vline) || |
| 1039 | PRESENT(acs_plus)) { |
| 1040 | char buf2[MAX_TERMCAP_LENGTH]; |
| 1041 | |
| 1042 | _nc_str_init(&result, buf2, sizeof(buf2)); |
| 1043 | _nc_safe_strcat(&result, acs_chars); |
| 1044 | |
| 1045 | append_acs(&result, 'j', acs_lrcorner); |
| 1046 | append_acs(&result, 'k', acs_urcorner); |
| 1047 | append_acs(&result, 'l', acs_ulcorner); |
| 1048 | append_acs(&result, 'm', acs_llcorner); |
| 1049 | append_acs(&result, 'n', acs_plus); |
| 1050 | append_acs(&result, 'q', acs_hline); |
| 1051 | append_acs(&result, 't', acs_ltee); |
| 1052 | append_acs(&result, 'u', acs_rtee); |
| 1053 | append_acs(&result, 'v', acs_btee); |
| 1054 | append_acs(&result, 'w', acs_ttee); |
| 1055 | append_acs(&result, 'x', acs_vline); |
| 1056 | |
| 1057 | if (buf2[0]) { |
| 1058 | acs_chars = _nc_save_str(buf2); |
| 1059 | _nc_warning("acsc string synthesized from XENIX capabilities"); |
| 1060 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1061 | } else if (acs_chars == ABSENT_STRING |
| 1062 | && PRESENT(enter_alt_charset_mode) |
| 1063 | && PRESENT(exit_alt_charset_mode)) { |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1064 | acs_chars = _nc_save_str(VT_ACSC); |
| 1065 | } |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1066 | returnVoidDB; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1067 | } |
| 1068 | |
| 1069 | static void |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1070 | postprocess_terminfo(TERMTYPE2 *tp) |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1071 | { |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1072 | TR(TRACE_DATABASE, |
| 1073 | (T_CALLED("postprocess_terminfo(tp=%p)"), |
| 1074 | (void *) tp)); |
| 1075 | |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1076 | /* |
| 1077 | * TERMINFO-TO-TERMINFO MAPPINGS FOR SOURCE TRANSLATION |
| 1078 | * ---------------------------------------------------------------------- |
| 1079 | */ |
| 1080 | |
| 1081 | /* |
| 1082 | * Translate AIX forms characters. |
| 1083 | */ |
| 1084 | if (PRESENT(box_chars_1)) { |
| 1085 | char buf2[MAX_TERMCAP_LENGTH]; |
| 1086 | string_desc result; |
| 1087 | |
| 1088 | _nc_str_init(&result, buf2, sizeof(buf2)); |
| 1089 | _nc_safe_strcat(&result, acs_chars); |
| 1090 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1091 | append_acs0(&result, 'l', box_chars_1, 0); /* ACS_ULCORNER */ |
| 1092 | append_acs0(&result, 'q', box_chars_1, 1); /* ACS_HLINE */ |
| 1093 | append_acs0(&result, 'k', box_chars_1, 2); /* ACS_URCORNER */ |
| 1094 | append_acs0(&result, 'x', box_chars_1, 3); /* ACS_VLINE */ |
| 1095 | append_acs0(&result, 'j', box_chars_1, 4); /* ACS_LRCORNER */ |
| 1096 | append_acs0(&result, 'm', box_chars_1, 5); /* ACS_LLCORNER */ |
| 1097 | append_acs0(&result, 'w', box_chars_1, 6); /* ACS_TTEE */ |
| 1098 | append_acs0(&result, 'u', box_chars_1, 7); /* ACS_RTEE */ |
| 1099 | append_acs0(&result, 'v', box_chars_1, 8); /* ACS_BTEE */ |
| 1100 | append_acs0(&result, 't', box_chars_1, 9); /* ACS_LTEE */ |
| 1101 | append_acs0(&result, 'n', box_chars_1, 10); /* ACS_PLUS */ |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1102 | |
| 1103 | if (buf2[0]) { |
| 1104 | acs_chars = _nc_save_str(buf2); |
| 1105 | _nc_warning("acsc string synthesized from AIX capabilities"); |
| 1106 | box_chars_1 = ABSENT_STRING; |
| 1107 | } |
| 1108 | } |
| 1109 | /* |
| 1110 | * ---------------------------------------------------------------------- |
| 1111 | */ |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 1112 | returnVoidDB; |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1113 | } |
| 1114 | |
| 1115 | /* |
| 1116 | * Do a linear search through the terminfo tables to find a given full-name. |
| 1117 | * We don't expect to do this often, so there's no hashing function. |
| 1118 | * |
| 1119 | * In effect, this scans through the 3 lists of full-names, and looks them |
| 1120 | * up in _nc_info_table, which is organized so that the nte_index fields are |
| 1121 | * sorted, but the nte_type fields are not necessarily grouped together. |
| 1122 | */ |
| 1123 | static struct name_table_entry const * |
| 1124 | lookup_fullname(const char *find) |
| 1125 | { |
| 1126 | int state = -1; |
| 1127 | |
| 1128 | for (;;) { |
| 1129 | int count = 0; |
| 1130 | NCURSES_CONST char *const *names; |
| 1131 | |
| 1132 | switch (++state) { |
| 1133 | case BOOLEAN: |
| 1134 | names = boolfnames; |
| 1135 | break; |
| 1136 | case STRING: |
| 1137 | names = strfnames; |
| 1138 | break; |
| 1139 | case NUMBER: |
| 1140 | names = numfnames; |
| 1141 | break; |
| 1142 | default: |
| 1143 | return NOTFOUND; |
| 1144 | } |
| 1145 | |
| 1146 | for (count = 0; names[count] != 0; count++) { |
| 1147 | if (!strcmp(names[count], find)) { |
| 1148 | struct name_table_entry const *entry_ptr = _nc_get_table(FALSE); |
| 1149 | while (entry_ptr->nte_type != state |
| 1150 | || entry_ptr->nte_index != count) |
| 1151 | entry_ptr++; |
| 1152 | return entry_ptr; |
| 1153 | } |
| 1154 | } |
| 1155 | } |
| 1156 | } |
| 1157 | |
| 1158 | /* parse_entry.c ends here */ |