static stat_t _text_parser_kernal(char_t *str, nvObj_t *nv) { char_t *rd, *wr; // read and write pointers // char_t separators[] = {"="}; // STRICT: only separator allowed is = sign char_t separators[] = {" =:|\t"}; // RELAXED: any separator someone might use // pre-process and normalize the string // nv_reset_nv(nv); // initialize config object nv_copy_string(nv, str); // make a copy for eventual reporting if (*str == '$') str++; // ignore leading $ for (rd = wr = str; *rd != NUL; rd++, wr++) { *wr = tolower(*rd); // convert string to lower case if (*rd == ',') { *wr = *(++rd);} // skip over commas } *wr = NUL; // terminate the string // parse fields into the nv struct nv->valuetype = TYPE_NULL; if ((rd = strpbrk(str, separators)) == NULL) { // no value part strncpy(nv->token, str, TOKEN_LEN); } else { *rd = NUL; // terminate at end of name strncpy(nv->token, str, TOKEN_LEN); str = ++rd; nv->value = strtof(str, &rd); // rd used as end pointer if (rd != str) { nv->valuetype = TYPE_FLOAT; } } // validate and post-process the token if ((nv->index = nv_get_index((const char_t *)"", nv->token)) == NO_MATCH) { // get index or fail it return (STAT_UNRECOGNIZED_NAME); } strcpy_P(nv->group, cfgArray[nv->index].group); // capture the group string if there is one // see if you need to strip the token if (nv->group[0] != NUL) { wr = nv->token; rd = nv->token + strlen(nv->group); while (*rd != NUL) { *(wr)++ = *(rd)++;} *wr = NUL; } return (STAT_OK); }
// ASSUMES A RAM STRING. If you need to post a FLASH string use pstr2str to convert it to a RAM string nvObj_t *nv_add_string(const char_t *token, const char_t *string) // add a string object to the body { nvObj_t *nv = nv_body; for (uint8_t i=0; i<NV_BODY_LEN; i++) { if (nv->valuetype != TYPE_EMPTY) { if ((nv = nv->nx) == NULL) return(NULL); // not supposed to find a NULL; here for safety continue; } strncpy(nv->token, token, TOKEN_LEN); if (nv_copy_string(nv, string) != STAT_OK) return (NULL); nv->index = nv_get_index((const char_t *)"", nv->token); nv->valuetype = TYPE_STRING; return (nv); } return (NULL); }
void json_print_response(uint8_t status) { #ifdef __SILENCE_JSON_RESPONSES return; #endif if (js.json_verbosity == JV_SILENT) { // silent means no responses return; } if (js.json_verbosity == JV_EXCEPTIONS) { // cutout for JV_EXCEPTIONS mode if (status == STAT_OK) { if (cm.machine_state != MACHINE_INITIALIZING) { // always do full echo during startup return; } } } // Body processing nvObj_t *nv = nv_body; if (status == STAT_JSON_SYNTAX_ERROR) { nv_reset_nv_list(); nv_add_string((const char *)"err", escape_string(cs.bufp, cs.saved_buf)); } else if (cm.machine_state != MACHINE_INITIALIZING) { // always do full echo during startup uint8_t nv_type; do { if ((nv_type = nv_get_type(nv)) == NV_TYPE_NULL) break; if (nv_type == NV_TYPE_GCODE) { if (js.echo_json_gcode_block == false) { // kill command echo if not enabled nv->valuetype = TYPE_EMPTY; } //++++ } else if (nv_type == NV_TYPE_CONFIG) { // kill config echo if not enabled //fix me if (js.echo_json_configs == false) { // nv->valuetype = TYPE_EMPTY; // } } else if (nv_type == NV_TYPE_MESSAGE) { // kill message echo if not enabled if (js.echo_json_messages == false) { nv->valuetype = TYPE_EMPTY; } } else if (nv_type == NV_TYPE_LINENUM) { // kill line number echo if not enabled if ((js.echo_json_linenum == false) || (fp_ZERO(nv->value))) { // do not report line# 0 nv->valuetype = TYPE_EMPTY; } } } while ((nv = nv->nx) != NULL); } // Footer processing while(nv->valuetype != TYPE_EMPTY) { // find a free nvObj at end of the list... if ((nv = nv->nx) == NULL) { // oops! No free nvObj! rpt_exception(STAT_JSON_TOO_LONG, "json_print"); // report this as an exception return; } } char footer_string[NV_FOOTER_LEN]; // in xio.cpp:xio.readline the CR||LF read from the host is not appended to the string. // to ensure that the correct number of bytes are reported back to the host we add a +1 to // cs.linelen so that the number of bytes received matches the number of bytes reported sprintf((char *)footer_string, "%d,%d,%d", 1, status, cs.linelen + 1); cs.linelen = 0; // reset linelen so it's only reported once // if (xio.enable_window_mode) { // 2 footer styles are supported... // sprintf((char *)footer_string, "%d,%d,%d", 2, status, xio_get_window_slots()); //...windowing // } else { // sprintf((char *)footer_string, "%d,%d,%d", 1, status, cs.linelen); //...streaming // cs.linelen = 0; // reset linelen so it's only reported once // } nv_copy_string(nv, footer_string); // link string to nv object nv->depth = 0; // footer 'f' is a peer to response 'r' (hard wired to 0) nv->valuetype = TYPE_ARRAY; // declare it as an array strcpy(nv->token, "f"); // set it to Footer nv->nx = NULL; // terminate the list // serialize the JSON response and print it if there were no errors if (json_serialize(nv_header, cs.out_buf, sizeof(cs.out_buf)) >= 0) { fprintf(stderr, "%s", cs.out_buf); } }
static stat_t _get_nv_pair(nvObj_t *nv, char **pstr, int8_t *depth) { uint8_t i; char *tmp; char leaders[] = {"{,\""}; // open curly, quote and leading comma char separators[] = {":\""}; // colon and quote char terminators[] = {"},\""}; // close curly, comma and quote char value[] = {"{\".-+"}; // open curly, quote, period, minus and plus nv_reset_nv(nv); // wipes the object and sets the depth // --- Process name part --- // Find, terminate and set pointers for the name. Allow for leading and trailing name quotes. char * name = *pstr; for (i=0; true; i++, (*pstr)++) { if (strchr(leaders, (int)**pstr) == NULL) { // find leading character of name name = (*pstr)++; break; } if (i == MAX_PAD_CHARS) return (STAT_JSON_SYNTAX_ERROR); } // Find the end of name, NUL terminate and copy token for (i=0; true; i++, (*pstr)++) { if (strchr(separators, (int)**pstr) != NULL) { *(*pstr)++ = NUL; strncpy(nv->token, name, TOKEN_LEN+1); // copy the string to the token break; } if (i == MAX_NAME_CHARS) return (STAT_JSON_SYNTAX_ERROR); } // --- Process value part --- (organized from most to least frequently encountered) // Find the start of the value part for (i=0; true; i++, (*pstr)++) { if (isalnum((int)**pstr)) break; if (strchr(value, (int)**pstr) != NULL) break; if (i == MAX_PAD_CHARS) return (STAT_JSON_SYNTAX_ERROR); } // nulls (gets) if ((**pstr == 'n') || ((**pstr == '\"') && (*(*pstr+1) == '\"'))) { // process null value nv->valuetype = TYPE_NULL; nv->value = TYPE_NULL; // numbers } else if (isdigit(**pstr) || (**pstr == '-')) {// value is a number nv->value = (float)strtod(*pstr, &tmp); // tmp is the end pointer if(tmp == *pstr) { return (STAT_BAD_NUMBER_FORMAT);} nv->valuetype = TYPE_FLOAT; // object parent } else if (**pstr == '{') { nv->valuetype = TYPE_PARENT; // *depth += 1; // nv_reset_nv() sets the next object's level so this is redundant (*pstr)++; return(STAT_EAGAIN); // signal that there is more to parse // strings } else if (**pstr == '\"') { // value is a string (*pstr)++; nv->valuetype = TYPE_STRING; if ((tmp = strchr(*pstr, '\"')) == NULL) { return (STAT_JSON_SYNTAX_ERROR);} // find the end of the string *tmp = NUL; // if string begins with 0x it might be data, needs to be at least 3 chars long if( strlen(*pstr)>=3 && (*pstr)[0]=='0' && (*pstr)[1]=='x') { uint32_t *v = (uint32_t*)&nv->value; *v = strtoul((const char *)*pstr, 0L, 0); nv->valuetype = TYPE_DATA; } else { ritorno(nv_copy_string(nv, *pstr)); } *pstr = ++tmp; // boolean true/false } else if (**pstr == 't') { nv->valuetype = TYPE_BOOL; nv->value = true; } else if (**pstr == 'f') { nv->valuetype = TYPE_BOOL; nv->value = false; // arrays } else if (**pstr == '[') { nv->valuetype = TYPE_ARRAY; ritorno(nv_copy_string(nv, *pstr)); // copy array into string for error displays return (STAT_INPUT_VALUE_UNSUPPORTED); // return error as the parser doesn't do input arrays yet // general error condition } else { return (STAT_JSON_SYNTAX_ERROR); } // ill-formed JSON // process comma separators and end curlies if ((*pstr = strpbrk(*pstr, terminators)) == NULL) { // advance to terminator or err out return (STAT_JSON_SYNTAX_ERROR); } if (**pstr == '}') { *depth -= 1; // pop up a nesting level (*pstr)++; // advance to comma or whatever follows } if (**pstr == ',') { return (STAT_EAGAIN);} // signal that there is more to parse (*pstr)++; return (STAT_OK); // signal that parsing is complete }
stat_t gc_get_gc(nvObj_t *nv) { ritorno(nv_copy_string(nv, cs.saved_buf)); nv->valuetype = TYPE_STRING; return (STAT_OK); }