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