148c779cdSXin LI /*- 248c779cdSXin LI * Copyright (c) 2018 Christos Zoulas 348c779cdSXin LI * All rights reserved. 448c779cdSXin LI * 548c779cdSXin LI * Redistribution and use in source and binary forms, with or without 648c779cdSXin LI * modification, are permitted provided that the following conditions 748c779cdSXin LI * are met: 848c779cdSXin LI * 1. Redistributions of source code must retain the above copyright 948c779cdSXin LI * notice, this list of conditions and the following disclaimer. 1048c779cdSXin LI * 2. Redistributions in binary form must reproduce the above copyright 1148c779cdSXin LI * notice, this list of conditions and the following disclaimer in the 1248c779cdSXin LI * documentation and/or other materials provided with the distribution. 1348c779cdSXin LI * 1448c779cdSXin LI * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 1548c779cdSXin LI * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 1648c779cdSXin LI * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 1748c779cdSXin LI * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 1848c779cdSXin LI * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1948c779cdSXin LI * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2048c779cdSXin LI * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2148c779cdSXin LI * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2248c779cdSXin LI * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2348c779cdSXin LI * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2448c779cdSXin LI * POSSIBILITY OF SUCH DAMAGE. 2548c779cdSXin LI */ 2648c779cdSXin LI 2748c779cdSXin LI /* 2848c779cdSXin LI * Parse JSON object serialization format (RFC-7159) 2948c779cdSXin LI */ 3048c779cdSXin LI 3148c779cdSXin LI #ifndef TEST 3248c779cdSXin LI #include "file.h" 3348c779cdSXin LI 3448c779cdSXin LI #ifndef lint 35*2726a701SXin LI FILE_RCSID("@(#)$File: is_json.c,v 1.15 2020/06/07 19:05:47 christos Exp $") 3648c779cdSXin LI #endif 3748c779cdSXin LI 3848c779cdSXin LI #include <string.h> 3948c779cdSXin LI #include "magic.h" 4048c779cdSXin LI #endif 4148c779cdSXin LI 4248c779cdSXin LI #ifdef DEBUG 4348c779cdSXin LI #include <stdio.h> 4448c779cdSXin LI #define DPRINTF(a, b, c) \ 4548c779cdSXin LI printf("%s [%.2x/%c] %.20s\n", (a), *(b), *(b), (const char *)(c)) 4648c779cdSXin LI #else 4748c779cdSXin LI #define DPRINTF(a, b, c) do { } while (/*CONSTCOND*/0) 4848c779cdSXin LI #endif 4948c779cdSXin LI 5048c779cdSXin LI #define JSON_ARRAY 0 5148c779cdSXin LI #define JSON_CONSTANT 1 5248c779cdSXin LI #define JSON_NUMBER 2 5348c779cdSXin LI #define JSON_OBJECT 3 5448c779cdSXin LI #define JSON_STRING 4 5548c779cdSXin LI #define JSON_ARRAYN 5 5648c779cdSXin LI #define JSON_MAX 6 5748c779cdSXin LI 5848c779cdSXin LI /* 5948c779cdSXin LI * if JSON_COUNT != 0: 6048c779cdSXin LI * count all the objects, require that we have the whole data file 6148c779cdSXin LI * otherwise: 6248c779cdSXin LI * stop if we find an object or an array 6348c779cdSXin LI */ 6448c779cdSXin LI #ifndef JSON_COUNT 6548c779cdSXin LI #define JSON_COUNT 0 6648c779cdSXin LI #endif 6748c779cdSXin LI 6848c779cdSXin LI static int json_parse(const unsigned char **, const unsigned char *, size_t *, 6948c779cdSXin LI size_t); 7048c779cdSXin LI 7148c779cdSXin LI static int 7248c779cdSXin LI json_isspace(const unsigned char uc) 7348c779cdSXin LI { 7448c779cdSXin LI switch (uc) { 7548c779cdSXin LI case ' ': 7648c779cdSXin LI case '\n': 7748c779cdSXin LI case '\r': 7848c779cdSXin LI case '\t': 7948c779cdSXin LI return 1; 8048c779cdSXin LI default: 8148c779cdSXin LI return 0; 8248c779cdSXin LI } 8348c779cdSXin LI } 8448c779cdSXin LI 8548c779cdSXin LI static int 8648c779cdSXin LI json_isdigit(unsigned char uc) 8748c779cdSXin LI { 8848c779cdSXin LI switch (uc) { 8948c779cdSXin LI case '0': case '1': case '2': case '3': case '4': 9048c779cdSXin LI case '5': case '6': case '7': case '8': case '9': 9148c779cdSXin LI return 1; 9248c779cdSXin LI default: 9348c779cdSXin LI return 0; 9448c779cdSXin LI } 9548c779cdSXin LI } 9648c779cdSXin LI 9748c779cdSXin LI static int 9848c779cdSXin LI json_isxdigit(unsigned char uc) 9948c779cdSXin LI { 10048c779cdSXin LI if (json_isdigit(uc)) 10148c779cdSXin LI return 1; 10248c779cdSXin LI switch (uc) { 10348c779cdSXin LI case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 10448c779cdSXin LI case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 10548c779cdSXin LI return 1; 10648c779cdSXin LI default: 10748c779cdSXin LI return 0; 10848c779cdSXin LI } 10948c779cdSXin LI } 11048c779cdSXin LI 11148c779cdSXin LI static const unsigned char * 11248c779cdSXin LI json_skip_space(const unsigned char *uc, const unsigned char *ue) 11348c779cdSXin LI { 11448c779cdSXin LI while (uc < ue && json_isspace(*uc)) 11548c779cdSXin LI uc++; 11648c779cdSXin LI return uc; 11748c779cdSXin LI } 11848c779cdSXin LI 11948c779cdSXin LI static int 12048c779cdSXin LI json_parse_string(const unsigned char **ucp, const unsigned char *ue) 12148c779cdSXin LI { 12248c779cdSXin LI const unsigned char *uc = *ucp; 12348c779cdSXin LI size_t i; 12448c779cdSXin LI 12548c779cdSXin LI DPRINTF("Parse string: ", uc, *ucp); 12648c779cdSXin LI while (uc < ue) { 12748c779cdSXin LI switch (*uc++) { 12848c779cdSXin LI case '\0': 12948c779cdSXin LI goto out; 13048c779cdSXin LI case '\\': 13148c779cdSXin LI if (uc == ue) 13248c779cdSXin LI goto out; 13348c779cdSXin LI switch (*uc++) { 13448c779cdSXin LI case '\0': 13548c779cdSXin LI goto out; 13648c779cdSXin LI case '"': 13748c779cdSXin LI case '\\': 13848c779cdSXin LI case '/': 13948c779cdSXin LI case 'b': 14048c779cdSXin LI case 'f': 14148c779cdSXin LI case 'n': 14248c779cdSXin LI case 'r': 14348c779cdSXin LI case 't': 14448c779cdSXin LI continue; 14548c779cdSXin LI case 'u': 14648c779cdSXin LI if (ue - uc < 4) { 14748c779cdSXin LI uc = ue; 14848c779cdSXin LI goto out; 14948c779cdSXin LI } 15048c779cdSXin LI for (i = 0; i < 4; i++) 15148c779cdSXin LI if (!json_isxdigit(*uc++)) 15248c779cdSXin LI goto out; 15348c779cdSXin LI continue; 15448c779cdSXin LI default: 15548c779cdSXin LI goto out; 15648c779cdSXin LI } 15748c779cdSXin LI case '"': 15848c779cdSXin LI *ucp = uc; 159*2726a701SXin LI DPRINTF("Good string: ", uc, *ucp); 16048c779cdSXin LI return 1; 16148c779cdSXin LI default: 16248c779cdSXin LI continue; 16348c779cdSXin LI } 16448c779cdSXin LI } 16548c779cdSXin LI out: 16648c779cdSXin LI DPRINTF("Bad string: ", uc, *ucp); 16748c779cdSXin LI *ucp = uc; 16848c779cdSXin LI return 0; 16948c779cdSXin LI } 17048c779cdSXin LI 17148c779cdSXin LI static int 17248c779cdSXin LI json_parse_array(const unsigned char **ucp, const unsigned char *ue, 17348c779cdSXin LI size_t *st, size_t lvl) 17448c779cdSXin LI { 17548c779cdSXin LI const unsigned char *uc = *ucp; 17648c779cdSXin LI 17748c779cdSXin LI DPRINTF("Parse array: ", uc, *ucp); 17848c779cdSXin LI while (uc < ue) { 179*2726a701SXin LI if (*uc == ']') 180*2726a701SXin LI goto done; 18148c779cdSXin LI if (!json_parse(&uc, ue, st, lvl + 1)) 18248c779cdSXin LI goto out; 18348c779cdSXin LI if (uc == ue) 18448c779cdSXin LI goto out; 18548c779cdSXin LI switch (*uc) { 18648c779cdSXin LI case ',': 18748c779cdSXin LI uc++; 18848c779cdSXin LI continue; 18948c779cdSXin LI case ']': 190*2726a701SXin LI done: 19148c779cdSXin LI st[JSON_ARRAYN]++; 19248c779cdSXin LI *ucp = uc + 1; 193*2726a701SXin LI DPRINTF("Good array: ", uc, *ucp); 19448c779cdSXin LI return 1; 19548c779cdSXin LI default: 19648c779cdSXin LI goto out; 19748c779cdSXin LI } 19848c779cdSXin LI } 19948c779cdSXin LI out: 20048c779cdSXin LI DPRINTF("Bad array: ", uc, *ucp); 20148c779cdSXin LI *ucp = uc; 20248c779cdSXin LI return 0; 20348c779cdSXin LI } 20448c779cdSXin LI 20548c779cdSXin LI static int 20648c779cdSXin LI json_parse_object(const unsigned char **ucp, const unsigned char *ue, 20748c779cdSXin LI size_t *st, size_t lvl) 20848c779cdSXin LI { 20948c779cdSXin LI const unsigned char *uc = *ucp; 21048c779cdSXin LI DPRINTF("Parse object: ", uc, *ucp); 21148c779cdSXin LI while (uc < ue) { 21248c779cdSXin LI uc = json_skip_space(uc, ue); 21348c779cdSXin LI if (uc == ue) 21448c779cdSXin LI goto out; 215*2726a701SXin LI if (*uc == '}') { 216*2726a701SXin LI uc++; 217*2726a701SXin LI goto done; 218*2726a701SXin LI } 21948c779cdSXin LI if (*uc++ != '"') { 22048c779cdSXin LI DPRINTF("not string", uc, *ucp); 22148c779cdSXin LI goto out; 22248c779cdSXin LI } 22348c779cdSXin LI DPRINTF("next field", uc, *ucp); 22448c779cdSXin LI if (!json_parse_string(&uc, ue)) { 22548c779cdSXin LI DPRINTF("not string", uc, *ucp); 22648c779cdSXin LI goto out; 22748c779cdSXin LI } 22848c779cdSXin LI uc = json_skip_space(uc, ue); 22948c779cdSXin LI if (uc == ue) 23048c779cdSXin LI goto out; 23148c779cdSXin LI if (*uc++ != ':') { 23248c779cdSXin LI DPRINTF("not colon", uc, *ucp); 23348c779cdSXin LI goto out; 23448c779cdSXin LI } 23548c779cdSXin LI if (!json_parse(&uc, ue, st, lvl + 1)) { 23648c779cdSXin LI DPRINTF("not json", uc, *ucp); 23748c779cdSXin LI goto out; 23848c779cdSXin LI } 23948c779cdSXin LI if (uc == ue) 24048c779cdSXin LI goto out; 24148c779cdSXin LI switch (*uc++) { 24248c779cdSXin LI case ',': 24348c779cdSXin LI continue; 24448c779cdSXin LI case '}': /* { */ 245*2726a701SXin LI done: 24648c779cdSXin LI *ucp = uc; 24748c779cdSXin LI DPRINTF("Good object: ", uc, *ucp); 24848c779cdSXin LI return 1; 24948c779cdSXin LI default: 25048c779cdSXin LI *ucp = uc - 1; 25148c779cdSXin LI DPRINTF("not more", uc, *ucp); 25248c779cdSXin LI goto out; 25348c779cdSXin LI } 25448c779cdSXin LI } 25548c779cdSXin LI out: 25648c779cdSXin LI DPRINTF("Bad object: ", uc, *ucp); 25748c779cdSXin LI *ucp = uc; 25848c779cdSXin LI return 0; 25948c779cdSXin LI } 26048c779cdSXin LI 26148c779cdSXin LI static int 26248c779cdSXin LI json_parse_number(const unsigned char **ucp, const unsigned char *ue) 26348c779cdSXin LI { 26448c779cdSXin LI const unsigned char *uc = *ucp; 26548c779cdSXin LI int got = 0; 26648c779cdSXin LI 26748c779cdSXin LI DPRINTF("Parse number: ", uc, *ucp); 26848c779cdSXin LI if (uc == ue) 26948c779cdSXin LI return 0; 27048c779cdSXin LI if (*uc == '-') 27148c779cdSXin LI uc++; 27248c779cdSXin LI 27348c779cdSXin LI for (; uc < ue; uc++) { 27448c779cdSXin LI if (!json_isdigit(*uc)) 27548c779cdSXin LI break; 27648c779cdSXin LI got = 1; 27748c779cdSXin LI } 27848c779cdSXin LI if (uc == ue) 27948c779cdSXin LI goto out; 28048c779cdSXin LI if (*uc == '.') 28148c779cdSXin LI uc++; 28248c779cdSXin LI for (; uc < ue; uc++) { 28348c779cdSXin LI if (!json_isdigit(*uc)) 28448c779cdSXin LI break; 28548c779cdSXin LI got = 1; 28648c779cdSXin LI } 28748c779cdSXin LI if (uc == ue) 28848c779cdSXin LI goto out; 28948c779cdSXin LI if (got && (*uc == 'e' || *uc == 'E')) { 29048c779cdSXin LI uc++; 29148c779cdSXin LI got = 0; 29248c779cdSXin LI if (uc == ue) 29348c779cdSXin LI goto out; 29448c779cdSXin LI if (*uc == '+' || *uc == '-') 29548c779cdSXin LI uc++; 29648c779cdSXin LI for (; uc < ue; uc++) { 29748c779cdSXin LI if (!json_isdigit(*uc)) 29848c779cdSXin LI break; 29948c779cdSXin LI got = 1; 30048c779cdSXin LI } 30148c779cdSXin LI } 30248c779cdSXin LI out: 30348c779cdSXin LI if (!got) 30448c779cdSXin LI DPRINTF("Bad number: ", uc, *ucp); 30548c779cdSXin LI else 30648c779cdSXin LI DPRINTF("Good number: ", uc, *ucp); 30748c779cdSXin LI *ucp = uc; 30848c779cdSXin LI return got; 30948c779cdSXin LI } 31048c779cdSXin LI 31148c779cdSXin LI static int 31248c779cdSXin LI json_parse_const(const unsigned char **ucp, const unsigned char *ue, 31348c779cdSXin LI const char *str, size_t len) 31448c779cdSXin LI { 31548c779cdSXin LI const unsigned char *uc = *ucp; 31648c779cdSXin LI 31748c779cdSXin LI DPRINTF("Parse const: ", uc, *ucp); 31848c779cdSXin LI for (len--; uc < ue && --len;) { 31948c779cdSXin LI if (*uc++ == *++str) 32048c779cdSXin LI continue; 32148c779cdSXin LI } 32248c779cdSXin LI if (len) 32348c779cdSXin LI DPRINTF("Bad const: ", uc, *ucp); 32448c779cdSXin LI *ucp = uc; 32548c779cdSXin LI return len == 0; 32648c779cdSXin LI } 32748c779cdSXin LI 32848c779cdSXin LI static int 32948c779cdSXin LI json_parse(const unsigned char **ucp, const unsigned char *ue, 33048c779cdSXin LI size_t *st, size_t lvl) 33148c779cdSXin LI { 33248c779cdSXin LI const unsigned char *uc; 33348c779cdSXin LI int rv = 0; 33448c779cdSXin LI int t; 33548c779cdSXin LI 33648c779cdSXin LI uc = json_skip_space(*ucp, ue); 33748c779cdSXin LI if (uc == ue) 33848c779cdSXin LI goto out; 33948c779cdSXin LI 34048c779cdSXin LI // Avoid recursion 34148c779cdSXin LI if (lvl > 20) 34248c779cdSXin LI return 0; 34348c779cdSXin LI #if JSON_COUNT 34448c779cdSXin LI /* bail quickly if not counting */ 34548c779cdSXin LI if (lvl > 1 && (st[JSON_OBJECT] || st[JSON_ARRAYN])) 34648c779cdSXin LI return 1; 34748c779cdSXin LI #endif 34848c779cdSXin LI 34948c779cdSXin LI DPRINTF("Parse general: ", uc, *ucp); 35048c779cdSXin LI switch (*uc++) { 35148c779cdSXin LI case '"': 35248c779cdSXin LI rv = json_parse_string(&uc, ue); 35348c779cdSXin LI t = JSON_STRING; 35448c779cdSXin LI break; 35548c779cdSXin LI case '[': 35648c779cdSXin LI rv = json_parse_array(&uc, ue, st, lvl + 1); 35748c779cdSXin LI t = JSON_ARRAY; 35848c779cdSXin LI break; 35948c779cdSXin LI case '{': /* '}' */ 36048c779cdSXin LI rv = json_parse_object(&uc, ue, st, lvl + 1); 36148c779cdSXin LI t = JSON_OBJECT; 36248c779cdSXin LI break; 36348c779cdSXin LI case 't': 36448c779cdSXin LI rv = json_parse_const(&uc, ue, "true", sizeof("true")); 36548c779cdSXin LI t = JSON_CONSTANT; 36648c779cdSXin LI break; 36748c779cdSXin LI case 'f': 36848c779cdSXin LI rv = json_parse_const(&uc, ue, "false", sizeof("false")); 36948c779cdSXin LI t = JSON_CONSTANT; 37048c779cdSXin LI break; 37148c779cdSXin LI case 'n': 37248c779cdSXin LI rv = json_parse_const(&uc, ue, "null", sizeof("null")); 37348c779cdSXin LI t = JSON_CONSTANT; 37448c779cdSXin LI break; 37548c779cdSXin LI default: 37648c779cdSXin LI --uc; 37748c779cdSXin LI rv = json_parse_number(&uc, ue); 37848c779cdSXin LI t = JSON_NUMBER; 37948c779cdSXin LI break; 38048c779cdSXin LI } 38148c779cdSXin LI if (rv) 38248c779cdSXin LI st[t]++; 38348c779cdSXin LI uc = json_skip_space(uc, ue); 38448c779cdSXin LI out: 38548c779cdSXin LI *ucp = uc; 38648c779cdSXin LI DPRINTF("End general: ", uc, *ucp); 38748c779cdSXin LI if (lvl == 0) 38848c779cdSXin LI return rv && (st[JSON_ARRAYN] || st[JSON_OBJECT]); 38948c779cdSXin LI return rv; 39048c779cdSXin LI } 39148c779cdSXin LI 39248c779cdSXin LI #ifndef TEST 39348c779cdSXin LI int 39448c779cdSXin LI file_is_json(struct magic_set *ms, const struct buffer *b) 39548c779cdSXin LI { 39648c779cdSXin LI const unsigned char *uc = CAST(const unsigned char *, b->fbuf); 39748c779cdSXin LI const unsigned char *ue = uc + b->flen; 39848c779cdSXin LI size_t st[JSON_MAX]; 39948c779cdSXin LI int mime = ms->flags & MAGIC_MIME; 40048c779cdSXin LI 40148c779cdSXin LI 40248c779cdSXin LI if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0) 40348c779cdSXin LI return 0; 40448c779cdSXin LI 40548c779cdSXin LI memset(st, 0, sizeof(st)); 40648c779cdSXin LI 40748c779cdSXin LI if (!json_parse(&uc, ue, st, 0)) 40848c779cdSXin LI return 0; 40948c779cdSXin LI 41048c779cdSXin LI if (mime == MAGIC_MIME_ENCODING) 41148c779cdSXin LI return 1; 41248c779cdSXin LI if (mime) { 41348c779cdSXin LI if (file_printf(ms, "application/json") == -1) 41448c779cdSXin LI return -1; 41548c779cdSXin LI return 1; 41648c779cdSXin LI } 41748c779cdSXin LI if (file_printf(ms, "JSON data") == -1) 41848c779cdSXin LI return -1; 41948c779cdSXin LI #if JSON_COUNT 42048c779cdSXin LI #define P(n) st[n], st[n] > 1 ? "s" : "" 42148c779cdSXin LI if (file_printf(ms, " (%" SIZE_T_FORMAT "u object%s, %" SIZE_T_FORMAT 42248c779cdSXin LI "u array%s, %" SIZE_T_FORMAT "u string%s, %" SIZE_T_FORMAT 42348c779cdSXin LI "u constant%s, %" SIZE_T_FORMAT "u number%s, %" SIZE_T_FORMAT 42448c779cdSXin LI "u >1array%s)", 42548c779cdSXin LI P(JSON_OBJECT), P(JSON_ARRAY), P(JSON_STRING), P(JSON_CONSTANT), 42648c779cdSXin LI P(JSON_NUMBER), P(JSON_ARRAYN)) 42748c779cdSXin LI == -1) 42848c779cdSXin LI return -1; 42948c779cdSXin LI #endif 43048c779cdSXin LI return 1; 43148c779cdSXin LI } 43248c779cdSXin LI 43348c779cdSXin LI #else 43448c779cdSXin LI 43548c779cdSXin LI #include <sys/types.h> 43648c779cdSXin LI #include <sys/stat.h> 43748c779cdSXin LI #include <stdio.h> 43848c779cdSXin LI #include <fcntl.h> 43948c779cdSXin LI #include <unistd.h> 44048c779cdSXin LI #include <stdlib.h> 44148c779cdSXin LI #include <stdint.h> 44248c779cdSXin LI #include <err.h> 44348c779cdSXin LI 44448c779cdSXin LI int 44548c779cdSXin LI main(int argc, char *argv[]) 44648c779cdSXin LI { 44748c779cdSXin LI int fd, rv; 44848c779cdSXin LI struct stat st; 44948c779cdSXin LI unsigned char *p; 45048c779cdSXin LI size_t stats[JSON_MAX]; 45148c779cdSXin LI 45248c779cdSXin LI if ((fd = open(argv[1], O_RDONLY)) == -1) 45348c779cdSXin LI err(EXIT_FAILURE, "Can't open `%s'", argv[1]); 45448c779cdSXin LI 45548c779cdSXin LI if (fstat(fd, &st) == -1) 45648c779cdSXin LI err(EXIT_FAILURE, "Can't stat `%s'", argv[1]); 45748c779cdSXin LI 45848c779cdSXin LI if ((p = malloc(st.st_size)) == NULL) 45948c779cdSXin LI err(EXIT_FAILURE, "Can't allocate %jd bytes", 46048c779cdSXin LI (intmax_t)st.st_size); 46148c779cdSXin LI if (read(fd, p, st.st_size) != st.st_size) 46248c779cdSXin LI err(EXIT_FAILURE, "Can't read %jd bytes", 46348c779cdSXin LI (intmax_t)st.st_size); 46448c779cdSXin LI memset(stats, 0, sizeof(stats)); 46548c779cdSXin LI printf("is json %d\n", json_parse((const unsigned char **)&p, 46648c779cdSXin LI p + st.st_size, stats, 0)); 46748c779cdSXin LI return 0; 46848c779cdSXin LI } 46948c779cdSXin LI #endif 470