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