// 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 secondPass (int argc, char *argv[]) { const char * pcolon = NULL; char *daemonname; for (int i = 1; i < argc; i++) { // omit parameters which qualify switches if( matchPrefix(argv[i],"-pool", 2) || matchPrefix(argv[i],"-direct", 4) ) { i++; continue; } if( matchPrefix(argv[i],"-subsystem", 5) ) { i++; continue; } if (matchPrefix (argv[i], "-format", 2)) { pm.registerFormat (argv[i+1], argv[i+2]); StringList attributes; ClassAd ad; if(!ad.GetExprReferences(argv[i+2],NULL,&attributes)){ fprintf( stderr, "Error: Parse error of: %s\n", argv[i+2]); exit(1); } attributes.rewind(); char const *s; while( (s=attributes.next()) ) { projList.AppendArg(s); } if (diagnose) { printf ("Arg %d --- register format [%s] for [%s]\n", i, argv[i+1], argv[i+2]); } i += 2; continue; } if (*argv[i] == '-' && (is_arg_colon_prefix(argv[i]+1, "autoformat", &pcolon, 5) || is_arg_colon_prefix(argv[i]+1, "af", &pcolon, 2)) ) { // make sure we have at least one more argument if ( !argv[i+1] || *(argv[i+1]) == '-') { fprintf( stderr, "Error: Argument %s requires " "at last one attribute parameter\n", argv[i] ); fprintf( stderr, "Use \"%s -help\" for details\n", myName ); exit( 1 ); } bool flabel = false; bool fCapV = false; bool fRaw = false; bool fheadings = false; const char * prowpre = NULL; const char * pcolpre = " "; const char * pcolsux = NULL; if (pcolon) { ++pcolon; while (*pcolon) { switch (*pcolon) { case ',': pcolsux = ","; break; case 'n': pcolsux = "\n"; break; case 'g': pcolpre = NULL; prowpre = "\n"; break; case 't': pcolpre = "\t"; break; case 'l': flabel = true; break; case 'V': fCapV = true; break; case 'r': case 'o': fRaw = true; break; case 'h': fheadings = true; break; } ++pcolon; } } pm.SetAutoSep(prowpre, pcolpre, pcolsux, "\n"); while (argv[i+1] && *(argv[i+1]) != '-') { ++i; ClassAd ad; StringList attributes; if(!ad.GetExprReferences(argv[i],NULL,&attributes)){ fprintf( stderr, "Error: Parse error of: %s\n", argv[i]); exit(1); } attributes.rewind(); char const *s; while ((s = attributes.next())) { projList.AppendArg(s); } MyString lbl = ""; int wid = 0; int opts = FormatOptionNoTruncate; if (fheadings || pm_head.Length() > 0) { const char * hd = fheadings ? argv[i] : "(expr)"; wid = 0 - (int)strlen(hd); opts = FormatOptionAutoWidth | FormatOptionNoTruncate; pm_head.Append(hd); } else if (flabel) { lbl.formatstr("%s = ", argv[i]); wid = 0; opts = 0; } lbl += fRaw ? "%r" : (fCapV ? "%V" : "%v"); if (diagnose) { printf ("Arg %d --- register format [%s] width=%d, opt=0x%x for [%s]\n", i, lbl.Value(), wid, opts, argv[i]); } pm.registerFormat(lbl.Value(), wid, opts, argv[i]); } // if autoformat list ends in a '-' without any characters after it, just eat the arg and keep going. if (i+1 < argc && '-' == (argv[i+1])[0] && 0 == (argv[i+1])[1]) { ++i; } continue; } if (is_dash_arg_colon_prefix(argv[i], "print-format", &pcolon, 2)) { if ( (i+1 >= argc) || (*(argv[i+1]) == '-' && (argv[i+1])[1] != 0)) { fprintf( stderr, "Error: Argument -print-format requires a filename argument\n"); exit( 1 ); } // hack allow -pr ! to disable use of user-default print format files. if (MATCH == strcmp(argv[i+1], "!")) { ++i; disable_user_print_files = true; continue; } ppTotalStyle = ppStyle; setPPstyle (PP_CUSTOM, i, argv[i]); setPPwidth(); ++i; // skip to the next argument. if (set_status_print_mask_from_stream(argv[i], true, &mode_constraint) < 0) { fprintf(stderr, "Error: invalid select file %s\n", argv[i]); exit (1); } if (mode_constraint) { query->addANDConstraint(mode_constraint); } using_print_format = true; // so we can hack totals. continue; } if (matchPrefix (argv[i], "-target", 5)) { i++; continue; } if (is_dash_arg_prefix(argv[i], "ads", 2)) { ++i; continue; } if( matchPrefix(argv[i], "-sort", 3) ) { i++; if ( ! noSort) { sprintf( buffer, "%s =!= UNDEFINED", argv[i] ); query->addANDConstraint( buffer ); } continue; } if (matchPrefix (argv[i], "-statistics", 6)) { i += 2; sprintf(buffer,"STATISTICS_TO_PUBLISH = \"%s\"", statistics); if (diagnose) { printf ("[%s]\n", buffer); } query->addExtraAttribute(buffer); continue; } if (matchPrefix (argv[i], "-attributes", 3) ) { // parse attributes to be selected and split them along "," StringList more_attrs(argv[i+1],","); char const *s; more_attrs.rewind(); while( (s=more_attrs.next()) ) { projList.AppendArg(s); dashAttributes.append(s); } i++; continue; } // figure out what the other parameters should do if (*argv[i] != '-') { // display extra information for diagnosis if (diagnose) { printf ("Arg %d (%s) --- adding constraint", i, argv[i]); } if( !(daemonname = get_daemon_name(argv[i])) ) { if ( (mode==MODE_SCHEDD_SUBMITTORS) && strchr(argv[i],'@') ) { // For a submittor query, it is possible that the // hostname is really a UID_DOMAIN. And there is // no requirement that UID_DOMAIN actually have // an inverse lookup in DNS... so if get_daemon_name() // fails with a fully qualified submittor lookup, just // use what we are given and do not flag an error. daemonname = strnewp(argv[i]); } else { dprintf_WriteOnErrorBuffer(stderr, true); fprintf( stderr, "%s: unknown host %s\n", argv[0], get_host_part(argv[i]) ); exit(1); } } switch (mode) { case MODE_DEFRAG_NORMAL: case MODE_STARTD_NORMAL: case MODE_STARTD_COD: #ifdef HAVE_EXT_POSTGRESQL case MODE_QUILL_NORMAL: #endif /* HAVE_EXT_POSTGRESQL */ case MODE_SCHEDD_NORMAL: case MODE_SCHEDD_SUBMITTORS: case MODE_MASTER_NORMAL: case MODE_COLLECTOR_NORMAL: case MODE_CKPT_SRVR_NORMAL: case MODE_NEGOTIATOR_NORMAL: case MODE_STORAGE_NORMAL: case MODE_ANY_NORMAL: case MODE_GENERIC_NORMAL: case MODE_STARTD_AVAIL: case MODE_OTHER: case MODE_GRID_NORMAL: case MODE_HAD_NORMAL: sprintf(buffer,"(%s==\"%s\") || (%s==\"%s\")", ATTR_NAME, daemonname, ATTR_MACHINE, daemonname ); if (diagnose) { printf ("[%s]\n", buffer); } query->addORConstraint (buffer); break; case MODE_STARTD_RUN: sprintf (buffer,"%s == \"%s\"",ATTR_REMOTE_USER,argv[i]); if (diagnose) { printf ("[%s]\n", buffer); } query->addORConstraint (buffer); break; default: fprintf(stderr,"Error: Don't know how to process %s\n",argv[i]); } delete [] daemonname; daemonname = NULL; } else if (matchPrefix (argv[i], "-constraint", 4)) { if (diagnose) { printf ("[%s]\n", argv[i+1]); } query->addANDConstraint (argv[i+1]); i++; } } }