xref: /freebsd/contrib/ncurses/ncurses/tinfo/lib_tgoto.c (revision 21817992b3314c908ab50f0bb88d2ee750b9c4ac)
1 /****************************************************************************
2  * Copyright 2018-2020,2023 Thomas E. Dickey                                *
3  * Copyright 2000-2008,2012 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                                                *
32  ****************************************************************************/
33 
34 #include <curses.priv.h>
35 
36 #include <ctype.h>
37 #include <termcap.h>
38 
39 MODULE_ID("$Id: lib_tgoto.c,v 1.23 2023/04/16 17:19:40 tom Exp $")
40 
41 #if !PURE_TERMINFO
42 static bool
is_termcap(const char * string)43 is_termcap(const char *string)
44 {
45     bool result = TRUE;
46 
47     if (string == 0 || *string == '\0') {
48 	result = FALSE;		/* tparm() handles empty strings */
49     } else {
50 	while ((*string != '\0') && result) {
51 	    if (*string == '%') {
52 		switch (*++string) {
53 		case 'p':
54 		    result = FALSE;
55 		    break;
56 		case '\0':
57 		    string--;
58 		    break;
59 		}
60 	    } else if (string[0] == '$' && string[1] == '<') {
61 		result = FALSE;
62 	    }
63 	    string++;
64 	}
65     }
66     return result;
67 }
68 
69 static char *
tgoto_internal(const char * string,int x,int y)70 tgoto_internal(const char *string, int x, int y)
71 {
72     static char *result;
73     static size_t length;
74 
75     int swap_arg;
76     int param[3];
77     size_t used = 0;
78     size_t need = 10;
79     int *value = param;
80     bool need_BC = FALSE;
81 
82     if (BC)
83 	need += strlen(BC);
84 
85     param[0] = y;
86     param[1] = x;
87     param[2] = 0;
88 
89     while (*string != 0) {
90 	if ((used + need) > length) {
91 	    length += (used + need);
92 	    if ((result = typeRealloc(char, length, result)) == 0) {
93 		length = 0;
94 		break;
95 	    }
96 	}
97 	if (*string == '%') {
98 	    const char *fmt = 0;
99 
100 	    switch (*++string) {
101 	    case '\0':
102 		string--;
103 		break;
104 	    case 'd':
105 		fmt = "%d";
106 		break;
107 	    case '2':
108 		fmt = "%02d";
109 		*value %= 100;
110 		break;
111 	    case '3':
112 		fmt = "%03d";
113 		*value %= 1000;
114 		break;
115 	    case '+':
116 		*value += UChar(*++string);
117 		/* FALLTHRU */
118 	    case '.':
119 		/*
120 		 * Guard against tputs() seeing a truncated string.  The
121 		 * termcap documentation refers to a similar fixup for \n
122 		 * and \r, but I don't see that it could work -TD
123 		 */
124 		if (*value == 0) {
125 		    if (BC != 0) {
126 			*value += 1;
127 			need_BC = TRUE;
128 		    } else {
129 			/* tputs will pretend this is \0, which will almost
130 			 * always work since ANSI-compatible terminals ignore
131 			 * the character.  ECMA-48 does not document a C1
132 			 * control for this value.  A few (obsolete) terminals
133 			 * can use this value in special cases, such as cursor
134 			 * addressing using single-byte coordinates.
135 			 */
136 			*value = 0200;
137 		    }
138 		}
139 		result[used++] = (char) *value++;
140 		break;
141 	    case '%':
142 		result[used++] = *string;
143 		break;
144 	    case 'r':
145 		swap_arg = param[0];
146 		param[0] = param[1];
147 		param[1] = swap_arg;
148 		break;
149 	    case 'i':
150 		param[0] += 1;
151 		param[1] += 1;
152 		break;
153 	    case '>':
154 		if (*value > string[1])
155 		    *value += string[2];
156 		string += 2;
157 		break;
158 	    case 'n':		/* Datamedia 2500 */
159 		param[0] ^= 0140;
160 		param[1] ^= 0140;
161 		break;
162 	    case 'B':		/* BCD */
163 		*value = 16 * (*value / 10) + (*value % 10);
164 		break;
165 	    case 'D':		/* Reverse coding (Delta Data) */
166 		*value -= 2 * (*value % 16);
167 		break;
168 	    }
169 	    if (fmt != 0) {
170 		_nc_SPRINTF(result + used, _nc_SLIMIT(length - used)
171 			    fmt, *value++);
172 		used += strlen(result + used);
173 		fmt = 0;
174 	    }
175 	    if (value - param > 2) {
176 		value = param + 2;
177 		*value = 0;
178 	    }
179 	} else {
180 	    result[used++] = *string;
181 	}
182 	string++;
183     }
184     if (result != 0) {
185 	if (need_BC) {
186 	    _nc_STRCPY(result + used, BC, length - used);
187 	    used += strlen(BC);
188 	}
189 	result[used] = '\0';
190     }
191     return result;
192 }
193 #endif
194 
195 /*
196  * Retained solely for upward compatibility.  Note the intentional reversing of
197  * the last two arguments when invoking tparm().
198  */
199 NCURSES_EXPORT(char *)
tgoto(const char * string,int x,int y)200 tgoto(const char *string, int x, int y)
201 {
202     char *result;
203 
204     T((T_CALLED("tgoto(%s, %d, %d)"), _nc_visbuf(string), x, y));
205 #if !PURE_TERMINFO
206     if (is_termcap(string))
207 	result = tgoto_internal(string, x, y);
208     else
209 #endif
210     if ((result = TIPARM_2(string, y, x)) == NULL) {
211 	/*
212 	 * Because termcap did not provide a more general solution such as
213 	 * tparm(), it was necessary to handle single-parameter capabilities
214 	 * using tgoto().  The internal _nc_tiparm() function returns a NULL
215 	 * for that case; retry for the single-parameter case.
216 	 */
217 	if ((result = TIPARM_1(string, y)) == NULL) {
218 	    result = TIPARM_0(string);
219 	}
220     }
221     returnPtr(result);
222 }
223