static int __handle(char *iname, int argc, char **argv) { struct cmd_option *nodes = &tree[0]; struct cmd_option *node = NULL; int i; if (argc == 0) return more_args_expected(nodes); for (i = 0; i < argc; i++) { node = find_matches(nodes, argv[i]); if (!node) return unexpected_token(nodes, argv[i]); if (node->next) return ambiguous_token(node, argv[i]); if (node->handler) { return node->handler(iname, argc - i, &argv[i], node->args); } nodes = node->children; } return more_args_expected(node->children); }
// 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; }