diff --git a/src/nt/nttable.cpp b/src/nt/nttable.cpp index 7e14e3c..8c9c3b5 100644 --- a/src/nt/nttable.cpp +++ b/src/nt/nttable.cpp @@ -5,148 +5,177 @@ * in file LICENSE that is included with this distribution. */ +#include + #include -namespace epics { namespace pvData { +using namespace std; +using namespace epics::pvData; -using std::tr1::static_pointer_cast; +namespace epics { namespace nt { -bool NTTable::isNTTable(PVStructurePtr const & pvStructure) +namespace detail { + +static NTFieldPtr ntField = NTField::get(); + +NTTableBuilder::shared_pointer NTTableBuilder::add( + std::string const & name, epics::pvData::ScalarType scalarType + ) { - NTFieldPtr ntfield = NTField::get(); - PVStringArrayPtr pvLabel = static_pointer_cast - (pvStructure->getScalarArrayField("label",pvString)); - if(pvLabel.get()==NULL) return false; - size_t nfields = pvLabel->getLength(); - size_t nextra = 1; // label is 1 field - PVFieldPtr pvField = pvStructure->getSubField("function"); - if(pvField.get()!=NULL - && pvStructure->getStringField("function").get()!=NULL) nextra++; - pvField = pvStructure->getSubField("timeStamp"); - if(pvField!=0 && ntfield->isTimeStamp(pvField->getField())) { - nextra++; - } - pvField = pvStructure->getSubField("alarm"); - if(pvField.get()!=NULL && ntfield->isAlarm(pvField->getField())) { - nextra++; - } - if(nfields!=(pvStructure->getStructure()->getNumberFields()-nextra)) { + if (std::find(labels.begin(), labels.end(), name) != labels.end()) + throw std::runtime_error("duplicate column name"); + + labels.push_back(name); + types.push_back(scalarType); + + return shared_from_this(); +} + +StructureConstPtr NTTableBuilder::createStructure() +{ + FieldBuilderPtr builder = getFieldCreate()->createFieldBuilder(); + + FieldBuilderPtr nestedBuilder = + builder-> + setId(NTTable::URI)-> + addArray("labels", pvString)-> + addNestedStructure("value"); + + vector::size_type len = labels.size(); + for (vector::size_type i = 0; i < len; i++) + nestedBuilder->addArray(labels[i], types[i]); + + builder = nestedBuilder->endNested(); + + if (descriptor) + builder->add("descriptor", pvString); + + if (alarm) + builder->add("alarm", ntField->createAlarm()); + + if (timeStamp) + builder->add("timeStamp", ntField->createTimeStamp()); + + StructureConstPtr s = builder->createStructure(); + + reset(); + return s; +} + +NTTableBuilder::shared_pointer NTTableBuilder::addDescriptor() +{ + descriptor = true; + return shared_from_this(); +} + +NTTableBuilder::shared_pointer NTTableBuilder::addAlarm() +{ + alarm = true; + return shared_from_this(); +} + +NTTableBuilder::shared_pointer NTTableBuilder::addTimeStamp() +{ + timeStamp = true; + return shared_from_this(); +} + +PVStructurePtr NTTableBuilder::createPVStructure() +{ + PVStringArray::svector l; + l.resize(labels.size()); + std::copy(labels.begin(), labels.end(), l.begin()); + + PVStructurePtr s = getPVDataCreate()->createPVStructure(createStructure()); + s->getSubField("labels")->replace(freeze(l)); + + return s; +} + +NTTablePtr NTTableBuilder::create() +{ + return NTTablePtr(new NTTable(createPVStructure())); +} + +NTTableBuilder::NTTableBuilder() +{ + reset(); +} + +void NTTableBuilder::reset() +{ + labels.clear(); + types.clear(); + descriptor = false; + alarm = false; + timeStamp = false; +} + +} + +const std::string NTTable::URI("uri:ev4:nt/2012/pwd:NTTable"); + +bool NTTable::is_a(StructureConstPtr const & structure) +{ + return structure->getID() == URI; +} + +NTTableBuilderPtr NTTable::createBuilder() +{ + return NTTableBuilderPtr(new detail::NTTableBuilder()); +} + +bool NTTable::attachTimeStamp(PVTimeStamp &pvTimeStamp) const +{ + PVStructurePtr ts = getTimeStamp(); + if (ts) + return pvTimeStamp.attach(ts); + else return false; - } - FieldConstPtrArray fields = pvStructure->getStructure()->getFields(); - for(size_t i=0; igetType(); - if(type!=scalarArray && type!=structureArray) return false; - } - return true; } -NTTablePtr NTTable::create( - bool hasFunction,bool hasTimeStamp, bool hasAlarm, - shared_vector const & valueNames, - FieldConstPtrArray const &valueFields) +bool NTTable::attachAlarm(PVAlarm &pvAlarm) const { - StandardFieldPtr standardField = getStandardField(); - size_t nfields = 1; - if(hasFunction) nfields++; - if(hasTimeStamp) nfields++; - if(hasAlarm) nfields++; - nfields += valueFields.size(); - FieldCreatePtr fieldCreate = getFieldCreate(); - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - FieldConstPtrArray fields(nfields); - StringArray names(nfields); - size_t ind = 0; - if(hasFunction) { - names[ind] = "function"; - fields[ind++] = fieldCreate->createScalar(pvString); - } - if(hasTimeStamp) { - names[ind] = "timeStamp"; - fields[ind++] = standardField->timeStamp(); - } - if(hasAlarm) { - names[ind] = "alarm"; - fields[ind++] = standardField->alarm(); - } - names[ind] = "label"; - fields[ind++] = fieldCreate->createScalarArray(pvString); - size_t numberValues = valueNames.size(); - for(size_t i=0; icreateStructure(names,fields); - PVStructurePtr pvStructure = pvDataCreate->createPVStructure(st); - PVStringArrayPtr pvLabel = static_pointer_cast - (pvStructure->getScalarArrayField("label",pvString)); - shared_vector xxx(numberValues); - for(size_t i=0; i data(freeze(xxx)); - pvLabel->replace(data); - return NTTablePtr(new NTTable(pvStructure)); + PVStructurePtr al = getAlarm(); + if (al) + return pvAlarm.attach(al); + else + return false; } -NTTablePtr NTTable::clone(PVStructurePtr const & pv) +PVStructurePtr NTTable::getPVStructure() const { - PVStructurePtr pvStructure = getPVDataCreate()->createPVStructure(pv); - return NTTablePtr(new NTTable(pvStructure)); + return pvNTTable; } -NTTable::NTTable(PVStructurePtr const & pvStructure) -: pvNTTable(pvStructure), - offsetFields(1) +PVStringPtr NTTable::getDescriptor() const { - NTFieldPtr ntfield = NTField::get(); - PVScalarArrayPtr pvScalarArray - = pvStructure->getScalarArrayField("label",pvString); - pvLabel = static_pointer_cast(pvScalarArray); - PVFieldPtr pvField = pvStructure->getSubField("function"); - if(pvField.get()!=NULL) { - offsetFields++; - pvFunction = pvStructure->getStringField("function"); - } - pvField = pvStructure->getSubField("timeStamp"); - if(pvField.get()!=NULL && ntfield->isTimeStamp(pvField->getField())) { - offsetFields++; - pvTimeStamp = static_pointer_cast(pvField); - } - pvField = pvStructure->getSubField("alarm"); - if(pvField.get()!=NULL && ntfield->isAlarm(pvField->getField())) { - offsetFields++; - pvAlarm = static_pointer_cast(pvField); - } + return pvNTTable->getSubField("descriptor"); } - -void NTTable::attachTimeStamp(PVTimeStamp &pv) +PVStructurePtr NTTable::getTimeStamp() const { - if(pvTimeStamp.get()==NULL) return; - pv.attach(pvTimeStamp); + return pvNTTable->getSubField("timeStamp"); } -void NTTable::attachAlarm(PVAlarm &pv) +PVStructurePtr NTTable::getAlarm() const { - if(pvAlarm.get()==NULL) return; - pv.attach(pvAlarm); + return pvNTTable->getSubField("alarm"); } -size_t NTTable::getNumberValues() +PVStringArrayPtr NTTable::getLabels() const { - return pvLabel->getLength(); + return pvNTTable->getSubField("labels"); } -FieldConstPtr NTTable::getField(size_t index) +PVFieldPtr NTTable::getColumn(std::string const & columnName) const { - FieldConstPtrArray fields = pvNTTable->getStructure()->getFields(); - return fields[index + offsetFields]; + return pvNTTable->getSubField("value." + columnName); } -PVFieldPtr NTTable::getPVField(size_t index) -{ - PVFieldPtrArray pvFields = pvNTTable->getPVFields(); - return pvFields[index+offsetFields]; -} +NTTable::NTTable(PVStructurePtr const & pvStructure) : + pvNTTable(pvStructure) +{} + }} diff --git a/src/nt/nttable.h b/src/nt/nttable.h index 44e3e1e..6d5c817 100644 --- a/src/nt/nttable.h +++ b/src/nt/nttable.h @@ -9,107 +9,197 @@ #include -namespace epics { namespace pvData { +#include +#include -/** - * Convenience Class for NTTable - * @author mrk - * - */ +namespace epics { namespace nt { class NTTable; typedef std::tr1::shared_ptr NTTablePtr; +namespace detail { + + /** + * Interface for in-line creating of NTTable. + * One instance can be used to create multiple instances. + * An instance of this object must not be used concurrently (an object has a state). + * @author mse + */ + class epicsShareClass NTTableBuilder : + public std::tr1::enable_shared_from_this + { + public: + POINTER_DEFINITIONS(NTTableBuilder); + + /** + * Add a column of given {@code Scalar} type. + * @param name name of the column. + * @param scalarType column type, a scalar array. + * @return this instance of a {@code NTTableBuilder}. + */ + shared_pointer add(std::string const & name, epics::pvData::ScalarType scalarType); + + /** + * Add descriptor field to the NTTable. + * @return this instance of a {@code NTTableBuilder}. + */ + shared_pointer addDescriptor(); + + /** + * Add alarm structure to the NTTable. + * @return this instance of a {@code NTTableBuilder}. + */ + shared_pointer addAlarm(); + + /** + * Add timeStamp structure to the NTTable. + * @return this instance of a {@code NTTableBuilder}. + */ + shared_pointer addTimeStamp(); + + /** + * Create a {@code Structure} that represents NTTable. + * This resets this instance state and allows new instance to be created. + * @return a new instance of a {@code Structure}. + */ + epics::pvData::StructureConstPtr createStructure(); + + /** + * Create a {@code PVStructure} that represents NTTable. + * This resets this instance state and allows new {@code instance to be created. + * @return a new instance of a {@code PVStructure} + */ + epics::pvData::PVStructurePtr createPVStructure(); + + /** + * Create a {@code NTTable} instance. + * This resets this instance state and allows new {@code instance to be created. + * @return a new instance of a {@code NTTable} + */ + NTTablePtr create(); + + private: + NTTableBuilder(); + + void reset(); + + std::vector labels; + std::vector types; + + bool descriptor; + bool alarm; + bool timeStamp; + + friend class ::epics::nt::NTTable; + }; + +} + +typedef std::tr1::shared_ptr NTTableBuilderPtr; + + + +/** + * Convenience Class for NTTable + * @author mrk + */ class NTTable { public: POINTER_DEFINITIONS(NTTable); + + static const std::string URI; + /** - * Is the pvStructure an NTTable. - * @param pvStructure The pvStructure to test. - * @return (false,true) if (is not, is) an NTNameValuePair. + * Is the structure an NTTable. + * @param structure The structure to test. + * @return (false,true) if (is not, is) an NTTable. */ - static bool isNTTable(PVStructurePtr const &pvStructure); + static bool is_a(epics::pvData::StructureConstPtr const & structure); + /** - * Create an NTTable pvStructure. - * @param hasFunction Create a PVString field named function. - * @param hasTimeStamp Create a timeStamp structure field. - * @param hasAlarm Create an alarm structure field. - * @param numberValues The number of fields that follow the label field. - * @param valueFields The fields that follow the label field. - * @return an NTTablePtr + * Create a NTTable builder instance. + * @return builder instance. */ - static NTTablePtr create( - bool hasFunction,bool hasTimeStamp, bool hasAlarm, - shared_vector const & valueNames, - FieldConstPtrArray const &valueFields); - static NTTablePtr clone(PVStructurePtr const &); + static NTTableBuilderPtr createBuilder(); + /** - * Destructor + * Destructor. */ ~NTTable() {} - /** - * Get the function field. - * @return The pvString or null if no function field. - */ - PVStringPtr getFunction() {return pvFunction;} + /** * Attach a pvTimeStamp. * @param pvTimeStamp The pvTimeStamp that will be attached. - * Does nothing if no timeStamp + * Does nothing if no timeStamp. + * @return true if the operation was successfull (i.e. this instance has a timeStamp field), otherwise false. */ - void attachTimeStamp(PVTimeStamp &pvTimeStamp); + bool attachTimeStamp(epics::pvData::PVTimeStamp &pvTimeStamp) const; + /** * Attach an pvAlarm. * @param pvAlarm The pvAlarm that will be attached. - * Does nothing if no alarm + * Does nothing if no alarm. + * @return true if the operation was successfull (i.e. this instance has a timeStamp field), otherwise false. */ - void attachAlarm(PVAlarm &pvAlarm); + bool attachAlarm(epics::pvData::PVAlarm &pvAlarm) const; + /** * Get the pvStructure. * @return PVStructurePtr. */ - PVStructurePtr getPVStructure(){return pvNTTable;} + epics::pvData::PVStructurePtr getPVStructure() const; + + /** + * Get the descriptor field. + * @return The pvString or null if no function field. + */ + epics::pvData::PVStringPtr getDescriptor() const; + /** * Get the timeStamp. * @return PVStructurePtr which may be null. */ - PVStructurePtr getTimeStamp(){return pvTimeStamp;} + epics::pvData::PVStructurePtr getTimeStamp() const; + /** * Get the alarm. * @return PVStructurePtr which may be null. */ - PVStructurePtr getAlarm() {return pvAlarm;} + epics::pvData::PVStructurePtr getAlarm() const; + /** - * Get the label field. - * @return The pvStringArray for the label. + * Get the labels field. + * @return The pvStringArray for the labels. */ - PVStringArrayPtr getLabel() {return pvLabel;} + epics::pvData::PVStringArrayPtr getLabels() const; + /** - * Get the the number of fields that follow the label field. - * @return The number of fields. - */ - size_t getNumberValues(); - /** - * Get the Field for a field that follows the label field. - * @param index The index of the field desired. - * @return The FieldConstPtr for the field. - */ - FieldConstPtr getField(size_t index); - /** - * Get the PVField for a field that follows the label field. - * @param index The index of the field desired. + * Get the PVField (column) for a field that follows the label field. + * @param columnName The name of the column. * @return The PVFieldPtr for the field. */ - PVFieldPtr getPVField(size_t index); + epics::pvData::PVFieldPtr getColumn(std::string const & columnName) const; + + /** + * Get the PVField (column) for a field that follows the label field of a specified type (e.g. PVDoubleArray). + * @param columnName The name of the column. + * @return The field. + */ + template + std::tr1::shared_ptr getColumn(std::string const & columnName) const + { + epics::pvData::PVFieldPtr pvField = getColumn(columnName); + if (pvField.get()) + return std::tr1::dynamic_pointer_cast(pvField); + else + return std::tr1::shared_ptr(); + } + private: - NTTable(PVStructurePtr const & pvStructure); - PVStructurePtr pvNTTable; - PVStringPtr pvFunction; - PVStructurePtr pvTimeStamp; - PVStructurePtr pvAlarm; - PVStringArrayPtr pvLabel; - size_t offsetFields; + NTTable(epics::pvData::PVStructurePtr const & pvStructure); + epics::pvData::PVStructurePtr pvNTTable; + friend class detail::NTTableBuilder; }; }} diff --git a/test/nt/Makefile b/test/nt/Makefile index 21fb39b..cb1f656 100644 --- a/test/nt/Makefile +++ b/test/nt/Makefile @@ -2,6 +2,8 @@ TOP=../.. include $(TOP)/configure/CONFIG +PROD_LIBS += nt pvData Com + PROD_HOST += ntfieldTest ntfieldTest_SRCS += ntfieldTest.cpp ntfieldTest_LIBS += nt pvData Com @@ -10,9 +12,11 @@ PROD_HOST += ntnameValueTest ntnameValueTest_SRCS += ntnameValueTest.cpp ntnameValueTest_LIBS += nt pvData Com -PROD_HOST += nttableTest -nttableTest_SRCS += nttableTest.cpp -nttableTest_LIBS += nt pvData Com +TESTPROD_HOST += nttableTest +nttableTest_SRCS = nttableTest.cpp +TESTS += nttableTest + +TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES #---------------------------------------- diff --git a/test/nt/nttableTest.cpp b/test/nt/nttableTest.cpp index 78326dc..3680bd4 100644 --- a/test/nt/nttableTest.cpp +++ b/test/nt/nttableTest.cpp @@ -10,92 +10,203 @@ * Author: Marty Kraimer */ -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include +using namespace epics::nt; using namespace epics::pvData; -using std::tr1::static_pointer_cast; -using std::string; -using std::cout; -using std::endl; +using std::tr1::dynamic_pointer_cast; -static FieldCreatePtr fieldCreate = getFieldCreate(); -static PVDataCreatePtr pvDataCreate = getPVDataCreate(); -static NTFieldPtr ntField = NTField::get(); -static PVNTFieldPtr pvntField = PVNTField::get(); - -static void test(FILE * fd) +void test_builder() { - size_t n = 2; - FieldConstPtrArray fields(n); - shared_vector names(n); - names[0] = "position"; - names[1] = "alarms"; - fields[0] = fieldCreate->createScalarArray(pvDouble); - fields[1] = ntField->createAlarmArray(); - NTTablePtr ntTable = NTTable::create( - true,true,true,names,fields); - PVStructurePtr pvStructure = ntTable->getPVStructure(); - cout << *pvStructure << endl; - cout << *pvStructure->getStructure() << endl; - PVDoubleArrayPtr pvPositions - = static_pointer_cast(ntTable->getPVField(0)); - shared_vector positions(2); - positions[0] = 1.0; - positions[1] = 2.0; - pvPositions->replace(freeze(positions)); - PVStructureArrayPtr pvAlarms - = static_pointer_cast(ntTable->getPVField(1)); + testDiag("test_builder"); + + NTTableBuilderPtr builder = NTTable::createBuilder(); + testOk(builder.get() != 0, "Got builder"); + + StructureConstPtr structure = builder-> + add("column0", pvDouble)-> + add("column1", pvString)-> + add("column2", pvInt)-> + addDescriptor()-> + addAlarm()-> + addTimeStamp()-> + createStructure(); + testOk1(structure.get() != 0); + if (!structure) + return; + + testOk1(NTTable::is_a(structure)); + testOk1(structure->getID() == NTTable::URI); + testOk1(structure->getNumberFields() == 5); + testOk1(structure->getField("labels").get() != 0); + testOk1(structure->getField("value").get() != 0); + testOk1(structure->getField("descriptor").get() != 0); + testOk1(structure->getField("alarm").get() != 0); + testOk1(structure->getField("timeStamp").get() != 0); + + StructureConstPtr s = dynamic_pointer_cast(structure->getField("value")); +#define TEST_COLUMN(name, type) \ + testOk(s.get() != 0 && \ + s->getField(name).get() != 0 && \ + dynamic_pointer_cast(s->getField(name)).get() != 0 && \ + dynamic_pointer_cast(s->getField(name))->getElementType() == type, \ + name " check"); + TEST_COLUMN("column0", pvDouble); + TEST_COLUMN("column1", pvString); + TEST_COLUMN("column2", pvInt); +#undef TEST_COLUMN + + std::cout << *structure << std::endl; + + // duplicate test + try + { + structure = builder-> + add("column0", pvDouble)-> + add("column0", pvString)-> + createStructure(); + testFail("duplicate column name"); + } catch (std::runtime_error &) { + testPass("duplicate column name"); + } +} + +void test_labels() +{ + testDiag("test_labels"); + + NTTableBuilderPtr builder = NTTable::createBuilder(); + testOk(builder.get() != 0, "Got builder"); + + PVStructurePtr pvStructure = builder-> + add("column0", pvDouble)-> + add("column1", pvString)-> + add("column2", pvInt)-> + createPVStructure(); + testOk1(pvStructure.get() != 0); + if (!pvStructure) + return; + + std::cout << *pvStructure << std::endl; + + PVStringArrayPtr labels = pvStructure->getSubField("labels"); + testOk1(labels.get() != 0); + testOk1(labels->getLength() == 3); + + PVStringArray::const_svector l(labels->view()); + testOk1(l[0] == "column0"); + testOk1(l[1] == "column1"); + testOk1(l[2] == "column2"); +} + +void test_nttable() +{ + testDiag("test_nttable"); + + NTTableBuilderPtr builder = NTTable::createBuilder(); + testOk(builder.get() != 0, "Got builder"); + + NTTablePtr ntTable = builder-> + add("column0", pvDouble)-> + add("column1", pvString)-> + add("column2", pvInt)-> + addDescriptor()-> + addAlarm()-> + addTimeStamp()-> + create(); + testOk1(ntTable.get() != 0); + + testOk1(ntTable->getPVStructure().get() != 0); + testOk1(ntTable->getDescriptor().get() != 0); + testOk1(ntTable->getAlarm().get() != 0); + testOk1(ntTable->getTimeStamp().get() != 0); + testOk1(ntTable->getLabels().get() != 0); + + testOk1(ntTable->getColumn("column0").get() != 0); + testOk1(ntTable->getColumn("column1").get() != 0); + testOk1(ntTable->getColumn("column2").get() != 0); + + testOk1(ntTable->getColumn("invalid").get() == 0); + + // + // example how to set column values + // + PVIntArray::svector newValues; + newValues.push_back(1); + newValues.push_back(2); + newValues.push_back(8); + + PVIntArrayPtr intColumn = ntTable->getColumn("column2"); + intColumn->replace(freeze(newValues)); + + // + // example how to get column values + // + PVIntArray::const_svector values(intColumn->view()); + + testOk1(values.size() == 3); + testOk1(values[0] == 1); + testOk1(values[1] == 2); + testOk1(values[2] == 8); + + // + // timeStamp ops + // + PVTimeStamp pvTimeStamp; + if (ntTable->attachTimeStamp(pvTimeStamp)) + { + testPass("timeStamp attach"); + + // example how to set current time + TimeStamp ts; + ts.getCurrent(); + pvTimeStamp.set(ts); + + // example how to get EPICS time + TimeStamp ts2; + pvTimeStamp.get(ts2); + testOk1(ts2.getEpicsSecondsPastEpoch() != 0); + } + else + testFail("timeStamp attach fail"); + + // + // alarm ops + // PVAlarm pvAlarm; - Alarm alarm; - shared_vector palarms(n); - for(size_t i=0; icreateAlarm(); - pvAlarm.attach(palarms[i]); - alarm.setMessage("test"); - alarm.setSeverity(majorAlarm); - alarm.setStatus(clientStatus); + if (ntTable->attachAlarm(pvAlarm)) + { + testPass("alarm attach"); + + // example how to set an alarm + Alarm alarm; + alarm.setStatus(deviceStatus); + alarm.setSeverity(minorAlarm); + alarm.setMessage("simulation alarm"); pvAlarm.set(alarm); } - pvAlarms->replace(freeze(palarms)); - shared_vector labels(n); - labels[0] = pvPositions->getFieldName(); - labels[1] = pvAlarms->getFieldName(); - PVStringArrayPtr label = ntTable->getLabel(); - label->replace(freeze(labels)); - PVStringPtr function = ntTable->getFunction(); - function->put("test"); - ntTable->attachAlarm(pvAlarm); - alarm.setMessage("test alarm"); - alarm.setSeverity(majorAlarm); - alarm.setStatus(clientStatus); - pvAlarm.set(alarm); - PVTimeStamp pvTimeStamp; - ntTable->attachTimeStamp(pvTimeStamp); - TimeStamp timeStamp(1000,1000,10); - pvTimeStamp.set(timeStamp); - cout << *pvStructure << endl; - assert(NTTable::isNTTable(pvStructure)); + else + testFail("alarm attach fail"); + + // + // set descriptor + // + ntTable->getDescriptor()->put("This is a test NTTable"); + + // dump NTTable + std::cout << *ntTable->getPVStructure() << std::endl; + +} + +MAIN(testNTTable) { + testPlan(39); + test_builder(); + test_labels(); + test_nttable(); + return testDone(); } -int main(int argc,char *argv[]) -{ - char *fileName = 0; - if(argc>1) fileName = argv[1]; - FILE * fd = stdout; - if(fileName!=0 && fileName[0]!=0) { - fd = fopen(fileName,"w+"); - } - test(fd); - return(0); -} -