static blocktype parse_block_type(void) { char new_file_token_str[256]; blocktype block_type; // Parse the file token string as a name. if (!parse_name(file_token_str, new_file_token_str, false)) expected_token("a valid block type"); // Now verify it's a valid block type. if (!stricmp(new_file_token_str, "structural")) block_type = STRUCTURAL_BLOCK; else if (!stricmp(new_file_token_str, "multifaceted sprite")) block_type = MULTIFACETED_SPRITE; else if (!stricmp(new_file_token_str, "angled sprite")) block_type = ANGLED_SPRITE; else if (!stricmp(new_file_token_str, "revolving sprite")) block_type = REVOLVING_SPRITE; else if (!stricmp(new_file_token_str, "facing sprite")) block_type = FACING_SPRITE; else if (!stricmp(new_file_token_str, "player sprite")) block_type = PLAYER_SPRITE; else expected_token("block type 'structural', 'multifaceted sprite', " "'angled sprite', 'revolving sprite', 'facing sprite' or " "'player sprite'"); return(block_type); }
static char parse_char() { if (strlen(file_token_str) != 1) expected_token("a single-character string"); return(*file_token_str); }
static bool parse_bool() { bool value; start_parsing_value(); if (token_in_value_is("yes")) value = true; else if (token_in_value_is("no")) value = false; else expected_token("boolean 'yes' or 'no'"); stop_parsing_value(); return(value); }
static texstyle parse_texture_style(void) { texstyle style; start_parsing_value(); if (token_in_value_is("tiled")) style = TILED_TEXTURE; else if (token_in_value_is("scaled")) style = SCALED_TEXTURE; else if (token_in_value_is("stretched")) style = STRETCHED_TEXTURE; else expected_token("texture style 'tiled', 'scaled' or 'stretched'"); stop_parsing_value(); return(style); }
// Read a stream a line at a time, and parse it to fill out the print mask, // header, group_by, where expression, and projection attributes. // int SetAttrListPrintMaskFromStream ( SimpleInputStream & stream, // in: fetch lines from this stream until nextline() returns NULL const CustomFormatFnTable & FnTable, // in: table of custom output functions for SELECT AttrListPrintMask & mask, // out: columns and headers set in SELECT printmask_headerfooter_t & headfoot, // out: header and footer flags set in SELECT or SUMMARY printmask_aggregation_t & aggregate, // out: aggregation mode in SELECT std::vector<GroupByKeyInfo> & group_by, // out: ordered set of attributes/expressions in GROUP BY std::string & where_expression, // out: classad expression from WHERE StringList & attrs, // out ClassAd attributes referenced in mask or group_by outputs std::string & error_message) // out, if return is non-zero, this will be an error message { ClassAd ad; // so we can GetExprReferences enum section_t { NOWHERE=0, SELECT, SUMMARY, WHERE, GROUP}; enum cust_t { PRINTAS_STRING, PRINTAS_INT, PRINTAS_FLOAT }; bool label_fields = false; const char * labelsep = " = "; const char * prowpre = NULL; const char * pcolpre = " "; const char * pcolsux = NULL; const char * prowsux = "\n"; mask.SetAutoSep(prowpre, pcolpre, pcolsux, prowsux); error_message.clear(); aggregate = PR_NO_AGGREGATION; printmask_headerfooter_t usingHeadFoot = (printmask_headerfooter_t)(HF_CUSTOM | HF_NOSUMMARY); section_t sect = SELECT; tokener toke(""); while (toke.set(stream.nextline())) { if ( ! toke.next()) continue; if (toke.matches("#")) continue; if (toke.matches("SELECT")) { while (toke.next()) { if (toke.matches("FROM")) { if (toke.next()) { if (toke.matches("AUTOCLUSTER")) { aggregate = PR_FROM_AUTOCLUSTER; } else { std::string aa; toke.copy_token(aa); formatstr_cat(error_message, "Warning: Unknown header argument %s for SELECT FROM\n", aa.c_str()); } } } else if (toke.matches("UNIQUE")) { aggregate = PR_COUNT_UNIQUE; } else if (toke.matches("BARE")) { usingHeadFoot = HF_BARE; } else if (toke.matches("NOTITLE")) { usingHeadFoot = (printmask_headerfooter_t)(usingHeadFoot | HF_NOTITLE); } else if (toke.matches("NOHEADER")) { usingHeadFoot = (printmask_headerfooter_t)(usingHeadFoot | HF_NOHEADER); } else if (toke.matches("NOSUMMARY")) { usingHeadFoot = (printmask_headerfooter_t)(usingHeadFoot | HF_NOSUMMARY); } else if (toke.matches("LABEL")) { label_fields = true; } else if (label_fields && toke.matches("SEPARATOR")) { if (toke.next()) { std::string tmp; toke.copy_token(tmp); collapse_escapes(tmp); labelsep = mask.store(tmp.c_str()); } } else if (toke.matches("RECORDPREFIX")) { if (toke.next()) { std::string tmp; toke.copy_token(tmp); collapse_escapes(tmp); prowpre = mask.store(tmp.c_str()); } } else if (toke.matches("RECORDSUFFIX")) { if (toke.next()) { std::string tmp; toke.copy_token(tmp); collapse_escapes(tmp); prowsux = mask.store(tmp.c_str()); } } else if (toke.matches("FIELDPREFIX")) { if (toke.next()) { std::string tmp; toke.copy_token(tmp); collapse_escapes(tmp); pcolpre = mask.store(tmp.c_str()); } } else if (toke.matches("FIELDSUFFIX")) { if (toke.next()) { std::string tmp; toke.copy_token(tmp); collapse_escapes(tmp); pcolsux = mask.store(tmp.c_str()); } } else { std::string aa; toke.copy_token(aa); formatstr_cat(error_message, "Warning: Unknown header argument %s for SELECT\n", aa.c_str()); } } mask.SetAutoSep(prowpre, pcolpre, pcolsux, prowsux); sect = SELECT; continue; } else if (toke.matches("WHERE")) { sect = WHERE; if ( ! toke.next()) continue; } else if (toke.matches("GROUP")) { sect = GROUP; if ( ! toke.next() || (toke.matches("BY") && ! toke.next())) continue; } else if (toke.matches("SUMMARY")) { usingHeadFoot = (printmask_headerfooter_t)(usingHeadFoot & ~HF_NOSUMMARY); while (toke.next()) { if (toke.matches("STANDARD")) { // attrs.insert(ATTR_JOB_STATUS); } else if (toke.matches("NONE")) { usingHeadFoot = (printmask_headerfooter_t)(usingHeadFoot | HF_NOSUMMARY); } else { std::string aa; toke.copy_token(aa); formatstr_cat(error_message, "Unknown argument %s for SELECT\n", aa.c_str()); } } sect = SUMMARY; continue; } switch (sect) { case SELECT: { toke.mark(); std::string attr; std::string name; int opts = FormatOptionAutoWidth | FormatOptionNoTruncate; const char * fmt = "%v"; int wid = 0; CustomFormatFn cust; bool got_attr = false; while (toke.next()) { const Keyword * pkw = SelectKeywords.find_match(toke); if ( ! pkw) continue; // next token is a keyword, if we havent set the attribute yet // it's everything up to the current token. int kw = pkw->value; if ( ! got_attr) { toke.copy_marked(attr); got_attr = true; } switch (kw) { case kw_AS: { if (toke.next()) { toke.copy_token(name); if (toke.is_quoted_string()) { collapse_escapes(name); } } else { expected_token(error_message, "column name after AS", "SELECT", stream, toke); } toke.mark_after(); } break; case kw_PRINTF: { if (toke.next()) { std::string val; toke.copy_token(val); fmt = mask.store(val.c_str()); } else { expected_token(error_message, "format after PRINTF", "SELECT", stream, toke); } } break; case kw_PRINTAS: { if (toke.next()) { const CustomFormatFnTableItem * pcffi = FnTable.find_match(toke); if (pcffi) { cust = pcffi->cust; //cust_type = pcffi->cust; const char * pszz = pcffi->extra_attribs; if (pszz) { size_t cch = strlen(pszz); while (cch > 0) { attrs.insert(pszz); pszz += cch+1; cch = strlen(pszz); } } } else { std::string aa; toke.copy_token(aa); formatstr_cat(error_message, "Unknown argument %s for PRINTAS\n", aa.c_str()); } } else { expected_token(error_message, "function name after PRINTAS", "SELECT", stream, toke); } } break; case kw_NOSUFFIX: { opts |= FormatOptionNoSuffix; } break; case kw_NOPREFIX: { opts |= FormatOptionNoPrefix; } break; case kw_LEFT: { opts |= FormatOptionLeftAlign; } break; case kw_RIGHT: { opts &= ~FormatOptionLeftAlign; } break; case kw_TRUNCATE: { opts &= ~FormatOptionNoTruncate; } break; case kw_WIDTH: { if (toke.next()) { std::string val; toke.copy_token(val); if (toke.matches("AUTO")) { opts |= FormatOptionAutoWidth; } else { wid = atoi(val.c_str()); //if (wid) opts &= ~FormatOptionAutoWidth; //PRAGMA_REMIND("TJ: decide how LEFT & RIGHT interact with pos and neg widths." } } else { expected_token(error_message, "number or AUTO after WIDTH", "SELECT", stream, toke); } } break; default: unexpected_token(error_message, "SELECT", stream, toke); break; } // switch } // while if ( ! got_attr) { attr = toke.content(); } trim(attr); if (attr.empty() || attr[0] == '#') continue; const char * lbl = name.empty() ? attr.c_str() : name.c_str(); if (label_fields) { // build a format string that contains the label std::string label(lbl); if (labelsep) { label += labelsep; } if (fmt) { label += fmt; } else { label += "%"; if (wid) { if (opts & FormatOptionNoTruncate) formatstr_cat(label, "%d", wid); else formatstr_cat(label, "%d.%d", wid, wid < 0 ? -wid : wid); } label += cust ? "s" : "v"; } lbl = mask.store(label.c_str()); fmt = lbl; wid = 0; } else { if ( ! wid) { wid = 0 - (int)strlen(lbl); } mask.set_heading(lbl); lbl = NULL; } if (cust) { mask.registerFormat (lbl, wid, opts, cust, attr.c_str()); } else { mask.registerFormat(fmt, wid, opts, attr.c_str()); } ad.GetExprReferences(attr.c_str(), NULL, &attrs); } break; case WHERE: { toke.copy_to_end(where_expression); trim(where_expression); } break; case SUMMARY: { } break; case GROUP: { toke.mark(); GroupByKeyInfo key; // in case we end up finding no keywords, copy the remainder of the line now as the expression toke.copy_to_end(key.expr); bool got_expr = false; while (toke.next()) { const Keyword * pgw = GroupKeywords.find_match(toke); if ( ! pgw) continue; // got a keyword int gw = pgw->value; if ( ! got_expr) { toke.copy_marked(key.expr); got_expr = true; } switch (gw) { case gw_AS: { if (toke.next()) { toke.copy_token(key.name); } toke.mark_after(); } break; case gw_DECENDING: { key.decending = true; toke.mark_after(); } break; case gw_ASCENDING: { key.decending = false; toke.mark_after(); } break; default: unexpected_token(error_message, "GROUP BY", stream, toke); break; } // switch } // while toke.next trim(key.expr); if (key.expr.empty() || key.expr[0] == '#') continue; if ( ! ad.GetExprReferences(key.expr.c_str(), NULL, &attrs)) { formatstr_cat(error_message, "GROUP BY expression is not valid: %s\n", key.expr.c_str()); } else { group_by.push_back(key); } } break; default: break; } } headfoot = usingHeadFoot; return 0; }
void parse_param_list(token tag_name, param *param_list, token end_token) { param *param_ptr; int param_index; char old_name[256]; // Initialise the parameter list. init_param_list(); // Parse the parameter list. while ((param_ptr = parse_next_param(param_list)) != NULL) { switch (param_ptr->value_type) { case VALUE_BLOCK_NAME: parse_string(old_name, 256); if (!parse_name(old_name, (char *)param_ptr->variable_ptr, false)) file_error("Expected a valid block name rather than '%s'", old_name); break; case VALUE_BLOCK_TYPE: *(blocktype *)param_ptr->variable_ptr = parse_block_type(); break; case VALUE_BOOL: *(bool *)param_ptr->variable_ptr = parse_bool(); break; case VALUE_CHAR: *(char *)param_ptr->variable_ptr = parse_char(); break; case VALUE_DEGREES: *(double *)param_ptr->variable_ptr = parse_degrees(); break; case VALUE_DOUBLE: *(double *)param_ptr->variable_ptr = parse_double(); break; case VALUE_INTEGER: *(int *)param_ptr->variable_ptr = parse_integer(); break; case VALUE_PART_NAME: parse_string(old_name, 256); if (!parse_name(old_name, (char *)param_ptr->variable_ptr, false)) file_error("Expected a valid part name rather than '%s'", old_name); break; case VALUE_PERCENTAGE: *(double *)param_ptr->variable_ptr = parse_percentage(); break; case VALUE_REF_LIST: parse_ref_list((ref_list *)param_ptr->variable_ptr); break; case VALUE_RGB: *(RGBcolour *)param_ptr->variable_ptr = parse_RGB(); break; case VALUE_STRING: parse_string((char *)param_ptr->variable_ptr, 256); break; case VALUE_TEXCOORDS_LIST: parse_texcoords_list((texcoords_list *)param_ptr->variable_ptr); break; case VALUE_TEXTURE_STYLE: *(texstyle *)param_ptr->variable_ptr = parse_texture_style(); break; case VALUE_VERTEX_COORDS: *(vertex *)param_ptr->variable_ptr = parse_vertex_coordinates(); break; default: file_error("internal error--unrecognised parameter value type"); } } // If an end_token was specified, then verify that the end token parsed // matches it. if (end_token != TOKEN_NONE && file_token != end_token) expected_token(get_token_str(end_token)); // If there was a parameter list, verify that all required parameters were // parsed. if (param_list) { param_index = 0; while (param_list[param_index].param_name != TOKEN_NONE) { if (param_list[param_index].required && !matched_param[param_index]) file_error("parameter '%s' is missing from tag '%s'", get_token_str(param_list[param_index].param_name), get_token_str(tag_name)); param_index++; } } }
param * parse_next_param(param *param_list) { token param_name; char param_name_str[256]; int param_index; // Parse the next valid parameter, skipping over invalid parameters. while (true) { // If the next token is ">" or "/>" then there are no more parameters // to parse, so break out of the loop. read_next_file_token(); if (file_token == TOKEN_CLOSE_TAG || file_token == TOKEN_CLOSE_SINGLE_TAG) break; // If the token is a string token, this is an error. Otherwise store // it as the parameter token. if (file_token == VALUE_STRING) expected_token("a parameter name"); param_name = file_token; strcpy(param_name_str, file_token_str); // Parse the equal sign and the parameter value, which must be a // string value. match_next_file_token(TOKEN_EQUAL); read_next_file_token(); if (file_token != VALUE_STRING) expected_token("a string as a parameter value"); // If the parameter token was unknown or there is no parameter list, // skip over this parameter. if (param_name == TOKEN_UNKNOWN || param_list == NULL) continue; // Compare the parameter name against the entries in the parameter list; // if we find a match that hasn't been matched before, return the index // of that parameter entry. Otherwise ignore this parameter and parse // the next. param_index = 0; while (param_list[param_index].param_name != TOKEN_NONE) { if (param_name == param_list[param_index].param_name && !matched_param[param_index]) { matched_param[param_index] = true; return(¶m_list[param_index]); } param_index++; } } // Return a NULL pointer if the end of the parameter list was encountered. return(NULL); }