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 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 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  * Copyright(c) 1998 Sun Microsystems, Inc.
23  * All rights reserved.
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 
30 #define SI      0x0f
31 #define SO      0x0e
32 #define	MSB	0x80
33 #define	MSB_OFF	0x7f
34 #define ESC     0x1b
35 
36 #define NON_ID_CHAR '_'
37 
38 enum	_GSTATE { G0, G1, G2, G3 };
39 
40 typedef struct _icv_state {
41 	char	_lastc;
42 	short	_gstate;
43 } _iconv_st;
44 
45 
46 int gb_to_iso(char in_byte1, char in_byte2, char *buf, int buflen);
47 
48 /*
49  * Open; called from iconv_open()
50  */
51 void *
_icv_open()52 _icv_open()
53 {
54 	_iconv_st *st;
55 
56 	if ((st = (_iconv_st *)malloc(sizeof(_iconv_st))) == NULL) {
57 		errno = ENOMEM;
58 		return ((void *) -1);
59 	}
60 
61 	st->_gstate = G0;
62 	return ((void *)st);
63 }
64 
65 
66 /*
67  * Close; called from iconv_close()
68  */
69 void
_icv_close(_iconv_st * st)70 _icv_close(_iconv_st *st)
71 {
72 	if (st == NULL)
73 		errno = EBADF;
74 	else
75 		free(st);
76 }
77 
78 
79 /*
80  * Actual conversion; called from iconv()
81  *
82  *                       +----------------------------------+
83  *          MSB          V   MSB       ascii            MSB | (SO)
84  *  +-> G0 ------------> G1 ------> G2 ------------> G3 ----+
85  *  | ascii  (ESC,SO)    ^   MSB    |  (SI)   ^ ascii |
86  *  +----+               +----------+         +-------+
87  */
88 size_t
_icv_iconv(_iconv_st * st,char ** inbuf,size_t * inbytesleft,char ** outbuf,size_t * outbytesleft)89 _icv_iconv(_iconv_st *st, char **inbuf, size_t *inbytesleft,
90 			char **outbuf, size_t *outbytesleft)
91 {
92 	int	n;
93 
94 	if (st == NULL) {
95 		errno = EBADF;
96 		return (size_t)-1;
97 	}
98 	if (inbuf == NULL || *inbuf == NULL) { /* Reset request */
99 		if (st->_gstate == G1) {
100 		    if (outbytesleft && *outbytesleft >= 1
101 				&& outbuf && *outbuf) {
102 			**outbuf = SI;
103 			(*outbuf)++;
104 			(*outbytesleft)--;
105 		    } else {
106 			errno = E2BIG;
107 			return((size_t)-1);
108 		    }
109 		}
110 		st->_gstate = G0;
111 		return (size_t)0;
112 	}
113 
114 	errno = 0;
115 
116 	while (*inbytesleft > 0 && *outbytesleft > 0) {
117 	    switch (st->_gstate) {
118 	    case G0:
119 		if (**inbuf & MSB) {
120 		    if (*outbytesleft < 5) {
121 			errno = E2BIG;
122 			return (size_t)-1;
123 		    }
124 		    **outbuf = ESC;
125 		    *(*outbuf+1) = '$';
126 		    *(*outbuf+2) = ')';
127 		    *(*outbuf+3) = 'A';
128 		    *(*outbuf+4) = SO;
129 		    (*outbuf) += 5, (*outbytesleft) -= 5;
130 		    st->_lastc = **inbuf;
131 		    st->_gstate = G1;
132 		} else {		/* ASCII */
133 		    **outbuf = **inbuf;
134 		    (*outbuf)++, (*outbytesleft)--;
135 		}
136 		break;
137 	    case G1:
138 		if (**inbuf & MSB) {
139 		    n = gb_to_iso(st->_lastc, **inbuf, *outbuf, *outbytesleft);
140 		    if (n > 0) {
141 			(*outbuf) += n, (*outbytesleft) -= n;
142 			st->_gstate = G2;
143 		    } else {
144 			errno = E2BIG;
145 			return (size_t)-1;
146 		    }
147 	        } else {
148 		    errno = EILSEQ;
149 		}
150 		break;
151 	    case G2:
152 		if (**inbuf & MSB) {
153 		    st->_lastc = **inbuf;
154 		    st->_gstate = G1;
155 		} else {
156 		    if (*outbytesleft < 2) {
157 			errno = E2BIG;
158 			return (size_t)-1;
159 		    }
160 		    **outbuf = SI;
161 		    *(*outbuf+1) = **inbuf;
162 		    (*outbuf) += 2, (*outbytesleft) -= 2;
163 		    st->_gstate = G3;
164 		}
165 		break;
166 	    case G3:
167 		if (**inbuf & MSB) {
168 		    **outbuf = SO;
169 		    st->_lastc = **inbuf;
170 		    st->_gstate = G1;
171 		} else {
172 		    **outbuf = **inbuf;
173 		}
174 		(*outbuf)++, (*outbytesleft)--;
175 		break;
176 	    }
177 
178 	    (*inbuf)++, (*inbytesleft)--;
179 	    if (errno)
180 		return (size_t)-1;
181 	}
182 
183 	if (*inbytesleft > 0 && *outbytesleft == 0) {
184 		errno = E2BIG;
185 		return (size_t)-1;
186 	}
187 	return ((size_t)(*inbytesleft));
188 }
189 
190 
191 /*
192  * return: > 0 - converted with enough space
193  *	   = 0 - no space in outbuf
194  */
195 int
gb_to_iso(in_byte1,in_byte2,buf,buflen)196 gb_to_iso(in_byte1, in_byte2, buf, buflen)
197 char	in_byte1, in_byte2;
198 char	*buf;
199 int	buflen;
200 {
201 	if ( buflen < 2 )
202 		return 0;
203 	*buf = in_byte1 & MSB_OFF;
204 	*(buf+1) = in_byte2 & MSB_OFF;
205 	return 2;
206 }
207 
208 
209 /*
210  * ====================================================================
211  * enconv functions
212  * ====================================================================
213  */
214 
215 typedef struct _enconv_st {
216 	char	_lastc;
217 	short	_gstate;
218 } _enconv_st;
219 
220 
221 /*
222  * Open; called from enconv_open()
223  */
224 void *
_cv_open()225 _cv_open()
226 {
227 	_enconv_st *st;
228 
229 	if ((st = (_enconv_st *)malloc(sizeof(_enconv_st))) == NULL) {
230 		return ((void *) -1);
231 	}
232 
233 	st->_gstate = G0;
234 	return ((void *)st);
235 }
236 
237 
238 /*
239  * Close; called from enconv_close()
240  */
241 void
_cv_close(_enconv_st * st)242 _cv_close(_enconv_st *st)
243 {
244 	if (st != NULL)
245 		free(st);
246 }
247 
248 
249 /*
250  * Actual conversion; called from enconv()
251  *
252  *                       +----------------------------------+
253  *          MSB          V   MSB       ascii            MSB | (SO)
254  *  +-> G0 ------------> G1 ------> G2 ------------> G3 ----+
255  *  | ascii  (ESC,SO)    ^   MSB    |  (SI)   ^ ascii |
256  *  +----+               +----------+         +-------+
257  */
258 size_t
_cv_enconv(_enconv_st * st,char ** inbuf,size_t * inbytesleft,char ** outbuf,size_t * outbytesleft)259 _cv_enconv(_enconv_st *st, char **inbuf, size_t *inbytesleft,
260 			char **outbuf, size_t *outbytesleft)
261 {
262 	int	n;
263 
264 	if (st == NULL) {
265 		return -1;
266 	}
267 	if (inbuf == NULL || *inbuf == NULL) { /* Reset request */
268 		st->_gstate = G0;
269 		return 0;
270 	}
271 
272 	while (*inbytesleft > 0 && *outbytesleft > 0) {
273 	    switch (st->_gstate) {
274 	    case G0:
275 		if (**inbuf & MSB) {
276 		    if (*outbytesleft < 5) {
277 			return (*inbytesleft);
278 		    }
279 		    **outbuf = ESC;
280 		    *(*outbuf+1) = '$';
281 		    *(*outbuf+2) = ')';
282 		    *(*outbuf+3) = 'A';
283 		    *(*outbuf+4) = SO;
284 		    (*outbuf) += 5, (*outbytesleft) -= 5;
285 		    st->_lastc = **inbuf;
286 		    st->_gstate = G1;
287 		} else {		/* ASCII */
288 		    **outbuf = **inbuf;
289 		    (*outbuf)++, (*outbytesleft)--;
290 		}
291 		break;
292 	    case G1:
293 		if (**inbuf & MSB) {
294 		    n = gb_to_iso(st->_lastc, **inbuf, *outbuf, *outbytesleft);
295 		    if (n > 0) {
296 			(*outbuf) += n, (*outbytesleft) -= n;
297 			st->_gstate = G2;
298 		    } else {
299 			(*inbuf)++, (*inbytesleft)--;
300 			return (*inbytesleft);
301 		    }
302 	        }
303 		break;
304 	    case G2:
305 		if (**inbuf & MSB) {
306 		    st->_lastc = **inbuf;
307 		    st->_gstate = G1;
308 		} else {
309 		    if (*outbytesleft < 2) {
310 			return (*inbytesleft);
311 		    }
312 		    **outbuf = SI;
313 		    *(*outbuf+1) = **inbuf;
314 		    (*outbuf) += 2, (*outbytesleft) -= 2;
315 		    st->_gstate = G3;
316 		}
317 		break;
318 	    case G3:
319 		if (**inbuf & MSB) {
320 		    **outbuf = SO;
321 		    st->_lastc = **inbuf;
322 		    st->_gstate = G1;
323 		} else {
324 		    **outbuf = **inbuf;
325 		}
326 		(*outbuf)++, (*outbytesleft)--;
327 		break;
328 	    }
329 
330 	    (*inbuf)++, (*inbytesleft)--;
331 	}
332 
333 	return (*inbytesleft);
334 }
335