/* printer.cpp */ /* * Copyright information and license terms for this software can be * found in the file LICENSE that is included with the distribution */ #include #include #if defined(__linux__) || defined(__APPLE__) #include #define HAVE_ISATTY #endif #include #include #include #define epicsExportSharedSymbols #include #include #include namespace epics { namespace pvData { namespace format { static int indent_index = std::ios_base::xalloc(); long& indent_value(std::ios_base& ios) { return ios.iword(indent_index); } std::ostream& operator<<(std::ostream& os, indent_level const& indent) { indent_value(os) = indent.level; return os; } std::ostream& operator<<(std::ostream& os, indent const&) { long il = indent_value(os); for(long i=0, spaces = il * 4; i("severity")); PVScalar::const_shared_pointer pvStatus(sub.getSubField("status")); PVString::const_shared_pointer pvMessage(sub.getSubField("message")); switch(pvSeverity ? pvSeverity->getAs() : 0) { case 0: return; // nothing more to do here... case 1: strm<<"MINOR "; break; case 2: strm<<"MAJOR "; break; case 3: strm<<"INVALID "; break; case 4: strm<<"UNDEFINED "; break; default: strm<getAs()<<' '; } switch(pvStatus ? pvStatus->getAs() : 0) { case 0: break; case 1: strm<<"DEVICE "; break; case 2: strm<<"DRIVER "; break; case 3: strm<<"RECORD "; break; case 4: strm<<"DB "; break; case 5: strm<<"CONF "; break; case 6: strm<<"UNDEFINED "; break; case 7: strm<<"CLIENT "; break; default: strm<getAs()<<' '; } if(pvMessage && !pvMessage->get().empty()) strm<get()<<' '; } void printAlarmT(std::ostream& strm, const PVStructure& top) { PVStructure::const_shared_pointer sub(top.getSubField("alarm")); if(sub) printAlarmTx(strm, *sub); } void printTimeTx(std::ostream& strm, const PVStructure& tsubop) { char timeText[32]; epicsTimeStamp epicsTS; PVScalar::const_shared_pointer secf(tsubop.getSubField("secondsPastEpoch")), nsecf(tsubop.getSubField("nanoseconds")), tagf(tsubop.getSubField("userTag")); epicsTS.secPastEpoch = secf ? secf->getAs() : 0; epicsTS.nsec = nsecf ? nsecf->getAs() : 0; if(epicsTS.secPastEpoch > POSIX_TIME_AT_EPICS_EPOCH) epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH; else epicsTS.secPastEpoch = 0; epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%d %H:%M:%S.%03f", &epicsTS); strm <getAs(); if(tagv) strm << tagv << ' '; } } void printTimeT(std::ostream& strm, const PVStructure& top) { PVStructure::const_shared_pointer sub(top.getSubField("timeStamp")); if(sub) printTimeTx(strm, *sub); } bool printEnumT(std::ostream& strm, const PVStructure& top, bool fromtop) { PVStructure::const_shared_pointer value; if(fromtop) { value = top.getSubField("value"); } else { value = std::tr1::static_pointer_cast(top.shared_from_this()); } PVScalar::const_shared_pointer idx(value->getSubField("index")); PVStringArray::const_shared_pointer choices(value->getSubField("choices")); if(!idx || !choices) return false; if(fromtop) { strm<view()); uint32 I = idx->getAs(); strm<<'('<=ch.size()) { strm<<" "; } else { strm<<' '<("value")); if(!cf) return false; { const FieldConstPtrArray& fields = cf->getStructure()->getFields(); if(fields.empty()) return false; for(size_t i=0, N=fields.size(); igetType()!=scalarArray) return false; // cowardly refusing to handle anything else... } } // maybe output a line with meta-data { bool havets = !!top.getSubField("timeStamp"); bool haveal = !!top.getSubField("alarm"); if(havets || haveal) strm<("labels")); if(lf) { PVStringArray::const_svector L(lf->view()); labels = thaw(L); } } const PVFieldPtrArray& columns = cf->getPVFields(); std::vector > coldat(columns.size()); std::vector widths(columns.size()); labels.reserve(columns.size()); size_t nrows = size_t(-1); for(size_t i=0, N=columns.size(); i=labels.size()) { labels.push_back(cf->getStructure()->getFieldName(i)); } else { csvEscape(labels[i]); } widths[i] = labels[i].size(); { shared_vector ro; static_cast(columns[i].get())->getAs(ro); coldat[i] = thaw(ro); } // truncate if some columns are longer than others nrows = std::min(nrows, coldat[i].size()); for(size_t j=0, M=coldat[i].size(); j=0 && idxgetNextFieldOffset(); igetParent(); parent; parent = parent->getParent()) { mask.set(parent->getFieldOffset()); } } } } } } namespace epics { namespace pvData { void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur) { typedef std::deque > todo_t; todo_t todo; BitSet show, highlight; const long orig_indent = format::indent_value(strm); { if(format.xshow) show = *format.xshow; else show.set(0); if(format.xhighlight) highlight = *format.xhighlight; expandBS(format.xtop, show, true); expandBS(format.xtop, highlight, false); highlight &= show; // can't highlight hidden fields (paranoia) } if(!show.get(0)) return; // nothing to do here... todo.push_front(std::make_pair(&format.xtop, orig_indent)); while(!todo.empty()) { todo_t::value_type cur(todo.front()); todo.pop_front(); format::indent_value(strm) = cur.second; bool hl = highlight.get(cur.first->getFieldOffset()) && format.xmode==PVStructure::Formatter::ANSI; if(hl) strm<<"\x1b[1m"; // Bold switch(cur.first->getField()->getType()) { case structure: { const PVStructure* str = static_cast(cur.first); const PVFieldPtrArray& flds = str->getPVFields(); for(PVFieldPtrArray::const_reverse_iterator it(flds.rbegin()), end(flds.rend()); it!=end; ++it) { const PVField *fld = (*it).get(); if(!show.get(fld->getFieldOffset())) continue; todo.push_front(std::make_pair(fld, cur.second+1)); } std::string id(cur.first->getField()->getID()); strm<getFieldName(); if(id=="alarm_t") { strm.put(' '); printAlarmTx(strm, *str); } else if(id=="time_t") { strm.put(' '); printTimeTx(strm, *str); } else if(id=="enum_t") { strm.put(' '); printEnumT(strm, *str, false); } strm.put('\n'); } break; case scalar: case scalarArray: strm<getField()->getID()<<' '<getFieldName() <<' '<<*cur.first <<'\n'; break; case structureArray: case union_: case unionArray: strm<<*cur.first; break; } if(hl) strm<<"\x1b[0m"; // reset } format::indent_value(strm) = orig_indent; } std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format) { if(format.xfmt==PVStructure::Formatter::JSON) { JSONPrintOptions opts; opts.multiLine = false; printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts); strm<<'\n'; return strm; } else if(format.xfmt==PVStructure::Formatter::NT) { std::string id(format.xtop.getStructure()->getID()), idprefix(id.substr(0, id.find_first_of('.'))); // NTTable if(idprefix=="epics:nt/NTTable:1") { if(printTable(strm, format.xtop)) return strm; } else { //NTScalar, NTScalarArray, NTEnum, or anything with '.value' PVField::const_shared_pointer value(format.xtop.getSubField("value")); if(value) { switch(value->getField()->getType()) { case scalar: strm<(value.get())<<' '; printAlarmT(strm, format.xtop); strm<<'\n'; return strm; case scalarArray: strm<(value.get())<<'\n'; return strm; case structure: if(printEnumT(strm, format.xtop, true)) { strm<<'\n'; return strm; } break; default: break; } } } } // fall through unhandled as Raw PVStructure::Formatter format2(format); if(format2.xmode==PVStructure::Formatter::Auto) format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain; printRaw(strm, format2, format.xtop); return strm; } static char hexdigit(char c) { c &= 0xf; if(c<9) return '0'+c; else return 'A'+c-10; } escape::~escape() {} std::string escape::str() const { std::ostringstream strm; strm<<(*this); return strm.str(); } epicsShareFunc std::ostream& operator<<(std::ostream& strm, const escape& Q) { for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) { const char C = Q.orig[pos]; char quote = '\\', next; // compare me with epicsStrnEscapedFromRaw() switch(C) { case '\a': next = 'a'; break; case '\b': next = 'b'; break; case '\f': next = 'f'; break; case '\n': next = 'n'; break; case '\r': next = 'r'; break; case '\t': next = 't'; break; case '\v': next = 'v'; break; case '\\': next = '\\'; break; case '\'': next = '\''; break; case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break; default: if(!isprint((unsigned char)C)) { // print three charator escape strm<<"\\x"<>4)<