1/****************************************************************************
2 * Copyright (c) 1999-2002,2003 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 <dickey@clark.net> 1999                        *
31 ****************************************************************************/
32
33/*
34 * align_ttype.c --  functions for TERMTYPE
35 *
36 *	_nc_align_termtype()
37 *	_nc_copy_termtype()
38 *
39 */
40
41#include <curses.priv.h>
42
43#include <tic.h>
44#include <term_entry.h>
45
46MODULE_ID("$Id: alloc_ttype.c,v 1.14 2003/05/24 21:10:28 tom Exp $")
47
48#if NCURSES_XNAMES
49/*
50 * Merge the a/b lists into dst.  Both a/b are sorted (see _nc_extend_names()),
51 * so we do not have to worry about order dependencies.
52 */
53static int
54merge_names(char **dst, char **a, int na, char **b, int nb)
55{
56    int n = 0;
57    while (na > 0 && nb > 0) {
58	int cmp = strcmp(*a, *b);
59	if (cmp < 0) {
60	    dst[n++] = *a++;
61	    na--;
62	} else if (cmp > 0) {
63	    dst[n++] = *b++;
64	    nb--;
65	} else if (cmp == 0) {
66	    dst[n++] = *a;
67	    a++, b++;
68	    na--, nb--;
69	}
70    }
71    while (na-- > 0) {
72	dst[n++] = *a++;
73    }
74    while (nb-- > 0) {
75	dst[n++] = *b++;
76    }
77    DEBUG(4, ("merge_names -> %d", n));
78    return n;
79}
80
81static bool
82find_name(char **table, int length, char *name)
83{
84    while (length-- > 0) {
85	if (!strcmp(*table++, name)) {
86	    DEBUG(4, ("found name '%s'", name));
87	    return TRUE;
88	}
89    }
90    DEBUG(4, ("did not find name '%s'", name));
91    return FALSE;
92}
93
94static void
95realign_data(TERMTYPE * to, char **ext_Names,
96	     int ext_Booleans,
97	     int ext_Numbers,
98	     int ext_Strings)
99{
100    int n, m, base;
101    int limit = (to->ext_Booleans + to->ext_Numbers + to->ext_Strings);
102
103    if (to->ext_Booleans != ext_Booleans) {
104	to->num_Booleans += (ext_Booleans - to->ext_Booleans);
105	to->Booleans = typeRealloc(char, to->num_Booleans, to->Booleans);
106	for (n = to->ext_Booleans - 1,
107	     m = ext_Booleans - 1,
108	     base = to->num_Booleans - (m + 1); m >= 0; m--) {
109	    if (find_name(to->ext_Names, limit, ext_Names[m])) {
110		to->Booleans[base + m] = to->Booleans[base + n--];
111	    } else {
112		to->Booleans[base + m] = FALSE;
113	    }
114	}
115	to->ext_Booleans = ext_Booleans;
116    }
117    if (to->ext_Numbers != ext_Numbers) {
118	to->num_Numbers += (ext_Numbers - to->ext_Numbers);
119	to->Numbers = typeRealloc(short, to->num_Numbers, to->Numbers);
120	for (n = to->ext_Numbers - 1,
121	     m = ext_Numbers - 1,
122	     base = to->num_Numbers - (m + 1); m >= 0; m--) {
123	    if (find_name(to->ext_Names, limit, ext_Names[m + ext_Booleans])) {
124		to->Numbers[base + m] = to->Numbers[base + n--];
125	    } else {
126		to->Numbers[base + m] = ABSENT_NUMERIC;
127	    }
128	}
129	to->ext_Numbers = ext_Numbers;
130    }
131    if (to->ext_Strings != ext_Strings) {
132	to->num_Strings += (ext_Strings - to->ext_Strings);
133	to->Strings = typeRealloc(char *, to->num_Strings, to->Strings);
134	for (n = to->ext_Strings - 1,
135	     m = ext_Strings - 1,
136	     base = to->num_Strings - (m + 1); m >= 0; m--) {
137	    if (find_name(to->ext_Names, limit, ext_Names[m + ext_Booleans + ext_Numbers])) {
138		to->Strings[base + m] = to->Strings[base + n--];
139	    } else {
140		to->Strings[base + m] = ABSENT_STRING;
141	    }
142	}
143	to->ext_Strings = ext_Strings;
144    }
145}
146
147/*
148 * Returns the first index in ext_Names[] for the given token-type
149 */
150static int
151_nc_first_ext_name(TERMTYPE * tp, int token_type)
152{
153    int first;
154
155    switch (token_type) {
156    case BOOLEAN:
157	first = 0;
158	break;
159    case NUMBER:
160	first = tp->ext_Booleans;
161	break;
162    case STRING:
163	first = tp->ext_Booleans + tp->ext_Numbers;
164	break;
165    default:
166	first = 0;
167	break;
168    }
169    return first;
170}
171
172/*
173 * Returns the last index in ext_Names[] for the given token-type
174 */
175static int
176_nc_last_ext_name(TERMTYPE * tp, int token_type)
177{
178    int last;
179
180    switch (token_type) {
181    case BOOLEAN:
182	last = tp->ext_Booleans;
183	break;
184    case NUMBER:
185	last = tp->ext_Booleans + tp->ext_Numbers;
186	break;
187    default:
188    case STRING:
189	last = NUM_EXT_NAMES(tp);
190	break;
191    }
192    return last;
193}
194
195/*
196 * Lookup an entry from extended-names, returning -1 if not found
197 */
198static int
199_nc_find_ext_name(TERMTYPE * tp, char *name, int token_type)
200{
201    unsigned j;
202    unsigned first = _nc_first_ext_name(tp, token_type);
203    unsigned last = _nc_last_ext_name(tp, token_type);
204
205    for (j = first; j < last; j++) {
206	if (!strcmp(name, tp->ext_Names[j])) {
207	    return j;
208	}
209    }
210    return -1;
211}
212
213/*
214 * Translate an index into ext_Names[] into the corresponding index into data
215 * (e.g., Booleans[]).
216 */
217static int
218_nc_ext_data_index(TERMTYPE * tp, int n, int token_type)
219{
220    switch (token_type) {
221    case BOOLEAN:
222	n += (tp->num_Booleans - tp->ext_Booleans);
223	break;
224    case NUMBER:
225	n += (tp->num_Numbers - tp->ext_Numbers)
226	    - (tp->ext_Booleans);
227	break;
228    default:
229    case STRING:
230	n += (tp->num_Strings - tp->ext_Strings)
231	    - (tp->ext_Booleans + tp->ext_Numbers);
232    }
233    return n;
234}
235
236/*
237 * Adjust tables to remove (not free) an extended name and its corresponding
238 * data.
239 */
240static bool
241_nc_del_ext_name(TERMTYPE * tp, char *name, int token_type)
242{
243    int j;
244    int first, last;
245
246    if ((first = _nc_find_ext_name(tp, name, token_type)) >= 0) {
247	last = NUM_EXT_NAMES(tp) - 1;
248	for (j = first; j < last; j++) {
249	    tp->ext_Names[j] = tp->ext_Names[j + 1];
250	}
251	first = _nc_ext_data_index(tp, first, token_type);
252	switch (token_type) {
253	case BOOLEAN:
254	    last = tp->num_Booleans - 1;
255	    for (j = first; j < last; j++)
256		tp->Booleans[j] = tp->Booleans[j + 1];
257	    tp->ext_Booleans -= 1;
258	    tp->num_Booleans -= 1;
259	    break;
260	case NUMBER:
261	    last = tp->num_Numbers - 1;
262	    for (j = first; j < last; j++)
263		tp->Numbers[j] = tp->Numbers[j + 1];
264	    tp->ext_Numbers -= 1;
265	    tp->num_Numbers -= 1;
266	    break;
267	case STRING:
268	    last = tp->num_Strings - 1;
269	    for (j = first; j < last; j++)
270		tp->Strings[j] = tp->Strings[j + 1];
271	    tp->ext_Strings -= 1;
272	    tp->num_Strings -= 1;
273	    break;
274	}
275	return TRUE;
276    }
277    return FALSE;
278}
279
280/*
281 * Adjust tables to insert an extended name, making room for new data.  The
282 * index into the corresponding data array is returned.
283 */
284static int
285_nc_ins_ext_name(TERMTYPE * tp, char *name, int token_type)
286{
287    unsigned first = _nc_first_ext_name(tp, token_type);
288    unsigned last = _nc_last_ext_name(tp, token_type);
289    unsigned total = NUM_EXT_NAMES(tp) + 1;
290    unsigned j, k;
291
292    for (j = first; j < last; j++) {
293	int cmp = strcmp(name, tp->ext_Names[j]);
294	if (cmp == 0)
295	    /* already present */
296	    return _nc_ext_data_index(tp, (int) j, token_type);
297	if (cmp < 0) {
298	    break;
299	}
300    }
301
302    tp->ext_Names = typeRealloc(char *, total, tp->ext_Names);
303    for (k = total - 1; k > j; k--)
304	tp->ext_Names[k] = tp->ext_Names[k - 1];
305    tp->ext_Names[j] = name;
306    j = _nc_ext_data_index(tp, (int) j, token_type);
307
308    switch (token_type) {
309    case BOOLEAN:
310	tp->ext_Booleans += 1;
311	tp->num_Booleans += 1;
312	tp->Booleans = typeRealloc(char, tp->num_Booleans, tp->Booleans);
313	for (k = tp->num_Booleans - 1; k > j; k--)
314	    tp->Booleans[k] = tp->Booleans[k - 1];
315	break;
316    case NUMBER:
317	tp->ext_Numbers += 1;
318	tp->num_Numbers += 1;
319	tp->Numbers = typeRealloc(short, tp->num_Numbers, tp->Numbers);
320	for (k = tp->num_Numbers - 1; k > j; k--)
321	    tp->Numbers[k] = tp->Numbers[k - 1];
322	break;
323    case STRING:
324	tp->ext_Strings += 1;
325	tp->num_Strings += 1;
326	tp->Strings = typeRealloc(char *, tp->num_Strings, tp->Strings);
327	for (k = tp->num_Strings - 1; k > j; k--)
328	    tp->Strings[k] = tp->Strings[k - 1];
329	break;
330    }
331    return j;
332}
333
334/*
335 * Look for strings that are marked cancelled, which happen to be the same name
336 * as a boolean or number.  We'll get this as a special case when we get a
337 * cancellation of a name that is inherited from another entry.
338 */
339static void
340adjust_cancels(TERMTYPE * to, TERMTYPE * from)
341{
342    int first = to->ext_Booleans + to->ext_Numbers;
343    int last = first + to->ext_Strings;
344    int j, k;
345
346    for (j = first; j < last;) {
347	char *name = to->ext_Names[j];
348	unsigned j_str = to->num_Strings - first - to->ext_Strings;
349
350	if (to->Strings[j + j_str] == CANCELLED_STRING) {
351	    if ((k = _nc_find_ext_name(from, to->ext_Names[j], BOOLEAN)) >= 0) {
352		if (_nc_del_ext_name(to, name, STRING)
353		    || _nc_del_ext_name(to, name, NUMBER)) {
354		    k = _nc_ins_ext_name(to, name, BOOLEAN);
355		    to->Booleans[k] = FALSE;
356		} else {
357		    j++;
358		}
359	    } else if ((k = _nc_find_ext_name(from, to->ext_Names[j],
360					      NUMBER)) >= 0) {
361		if (_nc_del_ext_name(to, name, STRING)
362		    || _nc_del_ext_name(to, name, BOOLEAN)) {
363		    k = _nc_ins_ext_name(to, name, NUMBER);
364		    to->Numbers[k] = CANCELLED_NUMERIC;
365		} else {
366		    j++;
367		}
368	    }
369	} else {
370	    j++;
371	}
372    }
373}
374
375NCURSES_EXPORT(void)
376_nc_align_termtype(TERMTYPE * to, TERMTYPE * from)
377{
378    int na = NUM_EXT_NAMES(to);
379    int nb = NUM_EXT_NAMES(from);
380    int n;
381    bool same;
382    char **ext_Names;
383    int ext_Booleans, ext_Numbers, ext_Strings;
384
385    DEBUG(2, ("align_termtype to(%d:%s), from(%d:%s)", na, to->term_names,
386	      nb, from->term_names));
387
388    if (na != 0 || nb != 0) {
389	if ((na == nb)		/* check if the arrays are equivalent */
390	    &&(to->ext_Booleans == from->ext_Booleans)
391	    && (to->ext_Numbers == from->ext_Numbers)
392	    && (to->ext_Strings == from->ext_Strings)) {
393	    for (n = 0, same = TRUE; n < na; n++) {
394		if (strcmp(to->ext_Names[n], from->ext_Names[n])) {
395		    same = FALSE;
396		    break;
397		}
398	    }
399	    if (same)
400		return;
401	}
402	/*
403	 * This is where we pay for having a simple extension representation.
404	 * Allocate a new ext_Names array and merge the two ext_Names arrays
405	 * into it, updating to's counts for booleans, etc.  Fortunately we do
406	 * this only for the terminfo compiler (tic) and comparer (infocmp).
407	 */
408	ext_Names = typeMalloc(char *, na + nb);
409
410	if (to->ext_Strings && (from->ext_Booleans + from->ext_Numbers))
411	    adjust_cancels(to, from);
412
413	if (from->ext_Strings && (to->ext_Booleans + to->ext_Numbers))
414	    adjust_cancels(from, to);
415
416	ext_Booleans = merge_names(ext_Names,
417				   to->ext_Names,
418				   to->ext_Booleans,
419				   from->ext_Names,
420				   from->ext_Booleans);
421	ext_Numbers = merge_names(ext_Names + ext_Booleans,
422				  to->ext_Names
423				  + to->ext_Booleans,
424				  to->ext_Numbers,
425				  from->ext_Names
426				  + from->ext_Booleans,
427				  from->ext_Numbers);
428	ext_Strings = merge_names(ext_Names + ext_Numbers + ext_Booleans,
429				  to->ext_Names
430				  + to->ext_Booleans
431				  + to->ext_Numbers,
432				  to->ext_Strings,
433				  from->ext_Names
434				  + from->ext_Booleans
435				  + from->ext_Numbers,
436				  from->ext_Strings);
437	/*
438	 * Now we must reallocate the Booleans, etc., to allow the data to be
439	 * overlaid.
440	 */
441	if (na != (ext_Booleans + ext_Numbers + ext_Strings)) {
442	    realign_data(to, ext_Names, ext_Booleans, ext_Numbers, ext_Strings);
443	    FreeIfNeeded(to->ext_Names);
444	    to->ext_Names = ext_Names;
445	    DEBUG(2, ("realigned %d extended names for '%s' (to)",
446		      NUM_EXT_NAMES(to), to->term_names));
447	}
448	if (nb != (ext_Booleans + ext_Numbers + ext_Strings)) {
449	    nb = (ext_Booleans + ext_Numbers + ext_Strings);
450	    realign_data(from, ext_Names, ext_Booleans, ext_Numbers, ext_Strings);
451	    from->ext_Names = typeRealloc(char *, nb, from->ext_Names);
452	    memcpy(from->ext_Names, ext_Names, sizeof(char *) * nb);
453	    DEBUG(2, ("realigned %d extended names for '%s' (from)",
454		      NUM_EXT_NAMES(from), from->term_names));
455	}
456    }
457}
458#endif
459
460NCURSES_EXPORT(void)
461_nc_copy_termtype(TERMTYPE * dst, TERMTYPE * src)
462{
463    unsigned i;
464
465    *dst = *src;		/* ...to copy the sizes and string-tables */
466    dst->Booleans = typeMalloc(char, NUM_BOOLEANS(dst));
467    dst->Numbers = typeMalloc(short, NUM_NUMBERS(dst));
468    dst->Strings = typeMalloc(char *, NUM_STRINGS(dst));
469
470    /* FIXME: use memcpy for these and similar loops */
471    for_each_boolean(i, dst)
472	dst->Booleans[i] = src->Booleans[i];
473    for_each_number(i, dst)
474	dst->Numbers[i] = src->Numbers[i];
475    for_each_string(i, dst)
476	dst->Strings[i] = src->Strings[i];
477
478    /* FIXME: we probably should also copy str_table and ext_str_table,
479     * but tic and infocmp are not written to exploit that (yet).
480     */
481
482#if NCURSES_XNAMES
483    if ((i = NUM_EXT_NAMES(src)) != 0) {
484	dst->ext_Names = typeMalloc(char *, i);
485	memcpy(dst->ext_Names, src->ext_Names, i * sizeof(char *));
486    } else {
487	dst->ext_Names = 0;
488    }
489#endif
490
491}
492