jsmn.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. /*
  2. * MIT License
  3. *
  4. * Copyright (c) 2010 Serge Zaitsev
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. * SOFTWARE.
  23. */
  24. #ifndef JSMN_H
  25. #define JSMN_H
  26. #include <stddef.h>
  27. #ifdef __cplusplus
  28. extern "C" {
  29. #endif
  30. #ifdef JSMN_STATIC
  31. #define JSMN_API static
  32. #else
  33. #define JSMN_API extern
  34. #endif
  35. /**
  36. * JSON type identifier. Basic types are:
  37. * o Object
  38. * o Array
  39. * o String
  40. * o Other primitive: number, boolean (true/false) or null
  41. */
  42. typedef enum {
  43. JSMN_UNDEFINED = 0,
  44. JSMN_OBJECT = 1 << 0,
  45. JSMN_ARRAY = 1 << 1,
  46. JSMN_STRING = 1 << 2,
  47. JSMN_PRIMITIVE = 1 << 3
  48. } jsmntype_t;
  49. enum jsmnerr {
  50. /* Not enough tokens were provided */
  51. JSMN_ERROR_NOMEM = -1,
  52. /* Invalid character inside JSON string */
  53. JSMN_ERROR_INVAL = -2,
  54. /* The string is not a full JSON packet, more bytes expected */
  55. JSMN_ERROR_PART = -3
  56. };
  57. /**
  58. * JSON token description.
  59. * type type (object, array, string etc.)
  60. * start start position in JSON data string
  61. * end end position in JSON data string
  62. */
  63. typedef struct jsmntok {
  64. jsmntype_t type;
  65. int start;
  66. int end;
  67. int size;
  68. #ifdef JSMN_PARENT_LINKS
  69. int parent;
  70. #endif
  71. } jsmntok_t;
  72. /**
  73. * JSON parser. Contains an array of token blocks available. Also stores
  74. * the string being parsed now and current position in that string.
  75. */
  76. typedef struct jsmn_parser {
  77. unsigned int pos; /* offset in the JSON string */
  78. unsigned int toknext; /* next token to allocate */
  79. int toksuper; /* superior token node, e.g. parent object or array */
  80. } jsmn_parser;
  81. /**
  82. * Create JSON parser over an array of tokens
  83. */
  84. JSMN_API void jsmn_init(jsmn_parser* parser);
  85. /**
  86. * Run JSON parser. It parses a JSON data string into and array of tokens, each
  87. * describing
  88. * a single JSON object.
  89. */
  90. JSMN_API int jsmn_parse(
  91. jsmn_parser* parser,
  92. const char* js,
  93. const size_t len,
  94. jsmntok_t* tokens,
  95. const unsigned int num_tokens);
  96. #ifndef JSMN_HEADER
  97. /**
  98. * Allocates a fresh unused token from the token pool.
  99. */
  100. static jsmntok_t*
  101. jsmn_alloc_token(jsmn_parser* parser, jsmntok_t* tokens, const size_t num_tokens) {
  102. jsmntok_t* tok;
  103. if(parser->toknext >= num_tokens) {
  104. return NULL;
  105. }
  106. tok = &tokens[parser->toknext++];
  107. tok->start = tok->end = -1;
  108. tok->size = 0;
  109. #ifdef JSMN_PARENT_LINKS
  110. tok->parent = -1;
  111. #endif
  112. return tok;
  113. }
  114. /**
  115. * Fills token type and boundaries.
  116. */
  117. static void
  118. jsmn_fill_token(jsmntok_t* token, const jsmntype_t type, const int start, const int end) {
  119. token->type = type;
  120. token->start = start;
  121. token->end = end;
  122. token->size = 0;
  123. }
  124. /**
  125. * Fills next available token with JSON primitive.
  126. */
  127. static int jsmn_parse_primitive(
  128. jsmn_parser* parser,
  129. const char* js,
  130. const size_t len,
  131. jsmntok_t* tokens,
  132. const size_t num_tokens) {
  133. jsmntok_t* token;
  134. int start;
  135. start = parser->pos;
  136. for(; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
  137. switch(js[parser->pos]) {
  138. #ifndef JSMN_STRICT
  139. /* In strict mode primitive must be followed by "," or "}" or "]" */
  140. case ':':
  141. #endif
  142. case '\t':
  143. case '\r':
  144. case '\n':
  145. case ' ':
  146. case ',':
  147. case ']':
  148. case '}':
  149. goto found;
  150. default:
  151. /* to quiet a warning from gcc*/
  152. break;
  153. }
  154. if(js[parser->pos] < 32 || js[parser->pos] >= 127) {
  155. parser->pos = start;
  156. return JSMN_ERROR_INVAL;
  157. }
  158. }
  159. #ifdef JSMN_STRICT
  160. /* In strict mode primitive must be followed by a comma/object/array */
  161. parser->pos = start;
  162. return JSMN_ERROR_PART;
  163. #endif
  164. found:
  165. if(tokens == NULL) {
  166. parser->pos--;
  167. return 0;
  168. }
  169. token = jsmn_alloc_token(parser, tokens, num_tokens);
  170. if(token == NULL) {
  171. parser->pos = start;
  172. return JSMN_ERROR_NOMEM;
  173. }
  174. jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
  175. #ifdef JSMN_PARENT_LINKS
  176. token->parent = parser->toksuper;
  177. #endif
  178. parser->pos--;
  179. return 0;
  180. }
  181. /**
  182. * Fills next token with JSON string.
  183. */
  184. static int jsmn_parse_string(
  185. jsmn_parser* parser,
  186. const char* js,
  187. const size_t len,
  188. jsmntok_t* tokens,
  189. const size_t num_tokens) {
  190. jsmntok_t* token;
  191. int start = parser->pos;
  192. /* Skip starting quote */
  193. parser->pos++;
  194. for(; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
  195. char c = js[parser->pos];
  196. /* Quote: end of string */
  197. if(c == '\"') {
  198. if(tokens == NULL) {
  199. return 0;
  200. }
  201. token = jsmn_alloc_token(parser, tokens, num_tokens);
  202. if(token == NULL) {
  203. parser->pos = start;
  204. return JSMN_ERROR_NOMEM;
  205. }
  206. jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
  207. #ifdef JSMN_PARENT_LINKS
  208. token->parent = parser->toksuper;
  209. #endif
  210. return 0;
  211. }
  212. /* Backslash: Quoted symbol expected */
  213. if(c == '\\' && parser->pos + 1 < len) {
  214. int i;
  215. parser->pos++;
  216. switch(js[parser->pos]) {
  217. /* Allowed escaped symbols */
  218. case '\"':
  219. case '/':
  220. case '\\':
  221. case 'b':
  222. case 'f':
  223. case 'r':
  224. case 'n':
  225. case 't':
  226. break;
  227. /* Allows escaped symbol \uXXXX */
  228. case 'u':
  229. parser->pos++;
  230. for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
  231. /* If it isn't a hex character we have an error */
  232. if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
  233. (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
  234. (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
  235. parser->pos = start;
  236. return JSMN_ERROR_INVAL;
  237. }
  238. parser->pos++;
  239. }
  240. parser->pos--;
  241. break;
  242. /* Unexpected symbol */
  243. default:
  244. parser->pos = start;
  245. return JSMN_ERROR_INVAL;
  246. }
  247. }
  248. }
  249. parser->pos = start;
  250. return JSMN_ERROR_PART;
  251. }
  252. /**
  253. * Parse JSON string and fill tokens.
  254. */
  255. JSMN_API int jsmn_parse(
  256. jsmn_parser* parser,
  257. const char* js,
  258. const size_t len,
  259. jsmntok_t* tokens,
  260. const unsigned int num_tokens) {
  261. int r;
  262. int i;
  263. jsmntok_t* token;
  264. int count = parser->toknext;
  265. for(; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
  266. char c;
  267. jsmntype_t type;
  268. c = js[parser->pos];
  269. switch(c) {
  270. case '{':
  271. case '[':
  272. count++;
  273. if(tokens == NULL) {
  274. break;
  275. }
  276. token = jsmn_alloc_token(parser, tokens, num_tokens);
  277. if(token == NULL) {
  278. return JSMN_ERROR_NOMEM;
  279. }
  280. if(parser->toksuper != -1) {
  281. jsmntok_t* t = &tokens[parser->toksuper];
  282. #ifdef JSMN_STRICT
  283. /* In strict mode an object or array can't become a key */
  284. if(t->type == JSMN_OBJECT) {
  285. return JSMN_ERROR_INVAL;
  286. }
  287. #endif
  288. t->size++;
  289. #ifdef JSMN_PARENT_LINKS
  290. token->parent = parser->toksuper;
  291. #endif
  292. }
  293. token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
  294. token->start = parser->pos;
  295. parser->toksuper = parser->toknext - 1;
  296. break;
  297. case '}':
  298. case ']':
  299. if(tokens == NULL) {
  300. break;
  301. }
  302. type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
  303. #ifdef JSMN_PARENT_LINKS
  304. if(parser->toknext < 1) {
  305. return JSMN_ERROR_INVAL;
  306. }
  307. token = &tokens[parser->toknext - 1];
  308. for(;;) {
  309. if(token->start != -1 && token->end == -1) {
  310. if(token->type != type) {
  311. return JSMN_ERROR_INVAL;
  312. }
  313. token->end = parser->pos + 1;
  314. parser->toksuper = token->parent;
  315. break;
  316. }
  317. if(token->parent == -1) {
  318. if(token->type != type || parser->toksuper == -1) {
  319. return JSMN_ERROR_INVAL;
  320. }
  321. break;
  322. }
  323. token = &tokens[token->parent];
  324. }
  325. #else
  326. for(i = parser->toknext - 1; i >= 0; i--) {
  327. token = &tokens[i];
  328. if(token->start != -1 && token->end == -1) {
  329. if(token->type != type) {
  330. return JSMN_ERROR_INVAL;
  331. }
  332. parser->toksuper = -1;
  333. token->end = parser->pos + 1;
  334. break;
  335. }
  336. }
  337. /* Error if unmatched closing bracket */
  338. if(i == -1) {
  339. return JSMN_ERROR_INVAL;
  340. }
  341. for(; i >= 0; i--) {
  342. token = &tokens[i];
  343. if(token->start != -1 && token->end == -1) {
  344. parser->toksuper = i;
  345. break;
  346. }
  347. }
  348. #endif
  349. break;
  350. case '\"':
  351. r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
  352. if(r < 0) {
  353. return r;
  354. }
  355. count++;
  356. if(parser->toksuper != -1 && tokens != NULL) {
  357. tokens[parser->toksuper].size++;
  358. }
  359. break;
  360. case '\t':
  361. case '\r':
  362. case '\n':
  363. case ' ':
  364. break;
  365. case ':':
  366. parser->toksuper = parser->toknext - 1;
  367. break;
  368. case ',':
  369. if(tokens != NULL && parser->toksuper != -1 &&
  370. tokens[parser->toksuper].type != JSMN_ARRAY &&
  371. tokens[parser->toksuper].type != JSMN_OBJECT) {
  372. #ifdef JSMN_PARENT_LINKS
  373. parser->toksuper = tokens[parser->toksuper].parent;
  374. #else
  375. for(i = parser->toknext - 1; i >= 0; i--) {
  376. if(tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
  377. if(tokens[i].start != -1 && tokens[i].end == -1) {
  378. parser->toksuper = i;
  379. break;
  380. }
  381. }
  382. }
  383. #endif
  384. }
  385. break;
  386. #ifdef JSMN_STRICT
  387. /* In strict mode primitives are: numbers and booleans */
  388. case '-':
  389. case '0':
  390. case '1':
  391. case '2':
  392. case '3':
  393. case '4':
  394. case '5':
  395. case '6':
  396. case '7':
  397. case '8':
  398. case '9':
  399. case 't':
  400. case 'f':
  401. case 'n':
  402. /* And they must not be keys of the object */
  403. if(tokens != NULL && parser->toksuper != -1) {
  404. const jsmntok_t* t = &tokens[parser->toksuper];
  405. if(t->type == JSMN_OBJECT || (t->type == JSMN_STRING && t->size != 0)) {
  406. return JSMN_ERROR_INVAL;
  407. }
  408. }
  409. #else
  410. /* In non-strict mode every unquoted value is a primitive */
  411. default:
  412. #endif
  413. r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
  414. if(r < 0) {
  415. return r;
  416. }
  417. count++;
  418. if(parser->toksuper != -1 && tokens != NULL) {
  419. tokens[parser->toksuper].size++;
  420. }
  421. break;
  422. #ifdef JSMN_STRICT
  423. /* Unexpected char in strict mode */
  424. default:
  425. return JSMN_ERROR_INVAL;
  426. #endif
  427. }
  428. }
  429. if(tokens != NULL) {
  430. for(i = parser->toknext - 1; i >= 0; i--) {
  431. /* Unmatched opened object or array */
  432. if(tokens[i].start != -1 && tokens[i].end == -1) {
  433. return JSMN_ERROR_PART;
  434. }
  435. }
  436. }
  437. return count;
  438. }
  439. /**
  440. * Creates a new parser based over a given buffer with an array of tokens
  441. * available.
  442. */
  443. JSMN_API void jsmn_init(jsmn_parser* parser) {
  444. parser->pos = 0;
  445. parser->toknext = 0;
  446. parser->toksuper = -1;
  447. }
  448. #endif /* JSMN_HEADER */
  449. #ifdef __cplusplus
  450. }
  451. #endif
  452. #endif /* JSMN_H */
  453. #ifndef JB_JSMN_EDIT
  454. #define JB_JSMN_EDIT
  455. /* Added in by JBlanked on 2024-10-16 for use in Flipper Zero SDK*/
  456. #include <string.h>
  457. #include <stdint.h>
  458. #include <stdlib.h>
  459. #include <stdio.h>
  460. #include <furi.h>
  461. // Helper function to compare JSON keys
  462. int jsoneq(const char* json, jsmntok_t* tok, const char* s) {
  463. if(tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start &&
  464. strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
  465. return 0;
  466. }
  467. return -1;
  468. }
  469. // return the value of the key in the JSON data
  470. char* get_json_value(char* key, char* json_data, uint32_t max_tokens) {
  471. // Parse the JSON feed
  472. if(json_data != NULL) {
  473. jsmn_parser parser;
  474. jsmn_init(&parser);
  475. // Allocate tokens array on the heap
  476. jsmntok_t* tokens = malloc(sizeof(jsmntok_t) * max_tokens);
  477. if(tokens == NULL) {
  478. FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
  479. return NULL;
  480. }
  481. int ret = jsmn_parse(&parser, json_data, strlen(json_data), tokens, max_tokens);
  482. if(ret < 0) {
  483. // Handle parsing errors
  484. FURI_LOG_E("JSMM.H", "Failed to parse JSON: %d", ret);
  485. free(tokens);
  486. return NULL;
  487. }
  488. // Ensure that the root element is an object
  489. if(ret < 1 || tokens[0].type != JSMN_OBJECT) {
  490. FURI_LOG_E("JSMM.H", "Root element is not an object.");
  491. free(tokens);
  492. return NULL;
  493. }
  494. // Loop through the tokens to find the key
  495. for(int i = 1; i < ret; i++) {
  496. if(jsoneq(json_data, &tokens[i], key) == 0) {
  497. // We found the key. Now, return the associated value.
  498. int length = tokens[i + 1].end - tokens[i + 1].start;
  499. char* value = malloc(length + 1);
  500. if(value == NULL) {
  501. FURI_LOG_E("JSMM.H", "Failed to allocate memory for value.");
  502. free(tokens);
  503. return NULL;
  504. }
  505. strncpy(value, json_data + tokens[i + 1].start, length);
  506. value[length] = '\0'; // Null-terminate the string
  507. free(tokens); // Free the token array
  508. return value; // Return the extracted value
  509. }
  510. }
  511. // Free the token array if key was not found
  512. free(tokens);
  513. } else {
  514. FURI_LOG_E("JSMM.H", "JSON data is NULL");
  515. }
  516. FURI_LOG_E("JSMM.H", "Failed to find the key in the JSON.");
  517. return NULL; // Return NULL if something goes wrong
  518. }
  519. // Revised get_json_array_value function
  520. char* get_json_array_value(char* key, uint32_t index, char* json_data, uint32_t max_tokens) {
  521. // Retrieve the array string for the given key
  522. char* array_str = get_json_value(key, json_data, max_tokens);
  523. if(array_str == NULL) {
  524. FURI_LOG_E("JSMM.H", "Failed to get array for key: %s", key);
  525. return NULL;
  526. }
  527. // Initialize the JSON parser
  528. jsmn_parser parser;
  529. jsmn_init(&parser);
  530. // Allocate memory for JSON tokens
  531. jsmntok_t* tokens = malloc(sizeof(jsmntok_t) * max_tokens);
  532. if(tokens == NULL) {
  533. FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
  534. free(array_str);
  535. return NULL;
  536. }
  537. // Parse the JSON array
  538. int ret = jsmn_parse(&parser, array_str, strlen(array_str), tokens, max_tokens);
  539. if(ret < 0) {
  540. FURI_LOG_E("JSMM.H", "Failed to parse JSON array: %d", ret);
  541. free(tokens);
  542. free(array_str);
  543. return NULL;
  544. }
  545. // Ensure the root element is an array
  546. if(ret < 1 || tokens[0].type != JSMN_ARRAY) {
  547. FURI_LOG_E("JSMM.H", "Value for key '%s' is not an array.", key);
  548. free(tokens);
  549. free(array_str);
  550. return NULL;
  551. }
  552. // Check if the index is within bounds
  553. if(index >= (uint32_t)tokens[0].size) {
  554. FURI_LOG_E(
  555. "JSMM.H",
  556. "Index %lu out of bounds for array with size %d.",
  557. (unsigned long)index,
  558. tokens[0].size);
  559. free(tokens);
  560. free(array_str);
  561. return NULL;
  562. }
  563. // Locate the token corresponding to the desired array element
  564. int current_token = 1; // Start after the array token
  565. for(uint32_t i = 0; i < index; i++) {
  566. if(tokens[current_token].type == JSMN_OBJECT) {
  567. // For objects, skip all key-value pairs
  568. current_token += 1 + 2 * tokens[current_token].size;
  569. } else if(tokens[current_token].type == JSMN_ARRAY) {
  570. // For nested arrays, skip all elements
  571. current_token += 1 + tokens[current_token].size;
  572. } else {
  573. // For primitive types, simply move to the next token
  574. current_token += 1;
  575. }
  576. // Safety check to prevent out-of-bounds
  577. if(current_token >= ret) {
  578. FURI_LOG_E("JSMM.H", "Unexpected end of tokens while traversing array.");
  579. free(tokens);
  580. free(array_str);
  581. return NULL;
  582. }
  583. }
  584. // Extract the array element
  585. jsmntok_t element = tokens[current_token];
  586. int length = element.end - element.start;
  587. char* value = malloc(length + 1);
  588. if(value == NULL) {
  589. FURI_LOG_E("JSMM.H", "Failed to allocate memory for array element.");
  590. free(tokens);
  591. free(array_str);
  592. return NULL;
  593. }
  594. // Copy the element value to a new string
  595. strncpy(value, array_str + element.start, length);
  596. value[length] = '\0'; // Null-terminate the string
  597. // Clean up
  598. free(tokens);
  599. free(array_str);
  600. return value;
  601. }
  602. // Revised get_json_array_values function with correct token skipping
  603. char** get_json_array_values(char* key, char* json_data, uint32_t max_tokens, int* num_values) {
  604. // Retrieve the array string for the given key
  605. char* array_str = get_json_value(key, json_data, max_tokens);
  606. if(array_str == NULL) {
  607. FURI_LOG_E("JSMM.H", "Failed to get array for key: %s", key);
  608. return NULL;
  609. }
  610. // Initialize the JSON parser
  611. jsmn_parser parser;
  612. jsmn_init(&parser);
  613. // Allocate memory for JSON tokens
  614. jsmntok_t* tokens = malloc(sizeof(jsmntok_t) * max_tokens); // Allocate on the heap
  615. if(tokens == NULL) {
  616. FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
  617. free(array_str);
  618. return NULL;
  619. }
  620. // Parse the JSON array
  621. int ret = jsmn_parse(&parser, array_str, strlen(array_str), tokens, max_tokens);
  622. if(ret < 0) {
  623. FURI_LOG_E("JSMM.H", "Failed to parse JSON array: %d", ret);
  624. free(tokens);
  625. free(array_str);
  626. return NULL;
  627. }
  628. // Ensure the root element is an array
  629. if(tokens[0].type != JSMN_ARRAY) {
  630. FURI_LOG_E("JSMM.H", "Value for key '%s' is not an array.", key);
  631. free(tokens);
  632. free(array_str);
  633. return NULL;
  634. }
  635. // Allocate memory for the array of values (maximum possible)
  636. int array_size = tokens[0].size;
  637. char** values = malloc(array_size * sizeof(char*));
  638. if(values == NULL) {
  639. FURI_LOG_E("JSMM.H", "Failed to allocate memory for array of values.");
  640. free(tokens);
  641. free(array_str);
  642. return NULL;
  643. }
  644. int actual_num_values = 0;
  645. // Traverse the array and extract all object values
  646. int current_token = 1; // Start after the array token
  647. for(int i = 0; i < array_size; i++) {
  648. if(current_token >= ret) {
  649. FURI_LOG_E("JSMM.H", "Unexpected end of tokens while traversing array.");
  650. break;
  651. }
  652. jsmntok_t element = tokens[current_token];
  653. if(element.type != JSMN_OBJECT) {
  654. FURI_LOG_E("JSMM.H", "Array element %d is not an object, skipping.", i);
  655. // Skip this element
  656. current_token += 1;
  657. continue;
  658. }
  659. int length = element.end - element.start;
  660. // Allocate a new string for the value and copy the data
  661. char* value = malloc(length + 1);
  662. if(value == NULL) {
  663. FURI_LOG_E("JSMM.H", "Failed to allocate memory for array element.");
  664. for(int j = 0; j < actual_num_values; j++) {
  665. free(values[j]);
  666. }
  667. free(values);
  668. free(tokens);
  669. free(array_str);
  670. return NULL;
  671. }
  672. strncpy(value, array_str + element.start, length);
  673. value[length] = '\0'; // Null-terminate the string
  674. values[actual_num_values] = value;
  675. actual_num_values++;
  676. // Skip all tokens related to this object to avoid misparsing
  677. current_token += 1 + (2 * element.size); // Each key-value pair consumes two tokens
  678. }
  679. *num_values = actual_num_values;
  680. // Reallocate the values array to actual_num_values if necessary
  681. if(actual_num_values < array_size) {
  682. char** reduced_values = realloc(values, actual_num_values * sizeof(char*));
  683. if(reduced_values != NULL) {
  684. values = reduced_values;
  685. }
  686. // Free the remaining values
  687. for(int i = actual_num_values; i < array_size; i++) {
  688. free(values[i]);
  689. }
  690. }
  691. // Clean up
  692. free(tokens);
  693. free(array_str);
  694. return values;
  695. }
  696. #endif /* JB_JSMN_EDIT */