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 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28/*
29 * Translate a string into C literal string constant notation.
30 */
31
32#include	<stdio.h>
33#include	<ctype.h>
34#include	<_conv.h>
35#include	<c_literal_msg.h>
36
37
38/*
39 * Convert characters to the form used by the C language to represent
40 * literal strings:
41 *	- Printable characters are shown as themselves
42 *	- Convert special characters to their 2-character escaped forms:
43 *		alert (bell)	\a
44 *		backspace	\b
45 *		formfeed	\f
46 *		newline		\n
47 *		return		\r
48 *		horizontal tab	\t
49 *		vertical tab	\v
50 *		backspace	\\
51 *		single quote	\'
52 *		double quote	\"
53 *	- Display other non-printable characters as 4-character escaped
54 *		octal constants.
55 *
56 * entry:
57 *	buf - Buffer of characters to be processed
58 *	n # of characters in buf to be processed
59 *	outfunc - Function to be called to move output characters.
60 *	uvalue - User value. This argument is passed to outfunc without
61 *		examination. The caller can use it to pass additional
62 *		information required by the callback.
63 *
64 * exit:
65 *	The string has been processed, with the resulting data passed
66 *	to outfunc for processing.
67 */
68void
69conv_str_to_c_literal(const char *buf, size_t n,
70    Conv_str_to_c_literal_func_t *outfunc, void *uvalue)
71{
72	char	bs_buf[2];	/* For two-character backslash codes */
73	char	octal_buf[10];	/* For \000 style octal constants */
74
75	bs_buf[0] = '\\';
76	while (n > 0) {
77		switch (*buf) {
78		case '\0':
79			bs_buf[1] = '0';
80			break;
81		case '\a':
82			bs_buf[1] = 'a';
83			break;
84		case '\b':
85			bs_buf[1] = 'b';
86			break;
87		case '\f':
88			bs_buf[1] = 'f';
89			break;
90		case '\n':
91			bs_buf[1] = 'n';
92			break;
93		case '\r':
94			bs_buf[1] = 'r';
95			break;
96		case '\t':
97			bs_buf[1] = 't';
98			break;
99		case '\v':
100			bs_buf[1] = 'v';
101			break;
102		case '\\':
103			bs_buf[1] = '\\';
104			break;
105		case '\'':
106			bs_buf[1] = '\'';
107			break;
108		case '"':
109			bs_buf[1] = '"';
110			break;
111		default:
112			bs_buf[1] = '\0';
113		}
114
115		if (bs_buf[1] != '\0') {
116			(*outfunc)(bs_buf, 2, uvalue);
117			buf++;
118			n--;
119		} else if (isprint(*buf)) {
120			/*
121			 * Output the entire sequence of printable
122			 * characters in a single shot.
123			 */
124			const char	*start = buf;
125			size_t		outlen = 0;
126
127			for (start = buf; (n > 0) && isprint(*buf); buf++, n--)
128				outlen++;
129			(*outfunc)(start, outlen, uvalue);
130		} else {
131			/* Generic unprintable character: Use octal notation */
132			(void) snprintf(octal_buf, sizeof (octal_buf),
133			    MSG_ORIG(MSG_FMT_OCTCONST), (uchar_t)*buf);
134			(*outfunc)(octal_buf, strlen(octal_buf), uvalue);
135			buf++;
136			n--;
137		}
138	}
139}
140
141/*
142 * Given the pointer to the character following a '\' character in
143 * a C style literal, return the ASCII character code it represents,
144 * and advance the string pointer to the character following the last
145 * character in the escape sequence.
146 *
147 * entry:
148 *	str - Address of string pointer to first character following
149 *		the backslash.
150 *
151 * exit:
152 *	If the character is not valid, -1 is returned. Otherwise
153 *	it returns the ASCII code for the translated character, and
154 *	*str has been advanced.
155 */
156int
157conv_translate_c_esc(char **str)
158{
159	char	*s = *str;
160	int	ch, i;
161
162	ch = *s++;
163	switch (ch) {
164	case 'a':
165		ch = '\a';
166		break;
167	case 'b':
168		ch = '\b';
169		break;
170	case 'f':
171		ch = '\f';
172		break;
173	case 'n':
174		ch = '\n';
175		break;
176	case 'r':
177		ch = '\r';
178		break;
179	case 't':
180		ch = '\t';
181		break;
182	case 'v':
183		ch = '\v';
184		break;
185
186	case '0':
187	case '1':
188	case '2':
189	case '3':
190	case '4':
191	case '5':
192	case '6':
193	case '7':
194		/* Octal constant: There can be up to 3 digits */
195		ch -= '0';
196		for (i = 0; i < 2; i++) {
197			if ((*s < '0') || (*s > '7'))
198				break;
199			ch = (ch << 3) + (*s++ - '0');
200		}
201		break;
202
203	/*
204	 * There are some cases where ch already has the desired value.
205	 * These cases exist simply to remove the special meaning that
206	 * character would otherwise have. We need to match them to
207	 * prevent them from falling into the default error case.
208	 */
209	case '\\':
210	case '\'':
211	case '"':
212		break;
213
214	default:
215		ch = -1;
216		break;
217	}
218
219	*str = s;
220	return (ch);
221}
222