xref: /spdk/lib/json/json_parse.c (revision 6951b97964ca0d206b77ecc74486f8691fc52067)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk_internal/utf.h"
35 
36 #define SPDK_JSON_MAX_NESTING_DEPTH	64
37 
38 static int
39 hex_value(uint8_t c)
40 {
41 #define V(x, y) [x] = y + 1
42 	static const int8_t val[256] = {
43 		V('0', 0), V('1', 1), V('2', 2), V('3', 3), V('4', 4),
44 		V('5', 5), V('6', 6), V('7', 7), V('8', 8), V('9', 9),
45 		V('A', 0xA), V('B', 0xB), V('C', 0xC), V('D', 0xD), V('E', 0xE), V('F', 0xF),
46 		V('a', 0xA), V('b', 0xB), V('c', 0xC), V('d', 0xD), V('e', 0xE), V('f', 0xF),
47 	};
48 #undef V
49 
50 	return val[c] - 1;
51 }
52 
53 static int
54 json_decode_string_escape_unicode(uint8_t **strp, uint8_t *buf_end, uint8_t *out)
55 {
56 	uint8_t *str = *strp;
57 	int v0, v1, v2, v3;
58 	uint32_t val;
59 	uint32_t surrogate_high = 0;
60 	int rc;
61 decode:
62 	/* \uXXXX */
63 	assert(buf_end > str);
64 
65 	if (*str++ != '\\') { return SPDK_JSON_PARSE_INVALID; }
66 	if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; }
67 
68 	if (*str++ != 'u') { return SPDK_JSON_PARSE_INVALID; }
69 	if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; }
70 
71 	if ((v3 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; }
72 	if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; }
73 
74 	if ((v2 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; }
75 	if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; }
76 
77 	if ((v1 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; }
78 	if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; }
79 
80 	if ((v0 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; }
81 	if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; }
82 
83 	val = v0 | (v1 << 4) | (v2 << 8) | (v3 << 12);
84 
85 	if (surrogate_high) {
86 		/* We already parsed the high surrogate, so this should be the low part. */
87 		if (!utf16_valid_surrogate_low(val)) {
88 			return SPDK_JSON_PARSE_INVALID;
89 		}
90 
91 		/* Convert UTF-16 surrogate pair into codepoint and fall through to utf8_encode. */
92 		val = utf16_decode_surrogate_pair(surrogate_high, val);
93 	} else if (utf16_valid_surrogate_high(val)) {
94 		surrogate_high = val;
95 
96 		/*
97 		 * We parsed a \uXXXX sequence that decoded to the first half of a
98 		 *  UTF-16 surrogate pair, so it must be immediately followed by another
99 		 *  \uXXXX escape.
100 		 *
101 		 * Loop around to get the low half of the surrogate pair.
102 		 */
103 		if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; }
104 		goto decode;
105 	} else if (utf16_valid_surrogate_low(val)) {
106 		/*
107 		 * We found the second half of surrogate pair without the first half;
108 		 *  this is an invalid encoding.
109 		 */
110 		return SPDK_JSON_PARSE_INVALID;
111 	}
112 
113 	/*
114 	 * Convert Unicode escape (or surrogate pair) to UTF-8 in place.
115 	 *
116 	 * This is safe (will not write beyond the buffer) because the \uXXXX sequence is 6 bytes
117 	 *  (or 12 bytes for surrogate pairs), and the longest possible UTF-8 encoding of a
118 	 *  single codepoint is 4 bytes.
119 	 */
120 	if (out) {
121 		rc = utf8_encode_unsafe(out, val);
122 	} else {
123 		rc = utf8_codepoint_len(val);
124 	}
125 	if (rc < 0) {
126 		return SPDK_JSON_PARSE_INVALID;
127 	}
128 
129 	*strp = str; /* update input pointer */
130 	return rc; /* return number of bytes decoded */
131 }
132 
133 static int
134 json_decode_string_escape_twochar(uint8_t **strp, uint8_t *buf_end, uint8_t *out)
135 {
136 	static const uint8_t escapes[256] = {
137 		['b'] = '\b',
138 		['f'] = '\f',
139 		['n'] = '\n',
140 		['r'] = '\r',
141 		['t'] = '\t',
142 		['/'] = '/',
143 		['"'] = '"',
144 		['\\'] = '\\',
145 	};
146 	uint8_t *str = *strp;
147 	uint8_t c;
148 
149 	assert(buf_end > str);
150 	if (buf_end - str < 2) {
151 		return SPDK_JSON_PARSE_INCOMPLETE;
152 	}
153 
154 	assert(str[0] == '\\');
155 
156 	c = escapes[str[1]];
157 	if (c) {
158 		if (out) {
159 			*out = c;
160 		}
161 		*strp += 2; /* consumed two bytes */
162 		return 1; /* produced one byte */
163 	}
164 
165 	return SPDK_JSON_PARSE_INVALID;
166 }
167 
168 /*
169  * Decode JSON string backslash escape.
170  * \param strp pointer to pointer to first character of escape (the backslash).
171  *  *strp is also advanced to indicate how much input was consumed.
172  *
173  * \return Number of bytes appended to out
174  */
175 static int
176 json_decode_string_escape(uint8_t **strp, uint8_t *buf_end, uint8_t *out)
177 {
178 	int rc;
179 
180 	rc = json_decode_string_escape_twochar(strp, buf_end, out);
181 	if (rc > 0) {
182 		return rc;
183 	}
184 
185 	return json_decode_string_escape_unicode(strp, buf_end, out);
186 }
187 
188 /*
189  * Decode JSON string in place.
190  *
191  * \param str_start Pointer to the beginning of the string (the opening " character).
192  *
193  * \return Number of bytes in decoded string (beginning from start).
194  */
195 static int
196 json_decode_string(uint8_t *str_start, uint8_t *buf_end, uint8_t **str_end, uint32_t flags)
197 {
198 	uint8_t *str = str_start;
199 	uint8_t *out = str_start + 1; /* Decode string in place (skip the initial quote) */
200 	int rc;
201 
202 	if (buf_end - str_start < 2) {
203 		/*
204 		 * Shortest valid string (the empty string) is two bytes (""),
205 		 *  so this can't possibly be valid
206 		 */
207 		*str_end = str;
208 		return SPDK_JSON_PARSE_INCOMPLETE;
209 	}
210 
211 	if (*str++ != '"') {
212 		*str_end = str;
213 		return SPDK_JSON_PARSE_INVALID;
214 	}
215 
216 	while (str < buf_end) {
217 		if (str[0] == '"') {
218 			/*
219 			 * End of string.
220 			 * Update str_end to point at next input byte and return output length.
221 			 */
222 			*str_end = str + 1;
223 			return out - str_start - 1;
224 		} else if (str[0] == '\\') {
225 			rc = json_decode_string_escape(&str, buf_end,
226 						       flags & SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE ? out : NULL);
227 			assert(rc != 0);
228 			if (rc < 0) {
229 				*str_end = str;
230 				return rc;
231 			}
232 			out += rc;
233 		} else if (str[0] <= 0x1f) {
234 			/* control characters must be escaped */
235 			*str_end = str;
236 			return SPDK_JSON_PARSE_INVALID;
237 		} else {
238 			rc = utf8_valid(str, buf_end);
239 			if (rc == 0) {
240 				*str_end = str;
241 				return SPDK_JSON_PARSE_INCOMPLETE;
242 			} else if (rc < 0) {
243 				*str_end = str;
244 				return SPDK_JSON_PARSE_INVALID;
245 			}
246 
247 			if (out && out != str && (flags & SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE)) {
248 				memmove(out, str, rc);
249 			}
250 			out += rc;
251 			str += rc;
252 		}
253 	}
254 
255 	/* If execution gets here, we ran out of buffer. */
256 	*str_end = str;
257 	return SPDK_JSON_PARSE_INCOMPLETE;
258 }
259 
260 static int
261 json_valid_number(uint8_t *start, uint8_t *buf_end)
262 {
263 	uint8_t *p = start;
264 	uint8_t c;
265 
266 	if (p >= buf_end) { return -1; }
267 
268 	c = *p++;
269 	if (c >= '1' && c <= '9') { goto num_int_digits; }
270 	if (c == '0') { goto num_frac_or_exp; }
271 	if (c == '-') { goto num_int_first_digit; }
272 	p--;
273 	goto done_invalid;
274 
275 num_int_first_digit:
276 	if (spdk_likely(p != buf_end)) {
277 		c = *p++;
278 		if (c == '0') { goto num_frac_or_exp; }
279 		if (c >= '1' && c <= '9') { goto num_int_digits; }
280 		p--;
281 	}
282 	goto done_invalid;
283 
284 num_int_digits:
285 	if (spdk_likely(p != buf_end)) {
286 		c = *p++;
287 		if (c >= '0' && c <= '9') { goto num_int_digits; }
288 		if (c == '.') { goto num_frac_first_digit; }
289 		if (c == 'e' || c == 'E') { goto num_exp_sign; }
290 		p--;
291 	}
292 	goto done_valid;
293 
294 num_frac_or_exp:
295 	if (spdk_likely(p != buf_end)) {
296 		c = *p++;
297 		if (c == '.') { goto num_frac_first_digit; }
298 		if (c == 'e' || c == 'E') { goto num_exp_sign; }
299 		p--;
300 	}
301 	goto done_valid;
302 
303 num_frac_first_digit:
304 	if (spdk_likely(p != buf_end)) {
305 		c = *p++;
306 		if (c >= '0' && c <= '9') { goto num_frac_digits; }
307 		p--;
308 	}
309 	goto done_invalid;
310 
311 num_frac_digits:
312 	if (spdk_likely(p != buf_end)) {
313 		c = *p++;
314 		if (c >= '0' && c <= '9') { goto num_frac_digits; }
315 		if (c == 'e' || c == 'E') { goto num_exp_sign; }
316 		p--;
317 	}
318 	goto done_valid;
319 
320 num_exp_sign:
321 	if (spdk_likely(p != buf_end)) {
322 		c = *p++;
323 		if (c >= '0' && c <= '9') { goto num_exp_digits; }
324 		if (c == '-' || c == '+') { goto num_exp_first_digit; }
325 		p--;
326 	}
327 	goto done_invalid;
328 
329 num_exp_first_digit:
330 	if (spdk_likely(p != buf_end)) {
331 		c = *p++;
332 		if (c >= '0' && c <= '9') { goto num_exp_digits; }
333 		p--;
334 	}
335 	goto done_invalid;
336 
337 num_exp_digits:
338 	if (spdk_likely(p != buf_end)) {
339 		c = *p++;
340 		if (c >= '0' && c <= '9') { goto num_exp_digits; }
341 		p--;
342 	}
343 	goto done_valid;
344 
345 done_valid:
346 	/* Valid end state */
347 	return p - start;
348 
349 done_invalid:
350 	/* Invalid end state */
351 	if (p == buf_end) {
352 		/* Hit the end of the buffer - the stream is incomplete. */
353 		return SPDK_JSON_PARSE_INCOMPLETE;
354 	}
355 
356 	/* Found an invalid character in an invalid end state */
357 	return SPDK_JSON_PARSE_INVALID;
358 }
359 
360 static int
361 json_valid_comment(const uint8_t *start, const uint8_t *buf_end)
362 {
363 	const uint8_t *p = start;
364 	bool multiline;
365 
366 	assert(buf_end > p);
367 	if (buf_end - p < 2) {
368 		return SPDK_JSON_PARSE_INCOMPLETE;
369 	}
370 
371 	if (p[0] != '/') {
372 		return SPDK_JSON_PARSE_INVALID;
373 	}
374 	if (p[1] == '*') {
375 		multiline = true;
376 	} else if (p[1] == '/') {
377 		multiline = false;
378 	} else {
379 		return SPDK_JSON_PARSE_INVALID;
380 	}
381 	p += 2;
382 
383 	if (multiline) {
384 		while (p != buf_end - 1) {
385 			if (p[0] == '*' && p[1] == '/') {
386 				/* Include the terminating star and slash in the comment */
387 				return p - start + 2;
388 			}
389 			p++;
390 		}
391 	} else {
392 		while (p != buf_end) {
393 			if (*p == '\r' || *p == '\n') {
394 				/* Do not include the line terminator in the comment */
395 				return p - start;
396 			}
397 			p++;
398 		}
399 	}
400 
401 	return SPDK_JSON_PARSE_INCOMPLETE;
402 }
403 
404 struct json_literal {
405 	enum spdk_json_val_type type;
406 	uint32_t len;
407 	uint8_t str[8];
408 };
409 
410 /*
411  * JSON only defines 3 possible literals; they can be uniquely identified by bits
412  *  3 and 4 of the first character:
413  *   'f' = 0b11[00]110
414  *   'n' = 0b11[01]110
415  *   't' = 0b11[10]100
416  * These two bits can be used as an index into the g_json_literals array.
417  */
418 static const struct json_literal g_json_literals[] = {
419 	{SPDK_JSON_VAL_FALSE, 5, "false"},
420 	{SPDK_JSON_VAL_NULL,  4, "null"},
421 	{SPDK_JSON_VAL_TRUE,  4, "true"},
422 	{}
423 };
424 
425 static int
426 match_literal(const uint8_t *start, const uint8_t *end, const uint8_t *literal, size_t len)
427 {
428 	assert(end >= start);
429 	if ((size_t)(end - start) < len) {
430 		return SPDK_JSON_PARSE_INCOMPLETE;
431 	}
432 
433 	if (memcmp(start, literal, len) != 0) {
434 		return SPDK_JSON_PARSE_INVALID;
435 	}
436 
437 	return len;
438 }
439 
440 ssize_t
441 spdk_json_parse(void *json, size_t size, struct spdk_json_val *values, size_t num_values,
442 		void **end, uint32_t flags)
443 {
444 	uint8_t *json_end = json + size;
445 	enum spdk_json_val_type containers[SPDK_JSON_MAX_NESTING_DEPTH];
446 	size_t con_value[SPDK_JSON_MAX_NESTING_DEPTH];
447 	enum spdk_json_val_type con_type = SPDK_JSON_VAL_INVALID;
448 	bool trailing_comma = false;
449 	size_t depth = 0; /* index into containers */
450 	size_t cur_value = 0; /* index into values */
451 	size_t con_start_value;
452 	uint8_t *data = json;
453 	uint8_t *new_data;
454 	int rc = 0;
455 	const struct json_literal *lit;
456 	enum {
457 		STATE_VALUE, /* initial state */
458 		STATE_VALUE_SEPARATOR, /* value separator (comma) */
459 		STATE_NAME, /* "name": value */
460 		STATE_NAME_SEPARATOR, /* colon */
461 		STATE_END, /* parsed the complete value, so only whitespace is valid */
462 	} state = STATE_VALUE;
463 
464 #define ADD_VALUE(t, val_start_ptr, val_end_ptr) \
465 	if (values && cur_value < num_values) { \
466 		values[cur_value].type = t; \
467 		values[cur_value].start = val_start_ptr; \
468 		values[cur_value].len = val_end_ptr - val_start_ptr; \
469 	} \
470 	cur_value++
471 
472 	while (data < json_end) {
473 		uint8_t c = *data;
474 
475 		switch (c) {
476 		case ' ':
477 		case '\t':
478 		case '\r':
479 		case '\n':
480 			/* Whitespace is allowed between any tokens. */
481 			data++;
482 			break;
483 
484 		case 't':
485 		case 'f':
486 		case 'n':
487 			/* true, false, or null */
488 			if (state != STATE_VALUE) { goto done_invalid; }
489 			lit = &g_json_literals[(c >> 3) & 3]; /* See comment above g_json_literals[] */
490 			assert(lit->str[0] == c);
491 			rc = match_literal(data, json_end, lit->str, lit->len);
492 			if (rc < 0) { goto done_rc; }
493 			ADD_VALUE(lit->type, data, data + rc);
494 			data += rc;
495 			state = depth ? STATE_VALUE_SEPARATOR : STATE_END;
496 			trailing_comma = false;
497 			break;
498 
499 		case '"':
500 			if (state != STATE_VALUE && state != STATE_NAME) { goto done_invalid; }
501 			rc = json_decode_string(data, json_end, &new_data, flags);
502 			if (rc < 0) {
503 				data = new_data;
504 				goto done_rc;
505 			}
506 			/*
507 			 * Start is data + 1 to skip initial quote.
508 			 * Length is data + rc - 1 to skip both quotes.
509 			 */
510 			ADD_VALUE(state == STATE_VALUE ? SPDK_JSON_VAL_STRING : SPDK_JSON_VAL_NAME,
511 				  data + 1, data + rc - 1);
512 			data = new_data;
513 			if (state == STATE_NAME) {
514 				state = STATE_NAME_SEPARATOR;
515 			} else {
516 				state = depth ? STATE_VALUE_SEPARATOR : STATE_END;
517 			}
518 			trailing_comma = false;
519 			break;
520 
521 		case '-':
522 		case '0':
523 		case '1':
524 		case '2':
525 		case '3':
526 		case '4':
527 		case '5':
528 		case '6':
529 		case '7':
530 		case '8':
531 		case '9':
532 			if (state != STATE_VALUE) { goto done_invalid; }
533 			rc = json_valid_number(data, json_end);
534 			if (rc < 0) { goto done_rc; }
535 			ADD_VALUE(SPDK_JSON_VAL_NUMBER, data, data + rc);
536 			data += rc;
537 			state = depth ? STATE_VALUE_SEPARATOR : STATE_END;
538 			trailing_comma = false;
539 			break;
540 
541 		case '{':
542 		case '[':
543 			if (state != STATE_VALUE) { goto done_invalid; }
544 			if (depth == SPDK_JSON_MAX_NESTING_DEPTH) {
545 				rc = SPDK_JSON_PARSE_MAX_DEPTH_EXCEEDED;
546 				goto done_rc;
547 			}
548 			if (c == '{') {
549 				con_type = SPDK_JSON_VAL_OBJECT_BEGIN;
550 				state = STATE_NAME;
551 			} else {
552 				con_type = SPDK_JSON_VAL_ARRAY_BEGIN;
553 				state = STATE_VALUE;
554 			}
555 			con_value[depth] = cur_value;
556 			containers[depth++] = con_type;
557 			ADD_VALUE(con_type, data, data + 1);
558 			data++;
559 			trailing_comma = false;
560 			break;
561 
562 		case '}':
563 		case ']':
564 			if (trailing_comma) { goto done_invalid; }
565 			if (depth == 0) { goto done_invalid; }
566 			con_type = containers[--depth];
567 			con_start_value = con_value[depth];
568 			if (values && con_start_value < num_values) {
569 				values[con_start_value].len = cur_value - con_start_value - 1;
570 			}
571 			if (c == '}') {
572 				if (state != STATE_NAME && state != STATE_VALUE_SEPARATOR) {
573 					goto done_invalid;
574 				}
575 				if (con_type != SPDK_JSON_VAL_OBJECT_BEGIN) {
576 					goto done_invalid;
577 				}
578 				ADD_VALUE(SPDK_JSON_VAL_OBJECT_END, data, data + 1);
579 			} else {
580 				if (state != STATE_VALUE && state != STATE_VALUE_SEPARATOR) {
581 					goto done_invalid;
582 				}
583 				if (con_type != SPDK_JSON_VAL_ARRAY_BEGIN) {
584 					goto done_invalid;
585 				}
586 				ADD_VALUE(SPDK_JSON_VAL_ARRAY_END, data, data + 1);
587 			}
588 			con_type = depth == 0 ? SPDK_JSON_VAL_INVALID : containers[depth - 1];
589 			data++;
590 			state = depth ? STATE_VALUE_SEPARATOR : STATE_END;
591 			trailing_comma = false;
592 			break;
593 
594 		case ',':
595 			if (state != STATE_VALUE_SEPARATOR) { goto done_invalid; }
596 			data++;
597 			assert(con_type == SPDK_JSON_VAL_ARRAY_BEGIN ||
598 			       con_type == SPDK_JSON_VAL_OBJECT_BEGIN);
599 			state = con_type == SPDK_JSON_VAL_ARRAY_BEGIN ? STATE_VALUE : STATE_NAME;
600 			trailing_comma = true;
601 			break;
602 
603 		case ':':
604 			if (state != STATE_NAME_SEPARATOR) { goto done_invalid; }
605 			data++;
606 			state = STATE_VALUE;
607 			break;
608 
609 		case '/':
610 			if (!(flags & SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS)) {
611 				goto done_invalid;
612 			}
613 			rc = json_valid_comment(data, json_end);
614 			if (rc < 0) { goto done_rc; }
615 			/* Skip over comment */
616 			data += rc;
617 			break;
618 
619 		default:
620 			goto done_invalid;
621 		}
622 
623 		if (state == STATE_END) {
624 			break;
625 		}
626 	}
627 
628 	if (state == STATE_END) {
629 		/* Skip trailing whitespace */
630 		while (data < json_end) {
631 			uint8_t c = *data;
632 
633 			if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
634 				data++;
635 			} else {
636 				break;
637 			}
638 		}
639 
640 		/*
641 		 * These asserts are just for sanity checking - they are guaranteed by the allowed
642 		 *  state transitions.
643 		 */
644 		assert(depth == 0);
645 		assert(trailing_comma == false);
646 		assert(data <= json_end);
647 		if (end) {
648 			*end = data;
649 		}
650 		return cur_value;
651 	}
652 
653 	/* Invalid end state - ran out of data */
654 	rc = SPDK_JSON_PARSE_INCOMPLETE;
655 
656 done_rc:
657 	assert(rc < 0);
658 	if (end) {
659 		*end = data;
660 	}
661 	return rc;
662 
663 done_invalid:
664 	rc = SPDK_JSON_PARSE_INVALID;
665 	goto done_rc;
666 }
667