1/**************************************************************************** 2 * Copyright 2020-2021,2023 Thomas E. Dickey * 3 * Copyright 1998-2016,2017 Free Software Foundation, Inc. * 4 * * 5 * Permission is hereby granted, free of charge, to any person obtaining a * 6 * copy of this software and associated documentation files (the * 7 * "Software"), to deal in the Software without restriction, including * 8 * without limitation the rights to use, copy, modify, merge, publish, * 9 * distribute, distribute with modifications, sublicense, and/or sell * 10 * copies of the Software, and to permit persons to whom the Software is * 11 * furnished to do so, subject to the following conditions: * 12 * * 13 * The above copyright notice and this permission notice shall be included * 14 * in all copies or substantial portions of the Software. * 15 * * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 23 * * 24 * Except as contained in this notice, the name(s) of the above copyright * 25 * holders shall not be used in advertising or otherwise to promote the * 26 * sale, use or other dealings in this Software without prior written * 27 * authorization. * 28 ****************************************************************************/ 29 30/**************************************************************************** 31 * Author: Thomas E. Dickey 1998 * 32 ****************************************************************************/ 33 34#include <curses.priv.h> 35 36#include <ctype.h> 37#include <tic.h> 38 39MODULE_ID("$Id: comp_expand.c,v 1.35 2023/04/28 20:59:06 tom Exp $") 40 41#if 0 42#define DEBUG_THIS(p) DEBUG(9, p) 43#else 44#define DEBUG_THIS(p) /* nothing */ 45#endif 46 47static int 48trailing_spaces(const char *src) 49{ 50 while (*src == ' ') 51 src++; 52 return *src == 0; 53} 54 55/* this deals with differences over whether 0x7f and 0x80..0x9f are controls */ 56#define REALPRINT(s) (UChar(*(s)) < 127 && isprint(UChar(*(s)))) 57 58#define P_LIMIT(p) (length - (size_t)(p)) 59 60NCURSES_EXPORT(char *) 61_nc_tic_expand(const char *srcp, bool tic_format, int numbers) 62{ 63 static char *buffer; 64 static size_t length; 65 66 int bufp; 67 const char *str = VALID_STRING(srcp) ? srcp : "\0\0"; 68 size_t need = (2 + strlen(str)) * 4; 69 int ch; 70 int octals = 0; 71 struct { 72 int ch; 73 int offset; 74 } fixups[MAX_TC_FIXUPS]; 75 76 if (srcp == 0) { 77#if NO_LEAKS 78 if (buffer != 0) { 79 FreeAndNull(buffer); 80 length = 0; 81 } 82#endif 83 return 0; 84 } 85 if (buffer == 0 || need > length) { 86 if ((buffer = typeRealloc(char, length = need, buffer)) == 0) 87 return 0; 88 } 89 90 DEBUG_THIS(("_nc_tic_expand %s:%s:%s", 91 tic_format ? "ti" : "tc", 92 numbers ? "#" : "", 93 _nc_visbuf(srcp))); 94 bufp = 0; 95 while ((ch = UChar(*str)) != 0) { 96 if (ch == '%' && REALPRINT(str + 1)) { 97 buffer[bufp++] = *str++; 98 /* 99 * Though the character literals are more compact, most 100 * terminal descriptions use numbers and are not easy 101 * to read in character-literal form. 102 */ 103 switch (numbers) { 104 case -1: 105 if (str[0] == S_QUOTE 106 && str[1] != '\\' 107 && REALPRINT(str + 1) 108 && str[2] == S_QUOTE) { 109 _nc_SPRINTF(buffer + bufp, _nc_SLIMIT(P_LIMIT(bufp)) 110 "{%d}", str[1]); 111 bufp += (int) strlen(buffer + bufp); 112 str += 2; 113 } else { 114 buffer[bufp++] = *str; 115 } 116 break; 117 /* 118 * If we have a "%{number}", try to translate it into 119 * a "%'char'" form, since that will run a little faster 120 * when we're interpreting it. Also, having one form 121 * for the constant makes it simpler to compare terminal 122 * descriptions. 123 */ 124 case 1: 125 if (str[0] == L_BRACE 126 && isdigit(UChar(str[1]))) { 127 char *dst = 0; 128 long value = strtol(str + 1, &dst, 0); 129 if (dst != 0 130 && *dst == R_BRACE 131 && value < 127 132 && isprint((int) value)) { 133 ch = (int) value; 134 buffer[bufp++] = S_QUOTE; 135 if (ch == '\\' 136 || ch == S_QUOTE) 137 buffer[bufp++] = '\\'; 138 buffer[bufp++] = (char) ch; 139 buffer[bufp++] = S_QUOTE; 140 str = dst; 141 } else { 142 buffer[bufp++] = *str; 143 } 144 } else { 145 buffer[bufp++] = *str; 146 } 147 break; 148 default: 149 if (*str == ',') /* minitel1 uses this */ 150 buffer[bufp++] = '\\'; 151 buffer[bufp++] = *str; 152 break; 153 } 154 } else if (ch == 128) { 155 buffer[bufp++] = '\\'; 156 buffer[bufp++] = '0'; 157 } else if (ch == '\033') { 158 buffer[bufp++] = '\\'; 159 buffer[bufp++] = 'E'; 160 } else if (ch == '\\' && tic_format && (str == srcp || str[-1] != '^')) { 161 buffer[bufp++] = '\\'; 162 buffer[bufp++] = '\\'; 163 } else if (ch == ' ' && tic_format && (str == srcp || 164 trailing_spaces(str))) { 165 buffer[bufp++] = '\\'; 166 buffer[bufp++] = 's'; 167 } else if ((ch == ',' || ch == '^') && tic_format) { 168 buffer[bufp++] = '\\'; 169 buffer[bufp++] = (char) ch; 170 } else if (REALPRINT(str) 171 && (ch != ',' 172 && !(ch == ':' && !tic_format) 173 && !(ch == '!' && !tic_format) 174 && ch != '^')) 175 buffer[bufp++] = (char) ch; 176 else if (ch == '\r') { 177 buffer[bufp++] = '\\'; 178 buffer[bufp++] = 'r'; 179 } else if (ch == '\n') { 180 buffer[bufp++] = '\\'; 181 buffer[bufp++] = 'n'; 182 } 183#define UnCtl(c) ((c) + '@') 184 else if (UChar(ch) < 32 185 && isdigit(UChar(str[1]))) { 186 _nc_SPRINTF(&buffer[bufp], _nc_SLIMIT(P_LIMIT(bufp)) 187 "^%c", UnCtl(ch)); 188 bufp += 2; 189 } else { 190 _nc_SPRINTF(&buffer[bufp], _nc_SLIMIT(P_LIMIT(bufp)) 191 "\\%03o", ch); 192 if ((octals < MAX_TC_FIXUPS) && 193 ((tic_format && (ch == 127)) || ch < 32)) { 194 fixups[octals].ch = UChar(ch); 195 fixups[octals].offset = bufp; 196 ++octals; 197 } 198 bufp += 4; 199 } 200 201 str++; 202 } 203 204 buffer[bufp] = '\0'; 205 206 /* 207 * If most of a short string is ASCII control characters, reformat the 208 * string to show those in up-arrow format. For longer strings, it is 209 * more likely that the characters are just binary coding. 210 * 211 * If we're formatting termcap, just use the shorter format (up-arrows). 212 */ 213 if (octals != 0 && (!tic_format || (bufp - (4 * octals)) < MIN_TC_FIXUPS)) { 214 while (--octals >= 0) { 215 char *p = buffer + fixups[octals].offset; 216 *p++ = '^'; 217 *p++ = (char) ((fixups[octals].ch == 127) 218 ? '?' 219 : (fixups[octals].ch + (int) '@')); 220 while ((p[0] = p[2]) != 0) { 221 ++p; 222 } 223 } 224 } 225 DEBUG_THIS(("... %s", _nc_visbuf(buffer))); 226 return (buffer); 227} 228