| 48 | #define JSEL_TYPECHECK 3 /* ":" */ |
| 49 | #define JSEL_MAX_TOKEN 256 |
| 50 | cJSON *cJSON_Select(cJSON *o, const char *fmt, ...) { |
| 51 | int next = JSEL_INVALID; /* Type of the next selector. */ |
| 52 | char token[JSEL_MAX_TOKEN+1]; /* Current token. */ |
| 53 | int tlen; /* Current length of the token. */ |
| 54 | va_list ap; |
| 55 | |
| 56 | va_start(ap,fmt); |
| 57 | const char *p = fmt; |
| 58 | tlen = 0; |
| 59 | while(1) { |
| 60 | /* Our four special chars (plus the end of the string) signal the |
| 61 | * end of the previous token and the start of the next one. */ |
| 62 | if (tlen && (*p == '\0' || strchr(".[]:",*p))) { |
| 63 | token[tlen] = '\0'; |
| 64 | if (next == JSEL_INVALID) { |
| 65 | goto notfound; |
| 66 | } else if (next == JSEL_ARRAY) { |
| 67 | if (!cJSON_IsArray(o)) goto notfound; |
| 68 | int idx = atoi(token); /* cJSON API index is int. */ |
| 69 | if ((o = cJSON_GetArrayItem(o,idx)) == NULL) |
| 70 | goto notfound; |
| 71 | } else if (next == JSEL_OBJ) { |
| 72 | if (!cJSON_IsObject(o)) goto notfound; |
| 73 | if ((o = cJSON_GetObjectItemCaseSensitive(o,token)) == NULL) |
| 74 | goto notfound; |
| 75 | } else if (next == JSEL_TYPECHECK) { |
| 76 | if (token[0] == 's' && !cJSON_IsString(o)) goto notfound; |
| 77 | if (token[0] == 'n' && !cJSON_IsNumber(o)) goto notfound; |
| 78 | if (token[0] == 'o' && !cJSON_IsObject(o)) goto notfound; |
| 79 | if (token[0] == 'a' && !cJSON_IsArray(o)) goto notfound; |
| 80 | if (token[0] == 'b' && !cJSON_IsBool(o)) goto notfound; |
| 81 | if (token[0] == '!' && !cJSON_IsNull(o)) goto notfound; |
| 82 | } |
| 83 | } else if (next != JSEL_INVALID) { |
| 84 | /* Otherwise accumulate characters in the current token, note that |
| 85 | * the above check for JSEL_NEXT_INVALID prevents us from |
| 86 | * accumulating at the start of the fmt string if no token was |
| 87 | * yet selected. */ |
| 88 | if (*p != '*') { |
| 89 | token[tlen] = *p++; |
| 90 | tlen++; |
| 91 | if (tlen > JSEL_MAX_TOKEN) goto notfound; |
| 92 | continue; |
| 93 | } else { |
| 94 | /* The "*" character is special: if we are in the context |
| 95 | * of an array, we read an integer from the variable argument |
| 96 | * list, then concatenate it to the current string. |
| 97 | * |
| 98 | * If the context is an object, we read a string pointer |
| 99 | * from the variable argument string and concatenate the |
| 100 | * string to the current token. */ |
| 101 | int len; |
| 102 | char buf[64]; |
| 103 | char *s; |
| 104 | if (next == JSEL_ARRAY) { |
| 105 | int idx = va_arg(ap,int); |
| 106 | len = snprintf(buf,sizeof(buf),"%d",idx); |
| 107 | s = buf; |
no test coverage detected