1/*	$NetBSD: tyname.c,v 1.62 2024/03/09 13:20:55 rillig Exp $	*/
2
3/*-
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#if HAVE_NBTOOL_CONFIG_H
33#include "nbtool_config.h"
34#endif
35
36#include <sys/cdefs.h>
37#if defined(__RCSID)
38__RCSID("$NetBSD: tyname.c,v 1.62 2024/03/09 13:20:55 rillig Exp $");
39#endif
40
41#include <assert.h>
42#include <limits.h>
43#include <string.h>
44#include <stdlib.h>
45
46#if IS_LINT1
47#include "lint1.h"
48#else
49#include "lint2.h"
50#endif
51
52/* A tree of strings. */
53typedef struct name_tree_node {
54	const char *ntn_name;
55	struct name_tree_node *ntn_less;
56	struct name_tree_node *ntn_greater;
57} name_tree_node;
58
59static name_tree_node *type_names;
60
61static name_tree_node *
62new_name_tree_node(const char *name)
63{
64	name_tree_node *n;
65
66	n = xmalloc(sizeof(*n));
67	n->ntn_name = xstrdup(name);
68	n->ntn_less = NULL;
69	n->ntn_greater = NULL;
70	return n;
71}
72
73/* Return the canonical instance of the string, with unlimited lifetime. */
74static const char *
75intern(const char *name)
76{
77	name_tree_node *n = type_names, **next;
78	int cmp;
79
80	if (n == NULL) {
81		n = new_name_tree_node(name);
82		type_names = n;
83		return n->ntn_name;
84	}
85
86	while ((cmp = strcmp(name, n->ntn_name)) != 0) {
87		next = cmp < 0 ? &n->ntn_less : &n->ntn_greater;
88		if (*next == NULL) {
89			*next = new_name_tree_node(name);
90			return (*next)->ntn_name;
91		}
92		n = *next;
93	}
94	return n->ntn_name;
95}
96
97#if IS_LINT1
98void
99#else
100static void
101#endif
102buf_init(buffer *buf)
103{
104	buf->len = 0;
105	buf->cap = 128;
106	buf->data = xmalloc(buf->cap);
107	buf->data[0] = '\0';
108}
109
110static void
111buf_done(buffer *buf)
112{
113	free(buf->data);
114}
115
116static void
117buf_add_mem(buffer *buf, const char *s, size_t n)
118{
119	while (buf->len + n + 1 >= buf->cap) {
120		buf->cap *= 2;
121		buf->data = xrealloc(buf->data, buf->cap);
122	}
123
124	memcpy(buf->data + buf->len, s, n);
125	buf->len += n;
126	buf->data[buf->len] = '\0';
127}
128
129#if IS_LINT1
130void
131buf_add_char(buffer *buf, char c)
132{
133	buf_add_mem(buf, &c, 1);
134}
135#endif
136
137static void
138buf_add(buffer *buf, const char *s)
139{
140	buf_add_mem(buf, s, strlen(s));
141}
142
143static void
144buf_add_int(buffer *buf, int n)
145{
146	char num[1 + sizeof(n) * CHAR_BIT + 1];
147
148	(void)snprintf(num, sizeof(num), "%d", n);
149	buf_add(buf, num);
150}
151
152const char *
153tspec_name(tspec_t t)
154{
155	const char *name = ttab[t].tt_name;
156	assert(name != NULL);
157	return name;
158}
159
160static void
161type_name_of_function(buffer *buf, const type_t *tp)
162{
163	const char *sep = "";
164
165	buf_add(buf, "(");
166	if (tp->t_proto) {
167#if IS_LINT1
168		const sym_t *param = tp->u.params;
169		if (param == NULL)
170			buf_add(buf, "void");
171		for (; param != NULL; param = param->s_next) {
172			buf_add(buf, sep), sep = ", ";
173			buf_add(buf, type_name(param->s_type));
174		}
175#else
176		type_t **argtype;
177
178		argtype = tp->t_args;
179		if (argtype == NULL)
180			buf_add(buf, "void");
181		for (; *argtype != NULL; argtype++) {
182			buf_add(buf, sep), sep = ", ";
183			buf_add(buf, type_name(*argtype));
184		}
185#endif
186	}
187	if (tp->t_vararg) {
188		buf_add(buf, sep);
189		buf_add(buf, "...");
190	}
191	buf_add(buf, ") returning ");
192	buf_add(buf, type_name(tp->t_subt));
193}
194
195static void
196type_name_of_struct_or_union(buffer *buf, const type_t *tp)
197{
198	buf_add(buf, " ");
199#if IS_LINT1
200	if (tp->u.sou->sou_tag->s_name == unnamed &&
201	    tp->u.sou->sou_first_typedef != NULL) {
202		buf_add(buf, "typedef ");
203		buf_add(buf, tp->u.sou->sou_first_typedef->s_name);
204	} else {
205		buf_add(buf, tp->u.sou->sou_tag->s_name);
206	}
207#else
208	buf_add(buf, tp->t_isuniqpos ? "*anonymous*" : tp->t_tag->h_name);
209#endif
210}
211
212static void
213type_name_of_enum(buffer *buf, const type_t *tp)
214{
215	buf_add(buf, " ");
216#if IS_LINT1
217	if (tp->u.enumer->en_tag->s_name == unnamed &&
218	    tp->u.enumer->en_first_typedef != NULL) {
219		buf_add(buf, "typedef ");
220		buf_add(buf, tp->u.enumer->en_first_typedef->s_name);
221	} else {
222		buf_add(buf, tp->u.enumer->en_tag->s_name);
223	}
224#else
225	buf_add(buf, tp->t_isuniqpos ? "*anonymous*" : tp->t_tag->h_name);
226#endif
227}
228
229static void
230type_name_of_array(buffer *buf, const type_t *tp)
231{
232	buf_add(buf, "[");
233#if IS_LINT1
234	if (tp->t_incomplete_array)
235		buf_add(buf, "unknown_size");
236	else
237		buf_add_int(buf, tp->u.dimension);
238#else
239	buf_add_int(buf, tp->t_dim);
240#endif
241	buf_add(buf, "]");
242	buf_add(buf, " of ");
243	buf_add(buf, type_name(tp->t_subt));
244}
245
246const char *
247type_name(const type_t *tp)
248{
249	tspec_t t;
250	buffer buf;
251	const char *name;
252
253	if (tp == NULL)
254		return "(null)";
255
256	if ((t = tp->t_tspec) == INT && tp->t_is_enum)
257		t = ENUM;
258
259	buf_init(&buf);
260	if (tp->t_const)
261		buf_add(&buf, "const ");
262	if (tp->t_volatile)
263		buf_add(&buf, "volatile ");
264
265#if IS_LINT1
266	if (is_struct_or_union(t) && tp->u.sou->sou_incomplete)
267		buf_add(&buf, "incomplete ");
268#endif
269	buf_add(&buf, tspec_name(t));
270
271#if IS_LINT1
272	if (tp->t_bitfield) {
273		buf_add(&buf, ":");
274		buf_add_int(&buf, (int)tp->t_bit_field_width);
275	}
276#endif
277
278	switch (t) {
279	case PTR:
280		buf_add(&buf, " to ");
281		buf_add(&buf, type_name(tp->t_subt));
282		break;
283	case ENUM:
284		type_name_of_enum(&buf, tp);
285		break;
286	case STRUCT:
287	case UNION:
288		type_name_of_struct_or_union(&buf, tp);
289		break;
290	case ARRAY:
291		type_name_of_array(&buf, tp);
292		break;
293	case FUNC:
294		type_name_of_function(&buf, tp);
295		break;
296	default:
297		break;
298	}
299
300	name = intern(buf.data);
301	buf_done(&buf);
302	return name;
303}
304