1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include	<stdio.h>
28 #include	<strings.h>
29 #include	<_machelf.h>
30 #include	"_conv.h"
31 #include	"globals_msg.h"
32 
33 
34 /*
35  * Given an integer value, generate an ASCII representation of it.
36  *
37  * entry:
38  *	inv_buf - Buffer into which the resulting string is generated.
39  *	value - Value to be formatted.
40  *	fmt_flags - CONV_FMT_* values, used to specify formatting details.
41  *
42  * exit:
43  *	The formatted string is placed into inv_buf. The pointer
44  *	to the string is returned.
45  */
46 const char *
conv_invalid_val(Conv_inv_buf_t * inv_buf,Xword value,Conv_fmt_flags_t fmt_flags)47 conv_invalid_val(Conv_inv_buf_t *inv_buf, Xword value,
48     Conv_fmt_flags_t fmt_flags)
49 {
50 	const char	*fmt;
51 
52 	if (fmt_flags & CONV_FMT_DECIMAL) {
53 		if (fmt_flags & CONV_FMT_SPACE)
54 			fmt = MSG_ORIG(MSG_GBL_FMT_DECS);
55 		else
56 			fmt = MSG_ORIG(MSG_GBL_FMT_DEC);
57 	} else {
58 		if (fmt_flags & CONV_FMT_SPACE)
59 			fmt = MSG_ORIG(MSG_GBL_FMT_HEXS);
60 		else
61 			fmt = MSG_ORIG(MSG_GBL_FMT_HEX);
62 	}
63 	(void) snprintf(inv_buf->buf, sizeof (inv_buf->buf), fmt, value);
64 	return ((const char *)inv_buf->buf);
65 }
66 
67 
68 
69 /*
70  * cef_cp() is used by conv_expn_field() to fill in the output buffer.
71  * A CONV_EXPN_FIELD_STATE variable is used to maintain the buffer state
72  * as the operation progresses.
73  *
74  * entry:
75  *	arg - As passed to conv_expn_field().
76  *	state - Variable used to maintain buffer state between calls.
77  *	list_item - TRUE(1) if this is a list item, and FALSE(0)
78  *		if it is something else.
79  *	str - String to be added to the buffer.
80  *
81  * exit:
82  *	On Success:
83  *		buffer contains the output string, including a list
84  *		separator if appropriate. state has been updated.
85  *		TRUE(1) is returned.
86  *	On Failure:
87  *		Buffer contains the numeric representation for the flags,
88  *		and FALSE(0) is returned.
89  */
90 typedef struct {
91 	char *cur;		/* Current output position in buf */
92 	size_t room;		/* # of bytes left in buf */
93 	int list_cnt;		/* # of list items output into buf  */
94 	const char *sep_str;	/* String used as list separator */
95 	int sep_str_len;	/* strlen(sep_str) */
96 } CONV_EXPN_FIELD_STATE;
97 
98 static int
cef_cp(CONV_EXPN_FIELD_ARG * arg,CONV_EXPN_FIELD_STATE * state,int list_item,const char * str)99 cef_cp(CONV_EXPN_FIELD_ARG *arg, CONV_EXPN_FIELD_STATE *state,
100 	int list_item, const char *str)
101 {
102 	Conv_inv_buf_t inv_buf;
103 	int n;
104 
105 	if (list_item) {	/* This is a list item */
106 		/*
107 		 * If list is non-empty, and the buffer has room,
108 		 * then insert the separator.
109 		 */
110 		if (state->list_cnt != 0) {
111 			if (state->sep_str_len < state->room) {
112 				(void) memcpy(state->cur, state->sep_str,
113 				    state->sep_str_len);
114 				state->cur += state->sep_str_len;
115 				state->room -= state->sep_str_len;
116 			} else {
117 				/* Ensure code below will catch lack of room */
118 				state->room = 0;
119 			}
120 		}
121 		state->list_cnt++;
122 	}
123 
124 	n = strlen(str);
125 	if (n < state->room) {
126 		(void) memcpy(state->cur, str, n);
127 		state->cur += n;
128 		state->room -= n;
129 		return (TRUE);
130 	}
131 
132 	/* Buffer too small. Fill in the numeric value and report failure */
133 	(void) conv_invalid_val(&inv_buf, arg->oflags, 0);
134 	(void) strlcpy(arg->buf, inv_buf.buf, arg->bufsize);
135 	return (FALSE);
136 }
137 
138 
139 
140 /*
141  * Common setup code for conv_expn_field() and conv_expn_field2()
142  */
143 static int
cef_setup(CONV_EXPN_FIELD_ARG * arg,Conv_fmt_flags_t fmt_flags,CONV_EXPN_FIELD_STATE * state)144 cef_setup(CONV_EXPN_FIELD_ARG *arg, Conv_fmt_flags_t fmt_flags,
145     CONV_EXPN_FIELD_STATE *state)
146 {
147 	const char **lead_str;
148 
149 	/* Initialize buffer state */
150 	state->cur = arg->buf;
151 	state->room = arg->bufsize;
152 	state->list_cnt = 0;
153 	state->sep_str = arg->sep ? arg->sep : MSG_ORIG(MSG_GBL_SEP);
154 	state->sep_str_len = strlen(state->sep_str);
155 
156 	/* Prefix string */
157 	if ((fmt_flags & CONV_FMT_NOBKT) == 0)
158 		if (!cef_cp(arg, state, FALSE,
159 		    (arg->prefix ? arg->prefix : MSG_ORIG(MSG_GBL_OSQBRKT))))
160 			return (FALSE);
161 
162 	/* Any strings in the lead_str array go at the head of the list */
163 	lead_str = arg->lead_str;
164 	if (lead_str) {
165 		while (*lead_str) {
166 			if (!cef_cp(arg, state, TRUE, *lead_str++))
167 				return (FALSE);
168 		}
169 	}
170 
171 	return (TRUE);
172 }
173 
174 /*
175  * Common finishing code for conv_expn_field() and conv_expn_field2()
176  */
177 static int
cef_wrap(CONV_EXPN_FIELD_ARG * arg,Conv_fmt_flags_t fmt_flags,CONV_EXPN_FIELD_STATE * state,Xword rflags)178 cef_wrap(CONV_EXPN_FIELD_ARG *arg, Conv_fmt_flags_t fmt_flags,
179     CONV_EXPN_FIELD_STATE *state, Xword rflags)
180 {
181 	/*
182 	 * If any flags remain, then they are unidentified.  Add the numeric
183 	 * representation of these flags to the users output buffer.
184 	 */
185 	if (rflags) {
186 		Conv_inv_buf_t inv_buf;
187 
188 		(void) conv_invalid_val(&inv_buf, rflags, fmt_flags);
189 		if (!cef_cp(arg, state, TRUE, inv_buf.buf))
190 			return (FALSE);
191 	}
192 
193 	/* Suffix string */
194 	if ((fmt_flags & CONV_FMT_NOBKT) == 0)
195 		if (!cef_cp(arg, state, FALSE,
196 		    (arg->suffix ? arg->suffix : MSG_ORIG(MSG_GBL_CSQBRKT))))
197 			return (FALSE);
198 
199 	/* Terminate the buffer */
200 	*state->cur = '\0';
201 
202 	return (TRUE);
203 }
204 
205 /*
206  * Provide a focal point for expanding bit-fields values into
207  * their corresponding strings.
208  *
209  * entry:
210  *	arg - Specifies the operation to be carried out. See the
211  *		definition of CONV_EXPN_FIELD_ARG in conv.h for details.
212  *	vdp - Array of value descriptors, giving the possible bit values,
213  *		and their corresponding strings. Note that the final element
214  *		must contain only NULL values. This terminates the list.
215  *
216  * exit:
217  *	arg->buf contains the formatted result. True (1) is returned if there
218  *	was no error, and False (0) if the buffer was too small. In the failure
219  *	case, arg->buf contains a numeric representation of the value.
220  *
221  * note:
222  *	The Val_desc2 variant of this routine ignores entries from vdp that
223  *	have a non-zero osabi or machine value that does not match that
224  *	supplied by the caller.
225  */
226 /*ARGSUSED3*/
227 int
_conv_expn_field(CONV_EXPN_FIELD_ARG * arg,const Val_desc * vdp,Conv_fmt_flags_t fmt_flags,const char * local_sgs_msg)228 _conv_expn_field(CONV_EXPN_FIELD_ARG *arg, const Val_desc *vdp,
229     Conv_fmt_flags_t fmt_flags, const char *local_sgs_msg)
230 {
231 	CONV_EXPN_FIELD_STATE state;
232 	Xword rflags = arg->rflags;
233 
234 	if (cef_setup(arg, fmt_flags, &state) == FALSE)
235 		return (FALSE);
236 
237 	/*
238 	 * Traverse the callers Val_desc array and determine if the value
239 	 * corresponds to any array item and add those that are to the list.
240 	 */
241 	for (; vdp->v_msg; vdp++) {
242 		if (arg->oflags & vdp->v_val) {
243 			if (!cef_cp(arg, &state, TRUE,
244 			    MSG_ORIG_STRTAB(vdp->v_msg, local_sgs_msg)))
245 				return (FALSE);
246 
247 			/* Indicate this item has been collected */
248 			rflags &= ~(vdp->v_val);
249 		}
250 	}
251 
252 	return (cef_wrap(arg, fmt_flags, &state, rflags));
253 }
254 
255 /*ARGSUSED5*/
256 int
_conv_expn_field2(CONV_EXPN_FIELD_ARG * arg,uchar_t osabi,Half mach,const Val_desc2 * vdp,Conv_fmt_flags_t fmt_flags,const char * local_sgs_msg)257 _conv_expn_field2(CONV_EXPN_FIELD_ARG *arg, uchar_t osabi, Half mach,
258     const Val_desc2 *vdp, Conv_fmt_flags_t fmt_flags, const char *local_sgs_msg)
259 {
260 	CONV_EXPN_FIELD_STATE state;
261 	Xword rflags = arg->rflags;
262 
263 	if (cef_setup(arg, fmt_flags, &state) == FALSE)
264 		return (FALSE);
265 
266 	/*
267 	 * Traverse the callers Val_desc array and determine if the value
268 	 * corresponds to any array item and add those that are to the list.
269 	 */
270 	for (; vdp->v_msg; vdp++) {
271 		if (CONV_VD2_SKIP(osabi, mach, vdp))
272 			continue;
273 
274 		if (arg->oflags & vdp->v_val) {
275 			if (!cef_cp(arg, &state, TRUE,
276 			    MSG_ORIG_STRTAB(vdp->v_msg, local_sgs_msg)))
277 				return (FALSE);
278 
279 			/* Indicate this item has been collected */
280 			rflags &= ~(vdp->v_val);
281 		}
282 	}
283 
284 	return (cef_wrap(arg, fmt_flags, &state, rflags));
285 }
286