ID acdExpList TY list MO ajacd LB acd XX DE Functions for processing expressions in ACD dependencies XX // static AcdOExpList explist[] = { {"plus", &acdExpPlus}, {"minus", &acdExpMinus}, {"star", &acdExpStar}, {"div", &acdExpDiv}, {"not", &acdExpNot}, {"equal", &acdExpEqual}, {"notequal", &acdExpNotEqual}, {"greater", &acdExpGreater}, {"lesser", &acdExpLesser}, {"or", &acdExpOr}, {"and", &acdExpAnd}, {"cond", &acdExpCond}, {"oneof", &acdExpOneof}, {"case", &acdExpCase}, {"filename", &acdExpFilename}, {"exists", &acdExpExists}, {"value", &acdExpValue}, {NULL, NULL} }; ID acdKeywords TY list MO ajacd LB acd XX DE Processing predefined ACD keywords (application, variable, section, DE endsection) XX // AcdOKey acdKeywords[] = { {"qualifier", QUAL_STAGE, 0, NULL, NULL}, {"application", APPL_STAGE, 0, acdAttrAppl, acdSetAppl}, {"variable", VAR_STAGE, 0, acdAttrVar, acdSetVar}, {"relations", REL_STAGE, 0, acdAttrRel, acdSetRel}, {"section", SEC_STAGE, 0, acdAttrSec, acdSetSec}, {"endsection", ENDSEC_STAGE, 0, acdAttrEndsec, acdSetEndsec}, {NULL, BAD_STAGE, 0, NULL, NULL} }; ID acdOuttype TY list MO ajacd LB acd XX DE Processing output types and their formats XX // AcdOOuttype acdOuttype[] = { /* Name Format Type Padding Prompt Outformat */ {"outassembly", "bam", ajEOutfileTypeAssembly, 0, acdPromptOutassembly, ajAssemoutformatFind}, {"outcodon", "cut", ajEOutfileTypeCodon, 0, acdPromptOutcodon, ajCodoutformatFind}, {"outcpdb", "cpdb", ajEOutfileTypeCPDB, 0, acdPromptOutcpdb, acdOutFormatCpdb}, {"outdata", "text", ajEOutfileTypeUnknown, 0, acdPromptOutdata, acdOutFormatData}, {"outdiscrete", "phylip", ajEOutfileTypeDiscrete, 0, acdPromptOutdiscrete, acdOutFormatDiscrete}, {"outdistance", "phylip", ajEOutfileTypeDistance, 0, acdPromptOutdistance, acdOutFormatDistance}, {"outfreq", "phylip", ajEOutfileTypeFrequency, 0, acdPromptOutfreq, acdOutFormatFreq}, {"outmatrix", "emboss", ajEOutfileTypeMatrix, 0, acdPromptOutmatrix, acdOutFormatMatrix}, {"outmatrixf", "emboss", ajEOutfileTypeMatrixF, 0, acdPromptOutmatrix, acdOutFormatMatrixf}, {"outobo", "obo", ajEOutfileTypeOBO, 0, acdPromptOutobo, ajObooutformatFind}, {"outproperties", "phylip", ajEOutfileTypeProperties, 0, acdPromptOutproperties, acdOutFormatProperties}, {"outrefseq", "embl", ajEOutfileTypeRefseq, 0, acdPromptOutrefseq, ajRefseqoutformatFind}, {"outresource", "drcat", ajEOutfileTypeResource, 0, acdPromptOutresource, ajResourceoutformatFind}, {"outscop", "scop", ajEOutfileTypeSCOP, 0, acdPromptOutscop, acdOutFormatScop}, {"outtaxon", "tax", ajEOutfileTypeTaxon, 0, acdPromptOuttaxon, ajTaxoutformatFind}, {"outtext", "text", ajEOutfileTypeText, 0, acdPromptOuttext, ajTextoutformatFind}, {"outtree", "phylip", ajEOutfileTypeTree, 0, acdPromptOuttree, acdOutFormatTree}, {"outurl", "url", ajEOutfileTypeURL, 0, acdPromptOuturl, ajUrloutformatFind}, {"outvariation", "vcf", ajEOutfileTypeVariation, 0, acdPromptOutvariation, ajVaroutformatFind}, {NULL, NULL, ajEOutfileTypeUnknown, 0, NULL, NULL} }; ID acdType TY list MO ajacd LB acd XX DE Processing for ACD types DE DE Includes the acdSet functions for each ACD type XX // AcdOType acdType[] = { /* Name Group Section ** Attributes Qualifiers ** SetFunction HelpFunction Destructor ** PassRef StdPrompt PromptFunction UseCount UseClassCount ** Description of valid string for help */ {"align", "output", acdSecOutput, acdAttrAlign, acdQualAlign, &acdSetAlign, NULL, &acdDelAlign, AJTRUE, AJTRUE, &acdPromptAlign, &acdUseAlign, &acdUseOut, "Alignment output file" }, {"array", "simple", NULL, acdAttrArray, NULL, &acdSetArray, NULL, &acdDelFloat, AJTRUE, AJFALSE, NULL, &acdUseMisc, &acdUseMisc, "List of floating point numbers" }, {"assembly", "input", acdSecInput, acdAttrAssembly, acdQualAssembly, &acdSetAssembly, NULL, &acdDelAssembly, AJTRUE, AJTRUE, &acdPromptAssembly, &acdUseData, &acdUseIn, "Assembly of sequence reads" }, {"boolean", "simple", NULL, acdAttrBool, NULL, &acdSetBoolean, NULL, &acdFree, AJFALSE, AJFALSE, NULL, &acdUseMisc, &acdUseMisc, "Boolean value Yes/No" }, {"codon", "input", acdSecInput, acdAttrCodon, acdQualCodon, &acdSetCodon, NULL, &acdDelCod, AJTRUE, AJTRUE, &acdPromptCodon, &acdUseData, &acdUseIn, "Codon usage file in EMBOSS data path" }, {"cpdb", "input", acdSecInput, acdAttrCpdb, acdQualCpdb, &acdSetCpdb, NULL, &acdDelFile, AJTRUE, AJTRUE, acdPromptCpdb, &acdUseInfile, &acdUseIn, "Clean PDB file" }, {"datafile", "input", acdSecInput, acdAttrDatafile, NULL, &acdSetDatafile, NULL, &acdDelFile, AJTRUE, AJTRUE, &acdPromptDatafile, &acdUseData, &acdUseIn, "Data file" }, {"directory", "input", acdSecInput, acdAttrDirectory, acdQualDirectory, &acdSetDirectory, NULL, &acdDelDir, AJTRUE, AJTRUE, &acdPromptDirectory,&acdUseMisc, &acdUseIn, "Directory" }, {"dirlist", "input", acdSecInput, acdAttrDirlist, acdQualDirlist, &acdSetDirlist, NULL, &acdDelList, AJTRUE, AJTRUE, &acdPromptDirlist, &acdUseMisc, &acdUseIn, "Directory with files" }, {"discretestates", "input", acdSecInput, acdAttrDiscretestates, NULL, &acdSetDiscretestates, NULL, &acdDelPhyloState, AJTRUE, AJTRUE, &acdPromptDiscretestates, &acdUseData, &acdUseIn, "Discrete states file" }, {"distances", "input", acdSecInput, acdAttrDistances, NULL, &acdSetDistances, NULL, &acdDelPhyloDist, AJTRUE, AJTRUE, &acdPromptDistances, &acdUseData, &acdUseIn, "Distance matrix" }, {"featout", "output", acdSecOutput, acdAttrFeatout, acdQualFeatout, &acdSetFeatout, NULL, &acdDelFeattabOut, AJTRUE, AJTRUE, &acdPromptFeatout, &acdUseFeatout, &acdUseOut, "Writeable feature table" }, {"features", "input", acdSecInput, acdAttrFeatures, acdQualFeatures, &acdSetFeatures, NULL, &acdDelFeattable, AJTRUE, AJTRUE, &acdPromptFeatures, &acdUseFeatures, &acdUseIn, "Readable feature table" }, {"filelist", "input", acdSecInput, acdAttrFilelist, NULL, &acdSetFilelist, NULL, &acdDelList, AJTRUE, AJTRUE, &acdPromptFilelist, &acdUseMisc, &acdUseIn, "Comma-separated file list" }, {"float", "simple", NULL, acdAttrFloat, NULL, &acdSetFloat, NULL, &acdFree, AJFALSE, AJFALSE, NULL, &acdUseMisc, &acdUseMisc, "Floating point number" }, {"frequencies", "input", acdSecInput, acdAttrFrequencies, NULL, &acdSetFrequencies, NULL, &acdDelPhyloFreq, AJTRUE, AJTRUE, &acdPromptFrequencies, &acdUseData, &acdUseIn, "Frequency value(s)" }, {"graph", "graph", acdSecOutput, acdAttrGraph, acdQualGraph, &acdSetGraph, NULL, &acdFree, AJTRUE, AJTRUE, &acdPromptGraph, &acdUseGraph, &acdUseOut, "Graph device for a general graph" }, {"infile", "input", acdSecInput, acdAttrInfile, NULL, &acdSetInfile, NULL, &acdDelFile, AJTRUE, AJTRUE, &acdPromptInfile, &acdUseInfile, &acdUseIn, "Input file" }, {"integer", "simple", NULL, acdAttrInt, NULL, &acdSetInt, NULL, &acdFree, AJFALSE, AJFALSE, NULL, &acdUseMisc, &acdUseMisc, "Integer" }, {"list", "selection", NULL, acdAttrList, NULL, acdSetList, NULL, acdDelStrArray, AJTRUE, AJFALSE, NULL, &acdUseMisc, &acdUseMisc, "Choose from menu list of values" }, {"matrix", "input", acdSecInput, acdAttrMatrix, NULL, &acdSetMatrix, NULL, &acdDelMatrix, AJTRUE, AJFALSE, &acdPromptMatrix, &acdUseData, &acdUseIn, "Comparison matrix file in EMBOSS data path" }, {"matrixf", "input", acdSecInput, acdAttrMatrixf, NULL, &acdSetMatrixf, NULL, &acdDelMatrixf, AJTRUE, AJFALSE, &acdPromptMatrix, &acdUseData, &acdUseIn, "Comparison matrix file in EMBOSS data path" }, {"obo", "input", acdSecInput, acdAttrObo, acdQualObo, &acdSetObo, &acdHelpTextObo, &acdDelObo, AJTRUE, AJTRUE, &acdPromptObo, &acdUseData, &acdUseIn, "OBO bio-ontology term(s)" }, {"outassembly", "output", acdSecOutput, acdAttrOutassembly, acdQualOutassembly, &acdSetOutassembly, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutassembly, &acdUseOutfile, &acdUseOut, "Assembly of sequence reads" }, {"outcodon", "output", acdSecOutput, acdAttrOutcodon, acdQualOutcodon, &acdSetOutcodon, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutcodon, &acdUseOutfile, &acdUseOut, "Codon usage file" }, {"outcpdb", "output", acdSecOutput, acdAttrOutcpdb, NULL, &acdSetOutcpdb, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutcpdb, &acdUseOutfile, &acdUseOut, "Cleaned PDB file" }, {"outdata", "output", acdSecOutput, acdAttrOutdata, acdQualOutdata, &acdSetOutdata, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutdata, &acdUseOutfile, &acdUseOut, "Formatted output file" }, {"outdir", "output", acdSecOutput, acdAttrOutdir, acdQualOutdir, &acdSetOutdir, NULL, &acdDelDirout, AJTRUE, AJTRUE, &acdPromptOutdir, &acdUseMisc, &acdUseOut, "Output directory" }, {"outdiscrete", "output", acdSecOutput, acdAttrOutdiscrete, acdQualOutdiscrete, &acdSetOutdiscrete, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutdiscrete, &acdUseOutfile, &acdUseOut, "Discrete states file" }, {"outdistance", "output", acdSecOutput, acdAttrOutdistance, NULL, &acdSetOutdistance, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutdistance, &acdUseOutfile, &acdUseOut, "Distance matrix" }, {"outfile", "output", acdSecOutput, acdAttrOutfile, acdQualOutfile, &acdSetOutfile, NULL, &acdDelFile, AJTRUE, AJTRUE, &acdPromptOutfile, &acdUseOutfile, &acdUseOut, "Output file" }, {"outfreq", "output", acdSecOutput, acdAttrOutfreq, acdQualOutfreq, &acdSetOutfreq, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutfreq, &acdUseOutfile, &acdUseOut, "Frequency value(s)" }, {"outmatrix", "output", acdSecOutput, acdAttrOutmatrix, acdQualOutmatrix, &acdSetOutmatrix, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutmatrix, &acdUseOutfile, &acdUseOut, "Comparison matrix file" }, {"outmatrixf", "output", acdSecOutput, acdAttrOutmatrixf, acdQualOutmatrixf, &acdSetOutmatrixf, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutmatrix, &acdUseOutfile, &acdUseOut, "Comparison matrix file" }, {"outobo", "output", acdSecOutput, acdAttrOutobo, acdQualOutobo, &acdSetOutobo, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutobo, &acdUseOutfile, &acdUseOut, "OBO ontology term(s)" }, {"outproperties", "output", acdSecOutput, acdAttrOutproperties, acdQualOutproperties, &acdSetOutproperties, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutproperties, &acdUseOutfile, &acdUseOut, "Property value(s)" }, {"outrefseq", "output", acdSecOutput, acdAttrOutrefseq, acdQualOutrefseq, &acdSetOutrefseq, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutrefseq, &acdUseOutfile, &acdUseOut, "Reference sequence" }, {"outresource", "output", acdSecOutput, acdAttrOutresource, acdQualOutresource, &acdSetOutresource, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutresource, &acdUseOutfile, &acdUseOut, "Data resource entry" }, {"outscop", "output", acdSecOutput, acdAttrOutscop, acdQualOutscop, &acdSetOutscop, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutscop, &acdUseOutfile, &acdUseOut, "Scop entry" }, {"outtaxon", "output", acdSecOutput, acdAttrOuttaxon, acdQualOuttaxon, &acdSetOuttaxon, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOuttaxon, &acdUseOutfile, &acdUseOut, "NCBI taxonomy entries" }, {"outtext", "output", acdSecOutput, acdAttrOuttext, acdQualOuttext, &acdSetOuttext, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOuttext, &acdUseOutfile, &acdUseOut, "Text entries" }, {"outtree", "output", acdSecOutput, acdAttrOuttree, acdQualOuttree, &acdSetOuttree, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOuttree, &acdUseOutfile, &acdUseOut, "Phylogenetic tree" }, {"outurl", "output", acdSecOutput, acdAttrOuturl, acdQualOuturl, &acdSetOuturl, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOuturl, &acdUseOutfile, &acdUseOut, "URL entries" }, {"outvariation", "output", acdSecOutput, acdAttrOutvariation, acdQualOutvariation, &acdSetOutvariation, NULL, &acdDelOutfile, AJTRUE, AJTRUE, &acdPromptOutvariation, &acdUseOutfile, &acdUseOut, "Variation entries" }, {"pattern", "input", acdSecInput, acdAttrPattern, acdQualPattern, &acdSetPattern, NULL, &acdDelPatlist, AJTRUE, AJTRUE, &acdPromptPattern, &acdUseData, &acdUseIn, "Property value(s)" }, {"properties", "input", acdSecInput, acdAttrProperties, NULL, &acdSetProperties, NULL, &acdDelPhyloProp, AJTRUE, AJTRUE, &acdPromptProperties, &acdUseData, &acdUseIn, "Property value(s)" }, {"range", "simple", NULL, acdAttrRange, NULL, &acdSetRange, NULL, &acdDelRange, AJTRUE, AJFALSE, NULL, &acdUseMisc, &acdUseMisc, "Sequence range" }, {"refseq", "input", acdSecInput, acdAttrRefseq, acdQualRefseq, &acdSetRefseq, NULL, &acdDelRefseq, AJTRUE, AJTRUE, &acdPromptRefseq, &acdUseData, &acdUseIn, "Reference sequence" }, {"regexp", "input", acdSecInput, acdAttrRegexp, acdQualRegexp, &acdSetRegexp, NULL, &acdDelReg, AJTRUE, AJTRUE, &acdPromptRegexp, &acdUseMisc, &acdUseIn, "Regular expression pattern" }, {"report", "output", acdSecOutput, acdAttrReport, acdQualReport, &acdSetReport, NULL, &acdDelReport, AJTRUE, AJTRUE, &acdPromptReport, &acdUseReport, &acdUseOut, "Report output file" }, {"resource", "input", acdSecInput, acdAttrResource, acdQualResource, &acdSetResource, &acdHelpTextResource, &acdDelResource, AJTRUE, AJTRUE, &acdPromptResource, &acdUseData, &acdUseIn, "Data resource catalogue entry(s)" }, {"scop", "input", acdSecInput, acdAttrScop, acdQualScop, &acdSetScop, NULL, &acdDelFile, AJTRUE, AJTRUE, &acdPromptScop, &acdUseInfile, &acdUseIn, "Clean PDB file" }, {"selection", "selection", NULL, acdAttrSelect, NULL, &acdSetSelect, NULL, &acdDelStrArray, AJTRUE, AJFALSE, NULL, &acdUseMisc, &acdUseMisc, "Choose from selection list of values" }, {"sequence", "input", acdSecInput, acdAttrSeq, acdQualSeq, &acdSetSeq, &acdHelpTextSeq, &acdDelSeq, AJTRUE, AJTRUE, &acdPromptSeq, &acdUseSeq, &acdUseIn, "Readable sequence" }, {"seqall", "input", acdSecInput, acdAttrSeqall, acdQualSeqall, &acdSetSeqall, &acdHelpTextSeq, &acdDelSeqall, AJTRUE, AJTRUE, &acdPromptSeq, &acdUseSeq, &acdUseIn, "Readable sequence(s)" }, {"seqout", "output", acdSecOutput, acdAttrSeqout, acdQualSeqout, &acdSetSeqout, &acdHelpTextSeqout, &acdDelSeqout, AJTRUE, AJTRUE, &acdPromptSeqout, &acdUseSeqout, &acdUseOut, "Writeable sequence" }, {"seqoutall", "output", acdSecOutput, acdAttrSeqoutall, acdQualSeqoutall, &acdSetSeqoutall, &acdHelpTextSeqout, &acdDelSeqout, AJTRUE, AJTRUE, &acdPromptSeqout, &acdUseSeqout, &acdUseOut, "Writeable sequence(s)" }, {"seqoutset", "output", acdSecOutput, acdAttrSeqoutset, acdQualSeqoutset, &acdSetSeqoutset, &acdHelpTextSeqout, &acdDelSeqout, AJTRUE, AJTRUE, &acdPromptSeqout, &acdUseSeqout, &acdUseOut, "Writeable sequences" }, {"seqset", "input", acdSecInput, acdAttrSeqset, acdQualSeqset, &acdSetSeqset, &acdHelpTextSeq, &acdDelSeqset, AJTRUE, AJTRUE, &acdPromptSeq, &acdUseSeq, &acdUseIn, "Readable set of sequences" }, {"seqsetall", "input", acdSecInput, acdAttrSeqsetall, acdQualSeqsetall, &acdSetSeqsetall, &acdHelpTextSeq, &acdDelSeqsetArray, AJTRUE, AJTRUE, &acdPromptSeq, &acdUseSeq, &acdUseIn, "Readable sets of sequences" }, {"string", "simple", NULL, acdAttrString, NULL, acdSetString, NULL, acdDelStr, AJTRUE, AJFALSE, NULL, &acdUseMisc, &acdUseMisc, "String value" }, {"taxon", "input", acdSecInput, acdAttrTaxon, acdQualTaxon, &acdSetTaxon, &acdHelpTextTaxon, &acdDelTaxon, AJTRUE, AJTRUE, &acdPromptTaxon, &acdUseData, &acdUseIn, "NCBI taxonomy entries" }, {"text", "input", acdSecInput, acdAttrText, acdQualText, &acdSetText, &acdHelpTextText, &acdDelText, AJTRUE, AJTRUE, &acdPromptText, &acdUseData, &acdUseIn, "Text entries" }, {"toggle", "simple", NULL, acdAttrToggle, NULL, &acdSetToggle, NULL, &acdFree, AJFALSE, AJFALSE, NULL, &acdUseMisc, &acdUseMisc, "Toggle value Yes/No" }, {"tree", "input", acdSecInput, acdAttrTree, NULL, &acdSetTree, NULL, &acdDelPhyloTree, AJTRUE, AJTRUE, &acdPromptTree, &acdUseData, &acdUseIn, "Phylogenetic tree" }, {"url", "input", acdSecInput, acdAttrUrl, acdQualUrl, &acdSetUrl, &acdHelpTextUrl, &acdDelUrl, AJTRUE, AJTRUE, &acdPromptUrl, &acdUseData, &acdUseIn, "URL entries" }, {"variation", "input", acdSecInput, acdAttrVariation, acdQualVariation, &acdSetVariation, &acdHelpTextVariation, &acdDelVariation, AJTRUE, AJTRUE, &acdPromptVariation, &acdUseData, &acdUseIn, "Variation entries" }, {"xygraph", "graph", acdSecOutput, acdAttrGraphxy, acdQualGraphxy, &acdSetGraphxy, NULL, &acdFree, AJTRUE, AJTRUE, &acdPromptGraph, &acdUseGraph, &acdUseOut, "Graph device for a 2D graph" }, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, AJFALSE, AJFALSE, 0, NULL, NULL, NULL} }; ID acdValue TY list MO ajacd LB acd XX DE ACD type help processing, includes functions to describe valid DE values and expected values in -help output and -acdtable output XX // AcdOValid acdValue[] = { {"align", acdHelpValidAlign, acdHelpExpectOut}, {"codon", acdHelpValidCodon, acdHelpExpectCodon}, {"datafile", acdHelpValidData, acdHelpExpectData}, {"dirlist", acdHelpValidDirlist, acdHelpExpectDirlist}, {"featout", acdHelpValidFeatout, acdHelpExpectFeatout}, {"features", acdHelpValidFeatures, acdHelpExpectFeatures}, {"filelist", acdHelpValidFilelist, acdHelpExpectFilelist}, {"float", acdHelpValidFloat, acdHelpExpectFloat}, {"graph", acdHelpValidGraph, acdHelpExpectGraph}, {"infile", acdHelpValidIn, acdHelpExpectIn}, {"integer", acdHelpValidInt, acdHelpExpectInt}, {"list", acdHelpValidList, NULL}, {"matrix", acdHelpValidMatrix, acdHelpExpectMatrix}, {"matrixf", acdHelpValidMatrix, acdHelpExpectMatrix}, {"outfile", acdHelpValidOut, acdHelpExpectOut}, {"outobo", acdHelpValidOut, acdHelpExpectOut}, {"outresource", acdHelpValidOut, acdHelpExpectOut}, {"regexp", acdHelpValidRegexp, acdHelpExpectRegexp}, {"report", acdHelpValidReport, acdHelpExpectOut}, {"select", acdHelpValidSelect, NULL}, {"seqall", acdHelpValidSeq, acdHelpExpectSeq}, {"seqset", acdHelpValidSeq, acdHelpExpectSeq}, {"seqsetall", acdHelpValidSeq, acdHelpExpectSeq}, {"sequence", acdHelpValidSeq, acdHelpExpectSeq}, {"seqout", acdHelpValidSeqout, acdHelpExpectSeqout}, {"seqoutall", acdHelpValidSeqout, acdHelpExpectSeqout}, {"seqoutset", acdHelpValidSeqout, acdHelpExpectSeqout}, {"string", acdHelpValidString, acdHelpExpectString}, {"range", acdHelpValidRange, acdHelpExpectRange}, {"xygraph", acdHelpValidGraph, acdHelpExpectGraph}, {NULL, NULL, NULL} }; ID ajAcdInit TY public MO ajacd LB acd XX DE Initialises everything. Reads an ACD (AJAX Command Definition) file DE prompts the user for any missing information, reads all sequences DE and other input into local structures which applications can request. XX PN [1] PA r pgm const char* PD Application name, used as the name PD of the ACD file PX PN [2] PA r argc ajint PD Number of arguments provided on the command line, PD usually passed as-is by the calling application. PX PN [3] PA r argv char* const[] PD Actual arguments as an array of text. PX RT void RD RX // void ajAcdInit(const char *pgm, ajint argc, char * const argv[]) { ajAcdInitPV(pgm, argc, argv, "", ""); return; } ID ajAcdInitPV TY public MO ajacd LB acd XX DE Initialises everything. Reads an ACD (AJAX Command Definition) file DE prompts the user for any missing information, reads all sequences DE and other input into local structures which applications can request. XX PN [1] PA r pgm const char* PD Application name, used as the name PD of the ACD file PX PN [2] PA r argc ajint PD Number of arguments provided on the command line, PD usually passed as-is by the calling application. PX PN [3] PA r argv char* const[] PD Actual arguments as an array of text. PX PN [4] PA r package const char* PD Package name, used to find the ACD file PX PN [5] PA r packversion const char* PD Package version PX RT void RD RX // void ajAcdInitPV(const char *pgm, ajint argc, char * const argv[], const char *package, const char *packversion) { static AjPFile acdFile = NULL; static AjPStr acdLine = NULL; static AjPStr acdRoot = NULL; static AjPStr acdRootInst = NULL; static AjPStr acdPack = NULL; static AjPStr acdPackRoot = NULL; static AjPStr acdPackRootName = NULL; static AjPStr acdUtilRoot = NULL; AjPStr applversion = NULL; AjPStr versionstr = NULL; AjPStr comment = NULL; AjPStrTok tokenhandle = NULL; char white[] = " \t\n\r"; AjPList acdListWords = NULL; AjPList acdListCount = NULL; AjPStr tmpword = NULL; /* words to add to acdListWords */ ajint i; ajint *k = NULL; ajint *kc = NULL; ajuint *kp = NULL; size_t pos; acdProgram = ajStrNewC(pgm); acdSecList = ajListstrNew(); acdSecTable = ajTablestrNewCase(50); acdLog("testing acdprompts"); if(ajNamGetValueC("acdprompts", &acdTmpStr)) { acdLog("acdprompts '%S'", acdTmpStr); if(ajStrToInt(acdTmpStr, &i)) acdPromptTry = i; if(acdPromptTry < 1) acdPromptTry = 1; acdLog("acdPromptTry %d", acdPromptTry); } if(ajNamGetValueC("acdlog", &acdTmpStr)) ajStrToBool(acdTmpStr, &acdDoLog); if(ajNamGetValueC("acdwarnrange", &acdTmpStr)) ajStrToBool(acdTmpStr, &acdDoWarnRange); /* pre-parse the command line for special options */ acdArgsScan(argc, argv); ajDebug("ajAcdInitPV pgm '%s' package '%s'\n", pgm, package); ajDebug(" version '%S' system '%S'\n", ajNamValueVersion(), ajNamValueSystem()); /* open the command definition file */ if(*package) ajStrAssignC(&acdPackName, package); if(*packversion) ajStrAssignC(&acdPackVersion, packversion); ajStrAssignS(&acdPack, ajNamValuePackage()); ajStrAssignS(&acdRootInst, ajNamValueInstalldir()); ajDirnameFix(&acdRootInst); if(ajNamGetValueC("acdroot", &acdRoot)) { /* _acdroot variable defined */ ajDirnameFix(&acdRoot); ajFmtPrintS(&acdFName, "%S%s.acd", acdRoot, pgm); acdLog("Trying acdfile '%S' (acdroot)\n", acdFName); acdFile = ajFileNewInNameS(acdFName); } else if(*package) { /* separate package */ /* ajFmtPrintS(&acdFName, "%Sshare/%S/acd/%s.acd", acdRootInst, acdPack, pgm);*/ ajFmtPrintS(&acdFName, "%Sshare/EMBOSS/acd/%s.acd", acdRootInst, pgm); acdLog("Trying acdfile '%S' (package '%s' installed)\n", acdFName, package); acdFile = ajFileNewInNameS(acdFName); if(!acdFile) { acdLog("acdfile '%S' not opened\n", acdFName); ajStrAssignC(&acdPack, package); /* package name for acdInitPV */ ajStrFmtLower(&acdPack); ajStrAssignS(&acdPackRootName, acdPack); ajStrAppendC(&acdPackRootName, "acdroot"); if(ajNamGetValueS(acdPackRootName, &acdPackRoot)) { ajDirnameFix(&acdPackRoot); ajFmtPrintS(&acdFName, "%S%s.acd", acdPackRoot, pgm); acdLog("Trying acdfile '%S' (package %sacdroot)\n", acdFName, package); } else { ajStrAssignS(&acdPackRoot, ajNamValueRootdir()); ajDirnameUp(&acdPackRoot); ajFmtPrintS(&acdFName, "%Sembassy/%S/emboss_acd/%s.acd", acdPackRoot, acdPack, pgm); acdLog("Trying acdfile '%S' (package %s source)\n", acdFName, package); } acdFile = ajFileNewInNameS(acdFName); } } else { /* main package */ ajFmtPrintS(&acdFName, "%Sshare/%S/acd/%s.acd", acdRootInst, acdPack, pgm); acdLog("Trying acdfile '%S' (installed)\n", acdFName); acdFile = ajFileNewInNameS(acdFName); if(!acdFile) { acdLog("acdfile '%S' not opened\n", acdFName); ajStrAssignC(&acdPack, "emboss"); if(ajNamGetValueC("acdutilroot", &acdUtilRoot)) { ajDirnameFix(&acdUtilRoot); ajFmtPrintS(&acdFName, "%S%s.acd", acdUtilRoot, pgm); acdLog("Trying acdfile '%S' (acdutilroot)\n", acdFName); acdFile = ajFileNewInNameS(acdFName); } } if(!acdFile) { acdLog("acdfile '%S' not opened\n", acdFName); ajStrAssignS(&acdRoot, ajNamValueRootdir()); ajDirnameFix(&acdRoot); ajFmtPrintS(&acdFName, "%Sacd/%s.acd", acdRoot, pgm); acdLog("Trying acdfile '%S' (original main source)\n", acdFName); acdFile = ajFileNewInNameS(acdFName); if(!acdFile) { acdLog("acdfile '%S' not opened\n", acdFName); } } } if(!acdFile) /* test by nofile.acd */ acdError("ACD file not opened\n"); /* read the whole file into a string [change to use a list later] */ acdListWords = ajListstrNew(); acdListCount = ajListNew(); acdListComments = ajListstrNew(); acdListCommentsCount = ajListNew(); acdListCommentsColumn = ajListNew(); while(ajReadlineTrim(acdFile, &acdLine)) { AJNEW0(k); *k = (ajuint) ajListGetLength(acdListWords); ajListPushAppend(acdListCount, k); if(ajStrCutCommentsRestpos(&acdLine, &comment, &pos)) { tokenhandle = ajStrTokenNewC(acdLine, white); while(ajStrTokenNextParse(&tokenhandle, &tmpword)) { if(ajStrGetLen(tmpword)) /* nothing before first whitespace */ { ajListstrPushAppend(acdListWords, tmpword); tmpword = NULL; } else { ajStrDel(&tmpword); } } ajStrTokenDel(&tokenhandle); ajStrDel(&tmpword); /* empty token at the end */ } if (ajStrGetLen(comment)) { ajListstrPushAppend(acdListComments, comment); comment = NULL; AJNEW(kc); *kc = (ajuint) ajListGetLength(acdListWords); ajListPushAppend(acdListCommentsCount, kc); kc = NULL; AJNEW(kp); *kp = pos; ajListPushAppend(acdListCommentsColumn, kp); kp = NULL; } } ajFileClose(&acdFile); ajStrDel(&acdLine); AJNEW0(k); *k = (ajuint) ajListGetLength(acdListWords); ajListPushAppend(acdListCount, k); acdCodeInit(); acdKnowntypeInit(); /* Parse the input to set the initial definitions */ acdParse(acdListWords, acdListCount); ajListstrFreeData(&acdListWords); ajListFreeData(&acdListCount); if(acdDoVersion) { ajFmtPrintS(&versionstr, "EMBOSS:%s", VERSION); if(ajStrGetLen(versionstr) < 14) ajStrAppendC(&versionstr, ".0"); if(ajStrGetLen(acdPackVersion)) ajFmtPrintAppS(&versionstr, " %S:%S", acdPackName, acdPackVersion); acdAttrResolve(acdApplAcd, "versionnumber", &applversion); if(ajStrGetLen(applversion)) { ajFmtPrintAppS(&versionstr, " %s:%S", pgm, applversion); ajStrDel(&applversion); } ajUserDumpS(versionstr); ajStrDel(&versionstr); ajExit(); } if(acdDoPretty || acdDoValid) ajExit(); /* Fill in incomplete information like parameter numbers */ acdProcess(); AJCNEW0(acdParamSet, acdNParam+1); /* report on what we have so far */ acdListReport("Definitions in ACD file"); /* parse the command line and update the values */ acdArgsParse(argc, argv); /* report on what we have so far */ acdListReport("Results of parsing command line arguments"); /* set the true values and prompt for missing standard values */ if(acdDoTable || acdDoXsd || acdDoGalaxy) acdHelp(); acdSetAll(); /* report on what we have now */ acdListReport("Final results after setting values and prompting the user"); /* all done */ ajStrDel(&acdRoot); ajStrDel(&acdRootInst); ajStrDel(&acdPack); ajStrDel(&acdPackRoot); ajStrDel(&acdPackRootName); ajStrDel(&acdFName); ajStrDel(&comment); return; } ID acdStage TY static MO ajacd LB acd XX DE Tests next token to set the next parsing stage. XX PN [1] PA r token const AjPStr PD Current token PX RT AcdEStage RD Stage enumerated code RX // static AcdEStage acdStage(const AjPStr token) { ajint i; ajint ifound=0; AcdEStage j=BAD_STAGE; AjPStr ambigList = NULL; ambigList = ajStrNew(); if(!ajStrGetLen(token)) return BAD_STAGE; i = QUAL_STAGE + 1; while(acdKeywords[i].Name) /* ACD keywords */ { if(ajStrMatchC(token, acdKeywords[i].Name)) return acdKeywords[i].Stage; if(ajCharPrefixS(acdKeywords[i].Name, token)) { ifound++; j = acdKeywords[i].Stage; acdAmbigAppC(&ambigList, acdKeywords[i].Name); } i++; } i = 0; while(acdType[i].Name) /* ACD types as qualifiers */ { if(ajStrMatchC(token, acdType[i].Name)) return QUAL_STAGE; if(ajCharPrefixS(acdType[i].Name, token)) { ifound++; j = QUAL_STAGE; acdAmbigAppC(&ambigList, acdType[i].Name); } i++; } if(ifound == 1) { if (acdDoValid) acdWarn("Abbreviated stage '%S' (%S)", token, ambigList); return j; } if(ifound > 1) { /* test ambigtype.acd */ acdError("ambiguous acd type %S (%S)", token, ambigList); ajStrDel(&ambigList); } ajStrDel(&ambigList); return BAD_STAGE; } ID acdParse TY static MO ajacd LB acd XX DE Parse the command line definition and build data structures from it. XX PN [1] PA u listwords AjPList PD List of words (as strings) PD from ACD file. List empty on completion PX PN [2] PA u listcount AjPList PD List of word count before each line. PD List empty on completion PX RT void RD RX // static void acdParse(AjPList listwords, AjPList listcount) { AjPStr acdStrType = NULL; AjPStr acdStrAlias = NULL; AjPStr acdStrValue = NULL; AjPStr secname = NULL; ajint linecount = 0; ajint lineword = 0; ajint *iword = NULL; ajint *icword = NULL; ajint *icpos = NULL; AjPStr cmtstr = NULL; AjPTime today = NULL; /* initialise the global line number counter to zero */ acdLineNum = 0; if(acdDoValid) { if(ajNamGetValueC("edam", &acdEdamPath)) { acdEdamFile = ajFileNewInNameS(acdEdamPath); if(acdEdamFile) acdEdam = ajObodataParseObofile(acdEdamFile, "none"); } } while(ajListGetLength(acdListCommentsCount) && !acdCmtWord) { ajListPeek(acdListCommentsCount, (void**) &icword); acdCmtWord = *icword; if(!acdCmtWord) { ajStrDel(&cmtstr); ajListPop(acdListCommentsCount, (void**) &icword); ajListPop(acdListCommentsColumn, (void**) &icpos); ajListstrPop(acdListComments, &cmtstr); acdPrettyComment(cmtstr); AJFREE(icword); AJFREE(icpos); } } if(ajStrGetLen(cmtstr)) { acdPretty("\n"); ajStrDel(&cmtstr); } while(ajListGetLength(listcount) && (!lineword)) { ajListPeek(listcount, (void**) &iword); if(*iword) lineword = *iword; else { ajListPop(listcount, (void**) &iword); linecount++; acdLineNum = linecount - 1; AJFREE(iword); } } lineword = 0; acdWordNum = 0; while(ajListGetLength(listwords)) { acdWordNextName(listwords, &acdStrType); while(ajListGetLength(listcount) && (lineword < acdWordNum)) { ajListPop(listcount, (void**) &iword); lineword = *iword; linecount++; acdLineNum = linecount - 1; AJFREE(iword); } acdCurrentStage = acdStage(acdStrType); if(acdWordNum == 1) if(acdCurrentStage != APPL_STAGE) /* test noappl.acd */ acdError("Application definition required at start"); switch(acdCurrentStage) { case APPL_STAGE: if(acdWordNum != 1) acdError("Application definition allowed only at start"); /* application: then the appl name */ acdParseName(listwords, &acdStrName); if (!ajStrMatchS(acdStrName, acdProgram)) acdError("Application name '%S' does not match filename '%S'", acdStrName, acdProgram); acdNewCurr = acdNewAppl(acdStrName); acdApplAcd = acdNewCurr; acdWordNum++; /* add one for '[' */ acdPretty("%s: %S [\n", acdKeywords[acdCurrentStage].Name, acdStrName); acdWordNum--; /* not yet parsed '[' */ acdParseAttributes(acdNewCurr, listwords); acdAttrToBool(acdNewCurr, "wrapper", ajFalse, &acdWrapper); acdValidAppl(acdNewCurr); /* automatic $(today) variable */ ajStrAssignC(&acdStrName, "today"); today = ajTimeNewToday(); ajFmtPrintS(&acdStrValue, "%D", today); ajTimeDel(&today); acdNewCurr = acdNewVar(acdStrName); acdSetVarDef(acdNewCurr, acdStrValue); break; /* type: qualname alias[ attr: value ] ** ** The alias name is optional (defaults to the qualifier name) ** The [] are required so the token can be detected. Attributes ** are defined for each "type", as are associated ** qualifiers. There is no distinction between them here. The ** difference is that the qualifier values are defaults which ** can be overridden on the command line */ case QUAL_STAGE: acdParseAlpha(listwords, &acdStrName); if(acdNotLeftB(listwords)) { /* test badalias.acd */ if(!acdWordNextLower(listwords, &acdStrAlias) || !ajStrIsAlpha(acdStrAlias)) acdError("Bad syntax qualifier alias name '%S'", acdStrAlias); } else /* we have an alternate name before the '[' */ ajStrAssignS(&acdStrAlias, acdStrName); acdNewCurr = acdNewQual(acdStrName, acdStrAlias, &acdStrType); acdWordNum++; /* add one for '[' */ if(!ajStrMatchS(acdStrName, acdStrAlias)) acdPretty("\n%S: %S %S [\n", acdStrType, acdStrName, acdStrAlias); else acdPretty("\n%S: %S [\n", acdStrType, acdStrName); acdWordNum--; /* not yet parsed '[' */ acdParseAttributes(acdNewCurr, listwords); acdValidQual(acdNewCurr); break; case SEC_STAGE: /* section: name [ attrlist ] */ acdParseName(listwords, &acdStrName); acdNewCurr = acdNewSec(acdStrName); acdWordNum++; /* add one for '[' */ acdPretty("\n%s: %S [\n", acdKeywords[acdCurrentStage].Name, acdStrName); acdWordNum--; /* not yet parsed '[' */ acdParseAttributes(acdNewCurr, listwords); acdValidSection(acdNewCurr); acdPrettyShift(); break; case ENDSEC_STAGE: /* endsection: name */ /* remove from list of current sections */ acdParseName(listwords, &acdStrName); acdNewCurr = acdNewEndsec(acdStrName); acdValidSection(acdNewCurr); acdPrettyUnShift(); acdPretty("\n%s: %S\n", acdKeywords[acdCurrentStage].Name, acdStrName); break; /* catch-all for failed parsing */ case VAR_STAGE: /* then the variable name and the value */ acdParseName(listwords, &acdStrName); ajStrAssignS(&acdStrValue, acdParseValue(listwords)); acdNewCurr = acdNewVar(acdStrName); acdSetVarDef(acdNewCurr, acdStrValue); acdPretty("\n%s: %S \"%S\"\n", acdKeywords[acdCurrentStage].Name, acdStrName, acdStrValue); break; case REL_STAGE: /* relation: name [ attrlist ] */ acdParseName(listwords, &acdStrName); acdNewCurr = acdNewRel(acdStrName); acdWordNum++; /* add one for '[' */ acdPretty("\n%s: %S [\n", acdKeywords[acdCurrentStage].Name, acdStrName); acdWordNum--; /* not yet parsed '[' */ acdParseAttributes(acdNewCurr, listwords); acdValidRelation(acdNewCurr); acdPrettyShift(); break; case BAD_STAGE: /* test badstage.acd */ default: /* Fatal - should never happen */ acdError("Unrecognized token '%S'\n", acdStrType); break; } } acdLog("-- All Done --\n"); acdLog("-- All Done : acdSecList length %d\n", ajListstrGetLength(acdSecList)); acdLineNum = linecount; if(ajListstrGetLength(acdSecList)) /* fatal error, unclosed section(s) */ { while(ajListstrPop(acdSecList, &secname)) { ajDebug("Section '%S' has no endsection\n", secname); ajErr("Section '%S' has no endsection", secname); /* fails below */ ajStrDel(&secname); } acdLog("Unclosed sections in ACD file\n"); acdError("Unclosed sections in ACD file"); /* test noendsec.acd */ } acdPrettyClose(); ajStrDel(&acdStrName); /* the global string ... no longer needed */ ajStrDel(&acdStrAlias); ajStrDel(&acdStrType); ajStrDel(&acdStrValue); ajListstrFreeData(&acdSecList); acdLineNum = 0; while(ajListGetLength(listcount)) { ajListPop(listcount, (void**) &iword); AJFREE(iword); } return; } ID acdParseValue TY static MO ajacd LB acd XX DE Uses ajStrTok to complete a (possibly) quoted value. DE Note that ajStrTok has a stored internal copy of the text string DE which is set up at the start of acdParse and is being used here. DE DE Quotes can be single or double. DE DE The early versions also allowed any kind of parentheses, DE depending on the first character of the next token examined. DE This is now obsolete, to simplify the syntax and to allow DE future reuse of parentheses. XX PN [1] PA u listwords AjPList PD List of strings for each word PD to be parsed PX RT AjPStr RD String containing next value from list RX // static AjPStr acdParseValue(AjPList listwords) { AjPStr strp=NULL; char endq[]=" "; ajint iquote; char *cq; AjBool done = ajFalse; const char *quotes = "\"'"; const char *endquotes = "\"'"; acdParseQuotes = ajFalse; if(!acdWordNext(listwords, &strp)) /* test: novalue.acd */ acdErrorAcd(acdNewCurr, "Unexpected end of file, attribute value not found\n"); cq = strchr(quotes, ajStrGetCharFirst(strp)); if(!cq) /* no quotes, simple return */ { ajStrAssignS(&acdParseReturn, strp); ajStrDel(&strp); return acdParseReturn; } acdParseQuotes = ajTrue; /* quote found: parse up to closing quote then strip white space */ iquote = cq - quotes; endq[0] = endquotes[iquote]; ajStrCutStart(&strp, 1); ajStrDel(&acdParseReturn); while(!done) { if(ajStrSuffixC(strp, endq)) { /* check for trailing quotes */ ajStrCutEnd(&strp, 1); done = ajTrue; } if(ajStrGetLen(strp)) { if(ajStrGetLen(acdParseReturn)) { ajStrAppendC(&acdParseReturn, " "); ajStrAppendS(&acdParseReturn, strp); } else ajStrAssignS(&acdParseReturn, strp); } if(!done) if(!acdWordNext(listwords, &strp)) /* test noquote.acd */ acdErrorAcd(acdNewCurr, "Unexpected end of file, no closing quote\n"); } ajStrDel(&strp); return acdParseReturn; } ID acdWordNext TY static MO ajacd LB acd XX DE Returns the next word from a list XX PN [1] PA u listwords AjPList PD List of words parsed from ACD file PX PN [2] PA w pword AjPStr* PD Next word from the list PX RT AjBool RD ajTrue on success RX // static AjBool acdWordNext(AjPList listwords, AjPStr* pword) { ajStrDel(pword); if(ajListstrPop(listwords, pword)) { acdWordNum++; return ajTrue; } ajStrAssignClear(pword); return ajFalse; } ID acdWordNextLower TY static MO ajacd LB acd XX DE Returns the next word from a list, in lower case. XX PN [1] PA u listwords AjPList PD List of words parsed from ACD file PX PN [2] PA w pword AjPStr* PD Next word from the list PX RT AjBool RD ajTrue on success RX // static AjBool acdWordNextLower(AjPList listwords, AjPStr* pword) { if(acdWordNext(listwords, pword)) { if(!ajStrIsLower(*pword)) { acdWarn("Automatically converting '%S' to lower case", *pword); ajStrFmtLower(pword); } return ajTrue; } return ajFalse; } ID acdWordNextName TY static MO ajacd LB acd XX DE Returns the next word from a list, in lower case DE DE This must be an ACD name (type or attribute) alphabetic only, DE with a trailing ':' XX PN [1] PA u listwords AjPList PD List of words parsed from ACD file PX PN [2] PA w pword AjPStr* PD Next word from the list PX RT AjBool RD ajTrue on success RX // static AjBool acdWordNextName(AjPList listwords, AjPStr* pword) { if(acdWordNext(listwords, pword)) { if(ajStrGetCharLast(*pword) != ':') { if(ajStrGetCharFirst(*pword) == ':')/* test nocolon.acd */ acdError("Found ':' at start of word '%S'", *pword); else if(ajStrFindAnyK(*pword, ':') > 0) acdError("Expected space missing after ':' in '%S'", *pword); else acdError("Expected ':' not found after '%S'", *pword); return ajFalse; } ajStrCutEnd(pword, 1); if(!ajStrIsAlpha(*pword)) return ajFalse; ajStrFmtLower(pword); return ajTrue; } return ajFalse; } ID acdParseName TY static MO ajacd LB acd XX DE Returns the next word from a list, in lower case. DE DE This must be an ACD name (alphanumeric allowed as this is not used DE for qualifier names) XX PN [1] PA u listwords AjPList PD List of words parsed from ACD file PX PN [2] PA w pword AjPStr* PD Next word from the list PX RT void RD RX // static void acdParseName(AjPList listwords, AjPStr* pword) { if(acdWordNextLower(listwords, pword)) if(ajStrIsAlnum(*pword)) return; /* test noapplname.acd badapplname.acd */ acdError("Bad or missing %s name '%S'", acdKeywords[acdCurrentStage].Name, *pword); return; } ID acdParseAlpha TY static MO ajacd LB acd XX DE Returns the next word from a list, in lower case. DE DE This must be an alphabetic word, no numbers or underscores allowed. XX PN [1] PA u listwords AjPList PD List of words parsed from ACD file PX PN [2] PA w pword AjPStr* PD Next word from the list PX RT void RD RX // static void acdParseAlpha(AjPList listwords, AjPStr* pword) { if(acdWordNextLower(listwords, pword)) if(ajStrIsAlpha(*pword)) return; /* test noqualname.acd badqualname.acd */ acdError("Bad or missing %s alphabetic name '%S'", acdKeywords[acdCurrentStage].Name, *pword); return; } ID acdNotLeftB TY static MO ajacd LB acd XX DE Tests the start of the next word in the list for '[' at the start DE DE Does not remove the '[' XX PN [1] PA r listwords const AjPList PD List of words parsed from ACD file PX RT AjBool RD ajTrue if start of string does not match '[' RX // static AjBool acdNotLeftB(const AjPList listwords) { char ch; AjPStr pstr = NULL; if(!ajListstrPeek(listwords, &pstr)) return ajFalse; ch = ajStrGetCharFirst(pstr); if(ch == '[') return ajFalse; /* do not delete pstr - we only peeked */ return ajTrue; } ID acdIsLeftB TY static MO ajacd LB acd XX DE Tests the start of the next word in the list for '[' at the start XX PN [1] PA u listwords AjPList PD List of words parsed from ACD file PX RT AjBool RD ajTrue if start of string matches '[' RX // static AjBool acdIsLeftB(AjPList listwords) { char ch; AjPStr teststr = NULL; AjPStr pstr = NULL; if(!ajListstrPeek(listwords, &teststr)) return ajFalse; ch = ajStrGetCharFirst(teststr); if(ch == '[') { ajStrCutStart(&teststr, 1); /* trim the leading '[' in the list */ if(!ajStrGetLen(teststr)) { /* only the '[' so remove from the list */ acdWordNext(listwords, &pstr); /* must succeed - Peeked */ ajStrDel(&pstr); /* empty - ignored - so delete */ teststr = NULL; } return ajTrue; } /* do not delete teststr - it was only peeked - deleted as pstr */ return ajFalse; } ID acdIsRightB TY static MO ajacd LB acd XX DE Tests for ']' to look for ascent to a higher level of parsing. DE DE Tests the end of the current string DE If that fails, tests the start of the next word in the list. DE DE Afterwards, the value of pstr is the last word with any ']' removed XX PN [1] PA w pstr AjPStr* PD String which has a trailing ']' removed if found PX PN [2] PA u listwords AjPList PD List of remaining words to be parsed PX RT AjBool RD ajTrue if end of string matches ']' RX // static AjBool acdIsRightB(AjPStr* pstr, AjPList listwords) { AjPStr teststr = NULL; char ch; if(*pstr && !acdParseQuotes) { ch = ajStrGetCharLast(*pstr); if(ch == ']') /* test input pstr value for ']' at end */ { ajStrCutEnd(pstr, 1); return ajTrue; } } /* go on to the next word in the list */ if(!ajListstrPeek(listwords, &teststr)) /* leftend.acd valend.acd */ acdErrorAcd(acdNewCurr, "End of file looking for ']'"); ch = ajStrGetCharFirst(teststr); if(ch == ']') /* next word starts with ']' */ { ajStrCutStart(&teststr, 1); /* trim the word - in the list */ if(!ajStrGetLen(teststr)) /* only "]" so delete it */ { acdWordNext(listwords, &acdTmpStr); /* works - used ajListstrPeek */ teststr = NULL; } return ajTrue; } return ajFalse; } ID acdParseAttributes TY static MO ajacd LB acd XX DE Parse the attribute list for an ACD type XX PN [1] PA r acd const AcdPAcd PD Acd object PX PN [2] PA u listwords AjPList PD List of parsed words PX RT void RD RX // static void acdParseAttributes(const AcdPAcd acd, AjPList listwords) { AjPStr strAttr = NULL; AjPStr strValue = NULL; AjPStr strFixValue = NULL; AjBool done=ajFalse; ajint i=0; if(!acdIsLeftB(listwords)) /* test noleftappl.acd noleftsec.acd */ /* noleftq.acd */ acdErrorAcd(acdNewCurr, "Failed to find '[' for %s %S\n", acdKeywords[acdCurrentStage].Name, acdStrName); acdPrettyShift(); done = acdIsRightB(&strAttr, listwords); /* could be [ ] */ /* continue parsing until we reach a true closing ']' character */ while(!done) { if(!acdWordNextName(listwords, &strAttr)) /* test: noattname.acd */ acdErrorAcd(acdNewCurr, "Bad or missing attribute name '%S'", strAttr); for (i=0;acdAttrAlias[i].OldName;i++) if (ajStrMatchC(strAttr, acdAttrAlias[i].OldName)) { ajStrAssignC(&strAttr, acdAttrAlias[i].NewName); break; } ajStrAssignS(&strValue, acdParseValue(listwords)); done = acdIsRightB(&strValue, listwords); /* will this be last pair? */ ajStrAssignS(&strFixValue, strValue); acdTextFormat(&strFixValue); if(acdCurrentStage == QUAL_STAGE) acdSet(acd, &strAttr, strFixValue); else acdSetKey(acd, &strAttr, strFixValue); if(done) acdWordNum--; acdPrettyWrap(ajStrGetLen(strAttr)+3, "%S: \"%S\"", strAttr, strValue); if(done) acdWordNum++; } acdPrettyUnShift(); acdPretty("]\n"); ajStrDel(&strAttr); ajStrDel(&strValue); ajStrDel(&strFixValue); return; } ID acdNewAppl TY static MO ajacd LB acd XX DE Constructor front end for an application ACD object. XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX RT AcdPAcd RD ACD application object for name. RX // static AcdPAcd acdNewAppl(const AjPStr name) { AcdPAcd acd; AcdPAcd qacd; AcdPAcd saveqacd = NULL; AcdPQual quals; AjPStr qname = NULL; AjPStr qtype = NULL; static ajint firstcall = 1; static ajint ikey; ajint i; if(firstcall) { ikey = acdFindKeyC("application"); firstcall = 0; } i = 0; quals = acdQualAppl; if(quals) { while(quals[i].Name) { ajStrAssignC(&qname, quals[i].Name); ajStrAssignC(&qtype, quals[i].Type); /* qacd = acdNewQual(qname, qname, &qtype);*/ qacd = acdNewQualQual(qname, &qtype); if(*quals[i].Default) acdSetDefC(qacd, quals[i].Default); if(!i) saveqacd = qacd; /* save the location of the first one */ i++; } } acd = acdNewAcdKey(name, name, ikey); acd->Level = ACD_APPL; if(saveqacd) acd->AssocQuals = saveqacd; ajStrDel(&qname); ajStrDel(&qtype); return acd; } ID acdNewVar TY static MO ajacd LB acd XX DE Constructor front end for a variable ACD object. XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX RT AcdPAcd RD ACD variable object for name. RX // static AcdPAcd acdNewVar(const AjPStr name) { AcdPAcd acd; static ajint firstcall = 1; static ajint ikey; if(firstcall) { ikey = acdFindKeyC("variable"); firstcall = 0; } acd = acdNewAcdKey(name, name, ikey); acd->Level = ACD_VAR; return acd; } ID acdNewRel TY static MO ajacd LB acd XX DE Constructor front end for a relation ACD object. XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX RT AcdPAcd RD ACD variable object for name. RX // static AcdPAcd acdNewRel(const AjPStr name) { AcdPAcd acd; static ajint firstcall = 1; static ajint ikey; if(firstcall) { ikey = acdFindKeyC("relation"); firstcall = 0; } acdLog("acdNewRel '%S'\n", name); acd = acdNewAcdKey(name, name, ikey); acd->Level = ACD_RELATION; return acd; } ID acdNewSec TY static MO ajacd LB acd XX DE Constructor front end for a section ACD object. XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX RT AcdPAcd RD ACD variable object for name. RX // static AcdPAcd acdNewSec(const AjPStr name) { AcdPAcd acd; static ajint firstcall = 1; static ajint ikey; AjPStr secname=NULL; AjPStr secfull=NULL; AjPStr secfullvalue=NULL; if(firstcall) { ikey = acdFindKeyC("section"); firstcall = 0; } acdLog("acdNewSec '%S' acdSecList length %d\n", name, ajListstrGetLength(acdSecList)); acd = acdNewAcdKey(name, name, ikey); acd->Level = ACD_SEC; ajStrAssignS(&secname, name); ajListstrPush(acdSecList, secname); acdValidSectionFull(&secfull); ajStrAssignS(&secfullvalue, name); acdLog("Full section '%S'\n", secfull); if (ajTablePut(acdSecTable, secfull, secfullvalue)) acdError("Duplicate section '%S'", name); acdLog("acdNewSec acdSecList push '%S' new length %d\n", secname, ajListstrGetLength(acdSecList)); return acd; } ID acdNewEndsec TY static MO ajacd LB acd XX DE Constructor front end for an end of section ACD object. XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX RT AcdPAcd RD ACD variable object for name. RX // static AcdPAcd acdNewEndsec(const AjPStr name) { AcdPAcd acd; static ajint firstcall = 1; static ajint ikey; AjPStr secname=NULL; if(firstcall) { ikey = acdFindKeyC("endsection"); firstcall = 0; } acdLog("acdNewEndsec '%S' acdSecList length %d\n", name, ajListstrGetLength(acdSecList)); if(!ajListstrGetLength(acdSecList)) /* test endsecextra.acd */ { acdLog("Bad endsection '%S', not in a section\n", name); acdError("Bad endsection '%S', not in a section", name); } else { ajListstrPop(acdSecList, &secname); acdLog("Pop from acdSecList '%S' new length %d\n", secname, ajListstrGetLength(acdSecList)); if(!ajStrMatchS(name, secname)) /* test badendsec.acd */ { acdLog("Bad endsection '%S', current section is '%S\n'", name, secname); acdError("Bad endsection '%S', current section is '%S'", name, secname); } ajStrDel(&secname); } acd = acdNewAcdKey(name, name, ikey); acd->Level = ACD_ENDSEC; return acd; } ID acdNewQual TY static MO ajacd LB acd XX DE Constructor front end for a qualifier ACD object. XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX PN [2] PA r token const AjPStr PD Qualifier name to be used on command line PX PN [3] PA u type AjPStr* PD Type of value to be defined. Expanded to full PD type name. PX RT AcdPAcd RD ACD parameter object for name. RX // static AcdPAcd acdNewQual(const AjPStr name, const AjPStr token, AjPStr* type) { AcdPAcd acd; AcdPAcd qacd; AcdPAcd vacd; AcdPAcd saveqacd = NULL; AcdPQual quals; AjPStr protName = NULL; AjPStr qname = NULL; AjPStr qtype = NULL; ajint itype; ajint i; itype = acdFindType(*type); ajStrAssignC(type, acdType[itype].Name); /* do any associated qualifiers first so they are already complete when we come to the parameter later in processing */ i = 0; quals = acdType[itype].Quals; /* any associated qualifiers for itype? */ if(quals) while(quals[i].Name) { ajStrAssignC(&acdQNameTmp, quals[i].Name); ajStrAssignC(&acdQTypeTmp, quals[i].Type); qacd = acdNewQualQual(acdQNameTmp, &acdQTypeTmp); if(*quals[i].Default) acdSetDefC(qacd, quals[i].Default); if(!i) saveqacd = qacd; /* save the location of the first one */ i++; } /* ** now set up the new parameter, and link in the list of qualifiers ** (if any) from earlier */ acdTestUnknown(name, token); acd = acdNewAcd(name, token, itype); acd->Level = ACD_QUAL; if(saveqacd) acd->AssocQuals = saveqacd; ajStrDel(&qname); ajStrDel(&qtype); /* ** For the first sequence, set the sequence type variable */ if(!ajStrGetLen(acdVarAcdProtein)) if((acdType[itype].Attr == acdAttrSeq) || (acdType[itype].Attr == acdAttrSeqall) || (acdType[itype].Attr == acdAttrSeqset) || (acdType[itype].Attr == acdAttrSeqsetall) || (acdType[itype].Attr == acdAttrFeatures)) { ajStrAssignC(&protName, "acdprotein"); ajFmtPrintS(&acdVarAcdProtein, "$(%S.protein)", name); vacd = acdNewVar(protName); acdSetVarDef(vacd, acdVarAcdProtein); ajDebug("Set acdprotein value '%S'\n", acdVarAcdProtein); ajStrDel(&protName); } ajStrDel(&protName); return acd; } ID acdNewQualQual TY static MO ajacd LB acd XX DE Constructor front end for an associated qualifier ACD object. XX PN [1] PA r name const AjPStr PD Qualifier name to be used on command line PX PN [2] PA u type AjPStr* PD Type of value to be defined. Expanded to full PD type name. PX RT AcdPAcd RD ACD parameter object for name. RX // static AcdPAcd acdNewQualQual(const AjPStr name, AjPStr* type) { AcdPAcd acd; ajint itype; itype = acdFindType(*type); ajStrAssignC(type, acdType[itype].Name); acdTestAssocUnknown(name); acd = acdNewAcd(name, name, itype); acd->Level = ACD_QUAL; acd->Assoc = ajTrue; return acd; } ID acdNewAcd TY static MO ajacd LB acd XX DE General constructor for a new ACD qualifier object. Initialises all values DE in the ACD structure as appropriate. XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX PN [2] PA r token const AjPStr PD Qualifier name to be used on command line PX PN [3] PA r itype ajint PD Integer type of value to be defined PD as defined in acdFindType PX RT AcdPAcd RD ACD parameter object for name. RX // static AcdPAcd acdNewAcd(const AjPStr name, const AjPStr token, ajint itype) { ajint i; if(acdListLast) acdListLast = AJNEW0(acdListLast->Next); else acdListLast = AJNEW0(acdList); acdListLast->LineNum = acdLineNum; ajStrAssignS(&acdListLast->Name, name); ajStrAssignS(&acdListLast->Token, token); acdListLast->Type = itype; ++(*acdType[itype].UseCount); ++(*acdType[itype].UseClassCount); /* we do NAttr and AttrStr explicitly for clarity, */ /* though they are 0 and NULL from the AJNEW0 */ switch(acdCurrentStage) { case QUAL_STAGE: acdListLast->NAttr = acdAttrCount(itype); break; default: acdListLast->NAttr = 0; break; } if(acdListLast->NAttr) { acdListLast->AttrStr = AJCALLOC(acdListLast->NAttr, sizeof(AjPStr)); for(i = 0; i < acdListLast->NAttr; i++) acdListLast->AttrStr[i] = ajStrNew(); } else acdListLast->AttrStr = NULL; acdListLast->DefStr = AJCALLOC(nDefAttr, sizeof(AjPStr)); for(i = 0; i < nDefAttr; i++) acdListLast->DefStr[i] = ajStrNew(); acdListLast->Defined = ajFalse; acdListLast->Assoc = ajFalse; acdListLast->ValStr = NULL; return acdListLast; } ID acdNewAcdKey TY static MO ajacd LB acd XX DE General constructor for a new ACD general object. Initialises all values DE in the ACD structure as appropriate. XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX PN [2] PA r token const AjPStr PD Qualifier name to be used on command line PX PN [3] PA r ikey ajint PD Integer type of value to be defined PD as defined in acdFindKey PX RT AcdPAcd RD ACD parameter object for name. RX // static AcdPAcd acdNewAcdKey(const AjPStr name, const AjPStr token, ajint ikey) { ajint i; if(acdListLast) acdListLast = AJNEW0(acdListLast->Next); else acdListLast = AJNEW0(acdList); acdListLast->Next = NULL; acdListLast->LineNum = acdLineNum; ajStrAssignS(&acdListLast->Name, name); ajStrAssignS(&acdListLast->Token, token); acdListLast->PNum = 0; acdListLast->Level = ACD_APPL; acdListLast->Type = ikey; switch(acdCurrentStage) { case APPL_STAGE: acdListLast->NAttr = acdAttrKeyCount(ikey); break; case VAR_STAGE: acdListLast->NAttr = acdAttrKeyCount(ikey); break; case SEC_STAGE: acdListLast->NAttr = acdAttrKeyCount(ikey); break; default: acdListLast->NAttr = 0; break; } if(acdListLast->NAttr) { acdListLast->AttrStr = AJCALLOC(acdListLast->NAttr, sizeof(AjPStr)); for(i = 0; i < acdListLast->NAttr; i++) acdListLast->AttrStr[i] = ajStrNew(); } else acdListLast->AttrStr = NULL; acdListLast->DefStr = NULL; acdListLast->SAttr = 0; acdListLast->SetAttr = NULL; acdListLast->SetStr = NULL; acdListLast->Defined = ajFalse; acdListLast->Assoc = ajFalse; acdListLast->AssocQuals = NULL; acdListLast->ValStr = NULL; acdListLast->Value = NULL; return acdListLast; } ID acdDel TY static MO ajacd LB acd XX DE General destructor for any ACD object. XX PN [1] PA d Pacd AcdPAcd* PD Acd object PX RT void RD RX // static void acdDel(AcdPAcd *Pacd) { AcdPAcd pa = *Pacd; ajint i; if(pa->AttrStr) { for(i = 0; i < pa->NAttr; i++) ajStrDel(&pa->AttrStr[i]); AJFREE(pa->AttrStr); } if(pa->DefStr) { for(i = 0; i < nDefAttr; i++) ajStrDel(&pa->DefStr[i]); AJFREE(pa->DefStr); } if(pa->SetStr) { for(i = 0; i < pa->SAttr; i++) ajStrDel(&pa->SetStr[i]); AJFREE(pa->SetStr); } /* for variables and relations, clear the value */ /*if(pa->Level ==ACD_VAR || pa->Level == ACD_RELATION ) ajStrDel(&pa->Value); else if(!pa->PassByRef)*/ if(pa->Assoc && ajCharMatchC(acdType[pa->Type].Name,"string")) { ajStrDel((AjPStr*)&pa->Value); } else if(pa->Level == ACD_QUAL || pa->Level == ACD_PARAM) { if(!acdType[pa->Type].PassByRef) { AJFREE(pa->Value); } else if(pa->RefPassed != REF_ALL) /* pass-by-ref but never passed */ { if(pa->RefPassed == REF_NONE) { (*acdType[pa->Type].TypeDel)(&pa->Value); } else { AJFREE(pa->Value); } } } ajStrDel(&pa->Name); ajStrDel(&pa->Token); ajStrDel(&pa->StdPrompt); ajStrDel(&pa->OrigStr); ajStrDel(&pa->ValStr); AJFREE(*Pacd); return; } ID acdTestUnknown TY static MO ajacd LB acd XX DE Makes sure that a name, token and pnum do not match any DE current ACD object. DE DE Aborts the program with a fatal error in case of problems. XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX PN [2] PA r alias const AjPStr PD Qualifier name to be used on command line PX RT void RD RX // static void acdTestUnknown(const AjPStr name, const AjPStr alias) { AcdPAcd pa; pa = acdFindAcdTest(name, alias); if(!pa) return; if(ajStrMatchS(name, alias)) acdErrorAcd(pa, "Name '%S' not unique at line %d\n", name, acdLineNum); else acdErrorAcd(pa, "Name/Alias '%S'/'%S' not unique at line %d\n", name, alias, acdLineNum); return; } ID acdCountType TY static MO ajacd LB acd XX DE Counts number of qualifiers with a given type. XX PN [1] PA r type const char* PD ACD type PX RT ajint RD Number of qualifiers of this type RX // static ajint acdCountType(const char* type) { ajint ret = 0; AcdPAcd pa; ajint itype = 0; itype = acdFindTypeC(type); for(pa=acdList; pa; pa=pa->Next) { if(pa->Type == itype) ret++; } return ret; } ID acdTestAssocUnknown TY static MO ajacd LB acd XX DE Makes sure that a name does not match any known ACD object name or token DE for all associated qualifiers. DE DE Aborts the program with a fatal error in case of problems. XX PN [1] PA r name const AjPStr PD Name or token name PX RT void RD RX // static void acdTestAssocUnknown(const AjPStr name) { AcdPAcd pa; for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) continue; if(!pa->Assoc &&(ajStrMatchS(pa->Name, name) || ajStrMatchS(pa->Token, name))) { if(ajStrMatchS(pa->Name, pa->Token)) /* test: dupassoc.acd */ acdErrorAcd(pa, "Associated qualifier '%S' clashes with '%S' " "in ACD file\n", name, pa->Name); else acdErrorAcd(pa, /* test: dupassoc2.acd */ "Associated qualifier '%S' clashes with '%S'/'%S' " "in ACD file\n", name, pa->Name, pa->Token); break; } } return; } ID acdFindAcd TY static MO ajacd LB acd XX DE Locates an ACD object by name, token and parameter number. XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX PN [2] PA r token const AjPStr PD Qualifier name to be used on command line PX RT AcdPAcd RD ACD object or NULL if not found RX // static AcdPAcd acdFindAcd(const AjPStr name, const AjPStr token) { AcdPAcd pa; AjBool sametoken = ajFalse; sametoken = ajStrMatchS(name, token); acdLog("acdFindAcd ('%S', '%S', %d)\n", name, token); for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) continue; if(ajStrMatchS(pa->Name, name)) { acdLog("..found '%S' %d\n", pa->Name, pa->PNum); return pa; } if(ajStrMatchS(pa->Token, name)) { acdLog("..found '%S' %d\n", pa->Name, pa->PNum); return pa; } if(!sametoken) continue; if(ajStrMatchS(pa->Name, token)) { acdLog("..found '%S' %d\n", pa->Name, pa->PNum); return pa; } if(ajStrMatchS(pa->Token, token)) { acdLog("..found '%S' %d\n", pa->Name, pa->PNum); return pa; } } return NULL; } ID acdFindAcdTest TY static MO ajacd LB acd XX DE Locates an ACD object by name, and (if different) by token. DE DE Tests for unique 6 character prefix and issues a warning if DE a clash is found with any already defined qualifier. DE DE All other tests are the same as in acdFindAcd XX PN [1] PA r name const AjPStr PD Token name to be used by applications PX PN [2] PA r token const AjPStr PD Qualifier name to be used on command line PX RT AcdPAcd RD ACD object or NULL if not found RX // static AcdPAcd acdFindAcdTest(const AjPStr name, const AjPStr token) { AcdPAcd savepa = NULL; AcdPAcd pa; AjBool usetoken = ajFalse; ajuint minunique=7; ajStrAssignSubS(&acdPrefName, name, 0, minunique-1); usetoken = !ajStrMatchS(name, token); if(usetoken) { ajStrAssignSubS(&acdPrefToken, token, 0, minunique-1); acdLog("acdFindAcdTest ('%S' ['%S'], '%S' ['%S'])\n", name, acdPrefName, token, acdPrefToken); } else { acdLog("acdFindAcdTest ('%S' ['%S'])\n", name, acdPrefName); } for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) { if(ajStrMatchS(pa->Name, name)) acdWarn("Qualifier '%S' matches known section '%S'", name, pa->Name); continue; } if(acdIsAtype(pa)) { if(ajStrMatchS(pa->Name, name)) acdWarn("Qualifier '%S' matches application name '%S'", name, pa->Name); continue; } if(acdIsVtype(pa)) { if(ajStrMatchS(pa->Name, name)) acdWarn("Qualifier '%S' matches variable name '%S'", name, pa->Name); continue; } if(ajStrMatchS(pa->Name, name)) { acdLog("..found '%S' %d\n", pa->Name, pa->PNum); savepa = pa; } else if(ajStrPrefixS(pa->Name, acdPrefName)) { if(!acdWrapper) acdWarn("Qualifier '%S' matches start of known '%S'", name, pa->Name); } else if(ajStrPrefixS(acdPrefName, pa->Name)) { if(!acdWrapper) acdWarn("Known qualifier '%S' matches start of '%S'", pa->Name, name); } if(!ajStrMatchS(pa->Token, pa->Name)) /* qualifier has a token */ { if(ajStrMatchS(pa->Token, name)) { acdLog("..found '%S' %d\n", pa->Name, pa->PNum); savepa = pa; } else if(ajStrPrefixS(pa->Token, acdPrefName)) { if(!acdWrapper) acdWarn("Qualifier '%S' matches start of known token '%S'", name, pa->Token); } } if(!usetoken) continue; if(ajStrMatchS(pa->Name, token)) { acdLog("..found '%S' %d\n", pa->Name, pa->PNum); savepa = pa; } else if(ajStrPrefixS(acdPrefToken, pa->Name)) { if(!acdWrapper) acdWarn("Qualifier token '%S' matches start of known '%S'", token, pa->Name); } else if(ajStrPrefixS(pa->Name, acdPrefToken)) { if(!acdWrapper) acdWarn("Known qualifier '%S' matches start of token '%S'", pa->Name, token); } if(!ajStrMatchS(pa->Token, pa->Name)) /* qualifier has a token */ { if(ajStrMatchS(pa->Token, token)) { acdLog("..found '%S' %d\n", pa->Name, pa->PNum); savepa = pa; } else if(ajStrPrefixS(pa->Token, acdPrefToken)) { if(!acdWrapper) acdWarn("Qualifier token '%S' matches start of " "known token '%S'", token, pa->Token); } } } return savepa; } ID acdFindAssoc TY static MO ajacd LB acd XX DE Locates an ACD object for an associated qualifier by name. DE DE Aborts the program with a fatal error in case of problems. DE DE Used in defining ACD objects and in processing the commandline. XX PN [1] PA r thys const AcdPAcd PD ACD object for the parameter PX PN [2] PA r name const AjPStr PD Token name to be used by applications PX PN [3] PA r noname const AjPStr PD Alternative token name (e.g. qualifier PD with "no" prefix removed) PX RT AcdPAcd RD ACD object for the selected qualifier RX // static AcdPAcd acdFindAssoc(const AcdPAcd thys, const AjPStr name, const AjPStr noname) { AcdPAcd pa; ajint ifound=0; AcdPAcd ret=NULL; AjPStr ambigList = NULL; ambigList = ajStrNew(); for(pa=thys->AssocQuals; pa && pa->Assoc; pa=pa->Next) if(ajStrPrefixS(pa->Name, name) || ajStrPrefixS(pa->Name, noname)) { if(ajStrMatchS(pa->Name, name) || ajStrMatchS(pa->Name, noname)) return pa; ifound++; ret = pa; acdAmbigApp(&ambigList, pa->Name); } if(ifound == 1) { if (acdDoValid) acdWarn("Abbreviated associated qualifier '%S' (%S)", name, ambigList); ajStrDel(&ambigList); return ret; } if(ifound > 1) { ajWarn("Ambiguous name/token '%S' (%S)", name, ambigList); acdErrorAcd(thys, /* ambigdefattr.acd */ "Attribute or qualifier '%S' ambiguous (%S)\n", name, ambigList); ajStrDel(&ambigList); } ajStrDel(&ambigList); return NULL; } ID acdTestAssoc TY static MO ajacd LB acd XX DE Locates an ACD object for an associated qualifier by name. DE DE Only tests silently for a possible qualifier. DE DE If this fails, we check properly later. DE DE Used in defining ACD objects and in processing the commandline. XX PN [1] PA r thys const AcdPAcd PD ACD object for the parameter PX PN [2] PA r name const AjPStr PD Token name to be used by applications PX PN [3] PA r noname const AjPStr PD Alternative token name (e.g. qualifier PD with "no" prefix removed) PX RT AcdPAcd RD ACD object for the selected qualifier RX // static AcdPAcd acdTestAssoc(const AcdPAcd thys, const AjPStr name, const AjPStr noname) { AcdPAcd pa; ajint ifound=0; AcdPAcd ret=NULL; AjPStr ambigList = NULL; ambigList = ajStrNew(); for(pa=thys->AssocQuals; pa && pa->Assoc; pa=pa->Next) if(ajStrPrefixS(pa->Name, name) || ajStrPrefixS(pa->Name, noname)) { if(ajStrMatchS(pa->Name, name) || ajStrMatchS(pa->Name, noname)) { ajStrDel(&ambigList); return pa; } ifound++; ret = pa; } if(ifound == 1) { if (acdDoValid) acdWarn("Abbreviated associated qualifier '%S' (%S)", name, ambigList); ajStrDel(&ambigList); return ret; } ajStrDel(&ambigList); return NULL; } ID acdTestQualC TY static MO ajacd LB acd XX DE Tests whether "name" is a valid qualifier name. DE To be valid, it must begin with "-" or '/'. DE If not, it can be taken as a value for the previous qualifier DE DE Used after a boolean option to check for a possible value DE DE Should run silently - if not valid, we will test it next anyway XX PN [1] PA r name const char* PD Qualifier name PX RT AjBool RD ajTrue if RX // static AjBool acdTestQualC(const char *name) { static AjPStr qstr = NULL; static AjPStr qnostr = NULL; static AjPStr qmaster = NULL; AcdPAcd pa; AcdPAcd qa; AcdPAcd savepa=NULL; ajint qnum = 0; ajlong i; ajint ifound=0; AjPStr ambigList = NULL; acdLog("acdTestQualC '%s'\n", name); if(*name != '-' && *name != '/' && !strstr(name, "=")) return ajFalse; /* not a qualifier name */ ambigList = ajStrNew(); ajStrAssignC(&qstr, name+1); /* lose the - or / prefix */ i = ajStrFindC(qstr, "="); /* qualifier with value */ if(i > 0) ajStrKeepRange(&qstr, 0, i-1); /* strip any value and keep testing */ if(ajStrPrefixC(qstr, "no")) /* check for -no qualifiers */ ajStrAssignSubS(&qnostr, qstr, 2, -1); else ajStrAssignClear(&qnostr); acdQualParse(&qstr, &qnostr, &qmaster, &qnum); if(ajStrGetLen(qmaster)) /* master specified as -qstr_qmaster */ { for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) continue; if(ajStrMatchS(pa->Name, qmaster)) { acdLog(" *master matched* '%S'\n", pa->Name); qa = acdTestAssoc(pa, qstr, qnostr); ajStrDel(&ambigList); if(qa) return ajTrue; else return ajFalse; } } if(ajStrPrefixS(pa->Name, qstr)) { ifound++; savepa = pa; acdAmbigApp(&ambigList, pa->Name); } acdLog(" ifound: %d\n", ifound); if(ifound == 1) { qa = acdTestAssoc(savepa, qstr, qnostr); ajStrDel(&ambigList); ajStrDel(&qstr); ajStrDel(&qnostr); ajStrDel(&qmaster); if(qa) return ajTrue; else return ajFalse; } if(ifound > 1) /* master should be checked earlier */ { /* ajWarn("Ambiguous associated qualifier '%s' (%S)", name, ambigList); ajStrDel(&ambigList); */ ajStrDel(&ambigList); ajStrDel(&qstr); ajStrDel(&qnostr); ajStrDel(&qmaster); return ajFalse; } } else /* just qualifier name -qstr */ { for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) continue; if(ajStrMatchS(pa->Name, qstr)) { acdLog(" *matched* '%S'\n", pa->Name); ajStrDel(&ambigList); ajStrDel(&qstr); ajStrDel(&qnostr); ajStrDel(&qmaster); return ajTrue; } if(ajStrPrefixS(pa->Name, qstr)) { ifound++; acdAmbigApp(&ambigList, pa->Name); } } acdLog(" ifound: %d\n", ifound); if(ifound == 1) { ajStrDel(&ambigList); ajStrDel(&qstr); ajStrDel(&qnostr); ajStrDel(&qmaster); return ajTrue; } if(ifound > 1) { /* ajWarn("Ambiguous qualifier '%s' (%S)", name, ambigList); ajStrDel(&ambigList); */ ajStrDel(&ambigList); ajStrDel(&qstr); ajStrDel(&qnostr); ajStrDel(&qmaster); return ajFalse; } } ajStrDel(&ambigList); ajStrDel(&qstr); ajStrDel(&qnostr); ajStrDel(&qmaster); return ajFalse; } ID acdFindType TY static MO ajacd LB acd XX DE Looks for a Type by name, and returns the number in acdType XX PN [1] PA r type const AjPStr PD String containing the type name PX RT ajint RD Integer representing the type (if know). Can be RD used as position in the acdType array. RX // static ajint acdFindType(const AjPStr type) { ajint i; ajint ifound = 0; ajint j = 0; AjPStr ambigList = NULL; ambigList = ajStrNew(); for(i=0; acdType[i].Name; i++) { if(ajStrMatchC(type, acdType[i].Name)) { ajStrDel(&ambigList); return i; } if(ajCharPrefixS(acdType[i].Name, type)) { ifound++; j = i; acdAmbigAppC(&ambigList, acdType[i].Name); } } if(ifound > 1) { /* warn now with the list, fail below */ ajWarn("ambiguous type %S (%S)", type, ambigList); } if(ifound != 1) /* Fatal: but covered by other tests */ acdError("unknown type: '%S'\n", type); ajStrDel(&ambigList); return j; } ID acdFindTypeC TY static MO ajacd LB acd XX DE Looks for a Type by name, and returns the number in acdType XX PN [1] PA r type const char* PD Text string containing the type name PX RT ajint RD Integer representing the type (if known). Can be RD used as position in the acdType array. RX // static ajint acdFindTypeC(const char* type) { ajint i; ajint ifound=0; ajint j=-1; ajint ilen = strlen(type); AjPStr ambigList = NULL; ambigList = ajStrNew(); for(i=0; acdType[i].Name; i++) { if(!strcmp(type, acdType[i].Name)) { ajStrDel(&ambigList); return i; } if(!strncmp(acdType[i].Name, type, ilen)) { ifound++; j = i; acdAmbigAppC(&ambigList, acdType[i].Name); } } if(ifound > 1) { /* warn now with the list, fail below */ ajWarn("ambiguous type %s (%S)", type, ambigList); ajStrDel(&ambigList); } if(ifound != 1) /* Fatal: but covered by other tests */ acdError("unknown type: '%s'\n", type); ajStrDel(&ambigList); return j; } ID acdFindKeyC TY static MO ajacd LB acd XX DE Looks for a Keyword by name, and returns the number in acdKeywords XX PN [1] PA r key const char* PD Text string containing the keyword name PX RT ajint RD Integer representing the keyword (if known). Can be RD used as position in the acdKeywords array. RX // static ajint acdFindKeyC(const char* key) { ajint i; ajint ifound=0; ajint j=0; ajint ilen = strlen(key); AjPStr ambigList = NULL; ajStrAssignClear(&ambigList); for(i=QUAL_STAGE+1; acdKeywords[i].Name; i++) { if(!strcmp(key, acdKeywords[i].Name)) { ajStrDel(&ambigList); return i; } if(strncmp(acdKeywords[i].Name, key, ilen)) { ifound++; j = i; acdAmbigAppC(&ambigList, acdKeywords[i].Name); } } if(ifound > 1) { ajWarn("ambiguous keyword %s (%S)", key, ambigList); } if(ifound != 1) /* Fatal: but strings are hard coded */ acdError("unknown keyword: '%s'\n", key); ajStrDel(&ambigList); return j; } ID acdReplyInitC TY static MO ajacd LB acd XX DE Builds a default value for the reply first time around. Uses a default DE specially set in the ACD, or (if none) uses the default string passed in DE parameter "defval" and also sets this as the default in the ACD. XX PN [1] PA r thys const AcdPAcd PD ACD object for current item. PX PN [2] PA r defval const char* PD Default value, as a C string PX PN [3] PA w reply AjPStr* PD String containing default reply PX RT AjBool RD ajTrue if a value in the ACD was used. RX // static AjBool acdReplyInitC(const AcdPAcd thys, const char *defval, AjPStr* reply) { AjPStr def; if(thys->DefStr) { def = thys->DefStr[DEF_DEFAULT]; acdLog("acdReplyInitC '%S' : '%S'\n", thys->Name, def); if(ajStrGetLen(def) || thys->Defined) { ajStrAssignS(reply, def); acdVarResolve(reply); return ajTrue; } } ajStrAssignC(reply, defval); ajStrAssignC(&thys->DefStr[DEF_DEFAULT], defval); return ajFalse; } ID acdReplyInitS TY static MO ajacd LB acd XX DE Builds a default value for the reply first time around. Uses a default DE specially set in the ACD, or (if none) uses the default string passed in DE parameter "defval" and also sets this as the default in the ACD. XX PN [1] PA r thys const AcdPAcd PD ACD object for current item. PX PN [2] PA r defval const AjPStr PD Default value, as a string object PX PN [3] PA w reply AjPStr* PD String containing default reply PX RT AjBool RD ajTrue if a value in the ACD was used. RX // static AjBool acdReplyInitS(const AcdPAcd thys, const AjPStr defval, AjPStr* reply) { AjPStr def; if(thys->DefStr) { def = thys->DefStr[DEF_DEFAULT]; acdLog("acdReplyInitS '%S' : '%S'\n", thys->Name, def); if(ajStrGetLen(def) || thys->Defined) { ajStrAssignS(reply, def); acdVarResolve(reply); return ajTrue; } } ajStrAssignS(reply, defval); ajStrAssignS(&thys->DefStr[DEF_DEFAULT], defval); return ajFalse; } ID acdDefinedEmpty TY static MO ajacd LB acd XX DE Tests for a defined ACD value of an empty string XX PN [1] PA r thys const AcdPAcd PD ACD object for current item. PX RT AjBool RD ajTrue if a value in the ACD was used but is empty RX // static AjBool acdDefinedEmpty (const AcdPAcd thys) { AjPStr def; if(thys->DefStr) { def = thys->DefStr[DEF_DEFAULT]; if(thys->Defined && !thys->UserSetNull && !ajStrGetLen(def)) return ajTrue; } return ajFalse; } ID acdUserGet TY static MO ajacd LB acd XX DE Given an ACD containing a defined prompt, a help string, DE or a default (string) value, prompts DE the user for a string value and returns it. DE DE If prompt is set, it is used. Otherwise, info can be used. DE If neither are defined, the item name and type are used to make a DE meaningful prompt. DE DE The default value is offered if it is set. DE DE The user response is returned in "reply" DE DE If -auto is in effect, fails if there is no value. XX PN [1] PA u thys AcdPAcd PD ACD object for current item. PX PN [2] PA w reply AjPStr* PD The user response, or PD the default value if accepted. PX RT AjBool RD ajTrue if reply contains any text. RX // static AjBool acdUserGet(AcdPAcd thys, AjPStr* reply) { AjBool ret = ajFalse; AjPStr prompt; AjPStr info; AjPStr code; AjPStr help; acdLog("acdUserGet '%S' reply '%S'\n", thys->Name, *reply); if(thys->DefStr && !acdAuto) { prompt = thys->DefStr[DEF_PROMPT]; info = thys->DefStr[DEF_INFO]; code = thys->DefStr[DEF_CODE]; help = thys->DefStr[DEF_HELP]; ajStrAssignS(&acdUserReplyDef, *reply); if(ajStrGetLen(code)) acdCodeGet(code, &acdUserMsg); else if(ajStrGetLen(prompt)) ajStrAssignS(&acdUserMsg, prompt); else if(ajStrGetLen(info)) ajStrAssignS(&acdUserMsg, info); else if(ajStrGetLen(thys->StdPrompt)) ajStrAssignS(&acdUserMsg, thys->StdPrompt); else if(ajStrGetLen(help)) ajStrAssignS(&acdUserMsg, help); else { if(!acdCodeDef(thys, &acdUserMsg)) { ajStrAssignResC(&acdUserMsg, 512, ""); ajFmtPrintS(&acdUserMsg, "-%S : enter %s value", thys->Name, acdType[thys->Type].Name); } } acdVarResolve(&acdUserMsg); acdLog("acdUserGet '%S' replydef '%S' msg '%S'\n", thys->Name, acdUserReplyDef, acdUserMsg); if(ajStrGetLen(acdUserReplyDef)) ret = ajUserGet(reply, "%S [%S]: ", acdUserMsg, acdUserReplyDef); else ret = ajUserGet(reply, "%S: ", acdUserMsg); if(!ret) ajStrAssignS(reply, acdUserReplyDef); if(ret) thys->UserDefined = ajTrue; acdUserSavereply(thys, NULL, ret, *reply); } if(ajStrGetLen(*reply)) ret = ajTrue; ajStrDelStatic(&acdUserMsg); ajStrDelStatic(&acdUserReplyDef); return ret; } ID acdUserGetPrompt TY static MO ajacd LB acd XX DE Given a defined prompt, prompts DE the user for a string value and returns it. DE DE The default value is offered if it is set. DE DE The user response is returned in "reply" DE DE If -auto is in effect, fails if there is no value. XX PN [1] PA r thys const AcdPAcd PD ACD object for current item PX PN [2] PA r assocqual const char* PD Associated qualifier PX PN [3] PA r prompt const char* PD prompt string PX PN [4] PA w reply AjPStr* PD The user response, or PD the default value if accepted. PX RT AjBool RD ajTrue if reply contains any text. RX // static AjBool acdUserGetPrompt(const AcdPAcd thys, const char* assocqual, const char* prompt, AjPStr* reply) { AjBool ret = ajFalse; if(!acdAuto) { ajStrAssignS(&acdUserReplyDef, *reply); if(ajStrGetLen(acdUserReplyDef)) ret = ajUserGet(reply, " %s [%S]: ", prompt, acdUserReplyDef); else ret = ajUserGet(reply, " %s: ", prompt); if(!ret) ajStrAssignS(reply, acdUserReplyDef); acdUserSavereply(thys, assocqual, ret, *reply); } if(ajStrGetLen(*reply)) ret = ajTrue; ajStrDelStatic(&acdUserReplyDef); return ret; } ID acdUserSavereply TY static MO ajacd LB acd XX DE Save the reply from prompting the user XX PN [1] PA r thys const AcdPAcd PD ACD object for current item. PX PN [2] PA r assocqual const char* PD Associated qualifier PX PN [3] PA r userset AjBool PD Reply set by user PX PN [4] PA r reply const AjPStr PD Reply string PX RT void RD RX // static void acdUserSavereply(const AcdPAcd thys, const char* assocqual, AjBool userset, const AjPStr reply) { AjPStr qualname = NULL; if(assocqual) { ajStrAssignC(&qualname, assocqual); ajStrAppendK(&qualname, '_'); ajStrAppendS(&qualname, thys->Name); } else ajStrAssignS(&qualname, thys->Name); if(ajStrMatchS(acdInputName, qualname)) ajStrTruncateLen(&acdInputSave, acdInputLen); else { ajStrAssignS(&acdInputName, qualname); acdInputLen = ajStrGetLen(acdInputSave); } if(userset) { if(acdInputLen) ajStrAppendK(&acdInputSave, '\n'); ajStrAppendK(&acdInputSave, '-'); ajStrAppendS(&acdInputSave, qualname); ajStrAppendK(&acdInputSave, ' '); if(ajStrIsWord(reply) && (ajStrFindAnyC(reply, "*?[]{}|!&^") < 0)) ajStrAppendS(&acdInputSave, reply); else { ajStrAppendK(&acdInputSave, '\"'); ajStrAppendS(&acdInputSave, reply); ajStrAppendK(&acdInputSave, '\"'); } } ajStrDel(&qualname); return; } ID acdBadRetry TY static MO ajacd LB acd XX DE Writes a message to stderr, and kills the application. XX PN [1] PA r thys const AcdPAcd PD ACD object. PX RT void RD RX // static void acdBadRetry(const AcdPAcd thys) { /* test acdc-retry */ ajDie("%S terminated: Bad value for '-%S' and no more retries", acdProgram, thys->Name); } ID acdBadVal TY static MO ajacd LB acd XX DE Writes a message to stderr, returns only if this is a standard value DE and we are prompting for values. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r required AjBool PD If true, prompting for the value PD was possible. PX PN [3] PA r fmt const char* PD Format with ajFmt extensions PX PN [4] PA v vararg ... PD Optional arguments PX RT void RD RX // static void acdBadVal(const AcdPAcd thys, AjBool required, const char *fmt, ...) { va_list args; static AjPStr msg = NULL; /* ** replaced line below with following 2 to make msg more obvious to ** the user **/ acdLog("Failure for option '%S'\n",thys->Name); ajFmtPrintS(&msg, "%s", fmt); va_start(args, fmt) ; ajVErr(ajStrGetPtr(msg), args); va_end(args) ; if(!required && !acdAuto) /* test acdc-badadvanced */ ajDie("%S terminated: Bad value for '-%S' and no prompt", acdProgram, thys->Name); if(acdAuto) /* test acdc-badauto */ ajDie("%S terminated: Bad value for '-%S' with -auto defined", acdProgram, thys->Name); return; } ID acdSetXxxx TY static MO ajacd LB acd XX DE Dummy function to handle prompting and validation for an ACD item type. DE A similar routine is needed for any new item type. See the other acdSet DE functions for ideas on what to include. DE DE This functions knows all the attributes, defaults, associated qualifiers DE and validation rules for type Xxxx. If any attributes (etc) are added, DE this is where the processing will be done. DE DE The final value (of type Xxxx) is set in thys as Value DE and a string equivalent for easy printing is set in thys as Valstr DE DE The function does not need to return a value. It either succeeds in filling DE in all values, or aborts with a suitable error messages. XX PN [1] PA u thys AcdPAcd PD ACD for current item (which is PD always of type Xxxx) PX RT void RD RX // static void acdSetXxxx(AcdPAcd thys) { AjPStr val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; /* ** create storage for val if needed, e.g. with AJNEW0(val) */ val = NULL; /* set a default value */ required = acdIsRequired(thys); acdReplyInitC(thys, "", &acdReply); for(itry=acdPromptTry; itry && !ok; itry--) { if(required) /* need to prompt? */ acdUserGet(thys, &acdReply); ok = ajTrue; /* test the value somehow */ } if(!ok) acdBadRetry(thys); ajStrAssignS(&val, acdReply); /* use the validated reply */ thys->Value = val; /* set the value */ ajStrAssignS(&thys->ValStr, val); /* set the string value */ return; } ID acdSetAppl TY static MO ajacd LB acd XX DE Makes sure all application ACD item values have been set. DE DE Called when an "appl" type ACD item is checked. Should not be called DE for any other item. The "appl" item may not be the first, as there DE are some 'standard' qualifiers set in advance for debugging, help and DE so on. DE DE At present there is nothing to prompt for here, though there could DE be, for example, a report of what the program does which would appear DE before any user prompts. XX PN [1] PA u thys AcdPAcd PD ACD for the application item. PX RT void RD RX // static void acdSetAppl(AcdPAcd thys) { AjPStr appldoc = NULL; AjPStr applobsolete = NULL; AjPStr applexternal = NULL; AjPStrTok handle = NULL; AjPStr token = NULL; AjPStr message = NULL; AjPStr appname = NULL; /* saved to table */ AjPStr appfullname = NULL; /* saved to table */ acdAttrResolve(thys, "documentation", &appldoc); acdAttrResolve(thys, "obsolete", &applobsolete); acdAttrResolve(thys, "external", &applexternal); acdAppldoc = ajStrNewS(appldoc); if(!acdExternalTable) acdExternalTable = ajTablestrNewCase(50); if(!acdAuto && ajStrGetLen(appldoc)) { ajStrFmtWrap(&appldoc, 75); ajUserDumpS(appldoc); } ajStrAssignS(&thys->ValStr, thys->Name); if(ajStrGetLen(applobsolete)) { acdWarnObsolete(applobsolete); } if(ajStrGetLen(applexternal)) { ajStrTokenAssignC(&handle, applexternal, "|"); while(ajStrTokenNextParse(&handle, &token)) { ajStrExtractFirst(token, &message, &appname); ajStrAssignS(&appfullname, appname); if(!ajSysFileWhich(&appfullname)) { ajStrFmtWrapLeft(&message, 70, 5, 0); ajDie("%S uses external program '%S' " "which is not in the PATH or defined as %S_%US\n%S", acdProgram, appname, ajNamValuePackage(), appname, message); } ajTablePut(acdExternalTable, appname, appfullname); appname = NULL; appfullname = NULL; } } ajStrDel(&appldoc); ajStrDel(&applobsolete); ajStrDel(&applexternal); ajStrDel(&message); ajStrDel(&token); ajStrTokenDel(&handle); return; } ID acdSetEndsec TY static MO ajacd LB acd XX DE Ends the current ACD section DE DE Called when an "endsection" type ACD item is checked. Should not be called DE for any other item. DE DE At present there is nothing to prompt for here, though there could DE be, for example, a blank line at the end of a section where DE something was prompted for. XX PN [1] PA u thys AcdPAcd PD ACD for the endsection item. PX RT void RD RX // static void acdSetEndsec(AcdPAcd thys) { if(thys->DefStr) ajStrAssignS(&thys->ValStr, thys->DefStr[DEF_DEFAULT]); acdVarResolve(&thys->ValStr); return; } ID acdSetSec TY static MO ajacd LB acd XX DE Starts a new ACD section. DE DE Called when a "section" type ACD item is checked. Should not be called DE for any other item. DE DE At present there is nothing to prompt for here, though there could DE be, for example, a prompt issued before the first prompt for the section. DE This would be stored (the 'info' attribute) and used in the standard DE prompting functions. XX PN [1] PA u thys AcdPAcd PD ACD for the section item. PX RT void RD RX // static void acdSetSec(AcdPAcd thys) { ajint border = 1; AjPStr comment = NULL; AjPStr folder = NULL; AjPStr info = NULL; AjPStr side = NULL; AjPStr type = NULL; const char* sideVal[] = {"top", "bottom", "left", "right", NULL}; const char* typeVal[] = {"frame", "page", NULL}; if(acdAttrToStr(thys, "type", "", &type)) if(!acdVocabCheck(type, typeVal)) acdErrorAcd(thys, "section %S, bad attribute value type: %S", thys->Name, type); if(acdAttrToInt(thys, "border", 1, &border)) { if(!ajStrMatchCaseC(type, "frame")) ajWarn("section %S, border only used by type: frame", thys->Name); if(border < 1) { acdAttrToStr(thys, "border", "", &acdTmpStr); acdErrorAcd(thys, "section %S, bad attribute value type: %S", acdTmpStr); } } acdAttrToStr(thys, "comment", "", &comment); if(acdAttrToStr(thys, "folder", "", &folder)) { if(!ajStrMatchCaseC(type, "page")) ajWarn("section %S, folder only used by type: page", thys->Name); } acdAttrToStr(thys, "information", "", &info); if(acdAttrToStr(thys, "side", "", &side)) { if(!acdVocabCheck(side, sideVal)) acdErrorAcd(thys, "section %S, bad attribute value side: %S", thys->Name, side); if(!ajStrMatchCaseC(type, "frame")) ajWarn("section %S, side only used by type: frame", thys->Name); } ajStrDel(&comment); ajStrDel(&folder); ajStrDel(&info); ajStrDel(&side); ajStrDel(&type); return; } ID acdSetVar TY static MO ajacd LB acd XX DE Defines an ACD variable. DE DE Called when a "variable" type ACD item is checked. Should not be called DE for any other item. DE DE At present there is nothing to prompt for here, though there could DE be, for example, a report of what the program does which would appear DE before any user prompts. XX PN [1] PA u thys AcdPAcd PD ACD for the application item. PX RT void RD RX // static void acdSetVar(AcdPAcd thys) { if(thys->DefStr) ajStrAssignS(&thys->ValStr, thys->DefStr[DEF_DEFAULT]); acdVarResolve(&thys->ValStr); return; } ID ajAcdGetAlign TY public MO ajacd LB acd XX DE Returns an item of type Align as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPAlign RD Alignment output object. Already opened RD by ajAlignOpent so this just returns the object RX // AjPAlign ajAcdGetAlign(const char *token) { return acdGetValueRef(token, "align"); } ID acdSetAlign TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD alignment output item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifier "-aformat" DE is applied when writing the sequences. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetAlign(AcdPAcd thys) { AjPAlign val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjBool nulldefault; AjPStr name = NULL; AjPStr ext = NULL; AjPStr fmt = NULL; AjPStr dir = NULL; val = NULL; acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); acdGetValueAssoc(thys, "aformat", &fmt); acdGetValueAssoc(thys, "aextension", &ext); acdGetValueAssoc(thys, "aname", &name); acdGetValueAssoc(thys, "adirectory", &dir); acdOutDirectory(&dir); required = acdIsRequired(thys); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ acdOutFilename(&acdReplyDef, name, ext); else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else { acdOutFilename(&acdTmpOutFName, name, ext); acdReplyInitS(thys, acdTmpOutFName, &acdReplyDef); } acdPromptAlign(thys); ajStrDel(&name); ajStrDel(&ext); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajAlignNew(); ajStrAssignEmptyS(&val->Formatstr, fmt); acdAttrToStr(thys, "type", "", &val->Type); acdAttrToInt(thys, "minseqs", 0, &val->Nmin); acdAttrToInt(thys, "maxseqs", 0, &val->Nmax); acdAttrToBool(thys, "multiple", ajFalse, &val->Multi); acdQualToInt(thys, "awidth", 50, &val->Width, &acdTmpStr); acdQualToBool(thys, "aglobal", ajFalse, &val->Global, &acdTmpStr); acdQualToBool(thys, "aaccshow", ajFalse, &val->Showacc, &acdTmpStr); acdQualToBool(thys, "adesshow", ajFalse, &val->Showdes, &acdTmpStr); acdQualToBool(thys, "ausashow", ajFalse, &val->Showusa, &acdTmpStr); if(!ajAlignValid(val)) { /* test acdc-alignbadformat */ ajDie("Alignment option -%S: Validation failed", thys->Name); } ajStrAssignS(&acdOutFullFName, acdReply); ajFilenameReplacePathS(&acdOutFullFName, dir); ok = ajAlignOpen(val, acdOutFullFName); if(!ok) { acdBadVal(thys, required, "Unable to open alignment file '%S'", acdOutFullFName); ajAlignDel(&val); } } else if(!nullok) { acdBadVal(thys, required, "Alignment file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdOutFullFName); ajStrDel(&fmt); ajStrDel(&dir); return; } ID ajAcdGetArray TY public MO ajacd LB acd XX DE Returns an item of type array as defined in a named ACD item. Called by the DE application after all ACD values have been set, and simply returns DE what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPFloat RD Floating point array object RX // AjPFloat ajAcdGetArray(const char *token) { return acdGetValueRef(token, "array"); } ID acdSetArray TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD floating point array item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is "0.0" with a size of 1. DE DE Min and max limits, if set, are applied without comment. DE Precision is provided for logging purposes but otherwise not (yet) used. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetArray(AcdPAcd thys) { AjPFloat val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool warnrange; AjBool failrange; AjBool sumtest; AjBool truemin; float vfmin; float vfmax; ajint precision; ajuint size; float sum; float tolerance; float fdef; float ftol; float ftot; AjPStr deflist = NULL; ajuint i; float* array; AjPStr failmsg = NULL; acdRangeTestCalc(thys); acdAttrToFloat(thys, "minimum", -FLT_MAX, &vfmin); acdLog("minimum: %e\n", vfmin); acdAttrToFloat(thys, "maximum", FLT_MAX, &vfmax); acdLog("maximum: %e\n", vfmax); acdAttrToFloat(thys, "sum", (float)1.0, &sum); acdLog("sum: %e\n", sum); acdAttrToFloat(thys, "tolerance", (float)0.01, &tolerance); acdLog("tolerance: %e\n", tolerance); acdAttrToInt(thys, "precision", 3, &precision); acdLog("precision: %d\n", precision); acdAttrToBool(thys, "failrange", ajTrue, &failrange); acdLog("failrange: %B\n", failrange); if(failrange && (vfmin > vfmax)) { acdAttrResolve(thys, "rangemessage", &failmsg); if(ajStrGetLen(failmsg)) acdErrorAcd(thys, "Invalid range: %S", failmsg); else acdErrorAcd(thys, "Invalid range: " "minimum value %.3f more than maximum %.3f", vfmin, vfmax); } acdAttrToBool(thys, "warnrange", acdDoWarnRange, &warnrange); acdLog("warnrange: %B\n", warnrange); acdAttrToBool(thys, "sumtest", ajTrue, &sumtest); acdLog("sumtest: %B\n", sumtest); acdAttrToBool(thys, "trueminimum", ajFalse, &truemin); acdLog("trueminimum: %B\n", truemin); acdAttrToUint(thys, "size", 1, &size); acdLog("size: %d\n", size); if(size < 1) acdErrorAcd(thys, "Array attribute size: %d less than 1", size); fdef = sum / ((float) size); for(i=0; i < size; i++) { if(i) ajStrAppendK(&deflist, ' '); ajFmtPrintAppS(&deflist, "%.*f", precision, fdef); } val = ajFloatNewRes(size); /* create storage for the result */ required = acdIsRequired(thys); acdReplyInitS(thys, deflist, &acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); ok = ajFloatParse(acdReply, &val); if(ok && ajFloatLen(val) != size) { ajErr("Bad array value, expected %d values, found %d", size, ajFloatLen(val)); ok = ajFalse; } if(!ok) acdBadVal(thys, required, "Invalid array value '%S', please try again", acdReply); array = ajFloatFloat(val); ftot = 0.0; for(i=0; i< size; i++) { if(!truemin && array[i] < vfmin) { /* reset within limits */ if(warnrange) ajWarn("floating point value [%d] out of range %.*f " "less than (reset to) %.*f", i+1, precision, array[i], precision, vfmin); array[i] = vfmin; } if(array[i] > vfmax) { if(warnrange) ajWarn("floating point value [%d] out of range %.*f " "more than (reset to) %.*f", i+1, precision, array[i], precision, vfmax); array[i] = vfmax; } if(truemin && array[i] < vfmin) { /* reset within limits */ if(warnrange) ajWarn("floating point value [%d] out of range %.*f " "less than (reset to) %.*f", i+1, precision, array[i], precision, vfmin); array[i] = vfmin; } ftot += array[i]; } ftol = (float) fabs(ftot -sum); if(sumtest && ftol > tolerance) { ajWarn("Bad total %.*f, required total is %.*f with " "tolerance %.*f", precision, ftot, precision, sum,precision, tolerance); acdBadVal(thys, required, "Invalid array value '%S', please try again", acdReply); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajFloatStr(val, precision, &thys->ValStr); ajStrDel(&deflist); return; } ID ajAcdGetAssembly TY public MO ajacd LB acd XX DE Returns an item of type assembly as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPAssemload RD Assembly object RX // AjPAssemload ajAcdGetAssembly(const char *token) { AjPAssemload val = acdGetValueRef(token, "assembly"); return val; } ID acdSetAssembly TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD assembly input DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetAssembly(AcdPAcd thys) { AjPAssemload val; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool nullok = ajFalse; ajint itry; AjPStr infname = NULL; AjPStr tmp = NULL; val = ajAssemloadNew(); /* set the default value */ acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); ajStrDel(&infname); acdAttrToBool(thys, "entry", ajFalse, &val->Assemin->Input->Text); acdGetValueAssoc(thys, "cbegin", &tmp); ajStrToInt(tmp, &val->Assemin->cbegin); acdGetValueAssoc(thys, "cend", &tmp); ajStrToInt(tmp, &val->Assemin->cend); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajAssemloadDel(&val); break; } ajAsseminQryS(val->Assemin, acdReply); acdGetValueAssoc(thys, "iformat", &val->Assemin->Input->Formatstr); acdGetValueAssoc(thys, "iquery", &val->Assemin->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &val->Assemin->Input->Fpos, &acdTmpStr); ok = ajAsseminLoad(val->Assemin, val->Assem); } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, ajAssemGetId(val->Assem), ajTrue); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&tmp); return; } ID ajAcdGetBoolean TY public MO ajacd LB acd XX DE Returns an item of type Bool as defined in a named ACD item. Called by the DE application after all ACD values have been set, and simply returns DE what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjBool RD Boolean value from ACD item RX // AjBool ajAcdGetBoolean(const char *token) { AjBool* val; val = acdGetValue(token, "boolean"); return *val; } ID acdSetBoolean TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD boolean item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is "N" for ajFalse. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetBoolean(AcdPAcd thys) { AjBool* val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AJNEW0(val); /* create storage for the result */ *val = ajFalse; /* set the default value */ required = acdIsRequired(thys); acdReplyInitC(thys, "N", &acdReplyDef); acdLog("acdSetBool -%S def: %S\n", thys->Name, acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); ok = ajStrToBool(acdReply, val); if(!ok) acdBadVal(thys, required, "Invalid Y/N value '%S'", acdReply); } if(!ok) acdBadRetry(thys); thys->Value = val; ajFmtPrintS(&thys->ValStr, "%B", *val); acdSetQualAppl(thys, *val); /* check special application booleans */ acdLog("acdSetBool -%S val: %B\n", thys->Name, *val); if(ajStrMatchC(thys->Name, "help")) acdHelp(); return; } ID ajAcdGetCodon TY public MO ajacd LB acd XX DE Returns an item of type Codon as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPCod RD Codon object. RX // AjPCod ajAcdGetCodon(const char *token) { return acdGetValueRef(token, "codon"); } ID acdSetCodon TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outfile item. DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetCodon(AcdPAcd thys) { AjPCod val; AjPStr name = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjPStr fmt = NULL; val = ajCodNew(); /* set the default value */ acdAttrResolve(thys, "name", &name); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdLog("nullok: %B\n", nullok); if (!acdGetValueAssoc(thys, "format", &fmt)) ajStrAssignClear(&fmt); required = acdIsRequired(thys); acdReplyInitS(thys, name, &acdReplyDef); acdPromptCodon(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { if(!ajCodRead(val, acdReply, fmt)) { acdBadVal(thys, required, "Unable to read codon usage '%S'", acdReply); ok = ajFalse; } } else { if(nullok) { ajCodDel(&val); } else { acdBadVal(thys, required, "Codon file is required"); ok = ajFalse; } } } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, NULL, ajFalse); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&name); ajStrDel(&fmt); return; } ID ajAcdGetCpdb TY public MO ajacd LB acd XX DE Returns an item of type Cpdb as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPFile RD Cpdb input file. RX // AjPFile ajAcdGetCpdb(const char *token) { return acdGetValueRef(token, "cpdb"); } ID acdSetCpdb TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD clean pdb file item. DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetCpdb(AcdPAcd thys) { AjPFile val; AjPStr name = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjPStr fmt = NULL; val = NULL; /* set the default value */ acdAttrResolve(thys, "name", &name); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdLog("nullok: %B\n", nullok); if (!acdGetValueAssoc(thys, "format", &fmt)) ajStrAssignClear(&fmt); required = acdIsRequired(thys); acdReplyInitS(thys, name, &acdReplyDef); acdPromptCpdb(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajFileNewInNameS(acdReply); if(!val) { acdBadVal(thys, required, "Unable to read cleaned PDB data '%S'", acdReply); ok = ajFalse; } } else if(!nullok) { acdBadVal(thys, required, "Cleaned PDB data file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&name); ajStrDel(&fmt); return; } ID ajAcdGetDatafile TY public MO ajacd LB acd XX DE Returns an item of type Datafile as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPFile RD File object. The file was already opened by RD ajDatafileNewInNameS so this just returns the pointer. RX // AjPFile ajAcdGetDatafile(const char *token) { return acdGetValueRef(token, "datafile"); } ID acdSetDatafile TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD datafile item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value is "programname.dat" XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetDatafile(AcdPAcd thys) { AjPFile val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjPStr name = NULL; AjPStr ext = NULL; AjPStr dir = NULL; AjPStr datafname = NULL; val = NULL; /* set the default value */ acdAttrResolve(thys, "name", &name); acdAttrResolve(thys, "extension", &ext); acdAttrResolve(thys, "directory", &dir); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdLog("nullok: %B\n", nullok); acdDataFilename(&datafname, name, ext, nullok); required = acdIsRequired(thys); acdReplyInitS(thys, datafname, &acdReplyDef); /* acdPromptInfile(thys);*/ ajStrDel(&datafname); ajStrDel(&name); ajStrDel(&ext); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajDatafileNewInNamePathS(acdReply, dir); if(!val) { acdBadVal(thys, required, "Unable to open data file '%S' for input", acdReply); ok = ajFalse; } } else if(!nullok) { acdBadVal(thys, required, "Input file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&dir); return; } ID ajAcdGetDirectory TY public MO ajacd LB acd XX DE Returns an item of type AjPDir which has been validated as a DE directory. DE DE Optionally can be forced to have a fully qualified path when returned. XX PN [1] PA r token const char* PD Text token name PX RT AjPDir RD Directory object RX // AjPDir ajAcdGetDirectory(const char *token) { return acdGetValueRef(token, "directory"); } ID ajAcdGetDirectoryName TY public MO ajacd LB acd XX DE Returns an item of type AjPStr which has been validated as a DE directory name DE DE Optionally can be forced to have a fully qualified path when returned. XX PN [1] PA r token const char* PD Text token name PX RT AjPStr RD Directory path RX // AjPStr ajAcdGetDirectoryName(const char *token) { AjPStr ret = NULL; AjPDir dir; dir = acdGetValue(token, "directory"); ret = ajStrNewS(ajDirGetPath(dir)); return ret; } ID acdSetDirectory TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD directory item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value is "." the current directory. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetDirectory(AcdPAcd thys) { AjPDir val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok = ajFalse; AjBool nulldefault = ajFalse; AjBool dopath = ajFalse; AjPStr ext = NULL; val = NULL; /* set the default value */ acdAttrToBool(thys, "fullpath", ajFalse, &dopath); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); acdGetValueAssoc(thys, "extension", &ext); required = acdIsRequired(thys); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ acdReplyInitC(thys, ".", &acdReplyDef); else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else acdReplyInitC(thys, ".", &acdReplyDef); acdPromptDirectory(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { if(dopath) ok = ajDirnameFillPath(&acdReply); else ok = ajDirnameFixExists(&acdReply); if (ok) { val = ajDirNewPathExt(acdReply, ext); if (!val) ok = ajFalse; } if(!ok) acdBadVal(thys, required, "Unable to open directory '%S'", acdReply); } else if(!nullok) { acdBadVal(thys, required, "Directory path is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&ext); return; } ID ajAcdGetDirlist TY public MO ajacd LB acd XX DE Returns a list of files in a given directory. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPList RD List of files. RX // AjPList ajAcdGetDirlist(const char *token) { return acdGetValueRef(token, "dirlist"); } ID acdSetDirlist TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD directory item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value is "." the current directory. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetDirlist(AcdPAcd thys) { AjPList val; AjPStr t; AjPStr v; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok = ajFalse; AjBool dopath; AjPStr ext = NULL; ajint n; ajint i; val = NULL; acdAttrToBool(thys, "fullpath", ajFalse, &dopath); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdGetValueAssoc(thys, "extension", &ext); required = acdIsRequired(thys); acdReplyInitC(thys, ".", &acdReplyDef); acdPromptDirlist(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { if(dopath) ok = ajDirnameFillPath(&acdReply); else ok = ajDirnameFixExists(&acdReply); if(!ok) acdBadVal(thys, required, "Unable to open directories '%S' for input", acdReply); } else if(!nullok) { acdBadVal(thys, required, "Input file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); val = ajListstrNew(); /* set the default value */ t = ajStrNewC("*"); if(MAJSTRGETLEN(ext)) { if(ajStrGetCharFirst(ext) != '.') ajStrInsertC(&ext, 0, "."); ajStrAppendS(&t, ext); } ajFilelistAddPathWild(val, acdReply, t); /* Sort list so that list of files is system-independent */ ajListSort(val, &ajStrVcmp); n = (ajuint) ajListGetLength(val); ajDebug("acdSetDirlist '%S' listlength %d\n", acdReply, n); for(i=0;iValue = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&t); ajStrDel(&ext); return; } ID ajAcdGetDiscretestates TY public MO ajacd LB acd XX DE Returns an item of type Discrete states as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPPhyloState* RD Discrete states object. RD The string was already set by RD acdSetDiscretestates so this just returns the pointer. RX // AjPPhyloState* ajAcdGetDiscretestates(const char *token) { return acdGetValueRef(token, "discretestates"); } ID ajAcdGetDiscretestatesSingle TY public MO ajacd LB acd XX DE Returns an from an array item of type Discrete states as defined in a named DE ACD item, which is an array of objects terminated by a null value. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPPhyloState RD Discrete states object. The data was already set by RD acdSetDiscretestates so this just returns the pointer. RX // AjPPhyloState ajAcdGetDiscretestatesSingle(const char *token) { AjPPhyloState* val; ajint i; val = acdGetValueSingle(token, "discretestates"); for(i=0; val[i]; i++) continue; if(i > 1) ajWarn("Single list value %s, but can choose %d values", token, i); if(i < 1) ajWarn("Single list value %s, no value found: returning NULL value", token); return val[0]; } ID acdSetDiscretestates TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD weights file item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is an empty string. DE DE Attributes for length and maximum property character are applied with error DE messages if exceeded. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetDiscretestates(AcdPAcd thys) { AjPPhyloState* val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; AjPStr infname = NULL; ajint itry; AjBool nullok = ajFalse; ajint size; ajint len; AjPStr statechars = NULL; ajint i; val = NULL; acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToInt(thys, "size", 1, &size); acdAttrToInt(thys, "length", 1, &len); acdAttrToStr(thys, "characters", "", &statechars); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptInfile(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajPhyloStateRead(acdReply, statechars); if(!val) { acdBadVal(thys, required, "Unable to read discrete states from '%S'", acdReply); ok = ajFalse; } } else if(!nullok) { acdBadVal(thys, required, "Input discrete states file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, NULL, ajTrue); /* properties have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcDiscrete); thys->SetAttr = &acdCalcDiscrete[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); if(val) { for(i=0;val[i];i++) continue; ajStrFromInt(&thys->SetStr[0],val[0]->Len); /* string length */ ajStrFromInt(&thys->SetStr[1],val[0]->Size); /* string count */ ajStrFromInt(&thys->SetStr[2],i); /* number of sets */ ajStrAssignS(&thys->ValStr, acdReply); ajDebug("acdSetDiscretestates calc len: %d size: %d sets: %d\n", val[0]->Len, val[0]->Size, i); } else { ajStrFromInt(&thys->SetStr[0],0); /* string length */ ajStrFromInt(&thys->SetStr[1],0); /* string count */ ajStrFromInt(&thys->SetStr[2],0); /* number of sets */ ajStrAssignClear(&thys->ValStr); } ajStrDel(&statechars); thys->Value = val; return; } ID ajAcdGetDistances TY public MO ajacd LB acd XX DE Returns an item of type Distances as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPPhyloDist* RD Distances object. The string was already set by RD acdSetDistances so this just returns the pointer. RX // AjPPhyloDist* ajAcdGetDistances(const char *token) { return acdGetValueRef(token, "distances"); } ID ajAcdGetDistancesSingle TY public MO ajacd LB acd XX DE Returns an item of type Distances as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPPhyloDist RD Distances object. The string was already set by RD acdSetDistances so this just returns the pointer. RX // AjPPhyloDist ajAcdGetDistancesSingle(const char *token) { AjPPhyloDist *val; ajint i; val = acdGetValueSingle(token, "distances"); for(i=0; val[i]; i++) continue; if(i > 1) ajWarn("Single list value %s, but can choose %d values", token, i); if(i < 1) ajWarn("Single list value %s, no value found: returning NULL value", token); return val[0]; } ID acdSetDistances TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD weights file item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is an empty string. DE DE Attributes for length and maximum property character are applied with error DE messages if exceeded. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetDistances(AcdPAcd thys) { AjPPhyloDist* val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; AjPStr infname = NULL; ajint itry; AjBool nullok = ajFalse; ajint size; AjBool missing; ajint i; val = NULL; acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToInt(thys, "size", 0, &size); acdAttrToBool(thys, "missval", ajFalse, &missing); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptInfile(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajPhyloDistRead(acdReply, size, missing); if(!val) { acdBadVal(thys, required, "Unable to read distances file '%S'", acdReply); ok = ajFalse; } } else if(!nullok) { acdBadVal(thys, required, "Distances file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, NULL, ajTrue); /* properties have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcDistances); thys->SetAttr = &acdCalcDistances[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); if(val) { for(i=0;val[i];i++) continue; ajStrFromInt(&thys->SetStr[0],i); /* matrix count */ ajStrFromInt(&thys->SetStr[1],val[0]->Size); /* string count */ ajStrFromBool(&thys->SetStr[2],val[0]->HasReplicates); ajStrFromBool(&thys->SetStr[3],val[0]->HasMissing); ajStrAssignS(&thys->ValStr, acdReply); } else { ajStrFromInt(&thys->SetStr[0],0); /* matrix count */ ajStrFromInt(&thys->SetStr[1],0); /* string count */ ajStrFromBool(&thys->SetStr[2],ajFalse); ajStrFromBool(&thys->SetStr[3],ajFalse); ajStrAssignClear(&thys->ValStr); } thys->Value = val; return; } ID ajAcdGetFeatout TY public MO ajacd LB acd XX DE Returns an item of type FeatOut as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPFeattabOut RD Feature Table output object. Already opened RD by acdSetFeatout so this just returns the object RX // AjPFeattabOut ajAcdGetFeatout(const char *token) { return acdGetValueRef(token, "featout"); } ID acdSetFeatout TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD feature table item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-offormat", "-ofopenfile" DE are applied to the UFO before opening the output file. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetFeatout(AcdPAcd thys) { AjPFeattabOut val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjBool nulldefault; AjPStr name = NULL; AjPStr ext = NULL; AjPStr fmt = NULL; AjPStr type = NULL; val = NULL; if(!acdGetValueAssoc(thys, "ofname", &name)) acdAttrResolve(thys, "name", &name); if(acdGetValueAssoc(thys, "offormat", &fmt)) ajStrAssignS(&ext, fmt); else acdAttrResolve(thys, "extension", &ext); if(!ajStrGetLen(ext)) ajFeatOutFormatDefault(&ext); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); ajDebug("acdSetFeatout checking type\n"); if(!acdAttrToStr(thys, "type", "", &type)) { ajDebug("no type, try '%S'\n", type); if(!acdInTypeFeat(&type)) ajWarn("No output type specified for '%S'", thys->Name); } required = acdIsRequired(thys); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ acdOutFilename(&acdReplyDef, name, ext); else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else { acdOutFilename(&acdTmpOutFName, name, ext); acdReplyInitS(thys, acdTmpOutFName, &acdReplyDef); } ajStrDel(&name); ajStrDel(&ext); acdPromptFeatout(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajFeattabOutNew(); ajDebug("Type '%S' try ajFeattabOutSetType\n", type); if(!ajFeattabOutSetType(val, type)) acdError("Invalid type for feature output"); acdGetValueAssoc(thys, "ofopenfile", &val->Filename); acdGetValueAssoc(thys, "ofdirectory", &val->Directory); acdOutDirectory(&val->Directory); ajStrAssignEmptyS(&val->Formatstr, fmt); ok = ajFeattabOutOpen(val, acdReply); if(!ok) { if(ajStrGetLen(val->Directory)) acdBadVal(thys, required, "Unable to open features output '%S%S'", val->Directory, acdReply); else acdBadVal(thys, required, "Unable to open features output '%S'", acdReply); ajFeattabOutDel(&val); } } else if(!nullok) { acdBadVal(thys, required, "Output UFO is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&fmt); ajStrDel(&type); return; } ID ajAcdGetFeatures TY public MO ajacd LB acd XX DE Returns an item of type Features as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPFeattable RD Feature Table object. The table was already loaded by RD acdSetFeat so this just returns the pointer. RX // AjPFeattable ajAcdGetFeatures(const char *token) { AjPFeattaball val = acdGetValue(token, "features"); if(val->Multi) ajWarn("ajAcdGetFeatures request single feature table " "but maxreads > 1"); val->Returned = ajTrue; return val->Feattable; } ID ajAcdGetFeaturesall TY public MO ajacd LB acd XX DE Returns an input stream of an item of type Features as defined in a DE named ACD item. DE DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPFeattaball RD Features input stream RX // AjPFeattaball ajAcdGetFeaturesall(const char *token) { AjPFeattaball val = acdGetValueRef(token, "features"); if(!val->Multi) ajWarn("ajAcdGetFeattaball request features input stream " "but maxreads is 1"); return val; } ID acdSetFeatures TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD feature table item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-fformat", "-fopenfile" DE are applied to the UFO before reading the feature table. DE DE Associated qualifiers "-fbegin", "-fend" and "-freverse" DE are applied as appropriate, with prompting for values, DE after the feature table has been read. DE They are applied to the feature table, DE and the resulting table is what is set in the ACD item. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetFeatures(AcdPAcd thys) { AjPFeattaball val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjPStr infname = NULL; static AjPStr type = NULL; ajint fbegin = 0; ajint fend = 0; AjBool freverse = ajFalse; AjBool fprompt = ajFalse; ajint iattr; ajint maxreads; val = ajFeattaballNew(); /* set the default value */ acdQualToBool(thys, "fask", ajFalse, &fprompt, &acdReplyDef); acdAttrToInt(thys, "maxreads", INT_MAX, &maxreads); acdAttrToBool(thys, "entry", ajFalse, &val->Feattabin->Input->Text); if(acdAttrToStr(thys, "type", "", &type)) { if(!ajFeattabinSetTypeS(val->Feattabin, type)) acdError("Invalid type for feature input"); acdInTypeFeatSave(type); } else { acdInTypeFeatSave(NULL); } acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptFeatures(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); acdGetValueAssoc(thys, "fformat", &val->Feattabin->Formatstr); acdGetValueAssoc(thys, "fopenfile", &val->Feattabin->Filename); acdGetValueAssoc(thys, "iquery", &val->Feattabin->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &val->Feattabin->Input->Fpos, &acdTmpStr); ajFeattabinQryS(val->Feattabin, acdReply); ok = ajFeattabinRead(val->Feattabin, val->Feattable); } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, ajFeattableGetName(val->Feattable), ajTrue); /* save sequence name */ /* now process the begin, end and reverse options */ ok = acdQualToInt(thys, "fbegin", 1, &fbegin, &acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ajStrAssignS(&acdReply, acdReplyDef); if(fprompt) acdUserGetPrompt(thys, "fbegin", " Begin at position", &acdReply); ok = ajStrToInt(acdReply, &fbegin); if(!ok) acdBadVal(thys, ajTrue, "Invalid integer value '%S'", acdReply); } if(!ok) acdBadRetry(thys); ok = acdQualToInt(thys, "fend", ajFeattableGetLen(val->Feattable), &fend, &acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ajStrAssignS(&acdReply, acdReplyDef); if(fprompt) acdUserGetPrompt(thys, "fend", " End at position", &acdReply); ok = ajStrToInt(acdReply, &fend); if(!ok) acdBadVal(thys, ajTrue, "Invalid integer value '%S'", acdReply); } if(!ok) acdBadRetry(thys); ok = acdQualToBool(thys, "freverse", ajFalse, &freverse, &acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ajStrAssignS(&acdReply, acdReplyDef); if(fprompt) acdUserGetPrompt(thys, "freverse", " Reverse strand", &acdReply); ok = ajStrToBool(acdReply, &freverse); if(!ok) acdBadVal(thys, ajTrue, "Invalid Y/N value '%S'", acdReply); } if(!ok) acdBadRetry(thys); acdLog("sbegin: %d, send: %d, freverse: %B\n", fbegin, fend, freverse); if(freverse) { val->Feattabin->Rev = freverse; ajFeattableReverse(val->Feattable); } ajFeattabinSetRange(val->Feattabin, fbegin, fend); ajFeattableSetRange(val->Feattable, fbegin, fend); if(maxreads > 1) val->Multi = ajTrue; /* features tables have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcFeat); thys->SetAttr = &acdCalcFeat[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); iattr = 0; ajStrFromInt(&thys->SetStr[iattr++], ajFeattableGetBegin(val->Feattable)); ajStrFromInt(&thys->SetStr[iattr++], ajFeattableGetEnd(val->Feattable)); ajStrFromInt(&thys->SetStr[iattr++], ajFeattableGetLen(val->Feattable)); ajStrFromBool(&thys->SetStr[iattr++], ajFeattableIsProt(val->Feattable)); ajStrFromBool(&thys->SetStr[iattr++], ajFeattableIsNuc(val->Feattable)); ajStrAssignS(&thys->SetStr[iattr++], ajFeattableGetName(val->Feattable)); ajStrFromInt(&thys->SetStr[iattr++], ajFeattableGetSize(val->Feattable)); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&type); return; } ID ajAcdGetFilelist TY public MO ajacd LB acd XX DE Returns a list of files given a comma-separated list. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPList RD List of files. RX // AjPList ajAcdGetFilelist(const char *token) { return acdGetValueRef(token, "filelist"); } ID acdSetFilelist TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD file list item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE There is no default value XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetFilelist(AcdPAcd thys) { AjPList val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok = ajFalse; val = ajListNew(); acdAttrToBool(thys, "nullok", ajFalse, &nullok); required = acdIsRequired(thys); acdReplyInitC(thys, "", &acdReplyDef); acdPromptFilelist(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply)) if(!nullok) { acdBadVal(thys, required, "File list is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); ajFilelistAddListname(val, acdReply); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); return; } ID ajAcdGetFloat TY public MO ajacd LB acd XX DE Returns an item of type Float as defined in a named ACD item. Called by the DE application after all ACD values have been set, and simply returns DE what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT float RD Floating point value from ACD item RX // float ajAcdGetFloat(const char *token) { double *val; val = acdGetValue(token, "float"); return (float) *val; } ID ajAcdGetFloatDouble TY public MO ajacd LB acd XX DE Returns an item of type Float as defined in a named ACD item. Called by the DE application after all ACD values have been set, and simply returns DE what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT double RD Floating point value from ACD item RX // double ajAcdGetFloatDouble(const char *token) { double *val; val = acdGetValue(token, "float"); return *val; } ID acdSetFloat TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD floating point item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is "0.0". DE DE Min and max limits, if set, are applied without comment. DE Precision is provided for logging purposes but otherwise not (yet) used. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetFloat(AcdPAcd thys) { double* val; float fval; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool warnrange; AjBool failrange; AjBool isdouble; AjBool truemin; double vfmin; double vfmax; ajint precision; AjPStr failmsg = NULL; acdRangeTestCalc(thys); acdAttrToDouble(thys, "minimum", -FLT_MAX, &vfmin); acdLog("minimum: %e\n", vfmin); acdAttrToDouble(thys, "maximum", FLT_MAX, &vfmax); acdLog("maximum: %e\n", vfmax); acdAttrToInt(thys, "precision", 3, &precision); acdLog("precision: %d\n", precision); acdAttrToBool(thys, "failrange", ajTrue, &failrange); acdLog("failrange: %B\n", failrange); acdAttrToBool(thys, "warnrange", acdDoWarnRange, &warnrange); acdLog("warnrange: %B\n", warnrange); acdAttrToBool(thys, "large", AJFALSE, &isdouble); acdLog("large: %B\n", isdouble); acdAttrToBool(thys, "trueminimum", AJFALSE, &truemin); acdLog("trueminimum: %B\n", truemin); if(failrange && (vfmin > vfmax)) { acdAttrResolve(thys, "rangemessage", &failmsg); if(ajStrGetLen(failmsg)) acdErrorAcd(thys, "Invalid range: %S", failmsg); else acdErrorAcd(thys, "Invalid range: " "minimum value %.3f more than maximum %.3f", vfmin, vfmax); } AJNEW0(val); /* create storage for the result */ *val = 0.0; /* set the default value */ required = acdIsRequired(thys); acdReplyInitC(thys, "0.0", &acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(isdouble) ok = ajStrToDouble(acdReply, val); else { ok = ajStrToFloat(acdReply, &fval); *val = fval; } if(!ok) acdBadVal(thys, required, "Invalid decimal value '%S', please try again", acdReply); } if(!ok) acdBadRetry(thys); if(isdouble && vfmin == FLT_MIN) vfmin = DBL_MIN; if(isdouble && vfmax == FLT_MAX) vfmax = DBL_MAX; if(!truemin && *val < vfmin) { /* reset within limits */ if(warnrange) ajWarn("floating point value out of range %.*f " "less than (reset to) %.*f", precision, *val, precision, vfmin); *val = vfmin; } if(*val > vfmax) { if(warnrange) ajWarn("floating point value out of range %.*f " "more than (reset to) %.*f", precision, *val, precision, vfmax); *val = vfmax; } if(truemin && *val < vfmin) { /* reset within limits */ if(warnrange) ajWarn("floating point value out of range %.*f " "less than (reset to) %.*f", precision, *val, precision, vfmin); *val = vfmin; } thys->Value = val; ajStrFromFloat(&thys->ValStr, (float) *val, precision); return; } ID ajAcdGetFrequencies TY public MO ajacd LB acd XX DE Returns an item of type Frequencies as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPPhyloFreq RD Frequencies object. The string was already set by RD acdSetFrequencies so this just returns the pointer. RX // AjPPhyloFreq ajAcdGetFrequencies(const char *token) { return acdGetValueRef(token, "frequencies"); } ID acdSetFrequencies TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD weights file item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is an empty string. DE DE Attributes for length and maximum property character are applied with error DE messages if exceeded. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetFrequencies(AcdPAcd thys) { AjPPhyloFreq val; AjBool required = ajFalse; AjBool ok = ajFalse; AjPStr infname = NULL; ajint itry; AjBool nullok = ajFalse; AjBool contchar = ajFalse; AjBool genedata = ajFalse; AjBool within = ajFalse; ajint size; val = NULL; acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToInt(thys, "size", 1, &size); acdAttrToBool(thys, "continuous", ajFalse, &contchar); acdAttrToBool(thys, "genedata", ajFalse, &genedata); acdAttrToBool(thys, "within", ajFalse, &within); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptInfile(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajPhyloFreqRead(acdReply, contchar, genedata, within); if(!val) { acdBadVal(thys, required, "Unable to read frequencies file '%S'", acdReply); ok = ajFalse; } } else if(!nullok) { acdBadVal(thys, required, "Input frequencies file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, NULL, ajTrue); /* properties have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcFrequencies); thys->SetAttr = &acdCalcFrequencies[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); if(val) { ajStrFromInt(&thys->SetStr[0],val->Len); /* string count */ ajStrFromInt(&thys->SetStr[1],val->Size); /* string count */ ajStrFromInt(&thys->SetStr[2],val->Loci); /* string count */ ajStrFromBool(&thys->SetStr[3],!val->ContChar); /* genes */ ajStrFromBool(&thys->SetStr[4],val->ContChar); /* contin */ ajStrFromBool(&thys->SetStr[5],val->Within); /* indivs */ ajStrAssignS(&thys->ValStr, acdReply); } else { ajStrFromInt(&thys->SetStr[0],0); /* string count */ ajStrFromInt(&thys->SetStr[1],0); /* string count */ ajStrFromInt(&thys->SetStr[2],0); /* string count */ ajStrFromBool(&thys->SetStr[3],0); /* genes */ ajStrFromBool(&thys->SetStr[4],0); /* contin */ ajStrFromBool(&thys->SetStr[5],0); /* indivs */ ajStrAssignClear(&thys->ValStr); } thys->Value = val; return; } ID ajAcdGetGraph TY public MO ajacd LB acd XX DE Returns a graph object which hold user graphics options. XX PN [1] PA r token const char* PD Text token name PX RT AjPGraph RD Graph object. RX // AjPGraph ajAcdGetGraph(const char *token) { return acdGetValueRef(token, "graph"); } ID acdSetGraph TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD graph item. DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetGraph(AcdPAcd thys) { AjPGraph val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; AjPStr title = NULL; AjPStr gdev = NULL; ajint itry; AjBool nullok; AjBool nulldefault; AjBool hasseq = ajFalse; acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); required = acdIsRequired(thys); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ { if(ajNamGetValueC("GRAPHICS",&gdev)) acdReplyInitS(thys, gdev, &acdReplyDef); else #ifndef WIN32 #ifndef X_DISPLAY_MISSING /* X11 is available */ acdReplyInitC(thys, "x11", &acdReplyDef); #else #ifdef PLD_png /* if png/gd/zlib libraries available for png driver */ acdReplyInitC(thys, "png", &acdReplyDef); #else acdReplyInitC(thys, "ps", &acdReplyDef); #endif #endif #else acdReplyInitC(thys, "win3", &acdReplyDef); #endif } else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else { if(ajNamGetValueC("GRAPHICS",&gdev)) acdReplyInitS(thys, gdev, &acdReplyDef); else #ifndef WIN32 #ifndef X_DISPLAY_MISSING /* X11 is available */ acdReplyInitC(thys, "x11", &acdReplyDef); #else #ifdef PLD_png /* if png/gd/zlib libraries available for png driver */ acdReplyInitC(thys, "png", &acdReplyDef); #else acdReplyInitC(thys, "ps", &acdReplyDef); #endif #endif #else acdReplyInitC(thys, "win3", &acdReplyDef); #endif } ajStrDel(&gdev); acdPromptGraph(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) /* valid no graph type */ { if(!val) val = ajGraphNew(); ok = ajGraphSetDevicetype(val, acdReply); if(!ok) { ajGraphicsDumpDevices(); acdBadVal(thys, required, "Invalid graph value '%S'", acdReply); } } else if(!nullok) { acdBadVal(thys, required, "Graph is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignC(&thys->ValStr, "graph definition"); if(val) { acdAttrToBool(thys, "sequence", ajFalse, &hasseq); if(acdGetValueAssoc(thys, "gdesc", &title)) ajGraphSetDescS(val,title); if(acdGetValueAssoc(thys, "gtitle", &title)) ajGraphSetTitleS(val,title); if(acdGetValueAssoc(thys, "gsubtitle", &title)) ajGraphSetSubtitleS(val,title); if(acdGetValueAssoc(thys, "gxtitle", &title)) ajGraphSetXlabelS(val,title); else if(hasseq) ajGraphSetXlabelS(val, acdInFName); if(acdGetValueAssoc(thys, "gytitle", &title)) ajGraphSetYlabelS(val,title); if(acdGetValueAssoc(thys, "goutfile", &title)) ajGraphSetOutfileS(val,title); if(acdGetValueAssoc(thys, "gdirectory", &title)) ajGraphSetOutdirS(val,title); else { ajStrAssignClear(&title); if(acdOutDirectory(&title)) ajGraphSetOutdirS(val,title); } ajStrDel(&title); ajGraphTrace(val); } return; } ID ajAcdGetGraphxy TY public MO ajacd LB acd XX DE Returns a graph object which hold user graphics options. XX PN [1] PA r token const char* PD Text token name PX RT AjPGraph RD Graph object. RX // AjPGraph ajAcdGetGraphxy(const char *token) { return acdGetValueRef(token, "xygraph"); } ID acdSetGraphxy TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD XY graph item. DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetGraphxy(AcdPAcd thys) { AjPGraph val; AjPStr gdev = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; AjPStr title = NULL; ajint itry; AjBool nullok; AjBool nulldefault; ajint multi; AjBool hasseq = ajFalse; val = NULL; acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); acdAttrToInt(thys, "multi", 1, &multi); if(multi < 1) multi = 1; acdLog("multi: %d\n", multi); required = acdIsRequired(thys); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ { if(ajNamGetValueC("GRAPHICS",&gdev)) acdReplyInitS(thys, gdev, &acdReplyDef); else #ifndef WIN32 #ifndef X_DISPLAY_MISSING /* X11 is available */ acdReplyInitC(thys, "x11", &acdReplyDef); #else #ifdef PLD_png /* if png/gd/zlib libraries available for png driver */ acdReplyInitC(thys, "png", &acdReplyDef); #else acdReplyInitC(thys, "ps", &acdReplyDef); #endif #endif #else acdReplyInitC(thys, "win3", &acdReplyDef); #endif } else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else { if(ajNamGetValueC("GRAPHICS",&gdev)) acdReplyInitS(thys, gdev, &acdReplyDef); else #ifndef WIN32 #ifndef X_DISPLAY_MISSING /* X11 is available */ acdReplyInitC(thys, "x11", &acdReplyDef); #else #ifdef PLD_png /* if png/gd/zlib libraries available for png driver */ acdReplyInitC(thys, "png", &acdReplyDef); #else acdReplyInitC(thys, "ps", &acdReplyDef); #endif #endif #else acdReplyInitC(thys, "win3", &acdReplyDef); #endif } ajStrDel(&gdev); acdPromptGraph(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) /* valid no graph type */ { if(!val) val = ajGraphxyNewI(multi); ok = ajGraphxySetDevicetype(val, acdReply); if(!ok) { ajGraphicsDumpDevices(); acdBadVal(thys, required, "Invalid XY graph value '%S'", acdReply); } } else if(!nullok) { acdBadVal(thys, required, "Graph is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignC(&thys->ValStr, "XY graph definition"); if(val) { acdAttrToBool(thys, "sequence", ajFalse, &hasseq); if(hasseq) ajGraphSetDatanameS(val, acdInFName); if(acdGetValueAssoc(thys, "gdesc", &title)) ajGraphSetDescS(val,title); if(acdGetValueAssoc(thys, "gtitle", &title)) ajGraphSetTitleS(val,title); if(acdGetValueAssoc(thys, "gsubtitle", &title)) ajGraphSetSubtitleS(val,title); if(acdGetValueAssoc(thys, "gxtitle", &title)) ajGraphSetXlabelS(val,title); else if(hasseq) ajGraphSetXlabelS(val, acdInFName); if(acdGetValueAssoc(thys, "gytitle", &title)) ajGraphSetYlabelS(val,title); if(acdGetValueAssoc(thys, "goutfile", &title)) ajGraphSetOutfileS(val,title); if(acdGetValueAssoc(thys, "gdirectory", &title)) ajGraphSetOutdirS(val,title); else { ajStrAssignClear(&title); if(acdOutDirectory(&title)) ajGraphSetOutdirS(val,title); } ajStrDel(&title); ajGraphTrace(val); } return; } ID ajAcdGetInfile TY public MO ajacd LB acd XX DE Returns an item of type file as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPFile RD File object. The file was already opened by RD acdSetInfile so this just returns the pointer. RX // AjPFile ajAcdGetInfile(const char *token) { return acdGetValueRef(token, "infile"); } ID acdSetInfile TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD infile item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if filtering is on) is "stdin", but then DE prompting is turned off. DE DE Otherwise there is no default value unless the ACD file has one. DE DE Various file naming options are defined, but not yet implemented here. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetInfile(AcdPAcd thys) { AjPFile val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjBool trydefault; AjPStr infname = NULL; AjPStr inpath = NULL; val = NULL; /* set the default value */ acdAttrToStr(thys, "directory", "", &inpath); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "trydefault", ajFalse, &trydefault); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptInfile(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { if(!ajFilenameHasPath(acdReply)) val = ajFileNewInNamePathS(acdReply,inpath); else val = ajFileNewInNameS(acdReply); if(!val) { if(!nullok || !trydefault || !ajStrMatchS(acdReply, acdReplyDef)) { if(ajStrGetLen(inpath) && !ajFilenameHasPath(acdReply)) acdBadVal(thys, required, "Unable to open file '%S/%S' for input", inpath, acdReply); else acdBadVal(thys, required, "Unable to open file '%S' for input", acdReply); ok = ajFalse; } } } else if(!nullok) { acdBadVal(thys, required, "Input file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, NULL, ajTrue); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&inpath); return; } ID ajAcdGetInt TY public MO ajacd LB acd XX DE Returns an item of type ajint as defined in a named ACD item. Called by the DE application after all ACD values have been set, and simply returns DE what the ACD item already has. DE DE The ACD item is expected to have the large attribute set to false. XX PN [1] PA r token const char* PD Text token name PX RT ajint RD Integer value from ACD item RX // ajint ajAcdGetInt(const char *token) { ajlong *val; val = acdGetValue(token, "integer"); return (ajint) *val; } ID ajAcdGetIntLong TY public MO ajacd LB acd XX DE Returns an item of type ajlong as defined in a named ACD item. Called by the DE application after all ACD values have been set, and simply returns DE what the ACD item already has. DE DE The ACD item is expected to have the large attribute set to true. XX PN [1] PA r token const char* PD Text token name PX RT ajlong RD Integer value from ACD item RX // ajlong ajAcdGetIntLong(const char *token) { ajlong *val; val = acdGetValue(token, "integer"); return *val; } ID acdSetInt TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD integer item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is "0". DE DE Min and max limits, if set, are applied without comment. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetInt(AcdPAcd thys) { ajlong* val; ajint ival; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool failrange; AjBool warnrange; AjBool islong; AjBool truemin; ajlong imin; ajlong imax; AjPStr failmsg = NULL; AjBool iscalc = ajFalse; acdRangeTestCalc(thys); acdAttrToLong(thys, "minimum", INT_MIN, &imin); acdLog("minimum: %Ld\n", imin); acdAttrToLong(thys, "maximum", INT_MAX, &imax); acdLog("maximum: %Ld\n", imax); acdAttrToBool(thys, "failrange", ajTrue, &failrange); acdLog("failrange: %B\n", failrange); acdAttrToBool(thys, "warnrange", acdDoWarnRange, &warnrange); acdLog("warnrange: %B\n", warnrange); acdAttrToBool(thys, "large", AJFALSE, &islong); acdLog("large: %B\n", islong); acdAttrToBool(thys, "trueminimum", AJFALSE, &truemin); acdLog("trueminimum: %B\n", truemin); if(failrange && (imin > imax)) { acdAttrResolve(thys, "rangemessage", &failmsg); if(ajStrGetLen(failmsg)) acdErrorAcd(thys, "Invalid range: %S", failmsg); else acdErrorAcd(thys, "Invalid range: " "minimum value %d more than maximum %d", imin, imax); } AJNEW0(val); /* create storage for the result */ *val = 0; /* set the default value */ required = acdIsRequired(thys); acdReplyInitC(thys, "0", &acdReplyDef); acdLog("acdSetInt %S default '%S' Required: %B\n", thys->Name, acdReplyDef, required); for(itry=acdPromptTry; itry && !ok; itry--) { ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); acdLog(" reply: '%S' \n", acdReply); if(ajStrMatchC(acdReply, "default")) ajStrAssignC(&acdReply, "0"); if(islong) ok = ajStrToLong(acdReply, val); else { ok = ajStrToInt(acdReply, &ival); *val = ival; } acdLog(" modified reply: '%S' val: %Ld ok: %B\n", acdReply, *val, ok); if(!ok) acdBadVal(thys, required, "Invalid integer value '%S'", acdReply); } if(!ok) acdBadRetry(thys); if(islong && imin == INT_MIN) imin = LONG_MIN; if(islong && imax == INT_MAX) imax = LONG_MAX; if(iscalc) { if(!truemin && *val < imin) { /* reset within limits */ if(warnrange) ajWarn("integer value out of range %Ld less than " "(reset to) %Ld", *val, imin); *val = imin; } if(*val > imax) { if(warnrange) ajWarn("integer value out of range %Ld more than " "(reset to) %Ld", *val, imax); *val = imax; } if(truemin && *val < imin) { /* reset within limits */ if(warnrange) ajWarn("integer value out of range %Ld less than " "(reset to) %Ld", *val, imin); *val = imin; } } else { if(*val < imin) { /* reset within limits */ if(warnrange) ajWarn("integer value out of range %Ld less than " "(reset to) %Ld", *val, imin); *val = imin; } if(*val > imax) { if(warnrange) ajWarn("integer value out of range %Ld more than " "(reset to) %Ld", *val, imax); *val = imax; } } thys->Value = val; ajStrFromLong(&thys->ValStr, *val); return; } ID ajAcdGetList TY public MO ajacd LB acd XX DE Returns an item of type List as defined in a named ACD item, DE which is an array of strings terminated by a null value. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPStr* RD String array of values with NULL for last element. RX // AjPStr* ajAcdGetList(const char *token) { return acdGetValueRef(token, "list"); } ID ajAcdGetListSingle TY public MO ajacd LB acd XX DE Returns a single item from an array of type List as defined in a named DE ACD item, which is an array of strings terminated by a null value. DE Called by the application after all ACD values have been set, and DE simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPStr RD String array of values with NULL for last element. RX // AjPStr ajAcdGetListSingle(const char *token) { AjPStr *val; ajint i; val = acdGetValueSingle(token, "list"); for(i=0; val[i]; i++) continue; if(i > 1) ajWarn("Single list value %s, but can choose %d values", token, i); if(i < 1) ajWarn("Single list value %s, no value found: returning NULL value", token); return val[0]; } ID acdSetList TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outfile item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE If a value is required and not yet given, prints out a header DE and the list of options, then asks for a selection or (if max is DE more than 1) a list of selections. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetList(AcdPAcd thys) { AjPStr *val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; ajint i; ajint min, max; val = NULL; /* set the default value */ required = acdIsRequired(thys); acdReplyInitC(thys, "", &acdReplyDef); acdAttrToInt(thys, "minimum", 1, &min); acdAttrToInt(thys, "maximum", 1, &max); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) { acdListPrompt(thys); acdUserGet(thys, &acdReply); } val = acdListValue(thys, min, max, acdReply); if(!val) { acdBadVal(thys, required, "Bad menu option '%S'", acdReply); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; for(i=0; val[i]; i++) { acdLog("Storing val[%d] '%S'\n", i,val[i]); if(i) ajStrAppendC(&thys->ValStr, ";"); ajStrAppendS(&thys->ValStr, val[i]); } return; } ID ajAcdGetMatrix TY public MO ajacd LB acd XX DE Returns an item of type Matrix as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPMatrix RD Matrix object. RX // AjPMatrix ajAcdGetMatrix(const char *token) { return acdGetValueRef(token, "matrix"); } ID ajAcdGetMatrixf TY public MO ajacd LB acd XX DE Returns an item of type Matrix as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPMatrixf RD Float Matrix object. RX // AjPMatrixf ajAcdGetMatrixf(const char *token) { return acdGetValueRef(token, "matrixf"); } ID acdSetMatrix TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD matrix item. DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetMatrix(AcdPAcd thys) { AjPMatrix val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool isprot; AjPStr infname = NULL; val = NULL; /* set the default value */ acdAttrToBool(thys, "protein", ajTrue, &isprot); if(isprot) { acdAttrResolve(thys, "pname", &infname); if(!ajStrGetLen(infname)) ajStrAssignC(&infname, DEFBLOSUM); } else { acdAttrResolve(thys, "nname", &infname); if(!ajStrGetLen(infname)) ajStrAssignC(&infname, DEFDNA); } required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajMatrixNewFile(acdReply); if(!val) { acdBadVal(thys, required, "Unable to read matrix '%S'", acdReply); ok = ajFalse; } } else { acdBadVal(thys, required, "Matrix is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); return; } ID acdSetMatrixf TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD matrix item. DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetMatrixf(AcdPAcd thys) { AjPMatrixf val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool isprot; AjPStr infname = NULL; val = NULL; /* set the default value */ acdAttrToBool(thys, "protein", ajTrue, &isprot); if(isprot) { acdAttrResolve(thys, "pname", &infname); if(!ajStrGetLen(infname)) ajStrAssignC(&infname, DEFBLOSUM); } else { acdAttrResolve(thys, "nname", &infname); if(!ajStrGetLen(infname)) ajStrAssignC(&infname, DEFDNA); } required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajMatrixfNewFile(acdReply); if(!val) { acdBadVal(thys, required, "Unable to read matrix '%S'", acdReply); ok = ajFalse; } } else { acdBadVal(thys, required, "Matrix is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); return; } ID ajAcdGetObo TY public MO ajacd LB acd XX DE Returns an item of type Obo as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPObo RD Obo term object RX // AjPObo ajAcdGetObo(const char *token) { AjPOboall val = acdGetValue(token, "obo"); if(val->Multi) ajWarn("ajAcdGetObo request single obo term but maxreads > 1"); val->Returned = ajTrue; return val->Obo; } ID ajAcdGetOboall TY public MO ajacd LB acd XX DE Returns an input stream of an item of type Obo as defined in a DE named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOboall RD Obo input stream RX // AjPOboall ajAcdGetOboall(const char *token) { AjPOboall val = acdGetValueRef(token, "obo"); if(!val->Multi) ajWarn("ajAcdGetOboall request obo input stream but maxreads is 1"); return val; } ID acdSetObo TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD obo term input DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetObo(AcdPAcd thys) { AjPOboall val; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool nullok = ajFalse; ajint itry; ajint maxreads; AjPStr infname = NULL; val = ajOboallNew(); /* set the default value */ acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToInt(thys, "maxreads", INT_MAX, &maxreads); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); ajStrDel(&infname); acdAttrToBool(thys, "entry", ajFalse, &val->Oboin->Input->Text); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajOboallDel(&val); break; } ajOboinQryS(val->Oboin, acdReply); acdGetValueAssoc(thys, "iformat", &val->Oboin->Input->Formatstr); acdGetValueAssoc(thys, "iquery", &val->Oboin->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &val->Oboin->Input->Fpos, &acdTmpStr); ok = ajOboinRead(val->Oboin, val->Obo); } if(!ok) acdBadRetry(thys); if(maxreads > 1) val->Multi = ajTrue; acdInFileSave(acdReply, ajOboallGetoboId(val), ajTrue); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); return; } ID acdSetOutType TY static MO ajacd LB acd XX DE Generic definition for any of the ACD formatted output types. DE May be replaced by a specific acdSet function if additional attributes DE or qualifiers are to be processed. DE DE Understands all attributes and associated qualifiers for these item types. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX PN [2] PA r type const char* PD Standard output type name. PX RT AjPOutfile RD Output file object of the specified type RX // static AjPOutfile acdSetOutType(AcdPAcd thys, const char* type) { AjPOutfile val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjBool nulldefault; AjPStr name = NULL; AjPStr ext = NULL; AjPStr fmt = NULL; AjPStr dir = NULL; ajint itype = -1; ajint i; val = NULL; for (i=0; acdOuttype[i].Name; i++) if (ajCharMatchC(acdOuttype[i].Name, type)) { itype = i; break; } if (itype < 0) acdError("Unknown output type '%s'", type); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); acdAttrResolve(thys, "name", &name); if (!acdGetValueAssoc(thys, "oformat", &fmt)) ajStrAssignC(&fmt, acdOuttype[itype].Format); if(!acdAttrResolve(thys, "extension", &ext)) ajStrAssignS(&ext, fmt); acdGetValueAssoc(thys, "odirectory", &dir); acdOutDirectory(&dir); required = acdIsRequired(thys); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ acdOutFilename(&acdReplyDef, name, ext); else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else { acdOutFilename(&acdTmpOutFName, name, ext); acdReplyInitS(thys, acdTmpOutFName, &acdReplyDef); } ajStrDel(&name); ajStrDel(&ext); if(acdOuttype[itype].Prompt) (*acdOuttype[itype].Prompt)(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { ajStrAssignS(&acdOutFullFName, acdReply); ajFilenameReplacePathS(&acdOutFullFName, dir); val = ajOutfileNewNameS(acdOutFullFName); if(!ok) { acdBadVal(thys, required, "Unable to open output file '%S'", acdOutFullFName); ajOutfileClose(&val); } ajStrAssignEmptyS(&val->Formatstr, fmt); ajStrAssignEmptyC(&val->Type, type); if(acdOuttype[itype].Outformat) { if(!(*acdOuttype[itype].Outformat)(val->Formatstr, &val->Format)) { /* test acdc-outbadformat */ ajDie("Output option -%S: " "Format validation failed for type '%s'", thys->Name, type); } } } else if(!nullok) { acdBadVal(thys, required, "Output file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdOutFullFName); ajStrDel(&fmt); ajStrDel(&dir); return val; } ID ajAcdGetOutassembly TY public MO ajacd LB acd XX DE Returns an item of type Outassembly as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutassembly(const char *token) { return acdGetValueRef(token, "outassembly"); } ID acdSetOutassembly TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outassembly item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutassembly(AcdPAcd thys) { acdSetOutType(thys, "outassembly"); return; } ID ajAcdGetOutcodon TY public MO ajacd LB acd XX DE Returns an item of type Outcodon as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutcodon(const char *token) { return acdGetValueRef(token, "outcodon"); } ID acdSetOutcodon TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outcodon item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutcodon(AcdPAcd thys) { acdSetOutType(thys, "outcodon"); return; } ID ajAcdGetOutcpdb TY public MO ajacd LB acd XX DE Returns an item of type Outcpdb as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutcpdb(const char *token) { return acdGetValueRef(token, "outcpdb"); } ID acdSetOutcpdb TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outcpdb item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutcpdb(AcdPAcd thys) { acdSetOutType(thys, "outcpdb"); return; } ID ajAcdGetOutdata TY public MO ajacd LB acd XX DE Returns an item of type Outdata as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutdata(const char *token) { return acdGetValueRef(token, "outdata"); } ID acdSetOutdata TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outdata item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutdata(AcdPAcd thys) { acdSetOutType(thys, "outdata"); return; } ID ajAcdGetOutdir TY public MO ajacd LB acd XX DE Returns an item of type AjPDirout which has been validated as an output DE directory. DE DE Optionally can be forced to have a fully qualified path when returned. XX PN [1] PA r token const char* PD Text token name PX RT AjPDirout RD Output directory object RX // AjPDirout ajAcdGetOutdir(const char *token) { return acdGetValueRef(token, "outdir"); } ID ajAcdGetOutdirName TY public MO ajacd LB acd XX DE Returns an item of type AjPStr which has been validated as an output DE directory. DE DE Optionally can be forced to have a fully qualified path when returned. XX PN [1] PA r token const char* PD Text token name PX RT AjPStr RD String containing a directory name RX // AjPStr ajAcdGetOutdirName(const char *token) { AjPStr ret = NULL; AjPDirout dirout; dirout = acdGetValue(token, "outdir"); ret = ajStrNewS(ajDiroutGetPath(dirout)); return ret; } ID acdSetOutdir TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD output directory item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value is "." the current directory. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutdir(AcdPAcd thys) { AjPDirout val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok = ajFalse; AjBool dopath = ajFalse; AjBool create = ajFalse; AjPStr ext = NULL; val = NULL; /* set the default value */ acdAttrToBool(thys, "fullpath", ajFalse, &dopath); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "create", ajFalse, &create); acdGetValueAssoc(thys, "extension", &ext); required = acdIsRequired(thys); acdReplyInitC(thys, ".", &acdReplyDef); acdPromptOutdir(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { ajDebug("acdSetOutdir start reply '%S' dopath:%B ok:%B\n", acdReply, dopath,ok); if(dopath) ok = ajDirnameFillPath(&acdReply); ajDebug("acdSetOutdir dir done reply '%S' dopath:%B ok:%B\n", acdReply, dopath,ok); if (ok) { val = ajDiroutNewPathExt(acdReply, ext); if (!val) ok = ajFalse; else { if(create) ok = ajDiroutOpen(val); else ok = ajDiroutExists(val); } } if(!ok) acdBadVal(thys, required, "Unable to open output directory '%S'", acdReply); } else if(!nullok) { acdBadVal(thys, required, "Directory path is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&ext); return; } ID ajAcdGetOutdiscrete TY public MO ajacd LB acd XX DE Returns an item of type Outdiscrete as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutdiscrete(const char *token) { return acdGetValueRef(token, "outdiscrete"); } ID acdSetOutdiscrete TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outdiscrete item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutdiscrete(AcdPAcd thys) { acdSetOutType(thys, "outdiscrete"); return; } ID ajAcdGetOutdistance TY public MO ajacd LB acd XX DE Returns an item of type Outdistance as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutdistance(const char *token) { return acdGetValueRef(token, "outdistance"); } ID acdSetOutdistance TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outdistance item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutdistance(AcdPAcd thys) { acdSetOutType(thys, "outdistance"); return; } ID ajAcdGetOutfile TY public MO ajacd LB acd XX DE Returns an item of type Outfile as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPFile RD File object. The file was already opened by RD acdSetOutfile so this just returns the pointer. RX // AjPFile ajAcdGetOutfile(const char *token) { return acdGetValueRef(token, "outfile"); } ID ajAcdGetOutfileName TY public MO ajacd LB acd XX DE Returns the filename of an item of type Outfile as defined in a DE named ACD item. The file is closed and can be reused. If the file DE had the append attribute set it still has the original contents. DE DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPStr RD Filename. RX // AjPStr ajAcdGetOutfileName(const char *token) { AjPStr ret; AjPFile outfile; outfile = acdGetValueRef(token, "outfile"); if(!outfile) return NULL; ret = ajStrNewS(ajFileGetPrintnameS(outfile)); ajFileClose(&outfile); return ret; } ID acdSetOutfile TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outfile item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value, if stdout or filtering is on is "stdout" for the DE first file. DE DE Otherwise an output file name is constructed. DE DE Various file naming options are defined, but not yet implemented here. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutfile(AcdPAcd thys) { AjPFile val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjBool nulldefault; AjBool append; AjPStr name = NULL; AjPStr ext = NULL; AjPStr dir = NULL; val = NULL; /* set the default value */ acdAttrResolve(thys, "name", &name); acdAttrResolve(thys, "extension", &ext); acdGetValueAssoc(thys, "odirectory", &dir); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); acdAttrToBool(thys, "append", ajFalse, &append); acdOutDirectory(&dir); required = acdIsRequired(thys); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ acdOutFilename(&acdReplyDef, name, ext); else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else { acdOutFilename(&acdTmpOutFName, name, ext); acdReplyInitS(thys, acdTmpOutFName, &acdReplyDef); } ajStrDel(&name); ajStrDel(&ext); acdPromptOutfile(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); ajStrAssignS(&acdOutFullFName, acdReply); if(ajStrGetLen(acdReply)) { ajFilenameReplacePathS(&acdOutFullFName, dir); if(append) val = ajFileNewOutappendNameS(acdOutFullFName); else val = ajFileNewOutNameS(acdOutFullFName); if(!val) { acdBadVal(thys, required, "Unable to open file '%S' for output", acdOutFullFName); ok = ajFalse; } } else if(!nullok) { acdBadVal(thys, required, "Output file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdOutFullFName); ajStrDel(&dir); return; } ID ajAcdGetOutfreq TY public MO ajacd LB acd XX DE Returns an item of type Outfreq as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutfreq(const char *token) { return acdGetValueRef(token, "outfreq"); } ID acdSetOutfreq TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outfreq item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutfreq(AcdPAcd thys) { acdSetOutType(thys, "outfreq"); return; } ID ajAcdGetOutmatrix TY public MO ajacd LB acd XX DE Returns an item of type Outmatrix as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutmatrix(const char *token) { return acdGetValueRef(token, "outmatrix"); } ID acdSetOutmatrix TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outmatrix item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutmatrix(AcdPAcd thys) { acdSetOutType(thys, "outmatrix"); return; } ID ajAcdGetOutmatrixf TY public MO ajacd LB acd XX DE Returns an item of type Outmatrixf as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutmatrixf(const char *token) { return acdGetValueRef(token, "outmatrixf"); } ID acdSetOutmatrixf TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outmatrixf item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutmatrixf(AcdPAcd thys) { acdSetOutType(thys, "outmatrixf"); return; } ID ajAcdGetOutobo TY public MO ajacd LB acd XX DE Returns an item of type Outobo as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutobo(const char *token) { return acdGetValueRef(token, "outobo"); } ID acdSetOutobo TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outobo item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutobo(AcdPAcd thys) { acdSetOutType(thys, "outobo"); return; } ID ajAcdGetOutproperties TY public MO ajacd LB acd XX DE Returns an item of type Outproperties as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutproperties(const char *token) { return acdGetValueRef(token, "outproperties"); } ID acdSetOutproperties TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outproperties item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutproperties(AcdPAcd thys) { acdSetOutType(thys, "properties"); return; } ID ajAcdGetOutrefseq TY public MO ajacd LB acd XX DE Returns an item of type Outrefseq as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutrefseq(const char *token) { return acdGetValueRef(token, "outrefseq"); } ID acdSetOutrefseq TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outrefseq item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutrefseq(AcdPAcd thys) { acdSetOutType(thys, "outrefseq"); return; } ID ajAcdGetOutresource TY public MO ajacd LB acd XX DE Returns an item of type Outresource as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutresource(const char *token) { return acdGetValueRef(token, "outresource"); } ID acdSetOutresource TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD data resource output item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutresource(AcdPAcd thys) { acdSetOutType(thys, "outresource"); return; } ID ajAcdGetOutscop TY public MO ajacd LB acd XX DE Returns an item of type Outscop as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutscop(const char *token) { return acdGetValueRef(token, "outscop"); } ID acdSetOutscop TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outscop item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutscop(AcdPAcd thys) { acdSetOutType(thys, "outscop"); return; } ID ajAcdGetOuttaxon TY public MO ajacd LB acd XX DE Returns an item of type Outtaxon as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOuttaxon(const char *token) { return acdGetValueRef(token, "outtaxon"); } ID acdSetOuttaxon TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outtaxon item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOuttaxon(AcdPAcd thys) { acdSetOutType(thys, "outtaxon"); return; } ID ajAcdGetOuttext TY public MO ajacd LB acd XX DE Returns an item of type Outtext as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOuttext(const char *token) { return acdGetValueRef(token, "outtext"); } ID acdSetOuttext TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outtext item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOuttext(AcdPAcd thys) { acdSetOutType(thys, "outtext"); return; } ID ajAcdGetOuttree TY public MO ajacd LB acd XX DE Returns an item of type Outtree as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOuttree(const char *token) { return acdGetValueRef(token, "outtree"); } ID acdSetOuttree TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outtree item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOuttree(AcdPAcd thys) { acdSetOutType(thys, "outtree"); return; } ID ajAcdGetOuturl TY public MO ajacd LB acd XX DE Returns an item of type Outurl as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOuturl(const char *token) { return acdGetValueRef(token, "outurl"); } ID acdSetOuturl TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outurl item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOuturl(AcdPAcd thys) { acdSetOutType(thys, "outurl"); return; } ID ajAcdGetOutvariation TY public MO ajacd LB acd XX DE Returns an item of type Outvariation as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPOutfile RD File object. The file was already opened by RD acdSetOut so this just returns the pointer. RX // AjPOutfile ajAcdGetOutvariation(const char *token) { return acdGetValueRef(token, "outvariation"); } ID acdSetOutvariation TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD outvariation item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-oformat" DE are stored in the object and applied to the data on output. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetOutvariation(AcdPAcd thys) { acdSetOutType(thys, "outvariation"); return; } ID ajAcdGetPattern TY public MO ajacd LB acd XX DE Returns an item of type Pattern as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPPatlistSeq RD Compiled pattern list of sequence patterns. RD The original pattern string is available RD through a call to ajAcdGetValue RX // AjPPatlistSeq ajAcdGetPattern(const char *token) { return acdGetValueRef(token, "pattern"); } ID acdSetPattern TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD sequence pattern item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is NULL. DE DE Attributes for minimum and maximum length are applied with error DE messages if exceeded. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetPattern(AcdPAcd thys) { AjPPatlistSeq val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool upper; AjBool lower; ajint itry; ajint minlen; ajint maxlen; ajint maxsize; ajint len; AjPStr type = NULL; AjPStr patname = NULL; AjPStr fmt = NULL; ajint mismatch = 0; AjBool isprotein = ajTrue; acdAttrToInt(thys, "minlength", 1, &minlen); acdAttrToInt(thys, "maxlength", INT_MAX, &maxlen); acdAttrToBool(thys, "upper", ajFalse, &upper); acdAttrToBool(thys, "lower", ajFalse, &lower); acdAttrToStr(thys, "type", "protein", &type); acdAttrToInt(thys, "maxsize", INT_MAX, &maxsize); acdQualToInt(thys, "pmismatch", 0, &mismatch, &acdTmpStr); acdGetValueAssoc(thys, "pname", &patname); acdGetValueAssoc(thys, "pformat", &fmt); ajStrFmtLower(&type); if(ajStrGetCharFirst(type) != 'p') isprotein = ajFalse; if(mismatch < 0) mismatch = 0; ajDebug("acdSetPattern name '%S' mismatch %d type '%S' protein %B\n", patname, mismatch, type, isprotein); required = acdIsRequired(thys); acdReplyInitC(thys, "", &acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ if(val) ajPatlistSeqDel(&val); ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); len = ajStrGetLen(acdReply); if(len < minlen) { acdBadVal(thys, required, "Too short (%S) - minimum length is %d characters", thys->Name, minlen); ok = ajFalse; } if(len > maxlen) { acdBadVal(thys, required, "Too long (%S) - maximum length is %d characters", thys->Name,maxlen); ok = ajFalse; } if(ok) val = ajPatlistSeqRead(acdReply, patname, fmt, isprotein, mismatch); if(ok && !val) { acdBadVal(thys, required, "Bad pattern definition:\n '%S'", acdReply); ok = ajFalse; } } if(!ok) acdBadRetry(thys); ajStrDel(&patname); ajStrDel(&fmt); ajStrDel(&type); /* regexps have special set attributes the same as strings */ thys->SAttr = acdAttrListCount(acdCalcRegexp); thys->SetAttr = &acdCalcRegexp[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[0], ajStrGetLen(acdReply)); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); return; } ID ajAcdGetProperties TY public MO ajacd LB acd XX DE Returns an item of type Properties as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPPhyloProp RD Properties array. The data was already set by RD acdSetProperties so this just returns the pointer. RX // AjPPhyloProp ajAcdGetProperties(const char *token) { return acdGetValueRef(token, "properties"); } ID acdSetProperties TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD proerties file item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is an empty string. DE DE Attributes for length and maximum property character are applied with error DE messages if exceeded. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetProperties(AcdPAcd thys) { AjPPhyloProp val; AjBool required = ajFalse; AjBool ok = ajFalse; AjPStr infname = NULL; ajint itry; AjBool nullok = ajFalse; ajint size; ajint len; AjPStr propchars = NULL; val = NULL; acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToInt(thys, "length", 0, &len); acdAttrToInt(thys, "size", 1, &size); acdAttrToStr(thys, "characters", "", &propchars); ajDebug("acdSetProperties len: %d size: %d\n", len, size); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptInfile(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajPhyloPropRead(acdReply, propchars, len, size); if(!val) { acdBadVal(thys, required, "Unable to read properties from '%S'", acdReply); ok = ajFalse; } } else if(!nullok) { acdBadVal(thys, required, "Input file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, NULL, ajTrue); /* properties have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcProperties); thys->SetAttr = &acdCalcProperties[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); if(val) { ajStrFromInt(&thys->SetStr[0],val->Len); /* string length */ ajStrFromInt(&thys->SetStr[1],val->Size); /* string count */ ajStrAssignS(&thys->ValStr, val->Str[0]); } else { ajStrFromInt(&thys->SetStr[0],0); /* string length */ ajStrFromInt(&thys->SetStr[1],0); /* string count */ ajStrAssignClear(&thys->ValStr); } thys->Value = val; ajStrDel(&propchars); return; } ID ajAcdGetRange TY public MO ajacd LB acd XX DE Returns an item of type Range as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPRange RD Range object. RX // AjPRange ajAcdGetRange(const char *token) { return acdGetValueRef(token, "range"); } ID acdSetRange TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD range item. DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetRange(AcdPAcd thys) { AjPRange val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool warnrange; AjBool failrange; ajint itry; ajuint imin; ajuint imax; ajuint isize; ajuint iminsize; AjPStr failmsg = NULL; acdRangeTestCalc(thys); acdAttrToUint(thys, "minimum", 1, &imin); acdLog("minimum: %d\n", imin); acdAttrToUint(thys, "maximum", UINT_MAX, &imax); acdLog("maximum: %d\n", imax); acdAttrToUint(thys, "minsize", 0, &iminsize); acdLog("minsize: %d\n", iminsize); acdAttrToUint(thys, "size", 0, &isize); acdLog("size: %d\n", isize); acdAttrToBool(thys, "failrange", ajTrue, &failrange); acdLog("failrange: %B\n", failrange); acdAttrToBool(thys, "warnrange", acdDoWarnRange, &warnrange); acdLog("warnrange: %B\n", warnrange); if(failrange && (imin > imax)) { acdAttrResolve(thys, "rangemessage", &failmsg); if(ajStrGetLen(failmsg)) acdErrorAcd(thys, "Invalid range: %S", failmsg); else acdErrorAcd(thys, "Invalid range: " "minimum value %d more than maximum %d", imin, imax); } required = acdIsRequired(thys); acdReplyInitC(thys, "", &acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); val = ajRangeNewStringLimits(acdReply, imin, imax, iminsize, isize); if(!val) { acdBadVal(thys, required, "Bad range specification '%S'", acdReply); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); return; } ID ajAcdGetRefseq TY public MO ajacd LB acd XX DE Returns an item of type reference sequence as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPRefseq RD Reference sequence object RX // AjPRefseq ajAcdGetRefseq(const char *token) { AjPRefseq val = acdGetValueRef(token, "refseq"); return val; } ID acdSetRefseq TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD reference sequence input DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetRefseq(AcdPAcd thys) { AjPRefseq val; AjPRefseqin refseqin; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool nullok = ajFalse; ajint itry; AjPStr infname = NULL; val = ajRefseqNew(); /* set the default value */ refseqin = ajRefseqinNew(); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); ajStrDel(&infname); acdAttrToBool(thys, "entry", ajFalse, &refseqin->Input->Text); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajRefseqinDel(&refseqin); break; } ajRefseqinQryS(refseqin, acdReply); acdGetValueAssoc(thys, "iformat", &refseqin->Input->Formatstr); acdGetValueAssoc(thys, "iquery", &refseqin->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &refseqin->Input->Fpos, &acdTmpStr); ok = ajRefseqinRead(refseqin, val); } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, ajRefseqGetId(val), ajTrue); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajRefseqinDel(&refseqin); return; } ID ajAcdGetRegexp TY public MO ajacd LB acd XX DE Returns an item of type Regexp as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPPatlistRegex RD Compiled regular expression pattern. RD The original pattern string is available RD through a call to ajAcdGetValue RX // AjPPatlistRegex ajAcdGetRegexp(const char *token) { return acdGetValueRef(token, "regexp"); } ID ajAcdGetRegexpSingle TY public MO ajacd LB acd XX DE Returns an item of type Regexp as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPRegexp RD Compiled regular expression. RD The original pattern string is available RD through a call to ajAcdGetValue RX // AjPRegexp ajAcdGetRegexpSingle(const char *token) { AjPPatlistRegex val; AjPPatternRegex patreg = NULL; ajint i = 0; val = acdGetValueSingle(token, "regexp"); while (ajPatlistRegexGetNext(val, &patreg)) i++; if(i > 1) ajWarn("Single list value %s, but can choose %d values", token, i); if(i < 1) ajWarn("Single list value %s, no value found: returning NULL value", token); return ajPatternRegexGetCompiled(patreg); } ID acdSetRegexp TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD regular expression item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is NULL. DE DE Attributes for minimum and maximum length are applied with error DE messages if exceeded. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetRegexp(AcdPAcd thys) { AjPPatlistRegex val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool upper; AjBool lower; ajint itry; ajint minlen; ajint maxlen; ajint maxsize; ajint len; AjPStr type = NULL; AjPStr patname = NULL; AjPStr fmt = NULL; ajint itype = 0; acdAttrToInt(thys, "minlength", 1, &minlen); acdAttrToInt(thys, "maxlength", INT_MAX, &maxlen); acdAttrToBool(thys, "upper", ajFalse, &upper); acdAttrToBool(thys, "lower", ajFalse, &lower); acdAttrToStr(thys, "type", "string", &type); acdAttrToInt(thys, "maxsize", INT_MAX, &maxsize); acdGetValueAssoc(thys, "pname", &patname); acdGetValueAssoc(thys, "pformat", &fmt); ajStrFmtLower(&type); itype = ajPatternRegexType(type); required = acdIsRequired(thys); acdReplyInitC(thys, "", &acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ if(val) ajPatlistRegexDel(&val); ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); len = ajStrGetLen(acdReply); if(len < minlen) { acdBadVal(thys, required, "Too short (%S) - minimum length is %d characters", thys->Name, minlen); ok = ajFalse; } if(len > maxlen) { acdBadVal(thys, required, "Too long (%S) - maximum length is %d characters", thys->Name,maxlen); ok = ajFalse; } /* if(upper) ajStrFmtUpper(&acdReply); if(lower) ajStrFmtLower(&acdReply); */ if(ok) val = ajPatlistRegexRead(acdReply, patname, fmt, itype, upper, lower); if(ok && !val) { acdBadVal(thys, required, "Bad regular expression pattern:\n '%S'", acdReply); ok = ajFalse; } } if(!ok) acdBadRetry(thys); ajStrDel(&patname); ajStrDel(&fmt); ajStrDel(&type); /* regexps have special set attributes the same as strings */ thys->SAttr = acdAttrListCount(acdCalcRegexp); thys->SetAttr = &acdCalcRegexp[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[0], ajStrGetLen(acdReply)); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); return; } ID acdSetRel TY static MO ajacd LB acd XX DE Defines an ACD relation. DE DE Called when a "variable" type ACD item is checked. Should not be called DE for any other item. DE DE At present there is nothing to prompt for here, though there could DE be, for example, a report of what the program does which would appear DE before any user prompts. XX PN [1] PA u thys AcdPAcd PD ACD for the application item. PX RT void RD RX // static void acdSetRel(AcdPAcd thys) { acdAttrToStr(thys, "relations", "", &thys->ValStr); return; } ID ajAcdGetReport TY public MO ajacd LB acd XX DE Returns an item of type Report as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPReport RD Report output object. Already opened RD by ajReportOpen so this just returns the object RX // AjPReport ajAcdGetReport(const char *token) { return acdGetValueRef(token, "report"); } ID acdSetReport TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD report item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-rformat", "-ropenfile" DE are applied to the URO before opening the output file. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetReport(AcdPAcd thys) { AjPReport val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjBool nulldefault; AjPStr name = NULL; AjPStr ext = NULL; AjPStr fmt = NULL; AjPStr dir = NULL; AjPStr taglist = NULL; val = NULL; acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); acdGetValueAssoc(thys, "rdirectory", &dir); acdGetValueAssoc(thys, "rextension", &ext); acdGetValueAssoc(thys, "rname", &name); acdGetValueAssoc(thys, "rformat", &fmt); acdOutDirectory(&dir); required = acdIsRequired(thys); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ acdOutFilename(&acdReplyDef, name, ext); else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else { acdOutFilename(&acdTmpOutFName, name, ext); acdReplyInitS(thys, acdTmpOutFName, &acdReplyDef); } acdPromptReport(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajReportNew(); ajStrAssignEmptyS(&val->Formatstr, fmt); acdAttrToStr(thys, "type", "", &val->Type); acdAttrToStr(thys, "taglist", "", &taglist); acdAttrToBool(thys, "multiple", ajFalse, &val->Multi); acdAttrToInt(thys, "precision", 3, &val->Precision); acdQualToBool(thys, "raccshow", ajFalse, &val->Showacc, &acdReplyDef); acdQualToBool(thys, "rdesshow", ajFalse, &val->Showdes, &acdReplyDef); acdQualToBool(thys, "rscoreshow", ajTrue, &val->Showscore, &acdReplyDef); acdQualToBool(thys, "rstrandshow", ajTrue, &val->Showstrand, &acdReplyDef); acdQualToBool(thys, "rusashow", ajFalse, &val->Showusa, &acdReplyDef); acdQualToInt(thys, "rmaxall", 0, &val->MaxHitAll, &acdReplyDef); acdQualToInt(thys, "rmaxseq", 0, &val->MaxHitSeq, &acdReplyDef); /* test acdc-reportbadtaglist */ if(!ajReportSetTagsS(val, taglist)) acdErrorAcd(thys, "Bad tag list for report"); /* test acdc-reportbadtags */ if(!ajReportValid(val)) ajDie("Report option -%S: Validation failed", thys->Name); ajStrAssignS(&acdOutFullFName, acdReply); ajFilenameReplacePathS(&acdOutFullFName, dir); ok = ajReportOpen(val, acdOutFullFName); if(!ok) { acdBadVal(thys, required, "Unable to open report file '%S'", acdOutFullFName); ajReportDel(&val); } } else if(!nullok) { acdBadVal(thys, required, "Report file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdOutFullFName); ajStrDel(&acdReply); ajStrDel(&acdReplyDef); ajStrDel(&name); ajStrDel(&ext); ajStrDel(&fmt); ajStrDel(&dir); ajStrDel(&taglist); return; } ID ajAcdGetResource TY public MO ajacd LB acd XX DE Returns an item of type resource as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPResource RD Data resource object RX // AjPResource ajAcdGetResource(const char *token) { AjPResourceall val = acdGetValue(token, "resource"); if(val->Multi) ajWarn("ajAcdGetResource request single resource but maxreads > 1"); val->Returned = ajTrue; return val->Resource; } ID ajAcdGetResourceall TY public MO ajacd LB acd XX DE Returns an input stream of an item of type Resource as defined in a DE named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPResourceall RD Data resource input stream RX // AjPResourceall ajAcdGetResourceall(const char *token) { AjPResourceall val = acdGetValueRef(token, "resource"); if(!val->Multi) ajWarn("ajAcdGetResourceall request data resource input stream " "but maxreads is 1"); return val; } ID acdSetResource TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD data resource input DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetResource(AcdPAcd thys) { AjPResourceall val; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool nullok = ajFalse; ajint itry; ajint maxreads; AjPStr infname = NULL; val = ajResourceallNew(); /* set the default value */ acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToInt(thys, "maxreads", INT_MAX, &maxreads); acdAttrToBool(thys, "entry", ajFalse, &val->Resourcein->Input->Text); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajResourceallDel(&val); break; } ajResourceinQryS(val->Resourcein, acdReply); acdGetValueAssoc(thys, "iformat", &val->Resourcein->Input->Formatstr); acdGetValueAssoc(thys, "iquery", &val->Resourcein->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &val->Resourcein->Input->Fpos, &acdTmpStr); ok = ajResourceinRead(val->Resourcein, val->Resource); } if(!ok) acdBadRetry(thys); if(maxreads > 1) val->Multi = ajTrue; acdInFileSave(acdReply, ajResourceallGetresourceId(val), ajTrue); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); return; } ID ajAcdGetScop TY public MO ajacd LB acd XX DE Returns an item of type Scop as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPFile RD Scop input file. RX // AjPFile ajAcdGetScop(const char *token) { return acdGetValueRef(token, "scop"); } ID acdSetScop TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD clean pdb file item. DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetScop(AcdPAcd thys) { AjPFile val; AjPStr name = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjPStr fmt = NULL; val = NULL; /* set the default value */ acdAttrResolve(thys, "name", &name); if (!acdGetValueAssoc(thys, "format", &fmt)) ajStrAssignClear(&fmt); required = acdIsRequired(thys); acdReplyInitS(thys, name, &acdReplyDef); acdPromptScop(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajFileNewInNameS(acdReply); if(!val) { acdBadVal(thys, required, "Unable to read scop data '%S'", acdReply); ok = ajFalse; } } else { acdBadVal(thys, required, "Scop data file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&name); ajStrDel(&fmt); return; } ID ajAcdGetSelect TY public MO ajacd LB acd XX DE Returns an item of type Select as defined in a named ACD item, DE which is an array of strings terminated by a null value. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPStr* RD String array of values with NULL as last element. RX // AjPStr* ajAcdGetSelect(const char *token) { return acdGetValueRef(token, "selection"); } ID ajAcdGetSelectSingle TY public MO ajacd LB acd XX DE Returns one item from an array of type Select as defined in a named DE ACD item, which is an array of strings terminated by a null value. DE Called by the application after all ACD values have been set, and DE simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPStr RD String array of values with NULL as last element. RX // AjPStr ajAcdGetSelectSingle(const char *token) { AjPStr* val; ajint i; val = acdGetValueSingle(token, "select"); for(i=0; val[i]; i++) continue; if(i > 1) ajWarn("Single list value %s, but can choose %d values", token, i); if(i < 1) ajWarn("Single list value %s, no value found: returning NULL value", token); return val[0]; } ID acdSetSelect TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD select menu item. DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetSelect(AcdPAcd thys) { AjPStr* val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; ajint i; ajint min=0, max=5; val = NULL; /* set the default value */ required = acdIsRequired(thys); acdReplyInitC(thys, "", &acdReplyDef); acdAttrToInt(thys, "minimum", 1, &min); acdAttrToInt(thys, "maximum", 1, &max); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) { acdSelectPrompt(thys); acdUserGet(thys, &acdReply); } val = acdSelectValue(thys, min, max, acdReply); if(!val) { acdBadVal(thys, required, "Bad select option '%S'", acdReply); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; for(i=0; val[i]; i++) { if(i) ajStrAppendC(&thys->ValStr, ";"); ajStrAppendS(&thys->ValStr, val[i]); } return; } ID ajAcdGetSeq TY public MO ajacd LB acd XX DE Returns an item of type Seq as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPSeq RD Sequence object. The sequence was already loaded by RD acdSetSeq so this just returns the pointer. RX // AjPSeq ajAcdGetSeq(const char *token) { return acdGetValueRef(token, "sequence"); } ID acdSetSeq TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD sequence item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-sformat", "-sdbname", "-sopenfile", "-sid" DE are applied to the USA before reading the sequence. DE DE Associated qualifiers "-supper", "-slower" and "-sask" are applied DE after reading. DE DE Associated qualifiers "-sbegin", "-send" and "-sreverse" DE are applied as appropriate, with prompting for values, DE after the sequence has been read. They are applied to the sequence, DE and the resulting sequence is what is set in the ACD item. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetSeq(AcdPAcd thys) { AjPSeq val = NULL; AjPSeqin seqin; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool okbeg = ajFalse; AjBool okend = ajFalse; AjBool okrev = ajFalse; ajint itry; ajint i; AjPStr infname = NULL; ajint sqbegin = 0; ajint sqend = 0; AjBool sreverse = ajFalse; AjBool sprompt = ajFalse; AjBool snuc = ajFalse; AjBool sprot = ajFalse; AjBool nullok = ajFalse; AjPStr typestr = NULL; val = ajSeqNew(); /* set the default value */ seqin = ajSeqinNew(); /* set the default value */ acdQualToBool(thys, "snucleotide", ajFalse, &snuc, &acdReplyDef); acdQualToBool(thys, "sprotein", ajFalse, &sprot, &acdReplyDef); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToStr(thys, "type", "", &typestr); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptSeq(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajSeqDel(&val); break; } ajSeqinUsa(&seqin, acdReply); if(ajStrGetLen(typestr)) { ajStrAssignS(&seqin->Inputtype, typestr); acdInTypeSeqSave(seqin->Inputtype); } else acdInTypeSeqSave(NULL); acdAttrToBool(thys, "features", ajFalse, &seqin->Features); acdAttrToBool(thys, "entry", ajFalse, &seqin->Input->Text); acdGetValueAssoc(thys, "sformat", &seqin->Input->Formatstr); acdGetValueAssoc(thys, "sdbname", &seqin->Input->Db); /* acdGetValueAssoc(thys, "sopenfile", &seqin->Filename);*/ /* obsolete */ acdGetValueAssoc(thys, "sid", &seqin->Entryname); acdGetValueAssoc(thys, "iquery", &seqin->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &seqin->Input->Fpos, &acdTmpStr); acdGetValueAssoc(thys, "ufo", &seqin->Ufo); acdGetValueAssoc(thys, "fformat", &seqin->Ftquery->Formatstr); acdGetValueAssoc(thys, "fopenfile", &seqin->Ftquery->Filename); acdQualToBool(thys, "scircular", ajFalse, &seqin->Circular, &acdTmpStr); acdQualToBool(thys, "supper", ajFalse, &seqin->Upper, &acdTmpStr); acdQualToBool(thys, "slower", ajFalse, &seqin->Lower, &acdTmpStr); okbeg = acdQualToSeqbegin(thys, "sbegin", 0, &sqbegin, &acdTmpStr); okend = acdQualToSeqend(thys, "send", 0, &sqend, &acdTmpStr); okrev = acdQualToBool(thys, "sreverse", ajFalse, &sreverse, &acdTmpStr); if(snuc) ajSeqinSetNuc(seqin); if(sprot) ajSeqinSetProt(seqin); i = ajStrGetLen(seqin->Ufo) + ajStrGetLen(seqin->Ftquery->Formatstr) + ajStrGetLen(seqin->Ftquery->Filename); if(i && !seqin->Features) ajWarn("Feature table ignored"); if(seqin->Features) acdLog("acdSetSeq with features UFO '%S'\n", seqin->Ufo); /* (try to) read the sequence */ ok = ajSeqRead(val, seqin); if(!ok) { acdBadVal(thys, required, "Unable to read sequence '%S'", acdReply); } } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, ajSeqGetNameS(val), ajTrue); /* save sequence name */ /* some standard options using associated qualifiers */ acdQualToBool(thys, "sask", ajFalse, &sprompt, &acdReplyDef); if(val) { /* now process the begin, end and reverse options */ if(seqin->Begin) okbeg = ajTrue; for(itry=acdPromptTry; itry && !okbeg; itry--) { ajStrAssignC(&acdReplyPrompt, "start"); if(sprompt) acdUserGetPrompt(thys, "sbegin", " Begin at position", &acdReplyPrompt); if(ajStrMatchCaseC(acdReplyPrompt, "start")) ajStrAssignC(&acdReplyPrompt, "0"); okbeg = ajStrToInt(acdReplyPrompt, &sqbegin); if(!okbeg) acdBadVal(thys, sprompt, "Invalid sequence position '%S'", acdReplyPrompt); } if(!okbeg) acdBadRetry(thys); if(sqbegin) { seqin->Begin = sqbegin; val->Begin = sqbegin; acdSetQualDefInt(thys, "sbegin", sqbegin); } if(seqin->End) okend = ajTrue; for(itry=acdPromptTry; itry && !okend; itry--) { ajStrAssignC(&acdReplyPrompt, "end"); if(sprompt) acdUserGetPrompt(thys, "send", " End at position", &acdReplyPrompt); if(ajStrMatchCaseC(acdReplyPrompt, "end")) ajStrAssignC(&acdReplyPrompt, "0"); okend = ajStrToInt(acdReplyPrompt, &sqend); if(!okend) acdBadVal(thys, sprompt, "Invalid sequence position '%S'", acdReplyPrompt); } if(!okend) acdBadRetry(thys); if(sqend) { seqin->End = sqend; val->End = sqend; acdSetQualDefInt(thys, "send", sqend); } if(ajSeqIsNuc(val)) { for(itry=acdPromptTry; itry && !okrev; itry--) { ajStrAssignC(&acdReplyPrompt, "N"); if(sprompt) acdUserGetPrompt(thys, "sreverse", " Reverse strand", &acdReplyPrompt); okrev = ajStrToBool(acdReplyPrompt, &sreverse); if(!okrev) acdBadVal(thys, sprompt, "Invalid Y/N value '%S'", acdReplyPrompt); } if(!okrev) acdBadRetry(thys); if(sreverse) { seqin->Rev = sreverse; val->Rev = sreverse; acdSetQualDefBool(thys, "sreverse", sreverse); } } acdLog("sbegin: %d, send: %d, sreverse: %B\n", sqbegin, sqend, sreverse); if(val->Rev) ajSeqReverseDo(val); /* sequences have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcSeq); thys->SetAttr = &acdCalcSeq[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[ACD_SEQ_BEGIN], ajSeqGetBegin(val)); ajStrFromInt(&thys->SetStr[ACD_SEQ_END], ajSeqGetEnd(val)); ajStrFromInt(&thys->SetStr[ACD_SEQ_LENGTH], ajSeqGetLen(val)); ajStrFromBool(&thys->SetStr[ACD_SEQ_NUCLEIC], ajSeqIsNuc(val)); ajStrFromBool(&thys->SetStr[ACD_SEQ_PROTEIN], ajSeqIsProt(val)); ajStrAssignS(&thys->SetStr[ACD_SEQ_NAME], val->Name); ajStrAssignS(&thys->SetStr[ACD_SEQ_USA], ajSeqGetUsaS(val)); } else { /* sequences have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcSeq); thys->SetAttr = &acdCalcSeq[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[ACD_SEQ_BEGIN], 0); ajStrFromInt(&thys->SetStr[ACD_SEQ_END], 0); ajStrFromInt(&thys->SetStr[ACD_SEQ_LENGTH], 0); ajStrFromBool(&thys->SetStr[ACD_SEQ_NUCLEIC], ajFalse); ajStrFromBool(&thys->SetStr[ACD_SEQ_PROTEIN], ajFalse); ajStrAssignC(&thys->SetStr[ACD_SEQ_NAME], ""); ajStrAssignC(&thys->SetStr[ACD_SEQ_USA], ""); } thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajSeqinDel(&seqin); ajStrDel(&typestr); return; } ID ajAcdGetSeqall TY public MO ajacd LB acd XX DE Returns an item of type Seq as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPSeqall RD Sequence stream object. The sequence was already RD loaded by acdSetSeqall so this just returns the pointer. RX // AjPSeqall ajAcdGetSeqall(const char *token) { return acdGetValueRef(token, "seqall"); } ID acdSetSeqall TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD sequence item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-sformat", "-sdbname", "-sopenfile", "-sid" DE are applied to the USA before reading the sequence. DE DE Associated qualifier "-sask" is applied DE after reading. DE DE Associated qualifiers "-supper", "-slower" are applied DE globally DE DE Associated qualifiers "-sbegin", "-send" and "-sreverse" DE are applied as appropriate, with prompting for values, DE after the sequence has been read. They are applied to the sequence, DE and the resulting sequence is what is set in the ACD item. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetSeqall(AcdPAcd thys) { AjPSeqall val = NULL; AjPSeqin seqin; AjPSeq seq; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool okbeg = ajFalse; AjBool okend = ajFalse; AjBool okrev = ajFalse; ajint itry; AjBool nullok = ajFalse; AjPStr infname = NULL; ajint sqbegin = 0; ajint sqend = 0; AjBool sreverse = ajFalse; AjBool sprompt = ajFalse; AjBool snuc = ajFalse; AjBool sprot = ajFalse; AjPStr typestr = NULL; val = ajSeqallNew(); /* set the default value */ seqin = val->Seqin; seqin->Input->Multi = ajTrue; seq = val->Seq; acdQualToBool(thys, "snucleotide", ajFalse, &snuc, &acdReplyDef); acdQualToBool(thys, "sprotein", ajFalse, &sprot, &acdReplyDef); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToStr(thys, "type", "", &typestr); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptSeq(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajSeqallDel(&val); break; } ajSeqinUsa(&seqin, acdReply); if(ajStrGetLen(typestr)) { ajStrAssignS(&seqin->Inputtype, typestr); acdInTypeSeqSave(seqin->Inputtype); } else acdInTypeSeqSave(NULL); acdAttrToBool(thys, "features", ajFalse, &seqin->Features); acdAttrToBool(thys, "entry", ajFalse, &seqin->Input->Text); acdGetValueAssoc(thys, "sformat", &seqin->Input->Formatstr); acdGetValueAssoc(thys, "sdbname", &seqin->Input->Db); /* acdGetValueAssoc(thys, "sopenfile", &seqin->Filename);*/ /* obsolete */ acdGetValueAssoc(thys, "sid", &seqin->Entryname); acdGetValueAssoc(thys, "iquery", &seqin->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &seqin->Input->Fpos, &acdTmpStr); acdGetValueAssoc(thys, "ufo", &seqin->Ufo); acdGetValueAssoc(thys, "fformat", &seqin->Ftquery->Formatstr); acdGetValueAssoc(thys, "fopenfile", &seqin->Ftquery->Filename); acdQualToBool(thys, "scircular", ajFalse, &seqin->Circular, &acdTmpStr); acdQualToBool(thys, "supper", ajFalse, &seqin->Upper, &acdTmpStr); acdQualToBool(thys, "slower", ajFalse, &seqin->Lower, &acdTmpStr); okbeg = acdQualToSeqbegin(thys, "sbegin", 0, &seqin->Begin, &acdTmpStr); okend = acdQualToSeqend(thys, "send", 0, &seqin->End, &acdTmpStr); okrev = acdQualToBool(thys, "sreverse", ajFalse, &sreverse, &acdTmpStr); if(snuc) ajSeqinSetNuc(seqin); if(sprot) ajSeqinSetProt(seqin); if(ajStrGetLen(seqin->Ufo)) seqin->Features = ajTrue; ok = ajSeqAllRead(seq, seqin); if(!ok) acdBadVal(thys, required, "Unable to read sequence '%S'", acdReply); } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, ajSeqallGetseqName(val), ajTrue); /* commentedout__ajSeqinDel(&seqin);*/ acdQualToBool(thys, "sask", ajFalse, &sprompt, &acdReplyDef); /* now process the begin, end and reverse options */ if(val) { if(seqin->Begin) { okbeg = ajTrue; val->Begin = seq->Begin = seqin->Begin; } for(itry=acdPromptTry; itry && !okbeg; itry--) { ajStrAssignC(&acdReplyPrompt, "start"); if(sprompt) acdUserGetPrompt(thys, "sbegin", " Begin at position", &acdReplyPrompt); if(ajStrMatchCaseC(acdReplyPrompt, "start")) ajStrAssignC(&acdReplyPrompt, "0"); okbeg = ajStrToInt(acdReplyPrompt, &sqbegin); if(!okbeg) acdBadVal(thys, sprompt, "Invalid integer value '%S'", acdReplyPrompt); } if(!okbeg) acdBadRetry(thys); if(sqbegin) { seqin->Begin = sqbegin; seq->Begin = sqbegin; val->Begin = sqbegin; acdSetQualDefInt(thys, "sbegin", sqbegin); } if(seqin->End) { okend = ajTrue; val->End = seq->End = seqin->End; } for(itry=acdPromptTry; itry && !okend; itry--) { ajStrAssignC(&acdReplyPrompt, "end"); if(sprompt) acdUserGetPrompt(thys, "send", " End at position", &acdReplyPrompt); if(ajStrMatchCaseC(acdReplyPrompt, "end")) ajStrAssignC(&acdReplyPrompt, "0"); okend = ajStrToInt(acdReplyPrompt, &sqend); if(!okend) acdBadVal(thys, sprompt, "Invalid integer value '%S'", acdReplyPrompt); } if(!okend) acdBadRetry(thys); if(sqend) { seqin->End = sqend; seq->End = sqend; val->End = sqend; acdSetQualDefInt(thys, "send", sqend); } if(ajSeqIsNuc(seq)) { for(itry=acdPromptTry; itry && !okrev; itry--) { ajStrAssignC(&acdReplyPrompt, "N"); if(sprompt) acdUserGetPrompt(thys, "sreverse", " Reverse strand", &acdReplyPrompt); okrev = ajStrToBool(acdReplyPrompt, &sreverse); if(!okrev) acdBadVal(thys, sprompt, "Invalid Y/N value '%S'", acdReplyPrompt); } if(!okrev) acdBadRetry(thys); if(sreverse) { seqin->Rev = sreverse; seq->Rev = sreverse; val->Rev = sreverse; acdSetQualDefBool(thys, "sreverse", sreverse); } } acdLog("sbegin: %d, send: %d, sreverse: %B\n", sqbegin, sqend, sreverse); /* sequences have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcSeqall); thys->SetAttr = &acdCalcSeqall[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[ACD_SEQ_BEGIN], ajSeqallGetseqBegin(val)); ajStrFromInt(&thys->SetStr[ACD_SEQ_END], ajSeqallGetseqEnd(val)); ajStrFromInt(&thys->SetStr[ACD_SEQ_LENGTH], ajSeqGetLen(seq)); ajStrFromBool(&thys->SetStr[ACD_SEQ_NUCLEIC], ajSeqIsNuc(seq)); ajStrFromBool(&thys->SetStr[ACD_SEQ_PROTEIN], ajSeqIsProt(seq)); ajStrAssignS(&thys->SetStr[ACD_SEQ_NAME], seq->Name); ajStrAssignS(&thys->SetStr[ACD_SEQ_USA], ajSeqallGetUsa(val)); } else { /* sequences have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcSeqall); thys->SetAttr = &acdCalcSeq[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[ACD_SEQ_BEGIN], 0); ajStrFromInt(&thys->SetStr[ACD_SEQ_END], 0); ajStrFromInt(&thys->SetStr[ACD_SEQ_LENGTH], 0); ajStrFromBool(&thys->SetStr[ACD_SEQ_NUCLEIC], ajFalse); ajStrFromBool(&thys->SetStr[ACD_SEQ_PROTEIN], ajFalse); ajStrAssignC(&thys->SetStr[ACD_SEQ_NAME], ""); ajStrAssignC(&thys->SetStr[ACD_SEQ_USA], ""); } thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&typestr); return; } ID acdSetSeqsetall TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD sequence item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-sformat", "-sdbname", "-sopenfile", "-sid" DE are applied to the USA before reading the sequence. DE DE Associated qualifiers "-supper", "-slower" and "-sask" are applied DE after reading. DE DE Associated qualifiers "-sbegin", "-send" and "-sreverse" DE are applied as appropriate, with prompting for values, DE after the sequence has been read. They are applied to the sequence, DE and the resulting sequence is what is set in the ACD item. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetSeqsetall(AcdPAcd thys) { AjPSeqset *val = NULL; AjPSeqin seqin; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool okbeg = ajFalse; AjBool okend = ajFalse; AjBool okrev = ajFalse; ajint itry; ajint iattr; AjPStr infname = NULL; ajint sqbegin = 0; ajint sqend = 0; AjBool sreverse = ajFalse; AjBool sprompt = ajFalse; AjBool snuc = ajFalse; AjBool sprot = ajFalse; AjBool nullok = ajFalse; AjBool aligned = ajFalse; AjPStr typestr = NULL; void **sets = NULL; AjPList seqlist; ajint iset = 0; ajint nsets; seqlist = ajListNew(); seqin = ajSeqinNew(); /* set the default value */ seqin->Input->Multi = ajTrue; acdQualToBool(thys, "snucleotide", ajFalse, &snuc, &acdReplyDef); acdQualToBool(thys, "sprotein", ajFalse, &sprot, &acdReplyDef); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "aligned", ajFalse, &aligned); acdAttrToStr(thys, "type", "", &typestr); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptSeq(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) break; ajSeqinUsa(&seqin, acdReply); if(ajStrGetLen(typestr)) { ajStrAssignS(&seqin->Inputtype, typestr); acdInTypeSeqSave(seqin->Inputtype); } else acdInTypeSeqSave(NULL); acdAttrToBool(thys, "features", ajFalse, &seqin->Features); acdGetValueAssoc(thys, "sformat", &seqin->Input->Formatstr); acdGetValueAssoc(thys, "sdbname", &seqin->Input->Db); /* acdGetValueAssoc(thys, "sopenfile", &seqin->Filename);*/ /* obsolete */ acdGetValueAssoc(thys, "sid", &seqin->Entryname); acdGetValueAssoc(thys, "iquery", &seqin->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &seqin->Input->Fpos, &acdTmpStr); acdGetValueAssoc(thys, "ufo", &seqin->Ufo); acdGetValueAssoc(thys, "fformat", &seqin->Ftquery->Formatstr); acdGetValueAssoc(thys, "fopenfile", &seqin->Ftquery->Filename); acdQualToBool(thys, "scircular", ajFalse, &seqin->Circular, &acdTmpStr); acdQualToBool(thys, "supper", ajFalse, &seqin->Upper, &acdTmpStr); acdQualToBool(thys, "slower", ajFalse, &seqin->Lower, &acdTmpStr); okbeg = acdQualToSeqbegin(thys, "sbegin", 0, &seqin->Begin, &acdTmpStr); okend = acdQualToSeqend(thys, "send", 0, &seqin->End, &acdTmpStr); okrev = acdQualToBool(thys, "sreverse", ajFalse, &sreverse, &acdTmpStr); if(snuc) ajSeqinSetNuc(seqin); if(sprot) ajSeqinSetProt(seqin); if(ajStrGetLen(seqin->Ufo)) seqin->Features = ajTrue; ok = ajSeqsetallRead(seqlist, seqin); if(!ok) acdBadVal(thys, required, "Unable to read sequence '%S'", acdReply); } if(!ok) acdBadRetry(thys); nsets = (ajuint) ajListToarray(seqlist,(void***) &sets); if(!nsets) { acdInFileSave(acdReply, ajStrConstEmpty(), ajTrue); /* sequences have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcSeqsetall); thys->SetAttr = &acdCalcSeq[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[ACD_SEQ_BEGIN], 0); ajStrFromInt(&thys->SetStr[ACD_SEQ_END], 0); ajStrFromInt(&thys->SetStr[ACD_SEQ_LENGTH], 0); ajStrFromBool(&thys->SetStr[ACD_SEQ_NUCLEIC], ajFalse); ajStrFromBool(&thys->SetStr[ACD_SEQ_PROTEIN], ajFalse); ajStrAssignC(&thys->SetStr[ACD_SEQ_NAME], ""); ajStrAssignC(&thys->SetStr[ACD_SEQ_USA], ""); ajStrFromFloat(&thys->SetStr[ACD_SEQ_WEIGHT], 0.0, 3); ajStrFromInt(&thys->SetStr[ACD_SEQ_COUNT], 0); ajStrFromInt(&thys->SetStr[ACD_SEQ_MULTICOUNT], 0); } else { val = (AjPSeqset*) sets; ajListFree(&seqlist); acdInFileSave(acdReply, ajSeqsetGetNameS(val[0]), ajTrue); /* save sequence name */ acdQualToBool(thys, "sask", ajFalse, &sprompt, &acdReplyDef); /* now process the begin, end and reverse options */ if(seqin->Begin) { okbeg = ajTrue; for(iset=0;isetBegin = seqin->Begin; } for(itry=acdPromptTry; itry && !okbeg; itry--) { ajStrAssignC(&acdReplyPrompt, "start"); if(sprompt) acdUserGetPrompt(thys, "sbegin", " Begin at position", &acdReplyPrompt); if(ajStrMatchCaseC(acdReplyPrompt, "start")) ajStrAssignC(&acdReplyPrompt, "0"); okbeg = ajStrToInt(acdReplyPrompt, &sqbegin); if(!okbeg) acdBadVal(thys, sprompt, "Invalid integer value '%S'", acdReplyPrompt); } if(!okbeg) acdBadRetry(thys); if(sqbegin) { seqin->Begin = sqbegin; for(iset=0;isetBegin = sqbegin; acdSetQualDefInt(thys, "sbegin", sqbegin); } if(seqin->End) { okend = ajTrue; for(iset=0;isetEnd = seqin->End; } for(itry=acdPromptTry; itry && !okend; itry--) { ajStrAssignC(&acdReplyPrompt, "end"); if(sprompt) acdUserGetPrompt(thys, "send", " End at position", &acdReplyPrompt); if(ajStrMatchCaseC(acdReplyPrompt, "end")) ajStrAssignC(&acdReplyPrompt, "0"); okend = ajStrToInt(acdReplyPrompt, &sqend); if(!okend) acdBadVal(thys, sprompt, "Invalid integer value '%S'", acdReplyPrompt); } if(!okend) acdBadRetry(thys); if(sqend) { seqin->End = sqend; for(iset=0;isetEnd = sqend; acdSetQualDefInt(thys, "send", sqend); } if(ajSeqsetIsNuc(val[0])) { for(itry=acdPromptTry; itry && !okrev; itry--) { ajStrAssignC(&acdReplyPrompt, "N"); if(sprompt) acdUserGetPrompt(thys, "sreverse", " Reverse strand", &acdReplyPrompt); okrev = ajStrToBool(acdReplyPrompt, &sreverse); if(!okrev) acdBadVal(thys, sprompt, "Invalid Y/N value '%S'", acdReplyPrompt); } if(!okrev) acdBadRetry(thys); if(sreverse) { seqin->Rev = sreverse; for(iset=0;isetRev = sreverse; acdSetQualDefBool(thys, "sreverse", sreverse); } } acdLog("sbegin: %d, send: %d, sreverse: Bs\n", sqbegin, sqend, sreverse); if(aligned) for(iset=0;isetRev) ajSeqsetReverse(val[iset]); /* sequences have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcSeqsetall); thys->SetAttr = &acdCalcSeqsetall[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[ACD_SEQ_BEGIN], ajSeqsetGetBegin(val[0])); ajStrFromInt(&thys->SetStr[ACD_SEQ_END], ajSeqsetGetEnd(val[0])); ajStrFromInt(&thys->SetStr[ACD_SEQ_LENGTH], ajSeqsetGetLen(val[0])); ajStrFromBool(&thys->SetStr[ACD_SEQ_PROTEIN], ajSeqsetIsProt(val[0])); ajStrFromBool(&thys->SetStr[ACD_SEQ_NUCLEIC], ajSeqsetIsNuc(val[0])); ajStrAssignS(&thys->SetStr[ACD_SEQ_NAME], val[0]->Name); ajStrAssignS(&thys->SetStr[ACD_SEQ_USA], ajSeqsetGetUsa(val[0])); ajStrFromFloat(&thys->SetStr[ACD_SEQ_WEIGHT], ajSeqsetGetTotweight(val[0]), 3); ajStrFromInt(&thys->SetStr[ACD_SEQ_COUNT], ajSeqsetGetSize(val[0])); ajStrFromInt(&thys->SetStr[ACD_SEQ_MULTICOUNT], nsets); acdInFileSave(acdReply, ajSeqsetGetNameS(val[0]), ajTrue); for(iattr=0; iattr < thys->SAttr; iattr++) ajDebug("CalcAttr %s: '%S'\n", acdCalcSeqset[iattr].Name, thys->SetStr[iattr]); } thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajSeqinDel(&seqin); ajStrDel(&typestr); return; } ID ajAcdGetSeqout TY public MO ajacd LB acd XX DE Returns an item of type Seqout as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPSeqout RD Sequence output object. The file was already opened by RD acdSetSeqout so this just returns the pointer. RX // AjPSeqout ajAcdGetSeqout(const char *token) { return acdGetValueRef(token, "seqout"); } ID acdSetSeqout TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD sequence output item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is "stdout". DE DE Associated qualifier "-osformat" DE is applied to the USA before opening the output file. DE DE Associated qualifiers are defined but not yet implemented. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetSeqout(AcdPAcd thys) { AjPSeqout val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjBool nulldefault; AjPStr name = NULL; AjPStr ext = NULL; AjPStr fmt = NULL; AjPStr typestr = NULL; AjBool osfeat; val = NULL; if(!acdGetValueAssoc(thys, "osname", &name)) acdAttrResolve(thys, "name", &name); if(!acdGetValueAssoc(thys, "osextension", &ext)) acdAttrResolve(thys, "extension", &ext); acdGetValueAssoc(thys, "osformat", &fmt); ajStrAssignEmptyS(&ext, fmt); if(!ajStrGetLen(ext)) ajSeqoutstrGetFormatDefault(&ext); acdAttrToBool(thys, "features", ajFalse, &osfeat); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); if(!acdAttrToStr(thys, "type", "", &typestr)) acdInTypeSeq(&typestr); required = acdIsRequired(thys); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ acdOutFilename(&acdReplyDef, name, ext); else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else { acdOutFilename(&acdTmpOutFName, name, ext); acdReplyInitS(thys, acdTmpOutFName, &acdReplyDef); } ajStrDel(&name); acdPromptSeqout(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajSeqoutNew(); /* set the default value */ ajSeqoutClearUsa(val, acdReply); /* resets the AjPSeqout */ val->Features = osfeat; acdGetValueAssoc(thys, "osdirectory", &val->Directory); acdGetValueAssoc(thys, "osdbname", &val->Setoutdb); acdOutDirectory(&val->Directory); acdLog("acdSetSeqout features: %B dir '%S'\n", val->Features, val->Directory); ajStrAssignEmptyS(&val->Formatstr, fmt); if(!ajStrGetLen(val->Formatstr)) ajSeqoutstrGetFormatDefault(&val->Formatstr); ajStrAssignEmptyS(&val->Extension, ext); ajStrAssignEmptyS(&val->Extension, val->Formatstr); acdGetValueAssoc(thys, "oufo", &val->Ufo); acdGetValueAssoc(thys, "offormat", &val->Ftquery->Formatstr); if(!ajStrGetLen(val->Ftquery->Formatstr)) ajFeatOutFormatDefault(&val->Ftquery->Formatstr); acdGetValueAssoc(thys, "ofname", &val->Ftquery->Filename); acdGetValueAssoc(thys, "ofdirectory", &val->Ftquery->Directory); acdOutDirectory(&val->Ftquery->Directory); acdQualToBool(thys, "ossingle", ajFalse, &val->Single, &acdTmpStr); if(ajStrGetLen(typestr)) ajStrAssignS(&val->Outputtype, typestr); else { if(!acdInTypeSeq(&val->Outputtype)) ajWarn("No output type specified for '%S'", thys->Name); } if(!ajSeqoutOpen(val)) { if(ajStrGetLen(val->Directory)) acdBadVal(thys, required, "Unable to write sequence to '%S%S'", val->Directory, acdReply); else acdBadVal(thys, required, "Unable to write sequence to '%S'", acdReply); ok = ajFalse; ajSeqoutDel(&val); } } else if(!nullok) { acdBadVal(thys, required, "Output USA is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); if (val) { acdLog("acdSetSeqout features: %B\n", val->Features); if(val->Features) acdLog("acdSetSeqout with features UFO '%S'\n", val->Ufo); } ajStrDel(&typestr); ajStrDel(&fmt); ajStrDel(&ext); return; } ID ajAcdGetSeqoutall TY public MO ajacd LB acd XX DE Returns an item of type Seqoutall as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPSeqout RD Sequence output object. The file was already RD opened by acdSetSeqoutall so this just returns the pointer. RX // AjPSeqout ajAcdGetSeqoutall(const char *token) { return acdGetValueRef(token, "seqoutall"); } ID acdSetSeqoutall TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD sequence output item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is "stdout". DE DE Associated qualifier "-osformat" DE is applied to the USA before opening the output file. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetSeqoutall(AcdPAcd thys) { AjPSeqout val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AjBool nullok; AjBool nulldefault; AjPStr name = NULL; AjPStr ext = NULL; AjPStr fmt = NULL; AjPStr typestr = NULL; AjBool osfeat; val = NULL; if(!acdGetValueAssoc(thys, "osname", &name)) acdAttrResolve(thys, "name", &name); if(!acdGetValueAssoc(thys, "osextension", &ext)) acdAttrResolve(thys, "extension", &ext); acdGetValueAssoc(thys, "osformat", &fmt); ajStrAssignEmptyS(&ext, fmt); if(!ajStrGetLen(ext)) ajSeqoutstrGetFormatDefault(&ext); acdAttrToBool(thys, "features", ajFalse, &osfeat); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); if(!acdAttrToStr(thys, "type", "", &typestr)) acdInTypeSeq(&typestr); required = acdIsRequired(thys); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ acdOutFilename(&acdReplyDef, name, ext); else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else { acdOutFilename(&acdTmpOutFName, name, ext); acdReplyInitS(thys, acdTmpOutFName, &acdReplyDef); } ajStrDel(&name); acdPromptSeqout(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajSeqoutNew(); /* set the default value */ ajSeqoutClearUsa(val, acdReply); val->Features = osfeat; acdGetValueAssoc(thys, "osdirectory", &val->Directory); acdGetValueAssoc(thys, "osdbname", &val->Setoutdb); acdOutDirectory(&val->Directory); acdLog("acdSetSeqoutall features: %B\n", val->Features); ajStrAssignEmptyS(&val->Formatstr, fmt); if(!ajStrGetLen(val->Formatstr)) ajSeqoutstrGetFormatDefault(&val->Formatstr); ajStrAssignEmptyS(&val->Extension, ext); ajStrAssignEmptyS(&val->Extension, val->Formatstr); acdGetValueAssoc(thys, "oufo", &val->Ufo); acdGetValueAssoc(thys, "offormat", &val->Ftquery->Formatstr); if(!ajStrGetLen(val->Ftquery->Formatstr)) ajFeatOutFormatDefault(&val->Ftquery->Formatstr); acdGetValueAssoc(thys, "ofname", &val->Ftquery->Filename); acdGetValueAssoc(thys, "ofdirectory", &val->Ftquery->Directory); acdOutDirectory(&val->Ftquery->Directory); acdLog("acdSetSeqoutall ossingle default: %B\n", ajSeqoutstrIsFormatSingle(val->Formatstr)); acdQualToBool(thys, "ossingle", ajSeqoutstrIsFormatSingle(val->Formatstr), &val->Single, &acdTmpStr); acdLog("acdSetSeqoutall ossingle value %B '%S'\n", val->Single, acdTmpStr); if(ajStrGetLen(typestr)) ajStrAssignS(&val->Outputtype, typestr); else { if(!acdInTypeSeq(&val->Outputtype)) ajWarn("No output type specified for '%S'", thys->Name); } if(!ajSeqoutOpen(val)) { if(ajStrGetLen(val->Directory)) acdBadVal(thys, required, "Unable to write sequence to '%S%S'", val->Directory, acdReply); else acdBadVal(thys, required, "Unable to write sequence to '%S'", acdReply); ok = ajFalse; ajSeqoutDel(&val); } } else if(!nullok) { acdBadVal(thys, required, "Output USA is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&typestr); ajStrDel(&fmt); ajStrDel(&ext); return; } ID ajAcdGetSeqoutset TY public MO ajacd LB acd XX DE Returns an item of type Seqoutset as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPSeqout RD Sequence output object. The file was already RD opened by acdSetSeqoutset so this just returns the pointer. RX // AjPSeqout ajAcdGetSeqoutset(const char *token) { return acdGetValueRef(token, "seqoutset"); } ID acdSetSeqoutset TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD sequence output item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is "stdout". DE DE Associated qualifier "-osformat" DE is applied to the USA before opening the output file. DE DE Associated qualifiers are defined but not yet implemented. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetSeqoutset(AcdPAcd thys) { AjPSeqout val = NULL; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool nullok; AjBool nulldefault; ajint itry; AjPStr name = NULL; AjPStr ext = NULL; AjPStr fmt = NULL; AjPStr typestr = NULL; AjBool osfeat; val = NULL; if(!acdGetValueAssoc(thys, "osname", &name)) acdAttrResolve(thys, "name", &name); if(!acdGetValueAssoc(thys, "osextension", &ext)) acdAttrResolve(thys, "extension", &ext); acdGetValueAssoc(thys, "osformat", &fmt); ajStrAssignEmptyS(&ext, fmt); if(!ajStrGetLen(ext)) ajSeqoutstrGetFormatDefault(&ext); acdAttrToBool(thys, "features", ajFalse, &osfeat); required = acdIsRequired(thys); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "nulldefault", ajFalse, &nulldefault); if(!acdAttrToStr(thys, "type", "", &typestr)) acdInTypeSeq(&typestr); if(nullok && nulldefault) { if (acdDefinedEmpty(thys)) /* user set to empty - make default name */ acdOutFilename(&acdReplyDef, name, ext); else /* leave empty */ acdReplyInitC(thys, "", &acdReplyDef); } else { acdOutFilename(&acdTmpOutFName, name, ext); acdReplyInitS(thys, acdTmpOutFName, &acdReplyDef); } ajStrDel(&name); acdPromptSeqout(thys); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajSeqoutNew(); /* set the default value */ ajSeqoutClearUsa(val, acdReply); acdGetValueAssoc(thys, "osdbname", &val->Setoutdb); val->Features = osfeat; acdGetValueAssoc(thys, "osdirectory", &val->Directory); acdOutDirectory(&val->Directory); acdLog("acdSetSeqoutset features: %B\n", val->Features); ajStrAssignEmptyS(&val->Formatstr, fmt); if(!ajStrGetLen(val->Formatstr)) ajSeqoutstrGetFormatDefault(&val->Formatstr); ajStrAssignEmptyS(&val->Extension, ext); ajStrAssignEmptyS(&val->Extension, val->Formatstr); acdGetValueAssoc(thys, "oufo", &val->Ufo); acdGetValueAssoc(thys, "offormat", &val->Ftquery->Formatstr); if(!ajStrGetLen(val->Ftquery->Formatstr)) ajFeatOutFormatDefault(&val->Ftquery->Formatstr); acdGetValueAssoc(thys, "ofname", &val->Ftquery->Filename); acdGetValueAssoc(thys, "ofdirectory", &val->Ftquery->Directory); acdOutDirectory(&val->Ftquery->Directory); acdQualToBool(thys, "ossingle", ajSeqoutstrIsFormatSingle(val->Formatstr), &val->Single, &acdTmpStr); if(ajStrGetLen(typestr)) ajStrAssignS(&val->Outputtype, typestr); else { if(!acdInTypeSeq(&val->Outputtype)) ajWarn("No output type specified for '%S'", thys->Name); } if(!ajSeqoutOpen(val)) { if(ajStrGetLen(val->Directory)) acdBadVal(thys, required, "Unable to write sequence to '%S%S'", val->Directory, acdReply); else acdBadVal(thys, required, "Unable to write sequence to '%S'", acdReply); ok = ajFalse; ajSeqoutDel(&val); } } else if(!nullok) { acdBadVal(thys, required, "Output USA is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&typestr); ajStrDel(&fmt); ajStrDel(&ext); return; } ID ajAcdGetSeqset TY public MO ajacd LB acd XX DE Returns an item of type Seqset as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPSeqset RD Sequence set object. The sequence was already loaded by RD acdSetSeqset so this just returns the pointer. RX // AjPSeqset ajAcdGetSeqset(const char *token) { return acdGetValueRef(token, "seqset"); } ID acdSetSeqset TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD sequence item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is a null string, which DE is invalid. DE DE Associated qualifiers "-sformat", "-sdbname", "-sopenfile", "-sid" DE are applied to the USA before reading the sequence. DE DE Associated qualifiers "-supper", "-slower" and "-sask" are applied DE after reading. DE DE Associated qualifiers "-sbegin", "-send" and "-sreverse" DE are applied as appropriate, with prompting for values, DE after the sequence has been read. They are applied to the sequence, DE and the resulting sequence is what is set in the ACD item. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetSeqset(AcdPAcd thys) { AjPSeqset val = NULL; AjPSeqin seqin; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool okbeg = ajFalse; AjBool okend = ajFalse; AjBool okrev = ajFalse; AjBool aligned = ajFalse; ajint itry; AjPStr infname = NULL; ajint sqbegin = 0; ajint sqend = 0; AjBool sreverse = ajFalse; AjBool sprompt = ajFalse; AjBool snuc = ajFalse; AjBool sprot = ajFalse; AjBool nullok = ajFalse; AjPStr typestr = NULL; val = ajSeqsetNew(); /* set the default value */ seqin = ajSeqinNew(); /* set the default value */ seqin->Input->Multi = ajTrue; acdQualToBool(thys, "snucleotide", ajFalse, &snuc, &acdReplyDef); acdQualToBool(thys, "sprotein", ajFalse, &sprot, &acdReplyDef); acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToBool(thys, "aligned", ajFalse, &aligned); acdAttrToStr(thys, "type", "", &typestr); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptSeq(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajSeqsetDel(&val); break; } ajSeqinUsa(&seqin, acdReply); if(ajStrGetLen(typestr)) { ajStrAssignS(&seqin->Inputtype, typestr); acdInTypeSeqSave(seqin->Inputtype); } else acdInTypeSeqSave(NULL); acdAttrToBool(thys, "features", ajFalse, &seqin->Features); acdGetValueAssoc(thys, "sformat", &seqin->Input->Formatstr); acdGetValueAssoc(thys, "sdbname", &seqin->Input->Db); /* acdGetValueAssoc(thys, "sopenfile", &seqin->Filename);*/ /* obsolete */ acdGetValueAssoc(thys, "sid", &seqin->Entryname); acdGetValueAssoc(thys, "iquery", &seqin->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &seqin->Input->Fpos, &acdTmpStr); acdGetValueAssoc(thys, "ufo", &seqin->Ufo); acdGetValueAssoc(thys, "fformat", &seqin->Ftquery->Formatstr); acdGetValueAssoc(thys, "fopenfile", &seqin->Ftquery->Filename); acdQualToBool(thys, "scircular", ajFalse, &seqin->Circular, &acdTmpStr); acdQualToBool(thys, "supper", ajFalse, &seqin->Upper, &acdTmpStr); acdQualToBool(thys, "slower", ajFalse, &seqin->Lower, &acdTmpStr); okbeg = acdQualToSeqbegin(thys, "sbegin", 0, &seqin->Begin, &acdTmpStr); okend = acdQualToSeqend(thys, "send", 0, &seqin->End, &acdTmpStr); okrev = acdQualToBool(thys, "sreverse", ajFalse, &sreverse, &acdTmpStr); if(snuc) ajSeqinSetNuc(seqin); if(sprot) ajSeqinSetProt(seqin); if(ajStrGetLen(seqin->Ufo)) seqin->Features = ajTrue; ok = ajSeqsetRead(val, seqin); if(!ok) acdBadVal(thys, required, "Unable to read sequence '%S'", acdReply); } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, ajSeqsetGetNameS(val), ajTrue); /* save sequence name */ acdQualToBool(thys, "sask", ajFalse, &sprompt, &acdReplyDef); if(val) { /* now process the begin, end and reverse options */ if(seqin->Begin) { okbeg = ajTrue; val->Begin = seqin->Begin; } for(itry=acdPromptTry; itry && !okbeg; itry--) { ajStrAssignC(&acdReplyPrompt, "start"); if(sprompt) acdUserGetPrompt(thys, "sbegin", " Begin at position", &acdReplyPrompt); if(ajStrMatchCaseC(acdReplyPrompt, "start")) ajStrAssignC(&acdReplyPrompt, "0"); okbeg = ajStrToInt(acdReplyPrompt, &sqbegin); if(!okbeg) acdBadVal(thys, sprompt, "Invalid integer value '%S'", acdReplyPrompt); } if(!okbeg) acdBadRetry(thys); if(sqbegin) { seqin->Begin = sqbegin; val->Begin = sqbegin; acdSetQualDefInt(thys, "sbegin", sqbegin); } if(seqin->End) { okend = ajTrue; val->End = seqin->End; } for(itry=acdPromptTry; itry && !okend; itry--) { ajStrAssignC(&acdReplyPrompt, "end"); if(sprompt) acdUserGetPrompt(thys, "send", " End at position", &acdReplyPrompt); if(ajStrMatchCaseC(acdReplyPrompt, "end")) ajStrAssignC(&acdReplyPrompt, "0"); okend = ajStrToInt(acdReplyPrompt, &sqend); if(!okend) acdBadVal(thys, sprompt, "Invalid integer value '%S'", acdReplyPrompt); } if(!okend) acdBadRetry(thys); if(sqend) { seqin->End = sqend; val->End = sqend; acdSetQualDefInt(thys, "send", sqend); } if(ajSeqsetGetSize(val) && ajSeqsetIsNuc(val)) { for(itry=acdPromptTry; itry && !okrev; itry--) { ajStrAssignC(&acdReplyPrompt, "N"); if(sprompt) acdUserGetPrompt(thys, "sreverse", " Reverse strand", &acdReplyPrompt); okrev = ajStrToBool(acdReplyPrompt, &sreverse); if(!okrev) acdBadVal(thys, sprompt, "Invalid Y/N value '%S'", acdReplyPrompt); } if(!okrev) acdBadRetry(thys); if(sreverse) { seqin->Rev = sreverse; val->Rev = sreverse; acdSetQualDefBool(thys, "sreverse", sreverse); } } acdLog("sbegin: %d, send: %d, sreverse: %B\n", sqbegin, sqend, sreverse); if(val && aligned) ajSeqsetFill(val); if(val && val->Rev) ajSeqsetReverse(val); /* sequences have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcSeqset); thys->SetAttr = &acdCalcSeqset[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[ACD_SEQ_BEGIN], ajSeqsetGetBegin(val)); ajStrFromInt(&thys->SetStr[ACD_SEQ_END], ajSeqsetGetEnd(val)); ajStrFromInt(&thys->SetStr[ACD_SEQ_LENGTH], ajSeqsetGetLen(val)); ajStrFromBool(&thys->SetStr[ACD_SEQ_PROTEIN], ajSeqsetIsProt(val)); ajStrFromBool(&thys->SetStr[ACD_SEQ_NUCLEIC], ajSeqsetIsNuc(val)); if(ajStrGetLen(val->Name)) ajStrAssignS(&thys->SetStr[ACD_SEQ_NAME], val->Name); else ajStrAssignS(&thys->SetStr[ACD_SEQ_NAME], ajSeqsetGetseqNameS(val,0)); ajStrAssignS(&thys->SetStr[ACD_SEQ_USA], ajSeqsetGetUsa(val)); ajStrFromFloat(&thys->SetStr[ACD_SEQ_WEIGHT], ajSeqsetGetTotweight(val), 3); ajStrFromInt(&thys->SetStr[ACD_SEQ_COUNT], ajSeqsetGetSize(val)); acdInFileSave(acdReply, ajSeqsetGetNameS(val), ajTrue); } else { /* sequences have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcSeqset); thys->SetAttr = &acdCalcSeq[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[ACD_SEQ_BEGIN], 0); ajStrFromInt(&thys->SetStr[ACD_SEQ_END], 0); ajStrFromInt(&thys->SetStr[ACD_SEQ_LENGTH], 0); ajStrFromBool(&thys->SetStr[ACD_SEQ_NUCLEIC], ajFalse); ajStrFromBool(&thys->SetStr[ACD_SEQ_PROTEIN], ajFalse); ajStrAssignC(&thys->SetStr[ACD_SEQ_NAME], ""); ajStrAssignC(&thys->SetStr[ACD_SEQ_USA], ""); ajStrFromFloat(&thys->SetStr[ACD_SEQ_WEIGHT], 0.0, 3); ajStrFromInt(&thys->SetStr[ACD_SEQ_COUNT], 0); } thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajSeqinDel(&seqin); ajStrDel(&typestr); return; } ID ajAcdGetSeqsetall TY public MO ajacd LB acd XX DE Returns an item of type Seqset array as defined in a named ACD item. DE The array is terminated by a NULL. DE DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPSeqset* RD Sequence setall object. RD The sequence was already loaded by RD acdSetSeqset so this just returns the pointer. RX // AjPSeqset* ajAcdGetSeqsetall(const char *token) { return acdGetValueRef(token, "seqsetall"); } ID ajAcdGetSeqsetallSingle TY public MO ajacd LB acd XX DE Returns an item of type Seqset as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPSeqset RD Sequence set object. The sequence was already loaded by RD acdSetSeqset so this just returns the pointer. RX // AjPSeqset ajAcdGetSeqsetallSingle(const char *token) { AjPSeqset *val; ajint i; val = acdGetValueSingle(token, "seqsetall"); for(i=0; val[i]; i++) continue; if(i > 1) ajWarn("Single list value %s, but can choose %d values", token, i); if(i < 1) ajWarn("Single list value %s, no value found: returning NULL value", token); return val[0]; } ID ajAcdGetString TY public MO ajacd LB acd XX DE Returns an item of type String as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPStr RD String object. The string was already set by RD acdSetString so this just returns the pointer. RX // AjPStr ajAcdGetString(const char *token) { return acdGetValueRef(token, "string"); } ID acdSetString TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD string item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is an empty string. DE DE Attributes for minimum and maximum length are applied with error DE messages if exceeded. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetString(AcdPAcd thys) { AjPStr val; AjBool required = ajFalse; AjBool ok = ajFalse; AjPStr pattern = NULL; AjBool upper; AjBool lower; AjBool word; ajint itry; AjPRegexp patexp = NULL; ajint minlen; ajint maxlen; ajint len; val = ajStrNew(); /* set the default value */ acdAttrToInt(thys, "minlength", 0, &minlen); acdAttrToInt(thys, "maxlength", INT_MAX, &maxlen); acdAttrToStr(thys, "pattern", "", &pattern); acdAttrToBool(thys, "upper", ajFalse, &upper); acdAttrToBool(thys, "lower", ajFalse, &lower); acdAttrToBool(thys, "word", ajFalse, &word); if(ajStrGetLen(pattern)) patexp = ajRegComp(pattern); required = acdIsRequired(thys); acdReplyInitC(thys, "", &acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); len = ajStrGetLen(acdReply); if(len < minlen) { acdBadVal(thys, required, "Too short (%S) - minimum length is %d characters", thys->Name, minlen); ok = ajFalse; } if(len > maxlen) { acdBadVal(thys, required, "Too long (%S) - maximum length is %d characters", thys->Name, maxlen); ok = ajFalse; } if(patexp && !ajRegExec(patexp, acdReply)) { acdBadVal(thys, required, "String does not match pattern '%S'", pattern); ok = ajFalse; } if(word && !(ajStrIsWord(acdReply))) { acdBadVal(thys, required, "String contains disallowed whitespace characters"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); if(patexp) ajRegFree(&patexp); if(upper) ajStrFmtUpper(&val); if(lower) ajStrFmtLower(&val); /* strings have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcString); thys->SetAttr = &acdCalcString[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); ajStrFromInt(&thys->SetStr[0], ajStrGetLen(acdReply)); ajStrAssignS(&val, acdReply); thys->Value = val; ajStrAssignS(&thys->ValStr, val); ajStrDel(&pattern); return; } ID ajAcdGetTaxon TY public MO ajacd LB acd XX DE Returns an item of type Taxon as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPTax RD Taxon object RX // AjPTax ajAcdGetTaxon(const char *token) { AjPTaxall val = acdGetValue(token, "taxon"); if(val->Multi) ajWarn("ajAcdGetTaxon request single taxon but maxreads > 1"); val->Returned = ajTrue; return val->Tax; } ID ajAcdGetTaxonall TY public MO ajacd LB acd XX DE Returns an input stream of an item of type Taxon as defined in a DE named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPTaxall RD Taxon input stream RX // AjPTaxall ajAcdGetTaxonall(const char *token) { AjPTaxall val = acdGetValueRef(token, "taxon"); if(!val->Multi) ajWarn("ajAcdGetTaxonall request taxon input stream but maxreads is 1"); return val; } ID acdSetTaxon TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD taxon input DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetTaxon(AcdPAcd thys) { AjPTaxall val; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool nullok = ajFalse; ajint itry; ajint maxreads; AjPStr infname = NULL; AjPStr tmp = NULL; val = ajTaxallNew(); /* set the default value */ acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToInt(thys, "maxreads", INT_MAX, &maxreads); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajTaxallDel(&val); break; } ajTaxinQryS(val->Taxin, acdReply); acdGetValueAssoc(thys, "iformat", &val->Taxin->Input->Formatstr); acdGetValueAssoc(thys, "iquery", &val->Taxin->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &val->Taxin->Input->Fpos, &acdTmpStr); ok = ajTaxinRead(val->Taxin, val->Tax); } if(!ok) acdBadRetry(thys); if(maxreads > 1) val->Multi = ajTrue; acdAttrToBool(thys, "entry", ajFalse, &val->Taxin->Input->Text); acdInFileSave(acdReply, ajTaxallGettaxId(val), ajTrue); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&tmp); return; } ID ajAcdGetText TY public MO ajacd LB acd XX DE Returns an item of type Text as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPText RD Text object RX // AjPText ajAcdGetText(const char *token) { AjPTextall val = acdGetValue(token, "text"); if(val->Multi) ajWarn("ajAcdGetText request single text entry but maxreads > 1"); val->Returned = ajTrue; return val->Text; } ID ajAcdGetTextall TY public MO ajacd LB acd XX DE Returns an input stream of an item of type Text as defined in a DE named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPTextall RD Text input stream RX // AjPTextall ajAcdGetTextall(const char *token) { AjPTextall val = acdGetValueRef(token, "text"); if(!val->Multi) ajWarn("ajAcdGetTextall request text input stream but maxreads is 1"); return val; } ID acdSetText TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD text input DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetText(AcdPAcd thys) { AjPTextall val; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool nullok = ajFalse; ajint itry; ajint maxreads; AjPStr infname = NULL; AjPStr tmp = NULL; val = ajTextallNew(); /* set the default value */ acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToInt(thys, "maxreads", INT_MAX, &maxreads); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajTextallDel(&val); break; } ajTextinQryS(val->Textin, acdReply); acdGetValueAssoc(thys, "iformat", &val->Textin->Formatstr); acdGetValueAssoc(thys, "iquery", &val->Textin->QryFields); acdQualToLong(thys, "ioffset", 0L, &val->Textin->Fpos, &acdTmpStr); ok = ajTextinRead(val->Textin, val->Text); } if(!ok) acdBadRetry(thys); if(maxreads > 1) val->Multi = ajTrue; acdAttrToBool(thys, "entry", ajFalse, &val->Textin->Text); /* acdInFileSave(acdReply, ajTextallGettextId(val), ajTrue); */ thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&tmp); return; } ID ajAcdGetToggle TY public MO ajacd LB acd XX DE Returns an item of type Toggle as defined in a named ACD item. Called by the DE application after all ACD values have been set, and simply returns DE what the ACD item already has. DE DE Toggle is an AjBool that is used to switch (toggle) other ACD types. XX PN [1] PA r token const char* PD Text token name PX RT AjBool RD Boolean value from ACD item RX // AjBool ajAcdGetToggle(const char *token) { AjBool* val; val = acdGetValue(token, "toggle"); return *val; } ID acdSetToggle TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD toggle item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other available) is "N" for ajFalse. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetToggle(AcdPAcd thys) { AjBool* val; AjBool required = ajFalse; AjBool ok = ajFalse; ajint itry; AJNEW0(val); /* create storage for the result */ *val = ajFalse; /* set the default value */ required = acdIsRequired(thys); acdReplyInitC(thys, "N", &acdReplyDef); acdLog("acdSetToggle -%S def: %S\n", thys->Name, acdReplyDef); for(itry=acdPromptTry; itry && !ok; itry--) { ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); ok = ajStrToBool(acdReply, val); if(!ok) acdBadVal(thys, required, "Invalid Y/N value '%S'", acdReply); } if(!ok) acdBadRetry(thys); thys->Value = val; ajFmtPrintS(&thys->ValStr, "%B", *val); acdLog("acdSetToggle -%S val: %B\n", thys->Name, *val); if(ajStrMatchC(thys->Name, "help")) acdHelp(); return; } ID ajAcdGetTree TY public MO ajacd LB acd XX DE Returns an item of type Tree as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPPhyloTree* RD Tree object. The data was already set by RD acdSetTree so this just returns the pointer. RX // AjPPhyloTree* ajAcdGetTree(const char *token) { return acdGetValueRef(token, "tree"); } ID ajAcdGetTreeSingle TY public MO ajacd LB acd XX DE Returns an item of type Tree as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPPhyloTree RD Tree object. The data was already set by RD acdSetTree so this just returns the pointer. RX // AjPPhyloTree ajAcdGetTreeSingle(const char *token) { AjPPhyloTree *val; ajint i; val = acdGetValueSingle(token, "tree"); for(i=0; val[i]; i++) continue; if(i > 1) ajWarn("Single list value %s, but can choose %d values", token, i); if(i < 1) ajWarn("Single list value %s, no value found: returning NULL value", token); return val[0]; } ID acdSetTree TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD tree file item. DE DE Understands all attributes and associated qualifiers for this item type. DE DE The default value (if no other is available) is an empty string. DE DE Attributes for number of trees (size) are applied with error DE messages if exceeded. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetTree(AcdPAcd thys) { AjPPhyloTree *val; AjBool required = ajFalse; AjBool ok = ajFalse; AjPStr infname = NULL; ajint itry; ajint i; ajint size; AjBool nullok = ajFalse; val = NULL; acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToInt(thys, "size", 0, &size); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); acdPromptTree(thys); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(ajStrGetLen(acdReply)) { val = ajPhyloTreeRead(acdReply, size); if(!val) { acdBadVal(thys, required, "Unable to read tree data from '%S'", acdReply); ok = ajFalse; } } else if(!nullok) { acdBadVal(thys, required, "Input file is required"); ok = ajFalse; } } if(!ok) acdBadRetry(thys); acdInFileSave(acdReply, NULL, ajTrue); /* trees have special set attributes */ thys->SAttr = acdAttrListCount(acdCalcTree); thys->SetAttr = &acdCalcTree[0]; thys->SetStr = AJCALLOC0(thys->SAttr, sizeof(AjPStr)); if(val) { for(i=0;val[i];i++) continue; ajStrFromInt(&thys->SetStr[0],i); /* number of trees */ ajStrFromInt(&thys->SetStr[1],val[0]->Size); ajStrFromBool(&thys->SetStr[2],val[0]->HasLengths); ajStrAssignS(&thys->ValStr, val[0]->Tree); } else { ajStrFromInt(&thys->SetStr[0],0); /* number of trees */ ajStrFromInt(&thys->SetStr[1],0); ajStrFromBool(&thys->SetStr[2],ajFalse); ajStrAssignClear(&thys->ValStr); } thys->Value = val; return; } ID ajAcdGetUrl TY public MO ajacd LB acd XX DE Returns an item of type URL as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPUrl RD URL object RX // AjPUrl ajAcdGetUrl(const char *token) { AjPUrlall val = acdGetValue(token, "url"); if(val->Multi) ajWarn("ajAcdGetUrl request single URL but maxreads > 1"); val->Returned = ajTrue; return val->Url; } ID ajAcdGetUrlall TY public MO ajacd LB acd XX DE Returns an input stream of an item of type Taxon as defined in a DE named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPUrlall RD URL input stream RX // AjPUrlall ajAcdGetUrlall(const char *token) { AjPUrlall val = acdGetValueRef(token, "url"); if(!val->Multi) ajWarn("ajAcdGetUrlall request url input stream but maxreads is 1"); return val; } ID acdSetUrl TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD URL input DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetUrl(AcdPAcd thys) { AjPUrlall val; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool nullok = ajFalse; ajint itry; ajint maxreads; AjPStr infname = NULL; val = ajUrlallNew(); /* set the default value */ acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdAttrToInt(thys, "maxreads", INT_MAX, &maxreads); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajUrlallDel(&val); break; } ajUrlinQryS(val->Urlin, acdReply); /* these are reset by ajUrlinQryS - can safely set now */ acdQualToBool(thys, "swiss", ajFalse, &val->Urlin->IsSwiss, &acdTmpStr); acdQualToBool(thys, "embl", ajFalse, &val->Urlin->IsEmbl, &acdTmpStr); acdGetValueAssoc(thys, "accession", &val->Urlin->Accession); acdGetValueAssoc(thys, "identifier", &val->Urlin->IdTypes); acdGetValueAssoc(thys, "iformat", &val->Urlin->Input->Formatstr); ok = ajUrlinRead(val->Urlin, val->Url); } if(!ok) acdBadRetry(thys); if(maxreads > 1) val->Multi = ajTrue; acdAttrToBool(thys, "entry", ajFalse, &val->Urlin->Input->Text); acdInFileSave(acdReply, ajUrlallGeturlId(val), ajTrue); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); return; } ID ajAcdGetValue TY public MO ajacd LB acd XX DE Returns the string value of any ACD item XX PN [1] PA r token const char* PD Text token name PX RT const AjPStr RD String object. The string was already set by RD acdSetString so this just returns the pointer. RX // const AjPStr ajAcdGetValue(const char *token) { return acdGetValStr(token); } ID ajAcdGetValueDefault TY public MO ajacd LB acd XX DE Returns the default value of any ACD item XX PN [1] PA r token const char* PD Text token name PX RT const AjPStr RD Default value. RX // const AjPStr ajAcdGetValueDefault(const char *token) { return acdGetValDefault(token); } ID ajAcdGetVariation TY public MO ajacd LB acd XX DE Returns an item of type variation loader as defined in a named ACD item. DE Called by the application after all ACD values have been set, DE and simply returns what the ACD item already has. XX PN [1] PA r token const char* PD Text token name PX RT AjPVarload RD Variation loader object RX // AjPVarload ajAcdGetVariation(const char *token) { AjPVarload val = acdGetValueRef(token, "variation"); /*if(val->Multi) ajWarn("ajAcdGetVariation request single variation but maxreads > 1"); */ val->Returned = ajTrue; return val; } ID acdSetVariation TY static MO ajacd LB acd XX DE Using the definition in the ACD file, and any values for the DE item or its associated qualifiers provided on the command line, DE prompts the user if necessary (and possible) and DE sets the actual value for an ACD variation input DE DE Understands all attributes and associated qualifiers for this item type. XX PN [1] PA u thys AcdPAcd PD ACD item. PX RT void RD RX // static void acdSetVariation(AcdPAcd thys) { AjPVarload val; AjBool required = ajFalse; AjBool ok = ajFalse; AjBool nullok = ajFalse; ajint itry; AjPStr infname = NULL; AjPStr tmp = NULL; val = ajVarloadNew(); /* set the default value */ acdAttrToBool(thys, "nullok", ajFalse, &nullok); acdInFilename(&infname); required = acdIsRequired(thys); acdReplyInitS(thys, infname, &acdReplyDef); ajStrDel(&infname); for(itry=acdPromptTry; itry && !ok; itry--) { ok = ajTrue; /* accept the default if nothing changes */ ajStrAssignS(&acdReply, acdReplyDef); if(required) acdUserGet(thys, &acdReply); if(!ajStrGetLen(acdReply) && nullok) { ajVarloadDel(&val); break; } ajVarinQryS(val->Varin, acdReply); acdGetValueAssoc(thys, "iformat", &val->Varin->Input->Formatstr); acdGetValueAssoc(thys, "iquery", &val->Varin->Input->QryFields); acdQualToLong(thys, "ioffset", 0L, &val->Varin->Input->Fpos, &acdTmpStr); ok = ajVarinLoad(val->Varin, val->Var); } if(!ok) acdBadRetry(thys); acdAttrToBool(thys, "entry", ajFalse, &val->Varin->Input->Text); acdInFileSave(acdReply, ajVarloadGetvarId(val), ajTrue); thys->Value = val; ajStrAssignS(&thys->ValStr, acdReply); ajStrDel(&tmp); return; } ID ajAcdGetpathC TY public MO ajacd LB acd XX DE Returns the full path of an application defined in an external: attribute DE within the application definition. If the application was defined by an DE EMBOSS_appname environment variable this will use the substitution made DE when the external attribute was validated. XX PN [1] PA r token const char* PD External application name PX RT const AjPStr RD Executable application name RX // const AjPStr ajAcdGetpathC(const char* token) { const AjPStr value; /* not static - copy of a table text */ AjPStr tmpname = NULL; ajStrAssignC(&tmpname, token); value = ajAcdGetpathS(tmpname); ajStrDel(&tmpname); return value; } ID ajAcdGetpathS TY public MO ajacd LB acd XX DE Returns the full path of an application defined in an external: attribute DE within the application definition. If the application was defined by an DE EMBOSS_appname environment variable this will use the substitution made DE when the external attribute was validated. XX PN [1] PA r strtoken const AjPStr PD External application name PX RT const AjPStr RD Executable application name RX // const AjPStr ajAcdGetpathS(const AjPStr strtoken) { const AjPStr value; value = ajTableFetchS(acdExternalTable, strtoken); if(!value) ajWarn("Cannot find '%S', no ACD external definition found", strtoken); return value; } ID ajAcdIsUserdefinedC TY public MO ajacd LB acd XX DE Tests whether an ACD item has a value set by the user. XX PN [1] PA r token const char* PD Text token name PX RT AjBool RD True if token is not found as an ACD object name RX // AjBool ajAcdIsUserdefinedC(const char *token) { AcdPAcd acd; ajint pnum = 0; /* need to get from end of token */ AjPStr tmpstr = NULL; tmpstr = ajStrNewC(token); acdTokenToLowerS(&tmpstr, &pnum); acd = acdFindAcd(tmpstr, tmpstr); if(!acd) { ajErr("Qualifier '-%s' not found", token); return ajFalse; } ajStrDel(&tmpstr); return acd->UserDefined; } ID ajAcdIsUserdefinedS TY public MO ajacd LB acd XX DE Tests whether an ACD item has a value set by the user. XX PN [1] PA r strtoken const AjPStr PD Text token name PX RT AjBool RD True if token is not found as an ACD object name RX // AjBool ajAcdIsUserdefinedS(const AjPStr strtoken) { AcdPAcd acd; ajint pnum = 0; /* need to get from end of token */ AjPStr tmpstr = NULL; tmpstr = ajStrNewS(strtoken); acdTokenToLowerS(&tmpstr, &pnum); acd = acdFindAcd(tmpstr, tmpstr); if(!acd) { ajErr("Qualifier '-%S' not found", strtoken); return ajFalse; } ajStrDel(&tmpstr); return acd->UserDefined; } ID acdAttrCount TY static MO ajacd LB acd XX DE Simply counts all attributes for a numbered ACD type, XX PN [1] PA r itype ajint PD Numbered type as returned by acdFindType PX RT ajint RD number of attributes defined. RX // static ajint acdAttrCount(ajint itype) { AcdPAttr attr = acdType[itype].Attr; return acdAttrListCount(attr); } ID acdAttrKeyCount TY static MO ajacd LB acd XX DE Simply counts all attributes for a numbered ACD keyword, XX PN [1] PA r ikey ajint PD Numbered type as returned by acdFindKey PX RT ajint RD number of attributes defined. RX // static ajint acdAttrKeyCount(ajint ikey) { AcdPAttr attr = acdKeywords[ikey].Attr; return acdAttrListCount(attr); } ID acdAttrListCount TY static MO ajacd LB acd XX DE Simply counts all attributes for an attribute list. XX PN [1] PA r attr const AcdPAttr PD Attribute list PX RT ajint RD number of attributes defined. RX // static ajint acdAttrListCount(const AcdPAttr attr) { static ajint i; i = 0; while(attr[i].Name) i++; return i; } ID acdGetValue TY static MO ajacd LB acd XX DE Picks up a token by name and tests the type. DE The value is returned as type "void*", to be cast by the calling DE routine which is supposed to know what to expect. For example, only DE ajAcdGetInt should call with a type of "integer". XX PN [1] PA r token const char* PD Token name, optionally including a PD numeric suffix. PX PN [2] PA r type const char* PD Type. PX RT void* RD Value. RX // static void* acdGetValue(const char *token, const char* type) { void *ret; ajint pnum = 0; /* need to get from end of token */ AjPStr tmpstr = NULL; tmpstr = ajStrNewC(token); acdLog("acdGetValue '%s' (%s)\n", token, type); acdTokenToLowerS(&tmpstr, &pnum); ret = acdGetValueNumS(tmpstr, type, pnum, REF_NONE); acdLog("acdGetValue '%s' result %x\n", token, ret); ajStrDel(&tmpstr); return ret; } ID acdGetValueRef TY static MO ajacd LB acd XX DE Picks up a token by name and tests the type. DE The value is returned as type "void*", to be cast by the calling DE routine which is supposed to know what to expect. For example, only DE ajAcdGetInt should call with a type of "integer". DE DE The value reference is passed up to the original caller who now DE 'owns' the pointer. Some values will remain owned by ACD. Examples DE include integers and booleans, but also lists of options where only DE the first option is returned. XX PN [1] PA r token const char* PD Token name, optionally including a PD numeric suffix. PX PN [2] PA r type const char* PD Type. PX RT void* RD Value. RX // static void* acdGetValueRef(const char *token, const char* type) { void *ret; ajint pnum = 0; /* need to get from end of token */ AjPStr tmpstr = NULL; tmpstr = ajStrNewC(token); acdLog("acdGetValue '%s' (%s)\n", token, type); acdTokenToLowerS(&tmpstr, &pnum); ret = acdGetValueNumS(tmpstr, type, pnum, REF_ALL); acdLog("acdGetValueRef '%s' result %x\n", token, ret); ajStrDel(&tmpstr); return ret; } ID acdGetValueSingle TY static MO ajacd LB acd XX DE Picks up a token by name and tests the type. DE The value is returned as type "void*", to be cast by the calling DE routine which is supposed to know what to expect. For example, only DE ajAcdGetInt should call with a type of "integer". DE DE The value reference is passed up to the original caller who now DE 'owns' the pointer to the first data value, but the value array will DE need to be freed on exit. XX PN [1] PA r token const char* PD Token name, optionally including a PD numeric suffix. PX PN [2] PA r type const char* PD Type. PX RT void* RD Value. RX // static void* acdGetValueSingle(const char *token, const char* type) { void *ret; ajint pnum = 0; /* need to get from end of token */ AjPStr tmpstr = NULL; tmpstr = ajStrNewC(token); acdLog("acdGetValue '%s' (%s)\n", token, type); acdTokenToLowerS(&tmpstr, &pnum); ret = acdGetValueNumS(tmpstr, type, pnum, REF_SINGLE); acdLog("acdGetValueSingle '%s' result %x\n", token, ret); ajStrDel(&tmpstr); return ret; } ID acdGetValStr TY static MO ajacd LB acd XX DE Picks up a token by name and tests the type. DE The string value is returned for any data type. XX PN [1] PA r token const char* PD Token name, optionally including PD a numeric suffix. PX RT const AjPStr RD String. RX // static const AjPStr acdGetValStr(const char *token) { AcdPAcd acd; ajint pnum = 0; /* need to get from end of token */ AjPStr tmpstr = NULL; tmpstr = ajStrNewC(token); acdLog("acdGetValStr '%s' (%s)\n", token); acdTokenToLowerS(&tmpstr, &pnum); acd = acdFindAcd(tmpstr, tmpstr); ajStrDel(&tmpstr); if(!acd) return NULL; return acd->ValStr; } ID acdGetValDefault TY static MO ajacd LB acd XX DE Picks up a token by name and tests the type. DE The default string value is returned for any data type. XX PN [1] PA r token const char* PD Token name, optionally including PD a numeric suffix. PX RT const AjPStr RD String. RX // static const AjPStr acdGetValDefault(const char *token) { AcdPAcd acd; ajint pnum = 0; /* need to get from end of token */ AjPStr tmpstr = NULL; tmpstr = ajStrNewC(token); acdLog("acdGetValStr '%s' (%s)\n", token); acdTokenToLowerS(&tmpstr, &pnum); acd = acdFindAcd(tmpstr, tmpstr); ajStrDel(&tmpstr); if(!acd) return NULL; return acd->DefStr[DEF_DEFAULT]; } ID acdGetValueAssoc TY static MO ajacd LB acd XX DE Picks up the value for an associated qualifier as a string. XX PN [1] PA r thys const AcdPAcd PD ACD item for the master parameter/qualifier PX PN [2] PA r token const char* PD Token name, optionally including a PD numeric suffix. PX PN [3] PA w result AjPStr* PD String for the resulting value. PX RT AjBool RD ajTrue if found. RX // static AjBool acdGetValueAssoc(const AcdPAcd thys, const char *token, AjPStr *result) { ajint pnum = 0; /* need to get from end of token */ AcdPAcd pa; AjPStr tmpstr = NULL; tmpstr = ajStrNewC(token); acdLog("acdGetValueAssocDefault '%s' (%S)\n", token, thys->Name); acdTokenToLowerS(&tmpstr, &pnum); ajStrDel(&tmpstr); if(pnum) acdErrorAcd(thys, "associated token '%s' is numbered - not allowed\n", token); for(pa=thys->AssocQuals; pa && pa->Assoc; pa=pa->Next) if(ajStrMatchC(pa->Token, token)) { ajStrAssignS(result, pa->ValStr); return pa->Defined; } acdErrorAcd(thys, "Token '%s' not found\n", token); return ajFalse; } ID acdGetValueAssocDefault TY static MO ajacd LB acd XX DE Picks up the default value for an associated qualifier as a string. XX PN [1] PA r thys const AcdPAcd PD ACD item for the master parameter/qualifier PX PN [2] PA r token const char* PD Token name, optionally including a PD numeric suffix. PX PN [3] PA w result AjPStr* PD String for the resulting value. PX RT AjBool RD ajTrue if found. RX // static AjBool acdGetValueAssocDefault(const AcdPAcd thys, const char *token, AjPStr *result) { ajint pnum = 0; /* need to get from end of token */ AcdPAcd pa; AjPStr tmpstr = NULL; tmpstr = ajStrNewC(token); acdLog("acdGetValueAssocDefault '%s' (%S)\n", token, thys->Name); acdTokenToLowerS(&tmpstr, &pnum); ajStrDel(&tmpstr); if(pnum) acdErrorAcd(thys, "associated token '%s' is numbered - not allowed\n", token); for(pa=thys->AssocQuals; pa && pa->Assoc; pa=pa->Next) if(ajStrMatchC(pa->Token, token)) { ajStrAssignS(result, pa->DefStr[DEF_DEFAULT]); return pa->Defined; } acdErrorAcd(thys, "Token '%s' not found\n", token); return ajFalse; } ID acdGetValueNumC TY static MO ajacd LB acd XX DE Picks up the value by name, type and number. XX PN [1] PA r token const char* PD Token name PX PN [2] PA r type const char* PD ACD type PX PN [3] PA r pnum ajint PD parameter number, or 0 for a general qualifier. PX PN [4] PA r reftype ajint PD Reference passing type. PD REF_NONE means ACD still owns the value, PD REF_ALL means the caller owns the value, PD REF_SINGLE means the caller owns one value, PD but ACD owns the array PX RT void* RD Value of unknown type. RX // static void* acdGetValueNumC(const char *token, const char* type, ajint pnum, ajint reftype) { AcdPAcd pa; AcdPAcd ret = NULL; ajint itype = 0; ajint ifound = 0; AjPStr ambigList = NULL; ambigList = ajStrNew(); if(type) itype = acdFindTypeC(type); for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) continue; if(ajStrMatchC(pa->Token, token)) { acdLog("Found pa->Token '%S' pa->Type %d itype: %d\n", pa->Token, pa->Type, itype); if(pa->Level != ACD_QUAL && pa->Level != ACD_PARAM ) ajDie("Unknown qualifier '-%S'", pa->Token); if((itype>=0) && (pa->Type != itype)) /* program source error */ ajDie("Value for '-%S' is not of type %s (found type %s)", pa->Token, type, acdType[pa->Type].Name); if(pa->PNum == pnum) { acdLog("found %S [%d] '%S'\n", pa->Name, pa->PNum, pa->ValStr); if(pa->Used & USED_GET) ajWarn("Value for '-%S' used more than once", pa->Token); pa->Used |= USED_GET; ajStrDel(&ambigList); pa->RefPassed = reftype; return pa->Value; } else if(!pnum) /* matches any if no number, so count them */ { ifound++; ret = pa; acdAmbigApp(&ambigList, pa->Token); } } } if(ifound > 1) { ajWarn("Ambiguous qualifier '-%s' (%S)", token, ambigList); ajStrDel(&ambigList); } if(ifound == 1) { acdLog("found %S [%d] '%S'\n", ret->Name, ret->PNum, ret->ValStr); ret->Used |= USED_GET; if (acdDoValid) acdWarn("Abbreviated qualifier '%S' (%S)", token, ambigList); ajStrDel(&ambigList); ret->RefPassed = reftype; return ret->Value; } ajStrDel(&ambigList); /* program source error */ ajDie("Qualifier '-%s' not found", token); return NULL; } ID acdGetValueNumS TY static MO ajacd LB acd XX DE Picks up the value by name, type and number. XX PN [1] PA r token const AjPStr PD Token name PX PN [2] PA r type const char* PD ACD type PX PN [3] PA r pnum ajint PD parameter number, or 0 for a general qualifier. PX PN [4] PA r reftype ajint PD Reference passing type. PD REF_NONE means ACD still owns the value, PD REF_ALL means the caller owns the value, PD REF_SINGLE means the caller owns one value, PD but ACD owns the array PX RT void* RD Value of unknown type. RX // static void* acdGetValueNumS(const AjPStr token, const char* type, ajint pnum, ajint reftype) { return acdGetValueNumC(ajStrGetPtr(token), type, pnum, reftype); } ID acdHelp TY static MO ajacd LB acd XX DE Reports on program options if acdDoHelp is set, either by DE -help on the command line or by the prefix_HELP variable.. XX RT void RD RX // static void acdHelp(void) { AcdPAcd pa; static AjPStr helpReq = NULL; static AjPStr helpOpt = NULL; static AjPStr helpAdv = NULL; static AjPStr helpAss = NULL; static AjPStr helpGen = NULL; static AjPStr helpStr = NULL; enum { HELP_UNK, HELP_APP, HELP_REQ, HELP_OPT, HELP_ADV, HELP_ASS, HELP_GEN } helpType; AjPStr* def; AjBool tmpBool; char hlpFlag; ajint iattr = 0; ajlong igrp = 0; AjPStr groupname = NULL; AjPStr tmpstr = NULL; AjPStr applversion = NULL; AjBool flagReq = ajFalse; AjBool flagOpt = ajFalse; AjPList reqlist = NULL; AjPList optlist = NULL; AjPList advlist = NULL; AjPList genlist = NULL; AjPList asslist = NULL; AjPList inlist = NULL; AjPList outlist = NULL; acdLog("acdHelp %B\n", acdDoHelp); if(!acdDoHelp) return; if(acdDoTable) { reqlist = ajListNew(); optlist = ajListNew(); advlist = ajListNew(); genlist = ajListNew(); if(acdVerbose) asslist = ajListNew(); ajUserDumpC(""); /* was #f5f5ff */ } if(acdDoXsd || acdDoGalaxy) { inlist = ajListNew(); outlist = ajListNew(); } acdLog("++ acdHelp\n"); for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) continue; hlpFlag = ' '; acdLog("++ Name %S Level %d Assoc %B AssocQuals %x\n", pa->Name, pa->Level, pa->Assoc, pa->AssocQuals); helpType = HELP_ADV; if(pa->Level == ACD_APPL) helpType = HELP_APP; else if(!acdIsQtype(pa)) continue; def = pa->DefStr; if(def && ajStrGetLen(def[DEF_ADDITIONAL])) { if(acdHelpVarResolve(&helpStr, def[DEF_ADDITIONAL])) { if(!ajStrToBool(helpStr, &tmpBool)) acdErrorAcd(pa, "Bad additional flag %S\n", def[DEF_ADDITIONAL]); } else { tmpBool = ajTrue; hlpFlag = '*'; flagOpt = ajTrue; } if(tmpBool) helpType = HELP_OPT; } if(def && ajStrGetLen(def[DEF_STANDARD])) { if(acdHelpVarResolve(&helpStr, def[DEF_STANDARD])) { if(!ajStrToBool( helpStr, &tmpBool)) acdErrorAcd(pa, "Bad standard flag %S\n", def[DEF_STANDARD]); } else { tmpBool = ajTrue; hlpFlag = '*'; flagReq = ajTrue; } if(tmpBool) helpType = HELP_REQ; } if(pa->Assoc) helpType = HELP_ASS; acdLog("++ helpType %d\n", helpType); switch(helpType) { case HELP_APP: /* application, do nothing */ iattr = acdFindAttrC(acdAttrAppl, "groups"); ajStrAssignS(&groupname, pa->AttrStr[iattr]); iattr = acdFindAttrC(acdAttrAppl, "documentation"); ajStrAssignS(&acdAppldoc, pa->AttrStr[iattr]); igrp = ajStrFindAnyC(groupname, ",|"); if(igrp != -1) ajStrTruncateLen(&groupname, (size_t) igrp); ajStrFmtLower(&groupname); ajStrExchangeKK(&groupname, ':', '_'); ajStrExchangeKK(&groupname, ' ', '_'); if(acdDoGalaxy) { ajUser("\n" " %S\n" " " "" "emboss\n" " embossversion -auto" "", acdProgram, acdProgram, ajNamValueVersion(), acdAppldoc, ajNamValueVersion()); } if(acdDoXsd) { ajUserDumpC(""); ajUser(""); } break; case HELP_REQ: acdHelpAppend(pa, &helpReq, hlpFlag); acdHelpTable(pa, reqlist); break; case HELP_OPT: acdHelpAppend(pa, &helpOpt, hlpFlag); acdHelpTable(pa, optlist); break; case HELP_ADV: acdHelpAppend(pa, &helpAdv, hlpFlag); acdHelpTable(pa, advlist); break; case HELP_ASS: /* associated - process after the master */ break; case HELP_GEN: /* associated - process after the app */ break; default: acdErrorAcd(pa, "unknown qualifier type %d in acdHelp", helpType); } if(acdType[pa->Type].Section == acdSecOutput) { if(acdDoXsd) acdHelpXsd(pa, outlist); else if(acdDoGalaxy) acdHelpGalaxy(pa, outlist); } else if(pa->Level != ACD_APPL) { if(acdDoXsd) acdHelpXsd(pa, inlist); else if(acdDoGalaxy) acdHelpGalaxy(pa, inlist); } if(pa->AssocQuals) { if(helpType == HELP_APP) { if(acdVerbose) acdHelpAssoc(pa, &helpGen, NULL); else acdHelpAssoc(pa, &helpGen, "help"); acdHelpAssocTable(pa, genlist); } else { if(acdVerbose) { acdHelpAssoc(pa, &helpAss, NULL); acdHelpAssocTable(pa, asslist); } } } } if(!acdDoXsd && !acdDoGalaxy) { /* ** report 1-line documentation ** report version (s) */ acdAttrResolve(acdApplAcd, "documentation", &tmpstr); if(!acdAuto && !acdDoTable && ajStrGetLen(tmpstr)) { ajStrFmtWrap(&tmpstr, 75); ajUserDumpS(tmpstr); } ajFmtPrintS(&tmpstr, "EMBOSS:%s", VERSION); if(ajStrGetLen(tmpstr) < 14) ajStrAppendC(&tmpstr, ".0"); if(ajStrGetLen(acdPackVersion)) ajFmtPrintAppS(&tmpstr, " %S:%S", acdPackName, acdPackVersion); acdAttrResolve(acdApplAcd, "versionnumber", &applversion); if(ajStrGetLen(applversion)) { ajFmtPrintAppS(&tmpstr, " %S:%S", acdApplAcd->Name, applversion); ajStrDel(&applversion); } if(!acdDoTable) ajUser("Version: %S\n", tmpstr); ajStrDel(&tmpstr); ajStrDel(&applversion); if(flagReq) acdHelpShow(helpReq, "Standard (Mandatory) qualifiers " "(* if not always prompted)"); else acdHelpShow(helpReq, "Standard (Mandatory) qualifiers"); acdHelpTableShow(NULL, ""); acdHelpTableShow(reqlist, "Standard (Mandatory) qualifiers"); if(flagOpt) acdHelpShow(helpOpt, "Additional (Optional) qualifiers " "(* if not always prompted)"); else acdHelpShow(helpOpt, "Additional (Optional) qualifiers"); acdHelpTableShow(optlist, "Additional (Optional) qualifiers"); acdHelpShow(helpAdv, "Advanced (Unprompted) qualifiers"); acdHelpTableShow(advlist, "Advanced (Unprompted) qualifiers"); if(acdVerbose) acdHelpShow (helpAss, "Associated qualifiers"); acdHelpShow(helpGen, "General qualifiers"); if(acdVerbose && acdDoTable) acdHelpTableShow(asslist, "Associated qualifiers"); if(acdVerbose && acdDoTable) acdHelpTableShow(genlist, "General qualifiers"); } if(acdDoXsd) { acdHelpXsdShow(inlist, outlist); ajUserDumpC(""); } else if(acdDoGalaxy) { acdHelpGalaxyShow(inlist, outlist); ajUserDumpC(""); } else if(acdDoTable) ajUserDumpC("
"); ajExit(); return; } ID acdHelpAssoc TY static MO ajacd LB acd XX DE Processes all associated qualifiers for a qualifier or for the application. DE DE If a qualifier name is given (e.g. "help") then only that qualifier DE is processed. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA u str AjPStr* PD Help text being built PX PN [3] PA r name const char* PD Single name to process PX RT void RD RX // static void acdHelpAssoc(const AcdPAcd thys, AjPStr *str, const char* name) { static AjPStr line = NULL; static AjPStr qname = NULL; static AjPStr qtype = NULL; static AjPStr text = NULL; AcdPQual quals; ajint i; acdLog("++ acdHelpAssoc %S\n", thys->Name); if(thys->Level == ACD_APPL) quals = acdQualAppl; else { ajFmtPrintS(&line, "\n \"-%S\" associated qualifiers\n", thys->Name); ajStrAppendS(str, line); quals = acdType[thys->Type].Quals; } acdLog("++ type %d quals %x\n", thys->Type, quals); if(quals) { for(i=0; quals[i].Name; i++) { acdLog("++ quals[%d].Name %s\n", i, quals[i].Name); if(name && strcmp(name, quals[i].Name)) continue; if(thys->PNum) ajFmtPrintS(&qname, " -%s%d", quals[i].Name, thys->PNum); else ajFmtPrintS(&qname, " -%s", quals[i].Name); ajStrAssignC(&qtype, quals[i].Type); ajFmtPrintS(&line, " %-20S %-10S ", qname, qtype); ajStrAssignC(&text, quals[i].Help); acdTextFormat(&text); ajStrFmtWrapLeft(&text, 45, 34, 0); ajStrCutStart(&text, 34); ajStrAppendS(&line, text); ajStrAppendC(&line, "\n"); ajStrAppendS(str, line); } } return; } ID acdHelpAppend TY static MO ajacd LB acd XX DE Appends a qualifier and its help text to a help category string. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA u str AjPStr* PD Help text being built PX PN [3] PA r flag char PD Flag character. Usually blank, but an asterisk PD is used if the status (optional/required) is PD uncertain. PX RT void RD RX // static void acdHelpAppend(const AcdPAcd thys, AjPStr *str, char flag) { static AjPStr name = NULL; static AjPStr valstr = NULL; static AjPStr nostr = NULL; static AjPStr nullstr = NULL; static AjPStr text = NULL; static AjPStr line = NULL; static AjPStr type = NULL; AjBool boolval; AjPStr defstr; nullstr = ajStrNew(); if(ajCharMatchC("list", acdType[thys->Type].Name)) ajStrAssignC(&type, "menu"); else ajStrAssignC(&type, acdType[thys->Type].Name); if(thys->DefStr) defstr = thys->OrigStr; else defstr = nullstr; ajStrAssignClear(&nostr); if(acdIsQtype(thys) && (ajCharMatchC("boolean", acdType[thys->Type].Name) || ajCharMatchC("toggle", acdType[thys->Type].Name) )) { if(ajStrToBool(defstr, &boolval) && boolval) ajStrAssignC(&nostr, "[no]"); defstr = nullstr; } ajStrAssignS(&valstr, defstr); /* ** warning - don't try acdVarResolve here because we have not yet ** read in the data and things like calculated attributes do not exist */ if(thys->Level == ACD_PARAM) ajFmtPrintS(&name, "[-%S%S]", nostr, thys->Name); else ajFmtPrintS(&name, " -%S%S", nostr, thys->Name); ajFmtPrintS(&line, "%c %-20S %-10S ", flag, name, type); if(!acdDoTable) { acdHelpExpect(thys, ajFalse, &text); if(ajStrGetLen(text)) { ajStrInsertC(&text, 0, "["); ajStrAppendC(&text, "] "); } } acdHelpText(thys, &text); if(ajStrGetLen(text)) ajStrAppendC(&text, " "); if(!acdDoTable) acdHelpValid(thys, ajFalse, &text); ajStrRemoveWhiteSpaces(&text); acdTextFormat(&text); ajStrFmtWrapLeft(&text, 45, 34, 0); ajStrCutStart(&text, 34); ajStrAppendS(&line, text); ajStrAppendC(&line, "\n"); ajStrAppendS(str, line); ajStrDel(&name); ajStrDel(&valstr); ajStrDel(&nostr); ajStrDel(&nullstr); ajStrDel(&text); ajStrDel(&line); ajStrDel(&type); return; } ID acdHelpValidAlign TY static MO ajacd LB acd XX DE Generates valid description for a alignment output XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidAlign(const AcdPAcd thys, AjBool table, AjPStr* str) { AjPStr fmt = NULL; if(!thys) return; acdGetValueAssocDefault(thys, "aformat", &fmt); if(ajStrGetLen(fmt)) ajFmtPrintAppS(str, "(default -aformat %S)", fmt); else if(table) ajStrAssignClear(str); ajStrDel(&fmt); return; } ID acdHelpValidReport TY static MO ajacd LB acd XX DE Generates valid description for a report output XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidReport(const AcdPAcd thys, AjBool table, AjPStr* str) { AjPStr fmt = NULL; if(!thys) return; acdGetValueAssocDefault(thys, "rformat", &fmt); if(ajStrGetLen(fmt)) ajFmtPrintAppS(str, "(default -rformat %S)", fmt); else if(table) ajStrAssignClear(str); ajStrDel(&fmt); return; } ID acdHelpValidSeq TY static MO ajacd LB acd XX DE Generates valid description for an input sequence type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidSeq(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidSeqout TY static MO ajacd LB acd XX DE Generates valid description for an output sequence type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidSeqout(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidOut TY static MO ajacd LB acd XX DE Generates valid description for an outfile type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidOut(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidIn TY static MO ajacd LB acd XX DE Generates valid description for an infile type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidIn(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidData TY static MO ajacd LB acd XX DE Generates valid description for a datafile type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidData(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidInt TY static MO ajacd LB acd XX DE Generates valid description for an integer type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidInt(const AcdPAcd thys, AjBool table, AjPStr* str) { ajint imin; ajint imax; acdAttrValueStr(thys, "minimum", "$", &acdTmpStr); if(!ajStrToInt(acdTmpStr, &imin)) imin = INT_MIN; acdAttrValueStr(thys, "maximum", "$", &acdTmpStr); if(!ajStrToInt(acdTmpStr, &imax)) imax = INT_MAX; if(table) ajStrAssignClear(str); else ajStrAppendC(str, " ("); if(imax != INT_MAX) { if(imin != INT_MIN) ajFmtPrintAppS(str, "Integer from %d to %d", imin, imax); else ajFmtPrintAppS(str, "Integer up to %d", imax); } else { if(imin != INT_MIN) ajFmtPrintAppS(str, "Integer %d or more", imin); else ajFmtPrintAppS(str, "Any integer value"); } if(!table) ajStrAppendC(str, ")"); return; } ID acdHelpValidFloat TY static MO ajacd LB acd XX DE Generates valid description for a floating point type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidFloat(const AcdPAcd thys, AjBool table, AjPStr* str) { float vfmin; float vfmax; ajint iprec; acdAttrValueStr(thys, "minimum", "$", &acdTmpStr); if(!ajStrToFloat(acdTmpStr, &vfmin)) vfmin = -FLT_MAX; acdAttrValueStr(thys, "maximum", "$", &acdTmpStr); if(!ajStrToFloat(acdTmpStr, &vfmax)) vfmax = FLT_MAX; acdAttrValueStr(thys, "precision", "$", &acdTmpStr); if(!ajStrToInt(acdTmpStr, &iprec)) iprec = 3; if(table) ajStrAssignClear(str); else ajStrAppendC(str, " ("); if(vfmax != FLT_MAX) { if(vfmin != -FLT_MAX) ajFmtPrintAppS(str, "Number from %.*f to %.*f", iprec, vfmin, iprec, vfmax); else ajFmtPrintAppS(str, "Number up to %.*f", iprec, vfmax); } else { if(vfmin != -FLT_MAX) ajFmtPrintAppS(str, "Number %.*f or more", iprec, vfmin); else ajFmtPrintAppS(str, "Any numeric value"); } if(!table) ajStrAppendC(str, ")"); return; } ID acdHelpValidCodon TY static MO ajacd LB acd XX DE Generates valid description for a codon usage table type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidCodon(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidDirlist TY static MO ajacd LB acd XX DE Generates valid description for a dirlist type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidDirlist(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidFilelist TY static MO ajacd LB acd XX DE Generates valid description for a filelist type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidFilelist(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidMatrix TY static MO ajacd LB acd XX DE Generates valid description for a comparison matrix type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidMatrix(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidFeatout TY static MO ajacd LB acd XX DE Generates valid description for a feature output type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidFeatout(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidFeatures TY static MO ajacd LB acd XX DE Generates valid description for an input feature type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidFeatures(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidRange TY static MO ajacd LB acd XX DE Generates valid description for a sequence range. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidRange(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAssignClear(str); return; } ID acdHelpValidGraph TY static MO ajacd LB acd XX DE Generates valid description for a graphics device type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidGraph(const AcdPAcd thys, AjBool table, AjPStr* str) { AjPList list; AjPStr name = NULL; ajint i = 0; if(!thys) return; list = ajListstrNew(); ajGraphicsListDevices(list); if(table) ajFmtPrintAppS(str, "EMBOSS has a list of known devices, including "); else ajFmtPrintAppS(str, " ("); while(ajListstrPop(list, &name)) { if(i) ajFmtPrintAppS(str, ", "); ajFmtPrintAppS(str, "%S", name); ajStrDel(&name); i++; } if(!table) ajFmtPrintAppS(str, ")"); ajListFree(&list); return; } ID acdHelpValidString TY static MO ajacd LB acd XX DE Generates valid description for a string type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidString(const AcdPAcd thys, AjBool table, AjPStr* str) { ajint minlen; ajint maxlen; AjBool word; acdAttrValueStr(thys, "minlength", "0", &acdTmpStr); if(!ajStrToInt(acdTmpStr, &minlen)) minlen = 0; acdAttrValueStr(thys, "maxlength", "0", &acdTmpStr); if(!ajStrToInt(acdTmpStr, &maxlen)) maxlen = 0; acdAttrValueStr(thys, "word", "0", &acdTmpStr); if(!ajStrToBool(acdTmpStr, &word)) ajFatal("Bad boolean value"); if(table) ajStrAssignClear(str); else ajStrAppendC(str, " ("); if(word) ajFmtPrintAppS(str, "Any word"); else ajFmtPrintAppS(str, "Any string"); if(maxlen > 0) { if(minlen > 0) ajFmtPrintAppS(str, " from %d to %d characters", minlen, maxlen); else ajFmtPrintAppS(str, " up to %d characters", maxlen); } else { if(minlen > 0) ajFmtPrintAppS(str, " of at least %d characters", minlen); } acdAttrValueStr(thys, "pattern", "", &acdTmpStr); if(ajStrGetLen(acdTmpStr)) ajFmtPrintAppS(str, ", matching regular expression /%S/", acdTmpStr); if(!table) ajStrAppendC(str, ")"); return; } ID acdHelpValidRegexp TY static MO ajacd LB acd XX DE Generates valid description for a regular expression type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidRegexp(const AcdPAcd thys, AjBool table, AjPStr* str) { ajint minlen; ajint maxlen; acdAttrValueStr(thys, "minlength", "0", &acdTmpStr); if(!ajStrToInt(acdTmpStr, &minlen)) minlen = 0; acdAttrValueStr(thys, "maxlength", "0", &acdTmpStr); if(!ajStrToInt(acdTmpStr, &maxlen)) maxlen = 0; if(table) ajStrAssignClear(str); else ajStrAppendC(str, " ("); if(maxlen > 0) { if(minlen > 0) ajFmtPrintS(str, "A regular expression pattern from %d to " "%d characters", minlen, maxlen); else ajFmtPrintS(str, "A regular expression pattern up to %d characters", maxlen); } else { if(minlen > 0) ajFmtPrintS(str, "A regular expression pattern of at least " "%d characters", minlen); else ajStrAssignC(str, "Any regular expression pattern is accepted"); } if(!table) ajStrAppendC(str, ")"); return; } ID acdHelpValidList TY static MO ajacd LB acd XX DE Generates valid description for a list type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidList(const AcdPAcd thys, AjBool table, AjPStr* str) { AjPStr codedelim = NULL; AjPStr delim = NULL; AjPStr value = NULL; AjPStrTok handle; AjPStrTok codehandle; static AjPStr code = NULL; static AjPStr desc = NULL; static AjPStr line = NULL; acdAttrValueStr(thys, "delimiter", ";", &delim); acdAttrValueStr(thys, "value", "", &value); if(!ajStrGetLen(value)) if(!acdKnownValueList(thys, &value)) acdError("No value defined for list"); handle = ajStrTokenNewS(value, delim); if(table) ajFmtPrintAppS(str, ""); else ajFmtPrintAppS(str, " (Values: "); acdAttrValueStr(thys, "codedelimiter", ":", &codedelim); while(ajStrTokenNextFind(&handle, &line)) { codehandle = ajStrTokenNewS(line, codedelim); ajStrTokenNextParse(&codehandle, &code); ajStrTokenNextParseS(&codehandle, delim, &desc); acdTextTrim(&code); acdTextTrim(&desc); if(table) ajFmtPrintAppS(str, "", code, desc); else ajFmtPrintAppS(str, "%S (%S); ", code, desc); ajStrTokenDel(&codehandle); } if(table) ajFmtPrintAppS(str, "
%S (%S)
"); else { ajStrCutEnd(str, 2); ajStrAppendC(str, ")"); } ajStrTokenDel(&handle); return; } ID acdHelpValidSelect TY static MO ajacd LB acd XX DE Generates valid description for a select type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpValidSelect(const AcdPAcd thys, AjBool table, AjPStr* str) { AjPStr delim = NULL; AjPStr value = NULL; AjPStrTok handle; static AjPStr desc = NULL; static const char* white = " \t\n\r"; acdAttrValueStr(thys, "delimiter", ";", &delim); acdAttrValueStr(thys, "value", "", &value); if(!ajStrGetLen(value)) if(!acdKnownValueSelect(thys, &value)) acdError("No value defined for selection"); handle = ajStrTokenNewS(value, delim); while(ajStrTokenNextFind(&handle, &desc)) { ajStrTrimC(&desc, white); if(table) ajFmtPrintAppS(str, "%S
", desc); else { if(ajStrGetLen(*str)) ajStrAppendK(str, ' '); ajFmtPrintAppS(str, "%S", desc); } } ajStrTokenDel(&handle); return; } ID acdHelpValid TY static MO ajacd LB acd XX DE Generates help text for an ACD object using the help, info, prompt DE and code settings. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated. Only written to if PD initially empty PX RT void RD RX // static void acdHelpValid(const AcdPAcd thys, AjBool table, AjPStr* str) { ajint i; AjPStr tmpstr = NULL; if(table && ajStrGetLen(*str)) return; if(acdAttrValueStr(thys, "valid", "", &tmpstr)) { if(table) ajStrAppendS(str, tmpstr); else ajFmtPrintAppS(str, "(%S)", tmpstr); return; } /* special processing for sequences, outseq, outfile */ for(i=0; acdValue[i].Name; i++) if(ajCharMatchC(acdType[thys->Type].Name, acdValue[i].Name)) { /* Calling funclist acdValue() */ if(acdValue[i].Valid) (*acdValue[i].Valid)(thys, table, str); break; } if(ajStrGetLen(*str)) return; ajStrAppendC(str, acdType[thys->Type].Valid); return; } ID acdHelpExpectSeq TY static MO ajacd LB acd XX DE Generates expected value description for an input sequence type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectSeq(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajFmtPrintAppS(str, "Required"); return; } ID acdHelpExpectSeqout TY static MO ajacd LB acd XX DE Generates expected value description for an output sequence type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectSeqout(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajFmtPrintAppS(str, "<*>.format"); else ajFmtPrintAppS(str, "."); return; } ID acdHelpExpectOut TY static MO ajacd LB acd XX DE Generates expected value description for an outfile type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectOut(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajFmtPrintAppS(str, "<*>.%S", acdProgram); else ajFmtPrintAppS(str, "*.%S", acdProgram); return; } ID acdHelpExpectInt TY static MO ajacd LB acd XX DE Generates expected value description for an integer type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectInt(const AcdPAcd thys, AjBool table, AjPStr* str) { ajint i; acdAttrValueStr(thys, "default", "0", &acdTmpStr); if(ajStrToInt(acdTmpStr, &i)) ajFmtPrintAppS(str, "%d", i); else { if(table) ajFmtPrintAppS(str, "calculated value"); } return; } ID acdHelpExpectFloat TY static MO ajacd LB acd XX DE Generates expected value description for a floating point type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectFloat(const AcdPAcd thys, AjBool table, AjPStr* str) { float f; ajint iprec; acdAttrValueStr(thys, "default", "0.0", &acdTmpStr); if(!ajStrToFloat(acdTmpStr, &f)) f = 0.0; acdAttrValueStr (thys, "precision", "3", &acdTmpStr); if(!ajStrToInt(acdTmpStr, &iprec)) iprec = 3; if(table) ajFmtPrintAppS(str, "%.*f", iprec, f); else ajFmtPrintAppS(str, "%.*f", iprec, f); return; } ID acdHelpExpectIn TY static MO ajacd LB acd XX DE Generates expected value description for an infile type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectIn(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajFmtPrintS(str, "Required"); return; } ID acdHelpExpectData TY static MO ajacd LB acd XX DE Generates expected value description for a datafile type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectData(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajFmtPrintS(str, "File in the data file path"); return; } ID acdHelpExpectCodon TY static MO ajacd LB acd XX DE Generates expected value description for a codon usage table type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectCodon(const AcdPAcd thys, AjBool table, AjPStr* str) { AjPStr tmpstr = NULL; if(!thys) return; acdAttrResolve(thys, "name", &tmpstr); if(ajStrGetLen(tmpstr)) { if(table) ajStrAppendS(str, tmpstr); else ajStrAppendS(str, tmpstr); ajStrDel(&tmpstr); return; } return; } ID acdHelpExpectDirlist TY static MO ajacd LB acd XX DE Generates expected value description for a dirlist type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectDirlist(const AcdPAcd thys, AjBool table, AjPStr* str) { AjPStr tmpstr = NULL; if(!thys) return; acdAttrResolve(thys, "default", &tmpstr); if(ajStrGetLen(tmpstr)) { if(table) ajStrAppendS(str, tmpstr); else ajStrAppendS(str, tmpstr); ajStrDel(&tmpstr); return; } if(table) ajStrAssignC(str, DEFDLIST); else ajStrAssignC(str, DEFDLIST); return; } ID acdHelpExpectFilelist TY static MO ajacd LB acd XX DE Generates expected value description for a filelist type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectFilelist(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAppendC(str, "comma-separated file list"); return; } ID acdHelpExpectMatrix TY static MO ajacd LB acd XX DE Generates expected value description for a comparison matrix type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectMatrix(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAppendC(str, "EBLOSUM62 for protein
EDNAFULL for DNA"); else ajStrAppendC(str, "EBLOSUM62 for protein, EDNAFULL for DNA"); return; } ID acdHelpExpectFeatout TY static MO ajacd LB acd XX DE Generates expected value description for a feature output type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectFeatout(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAppendC(str, "unknown.gff"); else ajStrAppendC(str, "unknown.gff"); return; } ID acdHelpExpectFeatures TY static MO ajacd LB acd XX DE Generates expected value description for an input feature type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectFeatures(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajFmtPrintAppS(str, "Required"); return; } ID acdHelpExpectRange TY static MO ajacd LB acd XX DE Generates expected value description for a sequence range type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectRange(const AcdPAcd thys, AjBool table, AjPStr* str) { if(!thys) return; if(table) ajStrAppendC(str, "full sequence"); else ajStrAppendC(str, "(full sequence)"); return; } ID acdHelpExpectGraph TY static MO ajacd LB acd XX DE Generates expected value description for a graphics device type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectGraph(const AcdPAcd thys, AjBool table, AjPStr* str) { #ifndef WIN32 #ifndef X_DISPLAY_MISSING /* X11 is available */ const char* defdev = "x11"; #else #ifdef PLD_png /* if png/gd/zlib libraries available for png driver */ const char* defdev = "png"; #else const char* defdev = "ps"; #endif #endif #else const char* defdev = "win3"; #endif if(!thys) return; if(table) ajFmtPrintAppS(str, "EMBOSS_GRAPHICS value, or %s", defdev); else ajFmtPrintAppS(str, "$EMBOSS_GRAPHICS value, or %s", defdev); return; } ID acdHelpExpectRegexp TY static MO ajacd LB acd XX DE Generates expected value description for a regular expression type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectRegexp(const AcdPAcd thys, AjBool table, AjPStr* str) { ajint minlen; acdAttrValueStr(thys, "min", "1", &acdTmpStr); if(!ajStrToInt(acdTmpStr, &minlen)) minlen = 0; if(minlen > 0) { if(table) ajStrAppendC(str, "Required"); } else { if(table) ajStrAppendC(str, "An empty regular expression is accepted"); } return; } ID acdHelpExpectString TY static MO ajacd LB acd XX DE Generates expected value description for a string type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpectString(const AcdPAcd thys, AjBool table, AjPStr* str) { ajint minlen; acdAttrValueStr(thys, "min", "0", &acdTmpStr); if(!ajStrToInt(acdTmpStr, &minlen)) minlen = 0; if(minlen > 0) { if(table) ajStrAppendC(str, "Required"); } else { if(table) ajStrAppendC(str, " "); } return; } ID acdHelpExpect TY static MO ajacd LB acd XX DE Generates expected value text for an ACD object code settings. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r table AjBool PD True if writing acdtable HTML table PX PN [3] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpExpect(const AcdPAcd thys, AjBool table, AjPStr* str) { ajint i; AjPStr tmpstr = NULL; if(ajStrGetLen(*str)) return; if(!thys->AssocQuals && !acdDoGalaxy) if(acdAttrValueStr(thys, "expected", "", str)) return; if(acdAttrValueStr(thys, "default", "", str)) { if(acdDoGalaxy) { tmpstr = ajStrNewS(*str); if(!acdVarSimpleResolve(tmpstr, str)) ajStrAssignS(str, tmpstr); ajStrDel(&tmpstr); } return; } /* special processing for sequences, outseq, outfile */ for(i=0; acdValue[i].Name; i++) if(ajCharMatchC(acdType[thys->Type].Name, acdValue[i].Name)) { /* Calling funclist acdValue() */ if(acdValue[i].Expect) (*acdValue[i].Expect)(thys, table, str); break; } if(ajStrGetLen(*str)) return; ajStrAppendS(str, thys->DefStr[DEF_DEFAULT]); if(ajStrGetLen(*str)) return; if(table) ajStrAppendC(str, " "); return; } ID acdHelpText TY static MO ajacd LB acd XX DE Generates help text for an ACD object using the help, info, prompt DE and code settings. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA w str AjPStr* PD Help text (if any) generated PX RT void RD RX // static void acdHelpText(const AcdPAcd thys, AjPStr* str) { AjPStr prompt; AjPStr info; AjPStr code; AjPStr help; static AjPStr msg = NULL; if(thys->DefStr) { prompt = thys->DefStr[DEF_PROMPT]; info = thys->DefStr[DEF_INFO]; code = thys->DefStr[DEF_CODE]; help = thys->DefStr[DEF_HELP]; if(ajStrGetLen(help)) ajStrAssignS(&msg, help); else if(ajStrGetLen(code)) acdCodeGet(code, &msg); else if(ajStrGetLen(info)) ajStrAssignS(&msg, info); else if(ajStrGetLen(prompt)) ajStrAssignS(&msg, prompt); else if(acdType[thys->Type].HelpSet) (*acdType[thys->Type].HelpSet)(thys, &msg); else { if(!acdHelpCodeDef(thys, &msg)) { ajStrAssignResC(&msg, 512, ""); if(thys->Assoc) ajFmtPrintS(&msg, "%s value", acdType[thys->Type].Name); else ajFmtPrintS(&msg, "(no help text) %s value", acdType[thys->Type].Name); } } acdVarResolve(&msg); ajStrAppendS(str, msg); ajStrDel(&msg); } return; } ID acdHelpShow TY static MO ajacd LB acd XX DE Prints the qualifier category and the help for any DE qualifiers in that category (or "(none)" if there are none). XX PN [1] PA r str const AjPStr PD Help text (if any) PX PN [2] PA r title const char* PD Title line for this call PX RT void RD RX // static void acdHelpShow(const AjPStr str, const char* title) { if(acdDoTable) return; if(!ajStrGetLen(str)) { ajUser(" %s: (none)", title); return; } ajUser(" %s:", title); ajUserDumpS(str); return; } ID acdHelpTableShow TY static MO ajacd LB acd XX DE Prints the qualifier category and the help for any DE qualifiers in that category (or "(none)" if there are none). XX PN [1] PA r tablist const AjPList PD Help text (if any). PX PN [2] PA r title const char* PD Title line for this call PX RT void RD RX // static void acdHelpTableShow(const AjPList tablist, const char* title) { AcdPTableItem item; AjIList iter = NULL; ajuint nitem = 0; if(!acdDoTable) return; if(!tablist) { ajUserDumpC(""); /* was #FFFFD0 */ ajUserDumpC("Qualifier"); ajUserDumpC("Type"); ajUserDumpC("Description"); ajUserDumpC("Allowed values"); ajUserDumpC("Default"); ajUserDumpC("\n"); return; } /* new section */ if(title){ ajUserDumpC(""); /* was #FFFFD0 */ ajUser("%s", title); ajUserDumpC("\n"); } iter = ajListIterNewread(tablist); while((item = ajListIterGet(iter))) { ajUserDumpC(""); if(ajStrGetLen(item->Title)) { ajUser("%S", item->Title); } else { nitem++; acdTextTrim(&item->Help); ajUser("%S", item->Qual); ajUser("%S", item->Type); ajUser("%S", item->Help); ajUser("%S", item->Valid); ajUser("%S", item->Expect); } ajUserDumpC("\n"); } if(!nitem || !ajListGetLength(tablist)) { ajUserDumpC(""); ajUserDumpC("(none)"); ajUserDumpC("\n"); } ajListIterDel(&iter); return; } ID acdHelpXsdShow TY static MO ajacd LB acd XX DE Prints the qualifier category and the help for any DE qualifiers in that category (or "(none)" if there are none). XX PN [1] PA r inlist const AjPList PD Help text (if any). PX PN [2] PA r outlist const AjPList PD Help text (if any). PX RT void RD RX // static void acdHelpXsdShow(const AjPList inlist, const AjPList outlist) { AcdPXsdItem item; AjIList iter = NULL; AjPStrTok handle = NULL; AjPStr rest = NULL; AjPStr word = NULL; AjPStr tmpstr = NULL; if(!acdDoXsd) return; ajUserDumpC(" "); ajUserDumpC(" "); ajUserDumpC(" "); if(ajListGetLength(inlist)) { iter = ajListIterNewread(inlist); while((item = ajListIterGet(iter))) { acdTextTrim(&item->Annotation); /* Need specific strings for each datatype */ /* add a block here as a temporary solution */ /* ** Later create a functions for each datatype ** and a default function for general datatypes */ /* ** Datatypes with more than one value - sequences see above, others ** may explicitly write their choices */ #if 0 /* //ajUser(" ", // item->Qual); //ajUser(" "); */ #endif /* ** sequences: 1 for required values - e.g. parameters - 0 for ** others */ if (ajStrMatchC(item->Type, "long")) { ajUser(" Qual); if(ajStrGetLen(item->Relation)) { handle = ajStrTokenNewC(item->Relation, "|"); while(ajStrTokenNextParse(&handle, &tmpstr)) { ajStrExtractWord(tmpstr, &rest, &word); ajUser(" sawsdl:modelReference=" "\"http://purl.org%S\"", word); } ajStrTokenDel(&handle); } ajUser(" minOccurs=\"1\">"); ajUser(" "); ajUser(" "); ajUser(" %S", item->Annotation); ajUser(" "); ajUser(" "); ajUser(" "); ajUser(" "); ajUser(" "); /* TODO actual min value */ ajUser(" "); ajUser(" "); ajUser(" "); } else if (ajStrMatchC(item->Type, "seqall")){ ajUser(" Qual); if(ajStrGetLen(item->Relation)) { handle = ajStrTokenNewC(item->Relation, "|"); while(ajStrTokenNextParse(&handle, &tmpstr)) { ajStrExtractWord(tmpstr, &rest, &word); ajUser(" sawsdl:modelReference=" "\"http://purl.org%S\"", word); } ajStrTokenDel(&handle); } ajUser(" minOccurs=\"1\">"); ajUser(" "); ajUser(" "); ajUser(" %S", item->Annotation); ajUser(" "); ajUser(" "); } else { ajUser(" Qual, item->Type); if(ajStrGetLen(item->Relation)) { handle = ajStrTokenNewC(item->Relation, "|"); while(ajStrTokenNextParse(&handle, &tmpstr)) { ajStrExtractWord(tmpstr, &rest, &word); ajUser(" sawsdl:modelReference=" "\"http://purl.org%S\"", word); } ajStrTokenDel(&handle); } ajUser(" minOccurs=\"1\">"); ajUser(" "); ajUser(" "); ajUser(" %S", item->Annotation); ajUser(" "); ajUser(" "); } ajUser(" "); } } ajListIterDel(&iter); ajUserDumpC(" "); ajUserDumpC(" "); ajUserDumpC(" "); ajUserDumpC(" "); if(ajListGetLength(outlist)) { iter = ajListIterNewread(outlist); while((item = ajListIterGet(iter))) { if (ajStrMatchC(item->Type, "seqoutall")) { ajUser(" Qual, item->Type); } else { ajUser(" Qual, item->Type); } if(ajStrGetLen(item->Relation)) { ajUser("relation: '%S'", item->Relation); handle = ajStrTokenNewC(item->Relation, "|"); while(ajStrTokenNextParse(&handle, &tmpstr)) { ajStrExtractWord(tmpstr, &rest, &word); ajUser(" sawsdl:modelReference=\"http://purl.org%S\"", word); } ajStrTokenDel(&handle); } ajUser(" minOccurs=\"1\">"); ajUser(" "); ajUser(" "); ajUser(" %S", item->Annotation); ajUser(" "); ajUser(" "); ajUser(" "); } } ajUserDumpC(" "); ajUserDumpC(" "); ajListIterDel(&iter); ajStrDel(&tmpstr); ajStrDel(&rest); ajStrDel(&word); return; } ID acdHelpGalaxyShow TY static MO ajacd LB acd XX DE Prints the qualifier category and the help for any DE qualifiers in that category (or "(none)" if there are none). XX PN [1] PA r inlist const AjPList PD Help text (if any). PX PN [2] PA r outlist const AjPList PD Help text (if any). PX RT void RD RX // static void acdHelpGalaxyShow(const AjPList inlist, const AjPList outlist) { AcdPGalaxyItem item; AjIList iter = NULL; /* AjPStrTok handle = NULL;*/ AjPStr rest = NULL; AjPStr word = NULL; AjPStr tmpstr = NULL; AjPStr galaxyvar = NULL; ajuint ninputs = 0; ajuint noutputs = 0; ajuint i; AjPStrTok handle = NULL; AjPStr galaxyin = NULL; AjPStr galaxyout = NULL; AjPStr fmtname = NULL; AjPStr fmtdesc = NULL; const char* acdinputs[] = { "sequence", "seqall", "seqset", "seqsetall", "features", NULL }; const char* acdoutputs[] = { "seqout", "seqoutall", "seqoutset", "align", "report", "featout", "graph", "xygraph", NULL }; const char* acdoutformats[] = { "seqout", "seqoutall", "seqoutset", "align", "gff:GFF,pir:PIR,swiss:SWISS,excel:Excel (TAB Delimited)", "featout", "png:PNG", "png:PNG", NULL }; const char* acdoutfmtqual[] = { "format", "format", "format", "aformat", "rformat", "fformat", "", "", NULL }; if(!acdDoGalaxy) return; if(!acdGalaxyCmdStr) acdGalaxyCmdStr = ajStrNewS(acdProgram); if(ajListGetLength(inlist)) { iter = ajListIterNewread(inlist); while((item = ajListIterGet(iter))) { acdTextTrim(&item->Annotation); /* ajUser("AcdSGalaxyItem input"); ajUser(" qual: '%S'", item->Qual); ajUser(" type: '%S'", item->Type); ajUser(" annotation: '%S'", item->Annotation); ajUser(" relation: '%S'", item->Relation); ajUser(" valid: '%S'", item->Valid); ajUser(" expect: '%S'", item->Expect); ajUser(" required: '%B'", item->Required); ajUser(" optional: '%B'", item->Optional); */ /* ** test Type ** use to select format, input1/output1 name, type ** need for output format (note: this is an input!) ** set size=4 for int or float ** type text for string, integer for int, float for float */ for(i=0; acdinputs[i]; i++) if(ajStrMatchC(item->Type, acdinputs[i])) break; if(acdinputs[i]) { ajFmtPrintAppS(&galaxyin, " \n", ++ninputs); ajFmtPrintAppS(&galaxyin, " \n"); ajFmtPrintAppS(&galaxyin, " \n"); ajFmtPrintAppS(&acdGalaxyCmdStr, " -%S $input%u", item->Qual, ninputs); } else if(ajStrMatchC(item->Expect, "Required")) { ajFmtPrintAppS(&galaxyin, " \n", item->Qual); ajFmtPrintAppS(&galaxyin, " \n"); ajFmtPrintAppS(&galaxyin, " \n"); ajFmtPrintAppS(&acdGalaxyCmdStr, " -%S $%S", item->Qual, item->Qual); } else { if(ajStrMatchC(item->Type, "string")) ajFmtPrintAppS(&galaxyin, " \n", item->Qual, item->Expect); else if(ajStrMatchC(item->Type, "boolean") || ajStrMatchC(item->Type, "toggle")) { ajFmtPrintAppS(&galaxyin, " \n", item->Qual, item->Expect); } else if(ajStrMatchC(item->Type, "float") || ajStrMatchC(item->Type, "double")) ajFmtPrintAppS(&galaxyin, " \n", item->Qual, item->Expect); else if(ajStrMatchC(item->Type, "integer") || ajStrMatchC(item->Type, "long")) ajFmtPrintAppS(&galaxyin, " \n", item->Qual, item->Expect); else if(ajStrMatchC(item->Type, "select") || ajStrMatchC(item->Type, "list")) ajFmtPrintAppS(&galaxyin, " \n", item->Qual, item->Expect); else ajFmtPrintAppS(&galaxyin, " \n", item->Qual, item->Expect); ajFmtPrintAppS(&galaxyin, " \n", item->Prompt); ajFmtPrintAppS(&galaxyin, " \n"); ajStrDel(&galaxyvar); if(!ajStrGetLen(galaxyvar)) galaxyvar = item->Qual; ajFmtPrintAppS(&acdGalaxyCmdStr, " -%S $%S", item->Qual, galaxyvar); } /* Need specific strings for each datatype */ /* add a block here as a temporary solution */ /* ** sequences: 1 for required values - e.g. parameters - 0 for ** others */ /* if (ajStrMatchC(item->Type, "long")) { ajUserDumpC("long"); } else if (ajStrMatchC(item->Type, "seqall")){ ajUserDumpC("seqall"); } else { ajUserDumpC("other"); } ajUserDumpC("...initemdone"); */ } } ajListIterDel(&iter); if(ajListGetLength(outlist)) { iter = ajListIterNewread(outlist); while((item = ajListIterGet(iter))) { for(i=0; acdoutputs[i]; i++) if(ajStrMatchC(item->Type, acdoutputs[i])) break; if(acdoutputs[i]) { if(ajCharMatchC(acdoutputs[i], "graph") || ajCharMatchC(acdoutputs[i], "xygraph")) { ajFmtPrintAppS(&acdGalaxyCmdStr, " -%S png -goutfile $goutfile", item->Qual); ajFmtPrintAppS(&galaxyout, " \n", acdoutformats[i], acdProgram, ++noutputs); } else if(ajCharMatchC(acdoutputs[i], "report")) { ajFmtPrintAppS(&galaxyout, " \n", acdProgram, ++noutputs); if(ajCharMatchC(acdoutputs[i], "report")) { ajFmtPrintAppS(&galaxyin, " \n", noutputs); ajFmtPrintAppS(&galaxyin, " \n"); ajStrTokenAssigncharC(&handle, acdoutformats[i], ":,"); while(ajStrTokenNextParse(&handle, &fmtname)) { ajStrTokenNextParse(&handle, &fmtdesc); if(ajStrMatchC(fmtname, "gff2")) ajFmtPrintAppS(&galaxyin, " " "\n", fmtname, fmtdesc); else ajFmtPrintAppS(&galaxyin, " \n", fmtname, fmtdesc); } ajFmtPrintAppS(&galaxyin, " "); ajFmtPrintAppS(&acdGalaxyCmdStr, " -rformat $out_format%u", noutputs); } } else { ajFmtPrintAppS(&galaxyout, " \n", acdProgram, ++noutputs); if(ajCharMatchC(acdoutputs[i], "report")) { ajFmtPrintAppS(&galaxyin, " \n", noutputs); ajFmtPrintAppS(&galaxyin, " \n"); ajStrTokenAssigncharC(&handle, acdoutformats[i], ":,"); while(ajStrTokenNextParse(&handle, &fmtname)) { ajStrTokenNextParse(&handle, &fmtdesc); if(ajStrMatchS(fmtname, item->Expect)) ajFmtPrintAppS(&galaxyin, " " "\n", fmtname, fmtdesc); else ajFmtPrintAppS(&galaxyin, " \n", fmtname, fmtdesc); } ajFmtPrintAppS(&galaxyin, " "); ajFmtPrintAppS(&acdGalaxyCmdStr, " -%s $out_format%u", acdoutfmtqual[i], noutputs); } } } else { /* use type=file or type=data (which?) */ ajFmtPrintAppS(&galaxyout, " \n" " \n" " \n", item->Qual, item->Qual, item->Annotation); } /* ajUser("AcdSGalaxyItem output\n"); ajUser(" qual: '%S'", item->Qual); ajUser(" type: '%S'", item->Type); ajUser(" annotation: '%S'", item->Annotation); ajUser(" relation: '%S'", item->Relation); ajUser(" valid: '%S'", item->Valid); ajUser(" expect: '%S'", item->Expect); ajUser(" required: '%B'", item->Required); ajUser(" optional: '%B'", item->Optional); */ /* if (ajStrMatchC(item->Type, "seqoutall")) { ajUserDumpC("seqoutall"); } else { ajUserDumpC("other"); } if(ajStrGetLen(item->Relation)) { handle = ajStrTokenNewC(item->Relation, "|"); while(ajStrTokenNextParse(&handle, &tmpstr)) { ajStrExtractWord(tmpstr, &rest, &word); ajUserDumpC(""); } ajStrTokenDel(&handle); } ajUserDumpC("...outitemdone"); */ } } ajUser(" %S -auto", acdGalaxyCmdStr); ajUserDumpC(" "); ajUserDumpS(galaxyin); ajUserDumpC(" "); ajUserDumpC(" "); ajUserDumpS(galaxyout); ajUserDumpC(" "); /* ajUser(" "); */ /* for each QA test: */ /*ajUser(" \n" " \n" " \n" " \n" " \n" " " ); */ /* end of QA tests */ /* ajUser(" \n"); */ ajUser(" \n" " \n" "\n" " You can view the original documentation here_.\n" "\n" " .. _here: http://emboss.sourceforge.net/apps/release/6.4/" "emboss/apps/%S.html\n" " \n" "", acdProgram); ajListIterDel(&iter); ajStrDel(&tmpstr); ajStrDel(&rest); ajStrDel(&word); ajStrDel(&fmtname); ajStrDel(&fmtdesc); ajStrTokenDel(&handle); return; } ID acdHelpAssocTable TY static MO ajacd LB acd XX DE Appends an associated qualifier and its help text to the table list. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA u tablist AjPList PD Help text list being built PX RT void RD RX // static void acdHelpAssocTable(const AcdPAcd thys, AjPList tablist) { AcdPTableItem item; AcdPQual quals; ajint i; AcdPAcd pa; if(!acdDoTable) return; acdLog("++ acdHelpAssocTable %S\n", thys->Name); if(thys->Level == ACD_APPL) { quals = acdQualAppl; } else { AJNEW0(item); ajFmtPrintS(&item->Title, "\"-%S\" associated %s qualifiers\n", thys->Name,acdType[thys->Type].Name); ajListPushAppend(tablist, item); quals = acdType[thys->Type].Quals; } acdLog("++ type %d quals %x\n", thys->Type, quals); if(quals) { i=0; for(pa=thys->AssocQuals; pa && pa->Assoc; pa=pa->Next) { acdLog("++ assoc[%d].Name %s\n", i, quals[i].Name); AJNEW0(item); if(thys->PNum) ajFmtPrintS(&item->Qual, " -%s%d
-%s_%S", quals[i].Name, pa->PNum, quals[i].Name, thys->Name); else ajFmtPrintS(&item->Qual, " -%s", quals[i].Name); ajStrAssignC(&item->Type, quals[i].Type); ajStrAssignC(&item->Help, quals[i].Help); acdHelpValid(pa, ajTrue, &item->Valid); acdHelpExpect(pa, ajTrue, &item->Expect); ajListPushAppend(tablist, item); i++; } } return; } ID acdHelpTable TY static MO ajacd LB acd XX DE Appends a qualifier and its help text to the table list. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA u tablist AjPList PD Help text list being built PX RT void RD RX // static void acdHelpTable(const AcdPAcd thys, AjPList tablist) { AcdPTableItem item; static AjPStr nostr = NULL; static AjPStr nullstr = NULL; static AjPStr type = NULL; AjBool boolval; AjPStr defstr; if(!acdDoTable) return; AJNEW0(item); if(!nullstr) nullstr = ajStrNew(); if(thys->DefStr) defstr = thys->OrigStr; else defstr = nullstr; ajStrAssignClear(&nostr); if(acdIsQtype(thys) && (ajCharMatchC("boolean", acdType[thys->Type].Name) || ajCharMatchC("toggle", acdType[thys->Type].Name) )) { if(ajStrToBool(defstr, &boolval)) { if(boolval) ajStrAssignC(&nostr, "[no]"); ajFmtPrintS(&item->Expect, "%B", boolval); } else if(!ajStrGetLen(defstr)) ajFmtPrintS(&item->Expect, "%B", ajFalse); defstr = nullstr; } if(thys->Level == ACD_PARAM) ajFmtPrintS(&item->Qual, "[-%S%S]
(Parameter %d)", nostr, thys->Name, thys->PNum); else ajFmtPrintS(&item->Qual, "-%S%S", nostr, thys->Name); ajStrAssignC(&type, acdType[thys->Type].Name); acdHelpExpect(thys, ajTrue, &item->Expect); /* ** warning - don't try acdVarResolve here because we have not yet ** read in the data and things like calculated attributes do not exist */ ajStrAssignC(&item->Type, acdType[thys->Type].Name); acdHelpValid(thys, ajTrue, &item->Valid); acdHelpText(thys, &item->Help); ajListPushAppend(tablist, item); return; } ID acdHelpXsd TY static MO ajacd LB acd XX DE Appends a qualifier and its help text to the XSD list. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA u tablist AjPList PD Xsd list being built PX RT void RD RX // static void acdHelpXsd(const AcdPAcd thys, AjPList tablist) { AcdPXsdItem item; static AjPStr nostr = NULL; static AjPStr nullstr = NULL; AjBool boolval; AjBool tmpBool; AjPStr helpStr = NULL; AjPStr defstr = NULL; AjPStr* def = NULL; if(!acdDoXsd) return; /* skip associated qualifiers: ** handled separatedly in XSD for types that use them ** Note: this also excludes application associated general qualifiers ** -auto -verbose -filter and so on */ if(thys->Assoc) return; AJNEW0(item); if(!nullstr) nullstr = ajStrNew(); if(thys->DefStr) defstr = thys->OrigStr; else defstr = nullstr; ajStrAssignClear(&nostr); if(acdIsQtype(thys)) { if((ajCharMatchC("boolean", acdType[thys->Type].Name) || ajCharMatchC("toggle", acdType[thys->Type].Name) )) { if(ajStrToBool(defstr, &boolval)) { if(boolval) ajStrAssignC(&nostr, "no"); ajFmtPrintS(&item->Expect, "%B", boolval); } else if(!ajStrGetLen(defstr)) ajFmtPrintS(&item->Expect, "%B", ajFalse); } def = thys->DefStr; if(def && ajStrGetLen(def[DEF_ADDITIONAL])) { if(acdHelpVarResolve(&helpStr, def[DEF_ADDITIONAL])) { if(!ajStrToBool(helpStr, &tmpBool)) acdErrorAcd(thys, "Bad additional flag %S\n", def[DEF_ADDITIONAL]); } else { tmpBool = ajTrue; } if(tmpBool) item->Optional = ajTrue; } if(def && ajStrGetLen(def[DEF_STANDARD])) { if(acdHelpVarResolve(&helpStr, def[DEF_STANDARD])) { if(!ajStrToBool( helpStr, &tmpBool)) acdErrorAcd(thys, "Bad standard flag %S\n", def[DEF_STANDARD]); } else tmpBool = ajTrue; if(tmpBool) item->Required = ajTrue; } } ajStrAssignC(&item->Type, acdType[thys->Type].Name); ajStrAssignS(&item->Qual, thys->Name); acdHelpExpect(thys, ajTrue, &item->Expect); /* ** warning - don't try acdVarResolve here because we have not yet ** read in the data and things like calculated attributes do not exist */ acdHelpValid(thys, ajTrue, &item->Valid); acdHelpText(thys, &item->Annotation); acdAttrToStr(thys, "relation", "", &item->Relation); ajListPushAppend(tablist, item); ajStrDel (&helpStr); return; } ID acdHelpGalaxy TY static MO ajacd LB acd XX DE Appends a qualifier and its help text to the Galaxy list. XX PN [1] PA u thys AcdPAcd PD ACD object, may have prompt set PX PN [2] PA u tablist AjPList PD Galaxy list being built PX RT void RD RX // static void acdHelpGalaxy(AcdPAcd thys, AjPList tablist) { AcdPGalaxyItem item; static AjPStr nostr = NULL; static AjPStr nullstr = NULL; AjBool boolval; AjBool tmpBool; AjPStr helpStr = NULL; const AjPStr prompt = NULL; AjPStr defstr = NULL; AjPStr* def = NULL; ajuint itype; AjPStr galaxyExpectStr = NULL; ajint ipos; ajint cpos; ajint bpos; ajint qpos; if(!acdDoGalaxy) return; /* associated qualifiers:*/ if(thys->Assoc) return; AJNEW0(item); if(!nullstr) nullstr = ajStrNew(); if(thys->DefStr) defstr = thys->OrigStr; else defstr = nullstr; ajStrAssignClear(&nostr); if(acdIsQtype(thys)) { if((ajCharMatchC("boolean", acdType[thys->Type].Name) || ajCharMatchC("toggle", acdType[thys->Type].Name) )) { if(ajStrToBool(defstr, &boolval)) { if(boolval) ajStrAssignC(&item->Expect, "Yes"); else ajStrAssignC(&item->Expect, "No"); } else if(!ajStrGetLen(defstr)) ajFmtPrintS(&item->Expect, "%B", ajFalse); } def = thys->DefStr; if(def && ajStrGetLen(def[DEF_ADDITIONAL])) { if(acdHelpVarResolve(&helpStr, def[DEF_ADDITIONAL])) { if(!ajStrToBool(helpStr, &tmpBool)) acdErrorAcd(thys, "Bad additional flag %S\n", def[DEF_ADDITIONAL]); } else { tmpBool = ajTrue; } if(tmpBool) item->Optional = ajTrue; } if(def && ajStrGetLen(def[DEF_STANDARD])) { if(acdHelpVarResolve(&helpStr, def[DEF_STANDARD])) { if(!ajStrToBool( helpStr, &tmpBool)) acdErrorAcd(thys, "Bad standard flag %S\n", def[DEF_STANDARD]); } else tmpBool = ajTrue; if(tmpBool) item->Required = ajTrue; } } ajStrAssignC(&item->Type, acdType[thys->Type].Name); ajStrAssignS(&item->Qual, thys->Name); acdHelpExpect(thys, ajTrue, &item->Expect); ipos = (ajint) ajStrFindC(item->Expect, " for protein
"); if(ipos >= 0) /* matrix expected value */ { ajStrExchangeCC(&item->Expect, " for DNA", ""); if(acdDoGalaxyProt) { ajStrAssignSubS(&galaxyExpectStr, item->Expect, 0, ipos-1); } else if(acdDoGalaxyNuc) { ajStrAssignSubS(&galaxyExpectStr, item->Expect, ipos+16, -1); } else { ajStrAssignSubS(&galaxyExpectStr, item->Expect, ipos+16, -1); } ajStrTrimWhite(&galaxyExpectStr); ajStrAssignS(&item->Expect, galaxyExpectStr); } ajStrExchangeCC(&item->Expect, " ", ""); ajStrExchangeCC(&item->Expect, " for any sequence type", ""); ajStrExchangeCC(&item->Expect, " for any sequence", ""); ajStrExchangeCC(&item->Expect, "full sequence", ""); if(ajStrFindC(item->Expect, ".end)") >= 0) ajStrAssignC(&item->Expect, "-1"); if(ajStrFindC(item->Expect, ".begin)") >= 0) ajStrAssignC(&item->Expect, "00"); if(ajStrFindC(item->Expect, "@($(acdprotein)") >= 0) { qpos = (ajint) ajStrFindAnyK(item->Expect, '?'); cpos = (ajint) ajStrFindAnyK(item->Expect, ':'); bpos = (ajint) ajStrFindlastK(item->Expect, ')'); if(acdDoGalaxyProt) { ajStrAssignSubS(&galaxyExpectStr, item->Expect, qpos+1, cpos-1); } else if(acdDoGalaxyNuc) { ajStrAssignSubS(&galaxyExpectStr, item->Expect, cpos+1, bpos-1); } else { ajStrAssignSubS(&galaxyExpectStr, item->Expect, cpos+1, bpos-1); } ajStrTrimWhite(&galaxyExpectStr); ajStrAssignS(&item->Expect, galaxyExpectStr); } else if(ajStrFindC(item->Expect, "@(!$(acdprotein)") >= 0) { qpos = (ajint) ajStrFindAnyK(item->Expect, '?'); cpos = (ajint) ajStrFindAnyK(item->Expect, ':'); bpos = (ajint) ajStrFindlastK(item->Expect, ')'); if(acdDoGalaxyProt) { ajStrAssignSubS(&galaxyExpectStr, item->Expect, cpos+1, bpos-1); } else if(acdDoGalaxyNuc) { ajStrAssignSubS(&galaxyExpectStr, item->Expect, qpos+1, cpos-1); } else { ajStrAssignSubS(&galaxyExpectStr, item->Expect, qpos+1, cpos-1); } ajStrTrimWhite(&galaxyExpectStr); ajStrAssignS(&item->Expect, galaxyExpectStr); } prompt = acdAttrValue(thys, "code"); if(!ajStrGetLen(prompt)) prompt = acdAttrValue(thys, "prompt"); if(!ajStrGetLen(prompt)) prompt = acdAttrValue(thys, "information"); if(!ajStrGetLen(prompt)) { for (itype=0; acdType[itype].Name; itype++) if (ajStrMatchC(item->Type, acdType[itype].Name)) break; if(acdType[itype].Prompt) (*acdType[itype].Prompt)(thys); prompt = thys->StdPrompt; } if(!ajStrGetLen(prompt)) prompt = acdAttrValue(thys, "help"); ajStrAssignS(&item->Prompt, prompt); /* ** warning - don't try acdVarResolve here because we have not yet ** read in the data and things like calculated attributes do not exist */ acdHelpValid(thys, ajTrue, &item->Valid); acdHelpText(thys, &item->Annotation); acdAttrToStr(thys, "relation", "", &item->Relation); ajListPushAppend(tablist, item); ajStrDel (&helpStr); return; } ID acdListReport TY static MO ajacd LB acd XX DE Reports the current status of the ACD internal structures, converting DE values to a printable form as appropriate. XX PN [1] PA r title const char* PD Title line for this call PX RT void RD RX // static void acdListReport(const char* title) { AcdPAcd pa; ajint i = 0; ajint j = 0; char underline[256]; if(!acdDoLog) return; j = strlen(title); if(j > 255) j = 255; memset(underline, '=', j); underline[j] = '\0'; acdLog("\n"); acdLog("%s\n", title); acdLog("%s\n", underline); acdLog("\n"); for(pa=acdList; pa; pa=pa->Next) { acdLog("ACD %d\n", i); if(pa->PNum) { acdLog(" Name: '%S[%d]'\n", pa->Name, pa->PNum); acdLog(" Token: '%S[%d]'\n", pa->Token, pa->PNum); } else { acdLog(" Name: '%S'\n", pa->Name); acdLog(" Token: '%S'\n", pa->Token); } acdLog(" Param: %d\n", pa->PNum); acdLog(" Level: %d (%s)\n", pa->Level, acdLevel[pa->Level]); if(acdIsQtype(pa)) acdLog(" Qual Type: %d (%s)\n", pa->Type, acdType[pa->Type].Name); else acdLog(" Key Type: %d (%s)\n", pa->Type, acdKeywords[pa->Type].Name); acdLog(" NAttr: %d\n", pa->NAttr); acdLog(" Assoc: %B\n", pa->Assoc); if(pa->AssocQuals) acdLog(" AssocQuals: %S\n", pa->AssocQuals->Name); else acdLog(" AssocQuals: \n"); acdLog(" Defined: %B\n", pa->Defined); acdLog(" Userdefined: %B\n", pa->UserDefined); acdLog("Orig. Value: '%S'\n", pa->OrigStr); if(pa->ValStr) acdLog(" Value: '%S'\n", pa->ValStr); else acdLog(" Value: \n"); acdLog("\n"); if(pa->DefStr) { acdLog(" Default Attributes:\n"); acdListAttr(acdAttrDef, pa->DefStr, nDefAttr); acdLog("\n"); } acdLog(" Attributes:\n"); if(acdIsQtype(pa)) acdListAttr(acdType[pa->Type].Attr, pa->AttrStr, pa->NAttr); else acdListAttr(acdKeywords[pa->Type].Attr, pa->AttrStr, pa->NAttr); acdLog("\n"); i++; } return; } ID acdListAttr TY static MO ajacd LB acd XX DE Reports all attributes for an ACD attribute list. XX PN [1] PA r attr const AcdPAttr PD Attribute list PX PN [2] PA r valstr const AjPPStr PD Array of string attribute values PX PN [3] PA r nattr ajint PD Number of attributes in list PX RT void RD RX // static void acdListAttr(const AcdPAttr attr, const AjPPStr valstr, ajint nattr) { ajint i; if(!valstr) return; for(i=0; i < nattr; i++) { if(valstr[i]) acdLog(" %15.15s: '%S'\n", attr[i].Name, valstr[i] ); else acdLog(" %15.15s: \n", attr[i].Name); } return; } ID acdSet TY static MO ajacd LB acd XX DE Sets an attribute or associated qualifier value for an ACD item. DE DE All attributes, of whatever type, are treated as strings at this stage. XX PN [1] PA r thys const AcdPAcd PD ACD item. PX PN [2] PA u attrib AjPStr* PD Attribute name - converted to full name PX PN [3] PA r value const AjPStr PD Attribute value PX RT AjBool RD ajTrue if attribute is valid. RX // static AjBool acdSet(const AcdPAcd thys, AjPStr* attrib, const AjPStr value) { ajint iattr = -1; ajint idef = -1; AcdPAttr attr = acdType[thys->Type].Attr; AjPStr *attrstr = thys->AttrStr; AcdPAcd aqual; acdLog("acdSet attr '%S' val '%S' type '%S'\n", thys->Name, *attrib, value); /* recursion with associated qualifiers */ aqual = NULL; if(thys->AssocQuals) aqual = acdFindAssoc(thys, *attrib, NULL); iattr = acdFindAttr(attr, *attrib); if(thys->DefStr) /* try again with default attributes */ idef = acdFindAttr(acdAttrDef, *attrib); if(iattr >= 0 && idef >= 0) /* should never happen */ acdErrorAcd(thys, "Duplicate type and default attribute '%S'", *attrib); if(aqual) { if(iattr >= 0) acdErrorAcd(thys, /* no known case */ "'%S' matches attribute '%s' and " "associated qualifier '%S'", *attrib, attr[iattr].Name, aqual->Name); if(idef >= 0) acdErrorAcd(thys, /* no known case */ "'%S' matches default attribute '%s' and " "associated qualifier '%S'", *attrib, acdAttrDef[idef].Name, aqual->Name); } if(iattr >= 0) { ajStrAssignC(attrib, attr[iattr].Name); if(attr[iattr].Multiple) { if(ajStrGetLen(attrstr[iattr])) ajStrAppendK(&attrstr[iattr], '|'); ajStrAppendS(&attrstr[iattr], value); } else { if(acdDoValid && ajStrGetLen(attrstr[iattr])) acdWarn("duplicate attribute '%S'", *attrib); ajStrAssignS(&attrstr[iattr], value); } return ajTrue; } if(idef >= 0) { ajStrAssignC(attrib, acdAttrDef[idef].Name); if(acdAttrDef[idef].Multiple) { if(ajStrGetLen(thys->DefStr[idef])) ajStrAppendK(&thys->DefStr[idef], '|'); ajStrAppendS(&thys->DefStr[idef], value); } else { if(acdDoValid && ajStrGetLen(thys->DefStr[idef])) acdWarn("duplicate attribute '%S'", *attrib); ajStrAssignS(&thys->DefStr[idef], value); } return ajTrue; } if(aqual) return acdDef(aqual, value); /* test: wrongattr.acd */ acdErrorAcd(thys, "Attribute '%S' unknown\n", *attrib ); return ajFalse; } ID acdSetKey TY static MO ajacd LB acd XX DE Sets an attribute for an ACD key item. DE DE All attributes, of whatever type, are treated as strings at this stage. XX PN [1] PA r thys const AcdPAcd PD ACD item. PX PN [2] PA u attrib AjPStr* PD Attribute name, converted to full name PX PN [3] PA r value const AjPStr PD Attribute value PX RT AjBool RD ajTrue if attribute is valid. RX // static AjBool acdSetKey(const AcdPAcd thys, AjPStr* attrib, const AjPStr value) { ajint iattr = -1; AcdPAttr attr = acdKeywords[thys->Type].Attr; AjPStr* attrstr = thys->AttrStr; AcdPAcd aqual; /* recursion with associated qualifiers - e.g.for application */ aqual = NULL; if(thys->AssocQuals) aqual = acdFindAssoc(thys, *attrib, NULL); iattr = acdFindAttr(attr, *attrib); if(aqual) if(iattr >= 0) acdErrorAcd(thys, /* no known case */ "'%S' matches attribute '%s' and qualifier '%S'", *attrib, attr[iattr].Name, aqual->Name); if(iattr >= 0) { ajStrAssignC(attrib, attr[iattr].Name); if(attr[iattr].Multiple) { if(ajStrGetLen(attrstr[iattr])) ajStrAppendK(&attrstr[iattr], '|'); ajStrAppendS(&attrstr[iattr], value); } else { if(acdDoValid && ajStrGetLen(attrstr[iattr])) acdWarn("duplicate attribute '%S'", *attrib); ajStrAssignS(&attrstr[iattr], value); } return ajTrue; } if(aqual) return acdDef(aqual, value); /* test: wrongattr.acd */ acdErrorAcd(thys, "Attribute '%S' unknown\n", *attrib ); return ajFalse; } ID acdDef TY static MO ajacd LB acd XX DE Sets the default value for an ACD item, and flags in thys as Defined. XX PN [1] PA u thys AcdPAcd PD ACD item PX PN [2] PA r value const AjPStr PD Default value PX RT AjBool RD ajTrue always. RX // static AjBool acdDef(AcdPAcd thys, const AjPStr value) { AjPStr* attrstr = thys->DefStr; acdLog("acdDef %S '%S' %x\n", thys->Name, value, attrstr); acdSetDef(thys, value); thys->Defined = ajTrue; return ajTrue; } ID acdSetDef TY static MO ajacd LB acd XX DE Sets the default value for an ACD item. XX PN [1] PA u thys AcdPAcd PD ACD item PX PN [2] PA r value const AjPStr PD Default value PX RT AjBool RD ajTrue always. RX // static AjBool acdSetDef(AcdPAcd thys, const AjPStr value) { AjPStr* attrstr = thys->DefStr; acdLog("acdSetDef %S '%S' %x\n", thys->Name, value, attrstr); if(!thys->DefStr) return ajFalse; ajStrAssignS(&attrstr[DEF_DEFAULT], value); return ajTrue; } ID acdSetDefC TY static MO ajacd LB acd XX DE Sets the default value for an ACD item. XX PN [1] PA u thys AcdPAcd PD ACD item PX PN [2] PA r value const char* PD Default value PX RT AjBool RD ajTrue always. RX // static AjBool acdSetDefC(AcdPAcd thys, const char* value) { AjPStr *attrstr = thys->DefStr; acdLog("acdSetDefC %S '%s' %x\n", thys->Name, value, attrstr); if(!thys->DefStr) return ajFalse; ajStrAssignC(&attrstr[DEF_DEFAULT], value); return ajTrue; } ID acdSetQualDefBool TY static MO ajacd LB acd XX DE Sets the default value for an ACD item or one of its associated qualifiers XX PN [1] PA u thys AcdPAcd PD ACD item PX PN [2] PA r name const char* PD Qualifier name PX PN [3] PA r value AjBool PD Default value PX RT AjBool RD ajTrue always. RX // static AjBool acdSetQualDefBool(AcdPAcd thys, const char* name, AjBool value) { AjPStr *attrstr; AjPStr qname = NULL; AcdPAcd acd; ajStrAssignC(&qname, name); acd = acdFindQualAssoc(thys, qname, NULL, NULL, 0); ajStrDel(&qname); if(!acd) return ajFalse; attrstr = acd->DefStr; acdLog("acdSetQualDefBool %S [%d] '%s' %B\n", thys->Name, thys->PNum, name, value); if(!thys->DefStr) return ajFalse; ajFmtPrintS(&attrstr[DEF_DEFAULT], "%b", value); return ajTrue; } ID acdSetQualDefInt TY static MO ajacd LB acd XX DE Sets the default value for an ACD item or one of its associated qualifiers XX PN [1] PA u thys AcdPAcd PD ACD item PX PN [2] PA r name const char* PD Qualifier name PX PN [3] PA r value ajint PD Default value PX RT AjBool RD ajTrue always. RX // static AjBool acdSetQualDefInt(AcdPAcd thys, const char* name, ajint value) { AjPStr *attrstr; AjPStr qname = NULL; AcdPAcd acd; ajStrAssignC(&qname, name); acd = acdFindQualAssoc(thys, qname, NULL, NULL, 0); if(!acd) return ajFalse; attrstr = acd->DefStr; acdLog("acdSetQualDefInt %S [%d] '%s' %S [%d] %d\n", thys->Name, thys->PNum, name, acd->Name, acd->PNum, value); if(!thys->DefStr) return ajFalse; ajFmtPrintS(&attrstr[DEF_DEFAULT], "%d", value); ajStrDel(&qname); return ajTrue; } ID acdSetVarDef TY static MO ajacd LB acd XX DE Sets the default value for a variable ACD item. XX PN [1] PA u thys AcdPAcd PD ACD item PX PN [2] PA r value const AjPStr PD Default value PX RT AjBool RD ajTrue always. RX // static AjBool acdSetVarDef(AcdPAcd thys, const AjPStr value) { acdLog("acdSetVarDef %S '%S' %x\n", thys->Name, value, thys->ValStr); ajStrAssignS(&thys->ValStr, value); return ajTrue; } ID acdFindAttr TY static MO ajacd LB acd XX DE Locates an attribute by name in an attribute list. XX PN [1] PA r attr const AcdPAttr PD Attribute list PX PN [2] PA r attrib const AjPStr PD Attribute name to be found PX RT ajint RD offset in "attr" if found RX // static ajint acdFindAttr(const AcdPAttr attr, const AjPStr attrib) { static ajint i; static ajint j; ajint ifound = 0; AjPStr ambigList = NULL; ambigList = ajStrNew(); for(i=0; attr[i].Name; i++) { if(ajStrMatchC(attrib, attr[i].Name)) { ajStrDel(&ambigList); return i; } if(ajCharPrefixS(attr[i].Name, attrib)) { ifound++; j = i; acdAmbigAppC(&ambigList, attr[i].Name); } } if(ifound == 1) { if (acdDoValid) acdWarn("Abbreviated attribute '%S' (%S)", attrib, ambigList); ajStrDel(&ambigList); return j; } if(ifound > 1) { ajWarn("Ambiguous attribute %S (%S)", attrib, ambigList); } ajStrDel(&ambigList); return -1; } ID acdFindAttrC TY static MO ajacd LB acd XX DE Locates an attribute by name in an attribute list. XX PN [1] PA r attr const AcdPAttr PD Attribute list PX PN [2] PA r attrib const char* PD Attribute name to be found PX RT ajint RD offset in "attr" if found RX // static ajint acdFindAttrC(const AcdPAttr attr, const char* attrib) { static ajint i; static ajint j; ajint k; ajint ifound = 0; AjPStr ambigList = NULL; k = strlen(attrib); ambigList = ajStrNew(); for(i=0; attr[i].Name; i++) if(!strncmp(attr[i].Name, attrib, k)) { if(!strcmp(attr[i].Name, attrib)) { ajStrDel(&ambigList); return i; } ifound++; j = i; acdAmbigAppC(&ambigList, attr[i].Name); } if(ifound == 1) { if (acdDoValid) acdWarn("Abbreviated attribute '%s', %S", attrib, ambigList); ajStrDel(&ambigList); return j; } if(ifound > 1) { ajWarn("ambiguous attribute %s (%S)", attrib, ambigList); } ajStrDel(&ambigList); return -1; } ID acdProcess TY static MO ajacd LB acd XX DE Steps through all the ACD items, filling in missing information. DE Parameters are defined in the default attributes. The parameter DE number is generated here in the order they are found. DE DE Associated qualifiers (if any) also get a copy of the parameter number. XX RT void RD RX // static void acdProcess(void) { AcdPAcd pa; AcdPAcd qa = NULL; AjPStr reqstr = NULL; AjPStr yesstr = NULL; AjBool isreq; AjBool isparam; if(!reqstr) { ajStrAssignC(&reqstr, "standard"); ajStrAssignC(&yesstr, "Y"); } for(acdProcCurr=acdList; acdProcCurr; acdProcCurr=acdProcCurr->Next) { pa = acdProcCurr; if(pa->DefStr) ajStrAssignS(&pa->OrigStr, pa->DefStr[DEF_DEFAULT]); acdLog("acdProcess '%S' defstr '%x' test parameter\n", pa->Name, pa->DefStr); if(!pa->Assoc && pa->DefStr && acdAttrToBoolTest(pa, "parameter", ajFalse, &isparam)) { if(isparam) { acdNParam++; pa->PNum = acdNParam; pa->Level = ACD_PARAM; acdLog("acdProcess '%S' required\n", pa->Name); /* no unresolvable variables */ if(!(acdVarTest(acdAttrValue(pa, "standard")))) if(!(acdAttrToBool(pa, "standard", ajFalse, &isreq))) { acdSet(pa, &reqstr, yesstr); } qa = pa->AssocQuals; if(qa) while(qa->Assoc) { qa->PNum = acdNParam; qa = qa->Next; } } } } ajStrDel(&reqstr); ajStrDel(&yesstr); return; } ID acdSetAll TY static MO ajacd LB acd XX DE Steps through all the ACD items, calling the acdSet... function DE for each in turn to prompt the user for any missing values and DE to check that all is OK. XX RT void RD RX // static void acdSetAll(void) { AcdPAcd pa; AjBool isstd; AjBool isadd; const char* stdstring = "std"; const char* addstring = "opt"; const char* advstring = " "; const char* nostring = " "; const char* level = NULL; ajint iendsec = 0; if (acdDoTrace) { iendsec = acdFindKeyC("endsection"); ajUserDumpC("Trace:"); ajUserDumpC("Trace: Line Std ACD_Type Name and 'value'"); ajUserDumpC("Trace: ---- --- --------------- ----------------"); } for(acdSetCurr=acdList; acdSetCurr; acdSetCurr = acdSetCurr->Next) { pa = acdSetCurr; if(!pa->Assoc) acdMasterQual = pa; if(!acdIsStype(pa)) { if (acdIsQtype(pa)) (*acdType[pa->Type].TypeSet)(pa); else (*acdKeywords[pa->Type].KeySet)(pa); } if (acdDoTrace) { if(acdIsQtype(pa)) { acdAttrToBool(pa, "standard", ajFalse, &isstd); acdAttrToBool(pa, "parameter", isstd, &isstd); acdAttrToBool(pa, "additional", ajFalse, &isadd); if (isstd) level = stdstring; else if (isadd) level = addstring; else level = advstring; if (pa->Assoc) continue; else ajUser("Trace: %4d %s %15s: %15S '%S'", pa->LineNum, level, acdType[pa->Type].Name, pa->Name, pa->ValStr); } else if(acdIsStype(pa)) { ajUser("Trace: %4d %s %15s: %15S", pa->LineNum, level, acdKeywords[pa->Type].Name, pa->Name); if (pa->Type == iendsec) ajUserDumpC("Trace:"); } else { ajUser("Trace: %4d %s %15s: %15S '%S'", pa->LineNum, nostring, acdKeywords[pa->Type].Name, pa->Name, pa->ValStr); } } } return; } ID acdQualToBool TY static MO ajacd LB acd XX DE Converts an associated qualifier value into a boolean. DE Any variable references are resolved at this stage. XX PN [1] PA r thys const AcdPAcd PD ACD item of master parameter or qualifier. PX PN [2] PA r qual const char* PD Qualifier name PX PN [3] PA r defval AjBool PD default value PX PN [4] PA w result AjBool* PD Resulting value. PX PN [5] PA w valstr AjPStr* PD Resulting value as a string PX RT AjBool RD ajTrue on success RX // static AjBool acdQualToBool(const AcdPAcd thys, const char *qual, AjBool defval, AjBool *result, AjPStr* valstr) { AjBool ret; ret = acdGetValueAssoc(thys, qual, valstr); acdLog("acdQualToBool item: %S qual: %s defval: %B str: '%S', ret: %B\n", thys->Name, qual, defval, *valstr, ret); if(ret) { acdVarResolve(valstr); acdLog("resolved to: '%S'\n", *valstr); if(ajStrGetLen(*valstr)) { if(!ajStrToBool(*valstr, result)) { acdErrorAcd(thys, "Bad associated qualifier " "boolean value -%s = %S\n", thys->Name , qual, *valstr) ; } return ajTrue; } } *result = defval; ajFmtPrintS(valstr, "%b", defval); return ajFalse; } ID acdQualToFloat TY static MO ajacd LB acd XX DE Converts an associated qualifier value into a floating point number. DE Any variable references are resolved at this stage. XX PN [1] PA r thys const AcdPAcd PD ACD item of master parameter or qualifier. PX PN [2] PA r qual const char* PD Qualifier name PX PN [3] PA r defval float PD default value PX PN [4] PA r precision ajint PD floating point precision PX PN [5] PA w result float* PD Resulting value. PX PN [6] PA w valstr AjPStr* PD Resulting value as a string PX RT AjBool RD ajTrue on success RX // static AjBool acdQualToFloat(const AcdPAcd thys, const char *qual, float defval, ajint precision, float *result, AjPStr* valstr) { AjBool ret; ret = acdGetValueAssoc(thys, qual, valstr); acdLog("acdQualToFloat item: %S qual: %s defval: %.3f " "str: '%S' ret: %B\n", thys->Name, qual, defval, *valstr, ret); if(ret) { acdVarResolve(valstr); acdLog("resolved to: '%S'\n", *valstr); if(ajStrGetLen(*valstr)) { if(!ajStrToFloat(*valstr, result)) { acdErrorAcd(thys, "%S: Bad associated qualifier " "float value -%s = %S\n", qual, *valstr) ; } return ajTrue; } } *result = defval; ajStrFromFloat(valstr, defval, precision); return ajFalse; } ID acdQualToInt TY static MO ajacd LB acd XX DE Converts an associated qualifier value into an integer. DE Any variable references are resolved at this stage. XX PN [1] PA r thys const AcdPAcd PD ACD item of master parameter or qualifier. PX PN [2] PA r qual const char* PD Qualifier name PX PN [3] PA r defval ajint PD default value PX PN [4] PA w result ajint* PD Resulting value. PX PN [5] PA w valstr AjPStr* PD Qualifier value as a string PX RT AjBool RD ajTrue on success RX // static AjBool acdQualToInt(const AcdPAcd thys, const char *qual, ajint defval, ajint *result, AjPStr* valstr) { AjBool ret; ret = acdGetValueAssoc(thys, qual, valstr); acdLog("acdQualToInt item: %S qual: %s defval: %d str: '%S' ret: %B\n", thys->Name, qual, defval, *valstr, ret); if(ret) { acdVarResolve(valstr); acdLog("resolved to: '%S'\n", *valstr); if(ajStrGetLen(*valstr)) { if(ajStrMatchC(*valstr, "default")) ajStrAssignC(valstr, "0"); if(!ajStrToInt(*valstr, result)) acdErrorAcd(thys, "%S: Bad associated qualifier " "integer value -%s = %S\n", qual, *valstr); return ajTrue; } } *result = defval; ajStrFromInt(valstr, defval); return ajFalse; } ID acdQualToLong TY static MO ajacd LB acd XX DE Converts an associated qualifier value into a long integer. DE Any variable references are resolved at this stage. XX PN [1] PA r thys const AcdPAcd PD ACD item of master parameter or qualifier. PX PN [2] PA r qual const char* PD Qualifier name PX PN [3] PA r defval ajlong PD default value PX PN [4] PA w result ajlong* PD Resulting value. PX PN [5] PA w valstr AjPStr* PD Qualifier value as a string PX RT AjBool RD ajTrue on success RX // static AjBool acdQualToLong(const AcdPAcd thys, const char *qual, ajlong defval, ajlong *result, AjPStr* valstr) { AjBool ret; ret = acdGetValueAssoc(thys, qual, valstr); acdLog("acdQualToLong item: %S qual: %s defval: %Ld str: '%S' ret: %B\n", thys->Name, qual, defval, *valstr, ret); if(ret) { acdVarResolve(valstr); acdLog("resolved to: '%S'\n", *valstr); if(ajStrGetLen(*valstr)) { if(ajStrMatchC(*valstr, "default")) ajStrAssignC(valstr, "0"); if(!ajStrToLong(*valstr, result)) acdErrorAcd(thys, "%S: Bad associated qualifier " "long integer value -%s = %S\n", qual, *valstr); return ajTrue; } } *result = defval; ajStrFromLong(valstr, defval); return ajFalse; } ID acdQualToSeqbegin TY static MO ajacd LB acd XX DE Converts an associated qualifier value into an integer, or the DE string "start". DE DE Any variable references are resolved at this stage. XX PN [1] PA r thys const AcdPAcd PD ACD item of master parameter or qualifier. PX PN [2] PA r qual const char* PD Qualifier name PX PN [3] PA r defval ajint PD default value PX PN [4] PA w result ajint* PD Resulting value. PX PN [5] PA w valstr AjPStr* PD Qualifier value as a string PX RT AjBool RD ajTrue on success RX // static AjBool acdQualToSeqbegin(const AcdPAcd thys, const char *qual, ajint defval, ajint *result, AjPStr* valstr) { AjBool ret; ret = acdGetValueAssoc(thys, qual, valstr); acdLog("acdQualToSeqpos item: %S qual: %s defval: %d str: '%S' ret: %B\n", thys->Name, qual, defval, *valstr, ret); if(ret) { acdVarResolve(valstr); acdLog("resolved to: '%S'\n", *valstr); if(ajStrGetLen(*valstr)) { if(!ajStrMatchCaseC(*valstr, "default")) if(!ajStrToInt(*valstr, result)) { acdErrorAcd(thys, "Bad associated qualifier " "integer value -%s = %S\n", qual, *valstr); } acdLog("return value %B '%S'\n", ajTrue, *valstr); return ajTrue; } } *result = defval; if(!defval) ajStrAssignC(valstr, "start"); else ajStrFromInt(valstr, defval); acdLog("return default %B '%S'\n", ajFalse, *valstr); return ajFalse; } ID acdQualToSeqend TY static MO ajacd LB acd XX DE Converts an associated qualifier value into an integer, or the DE string "end". DE DE Any variable references are resolved at this stage. XX PN [1] PA r thys const AcdPAcd PD ACD item of master parameter or qualifier. PX PN [2] PA r qual const char* PD Qualifier name PX PN [3] PA r defval ajint PD default value PX PN [4] PA w result ajint* PD Resulting value. PX PN [5] PA w valstr AjPStr* PD Qualifier value as a string PX RT AjBool RD ajTrue on success RX // static AjBool acdQualToSeqend(const AcdPAcd thys, const char *qual, ajint defval, ajint *result, AjPStr* valstr) { AjBool ret; ret = acdGetValueAssoc(thys, qual, valstr); acdLog("acdQualToSeqpos item: %S qual: %s defval: %d str: '%S' ret: %B\n", thys->Name, qual, defval, *valstr, ret); if(ret) { acdVarResolve(valstr); acdLog("resolved to: '%S'\n", *valstr); if(ajStrGetLen(*valstr)) { if(!ajStrMatchCaseC(*valstr, "default")) if(!ajStrToInt(*valstr, result)) { acdErrorAcd(thys, "Bad associated qualifier " "integer value -%s = %S\n", qual, *valstr); } acdLog("return value %B '%S'\n", ajTrue, *valstr); return ajTrue; } } *result = defval; if(!defval) ajStrAssignC(valstr, "end"); else ajStrFromInt(valstr, defval); acdLog("return default %B '%S'\n", ajFalse, *valstr); return ajFalse; } ID acdAttrToBoolTest TY static MO ajacd LB acd XX DE Resolves and tests an attribute string. If it has a boolean value, returns DE true and sets the value. Otherwise returns false and the default value. DE DE Has to take care to test for variables, as their values are not yet DE set when this is called. XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attr const char* PD Attribute name PX PN [3] PA r defval AjBool PD Default value PX PN [4] PA w result AjBool* PD Resulting value. PX RT AjBool RD ajTrue if a value was defined, ajFalse if the RD default value was used. RX // static AjBool acdAttrToBoolTest(const AcdPAcd thys, const char *attr, AjBool defval, AjBool *result) { if(acdVarTest(acdAttrValue(thys, attr))) /* test calcparam.acd */ acdErrorAcd(thys, "'%s' attribute cannot use a variable. " "It is used to define " "the command line before values have been set", attr); return acdAttrToBool(thys, attr, defval, result); } ID acdAttrToBool TY static MO ajacd LB acd XX DE Resolves and tests an attribute string. If it has a boolean value, returns DE true and sets the value. Otherwise returns false and the default value. XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attr const char* PD Attribute name PX PN [3] PA r defval AjBool PD Default value PX PN [4] PA w result AjBool* PD Resulting value. PX RT AjBool RD ajTrue if a value was defined, ajFalse if the RD default value was used. RX // static AjBool acdAttrToBool(const AcdPAcd thys, const char *attr, AjBool defval, AjBool *result) { ajint i; acdAttrResolve(thys, attr, &acdAttrValTmp); if(ajStrGetLen(acdAttrValTmp)) { if(ajStrToBool(acdAttrValTmp, result)) { ajStrDelStatic(&acdAttrValTmp); return ajTrue; } if(ajStrToInt(acdAttrValTmp, &i)) { if(i) *result = ajTrue; else *result = ajFalse; ajStrDelStatic(&acdAttrValTmp); return ajTrue; } else { acdErrorAcd(thys, "%S: Bad attribute boolean value %s = %S\n", thys->Name, attr, acdAttrValTmp); } } *result = defval; ajStrDelStatic(&acdAttrValTmp); return ajFalse; } ID acdAttrToDouble TY static MO ajacd LB acd XX DE Resolves and tests an attribute string. If it has a float value, returns DE true and sets the value. Otherwise returns false and the default value. XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attr const char* PD Attribute name PX PN [3] PA r defval double PD Default value PX PN [4] PA w result double* PD Resulting value. PX RT AjBool RD ajTrue if a value was defined, ajFalse if the RD default value was used. RX // static AjBool acdAttrToDouble(const AcdPAcd thys, const char *attr, double defval, double *result) { acdAttrResolve(thys, attr, &acdAttrValTmp); if(ajStrGetLen(acdAttrValTmp)) { if(ajStrToDouble(acdAttrValTmp, result)) { ajStrDelStatic(&acdAttrValTmp); return ajTrue; } else { acdErrorAcd(thys, "Bad attribute double value %s = %S\n", attr, acdAttrValTmp); } } *result = defval; ajStrDelStatic(&acdAttrValTmp); return ajFalse; } ID acdAttrToFloat TY static MO ajacd LB acd XX DE Resolves and tests an attribute string. If it has a float value, returns DE true and sets the value. Otherwise returns false and the default value. XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attr const char* PD Attribute name PX PN [3] PA r defval float PD Default value PX PN [4] PA w result float* PD Resulting value. PX RT AjBool RD ajTrue if a value was defined, ajFalse if the RD default value was used. RX // static AjBool acdAttrToFloat(const AcdPAcd thys, const char *attr, float defval, float *result) { acdAttrResolve(thys, attr, &acdAttrValTmp); if(ajStrGetLen(acdAttrValTmp)) { if(ajStrToFloat(acdAttrValTmp, result)) { ajStrDelStatic(&acdAttrValTmp); return ajTrue; } else { acdErrorAcd(thys, "Bad attribute float value %s = %S\n", attr, acdAttrValTmp); } } *result = defval; ajStrDelStatic(&acdAttrValTmp); return ajFalse; } ID acdAttrToInt TY static MO ajacd LB acd XX DE Resolves and tests an attribute string. If it has an integer value, returns DE true and sets the value. Otherwise returns false and the default value. XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attr const char* PD Attribute name PX PN [3] PA r defval ajint PD Default value PX PN [4] PA w result ajint* PD Resulting value. PX RT AjBool RD ajTrue if a value was defined, ajFalse if the RD default value was used. RX // static AjBool acdAttrToInt(const AcdPAcd thys, const char *attr, ajint defval, ajint *result) { acdAttrResolve(thys, attr, &acdAttrValTmp); if(ajStrGetLen(acdAttrValTmp)) { if(ajStrToInt(acdAttrValTmp, result)) { ajStrDelStatic(&acdAttrValTmp); return ajTrue; } else acdErrorAcd(thys, "Bad attribute integer value %s = %S\n", attr, acdAttrValTmp); } *result = defval; ajStrDelStatic(&acdAttrValTmp); return ajFalse; } ID acdAttrToLong TY static MO ajacd LB acd XX DE Resolves and tests an attribute string. If it has an integer value, returns DE true and sets the value. Otherwise returns false and the default value. XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attr const char* PD Attribute name PX PN [3] PA r defval ajlong PD Default value PX PN [4] PA w result ajlong* PD Resulting value. PX RT AjBool RD ajTrue if a value was defined, ajFalse if the RD default value was used. RX // static AjBool acdAttrToLong(const AcdPAcd thys, const char *attr, ajlong defval, ajlong *result) { acdAttrResolve(thys, attr, &acdAttrValTmp); if(ajStrGetLen(acdAttrValTmp)) { if(ajStrToLong(acdAttrValTmp, result)) { ajStrDelStatic(&acdAttrValTmp); return ajTrue; } else acdErrorAcd(thys, "Bad attribute long integer value %s = %S\n", attr, acdAttrValTmp); } *result = defval; ajStrDelStatic(&acdAttrValTmp); return ajFalse; } ID acdAttrToStr TY static MO ajacd LB acd XX DE Resolves an attribute to a string with translation of variable name(s). XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attr const char* PD Attribute name PX PN [3] PA r defval const char* PD Default value PX PN [4] PA w result AjPStr* PD Resulting value. PX RT AjBool RD ajTrue if a value was defined, ajFalse if the RD default value was used. RX // static AjBool acdAttrToStr(const AcdPAcd thys, const char *attr, const char* defval, AjPStr *result) { if(acdAttrResolve(thys, attr, result)) return ajTrue; ajStrAssignC(result, defval); return ajFalse; } ID acdAttrToChar TY static MO ajacd LB acd XX DE Resolves an attribute to a character with translation of variable name(s). XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attr const char* PD Attribute name PX PN [3] PA r defval char PD Default value PX PN [4] PA w result char* PD Resulting value. PX RT AjBool RD ajTrue if a value was defined, ajFalse if the RD default value was used. RX // static AjBool acdAttrToChar(const AcdPAcd thys, const char *attr, char defval, char* result) { static AjPStr str = NULL; acdAttrResolve(thys, attr, &str); if(ajStrGetLen(str)) { *result = ajStrGetCharFirst(str); return ajTrue; } *result = defval; return ajFalse; } ID acdAttrToUint TY static MO ajacd LB acd XX DE Resolves and tests an attribute string. If it has an integer value, returns DE true and sets the value. Otherwise returns false and the default value. XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attr const char* PD Attribute name PX PN [3] PA r defval ajuint PD Default value PX PN [4] PA w result ajuint* PD Resulting value. PX RT AjBool RD ajTrue if a value was defined, ajFalse if the RD default value was used. RX // static AjBool acdAttrToUint(const AcdPAcd thys, const char *attr, ajuint defval, ajuint *result) { acdAttrResolve(thys, attr, &acdAttrValTmp); if(ajStrGetLen(acdAttrValTmp)) { if(ajStrToUint(acdAttrValTmp, result)) { ajStrDelStatic(&acdAttrValTmp); return ajTrue; } else acdErrorAcd(thys, "Bad attribute unsigned integer value %s = %S\n", attr, acdAttrValTmp); } *result = defval; ajStrDelStatic(&acdAttrValTmp); return ajFalse; } ID acdAttrResolve TY static MO ajacd LB acd XX DE Resolves an attribute to a string with translation of variable name(s). XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attr const char* PD Attribute name PX PN [3] PA w result AjPStr* PD Resulting value. PX RT AjBool RD ajTrue if a value was defined, RD ajFalse if an empty string is returned. RX // static AjBool acdAttrResolve(const AcdPAcd thys, const char *attr, AjPStr *result) { ajStrAssignS(result, acdAttrValue(thys, attr)); acdVarResolve(result); if(ajStrGetLen(*result)) return ajTrue; return ajFalse; } ID acdVarTest TY static MO ajacd LB acd XX DE tests for any variable (but not function) references in a string. DE DE Used to check whether variables might be used before we have set DE their values.. XX PN [1] PA r var const AjPStr PD String value PX RT AjBool RD ajTrue if a variable was found RX // static AjBool acdVarTest(const AjPStr var) { if(!acdRegVarInit) acdRegVarDefine(); if(ajRegExec(acdRegVarname, var)) return ajTrue; return ajFalse; } ID acdVarTestValid TY static MO ajacd LB acd XX DE Tests for any variable (but not function) references in a string. DE DE Used by acdvalid to check for dependencies that are too complex XX PN [1] PA r var const AjPStr PD String value PX PN [2] PA w toggle AjBool* PD ajTrue if the value is a simple true or false PD test of a toggle variable PX RT AjBool RD ajTrue if a variable was found RX // static AjBool acdVarTestValid(const AjPStr var, AjBool* toggle) { AjPStr varref = NULL; AjPStr varname = NULL; AcdPAcd acd = NULL; if(!acdRegVarInit) acdRegVarDefine(); *toggle = ajFalse; if(!ajRegExec(acdRegVarname, var)) return ajFalse; /* ** A variable - is it a simple (toggle) dependency? ** Toggles can be $(varname) or \@($(varname)) or \@(!$(varname)) ** Also allowed is automatic variable acdprotein */ acdLog("acdVarTestValid variable '%S'\n", var); if(ajRegExec(acdRegToggle, var)) { ajRegSubI(acdRegToggle, 2, &varref); /* returns $(varname) */ if(acdVarSimple(varref, &varname)) { acd = acdFindAcd(varname, varname); if (acd) { acdLog("acdVarTestValid varname %S acd %S type %s\n", varname, acd->Name, acdType[acd->Type].Name); if(!strcmp(acdType[acd->Type].Name, "toggle")) *toggle = ajTrue; if(ajStrMatchCaseC(varname, "acdprotein")) *toggle = ajTrue; } else acdLog("acdVarTestValid varname %S not found\n", varname); } acdLog("varname '%S' toggle %B\n", varname, *toggle); ajStrDel(&varref); ajStrDel(&varname); } return ajTrue; } ID acdVarSimple TY static MO ajacd LB acd XX DE Tests a variable reference is non recursive XX PN [1] PA r var const AjPStr PD String value to be tested PX PN [2] PA w varname AjPStr* PD Variable name if found, else unchanged PX RT AjBool RD ajTrue if no further variable or function is found RX // static AjBool acdVarSimple(const AjPStr var, AjPStr* varname) { AjPStr attrname = NULL; AjPStr result = NULL; AjPStr token = NULL; AjPStr newvar = NULL; AjPStr restvar = NULL; AjBool ret = ajTrue; if(!acdRegVarInit) acdRegVarDefine(); if(ajRegExec(acdRegVarname, var)) { ajRegSubI(acdRegVarname, 2, &token); /* variable name */ acdVarSplit(token, varname, &attrname); if(!ajStrGetLen(attrname)) ajStrAssignC(&attrname, "default"); if(!acdGetAttr(&result, *varname, attrname)) { acdLog("acdVarSimple failed to resolve '%S.%S\n", *varname, attrname); ajStrAssignClear(&result); } ajRegSubI(acdRegVarname, 1, &newvar); ajStrAppendS(&newvar, result); if(ajRegPost(acdRegVarname, &restvar)) /* any more? */ ajStrAppendS(&newvar, restvar); acdLog("acdVarSimple name %S resolved to '%S'\n", *varname, newvar); if(ajRegExec(acdRegVarname, newvar)) ret = ajFalse; else if(ajRegExec(acdRegFunction, newvar)) ret = ajFalse; } /* else no variable reference at found */ ajStrDel(&token); ajStrDel(&attrname); ajStrDel(&result); ajStrDel(&newvar); ajStrDel(&restvar); return ret; } ID acdVarSimpleResolve TY static MO ajacd LB acd XX DE Tests a variable reference is a simple copy and resolves XX PN [1] PA r var const AjPStr PD String value to be tested PX PN [2] PA w newvar AjPStr* PD Substituted value if found, else unchanged PX RT AjBool RD ajTrue if no further variable or function is found RX // static AjBool acdVarSimpleResolve(const AjPStr var, AjPStr* newvar) { AjPStr attrname = NULL; AjPStr result = NULL; AjPStr token = NULL; AjPStr varname = NULL; AjPStr restvar = NULL; AjBool ret = ajTrue; AjPStr tmpvar = NULL; if(!acdRegVarInit) acdRegVarDefine(); if(ajRegExec(acdRegVarname, var)) { tmpvar = ajStrNewS(*newvar); ajRegSubI(acdRegVarname, 2, &token); /* variable name */ acdVarSplit(token, &varname, &attrname); if(!ajStrGetLen(attrname)) ajStrAssignC(&attrname, "default"); if(!acdGetAttr(&result, varname, attrname)) { acdLog("acdVarSimpleResolve failed to resolve '%S.%S\n", varname, attrname); if(ajStrMatchC(attrname, "begin")) ajStrAssignC(&result, "0"); else if(ajStrMatchC(attrname, "end")) ajStrAssignC(&result, "0"); else ajStrAssignClear(&result); } ajRegSubI(acdRegVarname, 1, newvar); ajStrAppendS(newvar, result); if(ajRegPost(acdRegVarname, &restvar)) /* any more? */ ajStrAppendS(newvar, restvar); acdLog("acdVarSimpleResolve name %S resolved to '%S'\n", varname, *newvar); if(ajRegExec(acdRegVarname, *newvar)) ret = ajFalse; else if(ajRegExec(acdRegFunction, *newvar)) ret = ajFalse; if(!ret) ajStrAssignS(newvar, tmpvar); ajStrDel(&tmpvar); } /* else no variable reference at found */ ajStrDel(&token); ajStrDel(&attrname); ajStrDel(&result); ajStrDel(&varname); ajStrDel(&restvar); return ret; } ID acdVarResolve TY static MO ajacd LB acd XX DE Resolves any variable or function references in a string. DE DE First resolves variables in the form $(name) or $(name.attribute). DE This cunningly resolves internal () pairs. DE DE Then looks for function references and resolves them. XX PN [1] PA w var AjPStr* PD String value PX RT AjBool RD Always ajTrue so far RX // static AjBool acdVarResolve(AjPStr* var) { AjPStr varname = NULL; AjPStr attrname = NULL; AjPStr result = NULL; AjPStr token = NULL; AjPStr newvar = NULL; AjPStr restvar = NULL; AjPStr savein = NULL; ajint ivar = 0; ajint ifun = 0; if(!acdRegResolveVar) acdRegResolveVar = ajRegCompC("^(.*)\\$\\(([a-zA-Z0-9_.]+)\\)"); if(!acdRegResolveFun) acdRegResolveFun = ajRegCompC("^(.*)\\@\\(([^()]+)\\)"); /* resolve variable references first to resolve internal parentheses */ if(!var) { ajStrAssignClear(var); return ajTrue; } if(!ajStrGetLen(*var)) return ajTrue; ajStrAssignS(&savein, *var); acdLog("acdVarResolve '%S'\n", savein); while(ajRegExec(acdRegResolveVar, *var)) { ivar++; ajRegSubI(acdRegResolveVar, 2, &token); /* variable name */ acdVarSplit(token, &varname, &attrname); if(!acdGetAttr(&result, varname, attrname)) ajStrAssignClear(&result); ajRegSubI(acdRegResolveVar, 1, &newvar); ajStrAppendS(&newvar, result); if(ajRegPost(acdRegResolveVar, &restvar)) /* any more? */ ajStrAppendS(&newvar, restvar); ajStrAssignS(var, newvar); acdLog("... name %S resolved to '%S'\n", varname, newvar); } /* now resolve any function */ while(ajRegExec(acdRegResolveFun, *var)) { ifun++; ajRegSubI(acdRegResolveFun, 2, &token); /* function statement */ acdFunResolve(&result, token); ajRegSubI(acdRegResolveFun, 1, &newvar); ajStrAppendS(&newvar, result); if(ajRegPost(acdRegResolveFun, &restvar)) /* any more? */ ajStrAppendS(&newvar, restvar); ajStrAssignS(var, newvar); acdLog("... function %S resolved to '%S'\n", token, newvar); } if(ivar > 1) acdLog("Recursive variables in '%S'\n", savein); if(ifun > 1) acdLog("Recursive expressions in '%S'\n", savein); if(acdDoTrace) { if(ifun || ivar) ajUser("Trace: resolved '%S' => '%S'", savein, *var); } ajStrDel(&savein); ajStrDel(&result); ajStrDel(&varname); ajStrDel(&newvar); ajStrDel(&restvar); ajStrDel(&token); ajStrDel(&attrname); return ajTrue; } ID acdHelpVarResolve TY static MO ajacd LB acd XX DE Resolves any variable or function references in a string if clearly DE defined, otherwise returns ajFalse and sets the string to "". For DE use with strings that cannot be resolved in help processing DE because of functions, variable dependencies etc. XX PN [1] PA w str AjPStr* PD String value PX PN [2] PA r var const AjPStr PD Source string value PX RT AjBool RD ajTrue if it could be resolved cleanly RX // static AjBool acdHelpVarResolve(AjPStr* str, const AjPStr var) { if(!acdRegVarInit) acdRegVarDefine(); if(!var) { ajStrAssignClear(str); return ajTrue; } /* reject variable references first to resolve internal parentheses */ if(ajRegExec(acdRegVarname, var)) { ajStrAssignClear(str); return ajFalse; } /* reject any function */ if(ajRegExec(acdRegFunction, var)) { ajStrAssignClear(str); return ajFalse; } ajStrAssignS(str, var); return ajTrue; } ID acdFunResolve TY static MO ajacd LB acd XX DE Resolves a function reference. DE DE Has a list of all accepted function syntax. XX PN [1] PA w result AjPStr* PD Result returned PX PN [2] PA r str const AjPStr PD Function statement input PX RT AjBool RD Always ajTrue so far RX // static AjBool acdFunResolve(AjPStr* result, const AjPStr str) { ajint i; acdLog("acdFunResolve '%S'\n", str); for(i = 0; explist[i].Name; i++) { /* Calling funclist acdexplist() */ if((*explist[i].Func)(result, str)) { acdLog("resolved '%S' using '%s'\n", str, explist[i].Name); acdLog(" result '%S'\n", *result); return ajTrue; } } ajWarn("ACD expression invalid @(%S)", str); acdLog("@(%S) *failed**\n", str); ajStrAssignS(result, str); return ajFalse; } ID acdExpPlus TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpPlus(AjPStr* result, const AjPStr str) { ajint ia, ib; double da, db; if(!acdRegExpPlusI) /* ajint + ajint */ acdRegExpPlusI = ajRegCompC("^[ \t]*([0-9+-]+)[ \t]*[+][ \t]*" "([0-9+-]+)[ \t]*$"); if(ajRegExec(acdRegExpPlusI, str)) { acdLog("acdRegExpPlusI matched '%S'\n", str); ajRegSubI(acdRegExpPlusI, 1, &acdTmpStr); ajStrToInt(acdTmpStr, &ia); ajRegSubI(acdRegExpPlusI, 2, &acdTmpStr); ajStrToInt(acdTmpStr, &ib); ajFmtPrintS(result, "%d", ia+ib); acdLog("ia: %d + ib: %d = '%S'\n", ia, ib, *result); return ajTrue; } if(!acdRegExpPlusD) /* float + float */ acdRegExpPlusD = ajRegCompC("^[ \t]*([0-9.+-]+)[ \t]*[+][ \t]*" "([0-9.+-]+)[ \t]*$"); if(ajRegExec(acdRegExpPlusD, str)) { acdLog("dexp matched '%S'\n", str); ajRegSubI(acdRegExpPlusD, 1, &acdTmpStr); ajStrToDouble(acdTmpStr, &da); ajRegSubI(acdRegExpPlusD, 2, &acdTmpStr); ajStrToDouble(acdTmpStr, &db); ajFmtPrintS(result, "%f", da+db); acdLog("da: %f + db: %f = '%S'\n", da, db, *result); return ajTrue; } return ajFalse; } ID acdExpMinus TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpMinus(AjPStr* result, const AjPStr str) { ajint ia, ib; double da, db; if(!acdRegExpMinusI) /* ajint + ajint */ acdRegExpMinusI = ajRegCompC("^[ \t]*([0-9+-]+)[ \t]*[-][ \t]*" "([0-9+-]+)[ \t]*$"); if(ajRegExec(acdRegExpMinusI, str)) { acdLog("acdRegExpMinusI matched '%S'\n", str); ajRegSubI(acdRegExpMinusI, 1, &acdTmpStr); ajStrToInt(acdTmpStr, &ia); ajRegSubI(acdRegExpMinusI, 2, &acdTmpStr); ajStrToInt(acdTmpStr, &ib); ajFmtPrintS(result, "%d", ia-ib); acdLog("ia: %d - ib: %d = '%S'\n", ia, ib, *result); return ajTrue; } if(!acdRegExpMinusD) /* float + float */ acdRegExpMinusD = ajRegCompC("^[ \t]*([0-9.+-]+)[ \t]*[-][ \t]*" "([0-9.+-]+)[ \t]*$"); if(ajRegExec(acdRegExpMinusD, str)) { acdLog("acdRegExpMinusD matched '%S'\n", str); ajRegSubI(acdRegExpMinusD, 1, &acdTmpStr); ajStrToDouble(acdTmpStr, &da); ajRegSubI(acdRegExpMinusD, 2, &acdTmpStr); ajStrToDouble(acdTmpStr, &db); ajFmtPrintS(result, "%f", da-db); acdLog("da: %f - db: %f = '%S'\n", da, db, *result); return ajTrue; } return ajFalse; } ID acdExpStar TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpStar(AjPStr* result, const AjPStr str) { ajint ia, ib; double da, db; if(!acdRegExpStarI) /* ajint + ajint */ acdRegExpStarI = ajRegCompC("^[ \t]*([0-9+-]+)[ \t]*[*][ \t]*" "([0-9+-]+)[ \t]*$"); if(ajRegExec(acdRegExpStarI, str)) { acdLog("acdRegExpStarI matched '%S'\n", str); ajRegSubI(acdRegExpStarI, 1, &acdTmpStr); ajStrToInt(acdTmpStr, &ia); ajRegSubI(acdRegExpStarI, 2, &acdTmpStr); ajStrToInt(acdTmpStr, &ib); ajFmtPrintS(result, "%d", ia*ib); acdLog("ia: %d * ib: %d = '%S'\n", ia, ib, *result); return ajTrue; } if(!acdRegExpStarD) /* float + float */ acdRegExpStarD = ajRegCompC("^[ \t]*([0-9.+-]+)[ \t]*[*][ \t]*" "([0-9.+-]+)[ \t]*$"); if(ajRegExec(acdRegExpStarD, str)) { acdLog("acdRegExpStarD matched '%S'\n", str); ajRegSubI(acdRegExpStarD, 1, &acdTmpStr); ajStrToDouble(acdTmpStr, &da); ajRegSubI(acdRegExpStarD, 2, &acdTmpStr); ajStrToDouble(acdTmpStr, &db); ajFmtPrintS(result, "%f", da*db); acdLog("da: %f * db: %f = '%S'\n", da, db, *result); return ajTrue; } return ajFalse; } ID acdExpDiv TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpDiv(AjPStr* result, const AjPStr str) { ajint ia; ajint ib; double da; double db; if(!acdRegExpDivI) /* ajint + ajint */ acdRegExpDivI = ajRegCompC("^[ \t]*([0-9+-]+)[ \t]*[/][ \t]*" "([0-9+-]+)[ \t]*$"); if(ajRegExec(acdRegExpDivI, str)) { acdLog("acdRegExpDivI matched '%S'\n", str); ajRegSubI(acdRegExpDivI, 1, &acdTmpStr); ajStrToInt(acdTmpStr, &ia); ajRegSubI(acdRegExpDivI, 2, &acdTmpStr); ajStrToInt(acdTmpStr, &ib); ajFmtPrintS(result, "%d", ia/ib); acdLog("ia: %d / ib: %d = '%S'\n", ia, ib, *result); return ajTrue; } if(!acdRegExpDivD) /* float + float */ acdRegExpDivD = ajRegCompC("^[ \t]*([0-9.+-]+)[ \t]*[/][ \t]*" "([0-9.+-]+)[ \t]*$"); if(ajRegExec(acdRegExpDivD, str)) { acdLog("acdRegExpDivD matched '%S'\n", str); ajRegSubI(acdRegExpDivD, 1, &acdTmpStr); ajStrToDouble(acdTmpStr, &da); ajRegSubI(acdRegExpDivD, 2, &acdTmpStr); ajStrToDouble(acdTmpStr, &db); ajFmtPrintS(result, "%f", da/db); acdLog("da: %f / db: %f = '%S'\n", da, db, *result); return ajTrue; } return ajFalse; } ID acdExpNot TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpNot(AjPStr* result, const AjPStr str) { AjBool ba; if(!acdRegExpNot) /* ajint + ajint */ acdRegExpNot = ajRegCompC("^[ \t]*(!|[Nn][Oo][Tt])" "[ \t]*([A-Za-z0-9]+)[ \t]*$"); if(ajRegExec(acdRegExpNot, str)) { acdLog("nexp matched '%S'\n", str); ajRegSubI(acdRegExpNot, 2, &acdTmpStr); if(!ajStrToBool(acdTmpStr, &ba)) { acdLog("invalid bool value '%S' in acdExpNot\n", acdTmpStr); ba = ajFalse; } if(ba) ajFmtPrintS(result, "%b", ajFalse); else ajFmtPrintS(result, "%b", ajTrue); acdLog("ta: ! '%S' = '%S'\n", acdTmpStr, *result); return ajTrue; } return ajFalse; } ID acdExpEqual TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpEqual(AjPStr* result, const AjPStr str) { ajint ia; ajint ib; double da; double db; if(!acdRegExpEqualI) /* ajint + ajint */ acdRegExpEqualI = ajRegCompC("^[ \t]*([0-9+-]+)[ \t]*[=][=][ \t]*" "([0-9+-]+)[ \t]*$"); if(ajRegExec(acdRegExpEqualI, str)) { acdLog("acdRegExpEqualI matched '%S'\n", str); ajRegSubI(acdRegExpEqualI, 1, &acdTmpStr); ajStrToInt(acdTmpStr, &ia); ajRegSubI(acdRegExpEqualI, 2, &acdTmpStr); ajStrToInt(acdTmpStr, &ib); if(ia == ib) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ia: %d == ib: %d = '%S'\n", ia, ib, *result); return ajTrue; } if(!acdRegExpEqualD) /* float == float */ acdRegExpEqualD = ajRegCompC("^[ \t]*([0-9.+-]+)[ \t]*[=][=][ \t]*" "([0-9.+-]+)[ \t]*$"); if(ajRegExec(acdRegExpEqualD, str)) { acdLog("acdRegExpEqualD matched '%S'\n", str); ajRegSubI(acdRegExpEqualD, 1, &acdTmpStr); ajStrToDouble(acdTmpStr, &da); ajRegSubI(acdRegExpEqualD, 2, &acdTmpStr); ajStrToDouble(acdTmpStr, &db); if(da == db) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("da: %f == db: %f = '%S'\n", da, db, *result); return ajTrue; } if(!acdRegExpEqualT) /* string == string */ acdRegExpEqualT = ajRegCompC("^[ \t]*([^ \t]+)[ \t]*[=][=][ \t]*" "([^ \t{}]+)[ \t]*$"); /* for {} see acdExpOneof */ if(ajRegExec(acdRegExpEqualT, str)) { acdLog("acdRegExpEqualT matched '%S'\n", str); ajRegSubI(acdRegExpEqualT, 1, &acdTmpStr); ajRegSubI(acdRegExpEqualT, 2, &acdTmpStr2); if(ajStrMatchCaseS(acdTmpStr, acdTmpStr2)) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ta: '%S' == tb: '%S' = '%S'\n", acdTmpStr, acdTmpStr2, *result); return ajTrue; } return ajFalse; } ID acdExpNotEqual TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpNotEqual(AjPStr* result, const AjPStr str) { ajint ia; ajint ib; double da; double db; if(!acdRegExpNeI) /* int != int */ acdRegExpNeI = ajRegCompC("^[ \t]*([0-9+-]+)[ \t]*[!][=][ \t]*" "([0-9+-]+)[ \t]*$"); if(ajRegExec(acdRegExpNeI, str)) { acdLog("acdRegExpNeI matched '%S'\n", str); ajRegSubI(acdRegExpNeI, 1, &acdTmpStr); ajStrToInt(acdTmpStr, &ia); ajRegSubI(acdRegExpNeI, 2, &acdTmpStr); ajStrToInt(acdTmpStr, &ib); if(ia != ib) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ia: %d != ib: %d = '%S'\n", ia, ib, *result); return ajTrue; } if(!acdRegExpNeD) /* float != float */ acdRegExpNeD = ajRegCompC("^[ \t]*([0-9.+-]+)[ \t]*[!][=][ \t]*" "([0-9.+-]+)[ \t]*$"); if(ajRegExec(acdRegExpNeD, str)) { acdLog("acdRegExpNeD matched '%S'\n", str); ajRegSubI(acdRegExpNeD, 1, &acdTmpStr); ajStrToDouble(acdTmpStr, &da); ajRegSubI(acdRegExpNeD, 2, &acdTmpStr); ajStrToDouble(acdTmpStr, &db); if(da != db) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("da: %f != db: %f = '%S'\n", da, db, *result); return ajTrue; } if(!acdRegExpNeT) /* string != string*/ acdRegExpNeT = ajRegCompC("^[ \t]*([^ \t]+)[ \t]*[!][=][ \t]*" "([^ \t{}]+)[ \t]*$"); /* for {} see acdExpOneof */ if(ajRegExec(acdRegExpNeT, str)) { acdLog("acdRegExpNeT matched '%S'\n", str); ajRegSubI(acdRegExpNeT, 1, &acdTmpStr); ajRegSubI(acdRegExpNeT, 2, &acdTmpStr2); if(!ajStrMatchCaseS(acdTmpStr, acdTmpStr2)) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ta: '%S' != tb: '%S' = '%S'\n", acdTmpStr, acdTmpStr2, *result); return ajTrue; } return ajFalse; } ID acdExpGreater TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpGreater(AjPStr* result, const AjPStr str) { ajint ia; ajint ib; double da; double db; if(!acdRegExpGtI) /* ajint + ajint */ acdRegExpGtI = ajRegCompC("^[ \t]*([0-9+-]+)[ \t]*[>][ \t]*" "([0-9+-]+)[ \t]*$"); if(ajRegExec(acdRegExpGtI, str)) { acdLog("acdRegExpGtI matched '%S'\n", str); ajRegSubI(acdRegExpGtI, 1, &acdTmpStr); ajStrToInt(acdTmpStr, &ia); ajRegSubI(acdRegExpGtI, 2, &acdTmpStr); ajStrToInt(acdTmpStr, &ib); if(ia > ib) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ia: %d > ib: %d = '%S'\n", ia, ib, *result); return ajTrue; } if(!acdRegExpGtD) /* float + float */ acdRegExpGtD = ajRegCompC("^[ \t]*([0-9.+-]+)[ \t]*[>][ \t]*" "([0-9.+-]+)[ \t]*$"); if(ajRegExec(acdRegExpGtD, str)) { acdLog("acdRegExpGtD matched '%S'\n", str); ajRegSubI(acdRegExpGtD, 1, &acdTmpStr); ajStrToDouble(acdTmpStr, &da); ajRegSubI(acdRegExpGtD, 2, &acdTmpStr); ajStrToDouble(acdTmpStr, &db); if(da > db) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("da: %f > db: %f = '%S'\n", da, db, *result); return ajTrue; } if(!acdRegExpGtT) /* float + float */ acdRegExpGtT = ajRegCompC("^[ \t]*([^ \t]+)[ \t]*[>][ \t]*" "([^ \t]+)[ \t]*$"); if(ajRegExec(acdRegExpGtT, str)) { acdLog("acdRegExpGtT matched '%S'\n", str); ajRegSubI(acdRegExpGtT, 1, &acdTmpStr); ajRegSubI(acdRegExpGtT, 2, &acdTmpStr2); if(0 > ajStrCmpCaseS(acdTmpStr2, acdTmpStr)) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ta: '%S' > tb: '%S' = '%S'\n", acdTmpStr, acdTmpStr2, *result); return ajTrue; } return ajFalse; } ID acdExpLesser TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpLesser(AjPStr* result, const AjPStr str) { ajint ia; ajint ib; double da; double db; if(!acdRegExpLtI) /* ajint + ajint */ acdRegExpLtI = ajRegCompC("^[ \t]*([0-9+-]+)[ \t]*[<][ \t]*" "([0-9+-]+)[ \t]*$"); if(ajRegExec(acdRegExpLtI, str)) { acdLog("acdRegExpLtI matched '%S'\n", str); ajRegSubI(acdRegExpLtI, 1, &acdTmpStr); ajStrToInt(acdTmpStr, &ia); ajRegSubI(acdRegExpLtI, 2, &acdTmpStr); ajStrToInt(acdTmpStr, &ib); if(ia < ib) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ia: %d < ib: %d = '%S'\n", ia, ib, *result); return ajTrue; } if(!acdRegExpLtD) /* float + float */ acdRegExpLtD = ajRegCompC("^[ \t]*([0-9.+-]+)[ \t]*[<][ \t]*" "([0-9.+-]+)[ \t]*$"); if(ajRegExec(acdRegExpLtD, str)) { acdLog("acdRegExpLtD matched '%S'\n", str); ajRegSubI(acdRegExpLtD, 1, &acdTmpStr); ajStrToDouble(acdTmpStr, &da); ajRegSubI(acdRegExpLtD, 2, &acdTmpStr); ajStrToDouble(acdTmpStr, &db); if(da < db) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("da: %f < db: %f = '%S'\n", da, db, *result); return ajTrue; } if(!acdRegExpLtT) /* float + float */ acdRegExpLtT = ajRegCompC("^[ \t]*([^ \t]+)[ \t]*[<][ \t]*" "([^ \t]+)[ \t]*$"); if(ajRegExec(acdRegExpLtT, str)) { acdLog("acdRegExpLtT matched '%S'\n", str); ajRegSubI(acdRegExpLtT, 1, &acdTmpStr); ajRegSubI(acdRegExpLtT, 2, &acdTmpStr2); if(0 < ajStrCmpCaseS(acdTmpStr2, acdTmpStr)) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ta: '%S' < tb: '%S' = '%S'\n", acdTmpStr, acdTmpStr2, *result); return ajTrue; } return ajFalse; } ID acdExpOr TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpOr(AjPStr* result, const AjPStr str) { ajint ia; ajint ib; double da; double db; AjBool ba; AjBool bb; if(!acdRegExpOrI) /* ajint + ajint */ acdRegExpOrI = ajRegCompC("^[ \t]*([0-9+-]+)[ \t]*[|][ \t]*" "([0-9+-]+)[ \t]*$"); if(ajRegExec(acdRegExpOrI, str)) { acdLog("acdRegExpOrI matched '%S'\n", str); ajRegSubI(acdRegExpOrI, 1, &acdTmpStr); ajStrToInt(acdTmpStr, &ia); ajRegSubI(acdRegExpOrI, 2, &acdTmpStr); ajStrToInt(acdTmpStr, &ib); if(ia || ib) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ia: %d | ib: %d = '%S'\n", ia, ib, *result); return ajTrue; } if(!acdRegExpOrD) /* float + float */ acdRegExpOrD = ajRegCompC("^[ \t]*([0-9.+-]+)[ \t]*[|][ \t]*" "([0-9.+-]+)[ \t]*$"); if(ajRegExec(acdRegExpOrD, str)) { acdLog("acdRegExpOrD matched '%S'\n", str); ajRegSubI(acdRegExpOrD, 1, &acdTmpStr); ajStrToDouble(acdTmpStr, &da); ajRegSubI(acdRegExpOrD, 2, &acdTmpStr); ajStrToDouble(acdTmpStr, &db); if(da || db) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("da: %f | db: %f = '%S'\n", da, db, *result); return ajTrue; } if(!acdRegExpOrT) /* char + char */ acdRegExpOrT = ajRegCompC("^[ \t]*([^ \t]+)[ \t]*[|][ \t]*" "([^ \t]+)[ \t]*$"); if(ajRegExec(acdRegExpOrT, str)) { acdLog("acdRegExpOrT matched '%S'\n", str); ajRegSubI(acdRegExpOrT, 1, &acdTmpStr); ajRegSubI(acdRegExpOrT, 2, &acdTmpStr2); ajStrToBool(acdTmpStr2,&ba); ajStrToBool(acdTmpStr, &bb); if( ba || bb ) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ta: '%S' | tb: '%S' = '%S'\n", acdTmpStr, acdTmpStr2, *result); return ajTrue; } return ajFalse; } ID acdExpAnd TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpAnd(AjPStr* result, const AjPStr str) { ajint ia; ajint ib; double da; double db; AjBool ba; AjBool bb; if(!acdRegExpAndI) /* ajint + ajint */ acdRegExpAndI = ajRegCompC("^[ \t]*([0-9+-]+)[ \t]*[&][ \t]*" "([0-9+-]+)[ \t]*$"); if(ajRegExec(acdRegExpAndI, str)) { acdLog("acdRegExpAndI matched '%S'\n", str); ajRegSubI(acdRegExpAndI, 1, &acdTmpStr); ajStrToInt(acdTmpStr, &ia); ajRegSubI(acdRegExpAndI, 2, &acdTmpStr); ajStrToInt(acdTmpStr, &ib); if(ia && ib) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ia: %d & ib: %d = '%S'\n", ia, ib, *result); return ajTrue; } if(!acdRegExpAndD) /* float + float */ acdRegExpAndD = ajRegCompC("^[ \t]*([0-9.+-]+)[ \t]*[&][ \t]*" "([0-9.+-]+)[ \t]*$"); if(ajRegExec(acdRegExpAndD, str)) { acdLog("acdRegExpAndD matched '%S'\n", str); ajRegSubI(acdRegExpAndD, 1, &acdTmpStr); ajStrToDouble(acdTmpStr, &da); ajRegSubI(acdRegExpAndD, 2, &acdTmpStr); ajStrToDouble(acdTmpStr, &db); if(da && db) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("da: %f & db: %f = '%S'\n", da, db, *result); return ajTrue; } if(!acdRegExpAndT) /* char + char */ acdRegExpAndT = ajRegCompC("^[ \t]*([^ \t]+)[ \t]*[&][ \t]*" "([^ \t]+)[ \t]*$"); if(ajRegExec(acdRegExpAndT, str)) { acdLog("acdRegExpAndT matched '%S'\n", str); ajRegSubI(acdRegExpAndT, 1, &acdTmpStr); ajRegSubI(acdRegExpAndT, 2, &acdTmpStr2); ajStrToBool(acdTmpStr2,&ba); ajStrToBool(acdTmpStr, &bb); if( ba && bb ) ajFmtPrintS(result, "%b", ajTrue); else ajFmtPrintS(result, "%b", ajFalse); acdLog("ta: '%S' & tb: '%S' = '%S'\n", acdTmpStr, acdTmpStr2, *result); return ajTrue; } return ajFalse; } ID acdExpCond TY static MO ajacd LB acd XX DE Looks for and resolves an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpCond(AjPStr* result, const AjPStr str) { AjBool ba; if(!acdRegExpCond) /* bool ? iftrue : iffalse */ acdRegExpCond = ajRegCompC("^[ \t]*([.A-Za-z0-9+-]*)[ \t]*[?]" "[ \t]*([^: \t]+)[ \t]*[:]" "[ \t]*([^: \t]+)[ \t]*$"); if(ajRegExec(acdRegExpCond, str)) { ajRegSubI(acdRegExpCond, 1, &acdTmpStr); ajStrToBool(acdTmpStr, &ba); if(ba) ajRegSubI(acdRegExpCond, 2, result); else ajRegSubI(acdRegExpCond, 3, result); acdLog("ba: %B = '%S'\n", ba, *result); return ajTrue; } return ajFalse; } ID acdExpOneof TY static MO ajacd LB acd XX DE Looks for and resolves an expression as a test for a list of values DE \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpOneof(AjPStr* result, const AjPStr str) { AjBool todo; AjPStr testvar = NULL; AjPStr notvar = NULL; AjPStr restvar = NULL; AjPStr elsevar = NULL; if(!acdRegExpOneofCase) /* value = ( vala | valb | valc} ) */ acdRegExpOneofCase = ajRegCompC("^[ \t]*([A-Za-z0-9+-]+)[ \t]*" "([!=])[=][ \t]*[{]"); if(!acdRegExpOneofList) /* case : value */ acdRegExpOneofList = ajRegCompC("^[ \t]*([^| \t]+)[ \t]*[|}]"); if(ajRegExec(acdRegExpOneofCase, str)) { ajRegSubI(acdRegExpOneofCase, 1, &testvar); ajRegSubI(acdRegExpOneofCase, 2, ¬var); /* "!" or empty */ if(!ajRegPost(acdRegExpOneofCase, &restvar)) /* any more? */ { ajStrDel(&testvar); ajStrDel(¬var); return ajFalse; } ajStrAssignClear(&elsevar); todo = ajTrue; while(todo && ajRegExec(acdRegExpOneofList, restvar)) { ajRegSubI(acdRegExpOneofList, 1, &acdTmpStr); if(ajStrMatchS(acdTmpStr, testvar)) /* match, but did we want to find it? */ { if (ajStrGetCharFirst(notvar) == '=') ajStrAssignC(result, "Y"); else ajStrAssignC(result, "N"); ajStrDel(&testvar); ajStrDel(¬var); ajStrDel(&elsevar); ajStrDel(&restvar); return ajTrue; } todo = ajRegPost(acdRegExpOneofList, &restvar); } /* no match, but did we not want to find it? */ if (ajStrGetCharFirst(notvar) == '=') ajStrAssignC(result, "N"); else ajStrAssignC(result, "Y"); ajStrDel(&testvar); ajStrDel(¬var); ajStrDel(&elsevar); ajStrDel(&restvar); return ajTrue; } ajStrDel(&testvar); ajStrDel(¬var); ajStrDel(&elsevar); ajStrDel(&restvar); return ajFalse; } ID acdExpCase TY static MO ajacd LB acd XX DE Looks for and resolves an expression as a switch/case statement DE \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpCase(AjPStr* result, const AjPStr str) { ajint ifound; AjBool todo; static AjPStr testvar = NULL; static AjPStr restvar = NULL; static AjPStr elsevar = NULL; if(!acdRegExpCaseCase) /* value = (case : value, ...) */ acdRegExpCaseCase = ajRegCompC("^[ \t]*([A-Za-z0-9+-]+)[ \t]*[=]"); if(!acdRegExpCaseList) /* case : value */ acdRegExpCaseList = ajRegCompC("^[ \t]*([^: \t]+)[ \t]*[:]+" "[ \t]*([^: \t,]+)[ \t,]*"); if(ajRegExec(acdRegExpCaseCase, str)) { ajRegSubI(acdRegExpCaseCase, 1, &testvar); if(!ajRegPost(acdRegExpCaseCase, &restvar)) /* any more? */ { ajStrDel(&testvar); ajStrDel(&elsevar); ajStrDel(&restvar); return ajFalse; } ajStrAssignClear(&elsevar); todo = ajTrue; ifound = 0; while(todo && ajRegExec(acdRegExpCaseList, restvar)) { ajRegSubI(acdRegExpCaseList, 1, &acdTmpStr); if(ajStrMatchC(acdTmpStr, "else")) /* default */ ajRegSubI(acdRegExpCaseList, 2, &elsevar); if(ajStrMatchS(acdTmpStr, testvar)) /* match, take the value */ { ajRegSubI(acdRegExpCaseList, 2, result); acdLog("%S == %S : '%S'\n", testvar, acdTmpStr, *result); ajStrDel(&testvar); ajStrDel(&elsevar); ajStrDel(&restvar); return ajTrue; } if(ajStrPrefixS(testvar, acdTmpStr)) { ifound++; ajRegSubI(acdRegExpCaseList, 2, result); } todo = ajRegPost(acdRegExpCaseList, &restvar); } if(ifound) /* let ambiguous matches through */ { if(ifound > 1) { acdLog("@(=) ambiguous match, last match accepted %S\n", testvar); acdLog("@(=) ambiguous match, last match accepted %S\n", testvar); } acdLog("%S ~= %S : '%S'\n", testvar, acdTmpStr, *result); if (acdDoValid) acdWarn("Ambiguous case expression '%S' (%S)", testvar, *result); ajStrDel(&testvar); ajStrDel(&elsevar); ajStrDel(&restvar); return ajTrue; } if(ifound == 0) { ajStrAssignS(result, elsevar); acdLog("%S != else : '%S'\n", testvar, *result); ajStrDel(&testvar); ajStrDel(&elsevar); ajStrDel(&restvar); return ajTrue; } } ajStrDel(&testvar); ajStrDel(&elsevar); ajStrDel(&restvar); return ajFalse; } ID acdExpFilename TY static MO ajacd LB acd XX DE Looks for an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpFilename(AjPStr* result, const AjPStr str) { if(!acdRegExpFilename) /* file: name */ acdRegExpFilename = ajRegCompC("^[ \t]*[Ff][Ii][Ll][Ee]:[ \t]*([^ \t]+)[ \t]*$"); if(ajRegExec(acdRegExpFilename, str)) { acdLog("acdRegExpFilename matched '%S'\n", str); ajRegSubI(acdRegExpFilename, 1, &acdTmpStr); ajStrAssignS(result, acdTmpStr); ajFilenameTrimAll(result); ajStrFmtLower(result); acdLog("file: %S = '%S'\n", acdTmpStr, *result); return ajTrue; } return ajFalse; } ID acdExpExists TY static MO ajacd LB acd XX DE Looks for an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpExists(AjPStr* result, const AjPStr str) { AjBool test; if(!acdRegExpFileExists) /* file: name */ acdRegExpFileExists = ajRegCompC("^[ \t]*[Ii][Ss]:[ \t]*([^ \t]*)[ \t]*$"); if(ajRegExec(acdRegExpFileExists, str)) { acdLog("acdRegExpFileExists matched '%S'\n", str); ajRegSubI(acdRegExpFileExists, 1, &acdTmpStr); if(ajStrGetLen(acdTmpStr)) test = ajTrue; else test = ajFalse; ajFmtPrintS(result, "%b", test); acdLog("test: '%S' = '%S'\n", acdTmpStr, *result); return ajTrue; } return ajFalse; } ID acdExpValue TY static MO ajacd LB acd XX DE Looks for an expression \ XX PN [1] PA w result AjPStr* PD Expression result PX PN [2] PA r str const AjPStr PD String with possible expression PX RT AjBool RD ajTrue if successfully resolved RX // static AjBool acdExpValue(AjPStr* result, const AjPStr str) { if(!acdRegExpValue) /* file: name */ acdRegExpValue = ajRegCompC("^[ \t]*[Vv][Aa][Ll][Uu][Ee]:[ \t]*([^ \t]*)[ \t]*$"); if(ajRegExec(acdRegExpValue, str)) { acdLog("acdRegExpValue matched '%S'\n", str); ajRegSubI(acdRegExpValue, 1, &acdTmpStr); if(!ajNamGetValueS(acdTmpStr, result)) ajNamGetenvS(acdTmpStr, result); acdLog("test: '%S' = '%S'\n", acdTmpStr, *result); return ajTrue; } return ajFalse; } ID acdVarSplit TY static MO ajacd LB acd XX DE Splits a variable reference into name and attribute. DE Attribute is "default" if not specified XX PN [1] PA r var const AjPStr PD Variable reference PX PN [2] PA w name AjPStr* PD Variable name PX PN [3] PA w pattrname AjPStr* PD Attribute name, or "default" if not set. PX RT AjBool RD ajTrue if successfully split RX // static AjBool acdVarSplit(const AjPStr var, AjPStr* name, AjPStr* pattrname) { ajlong i; ajStrAssignS(name, var); i = ajStrFindC(*name, "."); /* qualifier with value */ if(i > 0) { ajStrAssignS(pattrname, var); ajStrKeepRange(name, 0, i-1); /* strip any value and keep testing */ ajStrCutStart(pattrname, (size_t) (i+1)); } else ajStrDelStatic(pattrname); return ajTrue; } ID acdAttrTest TY static MO ajacd LB acd XX DE Tests for the existence of a named attribute XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attrib const char* PD Attribute name PX RT AjBool RD ajTrue if the named attribute exists RX // static AjBool acdAttrTest(const AcdPAcd thys,const char *attrib) { AcdPAttr attr; AcdPAttr defattr = acdAttrDef; ajint i; if(acdIsQtype(thys)) attr = acdType[thys->Type].Attr; else attr = acdKeywords[thys->Type].Attr; i = acdFindAttrC(attr, attrib); if(i >= 0) return ajTrue; if(thys->DefStr) { i = acdFindAttrC(defattr, attrib); if(i >= 0) return ajTrue; } return ajFalse; } ID acdAttrTestDefined TY static MO ajacd LB acd XX DE Tests for the existence of a named attribute with a value XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attrib const char* PD Attribute name PX RT AjBool RD ajTrue if the named attribute exists RX // static AjBool acdAttrTestDefined(const AcdPAcd thys,const char *attrib) { AcdPAttr attr; AjPStr *attrstr; AcdPAttr defattr = acdAttrDef; AjPStr *defstr; ajint i; attrstr = thys->AttrStr; defstr = thys->DefStr; if(acdIsQtype(thys)) attr = acdType[thys->Type].Attr; else attr = acdKeywords[thys->Type].Attr; i = acdFindAttrC(attr, attrib); if(i >= 0) { if (ajStrGetLen(attrstr[i])) return ajTrue; else return ajFalse; } if(thys->DefStr) { i = acdFindAttrC(defattr, attrib); if(i >= 0) { if (ajStrGetLen(defstr[i])) return ajTrue; else return ajFalse; } } return ajFalse; } ID acdAttrTestValue TY static MO ajacd LB acd XX DE Tests for the existence of a named attribute with a simple value XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attrib const char* PD Attribute name PX RT AjBool RD ajTrue if the named attribute exists RX // static AjBool acdAttrTestValue(const AcdPAcd thys,const char *attrib) { AcdPAttr attr; AjPStr *attrstr; AcdPAttr defattr = acdAttrDef; AjPStr *defstr; ajint i; attrstr = thys->AttrStr; defstr = thys->DefStr; if(acdIsQtype(thys)) attr = acdType[thys->Type].Attr; else attr = acdKeywords[thys->Type].Attr; i = acdFindAttrC(attr, attrib); if(i >= 0) { if (ajStrGetLen(attrstr[i]) && ajStrFindAnyK(attrstr[i], '$') < 0) return ajTrue; else return ajFalse; } if(thys->DefStr) { i = acdFindAttrC(defattr, attrib); if(i >= 0) { if (ajStrGetLen(defstr[i]) && ajStrFindAnyK(defstr[i], '$') < 0) return ajTrue; else return ajFalse; } } return ajFalse; } ID acdAttrValue TY static MO ajacd LB acd XX DE Returns the string value for a named attribute XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attrib const char* PD Attribute name PX RT const AjPStr RD Attribute value. RX // static const AjPStr acdAttrValue(const AcdPAcd thys, const char *attrib) { AcdPAttr attr; AjPStr *attrstr; AcdPAttr defattr; AjPStr *defstr; ajint i; attrstr = thys->AttrStr; defattr = acdAttrDef; defstr = thys->DefStr; if(acdIsQtype(thys)) attr = acdType[thys->Type].Attr; else attr = acdKeywords[thys->Type].Attr; i = acdFindAttrC(attr, attrib); if(i >= 0) return attrstr[i]; if(thys->DefStr) { i = acdFindAttrC(defattr, attrib); if(i >= 0) return defstr[i]; } if(i < 0) acdErrorAcd(thys, "Unknown attribute '%s'\n", attrib); return NULL; } ID acdAttrValueStr TY static MO ajacd LB acd XX DE Returns the string value for a named attribute XX PN [1] PA r thys const AcdPAcd PD ACD item PX PN [2] PA r attrib const char* PD Attribute name PX PN [3] PA r def const char* PD Default value PX PN [4] PA w str AjPStr* PD Attribute value PX RT AjBool RD ajTrue if success. RX // static AjBool acdAttrValueStr(const AcdPAcd thys, const char *attrib, const char* def, AjPStr *str) { AcdPAttr attr; AjPStr *attrstr; AcdPAttr defattr; AjPStr *defstr; ajint i; attrstr = thys->AttrStr; defattr = acdAttrDef; defstr = thys->DefStr; if(acdIsQtype(thys)) attr = acdType[thys->Type].Attr; else attr = acdKeywords[thys->Type].Attr; i = acdFindAttrC(attr, attrib); if(i >= 0) { ajStrAssignS(str, attrstr[i]); if(ajStrGetLen(*str)) return ajTrue; ajStrAssignC(str, def); return ajFalse; } if(thys->DefStr) { i = acdFindAttrC(defattr, attrib); if(i >= 0) { ajStrAssignS(str, defstr[i]); if(ajStrGetLen(*str)) return ajTrue; ajStrAssignC(str, def); return ajFalse; } } if(i < 0) acdErrorAcd(thys, "Unknown attribute %s\n", attrib); return ajFalse; } ID ajAcdSetControl TY public MO ajacd LB acd XX DE Sets special qualifiers which were originally provided via the DE command line. DE DE Sets special internal variables to reflect their presence. DE DE Currently these are "acdhelp", "acdlog", "acdpretty", "acdtable", DE "acdtrace", "acdvalid", "acdverbose", "acdxsd" and "acdnocommandline" XX PN [1] PA r optionName const char* PD option name PX RT AjBool RD ajTrue if option was recognised RX // AjBool ajAcdSetControl(const char* optionName) { AcdPAcd vacd; AjPStr varname = NULL; if(!ajCharCmpCase(optionName, "acdhelp")) { acdDoHelp = ajTrue; return ajTrue; } if(!ajCharCmpCase(optionName, "acdlog")) { acdDoLog = ajTrue; return ajTrue; } if(!ajCharCmpCase(optionName, "acdnocommandline")) { acdCommandLine = ajFalse; return ajTrue; } if(!ajCharCmpCase(optionName, "acdpretty")) { acdDoPretty = ajTrue; return ajTrue; } if(!ajCharCmpCase(optionName, "acdtable")) { acdDoTable = ajTrue; return ajTrue; } if(!ajCharCmpCase(optionName, "acdtrace")) { acdDoTrace = ajTrue; return ajTrue; } if(!ajCharCmpCase(optionName, "acdvalid")) { acdDoValid = ajTrue; return ajTrue; } if(!ajCharCmpCase(optionName, "acdverbose")) { acdVerbose = ajTrue; return ajTrue; } if(!ajCharCmpCase(optionName, "acdgalaxy")) { acdDoGalaxy = ajTrue; return ajTrue; } if(!ajCharCmpCase(optionName, "acdgalaxynucleotide")) { acdDoGalaxyNuc = ajTrue; ajStrAssignC(&varname, "acdprotein"); ajStrAssignC(&acdVarAcdProtein, "N"); vacd = acdNewVar(varname); acdSetVarDef(vacd, acdVarAcdProtein); ajStrDel(&varname); return ajTrue; } if(!ajCharCmpCase(optionName, "acdgalaxyprotein")) { acdDoGalaxyProt = ajTrue; ajStrAssignC(&varname, "acdprotein"); ajStrAssignC(&acdVarAcdProtein, "Y"); vacd = acdNewVar(varname); acdSetVarDef(vacd, acdVarAcdProtein); ajStrDel(&varname); return ajTrue; } if(!ajCharCmpCase(optionName, "acdxsd")) { acdDoXsd = ajTrue; return ajTrue; } /* program source error */ ajDie("Unknown ajAcdSetControl control option '%s'", optionName); return ajFalse; } ID acdArgsScan TY static MO ajacd LB acd XX DE Steps through the command line and checks for special qualifiers. DE Sets special internal variables to reflect their presence. DE DE Currently these are "-debug", "-stdout", "-filter", "-options" DE "-help" and "-auto", plus the message controls DE "-warning", "-error", "-fatal", "-die" XX PN [1] PA r argc ajint PD Number of arguments PX PN [2] PA r argv char* const[] PD Actual arguments as a text array. PX RT void RD RX // static void acdArgsScan(ajint argc, char * const argv[]) { ajint i; char* cp; for(i=0; i < argc; i++) { cp = argv[i]; if(*cp && strchr("-/", *cp)) /* first character vs. qualifier starts */ { cp++; if(*cp == '-') /* allow --qualifier */ cp++; } else continue; if(!*cp) continue; if(!strcmp(cp, "debug")) { acdDebug = ajTrue; acdDebugSet = ajTrue; } if(!strcmp(cp, "nodebug")) { acdDebug = ajFalse; acdDebugSet = ajTrue; } if(!strcmp(cp, "stdout")) acdStdout = ajTrue; if(!strcmp(cp, "filter")) acdFilter = ajTrue; if(!strcmp(cp, "options")) acdOptions = ajTrue; if(!strcmp(cp, "verbose")) acdVerbose = ajTrue; if(!strcmp(cp, "version")) acdDoVersion = ajTrue; if(!strcmp(cp, "help")) acdDoHelp = ajTrue; if(!strcmp(cp, "auto")) acdAuto = ajTrue; if(!strcmp(cp, "warning")) AjErrorLevel.warning = ajTrue; if(!strcmp(cp, "nowarning")) AjErrorLevel.warning = ajFalse; if(!strcmp(cp, "error")) AjErrorLevel.error = ajTrue; if(!strcmp(cp, "noerror")) AjErrorLevel.error = ajFalse; if(!strcmp(cp, "-fatal")) AjErrorLevel.fatal = ajTrue; if(!strcmp(cp, "nofatal")) AjErrorLevel.fatal = ajFalse; if(!strcmp(cp, "die")) AjErrorLevel.die = ajTrue; if(!strcmp(cp, "nodie")) AjErrorLevel.die = ajFalse; if(!strcmp(cp, "help")) acdLog("acdArgsScan -help argv[%d]\n", i); } acdLog("acdArgsScan acdDebug %B acdDoHelp %B\n", acdDebug, acdDoHelp); return; } ID acdArgsParse TY static MO ajacd LB acd XX DE Steps through the command line and compares to the stored command structure. DE Capable of cunning tricks such as matching values to qualifiers if they fit, DE and otherwise treating them as parameters. XX PN [1] PA r argc ajint PD Number of arguments PX PN [2] PA r argv char* const[] PD Actual arguments as a text array. PX RT void RD RX // static void acdArgsParse(ajint argc, char * const argv[]) { ajint i; ajint j; ajint number; ajint iparam = 0; /* expected next param */ ajint jparam = 0; /* param found */ ajint itestparam = 0; ajint jtestparam = 0; AcdPAcd acd; AcdPAcd acd2; const char *cp; const char *cq; AjPStr qual = NULL; AjPStr noqual = NULL; AjPStr value = NULL; AjPStr param = NULL; AjPStr token = NULL; AjPStr master = NULL; AjPStr argvalstr = NULL; acdLog("ArgsParse\n=========\n"); acdLog("\n"); acdMasterQual = NULL; /* acdQualTestSkip = ajFalse; */ i = 1; while(i < argc) { acdLog("%s ", argv[i]); i++; } acdLog("\n"); acdLog("\n"); i = 1; /* skip the program name */ while(i < argc) { cp = argv[i]; if((i+1) < argc) cq = argv[i+1]; else cq = NULL; if(acdArgSave) ajStrAppendK(&acdArgSave, '\n'); acdLog("\n"); acdLog("argv[%d] <%s>", i, cp); if(cq) acdLog(" + argv[%d] <%s>", i+1, cq); acdLog("\n"); jparam = 0; if((j = acdIsQual(cp, cq, &jparam, &qual, &noqual, &value, &number, &master, &acd))) { if(jparam) { acdLog("Parameter (%d) ", jparam); acdParamSet[jparam-1] = ajTrue; if(iparam == (jparam-1)) { iparam = acdNextParam(iparam); acdLog("reset iparam = %d\n", iparam); } else acdLog("keep iparam = %d\n", iparam); } else acdLog("Qualifier "); acdLog("-%S ", noqual); if(number) acdLog("[%d] ", number); if(ajStrGetLen(value)) acdLog("= '%S'", value); acdLog("\n"); /* ** acdFindQualDetail dies (Unknown qualifier) if acd is not set, ** so we are safe here */ acdDef(acd, value); acdLog("set qualifier -%S[%d] (param %d) = %S\n", acd->Name, acd->PNum, jparam, value); /* loop over any associated qualifiers for the rest */ acdLog("number: %d jparam: %d acd->PNum: %d acdNParam: %d\n", number, jparam, acd->PNum, acdNParam); if(!ajStrGetLen(master) && !number && !jparam && acd->PNum) { for(itestparam = acd->PNum+1; itestparam <= acdNParam; itestparam++) { acdLog("test [%d] '%S'\n", itestparam, qual); acd2 = acdFindQualDetail(qual, NULL, NULL, itestparam, &jtestparam); if(acd2) { acdDef(acd2, value); acdLog("set next qualifier -%S[%d] (param %d) = %S\n", acd2->Name, acd2->PNum, jparam, value); } else acdLog("no -%S[%d]\n", qual, itestparam); } } acd->UserDefined = ajTrue; ajStrAppendK(&acdArgSave, '-'); ajStrAppendS(&acdArgSave, noqual); if(number) ajFmtPrintAppS(&acdArgSave, "%d", number); else if(ajStrGetLen(master)) ajFmtPrintAppS(&acdArgSave, "_%S", master); if(j==2) { i++; } ajStrAssignS(&argvalstr, value); if(!strcmp(acdType[acd->Type].Name, "boolean") || !strcmp(acdType[acd->Type].Name, "toggle")) { if(ajStrMatchS(qual, noqual)) /* -boolqual */ { if(ajStrMatchC(value, "Y")) ajStrAssignClear(&argvalstr); } else if(ajStrMatchC(value, "N")) /* -noboolqual */ ajStrAssignClear(&argvalstr); if(ajStrGetLen(argvalstr)) /* non-trivial boolean values */ { ajStrAppendK(&acdArgSave, ' '); ajStrAppendS(&acdArgSave, argvalstr); } } else if(ajStrIsWord(value) && (ajStrFindAnyC(value, "*?[]{}|!&^") < 0)) { ajStrAppendK(&acdArgSave, ' '); ajStrAppendS(&acdArgSave, value); } else if(ajStrMatchS(qual, noqual)) /* not -nomissfile */ { ajStrAppendK(&acdArgSave, ' '); ajStrAppendK(&acdArgSave, '\"'); ajStrAppendS(&acdArgSave, value); ajStrAppendK(&acdArgSave, '\"'); } } else /* not a qualifier - assume a parameter */ { iparam = acdNextParam(0); /* first free parameter */ acdIsParam(cp, ¶m, &iparam, &acd); /* die if too many */ acd->UserDefined = ajTrue; ajStrAppendC(&acdArgSave, "[-"); ajStrAppendS(&acdArgSave, acd->Name); ajStrAppendC(&acdArgSave, "] "); if(acdIsParamValue(param)) { acdLog("Parameter %d: %S = %S\n", iparam, acd->Name, param); acdDef(acd, param); acdParamSet[iparam-1] = ajTrue; ajStrAssignC(&argvalstr, cp); if(ajStrIsWord(argvalstr) && (ajStrFindAnyC(argvalstr, "*?[]{}|!&^") < 0)) ajStrAppendS(&acdArgSave, argvalstr); else { ajStrAppendK(&acdArgSave, '\"'); ajStrAppendS(&acdArgSave, argvalstr); ajStrAppendK(&acdArgSave, '\"'); } } else /* missing value "" ignored */ { acdLog("Parameter %d: %S = '%S' ** missing value **\n", iparam, acd->Name, param); ajStrAssignClear(¶m); acdDef(acd, param); acdParamSet[iparam-1] = ajTrue; ajStrAppendC(&acdArgSave, "\"\""); } } i++; } ajStrDel(&qual); ajStrDel(&noqual); ajStrDel(&value); ajStrDel(¶m); ajStrDel(&token); ajStrDel(&argvalstr); return; } ID acdIsParamValue TY static MO ajacd LB acd XX DE Tests whether a parameter value is 'missing', in which case DE it will be ignored for now. XX PN [1] PA r pval const AjPStr PD Parameter value PX RT AjBool RD ajFalse for a missing value. RX // static AjBool acdIsParamValue(const AjPStr pval) { if(!ajStrGetLen(pval)) return ajFalse; return ajTrue; } ID acdNextParam TY static MO ajacd LB acd XX DE Returns the next unknown parameter. Used for cases where parameters DE are specified by qualifier before their turn on the command line XX PN [1] PA r pnum ajint PD Current parameter number PX RT ajint RD next undefined parameter RX // static ajint acdNextParam(ajint pnum) { ajint i; if(pnum > acdNParam) return pnum+1; /* all done */ for(i=pnum;i= acdNParam) /* test acdc-toomanyparam */ { ajErr("Argument '%s' : Too many parameters %d/%d", arg, (*iparam), acdNParam); ajExitBad(); } (*iparam)++; *acd = acdFindParam(*iparam); if(!*cp) /* missing value */ { ajStrAssignClear(param); /* clear the parameter */ return ajTrue; } ajStrAssignC(param, arg); /* copy the argument value */ if(*acd) { if((*acd)->AssocQuals) { acdLog("acdMasterQual [param] set to -%S\n", (*acd)->Name); acdMasterQual = *acd; } else if(acdMasterQual) { acdLog("acdMasterQual cleared, was -%S\n", acdMasterQual->Name); acdMasterQual = NULL; } return ajTrue; } return ajFalse; } ID acdIsQual TY static MO ajacd LB acd XX DE Tests an argument to see whether it is a qualifier DE Qualifiers start with "-". DE Qualifiers are assumed to take a value, which is either DE delimited by an "=" sign or is the next argument. DE Qualifiers can also have a numbered suffix if matching one of the parameters DE or a specific master qualifier as a suffix after an underscore. XX PN [1] PA r arg const char* PD Argument PX PN [2] PA r arg2 const char* PD Next argument PX PN [3] PA w iparam ajint* PD Parameter number PX PN [4] PA w pqual AjPStr* PD Qualifier name copied on success PX PN [5] PA w pnoqual AjPStr* PD Qualifier name with possible 'no' prefix PX PN [6] PA w pvalue AjPStr* PD Qualifier value copied on success PX PN [7] PA w number ajint* PD Qualifier number PX PN [8] PA w pmaster AjPStr* PD Named master qualifier PX PN [9] PA w acd AcdPAcd* PD Qualifier data PX RT ajint RD Number of arguments consumed RX // static ajint acdIsQual(const char* arg, const char* arg2, ajint *iparam, AjPStr *pqual, AjPStr *pnoqual, AjPStr *pvalue, ajint* number, AjPStr *pmaster, AcdPAcd* acd) { ajint ret=0; const char *cp; ajlong i; AjBool gotvalue = ajFalse; AjBool ismissing = ajFalse; AjBool qstart = ajFalse; AjBool nullok = ajFalse; AjBool attrok = ajFalse; AjPStr noqual = NULL; acdLog("acdIsQual '%s' '%s'\n", arg, arg2); cp = arg; *number = 0; *acd = NULL; ajStrDel(pmaster); if(!strcmp(cp, "-")) /* stdin or stdout parameter */ return 0; if(!*cp) /* dummy parameter */ return 0; if(!strcmp(cp, "--")) /* special -- to turn off processing */ { /* acdQualTestSkip = ajTrue; */ return 0; } if(*cp && strchr("-/", *cp)) /* first character vs. qualifier starts */ { cp++; qstart = ajTrue; if(*cp == '-') /* allow --qualifier */ { cp++; } } ajStrAssignClear(pqual); ajStrAssignClear(pnoqual); if(!*cp) return 0; /* qualifier: now play hunt the value */ ret = 1; ajStrAssignC(pqual, cp); /* qualifier with '-' or '/' removed */ /* ** pqual could be ** qualname (unless boolean, look for next arg as the value) ** qualname=value (value as part of arg) ** noqualname (boolean negative, or nullok set to empty string */ /* ** First check whether we have a value (set gotvalue) in the first arg */ i = ajStrFindC(*pqual, "="); if(i >= 0) { if((i+1) == (ajint) ajStrGetLen(*pqual)) /* ended with '=' */ ajStrAssignC(pvalue, ""); else ajStrAssignSubS(pvalue, *pqual, (i+1), -1); acdLog("qualifier value '%S' '%S' %d .. %d\n", *pvalue, *pqual, (i+1), -1); ajStrKeepRange(pqual, 0, (i-1)); gotvalue = ajTrue; } else { if(!qstart) /* no start, no "=" assume it's a parameter */ return 0; if(!ajStrIsAlnum(*pqual)) /* funny characters, fail */ return 0; } acdQualParse(pqual, &noqual, pmaster, number); if(ajStrGetLen(*pmaster)) /* specific master, turn off auto processing */ acdMasterQual = NULL; /* pmaster resets this in acdFindQualDetail */ if(acdMasterQual) /* we are still working with a master */ { acdLog("(a) master, try associated with acdFindQualAssoc\n"); *acd = acdFindQualAssoc(acdMasterQual, *pqual, noqual, *pmaster, *number); if(!*acd) { acdLog("acdMasterQual cleared, was -%S\n", acdMasterQual->Name); acdMasterQual = NULL; } else { *number = acdMasterQual->PNum; acdLog("Qualifier -%S associated with -%S\n", *pqual, acdMasterQual->Name); } } if(!acdMasterQual) { acdLog("(b) no master, general test with acdFindQualDetail\n"); *acd = acdFindQualDetail(*pqual, noqual, *pmaster, *number, iparam); } if(!*acd) /* test acdc-badqual */ ajDie("Unknown qualifier %s", arg); ajStrAssignS(pqual, (*acd)->Name); ajStrAssignS(pnoqual, (*acd)->Name); if((*acd)->AssocQuals) /* this one is a new master */ { acdLog("acdMasterQual set to -%S\n", (*acd)->Name); acdMasterQual = *acd; } if(gotvalue) { /* test acdc-noprefixvalue */ if(ajStrPrefixS((*acd)->Name, noqual)) ajDie("'no' prefix used with value for '%s'", arg); } else { acdLog("testing for a value\n"); /* ** Bool qualifiers can have no value ** or can be followed by a valid Bool value */ if(ajStrPrefixS((*acd)->Name, noqual)) { /* we have a -noqual matched */ acdLog("we matched with -no\n"); ajStrAssignC(pnoqual, "no"); ajStrAppendS(pnoqual, (*acd)->Name); if(!strcmp(acdType[acdListCurr->Type].Name, "boolean") || !strcmp(acdType[acdListCurr->Type].Name, "toggle")) { acdLog("-no%S=N boolean or toggle accepted\n", noqual); gotvalue = ajTrue; ret = 1; ajStrAssignC(pvalue, "N"); ajStrDel(&noqual); return ret; } if(acdAttrTest(*acd, "nullok")) { if(acdAttrTestValue(*acd, "nullok")) attrok = acdAttrToBool(*acd, /* -no for null value */ "nullok", ajFalse, &nullok); else /* assume it can be true */ { attrok = ajTrue; nullok = ajTrue; } acdLog("check for nullok, found:%B value:%B\n", attrok, nullok); } else nullok = ajFalse; if(nullok) { acdLog("-no%S='' nullOK accepted\n", noqual); gotvalue = ajTrue; ret = 1; (*acd)->UserSetNull = ajTrue; ajStrAssignClear(pvalue); ajStrDel(&noqual); return ret; } else /* test acdc-noprefixbad acdc-noprefixbad2 */ ajDie("'no' prefix invalid for '%s'", arg); } /* ** just the qualifier name with no value */ if(!strcmp(acdType[acdListCurr->Type].Name, "boolean") || !strcmp(acdType[acdListCurr->Type].Name, "toggle")) { if(acdValIsBool(arg2)) /* bool value, accept */ { acdLog("acdValIsBool -%s '%s'\n", arg, arg2); gotvalue = ajTrue; ret = 2; ajStrAssignC(pvalue, arg2); } else /* we must mean true */ ajStrAssignC(pvalue, "Y"); } else { if(!arg2) /* no value - can be missing? */ { ajStrToBool((*acd)->DefStr[DEF_MISSING], &ismissing); if(!ismissing) /* test acdc-novalue */ ajDie("Value required for '%s'", arg); } /* test for known qualifiers */ else { if(*arg2 == '-') { if(!acdTestQualC(arg2)) /* not known qualifier */ /* must be value */ gotvalue = ajTrue; else { ajStrToBool((*acd)->DefStr[DEF_MISSING], &ismissing); if(!ismissing) /* test acdc-novalue2 */ ajDie("Value required for '%s' before '%s'", arg, arg2); } } else gotvalue = ajTrue; } if(gotvalue) { ret = 2; ajStrAssignC(pvalue, arg2); } else ajStrAssignClear(pvalue); } } ajStrDel(&noqual); return ret; } ID acdValIsBool TY static MO ajacd LB acd XX DE Tests whether a value on the command line is a valid Boolean value XX PN [1] PA r arg const char* PD COmmand live argument value PX RT AjBool RD ajTrue if the value is boolean, RD but not whether it is true or false. RX // static AjBool acdValIsBool(const char* arg) { if(!arg) return ajFalse; switch(*arg) { case 'n': case 'N': if(!arg[1]) return ajTrue; return ajCharMatchCaseC(arg, "no"); case 'y': case 'Y': if(!arg[1]) return ajTrue; return ajCharMatchCaseC(arg, "yes"); case 't': case 'T': if(!arg[1]) return ajTrue; return ajCharMatchCaseC(arg, "true"); case 'f': case 'F': if(!arg[1]) return ajTrue; return ajCharMatchCaseC(arg, "false"); case '0': if(!arg[1]) return ajTrue; return ajCharMatchCaseC(arg, "0"); case '1': if(!arg[1]) return ajTrue; return ajCharMatchCaseC(arg, "1"); default: break; } return ajFalse; } ID acdFindItem TY static MO ajacd LB acd XX DE Returns the ACD definition for a named item and DE (optionally) a given qualifier number. If the qualifier number DE is given, it is checked. If not, the first hit is used. DE DE Section and Endsection do not count XX PN [1] PA r item const AjPStr PD Item name PX PN [2] PA r number ajint PD Item number (zero if a general item) PX RT AcdPAcd RD ACD item required RX // static AcdPAcd acdFindItem(const AjPStr item, ajint number) { AcdPAcd ret = NULL; AcdPAcd pa; AjBool found = ajFalse; ajint ifound = 0; AjPStr ambigList = NULL; ambigList = ajStrNew(); for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) continue; found = ajFalse; if(ajStrPrefixS(pa->Name, item)) if(!number || number == pa->PNum) found = ajTrue; if(found) { if(ajStrMatchS(pa->Name, item)) { ajStrDel(&ambigList); return pa; } ifound++; ret = pa; acdAmbigApp(&ambigList, pa->Name); } } if(ifound == 1) { if (acdDoValid) acdWarn("Abbreviated item '%S' (%S)", item, ambigList); ajStrDel(&ambigList); return ret; } if(ifound > 1) { ajWarn("ambiguous item %S (%S)", item, ambigList); } ajStrDel(&ambigList); return NULL; } ID acdFindQual TY static MO ajacd LB acd XX DE Finds a qualifier by name, and returns the full name XX PN [1] PA u pqual AjPStr* PD Qualifier name PX RT AcdPAcd RD ACD item for qualifier RX // static AcdPAcd acdFindQual(AjPStr *pqual) { AcdPAcd ret = NULL; AcdPAcd pa; ajint ifound = 0; AjBool found = ajFalse; if(!ajStrGetLen(*pqual)) return NULL; for(pa=acdList; pa; pa=pa->Next) { found = ajFalse; if(acdIsStype(pa)) continue; found = ajFalse; if(pa->Level == ACD_QUAL || pa->Level == ACD_PARAM) { if(ajStrPrefixS(pa->Name, *pqual)) found = ajTrue; if(found) { if(ajStrMatchS(pa->Name, *pqual)) return pa; ifound++; ret = pa; } } } if(ifound == 1) { ajStrAssignS(pqual, ret->Name); return ret; } return NULL; } ID acdFindQualDetail TY static MO ajacd LB acd XX DE Returns the parameter definition for a named qualifier and DE (optionally) a given qualifier number. If the qualifier number DE is given, it is checked. If not, the current parameter number is checked. DE General qualifiers have no specified number and can match at any time. XX PN [1] PA r qual const AjPStr PD Qualifier name PX PN [2] PA r noqual const AjPStr PD Alternative qualifier name PD (qual with "no" prefix removed, or empty, or NULL) PX PN [3] PA rN master const AjPStr PD Master qualifier name PX PN [4] PA r PNum ajint PD Qualifier number (zero if a general qualifier) PX PN [5] PA u iparam ajint* PD Current parameter number PX RT AcdPAcd RD ACD item for qualifier RX // static AcdPAcd acdFindQualDetail(const AjPStr qual, const AjPStr noqual, const AjPStr master, ajint PNum, ajint *iparam) { /* test for match of parameter number and type */ /* PNum : number encoded in qualifier name ==> forced match */ /* iparam : current parameter number ==> possible match */ /* when both are zero, could be a generic match, like "-begin" for all sequences. Just return the first and let caller find the rest */ AcdPAcd ret = NULL; AcdPAcd pa; AjBool found = ajFalse; AjBool isparam = ajFalse; ajint ifound = 0; AjPStr ambigList = NULL; if(ajStrGetLen(master)) { acdLog("acdFindQualDetail ... call acdFindQualMaster\n", qual, noqual, PNum, *iparam); *iparam = 0; return acdFindQualMaster(qual, noqual, master, PNum); } ambigList = ajStrNew(); acdLog("acdFindQualDetail '%S' (%S) PNum: %d iparam: %d\n", qual, noqual, PNum, *iparam); for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) continue; found = ajFalse; if(pa->Level == ACD_QUAL) { if(ajStrPrefixS(pa->Name, qual) || ajStrPrefixS(pa->Name, noqual)) { acdLog("..matched qualifier '%S' [%d]\n", pa->Name, pa->PNum); if(PNum) /* -begin2 forces match to #2 */ { if(PNum == pa->PNum) { acdLog("..matched PNum '%S' [%d]\n", pa->Name, pa->PNum); found = ajTrue; } } else if(pa->PNum) /* defined for parameter pa->PNum */ { acdLog("..hit PNum '%S' [%d] (ambigList '%S')\n", pa->Name, pa->PNum, ambigList); if(!ifound || !ajStrMatchS(pa->Name, ambigList)) found = ajTrue; } else /* general match */ found = ajTrue; if(found) { if(ajStrMatchS(pa->Name, qual) || ajStrMatchS(pa->Name, noqual)) { acdListCurr = pa; ajStrDel(&ambigList); return pa; } acdAmbigApp(&ambigList, pa->Name); ifound++; ret = pa; acdLog("..prefix only '%S', ifound %d\n", pa->Name, ifound); } } } else if(pa->Level == ACD_PARAM) { if(ajStrPrefixS(pa->Name, qual) || ajStrPrefixS(pa->Name, noqual)) { acdLog("..matched param '%S' [%d]\n", pa->Name, pa->PNum); if(ajStrMatchS(pa->Name, qual) || ajStrMatchS(pa->Name, noqual)) { acdListCurr = pa; *iparam = pa->PNum; ajStrDel(&ambigList); return pa; } acdAmbigApp(&ambigList, pa->Name); ifound++; isparam = ajTrue; ret = pa; acdLog("..prefix only '%S', ifound %d\n", pa->Name, ifound); } } } if(ifound == 1) { acdListCurr = ret; if(isparam) *iparam = ret->PNum; if (acdDoValid) acdWarn("Abbreviated qualifier '%S' (%S)", qual, ambigList); ajStrDel(&ambigList); return ret; } if(ifound > 1) { ajWarn("ambiguous qualifier '%S' (%S)", qual, ambigList); } ajStrDel(&ambigList); return NULL; } ID acdFindQualMaster TY static MO ajacd LB acd XX DE Returns the parameter definition for a named qualifier and DE (optionally) a given qualifier number. If the qualifier number DE is given, it is checked. If not, the current parameter number is checked. DE General qualifiers have no specified number and can match at any time. XX PN [1] PA r qual const AjPStr PD Qualifier name PX PN [2] PA r noqual const AjPStr PD Alternative qualifier name PD (qual with "no" prefix removed, or empty, or NULL) PX PN [3] PA rN master const AjPStr PD Master qualifier name PX PN [4] PA r PNum ajint PD Qualifier number (zero if a general qualifier) PX RT AcdPAcd RD ACD item for qualifier RX // static AcdPAcd acdFindQualMaster(const AjPStr qual, const AjPStr noqual, const AjPStr master, ajint PNum) { /* test for match of parameter number and type */ /* ** PNum : number encoded in qualifier name ==> forced match ** iparam : current parameter number ==> possible match ** when both are zero, could be a generic match, like "-begin" for ** all sequences. Just return the first and let caller find the rest */ AcdPAcd ret = NULL; AcdPAcd pa; AjBool found = ajFalse; ajint ifound =0; AjPStr ambigList = NULL; ambigList = ajStrNew(); acdLog("acdFindQualMaster '%S_%S' (%S) PNum: %d\n", qual, master, noqual, PNum); for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) continue; found = ajFalse; if(pa->Level == ACD_QUAL) { if(ajStrPrefixS(pa->Name, master)) { acdLog("..matched qualifier '%S' [%d]\n", pa->Name, pa->PNum); if(PNum) /* -begin2 forces match to #2 */ { if(PNum == pa->PNum) { acdLog("..matched PNum '%S' [%d]\n", pa->Name, pa->PNum); found = ajTrue; } } else if(pa->PNum) /* defined for parameter pa->PNum */ { acdLog("..hit PNum '%S' [%d] (ambigList '%S')\n", pa->Name, pa->PNum, ambigList); if(!ifound || !ajStrMatchS(pa->Name, ambigList)) found = ajTrue; } else /* general match */ found = ajTrue; if(found) { if(ajStrMatchS(pa->Name, master)) { ret = pa; ifound = 1; break; } acdAmbigApp(&ambigList, pa->Name); ifound++; ret = pa; acdLog("..prefix only, ifound %d\n", ifound); } } } else if(pa->Level == ACD_PARAM) { if(ajStrPrefixS(pa->Name, master)) { acdLog("..matched param '%S' [%d]\n", pa->Name, pa->PNum); if(ajStrMatchS(pa->Name, master)) { ret = pa; ifound = 1; break; } acdAmbigApp(&ambigList, pa->Name); ifound++; ret = pa; acdLog("..prefix only, ifound %d\n", ifound); } } } if(ifound > 1) { acdLog("..ambiguous master qualifier for %S_%S (%S)", qual, master, ambigList); ajWarn("Ambiguous master qualifier '%S' in %S_%S (%S)", master, qual, master, ambigList); ajStrDel(&ambigList); return NULL; } if(!ifound) { acdLog("..master qualifier for %S_%S not found\n", qual, master); ajStrDel(&ambigList); return NULL; } acdLog("..master qualifier found '%S' %d\n", ret->Name, ret->PNum); ifound = 0; for(pa=ret->AssocQuals; pa && pa->Assoc; pa=pa->Next) { found = ajFalse; if(ajStrPrefixS(pa->Name, qual) || ajStrPrefixS(pa->Name, noqual)) { acdLog("..matched qualifier '%S' [%d]\n", pa->Name, pa->PNum); if(PNum) /* -begin2 forces match to #2 */ { if(PNum == pa->PNum) { acdLog("..matched PNum '%S' [%d]\n", pa->Name, pa->PNum); found = ajTrue; } } else if(pa->PNum) /* defined for parameter pa->PNum */ { acdLog("..hit PNum '%S' [%d] (ambigList '%S')\n", pa->Name, pa->PNum, ambigList); if(!ifound || !ajStrMatchS(pa->Name, ambigList)) { found = ajTrue; } } else /* general match */ found = ajTrue; if(found) { if(ajStrMatchS(pa->Name, qual) || ajStrMatchS(pa->Name, noqual)) { acdListCurr = pa; ajStrDel(&ambigList); return pa; } acdAmbigApp(&ambigList, pa->Name); ifound++; ret = pa; acdLog("..prefix only, ifound %d\n", ifound); } } } if(ifound == 1) { acdListCurr = ret; if (acdDoValid) acdWarn("Abbreviated associated qualifier '%S_%S' (%S)", qual, master, ambigList); ajStrDel(&ambigList); return ret; } if(ifound > 1) { acdLog("..ambiguous associated qualifier %S_%S (%S)", qual, master, ambigList); ajWarn("ambiguous associated qualifier %S_%S (%S)", qual, master, ambigList); } ajStrDel(&ambigList); acdLog("..associated qualifier %S_%S not found", qual, master); return NULL; } ID acdFindQualAssoc TY static MO ajacd LB acd XX DE Returns the definition for a named associated qualifier. DE If the qualifier number DE is given, it is checked. If not, the current parameter number is checked. DE General qualifiers have no specified number and can match at any time. XX PN [1] PA r thys const AcdPAcd PD Master ACD item PX PN [2] PA r qual const AjPStr PD Qualifier name PX PN [3] PA r noqual const AjPStr PD Alternative qualifier name PD (qual with "no" prefix removed, or empty, or NULL) PX PN [4] PA rN master const AjPStr PD Master qualifier name PX PN [5] PA r PNum ajint PD Qualifier number (zero if a general qualifier) PX RT AcdPAcd RD ACD item for associated qualifier RX // static AcdPAcd acdFindQualAssoc(const AcdPAcd thys, const AjPStr qual, const AjPStr noqual, const AjPStr master, ajint PNum) { /* test for match of parameter number and type */ /* ** PNum : number encoded in qualifier name ==> forced match ** iparam : current parameter number ==> possible match ** when both are zero, could be a generic match, like "-sbegin" for ** all sequences. Just return the first and let caller find the rest */ AcdPAcd pa = thys->AssocQuals; ajint ifound = 0; AcdPAcd ret = NULL; AjPStr ambigList = NULL; ajint iparam; /* acdLog("acdFindQualAssoc '%S' pnum: %d\n", qual, pnum); */ if(PNum && (pa->PNum != PNum)) /* must be for same number (if any) */ return NULL; ambigList = ajStrNew(); for(; pa && pa->Assoc; pa=pa->Next) { if(ajStrPrefixS(pa->Name, qual) || ajStrPrefixS(pa->Name, noqual)) { if(ajStrMatchS(pa->Name, qual) || ajStrMatchS(pa->Name, noqual)) { /* acdLog(" *matched* '%S'\n", pa->Name); */ acdListCurr = pa; ajStrDel(&ambigList); return acdListCurr; } ifound++; ret = pa; acdAmbigApp(&ambigList, pa->Name); } } /* acdLog(" ifound: %d\n", ifound); */ if(ifound == 1) { acdListCurr = ret; pa = acdFindQualDetail(qual,noqual,master,PNum, &iparam); if(pa == ret) { if (acdDoValid) acdWarn("Abbreviated associated qualifier '%S' (%S)", qual, ambigList); ajStrDel(&ambigList); return acdListCurr; } } if(ifound > 1) ajWarn("ambiguous qualifier %S (%S)", qual, ambigList); ajStrDel(&ambigList); return NULL; } ID acdFindParam TY static MO ajacd LB acd XX DE Returns the paremeter definition for a given parameter number XX PN [1] PA r PNum ajint PD Parameter number PX RT AcdPAcd RD ACD item for parameter number PNum RX // static AcdPAcd acdFindParam(ajint PNum) { /* test for match of parameter number and type */ AcdPAcd pa; for(pa=acdList; pa; pa=pa->Next) { if(acdIsStype(pa)) continue; if((pa->Level == ACD_PARAM) && (pa->PNum == PNum)) { acdListCurr = pa; return pa; } } return NULL; } ID acdGetAttr TY static MO ajacd LB acd XX DE Pick up a defined attribute for variable handling. DE The ACD item must be defined (already processed). DE Attributes include any specially calculated by the acdSet function DE for that type. XX PN [1] PA u presult AjPStr* PD Resulting attribute value PX PN [2] PA r name const AjPStr PD ACD item name PX PN [3] PA r attrib const AjPStr PD attribute name PX RT AjBool RD ajTrue on success RX // static AjBool acdGetAttr(AjPStr* presult, const AjPStr name, const AjPStr attrib) { const char *cp; const char *cq; ajint ilen; ajint number = 0; AjPStr tempstr=NULL; AcdPAcd pa = NULL; AcdPAttr attr = NULL; ajint i; acdLog("acdGetAttr name '%S' attrib '%S'\n", name, attrib); ajStrDelStatic(presult); ajStrAssignS(&tempstr, name); ajStrFmtLower(&tempstr); cp = ajStrGetPtr(tempstr); cq = &cp[ajStrGetLen(tempstr)]; if(isdigit((ajint)*--cq)) { while(isdigit((ajint)*--cq)); ++cq; number = (ajint) strtol(cq, NULL, 0); ilen = cq - cp - 1; ajStrKeepRange(&tempstr, 0, ilen); } pa = acdFindItem(tempstr, number); if(!pa) /* test ambigvar.acd */ acdError("Failed to resolve variable '%S'\n", name); ajStrDel(&tempstr); if(!pa->ValStr) { if (!acdDoValid && !acdDoGalaxy) { if(!acdDoHelp) /* test undefvar.acd */ acdError("Variable '%S' not yet defined\n", name); acdAttrValueStr(pa, "default", "...", presult); return ajTrue; } } if(!ajStrGetLen(attrib)) /* just use valstr */ { if (!acdDoValid && !acdDoGalaxy) { ajStrAssignS(presult, pa->ValStr); acdLog("no attribute name, use valstr for %S '%S'\n", pa->Name, *presult); pa->Used |= USED_ACD; return ajTrue; } } if(pa->DefStr) { attr = acdAttrDef; i = acdFindAttr(attr, attrib); if(i >= 0) { ajStrAssignS(presult, pa->DefStr[i]); acdLog("default attribute %S found for %S '%S'\n", attrib, pa->Name, *presult); return ajTrue; } } if(pa->NAttr) { attr = acdType[pa->Type].Attr; i = acdFindAttr(attr, attrib); if(i >= 0) { ajStrAssignS(presult, pa->AttrStr[i]); acdLog("type attribute %S found for %S '%S'\n", attrib, pa->Name, *presult); return ajTrue; } } if(pa->SAttr) { acdLog("++calc++ Testing SAttr %d\n", pa->SAttr); attr = pa->SetAttr; for(i=0; i < pa->SAttr; i++) acdLog("calcattr[%d] '%s'\n", i, attr[i].Name); i = acdFindAttr(attr, attrib); if(i >= 0) { ajStrAssignS(presult, pa->SetStr[i]); acdLog("calculated attribute %S found for %S '%S'\n", attrib, pa->Name, *presult); return ajTrue; } } if(ajStrMatchCaseC(attrib, "isdefined")) { acdLog("++isdefined++ Testing\n"); if (ajStrGetLen(pa->ValStr)) { ajStrAssignC(presult, "Y"); } else { ajStrAssignC(presult, "N"); } acdLog("isdefined attribute found for %S '%S'\n", pa->Name, *presult); return ajTrue; } acdLog("*attribute %S not found for %S*\n", attrib, pa->Name); return ajFalse; } ID acdQualParse TY static MO ajacd LB acd XX DE Converts a qualifier name to lower case and looks for a DE master qualifier name and a trailing number. XX PN [1] PA w pqual AjPStr* PD Qualifier name set to lower case PD with number suffix removed PX PN [2] PA w pnoqual AjPStr* PD Qualifier name as pqual, with "no" prefix PD removed, or empty string id pqual doesn't start with "no" PX PN [3] PA w pqmaster AjPStr* PD Master name for associated qualifier PX PN [4] PA w number ajint* PD Qualifier number suffix if any PX RT void RD RX // static void acdQualParse(AjPStr* pqual, AjPStr* pnoqual, AjPStr* pqmaster, ajint* number) { if(!acdRegQualParse) acdRegQualParse = ajRegCompC("^([a-z]+)(_([a-z]+))?([0-9]+)?$"); ajStrFmtLower(pqual); ajStrAssignS(&acdQualNameTmp, *pqual); if(!ajRegExec(acdRegQualParse, acdQualNameTmp)) { ajStrAssignClear(pqual); ajStrAssignClear(pnoqual); ajStrAssignClear(pqmaster); *number = 0; return; } ajRegSubI(acdRegQualParse, 1, pqual); ajRegSubI(acdRegQualParse, 3, pqmaster); ajRegSubI(acdRegQualParse, 4, &acdQualNumTmp); if(ajStrPrefixC(*pqual, "no")) ajStrAssignSubS(pnoqual, *pqual, 2, -1); else ajStrAssignClear(pnoqual); *number = 0; if(ajStrGetLen(acdQualNumTmp)) ajStrToInt(acdQualNumTmp, number); if(ajStrGetLen(*pqmaster)) acdFindQual(pqmaster); return; } ID acdTokenToLowerS TY static MO ajacd LB acd XX DE Converts a token name to lower case and looks for a trailing number. XX PN [1] PA u ptoken AjPStr* PD Qualifier name set to lower case PD with number suffix removed PX PN [2] PA w number ajint* PD Qualifier number suffix if any. PX RT void RD RX // static void acdTokenToLowerS(AjPStr *ptoken, ajint* number) { const char *cp; const char *cq; ajint ilen; ajStrFmtLower(ptoken); cp = ajStrGetPtr(*ptoken); cq = cp+ajStrGetLen(*ptoken); if(!isdigit((ajint)*--cq)) { *number = 0; return; } while(isdigit((ajint)*--cq)) ; ++cq; *number = (ajint) strtol(cq, NULL, 0); ilen = cq - cp; ajStrCutEnd(ptoken, ajStrGetLen(*ptoken) - ilen); return; } ID acdIsRequired TY static MO ajacd LB acd XX DE Returns true if an ACD item is required but not yet defined. DE Required means the standard attribute is set (which it is by DE default for a parameter), DE or the additional flag is set and -options was specified. XX PN [1] PA r thys const AcdPAcd PD ACD item PX RT AjBool RD ajTrue if "thys" is required but no value set yet. RX // static AjBool acdIsRequired(const AcdPAcd thys) { AjPStr *def = thys->DefStr; AjBool required = ajFalse; if (thys->DefStr) acdLog("acdIsRequired '%S' Defined %B DefStr '%x' std '%S' " "add '%S' def '%S'\n", thys->Name, thys->Defined, thys->DefStr, def[DEF_STANDARD], def[DEF_ADDITIONAL], def[DEF_DEFAULT]); else acdLog("acdIsRequired '%S' Defined %B\n", thys->Name, thys->Defined); if(thys->Defined) return ajFalse; if(!thys->DefStr) return ajFalse; if(ajStrGetLen(def[DEF_STANDARD])) { acdVarResolve(&def[DEF_STANDARD]); if(!ajStrToBool(def[DEF_STANDARD], &required)) acdErrorAcd(thys, "Bad standard flag %S\n", def[DEF_STANDARD]); return required; } if(acdOptions && ajStrGetLen(def[DEF_ADDITIONAL])) { acdVarResolve(&def[DEF_ADDITIONAL]); if(!ajStrToBool(def[DEF_ADDITIONAL], &required)) acdErrorAcd(thys, "Bad additional flag %S\n", def[DEF_ADDITIONAL]); return required; } return ajFalse; } ID acdPromptCodon TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a codon usage file XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptCodon(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); } else acdPromptStandard(thys, "Codon usage file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptDirectory TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a directory XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptDirectory(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " directory")) acdPromptStandardAppend(thys, " directory"); } else acdPromptStandard(thys, "Directory of input files", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptDirlist TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a dirlist XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptDirlist(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " directories")) acdPromptStandardAppend(thys, " directories"); } else acdPromptStandard(thys, "Directories with files", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptFilelist TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a filelist XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptFilelist(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file list")) acdPromptStandardAppend(thys, " file list"); } else acdPromptStandard(thys, "Comma-separated file list", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptFeatures TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a feature table DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptFeatures(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; ajint maxreads = 0; acdAttrToInt(thys, "maxreads", 1, &maxreads); knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " feature table")) acdPromptStandardAppend(thys, " feature table"); } else acdPromptStandard(thys, "input feature table", &count); if(maxreads > 1) acdPromptStandardAppend(thys, "(s)"); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptCpdb TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a clean PDB file XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptCpdb(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); } else acdPromptStandard(thys, "clean PDB file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptScop TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a scop entry XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptScop(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); } else acdPromptStandard(thys, "Scop entry vile", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdHelpTextSeq TY static MO ajacd LB acd XX DE Sets the help text for this ACD object to be a sequence DE description with the type included. XX PN [1] PA r thys const AcdPAcd PD Current ACD object. PX PN [2] PA w Pstr AjPStr* PD Help text PX RT void RD RX // static void acdHelpTextSeq(const AcdPAcd thys, AjPStr* Pstr) { const AjPStr typestr = NULL; AjPStr tmptype = NULL; AjBool gaps = AJFALSE; typestr = acdAttrValue(thys, "type"); ajSeqTypeSummary(typestr, &tmptype, &gaps); ajStrAssignClear(Pstr); if(ajStrGetLen(tmptype)) { ajStrAssignS(Pstr, tmptype); ajStrAppendC(Pstr, " "); ajStrDel(&tmptype); } ajStrAppendC(Pstr, "sequence"); if(ajCharMatchC(acdType[thys->Type].Name, "seqset")) { acdAttrToBool(thys, "aligned", gaps, &gaps); ajStrAppendC(Pstr, " set"); if(gaps) ajStrInsertC(Pstr, 0, "(aligned) "); } else if(ajCharMatchC(acdType[thys->Type].Name, "seqsetall")) { acdAttrToBool(thys, "aligned", gaps, &gaps); ajStrAppendC(Pstr, " set(s)"); if(gaps) ajStrInsertC(Pstr, 0, "(aligned) "); } else if(ajCharMatchC(acdType[thys->Type].Name, "seqall")) { ajStrAppendC(Pstr, "(s)"); if(gaps) ajStrInsertC(Pstr, 0, "(gapped) "); } else { if(gaps) ajStrInsertC(Pstr, 0, "(gapped) "); } ajStrFmtTitle(Pstr); ajStrAppendC(Pstr, " filename and optional format, or reference (input USA)"); return; } ID acdHelpTextSeqout TY static MO ajacd LB acd XX DE Sets the help text for this ACD object to be an output sequence DE description with the type included. XX PN [1] PA r thys const AcdPAcd PD Current ACD object. PX PN [2] PA w Pstr AjPStr* PD Help text PX RT void RD RX // static void acdHelpTextSeqout(const AcdPAcd thys, AjPStr* Pstr) { const AjPStr typestr = NULL; AjPStr tmptype = NULL; AjBool gaps = AJFALSE; typestr = acdAttrValue(thys, "type"); ajSeqTypeSummary(typestr, &tmptype, &gaps); ajStrAssignClear(Pstr); if(ajStrGetLen(tmptype)) { ajStrAssignS(Pstr, tmptype); ajStrAppendC(Pstr, " "); ajStrDel(&tmptype); } ajStrAppendC(Pstr, "sequence"); if(ajCharMatchC(acdType[thys->Type].Name, "seqoutset")) { ajStrAppendC(Pstr, " set"); if(gaps) ajStrInsertC(Pstr, 0, "(aligned) "); } else if(ajCharMatchC(acdType[thys->Type].Name, "seqoutall")) { ajStrAppendC(Pstr, " set(s)"); if(gaps) ajStrInsertC(Pstr, 0, "(aligned) "); } else { if(gaps) ajStrInsertC(Pstr, 0, "(gapped) "); } ajStrFmtTitle(Pstr); ajStrAppendC(Pstr, " filename and optional format (output USA)"); return; } ID acdPromptSeq TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a sequence DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptSeq(AcdPAcd thys) { static ajint count=0; AjPStr tmptype = NULL; AjBool gaps = AJFALSE; AjPStr seqPrompt = NULL; AjPStr seqPromptAlt = NULL; const AjPStr knowntype; const AjPStr type = NULL; AjBool aligned=ajFalse; knowntype = acdKnowntypeDesc(thys); type = acdAttrValue(thys, "type"); if(ajCharPrefixC(acdType[thys->Type].Name,"seqset")) acdAttrToBool(thys, "aligned", ajFalse, &aligned); ajSeqTypeSummary(type, &tmptype, &gaps); seqPrompt = ajStrNewRes(32); seqPromptAlt = ajStrNewRes(32); if(ajStrGetLen(knowntype)) ajFmtPrintAppS(&seqPromptAlt, "%S ", knowntype); if(ajStrGetLen(tmptype)) ajFmtPrintAppS(&seqPromptAlt, "%S ", tmptype); ajFmtPrintAppS(&seqPromptAlt, "sequence"); if(ajCharMatchC(acdType[thys->Type].Name, "seqset")) { ajStrAppendC(&seqPromptAlt, " set"); if(aligned) ajStrInsertC(&seqPromptAlt, 0, "(aligned) "); } else if(ajCharMatchC(acdType[thys->Type].Name, "seqsetall")) { ajStrAppendC(&seqPromptAlt, " set(s)"); if(aligned) ajStrInsertC(&seqPromptAlt, 0, "(aligned) "); } else if(ajCharMatchC(acdType[thys->Type].Name, "seqall")) { ajStrAppendC(&seqPromptAlt, "(s)"); if(gaps) ajStrInsertC(&seqPromptAlt, 0, "(gapped) "); } else { if(gaps) ajStrInsertC(&seqPromptAlt, 0, "(gapped) "); } ajFmtPrintS(&seqPrompt, "Input %S", seqPromptAlt); if(knowntype) { count++; acdPromptStandardS(thys, seqPromptAlt); } else acdPromptStandardAlt(thys, ajStrGetPtr(seqPrompt), ajStrGetPtr(seqPromptAlt), &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); ajStrDel(&tmptype); ajStrDel(&seqPrompt); ajStrDel(&seqPromptAlt); return thys->StdPrompt; } ID acdPromptTree TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a tree file DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptTree(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); } else acdPromptStandardAlt(thys, "Input tree file", "tree file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptGraph TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a sequence DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptGraph(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " graph")) acdPromptStandardAppend(thys, " graph"); } else acdPromptStandard(thys, "graph type", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptFeatout TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a features output DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptFeatout(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(ajStrSuffixC(knowntype, " features")) acdPromptStandardAppend(thys, " features"); acdPromptStandardAppend(thys, " output"); } else acdPromptStandard(thys, "features output", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptAlign TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a report output DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptAlign(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(ajStrSuffixC(knowntype, " output")) acdPromptStandardAppend(thys, " alignment"); else acdPromptStandardAppend(thys, " output alignment"); } else acdPromptStandard(thys, "output alignment", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptReport TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a report output DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptReport(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(ajStrSuffixC(knowntype, " output")) acdPromptStandardAppend(thys, " report"); else acdPromptStandardAppend(thys, " output report"); } else acdPromptStandard(thys, "output report", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptSeqout TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a sequence output DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptSeqout(AcdPAcd thys) { static ajint count=0; AjPStr tmptype = NULL; AjBool gaps = AJFALSE; AjPStr seqPrompt = NULL; AjPStr seqPromptAlt = NULL; const AjPStr knowntype; const AjPStr type = NULL; AjBool aligned=ajFalse; knowntype = acdKnowntypeDesc(thys); type = acdAttrValue(thys, "type"); if(ajCharPrefixC(acdType[thys->Type].Name,"seqoutset")) acdAttrToBool(thys, "aligned", ajFalse, &aligned); ajSeqTypeSummary(type, &tmptype, &gaps); seqPrompt = ajStrNewRes(32); seqPromptAlt = ajStrNewRes(32); if(ajStrGetLen(knowntype)) ajFmtPrintAppS(&seqPromptAlt, "%S ", knowntype); if(ajStrGetLen(tmptype)) ajFmtPrintAppS(&seqPromptAlt, "%S ", tmptype); ajFmtPrintAppS(&seqPromptAlt, "output sequence"); if(ajCharMatchC(acdType[thys->Type].Name, "seqoutset")) { ajStrAppendC(&seqPromptAlt, " set"); if(aligned) ajStrInsertC(&seqPromptAlt, 0, "(aligned) "); } else if(ajCharMatchC(acdType[thys->Type].Name, "seqoutsetall")) { ajStrAppendC(&seqPromptAlt, " set(s)"); if(aligned) ajStrInsertC(&seqPromptAlt, 0, "(aligned) "); } else if(ajCharMatchC(acdType[thys->Type].Name, "seqoutall")) { ajStrAppendC(&seqPromptAlt, "(s)"); if(gaps) ajStrInsertC(&seqPromptAlt, 0, "(gapped) "); } else { if(gaps) ajStrInsertC(&seqPromptAlt, 0, "(gapped) "); } ajFmtPrintS(&seqPrompt, "%S", seqPromptAlt); if(knowntype) { count++; acdPromptStandardS(thys, seqPromptAlt); } else acdPromptStandardAlt(thys, ajStrGetPtr(seqPrompt), ajStrGetPtr(seqPromptAlt), &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); ajStrDel(&tmptype); ajStrDel(&seqPrompt); ajStrDel(&seqPromptAlt); return thys->StdPrompt; } ID acdHelpTextObo TY static MO ajacd LB acd XX DE Sets the help text for this ACD object to be an obo term description XX PN [1] PA r thys const AcdPAcd PD Current ACD object. PX PN [2] PA w Pstr AjPStr* PD Help text PX RT void RD RX // static void acdHelpTextObo(const AcdPAcd thys, AjPStr* Pstr) { ajint maxreads; ajStrAssignClear(Pstr); acdAttrToInt(thys, "maxreads", 1, &maxreads); if(maxreads <= 1) ajStrAssignC(Pstr, "obo term"); else ajStrAssignC(Pstr, "obo term(s)"); ajStrFmtTitle(Pstr); ajStrAppendC(Pstr, " filename and optional format, or reference (input query)"); return; } ID acdHelpTextTaxon TY static MO ajacd LB acd XX DE Sets the help text for this ACD object to be an NCBI taxonomy entry XX PN [1] PA r thys const AcdPAcd PD Current ACD object. PX PN [2] PA w Pstr AjPStr* PD Help text PX RT void RD RX // static void acdHelpTextTaxon(const AcdPAcd thys, AjPStr* Pstr) { ajint maxreads; ajStrAssignClear(Pstr); acdAttrToInt(thys, "maxreads", 1, &maxreads); if(maxreads <= 1) ajStrAssignC(Pstr, "taxon"); else ajStrAssignC(Pstr, "taxon(s)"); ajStrFmtTitle(Pstr); ajStrAppendC(Pstr, " filename and optional format, or reference (input query)"); return; } ID acdHelpTextText TY static MO ajacd LB acd XX DE Sets the help text for this ACD object to be a text entry XX PN [1] PA r thys const AcdPAcd PD Current ACD object. PX PN [2] PA w Pstr AjPStr* PD Help text PX RT void RD RX // static void acdHelpTextText(const AcdPAcd thys, AjPStr* Pstr) { ajint maxreads; ajStrAssignClear(Pstr); acdAttrToInt(thys, "maxreads", 1, &maxreads); if(maxreads <= 1) ajStrAssignC(Pstr, "text"); else ajStrAssignC(Pstr, "text(s)"); ajStrFmtTitle(Pstr); ajStrAppendC(Pstr, " filename and optional format, or reference (input query)"); return; } ID acdHelpTextUrl TY static MO ajacd LB acd XX DE Sets the help text for this ACD object to be a URL data entry XX PN [1] PA r thys const AcdPAcd PD Current ACD object. PX PN [2] PA w Pstr AjPStr* PD Help text PX RT void RD RX // static void acdHelpTextUrl(const AcdPAcd thys, AjPStr* Pstr) { ajint maxreads; ajStrAssignClear(Pstr); acdAttrToInt(thys, "maxreads", 1, &maxreads); if(maxreads <= 1) ajStrAssignC(Pstr, "url"); else ajStrAssignC(Pstr, "url(s)"); ajStrFmtTitle(Pstr); ajStrAppendC(Pstr, " filename and optional format, or reference (input query)"); return; } ID acdHelpTextVariation TY static MO ajacd LB acd XX DE Sets the help text for this ACD object to be a variation data entry XX PN [1] PA r thys const AcdPAcd PD Current ACD object. PX PN [2] PA w Pstr AjPStr* PD Help text PX RT void RD RX // static void acdHelpTextVariation(const AcdPAcd thys, AjPStr* Pstr) { ajint maxreads; ajStrAssignClear(Pstr); acdAttrToInt(thys, "maxreads", 1, &maxreads); if(maxreads <= 1) ajStrAssignC(Pstr, "variation"); else ajStrAssignC(Pstr, "variation(s)"); ajStrFmtTitle(Pstr); ajStrAppendC(Pstr, " filename and optional format, or reference (input query)"); return; } ID acdPromptAssembly TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an assembly DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptAssembly(AcdPAcd thys) { static ajint count=0; AjPStr assemPrompt = NULL; AjPStr assemPromptAlt = NULL; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); assemPrompt = ajStrNewRes(32); assemPromptAlt = ajStrNewRes(32); if(ajStrGetLen(knowntype)) ajFmtPrintAppS(&assemPromptAlt, "%S ", knowntype); ajFmtPrintAppS(&assemPromptAlt, "assembly"); ajFmtPrintS(&assemPrompt, "Input %S", assemPromptAlt); if(knowntype) { count++; acdPromptStandardS(thys, assemPromptAlt); } else acdPromptStandardAlt(thys, ajStrGetPtr(assemPrompt), ajStrGetPtr(assemPromptAlt), &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); ajStrDel(&assemPrompt); ajStrDel(&assemPromptAlt); return thys->StdPrompt; } ID acdPromptObo TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an obo term DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptObo(AcdPAcd thys) { static ajint count=0; AjPStr oboPrompt = NULL; AjPStr oboPromptAlt = NULL; const AjPStr knowntype; ajint maxreads = 0; acdAttrToInt(thys, "maxreads", 1, &maxreads); knowntype = acdKnowntypeDesc(thys); oboPrompt = ajStrNewRes(32); oboPromptAlt = ajStrNewRes(32); if(ajStrGetLen(knowntype)) ajFmtPrintAppS(&oboPromptAlt, "%S ", knowntype); ajFmtPrintAppS(&oboPromptAlt, "obo term"); if(maxreads > 1) ajStrAppendC(&oboPromptAlt, "(s)"); ajFmtPrintS(&oboPrompt, "Input %S", oboPromptAlt); if(knowntype) { count++; acdPromptStandardS(thys, oboPromptAlt); } else acdPromptStandardAlt(thys, ajStrGetPtr(oboPrompt), ajStrGetPtr(oboPromptAlt), &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); ajStrDel(&oboPrompt); ajStrDel(&oboPromptAlt); return thys->StdPrompt; } ID acdPromptOutassembly TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutassembly(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " output file"); } else acdPromptStandard(thys, "assembly output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutcodon TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutcodon(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " output file"); } else acdPromptStandard(thys, "codon usage output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutcpdb TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutcpdb(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " output file"); } else acdPromptStandard(thys, "clean PDB output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutdata TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutdata(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " output file"); } else acdPromptStandard(thys, "output data file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutdir TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutdir(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); acdPromptStandardAppend(thys, " output directory"); } else acdPromptStandard(thys, "output directory", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutdiscrete TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutdiscrete(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " output discrete data file"); } else acdPromptStandard(thys, "discrete data output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutdistance TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutdistance(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " distance data output file"); } else acdPromptStandard(thys, "output distance data file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutfreq TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutfreq(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " frequency data output file"); } else acdPromptStandard(thys, "frequency data output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutmatrix TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutmatrix(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " matrix output file"); } else acdPromptStandard(thys, "matrix output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutobo TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutobo(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " obo output file"); } else acdPromptStandard(thys, "obo output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutproperties TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutproperties(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " properties data output file"); } else acdPromptStandard(thys, "properties data output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutrefseq TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutrefseq(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " output file"); } else acdPromptStandard(thys, "reference sequence output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutresource TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutresource(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " data resource output file"); } else acdPromptStandard(thys, "data resource output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutscop TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutscop(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " scop output file"); } else acdPromptStandard(thys, "scop output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOuttaxon TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOuttaxon(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " taxon output file"); } else acdPromptStandard(thys, "taxon output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOuttext TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOuttext(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " text output file"); } else acdPromptStandard(thys, "text output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOuttree TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOuttree(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " tree output file"); } else acdPromptStandard(thys, "tree output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOuturl TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOuturl(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " URL output file"); } else acdPromptStandard(thys, "URL output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptOutvariation TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a simple DE prompt with "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutvariation(AcdPAcd thys) { static ajint count=0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); acdPromptStandardAppend(thys, " variation output file"); } else acdPromptStandard(thys, "variation output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptRefseq TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a reference sequence DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptRefseq(AcdPAcd thys) { static ajint count=0; AjPStr refseqPrompt = NULL; AjPStr refseqPromptAlt = NULL; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); refseqPrompt = ajStrNewRes(32); refseqPromptAlt = ajStrNewRes(32); if(ajStrGetLen(knowntype)) ajFmtPrintAppS(&refseqPromptAlt, "%S ", knowntype); ajFmtPrintAppS(&refseqPromptAlt, "reference sequence"); ajFmtPrintS(&refseqPrompt, "Input %S", refseqPromptAlt); if(knowntype) { count++; acdPromptStandardS(thys, refseqPromptAlt); } else acdPromptStandardAlt(thys, ajStrGetPtr(refseqPrompt), ajStrGetPtr(refseqPromptAlt), &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); ajStrDel(&refseqPrompt); ajStrDel(&refseqPromptAlt); return thys->StdPrompt; } ID acdPromptTaxon TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an NCBI taxonomy entry DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptTaxon(AcdPAcd thys) { static ajint count=0; AjPStr taxPrompt = NULL; AjPStr taxPromptAlt = NULL; const AjPStr knowntype; ajint maxreads = 0; acdAttrToInt(thys, "maxreads", 1, &maxreads); knowntype = acdKnowntypeDesc(thys); taxPrompt = ajStrNewRes(32); taxPromptAlt = ajStrNewRes(32); if(ajStrGetLen(knowntype)) ajFmtPrintAppS(&taxPromptAlt, "%S ", knowntype); ajFmtPrintAppS(&taxPromptAlt, "taxon"); if(maxreads > 1) ajStrAppendC(&taxPromptAlt, "(s)"); ajFmtPrintS(&taxPrompt, "Input %S", taxPromptAlt); if(knowntype) { count++; acdPromptStandardS(thys, taxPromptAlt); } else acdPromptStandardAlt(thys, ajStrGetPtr(taxPrompt), ajStrGetPtr(taxPromptAlt), &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); ajStrDel(&taxPrompt); ajStrDel(&taxPromptAlt); return thys->StdPrompt; } ID acdPromptText TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a text entry DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptText(AcdPAcd thys) { static ajint count=0; AjPStr taxPrompt = NULL; AjPStr taxPromptAlt = NULL; const AjPStr knowntype; ajint maxreads = 0; acdAttrToInt(thys, "maxreads", 1, &maxreads); knowntype = acdKnowntypeDesc(thys); taxPrompt = ajStrNewRes(32); taxPromptAlt = ajStrNewRes(32); if(ajStrGetLen(knowntype)) ajFmtPrintAppS(&taxPromptAlt, "%S ", knowntype); ajFmtPrintAppS(&taxPromptAlt, "text"); if(maxreads > 1) ajStrAppendC(&taxPromptAlt, "(s)"); ajFmtPrintS(&taxPrompt, "Input %S", taxPromptAlt); if(knowntype) { count++; acdPromptStandardS(thys, taxPromptAlt); } else acdPromptStandardAlt(thys, ajStrGetPtr(taxPrompt), ajStrGetPtr(taxPromptAlt), &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); ajStrDel(&taxPrompt); ajStrDel(&taxPromptAlt); return thys->StdPrompt; } ID acdPromptUrl TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a URL data entry DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptUrl(AcdPAcd thys) { static ajint count=0; AjPStr urlPrompt = NULL; AjPStr urlPromptAlt = NULL; const AjPStr knowntype; ajint maxreads = 0; acdAttrToInt(thys, "maxreads", 1, &maxreads); knowntype = acdKnowntypeDesc(thys); urlPrompt = ajStrNewRes(32); urlPromptAlt = ajStrNewRes(32); if(ajStrGetLen(knowntype)) ajFmtPrintAppS(&urlPromptAlt, "%S ", knowntype); ajFmtPrintAppS(&urlPromptAlt, "url"); if(maxreads > 1) ajStrAppendC(&urlPromptAlt, "(s)"); ajFmtPrintS(&urlPrompt, "Input %S", urlPromptAlt); if(knowntype) { count++; acdPromptStandardS(thys, urlPromptAlt); } else acdPromptStandardAlt(thys, ajStrGetPtr(urlPrompt), ajStrGetPtr(urlPromptAlt), &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); ajStrDel(&urlPrompt); ajStrDel(&urlPromptAlt); return thys->StdPrompt; } ID acdPromptVariation TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a variation entry DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptVariation(AcdPAcd thys) { static ajint count=0; AjPStr varPrompt = NULL; AjPStr varPromptAlt = NULL; const AjPStr knowntype; ajint maxreads = 0; acdAttrToInt(thys, "maxreads", 1, &maxreads); knowntype = acdKnowntypeDesc(thys); varPrompt = ajStrNewRes(32); varPromptAlt = ajStrNewRes(32); if(ajStrGetLen(knowntype)) ajFmtPrintAppS(&varPromptAlt, "%S ", knowntype); ajFmtPrintAppS(&varPromptAlt, "variation"); if(maxreads > 1) ajStrAppendC(&varPromptAlt, "(s)"); ajFmtPrintS(&varPrompt, "Input %S", varPromptAlt); if(knowntype) { count++; acdPromptStandardS(thys, varPromptAlt); } else acdPromptStandardAlt(thys, ajStrGetPtr(varPrompt), ajStrGetPtr(varPromptAlt), &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); ajStrDel(&varPrompt); ajStrDel(&varPromptAlt); return thys->StdPrompt; } ID acdPromptStandard TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an output DE file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX PN [2] PA r type const char* PD Data type for prompt PX PN [3] PA w count ajint* PD count for number of calls PX RT void RD RX // static void acdPromptStandard(AcdPAcd thys, const char* type, ajint* count) { AjPStr *prompt; (*count)++; acdLog("acdPromptStandard '%s' count %d\n", type, *count); if(!thys->DefStr) { acdLog("acdPromptStandard '%s' thys->DefStr NULL\n", type); return; } prompt = &thys->DefStr[DEF_PROMPT]; if(ajStrGetLen(*prompt)) { acdLog("acdPromptStandard '%s' found thys->DefStr[DEF_PROMPT] '%S'\n", type, *prompt); /*ajStrTrace(*prompt);*/ /*ajStrTrace(thys->DefStr[DEF_PROMPT]);*/ return; } switch(*count) { case 1: ajFmtPrintS(&thys->StdPrompt, "%s", type); ajStrFmtTitle(&thys->StdPrompt); break; case 2: ajFmtPrintS(&thys->StdPrompt, "Second %s", type); break; case 3: ajFmtPrintS(&thys->StdPrompt, "Third %s", type); break; case 11: case 12: case 13: ajFmtPrintS(&thys->StdPrompt, "%dth %s", count, type); break; default: switch(*count % 10) { case 1: ajFmtPrintS(&thys->StdPrompt, "%dst %s", count, type); break; case 2: ajFmtPrintS(&thys->StdPrompt, "%dnd %s", count, type); break; case 3: ajFmtPrintS(&thys->StdPrompt, "%drd %s", count, type); break; default: ajFmtPrintS(&thys->StdPrompt, "%dth %s", count, type); break; } break; } return; } ID acdPromptStandardAlt TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to as specified, with DE "second", "third" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX PN [2] PA r firsttype const char* PD Data type for prompt first time PX PN [3] PA r type const char* PD Data type for prompt subsequent times PX PN [4] PA w count ajint* PD count for number of calls PX RT void RD RX // static void acdPromptStandardAlt(AcdPAcd thys, const char* firsttype, const char* type, ajint* count) { AjPStr *prompt; (*count)++; acdLog("acdPromptStandardAlt '%s' count %d\n", type, *count); if(!thys->DefStr) { acdLog("acdPromptStandardAlt '%s' thys->DefStr NULL\n", type); return; } prompt = &thys->DefStr[DEF_PROMPT]; if(ajStrGetLen(*prompt)) { acdLog("acdPromptStandardAlt '%s' " "found thys->DefStr[DEF_PROMPT] '%S'\n", type, *prompt); /*ajStrTrace(*prompt);*/ /*ajStrTrace(thys->DefStr[DEF_PROMPT]);*/ return; } switch(*count) { case 1: ajFmtPrintS(&thys->StdPrompt, "%s", firsttype); break; case 2: ajFmtPrintS(&thys->StdPrompt, "Second %s", type); break; case 3: ajFmtPrintS(&thys->StdPrompt, "Third %s", type); break; case 11: case 12: case 13: ajFmtPrintS(&thys->StdPrompt, "%dth %s", count, type); break; default: switch(*count % 10) { case 1: ajFmtPrintS(&thys->StdPrompt, "%dst %s", count, type); break; case 2: ajFmtPrintS(&thys->StdPrompt, "%dnd %s", count, type); break; case 3: ajFmtPrintS(&thys->StdPrompt, "%drd %s", count, type); break; default: ajFmtPrintS(&thys->StdPrompt, "%dth %s", count, type); break; } break; } return; } ID acdPromptStandardAppend TY static MO ajacd LB acd XX DE Appends to the default prompt for this ACD object XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX PN [2] PA r str const char* PD Suffix to append to prompt PX RT void RD RX // static void acdPromptStandardAppend(AcdPAcd thys, const char* str) { ajStrAppendC(&thys->StdPrompt, str); } ID acdPromptStandardS TY static MO ajacd LB acd XX DE Appends to the default prompt for this ACD object XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX PN [2] PA r str const AjPStr PD Data type for prompt PX RT void RD RX // static void acdPromptStandardS(AcdPAcd thys, const AjPStr str) { ajStrAssignS(&thys->StdPrompt, str); } ID acdPromptOutfile TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an output DE file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptOutfile(AcdPAcd thys) { static ajint count = 0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(ajStrSuffixC(knowntype, " output")) acdPromptStandardAppend(thys, " file"); else acdPromptStandardAppend(thys, " output file"); } else acdPromptStandard(thys, "output file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptInfile TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an input DE file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptInfile(AcdPAcd thys) { static ajint count = 0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); } else acdPromptStandard(thys, "input file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptDatafile TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an input data DE file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptDatafile(AcdPAcd thys) { static ajint count = 0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(ajStrSuffixC(knowntype, " data")) acdPromptStandardAppend(thys, " file"); else acdPromptStandardAppend(thys, " data file"); } else acdPromptStandard(thys, "data file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptMatrix TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an input data DE file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptMatrix(AcdPAcd thys) { static ajint count = 0; AjBool protein = ajFalse; const AjPStr knowntype; acdAttrToBool(thys, "protein", ajFalse, &protein); knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); } else { if(acdDoValid) acdPromptStandard(thys, "comparison matrix file", &count); else { if(protein) acdPromptStandard(thys, "(protein) comparison matrix file", &count); else acdPromptStandard(thys, "(nucleotide) comparison matrix file", &count); } } if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptDiscretestates TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an input discrete states DE file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptDiscretestates(AcdPAcd thys) { static ajint count = 0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); } else acdPromptStandard(thys, "discrete states file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptDistances TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an input distances DE file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptDistances(AcdPAcd thys) { static ajint count = 0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); } else acdPromptStandard(thys, "distances file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptFrequencies TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an input frequencies DE file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptFrequencies(AcdPAcd thys) { static ajint count = 0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); } else acdPromptStandard(thys, "frequencies file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptProperties TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be an input properties DE file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptProperties(AcdPAcd thys) { static ajint count = 0; const AjPStr knowntype; knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " file")) acdPromptStandardAppend(thys, " file"); } else acdPromptStandard(thys, "properties file", &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptPattern TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a pattern string or DE file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptPattern(AcdPAcd thys) { static ajint count = 0; AjPStr type = NULL; const AjPStr knowntype; acdAttrToStr(thys, "type", "", &type); knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " pattern")) acdPromptStandardAppend(thys, " pattern"); } else { if(ajStrPrefixCaseC(type, "p")) acdPromptStandard(thys, "protein pattern string or @file", &count); else if(ajStrPrefixCaseC(type, "n")) acdPromptStandard(thys, "nucleotide pattern string or @file", &count); else acdPromptStandard(thys, "pattern string or @file", &count); } if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdPromptRegexp TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a regular expression DE string or file with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptRegexp(AcdPAcd thys) { static ajint count = 0; AjPStr type = NULL; const AjPStr knowntype; acdAttrToStr(thys, "type", "", &type); knowntype = acdKnowntypeDesc(thys); if(ajStrGetLen(knowntype)) { count++; acdPromptStandardS(thys, knowntype); if(!ajStrSuffixC(knowntype, " regular expression")) acdPromptStandardAppend(thys, " regular expression"); } else { if(ajStrPrefixCaseC(type, "p")) acdPromptStandard(thys, "protein regular expression string or @file", &count); else if(ajStrPrefixCaseC(type, "n")) acdPromptStandard(thys, "nucleotide regular expression string or @file", &count); else acdPromptStandard(thys, "regular expression string or @file", &count); } if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); return thys->StdPrompt; } ID acdHelpTextResource TY static MO ajacd LB acd XX DE Sets the help text for this ACD object to be a data resource description XX PN [1] PA r thys const AcdPAcd PD Current ACD object. PX PN [2] PA w Pstr AjPStr* PD Help text PX RT void RD RX // static void acdHelpTextResource(const AcdPAcd thys, AjPStr* Pstr) { ajint maxreads = 0; ajStrAssignClear(Pstr); acdAttrToInt(thys, "maxreads", 1, &maxreads); if(maxreads <= 1) ajStrAssignC(Pstr, "data resource"); else ajStrAssignC(Pstr, "data resource(s)"); ajStrFmtTitle(Pstr); ajStrAppendC(Pstr, " filename and optional format, or reference (input query)"); return; } ID acdPromptResource TY static MO ajacd LB acd XX DE Sets the default prompt for this ACD object to be a data resource DE prompt with "first", "second" etc. added. XX PN [1] PA u thys AcdPAcd PD Current ACD object. PX RT const AjPStr RD Generated standard prompt RX // static const AjPStr acdPromptResource(AcdPAcd thys) { static ajint count=0; AjPStr resourcePrompt = NULL; AjPStr resourcePromptAlt = NULL; const AjPStr knowntype; ajint maxreads = 0; acdAttrToInt(thys, "maxreads", 1, &maxreads); knowntype = acdKnowntypeDesc(thys); resourcePrompt = ajStrNewRes(32); resourcePromptAlt = ajStrNewRes(32); if(ajStrGetLen(knowntype)) ajFmtPrintAppS(&resourcePromptAlt, "%S ", knowntype); ajFmtPrintAppS(&resourcePromptAlt, "data resource"); if(maxreads > 1) ajStrAppendC(&resourcePromptAlt, "(s)"); ajFmtPrintS(&resourcePrompt, "Input %S", resourcePromptAlt); if(knowntype) { count++; acdPromptStandardS(thys, resourcePromptAlt); } else acdPromptStandardAlt(thys, ajStrGetPtr(resourcePrompt), ajStrGetPtr(resourcePromptAlt), &count); if(!acdAttrTestDefined(thys, "default") && acdAttrTestDefined(thys, "nullok")) acdPromptStandardAppend(thys, " (optional)"); ajStrDel(&resourcePrompt); ajStrDel(&resourcePromptAlt); return thys->StdPrompt; } ID acdCodeGet TY static MO ajacd LB acd XX DE Translates a code into a message text using the code table DE for the current language. XX PN [1] PA r code const AjPStr PD Code name PX PN [2] PA w msg AjPStr* PD Message text for this code in current language PX RT AjBool RD ajTrue on success RX // static AjBool acdCodeGet(const AjPStr code, AjPStr *msg) { const AjPStr value; /* not static - copy of a table text */ AjBool ret = ajFalse; acdLog("acdCodeGet ('%S')\n", code); if(!acdCodeSet) acdCodeInit(); value = ajTableFetchS(acdCodeTable, code); if(value) { ajStrAssignS(msg, value); acdLog("%S value '%S'\n", code, *msg); ret = ajTrue; } return ret; } ID acdCodeDef TY static MO ajacd LB acd XX DE Generates a default code name of 'def' + qualifier type. DE Translates into a message text using the code table DE for the current language. XX PN [1] PA r thys const AcdPAcd PD Current ACD object PX PN [2] PA w msg AjPStr* PD Message text for default message PD in current language PX RT AjBool RD ajTrue on success RX // static AjBool acdCodeDef(const AcdPAcd thys, AjPStr *msg) { AjPStr code = NULL; AjPStr value = NULL; AjBool ret = ajFalse; acdLog("acdCodeDef '%s'\n", acdType[thys->Type].Name); if(!acdCodeSet) acdCodeInit(); code = ajStrNewC("def"); ajStrAppendC(&code, acdType[thys->Type].Name); ajStrFmtLower(&code); acdLog("look for defcode '%S'\n", code); if(acdCodeGet(code, &value)) { ajFmtPrintS(msg, "-%S : %S", thys->Name, value); ajStrDel(&value); ret = ajTrue; } else acdLog("defcode not found '%S'\n", code); ajStrDel(&code); return ret; } ID acdHelpCodeDef TY static MO ajacd LB acd XX DE Generates a default code name of 'help' + qualifier type. DE Translates into a message text using the code table DE for the current language. XX PN [1] PA r thys const AcdPAcd PD Current ACD object PX PN [2] PA w msg AjPStr* PD Help text in current language PX RT AjBool RD ajTrue on success RX // static AjBool acdHelpCodeDef(const AcdPAcd thys, AjPStr *msg) { AjPStr code = NULL; AjPStr value = NULL; AjBool ret = ajFalse; acdLog("acdHelpCodeDef '%s'\n", acdType[thys->Type].Name); if(!acdCodeSet) acdCodeInit(); code = ajStrNewC("help"); ajStrAppendC(&code, acdType[thys->Type].Name); ajStrFmtLower(&code); acdLog("look for helpcode '%S'\n", code); if(acdCodeGet(code, &value)) { ajFmtPrintS(msg, "%S", value); ajStrDel(&value); ret = ajTrue; } else acdLog("helpcode not found '%S'\n", code); ajStrDel(&code); return ret; } ID acdCodeInit TY static MO ajacd LB acd XX DE Sets up the code file data for the current language when needed XX RT void RD RX // static void acdCodeInit(void) { AjPFile codeFile = NULL; AjPStr codeFName = NULL; AjPStr codeRoot = NULL; AjPStr codeRootInst = NULL; AjPStr codePack = NULL; AjPStr codeCode = NULL; AjPStr codeValue = NULL; AjPStr codeLine = NULL; AjPStr codeText = NULL; AjPStr codeLanguage = NULL; AjPStr tmpstr = NULL; AjPRegexp codexp = NULL; if(acdCodeSet) return; ajStrAssignS(&codePack, ajNamValuePackage()); ajStrAssignS(&codeRootInst,ajNamValueInstalldir()); ajDirnameFix(&codeRootInst); if(!ajNamGetValueC("language", &codeLanguage)) ajStrAssignC(&codeLanguage, "english"); if(ajNamGetValueC("acdroot", &codeRoot)) { ajDirnameFix(&codeRoot); ajFmtPrintS(&codeFName, "%Scodes.%S", codeRoot, codeLanguage); codeFile = ajFileNewInNameS(codeFName); acdLog("Code file in acdroot: '%S'\n", codeFName); } else { ajFmtPrintS(&codeFName, "%Sshare/%S/acd/codes.%S", codeRootInst, codePack, codeLanguage); acdLog("Code file installed: '%S'\n", codeFName); codeFile = ajFileNewInNameS(codeFName); if(!codeFile) { acdLog("Code file '%S' not opened\n", codeFName); ajStrAssignS(&codeRoot, ajNamValueRootdir()); ajDirnameFix(&codeRoot); ajFmtPrintS(&codeFName, "%Sacd/codes.%S", codeRoot, codeLanguage); acdLog("Code file from source dir: '%S'\n", codeFName); codeFile = ajFileNewInNameS(codeFName); } } if(!codeFile) /* test acdc-codemissing */ ajWarn("Code file %S not found", codeFName); else acdLog("Code file %F used\n", codeFile); codeText = ajStrNew(); /* fix by Nicolas Joly */ while(codeFile && ajReadlineTrim(codeFile, &codeLine)) if(ajStrCutComments(&codeLine)) { ajStrAppendS(&codeText, codeLine); ajStrAppendC(&codeText, " "); } ajFileClose(&codeFile); ajStrDel(&codeLine); acdCodeTable = ajTablestrNewCase(100); codexp = ajRegCompC("^ *([^ ]+) +\"([^\"]*)\""); while(ajRegExec(codexp, codeText)) { codeCode = codeValue = NULL; /* need to save in table each time */ ajRegSubI(codexp, 1, &codeCode); ajRegSubI(codexp, 2, &codeValue); ajStrFmtLower(&codeCode); ajTablePut(acdCodeTable, codeCode, codeValue); acdLog("add to table %S '%S'\n", codeCode, codeValue); ajRegPost(codexp, &tmpstr); ajStrAssignS(&codeText, tmpstr); } if(!ajStrIsWhite(codeText)) /* test acdc-codebad */ ajDie("Bad format in codes file %S after '%S \"%S\"'", codeFName, codeCode, codeValue); codeCode = codeValue = NULL; /* saved in the table */ ajRegFree(&codexp); ajStrDel(&codeText); ajStrDel(&codeFName); ajStrDel(&codeRoot); ajStrDel(&codeRootInst); ajStrDel(&codePack); ajStrDel(&codeLanguage); ajStrDel(&tmpstr); acdCodeSet = ajTrue; return; } ID acdKnowntypeInit TY static MO ajacd LB acd XX DE Sets up the known type data XX RT void RD RX // static void acdKnowntypeInit(void) { if(acdKnowntypeSet) return; acdReadKnowntype(&acdKnowntypeDescTable, &acdKnowntypeTypeTable); acdKnowntypeSet = ajTrue; return; } ID acdSetQualAppl TY static MO ajacd LB acd XX DE Sets internal variables for the application booleans -debug etc. XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r val AjBool PD Value PX RT AjBool RD ajTrue if this was an application-wide variable. RX // static AjBool acdSetQualAppl(const AcdPAcd thys, AjBool val) { ajint i = 0; AjBool setval; AjPStr setstr = NULL; static AjPStr valstr = NULL; static AjPStr bufstr = NULL; acdLog("acdSetQualAppl '%S'\n", thys->Name); for(i=0; acdQualAppl[i].Name; i++) { if(ajStrMatchC(thys->Name, acdQualAppl[i].Name)) { if(thys->Defined) /* User put it on the command line */ { setval = val; acdLog("Appl qualifier defined %S = %b\n", thys->Name, setval); } else /* look for a variable */ { ajFmtPrintS(&setstr, "%S", thys->Name); if(ajNamGetValueS(setstr, &valstr)) { ajStrToBool(valstr, &setval); acdLog("Appl qualifier variable %S = %b\n", setstr, setval); } else /* nothing found, use the default value */ setval = val; ajStrDel(&setstr); } switch(i) /* see acdQualAppl for the correct order */ { case 0: acdAuto = setval; break; case 1: acdStdout = setval; break; case 2: acdFilter = setval; if(acdFilter) { acdAuto = ajTrue; acdStdout = ajTrue; } break; case 3: acdOptions = setval; break; case 4: acdDebug = setval; /* acdLog("acdSetQualAppl acdDebug %B\n", acdDebug); */ acdDebugSet = ajTrue; if(ajNamGetValueC("debugbuffer", &bufstr)) { ajStrToBool(bufstr, &acdDebugBuffer); } break; case 5: acdVerbose = setval; break; case 6: acdDoHelp = setval; break; case 7: AjErrorLevel.warning = setval; break; case 8: AjErrorLevel.error = setval; break; case 9: AjErrorLevel.fatal = setval; break; case 10: AjErrorLevel.die = setval; break; } return ajTrue; } } return ajFalse; } ID acdSelectPrompt TY static MO ajacd LB acd XX DE Present the options as a simple numbered list XX PN [1] PA r thys const AcdPAcd PD ACD object PX RT void RD RX // static void acdSelectPrompt(const AcdPAcd thys) { const AjPStr hdrstr; AjPStr delim = NULL; AjPStr value = NULL; AjPStrTok handle; AjPStr line = NULL; static const char* white = " \t\n\r"; ajint i = 0; if(acdAuto) return; hdrstr = acdAttrValue(thys, "header"); if(ajStrGetLen(hdrstr)) ajUserDumpS(hdrstr); ajStrAssignS(&delim,acdAttrValue(thys, "delimiter")); if(!ajStrGetLen(delim)) ajStrAssignC(&delim, ";"); ajStrAssignS(&value, acdAttrValue(thys, "values")); acdVarResolve(&value); if(!ajStrGetLen(value)) if(!acdKnownValueSelect(thys, &value)) acdError("No value defined for selection"); handle = ajStrTokenNewS(value, delim); while(ajStrTokenNextFind(&handle, &line)) { ajStrTrimC(&line, white); ajUser(" %5d : %S", ++i, line); } ajStrTokenDel(&handle); ajStrDel(&line); ajStrDel(&value); ajStrDel(&delim); return; } ID acdListPrompt TY static MO ajacd LB acd XX DE Present the options as a list with option codes selectable by the user XX PN [1] PA r thys const AcdPAcd PD ACD object PX RT void RD RX // static void acdListPrompt(const AcdPAcd thys) { const AjPStr hdrstr; AjPStr codedelim = NULL; AjPStr delim = NULL; AjPStr value = NULL; AjPStrTok handle; AjPStrTok codehandle; AjPStr line = NULL; AjPStr code = NULL; AjPStr desc = NULL; ajuint margin = 8; static const char* white = " \t\n\r"; if(acdAuto) return; hdrstr = acdAttrValue(thys, "header"); if(ajStrGetLen(hdrstr)) ajUserDumpS(hdrstr); ajStrAssignS(&delim,acdAttrValue(thys, "delimiter")); if(!ajStrGetLen(delim)) { ajStrAssignC(&delim, ";"); } ajStrAssignS(&codedelim,acdAttrValue(thys, "codedelimiter")); if(!ajStrGetLen(codedelim)) { ajStrAssignC(&codedelim, ":"); } ajStrAssignS(&value, acdAttrValue(thys, "values")); acdVarResolve(&value); if(!ajStrGetLen(value)) if(!acdKnownValueList(thys, &value)) acdError("No value defined for list"); handle = ajStrTokenNewS(value, delim); while(ajStrTokenNextFind(&handle, &line)) { codehandle = ajStrTokenNewS(line, codedelim); ajStrTokenNextParse(&codehandle, &code); ajStrTrimC(&code, white); if(ajStrGetLen(code) > margin) margin = ajStrGetLen(code); ajStrTokenDel(&codehandle); } ajStrTokenAssignS(&handle, value, delim); while(ajStrTokenNextFind(&handle, &line)) { codehandle = ajStrTokenNewS(line, codedelim); ajStrTokenNextParse(&codehandle, &code); ajStrTokenNextParseS(&codehandle, delim, &desc); ajStrTrimC(&code, white); ajStrTrimC(&desc, white); ajUser(" %*S : %S", margin, code, desc); ajStrTokenDel(&codehandle); } ajStrTokenDel(&handle); ajStrDel(&line); ajStrDel(&code); ajStrDel(&desc); ajStrDel(&delim); ajStrDel(&codedelim); ajStrDel(&value); return; } ID acdListValue TY static MO ajacd LB acd XX DE Checks the user setting against the menu list of codes and descriptions. DE DE An unambiguous match to the codes counts as valid. DE If this fails, an unambiguous match to the descriptions counts. XX PN [1] PA r thys const AcdPAcd PD ACD Object PX PN [2] PA r min ajint PD Minimum number of values required PX PN [3] PA r max ajint PD Maximum number of values required PX PN [4] PA r reply const AjPStr PD Default value PX RT AjPStr* RD Array of accepted matches, ending with a NULL. RX // static AjPStr* acdListValue(const AcdPAcd thys, ajint min, ajint max, const AjPStr reply) { AjPStr* val = NULL; AjPStr codedelim = NULL; AjPStr delim = NULL; AjPStr value = NULL; AjBool exactcase; AjPStrTok handle; AjPStrTok rephandle; AjPStrTok codehandle; AjPStr line = NULL; AjPStr code = NULL; AjPStr desc = NULL; AjPList list = NULL; AjPStr repstr = NULL; AjPStr hitstr = NULL; AjPStr validstr = NULL; AjPStr hitstr1 = NULL; AjPStr hitstr2 = NULL; AjPStr ambigList = NULL; AjPStr repdelim = NULL; static const char* white = " \t\n\r"; ajint k = 0; ajint ifound = 0; ajint jfound = 0; ajint ilen; ajint itoken = 0; AjBool ok = ajTrue; list = ajListstrNew(); acdAttrToBool(thys, "casesensitive", ajFalse, &exactcase); acdAttrValueStr(thys, "delimiter", ";", &delim); acdAttrValueStr(thys, "codedelimiter", ":", &codedelim); if(!repdelim) { repdelim = ajStrNewRes(10); ajStrAssignC(&repdelim, ","); } ajStrAssignS(&value,acdAttrValue(thys, "values")); acdVarResolve(&value); if(!ajStrGetLen(value)) if(!acdKnownValueList(thys, &value)) acdError("No value defined for list"); ambigList = ajStrNew(); ajStrAssignClear(&validstr); rephandle = ajStrTokenNewS(reply, repdelim); while(ajStrTokenNextParse(&rephandle, &repstr)) { itoken++; acdLog("testing '%S'\n", repstr); handle = ajStrTokenNewS(value, delim); ifound = jfound = 0; ajStrAssignClear(&validstr); if(ajStrMatchC(repstr, "*")) { while(ajStrTokenNextFind(&handle, &line)) { codehandle = ajStrTokenNewS(line, codedelim); ajStrTokenNextParse(&codehandle, &code); ajStrTrimC(&code, white); ajStrAssignS(&hitstr, code); ajListstrPushAppend(list, hitstr); hitstr = NULL; ajStrTokenDel(&codehandle); } } else { while(ajStrTokenNextFind(&handle, &line)) { codehandle = ajStrTokenNewS(line, codedelim); ajStrTokenNextParse(&codehandle, &code); ajStrTokenNextParseS(&codehandle, delim, &desc); ajStrTrimC(&code, white); ajStrTrimC(&desc, white); if(ajStrGetLen(validstr)) ajStrAppendK(&validstr, ','); ajStrAppendS(&validstr, code); if(ajStrMatchS(code, repstr) || (!exactcase && ajStrMatchCaseS(code, repstr))) { ifound = 1; ajStrAssignS(&hitstr1, code); break; } if(ajStrMatchS(desc, repstr) || (!exactcase && ajStrMatchCaseS(desc, repstr))) { jfound = 1; ajStrAssignS(&hitstr2, code); break; } if(ajStrPrefixS(code, repstr) || (!exactcase && ajStrPrefixCaseS(code, repstr))) { ifound++; ajStrAssignS(&hitstr1, code); acdAmbigApp(&ambigList, code); } if(ajStrPrefixS(desc, repstr) || (!exactcase && ajStrPrefixCaseS(desc, repstr))) { jfound++; ajStrAssignS(&hitstr2, code); acdAmbigApp(&ambigList, desc); } ajStrTokenDel(&codehandle); codehandle = NULL; } /* end of while */ ajStrTokenDel(&codehandle); if(ifound == 1) { ajStrAssignS(&hitstr,hitstr1); ajListstrPushAppend(list, hitstr); hitstr = NULL; } else if(jfound == 1) { ajStrAssignS(&hitstr, hitstr2); ajListstrPushAppend(list, hitstr); hitstr = NULL; } else { if(ifound || jfound) /* test acdc-listambig1 acdc-listambig2 */ ajErr("'%S' is ambiguous (%S)", repstr, ambigList); else /* test acdc-listbad */ ajErr("'%S' is not a valid menu option\n" "Accepted short codes are: %S", repstr, validstr); ok = ajFalse; break; } ajStrTokenDel(&handle); } } ajStrTokenDel(&rephandle); ajStrDel(&repstr); ilen = (ajuint) ajListstrGetLength(list); acdLog("Found %d matches OK: %b min: %d max: %d\n", ilen, ok, min, max); if(ok) { if(ilen < min) { /* test acdc-listmin */ if(min <= 1) ajErr("Menu needs %d value", min); else ajErr("Menu needs %d values", min); ok = ajFalse; } if(ilen > max) /* test acdc-listmax */ { if(max <= 1) ajErr("Menu allows no more than %d value", max); else ajErr("Menu allows no more than %d values", max); ok = ajFalse; } } if(ok) { AJCNEW0(val, ilen+1); for(k = 0; k < ilen; k++) { ajListstrPop(list, &val[k]); acdLog("Accept[%d]: '%S'\n", k,val[k]); } } acdLog("Found %d matches\n", ilen); acdLog("Menu length now %d\n", ajListstrGetLength(list)); if(ok) acdLog("Before return val[0] '%S'\n", val[0]); ajListstrFreeData(&list); ajStrDel(&delim); ajStrDel(&codedelim); ajStrDel(&repdelim); ajStrDel(&line); ajStrDel(&code); ajStrDel(&desc); ajStrDel(&ambigList); ajStrDel(&hitstr1); ajStrDel(&hitstr2); ajStrDel(&repstr); ajStrDel(&validstr); ajStrDel(&value); if(!ok) return NULL; return val; } ID acdSelectValue TY static MO ajacd LB acd XX DE Checks the user setting against the selection list set of codes DE DE An unambiguous match to the codes counts as valid. DE If this fails, an unambiguous match to the descriptions counts. XX PN [1] PA r thys const AcdPAcd PD ACD Object PX PN [2] PA r min ajint PD Minimum number of values required PX PN [3] PA r max ajint PD Maximum number of values required PX PN [4] PA r reply const AjPStr PD Default value PX RT AjPStr* RD Array of accepted matches, ending with a NULL. RX // static AjPStr* acdSelectValue(const AcdPAcd thys, ajint min, ajint max, const AjPStr reply) { AjPStr *val = NULL; AjPStr delim=NULL; const AjPStr tmpstr = NULL; AjPStr value = NULL; AjBool exactcase; AjPStrTok handle = NULL; AjPStrTok rephandle = NULL; AjPStr line = NULL; AjPStr code = NULL; AjPStr desc = NULL; AjPList list = NULL; AjPStr repstr = NULL; AjPStr hitstr = NULL; AjPStr validstr = NULL; AjPStr hitstr2 = NULL; AjPStr ambigList = NULL; AjPStr repdelim = NULL; static const char* white = " \t\n\r"; ajint i = 0; ajint k = 0; ajint jfound = 0; ajint icnt = 0; ajint ilen; ajint itoken = 0; AjBool ok = ajTrue; list = ajListstrNew(); acdAttrToBool(thys, "casesensitive", ajFalse, &exactcase); tmpstr = acdAttrValue(thys, "delimiter"); if(ajStrGetLen(tmpstr)) ajStrAssignS(&delim, tmpstr); else ajStrAssignC(&delim, ";"); if(!repdelim) { repdelim = ajStrNewRes(10); ajStrAssignC(&repdelim, ","); } ajStrAssignS(&value, acdAttrValue(thys, "values")); acdVarResolve(&value); if(!ajStrGetLen(value)) if(!acdKnownValueSelect(thys, &value)) acdError("No value defined for selection"); ajStrAssignClear(&ambigList); ajStrAssignClear(&validstr); rephandle = ajStrTokenNewS(reply, repdelim); while(ajStrTokenNextParse(&rephandle, &repstr)) { itoken++; acdLog("testing '%S'\n", repstr); handle = ajStrTokenNewS(value, delim); i = jfound = 0; if(ajStrMatchC(repstr, "*")) { for(icnt = 1; ajStrTokenNextFind(&handle, &desc); icnt++) { ajStrAssignS(&hitstr, desc); ajListstrPushAppend(list, hitstr); hitstr = NULL; } } else { for(icnt = 1; ajStrTokenNextFind(&handle, &desc); icnt++) { ajStrTrimC(&desc, white); if(itoken == 1) { if(ajStrGetLen(validstr)) ajStrAppendK(&validstr, ','); ajStrAppendS(&validstr, desc); } if(ajStrMatchS(desc, repstr) || (!exactcase && ajStrMatchCaseS(desc, repstr))) { jfound = 1; ajStrAssignS(&hitstr2, desc); break; } if(ajStrPrefixS(desc, repstr) || (!exactcase && ajStrPrefixCaseS(desc,repstr))) { jfound++; ajStrAssignS(&hitstr2, desc); acdAmbigApp(&ambigList, desc); } if(ajStrToInt(repstr, &i) && i == icnt) { jfound++; ajStrAssignS(&hitstr2, desc); acdAmbigApp(&ambigList, repstr); } } /* end of while */ if(jfound == 1) { ajStrAssignS(&hitstr, hitstr2); ajListstrPushAppend(list, hitstr); hitstr = NULL; } else { if(jfound) /* test acdc-selectambig */ ajErr("'%S' is ambiguous (%S)", repstr, ambigList); else /* test acdc-selectbad */ ajErr("'%S' is not a valid selection list option\n" "Accepted values are: %S", repstr, validstr); ok = ajFalse; break; } ajStrTokenDel(&handle); } } ajStrTokenDel(&rephandle); ajStrDel(&repstr); ilen = (ajuint) ajListstrGetLength(list); if(ok) { if(ilen < min) /* test acdc-selectmin */ { if(min <= 1) ajErr("Selection list needs %d value", min); else ajErr("Selection list needs %d values", min); ok = ajFalse; } if(ilen > max) /* test acdc-selectmax */ { if(max <= 1) ajErr("Selection list allows no more than %d value", max); else ajErr("Selection list allows no more than %d values", max); ok = ajFalse; } } if(ok) { AJCNEW0(val, ilen+1); for(k = 0; k < ilen; k++) ajListstrPop(list, &val[k]); } acdLog("Found %d matches\n", ilen); ajListstrFreeData(&list); ajStrDel(&value); ajStrDel(&line); ajStrDel(&code); ajStrDel(&desc); ajStrDel(&ambigList); ajStrDel(&hitstr2); ajStrDel(&delim); ajStrDel(&repdelim); ajStrDel(&validstr); if(!ok) return NULL; return val; } ID acdAmbigApp TY static MO ajacd LB acd XX DE Appends a token to a list, with commas as delimiters. Used to DE build a list of ambiguous matches for messages. XX PN [1] PA w pambigList AjPStr* PD List of tokens with ',' delimiter PX PN [2] PA r str const AjPStr PD Latest token to add PX RT void RD RX // static void acdAmbigApp(AjPStr* pambigList, const AjPStr str) { if(ajStrGetLen(*pambigList)) ajStrAppendC(pambigList, ","); ajStrAppendS(pambigList, str); return; } ID acdAmbigAppC TY static MO ajacd LB acd XX DE Appends a token to a list, with commas as delimiters. Used to DE build a list of ambiguous matches for messages. XX PN [1] PA w pambigList AjPStr* PD List of tokens with ',' delimiter PX PN [2] PA r txt const char* PD Latest token to add PX RT void RD RX // static void acdAmbigAppC(AjPStr* pambigList, const char* txt) { if(ajStrGetLen(*pambigList)) ajStrAppendC(pambigList, ","); ajStrAppendC(pambigList, txt); return; } ID acdDataFilename TY static MO ajacd LB acd XX DE Sets a default data file name. If no values are provided, it will be DE programname.dat XX PN [1] PA w infname AjPStr* PD Resulting file name PX PN [2] PA r name const AjPStr PD File name PX PN [3] PA r ext const AjPStr PD File extension PX PN [4] PA r nullok AjBool PD Can set as an empty string if true PX RT AjBool RD ajTrue if a name was successfully set RX // static AjBool acdDataFilename(AjPStr* infname, const AjPStr name, const AjPStr ext, AjBool nullok) { AjBool ret = ajTrue; if(ajStrGetLen(name)) ajStrAssignS(infname, name); else if(!nullok) ajStrAssignS(infname, acdProgram); else ajStrAssignClear(infname); if(ajStrGetLen(ext)) ajFilenameReplaceExtS(infname, ext); else if (!nullok) ajFilenameReplaceExtC(infname, "dat"); return ret; } ID acdInFilename TY static MO ajacd LB acd XX DE Sets a default input file name. If filtering is on, this will be stdin. DE Otherwise it is blank. XX PN [1] PA w infname AjPStr* PD Input file name PX RT AjBool RD ajTrue if a name was successfully set RX // static AjBool acdInFilename(AjPStr* infname) { AjBool ret = ajFalse; if(!acdInFile && acdFilter) { ajStrAssignC(infname, "stdin"); ret = ajTrue; } else ajStrAssignClear(infname); acdInFile++; return ret; } ID acdOutDirectory TY static MO ajacd LB acd XX DE Sets a default output file directory. Uses the _OUTDIRECTORY variable DE as a default value, but any input string overrides it. DE DE The recommendation is that the directory should always be provided DE in the emboss.defaults file or by an environment variable. DE DE As for all associated qualifiers, it is also possible to set a value DE in the ACD file. XX PN [1] PA w dir AjPStr* PD Specified directory PX RT AjBool RD ajTrue if a directory was successfully set RX // static AjBool acdOutDirectory(AjPStr* dir) { AjBool ret = ajFalse; AjPStr mydir = NULL; acdLog("acdOutDirectory ('%S')\n", *dir); if(!acdDirectoryDef) if(!ajNamGetValueC("outdirectory", &acdDirectoryDef)) ajStrAssignClear(&acdDirectoryDef); if(dir && ajStrGetLen(*dir)) ajStrAssignS(&mydir, *dir); else ajStrAssignS(&mydir, acdDirectoryDef); if(ajStrGetLen(mydir)) { ajDirnameFix(&mydir); ajStrAssignS(dir, mydir); ret = ajTrue; } else { ajStrAssignClear(dir); ret = ajFalse; } acdLog(". . . dir '%S' ret: %B\n", *dir, ret); ajStrDel(&mydir); return ret; } ID acdOutFilename TY static MO ajacd LB acd XX DE Sets a default output file name. If stdout or filtering are on, DE this will be stdout for the first output file. DE Otherwise it is built from the defaults provided. DE DE The base file name is usually specified in the ACD file as name: DE and passed in by the calling acdSet function. The default will DE be the base file name saved from the first input file, or "outfile" DE DE The extension is usually specified in the ACD file as extension: DE and passed in by the calling acdSet function. The default will DE be the program name for the first file, and "out2", "out3" and so DE on for later files. DE DE The recommendation is that the extension should always be provided DE in the ACD file, but that the base file name should be taken from DE the input file in most cases. XX PN [1] PA w outfname AjPStr* PD Input file name PX PN [2] PA r name const AjPStr PD Specified base file name PX PN [3] PA r ext const AjPStr PD Specified extension PX RT AjBool RD ajTrue if a name was successfully set RX // static AjBool acdOutFilename(AjPStr* outfname, const AjPStr name, const AjPStr ext) { AjBool ret = ajFalse; acdLog("acdOutFilename ('%S', '%S', '%S') acdStdout: %B\n", *outfname, name, ext, acdStdout); if(!acdOutFile && acdStdout) /* first outfile, running as a filter */ { ajStrAssignC(outfname, "stdout"); acdLog("outfile '%S'\n", *outfname); acdOutFile++; return ajTrue; } ajStrAssignEmptyS(&acdOutFName, name); /* use name if given */ ajStrFmtWord(&acdOutFName); ajStrAssignEmptyS(&acdOutFName, acdInFName); /* else use saved name */ ajStrAssignEmptyC(&acdOutFName, "outfile"); /* else, use "outfile" */ ajStrAssignEmptyS(&acdOutFExt, ext); /* use extension if given */ if(!acdOutFile) ajStrAssignEmptyS(&acdOutFExt, acdProgram); /* else try program name for first file */ if(!ajStrGetLen(acdOutFExt)) /* if all else fails, use out2 etc. */ ajFmtPrintS(&acdOutFExt, "out%d", acdOutFile+1); acdLog(". . . acdOutFName '%S', acdOutFExt '%S'\n", acdOutFName, acdOutFExt); if(ext && ajStrGetLen(acdOutFExt)) /* NULL ext means add no extension */ ajFmtPrintS(outfname, "%S.%S", acdOutFName, acdOutFExt); else ajStrAppendS(outfname, acdOutFName); acdOutFile++; acdLog("outfile %d %S.%S\n", acdOutFile, acdOutFName, acdOutFExt); ajStrDelStatic(&acdOutFName); ajStrDelStatic(&acdOutFExt); return ret; } ID acdInFileSave TY static MO ajacd LB acd XX DE For the first call, saves the input filename for use in building output DE file name(s). XX PN [1] PA r infname const AjPStr PD Input file name PX PN [2] PA r objname const AjPStr PD Input object name (or NULL) PX PN [3] PA r reset AjBool PD Reset the saved name if this is the first time PD a true value has been passed. PX RT AjBool RD ajTrue if a name was successfully set RX // static AjBool acdInFileSave(const AjPStr infname, const AjPStr objname, AjBool reset) { static AjBool usefile = AJFALSE; static ajint called = 0; AjBool useobj = ajTrue; if(!called) { if(ajNamGetValueC("acdfilename", &acdTmpStr)) ajStrToBool(acdTmpStr, &usefile); called = 1; } if(acdInFileSet) /* already have a name */ return ajFalse; if(!reset && ajStrGetLen(acdInFName)) /* have a name, no reset forced */ return ajFalse; if(usefile) { useobj = ajFalse; if(ajStrMatchC(infname, "stdin")) useobj = ajTrue; } acdLog("acdInFileSave (%S,%S) reset: %B usefile: %B, saved name '%S'\n", infname, objname, reset, usefile, acdInFName); if(useobj && ajStrGetLen(objname)) { if(!ajStrGetLen(objname)) return ajFalse; ajStrAssignS(&acdInFName, objname); ajFilenameTrimAll(&acdInFName); ajStrFmtLower(&acdInFName); } else { if(!ajStrGetLen(infname)) return ajFalse; ajStrAssignS(&acdInFName, infname); ajFilenameTrimAll(&acdInFName); if(useobj) ajStrFmtLower(&acdInFName); } if(reset) acdInFileSet = ajTrue; acdLog("acdInFileSave (%S, %S) input file set to '%S'\n", infname, objname, acdInFName); return ajTrue; } ID acdInTypeSeqSave TY static MO ajacd LB acd XX DE For the first call, saves the input filename for use in building output DE file name(s). XX PN [1] PA r intype const AjPStr PD Input sequence type PX RT AjBool RD ajTrue if a type was successfully set RX // static AjBool acdInTypeSeqSave(const AjPStr intype) { if(acdInTypeSeqName) return ajFalse; acdLog("acdInTypeSeqSave (%S)\n", intype); if(!ajStrGetLen(intype)) { ajStrAssignClear(&acdInTypeSeqName); acdLog("Input sequence type defaults to ''\n", acdInTypeSeqName); acdInTypeFeatSave(NULL); } else { ajStrAssignS(&acdInTypeSeqName, intype); ajStrFmtLower(&acdInTypeSeqName); if(ajSeqTypeIsAny(intype)) acdInTypeFeatSaveC(""); else if(ajSeqTypeIsProt(intype)) acdInTypeFeatSaveC("protein"); else if(ajSeqTypeIsNuc(intype)) acdInTypeFeatSaveC("nucleotide"); else acdInTypeFeatSaveC(""); } acdLog("acdInTypeSeqSave (%S) input type set to '%S'\n", intype, acdInTypeSeqName); return ajTrue; } ID acdInTypeSeq TY static MO ajacd LB acd XX DE Returns the input sequence type (if known) XX PN [1] PA w typename AjPStr* PD Input sequence type PX RT AjBool RD ajTrue if a type was successfully set RX // static AjBool acdInTypeSeq(AjPStr* typename) { AjBool ret = ajFalse; acdLog("acdInTypeSeq saved acdInTypeSeqName '%S'\n", acdInTypeSeqName); if(acdInTypeSeqName) /* could be an empty string */ { ajStrAssignS(typename, acdInTypeSeqName); ret = ajTrue; } else { ajStrAssignClear(typename); /* allow anything, return ajFalse */ ret = ajFalse; } return ret; } ID acdInTypeFeatSave TY static MO ajacd LB acd XX DE Saves the input feature type for use in setting the default output type XX PN [1] PA r intype const AjPStr PD Input feature type PX RT AjBool RD ajTrue if a type was successfully set RX // static AjBool acdInTypeFeatSave(const AjPStr intype) { if(acdInTypeFeatName) return ajFalse; acdLog("acdInTypeFeatSave (%S)\n", intype); if(!ajStrGetLen(intype)) { ajStrAssignClear(&acdInTypeFeatName); acdLog("Input feature type defaults to '%S'\n", acdInTypeFeatName); } else ajStrAssignS(&acdInTypeFeatName, intype); ajStrFmtLower(&acdInTypeFeatName); acdLog("acdInTypeFeatSave (%S) input feature type set to '%S'\n", intype, acdInTypeFeatName); return ajTrue; } ID acdInTypeFeatSaveC TY static MO ajacd LB acd XX DE Saves the input feature type for use in setting the default output type XX PN [1] PA r intype const char* PD Input feature type PX RT AjBool RD ajTrue if a type was successfully set RX // static AjBool acdInTypeFeatSaveC(const char* intype) { AjBool ret; AjPStr tmpstr = NULL; tmpstr = ajStrNewC(intype); ret = acdInTypeFeatSave(tmpstr); ajStrDel(&tmpstr); return ret; } ID acdInTypeFeat TY static MO ajacd LB acd XX DE Returns the input feature type (if known) XX PN [1] PA w typename AjPStr* PD Input feature type PX RT AjBool RD ajTrue if a type was successfully set RX // static AjBool acdInTypeFeat(AjPStr* typename) { AjBool ret = ajFalse; acdLog("acdInTypeFeat saved acdInTypeFeatName '%S'\n", acdInTypeFeatName); if(acdInTypeFeatName) /* could be an empty string */ { ajStrAssignS(typename, acdInTypeFeatName); ret = ajTrue; } else { ajStrAssignClear(typename); /* allow anything, return ajFalse */ ret = ajFalse; } return ret; } ID acdLog TY static MO ajacd LB acd XX DE Writes a message to the .acdlog file XX PN [1] PA r fmt const char* PD Format with ajFmt extensions PX PN [2] PA v vararg ... PD Optional arguments PX RT void RD RX // static void acdLog(const char *fmt, ...) { va_list args; if(!acdDoLog) return; if(!acdLogFName) { ajFmtPrintS(&acdLogFName, "%S.acdlog", acdProgram); acdLogFile = ajFileNewOutNameS(acdLogFName); ajFileSetUnbuffer(acdLogFile); } va_start(args, fmt) ; ajFmtVPrintF(acdLogFile, fmt, args); va_end(args) ; return; } ID acdPretty TY static MO ajacd LB acd XX DE Writes a pretty formatted version of the .acd syntax DE message to the .acdpretty file XX PN [1] PA r fmt const char* PD Format with ajFmt extensions PX PN [2] PA v vararg ... PD Optional arguments PX RT void RD RX // static void acdPretty(const char *fmt, ...) { va_list args ; static AjPStr tmpstr = NULL; ajint *icword = NULL; ajint *icpos = NULL; AjPStr cmtstr = NULL; ajint lastword; ajint wordcount; ajuint ipos; ajint i = 0; ajint j = 0; ajint ccnt=0; if(!acdDoPretty) return; if(!acdPrettyFName) { if(acdStdout) acdPrettyFile = ajFileNewFromCfile(stdout); else { ajFmtPrintS(&acdPrettyFName, "%S.acdpretty", acdProgram); acdPrettyFile = ajFileNewOutNameS(acdPrettyFName); ajFileSetUnbuffer(acdPrettyFile); ajFmtPrint("Created %S\n", acdPrettyFName); } } va_start(args, fmt); ajFmtVPrintS(&tmpstr, fmt, args); va_end(args); /* ** test for comment block before this line */ while(ajListGetLength(acdListCommentsCount) && (acdCmtWord <= acdWordSave)) { ajListPeek(acdListCommentsCount, (void**) &icword); ajListPeek(acdListCommentsColumn, (void**) &icpos); acdCmtWord = *icword; if((acdCmtWord == acdWordSave && !*icpos) || (acdCmtWord<=acdWordSave)) { if(!ajStrGetLen(cmtstr)) ajFmtPrintF(acdPrettyFile, "\n"); ajListPop(acdListCommentsCount, (void**) &icword); ajListPop(acdListCommentsColumn, (void**) &icpos); ajListstrPop(acdListComments, &cmtstr); acdPrettyComment(cmtstr); ajStrDel(&cmtstr); AJFREE(icword); AJFREE(icpos); ccnt++; } } if(ccnt && ajStrGetCharFirst(tmpstr) != '\n') ajFmtPrintF(acdPrettyFile, "\n"); while(ajStrGetCharFirst(tmpstr) == '\n') { ajFmtPrintF(acdPrettyFile, "\n"); ajStrCutStart(&tmpstr, 1); } wordcount = ajStrParseCount(tmpstr); lastword = acdWordSave + wordcount; /* ** now check for inline comment on this line of text */ if(ajListGetLength(acdListCommentsCount) && (acdCmtWord <= lastword)) { ajListPeek(acdListCommentsCount, (void**) &icword); ajListPeek(acdListCommentsColumn, (void**) &icpos); acdCmtWord = *icword; if((acdCmtWord == lastword && *icpos) | (acdCmtWord < lastword)) { ajListPop(acdListCommentsCount, (void**) &icword); ajListPop(acdListCommentsColumn, (void**) &icpos); ajListstrPop(acdListComments, &cmtstr); ajStrRemoveWhiteExcess(&cmtstr); if(!ajStrPrefixC(cmtstr, "#")) ajStrInsertC(&cmtstr, 0, "#"); if((ajStrGetLen(cmtstr) > 1) && !ajStrPrefixC(cmtstr, "# ")) ajStrInsertC(&cmtstr, 1, " "); i=0; while(ajStrGetCharLast(tmpstr) == '\n') { ajStrCutEnd(&tmpstr, 1); i++; } ipos = 75; if(ajStrGetLen(cmtstr) > (79 - ipos)) ipos = 79 - ajStrGetLen(cmtstr); if(ipos <= (ajStrGetLen(tmpstr)+acdPrettyMargin)) ipos = ajStrGetLen(tmpstr)+acdPrettyMargin+1; j = ipos-ajStrGetLen(tmpstr)-acdPrettyMargin; ajStrAppendCountK(&tmpstr, ' ', j); ajStrAppendS(&tmpstr, cmtstr); if(i) ajStrAppendCountK(&tmpstr, '\n', i); ajStrDel(&cmtstr); AJFREE(icword); AJFREE(icpos); } } if(acdPrettyMargin) ajFmtPrintF(acdPrettyFile, "%.*s", acdPrettyMargin, " "); ajFmtPrintF(acdPrettyFile, "%S", tmpstr); acdWordSave += wordcount; return; } ID acdPrettyClose TY static MO ajacd LB acd XX DE Writes any remaining comments and closes the pretty output file XX RT void RD RX // static void acdPrettyClose(void) { ajint *icword = NULL; ajint *icpos = NULL; AjPStr cmtstr = NULL; if(!acdDoPretty) return; if(!acdPrettyFName) { if(acdStdout) acdPrettyFile = ajFileNewFromCfile(stdout); else { ajFmtPrintS(&acdPrettyFName, "%S.acdpretty", acdProgram); acdPrettyFile = ajFileNewOutNameS(acdPrettyFName); ajFileSetUnbuffer(acdPrettyFile); ajFmtPrint("Created %S\n", acdPrettyFName); } } while(ajListGetLength(acdListCommentsCount) && (acdCmtWord <= acdWordSave)) { ajListPeek(acdListCommentsCount, (void**) &icword); acdCmtWord = *icword; if(acdCmtWord <= acdWordSave) { if(!ajStrGetLen(cmtstr)) ajFmtPrintF(acdPrettyFile, "\n"); ajStrDel(&cmtstr); ajListPop(acdListCommentsCount, (void**) &icword); ajListPop(acdListCommentsColumn, (void**) &icpos); ajListstrPop(acdListComments, &cmtstr); acdPrettyComment(cmtstr); AJFREE(icword); AJFREE(icpos); } } ajStrDel(&cmtstr); ajFileClose(&acdPrettyFile); return; } ID acdPrettyComment TY static MO ajacd LB acd XX DE Writes an indented one-line comment XX PN [1] PA r comment const AjPStr PD OComment to be printer PX RT void RD RX // static void acdPrettyComment(const AjPStr comment) { AjPStr tmpstr = NULL; if(!acdDoPretty) return; if(!acdPrettyFName) { if(acdStdout) acdPrettyFile = ajFileNewFromCfile(stdout); else { ajFmtPrintS(&acdPrettyFName, "%S.acdpretty", acdProgram); acdPrettyFile = ajFileNewOutNameS(acdPrettyFName); ajFileSetUnbuffer(acdPrettyFile); ajFmtPrint("Created %S\n", acdPrettyFName); } } ajStrAssignS(&tmpstr, comment); ajStrRemoveWhiteExcess(&tmpstr); if(!ajStrPrefixC(tmpstr, "#")) ajStrInsertC(&tmpstr, 0, "#"); if((ajStrGetLen(tmpstr) > 1) && !ajStrPrefixC(tmpstr, "# ")) ajStrInsertC(&tmpstr, 1, " "); /* ** margin is set for attributes ** reduce by 2 to align with definitions */ if(acdPrettyMargin) ajFmtPrintF(acdPrettyFile, "%.*s", acdPrettyMargin, " "); ajFmtPrintF(acdPrettyFile, "%S\n", tmpstr); ajStrDel(&tmpstr); return; } ID acdPrettyWrap TY static MO ajacd LB acd XX DE Writes a pretty formatted version of the .acd syntax DE line to the .acdpretty file XX PN [1] PA r left ajint PD Extra left margin for follow-on lines PX PN [2] PA r fmt const char* PD Format with ajFmt extensions PX PN [3] PA v vararg ... PD Optional arguments PX RT void RD RX // static void acdPrettyWrap(ajint left, const char *fmt, ...) { va_list args; static AjPStr tmpstr = NULL; ajint leftmargin = left + acdPrettyMargin; ajint width = 78 - leftmargin; ajint *icword = NULL; ajint *icpos = NULL; AjPStr cmtstr = NULL; ajint lastword; ajint wordcount; ajuint ipos; ajint ccnt = 0; ajlong jpos; ajlong kpos; ajlong ilen; AjIList itercmt; AjIList iterpos; AjIList iterword; if(!acdDoPretty) return; if(!acdPrettyFName) { if(acdStdout) acdPrettyFile = ajFileNewFromCfile(stdout); else { ajFmtPrintS(&acdPrettyFName, "%S.acdpretty", acdProgram); acdPrettyFile = ajFileNewOutNameS(acdPrettyFName); ajFileSetUnbuffer(acdPrettyFile); } } /* ** whole line comments before wrapped text */ while(ajListGetLength(acdListCommentsCount) && (acdCmtWord <= acdWordSave)) { ajListPeek(acdListCommentsCount, (void**) &icword); acdCmtWord = *icword; if(acdCmtWord <= acdWordSave) { if(!ajStrGetLen(cmtstr)) ajFmtPrintF(acdPrettyFile, "\n"); ajListPop(acdListCommentsCount, (void**) &icword); ajListPop(acdListCommentsColumn, (void**) &icpos); ajListstrPop(acdListComments, &cmtstr); acdPrettyComment(cmtstr); ajStrDel(&cmtstr); AJFREE(icword); AJFREE(icpos); } } va_start(args, fmt); ajFmtVPrintS(&tmpstr, fmt, args); va_end(args); ajStrExchangeCC(&tmpstr, " \\ ", " \\\n"); /* force newlines at '\' */ wordcount = ajStrParseCount(tmpstr); lastword = acdWordSave + wordcount; /* ** Now check for inline comments in these lines of text */ /* ** First iterate to get comment max length */ ipos = 75; if(ajListGetLength(acdListCommentsCount) && (acdCmtWord <= lastword)) { ajListPeek(acdListCommentsCount, (void**) &icword); acdCmtWord = *icword; itercmt = ajListIterNewread(acdListComments); iterpos = ajListIterNewread(acdListCommentsColumn); iterword = ajListIterNewread(acdListCommentsCount); while(!ajListIterDone(iterword) && (*icword <= lastword)) { icword = ajListIterGet(iterword); icpos = ajListIterGet(iterpos); cmtstr = ajListIterGet(itercmt); if(*icword <= lastword && *icpos > (acdPrettyMargin+3)) { ccnt++; if(ajStrGetLen(cmtstr) > (79 - ipos)) ipos = 79 - ajStrGetLen(cmtstr); /*if(*icpos < (ajint) ipos) ipos = *icpos;*/ } } ajListIterDel(&itercmt); ajListIterDel(&iterpos); ajListIterDel(&iterword); } if((ajint) ipos <= (leftmargin + 20)) ipos = leftmargin + 20; width = ipos - leftmargin - 1; ajStrFmtWrapLeft(&tmpstr, width, acdPrettyMargin, left); /* ** Insert any comments at ends of lines */ jpos=-1; while(ccnt--) { ajListPop(acdListCommentsCount, (void**) &icword); ajListPop(acdListCommentsColumn, (void**) &icpos); ajListstrPop(acdListComments, &cmtstr); ajStrRemoveWhiteExcess(&cmtstr); if(!ajStrPrefixC(cmtstr, "#")) ajStrInsertC(&cmtstr, 0, "#"); if((ajStrGetLen(cmtstr) > 1) && !ajStrPrefixC(cmtstr, "# ")) ajStrInsertC(&cmtstr, 1, " "); kpos = ajStrFindNextK(tmpstr, jpos+1, '\n'); if(kpos < 0) { if(jpos == (ajint) ajStrGetLen(tmpstr)) { ajStrAppendK(&tmpstr, '\n'); ilen = 0; } else ilen = ajStrGetLen(tmpstr) - jpos - 1; while(ilen < (ajlong) ipos) { ajStrAppendK(&tmpstr, ' '); ilen++; } ajStrAppendS(&tmpstr, cmtstr); jpos = ajStrGetLen(tmpstr); } else { ilen = kpos - jpos - 1; jpos = kpos; while(ilen < (ajlong) ipos) { ajStrInsertK(&tmpstr, jpos, ' '); ilen++; jpos++; } ajStrInsertS(&tmpstr, jpos, cmtstr); jpos += ajStrGetLen(cmtstr); } ajStrDel(&cmtstr); AJFREE(icword); AJFREE(icpos); } ajFmtPrintF(acdPrettyFile, "%S\n", tmpstr); acdWordSave += wordcount; return; } ID acdPrettyShift TY static MO ajacd LB acd XX DE Right shifts (indents) acdpretty printing XX RT void RD RX // static void acdPrettyShift(void) { acdPrettyMargin += acdPrettyIndent; return; } ID acdPrettyUnShift TY static MO ajacd LB acd XX DE Left shifts acdpretty printing XX RT void RD RX // static void acdPrettyUnShift(void) { acdPrettyMargin -= acdPrettyIndent; if(acdPrettyMargin < 0) { ajWarn("acdpretty printing indent error - too many left shifts"); acdPrettyMargin = 0; } return; } ID acdIsQtype TY static MO ajacd LB acd XX DE Tests whether an ACD object is a qualifier or parameter type. If not we DE assume it is a keyword type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX RT AjBool RD ajTrue if the object is a qualifier or parameter RX // static AjBool acdIsQtype(const AcdPAcd thys) { if((thys->Level == ACD_QUAL) || (thys->Level == ACD_PARAM)) return ajTrue; return ajFalse; } ID acdIsStype TY static MO ajacd LB acd XX DE Tests whether an ACD object is a section or endsection type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX RT AjBool RD ajTrue if the object is a section or endsection RD type definition RX // static AjBool acdIsStype(const AcdPAcd thys) { if((thys->Level == ACD_SEC) || (thys->Level == ACD_ENDSEC)) return ajTrue; return ajFalse; } ID acdIsAtype TY static MO ajacd LB acd XX DE Tests whether an ACD object is an application type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX RT AjBool RD ajTrue if the object is an application type definition RX // static AjBool acdIsAtype(const AcdPAcd thys) { if(thys->Level == ACD_APPL) return ajTrue; return ajFalse; } ID acdIsVtype TY static MO ajacd LB acd XX DE Tests whether an ACD object is a variable type. XX PN [1] PA r thys const AcdPAcd PD ACD object PX RT AjBool RD ajTrue if the object is a variable type definition RX // static AjBool acdIsVtype(const AcdPAcd thys) { if(thys->Level == ACD_VAR) return ajTrue; return ajFalse; } ID acdTextFormat TY static MO ajacd LB acd XX DE Converts backslash codes in a string into special characters XX PN [1] PA u text AjPStr* PD Text with backslash codes PX RT AjBool RD ajTrue if successful RX // static AjBool acdTextFormat(AjPStr* text) { ajStrExchangeCC(text, " \\ ", "\n"); return ajTrue; } ID acdTextTrim TY static MO ajacd LB acd XX DE Trims white space. For HTML output, replaces angled brackets XX PN [1] PA u text AjPStr* PD Text to be processed PX RT void RD RX // static void acdTextTrim(AjPStr* text) { ajStrTrimC(text, " \t\n\r"); if(acdDoTable) { ajStrExchangeCC(text, "<", "<"); ajStrExchangeCC(text, ">", ">"); } return; } ID ajAcdPrintAppl TY public MO ajacd LB acd XX DE Report details of all known ACD attributes for all applications. DE For use by EMBOSS entrails. XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report - currently no extra details printed PX RT void RD RX // void ajAcdPrintAppl(AjPFile outf, AjBool full) { ajint i; AcdPAttr attr; AjPStr tmpstr = NULL; ajuint maxtmp = 0; if(full) ajFmtPrintF(outf, "\n"); else ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# ACD Application Attributes\n"); ajFmtPrintF(outf, "# Attribute Type Default Helptext\n"); ajFmtPrintF(outf, "AttrAppl {\n"); for(i=0; acdAttrAppl[i].Name; i++) { attr = &acdAttrAppl[i]; ajFmtPrintF(outf, " %-15s", attr->Name); ajFmtPrintF(outf, " %-10s", acdValNames[attr->Type]); ajFmtPrintS(&tmpstr, "\"%s\"", attr->Default); if(ajStrGetLen(tmpstr) > maxtmp) maxtmp = ajStrGetLen(tmpstr); ajFmtPrintF(outf, " %-12S", tmpstr); ajFmtPrintF(outf, " \"%s\"", attr->Help); ajFmtPrintF(outf, "\n"); } ajFmtPrintF(outf, "}\n\n"); if(maxtmp > 12) ajWarn("ajAcdPrintAppl max tmpstr len %d", maxtmp); ajStrDel(&tmpstr); return; } ID ajAcdPrintQual TY public MO ajacd LB acd XX DE Report details of all known ACD qualifiers for all applications. DE For use by EMBOSS entrails. XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report - currently no extra details printed PX RT void RD RX // void ajAcdPrintQual(AjPFile outf, AjBool full) { ajint i; AcdPQual qual; AjPStr tmpstr = NULL; ajuint maxtmp = 0; if(full) ajFmtPrintF(outf, "\n"); else ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# ACD Application Qualifiers\n"); ajFmtPrintF(outf, "# Qualifier Type Default Helptext\n"); ajFmtPrintF(outf, "QualAppl {\n"); for(i=0; acdQualAppl[i].Name; i++) { qual = &acdQualAppl[i]; ajFmtPrintF(outf, " %-15s", qual->Name); ajFmtPrintF(outf, " %-10s", qual->Type); ajFmtPrintS(&tmpstr, " \"%s\"", qual->Default); if(ajStrGetLen(tmpstr) > maxtmp) maxtmp = ajStrGetLen(tmpstr); ajFmtPrintF(outf, " %-12S", tmpstr); ajFmtPrintF(outf, " \"%s\"", qual->Help); ajFmtPrintF(outf, "\n"); } ajFmtPrintF(outf, "}\n\n"); if(maxtmp > 12) ajWarn("ajAcdPrintQual max tmpstr len %d", maxtmp); ajStrDel(&tmpstr); return; } ID ajAcdPrintType TY public MO ajacd LB acd XX DE Report details of all known ACD types. DE For use by EMBOSS entrails. XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report PX RT void RD RX // void ajAcdPrintType(AjPFile outf, AjBool full) { AcdPType pat; AcdPAttr attr; AcdPQual qual; ajint i; AjPStr tmpstr = NULL; ajuint maxtmp = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# ACD Types\n"); ajFmtPrintF(outf, "# Name Group Description\n"); ajFmtPrintF(outf, "# Attribute Type " "Default Comment\n"); ajFmtPrintF(outf, "# Qualifier Type " "Default Helptext\n"); ajFmtPrintF(outf, "AcdType {\n"); for(i=0; acdType[i].Name; i++) { pat = &acdType[i]; ajFmtPrintF(outf, " %-14s", pat->Name); ajFmtPrintF(outf, " %-10s", pat->Group); ajFmtPrintF(outf, " \"%s\"", pat->Valid); ajFmtPrintF(outf, "\n"); if(full && pat->Attr) { ajFmtPrintF(outf, " attributes {\n"); for(attr=pat->Attr; attr->Name; attr++) { ajFmtPrintF(outf, " %-15s", attr->Name); ajFmtPrintF(outf, " %-10s", acdValNames[attr->Type]); ajFmtPrintS(&tmpstr, "\"%s\"", attr->Default); if(ajStrGetLen(tmpstr) > maxtmp) maxtmp = ajStrGetLen(tmpstr); ajFmtPrintF(outf, " %-12S", tmpstr); ajFmtPrintF(outf, " \"%s\"", attr->Help); ajFmtPrintF(outf, "\n"); } ajFmtPrintF(outf, " }\n"); } if(pat->Quals) { ajFmtPrintF(outf, " qualifiers {\n"); for(qual=pat->Quals; qual->Name; qual++) { ajFmtPrintF(outf, " %-15s", qual->Name); ajFmtPrintF(outf, " %-10s", qual->Type); ajFmtPrintS(&tmpstr, "\"%s\"", qual->Default); if(ajStrGetLen(tmpstr) > maxtmp) maxtmp = ajStrGetLen(tmpstr); ajFmtPrintF(outf, " %-12S", tmpstr); ajFmtPrintF(outf, " \"%s\"", qual->Help); ajFmtPrintF(outf, "\n"); } ajFmtPrintF(outf, " }\n"); } } ajFmtPrintF(outf, "}\n"); ajFmtPrintF(outf, "# ACD Default attributes\n"); ajFmtPrintF(outf, "# Name Type Default Comment\n"); for(i=0; acdAttrDef[i].Name; i++) { ajFmtPrintF(outf, " %-15s", acdAttrDef[i].Name); ajFmtPrintF(outf, " %-10s", acdValNames[acdAttrDef[i].Type]); ajFmtPrintS(&tmpstr, "\"%s\"", acdAttrDef[i].Default); if(ajStrGetLen(tmpstr) > maxtmp) maxtmp = ajStrGetLen(tmpstr); ajFmtPrintF(outf, " %-12S", tmpstr); ajFmtPrintF(outf, " \"%s\"", acdAttrDef[i].Help); ajFmtPrintF(outf, "\n"); } ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# ACD Calculated attributes\n"); ajFmtPrintF(outf, "# Name\n"); ajFmtPrintF(outf, "# Attribute Type " "Default Comment\n"); acdPrintCalcattr(outf, "distances", acdCalcDistances); acdPrintCalcattr(outf, "features", acdCalcFeat); acdPrintCalcattr(outf, "frequencies", acdCalcFrequencies); acdPrintCalcattr(outf, "properties", acdCalcProperties); acdPrintCalcattr(outf, "regexp", acdCalcRegexp); acdPrintCalcattr(outf, "sequence", acdCalcSeq); acdPrintCalcattr(outf, "seqall", acdCalcSeqall); acdPrintCalcattr(outf, "seqset", acdCalcSeqset); acdPrintCalcattr(outf, "seqsetall", acdCalcSeqsetall); acdPrintCalcattr(outf, "string", acdCalcString); acdPrintCalcattr(outf, "tree", acdCalcTree); ajFmtPrintF(outf, "\n"); if(maxtmp > 12) ajWarn("ajAcdPrintType max tmpstr len %d", maxtmp); ajStrDel(&tmpstr); return; } ID acdPrintCalcattr TY static MO ajacd LB acd XX DE Report calculated attributes set DE For use by EMBOSS entrails. XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r acdtype const char* PD ACD type name PX PN [3] PA r calcattr const AcdOAttr[] PD Acd calculated attributes PX RT void RD RX // static void acdPrintCalcattr(AjPFile outf, const char* acdtype, const AcdOAttr calcattr[]) { ajint i; AjPStr tmpstr = NULL; ajuint maxtmp = 0; ajFmtPrintF(outf, " %s",acdtype); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " attributes {\n"); for(i=0; calcattr[i].Name; i++) { ajFmtPrintF(outf, " %-14s", calcattr[i].Name); ajFmtPrintF(outf, " %-10s", acdValNames[calcattr[i].Type]); ajFmtPrintS(&tmpstr, "\"%s\"", calcattr[i].Default); if(ajStrGetLen(tmpstr) > maxtmp) maxtmp = ajStrGetLen(tmpstr); ajFmtPrintF(outf, " %-20S", tmpstr); ajFmtPrintF(outf, " \"%s\"", calcattr[i].Help); ajFmtPrintF(outf, "\n"); } ajFmtPrintF(outf, " }\n"); if(maxtmp > 20) ajWarn("acdPrintCalcAttr max tmpstr len %d", maxtmp); ajStrDel(&tmpstr); return; } ID acdVocabCheck TY static MO ajacd LB acd XX DE Checks for a string in a controlled vocabulary of character strings, DE ended with a NULL. XX PN [1] PA r str const AjPStr PD Test string PX PN [2] PA r vocab const char** PD Controlled vocabulary PX RT AjBool RD ajTrue if the string matched on of the words RX // static AjBool acdVocabCheck(const AjPStr str, const char** vocab) { ajint i = 0; while(vocab[i]) { if(ajStrMatchCaseC(str, vocab[i])) return ajTrue; i++; } return ajFalse; } ID acdError TY static MO ajacd LB acd XX DE Formatted write as an error message, then exits with ajExitBad XX PN [1] PA r fmt const char* PD Format string PX PN [2] PA v vararg ... PD Format arguments. PX RT void RD RX // __noreturn static void acdError(const char* fmt, ...) { va_list args ; AjPStr errstr = NULL; ajint linenum; acdErrorCount++; va_start(args, fmt) ; ajFmtVPrintS(&errstr, fmt, args); va_end(args) ; if(acdLineNum > 0) linenum = acdLineNum; else if(acdSetCurr) linenum = acdSetCurr->LineNum; else if(acdProcCurr) linenum = acdProcCurr->LineNum; else if(acdListCurr) linenum = acdListCurr->LineNum; else linenum = 0; ajErr("File %S line %d: %S", acdFName, linenum, errstr); ajStrDel(&errstr); ajExitBad(); } ID acdErrorValid TY static MO ajacd LB acd XX DE Formatted write as an error message, then continues (acdError exits) XX PN [1] PA r fmt const char* PD Format string PX PN [2] PA v vararg ... PD Format arguments. PX RT void RD RX // static void acdErrorValid(const char* fmt, ...) { va_list args; AjPStr errstr=NULL; ajint linenum; acdErrorCount++; va_start(args, fmt) ; ajFmtVPrintS(&errstr, fmt, args); va_end(args) ; if(acdLineNum > 0) linenum = acdLineNum; else if(acdSetCurr) linenum = acdSetCurr->LineNum; else if(acdProcCurr) linenum = acdProcCurr->LineNum; else if(acdListCurr) linenum = acdListCurr->LineNum; else linenum = 0; ajErr("File %S line %d: %S", acdFName, linenum, errstr); ajStrDel(&errstr); return; } ID acdWarnObsolete TY static MO ajacd LB acd XX DE Warning about obsolete status of an application XX PN [1] PA r str const AjPStr PD Format string PX RT void RD RX // static void acdWarnObsolete(const AjPStr str) { AjPStr outstr = NULL; AjPStr tmpstr = NULL; AjBool warnobsolete = ajTrue; if(ajNamGetValueC("warnobsolete", &tmpstr)) ajStrToBool(tmpstr, &warnobsolete); ajStrDel(&tmpstr); if(!warnobsolete) return; ajFmtPrintS(&outstr, "Application %S is marked as obsolete.\n%S", acdProgram, str); ajStrFmtWrap(&outstr, 75); ajWarn("%S", outstr); ajStrDel(&outstr); return; } ID acdWarn TY static MO ajacd LB acd XX DE Formatted write as an error message. XX PN [1] PA r fmt const char* PD Format string PX PN [2] PA v vararg ... PD Format arguments. PX RT void RD RX // static void acdWarn(const char* fmt, ...) { va_list args; AjPStr errstr = NULL; ajint linenum; acdErrorCount++; va_start(args, fmt) ; ajFmtVPrintS(&errstr, fmt, args); va_end(args) ; if(acdLineNum > 0) linenum = acdLineNum; else if(acdSetCurr) linenum = acdSetCurr->LineNum; else if(acdProcCurr) linenum = acdProcCurr->LineNum; else if(acdListCurr) linenum = acdListCurr->LineNum; else linenum = 0; ajWarn("File %S line %d: %S", acdFName, linenum, errstr); ajStrDel(&errstr); return; } ID acdErrorAcd TY static MO ajacd LB acd XX DE Formatted write as an error message, for a specified ACD object XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r fmt const char* PD Format string PX PN [3] PA v vararg ... PD Format arguments. PX RT void RD RX // __noreturn static void acdErrorAcd(const AcdPAcd thys, const char* fmt, ...) { va_list args; AjPStr errstr = NULL; acdErrorCount++; ajUtilCatch(); va_start(args, fmt) ; ajFmtVPrintS(&errstr, fmt, args); va_end(args) ; ajErr("File %S line %d: (%S) %S", acdFName, thys->LineNum, thys->Name, errstr); ajStrDel(&errstr); ajExitBad(); } ID ajAcdExit TY public MO ajacd LB acd XX DE Reports any unused ACD values DE DE Cleans up ACD processing internal memory XX PN [1] PA r silent AjBool PD Turn off messages (used when some messages PD are expected but can be ignored). PX RT void RD RX // void ajAcdExit(AjBool silent) { AcdPAcd pa; static AjBool staySilent = AJFALSE; AjPFile cmdlogfile = NULL; AjPStr cmdlog = NULL; AjPStr cmdstr = NULL; if(silent) staySilent = ajTrue; if(acdDoHelp) staySilent = ajTrue; if(acdDoPretty) staySilent = ajTrue; if(!staySilent) { /* turn off the warnings for now ... comment out this line to ** enable them. ** the problem is that some programs have conditionals around the ** ajAcdGet calls so they can sometimes fail to use values, even where ** the test cases say they are fine. ** also some ACD files (showdb for example) use values, ** but in some cases (showdb with all booleans on the command line) ** the value is not needed */ /* ajDebug("ajAcdExit Name................ Assoc Level Used\n");*/ for(pa=acdList; pa; pa=pa->Next) { /*ajDebug("ajAcdExit %20S %3B %3d %3B\n", pa->Name, pa->Assoc, pa->Level, pa->Used); */ if(pa->Assoc) continue; if(pa->Level != ACD_PARAM && pa->Level != ACD_QUAL) continue; if(!pa->Used) acdLog("ACD qualifier never used: %S = '%S' (assoc %B)", pa->Name, pa->ValStr, pa->Assoc); } } /* report the command line to a log if requested. ** added mainly to log command lines for the QA tests ** so they can also be used in memory leak tests ** ** Need to test acdProgram is still set - acdc calls this twice */ if(acdCommandLine && ajStrGetLen(acdProgram) && ajNamGetValueC("acdcommandlinelog", &cmdlog)) { cmdlogfile = ajFileNewOutappendNameS(cmdlog); ajStrAssignS(&cmdstr, acdArgSave); if(ajStrGetLen(acdInputSave)) { ajStrAppendK(&cmdstr, ' '); ajStrAppendS(&cmdstr, acdInputSave); } ajStrRemoveWhiteExcess(&cmdstr); ajFmtPrintF(cmdlogfile, "%S %S\n", acdProgram, cmdstr); ajFileClose(&cmdlogfile); ajStrDel(&cmdlog); ajStrDel(&cmdstr); } /* ** clean up memory: ** */ acdReset(); return; } ID acdPrintUsed TY static MO ajacd LB acd XX DE Quick printoout of all qualifiers believed to have been used XX RT void RD RX // static void acdPrintUsed(void) { AcdPAcd pa; ajUserDumpS(acdArgSave); for(pa=acdList; pa; pa=pa->Next) if(pa->UserDefined) ajUser("ACD qual defined: '%S'", pa->Name); return; } ID acdReset TY static MO ajacd LB acd XX DE Cleans up all memory allocated to ACD internals. DE DE All ACD objects are assumed to have been passed to the calling program DE and are no longer "owned" by ACD. DE DE In future, we could consider removing them once they have been fetched. XX RT void RD RX // static void acdReset(void) { AcdPAcd pa; AcdPAcd qa = NULL; /* acdPrintUsed(); */ for(pa=acdList; pa; pa=qa) { qa = pa->Next; acdDel(&pa); } ajStrDel(&acdPackName); ajStrDel(&acdPackVersion); ajTablestrFree(&acdKnowntypeTypeTable); ajTablestrFree(&acdKnowntypeDescTable); ajTablestrFree(&acdCodeTable); ajTablestrFree(&acdGrpTable); ajStrDel(&acdTmpStr); ajStrDel(&acdTmpStr2); ajStrDel(&acdPrefName); ajStrDel(&acdPrefToken); ajStrDel(&acdArgSave); ajStrDel(&acdInputSave); ajStrDel(&acdInputName); ajStrDel(&acdInFName); ajStrDel(&acdOutFName); ajStrDel(&acdOutFExt); ajStrDel(&acdInTypeFeatName); ajStrDel(&acdInTypeSeqName); ajStrDel(&acdTmpOutFName); ajStrDel(&acdLogFName); ajFileClose(&acdLogFile); ajListstrFreeData(&acdSecList); ajTablestrFree(&acdSecTable); ajTablestrFree(&acdExternalTable); ajStrDel(&acdPrettyFName); ajFileClose(&acdPrettyFile); ajStrDel(&acdFName); ajStrDel(&acdOutFullFName); ajStrDel(&acdVarAcdProtein); acdParseQuotes = ajFalse; ajStrDel(&acdParseReturn); ajStrDel(&acdReply); ajStrDel(&acdReplyDef); ajStrDel(&acdReplyPrompt); ajStrDel(&acdUserMsg); ajStrDel(&acdUserReplyDef); ajStrDel(&acdDirectoryDef); ajStrDel(&acdQNameTmp); ajStrDel(&acdQTypeTmp); ajStrDel(&acdAttrValTmp); ajStrDel(&acdQualNameTmp); ajStrDel(&acdQualNumTmp); ajRegFree(&acdRegQualParse); ajRegFree(&acdRegVarname); ajRegFree(&acdRegFunction); ajRegFree(&acdRegToggle); ajRegFree(&acdRegResolveVar); ajRegFree(&acdRegResolveFun); ajRegFree(&acdRegExpPlusI); ajRegFree(&acdRegExpPlusD); ajRegFree(&acdRegExpMinusI); ajRegFree(&acdRegExpMinusD); ajRegFree(&acdRegExpStarI); ajRegFree(&acdRegExpStarD); ajRegFree(&acdRegExpDivI); ajRegFree(&acdRegExpDivD); ajRegFree(&acdRegExpEqualI); ajRegFree(&acdRegExpEqualD); ajRegFree(&acdRegExpEqualT); ajRegFree(&acdRegExpNeI); ajRegFree(&acdRegExpNeD); ajRegFree(&acdRegExpNeT); ajRegFree(&acdRegExpGtI); ajRegFree(&acdRegExpGtD); ajRegFree(&acdRegExpGtT); ajRegFree(&acdRegExpLtI); ajRegFree(&acdRegExpLtD); ajRegFree(&acdRegExpLtT); ajRegFree(&acdRegExpOrI); ajRegFree(&acdRegExpOrD); ajRegFree(&acdRegExpOrT); ajRegFree(&acdRegExpAndI); ajRegFree(&acdRegExpAndD); ajRegFree(&acdRegExpAndT); ajRegFree(&acdRegExpCond); ajRegFree(&acdRegExpNot); ajRegFree(&acdRegExpOneofCase); ajRegFree(&acdRegExpOneofList); ajRegFree(&acdRegExpCaseCase); ajRegFree(&acdRegExpCaseList); ajRegFree(&acdRegExpFilename); ajRegFree(&acdRegExpFileExists); ajRegFree(&acdRegExpValue); ajListFreeData(&acdListCommentsCount); ajListFreeData(&acdListCommentsColumn); ajListstrFreeData(&acdListComments); ajStrDel(&acdProgram); ajStrDel(&acdAppldoc); AJFREE(acdParamSet); acdDoHelp = AJFALSE; acdDoLog = AJFALSE; acdDoWarnRange =AJTRUE; acdDoPretty = AJFALSE; acdDoTable = AJFALSE; acdDoValid = AJFALSE; acdVerbose = AJFALSE; acdAuto = AJFALSE; acdFilter = AJFALSE; acdOptions = AJFALSE; acdStdout = AJFALSE; acdCodeSet = AJFALSE; acdKnowntypeSet = AJFALSE; acdWrapper = AJFALSE; /* acdQualTestSkip = AJFALSE; */ acdInFile = 0; acdInFileSet = AJFALSE; acdOutFile = 0; acdPromptTry = 2; acdPrettyMargin = 0; acdPrettyIndent = 2; acdLineNum = 0; acdWordNum = 0; acdErrorCount = 0; acdUseData = 0; acdUseFeatures = 0; acdUseInfile = 0; acdUseSeq = 0; acdUseAlign = 0; acdUseFeatout = 0; acdUseOutfile = 0; acdUseReport = 0; acdUseSeqout = 0; acdUseGraph = 0; acdUseMisc = 0; acdCurrentStage = NO_STAGE; acdNParam=0; acdNewCurr = NULL; acdMasterQual = NULL; acdList = NULL; acdListLast = NULL; acdListCurr = NULL; acdProcCurr = NULL; acdSetCurr = NULL; return; } ID acdValidAppl TY static MO ajacd LB acd XX DE Validation for an application definition XX PN [1] PA r thys const AcdPAcd PD ACD object PX RT void RD RX // static void acdValidAppl(const AcdPAcd thys) { ajint i; ajuint idocmax = 70; /* maximum length of documentation string */ AjPStrTok tokenhandle = NULL; AjPStr relation = NULL; const AjPStr namespace; ajuint ireloper = 0; ajuint ireltopic = 0; if(!acdDoValid) return; /* must have a documentation attribute */ i = acdFindAttrC(acdAttrAppl, "documentation"); if(!ajStrGetLen(thys->AttrStr[i])) acdErrorValid("Application has no documentation defined"); else { if(!isupper((int)ajStrGetCharFirst(thys->AttrStr[i]))) { if (islower((int)ajStrGetCharFirst(thys->AttrStr[i]))) acdWarn("Documentation string starts in lower case"); else acdWarn("Documentation string starts non-alphabetic"); } if (ajStrGetLen(thys->AttrStr[i]) > idocmax) acdWarn("Documentation string %d exceeds %d characters", ajStrGetLen(thys->AttrStr[i]), idocmax); } /* must have a group attribute */ i = acdFindAttrC(acdAttrAppl, "groups"); if(!ajStrGetLen(thys->AttrStr[i])) acdErrorValid("Application has no groups defined"); /* group must be a known group (and subgroup) */ acdValidApplGroup(thys->AttrStr[i]); i = acdFindAttrC(acdAttrAppl, "keywords"); acdValidApplKeywords(thys->AttrStr[i]); /* for now, skip EMBASSY applications */ /* if(acdEdam) { i = acdFindAttrC(acdAttrAppl, "embassy"); if(ajStrGetLen(thys->AttrStr[i])) acdEdam = NULL; } */ if(acdEdam) { i = acdFindAttrC(acdAttrAppl, "relations"); tokenhandle = ajStrTokenNewC(thys->AttrStr[i], "|"); while(ajStrTokenNextParse(&tokenhandle, &relation)) { namespace = acdEdamTest(relation); if(!namespace) acdErrorValid("Application relation '%S' not found", relation); else if(ajStrMatchC(namespace, "operation")) ireloper++; else if(ajStrMatchC(namespace, "topic")) ireltopic++; else acdErrorValid("Application has relation with namespace '%S'", namespace); } if(!ireltopic) acdErrorValid("Application has no 'topic' relation"); if(!ireloper) acdErrorValid("Application has no 'operation' relation"); ajStrDel(&relation); ajStrTokenDel(&tokenhandle); } /* ** test for wrapper attribute ** if true, we use the original command line ** so we turn off EMBOSS validation of qualifier names */ return; } ID acdValidRelation TY static MO ajacd LB acd XX DE Validation for a relation definition XX PN [1] PA r thys const AcdPAcd PD ACD object PX RT void RD RX // static void acdValidRelation(const AcdPAcd thys) { const AjPStr tmpstr = NULL; if(!acdDoValid) return; tmpstr = acdAttrValue(thys, "relations"); if(!ajStrGetLen(tmpstr)) return; return; } ID acdValidSection TY static MO ajacd LB acd XX DE Validation for a section definition XX PN [1] PA r thys const AcdPAcd PD ACD object PX RT void RD RX // static void acdValidSection(const AcdPAcd thys) { static AjPTable typeTable = NULL; static AjPTable infoTable = NULL; static ajint sectLevel = 0; static ajint sectNumber= -1; const AjPStr sectType = NULL; /* string from table - no delete */ const AjPStr sectInfo = NULL; /* string from table - no delete */ const AjPStr tmpstr = NULL; static AjPStr sectNameTop; ajint i; if(!acdDoValid) return; if(!typeTable) acdReadSections(&typeTable, &infoTable); if(ajCharMatchC(acdKeywords[thys->Type].Name, "endsection")) { --sectLevel; if(sectLevel < 0) acdErrorValid("Too many endsections"); return; } ++sectLevel; if(sectLevel == 1) { ajStrAssignS(§NameTop, thys->Name); for(i=0; acdSections[i]; i++) { if(ajStrMatchC(thys->Name, acdSections[i]->Name)) { if(i < sectNumber) acdWarn("Section '%S' follows section '%s'", thys->Name, acdSections[sectNumber]->Name); else sectNumber = i; break; } } if(!acdSections[i]) acdWarn("No defined order for primary section '%S'", thys->Name); } /* should have a known name */ sectType = ajTableFetchS(typeTable, thys->Name); tmpstr = acdAttrValue(thys, "type"); if(!sectType) { if(sectLevel == 1) acdErrorValid("Section '%S' not defined in sections.standard file", thys->Name); else acdWarn("Sub level section '%S' not defined in " "sections.standard file", thys->Name); /* if unknown, must be distinctive - check for common words? */ return; } else { /* if known, must have a standard type */ if(sectLevel == 1) { if(!ajStrMatchC(tmpstr, "page")) acdErrorValid("Top level section '%S' not of type 'page'", thys->Name); else if(!ajStrMatchC(sectType, "page")) acdErrorValid("Top level section '%S' defined as " "sub type of '%S' in sections.standard", thys->Name, sectType); } else { if(ajStrMatchC(tmpstr, "page")) acdErrorValid("Sub level section '%S' not of type 'frame'", thys->Name); else if(ajStrPrefixC(sectType, "page")) acdErrorValid("Sub level section '%S' not defined " "as type 'frame' in sections.standard", thys->Name); else if(!ajStrMatchS(sectType, sectNameTop)) acdErrorValid("Sub level section '%S' should be under '%S'", thys->Name, sectType); } } sectInfo = ajTableFetchS(infoTable, thys->Name); tmpstr = acdAttrValue(thys, "information"); /* must have an information attribute */ if(!ajStrGetLen(tmpstr)) acdWarn("Section has no information string"); /* if known, must have a standard info string */ else { if(!ajStrMatchS(sectInfo, tmpstr)) { if(ajStrMatchCaseS(sectInfo, tmpstr)) acdWarn("Section info '%S' expected, case mismatch", sectInfo); else acdErrorValid("Section info '%S' expected, found '%S'", sectInfo, tmpstr); } } return; } ID acdValidQual TY static MO ajacd LB acd XX DE Validation for a qualifier definition XX PN [1] PA u thys AcdPAcd PD ACD object PX RT void RD RX // static void acdValidQual(AcdPAcd thys) { ajint itype; AjBool isparam = ajFalse; AjBool boolval; AjBool toggle; AjBool isParameter = ajFalse; AjBool isStandard = ajFalse; AjBool isAdditional = ajFalse; AjBool isToggle = ajFalse; static AjPStr secname = NULL; const AjPStr relstr = NULL; const AjPStr tmpstr = NULL; const AjPStr tmpinfo = NULL; const AjPStr tmpprompt = NULL; const AjPStr tmphelp = NULL; const AjPStr tmpstandard = NULL; static ajint qualCountSeq = 0; static ajint qualCountSeqout = 0; static ajint qualCountInfile = 0; static ajint qualCountOutfile = 0; static ajint qualCountIn = 0; static ajint qualCountOut = 0; static ajint qualCountFeat = 0; static ajint qualCountFeatout = 0; static AjPStr qualName = NULL; static AjPStr qualSeqFirst = NULL; static AjPStr qualSeqoutFirst = NULL; static AjPStr seqTypeIn = NULL; static AjPStr qualFeatFirst = NULL; static AjPStr qualFeatoutFirst = NULL; static AjBool seqMulti = AJFALSE; static AjBool seqoutMulti = AJFALSE; static AjBool featMulti = AJFALSE; static AjBool featoutMulti = AJFALSE; const AjPStr namespace = NULL; AjPStr relation = NULL; AjPStrTok tokenhandle = NULL; ajuint ireldata = 0; if(!acdDoValid) return; if(acdEdam) { relstr = acdAttrValue(thys, "relations"); tokenhandle = ajStrTokenNewC(relstr, "|"); while(ajStrTokenNextParse(&tokenhandle, &relation)) { namespace = acdEdamTest(relation); if(!namespace) acdErrorValid("Qualifier relation '%S' not found", relation); else if(ajStrMatchC(namespace, "data")) ireldata++; else if(ajStrMatchC(namespace, "format")) ireldata++; else if(ajStrMatchC(namespace, "identifier")) ireldata++; else acdErrorValid("Qualifier has relation '%S' with namespace '%S'", relation, namespace); } ajStrDel(&relation); ajStrTokenDel(&tokenhandle); } if(ajListGetLength(acdSecList)) { acdValidSectionFull(&secname); } else { acdErrorValid("No section defined for qualifier '%S'", thys->Name); ajStrAssignC(&secname, ""); } if (acdType[thys->Type].Section) { if (!acdValidSectionMatch(acdType[thys->Type].Section->Name)) acdErrorValid("Qualifier '%S' type '%s' not in section '%s'", thys->Name, acdType[thys->Type].Name, acdType[thys->Type].Section->Name); } if(ajCharMatchC(acdType[thys->Type].Name, "toggle")) isToggle = ajTrue; /* parameter, standard, additional only once, with 'Y' */ itype = 0; tmpstr = acdAttrValue(thys, "parameter"); if(ajStrGetLen(tmpstr)) { itype++; if(acdVarTestValid(tmpstr, &toggle)) { isParameter = ajTrue; acdErrorValid("Calculated parameter value for '%S'", thys->Name); } else { if(ajStrToBool(tmpstr,&isparam)) { if(isparam) { isParameter = ajTrue; } else { acdErrorValid("Parameter defined as false '%S'", tmpstr); itype--; } } } } tmpstr = acdAttrValue(thys, "standard"); if(ajStrGetLen(tmpstr)) { itype++; if(acdVarTestValid(tmpstr, &toggle)) { isStandard = ajTrue; if (!toggle) acdWarn("Calculated standard value for '%S'", thys->Name); if (acdValidSectionMatch("advanced")) acdWarn("Calculated standard value for qualifier '%S' " "in section '%S'", thys->Name, secname); } else { if(ajStrToBool(tmpstr,&boolval)) { if(boolval) { isStandard = ajTrue; } else { acdErrorValid("Standard defined as false '%S'", tmpstr); itype--; } } } } tmpstr = acdAttrValue(thys, "additional"); if(ajStrGetLen(tmpstr)) { itype++; if(acdVarTestValid(tmpstr, &toggle)) { isAdditional = ajTrue; if (!toggle) acdWarn("Calculated additional value for '%S'", thys->Name); if (acdValidSectionMatch("advanced")) acdWarn("Calculated additional value for qualifier '%S' " "in section '%S'", thys->Name, secname); } else { if(ajStrToBool(tmpstr,&boolval)) { if(boolval) isAdditional = ajTrue; else { acdErrorValid("Additional defined as false '%S'", tmpstr); itype--; } } } } if(itype > 1) acdErrorValid("Multiple definition of parameter/standard/additional"); if (isParameter) { if (acdValidSectionMatch("advanced") || acdValidSectionMatch("additional")) acdErrorValid("Parameter '%S' in section '%S'", thys->Name, secname); } else if (isStandard) { if (acdValidSectionMatch("advanced") || acdValidSectionMatch("additional")) acdWarn("Standard qualifier '%S' " "in section '%S'", thys->Name, secname); } else if (isAdditional) { if (acdValidSectionMatch("advanced") || (!isToggle && acdValidSectionMatch("required"))) acdWarn("Additional qualifier '%S' " "in section '%S'", thys->Name, secname); } else /* must be advanced */ { if (!isToggle) { if (acdValidSectionMatch("additional") || acdValidSectionMatch("required")) acdWarn("Advanced qualifier '%S' " "in section '%S'", thys->Name, secname); } } tmpinfo = acdAttrValue(thys, "information"); tmpprompt = acdAttrValue(thys, "prompt"); tmphelp = acdAttrValue(thys, "help"); if(acdType[thys->Type].Prompt) { tmpstandard = (*acdType[thys->Type].Prompt)(thys); if(ajStrGetLen(tmpstandard) && acdKnowntypeDesc(thys)) { if(ajStrGetLen(tmpinfo)) { if(!ajStrMatchS(tmpinfo, tmpstandard)) acdWarn("Information string for '%S' '%S' not standard '%S'", thys->Name, tmpinfo, tmpstandard); } else { acdWarn("Missing standard information '%S' expected '%S'", thys->Name, tmpstandard); } } /* else if(ajStrGetLen(tmpinfo)) { acdWarn("Standard prompt for '%S' is '%S' information '%S'", thys->Name, tmpstandard, tmpinfo); } */ } if(ajStrGetLen(tmpprompt) && !ajStrGetLen(tmpinfo)) acdErrorValid("Prompt specified but no information"); if(ajStrGetLen(tmpinfo) && !isupper((int)ajStrGetCharFirst(tmpinfo))) { if (islower((int)ajStrGetCharFirst(tmpinfo))) acdWarn("Information string for '%S' starts in lower case", thys->Name); else acdWarn("Information string for '%S' starts non-alphabetic", thys->Name); } if(ajStrGetLen(tmpprompt) && !isupper((int)ajStrGetCharFirst(tmpprompt))) { if (islower((int)ajStrGetCharFirst(tmpprompt))) acdWarn("Prompt string for '%S' starts in lower case", thys->Name); else acdWarn("Prompt string for '%S' starts non-alphabetic", thys->Name); } if(ajStrGetLen(tmphelp) && !isupper((int)ajStrGetCharFirst(tmphelp))) { if (islower((int)ajStrGetCharFirst(tmphelp))) acdWarn("Help string for '%S' starts in lower case", thys->Name); else acdWarn("Help string for '%S' starts non-alphabetic", thys->Name); } /* ** if known, must have a standard info string ** but we can make allowances for possibly confusing 2nd occurrence ** and beyond ** and also allow for those with a default value that are usually not ** prompted for */ if(acdType[thys->Type].Stdprompt && (ajStrGetLen(tmpinfo) || ajStrGetLen(tmpprompt)) && !acdAttrTestDefined(thys, "default") && !acdAttrTestDefined(thys, "knowntype") && *acdType[thys->Type].UseClassCount == 1) { acdWarn("Unexpected information value for type '%s'", acdType[thys->Type].Name); } /* else it must have an info attribute or a knowntype */ if(!acdType[thys->Type].Stdprompt && !ajStrGetLen(tmpinfo) && !ajStrGetLen(tmpprompt) && !acdAttrTestDefined(thys, "knowntype")) { acdErrorValid("Missing information value for type '%s'", acdType[thys->Type].Name); } /* if known, must have a standard type */ /* expected types should have a known name */ /* sequence seqset seqall see below - need to do seqout, infile, outfile */ if(ajCharMatchC(acdType[thys->Type].Name, "sequence") || ajCharMatchC(acdType[thys->Type].Name, "seqall") || ajCharMatchC(acdType[thys->Type].Name, "seqsetall") || ajCharMatchC(acdType[thys->Type].Name, "seqset")) { if(!isparam && !acdAttrTestDefined(thys, "default") && !acdAttrTestDefined(thys, "nullok")) { if(*acdType[thys->Type].UseClassCount == 1) acdErrorValid("First sequence input '%S' is not a parameter", thys->Token); else acdWarn("Subsequent sequence input '%S' is not a parameter", thys->Token); } qualCountIn++; qualCountSeq++; if(qualCountSeq == 1) ajStrAssignS(&qualSeqFirst, thys->Token); ajFmtPrintS(&qualName, "%csequence", (char) ('a' - 1 + qualCountSeq)); if(!(ajStrSuffixC(thys->Token, "sequence") || (ajCharMatchC(acdType[thys->Type].Name, "seqall") && ajStrSuffixC(thys->Token, "seqall")) || (ajCharMatchC(acdType[thys->Type].Name, "seqset") && ajStrSuffixC(thys->Token, "sequences")) || (ajCharMatchC(acdType[thys->Type].Name, "seqsetall") && ajStrSuffixC(thys->Token, "sequences")) )) { if(ajCharMatchC(acdType[thys->Type].Name, "seqall")) acdWarn("Sequence qualifier '%S' is not 'sequence' " "or '*sequence' or 'seqall' " "(should be '%Ssequence')", thys->Token, thys->Token); else if(ajCharMatchC(acdType[thys->Type].Name, "seqset")) acdWarn("Sequence qualifier '%S' is not 'sequence' " "or '*sequence' or 'sequences' " "(should be '%Ssequence')", thys->Token, thys->Token); else if(ajCharMatchC(acdType[thys->Type].Name, "seqsetall")) acdWarn("Sequence qualifier '%S' is not 'sequence' " "or '*sequence' or 'sequences' " "(should be '%Ssequence')", thys->Token, thys->Token); else acdWarn("Sequence qualifier '%S' is not 'sequence' " "or '*sequence' " "(should be '%Ssequence')", thys->Token, thys->Token); } else { if((qualCountSeq > 1) || !(ajStrMatchC(thys->Token, "sequence") || (ajCharMatchC(acdType[thys->Type].Name, "seqall") && ajStrSuffixC(thys->Token, "seqall")) || (ajCharMatchC(acdType[thys->Type].Name, "seqsetall") && ajStrSuffixC(thys->Token, "sequences")) || (ajCharMatchC(acdType[thys->Type].Name, "seqset") && ajStrSuffixC(thys->Token, "sequences")) )) seqMulti = ajTrue; if(seqMulti) { if ((ajStrGetLen(thys->Token) == ajStrGetLen(qualName)) && ajStrSuffixC(thys->Token, "sequence")) { if(!ajStrMatchS(thys->Token, qualName)) acdWarn("Expected sequence qualifier is '%S' " "found '%S'", qualName, thys->Token); } } } tmpstr = acdAttrValue(thys, "type"); if(!ajStrGetLen(tmpstr)) acdErrorValid("No type specified for input sequence"); else if(qualCountSeq == 1) ajStrAssignS(&seqTypeIn, tmpstr); if(ajCharMatchC(acdType[thys->Type].Name, "seqset") || ajCharMatchC(acdType[thys->Type].Name, "seqsetall")) { if(!acdAttrTestDefined(thys, "aligned")) acdErrorValid("Sequence set '%S' has no 'aligned' attribute", thys->Token); } } if(ajCharMatchC(acdType[thys->Type].Name, "feature")) { if(!isparam && !acdAttrTestDefined(thys, "default") && !acdAttrTestDefined(thys, "nullok")) { if(*acdType[thys->Type].UseClassCount == 1) acdErrorValid("First feature input '%S' is not a parameter", thys->Token); else acdWarn("Subsequent feature input '%S' is not a parameter", thys->Token); } qualCountFeat++; if(qualCountFeat == 1) ajStrAssignS(&qualFeatFirst, thys->Token); ajFmtPrintS(&qualName, "%cfeature", (char) ('a' - 1 + qualCountFeat)); if(!(ajStrSuffixC(thys->Token, "feature"))) { acdWarn("Feature qualifier '%S' is not 'feature' " "or '*feature' " "(should be '%Sfeature')", thys->Token, thys->Token); } else { if((qualCountFeat > 1) || !(ajStrMatchC(thys->Token, "feature"))) featMulti = ajTrue; if(featMulti) if ((ajStrGetLen(thys->Token) == ajStrGetLen(qualName)) && ajStrSuffixC(thys->Token, "feature")) { if(!ajStrMatchS(thys->Token, qualName)) acdWarn("Expected feature qualifier is '%S' " "found '%S'", qualName, thys->Token); } } /* still to do - check for type */ tmpstr = acdAttrValue(thys, "type"); if(!ajStrGetLen(tmpstr) && !ajStrGetLen(seqTypeIn)) acdErrorValid("No type specified for input feature, " "and no input sequence type as a default"); } /* infile - assume parameter -infile */ /* check for type */ if(ajCharMatchC(acdType[thys->Type].Name, "infile") || ajCharMatchC(acdType[thys->Type].Name, "filelist") || ajCharMatchC(acdType[thys->Type].Name, "directory") || ajCharMatchC(acdType[thys->Type].Name, "dirlist")) { if(!isparam && !acdAttrTestDefined(thys, "default") && !acdAttrTestDefined(thys, "nullok")) { if(ajCharMatchC(acdType[thys->Type].Name, "directory") || ajCharMatchC(acdType[thys->Type].Name, "dirlist")) { if(*acdType[thys->Type].UseClassCount == 1) acdErrorValid("First input directory '%S' is not a " "parameter", thys->Token); else acdWarn("Subsequent input directory '%S' is not a " "parameter", thys->Token); } else { if(*acdType[thys->Type].UseClassCount == 1) acdErrorValid("First input file '%S' is not a parameter", thys->Token); else acdWarn("Subsequent input file '%S' is not a parameter", thys->Token); } } qualCountIn++; qualCountInfile++; if(ajCharMatchC(acdType[thys->Type].Name, "infile")) { if(!ajStrSuffixC(thys->Token, "file")) acdWarn("Infile qualifier '%S' is not 'infile' or '*file' " "(should be '%Sfile')", thys->Token, thys->Token); else { if((qualCountIn == 1) && !ajStrMatchC(thys->Token, "infile") && !ajStrSuffixC(thys->Token, "file")) acdWarn("First input file qualifier '%S' is not " "'infile' or '*file' " "(should be '%Sfile')", thys->Token, thys->Token); } } else if(ajCharMatchC(acdType[thys->Type].Name, "filelist")) { if(!ajStrSuffixC(thys->Token, "files")) acdWarn("Filelist qualifier '%S' is not '*files'", thys->Token); /* no fixed qualifier name for first input filelist */ } else if(ajCharMatchC(acdType[thys->Type].Name, "directory") || ajCharMatchC(acdType[thys->Type].Name, "dirlist")) { if(ajStrSuffixC(thys->Token, "outdir")) acdWarn("Directory qualifier '%S' has outdir style name " "'*outdir'", thys->Token); else if(!ajStrSuffixC(thys->Token, "dir") && !ajStrSuffixC(thys->Token, "path") && !ajStrSuffixC(thys->Token, "directory")) acdWarn("Directory qualifier '%S' is not '*directory or *dir'" " or *path' " "(should be '%Sdir')", thys->Token, thys->Token); /* no fixed qualifier name for first input directory */ } tmpstr = acdAttrValue(thys, "knowntype"); if(!ajStrGetLen(tmpstr)) acdWarn("No knowntype specified for input file"); } /* datafile - no standard qualifier name yet */ /* check for knowntype */ if(ajCharMatchC(acdType[thys->Type].Name, "datafile")) { tmpstr = acdAttrValue(thys, "knowntype"); if(!ajStrGetLen(tmpstr)) acdWarn("No knowntype specified for data file"); } /* outfile - assume parameter unless default is stdout -outfile */ /* check for type */ if(ajCharMatchC(acdType[thys->Type].Name, "outfile")) { /* Skip this test - there is a good default for output files */ /* if(!isparam && !acdAttrTestDefined(thys, "default") && !acdAttrTestDefined(thys, "nullok")) { if(*acdType[thys->Type].UseClassCount == 1) acdErrorValid("First output file '%S' is not a parameter", thys->Token); else acdWarn("Subsequent output file '%S' is not a parameter", thys->Token); } */ qualCountOut++; qualCountOutfile++; if(ajCharMatchC(acdType[thys->Type].Name, "outfile") && !ajStrSuffixC(thys->Token, "file")) acdWarn("Outfile qualifier '%S' is not 'outfile' or '*file' " "(should be '%Sfile')", thys->Token, thys->Token); else { if((qualCountOut == 1) && !ajStrMatchC(thys->Token, "outfile")) { acdWarn("First output file qualifier '%S' is not 'outfile'", thys->Token); } } /* still to do - check for type */ tmpstr = acdAttrValue(thys, "knowntype"); if(!ajStrGetLen(tmpstr)) acdWarn("No knowntype specified for output file"); } /* outdirectory - assume parameter unless default is stdout -outfile */ /* check for type */ if(ajCharMatchC(acdType[thys->Type].Name, "outdir")) { if(!isparam && !acdAttrTestDefined(thys, "default") && !acdAttrTestDefined(thys, "nullok")) { if(*acdType[thys->Type].UseClassCount == 1) acdErrorValid("First output directory '%S' is not a parameter", thys->Token); else acdWarn("Subsequent output directory '%S' is not a parameter", thys->Token); } qualCountOut++; qualCountOutfile++; if(ajCharMatchC(acdType[thys->Type].Name, "outdir") && !ajStrSuffixC(thys->Token, "outdir")) acdWarn("Outdir qualifier '%S' is not 'outdir' or '*outdir'", thys->Token); else { if((qualCountOut == 1) && !ajStrMatchC(thys->Token, "outdir") && !ajStrSuffixC(thys->Token, "outdir")) acdWarn("First output directory qualifier '%S' " "is not 'outdir' or '*outdir' " "(should be '%Sfile')", thys->Token, thys->Token); } /* still to do - check for type */ tmpstr = acdAttrValue(thys, "knowntype"); if(!ajStrGetLen(tmpstr)) acdWarn("No knowntype specified for output directory"); } /* align - as for outfile? */ if(ajCharMatchC(acdType[thys->Type].Name, "align")) { if(!isparam && !acdAttrTestDefined(thys, "default") && !acdAttrTestDefined(thys, "nullok")) { if(*acdType[thys->Type].UseClassCount == 1) acdErrorValid("First alignment file '%S' is not a parameter", thys->Token); else acdWarn("Subsequent alignment file '%S' is not a parameter", thys->Token); } qualCountOut++; qualCountOutfile++; if(!ajStrSuffixC(thys->Token, "file")) acdWarn("Align qualifier '%S' is not 'outfile' or '*file' " "(should be '%Sfile')", thys->Token, thys->Token); else { if((qualCountOut == 1) && !ajStrMatchC(thys->Token, "outfile")) acdWarn("First alignment file qualifier '%S' is not 'outfile'", thys->Token); } } /* report - as for outfile? */ if(ajCharMatchC(acdType[thys->Type].Name, "report")) { if(!isparam && !acdAttrTestDefined(thys, "default") && !acdAttrTestDefined(thys, "nullok")) { if(*acdType[thys->Type].UseClassCount == 1) acdErrorValid("First report file '%S' is not a parameter", thys->Token); else acdWarn("Subsequent report file '%S' is not a parameter", thys->Token); } qualCountOut++; qualCountOutfile++; if(!ajStrSuffixC(thys->Token, "file")) acdWarn("Report qualifier '%S' is not 'outfile' or '*file' " "(should be '%Sfile')", thys->Token, thys->Token); else { if((qualCountOut == 1) && !ajStrMatchC(thys->Token, "outfile")) acdWarn("First report file qualifier '%S' is not 'outfile'", thys->Token); } } /* seqout* - assume parameter - what names? -outseq? */ /* type only if there is no sequence input */ if(ajCharMatchC(acdType[thys->Type].Name, "seqout") || ajCharMatchC(acdType[thys->Type].Name, "seqoutall") || ajCharMatchC(acdType[thys->Type].Name, "seqoutset")) { /* skip this test - there is a good default for sequence output */ /* if(!isparam && !acdAttrTestDefined(thys, "default") && !acdAttrTestDefined(thys, "nullok")) { if(*acdType[thys->Type].UseClassCount == 1) acdErrorValid("First sequence output '%S' is not a parameter", thys->Token); else acdWarn("Subsequent sequence output '%S' is not a parameter", thys->Token); } */ qualCountOut++; qualCountSeqout++; if(qualCountSeqout == 1) ajStrAssignS(&qualSeqoutFirst, thys->Token); ajFmtPrintS(&qualName, "%coutseq", (char) ('a' - 1 + qualCountSeqout)); if(!ajStrSuffixC(thys->Token, "outseq") && !ajStrSuffixC(thys->Token, "outfile")) acdWarn("Sequence output qualifier '%S' is not 'outseq' " "or '*outseq' or '*outfile'" "(should be '%Soutseq')", thys->Token, thys->Token); else { if((qualCountOut > 1) || (!ajStrMatchC(thys->Token, "outseq") && !ajStrMatchC(thys->Token, "outfile"))) seqoutMulti = ajTrue; if(seqoutMulti) { if ((ajStrGetLen(thys->Token) == ajStrGetLen(qualName)) && ajStrSuffixC(thys->Token, "outseq")) { if(!ajStrMatchS(thys->Token, qualName)) acdWarn("Expected sequence output qualifier is '%S' " "found '%S'", qualName, thys->Token); } } } /* still to do - check for type */ tmpstr = acdAttrValue(thys, "type"); if(!ajStrGetLen(tmpstr) && !ajStrGetLen(seqTypeIn)) acdErrorValid("No type specified for output sequence, " "and no input sequence type as a default"); } /* featout - assume parameter - what names? -outfeat? */ /* type only if there is no sequence input */ if(ajCharMatchC(acdType[thys->Type].Name, "featout")) { if(!isparam && !acdAttrTestDefined(thys, "default") && !acdAttrTestDefined(thys, "nullok")) { if(*acdType[thys->Type].UseClassCount == 1) acdErrorValid("First feature output '%S' is not a parameter", thys->Token); else acdWarn("Subsequent feature output '%S' is not a parameter", thys->Token); } qualCountFeatout++; if(qualCountFeatout == 1) ajStrAssignS(&qualFeatoutFirst, thys->Token); ajFmtPrintS(&qualName, "%coutfeat", (char) ('a' - 1 + qualCountFeatout)); if(!ajStrMatchC(thys->Token, "outfeat") && !ajStrSuffixC(thys->Token, "outfeat")) acdWarn("Feature output qualifier '%S' is not 'outfeat' " "or '*outfeat'" "(should be '%Soutfeat')", thys->Token, thys->Token); else { if((qualCountFeatout > 1) || (!ajStrMatchC(thys->Token, "outfeat"))) featoutMulti = ajTrue; if(featoutMulti) { if ((ajStrGetLen(thys->Token) == ajStrGetLen(qualName)) && ajStrSuffixC(thys->Token, "outfeat")) { if(!ajStrMatchS(thys->Token, qualName)) acdWarn("Expected feature output qualifier is '%S' " "found '%S'", qualName, thys->Token); } } } /* still to do - check for type */ tmpstr = acdAttrValue(thys, "type"); if(!ajStrGetLen(tmpstr) && !ajStrGetLen(seqTypeIn)) acdErrorValid("No type specified for output feature, " "and no input sequence type as a default"); } /* string - we don't ask much, but we do prefer strings to have a known type that does not suggest some other datatype can be used */ if(ajCharMatchC(acdType[thys->Type].Name, "string")) { tmpstr = acdAttrValue(thys, "knowntype"); if(!ajStrGetLen(tmpstr)) { tmpstr = acdAttrValue(thys, "pattern"); if (!ajStrGetLen(tmpstr)) acdWarn("No knowntype specified for string"); else acdWarn("Pattern but no knowntype specified for string"); } } acdValidKnowntype(thys); return; } ID acdValidKnowntype TY static MO ajacd LB acd XX DE Validation for Known type XX PN [1] PA r thys const AcdPAcd PD Current ACD object PX RT void RD RX // static void acdValidKnowntype(const AcdPAcd thys) { const AjPStr typestr = NULL; const AjPStr acdKnownType = NULL; static AjPStr defType = NULL; AjBool typeok = ajFalse; if(!acdDoValid) return; typestr = acdAttrValue(thys, "knowntype"); if (!ajStrGetLen(typestr)) return; if (!defType) ajFmtPrintS(&defType, "%S output", acdProgram); acdKnownType = ajTableFetchS(acdKnowntypeTypeTable, typestr); if (!acdKnownType) { if(ajStrFindAnyK(typestr, '_') >= 0) { acdWarn("Knowntype '%S' replace underscore(s) with spaces", typestr); } else { if (!ajStrMatchS(typestr, defType)) acdWarn("Knowntype '%S' not defined in knowntypes.standard", typestr); } return; } if (ajStrMatchC(acdKnownType, "file")) { if (!ajCharMatchC(acdType[thys->Type].Name, "infile") && !ajCharMatchC(acdType[thys->Type].Name, "datafile") && !ajCharMatchC(acdType[thys->Type].Name, "directory") && !ajCharMatchC(acdType[thys->Type].Name, "dirlist") && !ajCharMatchC(acdType[thys->Type].Name, "outfile") && !ajCharMatchC(acdType[thys->Type].Name, "outdir") && !ajCharMatchC(acdType[thys->Type].Name, "filelist")) { acdWarn("Knowntype '%S' defined for type '%S', used for '%s'", typestr, acdKnownType, acdType[thys->Type].Name); } } else if (ajStrMatchC(acdKnownType, "sequence")) { if (!ajCharMatchC(acdType[thys->Type].Name, "sequence") && !ajCharMatchC(acdType[thys->Type].Name, "seqall") && !ajCharMatchC(acdType[thys->Type].Name, "seqset") && !ajCharMatchC(acdType[thys->Type].Name, "seqsetall") && !ajCharMatchC(acdType[thys->Type].Name, "seqout") && !ajCharMatchC(acdType[thys->Type].Name, "seqoutall") && !ajCharMatchC(acdType[thys->Type].Name, "seqoutset") && !ajCharMatchC(acdType[thys->Type].Name, "seqoutsetall")) { acdWarn("Knowntype '%S' defined for type '%S', used for '%s'", typestr, acdKnownType, acdType[thys->Type].Name); } } else if (ajStrMatchC(acdKnownType, "codon") || ajStrMatchC(acdKnownType, "cpdb") || ajStrMatchC(acdKnownType, "matrix") || ajStrMatchC(acdKnownType, "discretestates") || ajStrMatchC(acdKnownType, "distances") || ajStrMatchC(acdKnownType, "frequencies") || ajStrMatchC(acdKnownType, "properties") || ajStrMatchC(acdKnownType, "scop") || ajStrMatchC(acdKnownType, "tree")) { typeok = ajFalse; if (ajStrMatchC(acdKnownType, "codon")) if(ajCharMatchC(acdType[thys->Type].Name, "codon") || ajCharMatchC(acdType[thys->Type].Name, "outcodon")) typeok = ajTrue; if (ajStrMatchC(acdKnownType, "cpdb")) if(ajCharMatchC(acdType[thys->Type].Name, "cpdb") || ajCharMatchC(acdType[thys->Type].Name, "outcpdb")) typeok = ajTrue; if (ajStrMatchC(acdKnownType, "discretestates")) if(ajCharMatchC(acdType[thys->Type].Name, "discretestates") || ajCharMatchC(acdType[thys->Type].Name, "outdiscrete")) typeok = ajTrue; if (ajStrMatchC(acdKnownType, "distances")) if(ajCharMatchC(acdType[thys->Type].Name, "distances") || ajCharMatchC(acdType[thys->Type].Name, "outdistances")) typeok = ajTrue; if (ajStrMatchC(acdKnownType, "frequencies")) if(ajCharMatchC(acdType[thys->Type].Name, "frequencies") || ajCharMatchC(acdType[thys->Type].Name, "outfreq")) typeok = ajTrue; if (ajStrMatchC(acdKnownType, "obo")) if(ajCharMatchC(acdType[thys->Type].Name, "obo") || ajCharMatchC(acdType[thys->Type].Name, "outobo")) typeok = ajTrue; if (ajStrMatchC(acdKnownType, "properties")) if(ajCharMatchC(acdType[thys->Type].Name, "properties") || ajCharMatchC(acdType[thys->Type].Name, "outproperties")) typeok = ajTrue; if (ajStrMatchC(acdKnownType, "resource")) if(ajCharMatchC(acdType[thys->Type].Name, "resource") || ajCharMatchC(acdType[thys->Type].Name, "outresource")) typeok = ajTrue; if (ajStrMatchC(acdKnownType, "scop")) if(ajCharMatchC(acdType[thys->Type].Name, "scop") || ajCharMatchC(acdType[thys->Type].Name, "outscop")) typeok = ajTrue; if (ajStrMatchC(acdKnownType, "tree")) if(ajCharMatchC(acdType[thys->Type].Name, "tree") || ajCharMatchC(acdType[thys->Type].Name, "outtree")) typeok = ajTrue; if (ajStrMatchC(acdKnownType, "matrix")) if(ajCharMatchC(acdType[thys->Type].Name, "matrix") || ajCharMatchC(acdType[thys->Type].Name, "matrixf") || ajCharMatchC(acdType[thys->Type].Name, "outmatrix") || ajCharMatchC(acdType[thys->Type].Name, "outmatrixf")) typeok = ajTrue; if (!ajCharMatchC(acdType[thys->Type].Name, "infile") && !ajCharMatchC(acdType[thys->Type].Name, "datafile") && !ajCharMatchC(acdType[thys->Type].Name, "directory") && !ajCharMatchC(acdType[thys->Type].Name, "dirlist") && !ajCharMatchC(acdType[thys->Type].Name, "outfile") && !ajCharMatchC(acdType[thys->Type].Name, "outdir") && !ajCharMatchC(acdType[thys->Type].Name, "filelist")) { if(!typeok) acdWarn("Knowntype '%S' defined for type '%S', used for '%s'", typestr, acdKnownType, acdType[thys->Type].Name); } } else if (!ajStrMatchC(acdKnownType, acdType[thys->Type].Name)) { acdWarn("Knowntype '%S' defined for type '%S', used for '%s'", typestr, acdKnownType, acdType[thys->Type].Name); } return; } ID acdKnowntypeDesc TY static MO ajacd LB acd XX DE Return description of known type XX PN [1] PA r thys const AcdPAcd PD Acd object PX RT const AjPStr RD Known type description or NULL if not defined. RX // static const AjPStr acdKnowntypeDesc(const AcdPAcd thys) { const AjPStr knowntype = NULL; const AjPStr knowndesc; knowntype = acdAttrValue(thys, "knowntype"); if(!ajStrGetLen(knowntype)) return NULL; knowndesc = ajTableFetchS(acdKnowntypeDescTable, knowntype); return knowndesc; } ID acdReadKnowntype TY static MO ajacd LB acd XX DE Read standard file of ACD sections and store in new AjPTable objects XX PN [1] PA wN desctable AjPTable* PD String table of section names and types PX PN [2] PA wN typetable AjPTable* PD String table of section names and PD descriptions PX RT void RD RX // static void acdReadKnowntype(AjPTable* desctable, AjPTable* typetable) { AjPFile knownFile = NULL; AjPStr knownFName = NULL; AjPStr knownRoot = NULL; AjPStr knownRootInst = NULL; AjPStr knownPack = NULL; AjPStr knownLine = NULL; AjBool ok = ajFalse; AjPStrTok handle = NULL; AjPStr knownName = NULL; AjPStr knownName2 = NULL; AjPStr knownType = NULL; AjPStr knownDesc = NULL; AjPStr knownEdam = NULL; AjPStr knownRest = NULL; ajint iline = 0; ajStrAssignS(&knownPack, ajNamValuePackage()); ajStrAssignS(&knownRootInst, ajNamValueInstalldir()); ajDirnameFix(&knownRootInst); *desctable = ajTablestrNewCase(2048); *typetable = ajTablestrNewCase(2048); if(ajNamGetValueC("acdroot", &knownRoot)) { ajDirnameFix(&knownRoot); ajFmtPrintS(&knownFName, "%Sknowntypes.standard", knownRoot); knownFile = ajFileNewInNameS(knownFName); acdLog("Knowntypes file in acdroot: '%S'\n", knownFName); } else { ajFmtPrintS(&knownFName, "%Sshare/%S/acd/knowntypes.standard", knownRootInst, knownPack); acdLog("Knowntypes file installed: '%S'\n", knownFName); knownFile = ajFileNewInNameS(knownFName); if(!knownFile) { acdLog("Knowntypes file '%S' not opened\n", knownFName); ajStrAssignS(&knownRoot, ajNamValueRootdir()); ajDirnameFix(&knownRoot); ajFmtPrintS(&knownFName, "%Sacd/knowntypes.standard", knownRoot); acdLog("Knowntypes file from source dir: '%S'\n", knownFName); knownFile = ajFileNewInNameS(knownFName); } } if(!knownFile) /* test acdc-knownmissing */ ajDie("Knowntypes file %S not found", knownFName); else acdLog("Knowntypes file %F used\n", knownFile); while(knownFile && ajReadlineTrim(knownFile, &knownLine)) { iline++; if(ajStrCutComments(&knownLine)) { handle = ajStrTokenNewC(knownLine, "|"); ok = ajStrTokenNextParse(&handle, &knownName); if(ok) ok = ajStrTokenNextParse(&handle, &knownType); if(ok) ok = ajStrTokenNextParse(&handle, &knownEdam); if(ok) ajStrTokenRestParse(&handle, &knownDesc); ajStrTokenDel(&handle); if(ok) { ajStrRemoveWhiteExcess(&knownName); ajStrExchangeKK(&knownName, '_', ' '); ajStrRemoveWhiteExcess(&knownType); if(ajStrMatchCaseC(knownType, "infile") || ajStrMatchCaseC(knownType, "filelist") || ajStrMatchCaseC(knownType, "datafile") || ajStrMatchCaseC(knownType, "outfile")) { ajWarn("Knowntype '%S' in file %S line %d - use 'file'", knownType, knownFName, iline); } if(ajTablePut(*typetable, knownName, knownType)) ajWarn("Duplicate knowntype name '%S' in file %S line %d", knownName, knownFName, iline); ajStrAssignS(&knownName2, knownName); ajStrRemoveWhiteExcess(&knownEdam); ajStrQuoteStrip(&knownEdam); if(ajStrPrefixC(knownEdam, "EDAM_data: ")) { ajStrCutStart(&knownEdam, 11); } else if(ajStrPrefixC(knownEdam, "EDAM_data:")) { ajStrCutStart(&knownEdam, 14); } else if(ajStrGetLen(knownEdam)) ajWarn("Knowntype '%S' in file %S line %d - EDAM badly formatted '%S'", knownType, knownFName, iline, knownEdam); ajStrRemoveWhiteExcess(&knownDesc); ajTablePut(*desctable, knownName2, knownDesc); /* ajUser("name '%S' type '%S' desc '%S' edam '%S'", knownName, knownType, knownDesc, knownEdam);*/ knownName = NULL; knownName2 = NULL; knownType = NULL; knownDesc = NULL; } else ajErr("Bad record in file %S:\n%S", knownFName, knownLine); } } ajFileClose(&knownFile); ajStrDel(&knownFName); ajStrDel(&knownRoot); ajStrDel(&knownRootInst); ajStrDel(&knownPack); ajStrDel(&knownLine); ajStrDel(&knownName); ajStrDel(&knownType); ajStrDel(&knownEdam); ajStrDel(&knownDesc); ajStrDel(&knownRest); return; } ID acdValidApplGroup TY static MO ajacd LB acd XX DE Validation for application groups XX PN [1] PA r groups const AjPStr PD Group name(s) PX RT void RD RX // static void acdValidApplGroup(const AjPStr groups) { AjPRegexp grpexp = NULL; AjPStr tmpGroups = NULL; AjPStr grpName = NULL; const AjPStr grpDesc = NULL; if(!acdDoValid) return; acdGrpTable = acdReadGroups(); ajStrAssignS(&tmpGroups, groups); /* step through each group */ grpexp = ajRegCompC("([^,|]+),?"); while(ajRegExec(grpexp, tmpGroups)) { ajRegSubI(grpexp, 1, &grpName); ajStrRemoveWhiteExcess(&grpName); grpDesc = ajTableFetchS(acdGrpTable, grpName); if(!grpDesc) acdErrorValid("Unknown group '%S' for application", grpName); ajRegPost(grpexp, &tmpGroups); } ajStrDel(&grpName); ajStrDel(&tmpGroups); ajRegFree(&grpexp); return; } ID acdValidApplKeywords TY static MO ajacd LB acd XX DE Validation for application keywords XX PN [1] PA r keys const AjPStr PD Keyword(s) PX RT void RD RX // static void acdValidApplKeywords(const AjPStr keys) { static AjPTable keyTable = NULL; AjPRegexp keyexp = NULL; AjPStr tmpKeys = NULL; AjPStr keyName = NULL; const AjPStr keyDesc = NULL; if(!acdDoValid) return; keyTable = acdReadKeywords(); ajStrAssignS(&tmpKeys, keys); /* step through each group */ keyexp = ajRegCompC("([^,|]+),?"); while(ajRegExec(keyexp, tmpKeys)) { ajRegSubI(keyexp, 1, &keyName); ajStrRemoveWhiteExcess(&keyName); keyDesc = ajTableFetchS(keyTable, keyName); if(!keyDesc) acdErrorValid("Unknown keyword '%S' for application", keyName); ajRegPost(keyexp, &tmpKeys); } ajStrDel(&keyName); ajStrDel(&tmpKeys); ajRegFree(&keyexp); return; } ID acdReadGroups TY static MO ajacd LB acd XX DE Read standard table of application groups XX RT AjPTable RD String table of group names and descriptions RX // static AjPTable acdReadGroups(void) { AjPTable ret = ajTablestrNewCase(50); AjPFile grpFile = NULL; AjPStr grpFName = NULL; AjPStr grpRoot = NULL; AjPStr grpRootInst = NULL; AjPStr grpPack = NULL; AjPStr grpLine = NULL; AjPRegexp grpxp = NULL; AjPStr grpName = NULL; AjPStr grpDesc = NULL; ajStrAssignS(&grpPack, ajNamValuePackage()); ajStrAssignS(&grpRootInst, ajNamValueInstalldir()); ajDirnameFix(&grpRootInst); if(ajNamGetValueC("acdroot", &grpRoot)) { ajDirnameFix(&grpRoot); ajFmtPrintS(&grpFName, "%Sgroups.standard", grpRoot); grpFile = ajFileNewInNameS(grpFName); acdLog("Group file in acdroot: '%S'\n", grpFName); } else { ajFmtPrintS(&grpFName, "%Sshare/%S/acd/groups.standard", grpRootInst, grpPack); acdLog("Group file installed: '%S'\n", grpFName); grpFile = ajFileNewInNameS(grpFName); if(!grpFile) { acdLog("Grp file '%S' not opened\n", grpFName); ajStrAssignS(&grpRoot, ajNamValueRootdir()); ajDirnameFix(&grpRoot); ajFmtPrintS(&grpFName, "%Sacd/groups.standard", grpRoot); acdLog("Grp file from source dir: '%S'\n", grpFName); grpFile = ajFileNewInNameS(grpFName); } } if(!grpFile) /* test acdc-grpmissing */ ajDie("Group file %S not found", grpFName); else acdLog("Group file %F used\n", grpFile); grpxp = ajRegCompC("^EDAM_[a-z]+:\\d+\\s+([^ ]+) +([^ ].*)"); while(grpFile && ajReadlineTrim(grpFile, &grpLine)) { if(ajStrCutComments(&grpLine)) { ajStrRemoveWhiteExcess(&grpLine); if(ajRegExec(grpxp, grpLine)) { ajRegSubI(grpxp, 1, &grpName); ajRegSubI(grpxp, 2, &grpDesc); ajStrExchangeKK(&grpName, '_', ' '); if(ajTablePut(ret, grpName, grpDesc)) ajWarn("Duplicate group name '%S' in file %S", grpName, grpFName); grpName = NULL; grpDesc = NULL; } else ajErr("Bad record in file %S:\n%S", grpFName, grpLine); } } ajFileClose(&grpFile); ajStrDel(&grpFName); ajStrDel(&grpRoot); ajStrDel(&grpRootInst); ajStrDel(&grpPack); ajStrDel(&grpLine); ajStrDel(&grpName); ajStrDel(&grpDesc); ajRegFree(&grpxp); return ret; } ID acdReadKeywords TY static MO ajacd LB acd XX DE Read standard table of application keywords XX RT AjPTable RD String table of keyword names and descriptions RX // static AjPTable acdReadKeywords(void) { AjPTable ret = ajTablestrNewCase(50); AjPFile keyFile = NULL; AjPStr keyFName = NULL; AjPStr keyRoot = NULL; AjPStr keyRootInst = NULL; AjPStr keyPack = NULL; AjPStr keyLine = NULL; AjPRegexp keyxp = NULL; AjPStr keyName = NULL; AjPStr keyDesc = NULL; if(!acdGrpTable) acdGrpTable = acdReadGroups(); ajStrAssignS(&keyPack, ajNamValuePackage()); ajStrAssignS(&keyRootInst, ajNamValueInstalldir()); ajDirnameFix(&keyRootInst); if(ajNamGetValueC("acdroot", &keyRoot)) { ajDirnameFix(&keyRoot); ajFmtPrintS(&keyFName, "%Skeywords.standard", keyRoot); keyFile = ajFileNewInNameS(keyFName); acdLog("Keyword file in acdroot: '%S'\n", keyFName); } else { ajFmtPrintS(&keyFName, "%Sshare/%S/acd/keywords.standard", keyRootInst, keyPack); acdLog("Keyword file installed: '%S'\n", keyFName); keyFile = ajFileNewInNameS(keyFName); if(!keyFile) { acdLog("keyword file '%S' not opened\n", keyFName); ajStrAssignS(&keyRoot, ajNamValueRootdir()); ajDirnameFix(&keyRoot); ajFmtPrintS(&keyFName, "%Sacd/keywords.standard", keyRoot); acdLog("Keywords file from source dir: '%S'\n", keyFName); keyFile = ajFileNewInNameS(keyFName); } } if(!keyFile) /* test acdc-keymissing */ ajDie("Keyword file %S not found", keyFName); else acdLog("Keyword file %F used\n", keyFile); keyxp = ajRegCompC("([^ ]+) +([^ ].*)"); while(keyFile && ajReadlineTrim(keyFile, &keyLine)) { if(ajStrCutComments(&keyLine)) { ajStrRemoveWhiteExcess(&keyLine); if(ajRegExec(keyxp, keyLine)) { ajRegSubI(keyxp, 1, &keyName); ajRegSubI(keyxp, 2, &keyDesc); ajStrExchangeKK(&keyName, '_', ' '); if(ajTableFetchS(acdGrpTable, keyName)) ajWarn("Keyword %S in file %S is a known group", keyName, keyFName); if(ajTablePut(ret, keyName, keyDesc)) ajWarn("Duplicate keyword in file %S", keyFName); keyName = NULL; keyDesc = NULL; } else ajErr("Bad record in file %S:\n%S", keyFName, keyLine); } } ajFileClose(&keyFile); ajStrDel(&keyFName); ajStrDel(&keyRoot); ajStrDel(&keyRootInst); ajStrDel(&keyPack); ajStrDel(&keyLine); ajStrDel(&keyName); ajStrDel(&keyDesc); ajRegFree(&keyxp); return ret; } ID acdReadSections TY static MO ajacd LB acd XX DE Read standard file of ACD sections and store in AjPTable objects XX PN [1] PA wN typetable AjPTable* PD String table of section names and types PX PN [2] PA wN infotable AjPTable* PD String table of section names and PD descriptions PX RT void RD RX // static void acdReadSections(AjPTable* typetable, AjPTable* infotable) { AjPFile sectFile = NULL; AjPStr sectFName = NULL; AjPStr sectRoot = NULL; AjPStr sectRootInst = NULL; AjPStr sectPack = NULL; AjPStr sectLine = NULL; AjPRegexp sectxp = NULL; AjPStr sectName = NULL; AjPStr sectType = NULL; AjPStr sectDesc = NULL; ajStrAssignS(§Pack, ajNamValuePackage()); ajStrAssignS(§RootInst, ajNamValueInstalldir()); ajDirnameFix(§RootInst); *typetable = ajTablestrNewCase(50); *infotable = ajTablestrNewCase(50); if(ajNamGetValueC("acdroot", §Root)) { ajDirnameFix(§Root); ajFmtPrintS(§FName, "%Ssections.standard", sectRoot); sectFile = ajFileNewInNameS(sectFName); acdLog("Section file in acdroot: '%S'\n", sectFName); } else { ajFmtPrintS(§FName, "%Sshare/%S/acd/sections.standard", sectRootInst, sectPack); acdLog("Section file installed: '%S'\n", sectFName); sectFile = ajFileNewInNameS(sectFName); if(!sectFile) { acdLog("Sect file '%S' not opened\n", sectFName); ajStrAssignS(§Root, ajNamValueRootdir()); ajDirnameFix(§Root); ajFmtPrintS(§FName, "%Sacd/sections.standard", sectRoot); acdLog("Sect file from source dir: '%S'\n", sectFName); sectFile = ajFileNewInNameS(sectFName); } } if(!sectFile) /* test acdc-sectmissing */ ajDie("Section file %S not found", sectFName); else acdLog("Section file %F used\n", sectFile); sectxp = ajRegCompC("([^ ]+) +([^ ]+) +([^ ].*)"); while(sectFile && ajReadlineTrim(sectFile, §Line)) { if(ajStrCutComments(§Line)) { ajStrRemoveWhiteExcess(§Line); if(ajRegExec(sectxp, sectLine)) { ajRegSubI(sectxp, 1, §Name); ajRegSubI(sectxp, 2, §Type); ajRegSubI(sectxp, 3, §Desc); ajStrExchangeKK(§Name, '_', ' '); if(ajTablePut(*typetable, sectName, sectType)) ajWarn("Duplicate section name in file %S", sectFName); if(ajTablePut(*infotable, sectName, sectDesc)) ajWarn("Duplicate section name in file %S", sectFName); sectName = NULL; sectType = NULL; sectDesc = NULL; } else ajErr("Bad record in file %S:\n%S", sectFName, sectLine); } } ajFileClose(§File); ajStrDel(§FName); ajStrDel(§Root); ajStrDel(§RootInst); ajStrDel(§Pack); ajStrDel(§Line); ajStrDel(§Name); ajStrDel(§Type); ajStrDel(§Desc); ajRegFree(§xp); return; } ID acdValidSectionMatch TY static MO ajacd LB acd XX DE Tests whether a named section appears in the current section list XX PN [1] PA r secname const char* PD Section name PX RT AjBool RD ajTrue if the named section was found RX // static AjBool acdValidSectionMatch(const char* secname) { AjIList iter = NULL; AjPStr listsecname; AjBool ret = ajFalse; if(!ajListGetLength(acdSecList)) return ajFalse; iter = ajListIterNewread(acdSecList); while (!ajListIterDone(iter)) { listsecname = ajListIterGet(iter); if(ajStrMatchC(listsecname, secname)) { ret = ajTrue; break; } } ajListIterDel(&iter); return ret; } ID acdValidSectionFull TY static MO ajacd LB acd XX DE Returns the full section name with the top level and any frame DE sub-sections XX PN [1] PA w secname AjPStr* PD Section name PX RT void RD RX // static void acdValidSectionFull(AjPStr* secname) { AjIList iter = NULL; AjPStr listsecname; ajStrAssignClear(secname); if(!ajListGetLength(acdSecList)) return; iter = ajListIterNewread(acdSecList); while (!ajListIterDone(iter)) { listsecname = ajListIterGet(iter); if (ajStrGetLen(*secname)) ajStrAppendK(secname,':'); ajStrAppendS(secname, listsecname); } ajListIterDel(&iter); return; } ID acdOutFormatCpdb TY static MO ajacd LB acd XX DE Tests the output format for an outcpdb ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool acdOutFormatCpdb(const AjPStr name, ajint* iformat) { ajint i; const char* format[] = { "cpdb", NULL }; for (i=0; format[i]; i++) if(ajStrMatchCaseC(name, format[i])) { *iformat = i; return ajTrue; } return ajFalse; } ID acdOutFormatData TY static MO ajacd LB acd XX DE Tests the output format for an out ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool acdOutFormatData(const AjPStr name, ajint* iformat) { ajint i; const char* format[] = { "text", NULL }; for (i=0; format[i]; i++) if(ajStrMatchCaseC(name, format[i])) { *iformat = i; return ajTrue; } return ajFalse; } ID acdOutFormatDiscrete TY static MO ajacd LB acd XX DE Tests the output format for an out ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool acdOutFormatDiscrete(const AjPStr name, ajint* iformat) { ajint i; const char* format[] = { "phylip", NULL }; for (i=0; format[i]; i++) if(ajStrMatchCaseC(name, format[i])) { *iformat = i; return ajTrue; } return ajFalse; } ID acdOutFormatDistance TY static MO ajacd LB acd XX DE Tests the output format for an outdistance ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool acdOutFormatDistance(const AjPStr name, ajint* iformat) { ajint i; const char* format[] = { "phylip", NULL }; for (i=0; format[i]; i++) if(ajStrMatchCaseC(name, format[i])) { *iformat = i; return ajTrue; } return ajFalse; } ID acdOutFormatFreq TY static MO ajacd LB acd XX DE Tests the output format for an outfreq ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool acdOutFormatFreq(const AjPStr name, ajint* iformat) { ajint i; const char* format[] = { "phylip", NULL }; for (i=0; format[i]; i++) if(ajStrMatchCaseC(name, format[i])) { *iformat = i; return ajTrue; } return ajFalse; } ID acdOutFormatMatrix TY static MO ajacd LB acd XX DE Tests the output format for an outmatrix ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool acdOutFormatMatrix(const AjPStr name, ajint* iformat) { ajint i; const char* format[] = { "emboss", NULL }; for (i=0; format[i]; i++) if(ajStrMatchCaseC(name, format[i])) { *iformat = i; return ajTrue; } return ajFalse; } ID acdOutFormatMatrixf TY static MO ajacd LB acd XX DE Tests the output format for an outmatrixf ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool acdOutFormatMatrixf(const AjPStr name, ajint* iformat) { ajint i; const char* format[] = { "emboss", NULL }; for (i=0; format[i]; i++) if(ajStrMatchCaseC(name, format[i])) { *iformat = i; return ajTrue; } return ajFalse; } ID acdOutFormatProperties TY static MO ajacd LB acd XX DE Tests the output format for an outproperties ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool acdOutFormatProperties(const AjPStr name, ajint* iformat) { ajint i; const char* format[] = { "phylip", NULL }; for (i=0; format[i]; i++) if(ajStrMatchCaseC(name, format[i])) { *iformat = i; return ajTrue; } return ajFalse; } ID acdOutFormatScop TY static MO ajacd LB acd XX DE Tests the output format for an outscop ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool acdOutFormatScop(const AjPStr name, ajint* iformat) { ajint i; const char* format[] = { "scop", NULL }; for (i=0; format[i]; i++) if(ajStrMatchCaseC(name, format[i])) { *iformat = i; return ajTrue; } return ajFalse; } ID acdOutFormatTree TY static MO ajacd LB acd XX DE Tests the output format for an outtree ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool acdOutFormatTree(const AjPStr name, ajint* iformat) { ajint i; const char* format[] = { "phylip", "newick", NULL }; for (i=0; format[i]; i++) if(ajStrMatchCaseC(name, format[i])) { *iformat = i; return ajTrue; } return ajFalse; } ID acdKnownValueList TY static MO ajacd LB acd XX DE Finds a list value associated with a known type XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA w value AjPStr* PD Standard value PX RT AjBool RD ajTrue if a value was set RX // static AjBool acdKnownValueList(const AcdPAcd thys, AjPStr* value) { const AjPStr type = NULL; AjPStr resource = NULL; AjPStr list = NULL; ajint i; type = acdAttrValue(thys, "knowntype"); if(!type) return ajFalse; for(i=0; acdResource[i]; i++) if(ajStrMatchCaseC(type, acdResource[i])) { resource = ajStrNewS(type); ajStrExchangeKK(&resource, ' ', '_'); if(ajNamRsListValue(resource, &list)) { if(acdResourceList(thys, list, value)) return ajTrue; } } return ajFalse; } ID acdKnownValueSelect TY static MO ajacd LB acd XX DE Finds a selection value associated with a known type XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA w value AjPStr* PD Standard value PX RT AjBool RD ajTrue if a value was set RX // static AjBool acdKnownValueSelect(const AcdPAcd thys, AjPStr* value) { const AjPStr type = NULL; AjPStr resource = NULL; AjPStr list = NULL; ajint i; type = acdAttrValue(thys, "knowntype"); if(!type) return ajFalse; for(i=0; acdResource[i]; i++) if(ajStrMatchCaseC(type, acdResource[i])) { resource = ajStrNewS(type); ajStrExchangeKK(&resource, ' ', '_'); if(ajNamRsListValue(resource, &list)) { if(acdResourceList(thys, list, value)) return ajTrue; } } return ajFalse; } ID acdResourceList TY static MO ajacd LB acd XX DE Finds a list value associated with a known type XX PN [1] PA r thys const AcdPAcd PD ACD object PX PN [2] PA r list const AjPStr PD Resource value PX PN [3] PA w value AjPStr* PD Standard list value PX RT AjBool RD ajTrue if a value was set RX // static AjBool acdResourceList(const AcdPAcd thys, const AjPStr list, AjPStr* value) { AjPStr delim = NULL; AjPStr codedelim = NULL; AjPFile infile = NULL; AjPStr line = NULL; AjPStr tok1 = NULL; AjPStr tok2 = NULL; AjPStrTok handle = NULL; AjPStr liststr = NULL; if (!ajStrGetLen(list)) return ajFalse; liststr = ajStrNewS(list); acdAttrValueStr(thys, "delimiter", ";", &delim); acdAttrValueStr(thys, "codedelimiter", ":", &codedelim); if(ajStrGetCharFirst(list) == '@') { ajStrAssignClear(value); ajStrCutStart(&liststr, 1); infile = ajDatafileNewInNameS(liststr); if(!infile) return ajFalse; while(ajReadlineTrim(infile, &line)) { ajStrTrimWhite(&line); if(ajStrGetCharFirst(line) == '#') continue; ajStrTokenAssignC(&handle, line, " "); ajStrTokenNextParse(&handle, &tok1); ajStrTokenRestParse(&handle, &tok2); if(ajStrGetLen(*value)) ajStrAppendS(value, delim); ajStrAppendS(value, tok1); ajStrAppendS(value, codedelim); ajStrAppendS(value, tok2); } ajFileClose(&infile); return ajTrue; } /* value will use : and ; as delimiters */ ajStrAssignS(value, liststr); ajStrExchangeKK(value, ';', '\1'); ajStrExchangeKK(value, ':', '\2'); ajStrExchangeKK(value, '\1', ajStrGetCharFirst(delim)); ajStrExchangeKK(value, '\2', ajStrGetCharFirst(codedelim)); return ajTrue; } ID acdDelAlign TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD alignment data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelAlign(void** PPval) { if(!*PPval) return; ajAlignDel((AjPAlign*)PPval); return; } ID acdDelAssembly TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD assembly input data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelAssembly(void** PPval) { if(!*PPval) return; ajAssemDel((AjPAssem*)PPval); return; } ID acdDelCod TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD codon usage data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelCod(void** PPval) { if(!*PPval) return; ajCodDel((AjPCod*)PPval); return; } ID acdDelDir TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD directory data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelDir(void** PPval) { if(!*PPval) return; ajDirDel((AjPDir*)PPval); return; } ID acdDelDirout TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD directory data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelDirout(void** PPval) { if(!*PPval) return; ajDiroutDel((AjPDirout*)PPval); return; } ID acdDelFeattable TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD feature table data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelFeattable(void** PPval) { if(!*PPval) return; ajFeattaballDel((AjPFeattaball*)PPval); return; } ID acdDelFeattabOut TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD output feature table data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelFeattabOut(void** PPval) { if(!*PPval) return; ajFeattabOutDel((AjPFeattabOut*)PPval); return; } ID acdDelFile TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD input file data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelFile(void** PPval) { if(!*PPval) return; ajFileClose((AjPFile*)PPval); return; } ID acdDelFloat TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD floating point array data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelFloat(void** PPval) { if(!*PPval) return; ajFloatDel((AjPFloat*)PPval); return; } ID acdDelList TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD list/selection data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelList(void** PPval) { if(!*PPval) return; ajListstrFreeData((AjPList*)PPval); return; } ID acdDelMatrix TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD integer comparison matrix data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelMatrix(void** PPval) { if(!*PPval) return; ajMatrixDel((AjPMatrix*)PPval); return; } ID acdDelMatrixf TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD floating point comparison DE matrix data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelMatrixf(void** PPval) { if(!*PPval) return; ajMatrixfDel((AjPMatrixf*)PPval); return; } ID acdDelObo TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD obo term input data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelObo(void** PPval) { if(!*PPval) return; ajOboallDel((AjPOboall*)PPval); return; } ID acdDelOutfile TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD output file data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelOutfile(void** PPval) { if(!*PPval) return; ajOutfileClose((AjPOutfile*)PPval); return; } ID acdDelPatlist TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD pattern list data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelPatlist(void** PPval) { if(!*PPval) return; ajPatlistSeqDel((AjPPatlistSeq*)PPval); return; } ID acdDelPhyloDist TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD phylogenetic distance data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelPhyloDist(void** PPval) { if(!*PPval) return; ajPhyloDistDel((AjPPhyloDist*)PPval); return; } ID acdDelPhyloFreq TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD phylogenetic frequency data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelPhyloFreq(void** PPval) { ajUser("acdDelPhyloFreq '%x'", *PPval); if(!*PPval) return; ajPhyloFreqDel((AjPPhyloFreq*)PPval); return; } ID acdDelPhyloProp TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD phylogenetic properties data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelPhyloProp(void** PPval) { if(!*PPval) return; ajPhyloPropDel((AjPPhyloProp*)PPval); return; } ID acdDelPhyloState TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD phylogenetic state data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelPhyloState(void** PPval) { if(!*PPval) return; ajPhyloStateDel((AjPPhyloState*)PPval); return; } ID acdDelPhyloTree TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD phylogenetic tree data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelPhyloTree(void** PPval) { if(!*PPval) return; ajPhyloTreeDelarray((AjPPhyloTree**)PPval); return; } ID acdDelRange TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD range data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelRange(void** PPval) { if(!*PPval) return; ajRangeDel((AjPRange*)PPval); return; } ID acdDelRefseq TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD reference sequence input data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelRefseq(void** PPval) { if(!*PPval) return; ajRefseqDel((AjPRefseq*)PPval); return; } ID acdDelReg TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD regular expression data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelReg(void** PPval) { if(!*PPval) return; ajPatlistRegexDel((AjPPatlistRegex*)PPval); return; } ID acdDelReport TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD report data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelReport(void** PPval) { if(!*PPval) return; ajReportDel((AjPReport*)PPval); return; } ID acdDelResource TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD data resource input data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelResource(void** PPval) { if(!*PPval) return; ajResourceallDel((AjPResourceall*)PPval); return; } ID acdDelSeq TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD sequence data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelSeq(void** PPval) { if(!*PPval) return; ajSeqDel((AjPSeq*)PPval); return; } ID acdDelSeqall TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD sequence stream data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelSeqall(void** PPval) { if(!*PPval) return; ajSeqallDel((AjPSeqall*)PPval); return; } ID acdDelSeqout TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD sequence output data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelSeqout(void** PPval) { if(!*PPval) return; ajSeqoutDel((AjPSeqout*)PPval); return; } ID acdDelSeqset TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD sequence set data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelSeqset(void** PPval) { if(!*PPval) return; ajSeqsetDel((AjPSeqset*)PPval); return; } ID acdDelSeqsetArray TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD sequence sets data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelSeqsetArray(void** PPval) { if(!*PPval) return; ajSeqsetDelarray((AjPSeqset**)PPval); return; } ID acdDelStr TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD string data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelStr(void** PPval) { if(!*PPval) return; ajStrDel((AjPStr*)PPval); return; } ID acdDelStrArray TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD string array data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelStrArray(void** PPval) { if(!*PPval) return; ajStrDelarray((AjPStr**)PPval); return; } ID acdDelTaxon TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD taxonomy input data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelTaxon(void** PPval) { if(!*PPval) return; ajTaxallDel((AjPTaxall*)PPval); return; } ID acdDelText TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD text input data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelText(void** PPval) { if(!*PPval) return; ajTextallDel((AjPTextall*)PPval); return; } ID acdDelUrl TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD URL input data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelUrl(void** PPval) { if(!*PPval) return; ajUrlallDel((AjPUrlall*)PPval); return; } ID acdDelVariation TY static MO ajacd LB acd XX DE Function with void** prototype to delete ACD variation input data XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdDelVariation(void** PPval) { if(!*PPval) return; ajVarloadDel((AjPVarload*)PPval); return; } ID acdFree TY static MO ajacd LB acd XX DE Function to delete ACD data using the standard free function XX PN [1] PA d PPval void** PD Value to be deleted PX RT void RD RX // static void acdFree(void** PPval) { if(!*PPval) return; AJFREE(*PPval); return; } ID ajAcdUnused TY public MO ajacd LB acd XX DE Dummy function to catch all unused functions defined in the ajacd DE source file. XX RT void RD RX // void ajAcdUnused(void) { AjPStr ajpstr=NULL; AcdPAcd acdpacd=NULL; float f=0.0; char c; acdSetXxxx(acdpacd); /* template function for acdSet */ acdAttrToChar(acdpacd, "attr", '.', &c); acdQualToFloat(acdpacd, "", 0.0, 0, &f, &ajpstr); acdCountType("outfile"); acdPrintUsed(); return; } ID acdRangeTestCalc TY static MO ajacd LB acd XX DE Tests for calculated minimum and maximum attributes. If found, requires DE other attributes to be defined to control the bahviour if the range DE is impossible in the current case XX PN [1] PA r thys const AcdPAcd PD Acd object PX RT AjBool RD True if a calculated value was found RX // static AjBool acdRangeTestCalc(const AcdPAcd thys) { const AjPStr tmpstr; AjBool toggle = ajFalse; AjBool iscalcmin = ajFalse; AjBool iscalcmax = ajFalse; AjBool iscalc = ajFalse; AjBool failrange = ajFalse; AjBool truemin = ajFalse; tmpstr = acdAttrValue(thys, "minimum"); if(!MAJSTRGETLEN(tmpstr)) return ajFalse; if(acdVarTestValid(tmpstr, &toggle)) iscalcmin = ajTrue; tmpstr = acdAttrValue(thys, "maximum"); if(!MAJSTRGETLEN(tmpstr)) return ajFalse; if(acdVarTestValid(tmpstr, &toggle)) iscalcmax = ajTrue; if(iscalcmin || iscalcmax) { iscalc = ajTrue; if(!acdAttrToBool(thys, "failrange", ajFalse, &failrange)) { acdErrorAcd(thys, "Attribute %s: required with any calculated min/max", "failrange", acdType[thys->Type].Name); } else { if(failrange) { tmpstr = acdAttrValue(thys, "rangemessage"); if(!tmpstr) acdErrorAcd(thys, "Attribute %s: required for failrange: \"Y\"", "rangemessage", acdType[thys->Type].Name); } else { if(!acdAttrToBool(thys, "trueminimum",ajFalse, &truemin)) acdErrorAcd(thys, "Attribute %s: required for failrange: \"N\"", "trueminimum", acdType[thys->Type].Name); } } } return iscalc; } ID acdRegVarDefine TY static MO ajacd LB acd XX DE Define regular expressions for parsing variables and functions XX RT void RD RX // static void acdRegVarDefine(void) { if(!acdRegVarname) acdRegVarname = ajRegCompC("^(.*)\\$\\(([a-zA-Z0-9_.]+)\\)"); if(!acdRegToggle) acdRegToggle = ajRegCompC("^(@\\([!])?(\\$\\([a-zA-Z0-9_.]+\\))\\)?$"); if(!acdRegFunction) acdRegFunction = ajRegCompC("^(.*)\\@\\(([^()]+)\\)"); return; } ID acdEdamTest TY static MO ajacd LB acd XX DE Test an EDAM term from a relations attribute XX PN [1] PA r relation const AjPStr PD Relation single attribute value PX RT const AjPStr RD Namespace RX // static const AjPStr acdEdamTest(const AjPStr relation) { AjPStr term = NULL; AjPStr name = NULL; AjPStr id = NULL; AjPStrTok handle = NULL; AjPStr token = NULL; AjPStr namespace = NULL; AjPObo oboterm = NULL; AjPObo obonameterm = NULL; AjPStr namequery = NULL; AjBool obonamefound = ajFalse; if(!acdEdam) return NULL; if(!ajStrPrefixC(relation, "EDAM")) { ajFmtPrintS(&namequery, "data:%S", relation); obonameterm = ajObodataFetchName(acdEdam, namequery); if(obonameterm) { obonamefound = ajTrue; acdErrorValid("Relation '%S' matches EDAM term EDAM_data:%S %S", relation, oboterm->Namespace, oboterm->Id); } ajFmtPrintS(&namequery, "topic:%S", relation); obonameterm = ajObodataFetchName(acdEdam, namequery); if(obonameterm) { obonamefound = ajTrue; acdErrorValid("Relation '%S' matches EDAM term EDAM_topic:%S %S", relation, oboterm->Id, oboterm->Namespace); } ajFmtPrintS(&namequery, "operation:%S", relation); obonameterm = ajObodataFetchName(acdEdam, namequery); if(obonameterm) { obonamefound = ajTrue; acdErrorValid("Relation '%S' matches EDAM term EDAM_operation:%S %S", relation, oboterm->Id, oboterm->Namespace); } if(!obonamefound) acdErrorValid("Not an EDAM term in relation '%S'", relation); return NULL; } ajStrExtractFirst(relation, &name, &term); handle = ajStrTokenNewC(term, ":"); if(!ajStrTokenNextParse(&handle, &namespace)) /* EDAM */ acdErrorValid("Bad relation term '%S'", term); else if(!ajStrTokenNextParse(&handle, &token)) /* ID */ acdErrorValid("Bad relation term '%S'", term); if(ajStrPrefixC(namespace, "EDAM_")) ajStrCutStart(&namespace, 5); if(!ajStrGetLen(namespace)) acdErrorValid("Bad relation no namespace"); ajFmtPrintS(&id, "EDAM_%S:%S", namespace, token); oboterm = ajObodataFetchId(acdEdam, id); if(!oboterm) { acdErrorValid("Unknown relation term '%S' fetch failed for '%S'", term, id); return NULL; } if(ajOboIsObsolete(oboterm)) { if(ajOboGetReplaced(oboterm)) { obonameterm = ajObodataFetchId(acdEdam, ajOboGetReplaced(oboterm)); if(obonameterm) acdErrorValid("Relation term '%S' obsolete, " "possible replacement %S %S %S", term, ajOboGetReplaced(oboterm), obonameterm->Namespace, obonameterm->Name); else acdErrorValid("Relation term '%S' obsolete, " "possible replacement '%S'", term, ajOboGetReplaced(oboterm)); } else { acdErrorValid("Relation term '%S' obsolete", term); } } if(!ajStrMatchS(id, oboterm->Id)) acdErrorValid("Relation term '%S' bad id, expected '%S' (%S)", term, oboterm->Id, id); if(!ajStrMatchS(name, oboterm->Name)) { obonamefound = ajFalse; ajFmtPrintS(&namequery, "%S:%S", namespace, name); obonameterm = ajObodataFetchName(acdEdam, namequery); if(obonameterm) { obonamefound = ajTrue; if(ajStrMatchS(id, obonameterm->Id)) acdErrorValid("Relation term '%S' bad name, expected '%S' " "but matches synonym", term, oboterm->Name); else acdErrorValid("Relation term '%S' bad name, expected '%S' " "but matches name for %S %S %S", term, oboterm->Name, obonameterm->Id, obonameterm->Namespace, obonameterm->Name); } if(!ajStrMatchC(namespace, "topic")){ ajFmtPrintS(&namequery, "topic:%S", name); obonameterm = ajObodataFetchName(acdEdam, namequery); if(obonameterm) { obonamefound = ajTrue; acdErrorValid("Relation term '%S' bad name, expected '%S' " "but matches name for %S %S %S", term, oboterm->Name, obonameterm->Id, obonameterm->Namespace, obonameterm->Name); } } if(!ajStrMatchC(namespace, "operation")){ ajFmtPrintS(&namequery, "operation:%S", name); obonameterm = ajObodataFetchName(acdEdam, namequery); if(obonameterm) { obonamefound = ajTrue; acdErrorValid("Relation term '%S' bad name, expected '%S' " "but matches name for %S %S %S", term, oboterm->Name, obonameterm->Id, obonameterm->Namespace, obonameterm->Name); } } if(!obonamefound) acdErrorValid("Relation term '%S' bad name, expected '%S'", term, oboterm->Name); } if(!ajStrMatchS(namespace, oboterm->Namespace)) acdErrorValid("Relation term '%S' bad namespace, expected '%S'", term, oboterm->Namespace); return oboterm->Namespace; } ID ajAcdedamParse TY public MO ajacd LB acd XX DE Parse an EDAM term from a relations attribute XX PN [1] PA r relation const AjPStr PD Relation single attribute value PX PN [2] PA w id AjPStr* PD EDAM term numeric id PX PN [3] PA w namespace AjPStr* PD EDAM term namespace PX PN [4] PA w name AjPStr* PD EDAM term name PX RT AjBool RD True on success RX // AjBool ajAcdedamParse(const AjPStr relation, AjPStr* id, AjPStr* namespace, AjPStr* name) { AjPStr token = NULL; AjPStr term = NULL; AjPStr rest = NULL; AjPStrTok handle = NULL; if(!ajStrPrefixC(relation, "EDAM_")) return ajFalse; ajStrExtractFirst(relation, &rest, &term); handle = ajStrTokenNewC(term, ":_"); if(!ajStrTokenNextParse(&handle, &token)) /* EDAM */ return ajFalse; else if(!ajStrTokenNextParse(&handle, namespace)) /* namespace */ return ajFalse; else if(!ajStrTokenNextParse(&handle, id)) /* ID */ { ajStrSetClear(namespace); return ajFalse; } ajStrAssignS(name, rest); ajStrDel(&token); ajStrDel(&term); ajStrDel(&rest); ajStrTokenDel(&handle); return ajTrue; } ID ajAcdtypeGetGroup TY public MO ajacd LB acd XX DE Return the ACD group for an ACD datatype XX PN [1] PA r type const AjPStr PD ACD type name PX RT const char* RD Group name RX // const char* ajAcdtypeGetGroup(const AjPStr type) { ajuint i; i = 0; while(acdType[i].Name) /* ACD types as qualifiers */ { if(ajStrMatchC(type, acdType[i].Name)) return acdType[i].Group; i++; } return NULL; } ID acdTestDebugIsSet TY static MO ajacd LB acd XX DE Tests whether the command line switch for debug messages has been set DE by testing internal variable 'acdDebugSet' XX RT AjBool RD Debugging status. RX // static AjBool acdTestDebugIsSet(void) { ajDebug("acdTestDebugIsSet returning %B\n", acdDebugSet); return acdDebugSet; } ID acdTestDebug TY static MO ajacd LB acd XX DE Tests whether debug messages are required by checking DE internal variable 'acdDebug' XX RT AjBool RD Debugging status. RX // static AjBool acdTestDebug(void) { ajDebug("acdDebug returning %B\n", acdDebug); return acdDebug; } ID acdTestFilter TY static MO ajacd LB acd XX DE Tests whether input and output use stdin and stdout as a filter DE by returning internal variable 'acdFilter' XX RT AjBool RD Filter status. RX // static AjBool acdTestFilter(void) { return acdFilter; } ID acdTestStdout TY static MO ajacd LB acd XX DE Tests whether output uses stdout for output by default DE by returning internal variable 'acdStdout' XX RT AjBool RD Stdout status. RX // static AjBool acdTestStdout(void) { return acdStdout; } ID alignFormat TY list MO ajalign LB core XX DE Functions to write alignments XX // static AlignOFormat alignFormat[] = { /* name description function */ /* Alias nucleic protein header min max zero */ /* standard sequence formats */ {"unknown", "Unknown format", AJFALSE, AJFALSE, AJFALSE, AJTRUE, AJTRUE, 0, 0, 0, &alignWriteSimple}, {"fasta", "Fasta format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteFasta}, {"a2m", "A2M (fasta) format sequence", /* same as fasta */ AJTRUE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteFasta}, {"msf", "MSF format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteMsf}, {"clustal", "clustalw format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteClustal}, {"mega", "Mega format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteMega}, {"meganon", "Mega non-interleaved format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteMeganon}, {"nexus", "nexus/paup format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteNexus}, {"paup", "nexus/paup format sequence (alias)", AJTRUE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteNexus}, {"nexusnon", "nexus/paup non-interleaved format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteNexusnon}, {"paupnon", "nexus/paup non-interleaved format sequence (alias)", AJTRUE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteNexusnon}, {"phylip", "phylip format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWritePhylip}, {"phylipnon", "phylip non-interleaved format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWritePhylipnon}, {"selex", "SELEX format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteSelex}, {"treecon", "Treecon format sequence", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteTreecon}, /* trace for debug */ {"debug", "Debugging trace of full internal data content", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 0, 0, &alignWriteTrace}, /* alignment formats */ {"markx0", "Pearson MARKX0 format", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 2, 2, &alignWriteMarkX0}, {"markx1", "Pearson MARKX1 format", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 2, 2, &alignWriteMarkX1}, {"markx2", "Pearson MARKX2 format", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 2, 2, &alignWriteMarkX2}, {"markx3", "Pearson MARKX3 format", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 2, 2, &alignWriteMarkX3}, {"markx10", "Pearson MARKX10 format", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 2, 2, &alignWriteMarkX10}, {"match","Start and end of matches between sequence pairs", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJFALSE, 0, 2, 2, &alignWriteMatch}, {"multiple", "Simple multiple alignment", AJTRUE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 0, 0, &alignWriteSimple}, {"pair", "Simple pairwise alignment", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 2, 2, &alignWriteSimple}, {"simple", "Simple multiple alignment", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 0, 0, &alignWriteSimple}, {"sam", "Sequence alignent/map (SAM) format", AJFALSE, AJTRUE, AJFALSE, AJFALSE, AJTRUE, 0, 2, 2, &alignWriteSam}, /* {"bam", "Binary sequence alignent/map (BAM) format", AJFALSE, AJTRUE, AJFALSE, AJFALSE, AJTRUE, 0, 2, 2, &alignWriteBam}, */ {"score", "Score values for pairs of sequences", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJFALSE, 0, 2, 2, &alignWriteScore}, {"srs", "Simple multiple sequence format for SRS", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 0, 0, &alignWriteSrs}, {"srspair", "Simple pairwise sequence format for SRS", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJTRUE, 0, 2, 2, &alignWriteSrsPair}, {"tcoffee", "TCOFFEE program format", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJTRUE, 0, 0, 0, &alignWriteTCoffee}, {NULL, NULL, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJTRUE, 0, 0, 0, NULL} }; ID alignWriteTrace TY static MO ajalign LB core XX DE Writes an alignment in Trace format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteTrace(AjPAlign thys) { const AjPSeq seq = NULL; ajint nali; ajint iali; ajint iseq; ajint nseq; ajint identity = 0; ajint similarity = 0; ajint gaps = 0; ajint seqlen = 0; AlignPData* pdata = NULL; AlignPData data = NULL; AjPStr tmpstr = NULL; AjPFile outf = NULL; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); ajFmtPrintF(thys->File, "Trace output\n"); ajFmtPrintF(thys->File, "============\n"); ajFmtPrintF(thys->File, "a: Type:'%S' Formatstr:'%S' Format:%d\n", thys->Type, thys->Formatstr, thys->Format); ajFmtPrintF(thys->File, "b: File:%F\n", thys->File); ajFmtPrintF(thys->File, "Show: ShowAcc:%B ShowDes:%B ShowUsa:%B\n", thys->Showacc, thys->Showdes, thys->Showusa); ajFmtPrintF(thys->File, "Booleans: Multi:%B Global:%B SeqOnly:%B SeqExternal:%B\n", thys->Multi, thys->Global, thys->SeqOnly, thys->SeqExternal); ajFmtPrintF(thys->File, "Numbers: NMin:%d NMax:%d Nseqs:%d Count:%d Width:%d\n", thys->Nmin, thys->Nmax, thys->Nseqs, thys->Count, thys->Width); ajFmtPrintF(thys->File, "Matrices: IMatrix:'%S'(%d) FMatrix:'%S'(%d)\n", ajMatrixGetName(thys->IMatrix), ajMatrixGetSize(thys->IMatrix), ajMatrixfGetName(thys->FMatrix), ajMatrixfGetSize(thys->FMatrix)); ajFmtPrintF(thys->File, "Strings: Matrix:'%S' GapPen:'%S' ExtPen:'%S'\n", thys->Matrix, thys->GapPen, thys->ExtPen); ajFmtPrintF(thys->File, "Header: '%S'\n", thys->Header); ajFmtPrintF(thys->File, "SubHeader: '%S'\n", thys->SubHeader); ajFmtPrintF(thys->File, "Tail: '%S'\n", thys->Tail); ajFmtPrintF(thys->File, "SubTail: '%S'\n", thys->Tail); ajFmtPrintF(thys->File, "Key: seqlen/len offset> start..end File, "\nalign%d: Nseqs:%d LenAli:%d " "NumId:%d NumSim:%d NumGap:%d Score:'%S'\n", iali, data->Nseqs, data->LenAli, data->NumId, data->NumSim, data->NumGap, data->Score); alignConsStats(thys, iali, &tmpstr, &identity, &similarity, &gaps, &seqlen); ajAlignSetStats(thys, iali, seqlen, identity, similarity, gaps, NULL); ajFmtPrintF(thys->File, "fixed%d: Nseqs:%d LenAli:%d " "NumId:%d NumSim:%d NumGap:%d Score:'%S'\n", iali, data->Nseqs, data->LenAli, data->NumId, data->NumSim, data->NumGap, data->Score); ajAlignSetSubStandard(thys, iali); outf = thys->File; /* turn off printing of the header, keep the calculation */ thys->File = NULL; alignWriteHeaderNum(thys,iali); thys->File = outf; nseq = thys->Nseqs; for(iseq=0; iseq < nseq; iseq++) { seq = alignSeq(thys, iseq, iali); ajFmtPrintF(thys->File, "Num%d.%d: %d/%d %d> %d..%d <%d (%d) Rev:%B %d..%d " "%d..%d\n", iali, iseq, (ajSeqGetLen(seq) - ajSeqCountGaps(seq)), data->Len[iseq], data->Offset[iseq], data->Start[iseq], data->End[iseq], data->Offend[iseq], data->SubOffset[iseq], data->Rev[iseq], alignSeqBegin(data, iseq), alignSeqEnd(data, iseq), alignSeqGapBegin(data, iseq), alignSeqGapEnd(data, iseq)); if(ajSeqGetLen(seq) <= 40) ajFmtPrintF(thys->File, "Seq%d.%d: %d '%S'\n", iali, iseq, ajSeqGetLen(seq), ajSeqGetSeqS(seq)); else { ajStrAssignSubS(&tmpstr, ajSeqGetSeqS(seq), -20, -1); ajFmtPrintF(thys->File, "Seq%d.%d: %d '%20.20S...%20S'\n", iali, iseq, ajSeqGetLen(seq), ajSeqGetSeqS(seq), tmpstr); } } } ajStrDel(&tmpstr); AJFREE(pdata); return; } ID alignWriteClustal TY static MO ajalign LB core XX DE Writes an alignment in ClustalW format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteClustal(AjPAlign thys) { alignWriteSeqformat(thys, 0, "clustal"); return; } ID alignWriteMega TY static MO ajalign LB core XX DE Writes an alignment in Mega format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteMega(AjPAlign thys) { alignWriteSeqformat(thys, 0, "mega"); return; } ID alignWriteMeganon TY static MO ajalign LB core XX DE Writes an alignment in Mega non-interleaved format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteMeganon(AjPAlign thys) { alignWriteSeqformat(thys, 0, "meganon"); return; } ID alignWriteMsf TY static MO ajalign LB core XX DE Writes an alignment in MSF format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteMsf(AjPAlign thys) { alignWriteSeqformat(thys, 0, "msf"); return; } ID alignWriteNexus TY static MO ajalign LB core XX DE Writes an alignment in Nexus/PAUP interleaved format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteNexus(AjPAlign thys) { alignWriteSeqformat(thys, 0, "nexus"); return; } ID alignWriteNexusnon TY static MO ajalign LB core XX DE Writes an alignment in Nexus/PAUP non-interleaved format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteNexusnon(AjPAlign thys) { alignWriteSeqformat(thys, 0, "nexusnon"); return; } ID alignWritePhylip TY static MO ajalign LB core XX DE Writes an alignment in Phylip interleaved format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWritePhylip(AjPAlign thys) { alignWriteSeqformat(thys, 0, "phylip"); return; } ID alignWritePhylipnon TY static MO ajalign LB core XX DE Writes an alignment in Phylip non-interleaved format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWritePhylipnon(AjPAlign thys) { alignWriteSeqformat(thys, 0, "phylipnon"); return; } ID alignWriteSelex TY static MO ajalign LB core XX DE Writes an alignment in SELEX format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteSelex(AjPAlign thys) { alignWriteSeqformat(thys, 0, "selex"); return; } ID alignWriteTreecon TY static MO ajalign LB core XX DE Writes an alignment in Treecon format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteTreecon(AjPAlign thys) { alignWriteSeqformat(thys, 0, "treecon"); return; } ID alignWriteFasta TY static MO ajalign LB core XX DE Writes an alignment in MSF format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteFasta(AjPAlign thys) { alignWriteSeqformat(thys, 0, "fasta"); return; } ID alignWriteSeqformat TY static MO ajalign LB core XX DE Writes an alignment in a sequence format. DE Usually called for only one alignment, ignoring any DE sub-alignments as they would overwrite. XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r iali ajint PD Alignment number PX PN [3] PA r sqfmt const char* PD Sequence output format PX RT void RD RX // static void alignWriteSeqformat(AjPAlign thys, ajint iali, const char* sqfmt) { AlignPData data = NULL; AjPSeq seq = NULL; AjPSeqout seqout; ajint iseq; ajint istart; ajint iend; ajint ilen; AjPStr tmpstr = NULL; seqout = ajSeqoutNewFile(thys->File); thys->SeqOnly = ajTrue; ajStrAssignC(&seqout->Formatstr, sqfmt); seqout->File = thys->File; ajSeqoutOpen(seqout); ajListPeekNumber(thys->Data, 0, (void**) &data); ilen = data->LenAli; for(iseq=0; iseq < thys->Nseqs; iseq++) { seq = ajSeqNewSeq(alignSeq(thys, iseq, iali)); istart = data->SubOffset[iseq]; iend = istart + ilen - 1; ajStrAssignSubS(&tmpstr, ajSeqGetSeqS(seq), istart, iend); ajSeqAssignSeqS(seq, tmpstr); ajSeqoutWriteSeq(seqout, seq); ajSeqDel(&seq); ajStrDel(&tmpstr); } ajSeqoutClose(seqout); seqout->File = NULL; ajSeqoutDel(&seqout); return; } ID alignWriteMarkX0 TY static MO ajalign LB core XX DE Writes an alignment in Fasta MarkX 0 format. DE DE This is the standard default output format for FASTA programs. XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteMarkX0(AjPAlign thys) { ajint iali; ajint nali; nali = (ajuint) ajListGetLength(thys->Data); for(iali=0; iali < nali; iali++) alignWriteMark(thys, iali, 0); return; } ID alignWriteMarkX1 TY static MO ajalign LB core XX DE Writes an alignment in Fasta MarkX 1 format. DE DE This is an alternative output format for FASTA programs in which DE identities are not marked. DE Instead conservative replacements are denoted by 'x' DE and non-conservative substitutions by 'X'. XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteMarkX1(AjPAlign thys) { ajint iali; ajint nali; nali = (ajuint) ajListGetLength(thys->Data); for(iali=0; iali < nali; iali++) alignWriteMark(thys, iali, 1); return; } ID alignWriteMarkX2 TY static MO ajalign LB core XX DE Writes an alignment in Fasta MarkX 2 format DE DE This is an alternative output format for FASTA programs in which DE the residues in the second sequence are only shown if they are DE different from the first. XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteMarkX2(AjPAlign thys) { ajint iali; ajint nali; nali = (ajuint) ajListGetLength(thys->Data); for(iali=0; iali < nali; iali++) alignWriteMark(thys, iali, 2); return; } ID alignWriteMarkX3 TY static MO ajalign LB core XX DE Writes an alignment in Fasta MarkX 3 format DE DE This is an alternative output format for FASTA programs in which DE the aligned library sequences are displayed in FASTA format DE These can be used to build a primitive multiple alignment. XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteMarkX3(AjPAlign thys) { ajint iali; ajint nali; nali = (ajuint) ajListGetLength(thys->Data); for(iali=0; iali < nali; iali++) alignWriteMark(thys, iali, 3); return; } ID alignWriteMarkX10 TY static MO ajalign LB core XX DE Writes an alignment in Fasta MarkX 10 format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteMarkX10(AjPAlign thys) { ajint iali; ajint nali; nali = (ajuint) ajListGetLength(thys->Data); for(iali=0; iali < nali; iali++) alignWriteMark(thys, iali, 10); return; } ID alignWriteMark TY static MO ajalign LB core XX DE Writes a pairwise alignment in a Fasta MarkX format. DE DE For now, seems to work with 0, 1, 2, 3 or 10. DE FASTA 3.4 has 4, 5, 6 and 9 as possible options DE but most seem to make no difference on pairwise comparisons. DE DE 0: identities marked with ':' similarities with '.' DE DE 1: differences marked with 'X' DE DE 2: identities marked with '.', differences with seq2 character DE DE 3: fasta format sequences with no description DE DE 10: aligned regions with associated data XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r iali ajint PD Alignment number PX PN [3] PA r markx ajint PD Markup type (as defined in Bill Pearson's PD FASTA suite PX RT void RD RX // static void alignWriteMark(AjPAlign thys, ajint iali, ajint markx) { AjPFile outf; int nseq; const AjPStr seq = NULL; const AjPSeq seqp = NULL; AlignPData data = NULL; ajint iseq; ajint i; ajint j; ajint jj; ajint kk; ajint istart; ajint iend; ajint ilen; ajint jstart; ajint jend; ajint iwidth = 50; const char* cp; AjPStr tmpstr = NULL; AjPStr mrkstr = NULL; AjPStr mrkcons = NULL; AjPStr cons = NULL; ajint identity = 0; ajint similarity = 0; ajint gaps = 0; ajint seqlen = 0; ajint* ipos = NULL; ajint* incs = NULL; AjBool* num = NULL; ajint jpos; ajint icnt; ajint increment = 1; AjPStr tmphdr = NULL; AjPStr tmpnum = NULL; AjBool lastline = ajFalse; AjBool isnuc = ajFalse; static ajint icount = 0; float ident = 0.0; ajDebug("alignWriteMark iali:%d markx:%d\n", iali, markx); icount++; nseq = thys->Nseqs; outf = thys->File; if(thys->Width) iwidth = thys->Width; ajListPeekNumber(thys->Data, iali, (void**) &data); AJCNEW0(ipos, nseq); AJCNEW0(incs, nseq); AJCNEW0(num, nseq); ajStrDel(&cons); ajStrDel(&tmphdr); ilen = data->LenAli; alignConsStats(thys, iali, &cons, &identity, &similarity, &gaps, &seqlen); ajAlignSetStats(thys, iali, seqlen, identity, similarity, gaps, NULL); ajAlignSetSubStandard(thys, iali); alignWriteHeaderNum(thys,iali); isnuc = ajSeqIsNuc(data->Seq[0]); ident = (float) data->NumId / (float) data->LenAli; /*ajDebug("# Consens: '%S'\n\n", cons);*/ if(markx == 1) alignSim(&cons, ' ', ' ', 'X', ' '); else if(markx == 2) { seq = ajSeqGetSeqS(data->Seq[0]); alignConsSet(thys, iali, 1, &cons); ajDebug("alignWriteMark(%d)\nseq0:%S\ncons:%S\nseq1:%S\n", markx, seq, cons, ajSeqGetSeqS(data->Seq[1])); alignDiff(&cons, seq, '.'); ajDebug("alignWriteMark(%d)\nseq0:%S\ncons:%S\nseq1:%S\n", markx, seq, cons, ajSeqGetSeqS(data->Seq[1])); } else alignSim(&cons, ':', '.', ' ', ' '); /*ajDebug("# Modcons: '%S'\n\n", cons);*/ ajDebug("# nseq:%d\n", nseq); ajDebug("# AliData [%d] len %d \n", iali, ilen); for(iseq=0; iseq < nseq; iseq++) { incs[iseq] = alignSeqIncrement(data, iseq); ipos[iseq] = alignSeqBegin(data, iseq)-incs[iseq]; /*ajDebug("# Seq[%d]'%S'\n", iseq, ajSeqGetSeqS(data->Seq[iseq]));*/ } /* for(iseq=0; iseq < nseq; iseq++) { ajDebug("# Seq[%d] Len:%d Start:%d End:%d Rev:%B Off:%d\n", iseq, data->Len[iseq], data->Start[iseq], data->End[iseq], data->Rev[iseq], data->Offset[iseq]); } */ if(markx==10) { if(isnuc) ajFmtPrintF(outf,">>>%s, %d nt vs %s, %d nt\n", ajSeqGetNameC(data->Seq[0]), data->Len[0], ajSeqGetNameC(data->Seq[1]), data->Len[1]); else ajFmtPrintF(outf,">>>%s, %d aa vs %s, %d aa\n", ajSeqGetNameC(data->Seq[0]), data->Len[0], ajSeqGetNameC(data->Seq[1]), data->Len[1]); ajFmtPrintF(outf,"; mp_name: EMBOSS\n"); ajFmtPrintF(outf,"; mp_ver: %S\n", ajNamValueVersion()); ajFmtPrintF(outf,"; pg_name: %S\n", ajUtilGetProgram()); ajFmtPrintF(outf,"; pg_ver: %S\n", ajNamValueVersion()); ajFmtPrintF(outf,"; pg_matrix: %S\n", thys->Matrix); ajFmtPrintF(outf,"; pg_gap-pen: -%S -%S\n", thys->GapPen, thys->ExtPen); ajFmtPrintF(outf,">>#%d\n", icount); ajFmtPrintF(outf,"; sw_score: %S\n", data->Score); ident = (float) data->NumId / (float) data->LenAli; ajFmtPrintF(outf,"; sw_ident: %5.3f\n", ident); ajFmtPrintF(outf,"; sw_overlap: %d\n", data->LenAli); } if(markx <= 2) { for(i=0; i < ilen; i += iwidth) { for(iseq=0; iseq < nseq; iseq++) { seqp = data->Seq[iseq]; seq = ajSeqGetSeqS(seqp); istart = i + data->SubOffset[iseq]; iend = (istart+iwidth-1); if(iend >= (data->SubOffset[iseq]+ilen-1)) { iend = data->SubOffset[iseq]+ilen-1; lastline = ajTrue; } increment = incs[iseq]; ajStrAssignSubS(&tmpstr, seq, istart, iend); ajStrAssignSubS(&mrkcons, cons, istart - data->SubOffset[iseq], iend - data->SubOffset[iseq]); ajStrExchangeCC(&tmpstr, ".", "-"); icnt = ajStrGetLen(tmpstr) - (size_t) ajStrCalcCountK(tmpstr, '-') - (size_t) ajStrCalcCountK(tmpstr, ' '); /* number the top sequence */ if(!iseq) { jpos = ipos[iseq]; ajStrAssignClear(&tmpnum); jj=7; cp = ajStrGetPtr(tmpstr); while(*cp) { jj ++; if((*cp != '-') && (*cp != ' ')) { jpos += increment; if(jpos%10 == 0) { ajFmtPrintAppS(&tmpnum,"%*d",jj,jpos); jj=0; num[iseq] = ajTrue; } } cp++; } if(lastline && !num[iseq]) { ajFmtPrintAppS(&tmpnum,"%*d",jj,jpos); jj=0; } if(jj) ajStrAppendCountK(&tmpnum, ' ', jj); ajFmtPrintF(outf, "%S\n", tmpnum); } if(!iseq) ajStrAssignS(&mrkstr, tmpstr); else alignSame(&mrkstr, tmpstr, ' '); if(markx==2 && nseq==2 && iseq==1) { ajFmtPrintF(outf, "%6.6S %S\n", alignSeqName(thys, seqp), mrkcons); } else { if(nseq==2 && iseq==1) ajFmtPrintF(outf, " %S\n", mrkcons); if(ajStrGetLen(tmpstr)) { ajFmtPrintF(outf, "%6.6S %S\n", alignSeqName(thys, seqp), tmpstr); } else ajFmtPrintF(outf, "%6.6S\n", alignSeqName(thys, seqp)); /* number the bottom sequence */ if(iseq+1 == nseq) { jpos = ipos[iseq]; ajStrAssignClear(&tmpnum); jj=7; kk=7; cp = ajStrGetPtr(tmpstr); while(*cp) { jj++; if((*cp != '-') && (*cp != ' ')) { kk = jj; jpos += increment; if(jpos%10 == 0) { ajFmtPrintAppS(&tmpnum,"%*d",jj,jpos); jj=0; kk=0; num[iseq] = ajTrue; } } cp++; } if(lastline && !num[iseq]) { ajFmtPrintAppS(&tmpnum,"%*d",kk,jpos); jj = jj - kk; } if(jj) ajStrAppendCountK(&tmpnum, ' ', jj); ajFmtPrintF(outf, "%S\n", tmpnum); } if(increment > 0) ipos[iseq] += icnt; else ipos[iseq] -= icnt; } } if(nseq > 2) /* 3 or more seqs, markup under */ ajFmtPrintF(outf, " %S\n", mrkcons); ajFmtPrintF(outf, "\n"); } } else { for(iseq=0; iseq < nseq; iseq++) { seqp = data->Seq[iseq]; seq = ajSeqGetSeqS(seqp); istart = data->SubOffset[iseq]; iend = istart + ilen - 1; ajStrAssignSubS(&tmpstr, seq, istart, iend); ajStrAssignSubS(&mrkcons, cons, istart - data->SubOffset[iseq], iend - data->SubOffset[iseq]); ajStrExchangeCC(&tmpstr, ".", "-"); icnt = ajStrGetLen(tmpstr) - (size_t) ajStrCalcCountK(tmpstr, '-') - (size_t) ajStrCalcCountK(tmpstr, ' '); if(!iseq) ajStrAssignS(&mrkstr, tmpstr); else alignSame(&mrkstr, tmpstr, ' '); if(markx==3) { ajFmtPrintF(outf, ">%S ..\n", alignSeqName(thys, seqp)); } else if(markx==10) { ajFmtPrintF(outf,">%S ..\n", alignSeqName(thys, seqp)); ajFmtPrintF(outf,"; sq_len: %d\n", data->Len[iseq]); if(ajSeqIsNuc(data->Seq[iseq])) ajFmtPrintF(outf,"; sq_type: D\n"); else ajFmtPrintF(outf,"; sq_type: p\n"); ajFmtPrintF(outf,"; al_start: %d\n", data->Offset[iseq] + data->Start[iseq]); ajFmtPrintF(outf,"; al_stop: %d\n", data->Offset[iseq] + data->Start[iseq] + icnt - 1); ajFmtPrintF(outf,"; al_display_start: %d\n", data->Offset[iseq] + data->Start[iseq]); } else { } for(j=0; j < ilen; j += iwidth) { jstart = j + data->SubOffset[iseq]; jend = AJMIN(data->SubOffset[iseq]+ilen-1, jstart+iwidth-1); ajStrAssignSubS(&tmpstr, seq, jstart, jend); if(ajStrGetLen(tmpstr)) { ajFmtPrintF(outf, "%S\n", tmpstr); } } ipos[iseq] += icnt; } } ajStrDel(&cons); ajStrDel(&mrkcons); ajStrDel(&mrkstr); ajStrDel(&tmpstr); ajStrDel(&tmphdr); ajStrDel(&tmpnum); AJFREE(ipos); AJFREE(incs); AJFREE(num); return; } ID alignWriteMatch TY static MO ajalign LB core XX DE Writes an alignment in Match format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteMatch(AjPAlign thys) { AjPFile outf; const AjPSeq seq1; const AjPSeq seq2; ajint nali; ajint iali; ajint len0; char fwd0 = '+'; char fwd1 = '+'; ajint endgaps = 0; ajint begingaps = 0; AlignPData* pdata = NULL; AlignPData data = NULL; outf = thys->File; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); ajAlignWriteHeader(thys); for(iali=0; ialiEnd[0] - data->Start[0] + 1; if(alignSeqRev(data, 0)) fwd0 = '-'; if(alignSeqRev(data, 1)) fwd1 = '-'; begingaps = alignSeqGapBegin(data,0) + alignSeqGapBegin(data,1); endgaps = alignSeqGapEnd(data,0) + alignSeqGapEnd(data,1); seq1 = alignSeq(thys, 0, iali); seq2 = alignSeq(thys, 1, iali); if(thys->Global) { len0 = len0 - endgaps - begingaps; ajFmtPrintF(outf, "%6d %-15.15S %c %8d..%-8d %-15.15S %c %8d..%d\n", len0, alignSeqName(thys, seq1), fwd0, alignSeqBegin(data,0) + alignSeqGapBegin(data,1), alignSeqEnd(data,0) - endgaps - alignSeqGapBegin(data,0), alignSeqName(thys, seq2), fwd1, alignSeqBegin(data,1) + alignSeqGapBegin(data,0), alignSeqEnd(data,1) - endgaps - alignSeqGapBegin(data,1)); } else { ajFmtPrintF(outf, "%6d %-15.15S %c %8d..%-8d %-15.15S %c %8d..%d\n", len0, alignSeqName(thys, seq1), fwd0, alignSeqBegin(data,0), alignSeqEnd(data,0), alignSeqName(thys, seq2), fwd1, alignSeqBegin(data,1), alignSeqEnd(data,1)); } } AJFREE(pdata); return; } ID alignWriteSimple TY static MO ajalign LB core XX DE Writes an alignment in Simple format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteSimple(AjPAlign thys) { AjPFile outf; int nseq; int nali; const AjPStr seq = NULL; AlignPData* pdata = NULL; AlignPData data = NULL; ajint iali; ajint iseq; ajint i; ajint istart; ajint iend; ajint ilen; ajint iwidth = 50; AjPStr tmpstr = NULL; AjPStr mrkstr = NULL; AjPStr mrkcons = NULL; AjPStr cons = NULL; ajint identity = 0; ajint similarity = 0; ajint gaps = 0; ajint seqlen = 0; ajint icnt; ajint* ipos = NULL; ajint* incs = NULL; AjPStr tmphdr = NULL; ajDebug("alignWriteSimple\n"); nseq = thys->Nseqs; outf = thys->File; if(thys->Width) iwidth = thys->Width; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); AJCNEW0(ipos, nseq); AJCNEW0(incs, nseq); for(iali=0; ialiLenAli; alignConsStats(thys, iali, &cons, &identity, &similarity, &gaps, &seqlen); ajAlignSetStats(thys, iali, seqlen, identity, similarity, gaps, NULL); ajAlignSetSubStandard(thys, iali); alignWriteHeaderNum(thys, iali); /*ajDebug("# Consens: '%S'\n\n", cons);*/ alignSim(&cons, '|', ':', '.', ' '); /*ajDebug("# Modcons: '%S'\n\n", cons);*/ ajDebug("# Nali:%d nseq:%d\n", nali, nseq); ajDebug("# AliData [%d] len %d \n", iali, ilen); for(iseq=0; iseq < nseq; iseq++) { incs[iseq] = alignSeqIncrement(data, iseq); ipos[iseq] = alignSeqBegin(data, iseq)-incs[iseq]; /* ajDebug("# Seq[%d]'%S'\n", iseq, ajSeqGetSeqS(data->Seq[iseq]));*/ } /* for(iseq=0; iseq < nseq; iseq++) { ajDebug("# Seq[%d] Len:%d Start:%d End:%d Rev:%B\n", iseq, data->Len[iseq], data->Start[iseq], data->End[iseq], data->Rev[iseq]); } */ for(i=0; i < ilen; i += iwidth) { for(iseq=0; iseq < nseq; iseq++) { seq = ajSeqGetSeqS(data->Seq[iseq]); istart = i + data->SubOffset[iseq]; iend = AJMIN(data->SubOffset[iseq]+ilen-1, istart+iwidth-1); ajStrAssignSubS(&tmpstr, seq, istart, iend); ajStrAssignSubS(&mrkcons, cons, istart - data->SubOffset[iseq], iend - data->SubOffset[iseq]); ajStrExchangeCC(&tmpstr, ".", "-"); icnt = incs[iseq] * (ajStrGetLen(tmpstr) - (size_t)ajStrCalcCountK(tmpstr, '-') - (size_t)ajStrCalcCountK(tmpstr, ' ')); if(!iseq) ajStrAssignS(&mrkstr, tmpstr); else alignSame(&mrkstr, tmpstr, ' '); if(nseq==2 && iseq==1) /* 2 seqs, markup between them */ ajFmtPrintF(outf, " %S\n", mrkcons); if(ajStrGetLen(tmpstr)) { ajFmtPrintF(outf, "%-13.13S %6d %S %6d\n", alignSeqName(thys, data->Seq[iseq]), ipos[iseq]+incs[iseq], tmpstr, ipos[iseq]+icnt); } else ajFmtPrintF(outf, "%-13.13S\n", alignSeqName(thys, data->Seq[iseq])); ipos[iseq] += icnt; } if(nseq > 2) /* 3 or more seqs, markup under */ ajFmtPrintF(outf, " %S\n", mrkcons); ajFmtPrintF(outf, "\n"); } } ajStrDel(&cons); ajStrDel(&mrkcons); ajStrDel(&mrkstr); ajStrDel(&tmpstr); AJFREE(ipos); AJFREE(incs); AJFREE(pdata); return; } ID alignWriteScore TY static MO ajalign LB core XX DE Writes an alignment in Score-only format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteScore(AjPAlign thys) { AjPFile outf; ajint nali; ajint iali; AlignPData* pdata = NULL; AlignPData data = NULL; const AjPSeq seq1; const AjPSeq seq2; outf = thys->File; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); for(iali=0; ialiSeq[0]; seq2 = data->Seq[1]; if(ajStrGetLen(data->Score)) ajFmtPrintF(outf, "%S %S %d (%S)\n", alignSeqName(thys, seq1), alignSeqName(thys, seq2), data->LenAli, data->Score); else ajFmtPrintF(outf, "%S %S %d\n", alignSeqName(thys, seq1), alignSeqName(thys, seq2), data->LenAli); } AJFREE(pdata); return; } ID alignWriteSam TY static MO ajalign LB core XX DE Writes an alignment in sequence alignment/map (SAM) format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteSam(AjPAlign thys) { AjPFile outf; ajint nali; ajint iali; ajint iseq; ajuint j; ajuint qrystart; ajuint qryend; ajuint qualstart; ajuint qualend; ajint refstart; ajint nmismatches; AlignPData* pdata = NULL; AlignPData data = NULL; ajint refpos = 0; /* 1-based leftmost position on the reference sequence */ const AjPSeq refseq; const AjPSeq qryseq; AjPStr seqacc = NULL; ajuint flag = 0; /* bitwise flag, section 2.2.2 in SAM specification */ ajuint mapq = 255; /* mapping quality, 255 means not available */ AjPStr cigar = NULL; /* extended CIGAR string */ AjPStr mrnm = NULL; /* mate reference sequence name */ ajuint mpos = 0; /* leftmost mate position of the clipped sequence */ ajuint isize = 0; /* inferred insert size */ AjPStr argstr; AjPStr mseq = NULL; /* match region of the query sequence */ mseq = ajStrNew(); mrnm = ajStrNewC("*");/* TODO: mate sequences not yet supported */ outf = thys->File; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); if(!thys->Count++) { ajFmtPrintF(outf, "@HD\tVN:1.3\n"); /* Program record */ argstr = ajStrNewS(ajUtilGetCmdline()); ajStrExchangeKK(&argstr, '\n', ' '); ajFmtPrintF(outf, "@PG\tID:%S\tVN:%S\tCL:%S\n", ajUtilGetProgram(), ajNamValueVersion(), argstr); ajStrDel(&argstr); } for(iali=0; ialiSeq[thys->RefSeq]; refpos = alignSeqBegin(data, thys->RefSeq) + 1 - alignSeqIncrement(data, thys->RefSeq); for(iseq=0; iseq < data->Nseqs; iseq++) { if(iseq==thys->RefSeq) continue; qryseq = data->Seq[iseq]; if(ajSeqIsReversed(qryseq)) flag |= 0x0010; if(thys->Global ||thys->SeqExternal) { qrystart = alignSeqBegin(data,iseq) - alignSeqIncrement(data,iseq); qryend = alignSeqEnd(data,iseq); refstart = refpos-1; } else { qrystart = 0; qryend = data->LenAli; refstart = 0; } qualstart = qrystart; qualend = AJMIN(qryend, qryseq->Qualsize); if(qryseq->Accuracy) { ajStrAssignClear(&seqacc); /* ASCII-33 gives the Phred base quality */ for(j=qualstart;j< qualend;j++) ajStrAppendK(&seqacc, (char) (33 + qryseq->Accuracy[j])); } else ajStrAssignC(&seqacc,"*"); ajStrAssignClear(&mseq); ajStrAssignClear(&cigar); /* get CIGAR, match region of the query sequence and #mismatches */ nmismatches = alignCigar(qryseq, refseq, qrystart, qryend, refstart, &mseq, &cigar, thys->SeqExternal); ajFmtPrintF(outf, "%S\t%u\t%S\t%d\t%d\t%S\t%S\t%u\t%u\t%S\t%S\t" "AS:i:%S\tNM:i:%d\n", alignSeqName(thys, qryseq), flag, alignSeqName(thys, refseq), refpos, mapq, cigar, mrnm, mpos, isize, mseq, seqacc, data->Score, nmismatches); } } AJFREE(pdata); ajStrDel(&cigar); ajStrDel(&mrnm); ajStrDel(&seqacc); ajStrDel(&mseq); return; } ID alignWriteSrs TY static MO ajalign LB core XX DE Writes an alignment in SRS format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteSrs(AjPAlign thys) { alignWriteSrsAny(thys, 0, ajFalse); return; } ID alignWriteSrsPair TY static MO ajalign LB core XX DE Writes an alignment in SrsPair format XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteSrsPair(AjPAlign thys) { alignWriteSrsAny(thys, 2, ajTrue); return; } ID alignWriteSrsAny TY static MO ajalign LB core XX DE Writes an alignment in SRS format (with switches for pairwise and general DE formatting) XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r imax ajint PD Maximum number of sequences (0 for unknown) PX PN [3] PA r mark AjBool PD Markup the alignment PX RT void RD RX // static void alignWriteSrsAny(AjPAlign thys, ajint imax, AjBool mark) { AjPFile outf; int nseq; int nali; const AjPStr seq = NULL; AlignPData* pdata = NULL; AlignPData data = NULL; ajint iali; ajint iseq; ajint i; ajint istart; ajint iend; ajint ilen; AjPStr tmpstr = NULL; AjPStr mrkstr = NULL; AjPStr mrkcons = NULL; AjPStr cons = NULL; ajint iwidth = 50; ajint identity = 0; ajint similarity = 0; ajint gaps=0; ajint seqlen=0; ajint icnt; ajint* ipos = NULL; ajint* incs = NULL; AjBool pair = ajFalse; AjPStr tmphdr = NULL; outf = thys->File; nseq = thys->Nseqs; if(nseq < 1) { ajAlignWriteHeader(thys); return; } if(thys->Width) iwidth = thys->Width; /* ** pair: if true, change consensus into |:. markup ** and put markup line between sequences */ if(imax == 2 && nseq == 2 && mark) pair = ajTrue; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); AJCNEW0(ipos, nseq); AJCNEW0(incs, nseq); for(iali=0; ialiLenAli; ajStrDel(&cons); ajStrDel(&tmphdr); alignConsStats(thys, iali, &cons, &identity, &similarity, &gaps, &seqlen); ajAlignSetStats(thys, iali, seqlen, identity, similarity, gaps, NULL); ajAlignSetSubStandard(thys, iali); alignWriteHeaderNum(thys, iali); /*ajDebug("# Consens: '%S'\n\n", cons);*/ if(pair) alignSim(&cons, '|', ':', '.', ' '); /*ajDebug("# Modcons: '%S'\n\n", cons);*/ ajDebug("# Nali:%d nseq:%d\n", nali, nseq); ajDebug("# AliData [%d] len %d \n", iali, ilen); for(iseq=0; iseq < nseq; iseq++) { incs[iseq] = alignSeqIncrement(data, iseq); ipos[iseq] = alignSeqBegin(data, iseq)-incs[iseq]; /*ajDebug("# Seq[%d] Off:%d Sta:%d End:%d '%S'\n", iseq, data->Offset[iseq], data->Start[iseq], data->End[iseq], ajSeqGetSeqS(data->Seq[iseq]));*/ } /* for(iseq=0; iseq < nseq; iseq++) ajDebug("# Seq[%d] Start:%d End:%d Rev:%B\n", iseq, data->Start[iseq], data->End[iseq], data->Rev[iseq]); */ for(i=0; i < ilen; i += iwidth) { for(iseq=0; iseq < nseq; iseq++) { seq = ajSeqGetSeqS(data->Seq[iseq]); istart = i + data->SubOffset[iseq]; iend = AJMIN(data->SubOffset[iseq]+ilen-1, istart+iwidth-1); ajStrAssignSubS(&tmpstr, seq, istart, iend); ajStrAssignSubS(&mrkcons, cons, istart - data->SubOffset[iseq], iend - data->SubOffset[iseq]); ajStrExchangeCC(&tmpstr, ".", "-"); icnt = incs[iseq] * (ajStrGetLen(tmpstr) - (size_t)ajStrCalcCountK(tmpstr, '-') - (size_t)ajStrCalcCountK(tmpstr, ' ')); if(!iseq) ajStrAssignS(&mrkstr, tmpstr); else alignSame(&mrkstr, tmpstr, ' '); if(pair && iseq==1) /* 2 seqs, markup between them */ ajFmtPrintF(outf, " %S\n", mrkcons); if(ajStrGetLen(tmpstr)) { if(icnt) ajFmtPrintF(outf, "%-13.13S %6d %S %6d\n", alignSeqName(thys, data->Seq[iseq]), ipos[iseq]+incs[iseq], tmpstr, ipos[iseq]+icnt); else ajFmtPrintF(outf, "%-13.13S %6d %S %6d\n", alignSeqName(thys, data->Seq[iseq]), ipos[iseq], tmpstr, ipos[iseq]); } else ajFmtPrintF(outf, "%-13.13S\n", alignSeqName(thys, data->Seq[iseq])); ipos[iseq] += icnt; } if(mark && !pair) /* 3 or more seqs, markup under */ ajFmtPrintF(outf, " %S\n", mrkcons); ajFmtPrintF(outf, "\n"); } } ajStrDel(&mrkcons); ajStrDel(&tmpstr); ajStrDel(&mrkstr); ajStrDel(&cons); AJFREE(ipos); AJFREE(incs); AJFREE(pdata); return; } ID alignWriteTCoffee TY static MO ajalign LB core XX DE Writes an alignment as a T-COFFEE library XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // static void alignWriteTCoffee (AjPAlign thys) { AjPFile outf = thys->File; int nseq; int nali; AlignPData* pdata = NULL; AlignPData data = NULL; ajint iali; ajint iseq; ajint i; ajint s1,s2; ajint n1,n2; ajint ilen; AjPStr sseq= NULL; const AjPStr sseq1= NULL; const AjPStr sseq2= NULL; ajint pidentity=0; ajDebug("alignWriteTCoffee\n"); nseq = thys->Nseqs; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); thys->SeqOnly = ajTrue; /* suppress output of tail */ /* print header */ ajFmtPrintF(outf, "%d\n", nseq); /* number of sequences */ for(iseq=0; iseq < nseq; iseq++) { ajStrAssignS(&sseq,ajSeqGetSeqS(pdata[0]->Seq[iseq])); ajStrRemoveWhite(&sseq); ajStrExchangeCC(&sseq, "-",""); /* */ ajFmtPrintF(outf, "%S %d %S\n", alignSeqName(thys, pdata[0]->Seq[iseq]), ajStrGetLen(sseq), sseq); ajStrDel(&sseq); } for(iali = 0; ialiLenAli; pidentity = (int)(0.5 + 100.0 * (float) data->NumId / (float) data->LenAli); ajFmtPrintF(outf, "! score=%S\n",data->Score); ajFmtPrintF(outf, "! matrix=%S\n",thys->Matrix); ajFmtPrintF(outf, "! gapopen=%S gapext=%S\n", thys->GapPen,thys->ExtPen); /* go through all pairwise alignments */ for(s1=0; s1Seq[s1]); for(s2=s1+1; s2Seq[s2]); for(i=0; i='A' && ajStrGetCharPos(sseq2,i)>='A') { /* output aligned pair, sequence weight, residue weight, match weight (format as guessed from the T-coffee docs) */ ajFmtPrintF(outf, "%d %d %d %d %d\n", n1, n2, pidentity, 1, 0); } if(ajStrGetCharPos(sseq1,i)>='A') n1++; if(ajStrGetCharPos(sseq2,i)>='A') n2++; } } } } /* write footer */ ajFmtPrintF(outf, "! SEQ_1_TO_N\n"); AJFREE(pdata); return; } ID ajAlignDefine TY public MO ajalign LB core XX DE Defines a sequence set as an alignment. The sequences are stored internally DE and may be edited by alignment processing. XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA u seqset AjPSeqset PD Sequence set object PX RT AjBool RD ajTrue on success RX // AjBool ajAlignDefine(AjPAlign thys, AjPSeqset seqset) { AlignPData data = NULL; ajint i; const AjPSeq seq = NULL; if(!thys->Nseqs) thys->Nseqs = ajSeqsetGetSize(seqset); data = alignDataNew(thys->Nseqs, thys->SeqExternal); for(i=0; i < thys->Nseqs; i++) { seq = ajSeqsetGetseqSeq(seqset, i); alignDataSetSequence(data, seq, i, thys->SeqExternal); } data->LenAli = ajSeqsetGetLen(seqset); ajListPushAppend(thys->Data, data); return ajTrue; } ID ajAlignDefineSS TY public MO ajalign LB core XX DE Defines a sequence pair as an alignment. Either new copies of the DE sequences or direct references to them are made depending on the value DE of the SeqExternal attribute of the alignment object. XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r seqa const AjPSeq PD Sequence object PX PN [3] PA r seqb const AjPSeq PD Second sequence object PX RT AjBool RD ajTrue on success RX // AjBool ajAlignDefineSS(AjPAlign thys, const AjPSeq seqa, const AjPSeq seqb) { AlignPData data = NULL; if(!thys->Nseqs) thys->Nseqs = 2; if(thys->Nseqs != 2) { ajErr("ajAlignDefineSS called with %d sequences in alignment", thys->Nseqs); } data = alignDataNew(2, thys->SeqExternal); ajDebug("ajAlignDefineSS '%S' '%S'\n", ajSeqGetNameS(seqa), ajSeqGetNameS(seqb)); alignDataSetSequence(data, seqa, 0, thys->SeqExternal); if(!thys->SeqExternal && !ajSeqIsTrimmed(data->RealSeq[0])) { ajSeqTrim(data->RealSeq[0]); } alignDataSetSequence(data, seqb, 1, thys->SeqExternal); if(!thys->SeqExternal && !ajSeqIsTrimmed(data->RealSeq[1])) { ajSeqTrim(data->RealSeq[1]); } data->LenAli = AJMIN(ajSeqGetLen(seqa), ajSeqGetLen(seqb)); ajListPushAppend(thys->Data, data); return ajTrue; } ID ajAlignDefineCC TY public MO ajalign LB core XX DE Defines a pair of char* strings as an alignment XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r seqa const char* PD First sequence PX PN [3] PA r seqb const char* PD Second sequence PX PN [4] PA r namea const char* PD Name of first sequence PX PN [5] PA r nameb const char* PD Name of second sequence PX RT AjBool RD ajTrue on success RX // AjBool ajAlignDefineCC(AjPAlign thys, const char* seqa, const char* seqb, const char* namea, const char* nameb) { AlignPData data = NULL; AjPStr tmpstr = NULL; if(!thys->Nseqs) thys->Nseqs = 2; data = alignDataNew(2, AJFALSE); ajStrAssignC(&tmpstr, seqa); data->Start[0] = 1; data->End[0] = ajStrGetLen(tmpstr); data->Len[0] = ajStrGetLen(tmpstr) - ajSeqstrCountGaps(tmpstr); data->Offset[0] = 0; data->Offend[0] = 0; data->SubOffset[0] = 0; data->Rev[0] = ajFalse; /* no external option - we do need to create the AjPSeqs */ data->RealSeq[0] = ajSeqNewNameC(seqa, namea); ajSeqGapStandard(data->RealSeq[0], '-'); data->Seq[0] = data->RealSeq[0]; ajStrAssignC(&tmpstr, seqb); data->Start[1] = 1; data->End[1] = ajStrGetLen(tmpstr); data->Len[1] = ajStrGetLen(tmpstr) - ajSeqstrCountGaps(tmpstr); data->Offset[1] = 0; data->Offend[1] = 0; data->SubOffset[1] = 0; data->Rev[1] = ajFalse; data->RealSeq[1] = ajSeqNewNameC(seqb, nameb); ajSeqGapStandard(data->RealSeq[1], '-'); data->Seq[1] = data->RealSeq[1]; data->LenAli = AJMIN(strlen(seqa), strlen(seqb)); ajDebug("ajAlignDefineCC %s %d %s %d\n", namea, ajSeqGetLen(data->Seq[0]), nameb, ajSeqGetLen(data->Seq[1])); ajListPushAppend(thys->Data, data); ajStrDel(&tmpstr); return ajTrue; } ID ajAlignDel TY public MO ajalign LB core XX DE Destructor for Alignment objects XX PN [1] PA d pthys AjPAlign* PD Alignment object reference PX RT void RD RX CA delete CT AjPAlign CD Default destructor CX // void ajAlignDel(AjPAlign* pthys) { AlignPData data = NULL; AjPAlign thys; thys = *pthys; if(!thys) return; ajDebug("ajAlignDel %d seqs\n", thys->Nseqs); ajStrDel(&thys->Formatstr); ajStrDel(&thys->Type); ajStrDel(&thys->Header); ajStrDel(&thys->SubHeader); ajStrDel(&thys->Tail); ajStrDel(&thys->SubTail); ajStrDel(&thys->Matrix); ajStrDel(&thys->GapPen); ajStrDel(&thys->ExtPen); ajMatrixDel(&thys->IMatrix); ajMatrixfDel(&thys->FMatrix); while(ajListPop(thys->Data, (void**) &data)) alignDataDel(&data, thys->SeqExternal); ajListFree(&thys->Data); AJFREE(*pthys); return; } ID ajAlignReset TY public MO ajalign LB core XX DE Reset for Alignment objects XX PN [1] PA w thys AjPAlign PD Alignment object reference PX RT void RD RX CA modify CT AjPAlign CD Resets ready for reuse. CX // void ajAlignReset(AjPAlign thys) { AlignPData data = NULL; while(ajListPop(thys->Data, (void**) &data)) alignDataDel(&data, thys->SeqExternal); ajListFree(&thys->Data); thys->Data = ajListNew(); thys->Nseqs = 0; return; } ID ajAlignOpen TY public MO ajalign LB core XX DE Opens a new align file XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r name const AjPStr PD File name PX RT AjBool RD ajTrue on success RX // AjBool ajAlignOpen(AjPAlign thys, const AjPStr name) { if(!ajAlignValid(thys)) return ajFalse; thys->File = ajFileNewOutNameS(name); if(thys->File) return ajTrue; return ajFalse; } ID ajAlignFormatDefault TY public MO ajalign LB core XX DE Sets the default format for an alignment XX PN [1] PA w pformat AjPStr* PD Default format returned PX RT AjBool RD ajTrue if format was returned RX // AjBool ajAlignFormatDefault(AjPStr* pformat) { if(ajStrGetLen(*pformat)) ajDebug("... output format '%S'\n", *pformat); else { /* ajStrAssignEmptyC(pformat, alignFormat[0].Name);*/ ajStrAssignEmptyC(pformat, "gff"); /* use the real name */ ajDebug("... output format not set, default to '%S'\n", *pformat); } return ajTrue; } ID ajAlignGetLen TY public MO ajalign LB core XX DE Returns the filename for an alignment. If the alignment has more than one DE sub-alignment, returns the total. XX PN [1] PA r thys const AjPAlign PD Alignment object. PX RT ajint RD Alignment length. RX // ajint ajAlignGetLen(const AjPAlign thys) { ajint ret = 0; ajint i; ajint nali; AlignPData* pdata = NULL; AlignPData data = NULL; if(!thys) return 0; if(!thys->Data) return 0; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); for(i=0; iLenAli; } AJFREE(pdata); return ret; } ID ajAlignGetFilename TY public MO ajalign LB core XX DE Returns the filename for an alignment. XX PN [1] PA r thys const AjPAlign PD Alignment object. PX RT const char* RD Filename. RX // const char * ajAlignGetFilename(const AjPAlign thys) { if(!thys) return NULL; if(!thys->File) return NULL; return ajFileGetPrintnameC(thys->File); } ID ajAlignGetFormat TY public MO ajalign LB core XX DE Returns the sequence format for an alignment. XX PN [1] PA r thys const AjPAlign PD Alignment object. PX RT const AjPStr RD Alignment format RX // const AjPStr ajAlignGetFormat(const AjPAlign thys) { if(!thys) return NULL; return thys->Formatstr; } ID ajAlignFormatShowsSequences TY public MO ajalign LB core XX DE Returns whether current alignment format includes sequences in output XX PN [1] PA r thys const AjPAlign PD Alignment object PX RT AjBool RD true if sequences appear in output RX // AjBool ajAlignFormatShowsSequences(const AjPAlign thys) { if(!thys) return AJTRUE; return alignFormat[thys->Format].Showseqs; } ID ajAlignFindFormat TY public MO ajalign LB core XX DE Looks for the specified align format in the internal definitions and DE returns the index. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // AjBool ajAlignFindFormat(const AjPStr format, ajint* iformat) { AjPStr tmpformat = NULL; ajint i = 0; ajDebug("ajAlignFindFormat '%S'\n", format); if(!ajStrGetLen(format)) return ajFalse; ajStrAssignS(&tmpformat, format); ajStrFmtLower(&tmpformat); while(alignFormat[i].Name) { if(ajStrMatchCaseC(tmpformat, alignFormat[i].Name)) { *iformat = i; ajStrDel(&tmpformat); ajDebug("... found at %d\n", i); return ajTrue; } i++; } ajDebug("... not found\n"); ajStrDel(&tmpformat); return ajFalse; } ID ajAlignValid TY public MO ajalign LB core XX DE Test for an alignment object. DE DE Checks the format works with the number of sequences. DE Checks the format works with the type (protein or nucleotide). XX PN [1] PA u thys AjPAlign PD Alignment object PX RT AjBool RD ajTrue on success RX // AjBool ajAlignValid(AjPAlign thys) { ajDebug("ajAlignValid format '%S' %d Nmin %d Nmax %d\n", thys->Formatstr, thys->Format, thys->Nmin, thys->Nmax); if(!thys->Format) /* test acdc-alignbadformat */ { if(ajStrGetLen(thys->Formatstr)) { if(!ajAlignFindFormat(thys->Formatstr, &thys->Format)) { ajErr("Unknown alignment format '%S'", thys->Formatstr); return ajFalse; } } else { ajStrAssignC(&thys->Formatstr,alignDefformat); ajAlignFindFormat(thys->Formatstr, &thys->Format); } } if( alignFormat[thys->Format].Minseq && thys->Nmin < alignFormat[thys->Format].Minseq) { ajErr("Alignment format %s specifies at least %d sequences, " "alignment has only %d", alignFormat[thys->Format].Name, alignFormat[thys->Format].Minseq, thys->Nmin); return ajFalse; } if( alignFormat[thys->Format].Maxseq && thys->Nmax > alignFormat[thys->Format].Maxseq) { ajErr("Alignment format %s specifies at most %d sequences, " "alignment has %d", alignFormat[thys->Format].Name, alignFormat[thys->Format].Maxseq, thys->Nmax); return ajFalse; } if(thys->Width < 10) { ajWarn("Alignment width (-awidth=%d) too narrow, reset to 10", thys->Width); thys->Width=10; } return ajTrue; } ID ajAlignNew TY public MO ajalign LB core XX DE Constructor for an alignment object XX RT AjPAlign RD New Alignment object RX CA new CT AjPAlign CD Default constructor CX // AjPAlign ajAlignNew(void) { AjPAlign pthis; AJNEW0(pthis); pthis->Count = 0; pthis->Formatstr = ajStrNew(); pthis->Format = 0; pthis->File = NULL; pthis->Data = ajListNew(); pthis->RefSeq = 0; return pthis; } ID ajAlignWrite TY public MO ajalign LB core XX DE Writes an alignment file XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX CA output CT AjPAlign CD Master alignment output routine CX // void ajAlignWrite(AjPAlign thys) { ajDebug("ajAlignWrite\n"); ajAlignTraceT(thys, "ajAlignWrite start"); if(!thys->Format) if(!ajAlignFindFormat(thys->Formatstr, &thys->Format)) ajErr("unknown align format '%S'", thys->Formatstr); ajDebug("ajAlignWrite %d '%s'\n", thys->Format, alignFormat[thys->Format].Name); ajAlignSetType(thys); /* Calling funclist alignFormat() */ (*alignFormat[thys->Format].Write)(thys); return; } ID ajAlignClose TY public MO ajalign LB core XX DE Closes an alignment XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // void ajAlignClose(AjPAlign thys) { ajDebug("ajAlignClose '%F'\n", thys->File); if(!thys->SeqOnly) ajAlignWriteTail(thys); ajFileClose(&thys->File); return; } ID ajAlignWriteHeader TY public MO ajalign LB core XX DE Writes an alignment header. XX PN [1] PA u thys AjPAlign PD Alignment object. Internal count is updated PD to avoid duplicate headers PX RT void RD RX CA output CT AjPAlign CD Master header output routine CX // void ajAlignWriteHeader(AjPAlign thys) { alignWriteHeaderNum(thys, 0); return; } ID alignWriteHeaderNum TY static MO ajalign LB core XX DE Writes an alignment header. XX PN [1] PA u thys AjPAlign PD Alignment object. Internal count is updated PD to avoid duplicate headers PX PN [2] PA r iali ajint PD Alignment number PX RT void RD RX CA output CT AjPAlign CD Master header output routine CX // static void alignWriteHeaderNum(AjPAlign thys, ajint iali) { AjPFile outf; AjPStr tmpstr = NULL; AjBool doSingle; ajint i; const AjPSeq seq; if(!alignFormat[thys->Format].Showheader) return; outf = thys->File; doSingle = ajFalse; /* turned off for now - always multi format */ if(!thys->Count) { ajFmtPrintF(outf, "########################################\n"); ajFmtPrintF(outf, "# Program: %S\n", ajUtilGetProgram()); ajFmtPrintF(outf, "# Rundate: %D\n", ajTimeRefTodayFmt("report")); ajFmtPrintF(outf, "# Commandline: %S\n", ajUtilGetProgram()); ajStrAssignS(&tmpstr, ajUtilGetCmdline()); if(ajStrGetLen(tmpstr)) { ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajFmtPrintF(outf, "# %S\n", tmpstr); } ajStrAssignS(&tmpstr, ajUtilGetInputs()); if(ajStrGetLen(tmpstr)) { ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajFmtPrintF(outf, "# %S\n", tmpstr); } ajFmtPrintF(outf, "# Align_format: %S\n", thys->Formatstr); ajFmtPrintF(outf, "# Report_file: %F\n", outf); if(!doSingle || thys->Multi) ajFmtPrintF(outf, "########################################\n"); else ajFmtPrintF(outf, "#\n"); } if(!doSingle || thys->Multi) ajFmtPrintF(outf, "\n#=======================================\n#\n"); ajFmtPrintF(outf, "# Aligned_sequences: %d\n", thys->Nseqs); for(i=0; i < thys->Nseqs; i++) { ajStrAssignClear(&tmpstr); seq = alignSeq(thys,i, iali); if(thys->Showacc) ajFmtPrintAppS(&tmpstr, " (%S)", ajSeqGetAccS(seq)); if(thys->Showdes) ajFmtPrintAppS(&tmpstr, " %S", ajSeqGetDescS(seq)); ajFmtPrintF(outf, "# %d: %S%S\n", i+1, alignSeqName(thys, seq), tmpstr); } if(ajStrGetLen(thys->Matrix)) ajFmtPrintF(outf, "# Matrix: %S\n", thys->Matrix); if(ajStrGetLen(thys->GapPen)) ajFmtPrintF(outf, "# Gap_penalty: %S\n", thys->GapPen); if(ajStrGetLen(thys->ExtPen)) ajFmtPrintF(outf, "# Extend_penalty: %S\n", thys->ExtPen); if(ajStrGetLen(thys->Header)) { ajStrAssignS(&tmpstr, thys->Header); ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajFmtPrintF(outf, "#\n"); ajFmtPrintF(outf, "# %S\n", tmpstr); ajFmtPrintF(outf, "#\n"); } if(ajStrGetLen(thys->SubHeader)) { ajStrAssignS(&tmpstr, thys->SubHeader); ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajFmtPrintF(outf, "#\n"); ajFmtPrintF(outf, "# %S\n", tmpstr); ajFmtPrintF(outf, "#\n"); ajStrDel(&thys->SubHeader); } if(!doSingle || thys->Multi) ajFmtPrintF(outf, "#=======================================\n\n"); else ajFmtPrintF(outf, "########################################\n\n"); ++thys->Count; ajStrDel(&tmpstr); return; } ID ajAlignWriteTail TY public MO ajalign LB core XX DE Writes an alignment tail XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX CA output CT AjPAlign CD Master footer output routine CX // void ajAlignWriteTail(AjPAlign thys) { AjPFile outf; AjPStr tmpstr = NULL; AjBool doSingle; if(!alignFormat[thys->Format].Showheader) return; outf = thys->File; doSingle = ajFalse; /* turned off for now - always multi format */ ajFmtPrintF(outf, "\n"); if(!doSingle || thys->Multi) ajFmtPrintF(outf, "#---------------------------------------\n"); else ajFmtPrintF(outf, "\n########################################\n"); if(ajStrGetLen(thys->SubTail)) { ajStrAssignS(&tmpstr, thys->SubTail); ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajFmtPrintF(outf, "#\n"); ajFmtPrintF(outf, "# %S\n", tmpstr); ajFmtPrintF(outf, "#\n"); ajStrDel(&thys->SubTail); } if(ajStrGetLen(thys->Tail)) { ajStrAssignS(&tmpstr, thys->Tail); ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajFmtPrintF(outf, "#\n"); ajFmtPrintF(outf, "# %S\n", tmpstr); ajFmtPrintF(outf, "#\n"); } if(!doSingle || thys->Multi) ajFmtPrintF(outf, "#---------------------------------------\n"); else ajFmtPrintF(outf, "########################################\n"); ajStrDel(&tmpstr); return; } ID ajAlignSetHeader TY public MO ajalign LB core XX DE Defines an alignment header XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r header const AjPStr PD Align header with embedded newlines PX RT void RD RX // void ajAlignSetHeader(AjPAlign thys, const AjPStr header) { ajStrAssignS(&thys->Header, header); ajDebug("ajAlignSetHeader len %d '%S'\n", ajStrGetLen(thys->Header), header); return; } ID ajAlignSetHeaderC TY public MO ajalign LB core XX DE Defines an alignment header XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r header const char* PD Align header with embedded newlines PX RT void RD RX // void ajAlignSetHeaderC(AjPAlign thys, const char* header) { ajStrAssignC(&thys->Header, header); ajDebug("ajAlignSetHeaderC len %d '%S'\n", ajStrGetLen(thys->Header), header); return; } ID ajAlignSetHeaderApp TY public MO ajalign LB core XX DE Appends to an alignment header XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r header const AjPStr PD Align header with embedded newlines PX RT void RD RX // void ajAlignSetHeaderApp(AjPAlign thys, const AjPStr header) { if(ajStrGetLen(thys->Header) && ajStrGetCharLast(thys->Header) != '\n') ajStrAppendC(&thys->Header, "\n"); ajStrAppendS(&thys->Header, header); ajDebug("ajAlignSetHeaderApp len %d '%S'\n", ajStrGetLen(thys->Header), header); return; } ID ajAlignSetTail TY public MO ajalign LB core XX DE Defines an alignment tail XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r tail const AjPStr PD Align tail with embedded newlines PX RT void RD RX // void ajAlignSetTail(AjPAlign thys, const AjPStr tail) { ajStrAssignS(&thys->Tail, tail); ajDebug("ajAlignSetTail len %d '%S'\n", ajStrGetLen(thys->Tail), tail); return; } ID ajAlignSetTailC TY public MO ajalign LB core XX DE Defines an alignment tail XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r tail const char* PD Align tail with embedded newlines PX RT void RD RX // void ajAlignSetTailC(AjPAlign thys, const char* tail) { ajStrAssignC(&thys->Tail, tail); ajDebug("ajAlignSetTailC len %d '%S'\n", ajStrGetLen(thys->Tail), tail); return; } ID ajAlignSetTailApp TY public MO ajalign LB core XX DE Appends to an alignment tail XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r tail const AjPStr PD Align tail with embedded newlines PX RT void RD RX // void ajAlignSetTailApp(AjPAlign thys, const AjPStr tail) { if(ajStrGetLen(thys->Tail) && ajStrGetCharLast(thys->Tail) != '\n') ajStrAppendC(&thys->Tail, "\n"); ajStrAppendS(&thys->Tail, tail); ajDebug("ajAlignSetTailApp len %d '%S'\n", ajStrGetLen(thys->Tail), tail); return; } ID ajAlignSetSubTail TY public MO ajalign LB core XX DE Defines an alignment tail XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r tail const AjPStr PD Align tail with embedded newlines PX RT void RD RX // void ajAlignSetSubTail(AjPAlign thys, const AjPStr tail) { ajStrAssignS(&thys->SubTail, tail); ajDebug("ajAlignSetSubTail len %d '%S'\n", ajStrGetLen(thys->SubTail), tail); return; } ID ajAlignSetSubTailC TY public MO ajalign LB core XX DE Defines an alignment tail XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r tail const char* PD Align tail with embedded newlines PX RT void RD RX // void ajAlignSetSubTailC(AjPAlign thys, const char* tail) { ajStrAssignC(&thys->SubTail, tail); ajDebug("ajAlignSetSubTailC len %d '%S'\n", ajStrGetLen(thys->SubTail), tail); return; } ID ajAlignSetSubTailApp TY public MO ajalign LB core XX DE Appends to an alignment tail XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r tail const AjPStr PD Align tail with embedded newlines PX RT void RD RX // void ajAlignSetSubTailApp(AjPAlign thys, const AjPStr tail) { if(ajStrGetLen(thys->SubTail) && ajStrGetCharLast(thys->SubTail) != '\n') ajStrAppendC(&thys->SubTail, "\n"); ajStrAppendS(&thys->SubTail, tail); ajDebug("ajAlignSetSubTailApp len %d '%S'\n", ajStrGetLen(thys->SubTail), tail); return; } ID ajAlignSetSubHeader TY public MO ajalign LB core XX DE Defines an alignment subheader (cleared after printing so it can DE be set again for the next alignment) XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r subheader const AjPStr PD Align subheader with embedded newlines PX RT void RD RX // void ajAlignSetSubHeader(AjPAlign thys, const AjPStr subheader) { ajStrAssignS(&thys->SubHeader, subheader); ajDebug("ajAlignSetSubHeader len %d '%S'\n", ajStrGetLen(thys->SubHeader), subheader); return; } ID ajAlignSetSubHeaderC TY public MO ajalign LB core XX DE Defines an alignment header (cleared after printing so it can DE be set again for the next alignment) XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r subheader const char* PD Align subheader with embedded newlines PX RT void RD RX // void ajAlignSetSubHeaderC(AjPAlign thys, const char* subheader) { ajStrAssignC(&thys->Header, subheader); ajDebug("ajAlignSetSubHeaderC len %d '%S'\n", ajStrGetLen(thys->SubHeader), subheader); return; } ID ajAlignSetSubHeaderApp TY public MO ajalign LB core XX DE Appends to an alignment subheader (cleared after printing so it can DE be set again for the next alignment) XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r subheader const AjPStr PD Align subheader with embedded newlines PX RT void RD RX // void ajAlignSetSubHeaderApp(AjPAlign thys, const AjPStr subheader) { if(ajStrGetLen(thys->SubHeader) && ajStrGetCharLast(thys->SubHeader) != '\n') ajStrAppendC(&thys->SubHeader, "\n"); ajStrAppendS(&thys->SubHeader, subheader); ajDebug("ajAlignSetSubHeaderApp len %d '%S'\n", ajStrGetLen(thys->SubHeader), subheader); return; } ID ajAlignSetSubHeaderPre TY public MO ajalign LB core XX DE Prepends to an alignment subheader (cleared after printing so it can DE be set again for the next alignment) XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r subheader const AjPStr PD Align subheader with embedded newlines PX RT void RD RX // void ajAlignSetSubHeaderPre(AjPAlign thys, const AjPStr subheader) { if(ajStrGetLen(thys->SubHeader) && ajStrGetCharLast(subheader) != '\n') ajStrInsertC(&thys->SubHeader, 0, "\n"); ajStrInsertS(&thys->SubHeader, 0, subheader); ajDebug("ajAlignSetSubHeaderPre len %d '%S'\n", ajStrGetLen(thys->SubHeader), subheader); return; } ID ajAlignSetMatrixName TY public MO ajalign LB core XX DE Defines an alignment matrix XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r matrix const AjPStr PD Matrix name PX RT void RD RX // void ajAlignSetMatrixName(AjPAlign thys, const AjPStr matrix) { ajAlignSetMatrixNameC(thys, ajStrGetPtr(matrix)); return; } ID ajAlignSetMatrixNameC TY public MO ajalign LB core XX DE Defines an alignment matrix XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r matrix const char* PD Matrix name PX RT void RD RX // void ajAlignSetMatrixNameC(AjPAlign thys, const char* matrix) { ajStrAssignC(&thys->Matrix, matrix); return; } ID ajAlignSetMatrixInt TY public MO ajalign LB core XX DE Defines an alignment matrix XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA u matrix AjPMatrix PD Matrix object PX RT void RD RX // void ajAlignSetMatrixInt(AjPAlign thys, AjPMatrix matrix) { if(!thys->IMatrix) { thys->IMatrix = matrix; ajAlignSetMatrixName(thys, ajMatrixGetName(matrix)); } if(thys->FMatrix) ajMatrixfDel(&thys->FMatrix); return; } ID ajAlignSetMatrixFloat TY public MO ajalign LB core XX DE Defines an alignment matrix XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA u matrix AjPMatrixf PD Matrix (floating point version) object PX RT void RD RX // void ajAlignSetMatrixFloat(AjPAlign thys, AjPMatrixf matrix) { if(!thys->FMatrix) { thys->FMatrix = matrix; ajAlignSetMatrixName(thys, ajMatrixfGetName(matrix)); } if(thys->IMatrix) ajMatrixDel(&thys->IMatrix); return; } ID ajAlignSetGapI TY public MO ajalign LB core XX DE Defines alignment gap penalties XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r gappen ajint PD Gap penalty PX PN [3] PA r extpen ajint PD Gap extension penalty PX RT void RD RX // void ajAlignSetGapI(AjPAlign thys, ajint gappen, ajint extpen) { AjPStr tmpstr = NULL; ajFmtPrintS(&tmpstr, "%d", gappen); ajStrAssignS(&thys->GapPen, tmpstr); ajFmtPrintS(&tmpstr, "%d", extpen); ajStrAssignS(&thys->ExtPen, tmpstr); ajStrDel(&tmpstr); return; } ID ajAlignSetGapR TY public MO ajalign LB core XX DE Defines alignment gap penalties XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r gappen float PD Gap penalty PX PN [3] PA r extpen float PD Gap extension penalty PX RT void RD RX // void ajAlignSetGapR(AjPAlign thys, float gappen, float extpen) { AjPStr tmpstr = NULL; ajint precision = 3; ajint i; ajFmtPrintS(&tmpstr, "%.*f", precision, gappen); for(i=1; iGapPen, tmpstr); ajFmtPrintS(&tmpstr, "%.*f", precision, extpen); for(i=1; iExtPen, tmpstr); ajStrDel(&tmpstr); return; } ID ajAlignSetScoreI TY public MO ajalign LB core XX DE Defines alignment score XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r score ajint PD score PX RT void RD RX // void ajAlignSetScoreI(AjPAlign thys, ajint score) { AjPStr tmpstr = NULL; AlignPData data = NULL; ajListPeekLast(thys->Data, (void**) &data); ajFmtPrintS(&tmpstr, "%d", score); ajStrAssignS(&data->Score, tmpstr); ajDebug("ajAlignSetScoreI: %d '%S' %d\n", score, data->Score, data->LenAli); ajStrDel(&tmpstr); return; } ID ajAlignSetScoreL TY public MO ajalign LB core XX DE Defines alignment score XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r score ajlong PD score PX RT void RD RX // void ajAlignSetScoreL(AjPAlign thys, ajlong score) { AjPStr tmpstr = NULL; AlignPData data = NULL; ajListPeekLast(thys->Data, (void**) &data); ajFmtPrintS(&tmpstr, "%Ld", score); ajStrAssignS(&data->Score, tmpstr); ajDebug("ajAlignSetScoreI: %Ld '%S' %d\n", score, data->Score, data->LenAli); ajStrDel(&tmpstr); return; } ID ajAlignSetScoreR TY public MO ajalign LB core XX DE Defines alignment score XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r score float PD score PX RT void RD RX // void ajAlignSetScoreR(AjPAlign thys, float score) { AjPStr tmpstr = NULL; ajint precision = 3; ajint i; AlignPData data = NULL; ajListPeekLast(thys->Data, (void**) &data); ajFmtPrintS(&tmpstr, "%.*f", precision, score); for(i=1; iScore, tmpstr); ajStrDel(&tmpstr); return; } ID ajAlignSetStats TY public MO ajalign LB core XX DE Sets standard properties for an alignment subheader. These are: DE Length, Identity, Gaps, Similarity, Score XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r iali ajint PD Alignment number PX PN [3] PA r len ajint PD Alignment length PX PN [4] PA r ident ajint PD Number of identities PX PN [5] PA r sim ajint PD Number of similarities PX PN [6] PA r gaps ajint PD Number of gaps PX PN [7] PA r score const AjPStr PD Alignment score (as saved by PD ajAlignSetScoreI or ajAlignSetScoreR) PX RT void RD RX // void ajAlignSetStats(AjPAlign thys, ajint iali, ajint len, ajint ident, ajint sim, ajint gaps, const AjPStr score) { AlignPData* pdata = NULL; AlignPData data = NULL; ajint nali; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); if(iali < 0) data = pdata[nali-1]; else data = pdata[iali]; /* ajDebug("ajAlignSetStats iali:%d len:%d id:%d " "sim:%d gap:%d score:'%S'\n", iali, len, ident, sim, gaps, score); */ data->LenAli = len; if(len > 0) { if(ident >= 0) data->NumId = ident; else data->NumId = -1; if(sim >= 0) data->NumSim = sim; else data->NumSim = -1; if(gaps >= 0) data->NumGap = gaps; else data->NumGap = -1; } if(ajStrGetLen(score)) ajStrAssignS(&data->Score, score); AJFREE(pdata); return; } ID ajAlignSetSubStandard TY public MO ajalign LB core XX DE Sets standard subheader using the properties for an alignment. DE These are: DE Length, Identity, Gaps, Similarity, Score XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r iali ajint PD Alignment number (or -1 for the latest) PX RT void RD RX // void ajAlignSetSubStandard(AjPAlign thys, ajint iali) { AjPStr tmphdr = NULL; AlignPData* pdata = NULL; AlignPData data = NULL; ajint nali; float pct; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); if(iali < 0) data = pdata[nali-1]; else data = pdata[iali]; ajFmtPrintAppS(&tmphdr, "Length: %d\n", data->LenAli); if(data->LenAli > 0) { if(data->NumId >= 0) { pct = (float)100. * (float) data->NumId / (float) data->LenAli; ajFmtPrintAppS(&tmphdr, "Identity: %5d/%d (%4.1f%%)\n", data->NumId, data->LenAli, pct); } if(data->NumSim >= 0) { pct = (float)100. * (float) data->NumSim / (float) data->LenAli; ajFmtPrintAppS(&tmphdr, "Similarity: %5d/%d (%4.1f%%)\n", data->NumSim, data->LenAli, pct); } if(data->NumGap >= 0) { pct = (float)100. * (float) data->NumGap / (float) data->LenAli; ajFmtPrintAppS(&tmphdr, "Gaps: %5d/%d (%4.1f%%)\n", data->NumGap, data->LenAli, pct); } } if(ajStrGetLen(data->Score)) ajFmtPrintAppS(&tmphdr, "Score: %S\n", data->Score); ajAlignSetSubHeaderPre(thys, tmphdr); ajStrDel(&tmphdr); AJFREE(pdata); return; } ID alignSeqs TY static MO ajalign LB core XX DE Returns the sequences for the nth alignment XX PN [1] PA r thys const AjPAlign PD Alignment object PX PN [2] PA r iali ajint PD Alignment number PX RT const AjPSeq* RD Pointer to the internal sequence array RX // static const AjPSeq* alignSeqs(const AjPAlign thys, ajint iali) { AlignPData data = NULL; ajListPeekNumber(thys->Data, iali, (void**) &data); return data->Seq; } ID alignSeq TY static MO ajalign LB core XX DE Returns the nth sequence for an alignment XX PN [1] PA r thys const AjPAlign PD Alignment object PX PN [2] PA r iseq ajint PD Sequence number PX PN [3] PA r iali ajint PD Alignment number PX RT const AjPSeq RD Pointer to the internal sequence RX // static const AjPSeq alignSeq(const AjPAlign thys, ajint iseq, ajint iali) { AlignPData data = NULL; ajListPeekNumber(thys->Data, iali, (void **) &data); return data->Seq[iseq]; } ID alignSeqBegin TY static MO ajalign LB core XX DE Returns the start position for the nth sequence for an alignment. DE DE Reverse direction is tested and the reverse complement numbering is used. XX PN [1] PA r data const AlignPData PD Alignment data object PX PN [2] PA r iseq ajint PD Sequence number PX RT ajint RD Start position RX // static ajint alignSeqBegin(const AlignPData data, ajint iseq) { ajint ret = 0; if(data->Rev[iseq]) { ret = data->Len[iseq] - data->Offend[iseq] - data->Start[iseq] + 1; ajDebug(" alignSeqBegin %d rev:%B len:%d Offend:%d Start:%d ret:%d\n", iseq, data->Rev[iseq], data->Len[iseq], data->Offend[iseq], data->Start[iseq], ret); } else { ret = data->Offset[iseq] + data->Start[iseq]; ajDebug(" alignSeqBegin %d rev:%B Offset:%d Start:%d ret:%d\n", iseq, data->Rev[iseq], data->Offset[iseq], data->Start[iseq], ret); } return ret; } ID alignSeqEnd TY static MO ajalign LB core XX DE Returns the end position for the nth sequence for an alignment. DE DE Reverse direction is tested and the reverse complement numbering is used. XX PN [1] PA r data const AlignPData PD Alignment data object PX PN [2] PA r iseq ajint PD Sequence number PX RT ajint RD Start position RX // static ajint alignSeqEnd(const AlignPData data, ajint iseq) { ajint ret = 0; ajint iend; if(data->End[iseq]) iend = data->End[iseq]; else iend = data->Len[iseq]; if(data->Rev[iseq]) ret = data->Len[iseq] - data->Offend[iseq] - iend + 1; else ret = data->Offset[iseq] + iend; return ret; } ID alignSeqIncrement TY static MO ajalign LB core XX DE Returns the position increment for the nth sequence for an alignment. DE DE Reverse direction gives a value of -1 for counting down. DE DE Forward direction gives +1. XX PN [1] PA r data const AlignPData PD Alignment data object PX PN [2] PA r iseq ajint PD Sequence number PX RT ajint RD Increment for position counting RX // static ajint alignSeqIncrement(const AlignPData data, ajint iseq) { ajint ret = 0; if(data->Rev[iseq]) ret = -1; else ret = 1; return ret; } ID alignSeqGapBegin TY static MO ajalign LB core XX DE Counts the gaps at the start of the nth sequence for an alignment. DE DE Reverse direction is tested and the reverse complement numbering is used. XX PN [1] PA r data const AlignPData PD Alignment data object PX PN [2] PA r iseq ajint PD Sequence number PX RT ajint RD Start position RX // static ajint alignSeqGapBegin(const AlignPData data, ajint iseq) { ajint ret = 0; static char testchars[] = "-~.? "; /* all known gap characters */ const char* cp; ajint i; if(data->Rev[iseq]) ret = data->Offend[iseq]; else ret = data->Offset[iseq]; cp = ajSeqGetSeqC(data->Seq[iseq]); i = strspn(cp+data->Start[iseq]-1, testchars); return ret+i; } ID alignSeqGapEnd TY static MO ajalign LB core XX DE Counts the gaps at the end of the nth sequence for an alignment. DE DE Reverse direction is tested and the reverse complement numbering is used. XX PN [1] PA r data const AlignPData PD Alignment data object PX PN [2] PA r iseq ajint PD Sequence number PX RT ajint RD End position RX // static ajint alignSeqGapEnd(const AlignPData data, ajint iseq) { ajint ret = 0; static char testchars[] = "-~.? "; /* all known gap characters */ const char* cp; const char* cpstart; ajint i; if(data->Rev[iseq]) ret = data->Offset[iseq]; else ret = data->Offend[iseq]; cpstart = ajSeqGetSeqC(data->Seq[iseq]); cp = cpstart + data->End[iseq]; for(i=0; cp > cpstart; cp--) { if(!strchr(testchars, *cp)) return ret+i-1; i++; } return ret+data->End[iseq]; } ID alignSeqRev TY static MO ajalign LB core XX DE Returns the direction for the nth sequence for an alignment. XX PN [1] PA r data const AlignPData PD Alignment data object PX PN [2] PA r iseq ajint PD Sequence number PX RT AjBool RD ajTrue if sequence is reversed RX // static AjBool alignSeqRev(const AlignPData data, ajint iseq) { AjBool ret = ajFalse; if(data->Rev[iseq]) ret = ajTrue; else ret = ajFalse; return ret; } ID alignData TY static MO ajalign LB core XX DE Returns the nth data structure for an alignment XX PN [1] PA r thys const AjPAlign PD Alignment object PX PN [2] PA r iali ajint PD Alignment number PX RT AlignPData RD Pointer to the internal alignment structure RX // static AlignPData alignData(const AjPAlign thys, ajint iali) { AlignPData data = NULL; ajListPeekNumber(thys->Data, iali, (void**) &data); return data; } ID alignLen TY static MO ajalign LB core XX DE Returns the length of the nth sequence for an alignment XX PN [1] PA r thys const AjPAlign PD Alignment object PX PN [2] PA r iali ajint PD Alignment number PX RT ajint RD Length of the internal sequence RX // static ajint alignLen(const AjPAlign thys, ajint iali) { AlignPData data = NULL; ajListPeekNumber(thys->Data, iali, (void**) &data); return data->LenAli; } ID ajAlignSetType TY public MO ajalign LB core XX DE Sets the align type (if it is not set already) XX PN [1] PA u thys AjPAlign PD Alignment object PX RT void RD RX // void ajAlignSetType(AjPAlign thys) { const AjPSeq seq; ajint i; ajDebug("ajAlignSetType '%S'\n", thys->Type); if(ajStrGetLen(thys->Type)) return; if(!thys->Nseqs) return; for(i=0; i < thys->Nseqs; i++) { ajDebug("Calling alignSeq d 0\n", i); seq = alignSeq(thys, i, 0); if(ajStrGetLen(seq->Type)) { ajStrAssignS(&thys->Type, seq->Type); return; } } ajDebug("testing alignSeq 0 0\n", i); if(ajSeqIsNuc(alignSeq(thys, 0, 0))) ajStrAssignC(&thys->Type, "N"); else ajStrAssignC(&thys->Type, "P"); return; } ID ajAlignSetExternal TY public MO ajalign LB core XX DE Sets the align object to use external sequence references, which are DE to be copied pointers rather than clones of the whole sequence. DE DE Intended for alignments of large sequences where there is no need to DE keep many copies. An example is the EMBOSS application wordmatch. XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r external AjBool PD If true, do not make copies of sequence data PD and do not delete internal sequence data PX RT void RD RX // void ajAlignSetExternal(AjPAlign thys, AjBool external) { ajDebug("ajAlignSetExternal old:%B new:%B\n", thys->SeqExternal, external); thys->SeqExternal = external; return; } ID ajAlignSetRefSeqIndx TY public MO ajalign LB core XX DE Sets the index of the reference sequence. XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r refseq ajint PD index of the reference sequence PX RT void RD RX // void ajAlignSetRefSeqIndx(AjPAlign thys, ajint refseq) { thys->RefSeq = refseq; return; } ID ajAlignSetRange TY public MO ajalign LB core XX DE Sets the alignment range in each sequence, but only for a DE pairwise alignment XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r start1 ajint PD Start in sequence 1 PX PN [3] PA r end1 ajint PD End in sequence 1 PX PN [4] PA r len1 ajint PD Length of sequence 1 PX PN [5] PA r off1 ajint PD Offset of sequence 1 PX PN [6] PA r start2 ajint PD Start in sequence 2 PX PN [7] PA r end2 ajint PD End in sequence 2 PX PN [8] PA r len2 ajint PD Length of sequence 2 PX PN [9] PA r off2 ajint PD Offset of sequence 2 PX RT AjBool RD ajTrue on success. Failure also writes an error message. RX // AjBool ajAlignSetRange(AjPAlign thys, ajint start1, ajint end1, ajint len1, ajint off1, ajint start2, ajint end2, ajint len2, ajint off2) { AlignPData data = NULL; ajint nali; ajDebug("ajAlignSetRange %d..%d (%d) %d..%d (%d)\n", start1, end1, len1, start2, end2, len2); if(thys->Nseqs != 2) { ajErr("ajAlignSetRange requires alignment of 2 sequences only"); return ajFalse; } ajListPeekLast(thys->Data, (void**) &data); nali = (ajuint) ajListGetLength(thys->Data); ajDebug("nali:%d set range %d\n", nali, nali-1); data->Start[0] = start1; data->End[0] = end1; data->Len[0] = len1; data->Offset[0] = off1; data->Rev[0] = ajFalse; data->Start[1] = start2; data->End[1] = end2; data->Len[1] = len2; data->Offset[1] = off2; data->Rev[1] = ajFalse; if(thys->SeqExternal) { data->LenAli = (end1 - start1) + 1; if(data->LenAli < (end2 - start2 + 1)) data->LenAli = (end2 - start2) + 1; ajDebug("len: %d\n", data->LenAli); } return ajTrue; } ID ajAlignSetSubRange TY public MO ajalign LB core XX DE Sets the local alignment sub range in each sequence, but only for a DE pairwise alignment. DE DE This sets the SubOffset in addition to the Start and End values, DE for use where the alignment has a large sequence, but only part of DE it is to be reported. DE DE The usual example is where there are many matches in a long sequence, DE defined with SeqExternal so we use pointers to one original to avoid DE making multiple copies in memory while building the AjPAlign and AlignPData DE structure. DE DE Resets the alignment length to be the length of the longest subsequence XX PN [1] PA u thys AjPAlign PD Alignment object PX PN [2] PA r substart1 ajint PD Subsequence offset in sequence 1 PX PN [3] PA r start1 ajint PD Subsequence start in sequence 1 PX PN [4] PA r end1 ajint PD Subsequence end in sequence 1 PX PN [5] PA r rev1 AjBool PD ajTrue if sequence 1 is reversed PX PN [6] PA r len1 ajint PD Length of sequence 1 PX PN [7] PA r substart2 ajint PD Subsequence offset in sequence 2 PX PN [8] PA r start2 ajint PD Subsequence start in sequence 2 PX PN [9] PA r end2 ajint PD Subsequence end in sequence 2 PX PN [10] PA r rev2 AjBool PD ajTrue if sequence 2 is reversed PX PN [11] PA r len2 ajint PD Length of sequence 2 PX RT AjBool RD ajTrue on success. Failure also writes an error message. RX // AjBool ajAlignSetSubRange(AjPAlign thys, ajint substart1, ajint start1, ajint end1, AjBool rev1, ajint len1, ajint substart2, ajint start2, ajint end2, AjBool rev2, ajint len2) { AlignPData data = NULL; ajint nali; ajDebug("ajAlignSetSubRange %d(%d)..%d (%d) %d(%d)..%d (%d)\n", start1, substart1, end1, len1, start2, substart2, end2, len2); if(thys->Nseqs != 2) { ajErr("ajAlignSetSubRange requires alignment of 2 sequences only"); return ajFalse; } ajListPeekLast(thys->Data, (void**) &data); nali = (ajuint) ajListGetLength(thys->Data); ajDebug("nali:%d set range %d\n", nali, nali-1); data->SubOffset[0] = substart1; data->Start[0] = start1; data->End[0] = end1; data->Len[0] = len1; data->Offset[0] = substart1; data->Offend[0] = len1 - (substart1 + end1 - start1 + 1); data->Rev[0] = rev1; data->SubOffset[1] = substart2; data->Start[1] = start2; data->End[1] = end2; data->Len[1] = len2; data->Offset[1] = substart2; data->Offend[1] = len2 - (substart2 + end2 - start2 + 1); data->Rev[1] = rev2; data->LenAli = (end1 - start1) + 1; if(data->LenAli < (end2 - start2 + 1)) { data->LenAli = (end2 - start2) + 1; } ajDebug("len: %d\n", data->LenAli); return ajTrue; } ID alignSeqName TY static MO ajalign LB core XX DE Returns the sequence name or USA depending on the setting in the DE Alignment object (derived from the ACD and command line -ausa option) XX PN [1] PA r thys const AjPAlign PD Alignment object PX PN [2] PA r seq const AjPSeq PD Sequence object PX RT const AjPStr RD Sequence name for this alignment RX // static const AjPStr alignSeqName(const AjPAlign thys, const AjPSeq seq) { if(thys->Showusa) return ajSeqGetUsaS(seq); return ajSeqGetNameS(seq); } ID alignDataDel TY static MO ajalign LB core XX DE Deletes an alignment data structure XX PN [1] PA d pthys AlignPData* PD Alignment data structure PX PN [2] PA r external AjBool PD Sequence is a pointer to an external PD object, do not delete. PX RT void RD RX // static void alignDataDel(AlignPData* pthys, AjBool external) { AlignPData thys; ajint i; void *freeptr = NULL; thys = *pthys; AJFREE(thys->Start); AJFREE(thys->End); AJFREE(thys->Len); AJFREE(thys->Offset); AJFREE(thys->Offend); AJFREE(thys->SubOffset); AJFREE(thys->Rev); ajStrDel(&thys->Score); ajDebug("alignDataDel NSeqs: %d External: %B\n", thys->Nseqs, external); if(!external) for(i=0;iNseqs;++i) { ajDebug("alignDataDel seq[%d] %S %d\n", i, ajSeqGetNameS(thys->Seq[i]), ajSeqGetLen(thys->Seq[i])); ajSeqDel(&thys->RealSeq[i]); } AJFREE(thys->RealSeq); freeptr = (void *)thys->Seq; AJFREE(freeptr); AJFREE(*pthys); return; } ID alignDataNew TY static MO ajalign LB core XX DE Creates and initialises an alignment data object DE with a specified number of sequences XX PN [1] PA r nseqs ajint PD Number of sequences alignment data object will PD store PX PN [2] PA r external AjBool PD Sequence is a pointer to an external PD object, no need for an own copy. PX RT AlignPData RD Pointer to the new alignment data object RX // static AlignPData alignDataNew(ajint nseqs, AjBool external) { AlignPData data = NULL; AJNEW0(data); data->Nseqs = nseqs; AJCNEW0(data->Start, nseqs); AJCNEW0(data->End, nseqs); AJCNEW0(data->Len, nseqs); AJCNEW0(data->Offset, nseqs); AJCNEW0(data->Offend, nseqs); AJCNEW0(data->SubOffset, nseqs); AJCNEW0(data->Rev, nseqs); AJCNEW0(data->Seq, nseqs); if(!external) AJCNEW0(data->RealSeq, nseqs); return data; } ID alignDataSetSequence TY static MO ajalign LB core XX DE Sets specified sequence in the alignment data object DE DE Note: In pre EMBOSS-6.3 the external option is set only by wordmatch DE and seqmatchall, both producing exact multiple matches and calling DE ajAlignSetSubRange after calling ajAlignDefineSS which repeats the settings DE made in this function when called via ajAlignDefineSS. DE Since ajSeqCountGaps function is time consuming for long sequences, DE function was modified to return quickly when the external option is true. DE After release 6.3 this function call hierarchy should be revised. XX PN [1] PA u thys AlignPData PD Alignment data object PX PN [2] PA r seq const AjPSeq PD Input sequence, either a new copy is made PD or a direct reference is made depending on PD the external parameter PX PN [3] PA r iseq ajint PD Index of the sequence PX PN [4] PA r external AjBool PD Sequence is a pointer to an external PD object PX RT void RD RX // static void alignDataSetSequence(AlignPData thys, const AjPSeq seq, ajint iseq, AjBool external) { if(external) { thys->Seq[iseq] = seq; return; } thys->Start[iseq] = ajSeqGetBegin(seq); thys->End[iseq] = ajSeqGetEnd(seq); thys->Len[iseq] = ajSeqGetLen(seq) + ajSeqGetOffset(seq) + ajSeqGetOffend(seq) - ajSeqCountGaps(seq); thys->Offset[iseq] = ajSeqGetOffset(seq); thys->Offend[iseq] = ajSeqGetOffend(seq); thys->SubOffset[iseq] = 0; thys->Rev[iseq] = ajSeqIsReversed(seq); thys->RealSeq[iseq] = ajSeqNewSeq(seq); ajSeqGapStandard(thys->RealSeq[iseq], '-'); thys->Seq[iseq] = thys->RealSeq[iseq]; return; } ID alignDiff TY static MO ajalign LB core XX DE Blank out identities between two strings XX PN [1] PA w pmark AjPStr* PD Mark string with spaces for identities PX PN [2] PA r seq const AjPStr PD String (sequence) to compare PX PN [3] PA r idchar char PD Character for identities PX RT void RD RX // static void alignDiff(AjPStr* pmark, const AjPStr seq, char idchar) { ajuint i; ajuint ilen; char c; char d; ilen = ajStrGetLen(seq); ajStrGetuniqueStr(pmark); if(ajStrGetLen(*pmark) < ilen) ilen = ajStrGetLen(*pmark); for(i=0; i < ilen; i++) { c = ajStrGetCharPos(*pmark, i); if(c == ' ') continue; if(c == '-') continue; d = ajStrGetCharPos(seq, i); if(d == ' ') continue; if(d == '-') continue; if(toupper((int)c) == toupper((int)d)) ajStrPasteCountK(pmark, i, idchar, 1); } return; } ID alignSame TY static MO ajalign LB core XX DE Blank out differences between two strings XX PN [1] PA w pmark AjPStr* PD Mark string with spaces for differences PX PN [2] PA r seq const AjPStr PD String (sequence) to compare PX PN [3] PA r diffchar char PD Character for differences PX RT void RD RX // static void alignSame(AjPStr* pmark, const AjPStr seq, char diffchar) { ajuint i; ajuint ilen; char c; char d; ilen = ajStrGetLen(seq); ajStrGetuniqueStr(pmark); if(ajStrGetLen(*pmark) < ilen) ilen = ajStrGetLen(*pmark); for(i=0; i < ilen; i++) { c = ajStrGetCharPos(*pmark, i); if(c == ' ') continue; d = ajStrGetCharPos(seq, i); if(toupper((int)c) == toupper((int)d)) continue; ajStrPasteCountK(pmark, i, diffchar, 1); } return; } ID alignSim TY static MO ajalign LB core XX DE Convert upper case (identical) positions to an identity character, DE and lower case (similar) positions to a similarity character XX PN [1] PA w pmark AjPStr* PD Mark string with spaces for differences PX PN [2] PA r idch const char PD Identity character PX PN [3] PA r simch const char PD Similarity character PX PN [4] PA r misch const char PD Mismatch character PX PN [5] PA r gapch const char PD Gap character PX RT void RD RX // static void alignSim(AjPStr* pmark, const char idch, const char simch, const char misch, const char gapch) { ajint i; ajint ilen; char c; /*ajDebug("alignSim '%S'\n", *pmark);*/ ajStrGetuniqueStr(pmark); ilen = ajStrGetLen(*pmark); for(i=0; i < ilen; i++) { c = ajStrGetCharPos(*pmark, i); if(tolower((int)c) == '#') ajStrPasteCountK(pmark, i, misch, 1); else if(isupper((int)c)) ajStrPasteCountK(pmark, i, idch, 1); else if(islower((int)c)) ajStrPasteCountK(pmark, i, simch, 1); else ajStrPasteCountK(pmark, i,gapch, 1); } /*ajDebug(" result '%S'\n", *pmark);*/ return; } ID alignConsStats TY static MO ajalign LB core XX DE Calculates alignment statistics (and a consensus). XX PN [1] PA u thys AjPAlign PD Alignment object. Matrix will be defined PD if not already set. PX PN [2] PA r iali ajint PD alignment number PX PN [3] PA w cons AjPStr* PD the created consensus sequence PX PN [4] PA w retident ajint* PD number of residues identical in all sequences PX PN [5] PA w retsim ajint* PD number of residues similar in all sequences PX PN [6] PA w retgap ajint* PD number of residues with a gap in 1 sequence PX PN [7] PA w retlen ajint* PD length of the alignment PX RT void RD RX // static void alignConsStats(AjPAlign thys, ajint iali, AjPStr *cons, ajint* retident, ajint* retsim, ajint* retgap, ajint* retlen) { ajint imat; /* iterate over identical and matching arrays */ ajint iseq; /* iterate over sequences (outer loop) */ ajint jseq; /* iterate over sequences (inner loop) */ ajint **matrix = NULL; float **fmatrix = NULL; ajint m1 = 0; ajint m2 = 0; ajint matsize; ajint matchingmaxindex; ajint identicalmaxindex; ajint nseqs; ajint mlen; float max; float contri = 0; float contrj = 0; AjPSeqCvt cvt = 0; AjPFloat posScore = NULL; /* cumulative similarity scores by sequence */ /* for matching all other sequences */ float *identical; /* cum. weight for each valid character */ float *matching; /* cum. weight for matching this character */ ajint highindex; /* position of highest score in posScore */ ajint kkpos; /* alignment position loop variable */ ajint kipos; /* alignment position kkpos + iseq start */ ajint kjpos; /* alignment position kkpos + jseq start */ ajint khpos; /* alignment position in highindex */ float himatch = 0.0; /* highest match score (often used) */ const char **seqcharptr; char res; char nocon; char gapch; float fplural; float fplurality = 51.0; float ident; AjBool isident; AjBool issim; AjBool isgap; const AjPSeq* seqs; ajint numres; /* number of residues (not spaces) */ AlignPData data = NULL; void *freeptr; data = alignData(thys, iali); if(!thys->IMatrix && !thys->FMatrix) { if(ajSeqIsNuc(alignSeq(thys, 0, iali))) ajStrAssignC(&thys->Matrix, "EDNAFULL"); else ajStrAssignC(&thys->Matrix, "EBLOSUM62"); thys->IMatrix = ajMatrixNewFile(thys->Matrix); } *retident=0; *retsim=0; *retgap=0; seqs = alignSeqs(thys, iali); nseqs = thys->Nseqs; mlen = alignLen(thys, iali); fplural = alignTotweight(thys, 0) * fplurality / (float)100.; ident = alignTotweight(thys, 0); ajDebug("alignConsStats ali:%d mlen:%d\n", iali, mlen); /* ajDebug("fplural:%.1f ident:%.1f mlen: %d\n", fplural, ident, mlen); */ if(thys->IMatrix) { matrix = ajMatrixGetMatrix(thys->IMatrix); cvt = ajMatrixGetCvt(thys->IMatrix); /* return conversion table */ matsize = ajMatrixGetSize(thys->IMatrix); } else { fmatrix = ajMatrixfGetMatrix(thys->FMatrix); cvt = ajMatrixfGetCvt(thys->FMatrix); /* return conversion table */ matsize = ajMatrixfGetSize(thys->FMatrix); } AJCNEW(seqcharptr,nseqs); AJCNEW(identical,matsize); AJCNEW(matching,matsize); posScore = ajFloatNew(); gapch = '-'; nocon = '#'; for(iseq=0;iseqSubOffset[iseq]; m1 = ajSeqcvtGetCodeK(cvt,seqcharptr[iseq][kipos]); if(m1 < 0) { ajUser("Bad character for matrix iseq:%d kipos:%d %x\n", iseq, kipos, (int) seqcharptr[iseq][kipos]); m1 = 0; } if(m1) identical[m1] += seqs[iseq]->Weight; for(jseq=iseq+1;jseqSubOffset[jseq]; m2 = ajSeqcvtGetCodeK(cvt,seqcharptr[jseq][kjpos]); if(m2 < 0) { ajDebug("Bad character for matrix jseq:%d kjpos:%d %x\n", jseq, kjpos, (int) seqcharptr[jseq][kjpos]); m2 = 0; } if(m1 && m2) { if(matrix) { contri = (float)matrix[m1][m2]*seqs[jseq]->Weight +ajFloatGet(posScore,iseq); contrj = (float)matrix[m1][m2]*seqs[iseq]->Weight +ajFloatGet(posScore,jseq); } else { contri = fmatrix[m1][m2]*seqs[jseq]->Weight +ajFloatGet(posScore,iseq); contrj = fmatrix[m1][m2]*seqs[iseq]->Weight +ajFloatGet(posScore,jseq); } ajFloatPut(&posScore,iseq,contri); ajFloatPut(&posScore,jseq,contrj); } } } /* ** highindex is the highest scoring position (seq no.) in posScore ** for 2 sequences this appears to be usually 0 */ highindex = -1; max = -FLT_MAX; numres = 0; for(iseq=0;iseqSubOffset[iseq]; if(seqcharptr[iseq][kipos] != ' ' && seqcharptr[iseq][kipos] != '-') numres++; if(ajFloatGet(posScore,iseq) > max) { highindex = iseq; max = ajFloatGet(posScore,iseq); } } /* highindex is now set */ /* ** find +ve matches in the column ** m1 is non-zero for a valid character in iseq ** m2 is non-zero for a valid character in jseq */ for(iseq=0;iseqSubOffset[iseq]; m1 = ajSeqcvtGetCodeK(cvt, seqcharptr[iseq][kipos]); if(m1 < 0) { ajDebug("Bad character for matrix iseq:%d kipos:%d %x\n", iseq, kipos, (int) seqcharptr[iseq][kipos]); m1 = 0; } if(E_FPZERO(matching[m1],U_FEPS)) { /* first time we have met this character */ for(jseq=0;jseqSubOffset[jseq]; m2 = ajSeqcvtGetCodeK(cvt, seqcharptr[jseq][kjpos]); if(m2 < 0) { ajDebug("Bad character for matrix jseq:%d kjpos:%d " "%x\n", jseq, kjpos, (int) seqcharptr[jseq][kjpos]); m2 = 0; } if(matrix) { /* 'matching' if positive */ if(m1 && m2 && matrix[m1][m2] > 0) matching[m1] += seqs[jseq]->Weight; } else if(m1 && m2 && fmatrix[m1][m2] > 0.0) matching[m1] += seqs[jseq]->Weight; /* if( iseq != jseq) /# skip the sequence we are on #/ { m2 = ajSeqcvtGetCodeK(cvt, seqcharptr[jseq][kjpos]); if(matrix) { if(m1 && m2 && matrix[m1][m2] > 0) /# 'matching' if positive #/ { matching[m1] += seqs[jseq]->Weight; } } else { if(m1 && m2 && fmatrix[m1][m2] > 0.0) { matching[m1] += seqs[jseq]->Weight; } } } */ } } } matchingmaxindex = 0; /* get max matching and identical */ identicalmaxindex = 0; for(iseq=0;iseqSubOffset[iseq]; m1 = ajSeqcvtGetCodeK(cvt,seqcharptr[iseq][kipos]); if(identical[m1] > identical[identicalmaxindex]) identicalmaxindex= m1; } for(iseq=0;iseqSubOffset[iseq]; m1 = ajSeqcvtGetCodeK(cvt,seqcharptr[iseq][kipos]); if(matching[m1] > matching[matchingmaxindex]) matchingmaxindex= m1; else if(E_FPEQ(matching[m1],matching[matchingmaxindex],U_FEPS)) { if(identical[m1] > identical[matchingmaxindex]) matchingmaxindex= m1; } if(seqcharptr[iseq][kipos] == '-' || seqcharptr[iseq][kipos] == ' ') isgap=ajTrue; } /* ajDebug("index[%d] ident:%d matching:%d high:%d\n", kpos, identicalmaxindex, matchingmaxindex, highindex); */ khpos = kkpos + data->SubOffset[highindex]; himatch = matching[ajSeqcvtGetCodeK(cvt,seqcharptr[highindex][khpos])]; if(thys->IMatrix) { /* debugstr1 = ajMatrixGetLabelNum(thys->IMatrix, identicalmaxindex-1); debugstr2 = ajMatrixGetLabelNum(thys->IMatrix, matchingmaxindex-1); */ /*ajDebug("index[%d] ident:%d '%S' %.1f matching:%d '%S' %.1f %.1f " "high:%d '%c' %.1f\n", kkpos, identicalmaxindex, debugstr1, identical[identicalmaxindex], matchingmaxindex, debugstr2, matching[matchingmaxindex], himatch, highindex, seqcharptr[highindex][khpos], seqs[highindex]->Weight); */ } else { /*debugstr1 = ajMatrixfGetLabelNum(thys->FMatrix, identicalmaxindex-1); debugstr2 = ajMatrixfGetLabelNum(thys->FMatrix, matchingmaxindex-1);*/ /* ajDebug("index[%d] ident:%d '%S' %.1f matching:%d '%S' %.1f " "%.1f " "high:%d '%c' %.1f\n", kkpos, identicalmaxindex, debugstr1, identical[identicalmaxindex], matchingmaxindex, debugstr2, matching[matchingmaxindex], himatch, highindex, seqcharptr[highindex][khpos], seqs[highindex]->Weight); */ } if(identical[identicalmaxindex] >= ident) isident=ajTrue; if(matching[matchingmaxindex] >= fplural) issim=ajTrue; /* plurality check */ if(himatch >= fplural) if(seqcharptr[highindex][khpos] != '-') res = toupper((int)seqcharptr[highindex][khpos]); if(E_FPEQ(himatch,seqs[highindex]->Weight,U_FEPS)) { if(numres > 1) res = nocon; else res = gapch; } if(issim && ! isident) res = tolower((int)res); ajStrAppendK(cons,res); if(isident) ++*retident; if(issim) ++*retsim; if(isgap) ++*retgap; /* ajDebug("id:%b sim:%b gap:%b res:%c '", isident, issim, isgap, res); */ for(iseq=0; iseqSubOffset[iseq]; /* ajDebug("%c", seqcharptr[iseq][kipos]); */ } /* ajDebug("\n"); */ } *retlen = alignLen(thys, iali); /* ajDebug("ret ident:%d sim:%d gap:%d len:%d\n", *retident, *retsim, *retgap, *retlen); */ freeptr = (void *) seqcharptr; AJFREE(freeptr); AJFREE(matching); AJFREE(identical); ajFloatDel(&posScore); return; } ID alignTotweight TY static MO ajalign LB core XX DE Returns the total weight for all sequences in an alignment. XX PN [1] PA r thys const AjPAlign PD Alignment object PX PN [2] PA r iali ajint PD Alignment number PX RT float RD Total weight for all sequences RX // static float alignTotweight(const AjPAlign thys, ajint iali) { ajint i; const AjPSeq seq = NULL; float ret = 0.0; for(i=0; i < thys->Nseqs; i++) { seq = alignSeq(thys, i, iali); ret += seq->Weight; } return ret; } ID ajAlignTraceT TY public MO ajalign LB core XX DE Reports an AjPAlign object to debug output XX PN [1] PA r thys const AjPAlign PD alignment object PX PN [2] PA r title const char* PD Trace report title PX RT void RD RX // void ajAlignTraceT(const AjPAlign thys, const char* title) { ajDebug("%s\n",title); ajAlignTrace(thys); return; } ID ajAlignTrace TY public MO ajalign LB core XX DE Reports an AjPAlign object to debug output XX PN [1] PA r thys const AjPAlign PD alignment object PX RT void RD RX // void ajAlignTrace(const AjPAlign thys) { ajDebug("AjAlignTrace\n"); ajDebug("============\n"); ajDebug("Type: '%S'\n", thys->Type); ajDebug("Formatstr: '%S'\n", thys->Formatstr); ajDebug("Format: %d\n", thys->Format); ajDebug("File: '%F'\n", thys->File); ajDebug("Header: '%S'\n", thys->Header); ajDebug("SubHeader: '%S'\n", thys->SubHeader); ajDebug("Tail: '%S'\n", thys->Tail); ajDebug("SubTail: '%S'\n", thys->SubTail); ajDebug("Showusa: %B\n", thys->Showusa); ajDebug("Multi: %B\n", thys->Multi); ajDebug("Global: %B\n", thys->Global); alignTraceData(thys); ajDebug("Nseqs: %d\n", thys->Nseqs); ajDebug("WidthNmin: %d\n", thys->Nmin); ajDebug("Nmax: %d\n", thys->Nmax); ajDebug("Width: %d\n", thys->Width); ajDebug("Count: %d\n", thys->Count); ajDebug("IMatrix: %x\n", thys->IMatrix); ajDebug("FMatrix: %x\n", thys->FMatrix); ajDebug("Matrix: '%S'\n", thys->Matrix); ajDebug("GapPen: '%S'\n", thys->GapPen); ajDebug("ExtPen: '%S'\n", thys->ExtPen); ajDebug("SeqOnly: %B\n", thys->SeqOnly); ajDebug("SeqExternal: %B\n", thys->SeqExternal); return; } ID alignTraceData TY static MO ajalign LB core XX DE Report alignment internal list data to debug output XX PN [1] PA r thys const AjPAlign PD Alignment object PX RT void RD RX // static void alignTraceData(const AjPAlign thys) { AlignPData* pdata = NULL; AlignPData data = NULL; ajint nali; ajint iali; ajint i; ajint nseq; AjPStr tmpstr = NULL; nseq = thys->Nseqs; nali = (ajuint) ajListToarray(thys->Data, (void***) &pdata); ajDebug("Data list length: %d\n", nali); if(!nali) return; for(iali=0; ialiLenAli); ajDebug("%d NumId: %d\n", iali, data->NumId); ajDebug("%d NumSim: %d\n", iali, data->NumSim); ajDebug("%d NumGap: %d\n", iali, data->NumGap); ajDebug("%d Score: '%S'\n", iali, data->Score); ajDebug("%d Start: {", iali); for(i=0; i < nseq; i++) ajDebug(" %d", data->Start[i]); ajDebug(" }\n"); ajDebug("%d End: {", iali); for(i=0; i < nseq; i++) ajDebug(" %d", data->End[i]); ajDebug(" }\n"); ajDebug("%d Len: {", iali); for(i=0; i < nseq; i++) ajDebug(" %d", data->Len[i]); ajDebug(" }\n"); ajDebug("%d Offset: {", iali); for(i=0; i < nseq; i++) ajDebug(" %d", data->Offset[i]); ajDebug(" }\n"); ajDebug("%d SubOffset: {", iali); for(i=0; i < nseq; i++) ajDebug(" %d", data->SubOffset[i]); ajDebug(" }\n"); ajDebug("%d Rev: {", iali); for(i=0; i < nseq; i++) ajDebug(" %b", data->Rev[i]); ajDebug(" }\n"); ajDebug("%d Seq: {\n", iali); for(i=0; i < nseq; i++) if(ajSeqGetLen(data->Seq[i]) > 40) { ajStrAssignSubS(&tmpstr, ajSeqGetSeqS(data->Seq[i]), -20, -1); ajDebug("%6d [%d] '%20.20S...%20S'\n", ajSeqGetLen(data->Seq[i]), i, ajSeqGetSeqS(data->Seq[i]), tmpstr); } else ajDebug(" %d '%S'\n", i, ajSeqGetSeqS(data->Seq[i])); ajDebug(" }\n"); } AJFREE(pdata); ajStrDel(&tmpstr); return; } ID ajAlignPrintFormat TY public MO ajalign LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajAlignPrintFormat(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Alignment output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Minseq Minimum number of sequences\n"); ajFmtPrintF(outf, "# Maxseq Minimum number of sequences\n"); ajFmtPrintF(outf, "# Nuc Valid for nucleotide sequences\n"); ajFmtPrintF(outf, "# Pro Valid for protein sequences\n"); ajFmtPrintF(outf, "# Header Include standard header/footer blocks\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Alias Nuc Nuc Pro Minseq Maxseq " "Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "AFormat {\n"); for(i=0; alignFormat[i].Name; i++) { if(full || !alignFormat[i].Alias) ajFmtPrintF(outf, " %-12s %5B %3B %3B %3B %6d %6d \"%s\"\n", alignFormat[i].Name, alignFormat[i].Alias, alignFormat[i].Nucleotide, alignFormat[i].Protein, alignFormat[i].Showheader, alignFormat[i].Minseq, alignFormat[i].Maxseq, alignFormat[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajAlignPrintbookFormat TY public MO ajalign LB core XX DE Reports the alignment format internals as Docbook text XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajAlignPrintbookFormat(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; AjPList fmtlist; AjPStr* names; fmtlist = ajListstrNew(); ajFmtPrintF(outf, "The supported alignment formats are summarised " "in the table below. The columns are as follows: " "Output format (format name), " "Nuc (\"true\" indicates nucleotide " "sequence data may be represented), " "Pro (\"true\" indicates protein " "sequence data may be represented, " "Header (whether the standard EMBOSS " "alignment header is included), " "Minseq " "(minimum sequences in alignment), " "Maxseq " "(maximum sequences in alignment) and " "Description (short description of " "the format). \n\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=1; alignFormat[i].Name; i++) { if(!alignFormat[i].Alias) { namestr = ajStrNewC(alignFormat[i].Name); ajListPush(fmtlist, namestr); namestr = NULL; } } ajListSort(fmtlist, &ajStrVcmp); ajListstrToarray(fmtlist, &names); for(i=0; names[i]; i++) { for(j=0; alignFormat[j].Name; j++) { if(ajStrMatchC(names[i],alignFormat[j].Name)) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", alignFormat[j].Name); ajFmtPrintF(outf, " \n", alignFormat[j].Nucleotide); ajFmtPrintF(outf, " \n", alignFormat[j].Protein); ajFmtPrintF(outf, " \n", alignFormat[j].Showheader); ajFmtPrintF(outf, " \n", alignFormat[j].Minseq); ajFmtPrintF(outf, " \n", alignFormat[j].Maxseq); ajFmtPrintF(outf, " \n", alignFormat[j].Desc); ajFmtPrintF(outf, " \n"); } } } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Alignment formats
Output FormatNucProHeaderMinseqMaxseqDescription
%s%B%B%B%d%d%s
\n"); ajStrDel(&namestr); names = NULL; ajListstrFreeData(&fmtlist); return; } ID ajAlignPrinthtmlFormat TY public MO ajalign LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajAlignPrinthtmlFormat(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, ""); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# alignment output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Minseq Minimum number of sequences\n"); ajFmtPrintF(outf, "# Maxseq Minimum number of sequences\n"); ajFmtPrintF(outf, "# Nuc Valid for nucleotide sequences\n"); ajFmtPrintF(outf, "# Pro Valid for protein sequences\n"); ajFmtPrintF(outf, "# Header Include standard header/footer blocks\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Alias Nuc Nuc Pro Minseq Maxseq " "Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "AFormat {\n"); for(i=0; alignFormat[i].Name; i++) { if(!alignFormat[i].Alias) ajFmtPrintF(outf, "" "" "\n", alignFormat[i].Name, alignFormat[i].Alias, alignFormat[i].Nucleotide, alignFormat[i].Protein, alignFormat[i].Showheader, alignFormat[i].Minseq, alignFormat[i].Maxseq, alignFormat[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajAlignPrintwikiFormat TY public MO ajalign LB core XX DE Reports the alignment format internals as wikitext XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajAlignPrintwikiFormat(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!Nuc!!Pro!!Header!!Min||Max!!" "class=\"unsortable\"|Description\n"); for(i=1; alignFormat[i].Name; i++) { if(!alignFormat[i].Alias) { ajFmtPrintF(outf, "|-\n"); ajStrAssignC(&namestr, alignFormat[i].Name); for(j=i+1; alignFormat[j].Name; j++) { if(alignFormat[j].Write == alignFormat[i].Write) { ajFmtPrintAppS(&namestr, "
%s", alignFormat[j].Name); if(!alignFormat[j].Alias) { ajWarn("Align output format '%s' same as '%s' " "but not alias", alignFormat[j].Name, alignFormat[i].Name); } } } ajFmtPrintF(outf, "|%S||%B||%B||%B||%d||%d||%s\n", namestr, alignFormat[i].Nucleotide, alignFormat[i].Protein, alignFormat[i].Showheader, alignFormat[i].Minseq, alignFormat[i].Maxseq, alignFormat[i].Desc); } } ajFmtPrintF(outf, "|}\n"); ajStrDel(&namestr); return; } ID alignConsSet TY static MO ajalign LB core XX DE Sets consensus to be a copy of a numbered sequence XX PN [1] PA r thys const AjPAlign PD Alignment object PX PN [2] PA r iali ajint PD alignment number PX PN [3] PA r numseq ajint PD Sequence number to use as the consensus PX PN [4] PA w cons AjPStr* PD the created consensus sequence PX RT AjBool RD True on success RX // static AjBool alignConsSet(const AjPAlign thys, ajint iali, ajint numseq, AjPStr *cons) { ajint nseqs; ajint mlen; ajint kkpos; /* alignment position loop variable */ const char *seqcharptr; char gapch; AlignPData data = NULL; const AjPSeq* seqs; const AjPSeq seq; data = alignData(thys, iali); seqs = alignSeqs(thys, iali); nseqs = thys->Nseqs; mlen = data->LenAli; ajDebug("alignConsSet iali:%d numseq:%d nseqs:%d mlen:%d\n", iali, numseq, nseqs, mlen); gapch = '-'; seq = seqs[numseq]; seqcharptr = ajSeqGetSeqC(seq); ajStrAssignClear(cons); /* For each position in the alignment, calculate consensus character */ for(kkpos=0; kkposSubOffset[0]; kkpos++) ajStrAppendK(cons, gapch); for(kkpos=0; kkpos< mlen; kkpos++) ajStrAppendK(cons,seqcharptr[kkpos+data->SubOffset[numseq]]); /* ajDebug("ret ident:%d sim:%d gap:%d len:%d\n", *retident, *retsim, *retgap, *retlen); */ return ajTrue; } ID ajAlignConsAmbig TY public MO ajalign LB core XX DE Calculates alignment consensus of ambiguity characters from a sequence set. XX PN [1] PA r thys const AjPSeqset PD Sequence set. PX PN [2] PA w cons AjPStr* PD the created consensus sequence PX RT AjBool RD ajTrue on success RX // AjBool ajAlignConsAmbig(const AjPSeqset thys, AjPStr *cons) { if(ajSeqsetIsNuc(thys)) return ajAlignConsAmbigNuc(thys, cons); return ajAlignConsAmbigProt(thys, cons); } ID ajAlignConsAmbigNuc TY public MO ajalign LB core XX DE Calculates alignment consensus of ambiguity characters from a sequence set. XX PN [1] PA r thys const AjPSeqset PD Sequence set. PX PN [2] PA w cons AjPStr* PD the created consensus sequence PX RT AjBool RD ajTrue on success RX // AjBool ajAlignConsAmbigNuc(const AjPSeqset thys, AjPStr *cons) { ajint iseq; /* iterate over sequences (outer loop) */ ajint nseqs; ajint mlen; AjBool isgap; ajint kipos; /* alignment position */ const char **seqcharptr; char res; ajuint binres; void *freeptr = NULL; nseqs = thys->Size; mlen = thys->Len; AJCNEW(seqcharptr,nseqs); /* ajDebug("fplural:%.2f ident:%.1f mlen: %d\n", fplural, ident, mlen); */ for(iseq=0;iseqSize; mlen = thys->Len; AJCNEW(seqcharptr,nseqs); /* ajDebug("fplural:%.2f ident:%.1f mlen: %d\n", fplural, ident, mlen); */ for(iseq=0;iseqSize; mlen = thys->Len; fplural = ajSeqsetGetTotweight(thys) * fplurality / (float)100.; ident = ajSeqsetGetTotweight(thys); /* ajDebug("fplural:%.2f ident:%.1f mlen: %d\n", fplural, ident, mlen); */ matrix = ajMatrixGetMatrix(imatrix); cvt = ajMatrixGetCvt(imatrix); /* return conversion table */ matsize = ajMatrixGetSize(imatrix); AJCNEW(seqs,nseqs); AJCNEW(seqcharptr,nseqs); AJCNEW(identical,matsize); AJCNEW(matching,matsize); posScore = ajFloatNew(); gapch = '-'; nocon = 'x'; for(iseq=0;iseqWeight; for(jseq=iseq+1;jseqWeight +ajFloatGet(posScore,iseq); contrj = (float)matrix[m1][m2]*seqs[iseq]->Weight +ajFloatGet(posScore,jseq); } else { contri = fmatrix[m1][m2]*seqs[jseq]->Weight +ajFloatGet(posScore,iseq); contrj = fmatrix[m1][m2]*seqs[iseq]->Weight +ajFloatGet(posScore,jseq); } ajFloatPut(&posScore,iseq,contri); ajFloatPut(&posScore,jseq,contrj); } } } /* ** highindex is the highest scoring position (seq no.) in posScore ** for 2 sequences this appears to be usually 0 */ highindex = -1; max = -FLT_MAX; numres = 0; for(iseq=0;iseq max) { highindex = iseq; max = ajFloatGet(posScore,iseq); } } /* highindex is now set */ /* ** find +ve matches in the column ** m1 is non-zero for a valid character in iseq ** m2 is non-zero for a valid character in jseq */ for(iseq=0;iseq 0) { /* 'matching' if positive */ matching[m1] += seqs[jseq]->Weight; } } else { if(m1 && m2 && fmatrix[m1][m2] > 0.0) { matching[m1] += seqs[jseq]->Weight; } } } } } matchingmaxindex = 0; /* get max matching and identical */ identicalmaxindex = 0; for(iseq=0;iseq identical[identicalmaxindex]) identicalmaxindex= m1; } for(iseq=0;iseq matching[matchingmaxindex]) { matchingmaxindex= m1; } else if(E_FPEQ(matching[m1],matching[matchingmaxindex],U_FEPS)) { if(identical[m1] > identical[matchingmaxindex]) matchingmaxindex= m1; } if(seqcharptr[iseq][kipos] == '-' || seqcharptr[iseq][kipos] == ' ') isgap=ajTrue; } khpos = kkpos; himatch = matching[ajSeqcvtGetCodeK(cvt,seqcharptr[highindex][khpos])]; if(identical[identicalmaxindex] >= ident) isident=ajTrue; if(matching[matchingmaxindex] >= fplural) issim=ajTrue; /* plurality check */ res = gapch; if(himatch >= fplural) if(seqcharptr[highindex][khpos] != '-') res = toupper((int)seqcharptr[highindex][khpos]); if(nseqs > 1 && E_FPEQ(himatch,seqs[highindex]->Weight,U_FEPS)) { if(numres > 1) res = nocon; else res = gapch; } if(issim && ! isident) res = tolower((int)res); ajStrAppendK(cons,res); if(isident) ++*retident; if(issim) ++*retsim; if(isgap) ++*retgap; /* ajDebug("id:%b sim:%b gap:%b res:%c '", isident, issim, isgap, res); */ for(iseq=0; iseq0) ajFmtPrintAppS(cigar, "%dH",qoffend); else if(qoffset>0) ajFmtPrintAppS(cigar, "%dH",qoffset); for(; qstart0) { ajFmtPrintAppS(cigar, "%uM",m); m=0; } if(i>0) { ajFmtPrintAppS(cigar, "%uI",d); i=0; } } else if(strchr(gapchars,refseqc[rstart])) { i++; if(m>0) { ajFmtPrintAppS(cigar, "%uM",m); m=0; } if(d>0) { ajFmtPrintAppS(cigar, "%uD",i); d=0; } ajStrAppendK(qseq,qryseqc[qstart]); } else /* match */ { m++; ajStrAppendK(qseq,qryseqc[qstart]); if(toupper((int)qryseqc[qstart])!=toupper((int)refseqc[rstart])) nmismatches ++; if(d>0) { ajFmtPrintAppS(cigar, "%uD",d); d=0; } if(i>0) { ajFmtPrintAppS(cigar, "%uI",i); i=0; } } } if(m>0) ajFmtPrintAppS(cigar, "%uM",m); else if(d>0) ajFmtPrintAppS(cigar, "%uD",d); else if(i>0) ajFmtPrintAppS(cigar, "%uI",i); if(ajSeqIsReversed(qryseq)&&qend>qseqlen) ajFmtPrintAppS(cigar, "%dH",qseqlen-qend); else if(qendPtr = AJALLOC0(RESERVED_SIZE*sizeof(char)); thys->Len = 0; thys->Res = RESERVED_SIZE; arrTotal++; arrAlloc += RESERVED_SIZE*sizeof(char); return thys; } ID ajChararrNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size PX RT AjPChar RD Pointer to an empty character array struct RD of specified size. RX CA new CT AjPChar CD Constructor with reserved size CX // AjPChar ajChararrNewRes(ajuint size) { AjPChar thys; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(char)); thys->Len = 0; thys->Res = size; arrTotal++; arrAlloc += size*sizeof(char); return thys; } ID ajChararrDel TY public MO ajarr LB core XX DE Default destructor for AJAX character arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPChar* PD Pointer to the char array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPChar CD Default destructor CX // void ajChararrDel(AjPChar *thys) { if(!thys || !*thys) return; /*ajDebug("ajChararrDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res);*/ AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arrFreeCount++; return; } ID ajChararrGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX character array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPChar PD Pointer to the char array. PX PN [2] PA r elem ajuint PD array element. PX RT char RD contents of array element RX CA cast CT AjPChar CD Retrieve a character from an array CX // char ajChararrGet(const AjPChar thys, ajuint elem) { if(!thys || elem>=thys->Len) ajErr("Attempt to access bad char array index %d\n",elem); return thys->Ptr[elem]; } ID ajChararrPut TY public MO ajarr LB core XX DE Load a character array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPChar* PD Pointer to the char array. PX PN [2] PA r elem ajuint PD array element. PX PN [3] PA r v char PD value to load. PX RT AjBool RD true if the array was extended. RX CA modify CT AjPChar CD Load a character array element CX // AjBool ajChararrPut(AjPChar *thys, ajuint elem, char v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value %d\n",elem); if(elem < (*thys)->Res) { if(elem>=(*thys)->Len) (*thys)->Len = elem+1; (*thys)->Ptr[elem] = v; return ajFalse; } arrChararrResize(thys, elem); (*thys)->Ptr[elem] = v; return ajTrue; } ID ajChararrChararr TY public MO ajarr LB core XX DE Returns the current char* pointer. This will remain valid until DE the array is resized or deleted. XX PN [1] PA r thys const AjPChar PD Source array PX RT char* RD Current array pointer, or a null string if undefined. RX CA cast CT AjPChar CD Retrieve internal pointer CX // char* ajChararrChararr(const AjPChar thys) { if(!thys || !thys->Len) return NULL; return thys->Ptr; } ID ajChararrLen TY public MO ajarr LB core XX DE Get length of dynamic character array XX PN [1] PA r thys const AjPChar PD Source array PX RT ajuint RD length RX // ajuint ajChararrLen(const AjPChar thys) { return thys->Len; } ID ajIntNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX integer arrays. XX RT AjPInt RD Pointer to an empty integer array structure RX CA new CT AjPInt CD Default constructor CX // AjPInt ajIntNew(void) { AjPInt thys; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(ajint)); thys->Len = 0; thys->Res = RESERVED_SIZE; arrTotal++; arrAlloc += RESERVED_SIZE*sizeof(ajint); return thys; } ID ajIntNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size PX RT AjPInt RD Pointer to an empty integer array struct of specified size. RX CA new CT AjPInt CD Constructor with reserved size CX // AjPInt ajIntNewRes(ajuint size) { AjPInt thys; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(ajint)); thys->Len = 0; thys->Res = size; arrTotal++; arrAlloc += size*sizeof(ajint); /*ajDebug("ajIntNewRes size %d*%d %d\n", size, sizeof(ajint), size*sizeof(ajint));*/ return thys; } ID ajIntDel TY public MO ajarr LB core XX DE Default destructor for AJAX integer arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPInt* PD Pointer to the ajint array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPInt CD Default destructor CX // void ajIntDel(AjPInt *thys) { if(!thys || !*thys) return; /*ajDebug("ajIntDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res);*/ AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arrFreeCount++; return; } ID ajIntGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX integer array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPInt PD Pointer to the ajint array. PX PN [2] PA r elem ajuint PD array element. PX RT ajint RD contents of array element RX CA cast CT AjPInt CD Retrieve an integer from an array CX // ajint ajIntGet(const AjPInt thys, ajuint elem) { if(!thys || elem>=thys->Len) ajErr("Attempt to access bad ajint array index %d\n",elem); return thys->Ptr[elem]; } ID ajIntPut TY public MO ajarr LB core XX DE Load an integer array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPInt* PD Pointer to the ajint array. PX PN [2] PA r elem ajuint PD array element. PX PN [3] PA r v ajint PD value to load. PX RT AjBool RD true if the array was extended. RX CA modify CT AjPInt CD Load an integer array element CX // AjBool ajIntPut(AjPInt *thys, ajuint elem, ajint v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value %d\n",elem); if(elem < (*thys)->Res) { if(elem>=(*thys)->Len) (*thys)->Len = elem+1; (*thys)->Ptr[elem] = v; return ajFalse; } arrIntResize(thys, elem); (*thys)->Ptr[elem] = v; return ajTrue; } ID ajIntInc TY public MO ajarr LB core XX DE Increment an integer array element. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPInt* PD Pointer to the ajint array. PX PN [2] PA r elem ajuint PD array element. PX RT void RD RX // void ajIntInc(AjPInt *thys, ajuint elem) { if(!thys || !*thys || elem>(*thys)->Len) ajErr("Attempt to write to illegal array value %d\n",elem); ++(*thys)->Ptr[elem]; return; } ID ajIntDec TY public MO ajarr LB core XX DE Decrement an integer array element. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPInt* PD Pointer to the ajint array. PX PN [2] PA r elem ajuint PD array element. PX RT void RD RX // void ajIntDec(AjPInt *thys, ajuint elem) { if(!thys || !*thys || elem>(*thys)->Len) ajErr("Attempt to write to illegal array value %d\n",elem); --(*thys)->Ptr[elem]; return; } ID ajUintNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX unsigned integer arrays. XX RT AjPUint RD Pointer to an empty unsigned integer array structure RX CA new CT AjPUint CD Default constructor CX // AjPUint ajUintNew(void) { AjPUint thys; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(ajuint)); thys->Len = 0; thys->Res = RESERVED_SIZE; arrTotal++; arrAlloc += RESERVED_SIZE*sizeof(ajuint); return thys; } ID ajUintNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size PX RT AjPUint RD Pointer to an empty unsigned integer array struct RD of specified size. RX CA new CT AjPUint CD Constructor with reserved size CX // AjPUint ajUintNewRes(ajuint size) { AjPUint thys; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(ajuint)); thys->Len = 0; thys->Res = size; arrTotal++; arrAlloc += size*sizeof(ajuint); /*ajDebug("ajUintNewL size %d*%d %d\n", size, sizeof(ajuint), size*sizeof(ajuint));*/ return thys; } ID ajUintDel TY public MO ajarr LB core XX DE Default destructor for AJAX integer integer arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPUint* PD Pointer to the ajuint array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPUint CD Default destructor CX // void ajUintDel(AjPUint *thys) { if(!thys || !*thys) return; /*ajDebug("ajUintDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res);*/ AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arrFreeCount++; return; } ID ajUintGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX unsigned integer array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPUint PD Pointer to the ajuint array. PX PN [2] PA r elem ajuint PD array element. PX RT ajuint RD contents of array element RX CA cast CT AjPUint CD Retrieve an integer from an array CX // ajuint ajUintGet(const AjPUint thys, ajuint elem) { if(!thys || elem>=thys->Len) ajErr("Attempt to access bad ajuint array index %d\n",elem); return thys->Ptr[elem]; } ID ajUintPut TY public MO ajarr LB core XX DE Load an unsigned integer array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPUint* PD Pointer to the ajuint array. PX PN [2] PA r elem ajuint PD array element. PX PN [3] PA r v ajuint PD value to load. PX RT AjBool RD true if the array was extended. RX CA modify CT AjPUint CD Load an integer array element CX // AjBool ajUintPut(AjPUint *thys, ajuint elem, ajuint v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value %d\n",elem); if(elem < (*thys)->Res) { if(elem>=(*thys)->Len) (*thys)->Len = elem+1; (*thys)->Ptr[elem] = v; return ajFalse; } arrUintResize(thys, elem); (*thys)->Ptr[elem] = v; return ajTrue; } ID ajUintInc TY public MO ajarr LB core XX DE Increment an unsigned integer array element. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPUint* PD Pointer to the ajuint array. PX PN [2] PA r elem ajuint PD array element. PX RT void RD RX // void ajUintInc(AjPUint *thys, ajuint elem) { if(!thys || !*thys || elem>(*thys)->Len) ajErr("Attempt to write to illegal array value %d\n",elem); ++(*thys)->Ptr[elem]; return; } ID ajUintDec TY public MO ajarr LB core XX DE Decrement an unsigned integer array element. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPUint* PD Pointer to the ajuint array. PX PN [2] PA r elem ajuint PD array element. PX RT void RD RX // void ajUintDec(AjPUint *thys, ajuint elem) { if(!thys || !*thys || elem>(*thys)->Len) ajErr("Attempt to write to illegal array value %d\n",elem); --(*thys)->Ptr[elem]; return; } ID arrChararrResize TY static MO ajarr LB core XX DE Resize a character array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPChar* PD Pointer to the char array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrChararrResize(AjPChar *thys, ajuint size) { AjPChar p = NULL; ajuint s; ajuint clen; ajuint limit; if(!thys || !*thys) ajErr("Illegal attempt to resize character array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajChararrResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; *thys = ajChararrNewRes(s); if(size < p->Len) limit = size+1; else limit = p->Len; memmove((*thys)->Ptr,p->Ptr,limit*sizeof(char)); (*thys)->Len = size+1; ajChararrDel(&p); arrResize++; return ajTrue; } ID arrIntResize TY static MO ajarr LB core XX DE Resize an integer array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPInt* PD Pointer to the ajint array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrIntResize(AjPInt *thys, ajuint size) { AjPInt p = NULL; ajuint s; ajuint clen; ajuint limit; if(!thys || !*thys) ajErr("Illegal attempt to resize integer array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajIntResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; *thys = ajIntNewRes(s); if(size < p->Len) limit = size+1; else limit = p->Len; memmove((*thys)->Ptr,p->Ptr,limit*sizeof(ajint)); (*thys)->Len = size+1; ajIntDel(&p); arrResize++; return ajTrue; } ID ajIntInt TY public MO ajarr LB core XX DE Returns the current ajint* pointer. This will remain valid until DE the array is resized or deleted. XX PN [1] PA r thys const AjPInt PD Source array PX RT ajint* RD Current array pointer, or a null string if undefined. RX CA cast CT AjPInt CD Retrieve internal pointer CX // ajint* ajIntInt(const AjPInt thys) { if(!thys || !thys->Len) return NULL; return thys->Ptr; } ID ajIntLen TY public MO ajarr LB core XX DE Get length of dynamic 1d ajint array XX PN [1] PA r thys const AjPInt PD Source array PX RT ajuint RD length RX // ajuint ajIntLen(const AjPInt thys) { return thys->Len; } ID arrUintResize TY static MO ajarr LB core XX DE Resize an unsigned integer array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPUint* PD Pointer to the ajuint array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrUintResize(AjPUint *thys, ajuint size) { AjPUint p = NULL; ajuint s; ajuint clen; ajuint limit; if(!thys || !*thys) ajErr("Illegal attempt to resize unsigned integer array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajIntResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; *thys = ajUintNewRes(s); if(size < p->Len) limit = size+1; else limit = p->Len; memmove((*thys)->Ptr,p->Ptr,limit*sizeof(ajuint)); (*thys)->Len = size+1; ajUintDel(&p); arrResize++; return ajTrue; } ID ajUintUint TY public MO ajarr LB core XX DE Returns the current ajuint* pointer. This will remain valid until DE the array is resized or deleted. XX PN [1] PA r thys const AjPUint PD Source array PX RT ajuint* RD Current array pointer, or a null string if undefined. RX CA cast CT AjPUint CD Retrieve internal pointer CX // ajuint* ajUintUint(const AjPUint thys) { if(!thys || !thys->Len) return NULL; return thys->Ptr; } ID ajUintLen TY public MO ajarr LB core XX DE Get length of dynamic 1d ajuint array XX PN [1] PA r thys const AjPUint PD Source array PX RT ajuint RD length RX // ajuint ajUintLen(const AjPUint thys) { return thys->Len; } ID ajFloatNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX float arrays. XX RT AjPFloat RD Pointer to an empty float array structure RX CA new CT AjPFloat CD Default constructor CX // AjPFloat ajFloatNew(void) { AjPFloat thys; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(float)); thys->Len = 0; thys->Res = RESERVED_SIZE; arrTotal++; arrAlloc += RESERVED_SIZE*sizeof(float); return thys; } ID ajFloatNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size PX RT AjPFloat RD Pointer to an empty float array struct of specified size. RX CA new CT AjPFloat CD Constructor with reserved size CX // AjPFloat ajFloatNewRes(ajuint size) { AjPFloat thys; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(float)); thys->Len = 0; thys->Res = size; arrTotal++; arrAlloc += size*sizeof(float); return thys; } ID ajFloatDel TY public MO ajarr LB core XX DE Default destructor for AJAX float arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPFloat* PD Pointer to the float array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPFloat CD Default destructor CX // void ajFloatDel(AjPFloat *thys) { if(!thys || !*thys) return; /*ajDebug("ajFloatDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res);*/ AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arrFreeCount++; return; } ID ajFloatGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX float array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPFloat PD Pointer to the float array. PX PN [2] PA r elem ajuint PD array element. PX RT float RD contents of array element RX CA cast CT AjPFloat CD Retrieve a float from an array CX // float ajFloatGet(const AjPFloat thys, ajuint elem) { if(!thys || elem>=thys->Len) ajErr("Attempt to access bad float array index %d\n",elem); return thys->Ptr[elem]; } ID ajFloatPut TY public MO ajarr LB core XX DE Load a float array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPFloat* PD Pointer to the float array. PX PN [2] PA r elem ajuint PD array element. PX PN [3] PA r v float PD value to load. PX RT AjBool RD true if the array was extended. RX CA modify CT AjPFloat CD Load a float array element CX // AjBool ajFloatPut(AjPFloat *thys, ajuint elem, float v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value %d\n",elem); if(elem < (*thys)->Res) { if(elem>=(*thys)->Len) (*thys)->Len = elem+1; (*thys)->Ptr[elem] = v; return ajFalse; } arrFloatResize(thys, elem); (*thys)->Ptr[elem] = v; return ajTrue; } ID arrFloatResize TY static MO ajarr LB core XX DE Resize a float array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPFloat* PD Pointer to the float array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrFloatResize(AjPFloat *thys, ajuint size) { AjPFloat p = NULL; ajuint s; ajuint clen; ajuint limit; if(!thys || !*thys) ajErr("Illegal attempt to resize float array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajFloatResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; *thys = ajFloatNewRes(s); if(size < p->Len) limit = size+1; else limit = p->Len; memmove((*thys)->Ptr,p->Ptr,limit*sizeof(float)); (*thys)->Len = size+1; ajFloatDel(&p); arrResize++; return ajTrue; } ID ajFloatFloat TY public MO ajarr LB core XX DE Returns the current float* pointer. This will remain valid until DE the array is resized or deleted. XX PN [1] PA r thys const AjPFloat PD Source array PX RT float* RD Current array pointer, or a null string if undefined. RX CA cast CT AjPFloat CD Retrieve internal pointer CX // float* ajFloatFloat(const AjPFloat thys) { if(!thys || !thys->Len) return NULL; return thys->Ptr; } ID ajFloatLen TY public MO ajarr LB core XX DE Get length of dynamic 1d float array XX PN [1] PA r thys const AjPFloat PD Source array PX RT ajuint RD length RX // ajuint ajFloatLen(const AjPFloat thys) { return thys->Len; } ID ajDoubleNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX double arrays. XX RT AjPDouble RD Pointer to an empty double array structure RX CA new CT AjPDouble CD Default constructor CX // AjPDouble ajDoubleNew(void) { AjPDouble thys; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(double)); thys->Len = 0; thys->Res = RESERVED_SIZE; arrTotal++; arrAlloc += RESERVED_SIZE*sizeof(double); return thys; } ID ajDoubleNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size PX RT AjPDouble RD Pointer to an empty double array struct RD of specified size. RX CA new CT AjPDouble CD Constructor with reserved size CX // AjPDouble ajDoubleNewRes(ajuint size) { AjPDouble thys; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(double)); thys->Len = 0; thys->Res = size; arrTotal++; arrAlloc += size*sizeof(double); return thys; } ID ajDoubleDel TY public MO ajarr LB core XX DE Default destructor for AJAX double arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPDouble* PD Pointer to the double array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPDouble CD Default destructor CX // void ajDoubleDel(AjPDouble *thys) { if(!thys || !*thys) return; /*ajDebug("ajDoubleDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res);*/ AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arrFreeCount++; return; } ID ajDoubleGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX double array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPDouble PD Pointer to the double array. PX PN [2] PA r elem ajuint PD array element. PX RT double RD contents of array element RX CA cast CT AjPDouble CD Retrieve a double from an array CX // double ajDoubleGet(const AjPDouble thys, ajuint elem) { if(!thys || elem>=thys->Len) ajErr("Attempt to access bad double array index %d\n",elem); return thys->Ptr[elem]; } ID ajDoublePut TY public MO ajarr LB core XX DE Load a double array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPDouble* PD Pointer to the double array. PX PN [2] PA r elem ajuint PD array element. PX PN [3] PA r v double PD value to load. PX RT AjBool RD true if the array was extended. RX CA modify CT AjPDouble CD Load a double array element CX // AjBool ajDoublePut(AjPDouble *thys, ajuint elem, double v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value %d\n",elem); if(elem < (*thys)->Res) { if(elem>=(*thys)->Len) (*thys)->Len = elem+1; (*thys)->Ptr[elem] = v; return ajFalse; } arrDoubleResize(thys, elem); (*thys)->Ptr[elem] = v; return ajTrue; } ID arrDoubleResize TY static MO ajarr LB core XX DE Resize a double array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPDouble* PD Pointer to the double array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrDoubleResize(AjPDouble *thys, ajuint size) { AjPDouble p = NULL; ajuint s; ajuint clen; ajuint limit; if(!thys || !*thys) ajErr("Illegal attempt to resize double array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajDoubleResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; *thys = ajDoubleNewRes(s); if(size < p->Len) limit = size+1; else limit = p->Len; memmove((*thys)->Ptr,p->Ptr,limit*sizeof(double)); (*thys)->Len = size+1; ajDoubleDel(&p); arrResize++; return ajTrue; } ID ajDoubleDouble TY public MO ajarr LB core XX DE Returns the current double* pointer. This will remain valid until DE the array is resized or deleted. XX PN [1] PA r thys const AjPDouble PD Source array PX RT double* RD Current array pointer, or a null string if undefined. RX CA cast CT AjPDouble CD Retrieve internal pointer CX // double* ajDoubleDouble(const AjPDouble thys) { if(!thys || !thys->Len) return NULL; return thys->Ptr; } ID ajDoubleLen TY public MO ajarr LB core XX DE Get length of dynamic 1d double array XX PN [1] PA r thys const AjPDouble PD Source array PX RT ajuint RD length RX // ajuint ajDoubleLen(const AjPDouble thys) { return thys->Len; } ID ajShortNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX short arrays. XX RT AjPShort RD Pointer to an empty short array structure RX CA new CT AjPShort CD Default constructor CX // AjPShort ajShortNew(void) { AjPShort thys; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(short)); thys->Len = 0; thys->Res = RESERVED_SIZE; arrTotal++; arrAlloc += RESERVED_SIZE*sizeof(short); return thys; } ID ajShortNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size PX RT AjPShort RD Pointer to an empty short array struct of specified size. RX CA new CT AjPShort CD Constructor with reserved size CX // AjPShort ajShortNewRes(ajuint size) { AjPShort thys; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(short)); thys->Len = 0; thys->Res = size; arrTotal++; arrAlloc += size*sizeof(short); return thys; } ID ajShortDel TY public MO ajarr LB core XX DE Default destructor for AJAX short arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPShort* PD Pointer to the short array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPShort CD Default destructor CX // void ajShortDel(AjPShort *thys) { if(!thys || !*thys) return; /*ajDebug("ajShortDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res);*/ AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arrFreeCount++; return; } ID ajShortGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX short array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPShort PD Pointer to the short array. PX PN [2] PA r elem ajuint PD array element. PX RT short RD contents of array element RX CA cast CT AjPShort CD Retrieve a short from an array CX // short ajShortGet(const AjPShort thys, ajuint elem) { if(!thys || elem>=thys->Len) ajErr("Attempt to access bad short array index %d\n",elem); return thys->Ptr[elem]; } ID ajShortPut TY public MO ajarr LB core XX DE Load a short array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPShort* PD Pointer to the short integer array. PX PN [2] PA r elem ajuint PD array element. PX PN [3] PA r v short PD value to load. PX RT AjBool RD true if the array was extended. RX CA modify CT AjPShort CD Load a short array element CX // AjBool ajShortPut(AjPShort *thys, ajuint elem, short v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value %d\n",elem); if(elem < (*thys)->Res) { if(elem>=(*thys)->Len) (*thys)->Len = elem+1; (*thys)->Ptr[elem] = v; return ajFalse; } arrShortResize(thys, elem); (*thys)->Ptr[elem] = v; return ajTrue; } ID arrShortResize TY static MO ajarr LB core XX DE Resize a short array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPShort* PD Pointer to the short integer array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrShortResize(AjPShort *thys, ajuint size) { AjPShort p = NULL; ajuint s; ajuint clen; ajuint limit; if(!thys || !*thys) ajErr("Illegal attempt to resize short array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajShortResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; *thys = ajShortNewRes(s); if(size < p->Len) limit = size+1; else limit = p->Len; memmove((*thys)->Ptr,p->Ptr,limit*sizeof(short)); (*thys)->Len = size+1; ajShortDel(&p); arrResize++; return ajTrue; } ID ajShortShort TY public MO ajarr LB core XX DE Returns the current short* pointer. This will remain valid until DE the array is resized or deleted. XX PN [1] PA r thys const AjPShort PD Source array PX RT short* RD Current array pointer, or a null string if undefined. RX CA cast CT AjPShort CD Retrieve internal pointer CX // short* ajShortShort(const AjPShort thys) { if(!thys || !thys->Len) return NULL; return thys->Ptr; } ID ajShortLen TY public MO ajarr LB core XX DE Get length of dynamic 1d short array XX PN [1] PA r thys const AjPShort PD Source array PX RT ajuint RD length RX // ajuint ajShortLen(const AjPShort thys) { return thys->Len; } ID ajLongNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX ajlong arrays. XX RT AjPLong RD Pointer to an empty ajlong array structure RX CA new CT AjPLong CD Default constructor CX // AjPLong ajLongNew(void) { AjPLong thys; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(ajlong)); thys->Len = 0; thys->Res = RESERVED_SIZE; arrTotal++; arrAlloc += RESERVED_SIZE*sizeof(ajlong); return thys; } ID ajLongNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size PX RT AjPLong RD Pointer to an empty ajlong array struct of specified size. RX CA new CT AjPLong CD Constructor with reserved size CX // AjPLong ajLongNewRes(ajuint size) { AjPLong thys; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(ajlong)); thys->Len = 0; thys->Res = size; arrTotal++; arrAlloc += size*sizeof(ajlong); return thys; } ID ajLongDel TY public MO ajarr LB core XX DE Default destructor for AJAX ajlong arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPLong* PD Pointer to the ajlong array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPLong CD Default destructor CX // void ajLongDel(AjPLong *thys) { if(!thys || !*thys) return; /*ajDebug("ajLongDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res);*/ AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arrFreeCount++; return; } ID ajLongGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX ajlong array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPLong PD Pointer to the ajlong array. PX PN [2] PA r elem ajuint PD array element. PX RT ajlong RD contents of array element RX CA cast CT AjPLong CD Retrieve a ajlong from an array CX // ajlong ajLongGet(const AjPLong thys, ajuint elem) { if(!thys || elem>=thys->Len) ajErr("Attempt to access bad ajlong array index %d\n",elem); return thys->Ptr[elem]; } ID ajLongPut TY public MO ajarr LB core XX DE Load a long integer array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPLong* PD Pointer to the long integer array. PX PN [2] PA r elem ajuint PD array element. PX PN [3] PA r v ajlong PD value to load. PX RT AjBool RD true if the array was extended. RX CA modify CT AjPLong CD Load a ajlong array element CX // AjBool ajLongPut(AjPLong *thys, ajuint elem, ajlong v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value %d\n",elem); if(elem < (*thys)->Res) { if(elem>=(*thys)->Len) (*thys)->Len = elem+1; (*thys)->Ptr[elem] = v; return ajFalse; } arrLongResize(thys, elem); (*thys)->Ptr[elem] = v; return ajTrue; } ID arrLongResize TY static MO ajarr LB core XX DE Resize a long integer array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPLong* PD Pointer to the long integer array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrLongResize(AjPLong *thys, ajuint size) { AjPLong p = NULL; ajuint s; ajuint clen; ajuint limit; if(!thys || !*thys) ajErr("Illegal attempt to resize ajlong array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajLongResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; *thys = ajLongNewRes(s); if(size < p->Len) limit = size+1; else limit = p->Len; memmove((*thys)->Ptr,p->Ptr,limit*sizeof(ajlong)); (*thys)->Len = size+1; ajLongDel(&p); arrResize++; return ajTrue; } ID ajLongLong TY public MO ajarr LB core XX DE Returns the current ajlong* pointer. This will remain valid until DE the array is resized or deleted. XX PN [1] PA r thys const AjPLong PD Source array PX RT ajlong* RD Current array pointer, or a null string if undefined. RX CA cast CT AjPLong CD Retrieve internal pointer CX // ajlong* ajLongLong(const AjPLong thys) { if(!thys || !thys->Len) return NULL; return thys->Ptr; } ID ajLongLen TY public MO ajarr LB core XX DE Get length of dynamic 1d ajlong array XX PN [1] PA r thys const AjPLong PD Source array PX RT ajuint RD length RX // ajuint ajLongLen(const AjPLong thys) { return thys->Len; } ID ajFloatParse TY public MO ajarr LB core XX DE Parses a string into a floating point array. DE DE The array size is already set. XX PN [1] PA r str const AjPStr PD Input string PX PN [2] PA w array AjPFloat* PD Array PX RT AjBool RD ajTrue on success. RX // AjBool ajFloatParse (const AjPStr str, AjPFloat* array) { ajuint i = 0; float t = 0.0; AjPStr tmpstr = NULL; AjPStr tmpstr2 = NULL; AjPStr numstr = NULL; if (!floatRegNum) floatRegNum = ajRegCompC ("[+-]?[0-9.]+"); ajStrAssignS(&tmpstr, str); while (ajRegExec (floatRegNum, tmpstr)) { ajRegSubI (floatRegNum, 0, &numstr); ajRegPost (floatRegNum, &tmpstr2); ajStrAssignS(&tmpstr, tmpstr2); ajStrToFloat (numstr, &t); ajFloatPut(array,i,t); i++; } ajStrDel(&numstr); ajStrDel(&tmpstr); ajStrDel(&tmpstr2); if(!i) return ajFalse; return ajTrue; } ID ajFloatStr TY public MO ajarr LB core XX DE Writes a floating point array as a string XX PN [1] PA r array const AjPFloat PD Array PX PN [2] PA r precision ajint PD floating point precision PX PN [3] PA w str AjPStr* PD Output string PX RT void RD RX // void ajFloatStr (const AjPFloat array, ajint precision, AjPStr* str) { ajuint i; for (i=0; i < array->Len; i++) { if (i) ajStrAppendK(str, ' '); ajFmtPrintAppS (str, "%.*f", precision, ajFloatGet(array,i)); } return; } ID ajFloatTrace TY public MO ajarr LB core XX DE Writes a floating point array to the debug file XX PN [1] PA r array const AjPFloat PD Array PX PN [2] PA r precision ajint PD floating point precision PX PN [3] PA r text const char* PD Report title PX RT void RD RX // void ajFloatTrace (const AjPFloat array, ajint precision, const char* text) { ajuint i; ajDebug ("%s\n", text); for (i=0; i < array->Len; i++) ajDebug ("%3d: %.*f\n", i, precision, ajFloatGet(array,i)); ajDebug ("\n"); return; } ID ajArrCommaList TY public MO ajarr LB core XX DE Creates an AjPStr array from a string of comma separated tokens XX PN [1] PA r s const AjPStr PD Line containing comma separated strings PX PN [2] PA w a AjPStr** PD array pointer to create and load PX RT ajuint RD number of array elements created RX // ajuint ajArrCommaList(const AjPStr s, AjPStr **a) { AjPStr x; AjPStrTok t; ajuint n; ajuint i; n = ajStrParseCountC(s,",\n"); if(!n) return 0; AJCNEW(*a, n); x = ajStrNew(); t = ajStrTokenNewC(s,",\n"); for(i=0;iPtr = AJALLOC0(RESERVED_SIZE*sizeof(AjPInt)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; arr2dTotal++; arr2dAlloc += RESERVED_SIZE; return thys; } ID ajInt2dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPInt2d RD Pointer to an empty integer 2d array struct of RD specified size. RX CA new CT AjPInt2d CD Constructor with reserved size CX // AjPInt2d ajInt2dNewRes(ajuint size) { AjPInt2d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPInt)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; arr2dAlloc++; return thys; } ID ajInt2dNewResRes2 TY public MO ajarr LB core XX DE Constructor given an initial reserved size in both dimensions XX PN [1] PA r size ajuint PD Reserved size 1st dim PX PN [2] PA r size2 ajuint PD Reserved size 2nd dim PX RT AjPInt2d RD Pointer to an empty integer 2d array struct of RD specified size. RX CA new CT AjPInt2d CD Constructor with reserved size CX // AjPInt2d ajInt2dNewResRes2(ajuint size, ajuint size2) { AjPInt2d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPInt)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = ajIntNewRes(size2); arr2dAlloc++; /*ajDebug("ajInt2dNewLL %d*%d %d; %d*%d*%d %d\n", size, sizeof(AjPInt*), size*sizeof(AjPInt*), size, size2, sizeof(ajint), size*size2*sizeof(ajint));*/ return thys; } ID ajInt2dDel TY public MO ajarr LB core XX DE Default destructor for AJAX integer arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPInt2d* PD Pointer to the ajint array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPInt2d CD Default destructor CX // void ajInt2dDel(AjPInt2d *thys) { ajint i; if(!thys || !*thys) return; /*ajDebug("ajInt2dDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res);*/ for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajIntDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr2dFreeCount++; return; } ID ajInt2dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 2d integer array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPInt2d PD Pointer to the ajint array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX RT ajint RD contents of array element RX CA cast CT AjPInt2d CD Retrieve an integer from an array CX // ajint ajInt2dGet(const AjPInt2d thys, ajuint elem1, ajuint elem2) { AjPInt t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad ajint array index [%d][%d]\n",elem1, elem2); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][]\n",elem1); return ajIntGet(t,elem2); } ID ajInt2dPut TY public MO ajarr LB core XX DE Load an integer 2d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPInt2d* PD Pointer to the ajint array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r v ajint PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPInt2d CD Load an integer array element CX // AjBool ajInt2dPut(AjPInt2d *thys, ajuint elem1, ajuint elem2, ajint v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d]\n",elem1, elem2); if(elem1 < (*thys)->Res) { /*ajDebug("ajInt2dPut [%u][%u] %d ([%u] %x)\n", elem1, elem2, v, (*thys)->Len, (*thys)->Ptr[elem1]);*/ if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajIntNew(); return ajIntPut(&(*thys)->Ptr[elem1],elem2,v); } arrInt2dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajIntNew(); ajIntPut(&(*thys)->Ptr[elem1],elem2,v); return ajTrue; } ID arrInt2dResize TY static MO ajarr LB core XX DE Resize an integer array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPInt2d* PD Pointer to the ajint array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrInt2dResize(AjPInt2d *thys, ajuint size) { AjPInt2d nthys; AjPInt2d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize integer array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajInt2dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPInt)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPInt)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajIntDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr2dResize++; return ajTrue; } ID ajInt2dLen TY public MO ajarr LB core XX DE Get lengths of 2d ajint array XX PN [1] PA r thys const AjPInt2d PD Pointer to the ajint array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX RT void RD RX // void ajInt2dLen(const AjPInt2d thys, ajuint* len1, ajuint* len2) { AjPInt t; ajuint i; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t=thys->Ptr[i])) *len2 = (*len2 > t->Len) ? *len2 : t->Len; return; } ID ajInt2dInt TY public MO ajarr LB core XX DE Convert AjPInt2d to ajint XX PN [1] PA r thys const AjPInt2d PD Pointer to the ajint array. PX RT ajint** RD converted value. RX CA cast CT AjPInt2d CD Retrieve internal pointer CX // ajint** ajInt2dInt(const AjPInt2d thys) { AjPInt t = NULL; ajint **array; ajuint d1; ajuint d2; ajuint i; ajInt2dLen(thys,&d1,&d2); AJCNEW(array,d1); for(i=0;iPtr[i])) memmove(array[i],t->Ptr,t->Len*sizeof(ajint)); } return array; } ID ajInt3dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 3D integer arrays. XX RT AjPInt3d RD Pointer to an empty integer array structure RX CA new CT AjPInt3d CD Default constructor CX // AjPInt3d ajInt3dNew(void) { AjPInt3d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPInt2d)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; return thys; } ID ajInt3dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPInt3d RD Pointer to an empty integer 3d array struct of RD specified size. RX CA new CT AjPInt3d CD Constructor with reserved size CX // AjPInt3d ajInt3dNewRes(ajuint size) { AjPInt3d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPInt2d)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; return thys; } ID ajInt3dDel TY public MO ajarr LB core XX DE Default destructor for AJAX integer arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPInt3d* PD Pointer to the ajint array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPInt3d CD Default destructor CX // void ajInt3dDel(AjPInt3d *thys) { ajint i; if(!thys || !*thys) return; /*ajDebug("ajInt3dDel Len %u Res %u\n", (*thys)->Len, (*thys)->Res);*/ for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajInt2dDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr3dFreeCount++; return; } ID ajInt3dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 3d integer array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPInt3d PD Pointer to the ajint array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX RT ajint RD contents of array element RX CA cast CT AjPInt3d CD Retrieve an integer from an array CX // ajint ajInt3dGet(const AjPInt3d thys, ajuint elem1, ajuint elem2, ajuint elem3) { AjPInt2d t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad ajint array index [%d][%d][%d]\n",elem1, elem2,elem3); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][][]\n",elem1); return ajInt2dGet(t,elem2,elem3); } ID ajInt3dPut TY public MO ajarr LB core XX DE Load an integer 3d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPInt3d* PD Pointer to the ajint array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX PN [5] PA r v ajint PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPInt3d CD Load an integer array element CX // AjBool ajInt3dPut(AjPInt3d *thys, ajuint elem1, ajuint elem2, ajuint elem3, ajint v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d][%d]\n",elem1, elem2,elem3); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajInt2dNew(); return ajInt2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); } arrInt3dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajInt2dNew(); ajInt2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); return ajTrue; } ID arrInt3dResize TY static MO ajarr LB core XX DE Resize an integer array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPInt3d* PD Pointer to the ajint array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrInt3dResize(AjPInt3d *thys, ajuint size) { AjPInt3d nthys; AjPInt3d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize integer array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajInt3dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPInt2d)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPInt2d)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajInt2dDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr3dResize++; return ajTrue; } ID ajInt3dLen TY public MO ajarr LB core XX DE Get lengths of 3d ajint array XX PN [1] PA r thys const AjPInt3d PD Pointer to the ajint array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX PN [4] PA w len3 ajuint* PD Length of 3rd dim PX RT void RD RX // void ajInt3dLen(const AjPInt3d thys, ajuint* len1, ajuint* len2, ajuint* len3) { AjPInt2d t2; AjPInt t1; ajuint i; ajuint j; ajuint v; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) *len2 = (*len2 > t2->Len) ? *len2 : t2->Len; *len3=0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) { v = t2->Len; for(j=0;jPtr[j])) *len3 = (*len3 > t1->Len) ? *len3 : t1->Len; } return; } ID ajInt3dInt TY public MO ajarr LB core XX DE Convert AjPInt3d to ajint XX PN [1] PA r thys const AjPInt3d PD Pointer to the ajint array. PX RT ajint*** RD converted values. RX CA cast CT AjPInt3d CD Retrieve internal pointer CX // ajint*** ajInt3dInt(const AjPInt3d thys) { AjPInt2d t2 = NULL; AjPInt t1 = NULL; ajint ***array; ajuint d1; ajuint d2; ajuint d3; ajuint i; ajuint j; ajInt3dLen(thys,&d1,&d2,&d3); AJCNEW0(array,d1); for(i=0;iPtr[i]; for(j=0;j=t2->Len) continue; if((t1=t2->Ptr[j])) memmove(array[i][j],t1->Ptr, t1->Len*sizeof(ajint)); } } } return array; } ID ajUint2dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 2D integer arrays. XX RT AjPUint2d RD Pointer to an empty integer array structure RX CA new CT AjPUint2d CD Default constructor CX // AjPUint2d ajUint2dNew(void) { AjPUint2d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPUint)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; arr2dTotal++; arr2dAlloc += RESERVED_SIZE; return thys; } ID ajUint2dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPUint2d RD Pointer to an empty integer 2d array struct of RD specified size. RX CA new CT AjPUint2d CD Constructor with reserved size CX // AjPUint2d ajUint2dNewRes(ajuint size) { AjPUint2d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPUint)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; arr2dAlloc++; return thys; } ID ajUint2dNewResRes2 TY public MO ajarr LB core XX DE Constructor given an initial reserved size in both dimensions XX PN [1] PA r size ajuint PD Reserved size 1st dim PX PN [2] PA r size2 ajuint PD Reserved size 2nd dim PX RT AjPUint2d RD Pointer to an empty integer 2d array struct of RD specified size. RX CA new CT AjPUint2d CD Constructor with reserved size CX // AjPUint2d ajUint2dNewResRes2(ajuint size, ajuint size2) { AjPUint2d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPUint)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = ajUintNewRes(size2); arr2dAlloc++; /*ajDebug("ajUint2dNewLL %d*%d %d; %d*%d*%d %d\n", size, sizeof(AjPUint*), size*sizeof(AjPUint*), size, size2, sizeof(ajuint), size*size2*sizeof(ajuint));*/ return thys; } ID ajUint2dDel TY public MO ajarr LB core XX DE Default destructor for AJAX unsigned integer arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPUint2d* PD Pointer to the ajuint array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPUint2d CD Default destructor CX // void ajUint2dDel(AjPUint2d *thys) { ajint i; if(!thys || !*thys) return; for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajUintDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr2dFreeCount++; return; } ID ajUint2dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 2d unsigned integer array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPUint2d PD Pointer to the ajuint array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX RT ajuint RD contents of array element RX CA cast CT AjPUint2d CD Retrieve an integer from an array CX // ajuint ajUint2dGet(const AjPUint2d thys, ajuint elem1, ajuint elem2) { AjPUint t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad ajuint array index [%d][%d]\n",elem1, elem2); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][]\n",elem1); return ajUintGet(t,elem2); } ID ajUint2dPut TY public MO ajarr LB core XX DE Load an unsigned integer 2d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPUint2d* PD Pointer to the ajuint array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r v ajuint PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPUint2d CD Load an integer array element CX // AjBool ajUint2dPut(AjPUint2d *thys, ajuint elem1, ajuint elem2, ajuint v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d]\n",elem1, elem2); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajUintNew(); return ajUintPut(&(*thys)->Ptr[elem1],elem2,v); } arrUint2dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajUintNew(); ajUintPut(&(*thys)->Ptr[elem1],elem2,v); return ajTrue; } ID arrUint2dResize TY static MO ajarr LB core XX DE Resize an unsigned integer array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPUint2d* PD Pointer to the ajuint array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrUint2dResize(AjPUint2d *thys, ajuint size) { AjPUint2d nthys; AjPUint2d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize integer array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajUint2dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPUint)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPUint)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajUintDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr2dResize++; return ajTrue; } ID ajUint2dLen TY public MO ajarr LB core XX DE Get lengths of 2d ajuint array XX PN [1] PA r thys const AjPUint2d PD Pointer to the ajuint array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX RT void RD RX // void ajUint2dLen(const AjPUint2d thys, ajuint* len1, ajuint* len2) { AjPUint t; ajuint i; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t=thys->Ptr[i])) *len2 = (*len2 > t->Len) ? *len2 : t->Len; return; } ID ajUint2dUint TY public MO ajarr LB core XX DE Convert AjPUint2d to ajuint XX PN [1] PA r thys const AjPUint2d PD Pointer to the ajuint array. PX RT ajuint** RD converted value. RX CA cast CT AjPUint2d CD Retrieve internal pointer CX // ajuint** ajUint2dUint(const AjPUint2d thys) { AjPUint t = NULL; ajuint **array; ajuint d1; ajuint d2; ajuint i; ajUint2dLen(thys,&d1,&d2); AJCNEW(array,d1); for(i=0;iPtr[i])) memmove(array[i],t->Ptr,t->Len*sizeof(ajuint)); } return array; } ID ajUint3dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 3D unsigned integer arrays. XX RT AjPUint3d RD Pointer to an empty unsigned integer array structure RX CA new CT AjPUint3d CD Default constructor CX // AjPUint3d ajUint3dNew(void) { AjPUint3d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPUint2d)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; return thys; } ID ajUint3dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPUint3d RD Pointer to an empty unsigned integer 3d array struct of RD specified size. RX CA new CT AjPUint3d CD Constructor with reserved size CX // AjPUint3d ajUint3dNewRes(ajuint size) { AjPUint3d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPInt2d)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; return thys; } ID ajUint3dDel TY public MO ajarr LB core XX DE Default destructor for AJAX unsigned integer arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPUint3d* PD Pointer to the ajuint array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPUint3d CD Default destructor CX // void ajUint3dDel(AjPUint3d *thys) { ajint i; if(!thys || !*thys) return; for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajUint2dDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr3dFreeCount++; return; } ID ajUint3dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 3d integer array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPUint3d PD Pointer to the ajuint array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX RT ajuint RD contents of array element RX CA cast CT AjPUint3d CD Retrieve an integer from an array CX // ajuint ajUint3dGet(const AjPUint3d thys, ajuint elem1, ajuint elem2, ajuint elem3) { AjPUint2d t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad ajuint array index [%d][%d][%d]\n",elem1, elem2,elem3); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][][]\n",elem1); return ajUint2dGet(t,elem2,elem3); } ID ajUint3dPut TY public MO ajarr LB core XX DE Load an unsigned integer 3d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPUint3d* PD Pointer to the ajint array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX PN [5] PA r v ajuint PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPUint3d CD Load an integer array element CX // AjBool ajUint3dPut(AjPUint3d *thys, ajuint elem1, ajuint elem2, ajuint elem3, ajuint v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d][%d]\n",elem1, elem2,elem3); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajUint2dNew(); return ajUint2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); } arrUint3dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajUint2dNew(); ajUint2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); return ajTrue; } ID arrUint3dResize TY static MO ajarr LB core XX DE Resize an unsigned integer array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPUint3d* PD Pointer to the ajuint array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrUint3dResize(AjPUint3d *thys, ajuint size) { AjPUint3d nthys; AjPUint3d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize unsigned integer array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajUint3dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPUint2d)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPUint2d)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajUint2dDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr3dResize++; return ajTrue; } ID ajUint3dLen TY public MO ajarr LB core XX DE Get lengths of 3d ajuint array XX PN [1] PA r thys const AjPUint3d PD Pointer to the ajuint array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX PN [4] PA w len3 ajuint* PD Length of 3rd dim PX RT void RD RX // void ajUint3dLen(const AjPUint3d thys, ajuint* len1, ajuint* len2, ajuint* len3) { AjPUint2d t2; AjPUint t1; ajuint i; ajuint j; ajuint v; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) *len2 = (*len2 > t2->Len) ? *len2 : t2->Len; *len3=0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) { v = t2->Len; for(j=0;jPtr[j])) *len3 = (*len3 > t1->Len) ? *len3 : t1->Len; } return; } ID ajUint3dUint TY public MO ajarr LB core XX DE Convert AjPUint3d to ajuint XX PN [1] PA r thys const AjPUint3d PD Pointer to the ajuint array. PX RT ajuint*** RD converted values. RX CA cast CT AjPUint3d CD Retrieve internal pointer CX // ajuint*** ajUint3dUint(const AjPUint3d thys) { AjPUint2d t2 = NULL; AjPUint t1 = NULL; ajuint ***array; ajuint d1; ajuint d2; ajuint d3; ajuint i; ajuint j; ajUint3dLen(thys,&d1,&d2,&d3); AJCNEW0(array,d1); for(i=0;iPtr[i]; for(j=0;j=t2->Len) continue; if((t1=t2->Ptr[j])) memmove(array[i][j],t1->Ptr, t1->Len*sizeof(ajuint)); } } } return array; } ID ajFloat2dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 2D float arrays. XX RT AjPFloat2d RD Pointer to an empty float array structure RX CA new CT AjPFloat2d CD Default constructor CX // AjPFloat2d ajFloat2dNew(void) { AjPFloat2d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPFloat)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; return thys; } ID ajFloat2dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPFloat2d RD Pointer to an empty float 2d array struct of RD specified size. RX CA new CT AjPFloat2d CD Constructor with reserved size CX // AjPFloat2d ajFloat2dNewRes(ajuint size) { AjPFloat2d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPFloat)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; return thys; } ID ajFloat2dDel TY public MO ajarr LB core XX DE Default destructor for AJAX float arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPFloat2d* PD Pointer to the float array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPFloat2d CD Default destructor CX // void ajFloat2dDel(AjPFloat2d *thys) { ajint i; if(!thys || !*thys) return; for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajFloatDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr2dFreeCount++; return; } ID ajFloat2dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 2d float array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPFloat2d PD Pointer to the float array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX RT float RD contents of array element RX CA cast CT AjPFloat2d CD Retrieve a float from an array CX // float ajFloat2dGet(const AjPFloat2d thys, ajuint elem1, ajuint elem2) { AjPFloat t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad float array index [%d][%d]\n",elem1, elem2); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][]\n",elem1); return ajFloatGet(t,elem2); } ID ajFloat2dPut TY public MO ajarr LB core XX DE Load a float 2d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPFloat2d* PD Pointer to the float array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r v float PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPFloat2d CD Load a float array element CX // AjBool ajFloat2dPut(AjPFloat2d *thys, ajuint elem1, ajuint elem2, float v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d]\n",elem1, elem2); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajFloatNew(); return ajFloatPut(&(*thys)->Ptr[elem1],elem2,v); } arrFloat2dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajFloatNew(); ajFloatPut(&(*thys)->Ptr[elem1],elem2,v); return ajTrue; } ID arrFloat2dResize TY static MO ajarr LB core XX DE Resize a float array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPFloat2d* PD Pointer to the float array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrFloat2dResize(AjPFloat2d *thys, ajuint size) { AjPFloat2d nthys; AjPFloat2d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize float array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajFloat2dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPFloat)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPFloat)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajFloatDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr2dResize++; return ajTrue; } ID ajFloat2dLen TY public MO ajarr LB core XX DE Get lengths of 2d float array XX PN [1] PA r thys const AjPFloat2d PD Pointer to the float array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX RT void RD RX // void ajFloat2dLen(const AjPFloat2d thys, ajuint* len1, ajuint* len2) { AjPFloat t; ajuint i; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t=thys->Ptr[i])) *len2 = (*len2 > t->Len) ? *len2 : t->Len; return; } ID ajFloat2dFloat TY public MO ajarr LB core XX DE Convert AjPFloat2d to float XX PN [1] PA r thys const AjPFloat2d PD Pointer to the float array. PX RT float** RD converted values. RX CA cast CT AjPFloat2d CD Retrieve internal pointer CX // float** ajFloat2dFloat(const AjPFloat2d thys) { AjPFloat t = NULL; float **array; ajuint d1; ajuint d2; ajuint i; ajFloat2dLen(thys,&d1,&d2); AJCNEW(array,d1); for(i=0;iPtr[i])) memmove(array[i],t->Ptr,t->Len*sizeof(float)); } return array; } ID ajFloat3dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 3D float arrays. XX RT AjPFloat3d RD Pointer to an empty float array structure RX CA new CT AjPFloat3d CD Default constructor CX // AjPFloat3d ajFloat3dNew(void) { AjPFloat3d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPFloat2d)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; return thys; } ID ajFloat3dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPFloat3d RD Pointer to an empty float 3d array struct of RD specified size. RX CA new CT AjPFloat3d CD Constructor with reserved size CX // AjPFloat3d ajFloat3dNewRes(ajuint size) { AjPFloat3d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPFloat2d)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; return thys; } ID ajFloat3dDel TY public MO ajarr LB core XX DE Default destructor for AJAX float arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPFloat3d* PD Pointer to the float array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPFloat3d CD Default destructor CX // void ajFloat3dDel(AjPFloat3d *thys) { ajint i; if(!thys || !*thys) return; for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajFloat2dDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr3dFreeCount++; return; } ID ajFloat3dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 3d float array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPFloat3d PD Pointer to the float array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX RT float RD contents of array element RX CA cast CT AjPFloat3d CD Retrieve a float from an array CX // float ajFloat3dGet(const AjPFloat3d thys, ajuint elem1, ajuint elem2, ajuint elem3) { AjPFloat2d t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad float array index [%d][%d][%d]\n",elem1, elem2,elem3); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][][]\n",elem1); return ajFloat2dGet(t,elem2,elem3); } ID ajFloat3dPut TY public MO ajarr LB core XX DE Load a float 3d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPFloat3d* PD Pointer to the float array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX PN [5] PA r v float PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPFloat3d CD Load a float array element CX // AjBool ajFloat3dPut(AjPFloat3d *thys, ajuint elem1, ajuint elem2, ajuint elem3, float v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d][%d]\n",elem1, elem2,elem3); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajFloat2dNew(); return ajFloat2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); } arrFloat3dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajFloat2dNew(); ajFloat2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); return ajTrue; } ID arrFloat3dResize TY static MO ajarr LB core XX DE Resize a float array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPFloat3d* PD Pointer to the float array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrFloat3dResize(AjPFloat3d *thys, ajuint size) { AjPFloat3d nthys; AjPFloat3d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize float array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajFloat3dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPFloat2d)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPFloat2d)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajFloat2dDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr3dResize++; return ajTrue; } ID ajFloat3dLen TY public MO ajarr LB core XX DE Get lengths of 3d float array XX PN [1] PA r thys const AjPFloat3d PD Pointer to the float array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX PN [4] PA w len3 ajuint* PD Length of 3rd dim PX RT void RD RX // void ajFloat3dLen(const AjPFloat3d thys, ajuint* len1, ajuint* len2, ajuint* len3) { AjPFloat2d t2; AjPFloat t1; ajuint i; ajuint j; ajuint v; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) *len2 = (*len2 > t2->Len) ? *len2 : t2->Len; *len3 = 0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) { v = t2->Len; for(j=0;jPtr[j])) *len3 = (*len3 > t1->Len) ? *len3 : t1->Len; } return; } ID ajFloat3dFloat TY public MO ajarr LB core XX DE Convert AjPFloat3d to float XX PN [1] PA r thys const AjPFloat3d PD Pointer to the float array. PX RT float*** RD converted values. RX CA cast CT AjPFloat3d CD Retrieve internal pointer CX // float*** ajFloat3dFloat(const AjPFloat3d thys) { AjPFloat2d t2 = NULL; AjPFloat t1 = NULL; float ***array; ajuint d1; ajuint d2; ajuint d3; ajuint i; ajuint j; ajFloat3dLen(thys,&d1,&d2,&d3); AJCNEW0(array,d1); for(i=0;iPtr[i]; for(j=0;j=t2->Len) continue; if((t1=t2->Ptr[j])) memmove(array[i][j],t1->Ptr, t1->Len*sizeof(float)); } } } return array; } ID ajDouble2dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 2D double arrays. XX RT AjPDouble2d RD Pointer to an empty double array structure RX CA new CT AjPDouble2d CD Default constructor CX // AjPDouble2d ajDouble2dNew(void) { AjPDouble2d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPDouble)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; return thys; } ID ajDouble2dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPDouble2d RD Pointer to an empty double 2d array struct of RD specified size. RX CA new CT AjPDouble2d CD Constructor with reserved size CX // AjPDouble2d ajDouble2dNewRes(ajuint size) { AjPDouble2d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPDouble)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; return thys; } ID ajDouble2dDel TY public MO ajarr LB core XX DE Default destructor for AJAX double arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPDouble2d* PD Pointer to the double array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPDouble2d CD Default destructor CX // void ajDouble2dDel(AjPDouble2d *thys) { ajint i; if(!thys || !*thys) return; for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajDoubleDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr2dFreeCount++; return; } ID ajDouble2dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 2d double array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPDouble2d PD Pointer to the double array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX RT double RD contents of array element RX CA cast CT AjPDouble2d CD Retrieve a double from an array CX // double ajDouble2dGet(const AjPDouble2d thys, ajuint elem1, ajuint elem2) { AjPDouble t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad double array index [%d][%d]\n",elem1, elem2); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][]\n",elem1); return ajDoubleGet(t,elem2); } ID ajDouble2dPut TY public MO ajarr LB core XX DE Load a double 2d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPDouble2d* PD Pointer to the double array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r v double PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPDouble2d CD Load a double array element CX // AjBool ajDouble2dPut(AjPDouble2d *thys, ajuint elem1, ajuint elem2, double v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d]\n",elem1, elem2); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajDoubleNew(); return ajDoublePut(&(*thys)->Ptr[elem1],elem2,v); } arrDouble2dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajDoubleNew(); ajDoublePut(&(*thys)->Ptr[elem1],elem2,v); return ajTrue; } ID arrDouble2dResize TY static MO ajarr LB core XX DE Resize a double array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPDouble2d* PD Pointer to the double array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrDouble2dResize(AjPDouble2d *thys, ajuint size) { AjPDouble2d nthys; AjPDouble2d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize double array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajDouble2dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPDouble)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPDouble)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajDoubleDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr2dResize++; return ajTrue; } ID ajDouble2dLen TY public MO ajarr LB core XX DE Get lengths of 2d double array XX PN [1] PA r thys const AjPDouble2d PD Pointer to the double array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX RT void RD RX // void ajDouble2dLen(const AjPDouble2d thys, ajuint* len1, ajuint* len2) { AjPDouble t; ajuint i; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t=thys->Ptr[i])) *len2 = (*len2 > t->Len) ? *len2 : t->Len; return; } ID ajDouble2dDouble TY public MO ajarr LB core XX DE Convert AjPDouble2d to double XX PN [1] PA r thys const AjPDouble2d PD Pointer to the double array. PX RT double** RD converted values. RX CA cast CT AjPDouble2d CD Retrieve internal pointer CX // double** ajDouble2dDouble(const AjPDouble2d thys) { AjPDouble t = NULL; double **array; ajuint d1; ajuint d2; ajuint i; ajDouble2dLen(thys,&d1,&d2); AJCNEW(array,d1); for(i=0;iPtr[i])) memmove(array[i],t->Ptr,t->Len*sizeof(double)); } return array; } ID ajDouble3dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 3D double arrays. XX RT AjPDouble3d RD Pointer to an empty double array structure RX CA new CT AjPDouble3d CD Default constructor CX // AjPDouble3d ajDouble3dNew(void) { AjPDouble3d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPDouble2d)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; return thys; } ID ajDouble3dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPDouble3d RD Pointer to an empty double 3d array struct of RD specified size. RX CA new CT AjPDouble3d CD Constructor with reserved size CX // AjPDouble3d ajDouble3dNewRes(ajuint size) { AjPDouble3d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPDouble2d)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; return thys; } ID ajDouble3dDel TY public MO ajarr LB core XX DE Default destructor for AJAX double arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPDouble3d* PD Pointer to the double array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPDouble3d CD Default destructor CX // void ajDouble3dDel(AjPDouble3d *thys) { ajint i; if(!thys || !*thys) return; for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajDouble2dDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr3dFreeCount++; return; } ID ajDouble3dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 3d double array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPDouble3d PD Pointer to the double array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX RT double RD contents of array element RX CA cast CT AjPDouble3d CD Retrieve a double from an array CX // double ajDouble3dGet(const AjPDouble3d thys, ajuint elem1, ajuint elem2, ajuint elem3) { AjPDouble2d t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad double array index [%d][%d][%d]\n",elem1, elem2,elem3); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][][]\n",elem1); return ajDouble2dGet(t,elem2,elem3); } ID ajDouble3dPut TY public MO ajarr LB core XX DE Load a double 3d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPDouble3d* PD Pointer to the double array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX PN [5] PA r v double PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPDouble3d CD Load a double array element CX // AjBool ajDouble3dPut(AjPDouble3d *thys, ajuint elem1, ajuint elem2, ajuint elem3, double v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d][%d]\n",elem1, elem2,elem3); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajDouble2dNew(); return ajDouble2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); } arrDouble3dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajDouble2dNew(); ajDouble2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); return ajTrue; } ID arrDouble3dResize TY static MO ajarr LB core XX DE Resize a double array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPDouble3d* PD Pointer to the double array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrDouble3dResize(AjPDouble3d *thys, ajuint size) { AjPDouble3d nthys; AjPDouble3d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize double array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajDouble3dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPDouble2d)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPDouble2d)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajDouble2dDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr3dResize++; return ajTrue; } ID ajDouble3dLen TY public MO ajarr LB core XX DE Get lengths of 3d double array XX PN [1] PA r thys const AjPDouble3d PD Pointer to the double array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX PN [4] PA w len3 ajuint* PD Length of 3rd dim PX RT void RD RX // void ajDouble3dLen(const AjPDouble3d thys, ajuint* len1, ajuint* len2, ajuint* len3) { AjPDouble2d t2; AjPDouble t1; ajuint i; ajuint j; ajuint v; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) *len2 = (*len2 > t2->Len) ? *len2 : t2->Len; *len3 = 0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) { v = t2->Len; for(j=0;jPtr[j])) *len3 = (*len3 > t1->Len) ? *len3 : t1->Len; } return; } ID ajDouble3dDouble TY public MO ajarr LB core XX DE Convert AjPDouble3d to double XX PN [1] PA r thys const AjPDouble3d PD Pointer to the double array. PX RT double*** RD converted values. RX CA cast CT AjPDouble3d CD Retrieve internal pointer CX // double*** ajDouble3dDouble(const AjPDouble3d thys) { AjPDouble2d t2 = NULL; AjPDouble t1 = NULL; double ***array; ajuint d1; ajuint d2; ajuint d3; ajuint i; ajuint j; ajDouble3dLen(thys,&d1,&d2,&d3); AJCNEW0(array,d1); for(i=0;iPtr[i]; for(j=0;j=t2->Len) continue; if((t1=t2->Ptr[j])) memmove(array[i][j],t1->Ptr, t1->Len*sizeof(double)); } } } return array; } ID ajShort2dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 2D short arrays. XX RT AjPShort2d RD Pointer to an empty short array structure RX CA new CT AjPShort2d CD Default constructor CX // AjPShort2d ajShort2dNew(void) { AjPShort2d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPShort)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; return thys; } ID ajShort2dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPShort2d RD Pointer to an empty short 2d array struct of RD specified size. RX CA new CT AjPShort2d CD Constructor with reserved size CX // AjPShort2d ajShort2dNewRes(ajuint size) { AjPShort2d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPShort)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; return thys; } ID ajShort2dDel TY public MO ajarr LB core XX DE Default destructor for AJAX short arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPShort2d* PD Pointer to the short array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPShort2d CD Default destructor CX // void ajShort2dDel(AjPShort2d *thys) { ajint i; if(!thys || !*thys) return; for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajShortDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr2dFreeCount++; return; } ID ajShort2dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 2d short array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPShort2d PD Pointer to the short array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX RT short RD contents of array element RX CA cast CT AjPShort2d CD Retrieve a short from an array CX // short ajShort2dGet(const AjPShort2d thys, ajuint elem1, ajuint elem2) { AjPShort t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad short array index [%d][%d]\n",elem1, elem2); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][]\n",elem1); return ajShortGet(t,elem2); } ID ajShort2dPut TY public MO ajarr LB core XX DE Load a short 2d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPShort2d* PD Pointer to the short array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r v short PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPShort2d CD Load a short array element CX // AjBool ajShort2dPut(AjPShort2d *thys, ajuint elem1, ajuint elem2, short v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d]\n",elem1, elem2); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajShortNew(); return ajShortPut(&(*thys)->Ptr[elem1],elem2,v); } arrShort2dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajShortNew(); ajShortPut(&(*thys)->Ptr[elem1],elem2,v); return ajTrue; } ID arrShort2dResize TY static MO ajarr LB core XX DE Resize a short array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPShort2d* PD Pointer to the short array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrShort2dResize(AjPShort2d *thys, ajuint size) { AjPShort2d nthys; AjPShort2d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize short array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajShort2dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPShort)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPShort)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajShortDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr2dResize++; return ajTrue; } ID ajShort2dLen TY public MO ajarr LB core XX DE Get lengths of 2d short array XX PN [1] PA r thys const AjPShort2d PD Pointer to the short array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX RT void RD RX // void ajShort2dLen(const AjPShort2d thys, ajuint* len1, ajuint* len2) { AjPShort t; ajuint i; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t=thys->Ptr[i])) *len2 = (*len2 > t->Len) ? *len2 : t->Len; return; } ID ajShort2dShort TY public MO ajarr LB core XX DE Convert AjPShort2d to short XX PN [1] PA r thys const AjPShort2d PD Pointer to the short array. PX RT short** RD converted values RX CA cast CT AjPShort2d CD Retrieve internal pointer CX // short** ajShort2dShort(const AjPShort2d thys) { AjPShort t = NULL; short **array; ajuint d1; ajuint d2; ajuint i; ajShort2dLen(thys,&d1,&d2); AJCNEW(array,d1); for(i=0;iPtr[i])) memmove(array[i],t->Ptr,t->Len*sizeof(short)); } return array; } ID ajShort3dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 3D short arrays. XX RT AjPShort3d RD Pointer to an empty short array structure RX CA new CT AjPShort3d CD Default constructor CX // AjPShort3d ajShort3dNew(void) { AjPShort3d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPShort2d)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; return thys; } ID ajShort3dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPShort3d RD Pointer to an empty short 3d array struct of RD specified size. RX CA new CT AjPShort3d CD Constructor with reserved size CX // AjPShort3d ajShort3dNewRes(ajuint size) { AjPShort3d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPShort2d)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; return thys; } ID ajShort3dDel TY public MO ajarr LB core XX DE Default destructor for AJAX short arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPShort3d* PD Pointer to the short array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPShort3d CD Default destructor CX // void ajShort3dDel(AjPShort3d *thys) { ajint i; if(!thys || !*thys) return; for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajShort2dDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr3dFreeCount++; return; } ID ajShort3dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 3d short array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPShort3d PD Pointer to the short array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX RT short RD contents of array element RX CA cast CT AjPShort3d CD Retrieve a short from an array CX // short ajShort3dGet(const AjPShort3d thys, ajuint elem1, ajuint elem2, ajuint elem3) { AjPShort2d t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad short array index [%d][%d][%d]\n",elem1, elem2,elem3); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][][]\n",elem1); return ajShort2dGet(t,elem2,elem3); } ID ajShort3dPut TY public MO ajarr LB core XX DE Load a short 3d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPShort3d* PD Pointer to the short array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX PN [5] PA r v short PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPShort3d CD Load a short array element CX // AjBool ajShort3dPut(AjPShort3d *thys, ajuint elem1, ajuint elem2, ajuint elem3, short v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d][%d]\n",elem1, elem2,elem3); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajShort2dNew(); return ajShort2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); } arrShort3dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajShort2dNew(); ajShort2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); return ajTrue; } ID arrShort3dResize TY static MO ajarr LB core XX DE Resize a short array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPShort3d* PD Pointer to the short array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrShort3dResize(AjPShort3d *thys, ajuint size) { AjPShort3d nthys; AjPShort3d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize short array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajShort3dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPShort2d)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPShort2d)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajShort2dDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr3dResize++; return ajTrue; } ID ajShort3dLen TY public MO ajarr LB core XX DE Get lengths of 3d short array XX PN [1] PA r thys const AjPShort3d PD Pointer to the short array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX PN [4] PA w len3 ajuint* PD Length of 3rd dim PX RT void RD RX // void ajShort3dLen(const AjPShort3d thys, ajuint* len1, ajuint* len2, ajuint* len3) { AjPShort2d t2; AjPShort t1; ajuint i; ajuint j; ajuint v; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) *len2 = (*len2 > t2->Len) ? *len2 : t2->Len; *len3 = 0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) { v = t2->Len; for(j=0;jPtr[j])) *len3 = (*len3 > t1->Len) ? *len3 : t1->Len; } return; } ID ajShort3dShort TY public MO ajarr LB core XX DE Convert AjPShort3d to short XX PN [1] PA r thys const AjPShort3d PD Pointer to the short array. PX RT short*** RD converted values. RX CA cast CT AjPShort3d CD Retrieve internal pointer CX // short*** ajShort3dShort(const AjPShort3d thys) { AjPShort2d t2 = NULL; AjPShort t1 = NULL; short ***array; ajuint d1; ajuint d2; ajuint d3; ajuint i; ajuint j; ajShort3dLen(thys,&d1,&d2,&d3); AJCNEW0(array,d1); for(i=0;iPtr[i]; for(j=0;j=t2->Len) continue; if((t1=t2->Ptr[j])) memmove(array[i][j],t1->Ptr, t1->Len*sizeof(short)); } } } return array; } ID ajLong2dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 2D ajlong arrays. XX RT AjPLong2d RD Pointer to an empty ajlong array structure RX CA new CT AjPLong2d CD Default constructor CX // AjPLong2d ajLong2dNew(void) { AjPLong2d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPLong)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; return thys; } ID ajLong2dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPLong2d RD Pointer to an empty ajlong 2d array struct of RD specified size. RX CA new CT AjPLong2d CD Constructor with reserved size CX // AjPLong2d ajLong2dNewRes(ajuint size) { AjPLong2d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPLong)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; return thys; } ID ajLong2dDel TY public MO ajarr LB core XX DE Default destructor for AJAX ajlong arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPLong2d* PD Pointer to the ajlong array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPLong2d CD Default destructor CX // void ajLong2dDel(AjPLong2d *thys) { ajint i; if(!thys || !*thys) return; for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajLongDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr2dFreeCount++; return; } ID ajLong2dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 2d ajlong array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPLong2d PD Pointer to the ajlong array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX RT ajlong RD contents of array element RX CA cast CT AjPLong2d CD Retrieve a ajlong from an array CX // ajlong ajLong2dGet(const AjPLong2d thys, ajuint elem1, ajuint elem2) { AjPLong t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad ajlong array index [%d][%d]\n",elem1, elem2); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][]\n",elem1); return ajLongGet(t,elem2); } ID ajLong2dPut TY public MO ajarr LB core XX DE Load a ajlong 2d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPLong2d* PD Pointer to the ajlong array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r v ajlong PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPLong2d CD Load a ajlong array element CX // AjBool ajLong2dPut(AjPLong2d *thys, ajuint elem1, ajuint elem2, ajlong v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d]\n",elem1, elem2); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajLongNew(); return ajLongPut(&(*thys)->Ptr[elem1],elem2,v); } arrLong2dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajLongNew(); ajLongPut(&(*thys)->Ptr[elem1],elem2,v); return ajTrue; } ID arrLong2dResize TY static MO ajarr LB core XX DE Resize a ajlong array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPLong2d* PD Pointer to the ajlong array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrLong2dResize(AjPLong2d *thys, ajuint size) { AjPLong2d nthys; AjPLong2d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize ajlong array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajLong2dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPLong)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPLong)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajLongDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr2dResize++; return ajTrue; } ID ajLong2dLen TY public MO ajarr LB core XX DE Get lengths of 2d ajlong array XX PN [1] PA r thys const AjPLong2d PD Pointer to the ajlong array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX RT void RD RX // void ajLong2dLen(const AjPLong2d thys, ajuint* len1, ajuint* len2) { AjPLong t; ajuint i; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t=thys->Ptr[i])) *len2 = (*len2 > t->Len) ? *len2 : t->Len; return; } ID ajLong2dLong TY public MO ajarr LB core XX DE Convert AjPLong2d to ajlong XX PN [1] PA r thys const AjPLong2d PD Pointer to the ajlong array. PX RT ajlong** RD converted values. RX CA cast CT AjPLong2d CD Retrieve internal pointer CX // ajlong** ajLong2dLong(const AjPLong2d thys) { AjPLong t = NULL; ajlong **array; ajuint d1; ajuint d2; ajuint i; ajLong2dLen(thys,&d1,&d2); AJCNEW(array,d1); for(i=0;iPtr[i])) memmove(array[i],t->Ptr,t->Len*sizeof(ajlong)); } return array; } ID ajLong3dNew TY public MO ajarr LB core XX DE Default constructor for empty AJAX 3D ajlong arrays. XX RT AjPLong3d RD Pointer to an empty ajlong array structure RX CA new CT AjPLong3d CD Default constructor CX // AjPLong3d ajLong3dNew(void) { AjPLong3d thys; ajuint i; AJNEW0(thys); thys->Ptr = AJALLOC0(RESERVED_SIZE*sizeof(AjPLong2d)); thys->Len = 0; thys->Res = RESERVED_SIZE; for(i=0;iPtr[i] = NULL; return thys; } ID ajLong3dNewRes TY public MO ajarr LB core XX DE Constructor given an initial reserved size. XX PN [1] PA r size ajuint PD Reserved size 1st dim PX RT AjPLong3d RD Pointer to an empty ajlong 3d array struct of RD specified size. RX CA new CT AjPLong3d CD Constructor with reserved size CX // AjPLong3d ajLong3dNewRes(ajuint size) { AjPLong3d thys; ajuint i; size = ajRound(size,RESERVED_SIZE); AJNEW0(thys); thys->Ptr = AJALLOC0(size*sizeof(AjPLong2d)); thys->Len = 0; thys->Res = size; for(i=0;iPtr[i] = NULL; return thys; } ID ajLong3dDel TY public MO ajarr LB core XX DE Default destructor for AJAX ajlong arrays. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA d thys AjPLong3d* PD Pointer to the ajlong array to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPLong3d CD Default destructor CX // void ajLong3dDel(AjPLong3d *thys) { ajint i; if(!thys || !*thys) return; for(i=(*thys)->Res-1;i>-1;--i) if((*thys)->Ptr[i]) ajLong2dDel(&((*thys)->Ptr[i])); AJFREE((*thys)->Ptr); AJFREE(*thys); *thys = NULL; arr3dFreeCount++; return; } ID ajLong3dGet TY public MO ajarr LB core XX DE Retrieve an element from an AJAX 3d ajlong array. DE DE If the given array is a NULL pointer, simply returns. XX PN [1] PA r thys const AjPLong3d PD Pointer to the ajlong array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX RT ajlong RD contents of array element RX CA cast CT AjPLong3d CD Retrieve a ajlong from an array CX // ajlong ajLong3dGet(const AjPLong3d thys, ajuint elem1, ajuint elem2, ajuint elem3) { AjPLong2d t; if(!thys || elem1>=thys->Len) ajErr("Attempt to access bad ajlong array index [%d][%d][%d]\n",elem1, elem2,elem3); t = thys->Ptr[elem1]; if(!t) ajErr("Attempt to access bad 1st dimension [%d][][]\n",elem1); return ajLong2dGet(t,elem2,elem3); } ID ajLong3dPut TY public MO ajarr LB core XX DE Load a ajlong 3d array element. DE DE If the given array is a NULL pointer an error is generated. DE If the array is of insufficient size then the array is extended. DE Negative indices generate an error. XX PN [1] PA w thys AjPLong3d* PD Pointer to the ajlong array. PX PN [2] PA r elem1 ajuint PD array element. PX PN [3] PA r elem2 ajuint PD array element. PX PN [4] PA r elem3 ajuint PD array element. PX PN [5] PA r v ajlong PD value to load. PX RT AjBool RD true if any array was extended. RX CA modify CT AjPLong3d CD Load a ajlong array element CX // AjBool ajLong3dPut(AjPLong3d *thys, ajuint elem1, ajuint elem2, ajuint elem3, ajlong v) { if(!thys || !*thys) ajErr("Attempt to write to illegal array value [%d][%d][%d]\n",elem1, elem2,elem3); if(elem1 < (*thys)->Res) { if(elem1>=(*thys)->Len) (*thys)->Len = elem1+1; if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajLong2dNew(); return ajLong2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); } arrLong3dResize(thys, elem1); if(!(*thys)->Ptr[elem1]) (*thys)->Ptr[elem1] = ajLong2dNew(); ajLong2dPut(&(*thys)->Ptr[elem1],elem2,elem3,v); return ajTrue; } ID arrLong3dResize TY static MO ajarr LB core XX DE Resize a ajlong array. DE DE If the given array is a NULL pointer an error is generated. DE Negative indices generate an error. XX PN [1] PA w thys AjPLong3d* PD Pointer to the ajlong array. PX PN [2] PA r size ajuint PD new size. PX RT AjBool RD true if the array was extended. RX // static AjBool arrLong3dResize(AjPLong3d *thys, ajuint size) { AjPLong3d nthys; AjPLong3d p = NULL; ajuint s; ajuint clen; ajuint limit; ajuint i; if(!thys || !*thys) ajErr("Illegal attempt to resize ajlong array"); clen = ajRound((*thys)->Len-1,RESERVED_SIZE); s = ajRound(size+1,RESERVED_SIZE); if(s <= clen) return ajFalse; /*ajDebug("ajLong3dResize %d (%d) -> %d (%d)\n", (*thys)->Len, clen, size, s);*/ p = *thys; AJNEW0(nthys); nthys->Ptr = AJALLOC0(s*sizeof(AjPLong2d)); nthys->Res = s; if(size < p->Len) limit = size+1; else limit = p->Len; memmove(nthys->Ptr,p->Ptr,limit*sizeof(AjPLong2d)); i = nthys->Len = size+1; for(;iRes;++i) if(p->Ptr[i]) ajLong2dDel(&p->Ptr[i]); AJFREE(p->Ptr); AJFREE(p); *thys = nthys; arr3dResize++; return ajTrue; } ID ajLong3dLen TY public MO ajarr LB core XX DE Get lengths of 3d ajlong array XX PN [1] PA r thys const AjPLong3d PD Pointer to the ajlong array. PX PN [2] PA w len1 ajuint* PD Length of 1st dim PX PN [3] PA w len2 ajuint* PD Length of 2nd dim PX PN [4] PA w len3 ajuint* PD Length of 3rd dim PX RT void RD RX // void ajLong3dLen(const AjPLong3d thys, ajuint* len1, ajuint* len2, ajuint* len3) { AjPLong2d t2; AjPLong t1; ajuint i; ajuint j; ajuint v; *len1 = thys->Len; *len2 = 0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) *len2 = (*len2 > t2->Len) ? *len2 : t2->Len; *len3 = 0; for(i=0;i<*len1;++i) if((t2=thys->Ptr[i])) { v = t2->Len; for(j=0;jPtr[j])) *len3 = (*len3 > t1->Len) ? *len3 : t1->Len; } return; } ID ajLong3dLong TY public MO ajarr LB core XX DE Convert AjPLong3d to ajlong XX PN [1] PA r thys const AjPLong3d PD Pointer to the ajlong array. PX RT ajlong*** RD converted values. RX CA cast CT AjPLong3d CD Retrieve internal pointer CX // ajlong*** ajLong3dLong(const AjPLong3d thys) { AjPLong2d t2 = NULL; AjPLong t1 = NULL; ajlong ***array; ajuint d1; ajuint d2; ajuint d3; ajuint i; ajuint j; ajLong3dLen(thys,&d1,&d2,&d3); AJCNEW0(array,d1); for(i=0;iPtr[i]; for(j=0;j=t2->Len) continue; if((t1=t2->Ptr[j])) memmove(array[i][j],t1->Ptr, t1->Len*sizeof(ajlong)); } } } return array; } ID ajArrExit TY public MO ajarr LB core XX DE Cleanup of array handling internals, and debug report of memory use XX RT void RD RX // void ajArrExit(void) { ajRegFree(&floatRegNum); ajDebug("Array usage (bytes): %Ld allocated, %Ld freed, %Ld in use\n", arrAlloc, arrFree, (arrAlloc - arrFree)); ajDebug("Array usage (number): %Ld allocated, %Ld freed, %Ld resized," " %Ld in use\n", arrTotal, arrFreeCount, arrResize, arrCount); ajDebug("Array usage 2D (bytes): %Ld allocated, %Ld freed, %Ld in use\n", arr2dAlloc, arr2dFree, (arr2dAlloc - arr2dFree)); ajDebug("Array usage 2D (number): %Ld allocated, %Ld freed, %Ld resized," " %Ld in use\n", arr2dTotal, arr2dFreeCount, arr2dResize, arr2dCount); ajDebug("Array usage 3D (bytes): %Ld allocated, %Ld freed, %Ld in use\n", arr3dAlloc, arr3dFree, (arr3dAlloc - arr3dFree)); ajDebug("Array usage 3D (number): %Ld allocated, %Ld freed, %Ld resized," " %Ld in use\n", arr3dTotal, arr3dFreeCount, arr3dResize, arr3dCount); return; } ID ajAssemNew TY public MO ajassem LB core XX DE Assem data constructor XX RT AjPAssem RD New object RX // AjPAssem ajAssemNew(void) { AjPAssem ret; AJNEW0(ret); ret->Contigs = ajTablestrNewCaseConst(16); ret->ContigsIgnored = ajTablestrNew(10); ret->ContigsOrder = ajListNew(); ret->Reads = ajListNew(); ret->Readgroups = ajTablestrNewConst(16); ajTableSetDestroyvalue(ret->Readgroups, (void(*)(void**))&assemReadgroupDel); return ret; } ID ajAssemDel TY public MO ajassem LB core XX DE Assem data destructor XX PN [1] PA d Passem AjPAssem* PD Assem data object to delete PX RT void RD RX // void ajAssemDel(AjPAssem *Passem) { AjPAssem assem = NULL; AjPAssemContig contig = NULL; AjPAssemRead r = NULL; AjPAssemTag tag = NULL; AjPAssemContig* contigs = NULL; ajint i = 0; if(!Passem) return; if(!(*Passem)) return; assem = *Passem; ajStrDel(&assem->Id); ajStrDel(&assem->Db); ajStrDel(&assem->Setdb); ajStrDel(&assem->Full); ajStrDel(&assem->Qry); ajStrDel(&assem->Formatstr); ajStrDel(&assem->Filename); ajStrDel(&assem->Textptr); ajSeqBamHeaderDel(&assem->BamHeader); while(ajListPop(assem->Reads, (void**) &r)) { ajAssemreadDel(&r); } ajListFree(&assem->Reads); ajTableToarrayValues(assem->Contigs, (void***)&contigs); while (contigs[i]) /* contigs */ { contig = contigs[i++]; ajStrDel(&contig->Name); ajStrDel(&contig->Consensus); ajStrDel(&contig->ConsensusQ); ajStrDel(&contig->AssemblyID); ajStrDel(&contig->URI); ajStrDel(&contig->Species); ajStrDel(&contig->MD5); while(ajListPop(contig->Tags, (void**) &tag)) { ajStrDel(&tag->Comment); ajStrDel(&tag->Name); AJFREE(tag); } ajListFree(&contig->Tags); AJFREE(contig); } AJFREE(assem->ContigArray); ajTableDel(&assem->Contigs); ajTableFree(&assem->ContigsIgnored); ajListFree(&assem->ContigsOrder); ajTableDel(&assem->Readgroups); AJFREE(*Passem); AJFREE(contigs); *Passem = NULL; return; } ID ajAssemGetId TY public MO ajassem LB core XX DE Return the identifier XX PN [1] PA r assem const AjPAssem PD Assembly PX RT const AjPStr RD Returned id RX // const AjPStr ajAssemGetId(const AjPAssem assem) { return assem->Id; } ID ajAssemGetQryC TY public MO ajassem LB core XX DE Returns the query string of an assembly data object. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r assem const AjPAssem PD Assem data object. PX RT const char* RD Query as a character string. RX // const char* ajAssemGetQryC(const AjPAssem assem) { return MAJSTRGETPTR(ajAssemGetQryS(assem)); } ID ajAssemGetQryS TY public MO ajassem LB core XX DE Returns the query string of an assembly data object. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r assem const AjPAssem PD Assem data object. PX RT const AjPStr RD Query as a string. RX // const AjPStr ajAssemGetQryS(const AjPAssem assem) { ajDebug("ajAssemGetQryS '%S'\n", assem->Qry); if(ajStrGetLen(assem->Qry)) return assem->Qry; assemMakeQry(assem, &assemTempQry); return assemTempQry; } ID ajAssemGetSortorderC TY public MO ajassem LB core XX DE Returns sort-order name of the given assembly XX PN [1] PA r assem const AjPAssem PD assembly PX RT const char* RD sort-order name RX // const char* ajAssemGetSortorderC(const AjPAssem assem) { if(!assem) return NULL; return sortorderNames[assem->SO]; } ID assemMakeQry TY static MO ajassem LB core XX DE Sets the query for an assembly data object. XX PN [1] PA r thys const AjPAssem PD Assem data object PX PN [2] PA w qry AjPStr* PD Query string in full PX RT void RD RX // static void assemMakeQry(const AjPAssem thys, AjPStr* qry) { ajDebug("assemMakeQry (Id <%S> Formatstr <%S> Db <%S> " "Filename <%S>)\n", thys->Id, thys->Formatstr, thys->Db, thys->Filename); /* ajAssemTrace(thys); */ if(ajStrGetLen(thys->Db)) ajFmtPrintS(qry, "%S-id:%S", thys->Db, thys->Id); else { ajFmtPrintS(qry, "%S::%S:%S", thys->Formatstr, thys->Filename,thys->Id); } ajDebug(" result: <%S>\n", *qry); return; } ID ajAssemClear TY public MO ajassem LB core XX DE Resets all data for an assembly data object so that it can be reused. XX PN [1] PA u assem AjPAssem PD assem data PX RT void RD RX // void ajAssemClear(AjPAssem assem) { AjPAssemRead r = NULL; if(MAJSTRGETLEN(assem->Id)) ajStrSetClear(&assem->Id); if(MAJSTRGETLEN(assem->Db)) ajStrSetClear(&assem->Db); if(MAJSTRGETLEN(assem->Setdb)) ajStrSetClear(&assem->Setdb); if(MAJSTRGETLEN(assem->Full)) ajStrSetClear(&assem->Full); if(MAJSTRGETLEN(assem->Qry)) ajStrSetClear(&assem->Qry); if(MAJSTRGETLEN(assem->Formatstr)) ajStrSetClear(&assem->Formatstr); if(MAJSTRGETLEN(assem->Filename)) ajStrSetClear(&assem->Filename); ajStrDel(&assem->Textptr); while(ajListPop(assem->Reads, (void**) &r)) { ajAssemreadDel(&r); } assem->Count = 0; assem->Fpos = 0L; assem->Format = 0; assem->Hasdata = ajFalse; return; } ID ajAssemReset TY public MO ajassem LB core XX DE Resets read data for an assembly data object so that it can be reused. XX PN [1] PA u assem AjPAssem PD assem data PX RT void RD RX // void ajAssemReset(AjPAssem assem) { AjPAssemRead r = NULL; while(ajListPop(assem->Reads, (void**) &r)) { ajAssemreadDel(&r); } return; } ID ajAssemSetContigattributes TY public MO ajassem LB core XX DE Set contig attributes uri, md5, species and assemblyid DE using the values provided in contigtags table. XX PN [1] PA u assem AjPAssem PD Assembly object PX PN [2] PA r tags const AjPTable PD Table of tables for contig tags. PD Main table have an entry for each contig. PD Sub-tables include tag values for a contig. PX RT void RD RX // void ajAssemSetContigattributes(AjPAssem assem, const AjPTable tags) { char** refseqnames=NULL; char* refseq; AjPTable* tagtables=NULL; AjPAssemContig c = NULL; AjPTable hline; AjPTable contigs; int i=0; if(tags == 0 || assem==0) return; ajDebug("assemSetContigAttributes\n"); contigs = assem->Contigs; ajTableToarrayKeysValues(tags, (void***)&refseqnames, (void***)&tagtables); while(refseqnames[i]) { refseq = refseqnames[i]; hline = tagtables[i++]; c = ajTableFetchmodC(contigs, refseq); if(!c) { ajDebug("ref seq '%s' listed in contigstags table " "but not in actual contig list of the assembly", refseq); continue; } if(ajTableFetchC(hline,"UR")) c->URI = ajStrNewC(ajTableFetchmodC(hline,"UR")); if(ajTableFetchC(hline,"M5")) c->MD5 = ajStrNewC(ajTableFetchmodC(hline,"M5")); if(ajTableFetchC(hline,"SP")) c->Species = ajStrNewC(ajTableFetchmodC(hline,"SP")); if(ajTableFetchC(hline,"AS")) c->AssemblyID = ajStrNewC(ajTableFetchmodC(hline,"AS")); } AJFREE(refseqnames); AJFREE(tagtables); return; } ID ajAssemSetReadgroups TY public MO ajassem LB core XX DE Set assembly readgroups using the values in readgrouptags table XX PN [1] PA u assem AjPAssem PD Assembly object PX PN [2] PA r tags const AjPTable PD Table of tables for readgroup tags. PD Main table have an entry for each read-group. PD Sub-tables include tag values for a read-group. PX RT void RD RX // void ajAssemSetReadgroups(AjPAssem assem, const AjPTable tags) { char** rgids=NULL; AjPTable* tagtables=NULL; AjPAssemReadgroup rg = NULL; AjPTable hline = NULL; AjPStr tmp = NULL; int i=0; if(tags == 0 || assem==0) return; ajDebug("assemSetReadgroups\n"); ajTableToarrayKeysValues(tags, (void***)&rgids, (void***)&tagtables); while(rgids[i]) { AJNEW0(rg); rg->ID = ajStrNewC(rgids[i]); hline = tagtables[i++]; if(ajTableFetchC(hline,"CN")) rg->CN = ajStrNewC(ajTableFetchmodC(hline,"CN")); if(ajTableFetchC(hline,"DS")) rg->Desc = ajStrNewC(ajTableFetchmodC(hline,"DS")); if(ajTableFetchC(hline,"DT")) rg->Date = ajStrNewC(ajTableFetchmodC(hline,"DT")); if(ajTableFetchC(hline,"FO")) rg->FlowOrder = ajStrNewC(ajTableFetchmodC(hline,"FO")); if(ajTableFetchC(hline,"KS")) rg->KeySeq = ajStrNewC(ajTableFetchmodC(hline,"KS")); if(ajTableFetchC(hline,"LB")) rg->Library = ajStrNewC(ajTableFetchmodC(hline,"LB")); if(ajTableFetchC(hline,"PG")) rg->Programs = ajStrNewC(ajTableFetchmodC(hline,"PG")); if(ajTableFetchC(hline,"PI")) { tmp = ajStrNewC(ajTableFetchmodC(hline,"PI")); ajStrToInt(tmp, &rg->Isize); ajStrDel(&tmp); } if(ajTableFetchC(hline,"PL")) rg->Platform = ajAssemplatformGetType( ajTableFetchmodC(hline,"PL")); if(ajTableFetchC(hline,"PU")) rg->Unit = ajStrNewC(ajTableFetchmodC(hline,"PU")); if(ajTableFetchC(hline,"SM")) rg->Sample = ajStrNewC(ajTableFetchmodC(hline,"SM")); ajTablePut(assem->Readgroups, rg->ID, rg); } AJFREE(rgids); AJFREE(tagtables); return; } ID ajAssemreadDel TY public MO ajassem LB core XX DE Delete assembly read/alignment records XX PN [1] PA d Passemread AjPAssemRead* PD Assembly read object to delete PX RT void RD RX // void ajAssemreadDel(AjPAssemRead *Passemread) { AjPAssemRead r = NULL; AjPAssemTag tag = NULL; if(!Passemread) return; if(!(*Passemread)) return; r = *Passemread; ajStrDel(&r->File); ajStrDel(&r->Name); ajStrDel(&r->Cigar); ajStrDel(&r->Seq); ajStrDel(&r->SeqQ); ajStrDel(&r->Technology); ajStrDel(&r->Template); while(ajListPop(r->Tags, (void**) &tag)) { ajStrDel(&tag->Comment); ajStrDel(&tag->Name); AJFREE(tag); } ajListFree(&r->Tags); AJFREE(r); *Passemread = NULL; return; } ID ajAssemreadGetAlignmentend TY public MO ajassem LB core XX DE 1-based inclusive rightmost position of the clipped sequence, DE or 0 if read unmapped XX PN [1] PA r r const AjPAssemRead PD read PX RT ajint RD end position of the alignment on the reference sequence RX // ajint ajAssemreadGetAlignmentend(const AjPAssemRead r) { AjPCigar cigar = NULL; ajint ret=0; if(ajAssemreadGetFlagUnmapped(r)) return 0; cigar = ajCigarNewS(r->Cigar); ret = r->y1 + ajCigarGetReferenceLength(cigar); ajCigarDel(&cigar); return ret; } ID ajAssemreadGetAlignmentstart TY public MO ajassem LB core XX DE Return alignment start (1-based) XX PN [1] PA r r const AjPAssemRead PD read PX RT ajint RD start position of the alignment on the reference sequence RX // ajint ajAssemreadGetAlignmentstart(const AjPAssemRead r) { if(ajAssemreadGetFlagUnmapped(r)) return 0; if(r->Reversed) return r->y1; return r->x1; } ID ajAssemreadGetFlagFirstofpair TY public MO ajassem LB core XX DE Return whether the read is the first read in a pair XX PN [1] PA r r const AjPAssemRead PD read PX RT AjBool RD true if the read is the first read in a pair RX // AjBool ajAssemreadGetFlagFirstofpair(const AjPAssemRead r) { return (r->Flag & BAM_FREAD1) != 0; } ID ajAssemreadGetFlagNegativestrand TY public MO ajassem LB core XX DE Return strand of the query (false for forward; true for reverse strand) XX PN [1] PA r r const AjPAssemRead PD read PX RT AjBool RD true if the read is reverse strand RX // AjBool ajAssemreadGetFlagNegativestrand(const AjPAssemRead r) { return (r->Flag & BAM_FREVERSE) != 0; } ID ajAssemreadGetFlagPaired TY public MO ajassem LB core XX DE Return whether the read is paired in sequencing, DE no matter whether it is mapped in a pair XX PN [1] PA r r const AjPAssemRead PD read PX RT AjBool RD true if the read is paired RX // AjBool ajAssemreadGetFlagPaired(const AjPAssemRead r) { return (r->Flag & BAM_FPAIRED) != 0; } ID ajAssemreadGetFlagUnmapped TY public MO ajassem LB core XX DE Return whether the read is unmapped XX PN [1] PA r r const AjPAssemRead PD read PX RT AjBool RD true if the read is unmapped RX // AjBool ajAssemreadGetFlagUnmapped(const AjPAssemRead r) { return (r->Flag & BAM_FUNMAP) != 0; } ID ajAssemreadSetAlignblocks TY public MO ajassem LB core XX DE Returns blocks of the read sequence that have been aligned directly to the DE reference sequence. Clipped portions of the read and inserted and deleted DE bases (vs. the reference) are not represented in the alignment blocks. DE DE Results are stored in the AlignmentBlocks attribute for reuse. XX PN [1] PA u r AjPAssemRead PD read/alignment PD 8 PX RT AjPList RD list of alignment blocks RX // AjPList ajAssemreadSetAlignblocks(AjPAssemRead r) { AjPCigar cigar = NULL; AjPList alignmentBlocks=NULL; int readBase=0; int refBase=0; int i=0; int length=0; AjPAssemReadalignmentblock ablock=NULL; if (r->AlignmentBlocks != NULL) return r->AlignmentBlocks; /* todo: we should store the cigar object as part of the read? */ cigar = ajCigarNewS(r->Cigar); if (cigar == NULL) return NULL; alignmentBlocks = ajListNew(); readBase = 1; refBase = ajAssemreadGetAlignmentstart(r); for (i=0; in; i++) { switch (cigar->cigoperator[i]) { case 'H' : break; /* ignore hard clips */ case 'P' : break; /* ignore pads */ case 'S' : readBase += cigar->length[i]; break; /* soft clip read bases */ case 'N' : refBase += cigar->length[i]; break; /* reference skip */ case 'D' : refBase += cigar->length[i]; break; case 'I' : readBase += cigar->length[i]; break; case 'M' : case '=' : case 'X' : length = cigar->length[i]; AJNEW0(ablock); ablock->readStart = readBase; if(refBase<=0) ajErr("referenceBlockStart: %d is <= 0", refBase); ablock->referenceStart = refBase; ablock->length = length; ajListPushAppend(alignmentBlocks, ablock); ablock=NULL; readBase += length; refBase += length; break; default : ajErr("unrecognised cigar op: " + cigar->cigoperator[i]); } } ajCigarDel(&cigar); r->AlignmentBlocks = alignmentBlocks; return alignmentBlocks; } ID assemReadgroupDel TY static MO ajassem LB core XX DE Delete assembly read-group data XX PN [1] PA d Prg AjPAssemReadgroup* PD read-group object to delete PX RT void RD RX // static void assemReadgroupDel(AjPAssemReadgroup *Prg) { AjPAssemReadgroup rg = NULL; if(!Prg || !(*Prg)) return; rg = *Prg; ajStrDel(&rg->CN); ajStrDel(&rg->Date); ajStrDel(&rg->Desc); ajStrDel(&rg->FlowOrder); ajStrDel(&rg->ID); ajStrDel(&rg->KeySeq); ajStrDel(&rg->Library); ajStrDel(&rg->Programs); ajStrDel(&rg->Sample); ajStrDel(&rg->Unit); AJFREE(rg); *Prg = NULL; return; } ID ajAssemreadgroupGetPlatformname TY public MO ajassem LB core XX DE Returns platform name of the given read-group XX PN [1] PA r rg const AjPAssemReadgroup PD read group PX RT const char* RD platform name RX // const char* ajAssemreadgroupGetPlatformname(const AjPAssemReadgroup rg) { if(!rg) return NULL; return platfNames[rg->Platform]; } ID ajAssemplatformGetType TY public MO ajassem LB core XX DE Returns platform type enumeration for the given platform XX PN [1] PA r name const char* PD platform name PX RT AjEAssemPlatform RD platform type RX // AjEAssemPlatform ajAssemplatformGetType(const char* name) { int i=0; while(platfNames[i]) { if(ajCharCmpCase(platfNames[i], name)==0) return i; i++; } return 0; } ID ajAssemsortorderGetType TY public MO ajassem LB core XX DE Returns sort-order type for the given sort-order-name XX PN [1] PA r name const char* PD sort order name PX RT AjEAssemSortOrder RD sort order type RX // AjEAssemSortOrder ajAssemsortorderGetType(const char* name) { int i=0; if(!name) return 0; while(sortorderNames[i]) { if(ajCharCmpCase(sortorderNames[i], name)==0) return i; i++; } return 0; } ID ajAssemExit TY public MO ajassem LB core XX DE Cleans up assembly processing internal memory XX RT void RD RX // void ajAssemExit(void) { ajAsseminExit(); ajAssemoutExit(); return; } ID assemAccess TY list MO ajassemdb LB ajaxdb XX DE Functions to access each database or assembly access method XX // static AjOAssemAccess assemAccess[] = { /* Name AccessFunction FreeFunction Qlink Description Alias Entry Query All Chunk Padding */ {NULL, NULL, NULL, NULL, NULL, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE}, {NULL, NULL, NULL, NULL, NULL, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE} }; ID ajAssemdbInit TY public MO ajassemdb LB ajaxdb XX DE Initialise assembly database internals XX RT void RD RX // void ajAssemdbInit(void) { AjPTable table; ajuint i = 0; table = ajAssemaccessGetDb(); while(assemAccess[i].Name) { ajCallTableRegister(table, assemAccess[i].Name, (void*) &assemAccess[i]); i++; } return; } ID ajAssemdbPrintAccess TY public MO ajassemdb LB ajaxdb XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajAssemdbPrintAccess(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Assembly access methods\n"); ajFmtPrintF(outf, "# Name Alias Entry Query All Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "method {\n"); for(i=0; assemAccess[i].Name; i++) if(full || !assemAccess[i].Alias) ajFmtPrintF(outf, " %-10s %5B %5B %5B %5B \"%s\"\n", assemAccess[i].Name, assemAccess[i].Alias, assemAccess[i].Entry, assemAccess[i].Query, assemAccess[i].All, assemAccess[i].Desc); ajFmtPrintF(outf, "}\n\n"); return; } ID ajAssemdbExit TY public MO ajassemdb LB ajaxdb XX DE Cleans up assembly database processing internal memory XX RT void RD RX // void ajAssemdbExit(void) { return; } ID ajAsseminNew TY public MO ajassemread LB core XX DE Creates a new assembly input object. XX RT AjPAssemin RD New assembly input object. RX CA new CT AjPAssemin CD Default constructor CX // AjPAssemin ajAsseminNew(void) { AjPAssemin pthis; AJNEW0(pthis); pthis->Input = ajTextinNewDatatype(AJDATATYPE_ASSEMBLY); return pthis; } ID ajAsseminDel TY public MO ajassemread LB core XX DE Deletes an assembly input object. XX PN [1] PA d pthis AjPAssemin* PD Assembly input PX RT void RD RX CA delete CT AjPAssemin CD Default destructor CX // void ajAsseminDel(AjPAssemin* pthis) { AjPAssemin thys; AjPSeqBamBgzf gzfile = NULL; if(!pthis) return; if(!*pthis) return; thys = *pthis; ajDebug("ajAsseminDel called qry:'%S'\n", thys->Input->Qry); if(thys->BamInput) { gzfile = thys->Input->TextData; ajSeqBamBgzfClose(gzfile); } ajTextinDel(&thys->Input); AJFREE(*pthis); return; } ID ajAsseminClear TY public MO ajassemread LB core XX DE Clears an assembly input object back to "as new" condition, except DE for the query list which must be preserved. XX PN [1] PA w thys AjPAssemin PD Assembly input PX RT void RD RX CA modify CT AjPAssemin CD Resets ready for reuse. CX // void ajAsseminClear(AjPAssemin thys) { ajDebug("ajAsseminClear called\n"); if(!thys) return; ajTextinClear(thys->Input); thys->Loading = ajFalse; return; } ID ajAsseminQryC TY public MO ajassemread LB core XX DE Resets an assembly input object using a new Universal DE Query Address XX PN [1] PA u thys AjPAssemin PD Assembly input object. PX PN [2] PA r txt const char* PD Query PX RT void RD RX // void ajAsseminQryC(AjPAssemin thys, const char* txt) { ajAsseminClear(thys); ajStrAssignC(&thys->Input->Qry, txt); return; } ID ajAsseminQryS TY public MO ajassemread LB core XX DE Resets an assembly input object using a new Universal DE Query Address XX PN [1] PA u thys AjPAssemin PD Assembly input object. PX PN [2] PA r str const AjPStr PD Query PX RT void RD RX // void ajAsseminQryS(AjPAssemin thys, const AjPStr str) { ajAsseminClear(thys); ajStrAssignS(&thys->Input->Qry, str); return; } ID ajAsseminTrace TY public MO ajassemread LB core XX DE Debug calls to trace the data in an assembly input object. XX PN [1] PA r thys const AjPAssemin PD Assembly input object. PX RT void RD RX // void ajAsseminTrace(const AjPAssemin thys) { ajDebug("assembly input trace\n"); ajDebug("====================\n\n"); ajTextinTrace(thys->Input); return; } ID ajAsseminLoad TY public MO ajassemread LB core XX DE If the file is not yet open, calls asseminQryProcess to convert the query DE into an open file stream. DE DE Uses asseminLoad for the actual file reading. DE DE Returns the results in the AjPAssem object. XX PN [1] PA u assemin AjPAssemin PD assembly data input definitions PX PN [2] PA w assem AjPAssem PD assembly data returned. PX RT AjBool RD ajTrue on success. RX CA input CT AjPAssem CD Master assembly data input, calls specific functions for file access type and assembly data format. CX // AjBool ajAsseminLoad(AjPAssemin assemin, AjPAssem assem) { AjBool ret = ajFalse; AjPQueryList node = NULL; AjBool listdata = ajFalse; AjPTextin input = assemin->Input; AjPFilebuff buff = input->Filebuff; ajDebug("ajAsseminLoad '%F' EOF: %B " "records: %u dataread: %B datadone: %B\n", ajFilebuffGetFile(buff), ajFilebuffIsEof(buff), input->Records, input->Dataread, input->Datadone); if(buff) { /* (a) if file still open, keep reading */ ajDebug("ajAsseminLoad: input file '%F' still there, try again\n", assemin->Input->Filebuff->File); ret = asseminLoad(assemin, assem); ajDebug("ajAsseminLoad: open buffer qry: '%S' returns: %B\n", assemin->Input->Qry, ret); } else { /* (b) if we have a list, try the next query in the list */ if(ajListGetLength(assemin->Input->List)) { listdata = ajTrue; ajListPop(assemin->Input->List, (void**) &node); ajDebug("++pop from list '%S'\n", node->Qry); ajAsseminQryS(assemin, node->Qry); ajDebug("++SAVE ASSEMIN '%S' '%S' %d\n", assemin->Input->Qry, assemin->Input->Formatstr, assemin->Input->Format); asseminQryRestore(assemin, node); ajStrDel(&node->Qry); ajStrDel(&node->Formatstr); AJFREE(node); ajDebug("ajAsseminLoad: open list, try '%S'\n", assemin->Input->Qry); if(!asseminQryProcess(assemin, assem) && !ajListGetLength(assemin->Input->List)) return ajFalse; ret = asseminLoad(assemin, assem); ajDebug("ajAsseminLoad: list qry: '%S' returns: %B\n", assemin->Input->Qry, ret); } else { ajDebug("ajAsseminLoad: no file yet - test query '%S'\n", assemin->Input->Qry); /* (c) Must be a query - decode it */ if(!asseminQryProcess(assemin, assem) && !ajListGetLength(assemin->Input->List)) { return ajFalse; } if(ajListGetLength(assemin->Input->List)) /* could be a new list */ listdata = ajTrue; ret = asseminLoad(assemin, assem); ajDebug("ajAsseminLoad: new qry: '%S' returns: %B\n", assemin->Input->Qry, ret); } } /* Now read whatever we got */ while(!ret && ajListGetLength(assemin->Input->List)) { /* Failed, but we have a list still - keep trying it */ if(listdata) ajErr("Failed to read assembly data '%S'", assemin->Input->Qry); listdata = ajTrue; ajListPop(assemin->Input->List,(void**) &node); ajDebug("++try again: pop from list '%S'\n", node->Qry); ajAsseminQryS(assemin, node->Qry); ajDebug("++SAVE (AGAIN) ASSEMIN '%S' '%S' %d\n", assemin->Input->Qry, assemin->Input->Formatstr, assemin->Input->Format); asseminQryRestore(assemin, node); ajStrDel(&node->Qry); ajStrDel(&node->Formatstr); AJFREE(node); if(!asseminQryProcess(assemin, assem)) continue; ret = asseminLoad(assemin, assem); ajDebug("ajAsseminLoad: list retry qry: '%S' returns: %B\n", assemin->Input->Qry, ret); } if(!ret) { if(listdata) ajErr("Failed to read assembly data '%S'", assemin->Input->Qry); return ajFalse; } assemDefine(assem, assemin); return ajTrue; } ID assemQueryMatch TY static MO ajassemread LB core XX DE Compares an assembly data item to a query and returns true if they match. XX PN [1] PA r thys const AjPQuery PD query. PX PN [2] PA r assem const AjPAssem PD Assembly data. PX RT AjBool RD ajTrue if the assembly data matches the query. RX // static AjBool assemQueryMatch(const AjPQuery thys, const AjPAssem assem) { AjBool tested = ajFalse; AjIList iterfield = NULL; AjIList iter = NULL; AjPQueryField field = NULL; AjBool ok = ajFalse; const AjPAssemContig c = NULL; ajDebug("assemQueryMatch '%S' fields: %Lu Case %B Done %B\n", assem->Id, ajListGetLength(thys->QueryFields), thys->CaseId, thys->QryDone); if(!thys) /* no query to test, that's fine */ return ajTrue; if(thys->QryDone) /* do we need to test here? */ return ajTrue; /* test the query field(s) */ iterfield = ajListIterNewread(thys->QueryFields); while(!ajListIterDone(iterfield)) { field = ajListIterGet(iterfield); ajDebug(" field: '%S' Query: '%S'\n", field->Field, field->Wildquery); if(ajStrMatchC(field->Field, "id")) { ajDebug(" id test: %Lu\n", ajListGetLength(assem->ContigsOrder)); iter = ajListIterNewread(assem->ContigsOrder); while(!ajListIterDone(iter)) { c = ajListIterGet(iter); ajDebug("test case %B '%S'\n", thys->CaseId, c->Name); if(thys->CaseId) { if(ajStrMatchWildS(c->Name, field->Wildquery)) { ajListIterDel(&iterfield); ajListIterDel(&iter); return ajTrue; } } else { if(ajStrMatchWildCaseS(c->Name, field->Wildquery)) { ajListIterDel(&iterfield); ajListIterDel(&iter); return ajTrue; } } } ajListIterDel(&iter); tested = ajTrue; } } ajListIterDel(&iterfield); if(!tested) /* nothing to test, so accept it anyway */ { ajDebug(" no tests: assume OK\n"); return ajTrue; } ajDebug("result: %B\n", ok); return ok; } ID assemDefine TY static MO ajassemread LB core XX DE Make sure all assembly data object attributes are defined DE using values from the assembly input object if needed XX PN [1] PA w thys AjPAssem PD Assembly data returned. PX PN [2] PA u assemin AjPAssemin PD Assembly data input definitions PX RT AjBool RD ajTrue on success. RX // static AjBool assemDefine(AjPAssem thys, AjPAssemin assemin) { /* if values are missing in the assembly object, we can use defaults from assemin or calculate where possible */ /* assign the dbname if defined in the assemin object */ if(ajStrGetLen(assemin->Input->Db)) ajStrAssignS(&thys->Db, assemin->Input->Db); return ajTrue; } ID asseminLoadFmt TY static MO ajassemread LB core XX DE Tests whether assembly data can be read using the specified format. DE Then tests whether the assembly data matches assembly data query criteria DE and checks any specified type. Applies upper and lower case. XX PN [1] PA u assemin AjPAssemin PD Assembly data input object PX PN [2] PA w assem AjPAssem PD Assembly data object PX PN [3] PA r format ajuint PD input format code PX RT ajuint RD 0 if successful. RD 1 if the query match failed. RD 2 if the assembly data type failed RD 3 if it failed to read a assembly data RX // static ajuint asseminLoadFmt(AjPAssemin assemin, AjPAssem assem, ajuint format) { AjPTextin input = assemin->Input; AjPFilebuff buff = input->Filebuff; ajDebug("++asseminLoadFmt format %d (%s) '%S'\n", format, asseminFormatDef[format].Name, input->Qry); if(!input->Dataread) input->Records = 0; /* Calling funclist asseminFormatDef() */ if((*asseminFormatDef[format].Load)(assemin, assem)) { input->Dataread = ajTrue; ajDebug("asseminLoadFmt success with format %d (%s)\n", format, asseminFormatDef[format].Name); ajDebug("id: '%S'\n", assem->Id); input->Format = format; ajStrAssignC(&input->Formatstr, asseminFormatDef[format].Name); ajStrAssignC(&assem->Formatstr, asseminFormatDef[format].Name); ajStrAssignEmptyS(&assem->Db, input->Db); ajStrAssignS(&assem->Filename, input->Filename); if(assemQueryMatch(input->Query, assem)) { /* ajAsseminTrace(assemin); */ return FMT_OK; } ajDebug("query match failed, continuing ...\n"); ajAssemClear(assem); return FMT_NOMATCH; } else { ajDebug("Testing input buffer: IsBuff: %B Eof: %B\n", ajFilebuffIsBuffered(buff), ajFilebuffIsEof(buff)); if (!ajFilebuffIsBuffered(buff) && ajFilebuffIsEof(buff)) return FMT_EOF; ajFilebuffReset(buff); ajDebug("Format %d (%s) failed, file buffer reset by asseminLoadFmt\n", format, asseminFormatDef[format].Name); /* ajFilebuffTraceFull(assemin->Filebuff, 10, 10);*/ } ajDebug("++asseminLoadFmt failed - nothing read\n"); return FMT_FAIL; } ID asseminLoad TY static MO ajassemread LB core XX DE Given data in a assemin structure, tries to read everything needed DE using the specified format or by trial and error. XX PN [1] PA u assemin AjPAssemin PD Assembly data input object PX PN [2] PA w assem AjPAssem PD Assembly data object PX RT AjBool RD ajTrue on success RX // static AjBool asseminLoad(AjPAssemin assemin, AjPAssem assem) { ajuint i; ajuint istat = 0; ajuint jstat = 0; AjPTextin input = assemin->Input; AjPFilebuff buff = input->Filebuff; AjBool ok; AjPTextAccess textaccess = input->Query->TextAccess; AjPAssemAccess assemaccess = input->Query->Access; if(!input->Records) { ajAssemClear(assem); ajDebug("asseminLoad: cleared\n"); } if(input->Single && input->Count && !input->Datacount) { /* ** One assembly data item at a time is read. ** The first assembly data item was read by ACD ** for the following ones we need to reset the AjPAssemin ** ** Single is set by the access method */ ajDebug("asseminLoad: single access - count %d - call access" " routine again\n", input->Count); /* Calling funclist asseminAccess() */ if(textaccess) { if(!(*textaccess->Access)(input)) { ajDebug("asseminLoad: (*textaccess->Access)(assemin->Input) " "*failed*\n"); return ajFalse; } } if(assemaccess) { if(!(*assemaccess->Access)(assemin)) { ajDebug("asseminLoad: (*assemaccess->Access)(assemin) " "*failed*\n"); return ajFalse; } } buff = input->Filebuff; } ajDebug("asseminLoad: assemin format %d '%S'\n", input->Format, input->Formatstr); input->Count++; if(!input->Filebuff) return ajFalse; ok = ajFilebuffIsBuffered(input->Filebuff); while(ok) { /* skip blank lines */ ok = ajBuffreadLine(input->Filebuff, &asseminReadLine); if(!ajStrIsWhite(asseminReadLine)) { ajFilebuffClear(input->Filebuff,1); break; } } if(!input->Format) { /* no format specified, try all defaults */ /*regfile = ajFileIsFile(ajFilebuffGetFile(assemin->Input->Filebuff));*/ for(i = 1; asseminFormatDef[i].Name; i++) { if(!asseminFormatDef[i].Try) /* skip if Try is ajFalse */ continue; ajDebug("asseminLoad:try format %d (%s)\n", i, asseminFormatDef[i].Name); istat = asseminLoadFmt(assemin, assem, i); switch(istat) { case FMT_OK: ajDebug("++asseminLoad OK, set format %d\n", input->Format); assemDefine(assem, assemin); return ajTrue; case FMT_BADTYPE: ajDebug("asseminload: (a1) " "asseminLoadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("asseminLoad: (b1) " "asseminLoadFmt stat == FAIL *failed*\n"); break; /* we can try next format */ case FMT_NOMATCH: ajDebug("asseminLoad: (c1) " "asseminLoadFmt stat==NOMATCH try again\n"); break; case FMT_EOF: ajDebug("asseminLoad: (d1) " "asseminLoadFmt stat == EOF *failed*\n"); return ajFalse; /* EOF and unbuffered */ case FMT_EMPTY: ajWarn("assembly data '%S' has zero length, ignored", ajAssemGetQryS(assem)); ajDebug("asseminLoad: (e1) " "asseminLoadFmt stat==EMPTY try again\n"); break; default: ajDebug("unknown code %d from asseminLoadFmt\n", stat); } ajAssemClear(assem); if(input->Format) break; /* we read something */ ajFilebuffTrace(input->Filebuff); } if(!input->Format) { /* all default formats failed, give up */ ajDebug("asseminLoad:all default formats failed, give up\n"); return ajFalse; } ajDebug("++asseminLoad set format %d\n", input->Format); } else { /* one format specified */ ajDebug("asseminLoad: one format specified\n"); ajFilebuffSetUnbuffered(input->Filebuff); ajDebug("++asseminLoad known format %d\n", input->Format); istat = asseminLoadFmt(assemin, assem, input->Format); switch(istat) { case FMT_OK: assemDefine(assem, assemin); return ajTrue; case FMT_BADTYPE: ajDebug("asseminLoad: (a2) " "asseminLoadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("asseminLoad: (b2) " "asseminLoadFmt stat == FAIL *failed*\n"); return ajFalse; case FMT_NOMATCH: ajDebug("asseminLoad: (c2) " "asseminLoadFmt stat == NOMATCH *try again*\n"); break; case FMT_EOF: ajDebug("asseminLoad: (d2) " "asseminLoadFmt stat == EOF *try again*\n"); if(assemin->Input->Records) ajErr("Error reading file '%F' with format '%s': " "end-of-file before end of data " "(read %u records)", ajFilebuffGetFile(input->Filebuff), asseminFormatDef[input->Format].Name, input->Records); break; /* simply end-of-file */ case FMT_EMPTY: ajWarn("assmebly data '%S' has zero length, ignored", ajAssemGetQryS(assem)); ajDebug("asseminLoad: (e2) " "asseminLoadFmt stat == EMPTY *try again*\n"); break; default: ajDebug("unknown code %d from asseminLoadFmt\n", stat); } ajAssemClear(assem); /* 1 : read, failed to match id/acc/query */ } /* failed - probably entry/accession query failed. Can we try again? */ ajDebug("asseminLoad failed - try again with format %d '%s' code %d\n", input->Format, asseminFormatDef[input->Format].Name, istat); ajDebug("Search:%B Chunk:%B Data:%x ajFileBuffEmpty:%B\n", input->Search, input->ChunkEntries, input->TextData, ajFilebuffIsEmpty(buff)); if(ajFilebuffIsEmpty(buff) && input->ChunkEntries) { if(textaccess && !(*textaccess->Access)(input)) return ajFalse; else if(assemaccess && !(*assemaccess->Access)(assemin)) return ajFalse; buff = input->Filebuff; } /* need to check end-of-file to avoid repeats */ while(input->Search && (input->TextData || !ajFilebuffIsEmpty(buff))) { jstat = asseminLoadFmt(assemin, assem, input->Format); switch(jstat) { case FMT_OK: assemDefine(assem, assemin); return ajTrue; case FMT_BADTYPE: ajDebug("asseminLoad: (a3) " "asseminLoadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("asseminLoad: (b3) " "asseminLoadFmt stat == FAIL *failed*\n"); return ajFalse; case FMT_NOMATCH: ajDebug("asseminLoad: (c3) " "asseminLoadFmt stat == NOMATCH *try again*\n"); break; case FMT_EOF: ajDebug("asseminLoad: (d3) " "asseminLoadFmt stat == EOF *failed*\n"); return ajFalse; /* we already tried again */ case FMT_EMPTY: if(istat != FMT_EMPTY) ajWarn("assmebly data '%S' has zero length, ignored", ajAssemGetQryS(assem)); ajDebug("asseminLoad: (e3) " "asseminLoadFmt stat == EMPTY *try again*\n"); break; default: ajDebug("unknown code %d from asseminLoadFmt\n", stat); } ajAssemClear(assem); /* 1 : read, failed to match id/acc/query */ } if(input->Format) ajDebug("asseminLoad: *failed* to read assembly data %S " "using format %s\n", input->Qry, asseminFormatDef[input->Format].Name); else ajDebug("asseminLoad: *failed* to read assembly data %S " "using any format\n", assemin->Input->Qry); return ajFalse; } ID asseminReadBamHeader TY static MO ajassemread LB core XX DE Read header information from BAM files XX PN [1] PA u assemin AjPAssemin PD Assembly input object PX PN [2] PA w assem AjPAssem PD Assembly object PX RT AjPSeqBamHeader RD BAM header object on success RX // static AjPSeqBamHeader asseminReadBamHeader(AjPAssemin assemin, AjPAssem assem) { AjPFilebuff buff = NULL; AjPFile infile = NULL; AjPAssemContig contig = NULL; AjPAssemContig* contigs = NULL; AjPSeqBamBgzf gzfile = NULL; AjPSeqBamHeader header = NULL; AjPBamIndex idx = NULL; char* targetname = NULL; ajint tid; ajint filestat = 0; ajint tid_new = 0; AjPTextin input; AjIList iterfield = NULL; AjPQuery qry = NULL; AjPQueryField field = NULL; AjBool ok = ajFalse; input = assemin->Input; buff = input->Filebuff; infile = ajFilebuffGetFile(buff); qry = input->Query; ajDebug("asseminReadBamHeader ...\n"); if(!infile) return NULL; ajFileTrace(infile); ajFilebuffTrace(buff); /* reset to beginning of file - ** has at least been tested for blank lines */ filestat = ajFileSeek(infile, 0L, SEEK_SET); if(filestat != 0) { ajDebug("asseminReadBamHeader rewind failed errno %d: %s\n", errno, strerror(errno)); return NULL; } assemin->BamInput = ajTrue; gzfile = ajSeqBamBgzfNew(ajFilebuffGetFileptr(buff), "r"); assemin->Input->TextData = gzfile; header = ajSeqBamHeaderRead(gzfile); if (!header) { ajDebug("ajSeqBamHeaderReadHeader failed\n"); ajSeqBamBgzfClose(gzfile); ajFileSeek(infile,filestat,0); ajFilebuffResetPos(buff); ajFileTrace(infile); ajFilebuffTrace(buff); return NULL; } input->Records++; if (ajListGetLength(qry->QueryFields)) assemin->BamIdx = ajBamIndexLoad(ajFileGetNameC(infile)); idx = assemin->BamIdx; /* read reference sequence names and lengths */ contigs = AJCALLOC(header->n_targets, sizeof(AjPAssemContig)); for(tid=0; tid < header->n_targets; tid++) { targetname = header->target_name[tid]; ajDebug("bam target[%d] '%s'\n", tid, targetname); if(idx) { ok = ajFalse; iterfield = ajListIterNewread(qry->QueryFields); while(!ajListIterDone(iterfield)) { field = ajListIterGet(iterfield); if(ajStrMatchC(field->Field, "id")) { if(qry->CaseId) { if(ajCharMatchWildS(targetname, field->Wildquery)) ok = ajTrue; } else { if(ajCharMatchWildCaseS(targetname, field->Wildquery)) ok = ajTrue; } } } ajListIterDel(&iterfield); if(!ok) continue; assem->Count = tid_new; tid_new++; ajBamFetch(gzfile, idx, tid, assemin->cbegin, assemin->cend, assem, asseminAppendAlignmentRecs); } AJNEW0(contig); contig->Name = ajStrNewC(targetname); ajDebug("reference sequence:%S\n", contig->Name); ajTablePut(assem->Contigs, contig->Name, contig); ajListPushAppend(assem->ContigsOrder, contig); contig->Length = header->target_len[tid]; ajDebug("ref seq length:%d\n", contig->Length); contigs[tid] = contig; } assem->SO = ajAssemsortorderGetType(ajSeqBamHeaderGetSortorder(header)); ajListToarray(assem->ContigsOrder, (void***)&assem->ContigArray); AJFREE(contigs); return header; } ID asseminLoadBam TY static MO ajassemread LB core XX DE Given data in an assembly structure, tries to read everything needed DE using binary alignment/map (BAM) format. XX PN [1] PA u assemin AjPAssemin PD Assembly input object PX PN [2] PA w assem AjPAssem PD Assembly object PX RT AjBool RD ajTrue on success RX // static AjBool asseminLoadBam(AjPAssemin assemin, AjPAssem assem) { AjPTextin input = assemin->Input; AjPFilebuff buff = input->Filebuff; AjPFile infile = NULL; AjPSeqBamBgzf gzfile = NULL; AjPSeqBam b = NULL; AjPSeqBamHeader header = NULL; AjPTable contigtags = NULL; AjPTable readgroups = NULL; ajuint iread = 0; ajuint maxread = 1; ajDebug("asseminLoadBam '%F' EOF: %B records: %u " "dataread: %B datadone: %B hasdata: %B\n", ajFilebuffGetFile(buff), ajFilebuffIsEof(buff), input->Records, input->Dataread, input->Datadone, assem->Hasdata); if(input->Datadone) { return ajFalse; } infile = ajFilebuffGetFile(buff); if(!infile) return ajFalse; ajFileTrace(infile); ajFilebuffTrace(buff); if(!input->Records) { ajAssemClear(assem); assem->BamHeader = asseminReadBamHeader(assemin, assem); if(!assem->BamHeader) return ajFalse; readgroups = ajSeqBamHeaderGetReadgroupTags(assem->BamHeader); ajAssemSetReadgroups(assem, readgroups); contigtags = ajSeqBamHeaderGetRefseqTags(assem->BamHeader); ajAssemSetContigattributes(assem, contigtags); assem->SO = ajAssemsortorderGetType( ajSeqBamHeaderGetSortorder(assem->BamHeader)); ajTableSetDestroyvalue(readgroups, (void(*)(void**))&ajTableDel); ajTableDel(&readgroups); ajTableSetDestroyvalue(contigtags, (void(*)(void**))&ajTableDel); ajTableDel(&contigtags); return ajTrue; } /* ** On later calls, read a chunk of data */ /* clear current chunk */ ajDebug("continue processing: maxread %u Datacount %u\n", maxread, input->Datacount); ajAssemReset(assem); assem->Hasdata = ajTrue; iread = 0; header = assem->BamHeader; gzfile = input->TextData; if(!assemin->BamIdx) { AJNEW0(b); /* todo: should store b in an iter obj so won't need to be allocated each time before reading a bam record here */ assem->rec = asseminReadBamAlignments(gzfile, header, b); AJFREE(b->data); AJFREE(b); if(assem->rec) { ajListPushAppend(assem->Reads, assem->rec); iread++; input->Records++; input->Datacount += iread; return ajTrue; } } ajSeqBamBgzfClose(gzfile); ajSeqBamHeaderDel(&assem->BamHeader); ajBamIndexDel(&assemin->BamIdx); input->Datadone = ajTrue; ajFilebuffClear(buff, -1); buff->File->End = ajTrue; input->TextData = NULL; return ajTrue; } ID asseminReadBamAlignments TY static MO ajassemread LB core XX DE Read BAM alignment records, when a query was not made DE DE TODO: should we update this function to append only the alignments in DE the query contigs? DE samtools simply rejects any queries when there is no index found XX PN [1] PA u gzfile AjPSeqBamBgzf PD Bam file handler PX PN [2] PA u header AjPSeqBamHeader PD Bam header PX PN [3] PA u b AjPSeqBam PD Bam data PX RT AjPAssemRead RD Read data object RX // static AjPAssemRead asseminReadBamAlignments(AjPSeqBamBgzf gzfile, AjPSeqBamHeader header, AjPSeqBam b) { ajint ret = 0; AjPAssemRead r = NULL; const char* library = NULL; const char* querylib = NULL; /* TODO: not yet implemented" */ /* next BAM record */ ret = ajSeqBamRead(gzfile, b); if(ret < -1) ajErr("seqReadBamAlignments truncated file return %d\n", ret); if(ret == -1) return NULL; if (querylib) { library = ajSeqBamGetLibrary(header, b); if(strcmp(library,querylib)) { return NULL; /* fixme: should try reading the next record until a match*/ } } r = asseminBamAlignmentRec2Emboss(b); return r; } ID asseminAppendAlignmentRecs TY static MO ajassemread LB core XX DE Append BAM alignment records to the assembly object after reformatting DE the alignment data. XX PN [1] PA u b AjPSeqBam PD BAM record PX PN [2] PA u data void* PD Assembly object PX RT AjBool RD True on success RX // static AjBool asseminAppendAlignmentRecs(AjPSeqBam b, void*data) { AjPAssemRead r = NULL; AjPAssem assem = NULL; assem = (AjPAssem)data; r = asseminBamAlignmentRec2Emboss(b); if(r->Rnext == r->Reference) { r->Reference = ajTableGetLength(assem->Contigs); r->Rnext = r->Reference; } else { r->Reference = ajTableGetLength(assem->Contigs); r->Rnext = -1; /* FIXME: proper mapping needed */ } ajListPushAppend(assem->Reads, r); return ajTrue; } ID asseminBamAlignmentRec2Emboss TY static MO ajassemread LB core XX DE Load alignment records into assembly object XX PN [1] PA u b AjPSeqBam PD BAM record PX RT AjPAssemRead RD Assembly object RX // static AjPAssemRead asseminBamAlignmentRec2Emboss(AjPSeqBam b) { AjPStr cigarstr = NULL; AjPStr seqstr = NULL; AjPStr qualstr = NULL; AjPStr tagstr = NULL; unsigned char dp; unsigned char* d; AjPAssemRead r = NULL; AjPSeqBamCore c = NULL; AjPAssemTag tag = NULL; ajuint i=0; ajuint dpos=0; ajint cigop=0; ajuint cigend=0; ajuint cigint=0; c = &b->core; ajDebug("rID: %d pos: %d bin: %hd mapQual: %d read_name_len: %d" " flag_nc: %hd cigar_len: %hd read_len: %d" " mate_rID: %d mate_pos: %d ins_size: %d\n", c->tid, c->pos, c->bin, c->qual, c->l_qname, c->flag, c->n_cigar, c->l_qseq, c->mtid, c->mpos, c->isize); ajDebug("l_aux: %d data_len:%d m_data:%d\n", b->l_aux, b->data_len, b->m_data); AJNEW0(r); r->Flag = c->flag; if(r->Flag & BAM_FREVERSE) { r->y1 = c->pos+1; r->Reversed = ajTrue; } else r->x1 = c->pos+1; r->Reference = c->tid; r->MapQ = c->qual; r->Tlen = c->isize; r->Rnext = c->mtid; r->Pnext = c->mpos+1; d = b->data; dpos = 0; ajStrAssignC(&r->Name, (const char*) &d[dpos]); ajDebug("read name: %p '%S'\n", dpos, r->Name); dpos += (c->l_qname); /* l_qname includes trailing null */ ajStrAssignC(&cigarstr, ""); for(i=0; i < c->n_cigar; i++) { memcpy(&cigint, &d[dpos], 4); cigop = cigint & BAM_CIGAR_MASK; cigend = cigint >> BAM_CIGAR_SHIFT; ajFmtPrintAppS(&cigarstr, "%u%c", cigend, cigarcode[cigop]); dpos += 4; } ajDebug("cigar: %p %S\n", dpos, cigarstr); ajStrAssignRef(&r->Cigar, cigarstr); ajStrDel(&cigarstr); ajStrAssignC(&seqstr, ""); for(i=0; i < (ajuint) c->l_qseq; i++) { ajStrAppendK(&seqstr, bam_nt16_rev_table[MAJSEQBAMSEQI(&d[dpos], i)]); } dpos += (c->l_qseq+1)/2; ajDebug("seq: %p '%S'\n", dpos, seqstr); ajStrAssignRef(&r->Seq, seqstr); ajStrDel(&seqstr); if(c->flag & BAM_FREVERSE) r->Reversed = ajTrue; if(d[dpos] == 0xFF) { ajDebug("qual: MISSING\n"); dpos += c->l_qseq; ajStrAssignK(&r->SeqQ, '*'); } else { qualstr = ajStrNewRes(c->l_qseq+1); for(i=0; i < (ajuint) c->l_qseq; i++) ajStrAppendK(&qualstr, (char)(33 + d[dpos++])); ajStrAssignRef(&r->SeqQ, qualstr); ajDebug("qual: %p %S\n", dpos, qualstr); ajStrDel(&qualstr); } /* tag values */ while (dpos < (ajuint) b->data_len) { ajStrAssignC(&tagstr, ""); AJNEW0(tag); ajStrAppendK(&tagstr, d[dpos++]); ajStrAppendK(&tagstr, d[dpos++]); ajStrAssignS(&tag->Name, tagstr); dp = d[dpos++]; tag->type = dp; ajStrSetClear(&tagstr); if (dp == 'Z' || dp == 'H') { ajFmtPrintAppS(&tagstr,"%s", &d[dpos]); while(d[dpos]) dpos++; dpos++; } else if (dp == 'f') { ajFmtPrintAppS(&tagstr,"%f", (float) *(&d[dpos])); dpos += 4; } else if (dp == 'd') { ajFmtPrintAppS(&tagstr,"%lf", (double) *(&d[dpos])); dpos += 8; } else if (dp == 'A') { ajStrAssignK(&tagstr, d[dpos++]); } else if (dp == 'c') { ajFmtPrintAppS(&tagstr,"%d", (ajint) (signed char) d[dpos++]); } else if (dp == 's') { ajFmtPrintAppS(&tagstr,"%hd", (ajshort) *(&d[dpos])); dpos += 2; } else if (dp == 'i') { ajFmtPrintAppS(&tagstr,"%d", (ajint) *(&d[dpos])); dpos += 4; } else if (dp == 'C') { ajFmtPrintAppS(&tagstr,"%u", (ajuint) d[dpos++]); } else if (dp == 'S') { ajFmtPrintAppS(&tagstr,"%hu", (ajushort) *(&d[dpos])); dpos += 2; } else if (dp == 'I') { ajFmtPrintAppS(&tagstr,"%u", (ajuint) d[dpos]); dpos += 4; } else { ajWarn("Unknown BAM aux type hex(%x) uint(%u) '%c'\n", (ajuint) dp, (ajuint) dp, dp); } ajStrAssignS(&tag->Comment, tagstr); if(!r->Tags) r->Tags = ajListNew(); ajListPushAppend(r->Tags, tag); ajDebug("tags: %p '%S'\n", dpos, tagstr); ajStrDel(&tagstr); } return r; } ID asseminLoadMaf TY static MO ajassemread LB core XX DE Reads assemblies in MAF format, used by the MIRA assembly software. XX PN [1] PA u assemin AjPAssemin PD Assembly input object PX PN [2] PA w assem AjPAssem PD Assembly object PX RT AjBool RD ajTrue on success RX // static AjBool asseminLoadMaf(AjPAssemin assemin, AjPAssem assem) { AjPTextin input = assemin->Input; AjPFilebuff buff = input->Filebuff; AjPFile infile = NULL; AjPStrTok handle = NULL; AjPStr keyword = NULL; AjPStr token = NULL; AjPAssemContig contig = NULL; AjPAssemRead r = NULL; AjPAssemTag tag = NULL; AjBool ok = ajTrue; ajlong fpos = 0; ajint contigid = -1; AjBool endofaReadorContig = ajFalse; ajuint iread = 0; ajuint maxread = 1000; ajDebug("asseminLoadMaf '%F' EOF: %B records: %u " "dataread: %B datadone: %B hasdata: %B\n", ajFilebuffGetFile(buff), ajFilebuffIsEof(buff), input->Records, input->Dataread, input->Datadone, assem->Hasdata); /* ajFilebuffTrace(buff); */ if(input->Datadone) { return ajFalse; } infile = ajFilebuffGetFile(buff); if(!infile) return ajFalse; /* ** On first call, we read the header only */ if(!input->Records) { ajAssemClear(assem); ajListPeekLast(assem->ContigsOrder, (void**)&contig); contigid = (ajuint) ajListGetLength(assem->ContigsOrder)-1; while (ajBuffreadLinePos(buff, &asseminReadLine, &fpos)) { input->Records++; ajStrTokenAssignC(&handle, asseminReadLine, " \n\r\t"); ajStrTokenNextParse(&handle, &keyword); ajStrTokenNextParse(&handle, &token); switch(CASE2(ajStrGetCharFirst(asseminReadLine), ajStrGetCharPos(asseminReadLine, 1))) { case CASE2('\\','\\'): ajDebug("start of read data for the contig\n"); // means end-of-contig-definition endofaReadorContig=ajTrue; break; case CASE2('C','O'): ajDebug("new contig: %S", token); AJNEW0(contig); ajStrAssignS(&contig->Name, token); ajTablePut(assem->Contigs, contig->Name, contig); ajListPushAppend(assem->ContigsOrder, contig); contigid = (ajuint) ajListGetLength(assem->ContigsOrder)-1; contig->Tags = ajListNew(); break; case CASE2('N', 'R'): ajStrToInt(token, &contig->Nreads); ajDebug("number of reads in contig %d\n", contig->Nreads); break; case CASE2('L','C'): ajStrToInt(token, &contig->Length); break; case CASE2('C','S'): ajStrAssignRef(&contig->Consensus, token); break; case CASE2('C','Q'): ajStrAssignS(&contig->ConsensusQ, token); break; case CASE2('C','T'): AJNEW0(tag); ajStrAssignS(&tag->Name, token); ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &tag->x1); ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &tag->y1); ajStrTokenRestParse(&handle, &token); ajStrTrimEndC(&token, "\r\n"); ajStrAssignS(&tag->Comment, token); ajListPushAppend(contig->Tags, tag); tag = NULL; break; default: ajWarn("unknown MAF contig tag: %S - %S\n", keyword, asseminReadLine); } ajStrDel(&keyword); ajStrDel(&token); ajStrDel(&asseminReadLine); ajStrTokenDel(&handle); if(endofaReadorContig) { return ajTrue; } } } ajAssemReset(assem); assem->Hasdata = ajTrue; ajListPeekLast(assem->ContigsOrder, (void**)&contig); contigid = (ajuint) ajListGetLength(assem->ContigsOrder)-1; ok = ajTrue; while (ok && (iread < maxread)) { ok = ajBuffreadLinePos(buff, &asseminReadLine, &fpos); if(!ok) break; ajStrTokenAssignC(&handle, asseminReadLine, " \n\r\t"); ajStrTokenNextParse(&handle, &keyword); ajStrTokenNextParse(&handle, &token); input->Records++; switch(CASE2(ajStrGetCharFirst(asseminReadLine), ajStrGetCharPos(asseminReadLine, 1))) { case CASE2('R','D'): AJNEW0(r); r->Flag = 0x0; ajStrAssignS(&r->Name, token); r->Reference = contigid; iread++; break; case CASE2('R','S'): ajStrAssignS(&r->Seq, token); break; case CASE2('R','Q'): ajStrAssignS(&r->SeqQ, token); break; case CASE2('T','N'): ajStrAssignS(&r->Template, token); break; case CASE2('D','I'): r->Direction = ajStrGetCharFirst(token); break; case CASE2 ('T', 'F'): ajStrToInt(token, &r->TemplateSizeMin); break; case CASE2('T','T'): ajStrToInt(token, &r->TemplateSizeMax); break; case CASE2('S', 'F'): ajStrAssignS(&r->File, token); break; case CASE2('C','L'): ajStrToInt(token, &r->ClipLeft); break; case CASE2('C','R'): ajStrToInt(token, &r->ClipRight); break; case CASE2('S','L'): ajStrToInt(token, &r->VectorLeft); break; case CASE2('S', 'R'): ajStrToInt(token, &r->VectorRight); break; case CASE2('Q','L'): ajStrToInt(token, &r->QualLeft); break; case CASE2('Q','R'): ajStrToInt(token, &r->QualRight); break; case CASE2('S','T'): ajStrAssignS(&r->Technology, token); break; case CASE2('E','R'): ajDebug("end of the read"); break; case CASE2('R','T'): AJNEW0(tag); ajStrAssignS(&tag->Name, token); ajStrTokenNextParseC(&handle, " \t", &token); ajStrToUint(token, &tag->x1); ajStrTokenNextParseC(&handle, " \t", &token); ajStrToUint(token, &tag->y1); if(!r->Tags) r->Tags = ajListNew(); ajListPushAppend(r->Tags, tag); tag = NULL; break; case CASE2('L','R'): { /* TODO: read length * MIRA writes LR lines for reads with more than 2000 bases */ break; } case CASE2('A','O'): { /* TODO */ break; } case CASE2('/','/'): ajDebug("end of the read data for the contig\n"); break; case CASE2('E','C'): ajDebug("end of contig\n"); break; case CASE2('A','T'): /* placement of the read */ ajStrToInt(token, &r->x1); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &r->y1); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &r->x2); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &r->y2); if (r->x1 > r->y1) r->Flag |= BAM_FREVERSE; assem->rec = r; endofaReadorContig=ajTrue; break; default: ajWarn("unknown MAF read tag: %S - %S\n", keyword, asseminReadLine); } if(endofaReadorContig) { if(assem->rec) { ajListPushAppend(assem->Reads, assem->rec); assem->rec = NULL; r = NULL; } input->Datacount += iread; ajStrDel(&keyword); ajStrDel(&token); ajStrTokenDel(&handle); return ajTrue; } } ajStrDel(&keyword); ajStrDel(&token); ajStrTokenDel(&handle); if(assem->rec) { ajListPushAppend(assem->Reads, assem->rec); assem->rec = NULL; r = NULL; } input->Datacount += iread; ajDebug("data done ok: %B Datacount: %u iread: %u/%u\n", ok, input->Datacount, iread, maxread); if(!ok) { input->Datadone = ajTrue; ajFilebuffClear(buff, 0); } return ajTrue; } ID asseminReadSamHeader TY static MO ajassemread LB core XX DE Reads assemblies in sequence alignment/map (SAM) format. XX PN [1] PA w assemin AjPAssemin PD Assembly input object PX PN [2] PA u assem AjPAssem PD Assembly object PX RT AjBool RD ajTrue on success RX // static AjBool asseminReadSamHeader(AjPAssemin assemin, AjPAssem assem) { AjPTextin input = assemin->Input; AjPFilebuff buff = input->Filebuff; AjPStr tmp = NULL; AjPTable hlinemap = NULL; AjPAssemContig contig = NULL; AjPTable readgroups = NULL; AjBool badformat = ajFalse; const char *cp = NULL; ajlong fpos = 0; ajDebug("asseminReadSamHeader\n"); readgroups = ajTablecharNew(16); ajTableSetDestroyvalue(readgroups, (void(*)(void**))ajTableDel); /* === header section === */ while(ajBuffreadLinePos(buff, &asseminReadLine, &fpos) && ajStrGetCharFirst(asseminReadLine) == '@') { input->Records++; ajStrTrimWhiteEnd(&asseminReadLine); /* * TODO:following function prints warning messages, * should we have a silence argument * if we are going to try different formats? */ hlinemap = ajSeqBamHeaderLineParse(ajStrGetPtr(asseminReadLine)); if(!hlinemap) { badformat = ajTrue; continue; /* should this return rather than continue */ } switch(((const char*)ajTableFetchC(hlinemap, "type"))[0]) { case 'H': /* @HD * VN: Format version. Accepted format: /^[0-9]+\.[0-9]+$/. * SO: Sorting order of alignments. * Valid values: unknown (default), unsorted, queryname and * coordinate. For coordinate sort, the major sort key is the RNAME * field, with order defined by the order of @SQ lines * in the header. The minor sort key is the POS field. * Alignments with ‘*’ in RNAME field follow alignments with * some other value but otherwise are in arbitrary order. **/ cp = ajTableFetchC(hlinemap, "SO"); if(cp) { assem->SO = ajAssemsortorderGetType(cp); ajDebug("sort order: %s %d %s\n", cp, assem->SO, ajAssemGetSortorderC(assem)); } break; case 'S': /* @SQ */ cp = ajTableFetchC(hlinemap, "SN"); if(cp) { AJNEW0(contig); ajStrAssignC(&contig->Name, cp); ajDebug("reference sequence:%S\n", contig->Name); ajTablePut(assem->Contigs, contig->Name, contig); ajListPushAppend(assem->ContigsOrder, contig); cp = ajTableFetchC(hlinemap, "LN"); if(cp) { tmp = ajStrNewC(cp); ajStrToInt(tmp, &contig->Length); ajDebug("ref seq length:%d\n", contig->Length); ajStrDel(&tmp); } } break; case 'R': /* @RG */ cp = ajTableFetchC(hlinemap, "ID"); if(cp) { ajTablePut(readgroups, ajCharNewC(cp), hlinemap); continue; /* in order not to delete the table hlinemap */ } break; case 'P': /* @PG */ break; case 'C': /* @CO */ break; default: badformat = ajTrue; break; } if(badformat) { ajErr("bad SAM header line: '%S'", asseminReadLine); return ajFalse; } ajTableDel(&hlinemap); } if(!input->Records) { return ajFalse; } ajAssemSetReadgroups(assem, readgroups); ajListToarray(assem->ContigsOrder, (void***)&assem->ContigArray); ajTableDel(&readgroups); return ajTrue; } ID asseminLoadSam TY static MO ajassemread LB core XX DE Reads assemblies in sequence alignment/map (SAM) format. XX PN [1] PA w assemin AjPAssemin PD Assembly input object PX PN [2] PA u assem AjPAssem PD Assembly object PX RT AjBool RD ajTrue on success RX // static AjBool asseminLoadSam(AjPAssemin assemin, AjPAssem assem) { AjPTextin input = assemin->Input; AjPFilebuff buff = input->Filebuff; AjPFile infile = NULL; ajlong fpos = 0; ajuint iread = 0; ajuint maxread = 1; AjBool ok = ajTrue; ajDebug("asseminLoadSam '%F' EOF: %B records: %u " "dataread: %B datadone: %B hasdata: %B\n", ajFilebuffGetFile(buff), ajFilebuffIsEof(buff), input->Records, input->Dataread, input->Datadone, assem->Hasdata); if(input->Datadone) { return ajFalse; } infile = ajFilebuffGetFile(buff); if(!infile) return ajFalse; if(!input->Records) { ajAssemClear(assem); return asseminReadSamHeader(assemin, assem); } ajAssemReset(assem); assem->Hasdata = ajTrue; while(ok && (iread < maxread)) { if(input->Count != 2 ) /* first SAM record may have been read while reading the header */ { ok = ajBuffreadLinePos(buff, &asseminReadLine, &fpos); if(!ok) break; } assem->rec = asseminReadSamAlignments(assemin, assem); if(assem->rec) { input->Records++; ajListPushAppend(assem->Reads, assem->rec); iread++; } } input->Datacount += iread; if(!ok) { input->Datadone = ajTrue; ajFilebuffClear(buff, 0); } else if(!iread) { input->Datadone = ajTrue; ajFilebuffClear(buff, 0); return ajFalse; } return ajTrue; } ID asseminReadSamAlignments TY static MO ajassemread LB core XX DE Reads read/alignment records from SAM files XX PN [1] PA w assemin AjPAssemin PD Assembly input object PX PN [2] PA u assem AjPAssem PD Assembly object PX RT AjPAssemRead RD read/alignment record on success RX // static AjPAssemRead asseminReadSamAlignments(AjPAssemin assemin, AjPAssem assem) { AjPFilebuff buff = NULL; AjPFile infile = NULL; AjPStrTok handle = NULL; AjPStr token = NULL; AjPAssemContig contig = NULL; AjPAssemRead r = NULL; AjPAssemContig* contigs = NULL; AjPAssemTag tag = NULL; const char *cp = NULL; ajint iqual; ajuint seqlen = 0; ajint qmin = 33; ajint qmax = 126; ajuint pos; ajint i=0; buff = assemin->Input->Filebuff; infile = ajFilebuffGetFile(buff); contigs = assem->ContigArray; if(ajStrParseCountC(asseminReadLine, "\t") < 11) return NULL; AJNEW0(r); ajStrTokenAssignC(&handle, asseminReadLine, "\t\n"); ajStrTokenNextParseNoskip(&handle,&token); /* QNAME */ ajStrAssignS(&r->Name, token); ajDebug("QNAME '%S' '%S'\n", token, r->Name); ajStrTokenNextParseNoskip(&handle,&token); /* FLAG */ ajDebug("FLAG '%S'\n", token); ajStrToInt(token, &r->Flag); ajStrTokenNextParseNoskip(&handle,&token); /* RNAME */ contig = ajTableFetchmodS(assem->Contigs, token); if(contig) { r->Reference = -1; if(contigs) for(i=0;contigs[i];i++) { if(contigs[i] == contig) { r->Reference = i; break; } } contig->Nreads++; } else { if( !ajStrMatchC(token, "*") && !ajTableFetchmodS(assem->ContigsIgnored, token) ) { ajWarn("reference '%S' is recognised as '*', " "no header (@SQ) entries was found for it", token); ajTablePut(assem->ContigsIgnored, token, (void*)assemin); } contig = ajTableFetchmodC(assem->Contigs, "*"); if(!contig) { AJNEW0(contig); ajStrAssignC(&contig->Name,"*"); ajDebug("reference sequence:%S\n", contig->Name); ajTablePut(assem->Contigs, contig->Name, contig); ajListPushAppend(assem->ContigsOrder, contig); } r->Reference = ajListGetLength(assem->ContigsOrder)-1; contig->Nreads++; } ajDebug("RNAME '%S'\n", token); ajStrTokenNextParseNoskip(&handle,&token); /* POS */ ajDebug("POS '%S'\n", token); if(ajStrGetLen(token)) { if(!ajStrToUint(token, &pos)) { ajErr("SAM %F '%S' invalid POS value %S\n", infile, r->Name, token); return NULL; } if(r->Flag & BAM_FREVERSE) { r->y1 = pos; r->Reversed = ajTrue; } else r->x1 = pos; } ajStrTokenNextParseNoskip(&handle,&token); /* MAPQ */ ajDebug("MAPQ '%S'\n", token); ajStrToInt(token, &r->MapQ); ajStrTokenNextParseNoskip(&handle,&token); /* CIGAR */ ajStrAssignS(&r->Cigar, token); ajDebug("CIGAR '%S'\n", token); ajStrTokenNextParseNoskip(&handle,&token); /* RNEXT */ if(ajStrMatchC(token,"=")) r->Rnext = r->Reference; else if(ajStrMatchC(token,"*")) r->Rnext = -1; else { contig = ajTableFetchmodS(assem->Contigs, token); if(contig) { for(i=0;contigs[i];i++) { if(contigs[i] == contig) { r->Rnext = i; break; } } } else r->Rnext = -1; } /* in earlier SAM specs PNEXT was known as MPOS */ ajStrTokenNextParseNoskip(&handle,&token); /* PNEXT */ if(ajStrGetLen(token)) { if(!ajStrToLong(token, &r->Pnext)) { ajErr("SAM %F '%S' invalid PNEXT(MPOS) value %S\n", infile, r->Name, token); return NULL; } } ajStrTokenNextParseNoskip(&handle,&token); /* TLEN */ if(ajStrGetLen(token)) { if(!ajStrToInt(token, &r->Tlen)) { ajErr("SAM %F '%S' invalid TLEN(ISIZE) value %S\n", infile, r->Name, token); return NULL; } } ajStrTokenNextParseNoskip(&handle,&token); /* SEQ */ ajDebug("SEQ '%S'\n", token); ajStrAssignS(&r->Seq, token); seqlen = MAJSTRGETLEN(token); ajStrTokenNextParseNoskip(&handle,&token); /* QUAL */ ajDebug("QUAL '%S'\n", token); ajStrAssignS(&r->SeqQ, token); if(ajStrCmpC(token,"*")!=0) { if(MAJSTRGETLEN(token) != seqlen) { ajErr("SAM quality length mismatch '%F' '%S' " "expected: %u found: %u '%S' '%S'", infile, r->Name, seqlen, ajStrGetLen(token), r->Seq, token); return NULL; } cp = MAJSTRGETPTR(token); ajStrAssignS(&r->SeqQ, token); while (*cp) { iqual = *cp++; if(iqual < qmin) { ajWarn("SAM '%F' sequence '%S' " "quality value %d '%c' too low", infile, r->Name, (ajint) (cp - MAJSTRGETPTR(token)), (char) iqual); iqual = qmin; } else if(iqual > qmax) { ajWarn("SAM '%F' sequence '%S' " "quality value '%c' too high", infile, r->Name, (char) iqual); iqual = qmax; } } } while(ajStrTokenNextParseNoskip(&handle,&token)) /* tags */ { AJNEW0(tag); ajStrAssignSubS(&tag->Name, token,0,1); tag->type = ajStrGetCharPos(token,3); ajStrAssignSubS(&tag->Comment, token, 5, -1); if(!r->Tags) r->Tags = ajListNew(); ajListPushAppend(r->Tags, tag); } ajStrDel(&token); ajStrTokenDel(&handle); return r; } ID ajAsseminprintBook TY public MO ajassemread LB core XX DE Reports the internal data structures as a Docbook table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajAsseminprintBook(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; AjPList fmtlist; AjPStr* names; fmtlist = ajListstrNew(); ajFmtPrintF(outf, "The supported assembly formats are summarised " "in the table below. " "The columns are as follows: " "Input format (format name), " "Try (indicates whether the " "format can be detected automatically on input), and " "Description (short description of " "the format).\n\n"); ajFmtPrintF(outf, "
Alignment FormatAliasNucProShowheaderMinseqMaxseqDescription
\n%-12s\n%5B%3B%3B%3B%6d%6d\"%s\"
\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=1; asseminFormatDef[i].Name; i++) { if(!asseminFormatDef[i].Alias) { namestr = ajStrNewC(asseminFormatDef[i].Name); ajListPushAppend(fmtlist, namestr); namestr = NULL; } } ajListSort(fmtlist, &ajStrVcmp); ajListstrToarray(fmtlist, &names); for(i=0; names[i]; i++) { for(j=0; asseminFormatDef[j].Name; j++) { if(ajStrMatchC(names[i],asseminFormatDef[j].Name)) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", asseminFormatDef[j].Name); ajFmtPrintF(outf, " \n", asseminFormatDef[j].Try); ajFmtPrintF(outf, " \n", asseminFormatDef[j].Desc); ajFmtPrintF(outf, " \n"); } } } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Input assembly formats
Input FormatTryDescription
%s%B%s
\n"); ajStrDel(&namestr); names = NULL; ajListstrFreeData(&fmtlist); return; } ID ajAsseminprintHtml TY public MO ajassemread LB core XX DE Reports the internal data structures as an HTML table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajAsseminprintHtml(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, ""); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); for(i=1; asseminFormatDef[i].Name; i++) { ajStrAssignC(&namestr, asseminFormatDef[i].Name); if(!asseminFormatDef[i].Alias) { for(j=i+1; asseminFormatDef[j].Name; j++) { if(asseminFormatDef[j].Load == asseminFormatDef[i].Load) { ajFmtPrintAppS(&namestr, " %s", asseminFormatDef[j].Name); if(!asseminFormatDef[j].Alias) { ajWarn("Input format '%s' same as '%s' but not alias", asseminFormatDef[j].Name, asseminFormatDef[i].Name); } } } ajFmtPrintF(outf, "\n", namestr, asseminFormatDef[i].Try); ajFmtPrintF(outf, "\n", asseminFormatDef[i].Desc); } } ajFmtPrintF(outf, "
Input FormatAutoMultiDescription
\n%S\n%B\n%s\n
\n"); ajStrDel(&namestr); return; } ID ajAsseminprintText TY public MO ajassemread LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajAsseminprintText(AjPFile outf, AjBool full) { ajuint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Assembly input formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Alias Alias name\n"); ajFmtPrintF(outf, "# Try Test for unknown input files\n"); ajFmtPrintF(outf, "# Name Alias Try " "Description"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "InFormat {\n"); for(i=0; asseminFormatDef[i].Name; i++) if(full || !asseminFormatDef[i].Alias) ajFmtPrintF(outf, " %-12s %5B %3B \"%s\"\n", asseminFormatDef[i].Name, asseminFormatDef[i].Alias, asseminFormatDef[i].Try, asseminFormatDef[i].Desc); ajFmtPrintF(outf, "}\n\n"); return; } ID ajAsseminprintWiki TY public MO ajassemread LB core XX DE Reports the internal data structures as a wiki table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajAsseminprintWiki(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!Try!!" "class=\"unsortable\"|Description\n"); for(i=1; asseminFormatDef[i].Name; i++) { ajStrAssignC(&namestr, asseminFormatDef[i].Name); if(!asseminFormatDef[i].Alias) { for(j=i+1; asseminFormatDef[j].Name; j++) { if(asseminFormatDef[j].Load == asseminFormatDef[i].Load) { ajFmtPrintAppS(&namestr, "
%s", asseminFormatDef[j].Name); if(!asseminFormatDef[j].Alias) { ajWarn("Input format '%s' same as '%s' but not alias", asseminFormatDef[j].Name, asseminFormatDef[i].Name); } } } ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "|%S||%B||%s\n", namestr, asseminFormatDef[i].Try, asseminFormatDef[i].Desc); } } ajFmtPrintF(outf, "|}\n\n"); ajStrDel(&namestr); return; } ID ajAsseminExit TY public MO ajassemread LB core XX DE Cleans up assembly input internal memory XX RT void RD RX // void ajAsseminExit(void) { /* Query processing regular expressions */ ajTableDel(&assemDbMethods); ajStrDel(&asseminReadLine); return; } ID ajAsseminTypeGetFields TY public MO ajassemread LB core XX DE Returns the listof known field names for ajAsseminLoad XX RT const char* RD List of field names RX // const char* ajAsseminTypeGetFields(void) { return "id acc"; } ID ajAsseminTypeGetQlinks TY public MO ajassemread LB core XX DE Returns the listof known query link operators for ajAsseminLoad XX RT const char* RD List of field names RX // const char* ajAsseminTypeGetQlinks(void) { return ""; } ID ajAssemaccessGetDb TY public MO ajassemread LB core XX DE Returns the table in which assembly database access details are registered XX RT AjPTable RD Access functions hash table RX // AjPTable ajAssemaccessGetDb(void) { if(!assemDbMethods) assemDbMethods = ajCallTableNew(); return assemDbMethods; } ID ajAssemaccessMethodGetQlinks TY public MO ajassemread LB core XX DE Tests for a named method for assembly data reading returns the DE known query link operators XX PN [1] PA r method const AjPStr PD Method required. PX RT const char* RD Known link operators RX // const char* ajAssemaccessMethodGetQlinks(const AjPStr method) { const AjPAssemAccess methoddata; methoddata = ajCallTableGetS(assemDbMethods, method); if(!methoddata) return NULL; return methoddata->Qlink; } ID ajAssemaccessMethodGetScope TY public MO ajassemread LB core XX DE Tests for a named method for assembly data reading and returns the scope DE (entry, query or all). XX PN [1] PA r method const AjPStr PD Method required. PX RT ajuint RD Scope flags RX // ajuint ajAssemaccessMethodGetScope(const AjPStr method) { const AjPAssemAccess methoddata; ajuint ret = 0; methoddata = ajCallTableGetS(assemDbMethods, method); if(!methoddata) return 0; if(methoddata->Entry) ret |= AJMETHOD_ENTRY; if(methoddata->Query) ret |= AJMETHOD_QUERY; if(methoddata->All) ret |= AJMETHOD_ALL; return ret; } ID ajAssemaccessMethodTest TY public MO ajassemread LB core XX DE Tests for a named method for assembly data reading. XX PN [1] PA r method const AjPStr PD Method required. PX RT AjBool RD ajTrue on success. RX // AjBool ajAssemaccessMethodTest(const AjPStr method) { if(ajCallTableGetS(assemDbMethods, method)) return ajTrue; return ajFalse; } ID asseminQryRestore TY static MO ajassemread LB core XX DE Restores an assembly input specification from an AjPQueryList node XX PN [1] PA w assemin AjPAssemin PD Assembly input object PX PN [2] PA r node const AjPQueryList PD Query list node PX RT void RD RX // static void asseminQryRestore(AjPAssemin assemin, const AjPQueryList node) { assemin->Input->Format = node->Format; assemin->Input->Fpos = node->Fpos; ajStrAssignS(&assemin->Input->Formatstr, node->Formatstr); ajStrAssignS(&assemin->Input->QryFields, node->QryFields); return; } ID asseminQrySave TY static MO ajassemread LB core XX DE Saves an assembly input specification in an AjPQueryList node XX PN [1] PA w node AjPQueryList PD Query list node PX PN [2] PA r assemin const AjPAssemin PD Assembly input object PX RT void RD RX // static void asseminQrySave(AjPQueryList node, const AjPAssemin assemin) { node->Format = assemin->Input->Format; node->Fpos = assemin->Input->Fpos; ajStrAssignS(&node->Formatstr, assemin->Input->Formatstr); ajStrAssignS(&node->QryFields, assemin->Input->QryFields); return; } ID asseminQryProcess TY static MO ajassemread LB core XX DE Converts an assembly data query into an open file. DE DE Tests for "format::" and sets this if it is found DE DE Then tests for "list:" or " XX PN [1] PA u assemin AjPAssemin PD Assembly data input structure. PX PN [2] PA u assem AjPAssem PD Assembly data to be read. PD The format will be replaced PD if defined in the query string. PX RT AjBool RD ajTrue on success. RX // static AjBool asseminQryProcess(AjPAssemin assemin, AjPAssem assem) { AjBool ret = ajTrue; AjPStr qrystr = NULL; AjBool assemmethod = ajFalse; const AjPStr fmtstr = NULL; AjPTextin textin; AjPQuery qry; AjPAssemAccess assemaccess = NULL; AjBool foo; textin = assemin->Input; qry = textin->Query; /* pick up the original query string */ qrystr = ajStrNewS(textin->Qry); ajDebug("asseminQryProcess '%S'\n", qrystr); /* look for a format:: prefix */ fmtstr = ajQuerystrParseFormat(&qrystr, textin, asseminformatFind); ajDebug("asseminQryProcess ... fmtstr '%S' '%S'\n", fmtstr, qrystr); /* (seq/feat) DO NOT look for a [range] suffix */ ajQuerystrParseRange(&qrystr, &assemin->cbegin, &assemin->cend, &foo); /* look for a list:: or @:: listfile of queries - process and return */ if(ajQuerystrParseListfile(&qrystr)) { ajDebug("asseminQryProcess ... listfile '%S'\n", qrystr); ret = asseminListProcess(assemin, assem, qrystr); ajStrDel(&qrystr); return ret; } /* try general text access methods (file, asis, text database access */ ajDebug("asseminQryProcess ... no listfile '%S'\n", qrystr); if(!ajQuerystrParseRead(&qrystr, textin, asseminformatFind, &assemmethod)) { ajStrDel(&qrystr); return ajFalse; } asseminFormatSet(assemin, assem); ajDebug("asseminQryProcess ... read nontext: %B '%S'\n", assemmethod, qrystr); ajStrDel(&qrystr); /* we found a non-text method */ if(assemmethod) { ajDebug("asseminQryProcess ... call method '%S'\n", qry->Method); ajDebug("asseminQryProcess ... textin format %d '%S'\n", textin->Format, textin->Formatstr); ajDebug("asseminQryProcess ... query format '%S'\n", qry->Formatstr); qry->Access = ajCallTableGetS(assemDbMethods,qry->Method); assemaccess = qry->Access; return (*assemaccess->Access)(assemin); } ajDebug("asseminQryProcess text method '%S' success\n", qry->Method); return ajTrue; } ID asseminListProcess TY static MO ajassemread LB core XX DE Processes a file of queries. DE This function is called by, and calls, asseminQryProcess. There is DE a depth check to avoid infinite loops, for example where a list file DE refers to itself. DE DE This function produces a list (AjPList) of queries with all list references DE expanded into lists of queries. DE DE Because queries in a list can have their own format DE the prior settings are stored with each query in the list node so that they DE can be restored after. XX PN [1] PA u assemin AjPAssemin PD Assembly data input PX PN [2] PA u assem AjPAssem PD Assembly data PX PN [3] PA r listfile const AjPStr PD Name of list file., PX RT AjBool RD ajTrue on success. RX // static AjBool asseminListProcess(AjPAssemin assemin, AjPAssem assem, const AjPStr listfile) { AjPList list = NULL; AjPFile file = NULL; AjPStr token = NULL; AjPStrTok handle = NULL; AjBool ret = ajFalse; AjPQueryList node = NULL; static ajint depth = 0; static ajint MAXDEPTH = 16; depth++; ajDebug("++asseminListProcess %S depth %d\n", listfile, depth); if(depth > MAXDEPTH) ajFatal("Query list too deep"); if(!assemin->Input->List) assemin->Input->List = ajListNew(); list = ajListNew(); file = ajFileNewInNameS(listfile); if(!file) { ajErr("Failed to open list file '%S'", listfile); depth--; return ret; } while(ajReadlineTrim(file, &asseminReadLine)) { asseminListNoComment(&asseminReadLine); if(ajStrGetLen(asseminReadLine)) { ajStrTokenAssignC(&handle, asseminReadLine, " \t\n\r"); ajStrTokenNextParse(&handle, &token); /* ajDebug("Line '%S'\n");*/ /* ajDebug("token '%S'\n", asseminReadLine, token); */ if(ajStrGetLen(token)) { ajDebug("++Add to list: '%S'\n", token); AJNEW0(node); ajStrAssignS(&node->Qry, token); asseminQrySave(node, assemin); ajListPushAppend(list, node); } ajStrDel(&token); token = NULL; } } ajFileClose(&file); ajStrDel(&token); ajDebug("Trace assemin->Input->List\n"); ajQuerylistTrace(assemin->Input->List); ajDebug("Trace new list\n"); ajQuerylistTrace(list); ajListPushlist(assemin->Input->List, &list); ajDebug("Trace combined assemin->Input->List\n"); ajQuerylistTrace(assemin->Input->List); /* ** now try the first item on the list ** this can descend recursively if it is also a list ** which is why we check the depth above */ if(ajListPop(assemin->Input->List, (void**) &node)) { ajDebug("++pop first item '%S'\n", node->Qry); ajAsseminQryS(assemin, node->Qry); asseminQryRestore(assemin, node); ajStrDel(&node->Qry); ajStrDel(&node->Formatstr); AJFREE(node); ajDebug("descending with query '%S'\n", assemin->Input->Qry); ret = asseminQryProcess(assemin, assem); } ajStrTokenDel(&handle); depth--; ajDebug("++asseminListProcess depth: %d returns: %B\n", depth, ret); return ret; } ID asseminListNoComment TY static MO ajassemread LB core XX DE Strips comments from a character string (a line from an ACD file). DE Comments are blank lines or any text following a "#" character. XX PN [1] PA u text AjPStr* PD Line of text from input file. PX RT void RD RX // static void asseminListNoComment(AjPStr* text) { ajuint i; char *cp; i = ajStrGetLen(*text); if(!i) /* empty string */ return; MAJSTRGETUNIQUESTR(text); cp = strchr(ajStrGetPtr(*text), '#'); if(cp) { /* comment found */ *cp = '\0'; ajStrSetValid(text); } return; } ID asseminFormatSet TY static MO ajassemread LB core XX DE Sets the input format for assembly data using the assembly data DE input object's defined format XX PN [1] PA u assemin AjPAssemin PD Assembly data input. PX PN [2] PA u assem AjPAssem PD Assembly data PX RT AjBool RD ajTrue on success. RX // static AjBool asseminFormatSet(AjPAssemin assemin, AjPAssem assem) { if(ajStrGetLen(assemin->Input->Formatstr)) { ajDebug("... input format value '%S'\n", assemin->Input->Formatstr); if(asseminformatFind(assemin->Input->Formatstr, &assemin->Input->Format)) { ajStrAssignS(&assem->Formatstr, assemin->Input->Formatstr); assem->Format = assemin->Input->Format; ajDebug("...format OK '%S' = %d\n", assemin->Input->Formatstr, assemin->Input->Format); } else ajDebug("...format unknown '%S'\n", assemin->Input->Formatstr); return ajTrue; } else ajDebug("...input format not set\n"); return ajFalse; } ID ajAssemloadNew TY public MO ajassemread LB core XX DE Creates a new assembly loader object. XX RT AjPAssemload RD New assembly loader object. RX // AjPAssemload ajAssemloadNew(void) { AjPAssemload pthis; AJNEW0(pthis); pthis->Assemin = ajAsseminNew(); pthis->Assem = ajAssemNew(); return pthis; } ID ajAssemloadDel TY public MO ajassemread LB core XX DE Deletes an assembly loader object. XX PN [1] PA d pthis AjPAssemload* PD Assembly loader PX RT void RD RX // void ajAssemloadDel(AjPAssemload* pthis) { AjPAssemload thys; if(!pthis) return; thys = *pthis; if(!thys) return; ajAsseminDel(&thys->Assemin); if(!thys->Returned) ajAssemDel(&thys->Assem); AJFREE(*pthis); return; } ID ajAssemloadClear TY public MO ajassemread LB core XX DE Clears an assembly loader object back to "as new" condition, except DE for the query list which must be preserved. XX PN [1] PA w thys AjPAssemload PD Assembly loader PX RT void RD RX // void ajAssemloadClear(AjPAssemload thys) { ajDebug("ajAssemloadClear called\n"); ajAsseminClear(thys->Assemin); ajAssemClear(thys->Assem); thys->Loading = ajFalse; return; } ID ajAssemloadMore TY public MO ajassemread LB core XX DE Test whether more assembly data can be loaded DE DE Return the results in the AjPAssem object but leave the file open for DE future calls. XX PN [1] PA w thys AjPAssemload PD Assembly loader PX PN [2] PA u Passem AjPAssem* PD Assembly updated PX RT AjBool RD ajTrue on success. RX // AjBool ajAssemloadMore(AjPAssemload thys, AjPAssem *Passem) { AjPAssemin assemin = thys->Assemin; AjPTextin input = assemin->Input; ajDebug("ajAssemloadMore count:%u dataread: %B datadone: %B BamIdx: %p\n", thys->Count, input->Dataread, input->Datadone, thys->Assemin->BamIdx); if(thys->Assemin->BamIdx) { *Passem = thys->Assem; thys->Returned = ajTrue; ajBamIndexDel(&thys->Assemin->BamIdx); input->Datadone = ajTrue; return ajTrue; } if(input->Datadone) { input->Dataread = ajFalse; input->Datadone = ajFalse; ajDebug("ajAssemloadMore done\n"); return ajFalse; } if(!assemin->Loading) { assemin->Loading = ajTrue; thys->Count++; *Passem = thys->Assem; thys->Returned = ajTrue; ajDebug("ajAssemloadNext initial\n"); return ajTrue; } if(ajAsseminLoad(thys->Assemin, thys->Assem)) { *Passem = thys->Assem; thys->Returned = ajTrue; ajDebug("ajAssemloadMore success\n"); return ajTrue; } *Passem = NULL; ajDebug("ajAssemloadMore failed to read\n"); return ajFalse; } ID ajAssemloadNext TY public MO ajassemread LB core XX DE Parse an assembly query into format, access, file and entry DE DE Split at delimiters. Check for the first part as a valid format DE Check for the remaining first part as a database name or as a file DE that can be opened. DE Anything left is an entryname spec. DE DE Return the results in the AjPAssem object but leave the file open for DE future calls. XX PN [1] PA w thys AjPAssemload PD Assembly loader PX PN [2] PA u Passem AjPAssem* PD Assembly returned PX RT AjBool RD ajTrue on success. RX // AjBool ajAssemloadNext(AjPAssemload thys, AjPAssem *Passem) { AjPAssemin assemin = thys->Assemin; AjPTextin input = assemin->Input; ajDebug("..ajAssemloadNext count:%u dataread: %B datadone: %B\n", thys->Count, input->Dataread, input->Datadone); if(!thys->Loading) { thys->Loading = ajTrue; assemin->Loading = ajFalse; *Passem = thys->Assem; thys->Returned = ajTrue; ajDebug("ajAssemloadNext initial\n"); return ajTrue; } if(ajAsseminLoad(thys->Assemin, thys->Assem)) { assemin->Loading = ajFalse; *Passem = thys->Assem; thys->Returned = ajTrue; ajDebug("ajAssemloadNext success\n"); return ajTrue; } *Passem = NULL; ajDebug("ajAssemloadNext failed\n"); return ajFalse; } ID asseminformatFind TY static MO ajassemread LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Sets iformat as the recognised format, and returns ajTrue. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool asseminformatFind(const AjPStr format, ajint* iformat) { AjPStr tmpformat = NULL; ajuint i = 0; /* ajDebug("asseminformatFind '%S'\n", format); */ if(!ajStrGetLen(format)) return ajFalse; ajStrAssignS(&tmpformat, format); ajStrFmtLower(&tmpformat); for(i=0; asseminFormatDef[i].Name; i++) { /* ajDebug("test %d '%s' \n", i, asseminFormatDef[i].Name); */ if(ajStrMatchCaseC(tmpformat, asseminFormatDef[i].Name)) { *iformat = i; ajStrDel(&tmpformat); /* ajDebug("found '%s' at %d\n", asseminFormatDef[i].Name, i); */ return ajTrue; } } ajErr("Unknown input format '%S'", format); ajStrDel(&tmpformat); return ajFalse; } ID ajAsseminformatTerm TY public MO ajassemread LB core XX DE Tests whether a assembly data input format term is known XX PN [1] PA r term const AjPStr PD Format term EDAM ID PX RT AjBool RD ajTrue if formats was accepted RX // AjBool ajAsseminformatTerm(const AjPStr term) { ajuint i; for(i=0; asseminFormatDef[i].Name; i++) if(ajStrMatchCaseC(term, asseminFormatDef[i].Obo)) return ajTrue; return ajFalse; } ID ajAsseminformatTest TY public MO ajassemread LB core XX DE Tests whether a named assembly data input format is known XX PN [1] PA r format const AjPStr PD Format PX RT AjBool RD ajTrue if format was accepted RX // AjBool ajAsseminformatTest(const AjPStr format) { ajuint i; for(i=0; asseminFormatDef[i].Name; i++) if(ajStrMatchCaseC(format, asseminFormatDef[i].Name)) return ajTrue; return ajFalse; } ID ajAssemoutWrite TY public MO ajassemwrite LB core XX DE Write assembly data in a named format XX PN [1] PA u outf AjPOutfile PD Output file PX PN [2] PA r assem const AjPAssem PD Assembly data object PX RT AjBool RD True on success RX // AjBool ajAssemoutWrite(AjPOutfile outf, const AjPAssem assem) { ajuint i = ajOutfileGetFormatindex(outf); AjPFile outfile = ajOutfileGetFile(outf); ajDebug("ajAssemoutWrite %d\n",i); return (*assemoutFormatDef[i].Write)(outfile, assem); } ID ajAssemoutWriteNext TY public MO ajassemwrite LB core XX DE Write latest chunk of assembly data in a named format XX PN [1] PA u outf AjPOutfile PD Output file PX PN [2] PA r assem const AjPAssem PD Assembly data object PX RT AjBool RD True on success RX // AjBool ajAssemoutWriteNext(AjPOutfile outf, const AjPAssem assem) { ajuint i = ajOutfileGetFormatindex(outf); ajDebug("ajAssemoutWriteNext %d\n",i); if(!outf->Cleanup && assemoutFormatDef[i].Cleanup) outf->Cleanup = assemoutFormatDef[i].Cleanup; return (*assemoutFormatDef[i].WriteNext)(outf, assem); } ID assemoutWriteBam TY static MO ajassemwrite LB core XX DE Writes an assembly in binary alignment/map (BAM) format. DE DE The sort order is "unsorted". Samtools can re-sort the file. XX PN [1] PA u outf AjPFile PD Output file object. PX PN [2] PA r assem const AjPAssem PD Assembly object PX RT AjBool RD True on success RX // static AjBool assemoutWriteBam(AjPFile outf, const AjPAssem assem) { AjPSeqBamHeader header = NULL; AjPAssemContig c = NULL; AjPSeqBam bam; AjPAssemRead r = NULL; AjPAssemContig* contigs = NULL; AjPAssemTag t = NULL; AjIList j = NULL; AjPSeqBamBgzf gzfile = NULL; AjPStr headertext=NULL; const AjPStr rgheadertext=NULL; AjBool ret = ajTrue; ajint i=0; ajulong ncontigs=0UL; if(ajListGetLength(assem->ContigsOrder)) ncontigs = ajListToarray(assem->ContigsOrder, (void***)&contigs); else ncontigs = ajTableToarrayValues(assem->Contigs, (void***)&contigs); AJNEW0(bam); bam->m_data=10; AJCNEW0(bam->data, bam->m_data); gzfile = ajSeqBamBgzfNew(ajFileGetFileptr(outf), "w"); ajFmtPrintS(&headertext, "@HD\tVN:1.3\tSO:%s\n", ajAssemGetSortorderC(assem)); header = ajSeqBamHeaderNewN((ajuint) ncontigs); while (contigs[i]) /* contigs */ { c = contigs[i]; if(ajStrMatchC(c->Name, "*")) { i++; continue; } header->target_name[i] = strdup(ajStrGetPtr(c->Name)); header->target_len[i++] = c->Length; ajFmtPrintAppS(&headertext, "@SQ\tSN:%S\tLN:%d", c->Name, c->Length); if(c->URI) ajFmtPrintAppS(&headertext, "\tUR:%S", c->URI); if(c->MD5) ajFmtPrintAppS(&headertext, "\tM5:%S", c->MD5); if(c->Species) ajFmtPrintAppS(&headertext, "\tSP:%S", c->Species); ajFmtPrintAppS(&headertext, "\n"); j = ajListIterNewread(c->Tags); while (!ajListIterDone(j)) { t = ajListIterGet(j); ajFmtPrintAppS(&headertext, "@CO\t%S %u %u %S\n", t->Name, t->x1, t->y1, t->Comment); } ajListIterDel(&j); } rgheadertext = assemSAMGetReadgroupHeaderlines(assem); if(rgheadertext) ajStrAppendS(&headertext, rgheadertext); ajSeqBamHeaderSetTextC(header, ajStrGetPtr(headertext)); ajSeqBamHeaderWrite(gzfile, header); j = ajListIterNewread(assem->Reads); while (!ajListIterDone(j)) /* reads */ { r = ajListIterGet(j); assemoutWriteBamAlignment(gzfile, r, bam); } ajListIterDel(&j); ajSeqBamBgzfClose(gzfile); ajSeqBamHeaderDel(&header); ajStrDel(&headertext); AJFREE(contigs); AJFREE(bam->data); AJFREE(bam); ajBamIndexBuild(ajFileGetNameC(outf)); return ret; } ID assemoutBamIndex TY static MO ajassemwrite LB core XX DE Closes and idexes a BAM output file XX PN [1] PA u outf AjPOutfile PD Output file object. PX RT void RD RX // static void assemoutBamIndex(AjPOutfile outf) { char *fname = ajCharNewC(ajFileGetNameC(ajOutfileGetFile(outf))); AjPSeqBamBgzf gzfile = outf->OutData; ajSeqBamBgzfClose(gzfile); ajFileClose(&outf->File); ajBamIndexBuild(fname); AJFREE(fname); return; } ID assemoutWriteNextBam TY static MO ajassemwrite LB core XX DE Writes an assembly in binary alignment/map (BAM) format. DE DE The sort order is "unsorted". Samtools can re-sort the file. XX PN [1] PA u outfile AjPOutfile PD Output file object. PX PN [2] PA r assem const AjPAssem PD Assembly object PX RT AjBool RD True on success RX // static AjBool assemoutWriteNextBam(AjPOutfile outfile, const AjPAssem assem) { AjPFile outf = ajOutfileGetFile(outfile); AjPSeqBamHeader header = NULL; AjPAssemContig c = NULL; AjPSeqBam bam; AjPAssemRead r = NULL; AjPAssemContig* contigs = NULL; AjPAssemTag t = NULL; AjIList j = NULL; AjPSeqBamBgzf gzfile = NULL; AjPStr headertext=NULL; const AjPStr rgheadertext=NULL; AjBool ret = ajTrue; ajint i=0; ajulong ncontigs=0UL; if(!outf) return ajFalse; if(!assem) return ajFalse; if(!assem->Hasdata) { if(ajListGetLength(assem->ContigsOrder)) ncontigs = ajListToarray(assem->ContigsOrder, (void***)&contigs); else ncontigs = ajTableToarrayValues(assem->Contigs, (void***)&contigs); ajFmtPrintS(&headertext, "@HD\tVN:1.3\tSO:%s\n", ajAssemGetSortorderC(assem)); header = ajSeqBamHeaderNewN((ajuint) ncontigs); gzfile = ajSeqBamBgzfNew(ajFileGetFileptr(outf), "w"); outfile->OutData = gzfile; while (contigs[i]) /* contigs */ { c = contigs[i]; if(ajStrMatchC(c->Name, "*")) { i++; continue; } header->target_name[i] = strdup(ajStrGetPtr(c->Name)); header->target_len[i++] = c->Length; ajFmtPrintAppS(&headertext, "@SQ\tSN:%S\tLN:%d", c->Name, c->Length); if(c->URI) ajFmtPrintAppS(&headertext, "\tUR:%S", c->URI); if(c->MD5) ajFmtPrintAppS(&headertext, "\tM5:%S", c->MD5); if(c->Species) ajFmtPrintAppS(&headertext, "\tSP:%S", c->Species); ajFmtPrintAppS(&headertext, "\n"); j = ajListIterNewread(c->Tags); while (!ajListIterDone(j)) { t = ajListIterGet(j); ajFmtPrintAppS(&headertext, "@CO\t%S %u %u %S\n", t->Name, t->x1, t->y1, t->Comment); } ajListIterDel(&j); } rgheadertext = assemSAMGetReadgroupHeaderlines(assem); if(rgheadertext) ajStrAppendS(&headertext, rgheadertext); ajSeqBamHeaderSetTextC(header, ajStrGetPtr(headertext)); ajSeqBamHeaderWrite(gzfile, header); ajSeqBamHeaderDel(&header); ajStrDel(&headertext); AJFREE(contigs); if(!assem->BamHeader) return ajTrue; } /* data */ gzfile = outfile->OutData; AJNEW0(bam); bam->m_data=10; AJCNEW0(bam->data, bam->m_data); j = ajListIterNewread(assem->Reads); while (!ajListIterDone(j)) /* reads */ { r = ajListIterGet(j); assemoutWriteBamAlignment(gzfile, r, bam); } ajListIterDel(&j); AJFREE(bam->data); AJFREE(bam); /* ajSeqBamBgzfClose(gzfile);*/ return ret; } ID assemoutWriteBamAlignment TY static MO ajassemwrite LB core XX DE Write individual alignment records of assembly data in BAM format XX PN [1] PA u gzfile AjPSeqBamBgzf PD Output file PX PN [2] PA r r const AjPAssemRead PD Read object with alignment information PX PN [3] PA u bam AjPSeqBam PD samtools data structure to store PD alignment data before it is written in BAM format PX RT AjBool RD True on success RX // static AjBool assemoutWriteBamAlignment(AjPSeqBamBgzf gzfile, const AjPAssemRead r, AjPSeqBam bam) { AjPSeqBamCore c; AjPAssemTag tag; unsigned char *dpos; const char *s; ajuint ilen; ajuint slen; ajuint i; AjIList l = NULL; /* optional fields */ ajint tagvalsize = 0; const unsigned char* tagval = 0; ajint intval =0; /* processing cigar strings*/ char *t; int op; long x; unsigned char bam_nt16_table[256] = { 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 1, 2, 4, 8, 15,15,15,15, 15,15,15,15, 15, 0 /*=*/,15,15, 15, 1,14, 2, 13,15,15, 4, 11,15,15,12, 15, 3,15,15, 15,15, 5, 6, 8,15, 7, 9, 15,10,15,15, 15,15,15,15, 15, 1,14, 2, 13,15,15, 4, 11,15,15,12, 15, 3,15,15, 15,15, 5, 6, 8,15, 7, 9, 15,10,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15 }; /* bam_write1 for each alignment */ c = &bam->core; ilen = ajStrGetLen(r->Seq); c->tid = (int) r->Reference; if(r->Flag & BAM_FREVERSE) c->pos = r->y1-1; else c->pos = r->x1-1; /* BAM format is zero based; -1 is translated to 0, meaning unmapped */ c->bin = 0; c->qual = r->MapQ; c->l_qname = 1 + ajStrGetLen(r->Name); c->flag = r->Flag; c->n_cigar = 0; c->l_qseq = ilen; c->mtid = (int) r->Rnext; c->mpos = (int) r->Pnext-1; c->isize = r->Tlen; /* get cigar string length */ s = ajStrGetPtr(r->Cigar); if (strcmp(s,"*")) /* '*' means unavailable */ { for (; *s; ++s) { if ((isalpha((int)*s)) || (*s=='=')) ++c->n_cigar; else if (!isdigit((int)*s)) ajWarn("invalid CIGAR character: %c\n", *s); } } bam->data_len = c->n_cigar*4 + c->l_qname + (ilen + 1)/2 + ilen; /* allocation for optional tags are made as they are appended */ if(bam->data_len > bam->m_data) { AJCRESIZE0(bam->data,bam->m_data, bam->data_len); bam->m_data = bam->data_len; } dpos = bam->data; /* copy query name to bam->data */ memcpy(dpos, ajStrGetPtr(r->Name), c->l_qname); dpos += c->l_qname; /* copy cigar string to bam->data */ s = ajStrGetPtr(r->Cigar); for (i = 0; i != c->n_cigar; ++i) { x = strtol(s, &t, 10); op = toupper((int)*t); if (op == 'M') op = BAM_CMATCH; else if (op == 'I') op = BAM_CINS; else if (op == 'D') op = BAM_CDEL; else if (op == 'N') op = BAM_CREF_SKIP; else if (op == 'S') op = BAM_CSOFT_CLIP; else if (op == 'H') op = BAM_CHARD_CLIP; else if (op == 'P') op = BAM_CPAD; else if (op == '=') op = BAM_CEQUAL; else if (op == 'X') op = BAM_CDIFF; else ajWarn("invalid CIGAR operation: %c",op); s = t + 1; ((ajuint*)dpos)[i] = x << BAM_CIGAR_SHIFT | op; } if (*s && c->n_cigar) ajWarn("unmatched CIGAR operation: %c", *s); c->bin = ajSeqBamReg2bin(c->pos, ajSeqBamCalend(c, MAJSEQBAMCIGAR(bam))); dpos += c->n_cigar*4; /* copy sequence string to bam->data */ s = ajStrGetPtr(r->Seq); slen = (ilen+1)/2; for (i = 0; i < slen; ++i) dpos[i] = 0; for (i = 0; i < ilen; ++i) dpos[i/2] |= bam_nt16_table[(ajuint)s[i]] << 4*(1-i%2); dpos += slen; /* copy quality values to bam->data */ if(r->SeqQ && !ajStrMatchC(r->SeqQ, "*")) { s = ajStrGetPtr(r->SeqQ); for(i=0;iTags); bam->l_aux=0; while (!ajListIterDone(l)) { tag = ajListIterGet(l); /* TODO: array type 'B' and other types */ if(tag->type == 'i' || tag->type == 'I') { tagvalsize = 4; ajStrToInt(tag->Comment, &intval); tagval = (unsigned char*)&intval; } else if(tag->type =='s' || tag->type =='S') { tagvalsize = 2; ajStrToInt(tag->Comment, &intval); tagval = (unsigned char*)&intval; } else if(tag->type =='c' || tag->type =='C') { tagvalsize = 1; ajStrToInt(tag->Comment, &intval); tagval = (unsigned char*)&intval; } else if(tag->type =='A') { tagvalsize = 1; tagval = (const unsigned char*)ajStrGetPtr(tag->Comment); } else if(tag->type =='Z') { tagvalsize = ajStrGetLen(tag->Comment)+1; tagval = (const unsigned char*)ajStrGetPtr(tag->Comment); } else { ajWarn("tag type '%c' not yet supported",tag->type); continue; } ajSeqBamAuxAppend(bam, ajStrGetPtr(tag->Name), tag->type, tagvalsize, tagval); } ajListIterDel(&l); ajSeqBamWrite(gzfile, bam); return ajTrue; } ID assemoutWriteMaf TY static MO ajassemwrite LB core XX DE Write assembly data in Mira Assembly Format XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r assem const AjPAssem PD Assembly data object PX RT AjBool RD True on success RX // static AjBool assemoutWriteMaf(AjPFile outf, const AjPAssem assem) { AjPAssemContig c = NULL; AjPAssemRead r = NULL; AjPAssemTag t = NULL; ajint i = 0; ajint nreads = 0; AjIList j = NULL; AjIList k = NULL; AjIList reads = NULL; AjPAssemContig* contigs = NULL; if(!outf) return ajFalse; if(!assem) return ajFalse; ajTableToarrayValues(assem->Contigs, (void***)&contigs); reads = ajListIterNewread(assem->Reads); for (;contigs[i];i++) { c = contigs[i]; ajFmtPrintF(outf, "CO\t%S\n", c->Name); ajFmtPrintF(outf, "NR\t%d\n", c->Nreads); ajFmtPrintF(outf, "LC\t%d\n", c->Length); j = ajListIterNewread(c->Tags); while (!ajListIterDone(j)) { t = ajListIterGet(j); ajFmtPrintF(outf, "CT\t%S %u %u %S\n", t->Name, t->x1, t->y1, t->Comment); } ajListIterDel(&j); ajFmtPrintF(outf, "CS\t%S\n", c->Consensus); ajFmtPrintF(outf, "CQ\t%S\n", c->ConsensusQ); ajFmtPrintF(outf, "\\\\\n"); while (nreads++ < c->Nreads && !ajListIterDone(reads)) { r = ajListIterGet(reads); if(r->Reference !=i) ajErr("different reference/contig number(%d) than expected(%d)" "\nreads were expected to be sorted by contigs"); ajFmtPrintF(outf, "RD\t%S\n", r->Name); ajFmtPrintF(outf, "RS\t%S\n", r->Seq); ajFmtPrintF(outf, "RQ\t%S\n", r->SeqQ); ajFmtPrintF(outf, "TN\t%S\n", r->Template); if(r->Direction) ajFmtPrintF(outf, "DI\t%c\n", r->Direction); if(r->TemplateSizeMin) ajFmtPrintF(outf, "TF\t%d\n", r->TemplateSizeMin); if(r->TemplateSizeMax) ajFmtPrintF(outf, "TT\t%d\n", r->TemplateSizeMax); if(r->File) ajFmtPrintF(outf, "SF\t%S\n", r->File); if(r->VectorLeft) ajFmtPrintF(outf, "SL\t%d\n", r->VectorLeft); if(r->VectorRight) ajFmtPrintF(outf, "SR\t%d\n", r->VectorRight); if(r->QualLeft) ajFmtPrintF(outf, "QL\t%d\n", r->QualLeft); if(r->QualRight) ajFmtPrintF(outf, "QR\t%d\n", r->QualRight); if(r->ClipLeft) ajFmtPrintF(outf, "SL\t%d\n", r->ClipLeft); if(r->ClipRight) ajFmtPrintF(outf, "SR\t%d\n", r->ClipRight); k = ajListIterNewread(r->Tags); while (!ajListIterDone(k)) { t = ajListIterGet(k); ajFmtPrintF(outf, "RT\t%S %u %u\n", t->Name, t->x1, t->y1); } ajListIterDel(&k); ajFmtPrintF(outf, "ST\t%S\n", r->Technology); ajFmtPrintF(outf, "ER\n"); ajFmtPrintF(outf, "AT\t%u %u %u %u\n", r->x1, r->y1, r->x2, r->y2); } ajListIterDel(&j); ajFmtPrintF(outf, "//\n"); ajFmtPrintF(outf, "EC\n"); } AJFREE(contigs); ajListIterDel(&reads); return ajTrue; } ID assemoutWriteNextMaf TY static MO ajassemwrite LB core XX DE Write latest chunk of assembly data in Mira Assembly Format XX PN [1] PA u outfile AjPOutfile PD Output file PX PN [2] PA r assem const AjPAssem PD Assembly data object PX RT AjBool RD True on success RX // static AjBool assemoutWriteNextMaf(AjPOutfile outfile, const AjPAssem assem) { AjPFile outf = ajOutfileGetFile(outfile); AjPAssemContig c = NULL; AjPAssemRead r = NULL; AjPAssemTag t = NULL; ajint i = 0; ajint nreads = 0; AjIList j = NULL; AjIList k = NULL; AjIList reads = NULL; AjPAssemContig* contigs = NULL; if(!outf) return ajFalse; if(!assem) return ajFalse; if(!assem->Hasdata) { ajTableToarrayValues(assem->Contigs, (void***)&contigs); for (;contigs[i];i++) { c = contigs[i]; ajFmtPrintF(outf, "CO\t%S\n", c->Name); ajFmtPrintF(outf, "NR\t%d\n", c->Nreads); ajFmtPrintF(outf, "LC\t%d\n", c->Length); j = ajListIterNewread(c->Tags); while (!ajListIterDone(j)) { t = ajListIterGet(j); ajFmtPrintF(outf, "CT\t%S %u %u %S\n", t->Name, t->x1, t->y1, t->Comment); } ajListIterDel(&j); ajFmtPrintF(outf, "CS\t%S\n", c->Consensus); ajFmtPrintF(outf, "CQ\t%S\n", c->ConsensusQ); ajFmtPrintF(outf, "\\\\\n"); } AJFREE(contigs); return ajTrue; } /* data */ reads = ajListIterNewread(assem->Reads); while (!ajListIterDone(reads)) { nreads++; r = ajListIterGet(reads); if(r->Reference !=i) ajErr("different reference/contig number(%d) than expected(%d)" "\nreads were expected to be sorted by contigs"); ajFmtPrintF(outf, "RD\t%S\n", r->Name); ajFmtPrintF(outf, "RS\t%S\n", r->Seq); ajFmtPrintF(outf, "RQ\t%S\n", r->SeqQ); ajFmtPrintF(outf, "TN\t%S\n", r->Template); if(r->Direction) ajFmtPrintF(outf, "DI\t%c\n", r->Direction); if(r->TemplateSizeMin) ajFmtPrintF(outf, "TF\t%d\n", r->TemplateSizeMin); if(r->TemplateSizeMax) ajFmtPrintF(outf, "TT\t%d\n", r->TemplateSizeMax); if(r->File) ajFmtPrintF(outf, "SF\t%S\n", r->File); if(r->VectorLeft) ajFmtPrintF(outf, "SL\t%d\n", r->VectorLeft); if(r->VectorRight) ajFmtPrintF(outf, "SR\t%d\n", r->VectorRight); if(r->QualLeft) ajFmtPrintF(outf, "QL\t%d\n", r->QualLeft); if(r->QualRight) ajFmtPrintF(outf, "QR\t%d\n", r->QualRight); if(r->ClipLeft) ajFmtPrintF(outf, "SL\t%d\n", r->ClipLeft); if(r->ClipRight) ajFmtPrintF(outf, "SR\t%d\n", r->ClipRight); k = ajListIterNewread(r->Tags); while (!ajListIterDone(k)) { t = ajListIterGet(k); ajFmtPrintF(outf, "RT\t%S %u %u\n", t->Name, t->x1, t->y1); } ajListIterDel(&k); ajFmtPrintF(outf, "ST\t%S\n", r->Technology); ajFmtPrintF(outf, "ER\n"); ajFmtPrintF(outf, "AT\t%u %u %u %u\n", r->x1, r->y1, r->x2, r->y2); } ajListIterDel(&j); ajListIterDel(&reads); if(!nreads) { ajFmtPrintF(outf, "//\n"); ajFmtPrintF(outf, "EC\n"); } return ajTrue; } ID assemoutMakeCigar TY static MO ajassemwrite LB core XX DE Given a reference sequence region and a read sequence returns CIGAR string DE representing the alignment. DE DE Based on make_cigar function in Peter Cock's maf2sam project DE DE We have a similar function in ajalign.c, called alignCigar(), DE we should aim using that function rather than having two similar functions XX PN [1] PA r contig const char* PD Reference sequence region PX PN [2] PA r readseq const char* PD Read sequence PX RT AjPStr RD CIGAR string RX // static AjPStr assemoutMakeCigar(const char* contig, const char* readseq) { AjPStr cigar = NULL; ajint count =0; char mode = '\0'; char c; char r; c = contig[0]; r = readseq[0]; cigar = ajStrNewC(""); for(;c!=0; c=*++contig, r=*++readseq) { if(c=='*' && r=='*') continue; else if(c!='*' && r!='*') { /* alignment match/mismatch */ if (mode!='M') { if(count) ajFmtPrintAppS(&cigar, "%d%c", count, mode); mode = 'M'; count = 1; } else count++; } else if(c=='*') { if (mode!='I') { if(count) ajFmtPrintAppS(&cigar, "%d%c", count, mode); mode = 'I'; count = 1; } else count++; } else if(r=='*') { if (mode!='D') { if(count) ajFmtPrintAppS(&cigar, "%d%c", count, mode); mode = 'D'; count = 1; } else count++; } else ajErr("something wrong!!!"); } if(count) ajFmtPrintAppS(&cigar, "%d%c", count, mode); return cigar; } ID assemoutWriteSam TY static MO ajassemwrite LB core XX DE Write assembly data in SAM format XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r assem const AjPAssem PD Assembly data object PX RT AjBool RD True on success RX // static AjBool assemoutWriteSam(AjPFile outf, const AjPAssem assem) { AjPAssemContig c = NULL; AjPAssemRead r = NULL; AjPAssemTag t = NULL; AjPAssemContig* contigs = NULL; AjIList j = NULL; AjPStr argstr = NULL; const AjPStr headertext = NULL; ajint n = 0; ajint i = 0; AjBool ret = ajTrue; if(!outf || !assem) return ajFalse; ajDebug("assemoutWriteSam: # of contigs = %d\n", n); ajFmtPrintF(outf, "@HD\tVN:1.3\tSO:%s\n", ajAssemGetSortorderC(assem)); /* Program record */ argstr = ajStrNewS(ajUtilGetCmdline()); ajStrExchangeKK(&argstr, '\n', ' '); ajFmtPrintF(outf, "@PG\tID:%S\tVN:%S\tCL:%S\n", ajUtilGetProgram(), ajNamValueVersion(), argstr); ajStrDel(&argstr); if(ajListGetLength(assem->ContigsOrder)) ajListToarray(assem->ContigsOrder, (void***)&contigs); else ajTableToarrayValues(assem->Contigs, (void***)&contigs); while (contigs[i]) /* contigs */ { c = contigs[i++]; if(!ajStrMatchC(c->Name, "*")) { ajFmtPrintF(outf, "@SQ\tSN:%S\tLN:%d", c->Name, c->Length); if(c->URI) ajFmtPrintF(outf, "\tUR:%S", c->URI); if(c->MD5) ajFmtPrintF(outf, "\tM5:%S", c->MD5); if(c->Species) ajFmtPrintF(outf, "\tSP:%S", c->Species); ajFmtPrintF(outf, "\n"); j = ajListIterNewread(c->Tags); while (!ajListIterDone(j)) { t = ajListIterGet(j); ajFmtPrintF(outf, "@CO\t%S %u %u %S\n", t->Name, t->x1, t->y1, t->Comment); } ajListIterDel(&j); } } headertext = assemSAMGetReadgroupHeaderlines(assem); if(headertext) ajFmtPrintF(outf,"%S", headertext); j = ajListIterNewread(assem->Reads); while (!ajListIterDone(j)) /* reads */ { r = ajListIterGet(j); assemoutWriteSamAlignment(outf, r, contigs, i); } ajListIterDel(&j); AJFREE(contigs); return ret; } ID assemoutWriteNextSam TY static MO ajassemwrite LB core XX DE Write latest chunk of assembly data in SAM format XX PN [1] PA u outfile AjPOutfile PD Output file PX PN [2] PA r assem const AjPAssem PD Assembly data object PX RT AjBool RD True on success RX // static AjBool assemoutWriteNextSam(AjPOutfile outfile, const AjPAssem assem) { AjPFile outf = ajOutfileGetFile(outfile); AjPAssemContig c = NULL; AjPAssemRead r = NULL; AjPAssemTag t = NULL; AjPAssemContig* contigs = NULL; AjIList j = NULL; AjPStr argstr = NULL; const AjPStr headertext = NULL; ajint n = 0; ajulong i = 0UL; AjBool ret = ajTrue; if(!outf || !assem) return ajFalse; ajDebug("assemoutWriteSam: # of contigs = %d\n", n); if(!assem->Hasdata) { ajFmtPrintF(outf, "@HD\tVN:1.3\tSO:%s\n", ajAssemGetSortorderC(assem)); /* Program record */ argstr = ajStrNewS(ajUtilGetCmdline()); ajStrExchangeKK(&argstr, '\n', ' '); ajFmtPrintF(outf, "@PG\tID:%S\tVN:%S\tCL:%S\n", ajUtilGetProgram(), ajNamValueVersion(), argstr); ajStrDel(&argstr); if(ajListGetLength(assem->ContigsOrder)) ajListToarray(assem->ContigsOrder, (void***)&contigs); else ajTableToarrayValues(assem->Contigs, (void***)&contigs); while (contigs[i]) /* contigs */ { c = contigs[i++]; if(!ajStrMatchC(c->Name, "*")) { ajFmtPrintF(outf, "@SQ\tSN:%S\tLN:%d", c->Name, c->Length); if(c->URI) ajFmtPrintF(outf, "\tUR:%S", c->URI); if(c->MD5) ajFmtPrintF(outf, "\tM5:%S", c->MD5); if(c->Species) ajFmtPrintF(outf, "\tSP:%S", c->Species); ajFmtPrintF(outf, "\n"); j = ajListIterNewread(c->Tags); while (!ajListIterDone(j)) { t = ajListIterGet(j); ajFmtPrintF(outf, "@CO\t%S %u %u %S\n", t->Name, t->x1, t->y1, t->Comment); } ajListIterDel(&j); } } headertext = assemSAMGetReadgroupHeaderlines(assem); if(headertext) ajFmtPrintF(outf,"%S", headertext); AJFREE(contigs); if(!assem->BamHeader) return ajTrue; } /* data */ j = ajListIterNewread(assem->Reads); if(ajListGetLength(assem->ContigsOrder)) i = ajListToarray(assem->ContigsOrder, (void***)&contigs); else i = ajTableToarrayValues(assem->Contigs, (void***)&contigs); while (!ajListIterDone(j)) /* reads */ { r = ajListIterGet(j); assemoutWriteSamAlignment(outf, r, contigs, (ajuint) i); } ajListIterDel(&j); AJFREE(contigs); return ret; } ID assemSAMGetReadgroupHeaderlines TY static MO ajassemwrite LB core XX DE Returns read-group header lines as text for the given assembly XX PN [1] PA r assem const AjPAssem PD assembly object PX RT const AjPStr RD read-group header lines RX // static const AjPStr assemSAMGetReadgroupHeaderlines(const AjPAssem assem) { AjPStr* rgids = NULL; const AjPAssemReadgroup rg = NULL; ajint i =0; ajTableToarrayKeys(assem->Readgroups, (void***)&rgids); ajStrAssignC(&assemoutSamLinetxt, ""); while (rgids[i]) /* read groups */ { rg = ajTableFetchS(assem->Readgroups, rgids[i++]); ajFmtPrintAppS(&assemoutSamLinetxt, "@RG\tID:%S", rg->ID); if(rg->CN) ajFmtPrintAppS(&assemoutSamLinetxt, "\tCN:%S", rg->CN); if(rg->Desc) ajFmtPrintAppS(&assemoutSamLinetxt, "\tDS:%S", rg->Desc); if(rg->Date) ajFmtPrintAppS(&assemoutSamLinetxt, "\tDT:%S", rg->Date); if(rg->FlowOrder) ajFmtPrintAppS(&assemoutSamLinetxt, "\tFO:%S", rg->FlowOrder); if(rg->KeySeq) ajFmtPrintAppS(&assemoutSamLinetxt, "\tKS:%S", rg->KeySeq); if(rg->Library) ajFmtPrintAppS(&assemoutSamLinetxt, "\tLB:%S", rg->Library); if(rg->Programs) ajFmtPrintAppS(&assemoutSamLinetxt, "\tPG:%S", rg->Programs); if(rg->Isize) ajFmtPrintAppS(&assemoutSamLinetxt, "\tPI:%d", rg->Isize); if(rg->Platform) ajFmtPrintAppS(&assemoutSamLinetxt, "\tPL:%s", ajAssemreadgroupGetPlatformname(rg)); if(rg->Unit) ajFmtPrintAppS(&assemoutSamLinetxt, "\tPU:%S", rg->Unit); if(rg->Sample) ajFmtPrintAppS(&assemoutSamLinetxt, "\tSM:%S", rg->Sample); ajStrAppendC(&assemoutSamLinetxt, "\n"); } AJFREE(rgids); return assemoutSamLinetxt; } ID assemoutWriteSamAlignment TY static MO ajassemwrite LB core XX DE Write individual alignment records of assembly data in SAM format XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r r const AjPAssemRead PD Read object with alignment information PX PN [3] PA r contigs AjPAssemContig const* PD Contigs/refseqs the read PD aligns to PX PN [4] PA r ncontigs ajint PD Number of contigs PX RT AjBool RD True on success RX // static AjBool assemoutWriteSamAlignment(AjPFile outf, const AjPAssemRead r, AjPAssemContig const * contigs, ajint ncontigs) { AjPAssemTag t = NULL; AjIList l = NULL; AjPStr qualstr = NULL; AjPStr tmp = NULL; ajint POS = 0; AjPStr CIGAR = NULL; const char* RNEXT = NULL; AjPStr SEQ = NULL; AjPStr QUAL = NULL; AjPStr SEQunpadded = NULL; AjPStr QUALunpadded = NULL; AjPStr consensus = NULL; AjBool rc= ajFalse; AjBool ret = ajTrue; const char* refseq = NULL; const AjPAssemContig contig = NULL; ajuint k = 0; if(r->Reference>=ncontigs) ajDie("assemoutWriteSamAlignment: reference sequence number" " '%d' is larger than or equal to known number of reference" " sequences '%d'. Problem while processing read '%S'.", r->Reference, ncontigs, r->Name); contig = (r->Reference==-1 ? NULL : contigs[r->Reference]); ajStrAssignRef(&SEQ, r->Seq); consensus = contig==NULL? NULL : contig->Consensus; if (r->Rnext==-1) RNEXT= "*"; else if(r->Rnext==r->Reference) RNEXT = "="; else RNEXT = ajStrGetPtr(contigs[r->Rnext]->Name); if (r->Flag & BAM_FREVERSE) { rc = ajTrue; qualstr = ajStrNewS(r->SeqQ); if(!r->Reversed) { ajStrReverse(&qualstr); ajSeqstrReverse(&SEQ); } QUAL = qualstr; POS = r->y1; ajStrAssignSubS(&tmp, SEQ, ajStrGetLen(r->Seq) - r->y2, ajStrGetLen(r->Seq) - r->x2 ); } else { rc= ajFalse; POS = r->x1; QUAL = r->SeqQ; ajStrAssignSubS(&tmp, SEQ, r->x2-1, r->y2-1 ); } if(r->Cigar==NULL && consensus) { refseq = ajStrGetPtr(consensus) + (rc ? r->y1-1 : r->x1-1); CIGAR = assemoutMakeCigar(refseq, ajStrGetPtr(tmp)); SEQunpadded = ajStrNewRes(ajStrGetLen(SEQ)); QUALunpadded = ajStrNewRes(ajStrGetLen(SEQ)); for(k=0; k< ajStrGetLen(SEQ); k++) { if (ajStrGetCharPos(SEQ, k) == '*') continue; ajStrAppendK(&SEQunpadded, ajStrGetCharPos(SEQ, k)); ajStrAppendK(&QUALunpadded, ajStrGetCharPos(QUAL, k)); } ajDebug("cigar: %S\n", CIGAR); ajStrAssignS(&tmp, CIGAR); if(rc) { if(r->y2 < (ajint)ajStrGetLen(SEQ)) ajFmtPrintS(&CIGAR, "%dS%S", ajStrGetLen(SEQ) - r->y2, tmp); if(r->x2 > 1) ajFmtPrintAppS(&CIGAR, "%dS", r->x2 - 1); } else { if(r->x2 > 1) ajFmtPrintS(&CIGAR, "%dS%S", r->x2 - 1, tmp); if(r->y2 < (ajint)ajStrGetLen(SEQ)) ajFmtPrintAppS(&CIGAR, "%dS", ajStrGetLen(SEQ) - r->y2); } ajStrDel(&tmp); } else if(r->Cigar==NULL) { ajErr("both CIGAR string and consensus sequence not available"); ret = ajFalse; ajStrAssignK(&CIGAR, '*'); } else if(!ajStrGetLen(r->Cigar)) ajStrAssignK(&CIGAR, '*'); else if(ajStrGetLen(r->Cigar)) { if(!ajStrGetLen(SEQ)) ajStrAssignK(&SEQ, '*'); if(!ajStrGetLen(QUAL)) ajStrAssignK(&QUAL, '*'); } ajStrDel(&tmp); ajFmtPrintF(outf, "%S\t%d\t%s\t%d\t%d\t%S\t%s\t%Ld\t%d\t%S\t%S", r->Name, r->Flag, (contig==NULL ? "*" : ajStrGetPtr(contig->Name)), POS, r->MapQ, (CIGAR ? CIGAR : r->Cigar), RNEXT, r->Pnext, r->Tlen, (r->Cigar ? SEQ : SEQunpadded), (r->Cigar ? QUAL : QUALunpadded)); l = ajListIterNewread(r->Tags); while (!ajListIterDone(l)) { t = ajListIterGet(l); /* TODO: array type, 'B' */ /* In SAM, all single integer types are mapped to int32_t [SAM spec] */ ajFmtPrintF(outf, "\t%S:%c:", t->Name, (t->type == 'c' || t->type == 'C' || t->type == 's' || t->type == 'S' || t->type == 'I') ? 'i' : t->type ); if(t->x1 || t->y1) ajFmtPrintF(outf, " %u %u", t->x1, t->y1); if(t->Comment && ajStrGetLen(t->Comment)>0) ajFmtPrintF(outf, "%S", t->Comment); } ajListIterDel(&l); ajFmtPrintF(outf, "\n"); if(qualstr) ajStrDel(&qualstr); ajStrDel(&SEQ); ajStrDel(&CIGAR); ajStrDel(&SEQunpadded); ajStrDel(&QUALunpadded); return ret; } ID assemoutWriteList TY static MO ajassemwrite LB core XX DE Write an assembly as the simple id XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r assem const AjPAssem PD Assembly object PX RT AjBool RD True on success RX // static AjBool assemoutWriteList(AjPFile outf, const AjPAssem assem) { if(!outf) return ajFalse; if(!assem) return ajFalse; if(ajStrGetLen(assem->Db)) ajFmtPrintF(outf, "%S:%S\n", assem->Db, assem->Id); else ajFmtPrintF(outf, "%S\n", assem->Id); return ajTrue; } ID assemoutWriteNextList TY static MO ajassemwrite LB core XX DE Write an assembly as the simple id XX PN [1] PA u outfile AjPOutfile PD Output file PX PN [2] PA r assem const AjPAssem PD Assembly object PX RT AjBool RD True on success RX // static AjBool assemoutWriteNextList(AjPOutfile outfile, const AjPAssem assem) { AjPFile outf = ajOutfileGetFile(outfile); if(!outf) return ajFalse; if(!assem) return ajFalse; if(!assem->Hasdata) return ajTrue; if(ajStrGetLen(assem->Db)) ajFmtPrintF(outf, "%S:%S\n", assem->Db, assem->Id); else ajFmtPrintF(outf, "%S\n", assem->Id); return ajTrue; } ID ajAssemoutprintBook TY public MO ajassemwrite LB core XX DE Reports the assembly format internals as Docbook text XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajAssemoutprintBook(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, "The supported assembly output " "formats are summarised " "in the table below. The columns are as follows: " "Output format (format name), " "Description (short description of " "the format). \n\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=0; assemoutFormatDef[i].Name; i++) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", assemoutFormatDef[i].Name); ajFmtPrintF(outf, " \n", assemoutFormatDef[i].Desc); } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Assembly output formats
Output FormatDescription
%s%s
\n"); return; } ID ajAssemoutprintHtml TY public MO ajassemwrite LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajAssemoutprintHtml(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, ""); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Assembly output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "AssemFormat {\n"); for(i=0; assemoutFormatDef[i].Name; i++) { ajFmtPrintF(outf, "\n", assemoutFormatDef[i].Name, assemoutFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajAssemoutprintText TY public MO ajassemwrite LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajAssemoutprintText(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Assembly output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "AssemFormat {\n"); for(i=0; assemoutFormatDef[i].Name; i++) { if(full) ajFmtPrintF(outf, " %-12s \"%s\"\n", assemoutFormatDef[i].Name, assemoutFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajAssemoutprintWiki TY public MO ajassemwrite LB core XX DE Reports the asembly output format internals as wikitext XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajAssemoutprintWiki(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!" "class=\"unsortable\"|Description\n"); for(i=0; assemoutFormatDef[i].Name; i++) { ajFmtPrintF(outf, "|-\n"); ajStrAssignC(&namestr, assemoutFormatDef[i].Name); for(j=i+1; assemoutFormatDef[j].Name; j++) { if(assemoutFormatDef[j].Write == assemoutFormatDef[i].Write) { ajFmtPrintAppS(&namestr, "
%s", assemoutFormatDef[j].Name); ajWarn("Assembly output format '%s' same as '%s' " "but not alias", assemoutFormatDef[j].Name, assemoutFormatDef[i].Name); } } ajFmtPrintF(outf, "|%S||%s\n", namestr, assemoutFormatDef[i].Desc); } ajFmtPrintF(outf, "|}\n"); ajStrDel(&namestr); return; } ID ajAssemoutformatFind TY public MO ajassemwrite LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Sets iformat as the recognised format, and returns ajTrue. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // AjBool ajAssemoutformatFind(const AjPStr format, ajint* iformat) { AjPStr tmpformat = NULL; ajuint i = 0; /* ajDebug("ajAssemoutformatFind '%S'\n", format); */ if(!ajStrGetLen(format)) return ajFalse; ajStrAssignS(&tmpformat, format); ajStrFmtLower(&tmpformat); for(i=0; assemoutFormatDef[i].Name; i++) { /* ajDebug("test %d '%s' \n", i, assemoutFormatDef[i].Name); */ if(ajStrMatchCaseC(tmpformat, assemoutFormatDef[i].Name)) { *iformat = i; ajStrDel(&tmpformat); /* ajDebug("found '%s' at %d\n", assemoutFormatDef[i].Name, i); */ return ajTrue; } } ajErr("Unknown output format '%S'", format); ajStrDel(&tmpformat); *iformat = 0; return ajFalse; } ID ajAssemoutformatTest TY public MO ajassemwrite LB core XX DE Tests whether a named assembly output format is known XX PN [1] PA r format const AjPStr PD Format PX RT AjBool RD ajTrue if formats was accepted RX // AjBool ajAssemoutformatTest(const AjPStr format) { ajint i; for (i=0; assemoutFormatDef[i].Name; i++) if(ajStrMatchCaseC(format, assemoutFormatDef[i].Name)) return ajTrue; return ajFalse; } ID ajAssemoutExit TY public MO ajassemwrite LB core XX DE Cleans up assembly output internal memory XX RT void RD RX // void ajAssemoutExit(void) { ajStrDel(&assemoutSamLinetxt); return; } ID assert TY public MO ajassert LB core XX DE The assert() macro inserts diagnostics into programs. XX PN [1] PA r e ajint PD test for assertion. PX RT void RD RX // void (assert) (ajint e) { assert(e); return; } ID bamInsertOffset TY static MO ajbamindex LB core XX DE Inserts a new chunk to the binning index h XX PN [1] PA u h AjPTable PD binning index PX PN [2] PA r bin ajint PD bin number PX PN [3] PA r beg ajulong PD start of target region PX PN [4] PA r end ajulong PD end of target region PX RT void RD RX // static inline void bamInsertOffset(AjPTable h, ajint bin, ajulong beg, ajulong end) { bam_binlist_t *l = NULL; int* tmp = NULL; l = ajTableFetchmodV(h, &bin); if (!l) { AJNEW0(l); l->m = 1; l->n = 0; l->list = (pair64_t*)calloc(l->m, 16); AJNEW0(tmp); *tmp = bin; ajTablePut(h, tmp, l); } if (l->n == l->m) { l->m <<= 1; l->list = (pair64_t*)realloc(l->list, l->m * 16); } l->list[l->n].u = beg; l->list[l->n++].v = end; } ID bamInsertOffset2 TY static MO ajbamindex LB core XX DE Inserts a new offset to the linear index XX PN [1] PA u index2 bam_lidx_t* PD linear index PX PN [2] PA r b const AjPSeqBam PD alignment record PX PN [3] PA r offset ajulong PD offset PX RT void RD RX // static inline void bamInsertOffset2(bam_lidx_t *index2, const AjPSeqBam b, ajulong offset) { ajint i; ajint beg; ajint end; ajint old_m; beg = b->core.pos >> BAM_LIDX_SHIFT; end = (ajSeqBamCalend(&b->core, MAJSEQBAMCIGAR(b)) - 1) >> BAM_LIDX_SHIFT; if (index2->m < end + 1) { old_m = index2->m; index2->m = end + 1; kroundup32(index2->m); index2->offset = (ajulong*)realloc(index2->offset, index2->m * 8); memset(index2->offset + old_m, 0, 8 * (index2->m - old_m)); } if (beg == end) { if (index2->offset[beg] == 0) index2->offset[beg] = offset; } else { for (i = beg; i <= end; ++i) if (index2->offset[i] == 0) index2->offset[i] = offset; } index2->n = end + 1; } ID bamMergeChunks TY static MO ajbamindex LB core XX DE merge binning index chunks XX PN [1] PA u idx AjPBamIndex PD index object PX RT void RD RX // static void bamMergeChunks(AjPBamIndex idx) { #if defined(BAM_TRUE_OFFSET) || defined(BAM_VIRTUAL_OFFSET16) AjPTable bindex = NULL; bam_binlist_t* p = NULL; bam_binlist_t** bins = NULL; ajint** binids = NULL; int i, m; ajulong n; ajuint l; ajulong k =0UL; for (i = 0; i < idx->n; ++i) { bindex = idx->bindex[i]; n = ajTableToarrayKeysValues(bindex, (void***)&binids, (void***)&bins); for (k = 0; kn; ++l) { #ifdef BAM_TRUE_OFFSET if (p->list[m].v + BAM_MIN_CHUNK_GAP > p->list[l].u) p->list[m].v = p->list[l].v; #else if (p->list[m].v>>16 == p->list[l].u>>16) p->list[m].v = p->list[l].v; #endif else p->list[++m] = p->list[l]; } p->n = m + 1; } } AJFREE(bins); AJFREE(binids); #endif } ID bamFillMissing TY static MO ajbamindex LB core XX DE Fills missing linear index bits with the values of previous cells DE in the index XX PN [1] PA u idx AjPBamIndex PD index object PX RT void RD RX // static void bamFillMissing(AjPBamIndex idx) { bam_lidx_t *idx2 = NULL; ajint i; ajint j; for (i = 0; i < idx->n; ++i) { idx2 = &idx->index2[i]; for (j = 1; j < idx2->n; ++j) if (idx2->offset[j] == 0) idx2->offset[j] = idx2->offset[j-1]; } } ID bamIndexCore TY static MO ajbamindex LB core XX DE Reads BAM index files XX PN [1] PA u fp AjPSeqBamBgzf PD BAM file handler PX RT AjPBamIndex RD Bam index object RX // static AjPBamIndex bamIndexCore(AjPSeqBamBgzf fp) { AjPSeqBam b = NULL; AjPSeqBamHeader h = NULL; AjPBamIndex idx = NULL; AjPSeqBamCore c = NULL; ajint i; ajint ret; ajuint last_bin, save_bin; ajint last_coor; ajint last_tid; ajint save_tid; ajlong last_off; ajulong save_off, n_mapped, n_unmapped, off_beg, off_end, n_no_coor; AJNEW0(idx); AJNEW0(b); h = ajSeqBamHeaderRead(fp); c = &b->core; idx->n = h->n_targets; ajSeqBamHeaderDel(&h); idx->bindex = (AjPTable*)calloc(idx->n, sizeof(AjPTable)); for (i = 0; i < idx->n; ++i) { idx->bindex[i] = ajTableintNew(100); } idx->index2 = (bam_lidx_t*)calloc(idx->n, sizeof(bam_lidx_t)); save_bin = save_tid = last_tid = last_bin = 0xffffffffu; save_off = last_off = bam_tell(fp); last_coor = 0xffffffffu; n_mapped = n_unmapped = n_no_coor = off_end = 0; off_beg = off_end = bam_tell(fp); while ((ret = ajSeqBamRead(fp, b)) >= 0) { if (c->tid < 0) ++n_no_coor; if (last_tid < c->tid || (last_tid >= 0 && c->tid < 0)) { /* change of chromosomes/reference-sequences */ last_tid = c->tid; last_bin = 0xffffffffu; } else if (last_tid > c->tid) { ajErr("[bam_index_core] the alignment is not sorted (%s):" " %d-th chr > %d-th chr\n", MAJSEQBAMQNAME(b), last_tid+1, c->tid+1); return NULL; } else if (c->tid >= 0 && last_coor > c->pos) { ajErr("[bam_index_core] the alignment is not sorted (%s):" " %u > %u in %d-th chr\n", MAJSEQBAMQNAME(b), last_coor, c->pos, c->tid+1); return NULL; } if (c->tid >= 0 && !(c->flag & BAM_FUNMAP)) bamInsertOffset2(&idx->index2[b->core.tid], b, last_off); if (c->bin != last_bin) /* then possibly write the binning index */ { /* save_bin==0xffffffffu only happens to the first record */ if (save_bin != 0xffffffffu) bamInsertOffset(idx->bindex[save_tid], save_bin, save_off, last_off); if (last_bin == 0xffffffffu && save_tid != -1) { /* write the meta element */ off_end = last_off; bamInsertOffset(idx->bindex[save_tid], BAM_MAX_BIN, off_beg, off_end); bamInsertOffset(idx->bindex[save_tid], BAM_MAX_BIN, n_mapped, n_unmapped); n_mapped = n_unmapped = 0; off_beg = off_end; } save_off = last_off; save_bin = last_bin = c->bin; save_tid = c->tid; if (c->tid < 0) break; } if (bam_tell(fp) <= last_off) { ajErr("bamIndexCore: bug in BGZF/RAZF: %llx < %llx\n", (unsigned long long)bam_tell(fp), (unsigned long long)last_off); return NULL; } if (c->flag & BAM_FUNMAP) ++n_unmapped; else ++n_mapped; last_off = bam_tell(fp); last_coor = b->core.pos; } if (save_tid >= 0) { bamInsertOffset(idx->bindex[save_tid], save_bin, save_off, bam_tell(fp)); bamInsertOffset(idx->bindex[save_tid], BAM_MAX_BIN, off_beg, bam_tell(fp)); bamInsertOffset(idx->bindex[save_tid], BAM_MAX_BIN, n_mapped, n_unmapped); } bamMergeChunks(idx); bamFillMissing(idx); if (ret >= 0) { while ((ret = ajSeqBamRead(fp, b)) >= 0) { ++n_no_coor; if (c->tid >= 0 && n_no_coor) { ajErr("bamIndexCore: the alignment is not sorted:" " reads without coordinates" " prior to reads with coordinates."); return NULL; } } } if (ret < -1) ajWarn("bamIndexCore: truncated file"); AJFREE(b->data); AJFREE(b); idx->n_no_coor = n_no_coor; return idx; } ID bamBinDel TY static MO ajbamindex LB core XX DE Deletes a bin of a binning index DE DE is called from ajTableSetDestroyvalue function XX PN [1] PA d bin void** PD bin object in a binning index PX RT void RD RX // static void bamBinDel(void** bin) { bam_binlist_t* p = *bin; AJFREE(p->list); AJFREE(p); *bin = NULL; } ID ajBamIndexDel TY public MO ajbamindex LB core XX DE Deletes a BAM index object XX PN [1] PA d Pidx AjPBamIndex* PD index object PX RT void RD RX // void ajBamIndexDel(AjPBamIndex* Pidx) { AjPBamIndex idx = NULL; int i; idx = *Pidx; if (idx == 0) return; for (i = 0; i < idx->n; ++i) { bam_lidx_t *index2 = idx->index2 + i; free(index2->offset); ajTableSetDestroyvalue(idx->bindex[i], bamBinDel); ajTableDel(&idx->bindex[i]); } AJFREE(idx->index2); AJFREE(idx->bindex); AJFREE(*Pidx); } ID bamIndexSave TY static MO ajbamindex LB core XX DE Save an index XX PN [1] PA r idx const AjPBamIndex PD index object PX PN [2] PA u fp FILE* PD file pointer PX RT void RD RX // static void bamIndexSave(const AjPBamIndex idx, FILE *fp) { AjPTable bindex = NULL; bam_lidx_t *index2 = NULL; bam_binlist_t **bins = NULL; ajuint **binids = NULL; ajulong l = 0; ajint i=0; ajuint j=0; ajuint size=0; ajint k=0; ajuint x = 0; ajint bam_is_be = 0; bam_is_be = ajUtilGetBigendian(); fwrite("BAI\1", 1, 4, fp); if (bam_is_be) { x = idx->n; ajByteRevLen4u(&x); fwrite(&x, 4, 1, fp); } else fwrite(&idx->n, 4, 1, fp); for (i = 0; i < idx->n; ++i) { bindex = idx->bindex[i]; index2 = idx->index2 + i; /* binning index */ /* # distinct bins */ size = (ajuint) ajTableToarrayKeysValues(bindex, (void***)&binids, (void***)&bins); if (bam_is_be) /*big endian*/ { x = size; ajByteRevLen4u(&x); fwrite(&x, 4, 1, fp); } else fwrite(&size, 4, 1, fp); for (j = 0; j < size; ++j) { bam_binlist_t *binlist = bins[j]; if (bam_is_be) /*big endian*/ { x = *binids[j]; ajByteRevLen4u(&x); fwrite(&x, 4, 1, fp); x = binlist->n; ajByteRevLen4u(&x); fwrite(&x, 4, 1, fp); for (x = 0; x < binlist->n; ++x) { ajByteRevLen8u((ajulong*)&binlist->list[x].u); ajByteRevLen8u((ajulong*)&binlist->list[x].v); } fwrite(binlist->list, 16, binlist->n, fp); for (x = 0; x < binlist->n; ++x) { ajByteRevLen8u((ajulong*)&binlist->list[x].u); ajByteRevLen8u((ajulong*)&binlist->list[x].v); } } else { fwrite(binids[j], 4, 1, fp); fwrite(&binlist->n, 4, 1, fp); fwrite(binlist->list, 16, binlist->n, fp); } } /* write linear index (index2) */ if (bam_is_be) { x = index2->n; ajByteRevLen4u(&x); fwrite(&x, 4, 1, fp); } else fwrite(&index2->n, 4, 1, fp); if (bam_is_be) { for (k = 0; k < index2->n; ++k) ajByteRevLen8u((ajulong*)&index2->offset[k]); fwrite(index2->offset, 8, index2->n, fp); for (k = 0; k < index2->n; ++k) ajByteRevLen8u((ajulong*)&index2->offset[k]); } else fwrite(index2->offset, 8, index2->n, fp); AJFREE(bins); AJFREE(binids); } { /* write the number of reads without coordinate */ l = idx->n_no_coor; if (bam_is_be) ajByteRevLen8u((ajulong*)&l); fwrite(&l, 8, 1, fp); } fflush(fp); } ID bamIndexLoadCore TY static MO ajbamindex LB core XX DE Load BAM index data from a file XX PN [1] PA u fp FILE* PD file pointer PX RT AjPBamIndex RD BAM index RX // static AjPBamIndex bamIndexLoadCore(FILE *fp) { AjPBamIndex idx = NULL; ajint i; char magic[4]; ajint bam_is_be = 0; bam_lidx_t *index2 = NULL; ajuint binid; ajint n_bin; /* # distinct bins */ ajint j; bam_binlist_t *binlist = NULL; ajint* tmp = NULL; ajuint x; bam_is_be = ajUtilGetBigendian(); if (fp == 0) { ajErr("bamIndexLoadCore: fail to load index.\n"); return 0; } fread(magic, 1, 4, fp); if (strncmp(magic, "BAI\1", 4)) { ajErr("bamIndexLoadCore: wrong magic number.\n"); fclose(fp); return 0; } AJNEW0(idx); fread(&idx->n, 4, 1, fp); if (bam_is_be) ajByteRevLen4(&idx->n); ajDebug("# reference sequences: %d\n", idx->n); idx->bindex = (AjPTable*)calloc(idx->n, sizeof(AjPTable)); idx->index2 = (bam_lidx_t*)calloc(idx->n, sizeof(bam_lidx_t)); for (i = 0; i < idx->n; ++i) { index2 = idx->index2 + i; binlist = NULL; idx->bindex[i] = ajTableintNew(10); /* load binning index */ fread(&n_bin, 4, 1, fp); if (bam_is_be) ajByteRevLen4(&n_bin); ajDebug("# distinct bins (for the reference sequence %d): %d\n", i, n_bin); for (j = 0; j < n_bin; ++j) { fread(&binid, 4, 1, fp); if (bam_is_be) ajByteRevLen4u(&binid); AJNEW0(tmp); *tmp = binid; AJNEW0(binlist); ajTablePut(idx->bindex[i], tmp, binlist); fread(&binlist->n, 4, 1, fp); if (bam_is_be) ajByteRevLen4u(&binlist->n); ajDebug("\tbin %u has %d chunks\n", binid, binlist->n); binlist->m = binlist->n; binlist->list = (pair64_t*)malloc(binlist->m * 16); fread(binlist->list, 16, binlist->n, fp); if (bam_is_be) { for (x = 0; x < binlist->n; ++x) { ajByteRevLen8u((ajulong*)&binlist->list[x].u); ajByteRevLen8u((ajulong*)&binlist->list[x].v); } } if (ajDebugOn()) for (x = 0; x < binlist->n; ++x) { ajlong mChunkStart = binlist->list[x].u; ajlong mChunkEnd = binlist->list[x].v; ajDebug("\t\tchunk: %d:%d-%d:%d\n", mChunkStart >> 16, mChunkStart & 0xFFFF, mChunkEnd >> 16, mChunkEnd & 0xFFFF); } } /* load linear index */ fread(&index2->n, 4, 1, fp); if (bam_is_be) ajByteRevLen4(&index2->n); index2->m = index2->n; ajDebug("\tRef %u has %d 16kbp intervals\n", i, index2->n); index2->offset = (ajulong*)calloc(index2->m, 8); fread(index2->offset, index2->n, 8, fp); if (bam_is_be) for (j = 0; j < index2->n; ++j) ajByteRevLen8u((ajulong*)&index2->offset[j]); } if (fread(&idx->n_no_coor, 8, 1, fp) == 0) idx->n_no_coor = 0; if (bam_is_be) ajByteRevLen8u((ajulong*)&idx->n_no_coor); return idx; } ID ajBamIndexLoad TY public MO ajbamindex LB core XX DE Load index from file "filename.bai" DE DE TODO: ftp and http support not yet implemented XX PN [1] PA r _fn const char* PD name of the BAM file (NOT the index file) PX RT AjPBamIndex RD BAM index object RX // AjPBamIndex ajBamIndexLoad(const char *_fn) { FILE *fp = NULL; AjPBamIndex idx = NULL; char *fnidx = NULL; char *fn = NULL; char *s = NULL; const char *p = NULL; ajint l = 0; if (strstr(_fn, "ftp://") == _fn || strstr(_fn, "http://") == _fn) { l = strlen(_fn); for (p = _fn + l - 1; p >= _fn; --p) if (*p == '/') break; fn = strdup(p + 1); } else fn = strdup(_fn); fnidx = (char*)calloc(strlen(fn) + 5, 1); strcpy(fnidx, fn); strcat(fnidx, ".bai"); fp = fopen(fnidx, "rb"); if (fp == 0) { /* try "{base}.bai" */ s = strstr(fn, "bam"); if (s == fn + strlen(fn) - 3) { strcpy(fnidx, fn); fnidx[strlen(fn)-1] = 'i'; fp = fopen(fnidx, "rb"); } } free(fnidx); free(fn); if (fp) { idx = bamIndexLoadCore(fp); fclose(fp); return idx; } else return NULL; } ID ajBamIndexBuild TY public MO ajbamindex LB core XX DE Build index for a BAM file, using .bai extension for the index file XX PN [1] PA r fn const char* PD name of the BAM file PX RT int RD 0 if the index build was successful RX // int ajBamIndexBuild(const char *fn) { char *fnidx; FILE *fpidx = NULL; AjPSeqBamBgzf fp = NULL; AjPBamIndex idx = NULL; if(!strcmp(fn, "stdout")) return -1; if ((fp = ajSeqBamBgzfOpenC(fn, "r")) == 0) { ajErr("ajBamIndexBuild: fail to open the BAM file '%s'.", fn); return -1; } idx = bamIndexCore(fp); ajSeqBamBgzfClose(fp); if(idx == 0) { ajErr("ajBamIndexBuild: fail to index the BAM file '%s'.", fn); return -1; } fnidx = (char*)calloc(strlen(fn) + 5, 1); strcpy(fnidx, fn); strcat(fnidx, ".bai"); fpidx = fopen(fnidx, "wb"); if (fpidx == 0) { ajErr("ajBamIndexBuild: fail to create the index file '%s'.", fn); free(fnidx); return -1; } bamIndexSave(idx, fpidx); AJFREE(fnidx); ajBamIndexDel(&idx); fclose(fpidx); return 0; } ID bamReg2bins TY static MO ajbamindex LB core XX DE Calculate the list of bins that may overlap with region [beg, end) DE (zero based). DE DE picard def: Get candidate bins for the specified region. XX PN [1] PA r beg ajuint PD 1-based start of target region, inclusive PX PN [2] PA r end ajuint PD 1-based end of target region, inclusive PX PN [3] PA w list ajushort[BAM_MAX_BIN] PD list of bins that may overlap PD with region PX RT int RD number of bins RX // static inline int bamReg2bins(ajuint beg, ajuint end, ajushort list[BAM_MAX_BIN]) { ajuint i=0; ajuint k=0; if (beg >= end) return 0; if (end >= 1u<<29) end = 1u<<29; --end; list[i++] = 0; for (k = 1 + (beg>>26); k <= 1 + (end>>26); ++k) list[i++] = k; for (k = 9 + (beg>>23); k <= 9 + (end>>23); ++k) list[i++] = k; for (k = 73 + (beg>>20); k <= 73 + (end>>20); ++k) list[i++] = k; for (k = 585 + (beg>>17); k <= 585 + (end>>17); ++k) list[i++] = k; for (k = 4681 + (beg>>14); k <= 4681 + (end>>14); ++k) list[i++] = k; return i; } ID bamIsOverlap TY static MO ajbamindex LB core XX DE Checks whether a region overlaps a BAM record XX PN [1] PA r beg ajuint PD region start position PX PN [2] PA r end ajuint PD region end position PX PN [3] PA r b const AjPSeqBam PD BAM record PX RT int RD 1 if the region overlaps the BAM record, 0 otherwise RX // static inline int bamIsOverlap(ajuint beg, ajuint end, const AjPSeqBam b) { ajuint rbeg = b->core.pos; ajuint rend = 0; if(b->core.n_cigar) rend = ajSeqBamCalend(&b->core, MAJSEQBAMCIGAR(b)); else rend = b->core.pos + 1; return (rend > beg && rbeg < end); } ID bamChunkCompare TY static MO ajbamindex LB core XX DE Compares the virtual file offset of the start of two chunks DE for binning indices XX PN [1] PA r a const void* PD first chunk PX PN [2] PA r b const void* PD second chunk PX RT ajint RD 1 if the first virtual offset is smaller, 0 otherwise RX // static ajint bamChunkCompare(const void* a, const void* b) { return (((pair64_t const *)a)->u > ((pair64_t const *)b)->u); } ID bamIterQuery TY static MO ajbamindex LB core XX DE query a region on a reference sequence XX PN [1] PA r idx const AjPBamIndex PD BAM index PX PN [2] PA r tid int PD reference sequence id PX PN [3] PA r beg int PD 1-based start of target region, inclusive PX PN [4] PA r end int PD 1-based end of target region, inclusive PX RT bam_iter_t RD iteration object RX // static bam_iter_t bamIterQuery(const AjPBamIndex idx, int tid, int beg, int end) { pair64_t *off=NULL; AjPTable bindex = NULL; AjPSeqBam b = NULL; ajushort *bins=NULL; const bam_binlist_t *p = NULL; ajulong min_off; bam_iter_t iter = 0; ajint i=0; ajint n_bins=0; ajint n_off=0; ajint key; ajint l; ajint n; ajuint j; if (beg < 0) beg = 0; if (end < beg) return 0; /* initialize iter */ iter = calloc(1, sizeof(struct __bam_iter_t)); iter->tid = tid; iter->beg = beg; iter->end = end; iter->i = -1; bins = (ajushort*)calloc(BAM_MAX_BIN, 2); n_bins = bamReg2bins(beg, end, bins); bindex = idx->bindex[tid]; if (idx->index2[tid].n > 0) { if (beg>>BAM_LIDX_SHIFT >= idx->index2[tid].n) min_off = idx->index2[tid].offset[idx->index2[tid].n-1]; else min_off = idx->index2[tid].offset[beg>>BAM_LIDX_SHIFT]; if (min_off == 0) { /* improvement for mapindex files built by tabix prior to 0.1.4 */ n = beg>>BAM_LIDX_SHIFT; if (n > idx->index2[tid].n) n = idx->index2[tid].n; for (i = n - 1; i >= 0; --i) if (idx->index2[tid].offset[i] != 0) break; if (i >= 0) min_off = idx->index2[tid].offset[i]; } } else min_off = 0; /* tabix 0.1.2 may produce such mapindex files */ /* iterate over the list of bins that may overlap with region */ for (i = n_off = 0; i < n_bins; ++i) { key = bins[i]; p = ajTableFetchV(bindex, &key); if(p) { n_off += p->n; ajDebug("bin:%u # chunks:%d\n", key, p->n); } } ajDebug("n_off:%d\n", n_off); if (n_off == 0) { free(bins); return iter; } off = (pair64_t*)calloc(n_off, 16); for (i = n_off = 0; i < n_bins; ++i) { key = bins[i]; p = ajTableFetchV(bindex, &key); if(p) { for (j = 0; j < p->n; ++j) { if (p->list[j].v > min_off) off[n_off++] = p->list[j]; ajDebug("p->list[%d].v:%u min_off:%d\n", j, p->list[j].v, min_off); } } } free(bins); if (n_off == 0) { free(off); return iter; } AJNEW0(b); qsort(off, n_off, sizeof(pair64_t), bamChunkCompare); /* resolve completely contained adjacent blocks */ for (i = 1, l = 0; i < n_off; ++i) if (off[l].v < off[i].v) off[++l] = off[i]; n_off = l + 1; /* resolve overlaps between adjacent blocks; * this may happen due to the merge in indexing */ for (i = 1; i < n_off; ++i) if (off[i-1].v >= off[i].u) off[i-1].v = off[i].u; { /* merge adjacent blocks */ #if defined(BAM_TRUE_OFFSET) || defined(BAM_VIRTUAL_OFFSET16) for (i = 1, l = 0; i < n_off; ++i) { #ifdef BAM_TRUE_OFFSET if (off[l].v + BAM_MIN_CHUNK_GAP > off[i].u) off[l].v = off[i].v; #else if (off[l].v>>16 == off[i].u>>16) off[l].v = off[i].v; #endif else off[++l] = off[i]; } n_off = l + 1; #endif } ajSeqBamDel(&b); iter->n_off = n_off; iter->off = off; return iter; } ID bamIterDel TY static MO ajbamindex LB core XX DE Deletes a BAM iteration object XX PN [1] PA d iterp bam_iter_t* PD pointer to iteration object to be deleted PX RT void RD RX // static void bamIterDel(bam_iter_t* iterp) { bam_iter_t iter; iter = *iterp; if (iter) { free(iter->off); free(iter); } *iterp = NULL; } ID bamIterRead TY static MO ajbamindex LB core XX DE ajBamFetch helper function retrieves data into alignment record b XX PN [1] PA u fp AjPSeqBamBgzf PD BAM file PX PN [2] PA u iter bam_iter_t PD BAM iteration object PX PN [3] PA u b AjPSeqBam PD BAM record PX RT int RD positive integer (1 or more) if a record successfully read RX // static int bamIterRead(AjPSeqBamBgzf fp, bam_iter_t iter, AjPSeqBam b) { int ret; if (iter && iter->finished) return -1; if (iter == 0 || iter->from_first) { ret = ajSeqBamRead(fp, b); if (ret < 0 && iter) iter->finished = 1; return ret; } if (iter->off == 0) return -1; for (;;) { if (iter->curr_off == 0 || iter->curr_off >= iter->off[iter->i].v) { /* then jump to the next chunk */ if (iter->i == iter->n_off - 1) { ret = -1; break; /* no more chunks */ } if (iter->i >= 0) assert(iter->curr_off==iter->off[iter->i].v); /*otherwise bug*/ if (iter->i < 0 || iter->off[iter->i].v != iter->off[iter->i+1].u) { /* not adjacent chunks; then seek */ ajSeqBamBgzfSeek(fp, iter->off[iter->i+1].u, SEEK_SET); iter->curr_off = bam_tell(fp); } ++iter->i; } if ((ret = ajSeqBamRead(fp, b)) >= 0) { iter->curr_off = bam_tell(fp); if (b->core.tid != iter->tid || b->core.pos >= iter->end) { /* no need to proceed */ /* determine whether end of region or error */ ret = ajSeqBamValidate(NULL, b)? -1 : -5; break; } else if (bamIsOverlap(iter->beg, iter->end, b)) return ret; } else break; /* end of file or error */ } iter->finished = 1; return ret; } ID ajBamFetch TY public MO ajbamindex LB core XX DE Retrieve the alignments that are overlapped with the specified region. XX PN [1] PA u fp AjPSeqBamBgzf PD BAM file handler PX PN [2] PA r idx const AjPBamIndex PD pointer to the alignment index PX PN [3] PA r tid int PD chromosome ID as is defined in the header PX PN [4] PA r beg int PD start coordinate, 0-based PX PN [5] PA r end int PD end coordinate, 0-based PX PN [6] PA u data void* PD user provided data PD (will be transferred to func) PX PN [7] PA f func bam_fetch_f PD user defined function PX RT int RD Comparison value. 0 if equal, -1 if first is lower, RD +1 if first is higher. RX // int ajBamFetch(AjPSeqBamBgzf fp, const AjPBamIndex idx, int tid, int beg, int end, void *data, bam_fetch_f func) { int ret; bam_iter_t iter = NULL; AjPSeqBam b; AJNEW0(b); iter = bamIterQuery(idx, tid, beg, end); while ((ret = bamIterRead(fp, iter, b)) >= 0) func(b, data); bamIterDel(&iter); ajSeqBamDel(&b); return (ret == -1)? 0 : ret; } ID ajBaseAlphaCompare TY public MO ajbase LB core XX DE Returns an element of the base match probability array XX PN [1] PA r base ajint PD First base offset PX PN [2] PA r base2 ajint PD Second base offset PX RT float RD Base probability value RX // float ajBaseAlphaCompare(ajint base, ajint base2) { ajint b1; ajint b2; if(!aj_base_I) baseInit(); b1 = base; b2 = base2; if(b1<0) b1=0; if(b1>31) b1=31; if(b2<0) b2=0; if(b2>31) b2=31; return aj_base_prob[b1][b2]; } ID ajBaseAlphaToBin TY public MO ajbase LB core XX DE Returns a binary OR'd representation of an IUB base where A=1, C=2, DE G=4 and T=8 DE Uses the base table set up by daseInit XX PN [1] PA r base ajint PD character to convert PX RT ajint RD Binary OR'd representation RX // ajint ajBaseAlphaToBin(ajint base) { if(!aj_base_I) baseInit(); return (aj_base_table[toupper(base)]); } ID ajBaseAlphacharCompare TY public MO ajbase LB core XX DE Check ambiguity codes (IUB) to estimate the distance score. XX PN [1] PA r c char PD First base to compare PX PN [2] PA r c2 char PD Second base to compare PX RT float RD estimated match RX // float ajBaseAlphacharCompare(char c, char c2) { const AjPStr b1 = NULL; const AjPStr b2 = NULL; AjPStr b = NULL; AjPRegexp rexp = NULL; AjBool pmatch = ajFalse; ajuint i; float n; ajint len1; ajint len2; if(!aj_base_I) baseInit(); len1 = ajStrGetLen(aj_base_iubS[(int)c].list)-1; len2 = ajStrGetLen(aj_base_iubS[(int)c2].list)-1; b1 = aj_base_iubS[(int)c].list; b2 = aj_base_iubS[(int)c2].list; /* ** for each base code in 1 cf. base code ** for seq 2 to see if there is a match */ for(i = 0;i < (ajuint) len1;i++) { b = ajStrNew(); ajStrAssignSubS(&b,b1,i,i); rexp = ajRegComp(b); if(ajRegExec(rexp,b2)) pmatch = ajTrue; ajRegFree(&rexp); ajStrDel(&b); } if(pmatch) n = ((float)1./len1)*((float)1./len2); else n = 0.; return n; } ID ajBaseAlphacharToBin TY public MO ajbase LB core XX DE Returns a binary OR'd representation of an IUB base where A=1, C=2, DE G=4 and T=8 DE Uses the base table set up by baseInit XX PN [1] PA r c char PD character to convert PX RT char RD Binary OR'd representation RX // char ajBaseAlphacharToBin(char c) { if(!aj_base_I) baseInit(); return ajSysCastItoc(aj_base_table[toupper((ajint) c)]); } ID ajBaseAlphacharComp TY public MO ajbase LB core XX DE Complements a nucleotide base. XX PN [1] PA r c char PD Base character. PX RT char RD Complementary base. RX // char ajBaseAlphacharComp(char c) { static char fwd[]="ACGTURYWSMKBDHVNXacgturywsmkbdhvnx"; static char rev[]="TGCAAYRWSKMVHDBNXtgcaayrwskmvhdbnx"; char *cp; char *cq; cp = strchr(fwd,c); if(cp) { cq = cp - fwd + rev; return *cq; } return c; } ID ajBaseBinToAlpha TY public MO ajbase LB core XX DE Converts a binary OR'd representation of an IUB base where A=1, C=2, DE G=4 and T=8 into an ambiguous DNA base code (uses T rather than U). DE DE Uses the base table set up by baseInit XX PN [1] PA r c ajint PD character to convert PX RT char RD Ambiguous DNA base code RX // char ajBaseBinToAlpha(ajint c) { if(c<0) return 'N'; if(c>15) return 'N'; return (iubbases[c]); } ID ajBaseExistsBin TY public MO ajbase LB core XX DE Tests whether a base code exists XX PN [1] PA r base ajint PD Base code in range 0 to 31 PX RT AjBool RD True if base code is known RX // AjBool ajBaseExistsBin(ajint base) { if(!aj_base_I) baseInit(); if(ajStrGetLen(aj_base_iubS[base].code)) return ajTrue; return ajFalse; } ID ajBaseExistsChar TY public MO ajbase LB core XX DE Tests whether a base code exists XX PN [1] PA r c char PD Base character PX RT AjBool RD True if base code is known RX // AjBool ajBaseExistsChar(char c) { int itest; if(!aj_base_I) baseInit(); itest = toupper((int)c); if(ajStrGetLen(aj_base_iubS[itest].code)) return ajTrue; itest = tolower((int)c); if(ajStrGetLen(aj_base_iubS[itest].code)) return ajTrue; return ajFalse; } ID ajBaseGetCodes TY public MO ajbase LB core XX DE Returns a string of matching base codes XX PN [1] PA r base ajint PD Original base code PX RT const AjPStr RD Base codes RX // const AjPStr ajBaseGetCodes(ajint base) { if(!aj_base_I) baseInit(); return aj_base_iubS[base].list; } ID ajBaseGetMnemonic TY public MO ajbase LB core XX DE Returns a string of matching base codes XX PN [1] PA r base ajint PD Original base code PX RT const AjPStr RD Base codes RX // const AjPStr ajBaseGetMnemonic(ajint base) { if(!aj_base_I) baseInit(); return aj_base_iubS[base].mnemonic; } ID baseInit TY static MO ajbase LB core XX DE Sets up binary OR'd representation of an IUB bases in a table DE aj_base_table where A=1, C=2, G=4 and T=8 DE Also sets up a match probability array aj_base_prob holding the DE probability of one IUB base matching any other. DE Uses the Ebases.iub file DE Is initialised if necessary from other AJAX functions. XX RT AjBool RD True on success RX // static AjBool baseInit(void) { AjPFile bfptr = NULL; AjPStr bfname = NULL; AjPStr line = NULL; AjPStr code = NULL; AjPStr list = NULL; AjPStr mnemonic = NULL; ajint i; ajint j; ajint k; ajint c; ajint qc; ajint l1; ajint l2; ajint x; ajint y; ajint n; const char *p; const char *q; if(aj_base_I) return ajTrue; for(i=0;i<256;++i) { aj_base_iubS[i].code = ajStrNewC(""); aj_base_iubS[i].list = ajStrNewC(""); aj_base_iubS[i].mnemonic = ajStrNewC(""); aj_base_table[i] = 0; } code = ajStrNew(); list = ajStrNew(); ajStrAssignClear(&code); ajStrAssignC(&list,"ACGT"); bfname = ajStrNewC(IUBFILE); bfptr = ajDatafileNewInNameS(bfname); if(!bfptr) ajFatal("%S file not found\n", bfname); line = ajStrNew(); while(ajReadline(bfptr, &line)) { p = ajStrGetPtr(line); if(*p=='#' || *p=='!' || *p=='\n') continue; p = ajSysFuncStrtok(p," \t\r\n"); ajStrAssignC(&code,p); p=ajSysFuncStrtok(NULL," \t\r\n"); if(sscanf(p,"%d",&n)!=1) ajFatal("Bad format IUB file"); p = ajSysFuncStrtok(NULL," \t\r\n"); ajStrAssignC(&list,p); p = ajSysFuncStrtok(NULL," \t\r\n"); ajStrAssignC(&mnemonic,p); qc = (ajint) ajStrGetCharFirst(code); ajStrAssignS(&aj_base_iubS[toupper(qc)].code,code); ajStrAssignS(&aj_base_iubS[toupper(qc)].list,list); ajStrAssignS(&aj_base_iubS[toupper(qc)].mnemonic,mnemonic); ajStrAssignS(&aj_base_iubS[tolower(qc)].code,code); ajStrAssignS(&aj_base_iubS[tolower(qc)].list,list); ajStrAssignS(&aj_base_iubS[tolower(qc)].mnemonic,mnemonic); aj_base_table[toupper(qc)] = n; aj_base_table[tolower(qc)] = n; } ajStrDel(&code); ajStrDel(&list); ajStrDel(&line); ajStrDel(&mnemonic); ajStrDel(&bfname); ajFileClose(&bfptr); for(i=0;i<32;++i) { x = ajBasecodeFromInt(i); for(j=0;j<32;++j) { y = ajBasecodeFromInt(j); if(!(l1=ajStrGetLen(aj_base_iubS[x].code))) { aj_base_prob[i][j]=0.0; continue; } if(l1!=1) ajFatal("Bad IUB letter"); p = ajStrGetPtr(aj_base_iubS[x].list); q = ajStrGetPtr(aj_base_iubS[y].list); l1 = strlen(p); l2 = strlen(q); for(k=0,c=0;k25) return ajFalse; ajStrAssignC(Paa3, BaseAaTable[idx]); return ajTrue; } ID ajBasecodeFromInt TY public MO ajbase LB core XX DE Returns 'A' for 0 to 'Z' for 25 XX PN [1] PA r n ajint PD character to convert PX RT ajint RD 0 as 'A' up to 25 as 'Z' RX // ajint ajBasecodeFromInt(ajint n) { if(n>25) return (ajint) '*'; if(n<0) return (ajint) '*'; return(n+(ajint)'A'); } ID ajBasecodeToInt TY public MO ajbase LB core XX DE Returns A=0 to Z=25 or 27 otherwise XX PN [1] PA r c ajint PD character to convert PX RT ajint RD A=0 to Z=25 or 27 if unknown RX // ajint ajBasecodeToInt(ajint c) { ajint ic = toupper(c); if(ic >= (ajint) 'A' && ic <= (ajint) 'Z') return(ic - (ajint)'A'); return 27; } ID ajCallTableNew TY public MO ajcall LB core XX DE Create new function hash table XX RT AjPTable RD Hash table with string keys RX // AjPTable ajCallTableNew(void) { return ajTablecharNew(50); } ID ajCallTableRegister TY public MO ajcall LB core XX DE Create hash value pair using the name and function. XX PN [1] PA w table AjPTable PD name which is used later. PX PN [2] PA r name const char* PD name which is used later. PX PN [3] PA f func void* PD Access structure for function to be invoked by name PX RT void RD RX // void ajCallTableRegister(AjPTable table, const char *name, void *func) { const void *rec; char* keyname = NULL; if(!table) ajFatal("ajCallTableRegister called for undefined table with name '%s'", name); rec = ajTableFetchC(table, name); /* does it exist already */ if(!rec) { keyname = ajCharNewC(name); ajTablePut(table, keyname, (void *) func); } else { ajWarn("ajCallTableRegister duplicate name '%s'", name); } return; } ID ajCallRegister TY public MO ajcall LB core XX DE Create hash value pair using the name and function. XX PN [1] PA r name const char* PD name which is used later.. PX PN [2] PA f func CallFunc PD function to be called on name being called. PX RT void RD RX // void ajCallRegister(const char *name, CallFunc func) { const void *rec; char* keyname = NULL; if(!callTable) callTable = ajTablecharNew(50); rec = ajTableFetchC(callTable, name); /* does it exist already */ if(!rec) { keyname = ajCharNewC(name); ajTablePut(callTable, keyname, (void *) func); } return; } ID ajCallRegisterOld TY public MO ajcall LB core XX DE Create hash value pair using an obsolete name and function. XX PN [1] PA r name const char* PD name which is used later. PX PN [2] PA f func CallFunc PD function to be called on name being called. PX RT void RD RX // void ajCallRegisterOld(const char *name, CallFunc func) { const void *rec; char* keyname = NULL; ajuint *i; if(!oldcallTable) { oldcallTable = ajTablecharNew(50); oldcallCount = ajTablecharNew(50); } rec = ajTableFetchC(oldcallTable, name); /* does it exist already */ if(!rec) { keyname = ajCharNewC(name); ajTablePut(oldcallTable, keyname, (void *) func); keyname = ajCharNewC(name); AJNEW0(i); ajTablePut(oldcallCount, keyname, i); } return; } ID ajCall TY public MO ajcall LB core XX DE Call a function by its name. If it does not exist then give DE an error message saying so. XX PN [1] PA r name const char* PD name of the function to call. PX PN [2] PA v vararg ... PD Optional arguments PX RT void* RD NULL if function call not found. RX // void* ajCall(const char *name, ...) { va_list args; CallFunc rec; CallFunc recold; void *retval = NULL; ajuint *icount; if(!callTable) { ajMessCrash("Calls to %s not registered. For graphics devices use " "ajGraphInit in main function first",name); return retval; } rec = (CallFunc) ajTableFetchmodC(callTable, name); recold = (CallFunc) ajTableFetchmodC(oldcallTable, name); if(rec) { va_start(args, name); retval = (*(rec))(name, args); va_end(args); } else if(recold) { icount = (ajuint*) ajTableFetchmodC(oldcallCount, name); if(!(*icount)++) ajWarn("Obsolete graphics call '%s' called via ajCall", name); va_start(args, name); retval = (*(recold))(name, args); va_end(args); } else { ajMessCrash("Graphics call %s not found. " "Use ajGraphInit in main function first",name); } return retval; } ID ajCallTableGetC TY public MO ajcall LB core XX DE Returns an access structure defining the named function. If it does DE not exist then gives an error message saying so. XX PN [1] PA r table const AjPTable PD Function hash table PX PN [2] PA r name const char* PD name of the function PX RT void* RD Access structure defining the named function. RD NULL if not found RX // void* ajCallTableGetC(const AjPTable table, const char *name) { void *rec; if(!table) { ajMessCrash("ajCallTableGet no call table for '%s'", name); return NULL; } rec = ajTableFetchmodC(table, name); if(!rec) return NULL; return rec; } ID ajCallTableGetS TY public MO ajcall LB core XX DE Returns an access structure defining the named function. If it does DE not exist then gives an error message saying so. XX PN [1] PA r table const AjPTable PD Function hash table PX PN [2] PA r namestr const AjPStr PD name of the function PX RT void* RD Access structure defining the named function. RD NULL if not found RX // void* ajCallTableGetS(const AjPTable table, const AjPStr namestr) { void *rec; if(!table) { ajMessCrash("ajCallTableGet no call table for '%S'", namestr); return NULL; } rec = ajTableFetchmodS(table, namestr); if(!rec) return NULL; return rec; } ID ajCallExit TY public MO ajcall LB core XX DE Cleans up calls register internal memory XX RT void RD RX // void ajCallExit(void) { ajTableDel(&callTable); ajTableDel(&oldcallTable); ajTableDel(&oldcallCount); return; } ID ajCigarGetReferenceLength TY public MO ajcigar LB core XX DE Get reference length covered by a CIGAR string XX PN [1] PA r c const AjPCigar PD cigar object PX RT ajint RD length of the aligned reference sequence RX // ajint ajCigarGetReferenceLength(const AjPCigar c) { ajint i=0; ajint length = 0; for (i=0;in;i++) { switch (c->cigoperator[i]) { case 'M': case 'D': case 'N': case '=': case 'X': length += c->length[i]; } } return length; } ID ajCigarCountInsertedBases TY public MO ajcigar LB core XX DE Count bases inserted in a CIGAR string XX PN [1] PA r c const AjPCigar PD cigar object PX RT ajint RD length of the bases in the read but not in the reference seq RX // ajint ajCigarCountInsertedBases(const AjPCigar c) { ajint i=0; ajint length = 0; for (i=0; in; i++) { if (c->cigoperator[i]=='I') length += c->length[i]; } return length; } ID ajCigarCountDeletedBases TY public MO ajcigar LB core XX DE Count bases deleted in a CIGAR string XX PN [1] PA r c const AjPCigar PD cigar object PX RT ajint RD length of the bases in the reference sequence RD but not in the read RX // ajint ajCigarCountDeletedBases(const AjPCigar c) { ajint i=0; ajint length = 0; for (i=0; in; i++) { if (c->cigoperator[i]=='D') length += c->length[i]; } return length; } ID ajCigarNewS TY public MO ajcigar LB core XX DE Cigar data constructor XX PN [1] PA r cigar const AjPStr PD cigar string PX RT AjPCigar RD New object RX // AjPCigar ajCigarNewS(const AjPStr cigar) { AjPCigar c=NULL; const char* s=NULL; long x=0; int i =0; int n=0; char op; char* t; AJNEW0(c); n = ajStrGetLen(cigar); s = ajStrGetPtr(cigar); AJCNEW0(c->cigoperator, n); AJCNEW0(c->length, n); for (i=0; *s && icigoperator[i]=op; c->length[i]=x; } else ajWarn("invalid CIGAR operator: %c",op); s = t + 1; } c->n=i; AJCRESIZE(c->cigoperator, c->n); AJCRESIZE(c->length, c->n); return c; } ID ajCigarDel TY public MO ajcigar LB core XX DE Delete cigar objects XX PN [1] PA d Pcigar AjPCigar* PD Cigar data object to delete PX RT void RD RX // void ajCigarDel(AjPCigar *Pcigar) { AjPCigar cigar; if(!Pcigar) return; if(!(*Pcigar)) return; cigar = *Pcigar; AJFREE(cigar->cigoperator); AJFREE(cigar->length); AJFREE(*Pcigar); *Pcigar = NULL; return; } ID ajCodNew TY public MO ajcod LB core XX DE Default constructor for empty AJAX codon objects. XX RT AjPCod RD Pointer to an codon object RX // AjPCod ajCodNew(void) { AjPCod thys; AJNEW0(thys); thys->Name = ajStrNew(); thys->Species = ajStrNew(); thys->Division = ajStrNew(); thys->Release = ajStrNew(); thys->Desc = ajStrNew(); AJCNEW0(thys->back, AJCODAMINOS); AJCNEW0(thys->aa, AJCODSIZE); AJCNEW0(thys->num, AJCODSIZE); AJCNEW0(thys->tcount, AJCODSIZE); AJCNEW0(thys->fraction, AJCODSIZE); return thys; } ID ajCodNewCodenum TY public MO ajcod LB core XX DE Default constructor for empty AJAX codon usage objects, with the DE amino acid assignments taken from a standard genetic code. XX PN [1] PA r code ajint PD Genetic code number PX RT AjPCod RD Pointer to an codon object RX // AjPCod ajCodNewCodenum(ajint code) { AjPCod thys; AjPTrn trn = NULL; AjPStr aa = NULL; ajint i; ajint idx; ajint c; trn = ajTrnNewI(code); if(!trn) return NULL; AJNEW0(thys); thys->Name = ajStrNew(); thys->Species = ajStrNew(); thys->Division = ajStrNew(); thys->Release = ajStrNew(); thys->Desc = ajStrNew(); AJCNEW0(thys->back, AJCODAMINOS); AJCNEW0(thys->aa, AJCODSIZE); AJCNEW0(thys->num, AJCODSIZE); AJCNEW0(thys->tcount, AJCODSIZE); AJCNEW0(thys->fraction, AJCODSIZE); for(i=0;i<64;i++) { idx = ajCodIndexC(spsumcodons[i]); ajStrAssignK(&aa, ajTrnCodonC(trn, spsumcodons[i])); c = ajBasecodeToInt((ajint)ajStrGetCharFirst(aa)); if(c>25) c=27; thys->aa[idx] = c; } ajTrnDel(&trn); ajStrDel(&aa); return thys; } ID ajCodNewCod TY public MO ajcod LB core XX DE Duplicate a codon object XX PN [1] PA r thys const AjPCod PD Codon to duplicate PX RT AjPCod RD Pointer to an codon object RX // AjPCod ajCodNewCod(const AjPCod thys) { AjPCod dupcod; ajint i; dupcod = ajCodNew(); ajStrAssignS(&dupcod->Name,thys->Name); ajStrAssignS(&dupcod->Species,thys->Species); ajStrAssignS(&dupcod->Division,thys->Division); ajStrAssignS(&dupcod->Release,thys->Release); ajStrAssignS(&dupcod->Desc,thys->Desc); dupcod->CdsCount = thys->CdsCount; dupcod->CodonCount = thys->CodonCount; dupcod->GeneticCode = thys->GeneticCode; for(i=0;iaa[i] = thys->aa[i]; dupcod->num[i] = thys->num[i]; dupcod->tcount[i] = thys->tcount[i]; dupcod->fraction[i] = thys->fraction[i]; } for(i=0;iback[i] = thys->back[i]; return dupcod; } ID ajCodDel TY public MO ajcod LB core XX DE Default destructor for AJAX codon objects. XX PN [1] PA w pthys AjPCod* PD codon usage structure PX RT void RD RX // void ajCodDel(AjPCod *pthys) { AjPCod thys = *pthys; if(!thys) return; AJFREE(thys->fraction); AJFREE(thys->tcount); AJFREE(thys->num); AJFREE(thys->aa); AJFREE(thys->back); ajStrDel(&thys->Name); ajStrDel(&thys->Species); ajStrDel(&thys->Division); ajStrDel(&thys->Release); ajStrDel(&thys->Desc); AJFREE(*pthys); return; } ID ajCodBacktranslate TY public MO ajcod LB core XX DE Back translate a string XX PN [1] PA w b AjPStr* PD back translated sequence PX PN [2] PA r a const AjPStr PD sequence PX PN [3] PA r thys const AjPCod PD codon usage object PX RT void RD RX // void ajCodBacktranslate(AjPStr *b, const AjPStr a, const AjPCod thys) { const char *p; char q; ajint idx; ajStrAssignClear(b); p = ajStrGetPtr(a); while(*p) { if(*p=='-') { ++p; continue; } if(toupper((int)*p)==(int)'X') { ajStrAppendC(b,"NNN"); ++p; continue; } q = *p; if(toupper((int)q)==(int)'B') q = 'D'; if(toupper((int)q)==(int)'Z') q = 'E'; idx = thys->back[ajBasecodeToInt(q)]; /* if(thys->aa[idx]==27) { ajStrAppendC(b,"End"); ++p; continue; } */ ajStrAppendC(b,ajCodTriplet(idx)); ++p; } return; } ID ajCodBacktranslateAmbig TY public MO ajcod LB core XX DE Back translate a string to a fully ambiguous nucleotide sequence as a string XX PN [1] PA w b AjPStr* PD back translated sequence PX PN [2] PA r a const AjPStr PD sequence PX PN [3] PA r thys const AjPCod PD codon usage object PX RT void RD RX // void ajCodBacktranslateAmbig(AjPStr *b, const AjPStr a, const AjPCod thys) { const char *p; char q; char codon[4] = "NNN"; ajStrAssignClear(b); ajDebug("ajCodBacktranslateAmbig '%S'\n", a); p = ajStrGetPtr(a); while(*p) { codon[0] = codon[1] = codon[2] = '\0'; q = *p; if(toupper((int)q)==(int)'-') { strcpy(codon, "---"); } else if(toupper((int)q)==(int)'X') { strcpy(codon,"NNN"); } else if(toupper((int)q)==(int)'B') { codTripletAdd(thys, 'D', codon); codTripletAdd(thys, 'N', codon); codTripletBases(codon); } else if(toupper((int)q)==(int)'Z') { codTripletAdd(thys, 'E', codon); codTripletAdd(thys, 'Q', codon); codTripletBases(codon); } else if(toupper((int)q)==(int)'J') /* Unlikely - NMR code */ { codTripletAdd(thys, 'I', codon); codTripletAdd(thys, 'L', codon); codTripletBases(codon); } else if(toupper((int)q)==(int)'U') /* selenocysteine if found */ { strcpy(codon,"TGA"); } else { codTripletAdd(thys, q, codon); codTripletBases(codon); } ajStrAppendC(b,codon); ++p; } return; } ID codTripletAdd TY static MO ajcod LB core XX DE Add all possible bases at each position to a codon for one amino acid XX PN [1] PA r thys const AjPCod PD Codon usage table PX PN [2] PA r residue const char PD Amino acid code PX PN [3] PA u triplet char[4] PD Codon triplet PX RT AjBool RD ajTrue on success, ajFalse if the amino acid is not found.. RX // static AjBool codTripletAdd (const AjPCod thys, const char residue, char triplet[4]) { AjBool ret = ajFalse; ajint i; char aa; char* codon; aa = (char) toupper((ajint) residue); for (i=0;iaa[i]), triplet[0], triplet[1], triplet[2]); if(ajBasecodeFromInt(thys->aa[i]) == aa) { codon = ajCodTriplet(i); ajDebug("codTripletAdd '%c' %d '%c' %2x%2x%2x '%s'\n", residue, i, ajBasecodeFromInt(thys->aa[i]), triplet[0], triplet[1], triplet[2], codon); triplet[0] |= ajBaseAlphaToBin(codon[0]); triplet[1] |= ajBaseAlphaToBin(codon[1]); triplet[2] |= ajBaseAlphaToBin(codon[2]); ajDebug("codTripletAdd now %2x%2x%2x\n", triplet[0], triplet[1], triplet[2]); ret = ajTrue; } } return ret; } ID codTripletBases TY static MO ajcod LB core XX DE Converts a triplet of binary codes to base codes XX PN [1] PA u triplet char* PD Triplet in binary a=1 C=2 G=4 T=8 PD return [void] PX // static void codTripletBases(char* triplet) { ajint j; for(j=0;j<3;j++) triplet[j] = ajBaseBinToAlpha(triplet[j]); return; } ID ajCodBase TY public MO ajcod LB core XX DE Return one codon value given a possibly ambiguous base XX PN [1] PA r c ajint PD base PX RT ajint RD single base value RX // ajint ajCodBase(ajint c) { ajint v; v = ajBaseAlphaToBin(c); if(v & 1) return 0; if(v & 2) return 1; if(v & 4) return 2; if(v & 8) return 3; return 0; } ID ajCodClear TY public MO ajcod LB core XX DE Zero all entries DE DE To retain the genetics code (the amino acids for each codon) use DE ajCodClearData instead. XX PN [1] PA w thys AjPCod PD codon usage structure PX RT void RD RX // void ajCodClear(AjPCod thys) { ajint i; ajStrAssignClear(&thys->Name); ajStrAssignClear(&thys->Species); ajStrAssignClear(&thys->Division); ajStrAssignClear(&thys->Release); ajStrAssignClear(&thys->Desc); thys->CdsCount = 0; thys->CodonCount = 0; thys->GeneticCode = 0; for(i=0;ifraction[i] = 0.0; thys->tcount[i] = 0.0; thys->num[i] = 0; thys->aa[i] = 0; } for(i=0;iback[i] = 0; return; } ID ajCodClearData TY public MO ajcod LB core XX DE Zero the name, number count and fraction codon entries XX PN [1] PA w thys AjPCod PD codon usage structure PX RT void RD RX // void ajCodClearData(AjPCod thys) { ajint i; ajStrAssignClear(&thys->Name); ajStrAssignClear(&thys->Species); ajStrAssignClear(&thys->Division); ajStrAssignClear(&thys->Release); ajStrAssignClear(&thys->Desc); thys->CdsCount = 0; thys->CodonCount = 0; thys->GeneticCode = 0; for(i=0;ifraction[i] = 0.0; thys->tcount[i] = 0.0; thys->num[i] = 0; } for(i=0;iback[i] = 0; return; } ID ajCodSetTripletsS TY public MO ajcod LB core XX DE Load the num array of a codon structure DE DE Used for creating a codon usage table DE DE Skips triplets with ambiguity codes and any incomplete triplet at the end. XX PN [1] PA w thys AjPCod PD Codon object PX PN [2] PA r s const AjPStr PD dna sequence PX PN [3] PA w c ajint* PD triplet count PX RT void RD RX // void ajCodSetTripletsS(AjPCod thys, const AjPStr s, ajint *c) { const char *p; ajuint last; ajuint i; ajint idx; p = ajStrGetPtr(s); last = ajStrGetLen(s)-2; for(i=0;inum[idx]; else { ajDebug("ajCodSetTripletsS skipping triplet %3.3s\n", p); --(*c); } } return; } ID ajCodIndex TY public MO ajcod LB core XX DE Return a codon index given a three character codon DE DE Can be called with any coding sequence of 3 or more bases, and will DE use only the first 3 bases of the input. XX PN [1] PA r s const AjPStr PD Codon PX RT ajint RD Codon index AAA=0 TTT=3f RX // ajint ajCodIndex(const AjPStr s) { return ajCodIndexC(ajStrGetPtr(s)); } ID ajCodIndexC TY public MO ajcod LB core XX DE Return a codon index given a three character codon DE DE Can be called with any coding sequence of 3 or more bases, and will DE use only the first 3 bases of the input. XX PN [1] PA r codon const char* PD Codon pointer PX RT ajint RD codon index AAA=0 TTT=3f RX // ajint ajCodIndexC(const char *codon) { const char *p; ajint sum; ajint c; p = codon; sum = 0; if(!(c=*(p++))) return -1; sum += (ajCodBase(toupper(c))<<4); if(!(c=*(p++))) return -1; sum += (ajCodBase(toupper(c))<<2); if(!(c=*p)) return -1; sum += ajCodBase(toupper(c)); return sum; } ID ajCodRead TY public MO ajcod LB core XX DE Read a codon index from a filename using a specified format. DE DE The format can be in the format argument, as a prefix format:: to the DE filename, or empty to allow all known formats to be tried. XX PN [1] PA w thys AjPCod PD Codon object PX PN [2] PA r fn const AjPStr PD filename PX PN [3] PA r format const AjPStr PD format PX RT AjBool RD ajTrue on success RX CA input CT AjPCod CD Read codon index from a file CX // AjBool ajCodRead(AjPCod thys, const AjPStr fn, const AjPStr format) { AjBool ret = ajFalse; AjPFile inf = NULL; AjPFilebuff inbuff = NULL; AjPStr formatstr = NULL; AjPStr fname = NULL; AjPStr filename = NULL; ajlong i; i = ajStrFindC(fn, "::"); if(i == -1) { ajStrAssignS(&filename, fn); ajStrAssignS(&formatstr, format); } else { ajStrAssignSubS(&formatstr, fn, 0, i-1); ajStrAssignSubS(&fname, fn, i+1, -1); } fname = ajStrNewS(filename); inf = ajDatafileNewInNameS(fname); if(!inf) { ajStrAssignC(&fname,"CODONS/"); ajStrAppendS(&fname,filename); inf = ajDatafileNewInNameS(fname); if(!inf) { ajStrDel(&fname); return ajFalse; } } ajStrDel(&fname); inbuff = ajFilebuffNewFromFile(inf); for(i=0;codInFormatDef[i].Name; i++) { if(ajStrGetLen(formatstr)) { if(!ajStrMatchC(formatstr, codInFormatDef[i].Name)) continue; } else { if(!codInFormatDef[i].Try) continue; } ajDebug("ajCodRead Try format '%s'\n", codInFormatDef[i].Name); ret = (*codInFormatDef[i].Read)(thys, inbuff); if(ret) { ajStrAssignS(&thys->Name, filename); codFix(thys); ajDebug("ajCodRead Format '%s' success\n", codInFormatDef[i].Name); ajFilebuffDel(&inbuff); ajStrDel(&filename); ajStrDel(&formatstr); return ajTrue; } ajDebug("ajCodRead Format '%s' failed\n", codInFormatDef[i].Name); ajCodClear(thys); ajFilebuffReset(inbuff); } ajFilebuffDel(&inbuff); ajStrDel(&filename); ajStrDel(&formatstr); return ret; } ID codReadEmboss TY static MO ajcod LB core XX DE Read a codon index from a filename in EMBOSS format DE DE First readable line format is either the comment or the data DE DE #Codon AA Fraction Frequency Number<br> DE GCA A 0.070 7.080 544<br> XX PN [1] PA w thys AjPCod PD Codon object PX PN [2] PA u inbuff AjPFilebuff PD Input file buffer PX RT AjBool RD ajTrue on success RX CA input CT AjPCod CD Read codon index from a file CX // static AjBool codReadEmboss(AjPCod thys, AjPFilebuff inbuff) { AjPStr t; const char *p; ajint idx; ajint c; ajint num; double tcount; double fraction; AjPStr tok = NULL; AjPStrTok handle = NULL; t = ajStrNew(); while(ajBuffreadLine(inbuff,&codReadLine)) { p=ajStrGetPtr(codReadLine); if(*p=='\n') continue; else if(*p=='#') { codCommentProcess(thys, codReadLine); continue; } else if(*p=='!') continue; /* check record format */ if(ajStrParseCountC(codReadLine, " \t\r\n") != 5) return ajFalse; ajStrTokenAssignC(&handle, codReadLine, " \t\r\n"); ajStrTokenNextParse(&handle, &tok); if(!codIsCodon(&tok)) return ajFalse; idx = ajCodIndex(tok); ajStrTokenNextParse(&handle, &tok); if(!codIsAa(&tok)) return ajFalse; c = ajBasecodeToInt((ajint)ajStrGetCharFirst(tok)); if(c>25) c=27; /* stop */ ajStrTokenNextParse(&handle, &tok); if(!codIsFraction(tok)) return ajFalse; ajStrToDouble(tok,&fraction); ajStrTokenNextParse(&handle, &tok); if(!codIsFreq(tok)) return ajFalse; ajStrToDouble(tok,&tcount); ajStrTokenNextParse(&handle, &tok); if(!codIsNumber(tok)) return ajFalse; ajStrToInt(tok,&num); thys->aa[idx] = c; thys->num[idx] = num; thys->tcount[idx] = tcount; thys->fraction[idx] = fraction; } ajStrDel(&t); ajStrTokenDel(&handle); ajStrDel(&tok); return ajTrue; } ID codReadStaden TY static MO ajcod LB core XX DE Read a codon index from a filename in Staden format with counts. DE DE The first lines have the format: DE DE ===========================================<br> DE F TTT 170 S TCT 160 Y TAT 10 C TGT 20<br> DE DE Note that in this format the Staden package allows two codon usage tables DE in the same file. The first is the one we want as it is for the protein DE coding regions. The second is for the regions between CDSs. XX PN [1] PA w thys AjPCod PD Codon object PX PN [2] PA u inbuff AjPFilebuff PD Input file buffer PX RT AjBool RD ajTrue on success RX CA input CT AjPCod CD Read codon index from a file CX // static AjBool codReadStaden(AjPCod thys, AjPFilebuff inbuff) { AjPStr tok = NULL; AjPStrTok handle = NULL; ajint i; ajint c; ajint idx; ajint num; double tcount; ajint cdscount = 0; ajint icod = 0; while(ajBuffreadLine(inbuff,&codReadLine)) { ajStrRemoveWhiteExcess(&codReadLine); if(!ajStrGetLen(codReadLine)) continue; if(ajStrGetCharFirst(codReadLine) == '#') { codCommentProcess(thys, codReadLine); continue; } if(ajStrGetCharFirst(codReadLine) == '!') continue; if(ajStrFindRestC(codReadLine, "=") == -1) continue; if(icod > 63) /* ignore second non-coding set */ continue; /* check record format */ if(ajStrParseCountC(codReadLine, " \t\r\n") != 12) return ajFalse; ajStrTokenAssignC(&handle, codReadLine, " \t\r\n"); for(i=0;i<4;i++) { ajStrTokenNextParse(&handle, &tok); if(!codIsAa(&tok)) return ajFalse; c = ajBasecodeToInt((ajint)ajStrGetCharFirst(tok)); if(c>25) c=27; /* stop */ ajStrTokenNextParse(&handle, &tok); if(!codIsCodon(&tok)) return ajFalse; idx = ajCodIndex(tok); ajStrTokenNextParse(&handle, &tok); if(!codIsNumber(tok)) return ajFalse; ajStrToDouble(tok,&tcount); ajStrToInt(tok, &num); thys->aa[idx] = c; cdscount += num; thys->num[idx] = num; thys->tcount[idx] = 0.0; /* unknown - can calculate */ thys->fraction[idx] = 0.0; /* unknown fraction - can calculate */ icod++; } } codCalcFraction(thys); ajStrTokenDel(&handle); ajStrDel(&tok); return ajTrue; } ID codReadSpsum TY static MO ajcod LB core XX DE Read a codon index from a filename in CUTG .spsum file format XX PN [1] PA w thys AjPCod PD Codon object PX PN [2] PA u inbuff AjPFilebuff PD Input file buffer PX RT AjBool RD ajTrue on success RX CA input CT AjPCod CD Read codon index from a file CX // static AjBool codReadSpsum(AjPCod thys, AjPFilebuff inbuff) { AjPStrTok handle = NULL; AjPStr tok = NULL; ajint i; ajint j; ajint c; ajint num; if(!ajBuffreadLine(inbuff,&codReadLine)) return ajFalse; if(ajStrParseCountC(codReadLine, ":") != 2) return ajFalse; ajStrTokenAssignC(&handle, codReadLine, ":"); ajStrTokenNextParse(&handle, &tok); ajStrTrimWhite(&tok); ajStrAssignS(&thys->Species, tok); ajStrTokenNextParse(&handle, &tok); ajStrTrimWhite(&tok); if(!ajStrToInt(tok, &thys->CdsCount)) return ajFalse; if(!ajBuffreadLine(inbuff,&codReadLine)) return ajFalse; if(ajStrParseCountC(codReadLine, " \t\n\r") != 64) return ajFalse; ajStrTokenAssignC(&handle, codReadLine, " \t\r\n"); for(i=0;i<64;i++) { ajStrTokenNextParse(&handle, &tok); if(!ajStrToInt(tok, &num)) return ajFalse; j = ajCodIndexC(spsumcodons[i]); thys->num[j] = num; c = ajBasecodeToInt((ajint)spsumaa[i]); if(c>25) c=27; thys->aa[j] = c; } ajStrTokenDel(&handle); ajStrDel(&tok); return ajTrue; } ID codReadCutg TY static MO ajcod LB core XX DE Read a codon index from a filename in CUTG web site format XX PN [1] PA w thys AjPCod PD Codon object PX PN [2] PA u inbuff AjPFilebuff PD Input file buffer PX RT AjBool RD ajTrue on success RX CA input CT AjPCod CD Read codon index from a file CX // static AjBool codReadCutg(AjPCod thys, AjPFilebuff inbuff) { AjPStr tok = NULL; AjPStrTok handle = NULL; ajint i; ajint j; AjBool hasaa; ajint ilines = 0; ajint c; ajint idx; ajint num; double tcount; double fraction; AjPTrn trn = NULL; /* species [division]: 000 CDS's (0000 codons) */ ajBuffreadLine(inbuff,&codReadLine); if(-1 == ajStrFindC(codReadLine, "CDS's")) return ajFalse; if(-1 == ajStrFindC(codReadLine, "codons)")) return ajFalse; if(-1 == ajStrFindC(codReadLine, "]:")) return ajFalse; ajStrTokenAssignC(&handle, codReadLine, "["); ajStrTokenNextParse(&handle, &tok); if(!ajStrGetLen(tok)) return ajFalse; ajStrRemoveWhiteExcess(&tok); ajStrAssignS(&thys->Species, tok); ajStrTokenNextParseC(&handle, "]: ", &tok); if(!ajStrGetLen(tok)) return ajFalse; ajStrAssignS(&thys->Division, tok); ajStrTokenNextParseC(&handle, "CDS's (", &tok); if(!codIsNumber(tok)) return ajFalse; ajStrToInt(tok, &thys->CdsCount); ajStrTokenNextParseC(&handle, " codons)", &tok); if(!codIsNumber(tok)) return ajFalse; ajStrToInt(tok, &thys->CodonCount); while(ajBuffreadLine(inbuff,&codReadLine)) { ajStrTrimWhite(&codReadLine); if(ajStrPrefixC(codReadLine, "Coding GC ")) continue; else if(ajStrPrefixC(codReadLine, "Genetic code ")) { ajStrCutStart(&codReadLine, 13); ajStrTokenAssignC(&handle, codReadLine, " :"); ajStrTokenNextParse(&handle, &tok); if(codIsNumber(tok)) ajStrToInt(tok, &thys->GeneticCode); continue; } else { i = ajStrParseCountC(codReadLine, " ()"); if(i == 20) hasaa = ajTrue; else if(i == 12) hasaa = ajFalse; else continue; ajStrTokenAssignC(&handle, codReadLine, " "); for(j=0;j<4;j++) { ajStrTokenNextParse(&handle, &tok); if(!codIsCodon(&tok)) return ajFalse; idx = ajCodIndex(tok); if(hasaa) { ajStrTokenNextParse(&handle, &tok); if(!codIsAa(&tok)) return ajFalse; c = ajBasecodeToInt((ajint)ajStrGetCharFirst(tok)); ajStrTokenNextParse(&handle, &tok); if(!codIsFraction(tok)) return ajFalse; ajStrToDouble(tok,&fraction); } else { if(!trn) trn = ajTrnNewI(0); ajStrAssignK(&tok, ajTrnCodonS(trn, tok)); c = ajBasecodeToInt((ajint)ajStrGetCharFirst(tok)); } if(c>25) c=27; /* stop */ ajStrTokenNextParseC(&handle, " (", &tok); if(!codIsFreq(tok)) return ajFalse; ajStrToDouble(tok,&tcount); ajStrTokenNextParseC(&handle, " )", &tok); if(!codIsNumber(tok)) return ajFalse; ajStrToInt(tok,&num); thys->aa[idx] = c; thys->num[idx] = num; thys->tcount[idx] = tcount; if(hasaa) thys->fraction[idx] = fraction; } ilines++; } } ajStrTokenDel(&handle); ajStrDel(&tok); if(!ilines) return ajFalse; ajTrnDel(&trn); return ajTrue; } ID codReadCodehop TY static MO ajcod LB core XX DE Read a codon index from a filename in FHCRC codehop program format XX PN [1] PA w thys AjPCod PD Codon object PX PN [2] PA u inbuff AjPFilebuff PD Input file buffer PX RT AjBool RD ajTrue on success RX CA input CT AjPCod CD Read codon index from a file CX // static AjBool codReadCodehop(AjPCod thys, AjPFilebuff inbuff) { AjPStr line = NULL; AjPStrTok handle = NULL; AjPStr tok1 = NULL; AjPStr tok2 = NULL; AjPStr tok3 = NULL; AjPStr tok4 = NULL; ajlong i; ajint c; ajint idx; double fraction; double tcount; double res; AjPTrn trn = NULL; while(ajBuffreadLine(inbuff,&line)) { if(ajStrGetCharFirst(line) == '-') break; if(ajStrParseCountC(line, " \t\r\n[]") != 4) return ajFalse; ajStrTokenAssignC(&handle, line, " \t\r\n[]"); ajStrTokenNextParse(&handle, &tok1); ajStrTokenNextParse(&handle, &tok2); ajStrTokenNextParse(&handle, &tok3); ajStrTokenNextParse(&handle, &tok4); if(!codIsFreq(tok1)) return ajFalse; if(!codIsCodon(&tok4)) return ajFalse; ajStrToDouble(tok1, &fraction); tcount = fraction * 1000.0; if(!trn) trn = ajTrnNewI(0); ajStrAssignK(&tok2, ajTrnCodonS(trn, tok4)); c = ajBasecodeToInt((ajint)ajStrGetCharFirst(tok2)); if(c>25) c=27; /* stop */ idx = ajCodIndex(tok4); thys->aa[idx] = c; thys->tcount[idx] = tcount; } ajBuffreadLine(inbuff,&line); ajBuffreadLine(inbuff,&line); ajStrRemoveWhiteExcess(&line); if(ajStrPrefixC(line, "Codon usage for ")) { ajStrCutStart(&line, 16); i = ajStrFindlastC(line, ":"); if(i>0) { ajStrAssignSubS(&thys->Species, line, 0, i-1); ajStrAssignSubS(&tok1, line, i+1, -1); ajStrRemoveWhiteExcess(&tok1); if(codIsNumber(tok1)) ajStrToInt(tok1, &thys->CdsCount); } } ajBuffreadLine(inbuff,&line); ajStrRemoveWhiteExcess(&line); if(ajStrPrefixC(line, "from ")) { ajStrCutStart(&line, 5); ajStrRemoveWhiteExcess(&line); i = ajStrFindlastC(line, ":"); if(i>0) { ajStrAssignSubS(&tok1, line, 0, i-1); ajStrAssignSubS(&tok2, line, i+1, -1); ajStrRemoveWhiteExcess(&tok1); ajStrRemoveWhiteExcess(&tok2); i = ajStrFindC(tok1, "/"); if(i>0) { ajStrAssignSubS(&thys->Release, tok1, 0, i-1); ajStrAssignSubS(&thys->Division, tok1, i+1, -1); } else ajStrAssignS(&thys->Release, tok1); i = ajStrFindC(tok2, " "); if(i>0) { ajStrAssignSubS(&tok3, tok2, 0, i-1); if(codIsNumber(tok3)) ajStrToInt(tok3, &thys->CodonCount); } } } if(thys->CodonCount) for(i=0;i<64;i++) { res = ((double)thys->CodonCount* thys->tcount[i] / 1000.0 + 0.1); thys->num[i] = (ajint) res; } ajStrDel(&tok1); ajStrDel(&tok2); ajStrDel(&tok3); ajStrDel(&tok4); ajStrDel(&line); ajStrTokenDel(&handle); ajTrnDel(&trn); return ajTrue; } ID codReadGcg TY static MO ajcod LB core XX DE Read a codon index from a filename in GCG format XX PN [1] PA w thys AjPCod PD Codon object PX PN [2] PA u inbuff AjPFilebuff PD Input file buffer PX RT AjBool RD ajTrue on success RX CA input CT AjPCod CD Read codon index from a file CX // static AjBool codReadGcg(AjPCod thys, AjPFilebuff inbuff) { AjPStr line = NULL; AjBool header = ajTrue; AjPStr saveheader = NULL; AjPStr tok1 = NULL; ajlong i; while(ajBuffreadLine(inbuff,&line)) { if(ajStrSuffixC(line, "..\n")) { header = ajFalse; continue; } if(header) { ajStrRemoveWhiteExcess(&line); if(!ajStrGetLen(line)) continue; if(ajStrGetCharFirst(line) == '!') continue; if(ajStrGetCharFirst(line) == '#') { codCommentProcess(thys, line); continue; } i = ajStrFindC(line, " genes found in "); /* Lossy cast */ if(i == -1) ajStrAssignS(&saveheader, line); else { ajStrAssignSubS(&tok1, line, 0, i-1); if(!codIsNumber(tok1)) continue; ajStrToInt(tok1, &thys->CdsCount); ajStrAssignSubS(&thys->Release, line, i+16, -1); ajStrTrimEndC(&thys->Release, "."); ajStrExchangeKK(&thys->Release, ' ', '_'); ajStrAssignS(&thys->Species, saveheader); } continue; } codGcgProcess(thys, line); } ajStrDel(&line); ajStrDel(&saveheader); ajStrDel(&tok1); if(header) return ajFalse; return ajTrue; } ID codGcgProcess TY static MO ajcod LB core XX DE Read a codon index from a filename in GCG format XX PN [1] PA u thys AjPCod PD Codon object PX PN [2] PA r line const AjPStr PD Input line PX RT AjBool RD ajTrue on success RX // static AjBool codGcgProcess(AjPCod thys, const AjPStr line) { AjPStrTok handle = NULL; AjPStr tok = NULL; ajint idx; ajint c; ajint num; double tcount; double fraction; if(ajStrParseCountC(line, " \t\n\r") != 5) return ajFalse; ajStrTokenAssignC(&handle, line, " \t\r\n"); ajStrTokenNextParse(&handle, &tok); if(!codIsAa3(&tok, &c)) return ajFalse; ajStrTokenNextParse(&handle, &tok); if(!codIsCodon(&tok)) return ajFalse; idx = ajCodIndex(tok); ajStrTokenNextParse(&handle, &tok); if(!codIsNumberF(&tok)) return ajFalse; ajStrToInt(tok, &num); ajStrTokenNextParse(&handle, &tok); if(!codIsFreq(tok)) return ajFalse; ajStrToDouble(tok, &tcount); ajStrTokenNextParse(&handle, &tok); if(!codIsFraction(tok)) return ajFalse; ajStrToDouble(tok, &fraction); thys->aa[idx] = c; thys->tcount[idx] = tcount; thys->fraction[idx] = fraction; thys->num[idx] = num; ajStrTokenDel(&handle); ajStrDel(&tok); return ajTrue; } ID codCommentProcess TY static MO ajcod LB core XX DE Read codon usage extra information from an EMBOSS-style comment XX PN [1] PA w thys AjPCod PD Codon object PX PN [2] PA r ccline const AjPStr PD Input line PX RT AjBool RD ajTrue if something was read RX // static AjBool codCommentProcess(AjPCod thys, const AjPStr ccline) { ajStrAssignS(&codTmpLine, ccline); if(ajStrPrefixC(codTmpLine, "#Species:")) { ajStrCutStart(&codTmpLine, 9); ajStrRemoveWhiteExcess(&codTmpLine); ajStrAssignS(&thys->Species, codTmpLine); return ajTrue; } else if(ajStrPrefixC(codTmpLine, "#Division:")) { ajStrCutStart(&codTmpLine, 10); ajStrRemoveWhiteExcess(&codTmpLine); ajStrAssignS(&thys->Division, codTmpLine); return ajTrue; } else if(ajStrPrefixC(codTmpLine, "#Release:")) { ajStrCutStart(&codTmpLine, 9); ajStrRemoveWhiteExcess(&codTmpLine); ajStrAssignS(&thys->Release, codTmpLine); return ajTrue; } else if(ajStrPrefixC(codTmpLine, "#Description:")) { ajStrCutStart(&codTmpLine, 13); ajStrRemoveWhiteExcess(&codTmpLine); ajStrAssignS(&thys->Desc, codTmpLine); return ajTrue; } else if(ajStrPrefixC(codTmpLine, "#CdsCount:")) { ajStrCutStart(&codTmpLine, 10); ajStrRemoveWhiteExcess(&codTmpLine); ajStrToInt(codTmpLine, &thys->CdsCount); return ajTrue; } else if(ajStrPrefixC(codTmpLine, "#CodonCount:")) { ajStrCutStart(&codTmpLine, 12); ajStrRemoveWhiteExcess(&codTmpLine); ajStrToInt(codTmpLine, &thys->CodonCount); return ajTrue; } else if(ajStrPrefixC(codTmpLine, "#GeneticCode:")) { ajStrCutStart(&codTmpLine, 13); ajStrRemoveWhiteExcess(&codTmpLine); ajStrToInt(codTmpLine, &thys->GeneticCode); } return ajFalse; } ID codIsCodon TY static MO ajcod LB core XX DE Checks a string is a codon, makes sure it is ACGT only XX PN [1] PA u token AjPStr* PD String PX RT AjBool RD ajTrue if string is a valid codon sequence RX // static AjBool codIsCodon (AjPStr* token) { char* cp; if(ajStrGetLen(*token) != 3) return ajFalse; for(cp = ajStrGetuniquePtr(token); *cp; cp++) { if(islower((ajint) *cp)) *cp = toupper((ajint) *cp); if(*cp == 'U') *cp = 'T'; if(!strchr("ACGT",*cp)) return ajFalse; } return ajTrue; } ID codIsAa TY static MO ajcod LB core XX DE Checks a string is a codon, makes sure it is an amino acid or a stop (*). XX PN [1] PA u token AjPStr* PD String PX RT AjBool RD ajTrue if string is a valid amino acid code RX // static AjBool codIsAa(AjPStr* token) { char* cp; if(ajStrGetLen(*token) != 1) return ajFalse; cp = ajStrGetuniquePtr(token); if(islower((ajint) *cp)) *cp = toupper((ajint) *cp); if(!strchr("ACDEFGHIKLMNPQRSTUVWY*",*cp)) /* SelenoCysteine is U */ return ajFalse; return ajTrue; } ID codIsAa3 TY static MO ajcod LB core XX DE Checks a string is a 3 letter amino acid code XX PN [1] PA u token AjPStr* PD String PX PN [2] PA w idx ajint* PD Amino acid index number PX RT AjBool RD ajTrue if string is a valid amino acid 3-letter code RX // static AjBool codIsAa3(AjPStr* token, ajint* idx) { const char* cp; ajint i; static const char *tab[]= { "Ala","Asx","Cys","Asp","Glu","Phe","Gly","His", "Ile","" ,"Lys","Leu","Met","Asn","" ,"Pro", "Gln","Arg","Ser","Thr","Sel","Val","Trp","" , "Tyr","" ,"" ,"End",NULL }; if(ajStrGetLen(*token) != 3) return ajFalse; for(i=0;tab[i]; i++) { cp = tab[i]; if(!*cp) continue; if(ajStrMatchCaseC(*token, cp)) { if(!ajStrMatchC(*token, cp)) ajStrAssignC(token, cp); *idx = i; return ajTrue; } } return ajFalse; } ID codIsNumber TY static MO ajcod LB core XX DE Checks a string is numeric. XX PN [1] PA r token const AjPStr PD String PX RT AjBool RD ajTrue if string is a valid integer RX // static AjBool codIsNumber(const AjPStr token) { ajint ilen; ilen = ajStrGetLen(token); if(!ilen) return ajFalse; if(ajStrFindRestC(token, "0123456789") != -1) return ajFalse; return ajTrue; } ID codIsNumberF TY static MO ajcod LB core XX DE Checks a string is a number, optionally ending in .000 XX PN [1] PA u token AjPStr* PD String PX RT AjBool RD ajTrue if string is a valid floating point number RX // static AjBool codIsNumberF(AjPStr * token) { ajuint ilen; AjPStr tmpstr = NULL; ilen = ajStrGetLen(*token); if(!ilen) return ajFalse; if(ajStrFindRestC(*token, "0123456789") == -1) return ajTrue; ajStrAssignS(&tmpstr, *token); ilen = ajStrGetLen(tmpstr); ajStrTrimEndC(&tmpstr, "0"); if(ajStrGetLen(tmpstr) < ilen) ilen = ajStrGetLen(tmpstr); ajStrTrimEndC(&tmpstr,"."); if(ajStrGetLen(tmpstr) < ilen) { ajStrAssignS(token, tmpstr); ajStrDel(&tmpstr); return ajTrue; } ajStrDel(&tmpstr); return ajFalse; } ID codCalcFraction TY static MO ajcod LB core XX DE Calculates the fraction values for a codon usage object XX PN [1] PA u thys AjPCod PD Codon usage object PX RT void RD RX // static void codCalcFraction(AjPCod thys) { ajint count[64]; ajint i; ajint j; ajint idx; ajint icount; double fsum = 0.0; const char* cp; const char* aa = "ACDEFGHIKLMNPQRSTUVWY*"; cp = aa; while (*cp) { if(*cp == '*') idx = 27; else idx = *cp - 'A'; icount = 0; fsum = 0.0; for(i=0;i<64;i++) { if(thys->aa[i] == idx) { count[icount++] = i; fsum += thys->tcount[i]; } } for(j=0;j 0.0 */ if(!E_FPZERO(thys->tcount[count[j]],U_DEPS)) thys->fraction[count[j]] = thys->tcount[count[j]]/fsum; else thys->fraction[count[j]] = 0.0; } cp++; } return; } ID codIsFraction TY static MO ajcod LB core XX DE Checks a string is a fraction between 0.0 and 1.0. XX PN [1] PA r token const AjPStr PD String PX RT AjBool RD ajTrue if string is a valid fraction between 0.0 and 1.0 RX // static AjBool codIsFraction(const AjPStr token) { const char* cp; ajuint ilen; AjBool StartOne = ajFalse;; ilen = ajStrGetLen(token); if(!ilen) return ajFalse; cp = ajStrGetPtr(token); if(*cp == '0') { cp++; ilen--; } else if(*cp == '1') { cp++; ilen--; StartOne = ajTrue; } if(*cp == '.') { cp++; ilen--; } if(!*cp) return ajTrue; if(StartOne) { if(ilen != strspn(cp, "0")) return ajFalse; } else { if(ilen != strspn(cp, "0123456789")) return ajFalse; } return ajTrue; } ID codIsFreq TY static MO ajcod LB core XX DE Checks a string is a frequency between 0.0 and 1000.0. XX PN [1] PA r token const AjPStr PD String PX RT AjBool RD ajTrue if string is a valid frequency between RD 0.0 and 1000.0 RX // static AjBool codIsFreq(const AjPStr token) { const char* cp; float f; ajuint ilen; ilen = ajStrGetLen(token); if(!ilen) return ajFalse; cp = ajStrGetPtr(token); if(ilen != strspn(cp, "0123456789.")) return ajFalse; if(!ajStrToFloat(token, &f)) return ajFalse; if(f < 0.0) return ajFalse; if(f > 1000.0) return ajFalse; return ajTrue; } ID ajCodSetBacktranslate TY public MO ajcod LB core XX DE Fill the codon usage object "back" element with the most commonly DE used triplet index for the amino acids XX PN [1] PA u thys AjPCod PD codon usage structure PX RT void RD RX // void ajCodSetBacktranslate(AjPCod thys) { ajint i; ajint aa; double f; double f2; if(!thys) ajFatal("Codon usage object uninitialised"); for(i=0;iback[i] = -1; for(i=0;iaa[i]; if(thys->back[aa]<0) thys->back[aa] = i; f = thys->fraction[i]; f2 = thys->fraction[thys->back[aa]]; if(f>f2) thys->back[aa] = i; } return; } ID ajCodTriplet TY public MO ajcod LB core XX DE Convert triplet index to triple XX PN [1] PA r idx ajint PD triplet index PX RT char* RD Triplet RX // char* ajCodTriplet(ajint idx) { static char ret[4] = "AAA"; const char *conv = "ACGT"; char *p; p=ret; *p++ = conv[idx>>4]; *p++ = conv[(idx & 0xf)>>2]; *p = conv[idx & 0x3]; return ret; } ID ajCodWriteOut TY public MO ajcod LB core XX DE Write codon structure to output file XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA u outf AjPOutfile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // void ajCodWriteOut(const AjPCod thys, AjPOutfile outf) { ajint i; for(i=0;codInFormatDef[i].Name; i++) { if(ajStrMatchCaseC(ajOutfileGetFormat(outf), codoutFormatDef[i].Name)) { (*codoutFormatDef[i].Write)(thys, ajOutfileGetFile(outf)); return; } } return; } ID ajCodWrite TY public MO ajcod LB core XX DE Write codon structure to output file XX PN [1] PA u thys AjPCod PD codon usage PX PN [2] PA u outf AjPFile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // void ajCodWrite(AjPCod thys, AjPFile outf) { codFix(thys); codWriteEmboss(thys, outf); } ID codWriteEmboss TY static MO ajcod LB core XX DE Write codon structure to output file in EMBOSS format XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA u outf AjPFile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // static void codWriteEmboss(const AjPCod thys, AjPFile outf) { ajint i; ajint j; const char* codon; ajint gc[3] = {0,0,0}; float totgc = 0.0; if(ajStrGetLen(thys->Species)) ajFmtPrintF(outf, "#Species: %S\n", thys->Species); if(ajStrGetLen(thys->Division)) ajFmtPrintF(outf, "#Division: %S\n", thys->Division); if(ajStrGetLen(thys->Release)) ajFmtPrintF(outf, "#Release: %S\n", thys->Release); if(thys->GeneticCode) ajFmtPrintF(outf, "#GeneticCode: %d\n", thys->GeneticCode); if(thys->CdsCount) ajFmtPrintF(outf, "#CdsCount: %d\n", thys->CdsCount); for(j=0;jnum[j]; } totgc = (float) (gc[0] + gc[1] + gc[2]); ajFmtPrintF(outf, "\n#Coding GC %5.2f%%\n", (100.0 * totgc/(float)thys->CodonCount/3.0)); ajFmtPrintF(outf, "#1st letter GC %5.2f%%\n", (100.0 * (float)gc[0]/(float)thys->CodonCount)); ajFmtPrintF(outf, "#2nd letter GC %5.2f%%\n", (100.0 * (float)gc[1]/(float)thys->CodonCount)); ajFmtPrintF(outf, "#3rd letter GC %5.2f%%\n\n", (100.0 * (float)gc[2]/(float)thys->CodonCount)); ajFmtPrintF(outf, "#Codon AA Fraction Frequency Number\n"); for(i=0;iaa[j]==i) ajFmtPrintF(outf,"%s %c %8.3f %9.3f %6d\n", ajCodTriplet(j), i+'A',thys->fraction[j],thys->tcount[j], thys->num[j]); for(j=0;jaa[j]==27) ajFmtPrintF(outf,"%s * %8.3f %9.3f %6d\n", ajCodTriplet(j), thys->fraction[j],thys->tcount[j],thys->num[j]); return; } ID codWriteSpsum TY static MO ajcod LB core XX DE Write codon structure to output file inSpsum format XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA u outf AjPFile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // static void codWriteSpsum(const AjPCod thys, AjPFile outf) { AjPStr species = NULL; ajint i; ajint j; ajStrAssignS(&species, thys->Species); ajStrAssignEmptyC(&species, "unknown species"); ajFmtPrintF(outf, "%S: %d\n", species, thys->CdsCount); for(i=0;i<64;i++) { j = ajCodIndexC(spsumcodons[i]); if(i) ajFmtPrintF(outf, " %d", thys->num[j]); else ajFmtPrintF(outf, "%d", thys->num[j]); } ajFmtPrintF(outf, "\n"); ajStrDel(&species); return; } ID codWriteCutg TY static MO ajcod LB core XX DE Write codon structure to output file in Cutg format XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA u outf AjPFile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // static void codWriteCutg(const AjPCod thys, AjPFile outf) { const char* c0; const char* c1; const char* c2; const char* bases = "UCAG"; char codon[4]="GGG"; ajint i; float totgc; ajint gc[3] = {0,0,0}; AjPStr species = NULL; AjPStr division = NULL; AjPStr minusline = NULL; ajStrAssignS(&species, thys->Species); ajStrAssignEmptyC(&species, "unknown species"); ajStrAssignS(&division, thys->Division); ajStrAssignEmptyC(&division, "unknown.division"); ajFmtPrintF(outf, "%S [%S]: %d CDS's (%d codons)\n", species, division, thys->CdsCount, thys->CodonCount); ajStrAppendCountK(&minusline, '-', 80); ajFmtPrintF(outf, "%S\n", minusline); ajFmtPrintF(outf, "fields: [triplet] [frequency: per thousand] ([number])\n"); ajFmtPrintF(outf, "%S\n\n", minusline); c0 = bases; while(*c0) { codon[0]=*c0; c2 = bases; while(*c2) { codon[2]=*c2; c1 = bases; while(*c1) { codon[1]=*c1; i = ajCodIndexC(codon); if(*c0 == 'C' || *c0 == 'G') gc[0] += thys->num[i]; if(*c1 == 'C' || *c1 == 'G') gc[1] += thys->num[i]; if(*c2 == 'C' || *c2 == 'G') gc[2] += thys->num[i]; ajFmtPrintF(outf, "%s %4.1f(%6d)", codon, thys->tcount[i], thys->num[i]); c1++; if(*c1) ajFmtPrintF(outf, " "); } ajFmtPrintF(outf, "\n"); c2++; } c0++; if(*c0) ajFmtPrintF(outf, "\n"); } ajFmtPrintF(outf, "\n\n"); ajFmtPrintF(outf, "%S\n", minusline); totgc = (float) (gc[0] + gc[1] + gc[2]); ajFmtPrintF(outf, "Coding GC %5.2f%% 1st letter GC %5.2f%% " "2nd letter GC %5.2f%% 3rd letter GC %5.2f%%\n", (100.0 * totgc/(float)thys->CodonCount/3.0), (100.0 * (float)gc[0]/(float)thys->CodonCount), (100.0 * (float)gc[1]/(float)thys->CodonCount), (100.0 * (float)gc[2]/(float)thys->CodonCount)); ajStrDel(&species); ajStrDel(&division); ajStrDel(&minusline); return; } ID codWriteCutgaa TY static MO ajcod LB core XX DE Write codon structure to output file in Cutg format with amino acids XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA u outf AjPFile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // static void codWriteCutgaa(const AjPCod thys, AjPFile outf) { const char* c0; const char* c1; const char* c2; const char* bases = "UCAG"; char codon[4]="GGG"; ajint i; float totgc; ajint gc[3] = {0,0,0}; AjPStr species = NULL; AjPStr division = NULL; AjPStr minusline = NULL; char aa; ajStrAssignS(&species, thys->Species); ajStrAssignEmptyC(&species, "unknown species"); ajStrAssignS(&division, thys->Division); ajStrAssignEmptyC(&division, "unknown.division"); ajFmtPrintF(outf, "%S [%S]: %d CDS's (%d codons)\n", species, division, thys->CdsCount, thys->CodonCount); ajStrAppendCountK(&minusline, '-', 80); ajFmtPrintF(outf, "%S\n", minusline); ajFmtPrintF(outf, "fields: [triplet] [frequency: per thousand] ([number])\n"); ajFmtPrintF(outf, "%S\n\n", minusline); c0 = bases; while(*c0) { codon[0]=*c0; c2 = bases; while(*c2) { codon[2]=*c2; c1 = bases; while(*c1) { codon[1]=*c1; i = ajCodIndexC(codon); if(*c0 == 'C' || *c0 == 'G') gc[0] += thys->num[i]; if(*c1 == 'C' || *c1 == 'G') gc[1] += thys->num[i]; if(*c2 == 'C' || *c2 == 'G') gc[2] += thys->num[i]; if(thys->aa[i] > 25) aa = '*'; else aa = 'A' + thys->aa[i]; ajFmtPrintF(outf, "%s %c %4.2f %4.1f (%6d)", codon, aa, thys->fraction[i], thys->tcount[i], thys->num[i]); c1++; if(*c1) ajFmtPrintF(outf, " "); } ajFmtPrintF(outf, "\n"); c2++; } c0++; if(*c0) ajFmtPrintF(outf, "\n"); } ajFmtPrintF(outf, "\n\n"); ajFmtPrintF(outf, "%S\n", minusline); totgc = (float) (gc[0] + gc[1] + gc[2]); ajFmtPrintF(outf, "Coding GC %5.2f%% 1st letter GC %5.2f%% " "2nd letter GC %5.2f%% 3rd letter GC %5.2f%%\n", (100.0 * totgc/(float)thys->CodonCount/3.0), (100.0 * (float)gc[0]/(float)thys->CodonCount), (100.0 * (float)gc[1]/(float)thys->CodonCount), (100.0 * (float)gc[2]/(float)thys->CodonCount)); ajFmtPrintF(outf, "Genetic code %d: %S\n", thys->GeneticCode, ajTrnName(thys->GeneticCode)); ajStrDel(&species); ajStrDel(&division); ajStrDel(&minusline); return; } ID codWriteCherry TY static MO ajcod LB core XX DE Write codon structure to output file in Mike Cherry's codon usage database DE format. DE DE Very similar to GCG format, has a structured header comment from DE which we can extract the species, CDS count, and release. XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA u outf AjPFile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // static void codWriteCherry(const AjPCod thys, AjPFile outf) { const char* c0; const char* c1; const char* c2; const char* bases = "GATC"; char codon[4]="GGG"; ajint i; AjPStr ThreeAa = NULL; AjPStr release = NULL; AjPStr species = NULL; AjPStr division = NULL; ajStrAssignS(&release, thys->Release); ajStrAssignEmptyC(&release, "unknowndb0.0"); ajStrAssignS(&species, thys->Species); ajStrAssignEmptyC(&species, "unknown species"); ajStrAssignS(&division, thys->Division); ajStrAssignEmptyC(&division, "unknown.division"); ajFmtPrintF(outf, "\n%S\n\n", species); ajFmtPrintF(outf, "%d genes found in %S\n\n", thys->CdsCount, release); ajFmtPrintF(outf, "AmAcid Codon Number /1000 Fraction ..\n"); c0 = bases; while(*c0) { codon[0]=*c0; c1 = bases; while(*c1) { codon[1]=*c1; ajFmtPrintF(outf, " \n"); c2 = bases; while(*c2) { codon[2]=*c2; i = ajCodIndexC(codon); if(!ajResidueToTriplet(ajBasecodeFromInt(thys->aa[i]), &ThreeAa)) ajStrAssignC(&ThreeAa, "End"); ajStrFmtLower(&ThreeAa); ajStrFmtTitle(&ThreeAa); ajFmtPrintF(outf, "%S %s %10.2f %6.2f %4.2f\n", ThreeAa, codon, (float) thys->num[i], thys->tcount[i], thys->fraction[i]); c2++; } c1++; } c0++; } ajStrDel(&species); ajStrDel(&release); ajStrDel(&division); ajStrDel(&ThreeAa); return; } ID codWriteTransterm TY static MO ajcod LB core XX DE Write codon structure to output file in Transterm database format DE DE Very similar to GCG format. There is a fixed first line referring to the DE transterm program where we put today's date and time, and the line format DE has slightly different spacing to our "GCG" format. XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA u outf AjPFile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // static void codWriteTransterm(const AjPCod thys, AjPFile outf) { const char* c0; const char* c1; const char* c2; const char* bases = "GATC"; char codon[4]="GGG"; ajint i; AjPStr ThreeAa = NULL; AjPStr datestr = NULL; AjPStr timestr = NULL; ajFmtPrintS(&datestr, "%D", ajTimeRefTodayFmt("day")); ajFmtPrintS(×tr, "%D", ajTimeRefTodayFmt("time")); ajFmtPrintF(outf, "FISH_TERM version 4.45 run at %S at %S\n\n", datestr, timestr); ajStrDel(&datestr); ajStrDel(×tr); ajFmtPrintF(outf, "AmAcid Codon Number /1000 Fraction ..\n"); c0 = bases; while(*c0) { codon[0]=*c0; c1 = bases; while(*c1) { codon[1]=*c1; ajFmtPrintF(outf, "\n"); c2 = bases; while(*c2) { codon[2]=*c2; i = ajCodIndexC(codon); if(!ajResidueToTriplet(ajBasecodeFromInt(thys->aa[i]), &ThreeAa)) ajStrAssignC(&ThreeAa, "End"); ajStrFmtLower(&ThreeAa); ajStrFmtTitle(&ThreeAa); ajFmtPrintF(outf, "%S %s %10.2f %6.2f %4.2f\n", ThreeAa, codon, (float) thys->num[i], thys->tcount[i], thys->fraction[i]); c2++; } c1++; } c0++; } ajStrDel(&ThreeAa); return; } ID codWriteGcg TY static MO ajcod LB core XX DE Write codon structure to output file in Gcg format XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA u outf AjPFile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // static void codWriteGcg(const AjPCod thys, AjPFile outf) { const char* c0; const char* c1; const char* c2; const char* bases = "GATC"; char codon[4]="GGG"; ajint i; AjPStr ThreeAa = NULL; ajFmtPrintF(outf, "!!CODON_FREQUENCY 1.0\n\n"); ajFmtPrintF(outf, "AmAcid Codon Number /1000 Fraction ..\n"); c0 = bases; while(*c0) { codon[0]=*c0; c1 = bases; while(*c1) { codon[1]=*c1; ajFmtPrintF(outf, "\n"); c2 = bases; while(*c2) { codon[2]=*c2; i = ajCodIndexC(codon); if(!ajResidueToTriplet(ajBasecodeFromInt(thys->aa[i]), &ThreeAa)) ajStrAssignC(&ThreeAa, "End"); ajStrFmtLower(&ThreeAa); ajStrFmtTitle(&ThreeAa); ajFmtPrintF(outf, "%S %s %10.2f %8.2f %4.2f\n", ThreeAa, codon, (double) thys->num[i], thys->tcount[i], thys->fraction[i]); c2++; } c1++; } c0++; } ajStrDel(&ThreeAa); return; } ID codWriteCodehop TY static MO ajcod LB core XX DE Write codon structure to output file in Codehop program format XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA u outf AjPFile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // static void codWriteCodehop(const AjPCod thys, AjPFile outf) { const char* c0; const char* c1; const char* c2; const char* bases = "ACGU"; char codon[4]="AAA"; ajint i; ajint j = 0; AjPStr release = NULL; AjPStr species = NULL; AjPStr division = NULL; ajStrAssignS(&release, thys->Release); ajStrAssignEmptyC(&release, "unknowndb0.0"); ajStrAssignS(&species, thys->Species); ajStrAssignEmptyC(&species, "unknown species"); ajStrAssignS(&division, thys->Division); ajStrAssignEmptyC(&division, "unknown.division"); c0 = bases; while(*c0) { codon[0]=*c0; c1 = bases; while(*c1) { codon[1]=*c1; c2 = bases; while(*c2) { codon[2]=*c2; i = ajCodIndexC(codon); ajFmtPrintF(outf, "%8.6f\t-- [%2d] %s\n", 0.001 * thys->tcount[i], j++, codon); c2++; } c1++; } c0++; } ajFmtPrintF(outf, "---------------------------------------------\n"); ajFmtPrintF(outf, "%S\n", thys->Name); ajFmtPrintF(outf, "Codon usage for %S: %d\n", species, thys->CdsCount); ajFmtPrintF(outf, " from %S/%S: %d codons\n", release, division, thys->CodonCount); ajStrDel(&species); ajStrDel(&release); ajStrDel(&division); return; } ID codWriteStaden TY static MO ajcod LB core XX DE Write codon structure to output file in Staden format with numbers DE (as generated by nip4) instead of percentages as used by the original DE nip program. XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA u outf AjPFile PD output file PX RT void RD RX CA output CT AjPCod CD Write codon structure to output file CX // static void codWriteStaden(const AjPCod thys, AjPFile outf) { const char* c0; const char* c1; const char* c2; const char* bases = "TCAG"; char codon[4]="TTT"; const char* aa = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.*"; ajint i; c0 = bases; while(*c0) { codon[0]=*c0; ajFmtPrintF(outf, " ===========================================\n"); c2 = bases; while(*c2) { codon[2]=*c2; ajFmtPrintF(outf, " "); c1 = bases; while(*c1) { codon[1]=*c1; i = ajCodIndexC(codon); ajFmtPrintF(outf, " %c %s %4d", aa[thys->aa[i]], codon, thys->num[i]); c1++; } ajFmtPrintF(outf, "\n"); c2++; } c0++; } ajFmtPrintF(outf, " ===========================================\n"); return; } ID ajCodComp TY public MO ajcod LB core XX DE Calculate sequence composition XX PN [1] PA w NA ajint* PD number of A's PX PN [2] PA w NC ajint* PD number of C's PX PN [3] PA w NG ajint* PD number of G's PX PN [4] PA w NT ajint* PD number of T' PX PN [5] PA r str const char* PD sequence PX RT void RD RX // void ajCodComp(ajint *NA, ajint *NC, ajint *NG, ajint *NT, const char *str) { const char *p; ajint c; p = str; while((c = *p)) { if(c=='A') ++(*NA); else if(c=='C') ++(*NC); else if(c=='G') ++(*NG); else if(c=='T') ++(*NT); ++p; } return; } ID codRandom TY static MO ajcod LB core XX DE Calculate expected frequency of a codon XX PN [1] PA r NA ajint PD number of A's PX PN [2] PA r NC ajint PD number of C's PX PN [3] PA r NG ajint PD number of G's PX PN [4] PA r NT ajint PD number of T' PX PN [5] PA r len ajint PD sequence length PX PN [6] PA r p const char* PD triplet PX RT double RD triplet frequency RX // static double codRandom(ajint NA, ajint NC, ajint NG, ajint NT, ajint len, const char *p) { ajint i; double prod; double tot; ajint c; prod = 1; tot = 1.0; for(i=0;i<3;++i) { c = p[i]; if(c=='A') prod = (double)NA; if(c=='C') prod = (double)NC; if(c=='G') prod = (double)NG; if(c=='T') prod = (double)NT; tot *= prod/(double)len; } return tot; } ID codGetWstat TY static MO ajcod LB core XX DE Calculate codon adaptive index W values DE NAR 15:1281-1295 XX PN [1] PA r thys const AjPCod PD codon usage PX RT double* RD w value array RX // static double* codGetWstat(const AjPCod thys) { ajint i; ajint j; double *wk; ajint thisaa; double aamax; AJCNEW0(wk,AJCODSTART); for(i=0;iaa[i]; aamax = (double)INT_MIN; for(j=0;jaa[j]==thisaa) aamax = aamax > thys->tcount[j] ? aamax : thys->tcount[j]; if(!E_FPZERO(aamax,U_DEPS)) wk[i] = thys->tcount[i] / aamax; } return wk; } ID ajCodCalcCaiCod TY public MO ajcod LB core XX DE Calculate codon adaptive index using overall codon usage data only. DE DE See equation 8 in NAR 15:1281-1295 XX PN [1] PA r thys const AjPCod PD codon usage PX RT double RD CAI RX // double ajCodCalcCaiCod(const AjPCod thys) { double cai; double max; double sum; double res; double xij; double total; ajint i; ajint k; total = (double)0.; for(i=0;iaa[k]==27) continue; if(thys->aa[k]==i) max = (max> thys->fraction[k]) ? max : thys->fraction[k]; } sum = (double)0.; for(k=0;kaa[k]==27) continue; if(thys->aa[k]==i) { xij = thys->fraction[k]; if(!E_FPZERO(xij,U_DEPS)) { res = thys->tcount[k] * log(xij/max); sum += res; } } } total += sum; } total /= (double)1000.; cai = exp(total); return cai; } ID ajCodCalcCaiSeq TY public MO ajcod LB core XX DE Calculate codon adaptive index for a coding sequence DE DE See equation 7 in NAR 15:1281-1295 XX PN [1] PA r thys const AjPCod PD codon usage PX PN [2] PA r str const AjPStr PD sequence PX RT double RD CAI RX // double ajCodCalcCaiSeq(const AjPCod thys, const AjPStr str) { double *wk; ajint i; ajint len; const char *p; ajint limit; ajint idx; double total; wk = codGetWstat(thys); len = ajStrGetLen(str); p = ajStrGetPtr(str); limit = len / 3; total = (double)0.; for(i=0;ifraction[i]; NA = NC = NG = NT = 0; ajCodComp(&NA,&NC,&NG,&NT,ajStrGetPtr(s)); /* Get expected frequencies */ for(i=0;itcount[i] = codRandom(NA,NC,NG,NT,len,p); } /* Calculate expected family */ for(i=0;iaa[i]; for(j=0;jaa[j]==aa) x += thys->tcount[j]; fam[i] = x; } /* Calculate expected ratio etc */ for(i=0;itcount[i] / fam[i]; /* Work out ln(real/expected) */ thys->tcount[i]= log(frct[i] / z); } return; } ID ajCodCalcNc TY public MO ajcod LB core XX DE Calculate effective number of codons DE Wright, F. (1990) Gene 87:23-29 XX PN [1] PA r thys const AjPCod PD codon usage PX RT double RD Nc RX // double ajCodCalcNc(const AjPCod thys) { ajint *df = NULL; ajint *n = NULL; ajint i; ajint j; ajint v; ajint max; ajint ndx; ajint *nt = NULL; double *Fbar = NULL; double *F = NULL; double sum; double num = 0.0; AJCNEW0(df, AJCODAMINOS); AJCNEW0(n, AJCODAMINOS); for(i=0;iaa[i]; if(v==27) continue; ++df[v]; n[thys->aa[i]] += thys->num[i]; } for(i=0,max=INT_MIN;i df[i]) ? max : df[i]; AJCNEW0(Fbar, max); AJCNEW0(nt, max); AJCNEW0(F, AJCODAMINOS); for(i=0;iaa[j]==27) continue; if(thys->aa[j]==i) F[i] += pow(thys->fraction[j],2.0); } for(i=0;i61.0) return 61.0; return sum; } ID ajCodCalcUsage TY public MO ajcod LB core XX DE Calculate fractional and thousand elements of a codon object DE Used for creating a codon usage table DE DE If no counts are stored, warns of need to first run ajCodSetTriplets XX PN [1] PA u thys AjPCod PD Codon object PX PN [2] PA r c ajint PD triplet count PX RT void RD RX // void ajCodCalcUsage(AjPCod thys, ajint c) { ajint i; ajint *aasum; ajuint totcnt = 0; for(i=0;inum[i]; if(!totcnt) ajWarn("empty codon usage table, first call ajCodSetTripletsS"); /* Calculate thousands */ for(i=0;itcount[i] = 0.0; else thys->tcount[i] = ((double)thys->num[i] / (double)c) * 1000.0; /* Get number of codons per amino acid */ AJCNEW0(aasum, AJCODAMINOS); for(i=0;iaa[i]==27) aasum[27] += thys->num[i]; else aasum[thys->aa[i]] += thys->num[i]; /* Calculate fraction */ for(i=0;iaa[i]==27) { if(!aasum[27]) thys->fraction[i] = 0.0; else thys->fraction[i] = (double)thys->num[i] / (double)aasum[27]; } else { if(!aasum[thys->aa[i]]) thys->fraction[i] = 0.0; else thys->fraction[i] = (double)thys->num[i] / (double)aasum[thys->aa[i]]; } AJFREE(aasum); return; } ID ajCodGetName TY public MO ajcod LB core XX DE Returns the name of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT const AjPStr RD Original filename RX // const AjPStr ajCodGetName(const AjPCod thys) { return thys->Name; } ID ajCodGetNameC TY public MO ajcod LB core XX DE Returns the name of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT const char* RD Original filename RX // const char* ajCodGetNameC(const AjPCod thys) { return ajStrGetPtr(thys->Name); } ID ajCodGetDesc TY public MO ajcod LB core XX DE Returns the description of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT const AjPStr RD Original filename RX // const AjPStr ajCodGetDesc(const AjPCod thys) { return thys->Desc; } ID ajCodGetDescC TY public MO ajcod LB core XX DE Returns the description of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT const char* RD Original filename RX // const char* ajCodGetDescC(const AjPCod thys) { return ajStrGetPtr(thys->Desc); } ID ajCodGetSpecies TY public MO ajcod LB core XX DE Returns the species of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT const AjPStr RD Species RX // const AjPStr ajCodGetSpecies(const AjPCod thys) { return thys->Species; } ID ajCodGetSpeciesC TY public MO ajcod LB core XX DE Returns the species of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT const char* RD Species RX // const char* ajCodGetSpeciesC(const AjPCod thys) { return ajStrGetPtr(thys->Species); } ID ajCodGetDivision TY public MO ajcod LB core XX DE Returns the division of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT const AjPStr RD Division RX // const AjPStr ajCodGetDivision(const AjPCod thys) { return thys->Division; } ID ajCodGetDivisionC TY public MO ajcod LB core XX DE Returns the division of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT const char* RD Division RX // const char* ajCodGetDivisionC(const AjPCod thys) { return ajStrGetPtr(thys->Division); } ID ajCodGetRelease TY public MO ajcod LB core XX DE Returns the release of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT const AjPStr RD Release RX // const AjPStr ajCodGetRelease(const AjPCod thys) { return thys->Release; } ID ajCodGetReleaseC TY public MO ajcod LB core XX DE Returns the release of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT const char* RD Release RX // const char* ajCodGetReleaseC(const AjPCod thys) { return ajStrGetPtr(thys->Release); } ID ajCodGetNumcodon TY public MO ajcod LB core XX DE Returns the number of codons in a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT ajint RD Number of codons RX // ajint ajCodGetNumcodon(const AjPCod thys) { return thys->CodonCount; } ID ajCodGetNumcds TY public MO ajcod LB core XX DE Returns the numbers of CDSs in a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT ajint RD Number of CDSs RX // ajint ajCodGetNumcds(const AjPCod thys) { return thys->CdsCount; } ID ajCodGetCode TY public MO ajcod LB core XX DE Returns the genetic code of a codon table XX PN [1] PA r thys const AjPCod PD Codon usage object PX RT ajint RD Number of CDSs RX // ajint ajCodGetCode(const AjPCod thys) { return thys->GeneticCode; } ID ajCodSetCodenum TY public MO ajcod LB core XX DE Assigns the genetic code in a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r geneticcode ajint PD Genetic code PX RT void RD RX // void ajCodSetCodenum(AjPCod thys, ajint geneticcode) { thys->GeneticCode = geneticcode; return; } ID ajCodSetDescC TY public MO ajcod LB core XX DE Assigns the description of a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r desc const char* PD Description PX RT void RD RX // void ajCodSetDescC(AjPCod thys, const char* desc) { ajStrAssignC(&thys->Desc, desc); return; } ID ajCodSetDescS TY public MO ajcod LB core XX DE Assigns the description of a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r desc const AjPStr PD Description PX RT void RD RX // void ajCodSetDescS(AjPCod thys, const AjPStr desc) { ajStrAssignS(&thys->Desc,desc ); return; } ID ajCodSetDivisionC TY public MO ajcod LB core XX DE Assigns the division of a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r division const char* PD Division PX RT void RD RX // void ajCodSetDivisionC(AjPCod thys, const char* division) { ajStrAssignC(&thys->Division, division); return; } ID ajCodSetDivisionS TY public MO ajcod LB core XX DE Assigns the division of a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r division const AjPStr PD Division PX RT void RD RX // void ajCodSetDivisionS(AjPCod thys, const AjPStr division) { ajStrAssignS(&thys->Division, division); return; } ID ajCodSetNameC TY public MO ajcod LB core XX DE Assigns the name of a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r name const char* PD Name PX RT void RD RX // void ajCodSetNameC(AjPCod thys, const char* name) { ajStrAssignC(&thys->Name, name); return; } ID ajCodSetNameS TY public MO ajcod LB core XX DE Assigns the name of a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r name const AjPStr PD Name PX RT void RD RX // void ajCodSetNameS(AjPCod thys, const AjPStr name) { ajStrAssignS(&thys->Name, name); return; } ID ajCodSetNumcds TY public MO ajcod LB core XX DE Assigns the number of CDSs in a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r numcds ajint PD Number of codons PX RT void RD RX // void ajCodSetNumcds(AjPCod thys, ajint numcds) { thys->CdsCount = numcds; return; } ID ajCodSetNumcodons TY public MO ajcod LB core XX DE Assigns the number of codons in a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r numcodon ajint PD Number of codons PX RT void RD RX // void ajCodSetNumcodons(AjPCod thys, ajint numcodon) { thys->CodonCount = numcodon; return; } ID ajCodSetReleaseC TY public MO ajcod LB core XX DE Assigns the of a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r release const char* PD Release PX RT void RD RX // void ajCodSetReleaseC(AjPCod thys, const char* release) { ajStrAssignC(&thys->Release, release); return; } ID ajCodSetReleaseS TY public MO ajcod LB core XX DE Assigns the release of a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r release const AjPStr PD Release PX RT void RD RX // void ajCodSetReleaseS(AjPCod thys, const AjPStr release) { ajStrAssignS(&thys->Release, release); return; } ID ajCodSetSpeciesC TY public MO ajcod LB core XX DE Assigns the species of a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r species const char* PD Species PX RT void RD RX // void ajCodSetSpeciesC(AjPCod thys, const char* species) { ajStrAssignC(&thys->Species, species); return; } ID ajCodSetSpeciesS TY public MO ajcod LB core XX DE Assigns the species of a codon table XX PN [1] PA u thys AjPCod PD Codon usage object PX PN [2] PA r species const AjPStr PD Species PX RT void RD RX // void ajCodSetSpeciesS(AjPCod thys, const AjPStr species) { ajStrAssignS(&thys->Species, species); return; } ID ajCodoutformatFind TY public MO ajcod LB core XX DE Tests the output format for an outcodon ACD type XX PN [1] PA r name const AjPStr PD Format name PX PN [2] PA w iformat ajint* PD Internal format index PX RT AjBool RD True on success RX // AjBool ajCodoutformatFind(const AjPStr name, ajint *iformat) { ajuint i; for(i=0;codoutFormatDef[i].Name; i++) { if(ajStrMatchCaseC(name, codoutFormatDef[i].Name)) { *iformat = i; return ajTrue; } } *iformat = 0; return ajFalse; } ID codFix TY static MO ajcod LB core XX DE Fill in missing values in a codon usage object XX PN [1] PA u thys AjPCod PD Codon usage object PX RT void RD RX // static void codFix(AjPCod thys) { ajint i; ajint totnum = 0; ajint totcds = 0; double totfreq = 0.0; double totfrac = 0.0; ajint totaa = 0; ajint aa; ajint aacount[28] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; for(i=0;i<64;i++) { totnum += thys->num[i]; totfreq += thys->tcount[i]; totfrac += thys->fraction[i]; aa = thys->aa[i]; if(aa == 27) totcds += thys->num[i]; if(aa > 27) aa = 26; if(aa < 0) aa = 26; if(aacount[aa] == -1) { totaa++; aacount[aa] = 0; } aacount[aa] += thys->num[i]; } if(thys->CdsCount) { if(totcds > thys->CdsCount) /* can be less if stops ignored */ ajDebug("Codon usage file '%S' has %d stop codons, CDS count %d\n", thys->Name, totcds, thys->CdsCount); } else thys->CdsCount = totcds; if(thys->CodonCount) { if(totnum != thys->CodonCount) ajDebug("Codon usage file '%S' has %d codons, Codon count %d\n", thys->Name, totnum, thys->CodonCount); } else thys->CodonCount = totnum; if(E_FPZERO(totfreq,U_DEPS)) { for(i=0;i<64;i++) { thys->tcount[i] = 1000.0 * (double)thys->num[i] / (double) totnum; totfreq += thys->tcount[i]; } } if(fabs(totfreq - 1000.0) > 0.1) ajDebug("Codon usage file '%S' has total frequency/1000 of %.2f\n", thys->Name, totfreq); if(E_FPZERO(totfrac,U_DEPS)) { for(i=0;i<64;i++) { aa = thys->aa[i]; if(aa > 27) aa = 26; if(aa < 0) aa = 26; if(aacount[aa]) thys->fraction[i] = (double)thys->num[i]/(double)(aacount[aa]); totfrac += thys->fraction[i]; } } if(fabs(totfrac - (double)totaa) > 0.1) ajDebug("Codon usage file '%S' has total fraction of %.2f for %d " "amino acids\n", thys->Name, totfrac, totaa); return; } ID ajCodPrintFormat TY public MO ajcod LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report, currently no extra details printed PX RT void RD RX // void ajCodPrintFormat(AjPFile outf, AjBool full) { ajint i = 0; if(full) i=0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Codon usage input formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Try Test for unknown input files\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Try Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "Format {\n"); for(i=0; codInFormatDef[i].Name; i++) { ajFmtPrintF(outf, " %-12s %3B \"%s\"\n", codInFormatDef[i].Name, codInFormatDef[i].Try, codInFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Codon usage output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "OFormat {\n"); for(i=0; codoutFormatDef[i].Name; i++) { ajFmtPrintF(outf, " %-12s \"%s\"\n", codoutFormatDef[i].Name, codoutFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajCodGetCodonlist TY public MO ajcod LB core XX DE Writes codon triplets to a string list XX PN [1] PA r cod const AjPCod PD Cusp file PX PN [2] PA w list AjPList PD List with character distributions PX RT void RD RX // void ajCodGetCodonlist(const AjPCod cod, AjPList list) { ajint i; ajint j; AjPStr codon = NULL; for(i=0;itcount[i];j++) { codon = ajStrNewC(ajCodTriplet(i)); ajListstrPushAppend(list, codon); codon = NULL; } return; } ID ajCodExit TY public MO ajcod LB core XX DE Cleans up codon usage processing internal memory XX RT void RD RX // void ajCodExit(void) { ajStrDel(&codReadLine); ajStrDel(&codTmpLine); return; } ID ajMeltInit TY public MO ajdan LB core XX DE Initialises melt entropies, enthalpies and energies. Different data DE files are read for DNA or RNA heteroduplex. Also sets optional flag DE for array saving of the above. XX PN [1] PA r isdna AjBool PD true for DNA, false for RNA PX PN [2] PA r savesize ajint PD Size of array to save, or zero if none PX RT void RD Number of energies to save RX // void ajMeltInit(AjBool isdna, ajint savesize) { AjPFile mfptr; AjPStr mfname = NULL; AjPStr pair = NULL; AjPStr pair1 = NULL; AjPStr pair2 = NULL; AjPStr acgt = NULL; AjPStr line = NULL; ajint i; ajint j; ajint k; char *p; const char *q; float enthalpy; float entropy; float energy; ajuint iline = 0; AjBool got1; AjBool got2; aj_melt_savesize = savesize; aj_melt_saveinit = ajFalse; if(meltInitDone) return; mfname = ajStrNew(); if(isdna) ajStrAssignEmptyC(&mfname,DNAMELTFILE); else ajStrAssignEmptyC(&mfname,RNAMELTFILE); mfptr = ajDatafileNewInNameS(mfname); if(!mfptr) ajFatal("Entropy/enthalpy/energy file '%S' not found\n", mfname); pair1 = ajStrNew(); pair2 = ajStrNew(); line = ajStrNew(); ajStrAssignC(&pair,"AA"); ajStrAssignC(&acgt,"ACGT"); p = ajStrGetuniquePtr(&pair); q = ajStrGetPtr(acgt); for(i=0,k=0;i<4;++i) { *p = *(q+i); for(j=0;j<4;++j) { *(p+1) = *(q+j); meltTable[k++].pair = ajStrNewC(p); } } iline = 0; while(ajReadline(mfptr, &line)) { ajStrRemoveWhiteExcess(&line); iline++; p = ajStrGetuniquePtr(&line); if(*p=='#' || *p=='!' || !*p) continue; p = ajSysFuncStrtok(p," "); if(!p) ajDie("Bad melt data file '%F' line %d '%S'", mfptr, iline, line); ajStrAssignC(&pair1,p); p = ajSysFuncStrtok(NULL," "); if(!p) ajDie("Bad melt data file '%F' line %d '%S'", mfptr, iline, line); ajStrAssignC(&pair2,p); p = ajSysFuncStrtok(NULL," "); if(!p) ajDie("Bad melt data file '%F' line %d '%S'", mfptr, iline, line); if(sscanf(p,"%f",&enthalpy)!=1) ajDie("Bad melt data file '%F' line %d '%S'", mfptr, iline, line); p = ajSysFuncStrtok(NULL," "); if(sscanf(p,"%f",&entropy)!=1) ajDie("Bad melt data file '%F' line %d '%S'", mfptr, iline, line); p = ajSysFuncStrtok(NULL," "); if(sscanf(p,"%f",&energy)!=1) ajDie("Bad melt data file '%F' line %d '%S'", mfptr, iline, line); got1 = got2 = ajFalse; for(k=0;k<16;++k) if(!ajStrCmpS(meltTable[k].pair,pair1)) { meltTable[k].enthalpy = enthalpy; meltTable[k].entropy = entropy; meltTable[k].energy = energy; got1 = ajTrue; } for(k=0;k<16;++k) if(!ajStrCmpS(meltTable[k].pair,pair2)) { meltTable[k].enthalpy = enthalpy; meltTable[k].entropy = entropy; meltTable[k].energy = energy; got2 = ajTrue; } if(!got1 || !got2) ajDie("Bad melt data file '%F' line %d '%S' duplicate pair", mfptr, iline, line); } ajStrDel(&mfname); ajStrDel(&pair); ajStrDel(&pair1); ajStrDel(&pair2); ajStrDel(&acgt); ajStrDel(&line); ajFileClose(&mfptr); meltInitDone = ajTrue; return; } ID meltProbScore TY static MO ajdan LB core XX DE Gives a score for the probability of two sequences being the same. DE The sequences are the same length. DE DE Uses IUB ambiguity codes. The result is the sum of the probabilities DE at each position. XX PN [1] PA r seq1 const AjPStr PD Pointer to a sequence string PX PN [2] PA r seq2 const AjPStr PD Pointer to a another sequence PX PN [3] PA r len ajint PD Length of sequences PX RT float RD Match probability RX // static float meltProbScore(const AjPStr seq1, const AjPStr seq2, ajint len) { ajint mlen; float score; ajint i; ajint x; ajint y; const char *p; const char *q; mlen = (ajStrGetLen(seq1) < ajStrGetLen(seq2)) ? ajStrGetLen(seq1) : ajStrGetLen(seq2); if(len > 0) mlen = (mlen < len) ? mlen : len; score = 0.0; if(!mlen) return score; score = 1.0; p = ajStrGetPtr(seq1); q = ajStrGetPtr(seq2); for(i=0; i 0) ? ajTrue : ajFalse; ipos = 0; if(doShift) { if(!aj_melt_saveinit) { ipos = 0; for(i=0;i0.9) { if(doShift) { saveEnergy[ipos] += (ident * meltTable[j].energy); saveEntropy[ipos] += (ident * meltTable[j].entropy); saveEnthalpy[ipos] += (ident * meltTable[j].enthalpy); } else { energy += (ident * meltTable[j].energy); *entropy += (ident * meltTable[j].entropy); *enthalpy += (ident * meltTable[j].enthalpy); } } } if(doShift) { energy += saveEnergy[ipos]; *entropy += saveEntropy[ipos]; *enthalpy += saveEnthalpy[ipos]; } ++ipos; } ajStrDel(&line); return energy; } ID ajMeltTemp TY public MO ajdan LB core XX DE Calculates melt temperature of DNA or RNA DE An optional shift is given for stepping along the sequence and loading DE up energy arrays. XX PN [1] PA r strand const AjPStr PD Pointer to a sequence string PX PN [2] PA r len ajint PD Length of sequence PX PN [3] PA r shift ajint PD Stepping value PX PN [4] PA r saltconc float PD mM salt concentration PX PN [5] PA r DNAconc float PD nM DNA concentration PX PN [6] PA r isDNA AjBool PD DNA or RNA PX RT float RD Melt temperature RX // float ajMeltTemp(const AjPStr strand, ajint len, ajint shift, float saltconc, float DNAconc, AjBool isDNA) { double entropy; double enthalpy; double dTm; float Tm; static float sumEntropy; static float sumEnthalpy; float To; float R; double LogDNA; R = (float) 1.987; /* molar gas constant (cal/c * mol) */ LogDNA = R * (float)log((double)(DNAconc/4000000000.0)); /* nM */ To = (float) 273.15; ajMeltEnergy(strand, len, shift, isDNA, ajFalse, &sumEnthalpy, &sumEntropy); entropy = -10.8 - sumEntropy; entropy += (len-1) * (log10((double) (saltconc/1000.0))) * (float) 0.368; enthalpy = -sumEnthalpy; dTm = ((enthalpy*1000.0) / (entropy+LogDNA)) - To; Tm = (float) dTm; return Tm; } ID ajMeltGC TY public MO ajdan LB core XX DE Calculates GC fraction of a sequence allowing for ambiguity XX PN [1] PA r strand const AjPStr PD Pointer to a sequence string PX PN [2] PA r len ajint PD Length of sequence PX RT float RD GC fraction RX // float ajMeltGC(const AjPStr strand, ajint len) { ajint t; ajint i; const char *p; double count; p=ajStrGetPtr(strand); count = 0.0; for(i=0;i.9) { (*saveentr)[i] += (ident * meltTable[j].entropy); (*saveenth)[i] += (ident * meltTable[j].enthalpy); (*saveener)[i] += (ident * meltTable[j].energy); } } } ajStrDel(&line); energy = *enthalpy = *entropy = 0.0; for(i=0;ihost = ajStrNew(); ret->path = ajStrNew(); ret->port = 80; ret->sources = ajListNew(); return ret; } ID ajDasServerDel TY public MO ajdas LB ajaxdb XX DE Delete DASserver object XX PN [1] PA u thys AjPDasServer* PD DASserver object PX RT void RD RX // void ajDasServerDel(AjPDasServer *thys) { AjPDasServer pthis = NULL; AjPDasSource source; AjPDasCoordinate coord; AjIList iter; AjIList iterc; if(!thys) return; if(!*thys) return; pthis = *thys; ajStrDel(&pthis->host); ajStrDel(&pthis->path); iter = ajListIterNew(pthis->sources); while(!ajListIterDone(iter)) { source = ajListIterGet(iter); ajStrDel(&source->uri); ajStrDel(&source->title); ajStrDel(&source->description); ajStrDel(&source->sequence_query_uri); ajStrDel(&source->entry_points_uri); ajStrDel(&source->features_query_uri); iterc = ajListIterNew(source->coordinates); while(!ajListIterDone(iterc)) { coord = ajListIterGet(iterc); ajStrDel(&coord->authority); ajStrDel(&coord->source); ajStrDel(&coord->taxid); ajStrDel(&coord->test_range); ajStrDel(&coord->uri); ajStrDel(&coord->version); AJFREE(coord); } ajListFree(&source->coordinates); AJFREE(source); ajListIterDel(&iterc); } ajListIterDel(&iter); ajListFree(&pthis->sources); AJFREE(pthis); *thys = NULL; return; } ID ajDasSegmentDel TY public MO ajdas LB ajaxdb XX DE Delete DAS segment objects returned by entry_points queries XX PN [1] PA u Psegment AjPDasSegment* PD DAS segment object PX RT void RD RX // void ajDasSegmentDel(AjPDasSegment* Psegment) { if(Psegment==NULL || *Psegment==NULL) return; ajStrDel(&(*Psegment)->id); ajStrDel(&(*Psegment)->type); ajStrDel(&(*Psegment)->orientation); AJFREE(*Psegment); return; } ID dasRegistryElementstart TY static MO ajdas LB ajaxdb XX DE Handler for reading DAS registry XML elements XX PN [1] PA u data void* PD structure for storing results PX PN [2] PA r name const XML_Char* PD XML element name PX PN [3] PA r atts const XML_Char** PD array of element attributes, PD name/value pairs PX RT void RD RX // static void dasRegistryElementstart(void *data, const XML_Char *name, const XML_Char **atts) { AjPList sources = NULL; AjPDasSource source = NULL; const char* key = NULL; const char* value = NULL; const char* type = NULL; const char* query_uri = NULL; ajuint i; sources = (AjPList) data; ajDebug("dasRegistryElementstart: %s\n",name); if(ajCharMatchCaseC("SOURCES",name)) return; if(ajCharMatchCaseC(name,"SOURCE")) { AJNEW0(source); source->coordinates = ajListNew(); for(i = 0; atts[i]; i += 2) { key = atts[i]; value = atts[i+1]; if(ajCharMatchC(key,"uri")) ajStrAssignC(&source->uri,value); else if(ajCharMatchC(key,"title")) { ajStrAssignC(&source->title,value); ajStrTrimWhite(&source->title); } else if(ajCharMatchC(key,"description")) ajStrAssignC(&source->description,value); } ajListPushAppend(sources,source); } else if(ajCharMatchCaseC(name,"CAPABILITY")) { ajListPeekLast(sources,(void**)&source); for(i = 0; atts[i]; i += 2) { key = atts[i]; value = atts[i+1]; if(ajCharMatchC(key,"type")) type = value; else query_uri = value; } if(ajCharMatchCaseC(type,"das1:features")) { source->features = ajTrue; ajStrAssignC(&source->features_query_uri,query_uri); } else if(ajCharMatchCaseC(type,"das1:sequence")) { source->sequence = ajTrue; ajStrAssignC(&source->sequence_query_uri,query_uri); } else if(ajCharMatchCaseC(type,"das1:entry_points")) { source->entry_points = ajTrue; ajStrAssignC(&source->entry_points_uri,query_uri); } } else if(ajCharMatchCaseC(name,"COORDINATES")) { AjPDasCoordinate coord; AJNEW0(coord); ajListPeekLast(sources,(void**)&source); for(i = 0; atts[i]; i += 2) { key = atts[i]; value = atts[i+1]; if(ajCharMatchCaseC(key,"uri")) ajStrAssignC(&coord->uri, value); else if(ajCharMatchCaseC(key,"taxid")) ajStrAssignC(&coord->taxid, value); else if(ajCharMatchCaseC(key,"source")) ajStrAssignC(&coord->source,value); else if(ajCharMatchCaseC(key,"test_range")) ajStrAssignC(&coord->test_range,value); } ajListPushAppend(source->coordinates,coord); } else if(!(ajCharMatchC(name,"MAINTAINER") || ajCharMatchC(name,"VERSION") || ajCharMatchC(name,"PROP") )) ajDebug("DAS 'sources' request returned an unknown tag: %s",name); return; } ID dasEntrypointsElementstart TY static MO ajdas LB ajaxdb XX DE Handler for reading DAS entry-point XML elements XX PN [1] PA u data void* PD structure for storing results PX PN [2] PA r name const XML_Char* PD XML element name PX PN [3] PA r atts const XML_Char** PD array of name/value pairs PX RT void RD RX // static void dasEntrypointsElementstart(void *data, const XML_Char *name, const XML_Char **atts) { const char* key = NULL; AjPStr value = NULL; AjPList segments = data; AjPDasSegment segment; ajuint i; ajDebug("dasEntrypointsElementstart: %s\n",name); if(ajCharMatchCaseC("DASEP",name) || ajCharMatchCaseC("ENTRY_POINTS",name)) { return; } if(!(ajCharMatchC(name,"SEGMENT"))) ajDebug("DAS 'entrypoints' request returned an unknown tag: %s",name); { AJNEW0(segment); for(i = 0; atts[i]; i += 2) { key = atts[i]; value = ajStrNewC(atts[i+1]); if(ajCharMatchC(key,"id")) ajStrAssignS(&segment->id,value); else if(ajCharMatchC(key,"start")) { ajStrToUint(value, &segment->start); } else if(ajCharMatchC(key,"stop")) { ajStrToUint(value, &segment->stop); } else if(ajCharMatchC(key,"subparts")) { ajStrToBool(value, &segment->subparts); } else if(ajCharMatchC(key,"orientation")) { ajStrAssignS(&segment->orientation,value); } else if(ajCharMatchC(key,"type")) { ajStrAssignS(&segment->type,value); } ajStrDel(&value); } ajListPushAppend(segments,segment); } return; } ID ajDasParseRegistry TY public MO ajdas LB ajaxdb XX DE Parses XML list of DAS sources, either coming from dasregistry.org DE or from individual DAS servers supporting DAS 'sources' command XX PN [1] PA u buff AjPFilebuff PD XML input stream PX PN [2] PA u sources AjPList PD pointer to the data structure for storing PD parser results of DAS/XML 'source' objects PX RT AjBool RD True on success RX // AjBool ajDasParseRegistry(AjPFilebuff buff, AjPList sources) { return dasQueryParse(buff, sources, dasRegistryElementstart, NULL); } ID ajDasParseEntrypoints TY public MO ajdas LB ajaxdb XX DE Parses XML list of DAS entry-points, either coming from dasregistry.org DE or from individual DAS servers supporting DAS 'entry_points' command XX PN [1] PA u buff AjPFilebuff PD XML input stream PX PN [2] PA u segments AjPList PD pointer to the data structure for storing PD parser results of DAS/XML 'segment' objects PX RT AjBool RD True on success RX // AjBool ajDasParseEntrypoints(AjPFilebuff buff, AjPList segments) { return dasQueryParse(buff, segments, dasEntrypointsElementstart, NULL); } ID ajDasServerSethostS TY public MO ajdas LB ajaxdb XX DE Set the DAS server host name XX PN [1] PA u server AjPDasServer PD DASserver object PX PN [2] PA r host const AjPStr PD DAS server host name PX RT AjBool RD True on success RX // AjBool ajDasServerSethostS(AjPDasServer server, const AjPStr host) { ajStrAssignS(&server->host, host); return ajTrue; } ID ajDasServerSetpathS TY public MO ajdas LB ajaxdb XX DE Set the DAS server URL path XX PN [1] PA u server AjPDasServer PD DASserver object PX PN [2] PA r path const AjPStr PD DAS server URL path PX RT AjBool RD True on success RX // AjBool ajDasServerSetpathS(AjPDasServer server, const AjPStr path) { ajStrAssignS(&server->path, path); return ajTrue; } ID ajDasServerSetport TY public MO ajdas LB ajaxdb XX DE Set the DAS server port XX PN [1] PA u server AjPDasServer PD DASserver object PX PN [2] PA r port ajuint PD DAS server port PX RT AjBool RD True on success RX // AjBool ajDasServerSetport(AjPDasServer server, ajuint port) { server->port = port; return ajTrue; } ID ajDasServerGetSources TY public MO ajdas LB ajaxdb XX DE Makes an http request for getting either list of all DAS sources DE or a particular DAS source on a given DAS server. Http response is parsed DE and results are stored in server object's 'sources' attribute XX PN [1] PA u server AjPDasServer PD DAS server object PX PN [2] PA r cmd const AjPStr PD DAS 'sources' command, queries either PD all DAS sources or a particular DAS source PX RT void RD RX // void ajDasServerGetSources(AjPDasServer server, const AjPStr cmd) { AjPStr cmdpath = ajStrNew(); AjPStr httpver = ajStrNew(); AjPStr dbname = ajStrNew(); AjPStr dbproxy = ajStrNew(); AjPTextin textin = ajTextinNewDatatype(AJDATATYPE_TEXT); if(ajStrGetCharLast(server->path)!='/') ajFmtPrintS(&cmdpath, "%S/%S", server->path, cmd); else ajFmtPrintS(&cmdpath, "%S%S", server->path, cmd); ajFilebuffDel(&textin->Filebuff); textin->Filebuff = ajHttpRead(httpver, dbname, dbproxy, server->host, server->port, cmdpath); if(textin->Filebuff) { ajFilebuffHtmlNoheader(textin->Filebuff); ajDasParseRegistry(textin->Filebuff, server->sources); } ajTextinDel(&textin); ajStrDel(&cmdpath); ajStrDel(&httpver); ajStrDel(&dbname); ajStrDel(&dbproxy); } ID ajDasGetSequenceQueryURI TY public MO ajdas LB ajaxdb XX DE Given a DAS server URL, returns sequence query URI for the DAS source DE specified in user query. DE DE It first makes a DAS source command/query for the specific DAS source. DE If above fails then makes another query for all DAS sources DE and checks them against the current DAS source. XX PN [1] PA r qry const AjPQuery PD sequence query object PX PN [2] PA r sourceURIorTitle const AjPStr PD DAS source URI or title PD specified in current query PX RT AjPStr RD sequence-query URI, null if not found RX // AjPStr ajDasGetSequenceQueryURI(const AjPQuery qry, const AjPStr sourceURIorTitle) { AjPDasServer server = NULL; AjPDasSource source = NULL; AjPStr host = NULL; AjPStr path = NULL; AjPStr cmd = NULL; AjIList iter = NULL; AjPStr ret = NULL; ajint port = 80; AjBool validresponse = ajTrue; ajDebug("ajDasGetSequenceQueryURI: searching query URI for '%S'", sourceURIorTitle); if(!ajHttpQueryUrl(qry, &port, &host, &path)) { ajStrDel(&host); ajStrDel(&path); return ajFalse; } server = ajDasServerNew(); ajDasServerSethostS(server,host); ajDasServerSetport(server,port); ajDasServerSetpathS(server,path); /* first try direct source command */ ajFmtPrintS(&cmd, "sources/%S", sourceURIorTitle); ajDasServerGetSources(server, cmd); if (ajListGetLength(server->sources)==1) { ajListPeekFirst(server->sources, (void**)&source); if(source == NULL || ajListGetLength(source->coordinates)==0) { ajDebug("DAS data source query '%S' didn't return a valid result", cmd); validresponse = ajFalse; } else if(source->sequence == ajFalse) { ajErr("DAS data source (%S,%S) doesn't support sequence queries", source->uri, source->title); ajStrDel(&host); ajStrDel(&path); ajDasServerDel(&server); ajStrDel(&cmd); return NULL; } if(validresponse && ajStrMatchS(sourceURIorTitle, source->uri)) { if(source->sequence_query_uri!=NULL) { ret = ajStrNewRef(source->sequence_query_uri); ajDebug("dasGetSequenceQueryURI: DAS source found " "by direct query '%S'", ret); } else { //TODO: here we can try building the query_uri unless host // is dasregistry..... ajFmtPrintS(&ret, "%S",host); ajWarn("server source description didn't have a query_uri..."); } } } if(ret==NULL) { ajDebug("dasGetSequenceQueryURI: trying a full 'sources' query"); ajStrAssignC(&cmd, "sources"); ajDasServerGetSources(server, cmd); iter = ajListIterNew(server->sources); while(!ajListIterDone(iter)) { source = ajListIterGet(iter); if((ajStrMatchS(sourceURIorTitle, source->title) || ajStrMatchS(sourceURIorTitle, source->uri))) { if(source->sequence == ajFalse) { ajErr("DAS data source (%S,%S) doesn't support " "sequence queries", source->uri, source->title); ajStrDel(&host); ajStrDel(&path); ajDasServerDel(&server); ajStrDel(&cmd); ajListIterDel(&iter); return NULL; } ret = ajStrNewRef(source->sequence_query_uri); ajDebug("ajDasGetSequenceQueryURI: URI found '%S'", ret); break; } } ajListIterDel(&iter); } ajStrDel(&host); ajStrDel(&path); ajStrDel(&cmd); ajDasServerDel(&server); return ret; } ID dasQueryParse TY static MO ajdas LB ajaxdb XX DE Parses DAS query responses XX PN [1] PA u buff AjPFilebuff PD buffer object holding DAS query response PX PN [2] PA u results void* PD object for storing parser output PX PN [3] PA u starth XML_StartElementHandler PD handler for processing PD XML element attributes PX PN [4] PA u elementh XML_CharacterDataHandler PD handler for processing PD XML element data PX RT AjBool RD True on success RX // static AjBool dasQueryParse(AjPFilebuff buff, void* results, XML_StartElementHandler starth, XML_CharacterDataHandler elementh) { XML_Parser parser = NULL; AjPStr line = NULL; int done; size_t len; if(!buff) return ajFalse; line = ajStrNew(); parser = XML_ParserCreate(NULL); XML_SetElementHandler(parser, starth, NULL); if(elementh!=NULL) XML_SetCharacterDataHandler(parser, elementh); XML_SetUserData(parser, results); done = 0; do { ajBuffreadLine(buff,&line); done = ajFilebuffIsEmpty(buff); len = ajStrGetLen(line); if(!XML_Parse(parser, line->Ptr, len, done)) { ajErr("Failed to parse received DAS response at line %d: '%s'", XML_GetCurrentLineNumber(parser), XML_ErrorString(XML_GetErrorCode(parser))); XML_ParserFree(parser); ajStrDel(&line); return ajFalse; } } while (!done); XML_ParserFree(parser); ajStrDel(&line); return ajTrue; } ID ajDasSourceGetDBname TY public MO ajdas LB ajaxdb XX DE Returns an EMBOSS DB name for a given DAS source XX PN [1] PA r source const AjPDasSource PD DAS source object PX PN [2] PA r titleAndURI AjBool PD Include URI in name PX RT AjPStr RD DB name RX // AjPStr ajDasSourceGetDBname(const AjPDasSource source, AjBool titleAndURI) { AjPStr dbname = NULL; if (titleAndURI) { dbname = ajStrNewS(source->uri); ajStrAppendK(&dbname, '_'); ajStrAppendS(&dbname, source->title); } else dbname = ajStrNewS(source->title); ajStrExchangeSetCC(&dbname, " \":+-()./", "_________"); ajStrTrimC(&dbname, "_"); ajStrTrimStartC(&dbname, "0123456789"); return dbname; } ID ajDasPrintCachefile TY public MO ajdas LB ajaxdb XX DE Prints DB definition for the specified DAS server object XX PN [1] PA r server const AjPDasServer PD DAS server object PX PN [2] PA w cachef AjPFile PD server cachefile with DB definitions PX RT void RD RX // void ajDasPrintCachefile(const AjPDasServer server, AjPFile cachef) { AjPTime today = NULL; AjIList iter = NULL; AjPStr fname = NULL; AjPTable titlecount = NULL; AjPDasSource source = NULL; AjPStr title = NULL; ajuint* count = NULL; titlecount = ajTablestrNewCaseConst(ajListGetLength(server->sources)+20); fname = ajStrNewS(ajFileGetPrintnameS(cachef)); ajFilenameTrimPath(&fname); today = ajTimeNewTodayFmt("cachefile"); ajFmtPrintF(cachef,"# %S %D\n\n", fname, today); ajStrDel(&fname); ajTimeDel(&today); iter = ajListIterNew(server->sources); while(!ajListIterDone(iter)) { source = ajListIterGet(iter); title = source->title; count = ajTableFetchmodS(titlecount,title); if (count != NULL) { (*count)++; } else { AJNEW(count); *count = 1; ajTablePut(titlecount, title, (void*)count); } } ajListIterRewind(iter); while(!ajListIterDone(iter)) { source = ajListIterGet(iter); title = source->title; count = ajTableFetchmodS(titlecount,title); dasWriteDBdefinition(cachef, source, *count>1); } ajListIterDel(&iter); ajTableDelValdel(&titlecount, &ajMemFree); return; } ID dasWriteDBdefinition TY static MO ajdas LB ajaxdb XX DE Writes a DAS source DB definition to the specified cache-file XX PN [1] PA u cachef AjPFile PD cache file PX PN [2] PA r source const AjPDasSource PD DAS source object PX PN [3] PA r titleAndURI AjBool PD Include URI in name PX RT AjBool RD True on success RX // static AjBool dasWriteDBdefinition(AjPFile cachef, const AjPDasSource source, AjBool titleAndURI) { AjPStr entry = NULL; ajint ibegin = 0; ajint iend = 0; ajlong i = 0; AjPDasCoordinate coord = NULL; AjIList coordsi = NULL; AjPStr dbname = NULL; AjPStr comment = NULL; AjPStr type = NULL; AjPStr format = NULL; AjPStr url = NULL; AjPStr example = NULL; AjPStr taxon = NULL; AjPStr fields = NULL; if(!source->sequence && !source->features) return ajFalse; dbname = ajDasSourceGetDBname(source, titleAndURI); comment = ajStrNewS(source->description); ajStrExchangeKK(&comment, '"', '\''); ajStrRemoveWhiteExcess(&comment); coordsi = ajListIterNew(source->coordinates); type = ajStrNew(); format = ajStrNew(); url = ajStrNew(); taxon = ajStrNew(); while(!ajListIterDone(coordsi)) { coord = ajListIterGet(coordsi); example = ajDasTestrangeParse(coord->test_range, &entry, &ibegin, &iend); if (source->sequence) { if(ajStrFindCaseC(coord->source, "nucleotide")!=-1 || ajStrFindCaseC(coord->source, "chromosome")!=-1 || ajStrFindCaseC(coord->source, "contig")!=-1 || ajStrFindCaseC(coord->source, "scaffold")!=-1) ajStrAssignC(&type, "Nucleotide"); else if(ajStrFindCaseC(coord->source, "Protein")!=-1) ajStrAssignC(&type,"Protein"); else ajStrAssignC(&type,"Sequence"); ajStrAssignC(&format, "das"); ajStrAssignS(&url, source->sequence_query_uri); i = ajStrFindlastC(url,"/sequence"); if(i != -1) ajStrCutEnd(&url,ajStrGetLen(url) - (size_t) i); } if (source->features) { if(source->sequence) { ajStrAppendC(&type,", "); ajStrAppendC(&format,", "); } if(ajStrFindCaseC(coord->source, "nucleotide")!=-1 || ajStrFindCaseC(coord->source, "chromosome")!=-1 || ajStrFindCaseC(coord->source, "contig")!=-1 || ajStrFindCaseC(coord->source, "scaffold")!=-1) ajStrAppendC(&type, "Nucfeatures"); else if(ajStrFindCaseC(coord->source, "Protein")!=-1) ajStrAppendC(&type, "Protfeatures"); else ajStrAppendC(&type,"Features"); ajStrAppendC(&format, "dasgff"); if(ajStrGetLen(url) == 0) { ajStrAssignS(&url, source->features_query_uri); i = ajStrFindlastC(url,"/features"); if(i != -1) ajStrCutEnd(&url,ajStrGetLen(url)- (size_t) i); } fields = ajStrNewC("segment,type,category,categorize,feature_id"); } else fields = ajStrNewC("segment"); if (coord->taxid) ajFmtPrintS(&taxon, " taxon: \"%S\"\n", coord->taxid); ajFmtPrintF(cachef, "DB %S [\n" " method: das\n" " type: \"%S\"\n" "%S" " format: \"%S\"\n" " url: %S\n" " example: \"%S\"\n" " comment: \"%S\"\n" " hasaccession: \"N\"\n" " identifier: \"segment\"\n" " fields: \"%S\"\n" "]\n\n", dbname, type, taxon, format, url, example, comment, fields); break; /* TODO: multiple coordinates */ } ajStrDel(&dbname); ajStrDel(&comment); ajStrDel(&type); ajStrDel(&format); ajStrDel(&url); ajStrDel(&url); ajStrDel(&example); ajStrDel(&taxon); ajStrDel(&fields); ajStrDel(&entry); ajListIterDel(&coordsi); return ajTrue; } ID ajDasTestrangeParse TY public MO ajdas LB ajaxdb XX DE Parses a DAS test_range attribute and returns sequence identifier, and DE 'begin' and 'end' positions if any. XX PN [1] PA r testrange const AjPStr PD DAS test_range string PX PN [2] PA w id AjPStr* PD sequence identifier PX PN [3] PA w ibegin ajint* PD sequence begin position PX PN [4] PA w iend ajint* PD sequence end position PX RT AjPStr RD returns the example query in EMBOSS USA syntax RX // AjPStr ajDasTestrangeParse(const AjPStr testrange, AjPStr* id, ajint* ibegin, ajint* iend) { AjPStr tmp=NULL; AjPStrTok token; AjPStr ret = NULL; *ibegin = 0; *iend = 0; /* Parse the test_range string (identifier:begin,end) */ token = ajStrTokenNewC(testrange, ":,"); ret = ajStrNew(); if(!(ajStrTokenNextParseNoskip(&token, id))) { ajStrTokenDel(&token); return ret; } if(ajStrTokenNextParseNoskip(&token, &tmp)) { ajStrToInt(tmp, ibegin); if(ajStrTokenNextParseNoskip(&token, &tmp)) { ajStrToInt(tmp, iend); ajFmtPrintS(&ret,"%S[%d:%d]", *id, *ibegin, *iend); } } if(*iend == 0) ajStrAssignRef(&ret, *id); ajStrTokenDel(&token); ajStrDel(&tmp); return ret; } ID ajDmxScophitNew TY public MO ajdmx LB core XX DE Constructor for an AJAX SCOP Hit object. XX RT AjPScophit RD AJAX SCOP Hit object RX // AjPScophit ajDmxScophitNew(void) { AjPScophit scophit = NULL; AJNEW0(scophit); scophit->Class = ajStrNew(); scophit->Architecture = ajStrNew(); scophit->Topology = ajStrNew(); scophit->Fold = ajStrNew(); scophit->Superfamily = ajStrNew(); scophit->Family = ajStrNew(); scophit->Seq = ajStrNew(); scophit->Acc = ajStrNew(); scophit->Spr = ajStrNew(); scophit->Dom = ajStrNew(); scophit->Typeobj = ajStrNew(); scophit->Typesbj = ajStrNew(); scophit->Model = ajStrNew(); scophit->Alg = ajStrNew(); scophit->Group = ajStrNew(); scophit->Start = 0U; scophit->End = 0U; scophit->Rank = 0U; scophit->Score = 0.0F; scophit->Sunid_Family = 0U; scophit->Eval = 0.0F; scophit->Pval = 0.0F; scophit->Target = ajFalse; scophit->Target2 = ajFalse; scophit->Priority = ajFalse; return scophit; } ID ajDmxScopalgNew TY public MO ajdmx LB core XX DE Constructor for an AJAX SCOP Alignment object. DE This is normally called by the ajDmxScopalgRead function. DE Fore-knowledge of the number of sequences is required. XX PN [1] PA r n ajuint PD Number of sequences PX RT AjPScopalg RD AJAX SCOP Alignment object RX // AjPScopalg ajDmxScopalgNew(ajuint n) { ajuint i = 0U; AjPScopalg scopalg = NULL; AJNEW0(scopalg); scopalg->Class = ajStrNew(); scopalg->Architecture = ajStrNew(); scopalg->Topology = ajStrNew(); scopalg->Fold = ajStrNew(); scopalg->Superfamily = ajStrNew(); scopalg->Family = ajStrNew(); scopalg->Architecture = ajStrNew(); scopalg->Topology = ajStrNew(); scopalg->Post_similar = ajStrNew(); scopalg->Positions = ajStrNew(); scopalg->Width = 0; scopalg->Number = n; if(n) { AJCNEW0(scopalg->Codes, n); for(i = 0U; i < n; i++) scopalg->Codes[i] = ajStrNew(); AJCNEW0(scopalg->Seqs, n); for(i = 0U; i < n; i++) scopalg->Seqs[i] = ajStrNew(); } return scopalg; } ID ajDmxScopalgRead TY public MO ajdmx LB core XX DE Read an AJAX SCOP Alignment object from an AJAX File. XX PN [1] PA u inf AjPFile PD Input AJAX File PX PN [2] PA w Pscopalg AjPScopalg* PD AJAX SCOP Alignment address PX RT AjBool RD True if the file contained any data, even an empty RD alignment. False if the file did not contain a 'TY' record, which is RD taken to indicate a domain alignment file. RX // AjBool ajDmxScopalgRead(AjPFile inf, AjPScopalg *Pscopalg) { AjBool ok = ajFalse; /* True if the file contained 'TY' record. */ AjBool done_1st_blk = ajFalse; /* Flag for whether we've read first block of sequences */ ajuint i = 0U; /* Loop counter */ ajuint j = 0U; /* Loop counter */ ajint cnt = 0; /* Temp. counter of sequence */ ajuint nseq = 0U; /* No. of sequences in alignment */ ajint Sunid = 0; /* SCOP Sunid for family */ ajint ntok = 0; /* * No. string tokens in sequence line from alignment. * Sequence start and end may or may not be present, * therefore ntok is either 2 or 4: * (2) (ACC SEQ ) or * (4) (ACC start SEQ end) */ AjPList list_seqs = NULL; /* List of sequences */ AjPList list_codes = NULL; /* List of codes */ AjPStr *arr_seqs = NULL; /* Array of sequences */ AjPStr seq = NULL; AjPStr code = NULL; /* Id code of sequence */ /* Check args */ if(!inf) return ajFalse; if(!Pscopalg) return ajFalse; /* Allocate strings */ /* Only initialise strings if this is called for the first time */ if(!dmxGStrline) { dmxGStrtype = ajStrNew(); dmxGStrclass = ajStrNew(); dmxGStrfold = ajStrNew(); dmxGStrsuper = ajStrNew(); dmxGStrfamily = ajStrNew(); dmxGStrarch = ajStrNew(); dmxGStrtopo = ajStrNew(); dmxGStrline = ajStrNew(); dmxGStrpostsim = ajStrNew(); dmxGStrposttmp = ajStrNew(); dmxGStrposisim = ajStrNew(); dmxGStrpositmp = ajStrNew(); dmxGStrcodetmp = ajStrNew(); dmxGStrseq1 = ajStrNew(); } /* Create new lists */ list_seqs = ajListstrNew(); list_codes = ajListstrNew(); /* Read the rest of the file */ while(ajReadlineTrim(inf, &dmxGStrline)) { if(ajStrPrefixC(dmxGStrline, "# TY")) { ok = ajTrue; ajStrAssignC(&dmxGStrtype, ajStrGetPtr(dmxGStrline) + 5); ajStrRemoveWhiteExcess(&dmxGStrtype); } else if(ajStrPrefixC(dmxGStrline, "# SI")) { ajFmtScanS(dmxGStrline, "%*s %*s %d", &Sunid); } else if(ajStrPrefixC(dmxGStrline, "# CL")) { ajStrAssignC(&dmxGStrclass, ajStrGetPtr(dmxGStrline) + 5); ajStrRemoveWhiteExcess(&dmxGStrclass); } else if(ajStrPrefixC(dmxGStrline, "# FO")) { ajStrAssignC(&dmxGStrfold, ajStrGetPtr(dmxGStrline) + 5); while((ajReadlineTrim(inf, &dmxGStrline))) { if(ajStrPrefixC(dmxGStrline, "# XX")) break; ajStrAppendC(&dmxGStrfold, ajStrGetPtr(dmxGStrline) + 5); } ajStrRemoveWhiteExcess(&dmxGStrfold); } else if(ajStrPrefixC(dmxGStrline, "# SF")) { ajStrAssignC(&dmxGStrsuper, ajStrGetPtr(dmxGStrline) + 5); while((ajReadlineTrim(inf, &dmxGStrline))) { if(ajStrPrefixC(dmxGStrline, "# XX")) break; ajStrAppendC(&dmxGStrsuper, ajStrGetPtr(dmxGStrline) + 5); } ajStrRemoveWhiteExcess(&dmxGStrsuper); } else if(ajStrPrefixC(dmxGStrline, "# FA")) { ajStrAssignC(&dmxGStrfamily, ajStrGetPtr(dmxGStrline) + 5); while((ajReadlineTrim(inf, &dmxGStrline))) { if(ajStrPrefixC(dmxGStrline, "# XX")) break; ajStrAppendC(&dmxGStrfamily, ajStrGetPtr(dmxGStrline) + 5); } ajStrRemoveWhiteExcess(&dmxGStrfamily); } else if(ajStrPrefixC(dmxGStrline, "# AR")) { ajStrAssignC(&dmxGStrarch, ajStrGetPtr(dmxGStrline) + 5); while((ajReadlineTrim(inf, &dmxGStrline))) { if(ajStrPrefixC(dmxGStrline, "# XX")) break; ajStrAppendC(&dmxGStrarch, ajStrGetPtr(dmxGStrline) + 5); } ajStrRemoveWhiteExcess(&dmxGStrarch); } else if(ajStrPrefixC(dmxGStrline, "# TP")) { ajStrAssignC(&dmxGStrtopo, ajStrGetPtr(dmxGStrline) + 5); while((ajReadlineTrim(inf, &dmxGStrline))) { if(ajStrPrefixC(dmxGStrline, "# XX")) break; ajStrAppendC(&dmxGStrtopo, ajStrGetPtr(dmxGStrline) + 5); } ajStrRemoveWhiteExcess(&dmxGStrtopo); } else if(ajStrPrefixC(dmxGStrline, "# XX")) continue; else if (ajStrPrefixC(dmxGStrline, "# Post_similar")) { /* Parse post_similar line */ ajFmtScanS(dmxGStrline, "%*s %*s %S", &dmxGStrposttmp); if(done_1st_blk == ajTrue) ajStrAppendS(&dmxGStrpostsim, dmxGStrposttmp); else ajStrAssignS(&dmxGStrpostsim, dmxGStrposttmp); continue; } else if (ajStrPrefixC(dmxGStrline, "# Positions")) { /* Parse Positions line */ ajFmtScanS(dmxGStrline, "%*s %*s %S", &dmxGStrpositmp); if(done_1st_blk == ajTrue) ajStrAppendS(&dmxGStrposisim, dmxGStrpositmp); else ajStrAssignS(&dmxGStrposisim, dmxGStrpositmp); continue; } /* Ignore any other line beginning with '#' which are taken to be comments, e.g. 'Number' lines */ else if((ajStrPrefixC(dmxGStrline ,"#"))) continue; /* ajFileReadLine will have trimmed the tailing \n */ else if(ajStrGetCharPos(dmxGStrline, 1) == '\0') { /* ** The first blank line therefore the first block of sequences ** has been done */ if(!ok) { ajWarn("ajDmxScopalgRead but file was not identified as " "being a domain alignment file"); return ajFalse; } done_1st_blk = ajTrue; j++; if(j == 1) ajListstrToarray(list_seqs, &arr_seqs); cnt = 0; continue; } else { /* Line of sequence */ if(!ok) { ajWarn("ajDmxScopalgRead but file was not identified as " "being a domain alignment file"); return ajFalse; } /* Parse a line of sequence */ if(done_1st_blk == ajTrue) { /* already read in the first block of sequences */ if(ntok == 4) ajFmtScanS(dmxGStrline, "%*s %*s %S", &dmxGStrseq1); else if(ntok == 2) ajFmtScanS(dmxGStrline, "%*s %S", &dmxGStrseq1); else ajFatal("ajDmxScopalgRead could not parse alignment"); ajStrAppendS(&arr_seqs[cnt], dmxGStrseq1); cnt++; continue; } else { /* It is a sequence line from the first block */ nseq++; seq = ajStrNew(); code = ajStrNew(); if(((ntok = ajStrParseCountC(dmxGStrline, " ")) == 4)) ajFmtScanS(dmxGStrline, "%S %*s %S", &code, &seq); else if(ntok == 2) ajFmtScanS(dmxGStrline, "%S %S", &code, &seq); else ajFatal("ajDmxScopalgRead could not parse alignment"); /* Push strings onto lists */ ajListstrPushAppend(list_seqs, seq); ajListstrPushAppend(list_codes, code); continue; } } } if(!ok) { ajWarn("ajDmxScopalgRead but file was not identified as being " "a domain alignment file"); return ajFalse; } /* ** Cope for cases where alignment is in one block only, ** i.e. there were no empty lines: ** ** XX ** # Number 10 20 30 40 50 ** d1bsna1 1 QDLDEARAMEAKRKAEEHISSSHGDVDYAQASAELAKAIAQLRVIELTKK 50 ** d1e79h1 1 DMLDLGAAKANLEKAQSELLGAADEATRAEIQIRIEANEALVKAL----- 43 ** # Post_similar 111111111111111111111111111111111111111111111----- */ if(!done_1st_blk && nseq) ajListstrToarray(list_seqs, &arr_seqs); ajStrDel(&dmxGStrseq1); if(!nseq) ajWarn("No sequences in alignment !\n"); /* Allocate memory for Scopalg structure */ *Pscopalg = ajDmxScopalgNew(nseq); /* Assign domain records */ if(ajStrMatchC(dmxGStrtype, "SCOP")) (*Pscopalg)->Type = ajEDomainTypeSCOP; else if(ajStrMatchC(dmxGStrtype, "CATH")) (*Pscopalg)->Type = ajEDomainTypeCATH; ajStrAssignS(&(*Pscopalg)->Class, dmxGStrclass); ajStrAssignS(&(*Pscopalg)->Architecture, dmxGStrarch); ajStrAssignS(&(*Pscopalg)->Topology, dmxGStrtopo); ajStrAssignS(&(*Pscopalg)->Fold, dmxGStrfold); ajStrAssignS(&(*Pscopalg)->Superfamily, dmxGStrsuper); ajStrAssignS(&(*Pscopalg)->Family, dmxGStrfamily); (*Pscopalg)->Sunid_Family = Sunid; if(nseq) { /* Assign sequences and free memory */ for(i = 0U; i < nseq; i++) { ajStrAssignS(&(*Pscopalg)->Seqs[i], arr_seqs[i]); ajStrDel(&arr_seqs[i]); } AJFREE(arr_seqs); /* Assign width */ (*Pscopalg)->Width = ajStrGetLen((*Pscopalg)->Seqs[0]); for(i = 0U; ajListstrPop(list_codes, &dmxGStrcodetmp); i++) { ajStrAssignS(&(*Pscopalg)->Codes[i], dmxGStrcodetmp); ajStrDel(&dmxGStrcodetmp); } /* Assign Post_similar line */ ajStrAssignS(&(*Pscopalg)->Post_similar, dmxGStrpostsim); /* Assign Positions line */ ajStrAssignS(&(*Pscopalg)->Positions, dmxGStrposisim); } else ajWarn("ajDmxScopalgRead called but no sequences found."); ajListstrFree(&list_seqs); ajListstrFree(&list_codes); return ajTrue; } ID ajDmxScophitDel TY public MO ajdmx LB core XX DE Destructor for an AJAX SCOP Hit object. XX PN [1] PA w Pscophit AjPScophit* PD AJAX Scophit address PX RT void RD RX // void ajDmxScophitDel(AjPScophit *Pscophit) { AjPScophit scophit = NULL; if(!Pscophit || !*Pscophit) return; scophit = *Pscophit; ajStrDel(&scophit->Class); ajStrDel(&scophit->Architecture); ajStrDel(&scophit->Topology); ajStrDel(&scophit->Fold); ajStrDel(&scophit->Superfamily); ajStrDel(&scophit->Family); ajStrDel(&scophit->Seq); ajStrDel(&scophit->Acc); ajStrDel(&scophit->Spr); ajStrDel(&scophit->Dom); ajStrDel(&scophit->Typeobj); ajStrDel(&scophit->Typesbj); ajStrDel(&scophit->Model); ajStrDel(&scophit->Alg); ajStrDel(&scophit->Group); AJFREE(scophit); *Pscophit = NULL; return; } ID ajDmxScophitDelWrap TY public MO ajdmx LB core XX DE Wrapper to destructor for an AJAX SCOP Hit object for use with generic DE functions. XX PN [1] PA d Pitem void** PD AJAX SCOP Hit address PX RT void RD RX // void ajDmxScophitDelWrap(void **Pitem) { ajDmxScophitDel((AjPScophit *) Pitem); return; } ID ajDmxScopalgDel TY public MO ajdmx LB core XX DE Destructor for an AJAX SCOP Alignment object. XX PN [1] PA d Pscopalg AjPScopalg* PD AJAX SCOP Alignment address PX RT void RD RX // void ajDmxScopalgDel(AjPScopalg *Pscopalg) { ajuint i = 0U; AjPScopalg scopalg = NULL; if(!Pscopalg || !*Pscopalg) return; scopalg = *Pscopalg; ajStrDel(&scopalg->Class); ajStrDel(&scopalg->Architecture); ajStrDel(&scopalg->Topology); ajStrDel(&scopalg->Fold); ajStrDel(&scopalg->Superfamily); ajStrDel(&scopalg->Family); ajStrDel(&scopalg->Architecture); ajStrDel(&scopalg->Topology); ajStrDel(&scopalg->Post_similar); ajStrDel(&scopalg->Positions); for(i = 0U; i < scopalg->Number; i++) { ajStrDel(&scopalg->Codes[i]); ajStrDel(&scopalg->Seqs[i]); } AJFREE(scopalg->Codes); AJFREE(scopalg->Seqs); AJFREE(scopalg); *Pscopalg = NULL; return; } ID ajDmxScophitListCopy TY public MO ajdmx LB core XX DE Read an AJAX List of AJAX SCOP Hit objects and return a pointer to a DE duplicate of the AJAX List. XX PN [1] PA r scophits const AjPList PD AJAX List of AJAX SCOP Hit objects PX RT AjPList RD True on success (list was duplicated ok) RX // AjPList ajDmxScophitListCopy(const AjPList scophits) { AjPList list = NULL; AjIList iter = NULL; AjPScophit oldscophit = NULL; AjPScophit newscophit = NULL; /* Check arg's */ if(!scophits) { ajWarn("Bad arg's passed to ajDmxScophitListCopy\n"); return NULL; } /* Allocate the new list */ list = ajListNew(); /* Initialise the iterator */ iter = ajListIterNewread(scophits); /* Iterate through the list of Scophit objects */ while((oldscophit = (AjPScophit)ajListIterGet(iter))) { newscophit = ajDmxScophitNew(); ajDmxScophitCopy(&newscophit, oldscophit); /* Push scophit onto list */ ajListPushAppend(list, newscophit); } ajListIterDel(&iter); return list; } ID ajDmxScophitCopy TY public MO ajdmx LB core XX DE Copies the contents from one AJAX SCOP Hit object to another. XX PN [1] PA w Pto AjPScophit* PD AJAX SCOP Hit address PX PN [2] PA r from const AjPScophit PD AJAX SCOP Hit PX RT AjBool RD True if copy was successful. RX // AjBool ajDmxScophitCopy(AjPScophit *Pto, const AjPScophit from) { if(!Pto || !*Pto) return ajFalse; if(!from) return ajFalse; (*Pto)->Type = from->Type; ajStrAssignS(&(*Pto)->Class, from->Class); ajStrAssignS(&(*Pto)->Architecture, from->Architecture); ajStrAssignS(&(*Pto)->Topology, from->Topology); ajStrAssignS(&(*Pto)->Fold, from->Fold); ajStrAssignS(&(*Pto)->Superfamily, from->Superfamily); ajStrAssignS(&(*Pto)->Family, from->Family); ajStrAssignS(&(*Pto)->Seq, from->Seq); ajStrAssignS(&(*Pto)->Acc, from->Acc); ajStrAssignS(&(*Pto)->Spr, from->Spr); ajStrAssignS(&(*Pto)->Dom, from->Dom); ajStrAssignS(&(*Pto)->Typeobj, from->Typeobj); ajStrAssignS(&(*Pto)->Typesbj, from->Typesbj); ajStrAssignS(&(*Pto)->Model, from->Model); ajStrAssignS(&(*Pto)->Alg, from->Alg); ajStrAssignS(&(*Pto)->Group, from->Group); (*Pto)->Start = from->Start; (*Pto)->End = from->End; (*Pto)->Rank = from->Rank; (*Pto)->Score = from->Score; (*Pto)->Eval = from->Eval; (*Pto)->Pval = from->Pval; (*Pto)->Target = from->Target; (*Pto)->Target2 = from->Target2; (*Pto)->Priority = from->Priority; (*Pto)->Sunid_Family = from->Sunid_Family; return ajTrue; } ID ajDmxScophitTargetLowPriority TY public MO ajdmx LB core XX DE Sets the Target element of an AJAX SCOP Hit object to ajTrue if its DE Priority is low. XX PN [1] PA u Pscophit AjPScophit* PD AJAX SCOP Hit address PX RT AjBool RD True on success. False otherwise. RX // AjBool ajDmxScophitTargetLowPriority(AjPScophit *Pscophit) { if(!Pscophit || !*Pscophit) { ajWarn("Bad arg's passed to ajDmxScophitTargetLowPriority\n"); return ajFalse; } if((*Pscophit)->Priority == ajFalse) (*Pscophit)->Target = ajTrue; return ajTrue; } ID ajDmxScophitTarget2 TY public MO ajdmx LB core XX DE Sets the Target2 element of an AJAX SCOP Hit object to ajTrue. XX PN [1] PA u Pscophit AjPScophit* PD AJAX SCOP Hit address PX RT AjBool RD True on success. False otherwise. RX // AjBool ajDmxScophitTarget2(AjPScophit *Pscophit) { if(!Pscophit || !*Pscophit) { ajWarn("Bad arg's passed to ajDmxScophitTarget2\n"); return ajFalse; } (*Pscophit)->Target2 = ajTrue; return ajTrue; } ID ajDmxScophitTarget TY public MO ajdmx LB core XX DE Sets the Target element of an AJAX SCOP Hit object to ajTrue. XX PN [1] PA u Pscophit AjPScophit* PD AJAX SCOP Hit address PX RT AjBool RD True on success. False otherwise. RX // AjBool ajDmxScophitTarget(AjPScophit *Pscophit) { if(!Pscophit || !*Pscophit) { ajWarn("Bad arg's passed to ajDmxScophitTarget\n"); return ajFalse; } (*Pscophit)->Target = ajTrue; return ajTrue; } ID ajDmxScophitCheckTarget TY public MO ajdmx LB core XX DE Checks to see if the Target element of an AJAX SCOP Hit object equals DE ajTrue. XX PN [1] PA r scophit const AjPScophit PD AJAX SCOP Hit PX RT AjBool RD Returns ajTrue if the Target element of the AAX SCOP Hit RD object equals ajTrue, returns ajFalse otherwise. RX // AjBool ajDmxScophitCheckTarget(const AjPScophit scophit) { return scophit->Target; } ID ajDmxScophitCompScore TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by score member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD 1 if score1 < score2, 0 if score1 == score2, else -1. RX // ajint ajDmxScophitCompScore(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; if(scophit1->Score < scophit2->Score) return -1; else if(E_FPEQ(scophit1->Score, scophit2->Score, U_FEPS)) return 0; return 1; } ID ajDmxScophitCompPval TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by Pval member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD 1 if Pval1 > Pval2, 0 if Pval1 == Pval2, else -1. RX // ajint ajDmxScophitCompPval(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; if(scophit1->Pval < scophit2->Pval) return -1; else if(E_FPEQ(scophit1->Pval, scophit2->Pval, U_FEPS)) return 0; return 1; } ID ajDmxScophitCompAcc TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by Acc member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD -1 if Acc1 should sort before Acc2, RD +1 if the Acc2 should sort first. RD 0 if they are identical in length and content. RX // ajint ajDmxScophitCompAcc(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; dmxTraceScophit(scophit1, "CompAcc scophit1"); dmxTraceScophit(scophit2, "CompAcc scophit2"); return ajStrCmpS(scophit1->Acc, scophit2->Acc); } ID ajDmxScophitCompSunid TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by Sunid_Family member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD -1 if Sunid_Family1 < Sunid_Family2, +1 if the RD Sunid_Family2 should sort first. 0 if they are identical. RX // ajint ajDmxScophitCompSunid(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; if(scophit1->Sunid_Family < scophit2->Sunid_Family) return -1; else if(scophit1->Sunid_Family == scophit2->Sunid_Family) return 0; return 1; } ID ajDmxScophitCompSpr TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by Spr member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD -1 if Spr1 should sort before Spr2, RD +1 if the Spr2 should sort first. RD 0 if they are identical in length and content. RX // ajint ajDmxScophitCompSpr(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; return ajStrCmpS(scophit1->Spr, scophit2->Spr); } ID ajDmxScophitCompEnd TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by End member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD -1 if End1 should sort before End2, +1 if the End2 RD should sort first. 0 if they are identical. RX // ajint ajDmxScophitCompEnd(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; dmxTraceScophit(scophit1, "CompEnd scophit1"); dmxTraceScophit(scophit2, "CompEnd scophit2"); if(scophit1->End < scophit2->End) return -1; else if(scophit1->End == scophit2->End) return 0; return 1; } ID ajDmxScophitCompStart TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by Start member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD -1 if Start1 should sort before Start2, +1 if the Start2 RD should sort first. 0 if they are identical. RX // ajint ajDmxScophitCompStart(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; dmxTraceScophit(scophit1, "CompStart scophit1"); dmxTraceScophit(scophit2, "CompStart scophit2"); if(scophit1->Start < scophit2->Start) return -1; else if(scophit1->Start == scophit2->Start) return 0; return 1; } ID ajDmxScophitCompFam TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by Family member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD -1 if Family1 should sort before Family2, +1 if the RD Family2 should sort first. 0 if they are identical. RX // ajint ajDmxScophitCompFam(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; return ajStrCmpS(scophit1->Family, scophit2->Family); } ID ajDmxScophitCompSfam TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by Superfamily member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD -1 if Superfamily1 should sort before Superfamily2, +1 if RD the Superfamily2 should sort first. 0 if they are identical. RX // ajint ajDmxScophitCompSfam(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; return ajStrCmpS(scophit1->Superfamily, scophit2->Superfamily); } ID ajDmxScophitCompClass TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by Class member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD -1 if Class1 should sort before Class2, +1 if the Class2 RD should sort first. 0 if they are identical. RX // ajint ajDmxScophitCompClass(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; return ajStrCmpS(scophit1->Class, scophit2->Class); } ID ajDmxScophitCompFold TY public MO ajdmx LB core XX DE Function to compare AJAX SCOP Hit objects by Fold member. DE Usually called by ajListSort. XX PN [1] PA r item1 const void* PD AJAX SCOP Hit address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP Hit address 2 PX RT ajint RD -1 if Fold1 should sort before Fold2, +1 if the Fold2 RD should sort first. 0 if they are identical. RX // ajint ajDmxScophitCompFold(const void *item1, const void *item2) { AjPScophit scophit1 = *(AjOScophit *const *) item1; AjPScophit scophit2 = *(AjOScophit *const *) item2; return ajStrCmpS(scophit1->Fold, scophit2->Fold); } ID ajDmxScopalgGetseqs TY public MO ajdmx LB core XX DE Read an AJAX SCOP Alignment object and write an array of AJAX Strong objects DE containing the sequences without gaps. XX PN [1] PA r scopalg const AjPScopalg PD AJAX SCOP Alignment PX PN [2] PA w array AjPStr** PD Array of AJAX String objects PX RT ajuint RD Number of sequences read RX // ajuint ajDmxScopalgGetseqs(const AjPScopalg scopalg, AjPStr **array) { ajuint i = 0U; if(!scopalg || !array) { ajWarn("Null args passed to ajDmxScopalgGetseqs"); return 0; } *array = (AjPStr *) AJCALLOC0(scopalg->Number, sizeof (AjPStr)); for(i = 0U; i < scopalg->Number; ++i) { (*array)[i] = ajStrNew(); ajStrAssignS(&((*array)[i]), scopalg->Seqs[i]); ajStrRemoveGap(&((*array)[i])); } return scopalg->Number; } ID ajDmxScophitsWrite TY public MO ajdmx LB core XX DE Write contents of an AJAX List of AJAX SCOP Hit objects to an AJAX File in DE EMBL-like format. DE Text for Class, Architecture, Topology, Fold, Superfamily and Family DE is only written if the text is available. XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r scophits const AjPList PD AJAX List of AJAX SCOP Hit objects PX RT AjBool RD True on success RX // AjBool ajDmxScophitsWrite(AjPFile outf, const AjPList scophits) { AjIList iter = NULL; AjPScophit scophit = NULL; AjPSeqout outseq = NULL; iter = ajListIterNewread(scophits); while((scophit = (AjPScophit) ajListIterGet(iter))) { if(!scophit) return ajFalse; if(scophit->Type == ajEDomainTypeSCOP) ajFmtPrintF(outf, "TY SCOP\nXX\n"); else if(scophit->Type == ajEDomainTypeCATH) ajFmtPrintF(outf, "TY CATH\nXX\n"); if(MAJSTRGETLEN(scophit->Dom)) { ajFmtPrintF(outf, "%-5s%S\n", "DO", scophit->Dom); ajFmtPrintF(outf, "XX\n"); } if(MAJSTRGETLEN(scophit->Class)) { ajFmtPrintF(outf, "CL %S\n", scophit->Class); ajFmtPrintF(outf, "XX\n"); } if(MAJSTRGETLEN(scophit->Architecture)) { ajFmtPrintF(outf, "AR %S\n", scophit->Architecture); ajFmtPrintF(outf, "XX\n"); } if(MAJSTRGETLEN(scophit->Topology)) { ajFmtPrintF(outf, "TP %S\n", scophit->Topology); ajFmtPrintF(outf, "XX\n"); } if(MAJSTRGETLEN(scophit->Fold)) { ajFmtPrintSplit(outf, scophit->Fold, "FO ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); #if AJFALSE ajFmtPrintSplit(outf, scophit->Fold, "XX\nFO ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); #endif /* AJFALSE */ } if(MAJSTRGETLEN(scophit->Superfamily)) { ajFmtPrintSplit(outf, scophit->Superfamily, "SF ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); #if AJFALSE ajFmtPrintSplit(outf, scophit->Superfamily, "XX\nSF ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); #endif /* AJFALSE */ } if(MAJSTRGETLEN(scophit->Family)) { ajFmtPrintSplit(outf, scophit->Family,"FA ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); #if AJFALSE ajFmtPrintSplit(outf, scophit->Family, "XX\nFA ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); #endif } if(MAJSTRGETLEN(scophit->Family)) { ajFmtPrintF(outf, "XX\nSI %d\n", scophit->Sunid_Family); ajFmtPrintF(outf, "XX\n"); } #if AJFALSE if(MAJSTRGETLEN(scophit->Typeobj)) ajFmtPrintF(outf, "%-5s%S\n", "TY", scophit->Typeobj); #endif /* AJFALSE */ ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5s%.5f\n", "SC", scophit->Score); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5s%.3e\n", "PV", scophit->Pval); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5s%.3e\n", "EV", scophit->Eval); ajFmtPrintF(outf, "XX\n"); if(MAJSTRGETLEN(scophit->Group)) { ajFmtPrintF(outf, "%-5s%S\n", "GP", scophit->Group); ajFmtPrintF(outf, "XX\n"); } ajFmtPrintF(outf, "%-5s%S\n", "AC", scophit->Acc); ajFmtPrintF(outf, "XX\n"); if(MAJSTRGETLEN(scophit->Spr)) { ajFmtPrintF(outf, "%-5s%S\n", "SP", scophit->Spr); ajFmtPrintF(outf, "XX\n"); } ajFmtPrintF(outf, "%-5s%d START; %d END;\n", "RA", scophit->Start, scophit->End); ajFmtPrintF(outf, "XX\n"); outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, scophit->Seq, "SQ"); ajSeqoutDel(&outseq); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "//\n"); } ajListIterDel(&iter); return ajTrue; } ID ajDmxScophitsWriteFasta TY public MO ajdmx LB core XX DE Write contents of an AJAX List of AJAX SCOP Hit objects to an AJAX File DE in DHF format. DE Text for Class, Archhitecture, Topology, Fold, Superfamily and Family DE is only written if the text is available. XX PN [1] PA w outf AjPFile PD AJAX Output File stream PX PN [2] PA r scophits const AjPList PD AJAX List of AJAX SCOP Hit objects PX RT AjBool RD True on success RX // AjBool ajDmxScophitsWriteFasta(AjPFile outf, const AjPList scophits) { AjIList iter = NULL; AjPScophit scophit = NULL; iter = ajListIterNewread(scophits); while((scophit = (AjPScophit) ajListIterGet(iter))) { if(!scophit) return ajFalse; ajFmtPrintF(outf, "> "); if(MAJSTRGETLEN(scophit->Acc)) ajFmtPrintF(outf, "%S^", scophit->Acc); else ajFmtPrintF(outf, ".^"); if(MAJSTRGETLEN(scophit->Spr)) ajFmtPrintF(outf, "%S^", scophit->Spr); else ajFmtPrintF(outf, ".^"); ajFmtPrintF(outf, "%d^%d^", scophit->Start, scophit->End); if((scophit->Type == ajEDomainTypeSCOP)) ajFmtPrintF(outf, "SCOP^"); else if ((scophit->Type == ajEDomainTypeCATH)) ajFmtPrintF(outf, "CATH^"); else ajFmtPrintF(outf, ".^"); if(MAJSTRGETLEN(scophit->Dom)) ajFmtPrintF(outf, "%S^", scophit->Dom); else ajFmtPrintF(outf, ".^"); ajFmtPrintF(outf,"%d^", scophit->Sunid_Family); if(MAJSTRGETLEN(scophit->Class)) ajFmtPrintF(outf,"%S^",scophit->Class); else ajFmtPrintF(outf, ".^"); if(MAJSTRGETLEN(scophit->Architecture)) ajFmtPrintF(outf,"%S^",scophit->Architecture); else ajFmtPrintF(outf, ".^"); if(MAJSTRGETLEN(scophit->Topology)) ajFmtPrintF(outf,"%S^",scophit->Topology); else ajFmtPrintF(outf, ".^"); if(MAJSTRGETLEN(scophit->Fold)) ajFmtPrintF(outf,"%S^",scophit->Fold); else ajFmtPrintF(outf, ".^"); if(MAJSTRGETLEN(scophit->Superfamily)) ajFmtPrintF(outf,"%S^",scophit->Superfamily); else ajFmtPrintF(outf, ".^"); if(MAJSTRGETLEN(scophit->Family)) ajFmtPrintF(outf,"%S^",scophit->Family); else ajFmtPrintF(outf, ".^"); if(MAJSTRGETLEN(scophit->Model)) ajFmtPrintF(outf, "%S^", scophit->Model); else ajFmtPrintF(outf, ".^"); ajFmtPrintF(outf, "%.2f^", scophit->Score); ajFmtPrintF(outf, "%.3e^", scophit->Pval); ajFmtPrintF(outf, "%.3e", scophit->Eval); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "%S\n", scophit->Seq); } ajListIterDel(&iter); return ajTrue; } ID ajDmxScophitReadFasta TY public MO ajdmx LB core XX DE Read an AJAX SCOP Hit object from a file in extended FASTA format DE (see documentation for the DOMAINATRIX "seqsearch" application). XX PN [1] PA u inf AjPFile PD AJAX Input file stream PX RT AjPScophit RD AJAX SCOP Hit object, or NULL if the file was not in RD extended FASTA (DHF) format (indicated by a token count of the the lines RD beginning with '>'). RX // AjPScophit ajDmxScophitReadFasta(AjPFile inf) { AjPScophit scophit = NULL; /* Current hit */ AjBool donefirst = ajFalse; /* First '>' line has been read */ ajint ntok = 0; /* No. tokens in a line */ const AjPStr token = NULL; AjPStr line = NULL; /* Line of text */ AjPStr subline = NULL; AjPStr type = NULL; /* Allocate strings */ line = ajStrNew(); subline = ajStrNew(); type = ajStrNew(); while((ajReadlineTrim(inf, &line))) { if(ajStrPrefixC(line, ">")) { /* Process the last hit */ if(donefirst) { ajStrRemoveWhite(&scophit->Seq); ajStrDel(&line); ajStrDel(&subline); ajStrDel(&type); return scophit; } else scophit = ajDmxScophitNew(); /* Check line has correct no. of tokens and allocate Hit */ ajStrAssignSubS(&subline, line, 1, -1); if( (ntok=ajStrParseCountC(subline, "^")) != 17) { ajWarn("Wrong no. (%d) of tokens for a DHF file on line %S\n", ntok, line); ajStrDel(&line); ajStrDel(&subline); ajDmxScophitDel(&scophit); ajStrDel(&type); return NULL; } /* Acc */ token = ajStrParseC(subline, "^"); ajStrAssignS(&scophit->Acc, token); ajStrTrimWhite(&scophit->Acc); if(ajStrMatchC(scophit->Acc, ".")) ajStrSetClear(&scophit->Acc); /* Spr */ token = ajStrParseC(NULL, "^"); ajStrAssignS(&scophit->Spr, token); if(ajStrMatchC(scophit->Spr, ".")) ajStrSetClear(&scophit->Spr); /* Start */ token = ajStrParseC(NULL, "^"); ajFmtScanS(token, "%d", &scophit->Start); /* End */ token = ajStrParseC(NULL, "^"); ajFmtScanS(token, "%d", &scophit->End); /* Type */ token = ajStrParseC(NULL, "^"); ajStrAssignS(&type, token); if(ajStrMatchC(type, "SCOP")) scophit->Type = ajEDomainTypeSCOP; else if(ajStrMatchC(type, "CATH")) scophit->Type = ajEDomainTypeCATH; /* Dom */ token = ajStrParseC(NULL, "^"); ajStrAssignS(&scophit->Dom, token); if(ajStrMatchC(scophit->Dom, ".")) ajStrSetClear(&scophit->Dom); /* Domain identifier */ token = ajStrParseC(NULL, "^"); ajFmtScanS(token, "%d", &scophit->Sunid_Family); token = ajStrParseC(NULL, "^"); ajStrAssignS(&scophit->Class, token); if(ajStrMatchC(scophit->Class, ".")) ajStrSetClear(&scophit->Class); token = ajStrParseC(NULL, "^"); ajStrAssignS(&scophit->Architecture, token); if(ajStrMatchC(scophit->Architecture, ".")) ajStrSetClear(&scophit->Architecture); token = ajStrParseC(NULL, "^"); ajStrAssignS(&scophit->Topology, token); if(ajStrMatchC(scophit->Topology, ".")) ajStrSetClear(&scophit->Topology); token = ajStrParseC(NULL, "^"); ajStrAssignS(&scophit->Fold, token); if(ajStrMatchC(scophit->Fold, ".")) ajStrSetClear(&scophit->Fold); token = ajStrParseC(NULL, "^"); ajStrAssignS(&scophit->Superfamily, token); if(ajStrMatchC(scophit->Superfamily, ".")) ajStrSetClear(&scophit->Superfamily); token = ajStrParseC(NULL, "^"); ajStrAssignS(&scophit->Family, token); if(ajStrMatchC(scophit->Family, ".")) ajStrSetClear(&scophit->Family); token = ajStrParseC(NULL, "^"); ajStrAssignS(&scophit->Model, token); if(ajStrMatchC(scophit->Model, ".")) ajStrSetClear(&scophit->Model); token = ajStrParseC(NULL, "^"); ajFmtScanS(token, "%f", &scophit->Score); token = ajStrParseC(NULL, "^"); ajFmtScanS(token, "%f", &scophit->Pval); token = ajStrParseC(NULL, "^"); ajFmtScanS(token, "%f", &scophit->Eval); donefirst = ajTrue; } else { if(scophit) ajStrAppendS(&scophit->Seq, line); } } /* EOF therefore process last hit */ if(donefirst) { ajStrRemoveWhite(&scophit->Seq); ajStrDel(&line); ajStrDel(&subline); ajStrDel(&type); return scophit; } ajStrDel(&line); ajStrDel(&subline); ajStrDel(&type); ajDmxScophitDel(&scophit); return NULL; } ID ajDmxScopalgWrite TY public MO ajdmx LB core XX DE Write an AJAX SCOP Alignment object to an AJAX FIle in EMBOSS simple DE multiple sequence format (same as that used by clustal) annotated with DE domain classification as below (records are for SCOP domains in this DE example): DE DE # TY SCOP DE # XX DE # CL Alpha and beta proteins (a+b) DE # XX DE # FO Phospholipase D/nuclease DE # XX DE # SF Phospholipase D/nuclease DE # XX DE # FA Phospholipase D DE # XX DE # SI 64391 DE # XX DE d1f0ia1 1 AATPHLDAVEQTLRQVSPGLEGDVWERTSGNKLDGSAADPSDWLLQTP-GCWGDDKC 50 DE d1f0ia2 1 -----------------------------NVPV---------IAVG-GLG---VGIK 15 DE DE d1f0ia1 51 A-------------------------------D-RVGTKRLLAKMTENIGNATRTVD 75 DE d1f0ia2 16 DVDPKSTFRPDLPTASDTKCVVGLHDNTNADRDYDTV-NPEESALRALVASAKGHIE 65 XX PN [1] PA r scopalg const AjPScopalg PD AJAX SCOP Alignment PX PN [2] PA u outf AjPFile PD AJAX Output file stream PX RT AjBool RD True on success (an alignment was written) RX // AjBool ajDmxScopalgWrite(const AjPScopalg scopalg, AjPFile outf) { ajuint i = 0U; ajuint j = 0U; ajint tmp_wid = 0; /* Temp. variable for width */ ajint code_wid = 0; /* Max. code width +1 */ ajint seq_wid = 0; /* Width of alignment rounded up to nearest 60 */ ajuint nblk = 0U; /* Number of blocks of alignment in output */ AjPStr tmp_seq = NULL; /* Temp. variable for sequence */ AjPStr nogap = NULL; /* Temp. variable for sequence w/o gaps */ ajint len_nogap = 0; /* Length of no_gap */ ajint pos = 0; /* House-keeping */ ajint start = 0; /* Start position of sequence fragment wrt full length alignment */ ajuint end = 0; /* End position of sequence fragment wrt full length alignment */ AjPInt idx = NULL; /* Index */ idx = ajIntNewRes(scopalg->Number); for(i = 0U; i < scopalg->Number; i++) ajIntPut(&idx, scopalg->Number, 1); /* Write SCOP classification records to file */ if(scopalg->Type == ajEDomainTypeSCOP) { ajFmtPrintF(outf,"# TY SCOP\n# XX\n"); ajFmtPrintF(outf,"# CL %S\n# XX\n", scopalg->Class); ajFmtPrintSplit(outf, scopalg->Fold, "# FO ", 75, " \t\n\r"); ajFmtPrintF(outf, "# XX\n"); ajFmtPrintSplit(outf, scopalg->Superfamily, "# SF ", 75, " \t\n\r"); ajFmtPrintF(outf, "# XX\n"); ajFmtPrintSplit(outf,scopalg->Family, "# FA ", 75, " \t\n\r"); ajFmtPrintF(outf, "# XX\n"); #if AJFALSE ajFmtPrintSplit(outf,scopalg->Fold, "\nXX\n# FO ", 75, " \t\n\r"); ajFmtPrintSplit(outf,scopalg->Superfamily, "# XX\n# SF ", 75, " \t\n\r"); ajFmtPrintSplit(outf,scopalg->Family, "# XX\n# FA ", 75, " \t\n\r"); ajFmtPrintF(outf,"# XX\n"); #endif /* AJFALSE */ ajFmtPrintF(outf, "# SI %d\n# XX", scopalg->Sunid_Family); } else { ajFmtPrintF(outf, "# TY CATH\n# XX\n"); ajFmtPrintF(outf, "# CL %S\n# XX\n", scopalg->Class); ajFmtPrintSplit(outf, scopalg->Architecture, "# AR ", 75, " \t\n\r"); ajFmtPrintF(outf, "# XX\n"); ajFmtPrintSplit(outf, scopalg->Topology, "# TP ", 75, " \t\n\r"); ajFmtPrintF(outf, "# XX\n"); ajFmtPrintSplit(outf, scopalg->Superfamily, "# SF ", 75, " \t\n\r"); ajFmtPrintF(outf, "# XX\n"); #if AJFALSE ajFmtPrintSplit(outf, scopalg->Architecture, "\nXX\n# AR ", 75, " \t\n\r"); ajFmtPrintSplit(outf, scopalg->Topology, "# XX\n# TP ", 75, " \t\n\r"); ajFmtPrintSplit(outf, scopalg->Superfamily, "# XX\n# SF ", 75, " \t\n\r"); ajFmtPrintF(outf, "# XX\n"); #endif /* AJFALSE */ ajFmtPrintF(outf, "# SI %d\n# XX", scopalg->Sunid_Family); } /* Find max. width of code, and add 1 to it for 1 whitespace */ for(i = 0U; i < scopalg->Number; i++) if((tmp_wid = MAJSTRGETLEN(scopalg->Codes[i])) > code_wid) code_wid = tmp_wid; code_wid++; /* Calculate no. of blocks in alignment */ seq_wid = ajRound(scopalg->Width, 50); nblk = (ajint) (seq_wid / 50); /* Print out sequence in blocks */ for(i = 0U; i < nblk; i++) { start = i * 50; end = start + 49; if(end>=scopalg->Width) end = scopalg->Width - 1; ajFmtPrintF(outf, "\n"); for(j = 0U; j < scopalg->Number; j++) { ajStrAssignSubS(&tmp_seq, scopalg->Seqs[j], start, end); ajStrAssignS(&nogap, tmp_seq); /* Remove gap characters */ ajStrRemoveSetC(&nogap, " -"); len_nogap = MAJSTRGETLEN(nogap); pos = ajIntGet(idx, j); ajFmtPrintF(outf, "%*S%7d %-50S%7d\n", code_wid, pos, scopalg->Codes[j], tmp_seq, pos + len_nogap - 1); ajIntPut(&idx, j, pos + len_nogap); } } ajIntDel(&idx); return ajTrue; } ID ajDmxScopalgWriteClustal TY public MO ajdmx LB core XX DE Write an AJAX SCOP Alignment object to an AJAX File in CLUSTAL format DE (just the alignment without the domain classification information). XX PN [1] PA r scopalg const AjPScopalg PD AJAX SCOP Alignment PX PN [2] PA u outf AjPFile PD AJAX Outfile file PX RT AjBool RD True on success (a file has been written) RX // AjBool ajDmxScopalgWriteClustal(const AjPScopalg scopalg, AjPFile outf) { ajuint i = 0U; if(!scopalg) { ajWarn("Null args passed to ajDmxScopalgWriteClustal"); return ajFalse; } /* remove i from the print statement before committing */ ajFmtPrintF(outf, "CLUSTALW\n\n"); ajFmtPrintF(outf, "\n"); for(i = 0U; i < scopalg->Number; ++i) ajFmtPrintF(outf, "%S_%d %S\n", scopalg->Codes[i], i, scopalg->Seqs[i]); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); return ajTrue; } ID ajDmxScopalgWriteClustal2 TY public MO ajdmx LB core XX DE Write an AJAX SCOP Alignment object to an AJAX File in CLUSTAL format DE (just the alignment without the domain classification information). XX PN [1] PA r scopalg const AjPScopalg PD AJAX SCOP Alignment PX PN [2] PA u outf AjPFile PD AJAX Outfile file PX RT AjBool RD True on success (a file has been written) RX // AjBool ajDmxScopalgWriteClustal2(const AjPScopalg scopalg, AjPFile outf) { ajuint i = 0U; if(!scopalg) { ajWarn("Null args passed to ajDmxScopalgWriteClustal2"); return ajFalse; } /* remove i from the print statement before committing */ ajFmtPrintF(outf, "\n"); for(i = 0; i < scopalg->Number; i++) ajFmtPrintF(outf, "%S_%d %S\n", scopalg->Codes[i], i, scopalg->Seqs[i]); ajFmtPrintF(outf,"\n"); return ajTrue; } ID ajDmxScopalgWriteFasta TY public MO ajdmx LB core XX DE Write an AJAX SCOP Alignment object to an AJAX File in FASTA format DE (just the alignment without the domain classification information). XX PN [1] PA r scopalg const AjPScopalg PD AJAX SCOP Alignment PX PN [2] PA u outf AjPFile PD AJAX Outfile file PX RT AjBool RD True on success (a file has been written) RX // AjBool ajDmxScopalgWriteFasta(const AjPScopalg scopalg, AjPFile outf) { ajuint i = 0U; if(!scopalg) { ajWarn("Null args passed to ajDmxScopalgWriteFasta"); return ajFalse; } #if AJFALSE /* remove i from the print statement before committing */ ajFmtPrintF(outf, "CLUSTALW\n\n"); ajFmtPrintF(outf, "\n"); #endif /* AJFALSE */ for(i = 0U; i < scopalg->Number; i++) ajFmtPrintF(outf, ">%S_%d\n%S\n", scopalg->Codes[i], i, scopalg->Seqs[i]); ajFmtPrintF(outf,"\n"); ajFmtPrintF(outf,"\n"); return ajTrue; } ID ajDmxScopSeqFromSunid TY public MO ajdmx LB core XX DE Write a sequence corresponding to an AJAX SCOP domain given a Sunid for the DE domain. The sequence is taken from one of an AJAX List of AJAX SCOP objects DE that is provided. The SWISS-Prot sequence is taken in priority over the PDB DE sequence. XX PN [1] PA r identifier ajint PD Search term PX PN [2] PA w seq AjPStr* PD Result sequence PX PN [3] PA r scops const AjPList PD Sorted AJAX List of AJAX SCOP objects PX RT AjBool RD True if a SWISS-Prot identifier code was found for the RD PDB code. RX // AjBool ajDmxScopSeqFromSunid(ajint identifier, AjPStr *seq, const AjPList scops) { ajuint dim = 0U; /* Size of array */ ajint idx = 0; /* Index into array for the Pdb code */ AjPScop *array = NULL; /* Array derived from list */ if(!identifier || !scops) { ajWarn("Bad args passed to ajDmxScopSeqFromSunid"); return ajFalse; } dim = (ajuint) ajListToarray(scops, (void ***) &array); if(!dim) { ajWarn("Empty list passed to ajDmxScopSeqFromSunid"); return ajFalse; } if((idx = ajScopArrFindSunid(array, dim, identifier)) == -1) { AJFREE(array); return ajFalse; } /* swissprot sequence has priority */ if((ajStrGetLen(array[idx]->SeqSpr)) == 0) ajStrAssignS(seq, array[idx]->SeqPdb); else ajStrAssignS(seq, array[idx]->SeqSpr); AJFREE(array); return ajTrue; } ID ajDmxExit TY public MO ajdmx LB core XX DE Cleanup of Dmx function internals. XX RT void RD RX // void ajDmxExit(void) { ajStrDel(&dmxGStrline); ajStrDel(&dmxGStrtype); ajStrDel(&dmxGStrclass); ajStrDel(&dmxGStrfold); ajStrDel(&dmxGStrsuper); ajStrDel(&dmxGStrfamily); ajStrDel(&dmxGStrarch); ajStrDel(&dmxGStrtopo); ajStrDel(&dmxGStrpostsim); ajStrDel(&dmxGStrposttmp); ajStrDel(&dmxGStrposisim); ajStrDel(&dmxGStrpositmp); ajStrDel(&dmxGStrseq1); ajStrDel(&dmxGStrcodetmp); return; } ID ajDmxDummyFunction TY public MO ajdmx LB core XX DE Dummy function to catch all unused functions defined in the ajdmx DE source file. XX RT void RD RX // void ajDmxDummyFunction(void) { return; } ID dmxTraceScophit TY static MO ajdmx LB core XX DE Reports internals of a SCOPhit object XX PN [1] PA r scophit const AjPScophit PD AJAX SCOP Hit object PX PN [2] PA r title const char* PD title PX RT void RD RX // static void dmxTraceScophit(const AjPScophit scophit, const char *title) { ajDebug("SCOPhit trace: %s\n", title); ajDebug("Type: %d\n", scophit->Type); ajDebug("Acc: '%S'\n", scophit->Acc); ajDebug("Start: %d\n", scophit->Start); ajDebug("End: %d\n", scophit->End); return; } ID domRemoveFromMap TY static MO ajdom LB core XX DE Remove a key/value pair from a DOM node list XX PN [1] PA w list AjPDomNodeList PD node list PX PN [2] PA r key const AjPDomNode PD key PX RT void RD RX // static void domRemoveFromMap(AjPDomNodeList list, const AjPDomNode key) { AjPDomNode trukey; if(!list->table) return; /* ** don't free the values, just remove them from the map ** if(val) */ ajTableRemoveKey(list->table, key, (void **) &trukey); return; } ID domAddToMap TY static MO ajdom LB core XX DE Add a key/value pair to a DOM node list XX PN [1] PA w list AjPDomNodeList PD node list PX PN [2] PA o key AjPDomNode PD key PX PN [3] PA u val AjPDomNodeEntry PD value PX RT void RD RX // static void domAddToMap(AjPDomNodeList list, AjPDomNode key, AjPDomNodeEntry val) { if(!list->table) list->table = ajTableNew(AJDOM_TABLE_HINT); domRemoveFromMap(list, key); ajTablePut(list->table, key, val); return; } ID ajDomNodeListGetLen TY public MO ajdom LB core XX DE Return the length of a DOM node list XX PN [1] PA r list const AjPDomNodeList PD node list PX RT ajint RD Length (-1 if error) RX // ajint ajDomNodeListGetLen(const AjPDomNodeList list) { if(!list) return -1; return list->length; } ID ajDomNodeListAppend TY public MO ajdom LB core XX DE Append a child node to a DOM node list XX PN [1] PA w list AjPDomNodeList PD node list PX PN [2] PA u child AjPDomNode PD child PX RT AjPDomNodeEntry RD Node list child entry created RX // AjPDomNodeEntry ajDomNodeListAppend(AjPDomNodeList list, AjPDomNode child) { AjPDomNodeEntry p; AjPDomDocumentType doctype; if(!list) { ajDebug("ajDomNodeListAppend: Null pointer error\n"); return NULL; } if(list->filter) { ajDebug("ajDomNodeListAppend: Filtered list error\n"); return NULL; } AJNEW0(p); domAddToMap(list, child, p); p->node = child; if(!list->first) { list->first = p; list->last = p; } else { list->last->next = p; p->prev = list->last; list->last = p; } ++list->length; if(child->ownerdocument && (doctype = child->ownerdocument->sub.Document.doctype) && list == doctype->childnodes) { if(child->type == ajEDomNodeTypeNotation) ++doctype->sub.DocumentType.notations->length; else if(child->type == ajEDomNodeTypeEntityNode) ++doctype->sub.DocumentType.entities->length; } if(child->type == ajEDomNodeTypeAttribute) child->sub.Attr.ownerelement = list->ownerelement; return p; } ID ajDomNodeAppendChild TY public MO ajdom LB core XX DE Appends an extra child at the end of the child nodes list of a node. DE If extrachild is already in the list, it is first removed. XX PN [1] PA w node AjPDomNode PD node PX PN [2] PA u extrachild AjPDomNode PD extra child PX RT AjPDomNode RD Pointer to extrachild or NULL if error RX // AjPDomNode ajDomNodeAppendChild(AjPDomNode node, AjPDomNode extrachild) { AjPDomNode n = NULL; AjPDomNode next = NULL; if(!node || !extrachild) return NULL; if(extrachild->ownerdocument != node->ownerdocument && node->type != ajEDomNodeTypeDocumentNode && extrachild->type != ajEDomNodeTypeDocumentType) { ajDebug("ajDomNodeAppendChild: Wrong document"); return NULL; } if(extrachild->type == ajEDomNodeTypeDocumentFragment) { for(n = extrachild->firstchild; n; n = n->nextsibling) if(AJDOM_CANTDO(node, n) || domIsAncestor(n, node)) { ajDebug("ajDomNodeAppendChild: Hierarchy Request Error\n"); return NULL; } for(n = extrachild->firstchild; n; n = next) { next = n->nextsibling; if(!ajDomRemoveChild(extrachild, n)) return NULL; if(!ajDomNodeAppendChild(node, n)) { ajDomDocumentDestroyNode(n->ownerdocument, n); return NULL; } } return extrachild; } if(AJDOM_CANTDO(node, extrachild) || domIsAncestor(extrachild, node)) { ajDebug("ajDomNodeAppendChild: Hierarchy Request Error 2\n"); return NULL; } domDoRemoveChild(node, extrachild); if(!ajDomNodeListAppend(node->childnodes, extrachild)) return NULL; if(!node->firstchild) { node->firstchild = extrachild; node->lastchild = extrachild; extrachild->previoussibling = NULL; extrachild->nextsibling = NULL; } else { node->lastchild->nextsibling = extrachild; extrachild->previoussibling = node->lastchild; node->lastchild = extrachild; } extrachild->nextsibling = NULL; extrachild->parentnode = node; if(AJDOM_DOCMOD(node, extrachild)) node->sub.Document.documentelement = extrachild; else if(AJDOM_DOCTYPEMOD(node, extrachild)) { node->sub.Document.doctype = extrachild; extrachild->ownerdocument = node; } domUpdateNode(node); return extrachild; } ID domUpdateNode TY static MO ajdom LB core XX DE Update the common parent entry of a node XX PN [1] PA w node AjPDomNode PD node PX RT void RD RX // static void domUpdateNode(AjPDomNode node) { AjPDomNode p = NULL; AjPDomNode n = NULL; if(!node || !node->ownerdocument) return; if(!node->ownerdocument->sub.Document.commonparent) { node->ownerdocument->sub.Document.commonparent = node; return; } p = NULL; for(n = node; n; n = n->parentnode) if(n == node->ownerdocument->sub.Document.commonparent) return; else if(!p && n->subtreeModified == 1) p = n; else n->subtreeModified = 1; node->ownerdocument->sub.Document.commonparent = p; return; } ID domIsAncestor TY static MO ajdom LB core XX DE Tests whether a putative parent is the parent of a child XX PN [1] PA r node const AjPDomNode PD node PX PN [2] PA r parent const AjPDomNode PD putative parent PX RT AjBool RD true if ancestor found RX // static AjBool domIsAncestor(const AjPDomNode node, const AjPDomNode parent) { const AjPDomNode p = NULL; for(p = parent; p; p = p->parentnode) if(p == node) return ajTrue; return ajFalse; } ID ajDomRemoveChild TY public MO ajdom LB core XX DE Removes a child node from a list of children XX PN [1] PA w node AjPDomNode PD node PX PN [2] PA u child AjPDomNode PD child to remove PX RT AjPDomNode RD child removed RX // AjPDomNode ajDomRemoveChild(AjPDomNode node, AjPDomNode child) { if(!node || !child) return NULL; if(child->ownerdocument != node->ownerdocument && child->ownerdocument != node) { ajDebug("ajDomRemoveChild: Wrong document"); return NULL; } child = domDoRemoveChild(node, child); return child; } ID ajDomNodeListExists TY public MO ajdom LB core XX DE Check whether a child exists in a node list XX PN [1] PA u list AjPDomNodeList PD list PX PN [2] PA r child const AjPDomNode PD child PX RT AjBool RD true if child is in the list RX // AjBool ajDomNodeListExists(AjPDomNodeList list, const AjPDomNode child) { AjPDomNodeEntry e = NULL; if(!list || list->filter) return ajFalse; e = domDoLookupNode(list, child); if(e) return ajTrue; return ajFalse; } ID domDoLookupNode TY static MO ajdom LB core XX DE Return a pointer to a given node of a node list XX PN [1] PA r list const AjPDomNodeList PD list PX PN [2] PA r node const AjPDomNode PD node PX RT AjPDomNodeEntry RD node entry or NULL if not found RX // static AjPDomNodeEntry domDoLookupNode(const AjPDomNodeList list, const AjPDomNode node) { AjPDomNodeEntry p; p = (AjPDomNodeEntry) ajTableFetchmodV(list->table, node); return p; } ID ajDomNodeListRemove TY public MO ajdom LB core XX DE Remove a child from a node list XX PN [1] PA w list AjPDomNodeList PD list PX PN [2] PA u child AjPDomNode PD child PX RT AjPDomNodeEntry RD child removed or NULL if not found RX // AjPDomNodeEntry ajDomNodeListRemove(AjPDomNodeList list, AjPDomNode child) { AjPDomNodeEntry e; AjPDomNode trukey; if(!list) { ajDebug("ajDomNodeListRemove: Empty list"); return NULL; } if(list->filter) { ajDebug("ajDomNodeListRemove: Filtered list error"); return NULL; } e = domDoLookupNode(list, child); if(!e) return NULL; /* ** Don't delete the key/value - just remove them from the lookup ** table. The value is still required */ ajTableRemoveKey(list->table, child, (void **) &trukey); if(list->first == list->last) { list->first = NULL; list->last = NULL; } else if(e == list->first) { list->first = e->next; list->first->prev = NULL; } else if(e == list->last) { list->last = e->prev; list->last->next = NULL; } else { e->prev->next = e->next; e->next->prev = e->prev; } --list->length; if(child->type == ajEDomNodeTypeAttribute) child->sub.Attr.ownerelement = NULL; return e; } ID domDoRemoveChild TY static MO ajdom LB core XX DE Low level removal of a child node from a list of children XX PN [1] PA w node AjPDomNode PD node PX PN [2] PA u child AjPDomNode PD child to remove PX RT AjPDomNode RD child removed RX // static AjPDomNode domDoRemoveChild(AjPDomNode node, AjPDomNode child) { AjPDomNodeEntry e = NULL; if(!ajDomNodeListExists(node->childnodes, child)) return NULL; e = ajDomNodeListRemove(node->childnodes, child); free(e); if(node->firstchild == node->lastchild) { node->firstchild = NULL; node->lastchild = NULL; } else if(child == node->firstchild) { node->firstchild = child->nextsibling; node->firstchild->previoussibling = NULL; } else if(child == node->lastchild) { node->lastchild = child->previoussibling; node->lastchild->nextsibling = NULL; } else { child->previoussibling->nextsibling = child->nextsibling; child->nextsibling->previoussibling = child->previoussibling; } child->previoussibling = NULL; child->nextsibling = NULL; child->parentnode = NULL; if(AJDOM_DOCMOD(node, child)) node->sub.Document.documentelement = NULL; else if(AJDOM_DOCTYPEMOD(node, child)) { node->sub.Document.doctype = NULL; child->ownerdocument = NULL; } else domUpdateNode(node); return child; } ID ajDomDocumentDestroyNode TY public MO ajdom LB core XX DE Frees a node and its children XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA w node AjPDomNode PD node PX RT void RD RX // void ajDomDocumentDestroyNode(AjPDomDocument doc, AjPDomNode node) { if(!node) return; if(node->childnodes) ajDomDocumentDestroyNodeList(doc, node->childnodes, AJDOMDESTROY); switch(node->type) { case ajEDomNodeTypeElement: ajDomDocumentDestroyNodeList(doc, node->attributes, AJDOMDESTROY); ajStrDel(&node->name); break; case ajEDomNodeTypeText: ajStrDel(&node->name); ajStrDel(&node->value); break; case ajEDomNodeTypeComment: ajStrDel(&node->name); ajStrDel(&node->value); break; case ajEDomNodeTypeCdataSection: ajStrDel(&node->name); ajStrDel(&node->value); break; case ajEDomNodeTypeAttribute: ajStrDel(&node->name); ajStrDel(&node->value); break; case ajEDomNodeTypeEntityReference: case ajEDomNodeTypeEntityNode: ajStrDel(&node->name); ajStrDel(&node->value); ajStrDel(&node->sub.Entity.publicid); ajStrDel(&node->sub.Entity.systemid); ajStrDel(&node->sub.Entity.notationname); break; case ajEDomNodeTypeProcessingInstruction: ajStrDel(&node->name); ajStrDel(&node->value); break; case ajEDomNodeTypeDocumentNode: ajStrDel(&node->name); ajStrDel(&node->sub.Document.version); ajStrDel(&node->sub.Document.encoding); break; case ajEDomNodeTypeDocumentType: ajDomDocumentDestroyNodeList(doc, node->sub.DocumentType.entities, AJDOMDESTROY); ajDomDocumentDestroyNodeList(doc, node->sub.DocumentType.notations, AJDOMDESTROY); ajStrDel(&node->sub.DocumentType.publicid); ajStrDel(&node->sub.DocumentType.systemid); ajStrDel(&node->name); break; case ajEDomNodeTypeNotation: ajStrDel(&node->sub.Notation.publicid); ajStrDel(&node->sub.Notation.systemid); ajStrDel(&node->name); break; default: ajDebug("ajDomDocumentDestroyNode got unexpected " "AJAX DOM Node type %d.\n", node->type); } AJFREE(node); return; } ID ajDomDocumentDestroyNodeList TY public MO ajdom LB core XX DE Frees a node list XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA w list AjPDomNodeList PD list PX PN [3] PA r donodes AjBool PD free nodes as well if true PX RT void RD RX // void ajDomDocumentDestroyNodeList(AjPDomDocument doc, AjPDomNodeList list, AjBool donodes) { AjPDomNodeEntry entry = NULL; AjPDomNodeEntry tmp = NULL; if(list) { if(list->filter) { entry = list->first; while(entry) { if(donodes) ajDomDocumentDestroyNode(doc, entry->node); tmp = entry; entry = entry->next; AJFREE(tmp); } } if(list->table) { if(donodes) ajTableMapDel(list->table, &domClearMapAll, (void *) doc); else ajTableMapDel(list->table, &domClearMapValues, NULL); ajTableFree(&list->table); } AJFREE(list); } return; } ID domClearMapValues TY static MO ajdom LB core XX DE Clear only the values from the map table XX PN [1] PA r key void** PD Standard argument, table key. PX PN [2] PA r value void** PD Standard argument, table data item. PX PN [3] PA r cl void* PD Standard argument, usually NULL PX RT void RD RX // static void domClearMapValues(void **key, void **value, void *cl) { AjPDomNodeEntry entry = NULL; entry = (AjPDomNodeEntry) *value; AJFREE(entry); (void) cl; /* make it used */ (void) key; *value = NULL; return; } ID domClearMapAll TY static MO ajdom LB core XX DE Clear keys and values from the map table XX PN [1] PA r key void** PD Standard argument, table key. PX PN [2] PA r value void** PD Standard argument, table data item. PX PN [3] PA r cl void* PD Standard argument, usually NULL PX RT void RD RX // static void domClearMapAll(void **key, void **value, void *cl) { AjPDomNodeEntry entry = NULL; AjPDomNode node = NULL; AjPDomDocument doc = NULL; entry = (AjPDomNodeEntry) *value; AJFREE(entry); node = (AjPDomNode) *key; doc = (AjPDomDocument) cl; ajDomDocumentDestroyNode(doc, node); *value = NULL; return; } ID ajDomCreateNodeList TY public MO ajdom LB core XX DE Create a node list XX PN [1] PA u doc AjPDomDocument PD document PX RT AjPDomNodeList RD new list RX // AjPDomNodeList ajDomCreateNodeList(AjPDomDocument doc) { AjPDomNodeList list = NULL; AJNEW0(list); list->ownerdocument = doc; return list; } ID ajDomDocumentCreateNode TY public MO ajdom LB core XX DE General document node creation XX PN [1] PA u doc AjPDomDocument PD document PX PN [2] PA u nodetype AjEDomNodeType PD AJAX DOM Node Type enumeration PX RT AjPDomNode RD new node RX // AjPDomNode ajDomDocumentCreateNode(AjPDomDocument doc, AjEDomNodeType nodetype) { AjPDomNode node; if (!doc && nodetype != ajEDomNodeTypeDocumentNode && nodetype != ajEDomNodeTypeDocumentType) { ajDebug("ajDocumentCreateNode: allocation failure\n"); return NULL; } AJNEW0(node); node->ownerdocument = doc; node->type = nodetype; switch (nodetype) { case ajEDomNodeTypeDocumentNode: case ajEDomNodeTypeDocumentType: case ajEDomNodeTypeElement: case ajEDomNodeTypeAttribute: case ajEDomNodeTypeEntityReference: case ajEDomNodeTypeEntityNode: case ajEDomNodeTypeDocumentFragment: node->childnodes = ajDomCreateNodeList(doc); if(node->childnodes == NULL) { ajDebug("ajDocumentCreateNode: ajDocumentCreateNodeList" " failed\n"); ajDomDocumentDestroyNode(doc, node); return NULL; } default: ajDebug("ajDomDocumentCreateNode got unexpected " "AJAX DOM Node type %d.\n", nodetype); } return node; } ID ajDomImplementationCreateDocumentType TY public MO ajdom LB core XX DE Creates an empty DocumentType node into which entities/notations (etc) DE can be placed XX PN [1] PA r qualname const AjPStr PD qualified name PX PN [2] PA r publicid const AjPStr PD public id PX PN [3] PA r systemid const AjPStr PD systemid PX RT AjPDomDocumentType RD new DocumentType node RX // AjPDomDocumentType ajDomImplementationCreateDocumentType(const AjPStr qualname, const AjPStr publicid, const AjPStr systemid) { char *p = NULL; char *s = NULL; char *r = NULL; if(qualname) p = qualname->Ptr; if(publicid) r = publicid->Ptr; if(systemid) s = systemid->Ptr; return ajDomImplementationCreateDocumentTypeC(p, r, s); } ID ajDomImplementationCreateDocumentTypeC TY public MO ajdom LB core XX DE Creates an empty DocumentType node into which entities/notations (etc) DE can be placed XX PN [1] PA r qualname const char* PD qualified name PX PN [2] PA r publicid const char* PD public id PX PN [3] PA r systemid const char* PD systemid PX RT AjPDomDocumentType RD new DocumentType node RX // AjPDomDocumentType ajDomImplementationCreateDocumentTypeC(const char *qualname, const char *publicid, const char *systemid) { AjPDomDocumentType doctype; AjPDomNodeMap entities; AjPDomNodeMap notations; if(!(doctype = ajDomDocumentCreateNode(NULL, ajEDomNodeTypeDocumentType))) { ajDebug("ajDomImplementationCreateDocumentType: Cannot create node"); return NULL; } ajStrAssignC(&doctype->sub.DocumentType.name, qualname); doctype->name = doctype->sub.DocumentType.name; if(publicid) ajStrAssignC(&doctype->sub.DocumentType.publicid, publicid); if(systemid) ajStrAssignC(&doctype->sub.DocumentType.systemid, systemid); entities = ajDomCreateNodeList(NULL); notations = ajDomCreateNodeList(NULL); entities->filter = ajEDomNodeTypeEntityNode; entities->list = doctype->childnodes; notations->filter = ajEDomNodeTypeNotation; notations->list = doctype->childnodes; doctype->sub.DocumentType.entities = entities; doctype->sub.DocumentType.notations = notations; return doctype; } ID ajDomImplementationCreateDocument TY public MO ajdom LB core XX DE Creates an XML Document object of the specified type with its document DE element. XX PN [1] PA r uri const AjPStr PD uri (not implemented. Pass NULL) PX PN [2] PA r qualname const AjPStr PD qualified name PX PN [3] PA u doctype AjPDomDocumentType PD doctype PX RT AjPDomDocument RD new document RX // AjPDomDocument ajDomImplementationCreateDocument(const AjPStr uri, const AjPStr qualname, AjPDomDocumentType doctype) { const char *p = NULL; (void) uri; /* temporary use */ if(qualname) p = qualname->Ptr; return ajDomImplementationCreateDocumentC(NULL, p, doctype); } ID ajDomImplementationCreateDocumentC TY public MO ajdom LB core XX DE Creates an XML Document object of the specified type with its document DE element. XX PN [1] PA r uri const char* PD uri (not implemented. Pass NULL) PX PN [2] PA r qualname const char* PD qualified name PX PN [3] PA u doctype AjPDomDocumentType PD doctype PX RT AjPDomDocument RD new document RX // AjPDomDocument ajDomImplementationCreateDocumentC(const char *uri, const char *qualname, AjPDomDocumentType doctype) { AjPDomDocument doc = NULL; AjPDomElement element = NULL; (void) uri; if(!(doc = ajDomDocumentCreateNode(NULL, ajEDomNodeTypeDocumentNode))) { ajDebug("ajDomImplementationCreateDocumentC: document memory\n"); return NULL; } doc->name = ajStrNew(); ajStrAssignC(&doc->name, "#document"); if(doctype) ajDomNodeAppendChild(doc, doctype); if(qualname) if(strlen(qualname)) { element = ajDomDocumentCreateElementC(doc, qualname); if(!element) { ajDebug("ajDomImplementationCreateDocumentC: element memory\n"); ajDomDocumentDestroyNode(doc, doc); return NULL; } ajDomNodeAppendChild(doc, element); } return doc; } ID ajDomNodeMapGetItem TY public MO ajdom LB core XX DE Returns the named node from a node map XX PN [1] PA r map const AjPDomNodeMap PD map PX PN [2] PA r name const AjPStr PD name PX RT AjPDomNode RD node RX // AjPDomNode ajDomNodeMapGetItem(const AjPDomNodeMap map, const AjPStr name) { char *p = NULL; if(name) p = name->Ptr; return ajDomNodeMapGetItemC(map, p); } ID ajDomNodeMapGetItemC TY public MO ajdom LB core XX DE Returns the named node from a node map XX PN [1] PA r map const AjPDomNodeMap PD map PX PN [2] PA r name const char* PD name PX RT AjPDomNode RD node RX // AjPDomNode ajDomNodeMapGetItemC(const AjPDomNodeMap map, const char *name) { AjPDomNodeEntry e = NULL; ajuint nodetype; if(map && name) { if(map->filter) { nodetype = map->filter; map = map->list; for(e = map->first; e; e = e->next) if(e->node->type == nodetype && !strcmp(name, e->node->name->Ptr)) return e->node; } else for(e = map->first; e; e = e->next) if(!strcmp(name, e->node->name->Ptr)) return e->node; } return NULL; } ID ajDomElementGetAttribute TY public MO ajdom LB core XX DE Returns the value of a named attribute XX PN [1] PA r element const AjPDomElement PD element PX PN [2] PA r name const AjPStr PD name PX RT AjPStr RD value or empty string RX // AjPStr ajDomElementGetAttribute(const AjPDomElement element, const AjPStr name) { char *p = NULL; if(name) p = name->Ptr; return ajDomElementGetAttributeC(element, p); } ID ajDomElementGetAttributeC TY public MO ajdom LB core XX DE Returns the value of a named attribute XX PN [1] PA r element const AjPDomElement PD element PX PN [2] PA r name const char* PD name PX RT AjPStr RD value or empty string RX // AjPStr ajDomElementGetAttributeC(const AjPDomElement element, const char *name) { AjPDomNode node = NULL; AjPStr ret = NULL; ret = ajStrNewC(""); if(element && name && element->attributes) { if((node = ajDomNodeMapGetItemC(element->attributes, name))) ajStrAssignS(&ret, node->value); } return ret; } ID ajDomNodeMapSetItem TY public MO ajdom LB core XX DE Enter arg into the node map using name as the key. DE If a node with the same name already exists in the map it will be replaced DE with the new node and returned. DE The replaced node should usually be freed with DestroyNode. XX PN [1] PA u map AjPDomNodeMap PD map PX PN [2] PA u arg AjPDomNode PD arg PX RT AjPDomNode RD replaced node or NULL RX // AjPDomNode ajDomNodeMapSetItem(AjPDomNodeMap map, AjPDomNode arg) { AjPDomNodeEntry e = NULL; AjPDomNode tmp = NULL; if(map && arg) { if(map->filter) { ajDebug("ajDomNodeMapSetItem: No mod allowed\n"); return NULL; } if(map->ownerdocument != arg->ownerdocument) { ajDebug("ajDomNodeMapSetItem: Wrong document\n"); return NULL; } if(arg->type == ajEDomNodeTypeAttribute && arg->sub.Attr.ownerelement && arg->sub.Attr.ownerelement != map->ownerelement) { ajDebug("ajDomNodeMapSetItem: In use attribute error\n"); return NULL; } for(e = map->first; e && strcmp(arg->name->Ptr, e->node->name->Ptr); e = e->next) { ; } if(e) { tmp = e->node; e->node = arg; if(arg->type == ajEDomNodeTypeAttribute) { arg->sub.Attr.ownerelement = map->ownerelement; tmp->sub.Attr.ownerelement = NULL; } return tmp; } ajDomNodeListAppend(map, arg); } return NULL; } ID ajDomNodeMapRemoveItem TY public MO ajdom LB core XX DE Removes and returns item from a map XX PN [1] PA u map AjPDomNodeMap PD map PX PN [2] PA r name const AjPStr PD name PX RT AjPDomNode RD removed node or NULL RX // AjPDomNode ajDomNodeMapRemoveItem(AjPDomNodeMap map, const AjPStr name) { char *p = NULL; if(name) p = name->Ptr; return ajDomNodeMapRemoveItemC(map, p); } ID ajDomNodeMapRemoveItemC TY public MO ajdom LB core XX DE Removes and returns item from a map XX PN [1] PA w map AjPDomNodeMap PD map PX PN [2] PA r name const char* PD name PX RT AjPDomNode RD removed node or NULL RX // AjPDomNode ajDomNodeMapRemoveItemC(AjPDomNodeMap map, const char *name) { AjPDomNodeEntry e = NULL; AjPDomNode r = NULL; if(map && strlen(name)) { if(map->filter) { ajDebug("ajDomNodeMapRemoveItemC: No mod allowed\n"); return NULL; } for(e = map->first; e; e = e->next) { if(!strcmp(name, e->node->name->Ptr) && ajDomNodeListRemove(map, e->node)) { r = e->node; free(e); if(r->type == ajEDomNodeTypeAttribute) r->sub.Attr.ownerelement = NULL; return r; } } } ajDebug("ajDomNodeMapRemoveItemC: Not found error\n"); return NULL; } ID ajDomNodeMapItem TY public MO ajdom LB core XX DE Return the node in the map at a given index XX PN [1] PA r map const AjPDomNodeMap PD map PX PN [2] PA r indexnum ajint PD index PX RT AjPDomNode RD node or NULL RX // AjPDomNode ajDomNodeMapItem(const AjPDomNodeMap map, ajint indexnum) { return ajDomNodeListItem(map, indexnum); } ID domNodeListItemFiltered TY static MO ajdom LB core XX DE Filter node list item XX PN [1] PA r list const AjPDomNodeList PD list PX PN [2] PA r indexnum ajint PD index PX PN [3] PA r nodetype ajuint PD nodetype PX RT AjPDomNode RD node or NULL RX // static AjPDomNode domNodeListItemFiltered(const AjPDomNodeList list, ajint indexnum, ajuint nodetype) { AjPDomNodeEntry e = NULL; if(list && indexnum >= 0 && indexnum < list->length) for(e = list->first; e; e = e->next) if(e->node->type == nodetype) { if(!indexnum) return e->node; --indexnum; } return NULL; } ID ajDomNodeListItem TY public MO ajdom LB core XX DE Return the node in the list at a given index XX PN [1] PA r list const AjPDomNodeList PD list PX PN [2] PA r indexnum ajint PD index PX RT AjPDomNode RD node or NULL RX // AjPDomNode ajDomNodeListItem(const AjPDomNodeList list, ajint indexnum) { AjPDomNodeEntry e = NULL; if(list) { if(list->filter) return domNodeListItemFiltered(list->list, indexnum, list->filter); if(indexnum >= 0 && indexnum < list->length) for(e = list->first; e; e = e->next, --indexnum) if(!indexnum) return e->node; } return NULL; } ID ajDomElementSetAttribute TY public MO ajdom LB core XX DE Adds a new attribute or sets the value of an existing attribute. XX PN [1] PA r element const AjPDomElement PD element PX PN [2] PA r name const AjPStr PD name PX PN [3] PA r value const AjPStr PD value PX RT void RD RX // void ajDomElementSetAttribute(const AjPDomElement element, const AjPStr name, const AjPStr value) { if(!element || !name || !value || !element->attributes) return; if(!ajStrGetLen(name) || !ajStrGetLen(value)) return; ajDomElementSetAttributeC(element, name->Ptr, value->Ptr); return; } ID ajDomElementSetAttributeC TY public MO ajdom LB core XX DE Adds a new attribute or sets the value of an existing attribute. XX PN [1] PA r element const AjPDomElement PD element PX PN [2] PA r name const char* PD name PX PN [3] PA r value const char* PD value PX RT void RD RX // void ajDomElementSetAttributeC(const AjPDomElement element, const char *name, const char *value) { AjPDomNode attr = NULL; AjPStr prevvalue = NULL; if(!element || !name || !value || !element->attributes) return; attr = ajDomNodeMapGetItemC(element->attributes, name); if(attr) { prevvalue = attr->value; attr->sub.Attr.value = ajStrNew(); ajStrAssignC(&attr->sub.Attr.value, value); /* AJB: dirty */ attr->value = attr->sub.Attr.value; } else { prevvalue = NULL; if(!(attr = ajDomDocumentCreateAttributeC(element->ownerdocument, name))) { ajDebug("ajDomElementSetAttribute: DOM create failed\n"); return; } ajStrDel(&attr->value); attr->sub.Attr.value = ajStrNew(); ajStrAssignC(&attr->sub.Attr.value, value); /* AJB: dirty */ attr->value = attr->sub.Attr.value; ajDomNodeMapSetItem(element->attributes, attr); } domUpdateNode(element->parentnode); if(prevvalue) ajStrDel(&prevvalue); return; } ID ajDomElementRemoveAttribute TY public MO ajdom LB core XX DE Remove and free a named attribute XX PN [1] PA u element AjPDomElement PD element PX PN [2] PA r name const AjPStr PD name PX RT void RD RX // void ajDomElementRemoveAttribute(AjPDomElement element, const AjPStr name) { if(!name) return; ajDomElementRemoveAttributeC(element, name->Ptr); return; } ID ajDomElementRemoveAttributeC TY public MO ajdom LB core XX DE Remove and free a named attribute XX PN [1] PA u element AjPDomElement PD element PX PN [2] PA r name const char* PD name PX RT void RD RX // void ajDomElementRemoveAttributeC(AjPDomElement element, const char *name) { AjPDomNode attr = NULL; if(!element || !name) return; attr = ajDomNodeMapRemoveItemC(element->attributes, name); if(attr) { domUpdateNode(element->parentnode); ajDomDocumentDestroyNode(attr->ownerdocument, attr); } return; } ID ajDomElementGetAttributeNode TY public MO ajdom LB core XX DE Returns the named attribute node. XX PN [1] PA r element const AjPDomElement PD element PX PN [2] PA r name const AjPStr PD name PX RT AjPDomNode RD node or NULL RX // AjPDomNode ajDomElementGetAttributeNode(const AjPDomElement element, const AjPStr name) { if(element && name) return ajDomNodeMapGetItem(element->attributes, name); return NULL; } ID ajDomElementGetAttributeNodeC TY public MO ajdom LB core XX DE Returns the named attribute node. XX PN [1] PA r element const AjPDomElement PD element PX PN [2] PA r name const char* PD name PX RT AjPDomNode RD node or NULL RX // AjPDomNode ajDomElementGetAttributeNodeC(const AjPDomElement element, const char *name) { if(element && name) return ajDomNodeMapGetItemC(element->attributes, name); return NULL; } ID ajDomElementSetAttributeNode TY public MO ajdom LB core XX DE Add an attribute to the attributes of an element. DE If this element already has an attribute with the same name it will be DE replaced with the new attribute and returned. XX PN [1] PA u element AjPDomElement PD element PX PN [2] PA u newattr AjPDomNode PD name PX RT AjPDomNode RD new or replaced attribute RX // AjPDomNode ajDomElementSetAttributeNode(AjPDomElement element, AjPDomNode newattr) { AjPDomNode attr = NULL; if(!element || !newattr) return NULL; if(element->ownerdocument != newattr->ownerdocument) { ajDebug("ajDomElementSetAttributeNode: Wrong document\n"); return NULL; } attr = ajDomNodeMapSetItem(element->attributes, newattr); domUpdateNode(element->parentnode); return attr; } ID ajDomElementRemoveAttributeNode TY public MO ajdom LB core XX DE Removes and returns a pointer to an attribute XX PN [1] PA w element AjPDomElement PD element PX PN [2] PA u oldattr AjPDomNode PD attribute PX RT AjPDomNode RD removed attribute or NULL RX // AjPDomNode ajDomElementRemoveAttributeNode(AjPDomElement element, AjPDomNode oldattr) { if(!element || !oldattr || !ajDomNodeListRemove(element->attributes, oldattr)) { ajDebug("ajDomElementRemoveAttributeNode: DOM not found error\n"); return NULL; } domUpdateNode(element->parentnode); return oldattr; } ID domTraverse TY static MO ajdom LB core XX DE Pre-order elements XX PN [1] PA w list AjPDomNodeList PD list PX PN [2] PA u node AjPDomNode PD node PX PN [3] PA r tagname const AjPStr PD tagname PX RT void RD RX // static void domTraverse(AjPDomNodeList list, AjPDomNode node, const AjPStr tagname) { AjPDomNode n = NULL; if(list && node && node->type == ajEDomNodeTypeElement && tagname) { if(ajStrMatchC(tagname, "*") || !strcmp(tagname->Ptr, node->name->Ptr)) ajDomNodeListAppend(list, node); for(n = node->firstchild; n; n = n->nextsibling) domTraverse(list, n, tagname); } return; } ID domTraverseC TY static MO ajdom LB core XX DE Pre-order elements XX PN [1] PA w list AjPDomNodeList PD list PX PN [2] PA u node AjPDomNode PD node PX PN [3] PA r tagname const char* PD tagname PX RT void RD RX // static void domTraverseC(AjPDomNodeList list, AjPDomNode node, const char *tagname) { AjPDomNode n = NULL; if(list && node && node->type == ajEDomNodeTypeElement && tagname) { if(ajCharMatchC(tagname, "*") || !strcmp(tagname, node->name->Ptr)) ajDomNodeListAppend(list, node); for(n = node->firstchild; n; n = n->nextsibling) domTraverseC(list, n, tagname); } return; } ID ajDomElementGetElementsByTagName TY public MO ajdom LB core XX DE Perform a pre-order traversal of the entire document. DE Return a node list of the elements with the name tagname in the order DE in which they are found. XX PN [1] PA u element AjPDomElement PD element PX PN [2] PA r name const AjPStr PD name PX RT AjPDomNodeList RD node list RX // AjPDomNodeList ajDomElementGetElementsByTagName(AjPDomElement element, const AjPStr name) { if(!element || !name) return NULL; return ajDomElementGetElementsByTagNameC(element, name->Ptr); } ID ajDomElementGetElementsByTagNameC TY public MO ajdom LB core XX DE Perform a pre-order traversal of the entire document. DE Return a node list of the elements with the name tagname in the order DE in which they are found. XX PN [1] PA u element AjPDomElement PD element PX PN [2] PA r name const char* PD name PX RT AjPDomNodeList RD node list RX // AjPDomNodeList ajDomElementGetElementsByTagNameC(AjPDomElement element, const char *name) { AjPDomNodeList list = NULL; AjPDomNode n = NULL; if(element && element->type == ajEDomNodeTypeElement && name && (list=ajDomCreateNodeList(element->ownerdocument))) { for(n = element->firstchild; n; n = n->nextsibling) domTraverseC(list, n, name); return list; } return NULL; } ID ajDomElementNormalise TY public MO ajdom LB core XX DE Merge adjacent text node content into "normal" form in the subtree of DE a node. Remove empty text node. XX PN [1] PA w element AjPDomElement PD element PX RT void RD RX // void ajDomElementNormalise(AjPDomElement element) { AjPDomNode node = NULL; AjPDomText last = NULL; if(element) { for(node = element->firstchild; node; node = node->nextsibling) { if(node->type == ajEDomNodeTypeText) { if(last) { ajDomCharacterDataInsertData(node, 0, last->value); ajDomRemoveChild(element, last); ajDomDocumentDestroyNode(last->ownerdocument, last); } last = node; } else { last = NULL; ajDomElementNormalise(node); } /* AJB: Would do a return here if exception */ } } return; } ID ajDomCharacterDataSubstringData TY public MO ajdom LB core XX DE Return a string representing the substring beginning at the character at DE offset with a length of count. DE If the sum of offset and count exceeds the length of the character data, DE a string representing the remainder of the string is returned. XX PN [1] PA r data const AjPDomCharacterData PD character data PX PN [2] PA r offset ajint PD offset PX PN [3] PA r count ajint PD count PX RT AjPStr RD substring RX // AjPStr ajDomCharacterDataSubstringData(const AjPDomCharacterData data, ajint offset, ajint count) { AjPStr sub = NULL; ajint dlen = 0; if(!data) return NULL; if(offset < 0 || offset > (dlen = data->sub.CharacterData.length) || count < 0) return NULL; if(count > (dlen - offset)) count = dlen - offset; sub = ajStrNew(); ajStrAssignSubS(&sub, data->value, offset, offset + count - 1); return sub; } ID ajDomCharacterDataAppendData TY public MO ajdom LB core XX DE Append a string to a character data node XX PN [1] PA w data AjPDomCharacterData PD character data PX PN [2] PA r arg const AjPStr PD string PX RT void RD RX // void ajDomCharacterDataAppendData(AjPDomCharacterData data, const AjPStr arg) { if(!data || !arg) return; ajDomCharacterDataAppendDataC(data, arg->Ptr); return; } ID ajDomCharacterDataAppendDataC TY public MO ajdom LB core XX DE Append a string to a character data node XX PN [1] PA w data AjPDomCharacterData PD character data PX PN [2] PA r arg const char* PD string PX RT void RD RX // void ajDomCharacterDataAppendDataC(AjPDomCharacterData data, const char *arg) { if(!data || !arg) return; ajStrAppendC(&data->value, arg); data->sub.CharacterData.length = ajStrGetLen(data->value); data->sub.CharacterData.data = data->value; domUpdateNode(data->parentnode); return; } ID ajDomCharacterDataInsertData TY public MO ajdom LB core XX DE Insert a string at position offset XX PN [1] PA w data AjPDomCharacterData PD character data PX PN [2] PA r offset ajint PD offset PX PN [3] PA r arg const AjPStr PD string PX RT void RD RX // void ajDomCharacterDataInsertData(AjPDomCharacterData data, ajint offset, const AjPStr arg) { if(!data || !arg) return; ajDomCharacterDataInsertDataC(data, offset, arg->Ptr); return; } ID ajDomCharacterDataInsertDataC TY public MO ajdom LB core XX DE Insert a string at position offset XX PN [1] PA w data AjPDomCharacterData PD character data PX PN [2] PA r offset ajint PD offset PX PN [3] PA r arg const char* PD string PX RT void RD RX // void ajDomCharacterDataInsertDataC(AjPDomCharacterData data, ajint offset, const char *arg) { if(!data || !arg) return; if(offset < 0 || offset > data->sub.CharacterData.length) return; ajStrInsertC(&data->value, offset, arg); data->sub.CharacterData.length = ajStrGetLen(data->value); data->sub.CharacterData.data = data->value; domUpdateNode(data->parentnode); return; } ID ajDomCharacterDataDeleteData TY public MO ajdom LB core XX DE Remove at most count characters of position offset XX PN [1] PA w data AjPDomCharacterData PD character data PX PN [2] PA r offset ajint PD offset PX PN [3] PA r count ajint PD count PX RT void RD RX // void ajDomCharacterDataDeleteData(AjPDomCharacterData data, ajint offset, ajint count) { ajint dlen = 0; if(!data) return; dlen = ajStrGetLen(data->value); if(offset <0 || offset > dlen) return; if(count < 0 || (offset + count) > dlen) count = dlen - offset; ajStrCutRange(&data->value, offset, offset + count - 1); data->sub.CharacterData.length = ajStrGetLen(data->value); data->sub.CharacterData.data = data->value; domUpdateNode(data->parentnode); return; } ID ajDomCharacterDataReplaceData TY public MO ajdom LB core XX DE Replace offset/count characters with a string XX PN [1] PA w data AjPDomCharacterData PD character data PX PN [2] PA r offset ajint PD offset PX PN [3] PA r count ajint PD count PX PN [4] PA r arg const AjPStr PD replacement string PX RT void RD RX // void ajDomCharacterDataReplaceData(AjPDomCharacterData data, ajint offset, ajint count, const AjPStr arg) { ajDomCharacterDataDeleteData(data, offset, count); ajDomCharacterDataInsertData(data, offset, arg); return; } ID ajDomCharacterDataReplaceDataC TY public MO ajdom LB core XX DE Replace offset/count characters with a string XX PN [1] PA w data AjPDomCharacterData PD character data PX PN [2] PA r offset ajint PD offset PX PN [3] PA r count ajint PD count PX PN [4] PA r arg const char* PD replacement string PX RT void RD RX // void ajDomCharacterDataReplaceDataC(AjPDomCharacterData data, ajint offset, ajint count, const char *arg) { ajDomCharacterDataDeleteData(data, offset, count); ajDomCharacterDataInsertDataC(data, offset, arg); return; } ID ajDomCharacterDataGetLength TY public MO ajdom LB core XX DE Return length of character data XX PN [1] PA r data const AjPDomCharacterData PD character data PX RT ajint RD length RX // ajint ajDomCharacterDataGetLength(const AjPDomCharacterData data) { return data ? data->sub.CharacterData.length : 0; } ID ajDomTextSplitText TY public MO ajdom LB core XX DE Split a text node at the specified offset into two adjacent text nodes. DE The first is a string offset size in length whereas the second is the DE remainder. If offset is equal to the length of the string the new sibling DE has zero length XX PN [1] PA w text AjPDomText PD text PX PN [2] PA r offset ajint PD offset PX RT AjPDomText RD remainder node or NULL RX // AjPDomText ajDomTextSplitText(AjPDomText text, ajint offset) { AjPDomText node; AjPStr sub = NULL; ajint len = 0; if(!text || !text->parentnode) return NULL; if(offset < 0 || offset >text->sub.CharacterData.length) return NULL; sub = ajStrNew(); len = ajStrGetLen(text->value); ajStrAssignSubS(&sub, text->value, offset, len - 1); node = ajDomDocumentCreateTextNode(text->ownerdocument, sub); ajStrDel(&sub); if(!node) return NULL; ajDomCharacterDataDeleteData(text, offset, len - 1); ajDomNodeInsertBefore(text->parentnode, node, text->nextsibling); return node; } ID ajDomDocumentCreateElement TY public MO ajdom LB core XX DE Create a document element XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r tagname const AjPStr PD tagname PX RT AjPDomElement RD element RX // AjPDomElement ajDomDocumentCreateElement(AjPDomDocument doc, const AjPStr tagname) { if(!tagname) return NULL; return ajDomDocumentCreateElementC(doc, tagname->Ptr); } ID ajDomDocumentCreateElementC TY public MO ajdom LB core XX DE Create a document element XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r tagname const char* PD tagname PX RT AjPDomElement RD element RX // AjPDomElement ajDomDocumentCreateElementC(AjPDomDocument doc, const char *tagname) { AjPDomElement element = NULL; if(!tagname) return NULL; element = ajDomDocumentCreateNode(doc, ajEDomNodeTypeElement); if(!element) return NULL; element->sub.Element.tagname = ajStrNewC(tagname); element->name = element->sub.Element.tagname; element->attributes = ajDomCreateNodeList(doc); element->attributes->ownerelement = element; return element; } ID ajDomDocumentCreateDocumentFragment TY public MO ajdom LB core XX DE Create an empty document fragment XX PN [1] PA w doc AjPDomDocument PD document PX RT AjPDomDocumentFragment RD fragment RX // AjPDomDocumentFragment ajDomDocumentCreateDocumentFragment(AjPDomDocument doc) { AjPDomDocumentFragment frag = NULL; frag = ajDomDocumentCreateNode(doc, ajEDomNodeTypeDocumentFragment); if(frag) frag->name = ajStrNewC("#document-fragment"); return frag; } ID ajDomDocumentCreateTextNode TY public MO ajdom LB core XX DE Create a text node XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r data const AjPStr PD text PX RT AjPDomText RD text node RX // AjPDomText ajDomDocumentCreateTextNode(AjPDomDocument doc, const AjPStr data) { if(!data) return NULL; return ajDomDocumentCreateTextNodeC(doc, data->Ptr); } ID ajDomDocumentCreateTextNodeC TY public MO ajdom LB core XX DE Create a text node XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r data const char* PD text PX RT AjPDomText RD text node RX // AjPDomText ajDomDocumentCreateTextNodeC(AjPDomDocument doc, const char *data) { AjPDomText text = NULL; text = ajDomDocumentCreateNode(doc, ajEDomNodeTypeText); if(!text) return NULL; text->name = ajStrNewC("#text"); text->sub.CharacterData.data = ajStrNewC(data); text->value = text->sub.CharacterData.data; text->sub.CharacterData.length = ajStrGetLen(text->value); return text; } ID ajDomDocumentCreateComment TY public MO ajdom LB core XX DE Create a comment node XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r data const AjPStr PD text PX RT AjPDomComment RD comment node RX // AjPDomComment ajDomDocumentCreateComment(AjPDomDocument doc, const AjPStr data) { if(!data) return NULL; return ajDomDocumentCreateCommentC(doc, data->Ptr); } ID ajDomDocumentCreateCommentC TY public MO ajdom LB core XX DE Create a comment node XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r data const char* PD text PX RT AjPDomComment RD comment node RX // AjPDomComment ajDomDocumentCreateCommentC(AjPDomDocument doc, const char *data) { AjPDomComment comment = NULL; comment = ajDomDocumentCreateNode(doc, ajEDomNodeTypeComment); if(!comment) return NULL; comment->name = ajStrNewC("#comment"); comment->sub.CharacterData.data = ajStrNewC(data); comment->value = comment->sub.CharacterData.data; comment->sub.CharacterData.length = ajStrGetLen(comment->value); return comment; } ID ajDomDocumentCreateCDATASection TY public MO ajdom LB core XX DE Create a CDATA section XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r data const AjPStr PD text PX RT AjPDomCDATASection RD comment node RX // AjPDomCDATASection ajDomDocumentCreateCDATASection(AjPDomDocument doc, const AjPStr data) { if(!data) return NULL; return ajDomDocumentCreateCDATASectionC(doc, data->Ptr); } ID ajDomDocumentCreateCDATASectionC TY public MO ajdom LB core XX DE Create a CDATA section XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r data const char* PD text PX RT AjPDomCDATASection RD comment node RX // AjPDomCDATASection ajDomDocumentCreateCDATASectionC(AjPDomDocument doc, const char *data) { AjPDomCDATASection cdata = NULL; cdata = ajDomDocumentCreateNode(doc, ajEDomNodeTypeCdataSection); if(!cdata) return NULL; cdata->name = ajStrNewC("#cdata-section"); cdata->sub.CharacterData.data = ajStrNewC(data); cdata->value = cdata->sub.CharacterData.data; cdata->sub.CharacterData.length = ajStrGetLen(cdata->value); return cdata; } ID ajDomDocumentCreateAttribute TY public MO ajdom LB core XX DE Create an attribute node XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r name const AjPStr PD text PX RT AjPDomAttr RD attribute node RX // AjPDomAttr ajDomDocumentCreateAttribute(AjPDomDocument doc, const AjPStr name) { AjPDomAttr attr = NULL; attr = ajDomDocumentCreateNode(doc, ajEDomNodeTypeAttribute); if(!attr) return NULL; attr->sub.Attr.name = ajStrNewS(name); attr->name = attr->sub.Attr.name; attr->sub.Attr.value = ajStrNewC(""); attr->value = attr->sub.Attr.value; attr->sub.Attr.specified = 1; return attr; } ID ajDomDocumentCreateAttributeC TY public MO ajdom LB core XX DE Create an attribute node XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r name const char* PD text PX RT AjPDomAttr RD attribute node RX // AjPDomAttr ajDomDocumentCreateAttributeC(AjPDomDocument doc, const char *name) { AjPDomAttr attr = NULL; attr = ajDomDocumentCreateNode(doc, ajEDomNodeTypeAttribute); if(!attr) return NULL; attr->sub.Attr.name = ajStrNewC(name); attr->name = attr->sub.Attr.name; attr->sub.Attr.value = ajStrNewC(""); attr->value = attr->sub.Attr.value; attr->sub.Attr.specified = 1; return attr; } ID ajDomDocumentCreateEntityReference TY public MO ajdom LB core XX DE Create an entity reference node XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r name const AjPStr PD text PX RT AjPDomEntityReference RD entity reference node RX // AjPDomEntityReference ajDomDocumentCreateEntityReference(AjPDomDocument doc, const AjPStr name) { if(!name) return NULL; return ajDomDocumentCreateEntityReferenceC(doc, name->Ptr); } ID ajDomDocumentCreateEntityReferenceC TY public MO ajdom LB core XX DE Create an entity reference node XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r name const char* PD text PX RT AjPDomEntityReference RD entity reference node RX // AjPDomEntityReference ajDomDocumentCreateEntityReferenceC(AjPDomDocument doc, const char *name) { AjPDomEntityReference eref = NULL; eref = ajDomDocumentCreateNode(doc, ajEDomNodeTypeEntityReference); if(!eref) return NULL; eref->name = ajStrNewC(name); return eref; } ID ajDomDocumentCreateProcessingInstruction TY public MO ajdom LB core XX DE Create a processing instruction node XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r target const AjPStr PD target PX PN [3] PA r data const AjPStr PD data PX RT AjPDomPi RD entity reference node RX // AjPDomPi ajDomDocumentCreateProcessingInstruction(AjPDomDocument doc, const AjPStr target, const AjPStr data) { if(!target || !data) return NULL; return ajDomDocumentCreateProcessingInstructionC(doc, target->Ptr, data->Ptr); } ID ajDomDocumentCreateProcessingInstructionC TY public MO ajdom LB core XX DE Create a processing instruction node XX PN [1] PA w doc AjPDomDocument PD document PX PN [2] PA r target const char* PD target PX PN [3] PA r data const char* PD data PX RT AjPDomPi RD entity reference node RX // AjPDomPi ajDomDocumentCreateProcessingInstructionC(AjPDomDocument doc, const char *target, const char *data) { AjPDomPi pin = NULL; pin = ajDomDocumentCreateNode(doc, ajEDomNodeTypeProcessingInstruction); if(pin) { pin->sub.ProcessingInstruction.target = ajStrNewC(target); /* AJB: dirty */ pin->name = pin->sub.ProcessingInstruction.target; pin->sub.ProcessingInstruction.data = ajStrNewC(data); /* AJB: dirty */ pin->value = pin->sub.ProcessingInstruction.data; } return pin; } ID ajDomDocumentGetElementsByTagName TY public MO ajdom LB core XX DE Perform a pre-order traversal of the entire document. Return a node list DE of the elements matching tagname in the order in which they are found. XX PN [1] PA u doc AjPDomDocument PD document PX PN [2] PA r name const AjPStr PD name PX RT AjPDomNodeList RD node list RX // AjPDomNodeList ajDomDocumentGetElementsByTagName(AjPDomDocument doc, const AjPStr name) { AjPDomNodeList list = NULL; if(doc && doc->type == ajEDomNodeTypeDocumentNode && name && (list = ajDomCreateNodeList(doc))) { domTraverse(list, doc->sub.Document.documentelement, name); return list; } return NULL; } ID ajDomDocumentGetElementsByTagNameC TY public MO ajdom LB core XX DE Perform a pre-order traversal of the entire document. Return a node list DE of the elements matching tagname in the order in which they are found. XX PN [1] PA u doc AjPDomDocument PD document PX PN [2] PA r name const char* PD name PX RT AjPDomNodeList RD node list RX // AjPDomNodeList ajDomDocumentGetElementsByTagNameC(AjPDomDocument doc, const char *name) { AjPDomNodeList list = NULL; if(doc && doc->type == ajEDomNodeTypeDocumentNode && name && (list = ajDomCreateNodeList(doc))) { domTraverseC(list, doc->sub.Document.documentelement, name); return list; } return NULL; } ID ajDomDocumentGetDoctype TY public MO ajdom LB core XX DE Get document type XX PN [1] PA r doc const AjPDomDocument PD document PX RT AjPDomDocumentType RD doctype RX // AjPDomDocumentType ajDomDocumentGetDoctype(const AjPDomDocument doc) { return doc ? doc->sub.Document.doctype : NULL; } ID ajDomDocumentGetDocumentElement TY public MO ajdom LB core XX DE Get the root element of the document tree. The root element is also DE accessible through the child nodes node list member, however the children DE of a document may also be processing instructions, document type nodes, DE and comments which may precede the document element in the list. XX PN [1] PA r doc const AjPDomDocument PD document PX RT AjPDomElement RD root element RX // AjPDomElement ajDomDocumentGetDocumentElement(const AjPDomDocument doc) { return doc ? doc->sub.Document.documentelement : NULL; } ID ajDomPrintNode TY public MO ajdom LB core XX DE Print a node with indentation XX PN [1] PA r node const AjPDomNode PD node PX PN [2] PA r indent ajint PD indentation PX RT void RD RX // void ajDomPrintNode(const AjPDomNode node, ajint indent) { AjPDomNode n; ajint i; if(!node) { ajFmtPrint("Node was null\n"); return; } for(i = 0; i < indent; ++i) ajFmtPrint(" "); ajFmtPrint("%s: %S=%S\n", domKNodeinfo[node->type], node->name, node->value); if(node->type == ajEDomNodeTypeElement && node->attributes->length) { ajFmtPrint(" "); n = ajDomNodeMapItem(node->attributes, 0); ajFmtPrint("%S=%S", n->name, n->value); for(i = 1; i < node->attributes->length; ++i) { n = ajDomNodeMapItem(node->attributes, i); ajFmtPrint(",%S=%S", n->name, n->value); } ajFmtPrint("\n"); for(i = 0; i < indent; ++i) ajFmtPrint(" "); } for(n = node->firstchild; n; n = n->nextsibling) ajDomPrintNode(n, indent + 1); return; } ID ajDomPrintNode2 TY public MO ajdom LB core XX DE Print a node with zero indentation XX PN [1] PA r node const AjPDomNode PD node PX RT void RD RX // void ajDomPrintNode2(const AjPDomNode node) { ajFmtPrint("\n"); ajDomPrintNode(node, 0); return; } ID ajDomNodePrintNode TY public MO ajdom LB core XX DE Print a node internals XX PN [1] PA r node const AjPDomNode PD node PX RT void RD RX // void ajDomNodePrintNode(const AjPDomNode node) { if(!node) { ajFmtPrint("Node was null\n"); return; } ajFmtPrint("\nName=%S,Value=%S,", node->name, node->value); ajFmtPrint("\n\tType=%d", node->type); ajFmtPrint(",Parentnode->name=%S,Firstchild->name=%S", node->parentnode->name, node->firstchild->name); ajFmtPrint(",Lastchild->name=%S,\n\tChildnodes->length=%d", node->lastchild->name, (!node->childnodes ? 0 : node->childnodes->length)); ajFmtPrint(",Previoussibling->name=%S,Nextsibling->name=%S," "Attributes->length=%d\n", node->previoussibling->name, node->nextsibling->name, (!node->attributes ? 0 : node->attributes->length)); return; } ID ajDomNodeInsertBefore TY public MO ajdom LB core XX DE Insert newchild into node directly before the existing child refchild. DE If refchild is a null pointer, newchild will be appended to the list. DE If newchild is a fragment node, all children are moved into this node DE in the same order before refchild. DE If newchild is already in the list it will first be removed. XX PN [1] PA w node AjPDomNode PD node PX PN [2] PA u newchild AjPDomNode PD node to insert PX PN [3] PA u refchild AjPDomNode PD node to insert before PX RT AjPDomNode RD inserted node RX // AjPDomNode ajDomNodeInsertBefore(AjPDomNode node, AjPDomNode newchild, AjPDomNode refchild) { AjPDomNode n = NULL; AjPDomNode nxt = NULL; if(!node || !newchild) return NULL; if(newchild->ownerdocument != node->ownerdocument && newchild->ownerdocument != node) { ajDebug("ajDomNodeInsertBefore: Wrong document\n"); return NULL; } if(refchild && refchild->parentnode != node) { ajDebug("ajDomNodeInsertBefore: Hierarchy error\n"); return NULL; } if(newchild->type == ajEDomNodeTypeDocumentFragment) { for(n = newchild->firstchild; n; n = n->nextsibling) if(AJDOM_CANTDO(node, n) || domIsAncestor(n, node)) { ajDebug("ajDomNodeInsertBefore: Hierarchy Request Error\n"); return NULL; } for(n = newchild->firstchild; n; n = nxt) { nxt = n->nextsibling; if(!domDoRemoveChild(newchild, n)) return NULL; if(!ajDomNodeInsertBefore(node, n, refchild)) { ajDomDocumentDestroyNode(n->ownerdocument, n); return NULL; } } return newchild; } domDoRemoveChild(node, newchild); if(!(ajDomNodeListInsert(node->childnodes, newchild, refchild))) return NULL; if(!node->firstchild) { node->firstchild = newchild; node->lastchild = newchild; newchild->previoussibling = NULL; newchild->nextsibling = NULL; } else if(!refchild) { newchild->previoussibling = node->lastchild; node->lastchild->nextsibling = newchild; node->lastchild = newchild; newchild->nextsibling = NULL; } else { newchild->previoussibling = refchild->previoussibling; newchild->nextsibling = refchild; if(refchild == node->firstchild) { node->firstchild = newchild; newchild->previoussibling = NULL; } else refchild->previoussibling->nextsibling = newchild; refchild->previoussibling = newchild; } newchild->parentnode = node; if(AJDOM_DOCMOD(node, newchild)) node->sub.Document.documentelement = newchild; else if(AJDOM_DOCTYPEMOD(node, newchild)) { node->sub.Document.doctype = newchild; newchild->ownerdocument = node; } domUpdateNode(node); return newchild; } ID ajDomNodeListInsert TY public MO ajdom LB core XX DE Insert newchild into list directly before the existing child refchild. XX PN [1] PA u list AjPDomNodeList PD list PX PN [2] PA u newchild AjPDomNode PD node to insert PX PN [3] PA u refchild AjPDomNode PD node to insert before PX RT AjPDomNodeEntry RD inserted node RX // AjPDomNodeEntry ajDomNodeListInsert(AjPDomNodeList list, AjPDomNode newchild, AjPDomNode refchild) { AjPDomNodeEntry e = NULL; AjPDomNodeEntry s = NULL; if(!list) return NULL; if(list->filter) { ajDebug("ajDomNodeListInsert: Filtered list error\n"); return NULL; } if(refchild) { s = domDoLookupNode(list, refchild); if(!s || s->node != refchild) { ajDebug("ajDomNodeListInsert: not found error\n"); return NULL; } } AJNEW0(e); domAddToMap(list, newchild, e); e->node = newchild; if(!list->length) { list->first = e; list->last = e; } else if(!refchild) { e->prev = list->last; list->last->next = e; list->last = e; } else { e->prev = s->prev; e->next = s; if(s == list->first) list->first = e; else s->prev->next = e; s->prev = e; } ++list->length; if(newchild->type == ajEDomNodeTypeAttribute) newchild->sub.Attr.ownerelement = list->ownerelement; return e; } ID ajDomNodeReplaceChild TY public MO ajdom LB core XX DE Replace oldchild with newchild in the list of children. XX PN [1] PA u node AjPDomNode PD node PX PN [2] PA u newchild AjPDomNode PD node to insert PX PN [3] PA u oldchild AjPDomNode PD node to replace PX RT AjPDomNode RD oldchild node RX // AjPDomNode ajDomNodeReplaceChild(AjPDomNode node, AjPDomNode newchild, AjPDomNode oldchild) { AjPDomNode nxt = NULL; AjPDomNode n = NULL; if(!node || !newchild || !oldchild) return NULL; if(newchild->ownerdocument != node->ownerdocument && newchild->ownerdocument != node) { ajDebug("ajDomNodeReplaceChild: Wrong document\n"); return NULL; } if(!ajDomNodeListExists(node->childnodes, oldchild)) { ajDebug("ajDomNodeReplaceChild: Oldchild not found\n"); return NULL; } if(newchild->type == ajEDomNodeTypeDocumentFragment) { for(n = newchild->firstchild; n; n = n->nextsibling) if(AJDOM_CANTDO(node, n) || domIsAncestor(n, node)) { ajDebug("ajDomNodeReplaceChild: Hierarchy Request Error\n"); return NULL; } for(n = newchild->firstchild; n; n = nxt) { nxt = n->nextsibling; if(!domDoRemoveChild(newchild, n)) return NULL; if(!ajDomNodeInsertBefore(node, n, oldchild)) { ajDomDocumentDestroyNode(n->ownerdocument, n); return NULL; } } if(!domDoRemoveChild(node, oldchild)) return NULL; return oldchild; } if(AJDOM_CANTDO(node, newchild) || domIsAncestor(newchild, node)) { ajDebug("ajDomNodeReplaceChild: Hierarchy Request Error\n"); return NULL; } domDoRemoveChild(node, newchild); if(!ajDomNodeListExists(node->childnodes, oldchild)) return NULL; ajDomNodeListReplace(node->childnodes, newchild, oldchild); node->firstchild = node->childnodes->first->node; node->lastchild = node->childnodes->last->node; if((newchild->previoussibling = oldchild->previoussibling)) newchild->previoussibling->nextsibling = newchild; if((newchild->nextsibling = oldchild->nextsibling)) newchild->nextsibling->previoussibling = newchild; newchild->parentnode = node; oldchild->parentnode = NULL; oldchild->previoussibling = NULL; oldchild->nextsibling = NULL; if(AJDOM_DOCMOD(node, newchild)) node->sub.Document.documentelement = newchild; else if(AJDOM_DOCTYPEMOD(node, newchild)) { node->sub.Document.doctype = newchild; newchild->ownerdocument = node; } domUpdateNode(node); return oldchild; } ID ajDomNodeListReplace TY public MO ajdom LB core XX DE Replace oldchild with newchild in a node list. XX PN [1] PA u list AjPDomNodeList PD list PX PN [2] PA u newchild AjPDomNode PD node to insert PX PN [3] PA u oldchild AjPDomNode PD node to replace PX RT AjPDomNodeEntry RD inserted node RX // AjPDomNodeEntry ajDomNodeListReplace(AjPDomNodeList list, AjPDomNode newchild, AjPDomNode oldchild) { AjPDomNodeEntry e = NULL; if(!list) return NULL; if(list->filter) { ajDebug("ajDomNodeListReplace: Filtered list error\n"); return NULL; } e = domDoLookupNode(list, oldchild); if(!e) { ajDebug("ajDomNodeListReplace: Not found error\n"); return NULL; } domRemoveFromMap(list, oldchild); domAddToMap(list, newchild, e); e->node = newchild; if(oldchild->type == ajEDomNodeTypeAttribute) oldchild->sub.Attr.ownerelement = NULL; return e; } ID domNodeCloneNode TY static MO ajdom LB core XX DE Low level clone node XX PN [1] PA u ownerdocument AjPDomDocument PD owner document for clone PX PN [2] PA r node const AjPDomNode PD node to clone PX PN [3] PA r deep AjBool PD do a deep clone PX RT AjPDomNode RD clone RX // static AjPDomNode domNodeCloneNode(AjPDomDocument ownerdocument, const AjPDomNode node, AjBool deep) { AjPDomNode clone = NULL; AjPDomNode ntmp = NULL; AjPDomNode ctmp = NULL; AjPDomNodeEntry e = NULL; switch(node->type) { case ajEDomNodeTypeElement: clone = ajDomDocumentCreateElement(ownerdocument, node->name); if(clone) for(e = node->attributes->first; e; e = e->next) if(!(ctmp = domNodeCloneNode(ownerdocument, e->node, deep)) || !ajDomNodeListAppend(clone->attributes, ctmp)) { ajDomDocumentDestroyNode(clone->ownerdocument, ctmp); ajDomDocumentDestroyNode(clone->ownerdocument, clone); return NULL; } break; case ajEDomNodeTypeAttribute: if((clone = ajDomDocumentCreateAttribute(ownerdocument, node->name))) { clone->sub.Attr.specified = node->sub.Attr.specified; if(!node->value) return NULL; ajStrAssignS(&clone->value, node->value); /* AJB: dirty */ clone->sub.Attr.value = clone->value; } break; case ajEDomNodeTypeComment: clone = ajDomDocumentCreateComment(ownerdocument, node->value); break; case ajEDomNodeTypeText: clone = ajDomDocumentCreateTextNode(ownerdocument, node->value); break; case ajEDomNodeTypeCdataSection: clone = ajDomDocumentCreateCDATASection(ownerdocument, node->value); break; case ajEDomNodeTypeDocumentFragment: clone = ajDomDocumentCreateDocumentFragment(ownerdocument); break; case ajEDomNodeTypeDocumentNode: clone = ownerdocument; break; case ajEDomNodeTypeProcessingInstruction: clone = ajDomDocumentCreateProcessingInstruction( ownerdocument, node->sub.ProcessingInstruction.target, node->sub.ProcessingInstruction.data); break; case ajEDomNodeTypeEntityNode: if((clone = ajDomDocumentCreateNode(ownerdocument, ajEDomNodeTypeEntityNode))) { clone->name = ajStrNewS(node->name); clone->value = ajStrNewS(node->value); if(node->sub.Entity.publicid) clone->sub.Entity.publicid = ajStrNewS(node->sub.Entity. publicid); if(node->sub.Entity.systemid) clone->sub.Entity.systemid = ajStrNewS(node->sub.Entity. systemid); if(node->sub.Entity.notationname) clone->sub.Entity.notationname = ajStrNewS(node->sub. Entity. notationname); } break; case ajEDomNodeTypeNotation: if((clone = ajDomDocumentCreateNode(ownerdocument, ajEDomNodeTypeNotation))) { clone->name = ajStrNewS(node->name); if(node->sub.Notation.publicid) clone->sub.Notation.publicid = ajStrNewS(node->sub. Notation. publicid); if(node->sub.Notation.systemid) clone->sub.Notation.systemid = ajStrNewS(node->sub. Notation. systemid); } break; case ajEDomNodeTypeDocumentType: if((clone = ajDomImplementationCreateDocumentType(node->name, NULL, NULL))) { if(node->sub.DocumentType.publicid) clone->sub.DocumentType.publicid = ajStrNewS(node->sub. DocumentType. publicid); if(node->sub.DocumentType.systemid) clone->sub.DocumentType.systemid = ajStrNewS(node->sub. DocumentType. systemid); } ownerdocument->sub.Document.doctype = clone; clone->ownerdocument = ownerdocument; break; case ajEDomNodeTypeEntityReference: ajDebug("Entity reference clone not implemented\n"); return NULL; default: ajDebug("domNodeCloneNode got unexpected " "AJAX DOM Node type %d.\n", node->type); } if(deep && clone && node->childnodes) for(ntmp = node->firstchild; ntmp; ntmp = ntmp->nextsibling) { ctmp = domNodeCloneNode(ownerdocument, ntmp, ajTrue); if(!ctmp || !ajDomNodeAppendChild(clone, ctmp)) { ajDomDocumentDestroyNode(clone->ownerdocument, ctmp); ajDomDocumentDestroyNode(clone->ownerdocument, clone); return NULL; } } return clone; } ID ajDomNodeCloneNode TY public MO ajdom LB core XX DE Clone node XX PN [1] PA u node AjPDomNode PD node to clone PX PN [2] PA r deep AjBool PD do a deep clone PX RT AjPDomNode RD clone RX // AjPDomNode ajDomNodeCloneNode(AjPDomNode node, AjBool deep) { AjPDomDocument doc = NULL; if(!node) return NULL; if(node->type == ajEDomNodeTypeDocumentNode) { if(!(doc = ajDomImplementationCreateDocument(NULL, NULL, NULL))) return NULL; return domNodeCloneNode(doc, node, deep); } return domNodeCloneNode(node->ownerdocument, node, deep); } ID domWriteEncoded TY static MO ajdom LB core XX DE Write a string using entity substitutions XX PN [1] PA r s const AjPStr PD string PX PN [2] PA w outf AjPFile PD output file PX RT void RD RX // static void domWriteEncoded(const AjPStr s, AjPFile outf) { size_t len; const char *p; AjPStr tmp = NULL; tmp = ajStrNew(); p = ajStrGetPtr(s); while(*p) { len = strcspn(p, "<>&\""); if(len) { ajStrAssignSubC(&tmp, p, 0, len - 1); ajFmtPrintF(outf, "%S", tmp); p += len; } switch (*p) { case '\0': break; case '<': ajFmtPrintF(outf, "<"); break; case '>': ajFmtPrintF(outf, ">"); break; case '&': ajFmtPrintF(outf, "'"); break; case '"': ajFmtPrintF(outf, """); break; default: break; }; if(*p) ++p; } ajStrDel(&tmp); return; } ID ajDomNodeHasChildNodes TY public MO ajdom LB core XX DE Check whether a node has any children XX PN [1] PA r node const AjPDomNode PD node to clone PX RT AjBool RD true if children RX // AjBool ajDomNodeHasChildNodes(const AjPDomNode node) { return node != NULL && node->firstchild; } ID ajDomWrite TY public MO ajdom LB core XX DE Write XML from memory XX PN [1] PA r node const AjPDomDocument PD document to write PX PN [2] PA u outf AjPFile PD output file PX RT ajint RD zero OK, negative if error RX // ajint ajDomWrite(const AjPDomDocument node, AjPFile outf) { AjPDomNodeEntry e = NULL; AjPDomNode c = NULL; if(!node || !outf) return -1; switch(node->type) { case ajEDomNodeTypeElement: ajFmtPrintF(outf, "<"); ajFmtPrintF(outf, "%S", node->name); for(e = node->attributes->first; e; e = e->next) { ajFmtPrintF(outf, " %S=\"", e->node->name); domWriteEncoded(e->node->value, outf); ajFmtPrintF(outf, "\""); } if(ajDomNodeHasChildNodes(node)) { ajFmtPrintF(outf, ">"); for(c = node->firstchild; c; c = c->nextsibling) if(ajDomWrite(c, outf) == -1) return -1; ajFmtPrintF(outf, "name); ajFmtPrintF(outf, ">"); } else ajFmtPrintF(outf, "/>"); break; case ajEDomNodeTypeAttribute: break; case ajEDomNodeTypeText: domWriteEncoded(node->value, outf); break; case ajEDomNodeTypeCdataSection: break; case ajEDomNodeTypeEntityReference: break; case ajEDomNodeTypeNotation: ajFmtPrintF(outf, " name); if(node->sub.Notation.publicid) { ajFmtPrintF(outf, " PUBLIC \""); ajFmtPrintF(outf, "%S", node->sub.Notation.publicid); ajFmtPrintF(outf, "\" \""); ajFmtPrintF(outf, "%S", node->sub.Notation.systemid); ajFmtPrintF(outf, "\""); } else if(node->sub.Notation.systemid) { ajFmtPrintF(outf, " SYSTEM \""); ajFmtPrintF(outf, "%S", node->sub.Notation.systemid); ajFmtPrintF(outf, "\""); } ajFmtPrintF(outf, ">"); break; case ajEDomNodeTypeEntityNode: ajFmtPrintF(outf, " name); if(node->value) { ajFmtPrintF(outf, "\""); ajFmtPrintF(outf, "%S", node->value); ajFmtPrintF(outf, "\""); } else { if(node->sub.Entity.publicid) { ajFmtPrintF(outf, " PUBLIC \""); ajFmtPrintF(outf, "%S", node->sub.Entity.publicid); ajFmtPrintF(outf, "\" \""); ajFmtPrintF(outf, "%S", node->sub.Entity.systemid); ajFmtPrintF(outf, "\""); } else if(node->sub.Entity.systemid) { ajFmtPrintF(outf, " SYSTEM \""); ajFmtPrintF(outf, "%S", node->sub.Entity.systemid); ajFmtPrintF(outf, "\""); } if(node->sub.Entity.notationname) { ajFmtPrintF(outf, " NDATA "); ajFmtPrintF(outf, "%S", node->sub.Entity.notationname); } } ajFmtPrintF(outf, ">"); break; case ajEDomNodeTypeProcessingInstruction: ajFmtPrintF(outf, "sub.ProcessingInstruction.target); domWriteEncoded(node->sub.ProcessingInstruction.data, outf); ajFmtPrintF(outf, "?>"); break; case ajEDomNodeTypeComment: ajFmtPrintF(outf, ""); break; case ajEDomNodeTypeDocumentNode: ajFmtPrintF(outf, "sub.Document.version) ajFmtPrintF(outf, "%S", node->sub.Document.version); else ajFmtPrintF(outf, "1.0"); ajFmtPrintF(outf, "\""); if(node->sub.Document.standalone) ajFmtPrintF(outf, " standalone=\"yes\""); ajFmtPrintF(outf, "?>\n"); for(c=node->firstchild; c; c=c->nextsibling) if(ajDomWrite(c, outf) == -1) return -1; ajFmtPrintF(outf, "\n"); break; case ajEDomNodeTypeDocumentType: ajFmtPrintF(outf, "\nsub.DocumentType.name); if(node->sub.DocumentType.systemid) { ajFmtPrintF(outf, " SYSTEM \""); ajFmtPrintF(outf, "%S", node->sub.DocumentType.systemid); ajFmtPrintF(outf, "\""); } else if(node->sub.DocumentType.publicid) { ajFmtPrintF(outf, " PUBLIC \""); ajFmtPrintF(outf, "%S", node->sub.DocumentType.publicid); ajFmtPrintF(outf, "\""); } if(node->sub.DocumentType.internalsubset) { ajFmtPrintF(outf, " [\n"); ajFmtPrintF(outf, "%S", node->sub.DocumentType.internalsubset); ajFmtPrintF(outf, "]>\n"); } else ajFmtPrintF(outf, ">\n"); break; case ajEDomNodeTypeDocumentFragment: break; default: ajDebug("ajDomWrite got unexpected " "AJAX DOM Node type %d.\n", node->type); } return 0; } ID ajDomWriteIndent TY public MO ajdom LB core XX DE Write XML from memory XX PN [1] PA r node const AjPDomDocument PD document to write PX PN [2] PA u outf AjPFile PD output file PX PN [3] PA r indent ajint PD indent level PX RT ajint RD zero OK, negative if error RX // ajint ajDomWriteIndent(const AjPDomDocument node, AjPFile outf, ajint indent) { AjPDomNodeEntry e = NULL; AjPDomNode c = NULL; ajint i; if(!node || !outf) return -1; for(i = 0; i < indent; ++i) ajFmtPrintF(outf, " "); switch(node->type) { case ajEDomNodeTypeElement: ajFmtPrintF(outf, "<"); ajFmtPrintF(outf, "%S", node->name); for(e = node->attributes->first; e; e = e->next) { ajFmtPrintF(outf, " %S=\"", e->node->name); domWriteEncoded(e->node->value, outf); ajFmtPrintF(outf, "\""); } if(ajDomNodeHasChildNodes(node)) { ajFmtPrintF(outf, ">\n"); for(c = node->firstchild; c; c = c->nextsibling) if(ajDomWriteIndent(c, outf, indent + 2) == -1) return -1; for(i = 0; i < indent; ++i) ajFmtPrintF(outf, " "); ajFmtPrintF(outf, "name); ajFmtPrintF(outf, ">\n"); } else ajFmtPrintF(outf, "/>\n"); break; case ajEDomNodeTypeAttribute: break; case ajEDomNodeTypeText: domWriteEncoded(node->value, outf); break; case ajEDomNodeTypeCdataSection: break; case ajEDomNodeTypeEntityReference: break; case ajEDomNodeTypeNotation: ajFmtPrintF(outf, " name); if(node->sub.Notation.publicid) { ajFmtPrintF(outf, " PUBLIC \""); ajFmtPrintF(outf, "%S", node->sub.Notation.publicid); ajFmtPrintF(outf, "\" \""); ajFmtPrintF(outf, "%S", node->sub.Notation.systemid); ajFmtPrintF(outf, "\""); } else if(node->sub.Notation.systemid) { ajFmtPrintF(outf, " SYSTEM \""); ajFmtPrintF(outf, "%S", node->sub.Notation.systemid); ajFmtPrintF(outf, "\""); } ajFmtPrintF(outf, ">\n"); break; case ajEDomNodeTypeEntityNode: ajFmtPrintF(outf, " name); if(node->value) { ajFmtPrintF(outf, "\""); ajFmtPrintF(outf, "%S", node->value); ajFmtPrintF(outf, "\""); } else { if(node->sub.Entity.publicid) { ajFmtPrintF(outf, " PUBLIC \""); ajFmtPrintF(outf, "%S", node->sub.Entity.publicid); ajFmtPrintF(outf, "\" \""); ajFmtPrintF(outf, "%S", node->sub.Entity.systemid); ajFmtPrintF(outf, "\""); } else if(node->sub.Entity.systemid) { ajFmtPrintF(outf, " SYSTEM \""); ajFmtPrintF(outf, "%S", node->sub.Entity.systemid); ajFmtPrintF(outf, "\""); } if(node->sub.Entity.notationname) { ajFmtPrintF(outf, " NDATA "); ajFmtPrintF(outf, "%S", node->sub.Entity.notationname); } } ajFmtPrintF(outf, ">\n"); break; case ajEDomNodeTypeProcessingInstruction: ajFmtPrintF(outf, "sub.ProcessingInstruction.target); domWriteEncoded(node->sub.ProcessingInstruction.data, outf); ajFmtPrintF(outf, "?>\n"); break; case ajEDomNodeTypeComment: ajFmtPrintF(outf, "\n"); break; case ajEDomNodeTypeDocumentNode: ajFmtPrintF(outf, "sub.Document.version) ajFmtPrintF(outf, "%S", node->sub.Document.version); else ajFmtPrintF(outf, "1.0"); ajFmtPrintF(outf, "\""); if(node->sub.Document.standalone) ajFmtPrintF(outf, " standalone=\"yes\""); ajFmtPrintF(outf, "?>\n"); for(c = node->firstchild; c; c = c->nextsibling) if(ajDomWriteIndent(c, outf, indent + 2) == -1) return -1; ajFmtPrintF(outf, "\n"); break; case ajEDomNodeTypeDocumentType: ajFmtPrintF(outf, "\nsub.DocumentType.name); if(node->sub.DocumentType.systemid) { ajFmtPrintF(outf, " SYSTEM \""); ajFmtPrintF(outf, "%S", node->sub.DocumentType.systemid); ajFmtPrintF(outf, "\""); } else if(node->sub.DocumentType.publicid) { ajFmtPrintF(outf, " PUBLIC \""); ajFmtPrintF(outf, "%S", node->sub.DocumentType.publicid); ajFmtPrintF(outf, "\""); } if(node->sub.DocumentType.internalsubset) { ajFmtPrintF(outf, " [\n"); ajFmtPrintF(outf, "%S", node->sub.DocumentType.internalsubset); ajFmtPrintF(outf, "]>\n"); } else ajFmtPrintF(outf, ">\n"); break; case ajEDomNodeTypeDocumentFragment: break; default: ajDebug("ajDomWriteIndent got unexpected " "AJAX DOM Node type %d.\n", node->type); } return 0; } ID domUserdataNew TY static MO ajdom LB core XX DE Create userdata object XX RT AjPDomUserdata RD DOM Userdata for expat XML reading RX // static AjPDomUserdata domUserdataNew(void) { AjPDomUserdata ret = NULL; AJNEW0(ret); ret->Buffer = ajStrNew(); ret->Stack = ajListNew(); ret->Cdata = ajFalse; return ret; } ID domUserdataDel TY static MO ajdom LB core XX DE Destroy userdata object XX PN [1] PA d thys AjPDomUserdata* PD Userdata object pointer PX RT void RD RX // static void domUserdataDel(AjPDomUserdata *thys) { AjPDomUserdata pthis = NULL; if(!thys) return; pthis = *thys; if(!pthis) return; ajStrDel(&pthis->Buffer); ajListFree(&pthis->Stack); AJFREE(pthis); *thys = NULL; return; } ID domExpatStart TY static MO ajdom LB core XX DE XML reading Expat start function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r name const XML_Char* PD Name PX PN [3] PA r atts const XML_Char** PD Attributes PX RT void RD RX // static void domExpatStart(void *udata, const XML_Char *name, const XML_Char **atts) { AjPDomUserdata userdata = NULL; AjPDomDocument parent = NULL; AjPDomDocument child = NULL; ajint i = 0; AjPStr sname = NULL; if(!udata || !name || !atts) return; userdata = (AjPDomUserdata) udata; ajListPeek(userdata->Stack, (void **) &parent); if(!parent) return; ajStrAssignC(&userdata->Buffer, name); if(!(child = ajDomDocumentCreateElement(parent->ownerdocument, userdata->Buffer))) return; if(!ajDomNodeAppendChild(parent, child)) return; sname = ajStrNew(); for(i = 0; atts[i]; i += 2) { ajStrAssignC(&sname, atts[i]); ajStrAssignC(&userdata->Buffer, atts[i + 1]); ajDomElementSetAttribute(child, sname, userdata->Buffer); } ajListPush(userdata->Stack, (void *) child); ajStrDel(&sname); return; } ID domExpatEnd TY static MO ajdom LB core XX DE XML reading Expat end function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r name const XML_Char* PD Name PX RT void RD RX // static void domExpatEnd(void *udata, const XML_Char *name) { AjPDomUserdata userdata = NULL; AjPDomDocument node = NULL; userdata = (AjPDomUserdata) udata; ajListPop(userdata->Stack, (void **)&node); (void) name; return; } ID domExpatChardata TY static MO ajdom LB core XX DE XML reading Expat chardata function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r str const XML_Char* PD Char data PX PN [3] PA r len int PD Length PX RT void RD RX // static void domExpatChardata(void *udata, const XML_Char *str, int len) { AjPDomUserdata userdata = NULL; AjPDomDocument parent = NULL; AjPDomText txt = NULL; if(!udata || !str || !len) return; /* ** Care needs to be exercised with this function. ** First, it may need several callbacks to recover long strings. ** The operation might need to be converted to an append ** and only create nodes if the strings are complete ** ** Its main use is to maintain the whitepace formatting in ** the original XML file and could, in many cases, be ** ignored. */ userdata = (AjPDomUserdata) udata; ajListPeek(userdata->Stack, (void **) &parent); if(!parent) return; ajStrAssignLenC(&userdata->Buffer, str, len); if(userdata->Cdata) { if(!(txt = ajDomDocumentCreateCDATASection(parent->ownerdocument, userdata->Buffer))) return; } else { if(!(txt = ajDomDocumentCreateTextNode(parent->ownerdocument, userdata->Buffer))) return; } ajDomNodeAppendChild(parent, txt); return; } ID domExpatCdataStart TY static MO ajdom LB core XX DE XML reading Expat CDATA start function XX PN [1] PA u udata void* PD Userdata pointer PX RT void RD RX // static void domExpatCdataStart(void *udata) { AjPDomUserdata userdata = NULL; if(!udata) return; userdata = (AjPDomUserdata) udata; userdata->Cdata = ajTrue; return; } ID domExpatCdataEnd TY static MO ajdom LB core XX DE XML reading Expat CDATA end function XX PN [1] PA u udata void* PD Userdata pointer PX RT void RD RX // static void domExpatCdataEnd(void *udata) { AjPDomUserdata userdata = NULL; if(!udata) return; userdata = (AjPDomUserdata) udata; userdata->Cdata = ajFalse; return; } ID domExpatComment TY static MO ajdom LB core XX DE XML reading Expat comment function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r str const XML_Char* PD Char data PX RT void RD RX // static void domExpatComment(void *udata, const XML_Char *str) { AjPDomUserdata userdata = NULL; AjPDomDocument parent = NULL; AjPDomComment comment = NULL; if(!udata || !str) return; userdata = (AjPDomUserdata) udata; ajListPeek(userdata->Stack, (void **) &parent); if(!parent) return; ajStrAssignC(&userdata->Buffer, str); if((comment = ajDomDocumentCreateComment(parent->ownerdocument, userdata->Buffer))) ajDomNodeAppendChild(parent, comment); return; } ID domExpatProcessing TY static MO ajdom LB core XX DE XML reading Expat processing instruction function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r target const XML_Char* PD Char data PX PN [3] PA r str const XML_Char* PD Char data PX RT void RD RX // static void domExpatProcessing(void *udata, const XML_Char *target, const XML_Char *str) { AjPDomUserdata userdata = NULL; AjPDomDocument parent = NULL; AjPDomPi pi = NULL; AjPStr t = NULL; if(!udata || !target || !!str) return; userdata = (AjPDomUserdata) udata; ajListPeek(userdata->Stack, (void **) &parent); if(!parent) return; t = ajStrNew(); ajStrAssignC(&t, target); ajStrAssignC(&userdata->Buffer, str); if((pi = ajDomDocumentCreateProcessingInstruction(parent->ownerdocument, t, userdata->Buffer))) ajDomNodeAppendChild(parent, pi); ajStrDel(&t); return; } ID domExpatXmlDecl TY static MO ajdom LB core XX DE XML reading Expat XML declaration function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r version const XML_Char* PD Version PX PN [3] PA r encoding const XML_Char* PD Encoding PX PN [4] PA r standalone int PD Standalone PX RT void RD RX // static void domExpatXmlDecl(void *udata, const XML_Char *version, const XML_Char *encoding, int standalone) { AjPDomUserdata userdata = NULL; AjPDomDocument document = NULL; if(!udata) return; userdata = (AjPDomUserdata) udata; ajListPeek(userdata->Stack, (void **) &document); if(!document) return; if(!document->sub.Document.version) document->sub.Document.version = ajStrNew(); if(!document->sub.Document.encoding) document->sub.Document.encoding = ajStrNew(); if(version) ajStrAssignC(&document->sub.Document.version, version); if(encoding) ajStrAssignC(&document->sub.Document.encoding, encoding); document->sub.Document.standalone = standalone; return; } ID domExpatDoctypeStart TY static MO ajdom LB core XX DE XML reading Expat Doctype start function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r doctypename const XML_Char* PD Doctype name PX PN [3] PA r sysid const XML_Char* PD Sysid PX PN [4] PA r pubid const XML_Char* PD Sysid PX PN [5] PA r hasinternalsubset int PD Internal subset flag PX RT void RD RX // static void domExpatDoctypeStart(void *udata, const XML_Char *doctypename, const XML_Char *sysid, const XML_Char *pubid, int hasinternalsubset) { AjPDomUserdata userdata = NULL; AjPDomDocument doc = NULL; AjPDomDocumentType type = NULL; (void) hasinternalsubset; if(!udata) return; userdata = (AjPDomUserdata) udata; if(!ajListPeek(userdata->Stack, (void **) &doc)) return; if(!doc) return; if(doc->sub.Document.doctype) { ajDebug("domExpatDoctypeStart: doctype already exists"); return; } ajStrAssignC(&userdata->Buffer, doctypename); if(!(type = ajDomImplementationCreateDocumentType(userdata->Buffer, NULL, NULL))) return; if(sysid) ajStrAssignC(&type->sub.DocumentType.systemid, sysid); if(pubid) ajStrAssignC(&type->sub.DocumentType.publicid, pubid); if(!ajDomNodeAppendChild(doc, type)) return; doc->sub.Document.doctype = type; ajListPush(userdata->Stack, (void *) type); return; } ID domExpatDoctypeEnd TY static MO ajdom LB core XX DE XML reading Expat Doctype end function XX PN [1] PA u udata void* PD Userdata pointer PX RT void RD RX // static void domExpatDoctypeEnd(void *udata) { AjPDomUserdata userdata = NULL; AjPDomDocumentType type = NULL; if(!udata) return; userdata = (AjPDomUserdata) udata; ajListPop(userdata->Stack, (void **)&type); return; } ID domExpatElement TY static MO ajdom LB core XX DE XML reading Expat Element function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r name const XML_Char* PD Name PX PN [3] PA d model XML_Content* PD Model PX RT void RD RX // static void domExpatElement(void *udata, const XML_Char *name, XML_Content *model) { udata = NULL; name = NULL; (void) udata; (void) name; free(model); return; } ID domExpatAttlist TY static MO ajdom LB core XX DE XML reading Expat attribute list function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r name const XML_Char* PD Element name PX PN [3] PA r attname const XML_Char* PD Attribute Name PX PN [4] PA r atttype const XML_Char* PD Attribute Type PX PN [5] PA r deflt const XML_Char* PD Default PX PN [6] PA r isrequired int PD Required flag PX RT void RD RX // static void domExpatAttlist(void *udata, const XML_Char *name, const XML_Char *attname, const XML_Char *atttype, const XML_Char *deflt, int isrequired) { (void) udata; (void) name; (void) attname; (void) atttype; (void) deflt; (void) isrequired; return; } ID domExpatEntity TY static MO ajdom LB core XX DE XML reading Expat entity function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r entityname const XML_Char* PD Entity name PX PN [3] PA r isparam int PD Flag for parameter entity PX PN [4] PA r value const XML_Char* PD Value PX PN [5] PA r lenval int PD Value length PX PN [6] PA r base const XML_Char* PD Base PX PN [7] PA r systemid const XML_Char* PD System ID PX PN [8] PA r publicid const XML_Char* PD Public ID PX PN [9] PA r notname const XML_Char* PD Notation name PX RT void RD RX // static void domExpatEntity(void *udata, const XML_Char *entityname, int isparam, const XML_Char *value, int lenval, const XML_Char *base, const XML_Char *systemid, const XML_Char *publicid, const XML_Char *notname) { AjPDomUserdata userdata = NULL; AjPDomDocumentType doctype = NULL; AjPDomEntity entity = NULL; (void) base; if(isparam) return; if(!udata) return; userdata = (AjPDomUserdata) udata; ajListPop(userdata->Stack, (void **)&doctype); if(!(entity = ajDomDocumentCreateNode(doctype->ownerdocument, ajEDomNodeTypeEntityNode))) return; ajStrAssignC(&entity->name, entityname); if(value) ajStrAssignLenC(&entity->value, value, lenval); if(publicid) ajStrAssignC(&entity->sub.Entity.publicid, publicid); if(systemid) ajStrAssignC(&entity->sub.Entity.systemid, systemid); if(notname) ajStrAssignC(&entity->sub.Entity.notationname, notname); ajDomNodeAppendChild(doctype, entity); return; } ID domExpatNotation TY static MO ajdom LB core XX DE XML reading Expat notation function XX PN [1] PA u udata void* PD Userdata pointer PX PN [2] PA r notname const XML_Char* PD Entity name PX PN [3] PA r base const XML_Char* PD Base PX PN [4] PA r systemid const XML_Char* PD System ID PX PN [5] PA r publicid const XML_Char* PD Public ID PX RT void RD RX // static void domExpatNotation(void *udata, const XML_Char *notname, const XML_Char *base, const XML_Char *systemid, const XML_Char *publicid) { AjPDomUserdata userdata = NULL; AjPDomDocumentType doctype = NULL; AjPDomNotation notation = NULL; (void) base; if(!udata) return; userdata = (AjPDomUserdata) udata; ajListPop(userdata->Stack, (void **)&doctype); if(!(notation = ajDomDocumentCreateNode(doctype->ownerdocument, ajEDomNodeTypeNotation))) return; ajStrAssignC(¬ation->name, notname); if(publicid) ajStrAssignC(¬ation->sub.Notation.publicid, publicid); if(systemid) ajStrAssignC(¬ation->sub.Notation.systemid, systemid); ajDomNodeAppendChild(doctype, notation); return; } ID ajDomReadFp TY public MO ajdom LB core XX DE Read XML into memory from a file pointer XX PN [1] PA u node AjPDomDocument PD document to write PX PN [2] PA u stream FILE* PD stream PX RT ajint RD zero OK, negative if error RX // ajint ajDomReadFp(AjPDomDocument node, FILE *stream) { AjPDomUserdata userdata = NULL; XML_Parser parser = NULL; void *buf = NULL; ajlong n = 0L; ajint ret; ajint done; if(!node || !stream) return -1; parser = XML_ParserCreate(NULL); if(!parser) return -1; userdata = domUserdataNew(); node->ownerdocument = node; ajListPush(userdata->Stack, (void *) node); XML_SetElementHandler(parser, domExpatStart, domExpatEnd); XML_SetCharacterDataHandler(parser, domExpatChardata); XML_SetCdataSectionHandler(parser, domExpatCdataStart, domExpatCdataEnd); XML_SetCommentHandler(parser, domExpatComment); XML_SetProcessingInstructionHandler(parser, domExpatProcessing); XML_SetXmlDeclHandler(parser, domExpatXmlDecl); XML_SetDoctypeDeclHandler(parser, domExpatDoctypeStart, domExpatDoctypeEnd); XML_SetElementDeclHandler(parser, domExpatElement); XML_SetAttlistDeclHandler(parser, domExpatAttlist); XML_SetEntityDeclHandler(parser, domExpatEntity); XML_SetNotationDeclHandler(parser, domExpatNotation); XML_SetUserData(parser, userdata); ret = -1; for(;;) { if(!(buf = XML_GetBuffer(parser, AJXML_BUFSIZ))) break; if(!(n = fread(buf, 1, AJXML_BUFSIZ, stream)) && ferror(stream)) break; if(!XML_ParseBuffer(parser, (int) n , (done = feof(stream)))) { ajDebug("ajDomReadFp: Expat error [%s] at XML line %d", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); break; } if(done) { ret = 0; break; } } ajDomElementNormalise(node->sub.Document.documentelement); domUserdataDel(&userdata); XML_ParserFree(parser); return ret; } ID ajDomReadFilebuff TY public MO ajdom LB core XX DE Read XML into memory from a file pointer XX PN [1] PA u node AjPDomDocument PD document to write PX PN [2] PA u buff AjPFilebuff PD File buffer PX RT ajint RD zero OK, negative if error RX // ajint ajDomReadFilebuff(AjPDomDocument node, AjPFilebuff buff) { AjPDomUserdata userdata = NULL; XML_Parser parser = NULL; int done = 0; ajint len = 0; AjPStr line = NULL; parser = XML_ParserCreate(NULL); if(!parser) return -1; userdata = domUserdataNew(); node->ownerdocument = node; ajListPush(userdata->Stack, (void *) node); XML_SetElementHandler(parser, domExpatStart, domExpatEnd); XML_SetCharacterDataHandler(parser, domExpatChardata); XML_SetCdataSectionHandler(parser, domExpatCdataStart, domExpatCdataEnd); XML_SetCommentHandler(parser, domExpatComment); XML_SetProcessingInstructionHandler(parser, domExpatProcessing); XML_SetXmlDeclHandler(parser, domExpatXmlDecl); XML_SetDoctypeDeclHandler(parser, domExpatDoctypeStart, domExpatDoctypeEnd); XML_SetElementDeclHandler(parser, domExpatElement); XML_SetAttlistDeclHandler(parser, domExpatAttlist); XML_SetEntityDeclHandler(parser, domExpatEntity); XML_SetNotationDeclHandler(parser, domExpatNotation); XML_SetUserData(parser, userdata); line = ajStrNew(); do { ajBuffreadLine(buff, &line); done = ajFilebuffIsEmpty(buff); len = ajStrGetLen(line); if(!XML_Parse(parser, line->Ptr, len, done)) { ajDebug("ajDomReadFilebuff: %s at XML line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); domUserdataDel(&userdata); XML_ParserFree(parser); ajStrDel(&line); return -1; } } while (!done); ajDomElementNormalise(node->sub.Document.documentelement); domUserdataDel(&userdata); XML_ParserFree(parser); ajStrDel(&line); return 0; } ID ajDomReadString TY public MO ajdom LB core XX DE Read XML into memory from a string XX PN [1] PA u node AjPDomDocument PD document to write PX PN [2] PA u str AjPStr PD XML string PX RT ajint RD zero OK, negative if error RX // ajint ajDomReadString(AjPDomDocument node, AjPStr str) { AjPDomUserdata userdata = NULL; XML_Parser parser = NULL; int done = 0; ajint len = 0; parser = XML_ParserCreate(NULL); if(!parser) return -1; userdata = domUserdataNew(); node->ownerdocument = node; ajListPush(userdata->Stack, (void *) node); XML_SetElementHandler(parser, domExpatStart, domExpatEnd); XML_SetCharacterDataHandler(parser, domExpatChardata); XML_SetCdataSectionHandler(parser, domExpatCdataStart, domExpatCdataEnd); XML_SetCommentHandler(parser, domExpatComment); XML_SetProcessingInstructionHandler(parser, domExpatProcessing); XML_SetXmlDeclHandler(parser, domExpatXmlDecl); XML_SetDoctypeDeclHandler(parser, domExpatDoctypeStart, domExpatDoctypeEnd); XML_SetElementDeclHandler(parser, domExpatElement); XML_SetAttlistDeclHandler(parser, domExpatAttlist); XML_SetEntityDeclHandler(parser, domExpatEntity); XML_SetNotationDeclHandler(parser, domExpatNotation); XML_SetUserData(parser, userdata); done = 1; len = ajStrGetLen(str); if(!XML_Parse(parser, str->Ptr, len, done)) { ajDebug("ajDomReadString: %s at XML line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); return -1; } ajDomElementNormalise(node->sub.Document.documentelement); domUserdataDel(&userdata); XML_ParserFree(parser); return 0; } ID ajDomTextGetText TY public MO ajdom LB core XX DE Return the text from a text node XX PN [1] PA w text AjPDomText PD text PX RT AjPStr RD text or NULL RX // AjPStr ajDomTextGetText(AjPDomText text) { if(!text) return NULL; if(text->type != ajEDomNodeTypeText) return NULL; return text->value; } ID ajDomElementGetText TY public MO ajdom LB core XX DE Return the text from an element node XX PN [1] PA w element AjPDomElement PD element PX RT AjPStr RD text or NULL RX // AjPStr ajDomElementGetText(AjPDomElement element) { if(!element) return NULL; if(element->type != ajEDomNodeTypeElement) return NULL; if(!element->firstchild) return NULL; return ajDomTextGetText(element->firstchild); } ID ajDomElementGetNthChildByTagNameC TY public MO ajdom LB core XX DE Returns the nth child of a named element XX PN [1] PA u doc AjPDomDocument PD doc PX PN [2] PA u element AjPDomElement PD element PX PN [3] PA r name const char* PD element name PX PN [4] PA r n ajint PD number of child to get PX RT AjPDomElement RD child or NULL RX // AjPDomElement ajDomElementGetNthChildByTagNameC(AjPDomDocument doc, AjPDomElement element, const char *name, ajint n) { AjPDomNodeList list; ajint len; AjPDomElement ret = NULL; list = ajDomElementGetElementsByTagNameC(element, name); if(!list) return NULL; len = ajDomNodeListGetLen(list); if(!len) { ajDomDocumentDestroyNodeList(doc, list, AJDOMKEEP); return NULL; } if(n < 0 || n > len - 1) { ajDebug("ajDomElementgetNthChildByTagnameC: index out of range"); ajDomDocumentDestroyNodeList(doc, list, AJDOMKEEP); return NULL; } ret = ajDomNodeListItem(list, n); ajDomDocumentDestroyNodeList(doc, list, AJDOMKEEP); return ret; } ID ajDomElementGetFirstChildByTagNameC TY public MO ajdom LB core XX DE Returns the 1st child of a named element XX PN [1] PA u doc AjPDomDocument PD doc PX PN [2] PA u element AjPDomElement PD element PX PN [3] PA r name const char* PD element name PX RT AjPDomElement RD child or NULL RX // AjPDomElement ajDomElementGetFirstChildByTagNameC(AjPDomDocument doc, AjPDomElement element, const char *name) { return ajDomElementGetNthChildByTagNameC(doc, element, name, 0); } ID domainScopclaNew TY static MO ajdomain LB core XX DE Scopcla object constructor. Fore-knowledge of the number of chains is DE required. This is normally called by the domainScopclaReadC / DE domainScopclaRead functions. XX PN [1] PA r chains ajuint PD Number of chains PX RT AjPScopcla RD Pointer to a scopcla object RX // static AjPScopcla domainScopclaNew(ajuint chains) { ajuint i = 0U; AjPScopcla scopcla = NULL; AJNEW0(scopcla); scopcla->Entry = ajStrNew(); scopcla->Pdb = ajStrNew(); scopcla->Sccs = ajStrNew(); if(chains) { scopcla->Chain = ajCharNewRes(chains); AJCNEW0(scopcla->Start, chains); AJCNEW0(scopcla->End, chains); for(i = 0U; i < chains; i++) { scopcla->Start[i] = ajStrNew(); scopcla->End[i] = ajStrNew(); } } scopcla->Number = chains; return scopcla; } ID domainScopdesNew TY static MO ajdomain LB core XX DE Scopdes object constructor. DE DE This is normally called by the domainScopdesReadC / domainScopdesRead DE functions. XX RT AjPScopdes RD Pointer to a scopdes object RX // static AjPScopdes domainScopdesNew(void) { AjPScopdes scopdes = NULL; AJNEW0(scopdes); scopdes->Type = ajStrNew(); scopdes->Sccs = ajStrNew(); scopdes->Entry = ajStrNew(); scopdes->Desc = ajStrNew(); return scopdes; } ID domainScopclaRead TY static MO ajdomain LB core XX DE Read a Scopcla object for a given SCOP domain from the SCOP parsable DE file (dir.cla.scop.txt). XX PN [1] PA u inf AjPFile PD AJAX Input file stream PX PN [2] PA r entry const AjPStr PD Identifier PX RT AjPScopcla RD Scopcla object RX CA new CT AjPScopcla CD Read a Scopcla object for a given SCOP domain CX // static AjPScopcla domainScopclaRead(AjPFile inf, const AjPStr entry) { return domainScopclaReadC(inf, ajStrGetPtr(entry)); } ID domainScopdesRead TY static MO ajdomain LB core XX DE Read a Scopdes object for a given SCOP domain from the SCOP parsable DE file (dir.des.scop.txt). XX PN [1] PA u inf AjPFile PD AJAX Input file stream. PX PN [2] PA r entry const AjPStr PD CATH identifier of domain. PX RT AjPScopdes RD Scopdes object RX CA new CT AjPScopdes CD Read a Scopdes object for a given SCOP domain CX // static AjPScopdes domainScopdesRead(AjPFile inf, const AjPStr entry) { return domainScopdesReadC(inf, ajStrGetPtr(entry)); } ID domainScopdesReadC TY static MO ajdomain LB core XX DE Read a Scopdes object for a given SCOP domain from the SCOP parsable DE file (dir.des.scop.txt). XX PN [1] PA u inf AjPFile PD AJAX Input file stream. PX PN [2] PA r entry const char* PD SCOP identifier of domain. PX RT AjPScopdes RD Scopdes object RX CA new CT AjPScopdes CD Read a Scopdes object for a given SCOP domain CX // static AjPScopdes domainScopdesReadC(AjPFile inf, const char *entry) { AjPScopdes scopdes = NULL; AjBool ok = ajFalse; static AjBool called = AJFALSE; /* Only initialise strings if this is called for the first time */ if(!called) { called = ajTrue; if(!domainGStrline) domainGStrline = ajStrNew(); if(!domainGStrtentry) domainGStrtentry = ajStrNew(); domainGStrsunidstr = ajStrNew(); domainGStrtmp = ajStrNew(); domainGRegrexp = ajRegCompC( "^([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+"); } /* Read up to the correct entry (line) */ ajStrAssignC(&domainGStrtentry, entry); ajStrFmtUpper(&domainGStrtentry); while((ok = ajReadlineTrim(inf, &domainGStrline))) { if((ajFmtScanS(domainGStrline, "%S", &domainGStrsunidstr) == 0)) return NULL; /* Ignore comment lines */ if(*(domainGStrline->Ptr) == '#') continue; if(ajStrMatchWildS(domainGStrsunidstr, domainGStrtentry)) break; } if(!ok) return NULL; scopdes = domainScopdesNew(); if((ajFmtScanS(domainGStrline, "%d %S %S %S", &scopdes->Sunid, &scopdes->Type, &scopdes->Sccs, &scopdes->Entry) != 4)) { domainScopdesDel(&scopdes); return NULL; } /* Tokenise the line by ' ' and discard the first 4 strings */ if(!ajRegExec(domainGRegrexp, domainGStrline)) { ajFmtPrint("--> %S\n", domainGStrline); ajFatal("File read error in domainScopdesReadC"); } ajRegSubI(domainGRegrexp, 1, &domainGStrtmp); ajRegSubI(domainGRegrexp, 2, &domainGStrtmp); ajRegSubI(domainGRegrexp, 3, &domainGStrtmp); ajRegSubI(domainGRegrexp, 4, &domainGStrtmp); ajRegPost(domainGRegrexp, &scopdes->Desc); ajStrRemoveWhiteExcess(&scopdes->Desc); return scopdes; } ID domainScopclaReadC TY static MO ajdomain LB core XX DE Read a Scopcla object for a given SCOP domain from the SCOP parsable DE file (dir.des.scop.txt). XX PN [1] PA u inf AjPFile PD AJAX Input file stream PX PN [2] PA r entry const char* PD SCOP domain identifier PX RT AjPScopcla RD Scopcla object RX CA new CT AjPScopcla CD Read a Scopcla object for a given SCOP domain CX // static AjPScopcla domainScopclaReadC(AjPFile inf, const char *entry) { AjPScopcla scopcla = NULL; AjPStrTok handle = NULL; AjPStrTok bhandle = NULL; AjBool ok = ajFalse; char c = ' '; const char *p = NULL; ajuint n = 0U; ajuint i = 0U; ajint from; ajint to; static AjBool called = AJFALSE; /* Only initialise strings if this is called for the first time */ if(!called) { called = ajTrue; domainGStrline = ajStrNew(); domainGStrscopid = ajStrNew(); domainGStrpdbid = ajStrNew(); domainGStrchains = ajStrNew(); domainGStrsccs = ajStrNew(); domainGStrtentry = ajStrNew(); domainGStrtoken = ajStrNew(); domainGStrstr = ajStrNew(); domainGStrclass = ajStrNew(); domainGRegexp = ajRegCompC("^([0-9]+)([A-Za-z]+)[-]([0-9]+)"); } /* Read up to the correct entry (line) */ ajStrAssignC(&domainGStrtentry, entry); ajStrFmtUpper(&domainGStrtentry); while((ok = ajReadlineTrim(inf, &domainGStrline))) { if((ajFmtScanS(domainGStrline, "%S", &domainGStrscopid) == 0)) return NULL; /* Ignore comment lines */ if(ajStrGetCharFirst(domainGStrscopid) == '#') continue; if(ajStrMatchWildS(domainGStrscopid, domainGStrtentry)) break; } if(!ok) return NULL; if((ajFmtScanS(domainGStrline, "%*S %S %S %S %*d %S", &domainGStrpdbid, &domainGStrchains, &domainGStrsccs, &domainGStrclass) != 4)) return NULL; /* Count chains and allocate Scopcla object */ n = ajStrParseCountC(domainGStrchains, ","); scopcla = domainScopclaNew(n); ajStrFmtUpper(&domainGStrscopid); ajStrAssignS(&scopcla->Entry, domainGStrscopid); ajStrFmtUpper(&domainGStrpdbid); ajStrAssignS(&scopcla->Pdb, domainGStrpdbid); ajStrFmtUpper(&domainGStrsccs); ajStrAssignS(&scopcla->Sccs, domainGStrsccs); handle = ajStrTokenNewC(domainGStrchains, ","); for(i = 0U; i < n; i++) { ajStrTokenNextParse(&handle, &domainGStrtoken); p = ajStrGetPtr(domainGStrtoken); if(sscanf(p, "%d-%d", &from, &to) == 2) { scopcla->Chain[i] = '.'; ajFmtPrintS(&scopcla->Start[i], "%d", from); ajFmtPrintS(&scopcla->End[i], "%d", to); } else if(sscanf(p, "%c:%d-%d", &c, &from, &to) == 3) { ajFmtPrintS(&scopcla->Start[i], "%d", from); ajFmtPrintS(&scopcla->End[i], "%d", to); scopcla->Chain[i] = c; } else if(ajStrGetCharPos(domainGStrtoken, 1) == ':') { ajStrAssignC(&scopcla->Start[i], "."); ajStrAssignC(&scopcla->End[i], "."); scopcla->Chain[i] = *ajStrGetPtr(domainGStrtoken); } else if(ajRegExec(domainGRegexp, domainGStrtoken)) { ajRegSubI(domainGRegexp, 1, &domainGStrstr); ajStrAssignS(&scopcla->Start[i], domainGStrstr); ajRegSubI(domainGRegexp, 2, &domainGStrstr); scopcla->Chain[i] = *ajStrGetPtr(domainGStrstr); ajRegSubI(domainGRegexp, 3, &domainGStrstr); ajStrAssignS(&scopcla->End[i], domainGStrstr); } else if(ajStrGetCharFirst(domainGStrtoken) == '-') { scopcla->Chain[i] = '.'; ajStrAssignC(&scopcla->Start[i], "."); ajStrAssignC(&scopcla->End[i], "."); } else ajFatal("Unparseable chain line [%S]\n", domainGStrchains); } ajStrTokenDel(&handle); /* Read SCOP sunid's from classification string */ bhandle = ajStrTokenNewC(domainGStrclass, ",\n"); while(ajStrTokenNextParse(&bhandle, &domainGStrtoken)) { if(ajStrPrefixC(domainGStrtoken, "cl")) ajFmtScanS(domainGStrtoken, "cl=%d", &scopcla->Class); else if(ajStrPrefixC(domainGStrtoken, "cf")) ajFmtScanS(domainGStrtoken, "cf=%d", &scopcla->Fold); else if(ajStrPrefixC(domainGStrtoken, "sf")) ajFmtScanS(domainGStrtoken, "sf=%d", &scopcla->Superfamily); else if(ajStrPrefixC(domainGStrtoken, "fa")) ajFmtScanS(domainGStrtoken, "fa=%d", &scopcla->Family); else if(ajStrPrefixC(domainGStrtoken, "dm")) ajFmtScanS(domainGStrtoken, "dm=%d", &scopcla->Domain); else if(ajStrPrefixC(domainGStrtoken, "sp")) ajFmtScanS(domainGStrtoken, "sp=%d", &scopcla->Source); else if(ajStrPrefixC(domainGStrtoken, "px")) ajFmtScanS(domainGStrtoken, "px=%d", &scopcla->Domdat); } ajStrTokenDel(&bhandle); return scopcla; } ID domainScopclaDel TY static MO ajdomain LB core XX DE Destructor for scopcla object. XX PN [1] PA d Pscopcla AjPScopcla* PD Scopcla address PX RT void RD RX CA delete CT AjPScopcla CD Default destructor CX // static void domainScopclaDel(AjPScopcla *Pscopcla) { ajuint i = 0U; AjPScopcla scopcla = NULL; scopcla = *Pscopcla; if(!Pscopcla || !scopcla) return; ajStrDel(&scopcla->Entry); ajStrDel(&scopcla->Pdb); ajStrDel(&scopcla->Sccs); if(scopcla->Number) { for(i = 0U; i < scopcla->Number; i++) { ajStrDel(&scopcla->Start[i]); ajStrDel(&scopcla->End[i]); } AJFREE(scopcla->Start); AJFREE(scopcla->End); AJFREE(scopcla->Chain); } AJFREE(scopcla); *Pscopcla = NULL; return; } ID domainScopdesBinSearch TY static MO ajdomain LB core XX DE Performs a binary search for a Sunid over an array of Scopdes objects DE structures (which of course must first have been sorted, e.g. by using DE domainScopdesCompSunid). XX PN [1] PA r sunid ajint PD Search value of Sunid PX PN [2] PA r array AjPScopdes const* PD Array of Scopdes objects PX PN [3] PA r size ajint PD Size of array PX RT ajint RD Index of first Scopdes object found with a Sunid RD element matching id, or -1 if identifier is not found. RX // static ajint domainScopdesBinSearch(ajint sunid, AjPScopdes const *array, ajint size) { ajint l = 0; ajint m = 0; ajint h = size - 1; while(l <= h) { m = (l + h) >> 1; if(sunid < array[m]->Sunid) h = m - 1; else if(sunid > array[m]->Sunid) l = m + 1; else return m; } return -1; } ID domainScopdesDel TY static MO ajdomain LB core XX DE Scopdes object destructor. XX PN [1] PA d Pscopdes AjPScopdes* PD Scopdes address PX RT void RD RX CA delete CT AjPScopdes CD Default destructor CX // static void domainScopdesDel(AjPScopdes *Pscopdes) { if((Pscopdes == NULL) || (*Pscopdes == NULL)) return; if((*Pscopdes)->Type) ajStrDel(&((*Pscopdes)->Type)); if((*Pscopdes)->Sccs) ajStrDel(&((*Pscopdes)->Sccs)); if((*Pscopdes)->Entry) ajStrDel(&((*Pscopdes)->Entry)); if((*Pscopdes)->Desc) ajStrDel(&((*Pscopdes)->Desc)); AJFREE(*Pscopdes); *Pscopdes = NULL; return; } ID domainScopdesCompSunid TY static MO ajdomain LB core XX DE Function to comapre Scopdes objects by Sunid element. XX PN [1] PA r item1 const void* PD Scopdes address 1 PX PN [2] PA r item2 const void* PD Scopdes address 2 PX RT ajint RD -1 if Sunid1 should sort before Sunid2, +1 if the Sunid2 RD should sort first. 0 if they are identical in value. RX // static ajint domainScopdesCompSunid(const void *item1, const void *item2) { AjPScopdes scopdes1 = *(AjOScopdes *const *) item1; AjPScopdes scopdes2 = *(AjOScopdes *const *) item2; if(scopdes1->Sunid < scopdes2->Sunid) return -1; else if(scopdes1->Sunid == scopdes2->Sunid) return 0; return 1; } ID domainCathNameBinSearch TY static MO ajdomain LB core XX DE Performs a binary search for a domain code over an array of CathName DE structures (which of course must first have been sorted). This is a DE case-insensitive search. XX PN [1] PA r identifier const AjPStr PD Search term PX PN [2] PA r array AjPCathName const* PD Array of CathName objects PX PN [3] PA r size ajint PD Size of array PX RT ajint RD Index of first CathName object found with an CATH Id code RD matching identifier, or -1 if identifier is not found. RX // static ajint domainCathNameBinSearch(const AjPStr identifier, AjPCathName const *array, ajint size) { ajint l = 0; ajint m = 0; ajint h = size - 1; ajint c = 0; while(l <= h) { m = (l + h) >> 1; if((c = ajStrCmpCaseS(identifier, array[m]->Id)) < 0) h = m - 1; else if(c > 0) l = m + 1; else return m; } return -1; } ID domainCathDomBinSearch TY static MO ajdomain LB core XX DE Performs a binary search for a domain code over an array of CathDom DE structures (which of course must first have been sorted). This is a DE case-insensitive search. XX PN [1] PA r identifier const AjPStr PD Search term PX PN [2] PA r array AjPCathDom const* PD Array of AjPCathDom objects PX PN [3] PA r size ajint PD Size of array PX RT ajint RD Index of first AjPCathDom object found with an domain code RD matching identifier, or -1 if identifier is not found. RX // static ajint domainCathDomBinSearch(const AjPStr identifier, AjPCathDom const *array, ajint size) { ajint l = 0; ajint m = 0; ajint h = size - 1; ajint c = 0; while(l <= h) { m = (l + h) >> 1; if((c = ajStrCmpCaseS(identifier, array[m]->DomainID)) < 0) h = m - 1; else if(c > 0) l = m + 1; else return m; } return -1; } ID domainCathDomNew TY static MO ajdomain LB core XX DE CathDom object constructor. Fore-knowledge of the number of segments is DE required. XX PN [1] PA r nsegments ajuint PD Number of segments PX RT AjPCathDom RD Pointer to a CathDom object RX // static AjPCathDom domainCathDomNew(ajuint nsegments) { ajuint i = 0U; AjPCathDom cathdom = NULL; AJNEW0(cathdom); cathdom->DomainID = ajStrNew(); if(nsegments > 0) { AJCNEW0(cathdom->Start, nsegments); AJCNEW0(cathdom->End, nsegments); for(i = 0U; i < nsegments; i++) { cathdom->Start[i] = ajStrNew(); cathdom->End[i] = ajStrNew(); } } else { cathdom->Start = NULL; cathdom->End = NULL; } cathdom->NSegment = nsegments; return cathdom; } ID domainCathDomDel TY static MO ajdomain LB core XX DE Destructor for CathDom object. XX PN [1] PA w Pcathdom AjPCathDom* PD Cathdom address PX RT void RD RX // static void domainCathDomDel(AjPCathDom *Pcathdom) { ajuint i = 0U; AjPCathDom cathdom = NULL; if((Pcathdom == NULL) || (*Pcathdom == NULL)) return; cathdom = *Pcathdom; ajStrDel(&cathdom->DomainID); if(cathdom->NSegment) { for(i = 0U; i < cathdom->NSegment; i++) { ajStrDel(&cathdom->Start[i]); ajStrDel(&cathdom->End[i]); } AJFREE(cathdom->Start); AJFREE(cathdom->End); } AJFREE(cathdom); *Pcathdom = NULL; return; } ID domainCathNameNew TY static MO ajdomain LB core XX DE CATH Name object constructor. XX RT AjPCathName RD CATH Name object RX // static AjPCathName domainCathNameNew(void) { AjPCathName cathname = NULL; AJNEW0(cathname); cathname->Id = ajStrNew(); cathname->Desc = ajStrNew(); return cathname; } ID domainCathNameDel TY static MO ajdomain LB core XX DE Destructor for a CATH Name object. XX PN [1] PA d Pcathname AjPCathName* PD CATH Name address PX RT void RD RX // static void domainCathNameDel(AjPCathName *Pcathname) { AjPCathName cathname = NULL; if((Pcathname == NULL) || (*Pcathname == NULL)) return; cathname = *Pcathname; ajStrDel(&cathname->Id); ajStrDel(&cathname->Desc); AJFREE(cathname); *Pcathname = NULL; return; } ID domainSortCathNameId TY static MO ajdomain LB core XX DE Function to compare CATH Name objects by Id element. XX PN [1] PA r item1 const void* PD CATH Name address 1 PX PN [2] PA r item2 const void* PD CATH Name address 2 PX RT ajint RD -1 if Id1 should sort before Id2, +1 if the Id2 RD should sort first. 0 if they are identical in value. RX // static ajint domainSortCathNameId(const void *item1, const void *item2) { AjPCathName cathname1 = *(AjOCathName *const *) item1; AjPCathName cathname2 = *(AjOCathName *const *) item2; return ajStrCmpS(cathname1->Id, cathname2->Id); } ID domainSortCathDomDomainID TY static MO ajdomain LB core XX DE Function to sort CathDom objects by DomainID element. XX PN [1] PA r item1 const void* PD CATH Domain address 1 PX PN [2] PA r item2 const void* PD CATH Domain address 2 PX RT ajint RD -1 if DomainID1 should sort before DomainID2, +1 if the RD DomainID2 should sort first. 0 if they are identical in value. RX // static ajint domainSortCathDomDomainID(const void *item1, const void *item2) { AjPCathDom cathdom1 = *(AjOCathDom *const *) item1; AjPCathDom cathdom2 = *(AjOCathDom *const *) item2; return ajStrCmpS(cathdom1->DomainID, cathdom2->DomainID); } ID ajCathReadAllNew TY public MO ajdomain LB core XX DE Reads a DCF file (domain classification file) for CATH domains. This file DE is in DCF format (see documentation for DOMAINATRIX "cathparse" DE application) and creates an AJAX List of AJAX CATH objects for the DE entire content. XX PN [1] PA u inf AjPFile PD Pointer to CATH classification file PX RT AjPList RD AJAX List of AJAX CATH objects RX // AjPList ajCathReadAllNew(AjPFile inf) { AjPList caths = NULL; AjPCath cath = NULL; /* Check arg's */ if((!inf)) { ajWarn("Bad args passed to ajCathReadAllNew\n"); return NULL; } caths = ajListNew(); while((cath = ajCathReadCNew(inf, "*"))) ajListPushAppend(caths, cath); return caths; } ID ajCathReadAllRawNew TY public MO ajdomain LB core XX DE Reads the CATH parsable files (dir.cla.scop.txt & dir.des.scop.txt) and DE writes an AJAX List of AJAX CATH objects. XX PN [1] PA u cathf AjPFile PD Cath class file PX PN [2] PA u domf AjPFile PD Cath domain file PX PN [3] PA u namesf AjPFile PD Output file PX PN [4] PA u flog AjPFile PD Log file PX RT AjPList RD AJAX List of AJAX CATH objects RX // AjPList ajCathReadAllRawNew(AjPFile cathf, AjPFile domf, AjPFile namesf, AjPFile flog) { AjPList caths = NULL; AjPStr CathNameLine = NULL; /* String used to hold line from namesf */ AjPStr CathDomLine = NULL; /* String used to hold line from domf */ AjPStr CathListLine = NULL; /* String used to hold line from cathf */ AjPStr tmpDomainID = NULL; /* temp ptr to string to hold DomainId minus "0". */ AjPStr tmpStringDomPtr = NULL; /* temp ptr to string to hold DXX as a string. */ AjPStr tmpNumDomPtr = NULL; /* temp ptr to string to hold number of domains as a string. */ ajuint tmpDomInt = 0U; /* temp ptr to string to hold number of domains as int. */ AjPStr tmpDomIDNumDom = NULL; /* temp ptr to string to hold DomainId plus domain number before CathDom object created. */ ajint tmpNSegment = 0; /* temp ptr to string to hold NSegment before CathDom object created. */ AjPStr NDomAsString = NULL; /* temp ptr to string to hold (d) number of domains (appended onto tmpDomainID). */ AjPStr tmpNumString = NULL; /* temp string used to search CathName objects. */ AjPStr tmpNumString1 = NULL; /* temp string used to search CathName objects - first part. */ AjPStr tmpNumString2 = NULL; /* temp string used to search CathName objects - second part. */ AjPStr Search_DomainIDPtr = NULL; /* temp search string used to search CathDomList. */ const AjPStr StrTokPtr = NULL; AjPCathDom *CathDomArray = NULL; /* Array to hold sorted CathDomList */ AjPCathName *CathNameArray = NULL; /* Array to hold sorted CathNameList */ /* Initialise Integers */ ajint idxCathDom = 0; /* Index to CathDomList array */ ajint dimCathDom = 0; /* Dimension of CathDomList array */ ajint idxCathName= 0; /* Index to CathNameList array */ ajint dimCathName= 0; /* Dimension of CathNameList array */ ajint intC = 0; /* Class number as int */ ajint intA = 0; /* Architecture number as int */ ajint intT = 0; /* Topology number as int */ ajint intH = 0; /* Homologous Superfamily number as int */ ajint intF = 0; /* Family number as int */ ajint intNI = 0; /* Near Identical family number as int */ ajint intI = 0; /* Identical family number as int */ ajuint d = 0U; /* Declare int for looping through domains */ ajuint s = 0U; /* Declare int for looping through segments */ ajint single_seg = 1; /* Number of segments when no match found in CathDomList */ AjPCathName CathNamePtr = NULL; /* Reusable CathName object pointer */ AjPCathDom CathDomPtr = NULL; /* Reusable CathDom object pointer */ AjPCath CathPtr = NULL; /* Reusable CathList object pointer */ AjPList CathNameList = NULL; /* List containing ptrs to CathName objects */ AjPList CathDomList = NULL; /* List containing ptrs to CathDom objects */ AjPStrTok handle = NULL; AjPStr tmptok = NULL; /* Initialise strings */ tmptok = ajStrNew(); CathNameLine = ajStrNew(); CathDomLine = ajStrNew(); CathListLine = ajStrNew(); tmpDomainID = ajStrNew(); tmpStringDomPtr = ajStrNew(); tmpNumDomPtr = ajStrNew(); tmpDomIDNumDom = ajStrNew(); NDomAsString = ajStrNew(); tmpNumString = ajStrNew(); tmpNumString1 = ajStrNew(); tmpNumString2 = ajStrNew(); Search_DomainIDPtr = ajStrNew(); caths = ajListNew(); /* Create list for AjSCathName structures (CathNameList) to hold data from lines in CAT.names.all.v2.4 */ CathNameList = ajListNew(); CathDomList = ajListNew(); /* Read all the lines in CAT.names.all.v2.4 and populate ** CathNameList ** 1. Need a loop to read through every line in cathf ** ... while(ajFileReadLine) ** { ** 2. Create a CathName structure ... CathNameNew ** 3. Extract data from line and write data structure ** 4. Push the CathName pointer onto CathNameList ... ** } */ while(ajReadlineTrim(namesf, &CathNameLine)) { CathNamePtr = domainCathNameNew(); /*1st token is classification index e.g 0002.0160 */ handle = ajStrTokenNewC(CathNameLine, " \t"); ajStrTokenNextParseC(&handle, " \t", &(CathNamePtr)->Id); /*2nd token is domain code and should be discarded */ ajStrTokenNextParseC(&handle, " \t", &tmptok); /*3rd token is classification text */ ajStrTokenRestParse(&handle, &(CathNamePtr)->Desc); if(CathNamePtr->Desc->Ptr[0] == ':') ajStrCutStart(&(CathNamePtr)->Desc, 1); ajStrTokenDel(&handle); #if 0 /* AJB: This block can be deleted? */ StrTokPtr = ajStrParseWhite(CathNameLine); ajStrAssignS(&(CathNamePtr)->Id, StrTokPtr); StrTokPtr = ajStrParseWhite(NULL); ajStrTokenRestParse(NULL, &StrTokPtr); ajStrAssignS(&(CathNamePtr)->Desc, StrTokPtr); if(CathNamePtr->Desc->Ptr[0] == ':') ajStrCutStart(&(CathNamePtr)->Desc, 1); ajFmtScanS(CathNameLine, "%S", &(CathNamePtr)->Id); ajStrAssignSubS(&(CathNamePtr)->Desc, CathNameLine, 28, -1); ajStrTrimWhite(&(CathNamePtr)->Desc); if(CathNamePtr->Desc->Ptr[0] == ':') ajStrCutStart(&(CathNamePtr)->Desc, 1); #endif /* Push pointer to CathName object onto list */ ajListPush(CathNameList, CathNamePtr); } /* Sort the list by cath classification number, AjPStr Id */ /* Sort list using domainSortCathNameId function */ ajListSort(CathNameList, &domainSortCathNameId); /* make list into array and get array size - dimCathName */ dimCathName = (ajuint) ajListToarray(CathNameList, (void ***) &CathNameArray); /* We now have a list that we can do a binary search over */ /* Create list of AjSCathDom structures (CathDomList) to hold data from lines in domlist.v2.4 */ while(ajReadlineTrim(domf, &CathDomLine)) { /* ** 1st token is DomainID e.g 1cuk00 ** ajStrTok goes through each element of line */ StrTokPtr = ajStrParseWhite(CathDomLine); /* ** Remove last num from string (0). Assign StrTokPtr to temp ptr ** tmpDomainID e.g. 1cuk0 */ ajStrAssignSubS(&tmpDomainID, StrTokPtr, 0, 4); /* ajFmtPrint("tmpDomainID : %S\n", tmpDomainID ); fflush(stdout); */ StrTokPtr = ajStrParseWhite(NULL); /*2nd token is no. of domains */ /* Assign value of StrTokPtr (no. of domains) to tmpStringDomPtr */ ajStrAssignS(&tmpStringDomPtr, StrTokPtr); /* ** Remove first character (index 0 = the letter D) from ** tmpStringDomPtr and give to tmpDomInt */ ajStrAssignSubS(&tmpNumDomPtr, tmpStringDomPtr, 1, 2); ajStrToUint(tmpNumDomPtr, &tmpDomInt); /* ** ajFmtPrint("tmpDomInt : %u\n", tmpDomInt ); ** fflush(stdout); */ /* ** error-ajStrToInt(tmpDomIntPtr, &(tmpNumDomPtr)); ** Number of domains expressed as Int and assigned to tmpDomInt */ /*3rd token is no. of fragments, don't need this */ ajStrParseWhite(NULL); for (d = 1; d <= tmpDomInt; d++) /* For each domain */ { /* Get the number of segments */ StrTokPtr = ajStrParseWhite(NULL); /* Token= no. of segments */ /* ** ajFmtPrint("StrTokPtr : %S\n", StrTokPtr); ** fflush(stdout); */ /*Convert string containing no. of segments to int */ ajStrToInt(StrTokPtr, &(tmpNSegment)); /* Create CathDom object giving tmpNSegment as argument */ CathDomPtr = domainCathDomNew(tmpNSegment); /* Converts value of d to a string */ ajStrFromInt(&NDomAsString, d); if(d > 1) ajStrCutEnd(&tmpDomainID, 1); /* ** Append no of domains as a string (NDomAsString) onto tmpDomainID */ ajStrAppendS(&tmpDomainID, NDomAsString); /* Assign tmpDomainID to DomainID element in CathDom object */ ajStrAssignS(&(CathDomPtr->DomainID), tmpDomainID); for(s = 0U; s < CathDomPtr->NSegment; s++) /* For each segment */ { /* ** get Start and End residue numbers for each segment ** nth (starting at token no. 5) token is Chain of ** starting residue */ ajStrParseWhite(NULL); /* (n + 1)th token is start of segment res number */ StrTokPtr = ajStrParseWhite(NULL); ajStrAssignS(&(CathDomPtr->Start[s]), StrTokPtr); /* ** Assign Start res no. to Start element in AjPCathDom **error- ajStrToInt(StrTokPtr, &(CathDomPtr->Start[s])); ** (n+2)th token is "-" */ ajStrParseWhite(NULL); /* (n+3)th token is Chain of ending residue */ ajStrParseWhite(NULL); /* (n+4)th token is end of segment res number */ StrTokPtr = ajStrParseWhite(NULL); ajStrAssignS(&(CathDomPtr->End[s]), StrTokPtr); /* ** Assign End res no. to Start element in AjPCathDom ** error- ajStrToInt(StrTokPtr, &(CathDomPtr->End[s]));*/ ajStrParseWhite(NULL); /* (n+5)th token is "-" */ } /* ** Read all the lines in domlist.v2.4 and populate CathDomList ** Push pointer to CathDom object onto list */ ajListPush(CathDomList, CathDomPtr); } } /* ** Sort the list by domain code (ajListSort by DomainId) ** We now have a list that we can do a binary search over */ /* Sort list using domainSortCathDomDomainID function */ ajListSort(CathDomList, &domainSortCathDomDomainID); /* make list into array and get array size - dimCathDom */ dimCathDom = (ajuint) ajListToarray(CathDomList, (void ***) &CathDomArray); /* ** Start of main application loop ** while there is a line to read from caths.list.v2.4, ** read a line into a string ... ajFileReadLine */ while(ajReadlineTrim(cathf, &CathListLine)) { /* ** Extract DomainId from string and write to temp. variable ** - Search_DomainIDPtr */ /* DomainID held in temp string */ ajFmtScanS(CathListLine, "%S", &(Search_DomainIDPtr)); /* ** Binary search of Search_StringPtr against DomainID element ** in CathDomList */ /* ** Binary search of Search_DomainIDPtr over array of ** CathDom objects */ idxCathDom = domainCathDomBinSearch(Search_DomainIDPtr, CathDomArray, dimCathDom); /* sorted by AjPStr Id */ if(idxCathDom != -1 /*match found */) { /* ** Extract number of segments from CathDom and assign ** to tmpNSegment */ (tmpNSegment) = (CathDomArray[idxCathDom]->NSegment); /* Allocate the Cath object, AjXyzCathNew */ /* Create Cath object giving tmpNSegment as argument */ CathPtr = ajCathNew(tmpNSegment); /* Assign DomainId from CathDom to Cath objects */ ajStrAssignS(&(CathPtr->DomainID), CathDomArray[idxCathDom]->DomainID); /* Assign number of segments to NSegment element in Cath object */ (CathPtr->NSegment) = (CathDomArray[idxCathDom]->NSegment); /* ** Write the number of segments and start and end points ** ** printf("Number of segments for CathPtr : %d\n", ** CathPtr->NSegment); ** printf("Number of segments for CathDomPtr : %d\n\n", ** CathDomPtr->NSegment); */ for(s = 0U; s < CathPtr->NSegment; s++) /* For each segment */ { /* get Start and End residue numbers for each segment */ /* Assign value of start from CathDom to CathPtr */ ajStrAssignS(&(CathPtr->Start[s]), CathDomArray[idxCathDom]->Start[s]); /* Assign value of end from CathDom to CathPtr */ ajStrAssignS(&(CathPtr->End[s]), CathDomArray[idxCathDom]->End[s]); } } /* no match found => only one domain in protein */ else { /* ** Presume that domain contains a single segment, ** single_seg = 1 */ /* Allocate the Cath object, AjXyzCathNew */ /* Create Cath object giving tmpNSegment as argument */ CathPtr = ajCathNew(single_seg); /* Assign DomainId from Search_DomainIDPtr */ ajStrAssignS(&(CathPtr->DomainID), Search_DomainIDPtr); /* ** Assign the number of segments as 1 ** Assign number of segments to NSegment element in Cath ** object from single_seg */ (CathPtr->NSegment) = (single_seg); /* get Start and End residue numbers for each segment */ ajStrAssignC(&(CathPtr->Start[0]), "."); ajStrAssignC(&(CathPtr->End[0]), "."); } /* Extract Pdb code from DomainId */ ajStrAssignSubS(&(CathPtr->Pdb), CathPtr->DomainID, 0, 3); /* Extract chain identifier from DomainId */ CathPtr->Chain = ajStrGetCharPos(CathPtr->DomainID, 4); /* ** ajStrChar char from string ** error-ajStrAssignSubS(&(CathPtr->Chain), CathPtr->DomainID, 4, 4); */ /* ** Extract length of domain from string ** Take the 9th element of line and assign to Length in Cath object */ ajFmtScanS(CathListLine, "%*S %*d %*d %*d %*d %*d %*d %*d %d", &(CathPtr->Length)); /* ** Extract ajint Class_Id, Arch_Id, Topology_Id, Superfamily_Id, ** NIFamily_Id, Family_Id, IFamily_Id from string and write into ** AjPCath */ ajFmtScanS(CathListLine, "%*S %d %d %d %d %d %d %d", &intC, &intA, &intT, &intH, &intF, &intNI, &intI); (CathPtr->Class_Id) = (intC); (CathPtr->Arch_Id) = (intA); (CathPtr->Topology_Id) = (intT); (CathPtr->Superfamily_Id) = (intH); (CathPtr->Family_Id) = (intF); (CathPtr->NIFamily_Id) = (intNI); (CathPtr->IFamily_Id) = (intI); /* ** Construct number string for SUPERFAMILY from Class_Id, Arch_Id, ** Topology_Id, ** Superfamily_Id and store in temp. variable (format X.XX.XX.XX) */ /* Make string containing CATH id numbers */ ajFmtPrintS(&tmpNumString, "%d.%d.%d.%d", intC, intA, intT, intH); /* Binary search using temp. variable in AjSCathName */ ajFmtPrintF(flog, "%S\n", tmpNumString); /* Binary search of tmpNumString over array of CathName objects */ idxCathName = domainCathNameBinSearch(tmpNumString, CathNameArray, dimCathName); if ( idxCathName != -1) /*match found */ { /* Extract Superfamily string and write into AJPCath */ ajStrAssignS(&(CathPtr->Superfamily), (CathNameArray[idxCathName]->Desc)); } else /*no match found */ { /* Write Superfamily string as a '.' */ ajStrAssignC(&(CathPtr->Superfamily), "."); /*error (CathPtr->Superfamily) = "."); */ } /* ** Construct number string for TOPOLOGY from Class_Id, ** Arch_Id, Topology_Id, ** and store in temp. variable (format X.XX.XX) */ /* Make string containing CAT id numbers */ ajFmtPrintS(&tmpNumString, "%d.%d.%d", intC, intA, intT); /* Binary search using temp. variable in AjSCathName */ ajFmtPrintF(flog, "%S\n", tmpNumString); /* Binary search of tmpNumString over array of CathName objects */ idxCathName = domainCathNameBinSearch(tmpNumString, CathNameArray, dimCathName); if ( idxCathName != -1) /* match found */ { /* Extract Topology string and write into AJPCath */ ajStrAssignS(&(CathPtr->Topology), CathNameArray[idxCathName]->Desc); } else /* no match found */ { /* Write topology as a '.' */ ajStrAssignC(&(CathPtr->Topology), "."); /*error CathPtr->Topology) = "."); */ } /* ** Construct number string for ARCHITECTURE from Class_Id, Arch_Id, ** and store in temp. variable ** Class and Architecture numbers in domlist.v2.4 are in format ** XXXX.XXXX */ #if 0 /* AJB: Not keen on the ajFatal comments. Can this be deleted? */ /* if(intC < 10) ajFmtPrintS(&tmpNumString1, "000%d", intC); else ajFatal("MIKE GIVE A ERROR MESSAGE"); if(intA < 10) ajFmtPrintS(&tmpNumString2, "000%d", intA); else if(intA < 100) ajFmtPrintS(&tmpNumString2, "00%d", intA); else if(intA < 1000) ajFmtPrintS(&tmpNumString2, "0%d", intA); else if(intA < 10000) ajFmtPrintS(&tmpNumString2, "%d", intA); else ajFatal("MIKE GIVE A ERROR MESSAGE"); ajFmtPrintS(&tmpNumString, "%S.%S", tmpNumString1, tmpNumString2); */ #endif ajFmtPrintS(&tmpNumString, "%04d.%04d", intC, intA); /* ** Make string containing CA id numbers ** ajFmtPrintS(&tmpNumString, "000%d.%d", intC, intA); */ /* Binary search using temp. variable in AjSCathName */ ajFmtPrintF(flog, "%S\n", tmpNumString); /* Binary search of tmpNumString over array of CathName objects */ idxCathName = domainCathNameBinSearch(tmpNumString, CathNameArray, dimCathName); if ( idxCathName != -1 ) /* match found */ { /* Extract Architecture string and write into AJPCath */ ajStrAssignS(&(CathPtr->Architecture), CathNameArray[idxCathName]->Desc); } else /* no match found */ { /* Write architecture as a '.' */ ajStrAssignC(&(CathPtr->Architecture), "."); /*error (CathPtr->Architecture) = "."); */ } /* ** Construct number string for CLASS from Class_Id and store in **temp. variable */ if(intC < 10) ajFmtPrintS(&tmpNumString, "%04d", intC); else ajFatal("MIKE GIVE A ERROR MESSAGE"); /* Binary search using temp. variable in AjSCathName */ ajFmtPrintF(flog, "%S\n", tmpNumString); /* Binary search of tmpNumString over array of CathName objects */ idxCathName = domainCathNameBinSearch(tmpNumString, CathNameArray, dimCathName); if ( idxCathName != -1) /* match found */ { /* Extract Class string and write into AJPCath */ ajStrAssignS(&(CathPtr->Class), CathNameArray[idxCathName]->Desc); } else /* no match found */ { /* Write class as a '.' */ ajStrAssignC(&(CathPtr->Class), "."); /*error (CathPtr->Class) = "."); */ } /* Push the Cath object onto list */ ajListPushAppend(caths, CathPtr); } /* End of main loop */ /* ** Free the memory for the list and nodes in ** list of AjSCathName structures (ajListFree) */ while(ajListPop(CathNameList, (void **) &CathNamePtr)) domainCathNameDel(&CathNamePtr); ajListFree(&CathNameList); /* ** Free the memory for the list and nodes in ** list of AjSCathDom structures (ajListFree) */ while(ajListPop(CathDomList, (void **) &CathDomPtr)) domainCathDomDel(&CathDomPtr); ajListFree(&CathDomList); /* Tidy up */ ajStrDel(&tmptok); ajStrDel(&CathNameLine); ajStrDel(&CathDomLine); ajStrDel(&CathListLine); ajStrDel(&tmpStringDomPtr); ajStrDel(&tmpNumDomPtr); ajStrDel(&tmpDomainID); ajStrDel(&tmpDomIDNumDom); ajStrDel(&tmpNumString); ajStrDel(&tmpNumString1); ajStrDel(&tmpNumString2); AJFREE(CathDomArray); AJFREE(CathNameArray); ajStrDel(&Search_DomainIDPtr); ajStrDel(&NDomAsString); return caths; } ID ajCathReadCNew TY public MO ajdomain LB core XX DE Read a Cath object from a DCF file (domain classification file) in DCF DE format (see documentation for DE DOMAINATRIX "cathparse" application). XX PN [1] PA u inf AjPFile PD AJAX Input file stream PX PN [2] PA r entry const char* PD CATH id of entry to retrieve (or "*" for next PD domain in file). PX RT AjPCath RD Cath object. RX CA new CT AjPCath CD Cath constructor from reading dcf format file. CX // AjPCath ajCathReadCNew(AjPFile inf, const char *entry) { AjPCath cath = NULL; static AjPRegexp exp1 = NULL; static AjPRegexp exp2 = NULL; static AjPStr domainID = NULL; static AjPStr pdb = NULL; static AjPStr class = NULL; static AjPStr architecture = NULL; static AjPStr topology = NULL; static AjPStr superfamily = NULL; static AjPStr line = NULL; static AjPStr str = NULL; static AjPStr stmp = NULL; static AjPStr tentry = NULL; static AjPStr Acc = NULL; static AjPStr Spr = NULL; static AjPStr SeqPdb = NULL; static AjPStr SeqSpr = NULL; AjBool ok = ajFalse; ajint idx = 0; ajint n = 0; ajint Length; /* No. of residues in domain */ ajint Startd; /* Start of sequence relative to full length swissprot sequence */ ajint Endd; /* End of sequence relative to full length swissprot sequence */ /* Only initialise strings if this is called for the first time */ if(!line) { domainID = ajStrNew(); pdb = ajStrNew(); class = ajStrNew(); architecture = ajStrNew(); topology = ajStrNew(); superfamily = ajStrNew(); line = ajStrNew(); str = ajStrNew(); tentry = ajStrNew(); exp1 = ajRegCompC("^([^ \t\r\n]+)[ \t\n\r]+"); exp2 = ajRegCompC( "^([A-Za-z0-9.]+)[ ]*[^ \t\r\n]+[ ]*([0-9.-]+)[ ]*" "[^ \t\r\n]+[ ]*([0-9.-]+)"); Acc = ajStrNew(); Spr = ajStrNew(); } SeqSpr = ajStrNew(); SeqPdb = ajStrNew(); ajStrAssignC(&tentry, entry); ajStrFmtUpper(&tentry); while((ok = ajReadlineTrim(inf, &line))) { if(!ajStrPrefixC(line, "ID ")) continue; ajFmtScanS(line, "%*S %S", &stmp); if(ajStrMatchWildS(stmp, tentry)) break; } if(!ok) return NULL; while(ok && !ajStrPrefixC(line, "//")) { if(ajStrPrefixC(line, "XX")) { ok = ajReadlineTrim(inf, &line); continue; } ajRegExec(exp1, line); ajRegPost(exp1, &str); if(ajStrPrefixC(line, "ID")) ajStrAssignS(&domainID, str); else if(ajStrPrefixC(line, "EN")) ajStrAssignS(&pdb, str); else if(ajStrPrefixC(line, "CL")) ajStrAssignS(&class, str); else if(ajStrPrefixC(line, "AR")) { ajStrAssignS(&architecture, str); while(ajReadlineTrim(inf, &line)) { if(ajStrPrefixC(line, "XX")) break; ajStrAppendC(&architecture, ajStrGetPtr(line) + 3); } ajStrRemoveWhiteExcess(&architecture); } else if(ajStrPrefixC(line, "TP")) { ajStrAssignS(&topology, str); while(ajReadlineTrim(inf, &line)) { if(ajStrPrefixC(line, "XX")) break; ajStrAppendC(&topology, ajStrGetPtr(line) + 3); } ajStrRemoveWhiteExcess(&topology); } else if(ajStrPrefixC(line, "SF")) { ajStrAssignS(&superfamily, str); while(ajReadlineTrim(inf, &line)) { if(ajStrPrefixC(line, "XX")) break; ajStrAppendC(&superfamily, ajStrGetPtr(line) + 3); } ajStrRemoveWhiteExcess(&superfamily); } else if(ajStrPrefixC(line, "NR")) ajFmtScanS(line, "%*S %d", &Length); else if(ajStrPrefixC(line, "NC")) { ajStrToInt(str, &n); cath = ajCathNew(n); ajStrAssignS(&cath->DomainID, domainID); ajStrAssignS(&cath->Pdb, pdb); ajStrAssignS(&cath->Class, class); ajStrAssignS(&cath->Architecture, architecture); ajStrAssignS(&cath->Topology, topology); ajStrAssignS(&cath->Superfamily, superfamily); cath->Length = Length; ajStrAssignS(&cath->Acc, Acc); ajStrAssignS(&cath->Spr, Spr); ajStrAssignS(&cath->SeqPdb, SeqPdb); ajStrAssignS(&cath->SeqSpr, SeqSpr); } else if(ajStrPrefixC(line, "CN")) ajFmtScanS(line, "%*S %*c%d", &idx); else if(ajStrPrefixC(line, "CH")) { if(!ajRegExec(exp2, str)) return NULL; ajRegSubI(exp2, 1, &stmp); cath->Chain = *ajStrGetPtr(stmp); ajRegSubI(exp2, 2, &str); ajStrAssignC(&cath->Start[idx - 1], ajStrGetPtr(str)); ajRegSubI(exp2, 3, &str); ajStrAssignC(&cath->End[idx - 1], ajStrGetPtr(str)); } /* Sequence from pdb file */ else if(ajStrPrefixC(line, "DS")) { while((ok = ajReadlineTrim(inf, &line)) && !ajStrPrefixC(line, "XX")) ajStrAppendC(&SeqPdb, ajStrGetPtr(line)); ajStrRemoveWhite(&SeqPdb); continue; } /* Sequence from swissprot */ else if(ajStrPrefixC(line, "SQ")) { while((ok = ajReadlineTrim(inf, &line)) && !ajStrPrefixC(line, "XX")) ajStrAppendC(&SeqSpr, ajStrGetPtr(line)); ajStrRemoveWhite(&SeqSpr); continue; } /* Accession number */ else if(ajStrPrefixC(line, "AC")) ajFmtScanS(line, "%*s %S", &Acc); /* Swissprot code */ else if(ajStrPrefixC(line, "SF")) ajFmtScanS(line, "%*s %S", &Spr); /* Start and end relative to swissprot sequence */ else if(ajStrPrefixC(line, "RA")) ajFmtScanS(line, "%*s %d %*s %d", &Startd, &Endd); ok = ajReadlineTrim(inf, &line); } ajStrDel(&SeqSpr); ajStrDel(&SeqPdb); return cath; } ID ajCathReadNew TY public MO ajdomain LB core XX DE Read a Cath object from a DCF file (domain classification file) which is DE in DCF format (see documentation for DE DOMAINATRIX "cathparse" application). XX PN [1] PA u inf AjPFile PD Input file stream PX PN [2] PA r entry const AjPStr PD CATH id of entry to retrieve PD (or "*" for next domain in file). PX RT AjPCath RD Cath object. RX CA new CT AjPCath CD Cath constructor from reading dcf format file. CX // AjPCath ajCathReadNew(AjPFile inf, const AjPStr entry) { AjPCath cath = NULL; if((cath = ajCathReadCNew(inf, entry->Ptr))) return cath; return NULL; } ID ajDomainReadAllNew TY public MO ajdomain LB core XX DE Reads the DCF file (domain classification file; see documentation DE for DOMAINATRIX "scopparse" application) and creates a list of Domain DE objects for the entire content. XX PN [1] PA u inf AjPFile PD Pointer to DOMAIN classification file PX RT AjPList RD AJAX List of AJAX Domain objects or NULL RD (file read problem) RX // AjPList ajDomainReadAllNew(AjPFile inf) { AjPList domains = NULL; AjPDomain domain = NULL; AjEDomainType type = ajEDomainTypeNULL; /* Check arg's */ if((!inf)) { ajWarn("Bad args passed to ajDomainReadAllNew\n"); return NULL; } type = ajDomainDCFType(inf); if((type != ajEDomainTypeSCOP) && (type != ajEDomainTypeCATH)) { ajWarn("Could not ascertain domain type in ajDomainReadNew"); return NULL; } domains = ajListNew(); if(type == ajEDomainTypeSCOP) while((domain->Scop = ajScopReadCNew(inf, "*"))) ajListPushAppend(domains, domain); else while((domain->Cath = ajCathReadCNew(inf, "*"))) ajListPushAppend(domains, domain); return domains; } ID ajScopReadAllNew TY public MO ajdomain LB core XX DE Reads the DCF file (domain classification file) of SCOP domains. The DCF DE file is in DCF format (see documentation DE for DOMAINATRIX "scopparse" application). The function creates a list of DE cop objects for the entire content. XX PN [1] PA u inf AjPFile PD Pointer to SCOP classification file PX RT AjPList RD List of scop objects or NULL (file read problem). RX // AjPList ajScopReadAllNew(AjPFile inf) { AjPList scops = NULL; AjPScop scop = NULL; /* Check arg's */ if((!inf)) { ajWarn("Bad args passed to ajScopReadAllNew\n"); return NULL; } scops = ajListNew(); while((scop = ajScopReadCNew(inf, "*"))) ajListPushAppend(scops, scop); return scops; } ID ajScopReadAllRawNew TY public MO ajdomain LB core XX DE Reads the SCOP parsable files (dir.cla.scop.txt & dir.des.scop.txt) and DE creates a list of Scop objects. XX PN [1] PA u claf AjPFile PD Scop class file PX PN [2] PA u desf AjPFile PD Scop description file PX PN [3] PA r omit AjBool PD True if domains of 1 chain only are output PX RT AjPList RD List of Scop objects. RX // AjPList ajScopReadAllRawNew(AjPFile claf, AjPFile desf, AjBool omit) { AjPScopcla scopcla = NULL; AjPScopdes scopdes = NULL; AjPScopdes *desarr = NULL; AjPScop scop = NULL; AjPList clalist = NULL; AjPList deslist = NULL; AjPList scops = NULL; AjBool nooutput = ajFalse; char chn; ajint dim = 0; /* Dimension of array */ ajint idx = 0; /* Index into array */ ajuint i = 0U; clalist = ajListNew(); deslist = ajListNew(); scops = ajListNew(); /* Read the dir.cla.scop.txt file */ while((scopcla = domainScopclaReadC(claf, "*"))) { ajListPushAppend(clalist, scopcla); /* ajFmtPrint(" %d ", scopcla->Domdat); */ } /* Read the dir.des.scop.txt file, sort the list by Sunid and convert to an array */ while((scopdes = domainScopdesReadC(desf, "*"))) { ajListPush(deslist, scopdes); /* ajFmtPrint("%d\n", scopdes->Sunid); */ } ajListSort(deslist, &domainScopdesCompSunid); dim = (ajuint) ajListToarray(deslist, (void ***) &desarr); while(ajListPop(clalist, (void **) &scopcla)) { if(omit) { if(scopcla->Number > 1) { chn = scopcla->Chain[0]; for(nooutput = ajFalse, i = 1; i < scopcla->Number; i++) if(chn != scopcla->Chain[i]) { nooutput = ajTrue; break; } if(nooutput) continue; } } scop = ajScopNew(scopcla->Number); ajStrAssignS(&scop->Entry, scopcla->Entry); ajStrAssignS(&scop->Pdb, scopcla->Pdb); scop->Sunid_Class = scopcla->Class; scop->Sunid_Fold = scopcla->Fold; scop->Sunid_Superfamily = scopcla->Superfamily; scop->Sunid_Family = scopcla->Family; scop->Sunid_Domain = scopcla->Domain; scop->Sunid_Source = scopcla->Source; scop->Sunid_Domdat = scopcla->Domdat; idx = domainScopdesBinSearch(scopcla->Class, desarr, dim); ajStrAssignS(&scop->Class, desarr[idx]->Desc); idx = domainScopdesBinSearch(scopcla->Fold, desarr, dim); ajStrAssignS(&scop->Fold, desarr[idx]->Desc); idx = domainScopdesBinSearch(scopcla->Superfamily, desarr, dim); ajStrAssignS(&scop->Superfamily, desarr[idx]->Desc); idx = domainScopdesBinSearch(scopcla->Family, desarr, dim); ajStrAssignS(&scop->Family, desarr[idx]->Desc); idx = domainScopdesBinSearch(scopcla->Domain, desarr, dim); ajStrAssignS(&scop->Domain, desarr[idx]->Desc); idx = domainScopdesBinSearch(scopcla->Source, desarr, dim); ajStrAssignS(&scop->Source, desarr[idx]->Desc); for(i = 0U; i < scopcla->Number; i++) { scop->Chain[i] = scopcla->Chain[i]; ajStrAssignS(&scop->Start[i], scopcla->Start[i]); ajStrAssignS(&scop->End[i], scopcla->End[i]); } ajListPushAppend(scops, scop); domainScopclaDel(&scopcla); } while(ajListPop(deslist, (void **)&scopdes)) domainScopdesDel(&scopdes); /* Tidy up */ AJFREE(desarr); ajListFree(&clalist); ajListFree(&deslist); return scops; } ID ajCathNew TY public MO ajdomain LB core XX DE Cath object constructor. Fore-knowledge of the number of chain segments DE the domain is comprised of is required. XX PN [1] PA r n ajuint PD No. of chain segments PX RT AjPCath RD Pointer to a Cath object RX CA new CT AjPCath CD Cath default constructor. CX // AjPCath ajCathNew(ajuint n) { ajuint i = 0U; AjPCath cath = NULL; AJNEW0(cath); cath->DomainID = ajStrNew(); cath->Pdb = ajStrNew(); cath->Class = ajStrNew(); cath->Architecture = ajStrNew(); cath->Topology = ajStrNew(); cath->Superfamily = ajStrNew(); cath->Acc = ajStrNew(); cath->Spr = ajStrNew(); cath->SeqPdb = ajStrNew(); cath->SeqSpr = ajStrNew(); if(n > 0) { AJCNEW0(cath->Start, n); AJCNEW0(cath->End, n); for(i = 0U; i < n; i++) { cath->Start[i] = ajStrNew(); cath->End[i] = ajStrNew(); } } cath->NSegment = n; return cath; } ID ajDomainNew TY public MO ajdomain LB core XX DE Domain object constructor. Fore-knowledge of the number of chains (SCOP DE domains) or chain segments (CATH domains) the domain is comprised of is DE required. This is normally called by the ajDomainReadCNew / DE ajDomainReadNew functions. XX PN [1] PA r n ajuint PD Number of chains (SCOP) or chain segments (CATH) PX PN [2] PA u type AjEDomainType PD AJAX Domain Type enuimeration PX RT AjPDomain RD Pointer to a Domain object RX CA new CT AjPDomain CD Domain default constructor. CX // AjPDomain ajDomainNew(ajuint n, AjEDomainType type) { AjPDomain domain = NULL; if((type != ajEDomainTypeSCOP) && (type != ajEDomainTypeCATH)) { ajWarn("Bad args passed to ajDomainNew"); return NULL; } AJNEW0(domain); if(type == ajEDomainTypeSCOP) { domain->Scop = ajScopNew(n); domain->Type = ajEDomainTypeSCOP; } else { domain->Cath = ajCathNew(n); domain->Type = ajEDomainTypeCATH; } return domain; } ID ajScopNew TY public MO ajdomain LB core XX DE Scop object constructor. Fore-knowledge of the number of chains is DE required. This is normally called by the ajScopReadCNew / ajScopReadNew DE functions. XX PN [1] PA r chains ajuint PD Number of chains PX RT AjPScop RD Pointer to a Scop object RX CA new CT AjPScop CD Scop default constructor. CX // AjPScop ajScopNew(ajuint chains) { ajuint i = 0U; AjPScop scop = NULL; AJNEW0(scop); scop->Entry = ajStrNew(); scop->Pdb = ajStrNew(); scop->Class = ajStrNew(); scop->Fold = ajStrNew(); scop->Superfamily = ajStrNew(); scop->Family = ajStrNew(); scop->Domain = ajStrNew(); scop->Source = ajStrNew(); scop->Acc = ajStrNew(); scop->Spr = ajStrNew(); scop->SeqPdb = ajStrNew(); scop->SeqSpr = ajStrNew(); scop->Sse = ajStrNew(); scop->Sss = ajStrNew(); if(chains) { scop->Chain = ajCharNewRes(chains); AJCNEW0(scop->Start, chains); AJCNEW0(scop->End, chains); for(i = 0U; i < chains; i++) { scop->Start[i] = ajStrNew(); scop->End[i] = ajStrNew(); } } scop->Number = chains; return scop; } ID ajDomainReadNew TY public MO ajdomain LB core XX DE Read a Domain object from a DCF file (domain classification file - see DE documentation for DOMAINATRIX "scopparse" application). XX PN [1] PA u inf AjPFile PD Input file stream. PX PN [2] PA r entry const AjPStr PD Domain id of domain to read (or "*" for PD next domain in file). PX RT AjPDomain RD Domain object. RX CA new CT AjPDomain CD Domain constructor from reading dcf file. CX // AjPDomain ajDomainReadNew(AjPFile inf, const AjPStr entry) { AjEDomainType type = ajEDomainTypeNULL; AjPDomain domain = NULL; type = ajDomainDCFType(inf); if((type != ajEDomainTypeSCOP) && (type != ajEDomainTypeCATH)) { ajWarn("Could not ascertain domain type in ajDomainReadNew"); return NULL; } AJNEW0(domain); if(type == ajEDomainTypeSCOP) domain->Scop = ajScopReadCNew(inf, ajStrGetPtr(entry)); else domain->Cath = ajCathReadCNew(inf, ajStrGetPtr(entry)); return domain; } ID ajScopReadNew TY public MO ajdomain LB core XX DE Read a Scop object from a DCF file (domain classification file). The DCF DE file is in DCF format (see documentation for DE DOMAINATRIX "scopparse" application). XX PN [1] PA u inf AjPFile PD Input file stream. PX PN [2] PA r entry const AjPStr PD SCOP id of domain to read (or "*" for next PD domain in file). PX RT AjPScop RD Scop object. RX CA new CT AjPScop CD Scop constructor from reading dcf format file. CX // AjPScop ajScopReadNew(AjPFile inf, const AjPStr entry) { return ajScopReadCNew(inf, ajStrGetPtr(entry)); } ID ajDomainReadCNew TY public MO ajdomain LB core XX DE Read a Domain object from a DCF (domain classification file; see DE documentation for DOMAINATRIX "scopparse" application). XX PN [1] PA u inf AjPFile PD Input file stream PX PN [2] PA r entry const char* PD Domain id of domain to parse PX PN [3] PA u dtype AjEDomainType PD AJAX Domain Type enumeration in the DCF PD file, or ajEDomainTypeNULL if not sure. PX RT AjPDomain RD Domain object or NULL (file read problem). RX CA new CT AjPDomain CD Domain constructor from reading dcf file. CX // AjPDomain ajDomainReadCNew(AjPFile inf, const char *entry, AjEDomainType dtype) { AjEDomainType type = ajEDomainTypeNULL; AjPDomain domain = NULL; if(!dtype) type = ajDomainDCFType(inf); else type = dtype; if((type != ajEDomainTypeSCOP) && (type != ajEDomainTypeCATH)) { ajWarn("Could not ascertain domain type in ajDomainReadCNew"); return NULL; } AJNEW0(domain); domain->Type = type; if(domain->Type == ajEDomainTypeSCOP) domain->Scop = ajScopReadCNew(inf, entry); else domain->Cath = ajCathReadCNew(inf, entry); if(!domain->Scop && !domain->Cath) { AJFREE(domain); return NULL; } return domain; } ID ajScopReadCNew TY public MO ajdomain LB core XX DE Read a Scop object from a DCF file (domain classification file) in DCF DE format (see documentation for DE DOMAINATRIX "scopparse" application). XX PN [1] PA u inf AjPFile PD Input file stream PX PN [2] PA r entry const char* PD SCOP id of domain to parse PX RT AjPScop RD Scop object or NULL (file read problem). RX CA new CT AjPScop CD Scop constructor from reading dcf format file. CX // AjPScop ajScopReadCNew(AjPFile inf, const char *entry) { AjPScop scop = NULL; static AjPRegexp exp1 = NULL; static AjPRegexp exp2 = NULL; static AjPStr line = NULL; static AjPStr str = NULL; static AjPStr xentry = NULL; static AjPStr source = NULL; static AjPStr class = NULL; static AjPStr fold = NULL; static AjPStr super = NULL; static AjPStr family = NULL; static AjPStr domain = NULL; static AjPStr pdb = NULL; static AjPStr tentry = NULL; static AjPStr stmp = NULL; static AjPStr Acc = NULL; static AjPStr Spr = NULL; static AjPStr SeqPdb = NULL; static AjPStr SeqSpr = NULL; static AjPStr sse = NULL; static AjPStr sss = NULL; AjBool ok = ajFalse; const char *p; ajint idx = 0; ajint n = 0; ajint Startd; /* Start of sequence relative to full length swissprot sequence */ ajint Endd; /* End of sequence relative to full length swissprot sequence */ ajint Sunid_Class; /* SCOP sunid for class */ ajint Sunid_Fold; /* SCOP sunid for fold */ ajint Sunid_Superfamily; /* SCOP sunid for superfamily */ ajint Sunid_Family; /* SCOP sunid for family */ ajint Sunid_Domain; /* SCOP sunid for domain */ ajint Sunid_Source; /* SCOP sunid for species */ ajint Sunid_Domdat; /* SCOP sunid for domain data */ /* Only initialise strings if this is called for the first time */ if(!line) { str = ajStrNew(); xentry = ajStrNew(); pdb = ajStrNew(); source = ajStrNew(); class = ajStrNew(); fold = ajStrNew(); super = ajStrNew(); family = ajStrNew(); domain = ajStrNew(); line = ajStrNew(); tentry = ajStrNew(); stmp = ajStrNew(); Acc = ajStrNew(); Spr = ajStrNew(); sse = ajStrNew(); exp1 = ajRegCompC("^([^ \t\r\n]+)[ \t\n\r]+"); exp2 = ajRegCompC("^([A-Za-z0-9.]+)[ ]*[^ \t\r\n]+[ ]*" "([0-9.-]+)[ ]*" "[^ \t\r\n]+[ ]*([0-9.-]+)"); } SeqSpr = ajStrNew(); SeqPdb = ajStrNew(); sss = ajStrNew(); ajStrAssignC(&tentry, entry); ajStrFmtUpper(&tentry); while((ok = ajReadlineTrim(inf, &line))) { if(!ajStrPrefixC(line, "ID ")) continue; if(!ajRegExec(exp1, line)) return NULL; ajRegPost(exp1, &stmp); if(ajStrMatchWildS(stmp, tentry)) break; } if(!ok) { ajRegFree(&exp1); ajRegFree(&exp2); ajStrDel(&line); ajStrDel(&str); ajStrDel(&xentry); ajStrDel(&source); ajStrDel(&class); ajStrDel(&fold); ajStrDel(&super); ajStrDel(&family); ajStrDel(&domain); ajStrDel(&pdb); ajStrDel(&tentry); ajStrDel(&stmp); ajStrDel(&Acc); ajStrDel(&Spr); ajStrDel(&SeqPdb); ajStrDel(&SeqSpr); ajStrDel(&sse); ajStrDel(&sss); return NULL; } while(ok && (!ajStrPrefixC(line, "//"))) { if(ajStrPrefixC(line, "XX")) { ok = ajReadlineTrim(inf, &line); continue; } /* Empty line */ if(!(MAJSTRGETLEN(line))) { ok = ajReadlineTrim(inf, &line); continue; } ajRegExec(exp1, line); ajRegPost(exp1, &str); if(ajStrPrefixC(line, "ID")) ajStrAssignS(&xentry, str); else if(ajStrPrefixC(line, "EN")) ajStrAssignS(&pdb, str); else if(ajStrPrefixC(line, "OS")) ajStrAssignS(&source, str); else if(ajStrPrefixC(line, "CL")) ajStrAssignS(&class, str); else if(ajStrPrefixC(line, "FO")) { ajStrAssignS(&fold, str); while((ok = ajReadlineTrim(inf, &line))) { if(ajStrPrefixC(line, "XX")) break; ajStrAppendC(&fold, ajStrGetPtr(line) + 3); } ajStrRemoveWhiteExcess(&fold); } else if(ajStrPrefixC(line, "SF")) { ajStrAssignS(&super, str); while((ok = ajReadlineTrim(inf, &line))) { if(ajStrPrefixC(line, "XX")) break; ajStrAppendC(&super, ajStrGetPtr(line) + 3); } ajStrRemoveWhiteExcess(&super); } else if(ajStrPrefixC(line, "FA")) { ajStrAssignS(&family, str); while((ok = ajReadlineTrim(inf, &line))) { if(ajStrPrefixC(line, "XX")) break; ajStrAppendC(&family, ajStrGetPtr(line) + 3); } ajStrRemoveWhiteExcess(&family); } else if(ajStrPrefixC(line, "DO")) { ajStrAssignS(&domain, str); while((ok = ajReadlineTrim(inf, &line))) { if(ajStrPrefixC(line, "XX")) break; ajStrAppendC(&domain, ajStrGetPtr(line) + 3); } ajStrRemoveWhiteExcess(&domain); } else if(ajStrPrefixC(line, "NC")) { ajStrToInt(str, &n); scop = ajScopNew(n); ajStrAssignS(&scop->Entry, xentry); ajStrAssignS(&scop->Pdb, pdb); ajStrAssignS(&scop->Source, source); ajStrAssignS(&scop->Class, class); ajStrAssignS(&scop->Fold, fold); ajStrAssignS(&scop->Domain, domain); ajStrAssignS(&scop->Superfamily, super); ajStrAssignS(&scop->Family, family); ajStrAssignS(&scop->Acc, Acc); ajStrAssignS(&scop->Spr, Spr); ajStrAssignS(&scop->Sse, sse); ajStrAssignS(&scop->Sss, sss); ajStrAssignS(&scop->SeqPdb, SeqPdb); ajStrAssignS(&scop->SeqSpr, SeqSpr); scop->Sunid_Class = Sunid_Class; scop->Sunid_Fold = Sunid_Fold; scop->Sunid_Superfamily = Sunid_Superfamily; scop->Sunid_Family = Sunid_Family; scop->Sunid_Domain = Sunid_Domain; scop->Sunid_Source = Sunid_Source; scop->Sunid_Domdat = Sunid_Domdat; scop->Startd = Startd ; scop->Endd = Endd; } else if(ajStrPrefixC(line, "CN")) { p = ajStrGetPtr(str); sscanf(p, "[%d]", &idx); } else if(ajStrPrefixC(line, "CH")) { if(!ajRegExec(exp2, str)) return NULL; ajRegSubI(exp2, 1, &stmp); scop->Chain[idx - 1] = *ajStrGetPtr(stmp); ajRegSubI(exp2, 2, &str); ajStrAssignC(&(scop)->Start[idx - 1], ajStrGetPtr(str)); ajRegSubI(exp2, 3, &str); ajStrAssignC(&(scop)->End[idx - 1], ajStrGetPtr(str)); } /* Sequence from pdb file */ else if(ajStrPrefixC(line, "DS")) { while((ok = ajReadlineTrim(inf, &line)) && !ajStrPrefixC(line, "XX")) ajStrAppendC(&SeqPdb, ajStrGetPtr(line)); ajStrRemoveWhite(&SeqPdb); continue; } /* Sequence from swissprot */ else if(ajStrPrefixC(line, "SQ")) { while((ok = ajReadlineTrim(inf, &line)) && !ajStrPrefixC(line, "XX")) ajStrAppendC(&SeqSpr, ajStrGetPtr(line)); ajStrRemoveWhite(&SeqSpr); continue; } /* Accession number */ else if(ajStrPrefixC(line, "AC")) ajFmtScanS(line, "%*s %S", &Acc); /* Swissprot code */ else if(ajStrPrefixC(line, "SF")) ajFmtScanS(line, "%*s %S", &Spr); /* SSE string */ else if(ajStrPrefixC(line, "SE")) ajFmtScanS(line, "%*s %S", &sse); /* SSS string */ else if(ajStrPrefixC(line, "SS")) { while((ok = ajReadlineTrim(inf, &line)) && !ajStrPrefixC(line, "XX")) ajStrAppendC(&sss, ajStrGetPtr(line)); ajStrRemoveWhite(&sss); continue; } /* Start and end relative to swissprot sequence */ else if(ajStrPrefixC(line, "RA")) ajFmtScanS(line, "%*s %d %*s %d", &Startd, &Endd); /* Sunid of domain data */ else if(ajStrPrefixC(line, "SI")) ajFmtScanS(line, "%*s %d %*s %d %*s %d %*s " "%d %*s %d %*s %d %*s %d", &Sunid_Class, &Sunid_Fold, &Sunid_Superfamily, &Sunid_Family, &Sunid_Domain, &Sunid_Source, &Sunid_Domdat); ok = ajReadlineTrim(inf, &line); } ajStrDel(&SeqSpr); ajStrDel(&SeqPdb); ajStrDel(&sss); return scop; } ID ajDomainDel TY public MO ajdomain LB core XX DE Destructor for an AJAX Domain object. XX PN [1] PA d Pdomain AjPDomain* PD AJAX Domain address PX RT void RD RX CA delete CT AjPDomain CD Default AJAX Domain destructor. CX // void ajDomainDel(AjPDomain *Pdomain) { AjPDomain domain = NULL; if(!Pdomain || !*Pdomain) return; domain = *Pdomain; if(domain->Type == ajEDomainTypeSCOP) ajScopDel(&domain->Scop); else ajCathDel(&domain->Cath); AJFREE(domain); *Pdomain = NULL; return; } ID ajScopDel TY public MO ajdomain LB core XX DE Destructor for an AJAX SCOP object. XX PN [1] PA d Pscop AjPScop* PD AJAX SCOP address PX RT void RD RX CA delete CT AjPScop CD Default AJAX SCOP destructor. CX // void ajScopDel(AjPScop *Pscop) { ajuint i = 0U; AjPScop scop = NULL; if(!Pscop || !*Pscop) return; scop = *Pscop; ajStrDel(&scop->Entry); ajStrDel(&scop->Pdb); ajStrDel(&scop->Class); ajStrDel(&scop->Fold); ajStrDel(&scop->Superfamily); ajStrDel(&scop->Family); ajStrDel(&scop->Domain); ajStrDel(&scop->Source); ajStrDel(&scop->Acc); ajStrDel(&scop->Spr); ajStrDel(&scop->SeqPdb); ajStrDel(&scop->SeqSpr); ajStrDel(&scop->Sse); ajStrDel(&scop->Sss); if(scop->Number) { for(i = 0U; i < scop->Number; i++) { ajStrDel(&scop->Start[i]); ajStrDel(&scop->End[i]); } AJFREE(scop->Start); AJFREE(scop->End); AJFREE(scop->Chain); } AJFREE(scop); *Pscop = NULL; return; } ID ajCathDel TY public MO ajdomain LB core XX DE Destructor for an AJAX CATH object. XX PN [1] PA d Pcath AjPCath* PD AJAX CATH address PX RT void RD RX CA delete CT AjPCath CD Default AJAX CATH destructor. CX // void ajCathDel(AjPCath *Pcath) { ajuint i = 0U; AjPCath cath = NULL; if(!Pcath || !*Pcath) return; cath = *Pcath; ajStrDel(&cath->DomainID); ajStrDel(&cath->Pdb); ajStrDel(&cath->Class); ajStrDel(&cath->Architecture); ajStrDel(&cath->Topology); ajStrDel(&cath->Superfamily); ajStrDel(&cath->Acc); ajStrDel(&cath->Spr); ajStrDel(&cath->SeqPdb); ajStrDel(&cath->SeqSpr); if(cath->NSegment) { for(i = 0U; i < cath->NSegment; i++) { ajStrDel(&cath->Start[i]); ajStrDel(&cath->End[i]); } AJFREE(cath->Start); AJFREE(cath->End); } AJFREE(cath); *Pcath = NULL; return; } ID ajDomainCopy TY public MO ajdomain LB core XX DE Copies the contents from one AJAX Domain object to another. XX PN [1] PA w Pto AjPDomain* PD AJAX Domain address PX PN [2] PA r from const AjPDomain PD AJAX Domain PX RT AjBool RD True if copy was successful. RX CA assign CT AjPDomain CD Replicates an AJAX Domain object. CX // AjBool ajDomainCopy(AjPDomain *Pto, const AjPDomain from) { ajuint n = 0U; /* Check args */ if(!from) return ajFalse; if((*Pto)) ajDomainDel(Pto); if(from->Type == ajEDomainTypeSCOP) n = from->Scop->Number; else n = from->Cath->NSegment; (*Pto) = ajDomainNew(n, from->Type); (*Pto)->Type = from->Type; if(from->Type == ajEDomainTypeSCOP) ajScopCopy(&((*Pto)->Scop), from->Scop); else ajCathCopy(&((*Pto)->Cath), from->Cath); return ajTrue; } ID ajCathCopy TY public MO ajdomain LB core XX DE Copies the contents from one AJAX CATH object to another. XX PN [1] PA w Pto AjPCath* PD AJAX Cath address PX PN [2] PA r from const AjPCath PD AJAX Cath PX RT AjBool RD True if copy was successful. RX CA assign CT AjPCath CD Replicates an AJAX CATH object. CX // AjBool ajCathCopy(AjPCath *Pto, const AjPCath from) { ajuint i = 0U; /* Check args */ if(!from) return ajFalse; if(!*Pto) *Pto = ajCathNew(from->NSegment); ajStrAssignS(&(*Pto)->DomainID, from->DomainID); ajStrAssignS(&(*Pto)->Pdb, from->Pdb); ajStrAssignS(&(*Pto)->Class, from->Class); ajStrAssignS(&(*Pto)->Architecture, from->Architecture); ajStrAssignS(&(*Pto)->Topology, from->Topology); ajStrAssignS(&(*Pto)->Superfamily, from->Superfamily); (*Pto)->Length = from->Length; (*Pto)->Chain = from->Chain; for(i = 0U; i < from->NSegment; i++) { ajStrAssignS(&(*Pto)->Start[i], from->Start[i]); ajStrAssignS(&(*Pto)->End[i], from->End[i]); } ajStrAssignS(&(*Pto)->Acc, from->Acc); ajStrAssignS(&(*Pto)->Spr, from->Spr); ajStrAssignS(&(*Pto)->SeqPdb, from->SeqPdb); ajStrAssignS(&(*Pto)->SeqSpr, from->SeqSpr); (*Pto)->Startd = from->Startd; (*Pto)->Endd = from->Endd; (*Pto)->Class_Id = from->Class_Id; (*Pto)->Arch_Id = from->Arch_Id; (*Pto)->Topology_Id = from->Topology_Id; (*Pto)->Superfamily_Id = from->Superfamily_Id; (*Pto)->Family_Id = from->Family_Id; (*Pto)->NIFamily_Id = from->NIFamily_Id; (*Pto)->IFamily_Id = from->IFamily_Id; return ajTrue; } ID ajScopCopy TY public MO ajdomain LB core XX DE Copies the contents from one AJAX SCOP object to another. XX PN [1] PA w Pto AjPScop* PD AJAX SCOP address PX PN [2] PA r from const AjPScop PD AJAX SCOP PX RT AjBool RD True if copy was successful. RX CA assign CT AjPScop CD Replicates an AJAX SCOP object. CX // AjBool ajScopCopy(AjPScop *Pto, const AjPScop from) { ajuint i = 0U; /* Check args */ if(!from) return ajFalse; if(!*Pto) *Pto = ajScopNew(from->Number); ajStrAssignS(&(*Pto)->Entry, from->Entry); ajStrAssignS(&(*Pto)->Pdb, from->Pdb); ajStrAssignS(&(*Pto)->Class, from->Class); ajStrAssignS(&(*Pto)->Fold, from->Fold); ajStrAssignS(&(*Pto)->Superfamily, from->Superfamily); ajStrAssignS(&(*Pto)->Family, from->Family); ajStrAssignS(&(*Pto)->Domain, from->Domain); ajStrAssignS(&(*Pto)->Source, from->Source); ajStrAssignS(&(*Pto)->Sse, from->Sse); ajStrAssignS(&(*Pto)->Sss, from->Sss); for(i = 0U; i < from->Number; i++) { (*Pto)->Chain[i] = from->Chain[i]; ajStrAssignS(&(*Pto)->Start[i], from->Start[i]); ajStrAssignS(&(*Pto)->End[i], from->End[i]); } ajStrAssignS(&(*Pto)->Acc, from->Acc); ajStrAssignS(&(*Pto)->Spr, from->Spr); ajStrAssignS(&(*Pto)->SeqPdb, from->SeqPdb); ajStrAssignS(&(*Pto)->SeqSpr, from->SeqSpr); (*Pto)->Startd = from->Startd; (*Pto)->Endd = from->Endd; (*Pto)->Sunid_Class = from->Sunid_Class; (*Pto)->Sunid_Fold = from->Sunid_Fold; (*Pto)->Sunid_Superfamily = from->Sunid_Superfamily; (*Pto)->Sunid_Family = from->Sunid_Family; (*Pto)->Sunid_Domain = from->Sunid_Domain; (*Pto)->Sunid_Source = from->Sunid_Source; (*Pto)->Sunid_Domdat = from->Sunid_Domdat; (*Pto)->Score = from->Score; return ajTrue; } ID ajScopMatchSunid TY public MO ajdomain LB core XX DE Function to compare AJAX SCOP objects by Sunid_Family member. XX PN [1] PA r item1 const void* PD AJAX SCOP address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP address 2 PX RT ajint RD -1 if Start1 should sort before Start2, +1 if the Start2 RD should sort first. 0 if they are identical. RX CA use CT AjPScop CD Sort Scop objects by Sunid_Family member. CX // ajint ajScopMatchSunid(const void *item1, const void *item2) { AjPScop scop1 = *(AjOScop *const *) item1; AjPScop scop2 = *(AjOScop *const *) item2; if(scop1->Sunid_Family < scop2->Sunid_Family) return -1; else if(scop1->Sunid_Family == scop2->Sunid_Family) return 0; return 1; } ID ajScopMatchScopid TY public MO ajdomain LB core XX DE Function to comapre AJAX SCOP objects by Entry member. XX PN [1] PA r item1 const void* PD AJAX SCOP address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP address 2 PX RT ajint RD -1 if Entry1 should sort before Entry2, +1 if the Entry2 RD should sort first. 0 if they are identical in length and content. RX CA use CT AjPScop CD Sort Scop objects by Entry member. CX // ajint ajScopMatchScopid(const void *item1, const void *item2) { AjPScop scop1 = *(AjOScop *const *) item1; AjPScop scop2 = *(AjOScop *const *) item2; return ajStrCmpS(scop1->Entry, scop2->Entry); } ID ajScopMatchPdbId TY public MO ajdomain LB core XX DE Function to compare AJAX SCOP objects by Pdb member. XX PN [1] PA r item1 const void* PD AJAX SCOP address 1 PX PN [2] PA r item2 const void* PD AJAX SCOP address 2 PX RT ajint RD -1 if Pdb1 should sort before Pdb2, +1 if the Pdb2 RD should sort first. 0 if they are identical in length and content. RX CA use CT AjPScop CD Sort Scop objects by Pdb member. CX // ajint ajScopMatchPdbId(const void *item1, const void *item2) { AjPScop scop1 = *(AjOScop *const *) item1; AjPScop scop2 = *(AjOScop *const *) item2; return ajStrCmpS(scop1->Pdb, scop2->Pdb); } ID ajCathMatchPdbId TY public MO ajdomain LB core XX DE Function to compare AJAX CATH objects by Pdb member. XX PN [1] PA r item1 const void* PD AJAX CATH address 1 PX PN [2] PA r item2 const void* PD AJAX CATH address 2 PX RT ajint RD -1 if Pdb1 should sort before Pdb2, +1 if the Pdb2 RD should sort first. 0 if they are identical in length and content. RX CA use CT AjPScop CD Sort AJAX CATH objects by Pdb member. CX // ajint ajCathMatchPdbId(const void *item1, const void *item2) { AjPCath cath1 = *(AjOCath *const *) item1; AjPCath cath2 = *(AjOCath *const *) item2; return ajStrCmpS(cath1->Pdb, cath2->Pdb); } ID ajDomainGetId TY public MO ajdomain LB core XX DE Returns domain id, either DomainID element (Cath object) or Entry DE (Scop object). XX PN [1] PA r domain const AjPDomain PD AJAX Domain PX RT AjPStr RD DomainID, Entry or NULL on error RX // AjPStr ajDomainGetId(const AjPDomain domain) { if(!domain) { ajWarn("Bad args to ajDomainGetId"); return NULL; } if(domain->Type == ajEDomainTypeSCOP) return(domain->Scop->Entry); else if(domain->Type == ajEDomainTypeCATH) return(domain->Cath->DomainID); else ajWarn("Domain type not resolved in ajDomainGetId"); return NULL; } ID ajDomainGetSeqPdb TY public MO ajdomain LB core XX DE Returns the pdb sequence from an AJAX Domain object. XX PN [1] PA r domain const AjPDomain PD AJAX Domain PX RT AjPStr RD PDB Sequence or NULL on error RX // AjPStr ajDomainGetSeqPdb(const AjPDomain domain) { if(!domain) { ajWarn("Bad args to ajDomainGetSeqPdb"); return NULL; } if(domain->Type == ajEDomainTypeSCOP) return(domain->Scop->SeqPdb); else if(domain->Type == ajEDomainTypeCATH) return(domain->Cath->SeqPdb); else ajWarn("Domain type not resolved in ajDomainGetSeqPdb"); return NULL; } ID ajDomainGetSeqSpr TY public MO ajdomain LB core XX DE Returns the swissprot sequence from an AJAX Domain object. XX PN [1] PA r domain const AjPDomain PD AJAX Domain PX RT AjPStr RD Swiss-Prot sequence or NULL on error RX // AjPStr ajDomainGetSeqSpr(const AjPDomain domain) { if(!domain) { ajWarn("Bad args to ajDomainGetSeqSpr"); return NULL; } if(domain->Type == ajEDomainTypeSCOP) return(domain->Scop->SeqSpr); else if(domain->Type == ajEDomainTypeCATH) return(domain->Cath->SeqSpr); else ajWarn("Domain type not resolved in ajDomainGetSeqSpr"); return NULL; } ID ajDomainGetPdb TY public MO ajdomain LB core XX DE Returns the Pdb element from an AJAX Domain object. XX PN [1] PA r domain const AjPDomain PD AJAX Domain PX RT AjPStr RD Pdb or NULL on error. RX // AjPStr ajDomainGetPdb(const AjPDomain domain) { if(!domain) { ajWarn("Bad args to ajDomainGetSeqPdb"); return NULL; } if(domain->Type == ajEDomainTypeSCOP) return(domain->Scop->Pdb); else if(domain->Type == ajEDomainTypeCATH) return(domain->Cath->Pdb); else ajWarn("Domain type not resolved in ajDomainGetSeqPdb"); return NULL; } ID ajDomainGetAcc TY public MO ajdomain LB core XX DE Returns the Acc element from a Domain object. XX PN [1] PA r domain const AjPDomain PD AJAX Domain PX RT AjPStr RD Acc or NULL on error RX // AjPStr ajDomainGetAcc(const AjPDomain domain) { if(!domain) { ajWarn("Bad args to ajDomainGetSeqPdb"); return NULL; } if(domain->Type == ajEDomainTypeSCOP) return(domain->Scop->Acc); else if(domain->Type == ajEDomainTypeCATH) return(domain->Cath->Acc); else ajWarn("Domain type not resolved in ajDomainGetSeqPdb"); return NULL; } ID ajDomainGetSpr TY public MO ajdomain LB core XX DE Returns the Spr element from an AJAX Domain object. XX PN [1] PA r domain const AjPDomain PD AJAX Domain PX RT AjPStr RD Spr or NULL on error RX // AjPStr ajDomainGetSpr(const AjPDomain domain) { if(!domain) { ajWarn("Bad args to ajDomainGetSeqPdb"); return NULL; } if(domain->Type == ajEDomainTypeSCOP) return(domain->Scop->Spr); else if(domain->Type == ajEDomainTypeCATH) return(domain->Cath->Spr); else ajWarn("Domain type not resolved in ajDomainGetSeqPdb"); return NULL; } ID ajDomainGetN TY public MO ajdomain LB core XX DE Returns no. chains or chain segments in a domain, either NSegment element DE (Cath domains) or N element (Scop domains). XX PN [1] PA r domain const AjPDomain PD AJAX Domain PX RT ajint RD Or -1 on error. RX // ajint ajDomainGetN(const AjPDomain domain) { if(!domain) { ajWarn("Bad args to ajDomainGetSeqPdb"); return -1; } if(domain->Type == ajEDomainTypeSCOP) return(domain->Scop->Number); else if(domain->Type == ajEDomainTypeCATH) return(domain->Cath->NSegment); else ajWarn("Domain type not resolved in ajDomainGetSeqPdb"); return -1; } ID ajScopArrFindScopid TY public MO ajdomain LB core XX DE Performs a binary search for a SCOP domain id over an array of Scop DE structures (which of course must first have been sorted). This is a DE case-insensitive search. XX PN [1] PA r array AjPScop const* PD Array of AJAX SCOP objects PX PN [2] PA r size ajint PD Size of array PX PN [3] PA r identifier const AjPStr PD Search term PX RT ajint RD Index of first Scop object found with a SCOP domain id RD matching id, or -1 if id is not found. RX CA use CT AjPScop* CD Binary search for Entry element over array of Scop objects. CX // ajint ajScopArrFindScopid(AjPScop const *array, ajint size, const AjPStr identifier) { ajint l = 0; ajint m = 0; ajint h = size - 1; ajint c = 0; while(l <= h) { m = (l + h) >> 1; if((c = ajStrCmpCaseS(identifier, array[m]->Entry)) < 0) h = m - 1; else if(c > 0) l = m + 1; else return m; } return -1; } ID ajScopArrFindSunid TY public MO ajdomain LB core XX DE Performs a binary search for a SCOP sunid over an array of Scop DE objects (which of course must first have been sorted). This is a DE case-insensitive search. XX PN [1] PA r array AjPScop const* PD Array of AJAX SCOP objects PX PN [2] PA r size ajint PD Size of array PX PN [3] PA r identifier ajint PD Search term PX RT ajint RD Index of first Scop object found with an PDB code RD matching id, or -1 if id is not found. RX CA use CT AjPScop* CD Binary search for Sunid_Family element over array of Scop objects. CX // ajint ajScopArrFindSunid(AjPScop const *array, ajint size, ajint identifier) { ajint l = 0; ajint m = 0; ajint h = size - 1; while(l <= h) { m = (l + h) >> 1; if(identifier < array[m]->Sunid_Family) h = m - 1; else if(identifier > array[m]->Sunid_Family) l = m + 1; else return m; } return -1; } ID ajScopArrFindPdbid TY public MO ajdomain LB core XX DE Performs a binary search for a SCOP domain id over an array of Scop DE objects (which of course must first have been sorted). This is a DE case-insensitive search. XX PN [1] PA r array AjPScop const* PD Array of AJAX SCOP objects PX PN [2] PA r size ajint PD Size of array PX PN [3] PA r identifier const AjPStr PD Search term PX RT ajint RD Index of first Scop object found with a PDB code RD matching id, or -1 if id is not found. RX CA use CT AjPScop* CD Binary search for Pdb element over array of Scop objects. CX // ajint ajScopArrFindPdbid(AjPScop const *array, ajint size, const AjPStr identifier) { ajint l = 0; ajint m = 0; ajint h = size - 1; ajint c = 0; while(l <= h) { m = (l + h) >> 1; if((c = ajStrCmpCaseS(identifier, array[m]->Pdb)) < 0) h = m - 1; else if(c > 0) l = m + 1; else return m; } return -1; } ID ajCathArrFindPdbid TY public MO ajdomain LB core XX DE Performs a binary search for a CATH domain id over an array of Cath DE structures (which of course must first have been sorted). This is a DE case-insensitive search. XX PN [1] PA r array AjPCath const* PD Array of AjPCath objects PX PN [2] PA r size ajint PD Size of array PX PN [3] PA r identifier const AjPStr PD Search term PX RT ajint RD Index of first Cath object found with a PDB code RD matching id, or -1 if id is not found. RX CA use CT AjPCath* CD Binary search for Pdb element over array of Cath objects. CX // ajint ajCathArrFindPdbid(AjPCath const *array, ajint size, const AjPStr identifier) { ajint l = 0; ajint m = 0; ajint h = size - 1; ajint c = 0; while(l <= h) { m= (l + h) >> 1; if((c = ajStrCmpCaseS(identifier, array[m]->Pdb)) < 0) h = m - 1; else if(c > 0) l = m + 1; else return m; } return -1; } ID ajPdbWriteDomain TY public MO ajdomain LB core XX DE Writes a clean coordinate file for a SCOP domain. Where coordinates for DE multiple models (e.g. NMR structures) are given, data for model 1 are DE written. DE In the clean file, the coordinates are presented as belonging to a single DE chain regardless of how many chains the domain comprised. DE Coordinates for heterogens are NOT written to file. XX PN [1] PA u outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD Pdb object PX PN [3] PA r scop const AjPScop PD Scop object PX PN [4] PA u errf AjPFile PD Output file stream for error messages PX RT AjBool RD True on success RX // AjBool ajPdbWriteDomain(AjPFile outf, const AjPPdb pdb, const AjPScop scop, AjPFile errf) { /* ** rn_mod is a modifier to the residue number to give correct residue ** numbering for the domain */ ajuint i = 0U; ajuint chn = 0U; ajint start = 0; ajint end = 0; ajuint finalrn = 0U; ajint rn_mod = 0; ajint last_rn = 0; ajint this_rn = 0; char id = '\0'; AjPStr tmpseq = NULL; AjPStr seq = NULL; AjPStr tmpstr = NULL; AjBool found_start = ajFalse; AjBool found_end = ajFalse; AjBool nostart = ajFalse; AjBool noend = ajFalse; AjIList iter = NULL; AjPAtom atom1 = NULL; AjPAtom atom2 = NULL; AjPResidue residue1 = NULL; AjPResidue residue2 = NULL; /* AjPResidue *resarr = NULL; */ AjPSeqout outseq = NULL; if(!errf || !scop || !pdb || !outf) ajFatal("Bad args passed to ajPdbWriteDomain"); /* Initialise strings */ seq = ajStrNew(); tmpseq = ajStrNew(); tmpstr = ajStrNew(); /* Check for unknown or zero-length chains */ for(i = 0U; i < scop->Number; i++) if(!ajPdbChnidToNum(scop->Chain[i], pdb, &chn)) { ajWarn("Chain incompatibility error in " "ajPdbWriteDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Chain incompatibility " "error in ajPdbWriteDomain\n", scop->Entry); ajStrDel(&seq); ajStrDel(&tmpseq); ajStrDel(&tmpstr); return ajFalse; } else if(pdb->Chains[chn - 1]->Nres == 0) { ajWarn("Chain length zero"); ajFmtPrintF(errf, "//\n%S\nERROR Chain length zero\n", scop->Entry); ajStrDel(&seq); ajStrDel(&tmpseq); ajStrDel(&tmpstr); return ajFalse; } /* Write header info. to domain coordinate file */ ajFmtPrintF(outf, "%-5s%S\n", "ID", scop->Entry); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5sCo-ordinates for SCOP domain %S\n", "DE", scop->Entry); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5sSee Escop.dat for domain classification\n", "OS"); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5sMETHOD ", "EX"); if(pdb->Method == ajEPdbMethodXray) ajFmtPrintF(outf, "xray; "); else ajFmtPrintF(outf, "nmr_or_model; "); ajFmtPrintF(outf, "RESO %.2f; NMOD 1; NCHN 1; NGRP 0;\n", pdb->Reso); /* The NCHN and NMOD are hard-coded to 1 for domain files */ /* Start of main application loop */ /* Print out data up to coordinates list */ for(i = 0U; i < scop->Number; i++, found_start = ajFalse, found_end = ajFalse, nostart = ajFalse, noend = ajFalse, last_rn = 0) { /* ** Unknown or Zero sized chains have already been checked for ** so no additional checking is needed here */ ajPdbChnidToNum(scop->Chain[i], pdb, &chn); /* Initialise the iterator */ iter = ajListIterNewread(pdb->Chains[chn - 1]->Residues); /* ** If start of domain is unspecified ** then assign start to first residue in chain */ if(!ajStrCmpC(scop->Start[i], ".")) { nostart = ajTrue; start = 1; found_start = ajTrue; } /* ** If end of domain is unspecified ** then assign end to last residue in chain */ if(!ajStrCmpC(scop->End[i], ".")) { noend = ajTrue; end = pdb->Chains[chn - 1]->Nres; found_end = ajTrue; } /* Find start and end of domains in chain */ if(!found_start || !found_end) { /* Iterate through the list of atoms */ while((residue1 = (AjPResidue) ajListIterGet(iter))) { /* ** Hard-coded to work on model 1 ** Break if model no. != 1 */ if(residue1->Mod != 1 || (found_start && found_end)) break; /* if(residue1->Type != 'P' || residue1->Mod != 1 || (found_start && found_end)) break; */ /* If we are onto a new residue */ this_rn = residue1->Idx; if(this_rn != last_rn) { last_rn = this_rn; /* ** The start position was specified, but has not ** been found yet */ if(!found_start && !nostart) { ajStrAssignS(&tmpstr, scop->Start[i]); ajStrAppendK(&tmpstr, '*'); /* Start position found */ /* if(!ajStrCmpCaseS(residue1->Pdb, scop->Start[i])) */ if(ajStrMatchWildS(residue1->Pdb, tmpstr)) { if(!ajStrMatchS(residue1->Pdb, scop->Start[i])) { ajWarn("Domain start found by wildcard " "match only " "in ajPdbWriteDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain " "start found " "by wildcard match only in " "ajPdbWriteDomain\n", scop->Entry); } start = residue1->Idx; found_start = ajTrue; } else continue; } /* ** The end position was specified, but has not ** been found yet */ if(!found_end && !noend) { ajStrAssignS(&tmpstr, scop->End[i]); ajStrAppendK(&tmpstr, '*'); /* End position found */ /* if(!ajStrCmpCaseS(residue1->Pdb, scop->End[i])) */ if(ajStrMatchWildS(residue1->Pdb, tmpstr)) { if(!ajStrMatchS(residue1->Pdb, scop->End[i])) { ajWarn("Domain end found by wildcard " "match only " "in ajPdbWriteDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain end " "found " "by wildcard match only in " "ajPdbWriteDomain\n", scop->Entry); } end = residue1->Idx; found_end = ajTrue; break; } } } } } /* Diagnostics if start position was not found */ if(!found_start) { ajStrDel(&seq); ajStrDel(&tmpseq); ajStrDel(&tmpstr); ajListIterDel(&iter); ajWarn("Domain start not found in ajPdbWriteDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain start not found " "in in ajPdbWriteDomain\n", scop->Entry); return ajFalse; } /* Diagnostics if end position was not found */ if(!found_end) { ajStrDel(&seq); ajStrDel(&tmpseq); ajStrDel(&tmpstr); ajListIterDel(&iter); ajWarn("Domain end not found in ajPdbWriteDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain end not found " "in ajPdbWriteDomain\n", scop->Entry); return ajFalse; } /* Write string here */ ajStrAssignSubS(&tmpseq, pdb->Chains[chn - 1]->Seq, start - 1, end - 1); ajStrAppendS(&seq, tmpseq); /* Free the iterator */ ajListIterDel(&iter); } /* End of main application loop */ /* ** If the domain was composed of more than once chain then a '.' is ** given as the chain identifier */ if(scop->Number > 1) id = '.'; else { id = pdb->Chains[chn - 1]->Id; if(id == ' ') id = '.'; } /* Write sequence to domain coordinate file */ ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5s[1]\n", "CN"); ajFmtPrintF(outf, "XX\n"); /* ajFmtPrintF(outf, "%-5sID %c; NR %d; NL 0; NH %d; NE %d; NS %d; NT %d;\n", "IN", id, ajStrGetLen(seq), pdb->Chains[chn - 1]->numHelices, pdb->Chains[chn - 1]->numStrands, pdb->Chains[chn - 1]->numSheets, pdb->Chains[chn - 1]->numTurns); */ ajFmtPrintF(outf, "%-5sID %c; NRES %d; NL 0; NH %d; NE %d;\n", "IN", id, ajStrGetLen(seq), pdb->Chains[chn - 1]->numHelices, pdb->Chains[chn - 1]->numStrands); ajFmtPrintF(outf, "XX\n"); outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, seq, "SQ"); ajSeqoutDel(&outseq); ajFmtPrintF(outf, "XX\n"); /* Write residue list to domain coordinate file */ for(nostart = ajFalse, noend = ajFalse, i = 0U; i < scop->Number; i++, found_start = ajFalse, found_end = ajFalse) { /* ** Unknown or Zero length chains have already been checked for ** so no additional checking is needed here */ ajPdbChnidToNum(scop->Chain[i], pdb, &chn); /* Initialise the iterator */ iter = ajListIterNewread(pdb->Chains[chn - 1]->Residues); /* Increment res. counter from last chain if appropriate */ if(noend) rn_mod += residue2->Idx; else rn_mod += finalrn; /* Check whether start and end of domain are specified */ if(!ajStrCmpC(scop->Start[i], ".")) nostart = ajTrue; else nostart = ajFalse; if(!ajStrCmpC(scop->End[i], ".")) noend = ajTrue; else noend = ajFalse; /* Iterate through the list of residues */ while((residue1 = (AjPResidue) ajListIterGet(iter))) { /* Break if model no. != 1 */ if(residue1->Mod != 1) break; /* The start position has not been found yet */ if(!found_start) { /* Start position was specified */ if(!nostart) { ajStrAssignS(&tmpstr, scop->Start[i]); ajStrAppendK(&tmpstr, '*'); /* Start position found */ /* if(!ajStrCmpCaseS(residue1->Pdb, scop->Start[i])) */ if(ajStrMatchWildS(residue1->Pdb, tmpstr)) { if(!ajStrMatchS(residue1->Pdb, scop->Start[i])) { ajWarn("Domain start found by wildcard match only " "in ajPdbWriteDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain " "start found " "by wildcard match only in " "ajPdbWriteDomain\n", scop->Entry); } rn_mod -= residue1->Idx-1; found_start = ajTrue; } else continue; } else found_start = ajTrue; } /* ** The end position was specified, but has not ** been found yet */ if(!found_end && !noend) { ajStrAssignS(&tmpstr, scop->End[i]); ajStrAppendK(&tmpstr, '*'); /* End position found */ /* if(!ajStrCmpCaseS(residue1->Pdb, scop->End[i])) */ if(ajStrMatchWildS(residue1->Pdb, tmpstr)) { if(!ajStrMatchS(residue1->Pdb, scop->End[i])) { ajWarn("Domain end found by wildcard match only " "in ajPdbWriteDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain end found " "by wildcard match only in " "ajPdbWriteDomain\n", scop->Entry); } found_end = ajTrue; finalrn = residue1->Idx; } } /* ** The end position was specified and has been found, and ** the current atom no longer belongs to this final residue */ else if(residue1->Idx != finalrn && !noend) break; ajFmtPrintF(outf, "%-5s%-5d%-5d%-5d%-6S%-2c%-6S", "RE", residue1->Mod, /* It will always be 1 */ 1, /* chn number is always given as 1 */ residue1->Idx+rn_mod, residue1->Pdb, residue1->Id1, residue1->Id3); if(residue1->eNum != 0) ajFmtPrintF(outf, "%-5d", residue1->eNum); else ajFmtPrintF(outf, "%-5c", '.'); ajFmtPrintF(outf, "%-5S%-5c", residue1->eId, residue1->eType); if(residue1->eType == 'H') ajFmtPrintF(outf, "%-5d", residue1->eClass); else ajFmtPrintF(outf, "%-5c", '.'); if(residue1->eStrideNum != 0) ajFmtPrintF(outf, "%-5d", residue1->eStrideNum); else ajFmtPrintF(outf, "%-5c", '.'); ajFmtPrintF(outf, "%-5c", residue1->eStrideType); ajFmtPrintF(outf, "%8.2f%8.2f%8.2f%8.2f%8.2f" "%8.2f%8.2f%8.2f%8.2f%8.2f%8.2f%8.2f" "%8.2f\n", residue1->Phi, residue1->Psi, residue1->Area, residue1->all_abs, residue1->all_rel, residue1->side_abs, residue1->side_rel, residue1->main_abs, residue1->main_rel, residue1->npol_abs, residue1->npol_rel, residue1->pol_abs, residue1->pol_rel); /* Assign pointer for this chain */ residue2 = residue1; } ajListIterDel(&iter); } /* Write atom list to domain coordinate file */ for(nostart = ajFalse, noend = ajFalse, i = 0U; i < scop->Number; i++, found_start = ajFalse, found_end = ajFalse) { /* ** Unknown or Zero length chains have already been checked for ** so no additional checking is needed here */ ajPdbChnidToNum(scop->Chain[i], pdb, &chn); /* if(resarr) AJFREE(resarr); ajListToarray(pdb->Chains[chn-1]->Residues, (void ***) &resarr); */ /* Initialise the iterator */ iter = ajListIterNewread(pdb->Chains[chn-1]->Atoms); /* Increment res. counter from last chain if appropriate */ if(noend) rn_mod += atom2->Idx; else rn_mod += finalrn; /* Check whether start and end of domain are specified */ if(!ajStrCmpC(scop->Start[i], ".")) nostart = ajTrue; else nostart = ajFalse; if(!ajStrCmpC(scop->End[i], ".")) noend = ajTrue; else noend = ajFalse; /* Iterate through the list of atoms */ while((atom1 = (AjPAtom) ajListIterGet(iter))) { /* ** Continue if a non-protein atom is found or break if ** model no. != 1 */ if(atom1->Mod != 1) break; if(atom1->Type != 'P') continue; /* if(atom1->Mod != 1 || atom1->Type != 'P') break; */ /* The start position has not been found yet */ if(!found_start) { /* Start position was specified */ if(!nostart) { ajStrAssignS(&tmpstr, scop->Start[i]); ajStrAppendK(&tmpstr, '*'); /* Start position found */ /* if(!ajStrCmpCaseS(atom1->Pdb, scop->Start[i])) */ if(ajStrMatchWildS(atom1->Pdb, tmpstr)) /* if(ajStrMatchWildS(resarr[atom1->Idx - 1]->Pdb, tmpstr)) */ { if(!ajStrMatchS(atom1->Pdb, scop->Start[i])) /* if(!ajStrMatchS(resarr[atom1->Idx - 1]->Pdb, scop->Start[i])) */ { ajWarn("Domain start found by wildcard match only " "in ajPdbWriteDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain " "start found " "by wildcard match only in " "ajPdbWriteDomain\n", scop->Entry); } rn_mod -= atom1->Idx-1; found_start = ajTrue; } else continue; } else found_start = ajTrue; } /* ** The end position was specified, but has not ** been found yet */ if(!found_end && !noend) { ajStrAssignS(&tmpstr, scop->End[i]); ajStrAppendK(&tmpstr, '*'); /* End position found */ /* if(!ajStrCmpCaseS(atom1->Pdb, scop->End[i])) */ if(ajStrMatchWildS(atom1->Pdb, tmpstr)) /* if(ajStrMatchWildS(resarr[atom1->Idx - 1]->Pdb, tmpstr)) */ { if(!ajStrMatchS(atom1->Pdb, scop->End[i])) /* if(!ajStrMatchS(resarr[atom1->Idx - 1]->Pdb, scop->End[i])) */ { ajWarn("Domain end found by wildcard match only " "in ajPdbWriteDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain end found " "by wildcard match only in " "ajPdbWriteDomain\n", scop->Entry); } found_end = ajTrue; finalrn = atom1->Idx; } } /* ** The end position was specified and has been found, and ** the current atom no longer belongs to this final residue */ else if(atom1->Idx != finalrn && !noend) break; /* Print out coordinate line */ ajFmtPrintF(outf, "%-5s%-5d%-5d%-5c%-5d%-6S%-2c%-6S%-2c%-6S" "%9.3f%9.3f%9.3f%8.2f%8.2f\n", "AT", atom1->Mod, /* It will always be 1 */ atom1->Chn, /* chn number is always given as 1 */ '.', atom1->Idx+rn_mod, atom1->Pdb, atom1->Id1, atom1->Id3, atom1->Type, atom1->Atm, atom1->X, atom1->Y, atom1->Z, atom1->O, atom1->B); /* Assign pointer for this chain */ atom2 = atom1; } ajListIterDel(&iter); } /* Write last line in file */ ajFmtPrintF(outf, "//\n"); /* Tidy up */ /* if(resarr) AJFREE(resarr); */ ajStrDel(&seq); ajStrDel(&tmpseq); ajStrDel(&tmpstr); return ajTrue; } ID ajCathWrite TY public MO ajdomain LB core XX DE Write contents of an AJAX CATH object to an output DCF file (domain DE classification file) in DCF format (see documentation for DOMAINATRIX DE "cathparse" application). XX PN [1] PA u outf AjPFile PD AJAX Output file stream PX PN [2] PA r cath const AjPCath PD AJAX CATH PX RT AjBool RD True if file was written ok. RX // AjBool ajCathWrite(AjPFile outf, const AjPCath cath) { ajuint i = 0U; AjPStr tmp = NULL; AjPSeqout outseq = NULL; /* Check args */ if(!outf || !cath) { ajWarn("Bad args passed to ajCathWrite"); return ajFalse; } tmp = ajStrNew(); ajStrAssignS(&tmp, cath->DomainID); ajStrFmtUpper(&tmp); ajFmtPrintF(outf, "ID %S\nXX\n", tmp); ajStrAssignS(&tmp, cath->Pdb); ajStrFmtUpper(&tmp); ajFmtPrintF(outf, "EN %S\nXX\n", tmp); ajFmtPrintF(outf, "TY CATH\nXX\n"); ajFmtPrintF(outf, "CI %d CL; %d AR; %d TP; %d SF; %d FA; %d NI;" "%d IF;\nXX\n", cath->Class_Id, cath->Arch_Id, cath->Topology_Id, cath->Superfamily_Id, cath->Family_Id, cath->NIFamily_Id, cath->IFamily_Id); ajFmtPrintF(outf, "CL %S\n", cath->Class); ajFmtPrintF(outf, "XX\n"); ajFmtPrintSplit(outf, cath->Architecture, "AR ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); ajFmtPrintSplit(outf, cath->Topology, "TP ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); ajFmtPrintSplit(outf, cath->Superfamily, "SF ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); if(ajStrGetLen(cath->SeqPdb)) { ajFmtPrintF(outf, "XX\n"); outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, cath->SeqPdb, "DS"); ajSeqoutDel(&outseq); } ajFmtPrintF(outf, "NR %d\n", cath->Length); /* ajFmtPrintF(outf, "CL %S", cath->Class); ajFmtPrintSplit(outf, cath->Architecture, "\nXX\nAR ", 75, " \t\n\r"); ajFmtPrintSplit(outf, cath->Topology, "XX\nTP ", 75, " \t\n\r"); ajFmtPrintSplit(outf, cath->Superfamily, "XX\nSF ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\nNR %d\n", cath->Length); */ if(ajStrGetLen(cath->Acc)) ajFmtPrintF(outf, "XX\nAC %S\n", cath->Acc); if(ajStrGetLen(cath->Spr)) ajFmtPrintF(outf, "XX\nSP %S\n", cath->Spr); if(ajStrGetLen(cath->SeqSpr)) { ajFmtPrintF(outf, "XX\n%-5s%d START; %d END;\n", "RA", cath->Startd, cath->Endd); ajFmtPrintF(outf, "XX\n"); outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, cath->SeqSpr, "SQ"); ajSeqoutDel(&outseq); } ajFmtPrintF(outf, "XX\nNC %d\n", cath->NSegment); for(i = 0U; i < cath->NSegment; i++) { ajFmtPrintF(outf, "XX\nCN [%d]\n", i + 1); ajFmtPrintF(outf, "XX\nCH %c CHAIN; %S START; %S END;\n", cath->Chain, cath->Start[i], cath->End[i]); } ajFmtPrintF(outf, "//\n"); ajStrDel(&tmp); return ajTrue; } ID ajDomainWrite TY public MO ajdomain LB core XX DE Write contents of an AJAX Domain object to an output file in DCF format DE (see documentation for DOMAINATRIX "scopparse" application). XX PN [1] PA u outf AjPFile PD Output file stream PX PN [2] PA r domain const AjPDomain PD AJAX Domain PX RT AjBool RD True if file was written ok. RX // AjBool ajDomainWrite(AjPFile outf, const AjPDomain domain) { if(!outf || !domain) { ajWarn("Bad args passed to ajDomainWrite"); return ajFalse; } if(domain->Type == ajEDomainTypeSCOP) ajScopWrite(outf, domain->Scop); else ajCathWrite(outf, domain->Cath); return ajTrue; } ID ajScopWrite TY public MO ajdomain LB core XX DE Write contents of an AJAX SCOP object to a DCF file (domain classification DE file). The DCF file uses DCF format (see documentation for DOMAINATRIX DE "scopparse" application). XX PN [1] PA u outf AjPFile PD Output file stream PX PN [2] PA r scop const AjPScop PD AJAX SCOP PX RT AjBool RD True if file was written ok. RX // AjBool ajScopWrite(AjPFile outf, const AjPScop scop) { ajuint i = 0U; AjPSeqout outseq = NULL; if(!outf || !scop) { ajWarn("Bad args passed to ajScopWrite"); return ajFalse; } ajFmtPrintF(outf, "ID %S\nXX\n", scop->Entry); ajFmtPrintF(outf, "EN %S\nXX\n", scop->Pdb); ajFmtPrintF(outf, "TY SCOP\nXX\n"); ajFmtPrintF(outf, "SI %d CL; %d FO; %d SF; %d FA; %d DO; %d SO; " "%d DD;\nXX\n", scop->Sunid_Class, scop->Sunid_Fold, scop->Sunid_Superfamily, scop->Sunid_Family, scop->Sunid_Domain, scop->Sunid_Source, scop->Sunid_Domdat); ajFmtPrintF(outf, "CL %S\n", scop->Class); ajFmtPrintF(outf, "XX\n"); ajFmtPrintSplit(outf, scop->Fold, "FO ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); ajFmtPrintSplit(outf, scop->Superfamily, "SF ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); ajFmtPrintSplit(outf, scop->Family, "FA ", 75, " \t\n\r"); ajFmtPrintF(outf, "XX\n"); ajFmtPrintSplit(outf, scop->Domain, "DO ", 75, " \t\n\r");; ajFmtPrintF(outf, "XX\nOS %S\n", scop->Source); if(ajStrGetLen(scop->SeqPdb)) { ajFmtPrintF(outf, "XX\n"); outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, scop->SeqPdb, "DS"); ajSeqoutDel(&outseq); } if(ajStrGetLen(scop->Acc)) ajFmtPrintF(outf, "XX\nAC %S\n", scop->Acc); if(ajStrGetLen(scop->Spr)) ajFmtPrintF(outf, "XX\nSP %S\n", scop->Spr); if(ajStrGetLen(scop->SeqSpr)) { ajFmtPrintF(outf, "XX\n%-5s%d START; %d END;\n", "RA", scop->Startd, scop->Endd); ajFmtPrintF(outf, "XX\n"); outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, scop->SeqSpr, "SQ"); ajSeqoutDel(&outseq); } if(ajStrGetLen(scop->Sse)) ajFmtPrintF(outf, "XX\nSE %S\n", scop->Sse); if(ajStrGetLen(scop->Sss)) ajFmtPrintF(outf, "XX\n"); if(ajStrGetLen(scop->Sss)) { outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, scop->Sss, "SS"); ajSeqoutDel(&outseq); } ajFmtPrintF(outf, "XX\nNC %d\n", scop->Number); for(i = 0U; i < scop->Number; i++) { ajFmtPrintF(outf, "XX\nCN [%u]\n", i + 1); ajFmtPrintF(outf, "XX\nCH %c CHAIN; %S START; %S END;\n", scop->Chain[i], scop->Start[i], scop->End[i]); } ajFmtPrintF(outf, "//\n"); return ajTrue; } ID ajDomainDCFType TY public MO ajdomain LB core XX DE Reads a DCF file (domain classification file; see documentation for DE DOMAINATRIX "scopparse" application) and ascertains the type of domains DE (ajEDomainTypeSCOP or ajEDomainTypeCATH) within. XX PN [1] PA u inf AjPFile PD Input file stream PX RT AjEDomainType RD AJAX Domain Type enumeration ajEDomainTypeSCOP, RD ajEDomainTypeCATH or ajEDomainTypeNULL (error) as appropriate. RX // AjEDomainType ajDomainDCFType(AjPFile inf) { ajlong offset = 0; AjPStr line = NULL; AjPStr tmp = NULL; /* Only initialise strings if this is called for the first time */ if(!line) { line = ajStrNew(); tmp = ajStrNew(); } offset = ajFileResetPos(inf); while((ajReadlineTrim(inf, &line))) { if(!ajStrPrefixC(line, "TY ")) continue; ajFmtScanS(line, "%*S %S", &tmp); if(ajStrMatchC(tmp, "SCOP")) { ajFileSeek(inf, offset, 0); ajStrDel(&line); ajStrDel(&tmp); return ajEDomainTypeSCOP; } else if(ajStrMatchC(tmp, "CATH")) { ajFileSeek(inf, offset, 0); ajStrDel(&line); ajStrDel(&tmp); return ajEDomainTypeCATH; } else { ajWarn("Serious error: Unknown domain type in DCF file"); ajStrDel(&line); ajStrDel(&tmp); return ajEDomainTypeNULL; } } ajStrDel(&line); ajStrDel(&tmp); return ajEDomainTypeNULL; } ID ajDomainExit TY public MO ajdomain LB core XX DE Cleanup of Domain function internals. XX RT void RD RX // void ajDomainExit(void) { ajStrDel(&domainGStrline); ajStrDel(&domainGStrsunidstr); ajStrDel(&domainGStrtentry); ajStrDel(&domainGStrtmp); ajStrDel(&domainGStrscopid); ajStrDel(&domainGStrpdbid); ajStrDel(&domainGStrchains); ajStrDel(&domainGStrsccs); ajStrDel(&domainGStrclass); ajStrDel(&domainGStrtoken); ajStrDel(&domainGStrstr); ajRegFree(&domainGRegexp); ajRegFree(&domainGRegrexp); return; } ID ajDomainDummyFunction TY public MO ajdomain LB core XX DE Dummy function to catch all unused functions defined in the ajdomain DE source file. XX RT void RD RX // void ajDomainDummyFunction(void) { AjPStr str = NULL; AjPFile file = NULL; domainScopclaRead(file, str); domainScopdesRead(file, str); return; } ID ajExceptRaise TY public MO ajexcept LB core XX DE Check for exception. If not caught abort. Else call exception routine. XX PN [1] PA r e const T* PD Exception code PX PN [2] PA r file const char* PD file exceptions called from PX PN [3] PA r line ajint PD line number exception called from PX RT void RD RX // __noreturn void ajExceptRaise(const T* e, const char* file, ajint line) { Except_Frame *p; p = Except_stack; assert(e); if(p == NULL) { ajMessOut("Uncaught exception: "); if(e->reason) ajMessOut(" %s,", e->reason); else ajMessOut(" at 0x%p,", e); if(file && line > 0) ajMessOut(" raised at %s:%d\n", file, line); ajUtilCatch(); exit(EXIT_FAILURE); } p->exception = e; p->file = file; p->line = line; Except_stack = Except_stack->prev; longjmp(p->env, Except_raised); } ID featVocabDef TY list MO ajfeat LB core XX DE Feature vocabularies DE DE Includes pointers to the type and tag tables to be initialised XX // static FeatOVocab featVocabDef[] = { /* Name Truename Nucleotide Prot Typetable Tagstable Description Alias Padding */ {"gff3", "gff3", AJTRUE, AJFALSE, &FeatTypeTableGff3, &FeatTagsTableGff3, "GFF version 3", AJFALSE, 0}, {"gff3", "gff3protein", AJFALSE, AJTRUE, &FeatTypeTableGff3protein, &FeatTagsTableGff3protein, "GFF version 3", AJTRUE, 0}, {"gff", "gff3", AJTRUE, AJFALSE, &FeatTypeTableGff3, &FeatTagsTableGff3, "GFF version 3", AJFALSE, 0}, {"gff", "gff3protein", AJFALSE, AJTRUE, &FeatTypeTableGff3protein, &FeatTagsTableGff3protein, "GFF version 3", AJFALSE, 0}, {"gff2", "gff2", AJTRUE, AJFALSE, &FeatTypeTableGff2, &FeatTagsTableGff2, "GFF version 2", AJFALSE, 0}, {"gff2", "gff2protein", AJFALSE, AJTRUE, &FeatTypeTableGff2protein, &FeatTagsTableGff2protein, "GFF version 2", AJFALSE, 0}, {"embl", "embl", AJTRUE, AJFALSE, &FeatTypeTableEmbl, &FeatTagsTableEmbl, "embl format", AJFALSE, 0}, {"em", "embl", AJTRUE, AJFALSE, &FeatTypeTableEmbl, &FeatTagsTableEmbl, "embl format", AJTRUE, 0}, {"genbank", "embl", AJTRUE, AJFALSE, &FeatTypeTableEmbl, &FeatTagsTableEmbl, "genbank format", AJFALSE, 0}, {"gb", "embl", AJTRUE, AJFALSE, &FeatTypeTableEmbl, &FeatTagsTableEmbl, "genbank format", AJTRUE, 0}, {"ddbj", "embl", AJTRUE, AJFALSE, &FeatTypeTableEmbl, &FeatTagsTableEmbl, "ddbj format", AJFALSE, 0}, {"refseq", "embl", AJTRUE, AJFALSE, &FeatTypeTableEmbl, &FeatTagsTableEmbl, "genbank format", AJTRUE, 0}, {"refseqp", "refseqp", AJFALSE, AJTRUE, &FeatTypeTableRefseqp, &FeatTagsTableRefseqp, "genbank format", AJTRUE, 0}, {"pir", "pir", AJFALSE, AJTRUE, &FeatTypeTablePir, &FeatTagsTablePir, "PIR format", AJFALSE, 0}, {"nbrf", "pir", AJFALSE, AJTRUE, &FeatTypeTablePir, &FeatTagsTablePir, "PIR format", AJTRUE, 0}, {"sw", "swiss", AJFALSE, AJTRUE, &FeatTypeTableSwiss, &FeatTagsTableSwiss, "SwissProt format", AJTRUE, 0}, {"swissprot", "swiss", AJFALSE, AJTRUE, &FeatTypeTableSwiss, &FeatTagsTableSwiss, "SwissProt format", AJTRUE, 0}, {"dasgff", "gff3", AJTRUE, AJFALSE, &FeatTypeTableGff3, &FeatTagsTableGff3, "DAS GFF format", AJFALSE, 0}, {"dasgff", "gff3protein", AJFALSE, AJTRUE, &FeatTypeTableGff3protein, &FeatTagsTableGff3protein, "DAS GFF format", AJFALSE, 0}, {"debug", "emboss", AJTRUE, AJFALSE, &FeatTypeTableDna, &FeatTagsTableDna, "Debugging trace", AJFALSE, 0}, {"debug", "protein", AJFALSE, AJTRUE, &FeatTypeTableProtein, &FeatTagsTableProtein, "Debugging trace", AJFALSE, 0}, {NULL, NULL, AJFALSE, AJFALSE, NULL, NULL, NULL, AJFALSE, 0} }; ID ajFeatNew TY public MO ajfeat LB core XX DE Constructor - must specify associated 'ajFeattable' DE to which the new feature is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA r source const AjPStr PD Analysis basis for feature PX PN [3] PA r type const AjPStr PD Type of feature (e.g. exon) PX PN [4] PA r Start ajint PD Start position of the feature PX PN [5] PA r End ajint PD End position of the feature PX PN [6] PA r score float PD Analysis score for the feature PX PN [7] PA r strand char PD Strand of the feature PX PN [8] PA r frame ajint PD Frame of the feature PX RT AjPFeature RD newly allocated feature object RX CA new CT AjPFeature CD Constructor - must specify the associated (non-null) AjPFeattable CX // AjPFeature ajFeatNew(AjPFeattable thys, const AjPStr source, const AjPStr type, ajint Start, ajint End, float score, char strand, ajint frame) { ajuint flags = 0; AjPFeature ret = NULL; if(ajStrMatchC(thys->Type, "P")) ret = ajFeatNewProtFlags(thys,source,type,Start,End,score,flags); else ret = ajFeatNewNucFlags(thys,source,type,Start,End,score,strand,frame, 0,0,0,NULL, NULL,flags); return ret; } ID ajFeatNewBetween TY public MO ajfeat LB core XX DE Simple constructor with location between the start positions and DE the previous base. DE DE User must specify associated 'ajFeattable' to which the new feature DE is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA r Start ajint PD Start position of the feature PX RT AjPFeature RD newly allocated feature object RX // AjPFeature ajFeatNewBetween(AjPFeattable thys, ajint Start) { AjPStr source = NULL; float score = 0.0; char strand = '.'; ajint frame = 0; ajint flags = AJFEATFLAG_BETWEEN_SEQ; AjPFeature ret = NULL ; /*ajDebug("ajFeatNewBetween %d\n", Start);*/ if(!featTypeMiscfeat) featTypeMiscfeat = ajStrNewC("misc_feature"); ret = ajFeatNewNucFlags(thys,source,featTypeMiscfeat, Start-1,Start,score,strand,frame, 0,0,0,NULL,NULL,flags); return ret; } ID ajFeatNewSub TY public MO ajfeat LB core XX DE Constructor - must specify associated 'ajFeattable' DE to which the new feature is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA u parent AjPFeature PD Parent feature PX PN [3] PA r source const AjPStr PD Analysis basis for feature PX PN [4] PA r type const AjPStr PD Type of feature (e.g. exon) PX PN [5] PA r Start ajint PD Start position of the feature PX PN [6] PA r End ajint PD End position of the feature PX PN [7] PA r score float PD Analysis score for the feature PX PN [8] PA r strand char PD Strand of the feature PX PN [9] PA r frame ajint PD Frame of the feature PX RT AjPFeature RD newly allocated feature object RX CA new CT AjPFeature CD Constructor - must specify the associated (non-null) AjPFeattable CX // AjPFeature ajFeatNewSub(AjPFeattable thys, AjPFeature parent, const AjPStr source, const AjPStr type, ajint Start, ajint End, float score, char strand, ajint frame) { ajuint flags = 0; AjPFeature ret = NULL; if(ajStrMatchC(thys->Type, "P")) ret = ajFeatNewProtFlagsSub(thys,parent, source,type,Start,End,score,flags); else ret = ajFeatNewNucFlagsSub(thys,parent, source,type,Start,End,score,strand,frame, 0,0,0,NULL, NULL,flags); return ret; } ID ajFeatNewII TY public MO ajfeat LB core XX DE Simple constructor with only start and end positions DE DE User must specify associated 'ajFeattable' to which the new feature DE is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA r Start ajint PD Start position of the feature PX PN [3] PA r End ajint PD End position of the feature PX RT AjPFeature RD newly allocated feature object RX CA new CT AjPFeature CD Simple constructor with only start and end positions CX // AjPFeature ajFeatNewII(AjPFeattable thys, ajint Start, ajint End) { AjPStr source = NULL; float score = 0.0; char strand = '.'; ajint frame = 0; ajint flags = 0; AjPFeature ret = NULL ; /*ajDebug("ajFeatNewII %d %d\n", Start, End);*/ if(!featTypeMiscfeat) featTypeMiscfeat = ajStrNewC("misc_feature"); if(Start > End) ret = ajFeatNewNucFlags(thys,source,featTypeMiscfeat, End,Start,score,'-',frame, 0,0,0,NULL, NULL,flags); else ret = ajFeatNewNucFlags(thys,source,featTypeMiscfeat, Start,End,score,strand,frame, 0,0,0,NULL, NULL,flags); return ret; } ID ajFeatNewIISub TY public MO ajfeat LB core XX DE Simple constructor with only start and end positions DE DE User must specify associated 'ajFeattable' to which the new feature DE is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA u parent AjPFeature PD Parent feature PX PN [3] PA r Start ajint PD Start position of the feature PX PN [4] PA r End ajint PD End position of the feature PX RT AjPFeature RD newly allocated feature object RX CA new CT AjPFeature CD Simple constructor with only start and end positions CX // AjPFeature ajFeatNewIISub(AjPFeattable thys, AjPFeature parent, ajint Start, ajint End) { AjPStr source = NULL; float score = 0.0; char strand = '.'; ajint frame = 0; ajint flags = 0; AjPFeature ret = NULL ; /*ajDebug("ajFeatNewII %d %d\n", Start, End);*/ if(!featTypeMiscfeat) featTypeMiscfeat = ajStrNewC("misc_feature"); if(Start > End) ret = ajFeatNewNucFlagsSub(thys,parent, source,featTypeMiscfeat, End,Start,score,'-',frame, 0,0,0,NULL, NULL,flags); else ret = ajFeatNewNucFlagsSub(thys,parent, source,featTypeMiscfeat, Start,End,score,strand,frame, 0,0,0,NULL, NULL,flags); return ret; } ID ajFeatNewIIRev TY public MO ajfeat LB core XX DE Simple constructor with only start and end positions, sets feature to be DE on the reverse strand DE DE User must specify associated 'ajFeattable' to which the new feature DE is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA r Start ajint PD Start position of the feature PX PN [3] PA r End ajint PD End position of the feature PX RT AjPFeature RD newly allocated feature object RX CA new CT AjPFeature CD Simple constructor with only start and end positions, sets feature to be on the reverse strand CX // AjPFeature ajFeatNewIIRev(AjPFeattable thys, ajint Start, ajint End) { AjPFeature ret = NULL ; /*ajDebug("ajFeatNewIIRev %d %d\n", Start, End);*/ if(Start > End) ret = ajFeatNewII(thys,End,Start); else ret = ajFeatNewII(thys,Start,End); ret->Strand = '-'; return ret; } ID ajFeatNewIIRevSub TY public MO ajfeat LB core XX DE Simple constructor with only start and end positions, sets feature to be DE on the reverse strand DE DE User must specify associated 'ajFeattable' to which the new feature DE is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA u parent AjPFeature PD Parent feature PX PN [3] PA r Start ajint PD Start position of the feature PX PN [4] PA r End ajint PD End position of the feature PX RT AjPFeature RD newly allocated feature object RX CA new CT AjPFeature CD Simple constructor with only start and end positions, sets feature to be on the reverse strand CX // AjPFeature ajFeatNewIIRevSub(AjPFeattable thys, AjPFeature parent, ajint Start, ajint End) { AjPFeature ret = NULL ; /*ajDebug("ajFeatNewIIRev %d %d\n", Start, End);*/ if(Start > End) ret = ajFeatNewIISub(thys,parent,End,Start); else ret = ajFeatNewIISub(thys,parent,Start,End); ret->Strand = '-'; return ret; } ID ajFeatNewProt TY public MO ajfeat LB core XX DE Constructor - must specify associated 'ajFeattable' DE to which the new feature is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA r source const AjPStr PD Analysis basis for feature PX PN [3] PA r type const AjPStr PD Type of feature (e.g. exon) PX PN [4] PA r Start ajint PD Start position of the feature PX PN [5] PA r End ajint PD End position of the feature PX PN [6] PA r score float PD Analysis score for the feature PX RT AjPFeature RD newly allocated feature object RX CA new CT AjPFeature CD Protein-specific constructor - must specify the associated (non-null) AjPFeattable CX // AjPFeature ajFeatNewProt(AjPFeattable thys, const AjPStr source, const AjPStr type, ajint Start, ajint End, float score) { return ajFeatNewProtFlags(thys, source, type, Start, End, score, 0); } ID ajFeatNewProtSub TY public MO ajfeat LB core XX DE Constructor - must specify associated 'ajFeattable' DE to which the new feature is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA u parent AjPFeature PD Parent feature PX PN [3] PA r source const AjPStr PD Analysis basis for feature PX PN [4] PA r type const AjPStr PD Type of feature (e.g. exon) PX PN [5] PA r Start ajint PD Start position of the feature PX PN [6] PA r End ajint PD End position of the feature PX PN [7] PA r score float PD Analysis score for the feature PX RT AjPFeature RD newly allocated feature object RX CA new CT AjPFeature CD Protein-specific constructor - must specify the associated (non-null) AjPFeattable CX // AjPFeature ajFeatNewProtSub(AjPFeattable thys, AjPFeature parent, const AjPStr source, const AjPStr type, ajint Start, ajint End, float score) { return ajFeatNewProtFlagsSub(thys, parent, source, type, Start, End, score, 0); } ID ajFeatNewProtFlags TY public MO ajfeat LB core XX DE Constructor - must specify associated 'ajFeattable' DE to which the new feature is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA r source const AjPStr PD Analysis basis for feature PX PN [3] PA r type const AjPStr PD Type of feature (e.g. exon) PX PN [4] PA r Start ajint PD Start position of the feature PX PN [5] PA r End ajint PD End position of the feature PX PN [6] PA r score float PD Analysis score for the feature PX PN [7] PA r flags ajuint PD flags. PX RT AjPFeature RD newly allocated feature object RX CA new CT AjPFeature CD Protein-specific constructor - must specify the associated (non-null) AjPFeattable CX // AjPFeature ajFeatNewProtFlags(AjPFeattable thys, const AjPStr source, const AjPStr type, ajint Start, ajint End, float score, ajuint flags) { AjPFeature ret = NULL ; if(!featDefSource) ajStrAssignS(&featDefSource, ajUtilGetProgram()); ajDebug("\najFeatNewProtFlags '%S' %d .. %d %x\n", type, Start, End, flags); if(!ajStrGetLen(type)) return NULL; featInit(); /* Allocate the object... and a new Tags list */ ret = featFeatureNew() ; thys->Groups++; ret->Group = thys->Groups; ret->Exon = 0; if(ajStrGetLen(source)) ajStrAssignS(&ret->Source, source); else ajStrAssignS(&ret->Source, featDefSource); ajStrAssignS(&ret->Type, featTypeProt(type)); ajDebug("ajFeatNewProt feature type '%S' => '%S'\n", type, ret->Type); ret->Score = score; ret->Flags = flags; ret->Strand = '\0' ; ret->Frame = 0 ; ret->Start = Start; ret->End = End; ret->Start2 = 0; ret->End2 = 0; ret->Protein = ajTrue; if(!(ret->Flags & AJFEATFLAG_REMOTEID) && !(ret->Flags & AJFEATFLAG_LABEL)) { thys->Len = AJMAX(thys->Len, ret->Start); thys->Len = AJMAX(thys->Len, ret->End); } ajFeattableAdd(thys,ret) ; return ret; } ID ajFeatNewProtFlagsSub TY public MO ajfeat LB core XX DE Constructor - must specify associated 'ajFeattable' DE to which the new feature is automatically added! XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA u parent AjPFeature PD Parent feature PX PN [3] PA r source const AjPStr PD Analysis basis for feature PX PN [4] PA r type const AjPStr PD Type of feature (e.g. exon) PX PN [5] PA r Start ajint PD Start position of the feature PX PN [6] PA r End ajint PD End position of the feature PX PN [7] PA r score float PD Analysis score for the feature PX PN [8] PA r flags ajuint PD flags. PX RT AjPFeature RD newly allocated feature object RX CA new CT AjPFeature CD Protein-specific constructor - must specify the associated (non-null) AjPFeattable CX // AjPFeature ajFeatNewProtFlagsSub(AjPFeattable thys, AjPFeature parent, const AjPStr source, const AjPStr type, ajint Start, ajint End, float score, ajuint flags) { AjPFeature ret = NULL ; if(!featDefSource) ajStrAssignS(&featDefSource, ajUtilGetProgram()); ajDebug("\najFeatNewProtFlagsSub '%S' %d .. %d %x\n", type, Start, End, flags); if(!ajStrGetLen(type)) return NULL; featInit(); /* Allocate the object... and a new Tags list */ ret = featFeatureNew() ; thys->Groups++; ret->Group = thys->Groups; ret->Exon = 0; if(ajStrGetLen(source)) ajStrAssignS(&ret->Source, source); else ajStrAssignS(&ret->Source, featDefSource); ajStrAssignS(&ret->Type, featTypeProt(type)); ajDebug("ajFeatNewProt feature type '%S' => '%S'\n", type, ret->Type); ret->Score = score; ret->Flags = flags; ret->Strand = '\0' ; ret->Frame = 0 ; ret->Start = Start; ret->End = End; ret->Start2 = 0; ret->End2 = 0; ret->Protein = ajTrue; if(!(ret->Flags & AJFEATFLAG_REMOTEID) && !(ret->Flags & AJFEATFLAG_LABEL)) { thys->Len = AJMAX(thys->Len, ret->Start); thys->Len = AJMAX(thys->Len, ret->End); } if(!parent->Subfeatures) parent->Subfeatures = ajListNew(); ajListPushAppend(parent->Subfeatures, ret); return ret; } ID ajFeatCompByStart TY public MO ajfeat LB core XX DE Compare two features by their start. XX PN [1] PA r a const void* PD feature PX PN [2] PA r b const void* PD another feature PX RT ajint RD -1 if a is less than b, 0 if a is equal to b else +1. RX // ajint ajFeatCompByStart(const void *a, const void *b) { const AjPFeature gfa; const AjPFeature gfb; ajint val = 0; gfa = *(AjPFeature const *) a; gfb = *(AjPFeature const *) b; val = gfa->Start - gfb->Start; if(val) return val; else { val = gfb->End - gfa->End; if(val) return val; } return 0; } ID ajFeatCompByEnd TY public MO ajfeat LB core XX DE Compare two features by their end. XX PN [1] PA r a const void* PD feature PX PN [2] PA r b const void* PD another feature PX RT ajint RD -1 if a is less than b, 0 if a is equal to b else +1. RX // ajint ajFeatCompByEnd(const void *a, const void *b) { const AjPFeature gfa; const AjPFeature gfb; ajint val = 0; gfa = *(AjPFeature const *) a; gfb = *(AjPFeature const *) b; val = gfa->End - gfb->End; if(val) return val; else { val = gfa->Start - gfb->Start; if(val) return val; } return 0; } ID ajFeatCompByGroup TY public MO ajfeat LB core XX DE Compare two features by their group and exon numbers XX PN [1] PA r a const void* PD feature PX PN [2] PA r b const void* PD another feature PX RT ajint RD -1 if a is less than b, 0 if a is equal to b else +1. RX // ajint ajFeatCompByGroup(const void *a, const void *b) { const AjPFeature gfa; const AjPFeature gfb; ajint val = 0; gfa = *(AjPFeature const *) a; gfb = *(AjPFeature const *) b; val = gfa->Group - gfb->Group; if(val) return val; val = gfa->Exon - gfb->Exon; return val; } ID ajFeatCompByType TY public MO ajfeat LB core XX DE Compare two features by their type (key). XX PN [1] PA r a const void* PD feature PX PN [2] PA r b const void* PD another feature PX RT ajint RD -1 if a is less than b, 0 if a is equal to b else +1. RX // ajint ajFeatCompByType(const void *a, const void *b) { const AjPFeature gfa; const AjPFeature gfb; ajint val = 0; gfa = *(AjPFeature const *) a; gfb = *(AjPFeature const *) b; val = ajStrVcmp(&gfa->Type,&gfb->Type); if(val) return val; else { val = gfa->Start - gfb->Start; if(val) return val; else { val = gfa->End - gfb->End; if(val) return val; } } return 0; } ID ajFeatNewNuc TY public MO ajfeat LB core XX DE Constructor for a new feature, DE automatically added to the specified table. XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA r source const AjPStr PD Analysis basis for feature PX PN [3] PA r type const AjPStr PD Type of feature (e.g. exon) PX PN [4] PA r Start ajint PD Start position of the feature PX PN [5] PA r End ajint PD End position of the feature PX PN [6] PA r score float PD Analysis score for the feature PX PN [7] PA r strand char PD Strand of the feature PX PN [8] PA r frame ajint PD Frame of the feature PX PN [9] PA r exon ajint PD exon number (0 for default value) PX PN [10] PA r Start2 ajint PD 2nd Start position of the feature PX PN [11] PA r End2 ajint PD 2nd End position of the feature PX PN [12] PA r entryid const AjPStr PD Entry ID for location in PD another entry PX PN [13] PA r label const AjPStr PD Label for location (non-numeric) PX RT AjPFeature RD newly allocated feature object RX // AjPFeature ajFeatNewNuc(AjPFeattable thys, const AjPStr source, const AjPStr type, ajint Start, ajint End, float score, char strand, ajint frame, ajint exon, ajint Start2, ajint End2, const AjPStr entryid, const AjPStr label) { return ajFeatNewNucFlags(thys, source, type, Start, End, score, strand, frame, exon, Start2, End2, entryid, label, 0); } ID ajFeatNewNucSub TY public MO ajfeat LB core XX DE Constructor for a new feature, DE automatically added to the specified table. XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA u parent AjPFeature PD Parent feature PX PN [3] PA r source const AjPStr PD Analysis basis for feature PX PN [4] PA r type const AjPStr PD Type of feature (e.g. exon) PX PN [5] PA r Start ajint PD Start position of the feature PX PN [6] PA r End ajint PD End position of the feature PX PN [7] PA r score float PD Analysis score for the feature PX PN [8] PA r strand char PD Strand of the feature PX PN [9] PA r frame ajint PD Frame of the feature PX PN [10] PA r exon ajint PD exon number (0 for default value) PX PN [11] PA r Start2 ajint PD 2nd Start position of the feature PX PN [12] PA r End2 ajint PD 2nd End position of the feature PX PN [13] PA r entryid const AjPStr PD Entry ID for location in PD another entry PX PN [14] PA r label const AjPStr PD Label for location (non-numeric) PX RT AjPFeature RD newly allocated feature object RX // AjPFeature ajFeatNewNucSub(AjPFeattable thys, AjPFeature parent, const AjPStr source, const AjPStr type, ajint Start, ajint End, float score, char strand, ajint frame, ajint exon, ajint Start2, ajint End2, const AjPStr entryid, const AjPStr label) { return ajFeatNewNucFlagsSub(thys, parent, source, type, Start, End, score, strand, frame, exon, Start2, End2, entryid, label, 0); } ID ajFeatNewNucFlags TY public MO ajfeat LB core XX DE Constructor for a new feature, DE automatically added to the specified table. XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA r source const AjPStr PD Analysis basis for feature PX PN [3] PA r type const AjPStr PD Type of feature (e.g. exon) PX PN [4] PA r Start ajint PD Start position of the feature PX PN [5] PA r End ajint PD End position of the feature PX PN [6] PA r score float PD Analysis score for the feature PX PN [7] PA r strand char PD Strand of the feature PX PN [8] PA r frame ajint PD Frame of the feature PX PN [9] PA r exon ajint PD exon number (0 for default value) PX PN [10] PA r Start2 ajint PD 2nd Start position of the feature PX PN [11] PA r End2 ajint PD 2nd End position of the feature PX PN [12] PA r entryid const AjPStr PD Entry ID for location in PD another entry PX PN [13] PA r label const AjPStr PD Label for location (non-numeric) PX PN [14] PA r flags ajuint PD flags. PX RT AjPFeature RD newly allocated feature object RX // AjPFeature ajFeatNewNucFlags(AjPFeattable thys, const AjPStr source, const AjPStr type, ajint Start, ajint End, float score, char strand, ajint frame, ajint exon, ajint Start2, ajint End2, const AjPStr entryid, const AjPStr label, ajuint flags) { AjPFeature ret = NULL; ajDebug("ajFeatNewNucFlags %d %d '%c' type: '%S'\n", Start, End, strand, type); if(!featDefSource) ajStrAssignS(&featDefSource, ajUtilGetProgram()); if(!ajStrGetLen(type)) return NULL; featInit(); /* Allocate the object... */ ret = featFeatureNew(); thys->Groups++; ret->Group = thys->Groups; ret->Exon = exon; if(ajStrGetLen(source)) ajStrAssignS(&ret->Source, source); else ajStrAssignS(&ret->Source, featDefSource); /*ajDebug("featFeatNew type '%S'\n", thys->Type);*/ if(ajStrMatchC(thys->Type, "P")) ajStrAssignS(&ret->Type, featTypeProt(type)); else ajStrAssignS(&ret->Type, featTypeDna(type)); ajDebug("featFeatNew feature type '%S' => '%S'\n", type, ret->Type); ret->Score = score; ret->Flags = flags; if(strand == '+' || strand == '-') ret->Strand = strand; else ret->Strand = '\0'; ret->Frame = frame ; ret->Start = Start; ret->End = End; ret->Start2 = Start2; ret->End2 = End2; if(ajStrGetLen(entryid)) ajStrAssignS(&ret->Remote, entryid); else { if(!(ret->Flags & AJFEATFLAG_REMOTEID) && !(ret->Flags & AJFEATFLAG_LABEL)) { thys->Len = AJMAX(thys->Len, ret->Start); thys->Len = AJMAX(thys->Len, ret->End); } } if(ajStrGetLen(label)) { ajStrAssignS(&ret->Label, label); ajFeatWarn("%S: Feature label '%S' used", thys->Seqid, label); } ajFeattableAdd(thys,ret) ; return ret ; } ID ajFeatNewNucFlagsSub TY public MO ajfeat LB core XX DE Constructor for a new feature, DE automatically added to the specified table. XX PN [1] PA u thys AjPFeattable PD Pointer to the ajFeattable which PD will own the feature PX PN [2] PA u parent AjPFeature PD Parent feature PX PN [3] PA r source const AjPStr PD Analysis basis for feature PX PN [4] PA r type const AjPStr PD Type of feature (e.g. exon) PX PN [5] PA r Start ajint PD Start position of the feature PX PN [6] PA r End ajint PD End position of the feature PX PN [7] PA r score float PD Analysis score for the feature PX PN [8] PA r strand char PD Strand of the feature PX PN [9] PA r frame ajint PD Frame of the feature PX PN [10] PA r exon ajint PD exon number (0 for default value) PX PN [11] PA r Start2 ajint PD 2nd Start position of the feature PX PN [12] PA r End2 ajint PD 2nd End position of the feature PX PN [13] PA r entryid const AjPStr PD Entry ID for location in PD another entry PX PN [14] PA r label const AjPStr PD Label for location (non-numeric) PX PN [15] PA r flags ajuint PD flags. PX RT AjPFeature RD newly allocated feature object RX // AjPFeature ajFeatNewNucFlagsSub(AjPFeattable thys, AjPFeature parent, const AjPStr source, const AjPStr type, ajint Start, ajint End, float score, char strand, ajint frame, ajint exon, ajint Start2, ajint End2, const AjPStr entryid, const AjPStr label, ajuint flags) { AjPFeature ret = NULL; ajDebug("ajFeatNewNucFlagsSub %d %d '%c' type: '%S'\n", Start, End, strand, type); if(!featDefSource) ajStrAssignS(&featDefSource, ajUtilGetProgram()); if(!ajStrGetLen(type)) return NULL; featInit(); /* Allocate the object... */ ret = featFeatureNew(); thys->Groups++; ret->Group = thys->Groups; ret->Exon = exon; if(ajStrGetLen(source)) ajStrAssignS(&ret->Source, source); else ajStrAssignS(&ret->Source, featDefSource); /*ajDebug("featFeatNew type '%S'\n", thys->Type);*/ if(ajStrMatchC(thys->Type, "P")) ajStrAssignS(&ret->Type, featTypeProt(type)); else ajStrAssignS(&ret->Type, featTypeDna(type)); ajDebug("featFeatNew feature type '%S' => '%S'\n", type, ret->Type); ret->Score = score; ret->Flags = flags; if(strand == '+' || strand == '-') ret->Strand = strand; else ret->Strand = '\0'; ret->Frame = frame ; ret->Start = Start; ret->End = End; ret->Start2 = Start2; ret->End2 = End2; if(ajStrGetLen(entryid)) ajStrAssignS(&ret->Remote, entryid); else { if(!(ret->Flags & AJFEATFLAG_REMOTEID) && !(ret->Flags & AJFEATFLAG_LABEL)) { thys->Len = AJMAX(thys->Len, ret->Start); thys->Len = AJMAX(thys->Len, ret->End); } } if(ajStrGetLen(label)) { ajStrAssignS(&ret->Label, label); ajFeatWarn("%S: Feature label '%S' used", thys->Seqid, label); } if(!parent->Subfeatures) parent->Subfeatures = ajListNew(); ajListPushAppend(parent->Subfeatures, ret); return ret ; } ID ajFeattableDel TY public MO ajfeat LB core XX DE Destructor for ajFeattable objects. DE If the given object (pointer) is NULL, or a NULL pointer, simply returns. XX PN [1] PA d pthis AjPFeattable* PD Pointer to the object to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPFeattable CD Default destructor CX // void ajFeattableDel(AjPFeattable *pthis) { AjPFeattable thys; if(!pthis) return; thys = *pthis; /*ajDebug("ajFeattableDel %x\n", thys);*/ if(!thys) return; ajFeattableClear(thys); ajStrDel(&thys->Seqid); ajStrDel(&thys->Type); ajStrDel(&thys->Db); ajStrDel(&thys->Setdb); ajStrDel(&thys->Full); ajStrDel(&thys->Qry); ajStrDel(&thys->Formatstr); ajStrDel(&thys->Filename); ajStrDel(&thys->TextPtr); ajListFree(&thys->Features); AJFREE(*pthis); *pthis = NULL; return; } ID ajFeatDel TY public MO ajfeat LB core XX DE Destructor for AjPFeature objects. DE If the given object (pointer) is NULL, or a NULL pointer, simply returns. XX PN [1] PA d pthis AjPFeature* PD Pointer to the object to be deleted. PD The pointer is always deleted. PX RT void RD RX CA delete CT AjPFeature CD Default destructor CX // void ajFeatDel(AjPFeature *pthis) { if(!pthis) return ; if(!*pthis) return ; featClear(*pthis); AJFREE(*pthis); *pthis = NULL ; return; } ID featClear TY static MO ajfeat LB core XX DE Deletes all feature tag-value pairs from a feature line XX PN [1] PA u thys AjPFeature PD Feature PX RT void RD RX // static void featClear(AjPFeature thys) { AjIList iter = NULL; AjPTagval item = NULL; AjPFeature subfeat = NULL; if(!thys) return; /* We need to delete the associated Tag data structures too!!!*/ if(thys->Subfeatures) { iter = ajListIterNew(thys->Subfeatures); while(!ajListIterDone(iter)) { subfeat = (AjPFeature)ajListIterGet(iter); ajFeatDel(&subfeat); } ajListIterDel(&iter); } ajListFree(&(thys->Subfeatures)); if(thys->Tags) { iter = ajListIterNew(thys->Tags); while(!ajListIterDone(iter)) { item = (AjPTagval)ajListIterGet(iter); /* assuming a simple block memory free for now...*/ ajTagvalDel(&item); ajListIterRemove(iter); } ajListIterDel(&iter); } ajListFree(&(thys->Tags)); if(thys->GffTags) { iter = ajListIterNew(thys->GffTags); while(!ajListIterDone(iter)) { item = (AjPTagval)ajListIterGet(iter); /* assuming a simple block memory free for now...*/ ajTagvalDel(&item); ajListIterRemove(iter); } ajListIterDel(&iter); } ajListFree(&(thys->GffTags)); ajStrDel(&thys->Source); ajStrDel(&thys->Type); ajStrDel(&thys->Remote); ajStrDel(&thys->Label); return; } ID ajFeatSortByType TY public MO ajfeat LB core XX DE Sort Feature table by Type. XX PN [1] PA u Feattab AjPFeattable PD Feature table to be sorted. PX RT void RD RX // void ajFeatSortByType(AjPFeattable Feattab) { ajListSort(Feattab->Features, &ajFeatCompByType); return; } ID ajFeatSortByStart TY public MO ajfeat LB core XX DE Sort Feature table by Start position. XX PN [1] PA u Feattab AjPFeattable PD Feature table to be sorted. PX RT void RD RX // void ajFeatSortByStart(AjPFeattable Feattab) { ajListSort(Feattab->Features, &ajFeatCompByStart); } ID ajFeatSortByEnd TY public MO ajfeat LB core XX DE Sort Feature table by End position. XX PN [1] PA u Feattab AjPFeattable PD Feature table to be sorted. PX RT void RD RX // void ajFeatSortByEnd(AjPFeattable Feattab) { ajListSort(Feattab->Features, &ajFeatCompByEnd); return; } ID ajFeattableAdd TY public MO ajfeat LB core XX DE Method to add a new AjPFeature to a AjPFeattable XX PN [1] PA u thys AjPFeattable PD The feature table PX PN [2] PA u feature AjPFeature PD Feature to be added to the set PX RT void RD RX CA modify CT AjPFeattable CD Adds an AjPFeature to a set CX // void ajFeattableAdd(AjPFeattable thys, AjPFeature feature) { if(!(feature->Flags & AJFEATFLAG_REMOTEID) && !(feature->Flags & AJFEATFLAG_LABEL)) { thys->Len = AJMAX(thys->Len, feature->Start); thys->Len = AJMAX(thys->Len, feature->End); } ajListPushAppend(thys->Features, feature); /* if(feature->Type) ajDebug("ajFeattableAdd list size %Lu '%S' %d %d\n", ajListGetLength(thys->Features), feature->Type, feature->Start, feature->End); else ajDebug("ajFeattableAdd list size %Lu '%S' %d %d\n", ajListGetLength(thys->Features), NULL, feature->Start, feature->End); */ return; } ID ajFeattableMerge TY public MO ajfeat LB core XX DE Merges a feature table into an existing feature table. XX PN [1] PA u thys AjPFeattable PD Feature table to be appended to PX PN [2] PA r srctable const AjPFeattable PD Additional feature table PX RT ajuint RD Size of the new feature table. RX // ajuint ajFeattableMerge(AjPFeattable thys, const AjPFeattable srctable) { AjIList iter; AjPFeature featsrc; AjPFeature feat = NULL; if(!thys) return 0; if(!srctable) return 0; iter = ajListIterNewread(srctable->Features); while(!ajListIterDone(iter)) { featsrc = ajListIterGet(iter); feat = ajFeatNewFeat(featsrc); ajFeattableAdd(thys, feat); } ajListIterDel(&iter); return (ajuint) ajListGetLength(thys->Features); } ID featTableInit TY static MO ajfeat LB core XX DE Initialise the components of a previously allocated AjPFeattable object. XX PN [1] PA u thys AjPFeattable PD Target feature table object PX PN [2] PA r name const AjPStr PD Name of the table (e.g. PD sequence name) PX RT void RD RX // static void featTableInit(AjPFeattable thys, const AjPStr name) { /*ajDebug("featTableInit Entering...\n");*/ /*ajDebug("featTableInit initialising seqid: '%S'\n", name);*/ ajStrAssignS(&thys->Seqid,name) ; thys->Format = 0; return; } ID ajFeattableClear TY public MO ajfeat LB core XX DE Clears a feature table of all features XX PN [1] PA u thys AjPFeattable PD Feature table PX RT void RD RX // void ajFeattableClear(AjPFeattable thys) { AjIList iter = NULL ; AjPFeature feature = NULL ; if(!thys) return ; if(MAJSTRGETLEN(thys->Seqid)) ajStrSetClear(&thys->Seqid); if(MAJSTRGETLEN(thys->Type)) ajStrSetClear(&thys->Type); if(MAJSTRGETLEN(thys->Db)) ajStrSetClear(&thys->Db); if(MAJSTRGETLEN(thys->Setdb)) ajStrSetClear(&thys->Setdb); if(MAJSTRGETLEN(thys->Full)) ajStrSetClear(&thys->Full); if(MAJSTRGETLEN(thys->Qry)) ajStrSetClear(&thys->Qry); if(MAJSTRGETLEN(thys->Formatstr)) ajStrSetClear(&thys->Formatstr); if(MAJSTRGETLEN(thys->Filename)) ajStrSetClear(&thys->Filename); if(MAJSTRGETLEN(thys->TextPtr)) ajStrSetClear(&thys->TextPtr); if(thys->Features) { iter = ajListIterNew(thys->Features) ; while(!ajListIterDone(iter)) { feature = (AjPFeature)ajListIterGet(iter) ; ajFeatDel(&feature) ; ajListIterRemove(iter) ; } ajListIterDel(&iter) ; } return; } ID featFeatType TY static MO ajfeat LB core XX DE Process Efeatures file line DE DE Lines starting with text are new types DE DE Lines with '=' are aliases that update a previous defined type name DE and can have only + and - tag lines. DE DE Lines starting with / or m/ are tags for the current type DE DE Lines starting with +/ or -/ add and remove tags for the current type XX PN [1] PA r line const AjPStr PD Tags file line PX PN [2] PA w type AjPStr* PD Feature type PX PN [3] PA w intids AjPStr* PD Internal feature keys, preferably based on the PD Sequence Ontology Feature Annotation ID PX PN [4] PA w tag AjPStr* PD Tag name PX PN [5] PA w req AjPStr* PD Required (mandatory) code PD M = mandatory, m = alternate mandatory, PD + = add to existing tags, PD - = remove from existing tags PX RT AjBool RD ajTrue if a match was found RD ajFalse means an error occurred RX // static AjBool featFeatType(const AjPStr line, AjPStr* type, AjPStr* intids, AjPStr* tag, AjPStr* req) { const char* cp = ajStrGetPtr(line); const char* cq; ajint i; AjBool istag = ajFalse; ajStrAssignClear(type); ajStrAssignClear(intids); ajStrAssignClear(tag); ajStrAssignClear(req); while(isspace((ajint)*cp)) cp++; if(!*cp) return ajFalse; if(*cp == '/') istag = ajTrue; else if(*cp == 'm' || *cp == 'M' || *cp == '-') { if(*(cp+1) == '/') { istag = ajTrue; ajStrAppendK(req, *cp++); } } else if(*cp == '+') { if(*(cp+1) == 'm' || *(cp+1) == 'M') { if(*(cp+2) == '/') { istag = ajTrue; ajStrAppendK(req, *cp++); ajStrAppendK(req, *cp++); } } else { if(*(cp+1) == '/') { istag = ajTrue; ajStrAppendK(req, *cp++); } } } if(istag) { /* /tag name */ cp++; cq = cp; i = 0; while(*cp && !isspace((ajint)*cp++)) { i++; } ajStrAssignLenC(tag, cq, i); if(!ajStrGetLen(*tag)) return ajFalse; } else { /* type internal id */ cq = cp; i = 0; while(*cp && !isspace((ajint)*cp++)) { i++; } ajStrAssignLenC(type, cq, i); if(!ajStrGetLen(*type)) return ajFalse; while(*cp && isspace((ajint)*cp)) cp++; if(!*cp) return ajTrue; if(*cp == '=') ajStrAppendK(req, *cp++); while(*cp && isspace((ajint)*cp)) cp++; cq = cp; i = 0; while(*cp && !isspace((ajint)*cp++)) i++; ajStrAppendLenC(intids, cq, i); while(*cp) { while(*cp && isspace((ajint)*cp)) cp++; if(!*cp) return ajTrue; cq = cp; i = 0; while(*cp && !isspace((ajint)*cp++)) i++; ajStrAppendK(intids, ' '); ajStrAppendLenC(intids, cq, i); } } return ajTrue; } ID ajFeatTypeGetCategory TY public MO ajfeat LB core XX DE returns the category name for a feature type DE DE Used to identify category for DASGFF output XX PN [1] PA r type const AjPStr PD Feature type PX RT const AjPStr RD Feature category RX // const AjPStr ajFeatTypeGetCategory(const AjPStr type) { ajuint i; AjPStrTok catsplit = NULL; AjPStr token = NULL; AjPStr name = NULL; AjPStr types = NULL; AjPStr refname = NULL; const AjPStr ret = NULL; if(FeatCategoryTable == NULL) { FeatCategoryTable = ajTablestrNewCase(200); for(i=0;featCategory[i].Name;i++) { name = ajStrNewC(featCategory[i].Name); types = ajStrNewC(featCategory[i].Types); if(!i) { token = ajStrNewC(""); refname = ajStrNewRef(name); ajTablePut(FeatCategoryTable, token, refname); token = NULL; } catsplit = ajStrTokenNewC(types,","); while(ajStrTokenNextParse(&catsplit,&token)) { refname = ajStrNewRef(name); ajTablePut(FeatCategoryTable, token, refname); token = NULL; } ajStrDel(&token); ajStrDel(&name); ajStrDel(&types); ajStrTokenDel(&catsplit); } } ret = ajTablestrFetchS(FeatCategoryTable,type); if(!ret) { token = ajStrNewC(""); ret = ajTablestrFetchS(FeatCategoryTable,token); ajStrDel(&token); } return ret; } ID ajFeattableGetEntry TY public MO ajfeat LB core XX DE Return the full text XX PN [1] PA r thys const AjPFeattable PD Feature table object PX RT const AjPStr RD Returned full text RX // const AjPStr ajFeattableGetEntry(const AjPFeattable thys) { if(thys->TextPtr) return thys->TextPtr; return ajStrConstEmpty(); } ID ajFeattableGetQryC TY public MO ajfeat LB core XX DE Returns the query string of a feature table data object. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r ftable const AjPFeattable PD Feature table data object. PX RT const char* RD Query as a character string. RX // const char* ajFeattableGetQryC(const AjPFeattable ftable) { return MAJSTRGETPTR(ajFeattableGetQryS(ftable)); } ID ajFeattableGetQryS TY public MO ajfeat LB core XX DE Returns the query string of a feature table data object. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r ftable const AjPFeattable PD Feature table data object. PX RT const AjPStr RD Query as a string. RX // const AjPStr ajFeattableGetQryS(const AjPFeattable ftable) { ajDebug("ajFeattableGetQryS '%S'\n", ftable->Qry); if(ajStrGetLen(ftable->Qry)) return ftable->Qry; feattableMakeQry(ftable, &featTempQry); return featTempQry; } ID ajFeattableGetTypeC TY public MO ajfeat LB core XX DE Returns the name of a feature table object. This is a copy of the DE pointer to the name, and is still owned by the feature table DE and is not to be destroyed. XX PN [1] PA r thys const AjPFeattable PD Feature table PX RT const char* RD Feature table type. RX // const char* ajFeattableGetTypeC(const AjPFeattable thys) { return ajStrGetPtr(thys->Type); } ID ajFeattableGetTypeS TY public MO ajfeat LB core XX DE Returns the name of a feature table object. This is a copy of the DE pointer to the name, and is still owned by the feature table DE and is not to be destroyed. XX PN [1] PA r thys const AjPFeattable PD Feature table PX RT const AjPStr RD Feature table name. RX // const AjPStr ajFeattableGetTypeS(const AjPFeattable thys) { return thys->Type; } ID ajFeattableGetXrefs TY public MO ajfeat LB core XX DE Returns all cross-references from a feature table XX PN [1] PA r thys const AjPFeattable PD Feature table PX PN [2] PA u Pxreflist AjPList* PD List of sequence cross-reference objects PX PN [3] PA w Ptaxid ajuint* PD Taxon ID PX RT AjBool RD True on success RX // AjBool ajFeattableGetXrefs(const AjPFeattable thys, AjPList *Pxreflist, ajuint *Ptaxid) { AjIList iterfeat = NULL; AjIList itertags = NULL; AjPTagval item = NULL; AjPSeqXref xref = NULL; ajlong ipos; ajuint inum = 0; AjPFeature feat = NULL; AjPList xreflist; AjPStrTok handle = NULL; AjPStr tmpstr = NULL; const AjPStr tagval = NULL; if(!*Pxreflist) *Pxreflist = ajListNew(); xreflist = *Pxreflist; *Ptaxid = 0; if(thys->Features) { iterfeat = ajListIterNewread(thys->Features); while(!ajListIterDone(iterfeat)) { feat = (AjPFeature)ajListIterGet(iterfeat); if(feat->Tags) { itertags = ajListIterNewread(feat->Tags); while(!ajListIterDone(itertags)) { item = (AjPTagval)ajListIterGet(itertags); if(ajStrMatchCaseC(MAJTAGVALGETTAG(item), "db_xref")) { tagval = ajTagvalGetValue(item); ipos = ajStrFindAnyK(tagval, ':'); if(ipos > 0) { inum++; xref = ajSeqxrefNew(); ajStrAssignSubS(&xref->Db, tagval, 0, ipos-1); ajStrAssignSubS(&xref->Id, tagval, ipos+1, -1); ajListPushAppend(xreflist, xref); xref->Start = ajFeatGetStart(feat); xref->End = ajFeatGetEnd(feat); xref->Type = XREF_DBXREF; if(!*Ptaxid && ajStrMatchCaseC(xref->Db, "taxon")) { if(!ajStrToUint(xref->Id, Ptaxid)) *Ptaxid = 0; } xref = NULL; } } } ajListIterDel(&itertags); } if(feat->GffTags) { itertags = ajListIterNewread(feat->GffTags); while(!ajListIterDone(itertags)) { item = (AjPTagval)ajListIterGet(itertags); if(ajStrMatchCaseC(MAJTAGVALGETTAG(item), "Dbxref")) { tagval = ajTagvalGetValue(item); handle = ajStrTokenNewC(tagval, ","); while(ajStrTokenNextParse(&handle, &tmpstr)) { ipos = ajStrFindAnyK(tmpstr, ':'); if(ipos > 0) { inum++; xref = ajSeqxrefNew(); ajStrAssignSubS(&xref->Db, tmpstr, 0, ipos-1); ajStrAssignSubS(&xref->Id, tmpstr, ipos+1, -1); ajListPushAppend(xreflist, xref); xref->Start = ajFeatGetStart(feat); xref->End = ajFeatGetEnd(feat); xref->Type = XREF_DBXREF; if(!*Ptaxid && ajStrMatchCaseC(xref->Db, "taxon")) { if(!ajStrToUint(xref->Id, Ptaxid)) *Ptaxid = 0; } xref = NULL; } } ajStrTokenDel(&handle); ajStrDel(&tmpstr); } } ajListIterDel(&itertags); } } } ajListIterDel(&iterfeat); if(!inum) return ajFalse; return ajTrue; } ID feattableMakeQry TY static MO ajfeat LB core XX DE Sets the query for a feature table data object. XX PN [1] PA r thys const AjPFeattable PD Feature table data object PX PN [2] PA w qry AjPStr* PD Query string in full PX RT void RD RX // static void feattableMakeQry(const AjPFeattable thys, AjPStr* qry) { ajDebug("feattableMakeQry (Seqid <%S> Formatstr <%S> Db <%S> " "Filename <%S>)\n", thys->Seqid, thys->Formatstr, thys->Db, thys->Filename); /* ajResourceTrace(thys); */ if(ajStrGetLen(thys->Db)) ajFmtPrintS(qry, "%S-id:%S", thys->Db, thys->Seqid); else { ajFmtPrintS(qry, "%S::%S:%S", thys->Formatstr, thys->Filename,thys->Seqid); } ajDebug(" result: <%S>\n", *qry); return; } ID ajFeatframeGetFrame TY public MO ajfeat LB core XX DE Converts a frame number in the range 0 to 3 to a GFF frame character DE or '.' as the general default XX PN [1] PA r frame ajint PD Feature frame number PX RT char RD character for this frame in GFF RX // char ajFeatframeGetFrame(ajint frame) { static char framestr[] = ".012"; if(frame < 0) return '.'; if(frame > 3) return '.'; return framestr[frame]; } ID ajFeatframeGetFrameNuc TY public MO ajfeat LB core XX DE Converts a frame number in the range 0 to 3 to a GFF frame character DE or '0' for the nucleotide default XX PN [1] PA r frame ajint PD Feature frame number PX RT char RD character for this frame in GFF RX // char ajFeatframeGetFrameNuc(ajint frame) { static char framestr[] = ".012"; if(frame <= 0) return '0'; if(frame > 3) return '0'; return framestr[frame]; } ID ajFeatstrandGetStrand TY public MO ajfeat LB core XX DE Converts a strand number to a GFF strand character. NULL characters DE are converted to '+' All other values are simply cast to character. XX PN [1] PA r strand ajint PD Strand PX RT char RD GFF character for this strand. RX // char ajFeatstrandGetStrand(ajint strand) { if(ajSysCastItoc(strand) != '-') return '+'; return '-'; } ID ajFeattableIsCircular TY public MO ajfeat LB core XX DE Returns true if a feature table is circular XX PN [1] PA r thys const AjPFeattable PD Feature table PX RT AjBool RD ajTrue for a circular feature table RX // AjBool ajFeattableIsCircular(const AjPFeattable thys) { return thys->Circular; } ID ajFeattableIsNuc TY public MO ajfeat LB core XX DE Returns ajTrue if a feature table is nucleotide XX PN [1] PA r thys const AjPFeattable PD Feature table PX RT AjBool RD ajTrue for a protein feature table RX // AjBool ajFeattableIsNuc(const AjPFeattable thys) { if(ajStrMatchC(thys->Type, "N")) return ajTrue; if(ajStrMatchC(thys->Type, "P")) return ajFalse; return ajTrue; } ID ajFeattableIsProt TY public MO ajfeat LB core XX DE Returns ajTrue if a feature table is protein XX PN [1] PA r thys const AjPFeattable PD Feature table PX RT AjBool RD ajTrue for a protein feature table RX // AjBool ajFeattableIsProt(const AjPFeattable thys) { if(ajStrMatchC(thys->Type, "P")) return ajTrue; if(ajStrMatchC(thys->Type, "N")) return ajFalse; return ajTrue; } ID ajFeattableGetBegin TY public MO ajfeat LB core XX DE Returns the feature table start position, or 1 if no start has been set. XX PN [1] PA r thys const AjPFeattable PD feature table object PX RT ajint RD Start position. RX // ajint ajFeattableGetBegin(const AjPFeattable thys) { if(!thys->Start) return 1; return ajFeattablePos(thys, thys->Start); } ID ajFeattableGetEnd TY public MO ajfeat LB core XX DE Returns the features table end position, or the feature table length if DE no end has been set. XX PN [1] PA r thys const AjPFeattable PD feature table object PX RT ajint RD End position. RX // ajint ajFeattableGetEnd(const AjPFeattable thys) { if(!thys->End) return (ajFeattableGetLen(thys)); return ajFeattablePosI(thys, ajFeattableGetBegin(thys), thys->End); } ID ajFeattableGetLen TY public MO ajfeat LB core XX DE Returns the sequence length of a feature table XX PN [1] PA r thys const AjPFeattable PD Feature table PX RT ajint RD Length in bases or residues RX // ajint ajFeattableGetLen(const AjPFeattable thys) { if(!thys) return 0; return (thys->Len); } ID ajFeattableGetName TY public MO ajfeat LB core XX DE Returns the name of a feature table object. This is a copy of the DE pointer to the name, and is still owned by the feature table DE and is not to be destroyed. XX PN [1] PA r thys const AjPFeattable PD Feature table PX RT const AjPStr RD Feature table name. RX // const AjPStr ajFeattableGetName(const AjPFeattable thys) { if(!thys) return NULL; return thys->Seqid; } ID ajFeattableGetSize TY public MO ajfeat LB core XX DE Returns the size of a feature table object. XX PN [1] PA r thys const AjPFeattable PD Feature table PX RT ajuint RD Feature table size. RX // ajuint ajFeattableGetSize(const AjPFeattable thys) { if(!thys) return 0; return (ajuint) ajListGetLength(thys->Features); } ID ajFeattableGetScorerange TY public MO ajfeat LB core XX DE Returns the range of scores for feature table object. XX PN [1] PA r thys const AjPFeattable PD Feature table PX PN [2] PA w minscore float* PD Minimum score PX PN [3] PA w maxscore float* PD Maximum score PX RT AjBool RD True if scores were found RX // AjBool ajFeattableGetScorerange(const AjPFeattable thys, float *minscore, float *maxscore) { AjBool ret = ajFalse; float curmin = 0.0; float curmax = 0.0; AjIList iter; AjPFeature gf = NULL; if(!thys) return 0; *minscore = 0.0; *maxscore = 0.0; iter = ajListIterNewread(thys->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); curmin = 0.0; curmax = 0.0; if(ajFeatGetScorerange(gf, &curmin, &curmax)) { if(curmin < *minscore) { ret = ajTrue; *minscore = curmin; } if(curmax > *maxscore) { ret = ajTrue; *maxscore = curmax; } } } ajListIterDel(&iter); return ret; } ID ajFeatGetScorerange TY public MO ajfeat LB core XX DE Returns the range of scores for a feature object. XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA w minscore float* PD Minimum score PX PN [3] PA w maxscore float* PD Maximum score PX RT AjBool RD True if scores were found RX // AjBool ajFeatGetScorerange(const AjPFeature thys, float *minscore, float *maxscore) { AjBool ret = ajFalse; float curmin = 0.0; float curmax = 0.0; AjIList iter; AjPFeature gf = NULL; if(!thys) return 0; *minscore = 0.0; *maxscore = 0.0; if(thys->Score < *minscore) { ret = ajTrue; *minscore = curmin; } if(thys->Score > *maxscore) { ret = ajTrue; *maxscore = curmax; } iter = ajListIterNewread(thys->Subfeatures); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); curmin = 0.0; curmax = 0.0; if(ajFeatGetScorerange(gf, &curmin, &curmax)) { if(curmin < *minscore) { ret = ajTrue; *minscore = curmin; } if(curmax > *maxscore) { ret = ajTrue; *maxscore = curmax; } } } ajListIterDel(&iter); return ret; } ID ajFeatGetFlags TY public MO ajfeat LB core XX DE Returns the sequence matching a feature. For multiple location features DE (joins in an EMBL/GenBank feature table) the full feature table is used DE to find all exons. DE DE The database name is used to retrieve sequences from other entries XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA u Pflagstr AjPStr* PD Sequence for this feature PX RT AjBool RD True on success RX // AjBool ajFeatGetFlags(const AjPFeature thys, AjPStr* Pflagstr) { ajStrAssignC(Pflagstr, ""); if(thys->Flags & AJFEATFLAG_START_BEFORE_SEQ) ajStrAppendC(Pflagstr, "Flags & AJFEATFLAG_END_AFTER_SEQ) ajStrAppendC(Pflagstr, ">end "); if(thys->Flags & AJFEATFLAG_BETWEEN_SEQ) ajStrAppendC(Pflagstr, "x^y "); if(thys->Flags & AJFEATFLAG_START_TWO) ajStrAppendC(Pflagstr, "startrange "); if(thys->Flags & AJFEATFLAG_END_TWO) ajStrAppendC(Pflagstr, "endrange "); if(thys->Flags & AJFEATFLAG_POINT) ajStrAppendC(Pflagstr, "single-base "); if(thys->Flags & AJFEATFLAG_COMPLEMENT_MAIN) ajStrAppendC(Pflagstr, "complement(join) "); if(thys->Flags & AJFEATFLAG_MULTIPLE) ajStrAppendC(Pflagstr, "multiple "); if(thys->Flags & AJFEATFLAG_GROUP) ajStrAppendC(Pflagstr, "group "); if(thys->Flags & AJFEATFLAG_ORDER) ajStrAppendC(Pflagstr, "order "); if(thys->Flags & AJFEATFLAG_ONEOF) ajStrAppendC(Pflagstr, "oneof "); if(thys->Flags & AJFEATFLAG_REMOTEID) ajStrAppendC(Pflagstr, "remoteid "); if(thys->Flags & AJFEATFLAG_LABEL) ajStrAppendC(Pflagstr, "LABEL "); if(thys->Flags & AJFEATFLAG_START_UNSURE) ajStrAppendC(Pflagstr, "start-unsure "); if(thys->Flags & AJFEATFLAG_END_UNSURE) ajStrAppendC(Pflagstr, "end-unsure "); ajStrTrimWhite(Pflagstr); return ajTrue; } ID ajFeatGetSeq TY public MO ajfeat LB core XX DE Returns the sequence matching a feature. DE DE The database name is used to retrieve sequences from other entries XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA r seq const AjPSeq PD Sequence for the current feature table PX PN [3] PA u Pseqstr AjPStr* PD Sequence for this feature PX RT AjBool RD True on success RX // AjBool ajFeatGetSeq(const AjPFeature thys, const AjPSeq seq, AjPStr* Pseqstr) { AjPSeq remoteseq = NULL; /* AjBool isjoin = ajFalse; */ AjPStr tmpseq = NULL; AjBool compjoin = ajFalse; ajStrSetClear(Pseqstr); /* isjoin = ajFeatIsMultiple(thys); */ ajDebug("ajFeatGetSeq usa:%S\n", ajSeqGetUsaS(seq)); if(thys->Flags & AJFEATFLAG_BETWEEN_SEQ) return ajTrue; ajFeatTrace(thys); if(thys->Flags & AJFEATFLAG_REMOTEID) { if(!remoteseq) remoteseq = ajSeqNew(); ajFeatGetRemoteseq(thys, ajSeqGetUsaS(seq), remoteseq); ajStrAppendS(Pseqstr, ajSeqGetSeqS(remoteseq)); } else { if(thys->Strand == '-' && !compjoin) { ajStrAppendSubS(&tmpseq, ajSeqGetSeqS(seq), ajFeatGetStart(thys)-1, ajFeatGetEnd(thys)-1); ajSeqstrReverse(&tmpseq); ajStrInsertS(Pseqstr, 0, tmpseq); ajStrDel(&tmpseq); } else { ajStrAppendSubS(Pseqstr, ajSeqGetSeqS(seq), ajFeatGetStart(thys)-1, ajFeatGetEnd(thys)-1); } } ajSeqDel(&remoteseq); return ajTrue; } ID ajFeatGetSeqJoin TY public MO ajfeat LB core XX DE Returns the sequence matching a feature. For multiple location features DE (joins in an EMBL/GenBank feature table) the full feature table is used DE to find all exons. DE DE The database name is used to retrieve sequences from other entries XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA r seq const AjPSeq PD Sequence for the current feature table PX PN [3] PA u Pseqstr AjPStr* PD Sequence for this feature PX RT AjBool RD True on success RX // AjBool ajFeatGetSeqJoin(const AjPFeature thys, const AjPSeq seq, AjPStr* Pseqstr) { const AjPFeature gf; AjIList iter = NULL; AjPSeq remoteseq = NULL; /* AjBool isjoin = ajFalse; */ AjPStr tmpseq = NULL; AjBool compjoin = ajFalse; AjPStr flags = NULL; ajuint count=0; ajDebug("ajFeatGetSeqJoin nfeat:%Lu usa:%S %u..%u %S\n", ajListGetLength(thys->Subfeatures), ajSeqGetUsaS(seq), thys->Start, thys->End, thys->Type); if(!ajListGetLength(thys->Subfeatures)) return ajFeatGetSeq(thys, seq, Pseqstr); ajStrSetClear(Pseqstr); /* isjoin = ajFeatIsMultiple(thys); */ if(thys->Flags & AJFEATFLAG_COMPLEMENT_MAIN) compjoin = ajTrue; iter = ajListIterNewread(thys->Subfeatures); while(!ajListIterDone(iter)) { count++; gf = (const AjPFeature) ajListIterGet(iter); ajFeatGetFlags(gf, &flags); ajFeatTrace(gf); if(gf->Flags & AJFEATFLAG_BETWEEN_SEQ) continue; if(gf->Flags & AJFEATFLAG_REMOTEID) { if(!remoteseq) remoteseq = ajSeqNew(); ajFeatGetRemoteseq(gf, ajSeqGetUsaS(seq), remoteseq); ajStrAppendS(Pseqstr, ajSeqGetSeqS(remoteseq)); } else { if(gf->Strand == '-' && !compjoin) { ajStrAppendSubS(&tmpseq, ajSeqGetSeqS(seq), ajFeatGetStart(gf)-1, ajFeatGetEnd(gf)-1); ajSeqstrReverse(&tmpseq); ajStrAppendS(Pseqstr, tmpseq); ajStrDel(&tmpseq); } else { ajStrAppendSubS(Pseqstr, ajSeqGetSeqS(seq), ajFeatGetStart(gf)-1, ajFeatGetEnd(gf)-1); } } } if(compjoin) ajSeqstrReverse(Pseqstr); ajListIterDel(&iter); ajSeqDel(&remoteseq); ajStrDel(&flags); return ajTrue; } ID ajFeatGetRemoteseq TY public MO ajfeat LB core XX DE Returns a sequence entry from a feature location which points to DE another entry XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA r usa const AjPStr PD usa of query PX PN [3] PA u seq AjPSeq PD Sequence object for results PX RT AjBool RD True on success RX // AjBool ajFeatGetRemoteseq(const AjPFeature thys, const AjPStr usa, AjPSeq seq) { AjPStr baseusa = NULL; ajuint ilen; const AjPStr remoteid = NULL; remoteid = ajSeqtestIsSeqversion(thys->Remote); if(!remoteid) remoteid = thys->Remote; ajSeqUsaGetBase(usa, &baseusa); ajStrAppendK(&baseusa, ':'); ajStrAppendS(&baseusa, remoteid); ajSeqGetFromUsa(baseusa, thys->Protein, seq); ilen = ajSeqGetLen(seq); if(thys->Strand == '-') ajSeqSetRangeRev(seq, ajFeatGetStart(thys), ajFeatGetEnd(thys)); else ajSeqSetRange(seq, ajFeatGetStart(thys), ajFeatGetEnd(thys)); ajSeqTrim(seq); ajDebug("ajFeatGetRemoteseq (%S) '%S' => '%S' %u %u..%u (%u)\n", thys->Remote, usa, baseusa, ilen, ajFeatGetStart(thys), ajFeatGetEnd(thys), ajSeqGetLen(seq)); ajStrDel(&baseusa); return ajTrue; } ID ajFeatGetSubtype TY public MO ajfeat LB core XX DE Returns the feature type of the first subfeature XX PN [1] PA r thys const AjPFeature PD Feature PX RT const AjPStr RD Subfeature type RX // const AjPStr ajFeatGetSubtype(const AjPFeature thys) { AjPFeature subft= NULL; if(!ajListGetLength(thys->Subfeatures)) return NULL; ajListPeek(thys->Subfeatures, (void**) &subft); return subft->Type; } ID ajFeatGetXrefs TY public MO ajfeat LB core XX DE Returns all cross-references from a feature XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA u Pxreflist AjPList* PD List of sequence cross-reference objects PX RT AjBool RD True on success RX // AjBool ajFeatGetXrefs(const AjPFeature thys, AjPList *Pxreflist) { AjIList iter = NULL; AjPTagval item = NULL; AjPSeqXref xref = NULL; ajlong ipos; ajuint inum = 0; AjPList xreflist; AjPStrTok handle = NULL; AjPStr tmpstr = NULL; const AjPStr tagval = NULL; if(!*Pxreflist) *Pxreflist = ajListNew(); xreflist = *Pxreflist; if(thys->Tags) { iter = ajListIterNewread(thys->Tags); while(!ajListIterDone(iter)) { item = (AjPTagval)ajListIterGet(iter); if(ajStrMatchCaseC(MAJTAGVALGETTAG(item), "db_xref")) { tagval = ajTagvalGetValue(item); ipos = ajStrFindAnyK(tagval, ':'); if(ipos > 0) { inum++; xref = ajSeqxrefNew(); ajStrAssignSubS(&xref->Db, tagval, 0, ipos-1); ajStrAssignSubS(&xref->Id, tagval, ipos+1, -1); xref->Start = ajFeatGetStart(thys)-1; xref->End = ajFeatGetEnd(thys)-1; ajListPushAppend(xreflist, xref); xref->Type = XREF_DBXREF; xref = NULL; } } } ajListIterDel(&iter); } if(thys->GffTags) { iter = ajListIterNewread(thys->GffTags); while(!ajListIterDone(iter)) { item = (AjPTagval)ajListIterGet(iter); if(ajStrMatchCaseC(MAJTAGVALGETTAG(item), "Dbxref")) { tagval = ajTagvalGetValue(item); handle = ajStrTokenNewC(tagval, ","); while(ajStrTokenNextParse(&handle, &tmpstr)) { ipos = ajStrFindAnyK(tmpstr, ':'); if(ipos > 0) { inum++; xref = ajSeqxrefNew(); ajStrAssignSubS(&xref->Db, tmpstr, 0, ipos-1); ajStrAssignSubS(&xref->Id, tmpstr, ipos+1, -1); ajListPushAppend(xreflist, xref); xref->Start = ajFeatGetStart(thys); xref->End = ajFeatGetEnd(thys); xref->Type = XREF_DBXREF; } } ajStrTokenDel(&handle); ajStrDel(&tmpstr); } } ajListIterDel(&iter); } if(!inum) return ajFalse; return ajTrue; } ID ajFeatLocMark TY public MO ajfeat LB core XX DE Returns a sequence entry converted to lower case where a feature DE location matches. XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA r table const AjPFeattable PD Feature table PX PN [3] PA u Pseqstr AjPStr* PD Sequence to be marked in lower case PX RT AjBool RD true on success RX // AjBool ajFeatLocMark(const AjPFeature thys, const AjPFeattable table, AjPStr* Pseqstr) { const AjPFeature gf; AjIList iter = NULL; iter = ajListIterNewread(table->Features); while(!ajListIterDone(iter)) { gf = (const AjPFeature) ajListIterGet(iter); if(gf->Group == thys->Group) { if(gf->Flags & AJFEATFLAG_BETWEEN_SEQ) continue; if(gf->Flags & AJFEATFLAG_REMOTEID) continue; ajStrFmtLowerSub(Pseqstr,ajFeatGetStart(gf)-1,ajFeatGetEnd(gf)-1); } } ajListIterDel(&iter); return ajTrue; } ID ajFeatGetNoteC TY public MO ajfeat LB core XX DE Finds a named note tag (with a * prefix) XX PN [1] PA r thys const AjPFeature PD Feature object PX PN [2] PA r name const char* PD Tag name PX PN [3] PA w val AjPStr* PD Tag value (if found) PX RT AjBool RD ajTrue on success (feature tag found) RX // AjBool ajFeatGetNoteC(const AjPFeature thys, const char* name, AjPStr* val) { return ajFeatGetNoteCI(thys, name, 0, val); } ID ajFeatGetNoteCI TY public MO ajfeat LB core XX DE Finds a named note tag (with a * prefix) XX PN [1] PA r thys const AjPFeature PD Feature object PX PN [2] PA r name const char* PD Tag name PX PN [3] PA r count ajint PD Tag count: zero for any, 1 for first, 2 for second PX PN [4] PA w val AjPStr* PD Tag value (if found) PX RT AjBool RD ajTrue on success (feature tag found) RX // AjBool ajFeatGetNoteCI(const AjPFeature thys, const char* name, ajint count, AjPStr* val) { AjIList iter = NULL; AjPTagval item = NULL; ajint icount = 0; ajuint ilen = strlen(name); const AjPStr tagval = NULL; /*ajDebug("ajFeatGetNoteCI '%s'\n", name);*/ if(thys->Tags) { iter = ajListIterNewread(thys->Tags); while(!ajListIterDone(iter)) { item = (AjPTagval)ajListIterGet(iter); if(ajFeattagIsNote(MAJTAGVALGETTAG(item))) { tagval = ajTagvalGetValue(item); if(ajStrGetCharFirst(tagval) == '*') { /*ajDebug(" testing *name\n");*/ if(ajCharPrefixCaseC(ajStrGetPtr(tagval)+1, name)) { icount++; /*ajDebug(" found [%d] '%S'\n", icount, name);*/ if(icount >= count) { if(ajStrGetLen(tagval) > (ilen+1)) { if(ajStrGetCharPos(tagval, ilen+1) != ' ') return ajFalse; ajStrAssignC(val, ajStrGetPtr(tagval) + ilen+2); } else /* no value */ ajStrAssignClear(val); ajListIterDel(&iter); return ajTrue; } } } } } } ajStrDel(val); ajListIterDel(&iter); return ajFalse; } ID ajFeatGetNoteSI TY public MO ajfeat LB core XX DE Finds a named note tag (with a * prefix) XX PN [1] PA r thys const AjPFeature PD Feature object PX PN [2] PA r name const AjPStr PD Tag name PX PN [3] PA r count ajint PD Tag count: zero for any, 1 for first, 2 for second PX PN [4] PA w val AjPStr* PD Tag value (if found) PX RT AjBool RD ajTrue on success (feature tag found) RX // AjBool ajFeatGetNoteSI(const AjPFeature thys, const AjPStr name, ajint count, AjPStr* val) { return ajFeatGetNoteCI(thys, ajStrGetPtr(name), count, val); } ID ajFeatGetNoteS TY public MO ajfeat LB core XX DE Finds a named note tag (with a * prefix) XX PN [1] PA r thys const AjPFeature PD Feature object PX PN [2] PA r name const AjPStr PD Tag name PX PN [3] PA w val AjPStr* PD Tag value (if found) PX RT AjBool RD ajTrue on success (feature tag found) RX // AjBool ajFeatGetNoteS(const AjPFeature thys, const AjPStr name, AjPStr* val) { return ajFeatGetNoteSI(thys, name, 0, val); } ID ajFeatGetTagC TY public MO ajfeat LB core XX DE Returns the nth value of a named feature tag. DE DE If not found as a tag, also searches for a named note XX PN [1] PA r thys const AjPFeature PD Feature object PX PN [2] PA r tname const char* PD Tag name PX PN [3] PA r num ajint PD Tag number PX PN [4] PA w Pval AjPStr* PD Tag value (if found) PX RT AjBool RD ajTrue on success (feature tag found) RX // AjBool ajFeatGetTagC(const AjPFeature thys, const char* tname, ajint num, AjPStr* Pval) { AjIList iter = NULL; AjPTagval item = NULL; ajint inum = 0; AjBool isnote; ajint noteposcolon=0; ajint noteposvalue=0; const AjPStr tagval = NULL; ajDebug("ajFeatGetTagC '%s'\n", tname); isnote = ajCharMatchC(tname, "note"); noteposcolon = strlen(tname) + 1; noteposvalue = strlen(tname) + 3; if(thys->Tags) { iter = ajListIterNewread(thys->Tags); while(!ajListIterDone(iter)) { item = (AjPTagval)ajListIterGet(iter); tagval = ajTagvalGetValue(item); ajDebug("testing '%S'\n", ajTagvalGetTag(item)); if(ajStrMatchCaseC(MAJTAGVALGETTAG(item), tname)) { inum++; ajDebug("test1 inum %d\n", inum); if(num == inum) { ajDebug("++match1\n"); ajStrAssignS(Pval, tagval); ajListIterDel(&iter); return ajTrue; } } else if(!isnote && ajStrMatchCaseC(MAJTAGVALGETTAG(item), "note") && ajStrGetCharFirst(tagval) == '*' && ajCharPrefixCaseC(ajStrGetPtr(tagval)+1, tname) && ajStrGetCharPos(tagval, noteposcolon) == ':') { inum++; ajDebug("test2 inum %d\n", inum); if(num == inum) { ajDebug("++match2 from %d\n", noteposvalue); ajStrAssignSubS(Pval, tagval, noteposvalue, -1); ajListIterDel(&iter); return ajTrue; } } } } ajDebug("No match to '%s'\n", tname); ajStrDel(Pval); ajListIterDel(&iter); return ajFalse; } ID ajFeatGetTagS TY public MO ajfeat LB core XX DE Returns the nth value of a named feature tag. DE DE If not found as a tag, also searches for a named note XX PN [1] PA r thys const AjPFeature PD Feature object PX PN [2] PA r name const AjPStr PD Tag name PX PN [3] PA r num ajint PD Tag number PX PN [4] PA w val AjPStr* PD Tag value (if found) PX RT AjBool RD ajTrue on success (feature tag found) RX // AjBool ajFeatGetTagS(const AjPFeature thys, const AjPStr name, ajint num, AjPStr* val) { AjIList iter = NULL; AjPTagval item = NULL; ajint inum = 0; AjBool isnote; ajint noteposcolon=0; ajint noteposvalue=0; const AjPStr tagval = NULL; isnote = ajStrMatchC(name, "note"); noteposcolon = ajStrGetLen(name) + 1; noteposvalue = ajStrGetLen(name) + 3; if(thys->Tags) { iter = ajListIterNewread(thys->Tags); while(!ajListIterDone(iter)) { item = (AjPTagval)ajListIterGet(iter); tagval = ajTagvalGetValue(item); if(ajStrMatchCaseS(MAJTAGVALGETTAG(item), name)) { inum++; if(num == inum) { ajStrAssignS(val, tagval); ajListIterDel(&iter); return ajTrue; } } else if(!isnote && ajStrMatchCaseC(MAJTAGVALGETTAG(item), "note") && ajStrGetCharFirst(tagval) == '*' && ajCharPrefixCaseS(ajStrGetPtr(tagval)+1, name) && ajStrGetCharPos(tagval, noteposcolon) == ':') { inum++; if(num == inum) { ajStrAssignSubS(val, tagval, noteposvalue, -1); ajListIterDel(&iter); return ajTrue; } } } } ajStrDel(val); ajListIterDel(&iter); return ajFalse; } ID ajFeatGetType TY public MO ajfeat LB core XX DE Returns the type (key) of a feature object. This is a copy of the DE pointer to the type, and is still owned by the feature DE and is not to be destroyed. XX PN [1] PA r thys const AjPFeature PD Feature object PX RT const AjPStr RD Feature type, read only RX // const AjPStr ajFeatGetType(const AjPFeature thys) { if(!FeatInitDone) featInit(); if(thys->Protein) return ajFeattypeGetExternal(thys->Type, FeatTypeTableProtein); else return ajFeattypeGetExternal(thys->Type, FeatTypeTableDna); } ID ajFeatGetSource TY public MO ajfeat LB core XX DE Returns the source name of a feature object. XX PN [1] PA r thys const AjPFeature PD Feature object PX RT const AjPStr RD Feature source name RX // const AjPStr ajFeatGetSource(const AjPFeature thys) { return thys->Source; } ID ajFeatGetStart TY public MO ajfeat LB core XX DE Returns the start position of a feature object. XX PN [1] PA r thys const AjPFeature PD Feature object PX RT ajuint RD Feature start position RX // ajuint ajFeatGetStart(const AjPFeature thys) { if((thys->Flags & AJFEATFLAG_START_TWO) && (thys->Start2 < thys->Start)) return thys->Start2; return thys->Start; } ID ajFeatGetStrand TY public MO ajfeat LB core XX DE Returns the strand of a feature object. XX PN [1] PA r thys const AjPFeature PD Feature object PX RT char RD Feature strand code RX // char ajFeatGetStrand(const AjPFeature thys) { if(thys->Protein) return '.'; if(thys->Strand == '-') return '-'; else return '+'; } ID ajFeatGetEnd TY public MO ajfeat LB core XX DE Returns the end position of a feature object. XX PN [1] PA r thys const AjPFeature PD Feature object PX RT ajuint RD Feature end position RX // ajuint ajFeatGetEnd(const AjPFeature thys) { if((thys->Flags & AJFEATFLAG_END_TWO) && (thys->End2 < thys->End)) return thys->End2; return thys->End; } ID ajFeatGetLength TY public MO ajfeat LB core XX DE Returns the sequence length of a feature object. XX PN [1] PA r thys const AjPFeature PD Feature object PX RT ajuint RD Feature length RX // ajuint ajFeatGetLength(const AjPFeature thys) { return thys->End - thys->Start + 1; } ID ajFeatGetScore TY public MO ajfeat LB core XX DE Returns the score of a feature object. XX PN [1] PA r thys const AjPFeature PD Feature object PX RT float RD Feature score RX // float ajFeatGetScore(const AjPFeature thys) { return thys->Score; } ID ajFeatGetForward TY public MO ajfeat LB core XX DE Returns the direction of a feature object. XX PN [1] PA r thys const AjPFeature PD Feature object PX RT AjBool RD ajTrue for a forward direction, ajFalse for reverse RX // AjBool ajFeatGetForward(const AjPFeature thys) { if(ajSysCastItoc(thys->Strand) != '-') return ajTrue; return ajFalse; } ID ajFeatGetFrame TY public MO ajfeat LB core XX DE Returns the reading frame of a feature object. XX PN [1] PA r thys const AjPFeature PD Feature object PX RT ajint RD Feature reading frame (zero for undefined) RX // ajint ajFeatGetFrame(const AjPFeature thys) { return thys->Frame; } ID ajFeatGetTranslation TY public MO ajfeat LB core XX DE Returns translation tag value XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA w Ptrans AjPStr* PD Translations PX RT AjBool RD True if translation tag was found RX // AjBool ajFeatGetTranslation(const AjPFeature thys, AjPStr *Ptrans) { return ajFeatGetTagC(thys, "translation", 1, Ptrans); } ID ajFeatTest TY public MO ajfeat LB core XX DE Temporary testing function for feature handling DE to be deleted when all is working happily XX RT void RD RX // void ajFeatTest(void) { AjPFeattable table = NULL; AjPStr desc = NULL; AjPStr source = NULL; AjPStr type = NULL; AjPFeature ft = NULL; featInit(); table = ajFeattableNew(NULL); ajStrAssignC(&source, "testft"); ajStrAssignC(&type, "misc_feature"); ajStrAssignC(&desc, "Just testing"); ft = ajFeatNew(table, source, type, 5, 7, (float)1.23, '+', 0); ajFeatSetDesc(ft, desc); ajStrAssignC(&desc, "Testing again"); ft = ajFeatNew(table, source, type, 9, 19, (float)4.56, '-', 3); ajFeatSetDesc(ft, desc); ajFeattableTrace(table); ajFeattableDel(&table); ajStrDel(&desc); ajStrDel(&source); ajStrDel(&type); return; } ID featInit TY static MO ajfeat LB core XX DE Initialises everything needed for feature handling XX RT void RD RX // static void featInit(void) { if(FeatInitDone) return; FeatInitDone = ajTrue; if(!FeatTypeTableDna) { FeatTypeTableDna = ajTablestrNewCase(200); FeatTagsTableDna = ajTablestrNewCase(200); featVocabRead("emboss",FeatTypeTableDna, FeatTagsTableDna); } if(!FeatTypeTableProtein) { FeatTypeTableProtein = ajTablestrNewCase(200); FeatTagsTableProtein = ajTablestrNewCase(200); featVocabRead("protein", FeatTypeTableProtein, FeatTagsTableProtein); } /*ajDebug("Tables internal (Dna, Prot) Type: %x %x Tags: %x %x\n", FeatTypeTableDna, FeatTypeTableProtein, FeatTagsTableDna, FeatTagsTableProtein);*/ return; } ID featVocabRead TY static MO ajfeat LB core XX DE Reads the possible feature types (keys) and tags (qualifiers) DE from files. XX PN [1] PA r name const char* PD Feature type ("emboss", "protein", or external PD feature types "swiss", "gff", "embl", etc. PX PN [2] PA w pTypeTable AjPTable PD Feature type table PX PN [3] PA w pTagsTable AjPTable PD Feature tags table PX RT AjBool RD ajTrue on success RX // static AjBool featVocabRead(const char* name, AjPTable pTypeTable, AjPTable pTagsTable) { AjPStr TypeFName = NULL; AjPStr TagsFName = NULL; /* First read in the list of all possible tags */ ajFmtPrintS(&TagsFName, "Etags.%s", name); ajDebug("featVocabRead '%S' typetable %x tagstable %x\n", TagsFName, pTypeTable, pTagsTable); if(!featVocabReadTags(TagsFName, pTagsTable, ajTrue)) return ajFalse; /*ajDebug("Trace tagstable '%S'", TagsFName);*/ /*ajTablestrTrace(pTagsTable);*/ /* Efeatures file ** format: featuretype ** M/mandatorytag ** /tag ** /tag ** ** All tags must be defined in the Etags file (read earlier into pTagsTable) */ ajFmtPrintS(&TypeFName, "Efeatures.%s", name); /*ajDebug("Trying to open %S...\n",TypeFName);*/ if(!featVocabReadTypes(TypeFName, pTypeTable, pTagsTable, ajTrue)) return ajFalse; /*ajDebug("Trace typetable '%S'", TypeFName);*/ /*ajTablestrTrace(pTypeTable);*/ ajStrDel(&TagsFName); ajStrDel(&TypeFName); return ajTrue; } ID featVocabReadTags TY static MO ajfeat LB core XX DE Reads the possible feature tags (qualifiers) DE from files. XX PN [1] PA r fname const AjPStr PD Feature tags definitions datafile name PX PN [2] PA w pTagsTable AjPTable PD Feature tags table PX PN [3] PA r recursion AjBool PD If true process any include lines PX RT AjBool RD ajTrue on success RX // static AjBool featVocabReadTags(const AjPStr fname, AjPTable pTagsTable, AjBool recursion) { AjPFile TagsFile = NULL; AjPStr TagsFName = NULL; AjPStr line = NULL; AjPStr tagname = NULL; /* stored in table */ AjPStr tagstr = NULL; /* stored in table */ AjPStr defname = NULL; /* stored in table */ AjPStr tagtype = NULL; AjPStr token = NULL; AjPStr rest = NULL; ajint linecount = 0; ajlong tagscount = 0; ajint numtype = -1; ajint i; const char* TagType[] = { "QTEXT", /* quoted text */ "QWORD", /* quoted text, no space */ "BTEXT", /* bracketed text */ "TEXT", /* unquoted, no white space */ "VOID", /* no value */ "LIMITED", /* limited vocabulary */ "QLIMITED", /* limited vocabulary, with quotes */ "SPECIAL", /* special formatting */ "QSPECIAL", /* special formatting, with quotes */ "TAGVAL", /* /type=value for swissprot */ NULL }; TagsFile = ajDatafileNewInNameS(fname); if(!TagsFile) { ajErr("Unable to read feature tags data file '%S'\n", fname); return ajFalse; } tagscount = ajTableGetLength(pTagsTable); linecount = 0; while(ajReadlineTrim(TagsFile,&line)) { linecount++; ajStrRemoveWhiteExcess(&line); if(ajStrPrefixC(line, "#")) { if(recursion && ajStrPrefixCaseC(line, "#include ")) { ajStrCutStart(&line, 9); ajStrExtractWord(line, &rest, &TagsFName); ajStrDel(&rest); if(!featVocabReadTags(TagsFName, pTagsTable, ajTrue)) { ajStrDel(&line); ajStrDel(&TagsFName); return ajFalse; } tagscount = ajTableGetLength(pTagsTable); } if(recursion && ajStrPrefixCaseC(line, "#includeonly ")) { ajStrCutStart(&line, 13); ajStrExtractWord(line, &rest, &TagsFName); ajStrDel(&rest); if(!featVocabReadTags(TagsFName, pTagsTable, ajFalse)) { ajStrDel(&line); ajStrDel(&TagsFName); return ajFalse; } tagscount = ajTableGetLength(pTagsTable); } } else if(ajStrGetLen(line)) /* skip comments */ { ajStrDel(&tagname); /* create a new tag */ if(featTagName(line, &tagname, &tagtype, &featTmpStr)) { numtype = -1; for(i=0; TagType[i]; i++) if(!ajStrCmpC(tagtype,TagType[i])) { numtype = i; break; } if(numtype < 0) { /*ajDebug("Bad feature tag type '%S' in %F line %d\n", tagtype, TagsFile, linecount);*/ ajErr("Bad feature tag type '%S' in %F line %d", tagtype, TagsFile, linecount); break; } ajStrDel(&tagtype); tagscount++; if(tagscount == 1) /* save first tag as the default */ { tagstr = NULL; ajStrAssignClear(&defname); ajStrAssignS(&tagstr, tagname); if(ajTablePut (pTagsTable, defname, tagstr)) ajErr("%S duplicate tag for '%S'", fname, defname); tagstr = NULL; defname = NULL; } tagstr = NULL; ajFmtPrintS(&tagstr, "%s;", TagType[numtype]); /* ** Controlled vocabulary : ** read the list of valid values */ if(ajCharMatchCaseC(TagType[numtype], "LIMITED") || ajCharMatchCaseC(TagType[numtype], "QLIMITED")) { ajStrTokenAssignC(&featVocabSplit, featTmpStr, "\", \t"); while(ajStrTokenNextParse(&featVocabSplit, &token)) { ajFmtPrintAppS(&tagstr, "%S;", token); } ajStrDelStatic(&featTmpStr); ajStrTokenDel(&featVocabSplit); } if(ajTablePut(pTagsTable, tagname, tagstr)) ajErr("%S duplicate tag for '%S'", fname, tagname); tagstr = NULL; tagname = NULL; } else { /*ajDebug("** line format bad **\n%S", line);*/ } } } ajFileClose(&TagsFile); ajStrDel(&TagsFName); ajStrDel(&line); ajStrDel(&token); ajStrDel(&tagname); ajStrDel(&tagtype); /* ajTableTrace(pTagsTable); ajTablestrPrint(pTagsTable); */ return ajTrue; } ID featTagName TY static MO ajfeat LB core XX DE Process anything up to ':' as an entry ID (a feature in another entry) DE Anything up to ',' is this location XX PN [1] PA r line const AjPStr PD Tags file line PX PN [2] PA w name AjPStr* PD Tag name PX PN [3] PA w type AjPStr* PD Tag type PX PN [4] PA w rest AjPStr* PD Remainder of line PX RT AjBool RD ajTrue if a match was found RD ajFalse means an error occurred RX // static AjBool featTagName(const AjPStr line, AjPStr* name, AjPStr* type, AjPStr* rest) { const char* cp = ajStrGetPtr(line); ajStrAssignClear(name); ajStrAssignClear(type); ajStrAssignClear(rest); while(isspace((ajint)*cp)) cp++; while(*cp && !isspace((ajint)*cp)) ajStrAppendK(name, *cp++); while(isspace((ajint)*cp)) cp++; if(!*cp) { ajStrAssignClear(name); return ajFalse; } while(isalpha((ajint)*cp)) ajStrAppendK(type, *cp++); if(!ajStrMatchC(*type, "LIMITED") || ajStrMatchC(*type, "QLIMITED")) return ajTrue; while(isspace((ajint)*cp)) cp++; while(*cp) ajStrAppendK(rest, *cp++); return ajTrue; } ID featVocabReadTypes TY static MO ajfeat LB core XX DE Reads the possible feature types (keys) DE from files. XX PN [1] PA r fname const AjPStr PD Feature type definitions datafile name PX PN [2] PA w pTypeTable AjPTable PD Feature type table PX PN [3] PA r pTagsTable const AjPTable PD Feature tags table PX PN [4] PA r recursion AjBool PD If true process any include lines PX RT AjBool RD ajTrue on success RX // static AjBool featVocabReadTypes(const AjPStr fname, AjPTable pTypeTable, const AjPTable pTagsTable, AjBool recursion) { AjPStr TypeFName = NULL; AjPFile TypeFile = NULL; AjPStr line = NULL; AjPStr intids = NULL; AjPStr tag = NULL; AjPStr req = NULL; AjPStr localname = NULL; AjPStr sofaname = NULL; AjPStr type = NULL; AjPStr defname = NULL; AjPStr defid = NULL; AjPStr typtagstr = NULL; AjPStr savetype = NULL; AjPStr firstid = NULL; AjPStr token = NULL; AjPStr rest = NULL; AjPStr editstr = NULL; AjPStr modtype = NULL; const AjPStr tablestr = NULL; const AjPStr sofaid = NULL; const AjPStr storetype = NULL; ajlong typecount = 0; ajint filetypecount = 0; char reqchar; char modchar; ajlong ipos; AjBool ismodtype = ajFalse; AjPStr* Ptyptagstr = NULL; /* AjBool taginternal = ajFalse; */ AjBool newdefid = ajFalse; static ajuint level = 0; level++; TypeFile = ajDatafileNewInNameS(fname); if(!TypeFile) { ajErr("Unable to read feature types data file '%S'\n", fname); level--; return ajFalse; } filetypecount = 0; typecount = ajTableGetLength(pTypeTable); while(ajReadlineTrim(TypeFile,&line)) { ajStrRemoveWhiteExcess(&line); if(ajStrPrefixC(line,"#")) /* if a comment skip it */ { if(recursion && ajStrPrefixCaseC(line, "#include ")) { ajStrCutStart(&line, 9); ajStrExtractFirst(line, &rest, &TypeFName); ajStrDel(&rest); if(!featVocabReadTypes(TypeFName, pTypeTable, pTagsTable, ajTrue)) { ajStrDel(&line); ajStrDel(&TypeFName); ajStrDel(&firstid); ajStrDel(&savetype); ajStrDel(&sofaname); level--; return ajFalse; } typecount = ajTableGetLength(pTypeTable); } if(recursion && ajStrPrefixCaseC(line, "#includeonly ")) { ajStrCutStart(&line, 13); ajStrExtractFirst(line, &rest, &TypeFName); ajStrDel(&rest); if(!featVocabReadTypes(TypeFName, pTypeTable, pTagsTable, ajFalse)) { ajStrDel(&line); ajStrDel(&TypeFName); ajStrDel(&firstid); ajStrDel(&savetype); ajStrDel(&sofaname); level--; return ajFalse; } typecount = ajTableGetLength(pTypeTable); } } else if(ajStrGetLen(line)) { if(featFeatType(line, &type, &intids, &tag, &req)) { if(ajStrGetLen(type)) /* new feature type */ { if(!ismodtype && ajStrGetLen(savetype)) /* save previous type and tags */ { #if FEATDEBUG ajDebug("%S %Ld saved '%S' as '%S'\n", fname, typecount, savetype, typtagstr); ajDebug("+type %S='%S'\n", savetype, typtagstr); #endif /* tablestr = ajTablePut(pTypeTable, savetype, typtagstr); */ tablestr = ajTableFetchS(pTypeTable, savetype); if(tablestr) { if(recursion) { ajErr("%S duplicate type %S='%S' " "already defined as '%S'", fname, savetype, typtagstr, tablestr); } else { #if FEATDEBUG ajDebug("found savetype '%S' with value '%S'\n", savetype, tablestr); #endif } ajStrDel(&typtagstr); ajStrDel(&savetype); } else { ajTablePut(pTypeTable, savetype, typtagstr); typtagstr = NULL; savetype = NULL; } } if(ajStrMatchC(req, "=")) { ismodtype = ajTrue; ajStrAssignS(&modtype, type); if(ajStrMatchC(type, ".")) /* replace default */ ajStrAssignClear(&type); ajStrAssignS(&localname, type); ajStrAssignS(&sofaname, intids); tablestr = ajTableFetchS(pTypeTable, localname); if(tablestr) { #if FEATDEBUG ajDebug("%S duplicate alias type " "%S='%S' already defined as '%S'\n", fname, localname, sofaname, tablestr); #endif ajStrDel(&localname); } else { ajTablePut(pTypeTable, localname, sofaname); localname = NULL; sofaname = NULL; } Ptyptagstr = ajTablestrFetchmod(pTypeTable, intids); if(!Ptyptagstr) ajWarn("%S (level %u) undefined internal ID '%S'", fname, level, intids); ipos = ajStrFindAnyK(*Ptyptagstr, ';'); if(ipos >= 0) ajStrCutStart(Ptyptagstr, (size_t) ipos); else ajStrInsertK(Ptyptagstr, 0, ';'); ajStrInsertS(Ptyptagstr, 0, type); ajStrDel(&savetype); savetype = type; type = NULL; typtagstr = *Ptyptagstr; /* taginternal = ajTrue; */ } else { typecount++; filetypecount++; if(typecount == 1) /* type saved as "" default */ { defname = NULL; defid = NULL; ajStrAssignClear(&defname); ajStrAssignS(&defid, type); newdefid = ajTrue; tablestr = ajTableFetchS(pTypeTable, defname); if(tablestr) { ajErr("%S duplicate type %S='%S' " "already defined as '%S'", fname, defname, typtagstr, tablestr); ajStrDel(&typtagstr); ajStrDel(&defname); } else { ajTablePut(pTypeTable, defname, defid); defname = NULL; } } ismodtype = ajFalse; /* ** set up new feature type and type-tag strings ** ready to save the details */ typtagstr = ajStrNewResC(";", 256); /* taginternal = ajFalse; */ if(ajStrGetLen(intids)) { sofaid = ajStrParseWhite(intids); ajStrAssignS(&firstid, sofaid); storetype = type; while(sofaid) { if(newdefid) { ajStrAssignS(&defid, sofaid); newdefid = ajFalse; defid = NULL; } #if FEATDEBUG ajDebug("+type %B storetype:'%S' " "firstid:'%S'\n", ajStrMatchCaseS(storetype, firstid), storetype, firstid); #endif if(!ajStrMatchCaseS(storetype, firstid)) { ajStrAssignS(&localname, storetype); ajStrAssignS(&sofaname, firstid); #if FEATDEBUG ajDebug("%S sofaid " "'%S' = '%S'\n", fname,localname, sofaname); ajDebug("+type (alias) %S='%S'\n", localname, sofaname); #endif tablestr = ajTableFetchS(pTypeTable, localname); if(tablestr) { if(recursion) { ajErr("%S duplicate localname type " "%S='%S' already defined " "as '%S'", fname, localname, sofaname, tablestr); } ajStrDel(&localname); ajStrDel(&sofaname); } else { ajTablePut(pTypeTable, localname, sofaname); localname = NULL; sofaname = NULL; storetype = sofaid; } } sofaid = ajStrParseWhite(NULL); } ajStrInsertS(&typtagstr, 0, type); ajStrAssignClear(&type); ajStrDel(&savetype); savetype = firstid; firstid = NULL; sofaid = NULL; ajStrAssignClear(&intids); } else { ajStrDel(&savetype); savetype = type; type = NULL; ajStrAssignClear(&intids); } } } else /* tag name */ { if(!ajTableFetchS(pTagsTable, tag)) { if(ismodtype) ajFeatWarn("%S: tag %S (feature %S) " "not in Etags file", fname, tag, modtype); else ajFeatWarn("%S: tag %S (feature %S) " "not in Etags file", fname, tag, savetype); } modchar = ajStrGetCharFirst(req); reqchar = ajStrGetCharLast(req); if(modchar == '-') { ajFmtPrintS(&editstr, ";%S;", tag); ajStrExchangeSC(&typtagstr, editstr, ";"); if(!modtype) ajFeatWarn("%S: delete tag '%S' for %S not modified", fname, tag, modtype); } else { if(reqchar == 'M' || reqchar == 'm') ajFmtPrintAppS(&typtagstr, "*"); ajFmtPrintAppS(&typtagstr, "%S;", tag); if(modchar == '+') { if(!modtype) ajFeatWarn("%S: new tag '%S' for %S not modified", fname, tag, modtype); } } } } } } if(filetypecount > 0) /* save the last feature type */ { #if FEATDEBUG ajDebug("+type (final) %S='%S'\n", savetype, typtagstr); #endif tablestr = ajTableFetchS(pTypeTable, savetype); if(tablestr) { if(recursion) { ajErr("%S: duplicate type %S='%S' already defined as '%S'", fname, savetype, typtagstr, tablestr); } ajStrDel(&typtagstr); ajStrDel(&savetype); } else { ajTablePut(pTypeTable, savetype, typtagstr); typtagstr = NULL; savetype = NULL; } } ajFileClose(&TypeFile); ajDebug("Total types...: %Ld\n", typecount); ajStrDel(&line); ajStrDel(&token); ajStrDel(&intids); /* ajTableTrace(pTypeTable); ajTablestrPrint(pTypeTable); */ ajStrDelStatic(&featTmpStr); ajStrDel(&TypeFName); ajStrDel(&req); ajStrDel(&tag); ajStrDel(&type); ajStrDel(&intids); ajStrDel(&modtype); ajStrDel(&localname); ajStrDel(&sofaname); ajStrDel(&firstid); ajStrDel(&savetype); level--; return ajTrue; } ID ajFeatVocabGetTags TY public MO ajfeat LB core XX DE Returns tags table for a named feature format XX PN [1] PA r name const char* PD Feature format name PX RT const AjPTable RD Tags table on success RD NULL on failure RX // const AjPTable ajFeatVocabGetTags(const char* name) { ajuint i; if(!FeatInitDone) featInit(); for(i=0; featVocab[i].Name; i++) { if(!strcmp(name, featVocab[i].Truename) || !strcmp(name, featVocab[i].Name)) { if(!*(featVocab[i].Tagstable)) if(!featVocabInit(i)) return NULL; return *(featVocab[i].Tagstable); } } return NULL; } ID ajFeatVocabGetTagsNuc TY public MO ajfeat LB core XX DE Returns tags table for a named nucleotide feature format XX PN [1] PA r name const char* PD Feature format name PX RT const AjPTable RD Tags table on success RD NULL on failure RX // const AjPTable ajFeatVocabGetTagsNuc(const char* name) { ajuint i; if(!FeatInitDone) featInit(); for(i=0; featVocab[i].Name; i++) { if(!featVocab[i].Nucleotide) continue; if(!strcmp(name, featVocab[i].Truename) || !strcmp(name, featVocab[i].Name)) { if(!*(featVocab[i].Tagstable)) if(!featVocabInit(i)) return NULL; return *(featVocab[i].Tagstable); } } return NULL; } ID ajFeatVocabGetTagsProt TY public MO ajfeat LB core XX DE Returns tags table for a named protein feature format XX PN [1] PA r name const char* PD Feature format name PX RT const AjPTable RD Tags table on success RD NULL on failure RX // const AjPTable ajFeatVocabGetTagsProt(const char* name) { ajuint i; if(!FeatInitDone) featInit(); for(i=0; featVocab[i].Name; i++) { if(!featVocab[i].Protein) continue; if(!strcmp(name, featVocab[i].Truename) || !strcmp(name, featVocab[i].Name)) { if(!*(featVocab[i].Tagstable)) if(!featVocabInit(i)) return NULL; return *(featVocab[i].Tagstable); } } return NULL; } ID ajFeatVocabGetTypes TY public MO ajfeat LB core XX DE Returns types table for a named feature format XX PN [1] PA r name const char* PD Feature format name PX RT const AjPTable RD Type table on success RD NULL on failure RX // const AjPTable ajFeatVocabGetTypes(const char* name) { ajuint i; if(!FeatInitDone) featInit(); for(i=0; featVocab[i].Name; i++) { if(!strcmp(name, featVocab[i].Truename) || !strcmp(name, featVocab[i].Name)) { if(!*(featVocab[i].Typetable)) if(!featVocabInit(i)) return NULL; return *(featVocab[i].Typetable); } } return NULL; } ID ajFeatVocabGetTypesNuc TY public MO ajfeat LB core XX DE Returns types table for a named nucleotide feature format XX PN [1] PA r name const char* PD Feature format name PX RT const AjPTable RD Type table on success RD NULL on failure RX // const AjPTable ajFeatVocabGetTypesNuc(const char* name) { ajuint i; if(!FeatInitDone) featInit(); for(i=0; featVocab[i].Name; i++) { if(!featVocab[i].Nucleotide) continue; if(!strcmp(name, featVocab[i].Truename) || !strcmp(name, featVocab[i].Name)) { if(!*(featVocab[i].Typetable)) if(!featVocabInit(i)) return NULL; return *(featVocab[i].Typetable); } } return NULL; } ID ajFeatVocabGetTypesProt TY public MO ajfeat LB core XX DE Returns types table for a named protein feature format XX PN [1] PA r name const char* PD Feature format name PX RT const AjPTable RD Type table on success RD NULL on failure RX // const AjPTable ajFeatVocabGetTypesProt(const char* name) { ajuint i; if(!FeatInitDone) featInit(); for(i=0; featVocab[i].Name; i++) { if(!featVocab[i].Protein) continue; if(!strcmp(name, featVocab[i].Truename) || !strcmp(name, featVocab[i].Name)) { if(!*(featVocab[i].Typetable)) if(!featVocabInit(i)) return NULL; return *(featVocab[i].Typetable); } } return NULL; } ID ajFeatVocabInit TY public MO ajfeat LB core XX DE Initialises feature table internals for a named feature format XX PN [1] PA r name const char* PD Feature format name PX RT const AjPTable RD Type table on success RD NULL on failure RX // const AjPTable ajFeatVocabInit(const char* name) { ajuint i; if(!FeatInitDone) featInit(); for(i=0; featVocab[i].Name; i++) { if(!strcmp(name, featVocab[i].Truename) || !strcmp(name, featVocab[i].Name)) { if(*(featVocab[i].Typetable)) return *(featVocab[i].Typetable); else return featVocabInit(i); } } return NULL; } ID ajFeatVocabInitNuc TY public MO ajfeat LB core XX DE Initialises feature table internals for a named feature format XX PN [1] PA r name const char* PD Feature format name PX RT const AjPTable RD Type table on success RD NULL on failure RX // const AjPTable ajFeatVocabInitNuc(const char* name) { ajuint i; if(!FeatInitDone) featInit(); for(i=0; featVocab[i].Name; i++) { if(!featVocab[i].Nucleotide) continue; if(!strcmp(name, featVocab[i].Truename) || !strcmp(name, featVocab[i].Name)) { if(*(featVocab[i].Typetable)) return *(featVocab[i].Typetable); else return featVocabInit(i); } } return NULL; } ID ajFeatVocabInitProt TY public MO ajfeat LB core XX DE Initialises feature table internals for a named feature format XX PN [1] PA r name const char* PD Feature format name PX RT const AjPTable RD Type table on success RD NULL on failure RX // const AjPTable ajFeatVocabInitProt(const char* name) { ajuint i; if(!FeatInitDone) featInit(); for(i=0; featVocab[i].Name; i++) { if(!featVocab[i].Protein) continue; if(!strcmp(name, featVocab[i].Truename) || !strcmp(name, featVocab[i].Name)) { if(*(featVocab[i].Typetable)) return *(featVocab[i].Typetable); else return featVocabInit(i); } } return NULL; } ID featVocabInit TY static MO ajfeat LB core XX DE Initialises feature table internals for a named feature format DE and returns the types table XX PN [1] PA r ivocab ajuint PD Vocabulary index PX RT const AjPTable RD Table if found RX // static const AjPTable featVocabInit(ajuint ivocab) { const char* name = featVocab[ivocab].Truename; AjPTable *Ptypetable = featVocab[ivocab].Typetable; AjPTable *Ptagstable = featVocab[ivocab].Tagstable; if(!FeatInitDone) featInit(); if(!*Ptypetable) { *Ptypetable = ajTablestrNewCase(200); *Ptagstable = ajTablestrNewCase(200); featVocabRead(name, *Ptypetable, *Ptagstable); if(!*Ptagstable) return NULL; return *Ptypetable; } return *Ptypetable; } ID ajFeatSetDescApp TY public MO ajfeat LB core XX DE Sets the description for a feature XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r desc const AjPStr PD Feature description (simple text) PX RT void RD RX // void ajFeatSetDescApp(AjPFeature thys, const AjPStr desc) { AjPTagval tv = NULL; if(!featTagNote) ajStrAssignC(&featTagNote, "note"); tv = featTagval(thys, featTagNote); if(tv) { ajTagvalAppendC(tv, ", "); ajTagvalAppendS(tv, desc); } else ajFeatTagSet(thys, featTagNote, desc); return; } ID ajFeatSetDesc TY public MO ajfeat LB core XX DE Sets the description for a feature XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r desc const AjPStr PD Feature description (simple text) PX RT void RD RX // void ajFeatSetDesc(AjPFeature thys, const AjPStr desc) { ajFeatTagSetC(thys, "note", desc); return; } ID ajFeatSetSource TY public MO ajfeat LB core XX DE Sets the source for a feature XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r source const AjPStr PD Feature source PX RT void RD RX // void ajFeatSetSource(AjPFeature thys, const AjPStr source) { ajStrAssignS(&thys->Source, source); return; } ID ajFeatSetScore TY public MO ajfeat LB core XX DE Sets the score for a feature XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r score float PD Score value PX RT void RD RX // void ajFeatSetScore(AjPFeature thys, float score) { thys->Score = score; return; } ID ajFeatSetStrand TY public MO ajfeat LB core XX DE Sets the strand for a feature XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r rev AjBool PD True if reverse strand PX RT void RD RX // void ajFeatSetStrand(AjPFeature thys, AjBool rev) { if(rev) thys->Strand = '-'; else thys->Strand = '+'; return; } ID ajFeatTagSetC TY public MO ajfeat LB core XX DE Sets a feature tag value XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r tag const char* PD Feature tag PX PN [3] PA r value const AjPStr PD Feature tag value PX RT AjBool RD ajTrue is value was valid RD ajFalse if it was "corrected" RX // AjBool ajFeatTagSetC(AjPFeature thys, const char* tag, const AjPStr value) { AjPStr tmptag = NULL; AjBool ret = ajFalse; ajStrAssignC(&tmptag, tag); ret = ajFeatTagSet(thys, tmptag, value); ajStrDel(&tmptag); return ret; } ID ajFeatTagSet TY public MO ajfeat LB core XX DE Sets a feature tag value XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r tag const AjPStr PD Feature tag PX PN [3] PA r value const AjPStr PD Feature tag value PX RT AjBool RD ajTrue is value was valid RD ajFalse if it was "corrected" RX // AjBool ajFeatTagSet(AjPFeature thys, const AjPStr tag, const AjPStr value) { AjBool ret = ajTrue; AjPTagval tv = NULL; AjBool knowntag = ajTrue; const AjPStr tmptag = NULL; /* this comes from AjPTable */ /* so please, please don't delete */ const char* cp; ajDebug("ajFeatTagSet '%S' '%S' type: '%S' Prot: %B\n", tag, value, thys->Type, thys->Protein); featInit(); if(thys->Protein) { tmptag = ajFeattagGetNameS(tag, FeatTagsTableProtein, &knowntag); ajFeattagFormat(tmptag, FeatTagsTableProtein, &featFmtTmp); } else { tmptag = ajFeattagGetNameS(tag, FeatTagsTableDna, &knowntag); ajFeattagFormat(tmptag, FeatTagsTableDna, &featFmtTmp); } ajStrAssignS(&featValTmp, value); ajStrAssignS(&featTagTmp, tmptag); cp = ajStrGetPtr(featFmtTmp); switch(CASE2(cp[0], cp[1])) { case CASE2('L','I') : /* limited */ /* ajDebug("case limited\n"); */ break; case CASE2('Q', 'L') : /* limited, escape quotes */ /* ajDebug("case qlimited\n"); */ break; case CASE2('Q', 'S') : /* special regexp, quoted */ /* ajDebug("case qspecial\n"); */ if(!ajFeattagSpecial(tmptag, &featValTmp)) { ret = ajFalse; featTagSetDefault(thys, tmptag, value, &featTagTmp, &featValTmp); } break; case CASE2('S','P') : /* special regexp */ /* ajDebug("case special\n"); */ if(!ajFeattagSpecial(tmptag, &featValTmp)) { ret = ajFalse; featTagSetDefault(thys, tmptag, value, &featTagTmp, &featValTmp); } break; case CASE2('T','E') : /* no space, no quotes, wrap at margin */ /* ajDebug("case text\n"); */ break; case CASE2('V','O') : /* no value, so an error here */ /*ajDebug("case void\n");*/ break; case CASE2('Q','T') : /* escape quotes, wrap at space */ /* ajDebug("case qtext\n"); */ break; case CASE2('Q','W') : /* escape quotes, remove space */ /* ajDebug("case qword\n"); */ break; default: ajFeatWarn("Unknown internal feature tag type '%S' for '%S'", featFmtTmp, tmptag); } tv = featTagval(thys, featTagTmp); if(tv) /* replace current value */ { ajTagvalReplaceS(tv, featValTmp); return ret; } /* new tag-value */ tv = featTagvalNew(thys, featTagTmp, featValTmp); ajListPushAppend(thys->Tags, tv); /* ajDebug("...new tag-value\n"); */ return ret; } ID ajFeattagSpecialGff2 TY public MO ajfeat LB core XX DE Special processing for known GFF tags DE DE This function will be very similar to featTagSpecial, with scope DE for future GFF-specific extensions XX PN [1] PA r tag const AjPStr PD original tag name PX PN [2] PA u pval AjPStr* PD tag value PX RT AjBool RD ajTrue on success RX // AjBool ajFeattagSpecialGff2(const AjPStr tag, AjPStr* pval) { /*ajDebug("ajFeattagSpecialGff2 '%S' '%S'\n", tag, *pval);*/ if(ajStrMatchC(tag, "anticodon")) return featTagSpecialAllAnticodon(*pval); else if(ajStrMatchC(tag, "bio_material") || ajStrMatchC(tag, "culture_collection") || ajStrMatchC(tag, "specimen_voucher")) return featTagSpecialAllBiomaterial(*pval); else if(ajStrMatchC(tag, "citation")) return featTagSpecialAllCitation(*pval); else if(ajStrMatchC(tag, "codon")) return featTagSpecialAllCodon(pval); else if(ajStrMatchC(tag, "collection_date")) return featTagSpecialAllCollectiondate(*pval); else if(ajStrMatchC(tag, "compare")) return featTagSpecialAllCompare(*pval); else if(ajStrMatchC(tag, "cons_splice")) return featTagSpecialAllConssplice(pval); else if(ajStrMatchC(tag, "db_xref")) return featTagSpecialAllDbxref(*pval); else if(ajStrMatchC(tag, "estimated_length")) return featTagSpecialAllEstimatedlength(pval); else if(ajStrMatchC(tag, "inference")) return featTagSpecialAllInference(*pval); else if(ajStrMatchC(tag, "lat_lon")) return featTagSpecialAllLatlon(*pval); else if(ajStrMatchC(tag, "mobile_element")) return featTagSpecialAllMobile(*pval); else if(ajStrMatchC(tag, "PCR_primers")) return featTagSpecialAllPcrprimers(*pval); else if(ajStrMatchC(tag, "protein_id")) return featTagSpecialAllProteinid(*pval); else if(ajStrMatchC(tag, "replace")) return featTagSpecialAllReplace(pval); else if(ajStrMatchC(tag, "rpt_unit")) return featTagSpecialAllRptunit(*pval); else if(ajStrMatchC(tag, "rpt_unit_range") || ajStrMatchC(tag, "tag_peptide")) return featTagSpecialAllRange(*pval); else if(ajStrMatchC(tag, "rpt_unit_seq")) return featTagSpecialAllRptunitseq(pval); else if(ajStrMatchC(tag, "transl_except")) return featTagSpecialAllTranslexcept(*pval); else if(ajStrMatchC(tag, "translation")) return featTagSpecialAllTranslation(pval); /*ajDebug("Unrecognised special GFF feature tag '%S'\n", tag);*/ ajFeatWarn("Unrecognised special GFF feature tag '%S'", tag); return ajFalse; } ID featTagGff3PredefinedTag TY static MO ajfeat LB core XX DE Checks whether the specified tag is one of GFF3 tags DE that have predefined meanings XX PN [1] PA r tag const AjPStr PD tag name PX RT AjBool RD ajTrue on success RX // static AjBool featTagGff3PredefinedTag(const AjPStr tag) { const char* tags[] = {"ID", "Name", "Alias", "Parent", "Target", "Gap", "Derives_from", "Note", "Dbxref", "Ontology_term", "Is_circular", NULL}; int i=0; while(tags[i]) if(ajStrMatchC(tag, tags[i++])) return ajTrue; return ajFalse; } ID ajFeattagSpecialGff3 TY public MO ajfeat LB core XX DE Special processing for known GFF3 tags DE DE This function will be very similar to featTagSpecial, with scope DE for future GFF3-specific extensions XX PN [1] PA r tag const AjPStr PD original tag name PX PN [2] PA u pval AjPStr* PD tag value PX RT AjBool RD ajTrue on success RX // AjBool ajFeattagSpecialGff3(const AjPStr tag, AjPStr* pval) { /*ajDebug("featTagGff3Special '%S' '%S'\n", tag, *pval);*/ if(ajStrMatchC(tag, "anticodon")) return featTagSpecialAllAnticodon(*pval); else if(ajStrMatchC(tag, "bio_material") || ajStrMatchC(tag, "culture_collection") || ajStrMatchC(tag, "specimen_voucher")) return featTagSpecialAllBiomaterial(*pval); else if(ajStrMatchC(tag, "citation")) return featTagSpecialAllCitation(*pval); else if(ajStrMatchC(tag, "codon")) return featTagSpecialAllCodon(pval); else if(ajStrMatchC(tag, "collection_date")) return featTagSpecialAllCollectiondate(*pval); else if(ajStrMatchC(tag, "compare")) return featTagSpecialAllCompare(*pval); else if(ajStrMatchC(tag, "cons_splice")) return featTagSpecialAllConssplice(pval); else if(ajStrMatchC(tag, "db_xref")) return featTagSpecialAllDbxref(*pval); else if(ajStrMatchC(tag, "estimated_length")) return featTagSpecialAllEstimatedlength(pval); else if(ajStrMatchC(tag, "inference")) return featTagSpecialAllInference(*pval); else if(ajStrMatchC(tag, "lat_lon")) return featTagSpecialAllLatlon(*pval); else if(ajStrMatchC(tag, "mobile_element")) return featTagSpecialAllMobile(*pval); else if(ajStrMatchC(tag, "ncRNA_class")) return featTagSpecialAllNcrnaclass(*pval); else if(ajStrMatchC(tag, "PCR_primers")) return featTagSpecialAllPcrprimers(*pval); else if(ajStrMatchC(tag, "protein_id")) return featTagSpecialAllProteinid(*pval); else if(ajStrMatchC(tag, "replace")) return featTagSpecialAllReplace(pval); else if(ajStrMatchC(tag, "rpt_unit")) return featTagSpecialAllRptunit(*pval); else if(ajStrMatchC(tag, "rpt_unit_range") || ajStrMatchC(tag, "tag_peptide")) return featTagSpecialAllRange(*pval); else if(ajStrMatchC(tag, "rpt_unit_seq")) return featTagSpecialAllRptunitseq(pval); else if(ajStrMatchC(tag, "transl_except")) return featTagSpecialAllTranslexcept(*pval); else if(ajStrMatchC(tag, "translation")) return featTagSpecialAllTranslation(pval); /*ajDebug("Unrecognised special GFF feature tag '%S'\n", tag);*/ ajFeatWarn("Unrecognised special GFF feature tag '%S'", tag); return ajFalse; } ID ajFeatTagAddCC TY public MO ajfeat LB core XX DE Sets a feature tag value, creating a new feature tag even if one DE already exists. XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r tag const char* PD Feature tag PX PN [3] PA r value const char* PD Feature tag value PX RT AjBool RD ajTrue if value was valid RD ajFalse if it was bad and was "corrected" RX // AjBool ajFeatTagAddCC(AjPFeature thys, const char* tag, const char* value) { AjBool ret; AjPStr tagstr = NULL; AjPStr valstr = NULL; ajStrAssignC(&tagstr, tag); ajStrAssignC(&valstr, value); ret = ajFeatTagAddSS(thys, tagstr, valstr); ajStrDel(&tagstr); ajStrDel(&valstr); return ret; } ID ajFeatTagAddCS TY public MO ajfeat LB core XX DE Sets a feature tag value, creating a new feature tag even if one DE already exists. XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r tag const char* PD Feature tag PX PN [3] PA r value const AjPStr PD Feature tag value PX RT AjBool RD ajTrue if value was valid RD ajFalse if it was bad and was "corrected" RX // AjBool ajFeatTagAddCS(AjPFeature thys, const char* tag, const AjPStr value) { AjBool ret; static AjPStr tagstr = NULL; ajStrAssignC(&tagstr, tag); ret = ajFeatTagAddSS(thys, tagstr, value); ajStrDel(&tagstr); return ret; } ID ajFeatTagAddSS TY public MO ajfeat LB core XX DE Sets a feature tag value, creating a new feature tag even if one DE already exists. XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r tag const AjPStr PD Feature tag PX PN [3] PA r value const AjPStr PD Feature tag value PX RT AjBool RD ajTrue if value was valid RD ajFalse if it was bad and was "corrected" RX // AjBool ajFeatTagAddSS(AjPFeature thys, const AjPStr tag, const AjPStr value) { AjBool ret = ajTrue; AjPTagval tv = NULL; const AjPStr tmptag = NULL; /* this comes from AjPTable */ /* so please, please don't delete */ AjBool knowntag = ajTrue; const char* cp; #if FEATDEBUG ajDebug("ajFeatTagAddSS '%S' '%S' type: '%S' Prot: %B\n", tag, value, thys->Type, thys->Protein); #endif featInit(); if(thys->Protein) { tmptag = ajFeattagGetNameS(tag, FeatTagsTableProtein, &knowntag); ajFeattagFormat(tmptag, FeatTagsTableProtein, &featFmtTmp); } else { tmptag = ajFeattagGetNameS(tag, FeatTagsTableDna, &knowntag); ajFeattagFormat(tmptag, FeatTagsTableDna, &featFmtTmp); } #if FEATDEBUG ajDebug("tag: '%S' format: '%S'\n", tmptag, featFmtTmp); #endif ajStrAssignS(&featValTmp2, value); ajStrAssignS(&featTagTmp2, tmptag); cp = ajStrGetPtr(featFmtTmp); switch(CASE2(cp[0], cp[1])) { case CASE2('L','I') : /* limited */ /* ajDebug("case limited\n"); */ break; case CASE2('Q', 'L') : /* limited, escape quotes */ /* ajDebug("case qlimited\n"); */ break; case CASE2('Q', 'S') : /* special regexp, quoted */ /* ajDebug("case qspecial\n"); */ if(!ajFeattagSpecial(tmptag, &featValTmp2)) { ret = ajFalse; featTagSetDefault(thys, tmptag, value, &featTagTmp2, &featValTmp2); } break; case CASE2('S','P') : /* special regexp */ /* ajDebug("case special\n");*/ if(!ajFeattagSpecial(tmptag, &featValTmp2)) { ret = ajFalse; featTagSetDefault(thys, tmptag, value, &featTagTmp2, &featValTmp2); } break; case CASE2('T','E') : /* no space, no quotes, wrap at margin */ /* ajDebug("case text\n"); */ break; case CASE2('V','O') : /* no value, so an error here */ /* ajDebug("case void\n"); */ break; case CASE2('Q','T') : /* escape quotes, wrap at space */ /* ajDebug("case qtext\n"); */ if(!knowntag && tag) ajFmtPrintS(&featValTmp2, "*%S: %S", tag, value); /*ajDebug("ajFeatTagAdd qtext knowntag %B tag '%S' Tmp '%S' '%S'\n", knowntag, tag, featTagTmp2, featValTmp2);*/ break; case CASE2('Q','W') : /* escape quotes, remove space */ /* ajDebug("case qword\n"); */ break; default: ajFeatWarn("Unknown internal feature tag type '%S' for '%S'", featFmtTmp, tmptag); } tv = featTagvalNew(thys, featTagTmp2, featValTmp2); ajListPushAppend(thys->Tags, tv); /* ajDebug("...new tag-value\n"); */ return ret; } ID ajFeatTagAddTag TY public MO ajfeat LB core XX DE Sets a feature tag value, creating a new feature tag even if one DE already exists. XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r tagval const AjPTagval PD Tag value pair PX RT AjBool RD ajTrue if value was valid RD ajFalse if it was bad and was "corrected" RX // AjBool ajFeatTagAddTag(AjPFeature thys, const AjPTagval tagval) { return ajFeatTagAddSS(thys, MAJTAGVALGETTAG(tagval), MAJTAGVALGETVALUE(tagval)); } ID ajFeattagSpecial TY public MO ajfeat LB core XX DE Special processing for known internal (EMBL) tags XX PN [1] PA r tag const AjPStr PD original tag name PX PN [2] PA u pval AjPStr* PD parameter value PX RT AjBool RD ajTrue on success RX // AjBool ajFeattagSpecial(const AjPStr tag, AjPStr* pval) { /*ajDebug("featTagSpecial '%S'\n", tag);*/ if(ajStrMatchC(tag, "anticodon")) return featTagSpecialAllAnticodon(*pval); else if(ajStrMatchC(tag, "bio_material") || ajStrMatchC(tag, "culture_collection") || ajStrMatchC(tag, "specimen_voucher")) return featTagSpecialAllBiomaterial(*pval); else if(ajStrMatchC(tag, "citation")) return featTagSpecialAllCitation(*pval); else if(ajStrMatchC(tag, "codon")) return featTagSpecialAllCodon(pval); else if(ajStrMatchC(tag, "collection_date")) return featTagSpecialAllCollectiondate(*pval); else if(ajStrMatchC(tag, "compare")) return featTagSpecialAllCompare(*pval); else if(ajStrMatchC(tag, "cons_splice")) return featTagSpecialAllConssplice(pval); else if(ajStrMatchC(tag, "db_xref")) return featTagSpecialAllDbxref(*pval); else if(ajStrMatchC(tag, "estimated_length")) return featTagSpecialAllEstimatedlength(pval); else if(ajStrMatchC(tag, "inference")) return featTagSpecialAllInference(*pval); else if(ajStrMatchC(tag, "lat_lon")) return featTagSpecialAllLatlon(*pval); else if(ajStrMatchC(tag, "mobile_element")) return featTagSpecialAllMobile(*pval); else if(ajStrMatchC(tag, "ncRNA_class")) return featTagSpecialAllNcrnaclass(*pval); else if(ajStrMatchC(tag, "PCR_primers")) return featTagSpecialAllPcrprimers(*pval); else if(ajStrMatchC(tag, "protein_id")) return featTagSpecialAllProteinid(*pval); else if(ajStrMatchC(tag, "replace")) return featTagSpecialAllReplace(pval); else if(ajStrMatchC(tag, "rpt_unit")) return featTagSpecialAllRptunit(*pval); else if(ajStrMatchC(tag, "rpt_unit_range") || ajStrMatchC(tag, "tag_peptide")) return featTagSpecialAllRange(*pval); else if(ajStrMatchC(tag, "rpt_unit_seq")) return featTagSpecialAllRptunitseq(pval); else if(ajStrMatchC(tag, "transl_except")) return featTagSpecialAllTranslexcept(*pval); else if(ajStrMatchC(tag, "translation")) return featTagSpecialAllTranslation(pval); /*ajDebug("Unrecognised special EMBL feature tag '%S'\n", tag);*/ ajFeatWarn("Unrecognised special EMBL feature tag '%S'", tag); return ajFalse; } ID featTagSpecialAllBiomaterial TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /bio_material tag DE DE The format is a known type and optional :name XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllBiomaterial(const AjPStr val) { AjPStr inststr = NULL; AjPStr collstr = NULL; AjPStr idstr = NULL; AjBool ret = ajFalse; if(!featRegSpecialBiomaterial) featRegSpecialBiomaterial = ajRegCompC("^([^:]+)(:([^:]+))?(:(.*))?$"); if(ajRegExec(featRegSpecialBiomaterial, val)) { ajRegSubI(featRegSpecialBiomaterial, 1, &inststr); ajRegSubI(featRegSpecialBiomaterial, 3, &collstr); ajRegSubI(featRegSpecialBiomaterial, 5, &idstr); ret = ajTrue; } if(!ret) { ajFeatWarn("bad /bio_material value '%S'", val); } ajStrDel(&inststr); ajStrDel(&collstr); ajStrDel(&idstr); return ret; } ID featTagSpecialAllAnticodon TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /anticodon tag DE DE The format is (pos:base_range,aa:amino_acid) XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllAnticodon(const AjPStr val) { /* static AjPStr begstr = NULL; static AjPStr endstr = NULL; static AjPStr aastr = NULL; AjBool saveit = ajFalse; */ AjBool ret = ajFalse; if(!featRegSpecialAnticodon) featRegSpecialAnticodon = ajRegCompC("^[(]pos:([0-9]+)[.][.]([0-9]+),aa:([^)]+)[)]$"); if(ajRegExec(featRegSpecialAnticodon, val)) { ret = ajTrue; /* if(saveit) { ajRegSubI(featRegSpecialAnticodon, 1, &begstr); ajRegSubI(featRegSpecialAnticodon, 2, &endstr); ajRegSubI(featRegSpecialAnticodon, 3, &aastr); } */ } if(!ret) { ajFeatWarn("bad /anticodon value '%S'", val); } return ret; } ID featTagSpecialAllCitation TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /citation tag DE DE The format is [1] where the number is a citation in an EMBL entry. XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllCitation(const AjPStr val) { const char* cp = ajStrGetPtr(val); ajint i = 0; AjBool ret = ajFalse; if(*cp++ == '[') { while(*cp) { if(!isdigit((ajint)*cp)) break; i++; cp++; } if(*cp++ == ']') if(!*cp) ret = ajTrue; if(!i) ret = ajFalse; } if(!ret) { ajFeatWarn("bad /citation value '%S'", val); } return ret; } ID featTagSpecialAllCodon TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /codon tag DE DE The format is (seq:"ttt",aa:Leu) DE DE Corrects missing quotes XX PN [1] PA u pval AjPStr* PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllCodon(AjPStr* pval) { AjPStr seqstr = NULL; AjPStr aastr = NULL; AjBool saveit = ajFalse; AjBool ret = ajFalse; if(!featRegSpecialCodon) featRegSpecialCodon = ajRegCompC("^[(]seq:\"([acgt][acgt][acgt])\",aa:([^)]+)[)]$"); if(!featRegSpecialCodonBad) /* sometimes fails to quote sequence */ featRegSpecialCodonBad = ajRegCompC("^[(]seq:([acgt][acgt][acgt]),aa:([^)]+)[)]$"); if(ajRegExec(featRegSpecialCodon, *pval)) { ret = ajTrue; if(saveit) { ajRegSubI(featRegSpecialCodon, 1, &seqstr); ajRegSubI(featRegSpecialCodon, 2, &aastr); } } else if(ajRegExec(featRegSpecialCodonBad, *pval)) { ret = ajTrue; ajRegSubI(featRegSpecialCodonBad, 1, &seqstr); /* needed correction */ ajRegSubI(featRegSpecialCodonBad, 2, &aastr); ajFmtPrintS(pval, "(seq:\"%S\",aa:%S)",seqstr, aastr); ajFeatWarn("unquoted /codon value corrected to '%S'", *pval); } if(!ret) { ajFeatWarn("bad /codon value '%S'", *pval); } ajStrDel(&seqstr); ajStrDel(&aastr); return ret; } ID featTagSpecialAllCollectiondate TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /collection_date tag DE DE The format is YYYY Mmm-YYYY or DD-Mmm-YYYY XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllCollectiondate(const AjPStr val) { AjPStr daystr = NULL; AjPStr monstr = NULL; ajint day; ajint i; AjBool ret = ajFalse; AjBool ok; const char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; if(!featRegSpecialColdate) featRegSpecialColdate = ajRegCompC("^((\\d\\d)[-])?(([ADFJMNOS][A-Za-z][A-Za-z])[-])?([12]" "[7890]\\d\\d)$"); if(ajRegExec(featRegSpecialColdate, val)) { ret = ajTrue; ajRegSubI(featRegSpecialColdate, 2, &daystr); ajRegSubI(featRegSpecialColdate, 4, &monstr); if(ajStrGetLen(daystr)) { if(!ajStrGetLen(monstr)) ret = ajFalse; ajStrToInt(daystr, &day); if(day<1 || day > 31) ret = ajFalse; } if(ajStrGetLen(monstr)) { ok = ajFalse; for(i=0;i<12;i++) if(ajStrMatchCaseC(monstr, months[i])) { ok = ajTrue; break; } if(!ok) ret = ajFalse; } } if(!ret) ajFeatWarn("bad /collection_date value '%S'", val); ajStrDel(&daystr); ajStrDel(&monstr); return ret; } ID featTagSpecialAllConssplice TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /cons_splice tag DE DE The format is (5'site:YES,3'site:NO) where the booleans can DE be YES, NO, or ABSENT (intended for use where one site is NO and DE the other is missing) DE DE Corrects missing parts of the value and bad case. XX PN [1] PA u pval AjPStr* PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllConssplice(AjPStr* pval) { AjPStr begstr = NULL; AjPStr endstr = NULL; const char* cp; const char* cq; ajint i; AjBool islow = ajFalse; AjBool ret = ajFalse; if(!featRegSpecialConssplice) featRegSpecialConssplice = ajRegCompC("^[(]5'site:([A-Za-z]+),3'site:([A-Za-z]+)[)]$"); if(ajStrPrefixC(*pval, "(5'site:") && ajStrGetCharLast(*pval) == ')') { cp = ajStrGetPtr(*pval); cp += 8; cq = cp; i=0; while(*cp && (*cp != ',')) { if(islower((ajint)*cp++)) islow = ajTrue; i++; } ajStrAssignLenC(&begstr, cq, i); if(ajCharPrefixC(cp, ",3'site:")) { cp += 8; cq = cp; i=0; while(*cp && (*cp != ')')) { if(islower((ajint)*cp++)) islow = ajTrue; i++; } ajStrAssignLenC(&endstr, cq, i); if(*cp == ')' && !*(cp+1) && ajStrGetLen(begstr) && ajStrGetLen(endstr)) ret = ajTrue; switch(ajStrGetCharFirst(begstr)) { case 'Y': if(!ajStrMatchCaseC(begstr, "yes")) ret = ajFalse; break; case 'N': if(!ajStrMatchCaseC(begstr, "no")) ret = ajFalse; break; case 'A': if(!ajStrMatchCaseC(begstr, "absent")) ret = ajFalse; break; default: ret = ajFalse; } switch(ajStrGetCharFirst(endstr)) { case 'Y': if(!ajStrMatchCaseC(endstr, "yes")) ret = ajFalse; break; case 'N': if(!ajStrMatchCaseC(endstr, "no")) ret = ajFalse; break; case 'A': if(!ajStrMatchCaseC(endstr, "absent")) ret = ajFalse; break; default: ret = ajFalse; } } } if(!ret) { ajFeatWarn("bad /cons_splice value '%S'", *pval); ajDebug("bad /cons_splice value '%S' beg: '%S' end: '%S'", *pval, begstr, endstr); } if(islow) { ajFmtPrintS(pval, "(5'site:%S,3'site:%S)", begstr, endstr); ajFeatWarn("bad /cons_splice value corrected to '%S'", *pval); } ajStrDel(&begstr); ajStrDel(&endstr); return ret; } ID featTagSpecialAllInference TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /inference tag DE DE The format is TYPE:EVIDENCE_BASIS DE where TYPE is a list of defined values with optionally " (same species)" DE and EVIDENCE is dbname:accesion.version of algorithm:version XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllInference(const AjPStr val) { AjPStr typstr = NULL; ajint i; AjBool ret = ajFalse; AjBool ok; const char* types[] = { "non-experimental evidence, no additional details recorded", "similar to sequence", "similar to AA sequence", "similar to AA sequence sequence", /* found in EMBL 104 */ "similar to DNA sequence", "similar to RNA sequence", "similar to RNA sequence, mRNA", "similar to RNA sequence,mRNA", /* nonstandard, found in EMBL 104 */ "similar to RNA sequence, EST", "similar to RNA sequence, other RNA", "profile", "nucleotide motif", "protein motif", "ab initio prediction", "alignment", NULL }; if(!featRegSpecialInference) featRegSpecialInference = ajRegCompC("^([^:]+)(:([^:]+)?)*$"); if(ajRegExec(featRegSpecialInference, val)) { ret = ajTrue; ajRegSubI(featRegSpecialInference, 1, &typstr); ok = ajFalse; for(i=0; types[i]; i++) { if(ajStrPrefixC(typstr, types[i])) { if(ajStrMatchC(typstr, types[i])) { ok = ajTrue; break; } if(ajStrSuffixC(typstr, " (same species)")) { if(ajStrGetLen(typstr) == strlen(types[i]) + 15) { ok = ajTrue; break; } } else if(ajStrSuffixC(typstr, "(same species)")) { /* found in EMBL 104 without expected space */ if(ajStrGetLen(typstr) == strlen(types[i]) + 14) { ok = ajTrue; break; } } else if(ajStrSuffixC(typstr, " (same species) ")) { /* found in EMBL 104 with extra trailing space */ if(ajStrGetLen(typstr) == strlen(types[i]) + 16) { ok = ajTrue; break; } } } } if(!ok) ret = ajFalse; } if(!ret) ajFeatWarn("bad /inference value '%S' type: '%S'", val, typstr); ajStrDel(&typstr); return ret; } ID featTagSpecialAllLatlon TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /lat_lon tag XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllLatlon(const AjPStr val) { AjPStr labstr = NULL; AjBool saveit = ajFalse; AjBool ret = ajFalse; if(!featRegSpecialLatlon) featRegSpecialLatlon = ajRegCompC("^[0-9.]+ [NS] [0-9.]+ [EW]$"); if(ajRegExec(featRegSpecialLatlon, val)) { ret = ajTrue; if(saveit) ajRegSubI(featRegSpecialLatlon, 1, &labstr); } if(!ret) { ajFeatWarn("bad /lat_lon value '%S'", val); } return ret; } ID featTagSpecialAllPcrprimers TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /PCR_primers tag XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllPcrprimers(const AjPStr val) { AjBool ret = ajFalse; if(!featRegSpecialPrimer) featRegSpecialPrimer = ajRegCompC("^((fwd|rev)_name: [^,]+, )?" "(fwd|rev)_seq: [acgtubdhkmnrsvwxy<>i]+" "(, ((fwd|rev)_name: [^,]+, )?" "(fwd|rev)_seq: [acgtubdhkmnrsvwxy<>i]+)*"); if(ajRegExec(featRegSpecialPrimer, val)) { ret = ajTrue; } if(!ret) { ajFeatWarn("bad /PCR_primers value '%S'", val); } return ret; } ID featTagSpecialAllRptunit TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /rpt_unit tag DE DE The format is 123..789 DE Labels are also allowed which should be feature tags in the entry. DE Genbank (NCBI) appear to be putting the sequence consensus in. Usually DE this is a valid "label" - except of course that the label does not exist. DE One horror (July 2002) was: /rpt_unit=TCCTCACGTAG(T/C) DE others are /rpt_unit=gaa;taa /rpt_unit=(CA)2(TG)6(CA)2 DE /rpt_unit=attatatgata(n)6-7gttt(n)3gtagactagtt(n)3ttatgttt DE DE Version 6 of the feature table allows /rpt_unit="sequence" to include DE these nasties trim spaces and resolve ambiguities. Not to be used DE for "alu" etc. DE DE Location can be complement(nn..nn) XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllRptunit(const AjPStr val) { AjBool ret = ajFalse; /* AjBool saveit = ajFalse; AjPStr begstr = NULL; AjPStr endstr = NULL; AjPStr labstr = NULL; */ if(!featRegSpecialRptRange) /* base range */ featRegSpecialRptRange = ajRegCompC("^(([a-zA-Z0-9_]+:)?[0-9]+)[.][.]([0-9]+)$"); if(!featRegSpecialRptRangeLab) /* feature label */ featRegSpecialRptRangeLab = ajRegCompC("^(([a-zA-Z0-9_]+:)?[a-zA-Z0-9_]+)$"); if(!featRegSpecialRptRangeComp) /* complement(base range) */ featRegSpecialRptRangeComp = ajRegCompC("^(complement[(]([a-zA-Z0-9_]+:)?[0-9]+)" "[.][.]([0-9]+)[)]$"); if(!featRegSpecialRptunitSeq) featRegSpecialRptunitSeq = ajRegCompC("^([abcdghkmnrstuvwxyABCDGHKMNRSTUVWXY0-9/();-]+)$"); if(ajRegExec(featRegSpecialRptRange, val)) { ret = ajTrue; /* if(saveit) { ajRegSubI(featRegSpecialRptRange, 1, &begstr); ajRegSubI(featRegSpecialRptRange, 3, &endstr); } */ } else if(ajRegExec(featRegSpecialRptRangeLab, val)) { ret = ajTrue; /* if(saveit) ajRegSubI(featRegSpecialRptRangeLab, 1, &labstr); */ } else if(ajRegExec(featRegSpecialRptRangeComp, val)) { ret = ajTrue; /* if(saveit) { ajRegSubI(featRegSpecialRptRangeComp, 3, &begstr); ajRegSubI(featRegSpecialRptRangeComp, 1, &endstr); } */ } if(ajRegExec(featRegSpecialRptRangeComp, val)) { ret = ajTrue; /* if(saveit) { ajRegSubI(featRegSpecialRptRangeComp, 3, &begstr); ajRegSubI(featRegSpecialRptRangeComp, 1, &endstr); } */ } /* provided because EMBL/Genbank has things like this: /rpt_unit=TCCTCACGTAG(T/C) /rpt_unit=(A)n */ else if(ajRegExec(featRegSpecialRptunitSeq, val)) { ret = ajTrue; /* if(saveit) ajRegSubI(featRegSpecialRptunitSeq, 1, &labstr); */ } if(!ret) { ajFeatWarn("bad /rpt_unit value '%S'", val); } return ret; } ID featTagSpecialAllRange TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /rpt_unit_range tag DE DE The format is 123..789 DE Labels are also allowed which should be feature tags in the entry. DE Genbank (NCBI) appear to be putting the sequence consensus in. Usually DE this is a valid "label" - except of course that the label does not exist. DE One horror (July 2002) was: /rpt_unit=TCCTCACGTAG(T/C) DE others are /rpt_unit=gaa;taa /rpt_unit=(CA)2(TG)6(CA)2 DE /rpt_unit=attatatgata(n)6-7gttt(n)3gtagactagtt(n)3ttatgttt DE DE Version 6 of the feature table allows /rpt_unit="sequence" to include DE these nasties trim spaces and resolve ambiguities. Not to be used DE for "alu" etc. DE DE Location can be complement(nn..nn) XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllRange(const AjPStr val) { AjBool ret = ajFalse; /* AjBool saveit = ajFalse; AjPStr begstr = NULL; AjPStr endstr = NULL; AjPStr labstr = NULL; */ if(!featRegSpecialRptRange) /* base range */ featRegSpecialRptRange = ajRegCompC("^(([a-zA-Z0-9_]+:)?[0-9]+)[.][.]([0-9]+)$"); if(!featRegSpecialRptRangeLab) /* feature label */ featRegSpecialRptRangeLab = ajRegCompC("^(([a-zA-Z0-9_]+:)?[a-zA-Z0-9_]+)$"); if(!featRegSpecialRptRangeComp) /* complement(base range) */ featRegSpecialRptRangeComp = ajRegCompC("^(complement[(]([a-zA-Z0-9_]+:)?[0-9]+)" "[.][.]([0-9]+)[)]$"); if(ajRegExec(featRegSpecialRptRange, val)) { ret = ajTrue; /* if(saveit) { ajRegSubI(featRegSpecialRptRange, 1, &begstr); ajRegSubI(featRegSpecialRptRange, 3, &endstr); } */ } else if(ajRegExec(featRegSpecialRptRangeLab, val)) { ret = ajTrue; /* if(saveit) ajRegSubI(featRegSpecialRptRangeLab, 1, &labstr); */ } else if(ajRegExec(featRegSpecialRptRangeComp, val)) { ret = ajTrue; /* if(saveit) { ajRegSubI(featRegSpecialRptRangeComp, 3, &begstr); ajRegSubI(featRegSpecialRptRangeComp, 1, &endstr); } */ } if(!ret) { ajFeatWarn("bad /rpt_unit_tange value '%S'", val); } return ret; } ID featTagSpecialAllRptunitseq TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /rpt_unit_seq tag DE DE examples from GenBank include /rpt_unit_seq=TCCTCACGTAG(T/C) DE others are /rpt_unit_seq=gaa;taa /rpt_unit_seq=(CA)2(TG)6(CA)2 DE /rpt_unit_seq=attatatgata(n)6-7gttt(n)3gtagactagtt(n)3ttatgttt DE DE Corrects extra spaces from long sequence values XX PN [1] PA u pval AjPStr* PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllRptunitseq(AjPStr *pval) { AjBool ret = ajFalse; ajuint i; const char* specials[] = { "a-rich", /* found in EMBL 104 */ "at-rich", /* found in EMBL 104 */ "c-rich", /* found in EMBL 104 */ "ct-rich", /* found in EMBL 104 */ "CT-rich", /* found in EMBL 104 */ "g-rich", /* found in EMBL 104 */ "ga-rich", /* found in EMBL 104 */ "GA-rich", /* found in EMBL 104 */ "gc-rich", /* found in EMBL 104 */ "t-rich", /* found in EMBL 104 */ "CT_AT", /* found in EMBL 104 */ "TA_CA", /* found in EMBL 104 */ "AGAT_AGAC", /* found in EMBL 104 */ "GAA-repeat", /* found in EMBL 104 */ "GGA-repeat", /* found in EMBL 104 */ "tgtc (imperfect)", /* found in EMBL 104 */ "score", /* found in EMBL 104 */ "direct", /* found in EMBL 104 */ "dispersed", /* found in EMBL 104 */ "inverted", /* found in EMBL 104 */ "ca repeat", /* found in EMBL 104 */ "cac/caa/cag compound repeat", /* found in EMBL 104 */ "cata interrupted repeat", /* found in EMBL 104 */ "CA dinucleotide microsatellite", /* found in EMBL 104 */ "gaa/gac compound repeat", /* found in EMBL 104 */ "dodecanucleotide", /* found in EMBL 104 */ "heptamer", /* found in EMBL 104 */ "gttt..gt", /* found in EMBL 104 */ "telunit", /* found in EMBL 104 */ "af(12)", /* found in EMBL 104 */ "alu", /* found in EMBL 104 */ "Alu", /* found in EMBL 104 */ "AluJo", /* found in EMBL 104 */ "AluSg", /* found in EMBL 104 */ "AluY", /* found in EMBL 104 */ "hif2DR", /* found in EMBL 104 */ "B1element", /* found in EMBL 104 */ "L1MC/D", /* found in EMBL 104 */ "L1MC3", /* found in EMBL 104 */ "L1MD3", /* found in EMBL 104 */ "L1ME", /* found in EMBL 104 */ "RMER17B", /* found in EMBL 104 */ "RLTR11A", /* found in EMBL 104 */ "poly A", /* found in EMBL 104 */ "core-vntr1", /* found in EMBL 104 */ "core-vntr2", /* found in EMBL 104 */ NULL }; /* AjBool saveit = ajFalse; AjPStr labstr = NULL; */ if(!featRegSpecialRptunitSeq) featRegSpecialRptunitSeq = ajRegCompC("^([ abcdghkmnrstuvwxyABCDGHKMNRSTUVWXY0-9" "_/\\[\\]\\(\\):;.,+-]+)$"); if(!featRegSpecialRptunitSeqPos) featRegSpecialRptunitSeqPos = ajRegCompC("^((complement[\\(])?[<]?\\d+[.][.]?[>]?\\d+[\\)]?)$"); if(ajRegExec(featRegSpecialRptunitSeq, *pval)) { ajStrRemoveWhite(pval); /* remove wrapping spaces in long seq. */ ret = ajTrue; /* if(saveit) ajRegSubI(featRegSpecialRptunitSeq, 1, &labstr); */ } else if(ajRegExec(featRegSpecialRptunitSeqPos, *pval)) { ret = ajTrue; } else { for(i=0; specials[i]; i++) { if(ajStrMatchC(*pval, specials[i])) { ret = ajTrue; break; } } } if(!ret) { ajFeatWarn("bad /rpt_unit_seq value '%S'", *pval); } return ret; } ID featTagSpecialAllTranslexcept TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /transl_except tag DE DE The format is (pos:213..215,aa:Trp) DE DE FT 6.2 todo aa 3 letter codes, or Sec (U) or TERM or OTHER DE DE FT 6.2 todo pos can be 1 or 2 bases for polyA-completed stops (TERM) DE which should have a /note XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllTranslexcept(const AjPStr val) { AjBool saveit = ajFalse; AjBool ret = ajFalse; if(!featRegSpecialTrans) featRegSpecialTrans = ajRegCompC("^[(]pos:([0-9]+)[.][.]([0-9]+),aa:([^)]+)[)]$"); if(!featRegSpecialTransBad) /* start position only */ featRegSpecialTransBad = ajRegCompC("^[(]pos:([0-9]+),aa:([^)]+)[)]$"); if(!featRegSpecialTransComp) featRegSpecialTransComp = ajRegCompC("^[(]pos:complement\\(([0-9]+)[.][.]([0-9]+)\\)," "aa:([^)]+)[)]$"); if(!featRegSpecialTransBadComp) featRegSpecialTransBadComp = ajRegCompC("^[(]pos:complement\\(([0-9]+)\\)," "aa:([^)]+)[)]$"); if(ajRegExec(featRegSpecialTrans, val)) { ret = ajTrue; if(saveit) { ajRegSubI(featRegSpecialTrans, 1, &featTransBegStr); ajRegSubI(featRegSpecialTrans, 2, &featTransEndStr); ajRegSubI(featRegSpecialTrans, 3, &featTransAaStr); } } else if(ajRegExec(featRegSpecialTransComp, val)) { ret = ajTrue; if(saveit) { ajRegSubI(featRegSpecialTransComp, 1, &featTransBegStr); ajRegSubI(featRegSpecialTransComp, 2, &featTransEndStr); ajRegSubI(featRegSpecialTransComp, 3, &featTransAaStr); } } /* Can have single base (or 2 base) positions where trailing As are added as post-processing in some species */ else if(ajRegExec(featRegSpecialTransBad, val)) { ret = ajTrue; if(saveit) { ajRegSubI(featRegSpecialTransBad, 1, &featTransBegStr); ajRegSubI(featRegSpecialTransBad, 2, &featTransAaStr); } } else if(ajRegExec(featRegSpecialTransBadComp, val)) { ret = ajTrue; if(saveit) { ajRegSubI(featRegSpecialTransBadComp, 1, &featTransBegStr); ajRegSubI(featRegSpecialTransBadComp, 2, &featTransAaStr); } } if(!ret) { ajFeatWarn("bad /transl_except value '%S'", val); } return ret; } ID featTagSpecialAllDbxref TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /db_xref tag DE DE The format is <database>:<identifier> XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllDbxref(const AjPStr val) { const char* cp = NULL; AjBool ret = ajTrue; /* AjBool saveit = ajFalse; AjPStr dbstr = NULL; AjPStr idstr = NULL; */ cp = ajStrGetPtr(val); /* if(saveit) ajStrAssignResC(&dbstr, ajStrGetLen(val), ""); */ while(*cp && (*cp != ':')) { /* if(saveit) ajStrAppendK(&dbstr, *cp); */ cp++; } if(!*cp) ret = ajFalse; else { cp++; if(!*cp) ret = ajFalse; } /* if(saveit) ajStrAssignC(&idstr, cp); */ if(!ret) { ajFeatWarn("bad /db_xref value '%S'", val); } return ret; } ID featTagSpecialAllProteinid TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /protein_id tag DE DE The format is AAA12345.1 XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllProteinid(const AjPStr val) { const char* cp = ajStrGetPtr(val); /* const char* cq; */ ajint icp; ajint i = 0; ajint idot = 0; AjBool ret = ajFalse; /* AjBool saveit = ajFalse; AjPStr idstr = NULL; AjPStr preidstr = NULL; */ if(ajStrPrefixC(val, "ENSP")) /* older entries had ENSEMBL Protein IDs */ { cp += 4; while(*cp) { if(!isdigit((ajint)*cp++)) break; i++; } /* ENSP then all numbers */ if(!*cp && i > 5) ret = ajTrue; /* if(ret) ajFeatWarn("ENSEMBL /protein_id value '%S'", val);*/ /* if(saveit) { ajStrAssignS(&preidstr, val); ajStrAssignS(&idstr, val); } */ } else { cp = ajStrGetPtr(val); /* cq = cp; */ i=0; while(*cp) { i++; icp = *cp; if(i <= 3) { if(icp == '_') { if(i!=3) break; } else if(!isalpha(icp) || !isupper(icp)) break; } else if(*cp == '.') { if(idot) /* once only */ break; idot = i; if(idot < 9) /* at least ABC12345. */ break; } else { if(!isdigit(icp)) break; } cp++; } /* if(saveit) { ajStrAssignLenC(&preidstr, cq, idot-1); ajStrAssignLenC(&idstr, cq, i); } */ if(!*cp && idot && (i > idot)) ret = ajTrue; } if(!ret) { ajFeatWarn("bad /protein_id value '%S'", val); } return ret; } ID featTagSpecialAllReplace TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /replace tag DE DE The format is "<sequence>" where sequence is empty for a deletion DE DE Corrects extra spaces from long sequence values XX PN [1] PA u pval AjPStr* PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllReplace(AjPStr* pval) { AjBool ret = ajFalse; /* n is used in old_sequence */ /* and in misc_difference features */ if(!featRegTagReplace) featRegTagReplace = ajRegCompC("^([abcdghkmnrstuvwxyABCDGHKMNRSTUVWXY/-]*)$"); /* if(!featRegTagReplace) featRegTagReplace = ajRegCompC("^\"([acgt]*)\"$");*/ /* no need to add quotes here - we will add them if needed on output */ /* ajDebug("Before quote '%S' %c %c\n", *pval, ajStrGetCharFirst(*pval), ajStrGetCharLast(*pval)); ajStrFmtQuote(pval); ajDebug(" After quote '%S' %c %c\n", *pval, ajStrGetCharFirst(*pval), ajStrGetCharLast(*pval)); */ ajStrRemoveWhite(pval); /* remove wrapping spaces in long seq. */ if(ajRegExec(featRegTagReplace, *pval)) ret = ajTrue; /* substring 1 has the matched sequence */ if(!ret) { ajFeatWarn("bad /replace value '%S'", *pval); } return ret; } ID featTagSpecialAllTranslation TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /translation tag DE DE The format is valid amino acid codes, no white space DE DE Corrects extra spaces from long sequence values XX PN [1] PA u pval AjPStr* PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllTranslation(AjPStr* pval) { AjPStr seqstr = NULL; const char* cp; ajint icp; AjBool saveit = ajFalse; AjBool ret = ajFalse; ajStrRemoveWhite(pval); /* remove wrapping spaces in long seq. */ cp = ajStrGetPtr(*pval); if(saveit) ajStrAssignResC(&seqstr, ajStrGetLen(*pval), ""); while(*cp) { icp = *cp; if(!isalpha(icp)) break; if(!isupper(icp)) break; /* if(strchr("JO",icp)) break; */ if(saveit) ajStrAppendK(&seqstr, *cp); cp++; } if(!*cp) ret = ajTrue; if(!ret) { ajFeatWarn("bad /translation value '%S'", *pval); } return ret; } ID featTagSpecialAllEstimatedlength TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /estimated_length tag DE DE The format is a positive integer, or unknown (unquoted) XX PN [1] PA u pval AjPStr* PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllEstimatedlength(AjPStr* pval) { AjPStr numstr = NULL; AjBool saveit = ajFalse; AjBool ret = ajFalse; if(!featRegSpecialEstlen) featRegSpecialEstlen = ajRegCompC("^(unknown|[1-9][0-9]*)$"); if(ajRegExec(featRegSpecialEstlen, *pval)) { ret = ajTrue; if(saveit) ajRegSubI(featRegSpecialEstlen, 1, &numstr); } if(!ret) { ajFeatWarn("bad /estimated_length value '%S' corrected to 'unknown'", *pval); ajStrAssignC(pval, "unknown"); } ajStrDel(&numstr); return ret; } ID featTagSpecialAllCompare TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /compare tag DE DE The format is a sequence versin XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllCompare(const AjPStr val) { AjPStr numstr = NULL; AjBool saveit = ajFalse; AjBool ret = ajFalse; /* value is a sequence version accnumber.number */ if(!featRegSpecialCompare) featRegSpecialCompare = ajRegCompC("^([A-Z]+[0-9]+([.][1-9][0-9]*)?)$"); if(ajRegExec(featRegSpecialCompare, val)) { ret = ajTrue; if(saveit) ajRegSubI(featRegSpecialCompare, 1, &numstr); } if(!ret) { ajFeatWarn("bad /compare value '%S'", val); } ajStrDel(&numstr); return ret; } ID featTagSpecialAllMobile TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /mobile_element tag DE DE The format is a known type and optional :name XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllMobile(const AjPStr val) { AjPStr typstr = NULL; AjPStr namstr = NULL; AjBool saveit = ajFalse; AjBool ret = ajFalse; ajuint i; const char* elements[] = { "transposon", "retrotransposon", "integron", "insertion sequence", "non-LTR retrotransposon", "SINE", "MITE", "LINE", "other", NULL }; /* value is a type or type:name */ if(!featRegSpecialMobile) featRegSpecialMobile = ajRegCompC("^([^:]+)(:(.*))?$"); if(ajRegExec(featRegSpecialMobile, val)) { ajRegSubI(featRegSpecialMobile, 1, &typstr); for(i=0;elements[i];i++) if(ajStrMatchC(typstr, elements[i])) break; if(elements[i]) ret = ajTrue; if(saveit) { ajRegSubI(featRegSpecialMobile, 3, &namstr); } } if(!ret) { ajFeatWarn("bad /mobile_element value '%S'", val); } ajStrDel(&typstr); ajStrDel(&namstr); return ret; } ID featTagSpecialAllNcrnaclass TY static MO ajfeat LB core XX DE Tests a string as a valid internal (EMBL) feature /ncRNA_class tag DE DE The format is a known type and optional :name DE DE value is a term taken from the INSDC controlled vocabulary for ncRNA classes DE http://www.insdc.org/page.php?page=rna_vocab DE or http://www.ebi.ac.uk/embl/Documentation/ncRNA_class.html XX PN [1] PA r val const AjPStr PD parameter value PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagSpecialAllNcrnaclass(const AjPStr val) { AjPStr typstr = NULL; AjPStr namstr = NULL; AjBool ret = ajFalse; ajuint i; const char* classes[] = { "antisense_RNA", "autocatalytically_spliced_intron", "ribozyme", "hammerhead_ribozyme", "RNase_P_RNA", "RNase_MRP_RNA", "telomerase_RNA", "guide_RNA", "rasiRNA", "scRNA", "siRNA", "miRNA", "piRNA", "snoRNA", "snRNA", "SRP_RNA", "vault_RNA", "Y_RNA", "other", NULL }; for(i=0;classes[i];i++) if(ajStrMatchC(val, classes[i])) break; if(classes[i]) ret = ajTrue; if(!ret) { ajFeatWarn("bad /ncRNA_class value '%S'", val); } ajStrDel(&typstr); ajStrDel(&namstr); return ret; } ID featTagSetDefault TY static MO ajfeat LB core XX DE Sets a feature tag value, using the default feature tag XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r tag const AjPStr PD Feature tag PX PN [3] PA r value const AjPStr PD feature tag value PX PN [4] PA w Pdeftag AjPStr* PD Default tag PX PN [5] PA w Pdefval AjPStr* PD Default tag value as "*tag: value" PX RT void RD RX // static void featTagSetDefault(AjPFeature thys, const AjPStr tag, const AjPStr value, AjPStr* Pdeftag, AjPStr* Pdefval) { featInit(); if(thys->Protein) featTagSetDefaultDna(tag, value, Pdeftag, Pdefval); else featTagSetDefaultProt(tag, value, Pdeftag, Pdefval); return; } ID featTagSetDefaultDna TY static MO ajfeat LB core XX DE Sets a feature tag value, using the default DNA feature tag XX PN [1] PA r tag const AjPStr PD Feature tag PX PN [2] PA r value const AjPStr PD feature tag value PX PN [3] PA w Pdeftag AjPStr* PD Default tag PX PN [4] PA w Pdefval AjPStr* PD Default tag value as "*tag: value" PX RT void RD RX // static void featTagSetDefaultDna(const AjPStr tag, const AjPStr value, AjPStr* Pdeftag, AjPStr* Pdefval) { featInit(); ajStrAssignS(Pdeftag, (const AjPStr) ajTableFetchC(FeatTagsTableDna, "")); ajFmtPrintS(Pdefval, "*%S: %S", tag, value); return; } ID featTagSetDefaultProt TY static MO ajfeat LB core XX DE Sets a feature tag value, using the default protein feature tag XX PN [1] PA r tag const AjPStr PD Feature tag PX PN [2] PA r value const AjPStr PD feature tag value PX PN [3] PA w Pdeftag AjPStr* PD Default tag PX PN [4] PA w Pdefval AjPStr* PD Default tag value as "*tag: value" PX RT void RD RX // static void featTagSetDefaultProt(const AjPStr tag, const AjPStr value, AjPStr* Pdeftag, AjPStr* Pdefval) { featInit(); ajStrAssignS(Pdeftag, (const AjPStr) ajTableFetchC(FeatTagsTableProtein, "")); ajFmtPrintS(Pdefval, "*%S: %S", tag, value); return; } ID ajFeattableNew TY public MO ajfeat LB core XX DE Constructor for a new (generic) feature table. DE Does not define the feature table type. XX PN [1] PA r name const AjPStr PD Name for new feature table PD (or NULL for unnamed) PX RT AjPFeattable RD Pointer to a new (empty) feature table RX CA new CT AjPFeattable CD Constructor CX // AjPFeattable ajFeattableNew(const AjPStr name ) { AjPFeattable thys = NULL; /* Allocate the object... */ thys = featTableNewS(name) ; /*ajDebug("ajFeattableNew %x\n", thys);*/ return thys; } ID ajFeattableSetCircular TY public MO ajfeat LB core XX DE Sets a feature table to be circular XX PN [1] PA u thys AjPFeattable PD Feature table PX RT void RD RX // void ajFeattableSetCircular(AjPFeattable thys) { thys->Circular = ajTrue; return; } ID ajFeattableSetDefname TY public MO ajfeat LB core XX DE Provides a unique (for this program run) name for a feature table. XX PN [1] PA w thys AjPFeattable PD Feature table PX PN [2] PA r setname const AjPStr PD Name set by caller PX RT void RD RX // void ajFeattableSetDefname(AjPFeattable thys, const AjPStr setname) { if(ajStrGetLen(thys->Seqid)) { ajDebug("ajFeattableSetDefname already has a name '%S'\n", thys->Seqid); return; } if(ajStrGetLen(setname)) ajStrAssignS(&thys->Seqid, setname); ajDebug("ajFeattableSetDefname set to '%S'\n", setname); return; } ID ajFeattableSetLength TY public MO ajfeat LB core XX DE Sets the length of a feature table with the length of the source sequence. DE DE This is needed to reverse the table correctly XX PN [1] PA u thys AjPFeattable PD Feature table object PX PN [2] PA r len ajuint PD Length PX RT void RD RX // void ajFeattableSetLength(AjPFeattable thys, ajuint len) { thys->Len = len; return; } ID ajFeattableSetLinear TY public MO ajfeat LB core XX DE Sets a feature table to be linear XX PN [1] PA w thys AjPFeattable PD Feature table PX RT void RD RX // void ajFeattableSetLinear(AjPFeattable thys) { thys->Circular = ajFalse; return; } ID ajFeattableSetNuc TY public MO ajfeat LB core XX DE Sets the type of a feature table as nucleotide XX PN [1] PA u thys AjPFeattable PD Feature table object PX RT void RD RX // void ajFeattableSetNuc(AjPFeattable thys) { ajStrAssignEmptyC(&thys->Type, "N"); return; } ID ajFeattableSetProt TY public MO ajfeat LB core XX DE Sets the type of a feature table as Protein XX PN [1] PA u thys AjPFeattable PD Feature table object PX RT void RD RX // void ajFeattableSetProt(AjPFeattable thys) { ajStrAssignEmptyC(&thys->Type, "P"); return; } ID ajFeattableSetRange TY public MO ajfeat LB core XX DE Set the begin and end range for a feature table XX PN [1] PA u thys AjPFeattable PD Feature table object PX PN [2] PA r fbegin ajint PD Begin position PX PN [3] PA r fend ajint PD End position PX RT void RD RX // void ajFeattableSetRange(AjPFeattable thys, ajint fbegin, ajint fend) { thys->Start = ajFeattablePosI(thys, 1, fbegin); thys->End = ajFeattablePosII(thys->Len, thys->Start, fend); return; } ID ajFeattableReverse TY public MO ajfeat LB core XX DE Reverse the features in a feature table by iterating through and DE reversing all positions and strands. XX PN [1] PA u thys AjPFeattable PD Feature table object PX RT void RD RX // void ajFeattableReverse(AjPFeattable thys) { AjIList iter = NULL; AjPFeature gf = NULL; if(ajFeattableIsProt(thys)) return; iter = ajListIterNewread(thys->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); if((gf->Flags & AJFEATFLAG_REMOTEID) || (gf->Flags & AJFEATFLAG_LABEL)) continue; ajFeatReverse(gf, thys->Len) ; } ajListIterDel(&iter); return; } ID ajFeatReverse TY public MO ajfeat LB core XX DE Reverse one feature by reversing all positions and strand. XX PN [1] PA u thys AjPFeature PD Feature object PX PN [2] PA r ilen ajint PD Sequence length PX RT void RD RX // void ajFeatReverse(AjPFeature thys, ajint ilen) { AjIList iter = NULL; AjPFeature gf = NULL; ajint itmp; ajint saveflags; ajDebug("ajFeatReverse ilen %d %x '%c' %d..%d %d..%d\n", ilen, thys->Flags, thys->Strand, thys->Start, thys->End, thys->Start2, thys->End2); saveflags = thys->Flags; if(thys->Start != thys->End) { if(thys->Strand == '-') thys->Strand = '+'; else thys->Strand = '-'; } itmp = thys->Start; if(thys->End) thys->Start = 1 + ilen - thys->End; else thys->Start = 0; if(itmp) thys->End = 1 + ilen - itmp; else thys->End = 0; itmp = thys->Start2; if(thys->End2) thys->Start2 = 1 + ilen - thys->End2; else thys->Start2 = 0; /* reverse the flags */ if(saveflags & AJFEATFLAG_START_BEFORE_SEQ) thys->Flags |= AJFEATFLAG_END_AFTER_SEQ; else thys->Flags &= ~AJFEATFLAG_END_AFTER_SEQ; if(saveflags & AJFEATFLAG_END_AFTER_SEQ) thys->Flags |= AJFEATFLAG_START_BEFORE_SEQ; else thys->Flags &= ~AJFEATFLAG_START_BEFORE_SEQ; if(saveflags & AJFEATFLAG_START_TWO) thys->Flags |= AJFEATFLAG_END_TWO; else thys->Flags &= ~AJFEATFLAG_END_TWO; if(saveflags & AJFEATFLAG_END_TWO) thys->Flags |= AJFEATFLAG_START_TWO; else thys->Flags &= ~AJFEATFLAG_START_TWO; if(saveflags & AJFEATFLAG_START_UNSURE) thys->Flags |= AJFEATFLAG_END_UNSURE; else thys->Flags &= ~AJFEATFLAG_END_UNSURE; if(saveflags & AJFEATFLAG_END_UNSURE) thys->Flags |= AJFEATFLAG_START_UNSURE; else thys->Flags &= ~AJFEATFLAG_START_UNSURE; if(itmp) thys->End2 = 1 + ilen - itmp; else thys->End2 = 0; /* thys->Frame is rather hard to guess ... leave alone for now */ thys->Frame = 0; /* set to unknown */ /*ajDebug(" Reversed %x '%c' %d..%d %d..%d\n", thys->Flags, thys->Strand, thys->Start, thys->End, thys->Start2, thys->End2);*/ if(thys->Subfeatures) { iter = ajListIterNewread(thys->Subfeatures); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); ajFeatReverse(gf, ilen); } ajListIterDel(&iter); } return; } ID ajFeattableNewDna TY public MO ajfeat LB core XX DE Constructor for a new DNA feature table XX PN [1] PA r name const AjPStr PD Name for new feature table PD (or NULL for unnamed) PX RT AjPFeattable RD Pointer to a new (empty) feature table RX // AjPFeattable ajFeattableNewDna(const AjPStr name) { AjPFeattable thys = NULL; /* Allocate the object... */ thys = featTableNewS(name); ajStrAssignC(&thys->Type, "N"); /*ajDebug("ajFeattableNewDna %x\n", thys);*/ return thys; } ID ajFeattableNewSeq TY public MO ajfeat LB core XX DE Constructor for a new feature table for an existing sequence. DE The feature table type is determined by the sequence type. XX PN [1] PA r seq const AjPSeq PD Sequence object to provide the name and type PX RT AjPFeattable RD Pointer to a new (empty) feature table RX // AjPFeattable ajFeattableNewSeq(const AjPSeq seq) { AjPFeattable thys = NULL; /* Allocate the object... */ /* AJNEW0(thys); deleted by AJB */ if(ajSeqIsProt(seq)) thys = ajFeattableNewProt(ajSeqGetNameS(seq)); else thys = ajFeattableNewDna(ajSeqGetNameS(seq)); thys->Len = ajSeqGetLen(seq); return thys; } ID ajFeattableNewProt TY public MO ajfeat LB core XX DE Constructor for a new protein feature table XX PN [1] PA r name const AjPStr PD Name for new feature table PD (or NULL for unnamed) PX RT AjPFeattable RD Pointer to a new (empty) feature table RX // AjPFeattable ajFeattableNewProt(const AjPStr name) { AjPFeattable thys = NULL; /* Allocate the object... */ thys = featTableNewS(name); ajStrAssignC(&thys->Type, "P"); /*ajDebug("ajFeattableNewProt %x\n", thys);*/ return thys; } ID featTagvalNew TY static MO ajfeat LB core XX DE Constructor for a feature tag-value pair XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA r tag const AjPStr PD Tag name PX PN [3] PA r value const AjPStr PD Tag value PX RT AjPTagval RD New tag-value pair object RX // static AjPTagval featTagvalNew(const AjPFeature thys, const AjPStr tag, const AjPStr value) { AjPTagval ret; if(thys->Protein) ret = featTagvalNewProt(tag, value); else ret = featTagvalNewDna(tag, value); return ret; } ID ajFeatGfftagAddCS TY public MO ajfeat LB core XX DE Constructor for a feature GFF3 tag-value pair XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r tag const char* PD Tag name PX PN [3] PA r value const AjPStr PD Tag value list PX RT ajuint RD Number of values added RX // ajuint ajFeatGfftagAddCS(AjPFeature thys, const char* tag, const AjPStr value) { ajuint ret; AjPStr tagstr = NULL; tagstr= ajStrNewC(tag); ret = ajFeatGfftagAddSS(thys, tagstr, value); ajStrDel(&tagstr); return ret; } ID ajFeatGfftagAddSS TY public MO ajfeat LB core XX DE Constructor for a feature GFF3 tag-value pair XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r tag const AjPStr PD Tag name PX PN [3] PA r value const AjPStr PD Tag value list PX RT ajuint RD Number of values added RX // ajuint ajFeatGfftagAddSS(AjPFeature thys, const AjPStr tag, const AjPStr value) { ajuint ret = 0; AjPTagval tagval; AjPStrTok handle = NULL; AjPStr tmpstr = NULL; AjBool predefinedTag = ajFalse; featInit(); predefinedTag = featTagGff3PredefinedTag(tag); if(!thys->GffTags && predefinedTag) thys->GffTags = ajListNew(); handle = ajStrTokenNewC(value, ","); while(ajStrTokenNextParse(&handle, &tmpstr)) { tagval = ajTagvalNewS(tag, tmpstr); ret++; if(predefinedTag) ajListPushAppend(thys->GffTags, tagval); else ajListPushAppend(thys->Tags, tagval); tagval = NULL; } ajStrTokenDel(&handle); ajStrDel(&tmpstr); return ret; } ID ajFeatGfftagAddTag TY public MO ajfeat LB core XX DE Sets a feature tag value, creating a new feature tag even if one DE already exists. XX PN [1] PA u thys AjPFeature PD Feature PX PN [2] PA r tagval const AjPTagval PD Tag value pair PX RT AjBool RD ajTrue if value was valid RD ajFalse if it was bad and was "corrected" RX // AjBool ajFeatGfftagAddTag(AjPFeature thys, const AjPTagval tagval) { return ajFeatGfftagAddSS(thys, MAJTAGVALGETTAG(tagval), MAJTAGVALGETVALUE(tagval)); } ID featTagvalNewDna TY static MO ajfeat LB core XX DE Constructor for a feature tag-value pair XX PN [1] PA r tag const AjPStr PD Tag name PX PN [2] PA r value const AjPStr PD Tag value PX RT AjPTagval RD New tag-value pair object RX // static AjPTagval featTagvalNewDna(const AjPStr tag, const AjPStr value) { AjPTagval ret; const AjPStr tmptag = NULL; /* from AjPTable, don't delete */ AjBool knowntag = ajTrue; featInit(); tmptag = ajFeattagGetNameS(tag, FeatTagsTableDna, &knowntag); ret = ajTagvalNewS(tmptag, value); return ret; } ID featTagvalNewProt TY static MO ajfeat LB core XX DE Constructor for a protein feature tag-value pair XX PN [1] PA r tag const AjPStr PD Tag name PX PN [2] PA r value const AjPStr PD Tag value PX RT AjPTagval RD New tag-value pair object RX // static AjPTagval featTagvalNewProt(const AjPStr tag, const AjPStr value) { AjPTagval ret; const AjPStr tmptag = NULL; /* from AjPTable, don't delete */ AjBool knowntag = ajTrue; featInit(); tmptag = ajFeattagGetNameS(tag, FeatTagsTableProtein, &knowntag); ret = ajTagvalNewS(tmptag, value); return ret; } ID featTagval TY static MO ajfeat LB core XX DE Checks for the existence of a defined tag for a feature. XX PN [1] PA r thys const AjPFeature PD Feature object PX PN [2] PA r tag const AjPStr PD Tag name PX RT AjPTagval RD Returns the tag-value pair if found, RD NULL if not found. RX // static AjPTagval featTagval(const AjPFeature thys, const AjPStr tag) { AjIList iter = NULL; AjPTagval ret = NULL; AjPTagval tv = NULL; iter = ajListIterNewread(thys->Tags); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); if(ajStrMatchCaseS(MAJTAGVALGETTAG(tv), tag)) { /* ajDebug("featTagval '%S' found value '%S'\n", tag, MAJTAGVALGETVALUE(tv)); */ ret = tv; break; } } ajListIterDel(&iter); /* if(!ret) ajDebug("featTagval '%S' not found\n", tag); */ return ret; } ID ajFeattableNewFtable TY public MO ajfeat LB core XX DE Makes a copy of a feature table. DE DE For cases where we need a copy we can safely change and/or delete. XX PN [1] PA r orig const AjPFeattable PD Original feature table PX RT AjPFeattable RD Feature table copy of the original RX // AjPFeattable ajFeattableNewFtable(const AjPFeattable orig) { AjPFeattable ret = NULL; AjIList iter; AjPFeature featorig; AjPFeature feat = NULL; if(!orig) return NULL; ret = featTableNew(); ajStrAssignS(&ret->Seqid, orig->Seqid); ajStrAssignS(&ret->Type, orig->Type); ajStrAssignS(&ret->Formatstr, orig->Formatstr); ret->Format = orig->Format; ret->Start = orig->Start; ret->End = orig->End; ret->Len = orig->Len; ret->Groups = orig->Groups; iter = ajListIterNewread(orig->Features); while(!ajListIterDone(iter)) { featorig = ajListIterGet(iter); feat = ajFeatNewFeat(featorig); ajFeattableAdd(ret, feat); } ajListIterDel(&iter); return ret; } ID ajFeattableNewFtableLimit TY public MO ajfeat LB core XX DE Makes a copy of a feature table using only a limited number of features. DE DE For cases where we need a copy we can safely change and/or delete. XX PN [1] PA r orig const AjPFeattable PD Original feature table PX PN [2] PA r limit ajint PD Limit to number of features copied PX RT AjPFeattable RD Feature table copy of the original RX // AjPFeattable ajFeattableNewFtableLimit(const AjPFeattable orig, ajint limit) { AjPFeattable ret = NULL; AjIList iter; AjPFeature featorig; AjPFeature feat = NULL; ajint ift = 0; if(!orig) return NULL; ret = featTableNew(); ajStrAssignS(&ret->Seqid, orig->Seqid); ajStrAssignS(&ret->Type, orig->Type); ajStrAssignS(&ret->Formatstr, orig->Formatstr); ret->Format = orig->Format; ret->Start = orig->Start; ret->End = orig->End; ret->Len = orig->Len; ret->Groups = orig->Groups; iter = ajListIterNewread(orig->Features); while(!ajListIterDone(iter) && (ift++ < limit)) { featorig = ajListIterGet(iter); feat = ajFeatNewFeat(featorig); ajFeattableAdd(ret, feat); } ajListIterDel(&iter); return ret; } ID ajFeatNewFeat TY public MO ajfeat LB core XX DE Makes a copy of a feature. DE DE For cases where we need a copy we can safely change and/or delete. XX PN [1] PA r orig const AjPFeature PD Original feature PX RT AjPFeature RD Feature copy of the original RX CA new CT AjPFeature CD Copy constructor CX // AjPFeature ajFeatNewFeat(const AjPFeature orig) { AjPFeature ret; AjPFeature subfeat; AjIList iter; AjPTagval tvorig; ret = featFeatureNew(); if(orig->Source) ajStrAssignS(&ret->Source, orig->Source); if(orig->Type) ajStrAssignS(&ret->Type, orig->Type); if(orig->Remote) ajStrAssignS(&ret->Remote, orig->Remote); if(orig->Label) ajStrAssignS(&ret->Label, orig->Label); ret->Protein = orig->Protein; ret->Start = orig->Start; ret->End = orig->End; ret->Start2 = orig->Start2; ret->End2 = orig->End2; ret->Score = orig->Score; ret->Strand = orig->Strand; ret->Frame = orig->Frame; ret->Flags = orig->Flags; ret->Group = orig->Group; ret->Exon = orig->Exon; if(orig->Subfeatures) { iter = ajListIterNewread(orig->Subfeatures); while(!ajListIterDone(iter)) { subfeat = ajListIterGet(iter); ajListPushAppend(ret->Subfeatures, ajFeatNewFeat(subfeat)); } ajListIterDel(&iter); } if(orig->Tags) { iter = ajListIterNewread(orig->Tags); while(!ajListIterDone(iter)) { tvorig = ajListIterGet(iter); ajFeatTagAddTag(ret, tvorig); } ajListIterDel(&iter); } if(orig->GffTags) { iter = ajListIterNewread(orig->GffTags); while(!ajListIterDone(iter)) { tvorig = ajListIterGet(iter); ajFeatGfftagAddTag(ret, tvorig); } ajListIterDel(&iter); } return ret; } ID ajFeatTrace TY public MO ajfeat LB core XX DE Traces (to the debug file) a feature object XX PN [1] PA r thys const AjPFeature PD Feature PX RT void RD RX // void ajFeatTrace(const AjPFeature thys) { AjPStr flagstr = NULL; AjPFeature subft = NULL; AjIList iter = NULL; ajuint isub = 0; ajDebug(" address: %p\n", thys); ajDebug(" Source: '%S'\n", thys->Source); if(thys->Protein) ajDebug(" Type: '%S' protein: %B '%S'\n", thys->Type, thys->Protein, ajFeatTypeProt(thys->Type)); else ajDebug(" Type: '%S' protein: %B '%S'\n", thys->Type, thys->Protein, ajFeatTypeNuc(thys->Type)); ajDebug(" Location: %d..%d\n", thys->Start, thys->End); ajDebug(" Strand: '%c'\n", thys->Strand); ajDebug(" Frame: '%d'\n", thys->Frame); ajDebug(" Score: '%f'\n", thys->Score); ajDebug(" Start2: '%d'\n", thys->Start2); ajDebug(" End2: '%d'\n", thys->Start2); ajDebug(" RemoteId: '%S'\n", thys->Remote); ajDebug(" Label: '%S'\n", thys->Label); if(thys->Flags) { if(thys->Flags & AJFEATFLAG_START_BEFORE_SEQ) ajStrAppendC(&flagstr, "start_before "); if(thys->Flags & AJFEATFLAG_END_AFTER_SEQ) ajStrAppendC(&flagstr, "end_after "); if(thys->Flags & AJFEATFLAG_GENERATED) ajStrAppendC(&flagstr, "generated "); if(thys->Flags & AJFEATFLAG_BETWEEN_SEQ) ajStrAppendC(&flagstr, "between "); if(thys->Flags & AJFEATFLAG_START_TWO) ajStrAppendC(&flagstr, "start2 "); if(thys->Flags & AJFEATFLAG_END_TWO) ajStrAppendC(&flagstr, "end2 "); if(thys->Flags & AJFEATFLAG_POINT) ajStrAppendC(&flagstr, "point "); if(thys->Flags & AJFEATFLAG_COMPLEMENT_MAIN) ajStrAppendC(&flagstr, "overall_complement "); if(thys->Flags & AJFEATFLAG_MULTIPLE) ajStrAppendC(&flagstr, "multiple "); if(thys->Flags & AJFEATFLAG_ORDER) ajStrAppendC(&flagstr, "order "); if(thys->Flags & AJFEATFLAG_REMOTEID) ajStrAppendC(&flagstr, "remote_id "); if(thys->Flags & AJFEATFLAG_LABEL) ajStrAppendC(&flagstr, "label "); if(thys->Flags & AJFEATFLAG_START_UNSURE) ajStrAppendC(&flagstr, "start_unsure "); if(thys->Flags & AJFEATFLAG_END_UNSURE) ajStrAppendC(&flagstr, "end_unsure "); ajStrCutEnd(&flagstr, 1); ajDebug(" Flags: '%x' (%S)\n", thys->Flags, flagstr); } else { ajDebug(" Flags: '%x'\n", thys->Flags); } ajDebug(" Group: '%u' exon: %u\n", thys->Group, thys->Exon); ajFeatTagTrace(thys); ajStrDel(&flagstr); if(ajListGetLength(thys->Subfeatures)) { ajDebug(" Subfeatures: %Lu\n\n", ajListGetLength(thys->Subfeatures)); isub = 0; iter = ajListIterNewread(thys->Subfeatures); while(!ajListIterDone(iter)) { ajDebug("Subfeature[%u] ...\n", ++isub); subft = ajListIterGet(iter); ajFeatTrace(subft); } ajListIterDel(&iter); ajDebug(" Sub %u done\n\n", isub); } return; } ID ajFeatTagTrace TY public MO ajfeat LB core XX DE Traces (to the debug file) the tag-value pairs of a feature object XX PN [1] PA r thys const AjPFeature PD Feature PX RT void RD RX // void ajFeatTagTrace(const AjPFeature thys) { AjIList iter; ajint i = 0; AjPTagval tv = NULL; ajFeatGfftagTrace(thys); iter = ajListIterNewread(thys->Tags); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); ajDebug(" %3d %S : '%S'\n", ++i, ajTagvalGetTag(tv), ajTagvalGetValue(tv)); } ajListIterDel(&iter); return; } ID ajFeatGfftagTrace TY public MO ajfeat LB core XX DE Traces (to the debug file) the GFF tag-value pairs of a feature object XX PN [1] PA r thys const AjPFeature PD Feature PX RT void RD RX // void ajFeatGfftagTrace(const AjPFeature thys) { AjIList iter; ajint i = 0; AjPTagval tv = NULL; iter = ajListIterNewread(thys->GffTags); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); ajDebug(" %3d %S : '%S'\n", ++i, ajTagvalGetTag(tv), ajTagvalGetValue(tv)); } ajListIterDel(&iter); return; } ID ajFeatGfftagsNew TY public MO ajfeat LB core XX DE Creates a structure holding GFF tag value pairs XX RT AjPFeatGfftags RD GFF tag-values structure RX // AjPFeatGfftags ajFeatGfftagsNew(void) { AjPFeatGfftags ret = NULL; AJNEW0(ret); return ret; } ID ajFeatGfftagsDel TY public MO ajfeat LB core XX DE Destroys a structure holding GFF tag value pairs XX PN [1] PA d Pthys AjPFeatGfftags* PD GFF tag-values structure PX RT void RD RX // void ajFeatGfftagsDel(AjPFeatGfftags *Pthys) { AjPFeatGfftags thys; if(!Pthys) return; if(!*Pthys) return; thys = *Pthys; ajStrDel(&thys->Id); ajStrDel(&thys->Name); ajStrDel(&thys->Alias); ajStrDel(&thys->Parent); ajStrDel(&thys->Target); ajStrDel(&thys->Gap); ajStrDel(&thys->DerivesFrom); ajStrDel(&thys->Note); ajStrDel(&thys->Dbxref); ajStrDel(&thys->OntologyTerm); ajStrDel(&thys->IsCircular); AJFREE(*Pthys); return; } ID ajFeatGetGfftags TY public MO ajfeat LB core XX DE Returns an object with all feature GFF tag-value pairs XX PN [1] PA r thys const AjPFeature PD Feature PX RT AjPFeatGfftags RD Gff tags object RX // AjPFeatGfftags ajFeatGetGfftags(const AjPFeature thys) { AjPFeatGfftags ret = NULL; AjIList iter; AjPStr *Pvalstr = NULL; AjPStr tmpstr = NULL; AjPTagval tv = NULL; AjBool multivalues = ajFalse; const AjPStr tvtag = NULL; ret = ajFeatGfftagsNew(); if(!thys) return ret; if(!ajListGetLength(thys->GffTags)) return ret; iter = ajListIterNewread(thys->GffTags); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); tvtag = ajTagvalGetTag(tv); Pvalstr = NULL; multivalues = ajFalse; if(ajStrMatchC(tvtag, "ID")) Pvalstr = &ret->Id; else if(ajStrMatchC(tvtag, "Name")) Pvalstr = &ret->Name; else if(ajStrMatchC(tvtag, "Alias")) { multivalues = ajTrue; Pvalstr = &ret->Alias; } else if(ajStrMatchC(tvtag, "Parent")) { multivalues = ajTrue; Pvalstr = &ret->Parent; } else if(ajStrMatchC(tvtag, "Target")) Pvalstr = &ret->Target; else if(ajStrMatchC(tvtag, "Gap")) Pvalstr = &ret->Gap; else if(ajStrMatchC(tvtag, "Derives_from")) Pvalstr = &ret->DerivesFrom; else if(ajStrMatchC(tvtag, "Note")) { multivalues = ajTrue; Pvalstr = &ret->Note; } else if(ajStrMatchC(tvtag, "Dbxref")) { multivalues = ajTrue; Pvalstr = &ret->Dbxref; } else if(ajStrMatchC(tvtag, "Ontology_term")) { multivalues = ajTrue; Pvalstr = &ret->OntologyTerm; } else if(ajStrMatchC(tvtag, "Is_circular")) Pvalstr = &ret->IsCircular; if(Pvalstr) { ajStrAssignS(&tmpstr, ajTagvalGetValue(tv)); ajStrFmtPercentEncodeC(&tmpstr, ";=&%,"); if(multivalues) { if(ajStrGetLen(*Pvalstr)) ajStrAppendC(Pvalstr, ","); ajStrAppendS(Pvalstr, tmpstr); } else ajStrAssignS(Pvalstr, tmpstr); } } ajListIterDel(&iter); ajStrDel(&tmpstr); return ret; } ID ajFeatGetId TY public MO ajfeat LB core XX DE Returns the GFF ID tag of a feature with GFF tag-value pairs XX PN [1] PA r thys const AjPFeature PD Feature PX RT const AjPStr RD ID tag RX // const AjPStr ajFeatGetId(const AjPFeature thys) { AjIList iter; AjPTagval tv = NULL; if(!thys) return NULL; if(!ajListGetLength(thys->GffTags)) return NULL; iter = ajListIterNewread(thys->GffTags); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); if(ajStrMatchC(MAJTAGVALGETTAG(tv), "Id")) { ajListIterDel(&iter); return MAJTAGVALGETVALUE(tv); } } return NULL; } ID ajFeatGetParent TY public MO ajfeat LB core XX DE Returns the GFF Parent tag of a feature with GFF tag-value pairs XX PN [1] PA r thys const AjPFeature PD Feature PX RT const AjPStr RD Parent tag RX // const AjPStr ajFeatGetParent(const AjPFeature thys) { AjIList iter; AjPTagval tv = NULL; if(!thys) return NULL; if(!ajListGetLength(thys->GffTags)) return NULL; iter = ajListIterNewread(thys->GffTags); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); if(ajStrMatchC(MAJTAGVALGETTAG(tv), "Parent")) { ajListIterDel(&iter); return MAJTAGVALGETVALUE(tv); } } return NULL; } ID ajFeatTagIter TY public MO ajfeat LB core XX DE Returns an iterator over all feature tag-value pairs XX PN [1] PA r thys const AjPFeature PD Feature PX RT AjIList RD List iterator RX // AjIList ajFeatTagIter(const AjPFeature thys) { return ajListIterNewread(thys->Tags); } ID ajFeatSubIter TY public MO ajfeat LB core XX DE Returns an iterator over all subfeaturers XX PN [1] PA r thys const AjPFeature PD Feature PX RT AjIList RD List iterator RX // AjIList ajFeatSubIter(const AjPFeature thys) { if(!ajListGetLength(thys->Subfeatures)) return NULL; return ajListIterNewread(thys->Subfeatures); } ID ajFeatTagval TY public MO ajfeat LB core XX DE Returns the tag-value pairs of a feature object XX PN [1] PA u iter AjIList PD List iterator from ajFeatTagIter PX PN [2] PA w Ptagnam AjPStr* PD Tag name PX PN [3] PA w Ptagval AjPStr* PD Tag val PX RT AjBool RD ajTrue if another tag-value pair was returned RX // AjBool ajFeatTagval(AjIList iter, AjPStr* Ptagnam, AjPStr* Ptagval) { AjPTagval tv = NULL; tv = ajListIterGet(iter); if(!tv) return ajFalse; ajStrAssignS(Ptagnam, MAJTAGVALGETTAG(tv)); ajStrAssignS(Ptagval, MAJTAGVALGETVALUE(tv)); return ajTrue; } ID ajFeattableTrace TY public MO ajfeat LB core XX DE Traces (to the debug file) a complete feature table XX PN [1] PA r thys const AjPFeattable PD Feature table PX RT void RD RX // void ajFeattableTrace(const AjPFeattable thys) { AjIList iter = NULL; AjPFeature ft = NULL; ajint i = 0; ajDebug("== ajFeattableTrace Start ==\n"); if(!thys) { ajDebug("NULL table\n"); return; } ajDebug(" Seqid: '%S'\n", thys->Seqid); iter = ajListIterNew(thys->Features); while(!ajListIterDone(iter)) { ft = ajListIterGet(iter); ajDebug("Features[%d]\n", ++i); ajFeatTrace(ft); } ajListIterDel(&iter); ajDebug("== ajFeattableTrace Done ==\n"); return; } ID ajFeatTypeNuc TY public MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for the internal DNA feature table XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // const AjPStr ajFeatTypeNuc(const AjPStr type) { if(!FeatInitDone) featInit(); return ajFeattypeGetExternal(type, FeatTypeTableDna); } ID ajFeatTypeProt TY public MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for the internal protein feature table XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // const AjPStr ajFeatTypeProt(const AjPStr type) { if(!FeatInitDone) featInit(); return ajFeattypeGetExternal(type, FeatTypeTableProtein); } ID featTypeDna TY static MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for the internal DNA feature table XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // static const AjPStr featTypeDna(const AjPStr type) { if(!FeatInitDone) featInit(); return featTableTypeInternal(type, FeatTypeTableDna); } ID featTypeProt TY static MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for the internal protein feature table XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // static const AjPStr featTypeProt(const AjPStr type) { if(!FeatInitDone) featInit(); return featTableTypeInternal(type, FeatTypeTableProtein); } ID featTypeDnaLimit TY static MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for the internal DNA feature table DE following alternative names until a unique name is found XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // static const AjPStr featTypeDnaLimit(const AjPStr type) { if(!FeatInitDone) featInit(); return featTableTypeInternalLimit(type, FeatTypeTableDna); } ID featTypeProtLimit TY static MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for the internal protein feature table DE following alternative names until a unique name is found XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // static const AjPStr featTypeProtLimit(const AjPStr type) { if(!FeatInitDone) featInit(); return featTableTypeInternalLimit(type, FeatTypeTableProtein); } ID featTypeTestDnaWild TY static MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for the internal DNA feature table DE following alternative names until a matching wildcard name is found XX PN [1] PA r type const AjPStr PD Type name PX PN [2] PA r str const AjPStr PD Wildcard name PX RT AjBool RD True if a match is found RX // static AjBool featTypeTestDnaWild(const AjPStr type, const AjPStr str) { if(!FeatInitDone) featInit(); return featTableTypeTestWild(type, FeatTypeTableDna, str); } ID featTypeTestProtWild TY static MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for the internal protein feature table DE following alternative names until a unique name is found XX PN [1] PA r type const AjPStr PD Type name PX PN [2] PA r str const AjPStr PD Wildcard name PX RT AjBool RD True if a match is found RX // static AjBool featTypeTestProtWild(const AjPStr type, const AjPStr str) { if(!FeatInitDone) featInit(); return featTableTypeTestWild(type, FeatTypeTableProtein, str); } ID featTagDna TY static MO ajfeat LB core XX DE Given a feature tag name, DE returns the valid feature tag for the internal DNA feature table XX PN [1] PA r thys const AjPStr PD Tag name PX PN [2] PA w knowntag AjBool* PD ajTrue if the tag was found in the PD list of known tags PX RT const AjPStr RD Valid feature tag name RX // static const AjPStr featTagDna(const AjPStr thys, AjBool* knowntag) { featInit(); return ajFeattagGetNameS(thys, FeatTagsTableDna, knowntag); } ID featTagProt TY static MO ajfeat LB core XX DE Given a feature tag name, DE returns the valid feature tag for the internal protein feature table XX PN [1] PA r thys const AjPStr PD Tag name PX PN [2] PA w knowntag AjBool* PD ajTrue if the tag was found in the PD list of known tags PX RT const AjPStr RD Valid feature tag name RX // static const AjPStr featTagProt(const AjPStr thys, AjBool* knowntag) { featInit(); return ajFeattagGetNameS(thys, FeatTagsTableProtein, knowntag); } ID ajFeattypeGetExternal TY public MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for a feature table XX PN [1] PA r type const AjPStr PD Type name PX PN [2] PA r table const AjPTable PD Feature table PX RT const AjPStr RD Valid feature type RX // const AjPStr ajFeattypeGetExternal(const AjPStr type, const AjPTable table) { static const AjPStr ret = NULL; static const AjPStr retkey = NULL; AjPStr defaultid = NULL; AjPStr tmpstr = NULL; ajlong i; retkey = ajTablestrFetchkeyS(table, type); if(!retkey) { defaultid = ajStrNew(); retkey = ajTableFetchS(table, defaultid); ajStrDel(&defaultid); ajDebug("ajFeattypeGetExternal '%S' not in external table %x, " "use default '%S'\n", type, table, retkey); } ret = ajTableFetchS(table, retkey); /*ajDebug("ajFeattypeGetExternal a '%S' found in internal table as" " '%S' = '%S\n", type, retkey, ret);*/ if(ajStrGetCharLast(ret) != ';') { retkey = ret; ret = ajTableFetchS(table, retkey); ajDebug("ajFeattypeGetExternal b '%S' found in internal table" " as '%S' = '%S\n", type, retkey, ret); } if(ajStrGetCharLast(ret) == ';') { if(ajStrGetCharFirst(ret) == ';') { ret = retkey; } else { i = ajStrFindAnyK(ret, ';'); ajStrAssignSubS(&tmpstr, ret, 0, i-1); /*ajDebug("ajFeattypeGetExternal '%S' is an alias for '%S'\n", retkey, tmpstr);*/ ret = ajTablestrFetchkeyS(table, tmpstr); if(!ret) /* oops, back to the previous one */ { ajFeatWarn("ajFeattypeGetExternal failed to find" " '%S' alias '%S", type, tmpstr); ret = ajTableFetchS(table, retkey); } ajStrDel(&tmpstr); } } /*ajDebug("ajFeattypeGetExternal result '%S'\n", ret);*/ return ret; } ID ajFeattypeGetInternal TY public MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for an internal feature table XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // const AjPStr ajFeattypeGetInternal(const AjPStr type) { const AjPStr ret = NULL; if(!FeatInitDone) featInit(); ret = featTableTypeInternal(type, FeatTypeTableDna); if(ret) return ret; return featTableTypeInternal(type, FeatTypeTableProtein); } ID ajFeattypeGetInternalEmbl TY public MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for an EMBL format feature table XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // const AjPStr ajFeattypeGetInternalEmbl(const AjPStr type) { if(!FeatInitDone) featInit(); if(!FeatTypeTableEmbl) ajFeatVocabInitNuc("embl"); return featTableTypeInternal(type, FeatTypeTableEmbl); } ID ajFeattypeGetInternalNuc TY public MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for an internal feature table XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // const AjPStr ajFeattypeGetInternalNuc(const AjPStr type) { if(!FeatInitDone) featInit(); return featTableTypeInternal(type, FeatTypeTableDna); } ID ajFeattypeGetInternalPir TY public MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for an internal feature table XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // const AjPStr ajFeattypeGetInternalPir(const AjPStr type) { if(!FeatInitDone) featInit(); if(!FeatTypeTablePir) ajFeatVocabInitProt("pir"); return featTableTypeInternal(type, FeatTypeTablePir); } ID ajFeattypeGetInternalProt TY public MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for an internal feature table XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // const AjPStr ajFeattypeGetInternalProt(const AjPStr type) { if(!FeatInitDone) featInit(); return featTableTypeInternal(type, FeatTypeTableProtein); } ID ajFeattypeGetInternalRefseqp TY public MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for a REFSEQP format feature table XX PN [1] PA r type const AjPStr PD Type name PX RT const AjPStr RD Valid feature type RX // const AjPStr ajFeattypeGetInternalRefseqp(const AjPStr type) { if(!FeatInitDone) featInit(); if(!FeatTypeTableRefseqp) ajFeatVocabInitProt("refseqp"); return featTableTypeInternal(type, FeatTypeTableRefseqp); } ID featTableTypeInternal TY static MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for a feature table XX PN [1] PA r type const AjPStr PD Type name PX PN [2] PA r table const AjPTable PD Feature table PX RT const AjPStr RD Valid feature type RX // static const AjPStr featTableTypeInternal(const AjPStr type, const AjPTable table) { const AjPStr ret = NULL; const AjPStr retkey = NULL; const AjPStr tmpstr = NULL; retkey = ajTablestrFetchkeyS(table, type); if(retkey) { #if FEATDEBUG ajDebug("featTableTypeInternal '%S' found in internal table" " as '%S'\n",type, retkey); #endif tmpstr = ajTableFetchS(table, retkey); if(ajStrGetCharLast(tmpstr) != ';') { #if FEATDEBUG ajDebug("featTableTypeInternal '%S' is an alias for '%S'\n", retkey, tmpstr); #endif retkey = ajTablestrFetchkeyS(table, tmpstr); } ajDebug("featTableTypeInternal result '%S'\n", retkey); return retkey; } ret = ajTableFetchS(table, ajStrNew()); #if FEATDEBUG ajDebug("featTableTypeInternal '%S' not in internal table %x, " "default to '%S'\n", type, table, ret); /* ajTablestrTrace(table); */ /*ajDebug("featTableTypeInternal result '%S'\n", ret);*/ #endif return ret; } ID featTableTypeInternalLimit TY static MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for a feature table DE following alternative names which are common in the internal table DE which is a combination of multiple definitions XX PN [1] PA r type const AjPStr PD Type name PX PN [2] PA r table const AjPTable PD Feature table PX RT const AjPStr RD Valid feature type or NULL if not found RX // static const AjPStr featTableTypeInternalLimit(const AjPStr type, const AjPTable table) { const AjPStr retkey = NULL; const AjPStr tmpstr = NULL; const AjPStr savekey = NULL; ajuint i = 0; retkey = ajTablestrFetchkeyS(table, type); while(retkey && i < 5) { savekey = retkey; tmpstr = ajTableFetchS(table, retkey); #if FEATDEBUG ajDebug("featTableTypeInternalLimit '%S' found in internal table" " as '%S' (%S)\n",type, retkey, tmpstr); #endif if(ajStrGetCharLast(tmpstr) != ';') { #if FEATDEBUG ajDebug("featTableTypeInternalLimit '%S' is an alias for '%S'\n", retkey, tmpstr); #endif retkey = ajTablestrFetchkeyS(table, tmpstr); } else { tmpstr = ajTableFetchS(table, retkey); retkey = ajTablestrFetchkeyS(table, tmpstr); if(ajStrMatchS(retkey, savekey)) retkey = NULL; } #if FEATDEBUG ajDebug("featTableTypeInternalLimit result %u '%S'\n", ++i, savekey); #endif } if(savekey) return savekey; ajDebug("featTableTypeInternalLimit '%S' not in internal table %x, " "default to NULL\n", type, table); /* ajTablestrTrace(table); */ /*ajDebug("featTableTypeInternalLimit result '%S'\n", ret);*/ return NULL; } ID featTableTypeTestWild TY static MO ajfeat LB core XX DE Given a feature type name, DE returns the valid feature type for a feature table DE following alternative names which are common in the internal table DE which is a combination of multiple definitions XX PN [1] PA r type const AjPStr PD Type name PX PN [2] PA r table const AjPTable PD Feature table PX PN [3] PA r str const AjPStr PD Wildcard name PX RT AjBool RD True if a match is found RX // static AjBool featTableTypeTestWild(const AjPStr type, const AjPTable table, const AjPStr str) { const AjPStr retkey = NULL; const AjPStr tmpstr = NULL; AjPStr tmpkey = NULL; ajulong i = 0UL; ajulong nkeys; void **keys = NULL; AjPStr key = NULL; if(ajStrMatchWildS(type, str)) return ajTrue; retkey = ajTablestrFetchkeyS(table, type); while(retkey && i < 5) { tmpstr = ajTableFetchS(table, retkey); ajDebug("featTableTypeTestWild '%S' found in internal table" " as '%S' (%S)\n",type, retkey, tmpstr); if(ajStrGetCharLast(tmpstr) != ';') { if(ajStrMatchWildS(tmpstr, str)) return ajTrue; tmpstr = ajTableFetchS(table, retkey); retkey = ajTablestrFetchkeyS(table, tmpstr); } else { ajStrAssignSubS(&tmpkey, tmpstr, 0, ajStrFindAnyK(tmpstr, ';')-1); if(ajStrMatchWildS(tmpkey, str)) { ajStrDel(&tmpkey); return ajTrue; } ajStrDel(&tmpkey); ajDebug("featTableTypeTestWild '%S' is an alias for '%S'\n", retkey, tmpstr); retkey = ajTablestrFetchkeyS(table, tmpstr); } ajDebug("featTableTypeTestWild result %u '%S'\n", ++i, retkey); } nkeys = ajTableToarrayKeys(table, &keys); for (i=0; iSubfeatures = ajListNew() ; /* Assume empty until otherwise needed */ ret->Tags = ajListNew() ; /* Assume empty until otherwise needed */ ret->GffTags = ajListNew() ; /* Assume empty until otherwise needed */ return ret; } ID featTableNew TY static MO ajfeat LB core XX DE Constructor for a feature table object. DE DE The type is left uninitialised XX RT AjPFeattable RD New empty feature table RX // static AjPFeattable featTableNew(void) { AjPFeattable ret; AJNEW0(ret); ret->Features = ajListNew() ; /* assume empty until otherwise needed */ return ret; } ID featTableNewS TY static MO ajfeat LB core XX DE Constructor for a feature table object with a defined name DE DE The type is left uninitialised XX PN [1] PA r name const AjPStr PD Name for new feature table PD (or NULL for unnamed) PX RT AjPFeattable RD New empty feature table RX // static AjPFeattable featTableNewS(const AjPStr name) { AjPFeattable ret; ret = featTableNew(); featTableInit(ret, name); return ret; } ID ajFeatTypeIsCds TY public MO ajfeat LB core XX DE Tests whether the feature is a CDS feature XX PN [1] PA r gf const AjPFeature PD Feature PX RT AjBool RD ajTrue on success RX // AjBool ajFeatTypeIsCds(const AjPFeature gf) { if(ajStrMatchC(gf->Type, "SO:0000316")) return ajTrue; return ajFalse; } ID ajFeatTypeMatchC TY public MO ajfeat LB core XX DE Tests whether the feature type matches a given string DE including testing for alternative names XX PN [1] PA r gf const AjPFeature PD Feature PX PN [2] PA r txt const char* PD Feature type name to test PX RT AjBool RD ajTrue on success RX // AjBool ajFeatTypeMatchC(const AjPFeature gf, const char* txt) { AjBool ret = ajFalse; AjPStr tmpstr; const AjPStr tmptype = NULL; if(ajStrMatchC(gf->Type, txt)) return ajTrue; tmpstr = ajStrNewC(txt); if(gf->Protein) { tmptype = featTypeProtLimit(tmpstr); if(tmptype) ret = ajStrMatchS(featTypeProtLimit(gf->Type), tmptype); ajDebug("ajFeatTypeMatch: %B '%S' prot: '%S' <=> '%S'\n", ret, tmpstr, tmptype, gf->Type); } else { tmptype = featTypeDnaLimit(tmpstr); if(tmptype) ret = ajStrMatchS(featTypeDnaLimit(gf->Type), tmptype); ajDebug("ajFeatTypeMatch: %B '%S' dna: '%S' <=> '%S'\n", ret, tmpstr, tmptype, gf->Type); } ajStrDel(&tmpstr); return ret; } ID ajFeatTypeMatchS TY public MO ajfeat LB core XX DE Tests whether the feature type matches a given string DE including testing for alternative names XX PN [1] PA r gf const AjPFeature PD Feature PX PN [2] PA r str const AjPStr PD Feature type name to test PX RT AjBool RD ajTrue on success RX // AjBool ajFeatTypeMatchS(const AjPFeature gf, const AjPStr str) { AjBool ret = ajFalse; if(ajStrMatchS(gf->Type, str)) return ajTrue; if(gf->Protein) { ret = ajStrMatchS(featTypeProtLimit(gf->Type),featTypeProtLimit(str)); ajDebug("ajFeatTypeMatch: %B '%S' '%S' prot: '%S' <=> '%S'\n", ret, str, gf->Type, featTypeProtLimit(str), featTypeProtLimit(gf->Type)); } else { ret = ajStrMatchS(featTypeDnaLimit(gf->Type),featTypeDnaLimit(str)); ajDebug("ajFeatTypeMatch: %B '%S' '%S' dna: '%S' <=> '%S'\n", ret, str, gf->Type, featTypeDnaLimit(str), featTypeDnaLimit(gf->Type)); } return ret; } ID ajFeatTypeMatchWildS TY public MO ajfeat LB core XX DE Tests whether the feature type matches a given wildcard string DE including testing for alternative names XX PN [1] PA r gf const AjPFeature PD Feature PX PN [2] PA r str const AjPStr PD Feature type name to test PX RT AjBool RD ajTrue on success RX // AjBool ajFeatTypeMatchWildS(const AjPFeature gf, const AjPStr str) { AjBool ret = ajFalse; AjPStr mystr = NULL; mystr = ajStrNewS(str); ajStrFmtLower(&mystr); if(gf->Protein) { ret = featTypeTestProtWild(gf->Type, mystr); if(!ret) { if(ajStrIsWild(mystr)) ajStrTrimEndC(&mystr, "*"); ret = ajFeatTypeMatchS(gf, mystr); } ajDebug("ajFeatTypeMatchWildS: %B '%S' '%S' prot:'%S'\n", ret, mystr, gf->Type, featTypeProtLimit(gf->Type)); } else { ret = featTypeTestDnaWild(gf->Type, mystr); if(!ret) { if(ajStrIsWild(mystr)) ajStrTrimEndC(&mystr, "*"); ret = ajFeatTypeMatchS(gf, mystr); } ajDebug("ajFeatTypeMatchWildS: %B '%S' '%S' dna: '%S'\n", ret, mystr, gf->Type, featTypeDnaLimit(gf->Type)); } ajStrDel(&mystr); return ret; } ID ajFeatIsLocal TY public MO ajfeat LB core XX DE Tests whether the feature is local to the sequence. DE Returns AJTRUE if it is local, AJFALSE if remote. XX PN [1] PA r gf const AjPFeature PD Feature PX RT AjBool RD ajTrue on success RX // AjBool ajFeatIsLocal(const AjPFeature gf) { return !(gf->Flags & AJFEATFLAG_REMOTEID); } ID ajFeatIsLocalRange TY public MO ajfeat LB core XX DE Tests whether the feature is local and in the specified range of the DE sequence. DE Returns AJTRUE if it is local and within the range. DE (Any label location is assumed to be outside the range.) XX PN [1] PA r gf const AjPFeature PD Feature PX PN [2] PA r start ajuint PD start of range PX PN [3] PA r end ajuint PD end of range PX RT AjBool RD ajTrue on success RX // AjBool ajFeatIsLocalRange(const AjPFeature gf, ajuint start, ajuint end) { if(gf->Flags & AJFEATFLAG_REMOTEID) return AJFALSE; if(gf->Flags & AJFEATFLAG_LABEL) return AJFALSE; if(gf->End < start || gf->Start > end) return AJFALSE; return AJTRUE; } ID ajFeatIsMultiple TY public MO ajfeat LB core XX DE Tests whether the feature is a member of a join, group order or one_of XX PN [1] PA r gf const AjPFeature PD Feature PX RT AjBool RD Returns AJTRUE if it is a member RX // AjBool ajFeatIsMultiple(const AjPFeature gf) { return (gf->Flags & AJFEATFLAG_MULTIPLE); } ID ajFeatIsCompMult TY public MO ajfeat LB core XX DE Tests whether the feature is a member of a complement around a DE multiple (join, etc.) XX PN [1] PA r gf const AjPFeature PD Feature PX RT AjBool RD Returns AJTRUE if it is a complemented multiple RX // AjBool ajFeatIsCompMult(const AjPFeature gf) { return (gf->Flags & AJFEATFLAG_COMPLEMENT_MAIN); } ID ajFeattablePos TY public MO ajfeat LB core XX DE Converts a string position into a true position. If ipos is negative, DE it is counted from the end of the string rather than the beginning. DE DE For strings, the result can go off the end to the terminating NULL. DE For sequences the maximum is the last base. XX PN [1] PA r thys const AjPFeattable PD Target feature table. PX PN [2] PA r ipos ajint PD Position. PX RT ajuint RD string position between 1 and length. RX // ajuint ajFeattablePos(const AjPFeattable thys, ajint ipos) { return ajFeattablePosII(ajFeattableGetLen(thys), 1, ipos); } ID ajFeattablePosI TY public MO ajfeat LB core XX DE Converts a string position into a true position. If ipos is negative, DE it is counted from the end of the string rather than the beginning. DE DE imin is a minimum relative position, also counted from the end DE if negative. Usually this is the start position when the end of a range DE is being tested. XX PN [1] PA r thys const AjPFeattable PD Target feature table. PX PN [2] PA r imin ajuint PD Start position. PX PN [3] PA r ipos ajint PD Position. PX RT ajuint RD string position between 1 and length. RX // ajuint ajFeattablePosI(const AjPFeattable thys, ajuint imin, ajint ipos) { return ajFeattablePosII(ajFeattableGetLen(thys), imin, ipos); } ID ajFeattablePosII TY public MO ajfeat LB core XX DE Converts a position into a true position. If ipos is negative, DE it is counted from the end of the sequence rather than the beginning. DE DE imin is a minimum relative position, also counted from the end DE if negative. Usually this is the start position when the end of a range DE is being tested. DE DE For strings, the result can go off the end to the terminating NULL. DE For sequences the maximum is the last base. XX PN [1] PA r ilen ajuint PD maximum length. PX PN [2] PA r imin ajuint PD Start position. PX PN [3] PA r ipos ajint PD Position. PX RT ajuint RD string position between 1 and length. RX // ajuint ajFeattablePosII(ajuint ilen, ajuint imin, ajint ipos) { ajuint jpos; if(ipos < 0) jpos = ilen + ipos + 1; else { if(ipos) jpos = ipos; else jpos = 1; } if(jpos > ilen) jpos = ilen; if(jpos < imin) jpos = imin; /*ajDebug("ajFeattablePosII(ilen: %d imin: %d ipos: %d) = %d\n", ilen, imin, ipos, jpos);*/ return jpos; } ID ajFeattableTrimOff TY public MO ajfeat LB core XX DE Trim a feature table using the Begin and Ends. DE DE Called where a sequence has been trimmed, so we have to allow for DE missing sequence positions at the start (ioffset) or at the end (ilen). XX PN [1] PA u thys AjPFeattable PD Target feature table. PX PN [2] PA r ioffset ajuint PD Offset from start of sequence PX PN [3] PA r ilen ajuint PD Length of sequence PX RT AjBool RD AjTrue returned if successful. RX // AjBool ajFeattableTrimOff(AjPFeattable thys, ajuint ioffset, ajuint ilen) { AjBool ok = ajTrue; AjBool dobegin = ajFalse; AjBool doend = ajFalse; ajuint begin = 0; ajuint end = 0; ajuint iseqlen; AjIList iter = NULL ; AjPFeature ft = NULL ; /*ajDebug("ajFeattableTrimOff offset %d len %d\n", ioffset, ilen);*/ /* ajDebug("ajFeattableTrimOff table Start %d End %d Len %d Features %Lu\n", thys->Start, thys->End, thys->Len, ajListGetLength(thys->Features));*/ iseqlen = ilen + ioffset; begin = ajFeattablePos(thys, thys->Start); if(begin <= ioffset) begin = ioffset + 1; if(thys->End) end = ajFeattablePosI(thys, begin, thys->End); else end = thys->Len; if(end > iseqlen) end = iseqlen; if(begin > 1) dobegin = ajTrue; if(end < thys->Len) doend = ajTrue; /*ajDebug(" ready to trim dobegin %B doend %B begin %d end %d\n", dobegin, doend, begin, end);*/ iter = ajListIterNew(thys->Features) ; while(!ajListIterDone(iter)) { ft = (AjPFeature)ajListIterGet(iter); if(!ajFeatTrimOffRange(ft, ioffset, begin, end, dobegin, doend)) { ajFeatDel(&ft); ajListIterRemove(iter); } } ajListIterDel(&iter); thys->Offset = ioffset; return ok; } ID ajFeattableTrim TY public MO ajfeat LB core XX DE Trim a feature table using the Begin and Ends. XX PN [1] PA u thys AjPFeattable PD Target feature table. PX RT AjBool RD AjTrue returned if successful. RX // AjBool ajFeattableTrim(AjPFeattable thys) { AjBool ok = ajTrue; AjBool dobegin = ajFalse; AjBool doend = ajFalse; ajuint begin = 0; ajuint end = 0; AjIList iter = NULL ; AjPFeature ft = NULL ; /* ajDebug("ajFeattableTrim table Start %d End %d Len %d Features %Lu\n", thys->Start, thys->End, thys->Len, ajListGetLength(thys->Features));*/ begin = ajFeattablePos(thys, thys->Start); if(thys->End) end = ajFeattablePosI(thys, begin, thys->End); else end = thys->Len; if(begin > 1) dobegin = ajTrue; if(end < thys->Len) doend = ajTrue; /*ajDebug(" ready to trim dobegin %B doend %B begin %d end %d\n", dobegin, doend, begin, end);*/ iter = ajListIterNew(thys->Features) ; while(!ajListIterDone(iter)) { ft = (AjPFeature)ajListIterGet(iter); if(!ajFeatTrimOffRange(ft, 0, begin, end, dobegin, doend)) { ajFeatDel(&ft); ajListIterRemove(iter); } } ajListIterDel(&iter); thys->Offset = 0; return ok; } ID ajFeatTrimOffRange TY public MO ajfeat LB core XX DE Trim a feature table using the Begin and Ends. DE DE Called where a sequence has been trimmed, so we have to allow for DE missing sequence positions at the start (ioffset) XX PN [1] PA u ft AjPFeature PD Target feature PX PN [2] PA r ioffset ajuint PD Offset from start of sequence PX PN [3] PA r begin ajuint PD Range start of sequence PX PN [4] PA r end ajuint PD Range end of sequence PX PN [5] PA r dobegin AjBool PD Reset begin PX PN [6] PA r doend AjBool PD Reset end PX RT AjBool RD AjTrue returned if successful. RX // AjBool ajFeatTrimOffRange(AjPFeature ft, ajuint ioffset, ajuint begin, ajuint end, AjBool dobegin, AjBool doend) { AjBool ok = ajTrue; /* ajDebug("ft flags %x %d..%d %d..%d\n", ft->Flags, ft->Start, ft->End, ft->Start2, ft->End2); */ if(ft->Flags & AJFEATFLAG_REMOTEID) /* feature in another sequence */ return ajTrue; if(ft->Flags & AJFEATFLAG_LABEL) /* label, no positions */ return ajTrue; if(doend) { if(ft->Start > end) /* beyond the end - delete this feature */ return ajFalse; if(ft->Start2 > end) ft->Start2 = end; if(ft->End > end) { ft->End = end; ft->Flags |= AJFEATFLAG_END_AFTER_SEQ; } if(ft->End2 > end) ft->End2 = end; } if(dobegin) { if(ft->End < begin) return ajFalse; if(begin > (ioffset + 1) && ft->End2 < begin) ft->End2 = begin; if(ft->Start && ft->Start < begin) { ft->Start = begin; ft->Flags |= AJFEATFLAG_START_BEFORE_SEQ; } if(ft->Start2 && ft->Start2 < begin) ft->Start2 = begin; } if(ioffset) /* shift to sequence offset */ { if(ft->Start) ft->Start -= ioffset; if(ft->Start2) ft->Start2 -= ioffset; if(ft->End) ft->End -= ioffset; if(ft->End2) ft->End2 -= ioffset; } return ok; } ID ajFeattagIsNote TY public MO ajfeat LB core XX DE Tests whether the feature tag is a note (the default feature tag) XX PN [1] PA r tag const AjPStr PD Feature tag PX RT AjBool RD ajTrue on success RX // AjBool ajFeattagIsNote(const AjPStr tag) { if(ajStrMatchC(tag, "note")) return ajTrue; return ajFalse; } ID ajFeatExit TY public MO ajfeat LB core XX DE Cleans up feature table internal memory XX RT void RD RX // void ajFeatExit(void) { ajFeatreadExit(); ajFeatwriteExit(); ajRegFree(&featRegSpecialAnticodon); ajRegFree(&featRegSpecialBiomaterial); ajRegFree(&featRegSpecialCodon); ajRegFree(&featRegSpecialCodonBad); ajRegFree(&featRegSpecialColdate); ajRegFree(&featRegSpecialCompare); ajRegFree(&featRegSpecialConssplice); ajRegFree(&featRegSpecialEstlen); ajRegFree(&featRegSpecialInference); ajRegFree(&featRegSpecialLatlon); ajRegFree(&featRegSpecialMobile); ajRegFree(&featRegSpecialPrimer); ajRegFree(&featRegSpecialRptRange); ajRegFree(&featRegSpecialRptRangeLab); ajRegFree(&featRegSpecialRptRangeComp); ajRegFree(&featRegSpecialRptunitSeq); ajRegFree(&featRegSpecialRptunitSeqPos); ajRegFree(&featRegSpecialTrans); ajRegFree(&featRegSpecialTransBad); ajRegFree(&featRegSpecialTransComp); ajRegFree(&featRegSpecialTransBadComp); ajRegFree(&featRegTagReplace); ajTablestrFree(&FeatTypeTableEmbl); ajTablestrFree(&FeatTagsTableEmbl); ajTablestrFree(&FeatTypeTableGff2); ajTablestrFree(&FeatTagsTableGff2); ajTablestrFree(&FeatTypeTableGff3); ajTablestrFree(&FeatTagsTableGff3); ajTablestrFree(&FeatTypeTablePir); ajTablestrFree(&FeatTagsTablePir); ajTablestrFree(&FeatTypeTableGff2protein); ajTablestrFree(&FeatTagsTableGff2protein); ajTablestrFree(&FeatTypeTableGff3protein); ajTablestrFree(&FeatTagsTableGff3protein); ajTablestrFree(&FeatTypeTableSwiss); ajTablestrFree(&FeatTagsTableSwiss); ajTablestrFree(&FeatTypeTableDna); ajTablestrFree(&FeatTagsTableDna); ajTablestrFree(&FeatTypeTableProtein); ajTablestrFree(&FeatTagsTableProtein); ajTablestrFree(&FeatTypeTableRefseqp); ajTablestrFree(&FeatTagsTableRefseqp); ajTablestrFree(&FeatCategoryTable); ajStrDel(&featTypeMiscfeat); ajStrDel(&featTypeEmpty); ajStrDel(&featDefSource); ajStrDel(&featFmtTmp); ajStrDel(&featTagTmp); ajStrDel(&featTagTmp2); ajStrDel(&featValTmp); ajStrDel(&featValTmp2); ajStrDel(&featTmpStr); ajStrDel(&featTagNote); ajStrDel(&featTransBegStr); ajStrDel(&featTransEndStr); ajStrDel(&featTransAaStr); ajStrDel(&featTempQry); ajStrTokenDel(&featVocabSplit); return; } ID ajFeatWarn TY public MO ajfeat LB core XX DE Formatted write as a warning message. XX PN [1] PA r fmt const char* PD Format string PX PN [2] PA v vararg ... PD Format arguments. PX RT void RD RX // void ajFeatWarn(const char* fmt, ...) { va_list args; static AjBool dowarn = AJTRUE; AjPStr tmpstr = NULL; AjPStr errstr = NULL; if(!featWarnCount) { if(ajNamGetValueC("featwarn", &tmpstr)) { ajStrToBool(tmpstr, &dowarn); } ajStrDel(&tmpstr); } featWarnCount++; if(!dowarn) return; va_start(args, fmt) ; ajFmtVPrintS(&errstr, fmt, args); va_end(args) ; ajWarn("%S", errstr); ajStrDel(&errstr); return; } ID feattabAccess TY list MO ajfeatdb LB ajaxdb XX DE Functions to access each database or feature access method XX // static AjOFeattabAccess feattabAccess[] = { /* Name AccessFunction FreeFunction Qlink Description Alias Entry Query All Chunk Padding */ { "das", &featAccessDas, NULL, "&", "retrieve features from a DAS server", AJFALSE, AJTRUE, AJFALSE, AJFALSE, AJFALSE, AJFALSE }, { "chado", &featAccessChado, NULL, "", "retrieve features from a CHADO server", AJFALSE, AJTRUE, AJFALSE, AJFALSE, AJFALSE, AJFALSE }, { "gcg", &featAccessGcg, NULL, "|&!^=", "emboss dbigcg indexed", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJFALSE, AJFALSE }, { "embossgcg", &featAccessEmbossGcg, NULL, "|&!^=", "emboss dbxgcg indexed", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJFALSE, AJFALSE }, { NULL, NULL, NULL, NULL, NULL, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE } }; ID ajFeatdbInit TY public MO ajfeatdb LB ajaxdb XX DE Initialise feature database internals XX RT void RD RX // void ajFeatdbInit(void) { AjPTable table; ajuint i = 0; table = ajFeattabaccessGetDb(); while(feattabAccess[i].Name) { ajCallTableRegister(table, feattabAccess[i].Name, (void*) &feattabAccess[i]); i++; } return; } ID featCdFileOpen TY static MO ajfeatdb LB ajaxdb XX DE Opens a named EMBL CD-ROM index file. XX PN [1] PA r dir const AjPStr PD Directory PX PN [2] PA r name const char* PD File name. PX PN [3] PA w fullname AjPStr* PD Full file name with directory path PX RT FeatPCdFile RD EMBL CD-ROM index file object. RX // static FeatPCdFile featCdFileOpen(const AjPStr dir, const char* name, AjPStr* fullname) { FeatPCdFile thys = NULL; AJNEW0(thys); thys->File = ajFileNewInNamePathC(name, dir); if(!thys->File) { AJFREE(thys); return NULL; } AJNEW0(thys->Header); featCdReadHeader(thys); thys->NRecords = thys->Header->NRecords; thys->RecSize = thys->Header->RecSize; ajStrAssignS(fullname, ajFileGetPrintnameS(thys->File)); ajDebug("featCdFileOpen '%F' NRecords: %d RecSize: %d\n", thys->File, thys->NRecords, thys->RecSize); return thys; } ID featCdFileSeek TY static MO ajfeatdb LB ajaxdb XX DE Sets the file position in an EMBL CD-ROM index file. XX PN [1] PA u fil FeatPCdFile PD EMBL CD-ROM index file object. PX PN [2] PA r ipos ajuint PD Offset. PX RT ajint RD Return value from the seek operation. RX // static ajint featCdFileSeek(FeatPCdFile fil, ajuint ipos) { ajint ret; ajuint jpos; jpos = 300 + ipos*fil->RecSize; ret = ajFileSeek(fil->File, jpos, 0); /* ajDebug("featCdFileSeek rec %u pos %u tell %Ld returns %d\n", ipos, jpos, ajFileResetPos(fil->File), ret); */ return ret; } ID featCdFileClose TY static MO ajfeatdb LB ajaxdb XX DE Closes an EMBL CD-ROM index file. XX PN [1] PA d pthis FeatPCdFile* PD EMBL CD-ROM index file. PX RT void RD RX // static void featCdFileClose(FeatPCdFile* pthis) { FeatPCdFile thys; thys = *pthis; if(!thys) return; ajDebug("featCdFileClose of %F\n", (*pthis)->File); ajFileClose(&thys->File); AJFREE(thys->Header); AJFREE(*pthis); return; } ID featCdIdxSearch TY static MO ajfeatdb LB ajaxdb XX DE Binary search through an EMBL CD-ROM index file for an exact match. XX PN [1] PA u idxLine FeatPCdIdx PD Index file record. PX PN [2] PA r entry const AjPStr PD Entry name to search for. PX PN [3] PA u fil FeatPCdFile PD EMBL CD-ROM index file. PX RT ajuint RD Record number on success, -1 on failure. RX // static ajuint featCdIdxSearch(FeatPCdIdx idxLine, const AjPStr entry, FeatPCdFile fil) { AjPStr entrystr = NULL; ajint ihi; ajint ilo; ajint ipos = 0; ajint icmp = 0; char *name; ajStrAssignS(&entrystr, entry); ajStrFmtUpper(&entrystr); ajDebug("featCdIdxSearch (entry '%S') records: %d\n", entrystr, fil->NRecords); if(fil->NRecords < 1) return -1; ilo = 0; ihi = fil->NRecords - 1; while(ilo <= ihi) { ipos = (ilo + ihi)/2; name = featCdIdxName(ipos, fil); icmp = ajStrCmpC(entrystr, name); ajDebug("idx test %u '%s' %2d (+/- %u)\n", ipos, name, icmp, ihi-ilo); if(!icmp) break; if(icmp < 0) ihi = ipos-1; else ilo = ipos+1; } ajStrDel(&entrystr); if(icmp) return -1; featCdIdxLine(idxLine, ipos, fil); return ipos; } ID featCdIdxQuery TY static MO ajfeatdb LB ajaxdb XX DE Binary search of an EMBL CD-ROM index file for entries matching a DE wildcard entry name. XX PN [1] PA u qry AjPQuery PD Query object. PX PN [2] PA r idqry const AjPStr PD ID Query PX RT AjBool RD ajTrue on success. RX // static AjBool featCdIdxQuery(AjPQuery qry, const AjPStr idqry) { FeatPCdQry qryd; AjPList list; FeatPCdIdx idxLine; FeatPCdFile fil; AjPStr idstr = NULL; AjPStr idpref = NULL; ajint ihi; ajint ilo; ajint ipos = 0; ajint icmp; char *name; ajint i; ajint ilen; ajint jlo; ajint jhi; ajint khi; AjBool first; ajint ifail = 0; ajint iskip = 0; FeatPCdEntry entry; qryd = qry->QryData; list = qry->ResultsList; idxLine = qryd->idxLine; fil = qryd->ifp; ajStrAssignS(&idstr,idqry); ajStrFmtUpper(&idstr); ajStrAssignS(&idpref, idstr); ajStrRemoveWild(&idpref); ajDebug("featCdIdxQuery (wild '%S' prefix '%S')\n", idstr, idpref); jlo = ilo = 0; khi = jhi = ihi = fil->NRecords-1; ilen = ajStrGetLen(idpref); first = ajTrue; if(ilen) { /* find first entry with this prefix */ while(ilo <= ihi) { ipos = (ilo + ihi)/2; name = featCdIdxName(ipos, fil); name[ilen] = '\0'; icmp = ajStrCmpC(idpref, name); /* test prefix */ ajDebug("idx test %d '%s' %2d (+/- %d)\n", ipos, name, icmp, ihi-ilo); if(!icmp) { /* hit prefix - test for first */ ajDebug("idx hit %d\n", ipos); if(first) { jhi = ihi; first = ajFalse; khi = ipos; } jlo = ipos; } if(icmp > 0) ilo = ipos+1; else ihi = ipos-1; } if(first) { /* failed to find any with prefix */ ajStrDel(&idstr); ajStrDel(&idpref); return ajFalse; } ajDebug("first pass: ipos %d jlo %d jhi %d\n", ipos, jlo, jhi); /* now search below for last */ ilo = jlo+1; ihi = jhi; while(ilo <= ihi) { ipos = (ilo + ihi)/2; name = featCdIdxName(ipos, fil); name[ilen] = '\0'; icmp = ajStrCmpC(idpref, name); ajDebug("idx test %d '%s' %2d (+/- %d)\n", ipos, name, icmp, ihi-ilo); if(!icmp) { /* hit prefix */ ajDebug("idx hit %d\n", ipos); khi = ipos; } if(icmp < 0) ihi = ipos-1; else ilo = ipos+1; } ajDebug("second pass: ipos %d jlo %d khi %d\n", ipos, jlo, khi); name = featCdIdxName(jlo, fil); ajDebug("first %d '%s'\n", jlo, name); name = featCdIdxName(khi, fil); ajDebug(" last %d '%s'\n", khi, name); } for(i=jlo; i < (khi+1); i++) { featCdIdxLine(idxLine, i, fil); if(ajStrMatchWildS(idxLine->EntryName, idstr)) { if(!qryd->Skip[idxLine->DivCode-1]) { if(ifail) { ajDebug("FAIL: %d entries\n", ifail); ifail=0; } if(iskip) { ajDebug("SKIP: %d entries\n", iskip); iskip=0; } ajDebug(" OK: '%S'\n", idxLine->EntryName); AJNEW0(entry); entry->div = idxLine->DivCode; entry->annoff = idxLine->AnnOffset; entry->seqoff = idxLine->SeqOffset; ajListPushAppend(list, (void*)entry); } else { ajDebug("SKIP: '%S' [file %d]\n", idxLine->EntryName, idxLine->DivCode); iskip++; } } else { ++ifail; /* ajDebug("FAIL: '%S' '%S'\n", idxLine->EntryName, idstr);*/ } } if(ifail) { ajDebug("FAIL: %d entries\n", ifail); ifail=0; } if(iskip) { ajDebug("SKIP: %d entries\n", iskip); ifail=0; } ajStrDel(&idstr); ajStrDel(&idpref); if(ajListGetLength(list)) return ajTrue; return ajFalse; } ID featCdTrgSearch TY static MO ajfeatdb LB ajaxdb XX DE Binary search of EMBL CD-ROM target file, for example an accession number DE search. XX PN [1] PA u trgLine FeatPCdTrg PD Target file record. PX PN [2] PA r entry const AjPStr PD Entry name or accession number. PX PN [3] PA u fp FeatPCdFile PD EMBL CD-ROM target file PX RT ajuint RD Record number, or -1 on failure. RX // static ajuint featCdTrgSearch(FeatPCdTrg trgLine, const AjPStr entry, FeatPCdFile fp) { AjPStr entrystr = NULL; ajint ihi; ajint ilo; ajint ipos; ajint icmp; ajint itry; char *name; ajStrAssignS(&entrystr, entry); ajStrFmtUpper(&entrystr); if(fp->NRecords < 1) return -1; ilo = 0; ihi = fp->NRecords; ipos = (ilo + ihi)/2; icmp = -1; ajDebug("featCdTrgSearch '%S' recSize: %d\n", entry, fp->RecSize); name = featCdTrgName(ipos, fp); icmp = ajStrCmpC(entrystr, name); ajDebug("trg testa %d '%s' %2d (+/- %d)\n", ipos, name, icmp, ihi-ilo); while(icmp) { if(icmp < 0) ihi = ipos; else ilo = ipos; itry = (ilo + ihi)/2; if(itry == ipos) { ajDebug("'%S' not found in .trg\n", entrystr); ajStrDel(&entrystr); return -1; } ipos = itry; name = featCdTrgName(ipos, fp); icmp = ajStrCmpC(entrystr, name); ajDebug("trg testb %d '%s' %2d (+/- %d)\n", ipos, name, icmp, ihi-ilo); } featCdTrgLine(trgLine, ipos, fp); ajStrDel(&entrystr); if(!trgLine->NHits) return -1; ajDebug("found in .trg at record %d\n", ipos); return ipos; } ID featCdIdxName TY static MO ajfeatdb LB ajaxdb XX DE Reads the name from record ipos of an EMBL CD-ROM index file. DE The name length is known from the index file object. XX PN [1] PA r ipos ajuint PD Record number. PX PN [2] PA u fil FeatPCdFile PD EMBL CD-ROM index file. PX RT char* RD Name read from file. RX // static char* featCdIdxName(ajuint ipos, FeatPCdFile fil) { ajuint nameSize; nameSize = fil->RecSize-10; if(featCdMaxNameSize < nameSize) { featCdMaxNameSize = nameSize; if(featCdName) ajCharDel(&featCdName); featCdName = ajCharNewRes(featCdMaxNameSize+1); } featCdFileSeek(fil, ipos); ajReadbinCharTrim(fil->File, nameSize, featCdName); return featCdName; } ID featCdIdxLine TY static MO ajfeatdb LB ajaxdb XX DE Reads a numbered record from an EMBL CD-ROM index file. XX PN [1] PA u idxLine FeatPCdIdx PD Index file record. PX PN [2] PA r ipos ajuint PD Record number. PX PN [3] PA u fil FeatPCdFile PD EMBL CD-ROM index file. PX RT void RD RX // static void featCdIdxLine(FeatPCdIdx idxLine, ajuint ipos, FeatPCdFile fil) { ajuint nameSize; nameSize = fil->RecSize-10; ajDebug("featCdIdxLine nameSize: %u max: %u ipos: %u '%F'\n", nameSize, featCdMaxNameSize, ipos, fil->File); if(featCdMaxNameSize < nameSize) { featCdMaxNameSize = nameSize; if(featCdName) ajCharDel(&featCdName); featCdName = ajCharNewRes(featCdMaxNameSize+1); } featCdFileSeek(fil, ipos); ajReadbinCharTrim(fil->File, nameSize, featCdName); ajStrAssignC(&idxLine->EntryName,featCdName); ajReadbinUint(fil->File, &idxLine->AnnOffset); ajReadbinUint(fil->File, &idxLine->SeqOffset); ajReadbinUint2(fil->File, &idxLine->DivCode); ajDebug("read ann: %u seq: %u div: %u\n", idxLine->AnnOffset, idxLine->SeqOffset, (ajuint) idxLine->DivCode); return; } ID featCdTrgName TY static MO ajfeatdb LB ajaxdb XX DE Reads the target name from an EMBL CD-ROM index target file. XX PN [1] PA r ipos ajuint PD Record number. PX PN [2] PA u fil FeatPCdFile PD EMBL CD-ROM index target file. PX RT char* RD Name. RX // static char* featCdTrgName(ajuint ipos, FeatPCdFile fil) { ajuint nameSize; ajint i; nameSize = fil->RecSize-8; if(featCdMaxNameSize < nameSize) { featCdMaxNameSize = nameSize; if(featCdName) ajCharDel(&featCdName); featCdName = ajCharNewRes(featCdMaxNameSize+1); } featCdFileSeek(fil, ipos); ajReadbinInt(fil->File, &i); ajReadbinInt(fil->File, &i); ajReadbinCharTrim(fil->File, nameSize, featCdName); ajDebug("featCdTrgName maxNameSize:%d nameSize:%d name '%s'\n", featCdMaxNameSize, nameSize, featCdName); return featCdName; } ID featCdTrgLine TY static MO ajfeatdb LB ajaxdb XX DE Reads a line from an EMBL CD-ROM index target file. XX PN [1] PA w trgLine FeatPCdTrg PD Target file record. PX PN [2] PA r ipos ajuint PD Record number. PX PN [3] PA u fil FeatPCdFile PD EMBL CD-ROM index target file. PX RT void RD . RX // static void featCdTrgLine(FeatPCdTrg trgLine, ajuint ipos, FeatPCdFile fil) { ajuint nameSize; nameSize = fil->RecSize-8; if(featCdMaxNameSize < nameSize) { featCdMaxNameSize = nameSize; if(featCdName) ajCharDel(&featCdName); featCdName = ajCharNewRes(featCdMaxNameSize+1); } featCdFileSeek(fil, ipos); ajReadbinUint(fil->File, &trgLine->NHits); ajReadbinUint(fil->File, &trgLine->FirstHit); ajReadbinCharTrim(fil->File, nameSize, featCdName); ajStrAssignC(&trgLine->Target,featCdName); ajDebug("featCdTrgLine %d nHits %d firstHit %d target '%S'\n", ipos, trgLine->NHits, trgLine->FirstHit, trgLine->Target); return; } ID featCdReadHeader TY static MO ajfeatdb LB ajaxdb XX DE Reads the header of an EMBL CD-ROM index file. XX PN [1] PA u fil FeatPCdFile PD EMBL CD-ROM index file. PX RT AjBool RD ajTrue on success. RX // static AjBool featCdReadHeader(FeatPCdFile fil) { ajint i; FeatPCdFHeader header; char date[8]; /* ajReadbinCharTrim needs space for trailing null */ header = fil->Header; ajReadbinUint(fil->File, &header->FileSize); ajReadbinUint(fil->File, &header->NRecords); ajReadbinInt2(fil->File, &header->RecSize); header->IdSize = header->RecSize - 10; ajReadbinCharTrim(fil->File, 20, header->DbName); ajReadbinCharTrim(fil->File, 10, header->Release); ajReadbinCharTrim(fil->File, 4, date); for(i=1;i<4;i++) header->Date[i] = date[i]; header->RelYear = header->Date[1]; header->RelMonth = header->Date[2]; header->RelDay = header->Date[3]; ajDebug("featCdReadHeader file %F\n", fil->File); ajDebug(" FileSize: %d NRecords: %hd recsize: %d idsize: %d\n", header->FileSize, header->NRecords, header->RecSize, header->IdSize); return ajTrue; } ID featCdTrgOpen TY static MO ajfeatdb LB ajaxdb XX DE Opens an EMBL CD-ROM target file pair. XX PN [1] PA r dir const AjPStr PD Directory. PX PN [2] PA r name const char* PD File name. PX PN [3] PA w trgfil FeatPCdFile* PD Target file. PX PN [4] PA w hitfil FeatPCdFile* PD Hit file. PX RT AjBool RD ajTrue on success. RX // static AjBool featCdTrgOpen(const AjPStr dir, const char* name, FeatPCdFile* trgfil, FeatPCdFile* hitfil) { AjPStr tmpname = NULL; AjPStr fullname = NULL; ajDebug("featCdTrgOpen dir '%S' name '%s'\n", dir, name); ajFmtPrintS(&tmpname, "%s.trg",name); *trgfil = featCdFileOpen(dir, ajStrGetPtr(tmpname), &fullname); ajStrDel(&tmpname); if(!*trgfil) return ajFalse; ajFmtPrintS(&tmpname, "%s.hit",name); *hitfil = featCdFileOpen(dir, ajStrGetPtr(tmpname), &fullname); ajStrDel(&tmpname); ajStrDel(&fullname); if(!*hitfil) return ajFalse; return ajTrue; } ID featCdTrgClose TY static MO ajfeatdb LB ajaxdb XX DE Close an EMBL CD-ROM target file pair. XX PN [1] PA w ptrgfil FeatPCdFile* PD Target file. PX PN [2] PA w phitfil FeatPCdFile* PD Hit file. PX RT AjBool RD ajTrue on success. RX // static AjBool featCdTrgClose(FeatPCdFile* ptrgfil, FeatPCdFile* phitfil) { featCdFileClose(ptrgfil); featCdFileClose(phitfil); return ajTrue; } ID featAccessGcg TY static MO ajfeatdb LB ajaxdb XX DE Reads feature(s) from a GCG formatted database, using EMBLCD index DE files. Returns with the file pointer set to the position in the DE sequence file and reference files. XX PN [1] PA u fttabin AjPFeattabin PD Feature table input. PX RT AjBool RD ajTrue on success. RX // static AjBool featAccessGcg(AjPFeattabin fttabin) { AjBool retval = ajFalse; AjPQuery qry; FeatPCdQry qryd; ajDebug("featAccessGcg type %d\n", fttabin->Input->Query->QueryType); qry = fttabin->Input->Query; qryd = qry->QryData; if(qry->QueryType == AJQUERY_ALL) { retval = featGcgAll(fttabin); return retval; } /* we need to search the index files and return a query */ if(qry->QryData) { /* reuse unfinished query data */ if(!featCdQryReuse(qry)) return ajFalse; } else { fttabin->Input->Single = ajTrue; if(!featCdQryOpen(qry)) { ajWarn("Failed to open index for database '%S'", qry->DbName); return ajFalse; } qryd = qry->QryData; ajFilebuffDel(&fttabin->Input->Filebuff); fttabin->Input->Filebuff = ajFilebuffNewNofile(); /* binary search for the entryname we need */ if(qry->QueryType == AJQUERY_ENTRY) { ajDebug("entry fields: %Lu hasacc:%B\n", ajListGetLength(qry->QueryFields), qry->HasAcc); if(!featCdQryEntry(qry)) ajDebug("GCG Entry failed\n"); } if(qry->QueryType == AJQUERY_QUERY) { ajDebug("query fields: %Lu hasacc:%B\n", ajListGetLength(qry->QueryFields), qry->HasAcc); if(!featCdQryQuery(qry)) ajDebug("GCG Query failed\n"); } AJFREE(qryd->trgLine); } if(ajListGetLength(qry->ResultsList)) { retval = featCdQryNext(qry); if(retval) featGcgLoadBuff(fttabin); } if(!ajListGetLength(qry->ResultsList)) { ajFileClose(&qryd->libr); ajFileClose(&qryd->libs); featCdQryClose(qry); } if(retval) ajStrAssignS(&fttabin->Input->Db, qry->DbName); return retval; } ID featGcgLoadBuff TY static MO ajfeatdb LB ajaxdb XX DE Copies text data to a buffered file, and feature data for an DE AjPFeattabin internal data structure for reading later XX PN [1] PA u fttabin AjPFeattabin PD Feature table input object PX RT void RD RX // static void featGcgLoadBuff(AjPFeattabin fttabin) { AjPQuery qry; FeatPCdQry qryd; qry = fttabin->Input->Query; qryd = qry->QryData; if(!qry->QryData) ajFatal("featGcgLoadBuff Query Data not initialised"); /* copy all the ref data */ featGcgReadRef(fttabin); /* skip the sequence (do we care about the format?) */ featGcgReadSeq(fttabin); /* ajFilebuffTraceFull(fttabin->Input->Filebuff, 9999, 100); */ if(!qryd->libr) { ajFileClose(&qryd->libs); ajDebug("featGcgLoadBuff: closed files\n"); } return; } ID featGcgReadRef TY static MO ajfeatdb LB ajaxdb XX DE Copies text data to a buffered file for reading later XX PN [1] PA u fttabin AjPFeattabin PD Feature table input object PX RT AjBool RD ajTrue on success RX // static AjBool featGcgReadRef(AjPFeattabin fttabin) { AjPStr line = NULL; AjPQuery qry; FeatPCdQry qryd; ajlong rpos; AjPStr id = NULL; AjPStr idc = NULL; AjBool ispir = ajFalse; AjBool continued = ajFalse; AjBool testcontinue = ajFalse; char *p = NULL; qry = fttabin->Input->Query; qryd = qry->QryData; if(!featRegGcgRefId) featRegGcgRefId =ajRegCompC("^>...([^ \n]+)"); if(!featRegGcgSplit) featRegGcgSplit =ajRegCompC("_0+$"); if(!ajReadline(qryd->libr, &line)) /* end of file */ return ajFalse; if(ajStrGetCharFirst(line) != '>') /* not start of entry */ ajFatal("featGcgReadRef bad entry start:\n'%S'", line); if(ajStrGetCharPos(line, 3) == ';') /* PIR entry */ ispir = ajTrue; if(ispir) ajFilebuffLoadS(fttabin->Input->Filebuff, line); if(ajRegExec(featRegGcgRefId, line)) { continued = ajFalse; ajRegSubI(featRegGcgRefId, 1, &id); if(ajRegExec(featRegGcgSplit, id)) { continued = ajTrue; p = ajStrGetuniquePtr(&id); p = strrchr(p,(ajint)'_'); *(++p)='\0'; ajStrSetValid(&id); } } else { ajDebug("featGcgReadRef bad ID line\n'%S'\n", line); ajFatal("featGcgReadRef bad ID line\n'%S'\n", line); } if(!ajReadline(qryd->libr, &line)) /* blank desc line */ { ajStrDel(&id); return ajFalse; } if(ispir) ajFilebuffLoadS(fttabin->Input->Filebuff, line); rpos = ajFileResetPos(qryd->libr); while(ajReadline(qryd->libr, &line)) { /* end of file */ if(ajStrGetCharFirst(line) == '>') { /* start of next entry */ /* skip over split entries so it can be used for "all" */ if(continued) { testcontinue=ajTrue; ajRegExec(featRegGcgRefId, line); ajRegSubI(featRegGcgRefId, 1, &idc); if(!ajStrPrefixS(idc,id)) { ajFileSeek(qryd->libr, rpos, 0); ajStrDel(&line); ajStrDel(&id); ajStrDel(&idc); return ajTrue; } } else { ajFileSeek(qryd->libr, rpos, 0); ajStrDel(&line); ajStrDel(&id); ajStrDel(&idc); return ajTrue; } } rpos = ajFileResetPos(qryd->libr); if(!testcontinue) { ajStrExchangeCC(&line, ". .", ".."); ajFilebuffLoadS(fttabin->Input->Filebuff, line); } } /* at end of file */ ajFileClose(&qryd->libr); ajStrDel(&line); ajStrDel(&id); ajStrDel(&idc); return ajTrue; } ID featGcgReadSeq TY static MO ajfeatdb LB ajaxdb XX DE Skips unwanted sequence data so file is at start of next entry. XX PN [1] PA u fttabin AjPFeattabin PD Feature table input object PX RT AjBool RD ajTrue on success RX // static AjBool featGcgReadSeq(AjPFeattabin fttabin) { AjPStr line = NULL; AjPQuery qry; FeatPCdQry qryd; AjPStr gcgtype = NULL; AjPStr tmpstr = NULL; AjPStr dstr = NULL; AjPStr id = NULL; AjPStr idc = NULL; AjPStr contseq = NULL; ajint gcglen; ajint pos; ajint rblock; ajlong spos; AjBool ispir = ajFalse; char *p = NULL; AjBool continued = ajFalse; qry = fttabin->Input->Query; qryd = qry->QryData; if(!featRegGcgId) { featRegGcgId =ajRegCompC("^>...([^ ]+) +([^ ]+) +(Dummy Header|[^ ]+)" " +([^ ]+) +([0-9]+)"); featRegGcgId2=ajRegCompC("^>[PF]1;([^ ]+)"); } if(!featRegGcgSplit) featRegGcgSplit =ajRegCompC("_0+$"); ajDebug("featGcgReadSeq pos: %Ld\n", ajFileResetPos(qryd->libs)); if(!ajReadline(qryd->libs, &line)) /* end of file */ return ajFalse; ajDebug("test ID line\n'%S'\n", line); if(ajRegExec(featRegGcgId, line)) { continued = ajFalse; ajRegSubI(featRegGcgId, 3, &gcgtype); ajRegSubI(featRegGcgId, 5, &tmpstr); ajRegSubI(featRegGcgId, 1, &id); if(ajRegExec(featRegGcgSplit, id)) { continued = ajTrue; p = ajStrGetuniquePtr(&id); p = strrchr(p,(ajint)'_'); *(++p)='\0'; ajStrSetValid(&id); if(!contseq) contseq = ajStrNew(); if(!dstr) dstr = ajStrNew(); } ajStrToInt(tmpstr, &gcglen); } else if(ajRegExec(featRegGcgId2, line)) { ajStrAssignC(&gcgtype, "ASCII"); ajRegSubI(featRegGcgId, 1, &tmpstr); ispir = ajTrue; } else { ajDebug("featGcgReadSeq bad ID line\n'%S'\n", line); ajFatal("featGcgReadSeq bad ID line\n'%S'\n", line); return ajFalse; } if(!ajReadline(qryd->libs, &line)) /* desc line */ return ajFalse; /* ** need to pick up the length and type, and read to the end of sequence ** see fasta code to get a real sequence for this ** Also need to handle split entries and go find the rest */ if(ispir) { spos = ajFileResetPos(qryd->libs); while(ajReadline(qryd->libs, &line)) { /* end of file */ if(ajStrGetCharFirst(line) == '>') { /* start of next entry */ ajFileSeek(qryd->libs, spos, 0); break; } spos = ajFileResetPos(qryd->libs); ajFilebuffLoadS(fttabin->Input->Filebuff, line); } } else { ajStrSetRes(&contseq, gcglen+3); rblock = gcglen; if(ajStrGetCharFirst(gcgtype) == '2') rblock = (rblock+3)/4; if(!ajReadbinBinary(qryd->libs, rblock, 1, ajStrGetuniquePtr(&contseq))) ajFatal("error reading file %F", qryd->libs); /* convert 2bit to ascii */ if(ajStrGetCharFirst(gcgtype) == '2') featGcgBinDecode(&contseq, gcglen); else if(ajStrGetCharFirst(gcgtype) == 'A') { /* are seq chars OK? */ ajStrSetValidLen(&contseq, gcglen); } else { ajRegSubI(featRegGcgId, 1, &tmpstr); ajFatal("Unknown GCG entry type '%S', entry name '%S'", gcgtype, tmpstr); } if(!ajReadline(qryd->libs, &line)) /* newline at end */ ajFatal("error reading file %F", qryd->libs); ajStrDel(&contseq); if(continued) { spos = ajFileResetPos(qryd->libs); while(ajReadline(qryd->libs,&line)) { ajRegExec(featRegGcgId, line); ajRegSubI(featRegGcgId, 5, &tmpstr); ajRegSubI(featRegGcgId, 1, &idc); if(!ajStrPrefixS(idc,id)) { ajFileSeek(qryd->libs, spos, 0); break; } ajStrToInt(tmpstr, &gcglen); if(!ajReadline(qryd->libs, &dstr)) /* desc line */ return ajFalse; ajStrSetRes(&contseq, gcglen+3); rblock = gcglen; if(ajStrGetCharFirst(gcgtype) == '2') rblock = (rblock+3)/4; if(!ajReadbinBinary(qryd->libs, rblock, 1, ajStrGetuniquePtr(&contseq))) ajFatal("error reading file %F", qryd->libs); /* convert 2bit to ascii */ if(ajStrGetCharFirst(gcgtype) == '2') featGcgBinDecode(&contseq, gcglen); else if(ajStrGetCharFirst(gcgtype) == 'A') { /* are seq chars OK? */ ajStrSetValidLen(&contseq, gcglen); } else { ajRegSubI(featRegGcgId, 1, &tmpstr); ajFatal("Unknown GCG entry: name '%S'", tmpstr); } if(!ajReadline(qryd->libs, &line)) /* newline at end */ ajFatal("error reading file %F", qryd->libs); if(!featRegGcgCont) featRegGcgCont = ajRegCompC("^([^ ]+) +([^ ]+) +([^ ]+) +" "([^ ]+) +([^ ]+) +([^ ]+) " "+([^ ]+) +" "([^ ]+) +([0-9]+)"); ajRegExec(featRegGcgCont, dstr); ajRegSubI(featRegGcgCont, 9, &tmpstr); ajStrToInt(tmpstr, &pos); /*seqin->Inseq->Len = pos-1;*/ /*ajStrAppendS(&seqin->Inseq,contseq);*/ spos = ajFileResetPos(qryd->libs); } } } ajStrDel(&line); ajStrDel(&gcgtype); ajStrDel(&tmpstr); ajStrDel(&dstr); ajStrDel(&id); ajStrDel(&idc); ajStrDel(&contseq); return ajTrue; } ID featGcgBinDecode TY static MO ajfeatdb LB ajaxdb XX DE Convert GCG binary to ASCII sequence. XX PN [1] PA u pthis AjPStr* PD Binary string PX PN [2] PA r sqlen ajuint PD Expected sequence length PX RT void RD RX // static void featGcgBinDecode(AjPStr *pthis, ajuint sqlen) { char* seqp; char* cp; char* start; const char* gcgbton="CTAG"; char stmp; ajint rdlen; start = ajStrGetuniquePtr(pthis); rdlen = (sqlen+3)/4; seqp = start + rdlen; cp = start + 4*rdlen; ajDebug("seqp:%x start:%x cp:%x sqlen:%d len:%d size:%d (seqp-start):%d\n", seqp, start, cp, sqlen, ajStrGetLen(*pthis), ajStrGetRes(*pthis), (seqp - start)); while(seqp > start) { stmp = *--seqp; *--cp = gcgbton[stmp&3]; *--cp = gcgbton[(stmp >>= 2)&3]; *--cp = gcgbton[(stmp >>= 2)&3]; *--cp = gcgbton[(stmp >>= 2)&3]; } start[sqlen] = '\0'; ajStrSetValidLen(pthis, sqlen); return; } ID featGcgAll TY static MO ajfeatdb LB ajaxdb XX DE Opens the first or next GCG file for further reading XX PN [1] PA u fttabin AjPFeattabin PD Feature table input. PX RT AjBool RD ajTrue on success. RX // static AjBool featGcgAll(AjPFeattabin fttabin) { AjPQuery qry; FeatPCdQry qryd; qry = fttabin->Input->Query; qryd = qry->QryData; ajDebug("featGcgAll\n"); if(!qry->QryData) { ajDebug("featGcgAll initialising\n"); fttabin->Input->Single = ajTrue; if(!featCdQryOpen(qry)) { ajErr("featGcgAll failed"); return ajFalse; } } qryd = qry->QryData; ajFilebuffDel(&fttabin->Input->Filebuff); fttabin->Input->Filebuff = ajFilebuffNewNofile(); if(!qryd->libr) { if(!featCdDivNext(qry)) { featCdQryClose(qry); ajDebug("featGcgAll finished\n"); return ajFalse; } if(!featCdQryFile(qry)) { ajErr("featGcgAll out of data"); return ajFalse; } ajDebug("featCdQryOpen processing file %2d '%F'\n", qryd->div, qryd->libr); if(qryd->libs) ajDebug(" sequence file '%F'\n", qryd->libs); } featGcgLoadBuff(fttabin); if(!qry->CaseId) qry->QryDone = ajTrue; return ajTrue; } ID featCdDivNext TY static MO ajfeatdb LB ajaxdb XX DE Sets the division count to the next included file. We need the division DE file to be already open. XX PN [1] PA u qry AjPQuery PD query object. PX RT ajuint RD File number (starting at 1) or zero if all files are done. RX // static ajuint featCdDivNext(AjPQuery qry) { FeatPCdQry qryd; AjPStr fullName = NULL; ajuint i; qryd = qry->QryData; ajDebug("featCdDivNext div: %d dfp: %x nameSize: %d name '%s'\n", qryd->div, qryd->maxdiv, qryd->nameSize, qryd->name); for(i=qryd->div; i < qryd->maxdiv; i++) if(!qryd->Skip[i]) { qryd->div = i+1; ajDebug("next file is %d '%S'\n", qryd->div, fullName); return qryd->div; } else ajDebug("skip %d '%S'\n", (i+1), fullName); return 0; } ID featCdQryFile TY static MO ajfeatdb LB ajaxdb XX DE Opens a specific file number for an EMBLCD index XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue on success RX // static AjBool featCdQryFile(AjPQuery qry) { FeatPCdQry qryd; short j; if(!featCdDivExp) featCdDivExp = ajRegCompC("^([^ ]+)( +([^ ]+))?"); ajDebug("featCdQryFile qry %x\n",qry); qryd = qry->QryData; ajDebug("featCdQryFile qryd %x\n",qryd); ajDebug("featCdQryFile %F\n",qryd->dfp->File); featCdFileSeek(qryd->dfp, (qryd->div - 1)); /* note - we must not use featCdFileReadName - we need spaces for GCG */ ajReadbinInt2(qryd->dfp->File, &j); ajReadbinChar(qryd->dfp->File, qryd->nameSize, qryd->name); ajDebug("DivCode: %d, code: %2hd '%s'\n", qryd->div, j, qryd->name); /**ajCharFmtLower(qryd->name);**/ if(!ajRegExecC(featCdDivExp, qryd->name)) { ajErr("index division file error '%S'", qryd->name); return ajFalse; } ajRegSubI(featCdDivExp, 1, &qryd->datfile); ajRegSubI(featCdDivExp, 3, &qryd->seqfile); ajDebug("File(s) '%S' '%S'\n", qryd->datfile, qryd->seqfile); ajFileClose(&qryd->libr); qryd->libr = ajFileNewInNamePathS(qryd->datfile, qry->Directory); if(!qryd->libr) { ajErr("Cannot open database file '%S' for database '%S'", qryd->datfile, qry->DbName); return ajFalse; } if(ajStrGetLen(qryd->seqfile)) { ajFileClose(&qryd->libs); qryd->libs = ajFileNewInNamePathS(qryd->seqfile, qry->Directory); if(!qryd->libs) { ajErr("Cannot open sequence file '%S' for database '%S'", qryd->seqfile, qry->DbName); return ajFalse; } } else qryd->libs = NULL; return ajTrue; } ID featCdTrgQuery TY static MO ajfeatdb LB ajaxdb XX DE Binary search of an EMBL CD-ROM index file for entries matching a DE wildcard query. DE DE Where more than one query field is defined (usually acc and sv) it DE can test all and append to a single list. XX PN [1] PA u qry AjPQuery PD Query object. PX PN [2] PA r field const AjPStr PD Query field PX PN [3] PA r wildqry const AjPStr PD Query string PX RT AjBool RD ajTrue on success. RX // static AjBool featCdTrgQuery(AjPQuery qry, const AjPStr field, const AjPStr wildqry) { ajint ret=0; if(ajStrMatchC(field, "org")) ret += featCdTrgFind(qry, "taxon", wildqry); if(ajStrMatchC(field, "key")) ret += featCdTrgFind(qry, "keyword", wildqry); if(ajStrMatchC(field, "des")) ret += featCdTrgFind(qry, "des", wildqry); if(ajStrMatchC(field, "sv")) ret += featCdTrgFind(qry, "seqvn", wildqry); if(ajStrMatchC(field, "gi")) ret += featCdTrgFind(qry, "gi", wildqry); if(qry->HasAcc && ajStrMatchC(field, "acc")) ret += featCdTrgFind(qry, "acnum", wildqry); if(ret) return ajTrue; return ajFalse; } ID featCdTrgFind TY static MO ajfeatdb LB ajaxdb XX DE Binary search of an EMBL CD-ROM index file for entries matching a DE wildcard query. DE DE Where more than one query field is defined (usually acc and sv) it DE can test all and append to a single list. XX PN [1] PA u qry AjPQuery PD Query object. PX PN [2] PA r indexname const char* PD Index name. PX PN [3] PA r queryName const AjPStr PD Query string. PX RT ajuint RD Number of matches found RX // static ajuint featCdTrgFind(AjPQuery qry, const char* indexname, const AjPStr queryName) { FeatPCdQry wild; AjPList l; FeatPCdTrg trgline; FeatPCdIdx idxline; FeatPCdFile idxfp; FeatPCdFile trgfp; FeatPCdFile hitfp; AjBool *skip; AjPStr fdstr = NULL; AjPStr fdprefix = NULL; ajint t; ajint b; ajint t2; ajint b2; ajint t3; ajint pos = 0; ajint prefixlen; ajint start; ajint end; ajint i; ajint j; ajint k; ajint cmp; AjBool match; AjBool first; char *name; FeatPCdEntry entry; wild = qry->QryData; l = qry->ResultsList; trgline = wild->trgLine; idxline = wild->idxLine; idxfp = wild->ifp; trgfp = wild->trgfp; hitfp = wild->hitfp; skip = wild->Skip; if(!featCdTrgOpen(qry->IndexDir, indexname, &trgfp, &hitfp)) return 0; /* fdstr is the original query string, in uppercase */ /* fdprefix is the fixed (no wildcard) prefix of fdstr */ ajStrAssignS(&fdstr,queryName); ajStrFmtUpper(&fdstr); ajStrAssignS(&fdprefix,fdstr); ajStrRemoveWild(&fdprefix); ajDebug("queryName '%S' fdstr '%S' fdprefix '%S'\n", queryName, fdstr, fdprefix); b = b2 = 0; t = t2 = t3 = trgfp->NRecords - 1; prefixlen = ajStrGetLen(fdprefix); first = ajTrue; if(prefixlen) { /* ** (1a) we have a prefix (no wildcard at the start) ** look for the prefix fdprefix ** Set range of records that match (will be consecutive of course) ** from first match */ while(b<=t) { pos = (t+b)/2; name = featCdTrgName(pos,trgfp); name[prefixlen]='\0'; /* truncate to prefix length */ cmp = ajStrCmpC(fdprefix,name); /* match = ajStrMatchWildC(fdstr,name);*/ ajDebug(" trg testc %d '%s' '%S' %B (+/- %d)\n", pos,name,fdprefix,cmp, t-b); if(!cmp) { ajDebug(" trg hit %d\n",pos); if(first) { first = ajFalse; t2 = t; t3 = pos; } b2 = pos; } if(cmp>0) b = pos+1; else t = pos-1; } if(first) { ajStrDel(&fdprefix); ajStrDel(&fdstr); featCdTrgClose(&trgfp,&hitfp); return ajFalse; } ajDebug("first pass: pos:%d b2:%d t2:%d\n",pos,b2,t2); /* ** (1b) Process below */ b = b2-1; t = t2; while(b<=t) { pos = (t+b)/2; name = featCdTrgName(pos,trgfp); name[prefixlen]='\0'; cmp = ajStrCmpC(fdprefix,name); /* match = ajStrMatchWildC(fdstr,name); */ ajDebug(" trg testd %d '%s' '%S' %B (+/- %d)\n", pos,name,fdprefix,cmp,t-b); if(!cmp) { ajDebug(" trg hit %d\n",pos); t3 = pos; } if(cmp<0) t = pos-1; else b = pos+1; } ajDebug("second pass: pos:%d b2:%d t3:%d\n",pos,b2,t3); name = featCdTrgName(b2,trgfp); ajDebug("first %d '%s'\n",b2,name); name = featCdTrgName(t3,trgfp); ajDebug("last %d '%s'\n",t3,name); } start = b2; end = t3; for(i=start;i<(end+1);++i) { name = featCdTrgName(i,trgfp); match = ajCharMatchWildC(name, ajStrGetPtr(fdstr)); ajDebug("third pass: match:%B i:%d name '%s' queryName '%S'\n", match, i, name, fdstr); if(!match) continue; featCdTrgLine(trgline, i, trgfp); featCdFileSeek(hitfp,trgline->FirstHit-1); ajDebug("Query First: %d Count: %d\n", trgline->FirstHit, trgline->NHits); pos = trgline->FirstHit; for(j=0;j<(ajint)trgline->NHits;++j) { ajReadbinInt(hitfp->File, &k); --k; ajDebug("hitlist[%d] entry = %d\n",j,k); featCdIdxLine(idxline,k,idxfp); if(!skip[idxline->DivCode-1]) { AJNEW0(entry); entry->div = idxline->DivCode; entry->annoff = idxline->AnnOffset; entry->seqoff = idxline->SeqOffset; ajListPushAppend(l,(void*)entry); } else ajDebug("SKIP: token '%S' [file %d]\n", queryName,idxline->DivCode); } } featCdTrgClose(&trgfp, &hitfp); ajStrDel(&trgline->Target); ajStrDel(&fdstr); ajStrDel(&fdprefix); return (ajuint) ajListGetLength(l); } ID featCdIdxDel TY static MO ajfeatdb LB ajaxdb XX DE Destructor for FeatPCdIdx XX PN [1] PA d pthys FeatPCdIdx* PD Cd index object PX RT void RD RX // static void featCdIdxDel(FeatPCdIdx* pthys) { FeatPCdIdx thys = *pthys; if(!thys) return; ajStrDel(&thys->EntryName); AJFREE(*pthys); return; } ID featCdTrgDel TY static MO ajfeatdb LB ajaxdb XX DE Destructor for FeatPCdTrg XX PN [1] PA d pthys FeatPCdTrg* PD Cd index target object PX RT void RD RX // static void featCdTrgDel(FeatPCdTrg* pthys) { FeatPCdTrg thys = *pthys; if(!thys) return; ajStrDel(&thys->Target); AJFREE(*pthys); return; } ID featAccessEmbossGcg TY static MO ajfeatdb LB ajaxdb XX DE Reads feature(s) from a GCG formatted database, using B+tree index DE files. Returns with the file pointer set to the position in the DE reference and sequence file. XX PN [1] PA u fttabin AjPFeattabin PD Feature table input. PX RT AjBool RD ajTrue on success. RX // static AjBool featAccessEmbossGcg(AjPFeattabin fttabin) { AjBool retval = ajFalse; AjPQuery qry; FeatPEmbossQry qryd = NULL; qry = fttabin->Input->Query; qryd = qry->QryData; ajDebug("featAccessEmbossGcg type %d\n", qry->QueryType); if(!ajNamDbGetDbalias(qry->DbName, &qry->DbAlias)) ajStrAssignS(&qry->DbAlias, qry->DbName); if(qry->QueryType == AJQUERY_ALL) return featEmbossGcgAll(fttabin); if(!qry->QryData) { if(!featEmbossQryOpen(qry)) return ajFalse; qryd = qry->QryData; fttabin->Input->Single = ajTrue; ajFilebuffDel(&fttabin->Input->Filebuff); fttabin->Input->Filebuff = ajFilebuffNewNofile(); if(qry->QueryType == AJQUERY_ENTRY) { if(!featEmbossQryEntry(qry)) ajDebug("embossgcg B+tree Entry failed\n"); } if(qry->QueryType == AJQUERY_QUERY) { if(!featEmbossQryQuery(qry)) ajDebug("embossgcg B+tree Query failed\n"); } } else { if(!featEmbossQryReuse(qry)) { featEmbossQryClose(qry); return ajFalse; } ajFilebuffClear(fttabin->Input->Filebuff, -1); } if(ajListGetLength(qry->ResultsList)) { retval = featEmbossQryNext(qry); if(retval) { featEmbossGcgLoadBuff(fttabin); ajStrAssignS(&fttabin->Input->Db, qry->DbName); } } if(!ajListGetLength(qry->ResultsList)) /* could be emptied by code above */ { featEmbossQryClose(qry); ajFileClose(&qryd->libs); ajFileClose(&qryd->libr); } return retval; } ID featEmbossGcgAll TY static MO ajfeatdb LB ajaxdb XX DE Opens the first or next GCG file for further reading XX PN [1] PA u fttabin AjPFeattabin PD Feature table input. PX RT AjBool RD ajTrue on success. RX // static AjBool featEmbossGcgAll(AjPFeattabin fttabin) { AjPQuery qry; FeatPEmbossQry qryd; static ajint i = 0; ajuint iref; AjPStr name = NULL; AjBool ok = ajFalse; /* AjPStrTok handle = NULL; AjPStr wildname = NULL; AjBool found = ajFalse; */ qry = fttabin->Input->Query; qryd = qry->QryData; ajDebug("featEmbossGcgAll\n"); if(!qry->QryData) { ajDebug("featEmbossGcgAll initialising\n"); qry->QryData = AJNEW0(qryd); qryd = qry->QryData; i = -1; ajBtreeReadEntriesS(qry->DbAlias,qry->IndexDir, qry->Directory, &qryd->files, &qryd->reffiles, &qryd->refcount); fttabin->Input->Single = ajTrue; } qryd = qry->QryData; ajFilebuffDel(&fttabin->Input->Filebuff); fttabin->Input->Filebuff = ajFilebuffNewNofile(); if(!qryd->libs) { while(!ok && qryd->files[++i]) { ajStrAssignS(&name,qryd->files[i]); if(ajFilenameTestInclude(name, qry->Exclude, qry->Filename)) ok = ajTrue; } ajStrDel(&name); /* if(qry->Exclude) { ok = ajFalse; wildname = ajStrNew(); name = ajStrNew(); while(!ok) { ajStrAssignS(&name,qryd->files[i]); ajFilenameTrimPath(&name); handle = ajStrTokenNewC(qry->Exclude," \n"); found = ajFalse; while(ajStrTokenNextParseC(&handle," \n",&wildname)) if(ajStrMatchWildS(name,wildname)) { found = ajTrue; break; } ajStrTokenDel(&handle); if(!found) ok = ajTrue; else { ++i; if(!qryd->files[i]) ok = ajTrue; } } ajStrDel(&wildname); ajStrDel(&name); } */ if(!qryd->files[i]) { ajDebug("featEmbossGcgAll finished\n"); i=0; while(qryd->files[i]) { ajStrDel(&qryd->files[i]); if(qryd->reffiles) { for(iref=0; iref < qryd->refcount; iref++) ajStrDel(&qryd->reffiles[iref][i]); } ++i; } AJFREE(qryd->files); AJFREE(qryd->reffiles); AJFREE(qry->QryData); qry->QryData = NULL; return ajFalse; } qryd->libs = ajFileNewInNameS(qryd->files[i]); if(!qryd->libs) { ajDebug("featEmbossGcgAll: cannot open sequence file\n"); return ajFalse; } if(qryd->reffiles) qryd->libr = ajFileNewInNameS(qryd->reffiles[0][i]); if(!qryd->libr) { ajDebug("featEmbossGcgAll: cannot open reference file\n"); return ajFalse; } } featEmbossGcgLoadBuff(fttabin); if(!qry->CaseId) qry->QryDone = ajTrue; return ajTrue; } ID featEmbossGcgLoadBuff TY static MO ajfeatdb LB ajaxdb XX DE Copies text data to a buffered file, and skips sequence data. XX PN [1] PA u fttabin AjPFeattabin PD Feature table input object PX RT void RD RX // static void featEmbossGcgLoadBuff(AjPFeattabin fttabin) { AjPQuery qry; FeatPEmbossQry qryd; qry = fttabin->Input->Query; qryd = qry->QryData; ajDebug("featEmbossGcgLoadBuff\n"); if(!qry->QryData) ajFatal("featEmbossGcgLoadBuff Query Data not initialised"); /* copy all the ref data */ featEmbossGcgReadRef(fttabin); /* skip the sequence (do we care about the format?) */ featEmbossGcgReadSeq(fttabin); /* ajFilebuffTraceFull(fttabin->Input->Filebuff, 9999, 100); */ if(!qryd->libr) ajFileClose(&qryd->libs); return; } ID featEmbossGcgReadRef TY static MO ajfeatdb LB ajaxdb XX DE Copies text data to a buffered file for reading later XX PN [1] PA u fttabin AjPFeattabin PD Feature table input object PX RT AjBool RD ajTrue on success RX // static AjBool featEmbossGcgReadRef(AjPFeattabin fttabin) { AjPStr line = NULL; AjPQuery qry; FeatPEmbossQry qryd; ajlong rpos; AjPStr id = NULL; AjPStr idc = NULL; AjBool ispir = ajFalse; AjBool continued = ajFalse; AjBool testcontinue = ajFalse; char *p = NULL; qry = fttabin->Input->Query; qryd = qry->QryData; if(!featRegGcgRefId) featRegGcgRefId =ajRegCompC("^>...([^ \n]+)"); if(!featRegGcgSplit) featRegGcgSplit =ajRegCompC("_0+$"); if(!ajReadline(qryd->libr, &line)) /* end of file */ return ajFalse; if(ajStrGetCharFirst(line) != '>') /* not start of entry */ ajFatal("featGcgReadRef bad entry start:\n'%S'", line); if(ajStrGetCharPos(line, 3) == ';') /* PIR entry */ ispir = ajTrue; if(ispir) ajFilebuffLoadS(fttabin->Input->Filebuff, line); if(ajRegExec(featRegGcgRefId, line)) { continued = ajFalse; ajRegSubI(featRegGcgRefId, 1, &id); if(ajRegExec(featRegGcgSplit,id)) { continued = ajTrue; p = ajStrGetuniquePtr(&id); p = strrchr(p,(ajint)'_'); *(++p)='\0'; ajStrSetValid(&id); } } else { ajDebug("featEmbossGcgReadRef bad ID line\n'%S'\n", line); ajFatal("featEmbossGcgReadRef bad ID line\n'%S'\n", line); } if(!ajReadline(qryd->libr, &line)) /* blank desc line */ return ajFalse; if(ispir) ajFilebuffLoadS(fttabin->Input->Filebuff, line); rpos = ajFileResetPos(qryd->libr); while(ajReadline(qryd->libr, &line)) { /* end of file */ if(ajStrGetCharFirst(line) == '>') { /* start of next entry */ /* skip over split entries so it can be used for "all" */ if(continued) { testcontinue=ajTrue; ajRegExec(featRegGcgRefId, line); ajRegSubI(featRegGcgRefId, 1, &idc); if(!ajStrPrefixS(idc,id)) { ajFileSeek(qryd->libr, rpos, 0); ajStrDel(&line); ajStrDel(&id); ajStrDel(&idc); return ajTrue; } } else { ajFileSeek(qryd->libr, rpos, 0); ajStrDel(&line); ajStrDel(&id); ajStrDel(&idc); return ajTrue; } } rpos = ajFileResetPos(qryd->libr); if(!testcontinue) { ajStrExchangeCC(&line, ". .", ".."); ajFilebuffLoadS(fttabin->Input->Filebuff, line); } } /* at end of file */ ajFileClose(&qryd->libr); ajStrDel(&line); ajStrDel(&id); ajStrDel(&idc); return ajTrue; } ID featEmbossGcgReadSeq TY static MO ajfeatdb LB ajaxdb XX DE Skips sequence data. XX PN [1] PA u fttabin AjPFeattabin PD feature table input object PX RT AjBool RD ajTrue on success RX // static AjBool featEmbossGcgReadSeq(AjPFeattabin fttabin) { AjPStr line = NULL; AjPQuery qry; FeatPEmbossQry qryd; AjPStr gcgtype = NULL; AjPStr tmpstr = NULL; AjPStr dstr = NULL; AjPStr id = NULL; AjPStr idc = NULL; AjPStr contseq = NULL; ajint gcglen; ajint pos; ajint rblock; ajlong spos; AjBool ispir = ajFalse; char *p = NULL; AjBool continued = ajFalse; qry = fttabin->Input->Query; qryd = qry->QryData; if(!featRegGcgId) { featRegGcgId =ajRegCompC("^>...([^ ]+) +(Dummy Header|[^ ]+) +" "([^ ]+) +([^ ]+) +([0-9]+)"); featRegGcgId2=ajRegCompC("^>[PF]1;([^ ]+)"); } if(!featRegGcgSplit) featRegGcgSplit =ajRegCompC("_0+$"); ajDebug("featEmbossGcgReadSeq pos: %Ld\n", ajFileResetPos(qryd->libs)); if(!ajReadline(qryd->libs, &line)) /* end of file */ return ajFalse; ajDebug("test ID line\n'%S'\n", line); if(ajRegExec(featRegGcgId, line)) { continued = ajFalse; ajRegSubI(featRegGcgId, 3, &gcgtype); ajRegSubI(featRegGcgId, 5, &tmpstr); ajRegSubI(featRegGcgId, 1, &id); if(ajRegExec(featRegGcgSplit, id)) { continued = ajTrue; p = ajStrGetuniquePtr(&id); p = strrchr(p,(ajint)'_'); *(++p)='\0'; ajStrSetValid(&id); if(!contseq) contseq = ajStrNew(); if(!dstr) dstr = ajStrNew(); } ajStrToInt(tmpstr, &gcglen); } else if(ajRegExec(featRegGcgId2, line)) { ajStrAssignC(&gcgtype, "ASCII"); ajRegSubI(featRegGcgId, 1, &tmpstr); ispir = ajTrue; } else { ajDebug("featEmbossGcgReadSeq bad ID line\n'%S'\n", line); ajFatal("featEmbossGcgReadSeq bad ID line\n'%S'\n", line); return ajFalse; } if(!ajReadline(qryd->libs, &line)) /* desc line */ return ajFalse; /* ** need to pick up the length and type, and read to the end of sequence ** see fasta code to get a real sequence for this ** Also need to handle split entries and go find the rest */ if(ispir) { spos = ajFileResetPos(qryd->libs); while(ajReadline(qryd->libs, &line)) { /* end of file */ if(ajStrGetCharFirst(line) == '>') { /* start of next entry */ ajFileSeek(qryd->libs, spos, 0); break; } spos = ajFileResetPos(qryd->libs); ajFilebuffLoadS(fttabin->Input->Filebuff, line); } } else { ajStrSetRes(&contseq, gcglen+3); rblock = gcglen; if(ajStrGetCharFirst(gcgtype) == '2') rblock = (rblock+3)/4; if(!ajReadbinBinary(qryd->libs, rblock, 1, ajStrGetuniquePtr(&contseq))) ajFatal("error reading file %F", qryd->libs); /* convert 2bit to ascii */ if(ajStrGetCharFirst(gcgtype) == '2') featGcgBinDecode(&contseq, gcglen); else if(ajStrGetCharFirst(gcgtype) == 'A') { /* are seq chars OK? */ ajStrSetValidLen(&contseq, gcglen); } else { ajRegSubI(featRegGcgId, 1, &tmpstr); ajFatal("Unknown GCG entry type '%S', entry name '%S'", gcgtype, tmpstr); } if(!ajReadline(qryd->libs, &line)) /* newline at end */ ajFatal("error reading file %F", qryd->libs); if(continued) { spos = ajFileResetPos(qryd->libs); while(ajReadline(qryd->libs,&line)) { ajRegExec(featRegGcgId, line); ajRegSubI(featRegGcgId, 5, &tmpstr); ajRegSubI(featRegGcgId, 1, &idc); if(!ajStrPrefixS(idc,id)) { ajFileSeek(qryd->libs, spos, 0); break; } ajStrToInt(tmpstr, &gcglen); if(!ajReadline(qryd->libs, &dstr)) /* desc line */ return ajFalse; ajStrSetRes(&contseq, gcglen+3); rblock = gcglen; if(ajStrGetCharFirst(gcgtype) == '2') rblock = (rblock+3)/4; if(!ajReadbinBinary(qryd->libs, rblock, 1, ajStrGetuniquePtr(&contseq))) ajFatal("error reading file %F", qryd->libs); /* convert 2bit to ascii */ if(ajStrGetCharFirst(gcgtype) == '2') featGcgBinDecode(&contseq, gcglen); else if(ajStrGetCharFirst(gcgtype) == 'A') { /* are seq chars OK? */ ajStrSetValidLen(&contseq, gcglen); } else { ajRegSubI(featRegGcgId, 1, &tmpstr); ajFatal("Unknown GCG entry: name '%S'", tmpstr); } if(!ajReadline(qryd->libs, &line)) /* newline at end */ ajFatal("error reading file %F", qryd->libs); if(!featRegGcgCont) featRegGcgCont = ajRegCompC("^([^ ]+) +([^ ]+) +([^ ]+) +" "([^ ]+) +([^ ]+) +([^ ]+) " "+([^ ]+) +" "([^ ]+) +([0-9]+)"); ajRegExec(featRegGcgCont, dstr); ajRegSubI(featRegGcgCont, 9, &tmpstr); ajStrToInt(tmpstr, &pos); /*seqin->Inseq->Len = pos-1;*/ /*ajStrAppendS(&seqin->Inseq,contseq);*/ spos = ajFileResetPos(qryd->libs); } } } ajStrDel(&gcgtype); ajStrDel(&line); ajStrDel(&tmpstr); ajStrDel(&dstr); ajStrDel(&id); ajStrDel(&idc); ajStrDel(&contseq); return ajTrue; } ID featCdQryReuse TY static MO ajfeatdb LB ajaxdb XX DE Tests whether Cd index query data can be reused or whether we are finished. DE DE Clears qryData structure when finished. XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if we can continue, RD ajFalse if all is done. RX // static AjBool featCdQryReuse(AjPQuery qry) { FeatPCdQry qryd; qryd = qry->QryData; if(!qry || !qryd) return ajFalse; /* ajDebug("qry->ResultsList %x\n",qry->ResultsList);*/ if(!qry->ResultsList) { ajDebug("query data all finished\n"); AJFREE(qry->QryData); qryd = NULL; return ajFalse; } else { ajDebug("reusing data from previous call %x\n", qry->QryData); ajDebug("listlen %Lu\n", ajListGetLength(qry->ResultsList)); ajDebug("divfile '%S'\n", qryd->divfile); ajDebug("idxfile '%S'\n", qryd->idxfile); ajDebug("datfile '%S'\n", qryd->datfile); ajDebug("seqfile '%S'\n", qryd->seqfile); ajDebug("name '%s'\n", qryd->name); ajDebug("nameSize %d\n", qryd->nameSize); ajDebug("div %d\n", qryd->div); ajDebug("maxdiv %d\n", qryd->maxdiv); /*ajListTrace(qry->ResultsList);*/ } return ajTrue; } ID featCdQryOpen TY static MO ajfeatdb LB ajaxdb XX DE Opens everything for a new CD query XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if we can continue, RD ajFalse if all is done. RX // static AjBool featCdQryOpen(AjPQuery qry) { FeatPCdQry qryd; ajuint i; short j; static char *name; AjPStr fullName = NULL; if(!ajStrGetLen(qry->IndexDir)) { ajDebug("no indexdir defined for database '%S'\n", qry->DbName); ajErr("no indexdir defined for database '%S'", qry->DbName); return ajFalse; } ajDebug("directory '%S' fields: %Lu hasacc:%B\n", qry->IndexDir, ajListGetLength(qry->QueryFields), qry->HasAcc); qry->QryData = AJNEW0(qryd); AJNEW0(qryd->idxLine); AJNEW0(qryd->trgLine); qryd->dfp = featCdFileOpen(qry->IndexDir, "division.lkp", &qryd->divfile); if(!qryd->dfp) { ajWarn("Cannot open division file '%S' for database '%S'", qryd->divfile, qry->DbName); return ajFalse; } qryd->nameSize = qryd->dfp->RecSize - 2; qryd->maxdiv = qryd->dfp->NRecords; ajDebug("nameSize: %d maxdiv: %d\n", qryd->nameSize, qryd->maxdiv); qryd->name = ajCharNewRes(qryd->nameSize+1); name = ajCharNewRes(qryd->nameSize+1); AJCNEW0(qryd->Skip, qryd->maxdiv); featCdFileSeek(qryd->dfp, 0); for(i=0; i < qryd->maxdiv; i++) { ajReadbinInt2(qryd->dfp->File, &j); ajReadbinCharTrim(qryd->dfp->File, qryd->nameSize, name); ajStrAssignC(&fullName, name); ajFilenameReplacePathS(&fullName, qry->Directory); if(!ajFilenameTestInclude(fullName, qry->Exclude, qry->Filename)) qryd->Skip[i] = ajTrue; } qryd->ifp = featCdFileOpen(qry->IndexDir, "entrynam.idx", &qryd->idxfile); if(!qryd->ifp) { ajErr("Cannot open index file '%S' for database '%S'", qryd->idxfile, qry->DbName); return ajFalse; } ajStrDel(&fullName); ajCharDel(&name); return ajTrue; } ID featCdQryEntry TY static MO ajfeatdb LB ajaxdb XX DE Queries for a single entry in an EMBLCD index XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if we can continue, RD ajFalse if all is done. RX // static AjBool featCdQryEntry(AjPQuery qry) { FeatPCdEntry entry = NULL; ajint ipos = -1; ajint trghit; FeatPCdQry qryd; const AjPList fdlist; AjIList iter; AjPQueryField fd; AjPStr qrystr = NULL; ajuint i; ajuint ii; ajint j; const char* embossfields[] = { "id", "acc", "sv", "org", "key", "des", "gi", NULL }; const char* emblcdfields[] = { NULL, "acnum", "seqvn", "taxon", "keyword", "des", "gi", NULL }; fdlist = ajQueryGetallFields(qry); ajQueryGetQuery(qry, &qrystr); ajDebug("featCdQryEntry %S hasacc:%B\n", qrystr , qry->HasAcc); ajStrDel(&qrystr); qryd = qry->QryData; iter= ajListIterNewread(fdlist); while(!ajListIterDone(iter)) { fd = ajListIterGet(iter); ajDebug("query link: %u field '%S' wild '%S'\n", fd->Link, fd->Field, fd->Wildquery); if((fd->Link == AJQLINK_ELSE) && ajListGetLength(qry->ResultsList)) continue; for(i=0; embossfields[i]; i++) { ajDebug("test field[%u] '%S' = '%s'\n", i, fd->Field, emblcdfields[i]); if(ajStrMatchC(fd->Field, embossfields[i])) { ajDebug("match field[%u] '%S' = '%s'\n", i, fd->Field, emblcdfields[i]); if(!emblcdfields[i]) /* ID index */ { ipos = featCdIdxSearch(qryd->idxLine, fd->Wildquery, qryd->ifp); if(ipos >= 0) { if(!qryd->Skip[qryd->idxLine->DivCode-1]) { AJNEW0(entry); entry->div = qryd->idxLine->DivCode; entry->annoff = qryd->idxLine->AnnOffset; entry->seqoff = qryd->idxLine->SeqOffset; ajListPushAppend(qry->ResultsList, (void*)entry); } else ajDebug("SKIP: '%S' [file %d]\n", fd->Wildquery, qryd->idxLine->DivCode); } } else /* target/hit index */ { if(featCdTrgOpen(qry->IndexDir, emblcdfields[i], &qryd->trgfp, &qryd->hitfp)) { trghit = featCdTrgSearch(qryd->trgLine, fd->Wildquery, qryd->trgfp); if(trghit >= 0) { featCdFileSeek(qryd->hitfp, qryd->trgLine->FirstHit-1); ajDebug("%s First: %d Count: %d\n", emblcdfields[i], qryd->trgLine->FirstHit, qryd->trgLine->NHits); ipos = qryd->trgLine->FirstHit; for(ii = 0; ii < qryd->trgLine->NHits; ii++) { ajReadbinInt(qryd->hitfp->File, &j); j--; ajDebug("hitlist[%u] entry = %d\n", ii, j); featCdIdxLine(qryd->idxLine, j, qryd->ifp); if(!qryd->Skip[qryd->idxLine->DivCode-1]) { AJNEW0(entry); entry->div = qryd->idxLine->DivCode; entry->annoff = qryd->idxLine->AnnOffset; entry->seqoff = qryd->idxLine->SeqOffset; ajListPushAppend(qry->ResultsList, (void*)entry); } else ajDebug("SKIP: %s '%S' [file %d]\n", emblcdfields[i], fd->Wildquery, qryd->idxLine->DivCode); } } featCdTrgClose(&qryd->trgfp, &qryd->hitfp); ajStrDel(&qryd->trgLine->Target); } } break; } } if(!embossfields[i]) { if(featCdTrgOpen(qry->IndexDir, MAJSTRGETPTR(fd->Field), &qryd->trgfp, &qryd->hitfp)) { trghit = featCdTrgSearch(qryd->trgLine, fd->Wildquery, qryd->trgfp); if(trghit >= 0) { featCdFileSeek(qryd->hitfp, qryd->trgLine->FirstHit-1); ajDebug("%s First: %d Count: %d\n", fd->Field, qryd->trgLine->FirstHit, qryd->trgLine->NHits); ipos = qryd->trgLine->FirstHit; for(ii = 0; ii < qryd->trgLine->NHits; ii++) { ajReadbinInt(qryd->hitfp->File, &j); j--; ajDebug("hitlist[%u] entry = %d\n", ii, j); featCdIdxLine(qryd->idxLine, j, qryd->ifp); if(!qryd->Skip[qryd->idxLine->DivCode-1]) { AJNEW0(entry); entry->div = qryd->idxLine->DivCode; entry->annoff = qryd->idxLine->AnnOffset; entry->seqoff = qryd->idxLine->SeqOffset; ajListPushAppend(qry->ResultsList, (void*)entry); } else ajDebug("SKIP: %S '%S' [file %d]\n", fd->Field, fd->Wildquery, qryd->idxLine->DivCode); } } featCdTrgClose(&qryd->trgfp, &qryd->hitfp); ajStrDel(&qryd->trgLine->Target); } } } ajListIterDel(&iter); if(ipos < 0) return ajFalse; if(!ajListGetLength(qry->ResultsList)) return ajFalse; if(!qry->CaseId) qry->QryDone = ajTrue; return ajTrue; } ID featCdQryQuery TY static MO ajfeatdb LB ajaxdb XX DE Queries for one or more entries in an EMBLCD index XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if we can continue, RD ajFalse if all is done. RX // static AjBool featCdQryQuery(AjPQuery qry) { AjIList iter = NULL; AjPQueryField field = NULL; FeatPCdEntry newhit; FeatPCdEntry *allhits = NULL; ajulong** keys = NULL; AjPTable newtable = NULL; ajuint i; ajuint lasthits = 0; ajuint fdhits = 0; ajulong *ikey = NULL; ajuint ishift = sizeof(ajulong)/2; if(!qry->CaseId) qry->QryDone = ajTrue; ajTableSettypeUlong(qry->ResultsTable); ajTableSetDestroyboth(qry->ResultsTable); iter = ajListIterNewread(qry->QueryFields); while(!ajListIterDone(iter)) { field = ajListIterGet(iter); if((field->Link == AJQLINK_ELSE) && (lasthits > 0)) { ajDebug("ELSE: lasthits:%u skip\n", lasthits); continue; } if(ajStrMatchC(field->Field, "id")) featCdIdxQuery(qry, field->Wildquery); else featCdTrgQuery(qry, field->Field, field->Wildquery); fdhits = (ajuint) ajListGetLength(qry->ResultsList); ajDebug("featCdQryQuery hits: %u link: %u\n", fdhits, field->Link); switch(field->Link) { case AJQLINK_INIT: while(ajListPop(qry->ResultsList, (void**)&newhit)) { AJNEW(ikey); *ikey = (((ajulong)newhit->div) << ishift) + (ajulong)newhit->annoff; ajTablePutClean(qry->ResultsTable, ikey, newhit, &ajMemFree, &ajMemFree); ikey = NULL; } break; case AJQLINK_OR: while(ajListPop(qry->ResultsList, (void**)&newhit)) { AJNEW(ikey); *ikey = (((ajulong)newhit->div) << ishift) + (ajulong)newhit->annoff; ajTablePutClean(qry->ResultsTable, ikey, newhit, &ajMemFree, &ajMemFree); ikey = NULL; } break; case AJQLINK_AND: newtable = ajTableulongNew(fdhits); ajTableSetDestroyboth(newtable); while(ajListPop(qry->ResultsList, (void**)&newhit)) { AJNEW(ikey); *ikey = (((ajulong)newhit->div) << ishift) + (ajulong)newhit->annoff; ajTablePutClean(newtable, ikey, newhit, &ajMemFree, &ajMemFree); ikey = NULL; } ajTableMergeAnd(qry->ResultsTable, newtable); ajTableDelValdel(&newtable, &ajMemFree); break; case AJQLINK_EOR: case AJQLINK_ELSE: newtable = ajTableulongNew(fdhits); ajTableSetDestroyboth(newtable); while(ajListPop(qry->ResultsList, (void**)&newhit)) { AJNEW(ikey); *ikey = (((ajulong)newhit->div) << ishift) + newhit->annoff; ajTablePutClean(newtable, ikey, newhit, &ajMemFree, &ajMemFree); ikey = NULL; } ajTableMergeEor(qry->ResultsTable, newtable); ajTableDelValdel(&newtable, &ajMemFree); break; case AJQLINK_NOT: newtable = ajTableulongNew(fdhits); ajTableSetDestroyboth(newtable); while(ajListPop(qry->ResultsList, (void**)&newhit)) { AJNEW(ikey); *ikey = (((ajulong)newhit->div) << ishift) + newhit->annoff; ajTablePutClean(newtable, ikey, newhit, &ajMemFree, &ajMemFree); ikey = NULL; } ajTableMergeNot(qry->ResultsTable, newtable); ajTableDelValdel(&newtable, &ajMemFree); break; default: ajErr("Unexpected query link operator number '%u'", field->Link); break; } lasthits = fdhits; } ajListIterDel(&iter); ajTableToarrayKeysValues(qry->ResultsTable, (void***) &keys, (void***)&allhits); for(i=0; allhits[i]; i++) { AJFREE(keys[i]); ajListPushAppend(qry->ResultsList, (void*) allhits[i]); } AJFREE(keys); ajDebug("ajListSortUnique len:%Lu\n", ajListGetLength(qry->ResultsList)); ajListSortUnique(qry->ResultsList, &featCdEntryCmp, &featCdEntryDel); AJFREE(allhits); ajDebug("featCdQryQuery clear results table\n"); ajTableClear(qry->ResultsTable); if(ajListGetLength(qry->ResultsList)) return ajTrue; return ajFalse; } ID featCdEntryCmp TY static MO ajfeatdb LB ajaxdb XX DE Compares two FeatPEntry objects XX PN [1] PA r pa const void* PD FeatPEntry object PX PN [2] PA r pb const void* PD FeatPEntry object PX RT int RD -1 if first entry should sort before second, +1 if the RD second entry should sort first. 0 if they are identical RX // static int featCdEntryCmp(const void* pa, const void* pb) { const FeatPCdEntry a; const FeatPCdEntry b; a = *(FeatPCdEntry const *) pa; b = *(FeatPCdEntry const *) pb; /* ajDebug("featCdEntryCmp %x %d %d : %x %d %d\n", a, a->div, a->annoff, b, b->div, b->annoff); */ if(a->div != b->div) return (a->div - b->div); return (a->annoff - b->annoff); } ID featCdEntryDel TY static MO ajfeatdb LB ajaxdb XX DE Deletes a FeatPCdEntry object XX PN [1] PA r pentry void** PD Address of a FeatPCdEntry object PX PN [2] PA r cl void* PD Standard unused argument, usually NULL. PX RT void RD RX // static void featCdEntryDel(void** pentry, void* cl) { (void) cl; AJFREE(*pentry); return; } ID featCdQryNext TY static MO ajfeatdb LB ajaxdb XX DE Processes the next query for an EMBLCD index XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if successful RX // static AjBool featCdQryNext(AjPQuery qry) { FeatPCdEntry entry; FeatPCdQry qryd; void* item; qryd = qry->QryData; if(!ajListGetLength(qry->ResultsList)) return ajFalse; ajDebug("qry->ResultsList (b) length %Lu\n", ajListGetLength(qry->ResultsList)); /*ajListTrace(qry->ResultsList);*/ ajListPop(qry->ResultsList, &item); entry = (FeatPCdEntry) item; /* ajDebug("entry: %x div: %d (%d) ann: %d seq: %d\n", entry, entry->div, qryd->div, entry->annoff, entry->seqoff); */ qryd->idnum = entry->annoff - 1; /* ajDebug("idnum: %d\n", qryd->idnum); */ qryd->Samefile = ajTrue; if(entry->div != qryd->div) { qryd->Samefile = ajFalse; qryd->div = entry->div; /*ajDebug("div: %d\n", qryd->div);*/ if(!featCdQryFile(qry)) return ajFalse; } ajDebug("Offsets(cd) %d %d\n", entry->annoff, entry->seqoff); ajDebug("libr %x\n", qryd->libr); ajDebug("libr %F\n", qryd->libr); ajFileSeek(qryd->libr, entry->annoff,0); if(qryd->libs) ajFileSeek(qryd->libs, entry->seqoff,0); AJFREE(entry); if(!qry->CaseId) qry->QryDone = ajTrue; return ajTrue; } ID featCdQryClose TY static MO ajfeatdb LB ajaxdb XX DE Closes query data for an EMBLCD index XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if all is done RX // static AjBool featCdQryClose(AjPQuery qry) { FeatPCdQry qryd = NULL; ajDebug("featCdQryClose clean up qryd\n"); qryd = qry->QryData; if(!qryd) return ajTrue; ajCharDel(&qryd->name); ajStrDel(&qryd->divfile); ajStrDel(&qryd->idxfile); ajStrDel(&qryd->datfile); ajStrDel(&qryd->seqfile); ajStrDel(&qryd->srcfile); ajStrDel(&qryd->tblfile); featCdIdxDel(&qryd->idxLine); featCdTrgDel(&qryd->trgLine); featCdFileClose(&qryd->ifp); featCdFileClose(&qryd->dfp); /* defined in a buffer, cleared there */ /* ajFileClose(&qryd->libr); ajFileClose(&qryd->libs); */ qryd->libr=0; qryd->libs=0; AJFREE(qryd->trgLine); AJFREE(qryd->idxLine); AJFREE(qryd->Skip); /* keep QryData for use at top of loop */ return ajTrue; } ID featEmbossQryReuse TY static MO ajfeatdb LB ajaxdb XX DE Tests whether the B+tree index query data can be reused or it's finished. DE DE Clears qryData structure when finished. XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if reusable, RD ajFalse if finished. RX // static AjBool featEmbossQryReuse(AjPQuery qry) { FeatPEmbossQry qryd; qryd = qry->QryData; if(!qry || !qryd) return ajFalse; if(!qry->ResultsList) { ajDebug("featEmbossQryReuse: query data all finished\n"); return ajFalse; } else { ajDebug("featEmbossQryReuse: reusing data from previous call %x\n", qry->QryData); /*ajListTrace(qry->ResultsList);*/ } qryd->nentries = -1; return ajTrue; } ID featEmbossQryOpen TY static MO ajfeatdb LB ajaxdb XX DE Open caches (etc) for B+tree search XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if we can continue, RD ajFalse if all is done. RX // static AjBool featEmbossQryOpen(AjPQuery qry) { FeatPEmbossQry qryd; ajint i; AjPStr name = NULL; AjIList iter = NULL; AjPQueryField field = NULL; AjPBtcache cache = NULL; qry->QryData = AJNEW0(qryd); qryd = qry->QryData; qryd->div = -1; qryd->nentries = -1; qryd->Caches = ajListNew(); if(!ajStrGetLen(qry->IndexDir)) { ajDebug("no indexdir defined for database '%S'\n", qry->DbName); ajErr("no indexdir defined for database '%S'", qry->DbName); return ajFalse; } if(!featEmbossOpenCache(qry,"id",&qryd->idcache)) return ajFalse; iter = ajListIterNewread(qry->QueryFields); while(!ajListIterDone(iter)) { field = ajListIterGet(iter); ajStrFmtLower(&field->Wildquery); if(!featEmbossOpenCache(qry, MAJSTRGETPTR(field->Field), &cache)) return ajFalse; ajListPushAppend(qryd->Caches, cache); cache = NULL; } ajListIterDel(&iter); ajDebug("directory '%S'fields: %Lu hasacc:%B\n", qry->IndexDir, ajListGetLength(qry->QueryFields), qry->HasAcc); if(ajStrGetLen(qry->Exclude) && qryd->nentries >= 0) { AJCNEW0(qryd->Skip,qryd->nentries); name = ajStrNew(); for(i=0; i < qryd->nentries; ++i) { ajStrAssignS(&name,qryd->files[i]); if(!ajFilenameTestInclude(name, qry->Exclude, qry->Filename)) qryd->Skip[i] = ajTrue; } ajStrDel(&name); } return ajTrue; } ID featEmbossOpenCache TY static MO ajfeatdb LB ajaxdb XX DE Create primary B+tree index cache XX PN [1] PA u qry AjPQuery PD Query data PX PN [2] PA r ext const char* PD Index file extension PX PN [3] PA w cache AjPBtcache* PD cache PX RT AjBool RD True on success RX // static AjBool featEmbossOpenCache(AjPQuery qry, const char *ext, AjPBtcache *cache) { FeatPEmbossQry qryd; AjPStr indexextname = NULL; qryd = qry->QryData; indexextname = ajStrNewS(ajBtreeFieldGetExtensionC(ext)); *cache = ajBtreeCacheNewReadS(qry->DbAlias,indexextname, qry->IndexDir); ajStrDel(&indexextname); if(!*cache) { qryd->nentries = -1; return ajFalse; } if(qryd->nentries == -1) qryd->nentries = ajBtreeReadEntriesS(qry->DbAlias, qry->IndexDir, qry->Directory, &qryd->files, &qryd->reffiles, &qryd->refcount); return ajTrue; } ID featEmbossQryEntry TY static MO ajfeatdb LB ajaxdb XX DE Queries for a single entry in a B+tree index XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if can continue, RD ajFalse if all is done. RX // static AjBool featEmbossQryEntry(AjPQuery qry) { AjPBtHitref newhit = NULL; FeatPEmbossQry qryd; const AjPList fdlist; const AjPList cachelist; AjIList iter; AjIList icache; AjPBtcache cache; AjPQueryField fd; AjPBtHitref *allhits = NULL; ajuint i; ajDebug("featEmbossQryEntry fields: %Lu hasacc:%B\n", ajListGetLength(qry->QueryFields), qry->HasAcc); qryd = qry->QryData; fdlist = ajQueryGetallFields(qry); cachelist = qryd->Caches; iter= ajListIterNewread(fdlist); icache = ajListIterNewread(cachelist); while(!ajListIterDone(iter)) { fd = ajListIterGet(iter); cache = ajListIterGet(icache); ajDebug("qry type:%d field '%S' wild '%S'\n", fd->Link, fd->Field, fd->Wildquery); if((fd->Link == AJQLINK_ELSE) && ajListGetLength(qry->ResultsList)) continue; if(!ajBtreeCacheIsSecondary(cache)) { ajBtreeIdentFetchHitref(cache,fd->Wildquery, qry->ResultsList); } } ajListIterDel(&iter); ajListIterDel(&icache); if(ajStrGetLen(qry->Organisms)) { ajTableSetDestroy(qry->ResultsTable, NULL, &ajBtreeHitrefDelVoid); ajTableSettypeUser(qry->ResultsTable, &ajBtreeHitrefCmp, &ajBtreeHitrefHash); while(ajListPop(qry->ResultsList, (void**)&newhit)) ajTablePutClean(qry->ResultsTable, newhit, newhit, NULL, &ajBtreeHitrefDelVoid); featEmbossQryOrganisms(qry); ajTableToarrayValues(qry->ResultsTable, (void***)&allhits); for(i=0; allhits[i]; i++) ajListPushAppend(qry->ResultsList, (void*) allhits[i]); } if(!ajListGetLength(qry->ResultsList)) return ajFalse; if(!qry->CaseId) qry->QryDone = ajTrue; return ajTrue; } ID featEmbossQryNext TY static MO ajfeatdb LB ajaxdb XX DE Processes the next query for a B+tree index XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if successful RX // static AjBool featEmbossQryNext(AjPQuery qry) { AjPBtHitref entry; FeatPEmbossQry qryd; void* item; AjBool ok = ajFalse; qryd = qry->QryData; if(!ajListGetLength(qry->ResultsList)) return ajFalse; /*ajListTrace(qry->ResultsList);*/ if(!qryd->Skip) { ajListPop(qry->ResultsList, &item); entry = (AjPBtHitref) item; } else { ok = ajFalse; while(!ok) { ajListPop(qry->ResultsList, &item); entry = (AjPBtHitref) item; if(!qryd->Skip[entry->dbno]) ok = ajTrue; else { ajBtreeHitrefDel(&entry); if(!ajListGetLength(qry->ResultsList)) return ajFalse; } } } qryd->Samefile = ajTrue; if(entry->dbno != qryd->div) { qryd->Samefile = ajFalse; qryd->div = entry->dbno; ajFileClose(&qryd->libs); if(qryd->reffiles) ajFileClose(&qryd->libr); } if(!qryd->libs) { qryd->libs = ajFileNewInNameS(qryd->files[entry->dbno]); if(!qryd->libs) { ajBtreeHitrefDel(&entry); return ajFalse; } } if(qryd->reffiles && !qryd->libr) { ajFileClose(&qryd->libr); qryd->libr = ajFileNewInNameS(qryd->reffiles[0][entry->dbno]); if(!qryd->libr) { ajBtreeHitrefDel(&entry); return ajFalse; } } ajFileSeek(qryd->libs, (ajlong) entry->offset, 0); if(qryd->reffiles) ajFileSeek(qryd->libr, (ajlong) entry->refoffset, 0); ajBtreeHitrefDel(&entry); if(!qry->CaseId) qry->QryDone = ajTrue; return ajTrue; } ID featEmbossQryClose TY static MO ajfeatdb LB ajaxdb XX DE Closes query data for a B+tree index XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if we can continue, RD ajFalse if all is done. RX // static AjBool featEmbossQryClose(AjPQuery qry) { FeatPEmbossQry qryd; void* item; ajint i; ajuint iref; if(!qry) return ajFalse; if(!qry->QryData) return ajFalse; ajDebug("featEmbossQryClose clean up qryd\n"); qryd = qry->QryData; while(ajListGetLength(qryd->Caches)) { ajListPop(qryd->Caches, &item); ajBtreeCacheDel((AjPBtcache*) &item); } ajListFree(&qryd->Caches); ajBtreeCacheDel(&qryd->idcache); ajListFree(&qry->ResultsList); if(qryd->Skip) { AJFREE(qryd->Skip); qryd->Skip = NULL; } if(qryd->files) { i = 0; while(qryd->files[i]) { ajStrDel(&qryd->files[i]); if(qryd->reffiles) { for(iref=0; iref < qryd->refcount; iref++) ajStrDel(&qryd->reffiles[iref][i]); } ++i; } AJFREE(qryd->files); } if(qryd->reffiles) { for(iref=0; iref < qryd->refcount; iref++) AJFREE(qryd->reffiles[iref]); AJFREE(qryd->reffiles); } qryd->files = NULL; qryd->reffiles = NULL; /* keep QryData for use at top of loop */ return ajTrue; } ID featEmbossQryOrganisms TY static MO ajfeatdb LB ajaxdb XX DE Restricts results to matches to organism(s) in database XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue on success. RX // static AjBool featEmbossQryOrganisms(AjPQuery qry) { FeatPEmbossQry qryd; AjPBtcache orgcache; AjPStr orgqry = NULL; AjPStrTok orghandle = NULL; AjPTable orgtable = NULL; AjPList orglist = NULL; AjPBtHitref newhit; ajulong fdhits = 0UL; if(!ajStrGetLen(qry->Organisms)) return ajTrue; qryd = qry->QryData; featEmbossOpenCache(qry, "org", &orgcache); orglist = ajListNew(); orghandle = ajStrTokenNewC(qry->Organisms, "\t,;|"); while(ajStrTokenNextParse(&orghandle, &orgqry)) { if(ajBtreeCacheIsSecondary(orgcache)) { if(!qry->Wild) { ajBtreeKeyFetchHitref(orgcache,qryd->idcache, orgqry, orglist); } else { ajBtreeKeyFetchwildHitref(orgcache, qryd->idcache, orgqry, orglist); } } else { ajBtreeIdentFetchwildHitref(orgcache, orgqry, orglist); } fdhits += ajListGetLength(orglist); ajDebug("Organisms list orgqry '%S' list '%Lu'", orgqry, fdhits); } orgtable = ajTableNewFunctionLen(fdhits, &ajBtreeHitrefCmp, &ajBtreeHitrefHash, NULL, &ajBtreeHitrefDelVoid); while(ajListPop(orglist, (void**)&newhit)) ajTablePutClean(orgtable, newhit, newhit, NULL, &ajBtreeHitrefDelVoid); ajStrTokenDel(&orghandle); ajTableMergeAnd(qry->ResultsTable, orgtable); ajListFree(&orglist); ajBtreeCacheDel(&orgcache); ajTableFree(&orgtable); ajStrDel(&orgqry); return ajTrue; } ID featEmbossQryQuery TY static MO ajfeatdb LB ajaxdb XX DE Queries for one or more entries in an EMBOSS B+tree index XX PN [1] PA u qry AjPQuery PD Query data PX RT AjBool RD ajTrue if we can continue, RD ajFalse if all is done. RX // static AjBool featEmbossQryQuery(AjPQuery qry) { FeatPEmbossQry qryd; const AjPList fdlist; const AjPList cachelist; AjIList iter; AjIList icache; AjPBtcache cache; AjPQueryField fd; AjPBtHitref newhit; AjPBtHitref *allhits = NULL; AjPTable newtable = NULL; ajuint i; ajulong lasthits = 0UL; ajulong fdhits = 0UL; if(!qry->CaseId) qry->QryDone = ajTrue; qryd = qry->QryData; cachelist = qryd->Caches; ajTableSetDestroy(qry->ResultsTable, NULL, &ajBtreeHitrefDelVoid); ajTableSettypeUser(qry->ResultsTable, &ajBtreeHitrefCmp, &ajBtreeHitrefHash); fdlist = ajQueryGetallFields(qry); iter = ajListIterNewread(fdlist); icache = ajListIterNewread(cachelist); while(!ajListIterDone(iter)) { fd = ajListIterGet(iter); cache = ajListIterGet(icache); ajDebug("field '%S' query: '%S'\n", fd->Field, fd->Wildquery); if((fd->Link == AJQLINK_ELSE) && (lasthits > 0UL)) { continue; } /* is this a primary or secondary key (check the cache)? */ if(ajBtreeCacheIsSecondary(cache)) { if(!qry->Wild) { ajBtreeKeyFetchHitref(cache, qryd->idcache, fd->Wildquery, qry->ResultsList); } else { ajBtreeKeyFetchwildHitref(cache, qryd->idcache, fd->Wildquery, qry->ResultsList); } } else { ajBtreeIdentFetchwildHitref(cache,fd->Wildquery,qry->ResultsList); ajDebug("ajBtreeIdentFetchwild results:%Lu\n", ajListGetLength(qry->ResultsList)); } fdhits = ajListGetLength(qry->ResultsList); switch(fd->Link) { case AJQLINK_INIT: while(ajListPop(qry->ResultsList, (void**)&newhit)) ajTablePutClean(qry->ResultsTable, newhit, newhit, NULL, &ajBtreeHitrefDelVoid); break; case AJQLINK_OR: newtable = ajTableNewFunctionLen(fdhits, &ajBtreeHitrefCmp, &ajBtreeHitrefHash, NULL, &ajBtreeHitrefDelVoid); while(ajListPop(qry->ResultsList, (void**)&newhit)) ajTablePutClean(newtable, newhit, newhit, NULL, &ajBtreeHitrefDelVoid); ajTableMergeOr(qry->ResultsTable, newtable); ajTableDel(&newtable); break; case AJQLINK_AND: newtable = ajTableNewFunctionLen(fdhits, &ajBtreeHitrefCmp, &ajBtreeHitrefHash, NULL, &ajBtreeHitrefDelVoid); while(ajListPop(qry->ResultsList, (void**)&newhit)) ajTablePutClean(newtable, newhit, newhit, NULL, &ajBtreeHitrefDelVoid); ajTableMergeAnd(qry->ResultsTable, newtable); ajTableDel(&newtable); break; case AJQLINK_EOR: case AJQLINK_ELSE: newtable = ajTableNewFunctionLen(fdhits, &ajBtreeHitrefCmp, &ajBtreeHitrefHash, NULL, ajBtreeHitrefDelVoid); while(ajListPop(qry->ResultsList, (void**)&newhit)) ajTablePutClean(newtable, newhit, newhit, NULL, &ajBtreeHitrefDelVoid); ajTableMergeEor(qry->ResultsTable, newtable); ajTableDel(&newtable); break; case AJQLINK_NOT: newtable = ajTableNewFunctionLen(fdhits, &ajBtreeHitrefCmp, &ajBtreeHitrefHash, NULL, &ajBtreeHitrefDelVoid); while(ajListPop(qry->ResultsList, (void**)&newhit)) ajTablePutClean(newtable, newhit, newhit, NULL, &ajBtreeHitrefDelVoid); ajTableMergeNot(qry->ResultsTable, newtable); ajTableDel(&newtable); break; default: ajErr("Unexpected query link operator number '%u'", fd->Link); break; } lasthits = fdhits; } ajListIterDel(&iter); ajListIterDel(&icache); if(ajStrGetLen(qry->Organisms)) featEmbossQryOrganisms(qry); ajTableToarrayValues(qry->ResultsTable, (void***)&allhits); for(i=0; allhits[i]; i++) ajListPushAppend(qry->ResultsList, (void*) allhits[i]); AJFREE(allhits); ajTableClear(qry->ResultsTable); if(ajListGetLength(qry->ResultsList)) return ajTrue; return ajFalse; } ID featAccessChado TY static MO ajfeatdb LB ajaxdb XX DE Reads features from CHADO databases XX PN [1] PA u fttabin AjPFeattabin PD Feature input. PX RT AjBool RD ajTrue on success. RX // static AjBool featAccessChado(AjPFeattabin fttabin) { AjPQuery qry = NULL; AjPStr seqid = NULL; AjPStr filterseqregions = NULL; AjIList fielditer = NULL; AjPQueryField field = NULL; AjPFeattable feattab = NULL; AjPStr fieldname = NULL; AjPStr condition = NULL; AjPSqlconnection connection = NULL; AjBool ret = ajTrue; #if !defined(HAVE_MYSQL) && !defined(HAVE_POSTGRESQL) ajWarn("Cannot use access method chado without mysql or postgresql"); return ajFalse; #endif if(fttabin->Input->Records) return ajFalse; qry = fttabin->Input->Query; ajDebug("featAccesschado: %S fields: %Lu\n", qry->DbAlias, ajListGetLength(qry->QueryFields)); filterseqregions = ajStrNew(); fielditer = ajListIterNewread(qry->QueryFields); while(!ajListIterDone(fielditer)) { field = ajListIterGet(fielditer); ajStrAssignS(&fieldname, field->Field); ajDebug("field:%S - val:%S\n", field->Field, field->Wildquery); if(ajStrMatchCaseC(field->Field, "id")) { ajStrAssignS(&seqid, field->Wildquery); ajStrAssignC(&fieldname, "uniquename"); } if(ajStrFindAnyC(field->Wildquery,"*?") != -1) { ajStrExchangeKK(&field->Wildquery,'*','%'); ajStrExchangeKK(&field->Wildquery,'?','_'); ajFmtPrintS(&condition," %S LIKE '%S'", fieldname, field->Wildquery); } else ajFmtPrintS(&condition,"%S = '%S'", fieldname, field->Wildquery); if (ajStrGetLen(filterseqregions)) ajStrAppendC(&filterseqregions," AND "); ajStrAppendS(&filterseqregions, condition); ajStrDel(&fieldname); ajStrDel(&condition); } ajListIterDel(&fielditer); ajDebug("dbfilter: %S\n", qry->DbFilter); if(ajStrGetLen(qry->DbFilter)) ajFmtPrintAppS(&filterseqregions, " AND %S", qry->DbFilter); ajDebug("filter: %S\n", filterseqregions); feattab = ajFeattableNew(seqid); connection = featChadoConnect(qry); if(!ajStrGetLen(feattab->Db)) ajStrAssignS(&feattab->Db, qry->DbName); ret = featChadoQryfeatureQuery(connection, filterseqregions, feattab, fttabin->Start, fttabin->End); ajSqlconnectionDel(&connection); fttabin->Input->TextData = feattab; qry->QryDone = ajTrue; ajStrDel(&seqid); ajStrDel(&filterseqregions); return ret; } ID featChadoQryfeatureQuery TY static MO ajfeatdb LB ajaxdb XX DE Queries the connected chado database for the emboss feature query. DE More queries are made for the child features of the queried feature(s) DE using function featChadoChildfeatureQuery. XX PN [1] PA u connection AjPSqlconnection PD SQL Database connection PX PN [2] PA u filter AjPStr PD Filter conditions for the SQL query PX PN [3] PA u feattab AjPFeattable PD Feature table PX PN [4] PA r qrystart ajint PD start pos specified as part of the feature query PX PN [5] PA r qryend ajint PD end pos specified as part of the feature query PX RT AjBool RD returns ajFalse if no features found RX // static AjBool featChadoQryfeatureQuery(AjPSqlconnection connection, AjPStr filter, AjPFeattable feattab, ajint qrystart, ajint qryend) { AjPSqlstatement statement = NULL; AjBool debugOn = ajFalse; ajint i = 0; AjPStr sql = NULL; AjISqlrow iter = NULL; AjPSqlrow row = NULL; AjPStr colstr = NULL; AjPStr rowstr = NULL; AjPStr regionuniquename = NULL; AjBool ret = ajTrue; debugOn = ajDebugOn(); sql = ajStrNew(); ajFmtPrintS(&sql, "SELECT feature.name, feature.uniquename,\n" " (SELECT COUNT(*) FROM featureloc" " WHERE feature.feature_id = featureloc.srcfeature_id),\n" " srcfeatureloc.srcfeature_id," " srcfeature.uniquename," " srcfeatureloc.fmin," " srcfeatureloc.fmax," " srcfeatureloc.strand, srcfeatureloc.phase,\n" " feature.seqlen," " cvterm.name, db.name," " feature.timelastmodified, feature.is_obsolete\n" " FROM feature\n" " LEFT JOIN cvterm ON cvterm.cvterm_id = feature.type_id\n" " LEFT OUTER JOIN dbxref ON dbxref.dbxref_id = feature.dbxref_id\n" " LEFT OUTER JOIN db USING (db_id)\n" " LEFT OUTER JOIN featureloc srcfeatureloc" " ON feature.feature_id = srcfeatureloc.feature_id\n" " LEFT OUTER JOIN feature srcfeature" " ON srcfeature.feature_id = srcfeatureloc.srcfeature_id\n" " WHERE feature.%S\n", filter ); ajDebug("SQL, query-feature query:\n%S\n",sql); statement = ajSqlstatementNewRun(connection,sql); if(!statement) { ajErr("Could not execute SQL statement [%S]", sql); ajExitBad(); return ajFalse; } iter = ajSqlrowiterNew(statement); if(!iter) ret = ajFalse; while(!ajSqlrowiterDone(iter)) { row = ajSqlrowiterGet(iter); if(debugOn) { while(row->Current < row->Columns) { ajSqlcolumnToStr(row,&colstr); ajFmtPrintAppS(&rowstr, "\t%S",colstr); } ajDebug("qryfeature row %d: %S\n", ++i, rowstr); ajStrSetClear(&rowstr); } ajSqlcolumnRewind(row); regionuniquename = featChadoQryfeatureRow(feattab, row, qrystart, qryend); featChadoChildfeatureQuery(connection, feattab, regionuniquename); ajStrDel(®ionuniquename); } ajSqlrowiterDel(&iter); ajSqlstatementDel(&statement); ajStrDel(&colstr); ajStrDel(&rowstr); ajStrDel(&sql); return ret; } ID featChadoChildfeatureQuery TY static MO ajfeatdb LB ajaxdb XX DE Query to retrieve features located on the feature specified DE using the srcfeature parameter XX PN [1] PA u connection AjPSqlconnection PD SQL Database connection PX PN [2] PA u feattab AjPFeattable PD Feature table PX PN [3] PA r srcfeature const AjPStr PD Unique name of the sequence region PX RT void RD RX // static void featChadoChildfeatureQuery(AjPSqlconnection connection, AjPFeattable feattab, const AjPStr srcfeature) { AjPSqlstatement statement = NULL; AjBool debugOn = ajFalse; ajint i = 0; AjISqlrow iter = NULL; AjPSqlrow row = NULL; AjPStr colstr = NULL; AjPStr rowstr = NULL; AjPStr sql = NULL; AjPFeature seqregionf = NULL; debugOn = ajDebugOn(); sql = ajStrNew(); ajListPeekLast(feattab->Features, (void**)&seqregionf); /* * in SQL query below, feature refers to the sequence region being queried * sfeature refers to the features located within the sequence region, * * it is assumed that results are ordered such that features with the same * uniquename follows each other, we should check whether we need to use * explicit ORDER BY for any reason... */ ajFmtPrintS(&sql, "SELECT sfeature.name, sfeature.uniquename, relation.name," " object.uniquename, object_type.name, featureloc.fmin," " featureloc.fmax, featureloc.strand, featureloc.phase,\n" " cvterm.name, db.name, analysis.program, analysisfeature.rawscore," " sfeature.timelastmodified, sfeature.is_obsolete\n" " FROM feature\n" " JOIN featureloc" " ON feature.feature_id = featureloc.srcfeature_id\n" " JOIN feature sfeature" " ON sfeature.feature_id = featureloc.feature_id\n" " LEFT OUTER JOIN feature_relationship" " ON sfeature.feature_id = feature_relationship.subject_id\n" " LEFT OUTER JOIN cvterm relation" " ON relation.cvterm_id = feature_relationship.type_id\n" " LEFT OUTER JOIN feature object" " ON object.feature_id = feature_relationship.object_id\n" " LEFT OUTER JOIN cvterm object_type" " ON object.type_id = object_type.cvterm_id\n" " JOIN cvterm ON cvterm.cvterm_id = sfeature.type_id\n" " LEFT OUTER JOIN dbxref ON dbxref.dbxref_id = sfeature.dbxref_id\n" " LEFT OUTER JOIN db USING (db_id)\n" " LEFT OUTER JOIN analysisfeature" " ON sfeature.feature_id=analysisfeature.feature_id\n" " LEFT OUTER JOIN analysis USING(analysis_id)\n" " WHERE \n" " feature.uniquename='%S'" " AND" " ((featureloc.fmin>%d AND featureloc.fmin<%d)" " OR" " (featureloc.fmax>=%d AND featureloc.fmax<=%d))\n", srcfeature, seqregionf->Start, seqregionf->End, seqregionf->Start, seqregionf->End); ajDebug("SQL features:\n%S\n",sql); statement = ajSqlstatementNewRun(connection,sql); if(!statement) { ajErr("Could not execute SQL statement [%S]", sql); ajExitAbort(); } iter = ajSqlrowiterNew(statement); while(!ajSqlrowiterDone(iter)) { row = ajSqlrowiterGet(iter); if(debugOn) { while(row->Current < row->Columns) { ajSqlcolumnToStr(row,&colstr); ajFmtPrintAppS(&rowstr, "\t%S",colstr); } ajDebug("row %d: %S\n", ++i, rowstr); ajStrSetClear(&rowstr); } ajSqlcolumnRewind(row); featChadoChildfeatureRow(feattab, row); } ajSqlrowiterDel(&iter); ajSqlstatementDel(&statement); ajStrDel(&colstr); ajStrDel(&rowstr); ajStrDel(&sql); return; } ID featChadoChildfeatureRow TY static MO ajfeatdb LB ajaxdb XX DE Generates an emboss feature obj from a row of chado query results, and DE appends it to the feature table fttab. XX PN [1] PA u fttab AjPFeattable PD Feature table PX PN [2] PA u row AjPSqlrow PD Input row PX RT AjPFeature RD New feature RX // static AjPFeature featChadoChildfeatureRow(AjPFeattable fttab, AjPSqlrow row) { AjPFeature feature = NULL; AjPFeature prevft = NULL; AjPStr name = NULL; AjPStr type = NULL; AjPStr source = NULL; AjPStr sourcedb = NULL; AjPStr sourceprogram = NULL; AjPTagval idtag = NULL; ajint start = 0; ajint end = 0; float score = 0; char strand = '+'; ajint i = 0; ajint frame = 0; AjPStr entryid = NULL; AjBool updateprevft = ajFalse; AjPStr alias = NULL; AjPStr relation = NULL; /* The object can also be thought of as parent (containing feature), * and subject as child (contained feature or subfeature). ref:gmod.org */ AjPStr object = NULL; AjPStr objecttype = NULL; AjPTime timelm = NULL; AjPStr timelmS = NULL; AjBool isObsolete = ajFalse; AjPStr isObsoleteS = NULL; if(!ajSqlrowGetColumns(row)) return NULL; timelm = ajTimeNew(); ajSqlcolumnToStr(row, &name); ajSqlcolumnToStr(row, &entryid); ajSqlcolumnToStr(row, &relation); ajSqlcolumnToStr(row, &object); ajSqlcolumnToStr(row, &objecttype); ajSqlcolumnToInt(row, &start); ajSqlcolumnToInt(row, &end); ajSqlcolumnToInt(row, &i); if( i == 1 ) strand = '+'; else if( i == -1 ) strand = '-'; else strand = '\0'; /* change to \0 later */ ajSqlcolumnToInt(row, &frame); ajSqlcolumnToStr(row, &type); ajSqlcolumnToStr(row, &sourcedb); ajSqlcolumnToStr(row, &sourceprogram); ajSqlcolumnToFloat(row, &score); /*ajSqlcolumnToStr(row, &alias);*/ ajSqlcolumnToTime(row, &timelm); ajSqlcolumnToBool(row, &isObsolete); if(ajStrGetLen(sourcedb)) ajStrAssignS(&source, sourcedb); else if(ajStrGetLen(sourceprogram)) ajStrAssignS(&source, sourceprogram); else source = ajStrNewC("."); ajListPeekLast(fttab->Features, (void**)&prevft); if(++start==(ajint)prevft->Start && end==(ajint)prevft->End) { ajListPeekFirst(prevft->GffTags, (void**)&idtag); if(ajStrMatchS(entryid, MAJTAGVALGETVALUE(idtag))) { updateprevft = ajTrue; feature = prevft; } } if(!updateprevft) { feature = ajFeatNewNuc(fttab, source, type, start, end, score, strand, frame, 0,0,0, NULL, NULL); ajFeatGfftagAddCS(feature, "ID", entryid); ajFeatGfftagAddCS(feature, "Name", name); /*ajFeatGfftagAddC(feature, "Alias", alias);*/ /* FIXME: it looks we replace all type 'synonyms' with SO:0000110 * following tag is a workaround until it is fixed */ if(ajStrMatchC(feature->Type, "SO:0000110")) ajFeatGfftagAddCS(feature, "type", type); } if(ajStrMatchC(relation, "part_of") || ajStrMatchC(relation, "proper_part_of") || ajStrMatchC(relation, "partof") || ajStrMatchC(relation, "producedby")) { ajFeatGfftagAddCS(feature, "Parent", object); ajFeatGfftagAddCS(feature, "parent_type", objecttype); } else if(ajStrMatchC(relation, "derives_from")) { ajFeatGfftagAddCS(feature, "Derives_from", object); ajFeatGfftagAddCS(feature, "parent_type", objecttype); } else if(ajStrGetLen(relation)) { ajFeatGfftagAddSS(feature, relation, object); ajFeatGfftagAddCS(feature, "object_type", objecttype); } if(!updateprevft) { if(timelm) { ajFmtPrintS(&timelmS,"%D",timelm); ajFeatGfftagAddCS(feature,"timelastmodified", timelmS); } if(isObsolete) { isObsoleteS = ajStrNewC("true"); ajFeatGfftagAddCS(feature,"isObsolete", isObsoleteS); ajStrDel(&isObsoleteS); } } ajFeatTrace(feature); ajStrDel(&name); ajStrDel(&entryid); ajStrDel(&relation); ajStrDel(&object); ajStrDel(&objecttype); ajStrDel(&type); ajStrDel(&source); ajStrDel(&sourcedb); ajStrDel(&sourceprogram); ajStrDel(&alias); ajTimeDel(&timelm); ajStrDel(&timelmS); return feature; } ID featChadoQryfeatureRow TY static MO ajfeatdb LB ajaxdb XX DE Generates a new feature from chado query-feature query result row. DE Checks whether the query feature is located on another feature (seq region) XX PN [1] PA u fttab AjPFeattable PD Feature table PX PN [2] PA u row AjPSqlrow PD Input row PX PN [3] PA r qrystart ajint PD Query start position PX PN [4] PA r qryend ajint PD Query end position PX RT AjPStr RD New feature ID RX // static AjPStr featChadoQryfeatureRow(AjPFeattable fttab, AjPSqlrow row, ajint qrystart, ajint qryend) { AjPFeature gf = NULL; AjPStr name = NULL; AjPStr source = NULL; AjPStr type = NULL; ajint start = 1; ajint seqlen = 0; float score = 0; char strand = '+'; ajint loccount = 0; /* this is to be removed; initially thought we can use location count to decide whether the query feature is a sequence region, however current code decides it by checking whether a parent location is defined */ ajint i = 0; ajint frame = 0; AjPStr uniquename = NULL; AjPStr alias = NULL; AjPStr regionid = NULL; AjPStr regionuniquename = NULL; ajint regionstart = 1; ajint regionend = 0; AjPTime timelm = NULL; AjPStr timelmS = NULL; AjBool isObsolete = ajFalse; AjPStr isObsoleteS = NULL; if(!ajSqlrowGetColumns(row)) return NULL; timelm = ajTimeNew(); ajSqlcolumnToStr(row, &name); ajSqlcolumnToStr(row, &uniquename); ajSqlcolumnToInt(row, &loccount); ajSqlcolumnToStr(row, ®ionid); ajSqlcolumnToStr(row, ®ionuniquename); ajSqlcolumnToInt(row, ®ionstart); ajSqlcolumnToInt(row, ®ionend); ajDebug("location count:%d parent:%S parent-id:%S" " region start:%d region end:%d\n", loccount, regionuniquename, regionid, regionstart, regionend); ajSqlcolumnToInt(row, &i); if( i == 1 ) strand = '+'; else if( i == -1 ) strand = '-'; else strand = '\0'; ajSqlcolumnToInt(row, &frame); ajSqlcolumnToInt(row, &seqlen); ajSqlcolumnToStr(row, &type); ajSqlcolumnToStr(row, &source); /*ajSqlcolumnToStr(row, &alias);*/ ajSqlcolumnToTime(row, &timelm); ajSqlcolumnToBool(row, &isObsolete); if(!ajStrGetLen(source)) ajStrAssignS(&source, fttab->Db); if(qryend) { start = qrystart; seqlen = qryend; } regionstart++; gf = ajFeatNewNuc(fttab, source, type, (ajStrGetLen(regionuniquename) ? regionstart : start), (ajStrGetLen(regionuniquename) ? regionend : seqlen), score, strand, frame, 0,0,0, NULL, NULL); ajFeatGfftagAddCS(gf, "ID", uniquename); ajFeatGfftagAddCS(gf, "Name", name); /*ajFeatGfftagAddC(gf, "Alias", alias);*/ if(timelm) { ajFmtPrintS(&timelmS,"%D",timelm); ajFeatGfftagAddCS(gf,"timelastmodified", timelmS); } if(isObsolete) { isObsoleteS = ajStrNewC("true"); ajFeatGfftagAddCS(gf,"isObsolete", isObsoleteS); ajStrDel(&isObsoleteS); } ajFeatTrace(gf); ajStrDel(&name); ajStrDel(&type); ajStrDel(&source); ajStrDel(&alias); ajTimeDel(&timelm); ajStrDel(&timelmS); ajStrDel(®ionid); if(ajStrGetLen(regionuniquename)) { if(loccount) ajDebug("Feature '%S' is used as a source feature for %d other" " feature(s) but it is itself located on feature '%S';" " making queries on the parent feature '%S'\n", uniquename, loccount, regionuniquename, regionuniquename); ajStrAssignS(&fttab->Seqid, regionuniquename); ajStrDel(&uniquename); return regionuniquename; } ajStrDel(®ionuniquename); return uniquename; } ID featChadoConnect TY static MO ajfeatdb LB ajaxdb XX DE Connects to the chado database required by the query XX PN [1] PA r qry const AjPQuery PD Query object PX RT AjPSqlconnection RD SQL Database connection RX // static AjPSqlconnection featChadoConnect(const AjPQuery qry) { AjESqlconnectionClient client; ajint iport = 3306; AjPStr url = NULL; AjPUrlref uo = NULL; AjPStr password = NULL; AjPStr socketfile = NULL; AjPSqlconnection connection = NULL; url = ajStrNew(); if(!ajNamDbGetUrl(qry->DbName, &url)) { ajErr("no URL defined for database %S", qry->DbName); return ajFalse; } uo = ajHttpUrlrefNew(); ajHttpUrlrefParseC(&uo, ajStrGetPtr(url)); ajHttpUrlrefSplitPort(uo); ajHttpUrlrefSplitUsername(uo); if(ajStrMatchCaseC(uo->Method,"mysql")) client = ajESqlconnectionClientMySQL; else if(ajStrMatchCaseC(uo->Method,"postgresql")) { client = ajESqlconnectionClientPostgreSQL; iport = 5432; } else client = ajESqlconnectionClientNULL; if(!ajStrGetLen(uo->Port)) ajFmtPrintS(&uo->Port,"%d",iport); if(ajStrGetLen(uo->Password)) { password = ajStrNew(); ajStrAssignS(&password,uo->Password); } connection = ajSqlconnectionNewData(client,uo->Username,password, uo->Host,uo->Port,socketfile, uo->Absolute); ajStrDel(&password); if(!connection) ajErr("Could not connect to database server"); ajStrDel(&url); ajHttpUrlrefDel(&uo); return connection; } ID featAccessDas TY static MO ajfeatdb LB ajaxdb XX DE Feature access method for DAS feature sources XX PN [1] PA u fttabin AjPFeattabin PD Feature input. PX RT AjBool RD ajTrue on success. RX // static AjBool featAccessDas(AjPFeattabin fttabin) { AjPStr host = NULL; AjPStr path = NULL; AjIList iter = NULL; AjPQueryField field = NULL; AjPQuery qry = NULL; AjPTextin textin = NULL; AjPStr dasqueryurl = NULL; AjBool ret = ajTrue; ajint port = 80; textin = fttabin->Input; qry = textin->Query; if(qry->QryDone) return ajFalse; if(!ajHttpQueryUrl(qry, &port, &host, &path)) { ajStrDel(&host); ajStrDel(&path); return ajFalse; } if(ajStrGetCharLast(path)!='/') ajStrAppendK(&path,'/'); dasqueryurl = ajStrNew(); iter = ajListIterNewread(qry->QueryFields); while(!ajListIterDone(iter)) { field = ajListIterGet(iter); if(ajStrMatchCaseC(field->Field, "id")) { if(!ajStrGetLen(dasqueryurl)) ajFmtPrintS(&dasqueryurl,"segment=%S", field->Wildquery); else ajFmtPrintS(&dasqueryurl,"%S;segment=%S", dasqueryurl, field->Wildquery); /* TODO: segment specific start,end positions */ if(fttabin->End > 0) ajFmtPrintS(&dasqueryurl,"%S:%u,%u", dasqueryurl,fttabin->Start,fttabin->End); } else { if(!ajStrGetLen(dasqueryurl)) ajFmtPrintS(&dasqueryurl,"%S=%S", field->Field, field->Wildquery); else ajFmtPrintS(&dasqueryurl,"%S;%S=%S", dasqueryurl, field->Field, field->Wildquery); /* TODO: segment specific start,end positions */ if(fttabin->End > 0) ajFmtPrintS(&dasqueryurl,"%S:%u,%u", dasqueryurl,fttabin->Start,fttabin->End); } } ajFmtPrintS(&path,"%Sfeatures?%S",path, dasqueryurl); ajFilebuffDel(&textin->Filebuff); textin->Filebuff = ajHttpRead(qry->DbHttpVer, qry->DbName, qry->DbProxy, host, port, path); if (textin->Filebuff) ajFilebuffHtmlNoheader(textin->Filebuff); else ret = ajFalse; qry->QryDone = ajTrue; ajStrDel(&host); ajStrDel(&path); ajStrDel(&dasqueryurl); ajListIterDel(&iter); return ret; } ID ajFeatdbPrintAccess TY public MO ajfeatdb LB ajaxdb XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajFeatdbPrintAccess(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Feature access methods\n"); ajFmtPrintF(outf, "# Name Alias Entry Query All Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "method {\n"); for(i=0; feattabAccess[i].Name; i++) if(full || !feattabAccess[i].Alias) ajFmtPrintF(outf, " %-10s %5B %5B %5B %5B \"%s\"\n", feattabAccess[i].Name, feattabAccess[i].Alias, feattabAccess[i].Entry, feattabAccess[i].Query, feattabAccess[i].All, feattabAccess[i].Desc); ajFmtPrintF(outf, "}\n\n"); return; } ID ajFeatdbExit TY public MO ajfeatdb LB ajaxdb XX DE Cleans up feature database processing internal memory XX RT void RD RX // void ajFeatdbExit(void) { ajRegFree(&featCdDivExp); ajCharDel(&featCdName); ajRegFree(&featRegGcgId); ajRegFree(&featRegGcgCont); ajRegFree(&featRegGcgId2); ajRegFree(&featRegGcgSplit); ajRegFree(&featRegGcgRefId); return; } ID featinformatDef TY list MO ajfeatread LB core XX DE Input feature formats DE DE Includes the read function (featRead), and initialising (featRegInit) DE and deletion (featDelReg) of parsing regular expression. XX // static FeatOInformat featinformatDef[] = { /*Name OBO Alias Try Dna Prot Used (initially false) Padding ReadFunction RegInitFunction RegDelFunction Description*/ {"unknown", "0000", AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, &featReadGff3, NULL, NULL, "unknown format"}, {"embl", "1927", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJFALSE, AJFALSE, &featReadEmbl, &featRegInitEmbl, &featDelRegEmbl, "embl/genbank/ddbj format"}, {"em", "1927", AJTRUE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, AJFALSE, &featReadEmbl, &featRegInitEmbl, &featDelRegEmbl, "embl/genbank/ddbj format"}, {"genbank", "1936", AJTRUE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, AJFALSE, &featReadEmbl, &featRegInitEmbl, &featDelRegEmbl, "embl/genbank/ddbj format"}, {"gb", "1936", AJTRUE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, AJFALSE, &featReadEmbl, &featRegInitEmbl, &featDelRegEmbl, "embl/genbank/ddbj format"}, {"ddbj", "1936", AJTRUE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, AJFALSE, &featReadEmbl, &featRegInitEmbl, &featDelRegEmbl, "embl/genbank/ddbj format"}, {"refseq", "1958", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJFALSE, AJFALSE, &featReadRefseq, & featRegInitEmbl, &featDelRegEmbl, "embl/genbank/ddbj format"}, {"refseqp", "0000", AJFALSE, AJTRUE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, &featReadRefseqp, &featRegInitRefseqp, &featDelRegRefseqp, "RefSeq protein format"}, {"genpept", "0000", AJFALSE, AJTRUE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, &featReadGenpept, &featRegInitSwiss, &featDelRegSwiss, "genpept format"}, {"gff3", "1939", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJFALSE, AJFALSE, &featReadGff3, &featRegInitGff3, &featDelRegGff3, "GFF version 3"}, {"gff3emboss", "0000", AJFALSE, AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJFALSE, &featReadGff3old, &featRegInitGff3, &featDelRegGff3, "GFF version 3 written by EMBOSS before release 6.4.0"}, {"gff2", "1938", AJFALSE, AJTRUE, AJTRUE, AJTRUE, AJFALSE, AJFALSE, &featReadGff2, &featRegInitGff2, &featDelRegGff2, "GFF version 1 or 2"}, {"gff", "1939", AJTRUE, AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJFALSE, &featReadGff3, &featRegInitGff3, &featDelRegGff3, "GFF version3"}, {"swiss", "1963", AJFALSE, AJTRUE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, &featReadSwiss, &featRegInitSwiss, &featDelRegSwiss, "SwissProt format"}, {"sw", "2187", AJTRUE, AJFALSE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, &featReadSwiss, &featRegInitSwiss, &featDelRegSwiss, "SwissProt format"}, {"uniprot", "2188", AJTRUE, AJFALSE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, &featReadSwiss, &featRegInitSwiss, &featDelRegSwiss, "SwissProt format"}, {"swissprot", "0000", AJTRUE, AJTRUE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, &featReadSwiss, &featRegInitSwiss, &featDelRegSwiss, "SwissProt format"}, {"pir", "1948", AJFALSE, AJTRUE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, &featReadPir, &featRegInitPir, &featDelRegPir, "PIR format"}, {"nbrf", "1948", AJTRUE, AJFALSE, AJFALSE, AJTRUE, AJFALSE, AJFALSE, &featReadPir, &featRegInitPir, &featDelRegPir, "PIR format"}, {"dasgff", "1978", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJFALSE, AJFALSE, &featReadDasgff, NULL, NULL, "DAS versions 1.5 or 1.6"}, {"chado", "0000", AJFALSE, AJTRUE, AJTRUE, AJFALSE, AJFALSE, AJFALSE, &featReadChado, NULL, NULL, "CHADO"}, {NULL, NULL, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, NULL, NULL, NULL, NULL} }; ID ajFeattabinNew TY public MO ajfeatread LB core XX DE Constructor for an empty feature table input object XX RT AjPFeattabin RD Feature table input object RX CA new CT AjPFeattabin CD Constructor CX // AjPFeattabin ajFeattabinNew(void) { AjPFeattabin pthis; AJNEW0(pthis); pthis->Input = ajTextinNewDatatype(AJDATATYPE_FEATURES); /* ajDebug("ajFeatTabinNew %x\n", pthis);*/ return pthis; } ID ajFeattabinNewSS TY public MO ajfeatread LB core XX DE Constructor for an empty feature table input object. The format and DE name are read. XX PN [1] PA r fmt const AjPStr PD feature format PX PN [2] PA r name const AjPStr PD sequence name PX PN [3] PA r type const char* PD feature type PX RT AjPFeattabin RD Feature table input object RX CA new CT AjPFeattabin CD Constructor with format, name and type CX // AjPFeattabin ajFeattabinNewSS(const AjPStr fmt, const AjPStr name, const char* type) { AjPFeattabin pthis; ajint iformat = 0; if(!featFindInformatS(fmt, &iformat)) return NULL; pthis = ajFeattabinNew(); ajStrAssignC(&pthis->Formatstr, featinformatDef[pthis->Input->Format].Name); pthis->Input->Format = iformat; ajStrAssignC(&pthis->Type, type); ajStrAssignS(&pthis->Seqname, name); pthis->Input->Filebuff = ajFilebuffNewNofile(); return pthis; } ID ajFeattabinNewCSF TY public MO ajfeatread LB core XX DE Constructor for an empty feature table input object. The format and DE name are read. The file buffer is moved to the feature table input DE object and should not be deleted by the calling program. XX PN [1] PA r fmt const char* PD feature format PX PN [2] PA r name const AjPStr PD sequence name PX PN [3] PA r type const char* PD feature type PX PN [4] PA u buff AjPFilebuff PD Buffer containing feature data PX RT AjPFeattabin RD Feature table input object RX CA new CT AjPFeattabin CD Constructor with format, name, type and input file CX // AjPFeattabin ajFeattabinNewCSF(const char* fmt, const AjPStr name, const char* type, AjPFilebuff buff) { AjPFeattabin pthis; ajint iformat = 0; if(!featFindInformatC(fmt, &iformat)) return NULL; pthis = ajFeattabinNew(); ajStrAssignC(&pthis->Formatstr, featinformatDef[iformat].Name); pthis->Input->Format = iformat; ajStrAssignC(&pthis->Type, type); ajStrAssignS(&pthis->Seqname, name); pthis->Local = ajTrue; pthis->Input->Filebuff = buff; return pthis; } ID ajFeattabinNewSSF TY public MO ajfeatread LB core XX DE Constructor for an empty feature table input object. The format and DE name are read. The file buffer is moved to the feature table input DE object and should not be deleted by the calling program. XX PN [1] PA r fmt const AjPStr PD feature format PX PN [2] PA r name const AjPStr PD sequence name PX PN [3] PA r type const char* PD feature type PX PN [4] PA u buff AjPFilebuff PD Buffer containing feature data PX RT AjPFeattabin RD Feature table input object RX CA new CT AjPFeattabin CD Constructor with format, name, type and input file CX // AjPFeattabin ajFeattabinNewSSF(const AjPStr fmt, const AjPStr name, const char* type, AjPFilebuff buff) { AjPFeattabin pthis; ajint iformat = 0; if(!featFindInformatS(fmt, &iformat)) return NULL; pthis = ajFeattabinNew(); ajStrAssignC(&pthis->Formatstr, featinformatDef[iformat].Name); pthis->Input->Format = iformat; ajStrAssignC(&pthis->Type, type); ajStrAssignS(&pthis->Seqname, name); pthis->Local = ajTrue; pthis->Input->Filebuff = buff; return pthis; } ID ajFeattabinDel TY public MO ajfeatread LB core XX DE Destructor for a feature table input object XX PN [1] PA d pthis AjPFeattabin* PD Feature table input object PX RT void RD RX CA delete CT AjPFeattabin CD Destructor CX // void ajFeattabinDel(AjPFeattabin* pthis) { AjPFeattabin thys; thys = *pthis; if(!thys) return; ajTextinDel(&thys->Input); ajStrDel(&thys->Ufo); ajStrDel(&thys->Formatstr); ajStrDel(&thys->Filename); ajStrDel(&thys->Filename); ajStrDel(&thys->Seqid); ajStrDel(&thys->Seqname); ajStrDel(&thys->Type); AJFREE(*pthis); return; } ID ajFeattabinClear TY public MO ajfeatread LB core XX DE Clears a feature table input object back to "as new" condition, except DE for the USA list which must be preserved. XX PN [1] PA u thys AjPFeattabin PD Sequence input PX RT void RD RX // void ajFeattabinClear(AjPFeattabin thys) { ajDebug("ajFeattabinClear called Local:%B\n", thys->Local); if(!thys) return; ajStrSetClear(&thys->Ufo); ajStrSetClear(&thys->Seqname); ajStrSetClear(&thys->Formatstr); ajStrSetClear(&thys->Filename); ajStrSetClear(&thys->Seqid); ajStrSetClear(&thys->Type); if(!thys->Local) ajTextinClear(thys->Input); else ajTextinClearNofile(thys->Input); thys->Start = 0; thys->End = 0; thys->Rev = ajFalse; return; } ID ajFeattabinSetRange TY public MO ajfeatread LB core XX DE Set the begin and end range for a feature table XX PN [1] PA u thys AjPFeattabin PD Feature table input object PX PN [2] PA r fbegin ajint PD Begin position PX PN [3] PA r fend ajint PD End position PX RT void RD RX // void ajFeattabinSetRange(AjPFeattabin thys, ajint fbegin, ajint fend) { if(fbegin) thys->Start = fbegin; if(fend) thys->End = fend; return; } ID ajFeattabinSetTypeC TY public MO ajfeatread LB core XX DE Sets the type for feature input XX PN [1] PA u thys AjPFeattabin PD Feature input object PX PN [2] PA r type const char* PD Feature type "nucleotide" "protein" PX RT AjBool RD ajTrue on success RX // AjBool ajFeattabinSetTypeC(AjPFeattabin thys, const char* type) { ajint i = 0; if(!*type) return ajTrue; for(i=0; featinTypes[i].Name; i++) { if(ajCharMatchCaseC(featinTypes[i].Name, type)) { if(featinTypes[i].Value) ajStrAssignC(&thys->Type, featinTypes[i].Value); return ajTrue; } i++; } ajErr("Unrecognized feature input type '%s'", type); return ajFalse; } ID ajFeattabinSetTypeS TY public MO ajfeatread LB core XX DE Sets the type for feature input XX PN [1] PA u thys AjPFeattabin PD Feature input object PX PN [2] PA r type const AjPStr PD Feature type "nucleotide" "protein" PX RT AjBool RD ajTrue on success RX // AjBool ajFeattabinSetTypeS(AjPFeattabin thys, const AjPStr type) { return ajFeattabinSetTypeC(thys, ajStrGetPtr(type)); } ID ajFeattabinQryC TY public MO ajfeatread LB core XX DE Resets a feature table input object using a new Universal DE Query Address XX PN [1] PA u thys AjPFeattabin PD Feature table input object. PX PN [2] PA r txt const char* PD Query PX RT void RD RX // void ajFeattabinQryC(AjPFeattabin thys, const char* txt) { ajFeattabinClear(thys); ajStrAssignC(&thys->Input->Qry, txt); return; } ID ajFeattabinQryS TY public MO ajfeatread LB core XX DE Resets a feature table input object using a new Universal DE Query Address XX PN [1] PA u thys AjPFeattabin PD Feature table input object. PX PN [2] PA r str const AjPStr PD Query PX RT void RD RX // void ajFeattabinQryS(AjPFeattabin thys, const AjPStr str) { ajFeattabinClear(thys); ajStrAssignS(&thys->Input->Qry, str); return; } ID ajFeattabinRead TY public MO ajfeatread LB core XX DE Generic interface function for reading in features from a file DE given the file handle. XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table containing PD the features read in PX RT AjBool RD True on success RX // AjBool ajFeattabinRead(AjPFeattabin feattabin, AjPFeattable ftable) { AjBool ret = ajFalse; FeatPListUfo node = NULL; AjBool listdata = ajFalse; if(feattabin->Input->Filebuff) { /* (a) if file still open, keep reading */ ajDebug("ajFeattabinRead: input file '%F' still there, try again\n", feattabin->Input->Filebuff->File); ret = feattabinRead(feattabin, ftable); ajDebug("ajFeattabinRead: open buffer qry: '%S' returns: %B\n", feattabin->Input->Qry, ret); } else { /* (b) if we have a list, try the next query in the list */ if(ajListGetLength(feattabin->Input->List)) { listdata = ajTrue; ajListPop(feattabin->Input->List, (void**) &node); ajDebug("++pop from list '%S'\n", node->Ufo); ajFeattabinQryS(feattabin, node->Ufo); ajDebug("++SAVE FEATTABIN '%S' '%S' %d\n", feattabin->Input->Qry, feattabin->Input->Formatstr, feattabin->Input->Format); feattabinQryRestore(feattabin, node); ajStrDel(&node->Ufo); ajStrDel(&node->Formatstr); AJFREE(node); ajDebug("ajFeattabinRead: open list, try '%S'\n", feattabin->Input->Qry); if(!feattabinQryProcess(feattabin, ftable) && !ajListGetLength(feattabin->Input->List)) return ajFalse; ret = feattabinRead(feattabin, ftable); ajDebug("ajFeattabinRead: list qry: '%S' returns: %B\n", feattabin->Input->Qry, ret); } else { ajDebug("ajFeattabinRead: no file yet - test query '%S'\n", feattabin->Input->Qry); /* (c) Must be a query - decode it */ if(!feattabinQryProcess(feattabin, ftable) && !ajListGetLength(feattabin->Input->List)) return ajFalse; if(ajListGetLength(feattabin->Input->List)) /* could be new list */ listdata = ajTrue; ret = feattabinRead(feattabin, ftable); ajDebug("ajFeattabinRead: new qry: '%S' returns: %B\n", feattabin->Input->Qry, ret); } } /* Now read whatever we got */ while(!ret && ajListGetLength(feattabin->Input->List)) { /* Failed, but we have a list still - keep trying it */ if(listdata) ajErr("Failed to read data data '%S'", feattabin->Input->Qry); listdata = ajTrue; ajListPop(feattabin->Input->List,(void**) &node); ajDebug("++try again: pop from list '%S'\n", node->Ufo); ajFeattabinQryS(feattabin, node->Ufo); ajDebug("++SAVE (AGAIN) FEATTABIN '%S' '%S' %d\n", feattabin->Input->Qry, feattabin->Input->Formatstr, feattabin->Input->Format); feattabinQryRestore(feattabin, node); ajStrDel(&node->Ufo); ajStrDel(&node->Formatstr); AJFREE(node); if(!feattabinQryProcess(feattabin, ftable)) continue; ret = feattabinRead(feattabin, ftable); ajDebug("ajFeattabinRead: list retry qry: '%S' returns: %B\n", feattabin->Input->Qry, ret); } if(!ret) { if(listdata) ajErr("Failed to read data data '%S'", feattabin->Input->Qry); return ajFalse; } feattableDefine(ftable, feattabin); return ajTrue; } ID feattabinQueryMatch TY static MO ajfeatread LB core XX DE Compares a feature table item to a query and returns true if they match. XX PN [1] PA r thys const AjPQuery PD query. PX PN [2] PA r ftable const AjPFeattable PD Feature table data. PX RT AjBool RD ajTrue if the data matches the query. RX // static AjBool feattabinQueryMatch(const AjPQuery thys, const AjPFeattable ftable) { AjBool tested = ajFalse; AjIList iterfield = NULL; AjPQueryField field = NULL; AjBool ok = ajFalse; ajDebug("feattabinQueryMatch '%S' fields: %Lu Case %B Done %B\n", ftable->Seqid, ajListGetLength(thys->QueryFields), thys->CaseId, thys->QryDone); if(!thys) /* no query to test, that's fine */ return ajTrue; if(thys->QryDone) /* do we need to test here? */ return ajTrue; /* test the query field(s) */ iterfield = ajListIterNewread(thys->QueryFields); while(!ajListIterDone(iterfield)) { field = ajListIterGet(iterfield); ajDebug(" field: '%S' Query: '%S'\n", field->Field, field->Wildquery); if(ajStrMatchC(field->Field, "id")) { ajDebug(" id test: '%S'\n", ftable->Seqid); if(thys->CaseId) { if(ajStrMatchWildS(ftable->Seqid, field->Wildquery)) { ajListIterDel(&iterfield); return ajTrue; } } else { if(ajStrMatchWildCaseS(ftable->Seqid, field->Wildquery)) { ajListIterDel(&iterfield); return ajTrue; } } ajDebug("id test failed\n"); tested = ajTrue; ok = ajFalse; } if(ajStrMatchC(field->Field, "acc")) /* test id, use trueid */ { if(ajStrMatchWildCaseS(ftable->Seqid, field->Wildquery)) { ajListIterDel(&iterfield); return ajTrue; } } } ajListIterDel(&iterfield); if(!tested) /* nothing to test, so accept it anyway */ { ajDebug(" no tests: assume OK\n"); return ajTrue; } ajDebug("result: %B\n", ok); return ok; } ID feattableDefine TY static MO ajfeatread LB core XX DE Make sure all feature table object attributes are defined DE using values from the feature table input object if needed XX PN [1] PA u thys AjPFeattable PD feature table data returned. PX PN [2] PA u feattabin AjPFeattabin PD Feature table input definitions PX RT AjBool RD ajTrue on success. RX // static AjBool feattableDefine(AjPFeattable thys, AjPFeattabin feattabin) { /* if values are missing in the data object, we can use defaults from feattabin or calculate where possible */ /* assign the dbname if defined in the AjPFeattabin object */ if(ajStrGetLen(feattabin->Input->Db)) ajStrAssignS(&thys->Db, feattabin->Input->Db); return ajTrue; } ID feattabinReadFmt TY static MO ajfeatread LB core XX DE Tests whether data data can be read using the specified format. DE Then tests whether the data data matches data data query criteria DE and checks any specified type. Applies upper and lower case. XX PN [1] PA u feattabin AjPFeattabin PD Feature table input object PX PN [2] PA w ftable AjPFeattable PD Feature table data object PX PN [3] PA r format ajuint PD input format code PX RT ajuint RD 0 if successful. RD 1 if the query match failed. RD 2 if the data data type failed RD 3 if it failed to read any data data RX // static ajuint feattabinReadFmt(AjPFeattabin feattabin, AjPFeattable ftable, ajuint format) { ajDebug("++feattabinReadFmt format %d (%s) '%S'\n", format, featinformatDef[format].Name, feattabin->Input->Qry); feattabin->Input->Records = 0; if(!featinformatDef[format].Used) { if(featinformatDef[format].InitReg && !(*featinformatDef[format].InitReg)()) { ajDebug("Initialisation failed for %s\n", featinformatDef[format].Name); ajErr("Initialisation failed for feature format %s", featinformatDef[format].Name); } featinformatDef[format].Used = ajTrue; } /* Calling funclist featinFormatDef() */ if((*featinformatDef[format].Read)(feattabin, ftable)) { ajDebug("feattabinReadFmt success with format %d (%s)\n", format, featinformatDef[format].Name); ajDebug("id: '%S'\n", ftable->Seqid); feattabin->Input->Format = format; ajStrAssignC(&feattabin->Input->Formatstr, featinformatDef[format].Name); ajStrAssignC(&ftable->Formatstr, featinformatDef[format].Name); ajStrAssignEmptyS(&ftable->Db, feattabin->Input->Db); ajStrAssignS(&ftable->Filename, feattabin->Input->Filename); if(feattabinQueryMatch(feattabin->Input->Query, ftable)) { /* ajFeattabinTrace(feattabin); */ return FMT_OK; } ajDebug("query match failed, continuing ...\n"); ajFeattableClear(ftable); return FMT_NOMATCH; } else { ajDebug("Testing input buffer: IsBuff: %B Eof: %B\n", ajFilebuffIsBuffered(feattabin->Input->Filebuff), ajFilebuffIsEof(feattabin->Input->Filebuff)); if (!ajFilebuffIsBuffered(feattabin->Input->Filebuff) && ajFilebuffIsEof(feattabin->Input->Filebuff)) return FMT_EOF; ajFilebuffReset(feattabin->Input->Filebuff); ajDebug("Format %d (%s) failed, file buffer reset by feattabinReadFmt\n", format, featinformatDef[format].Name); /* ajFilebuffTraceFull(feattabin->Filebuff, 10, 10);*/ } ajDebug("++feattabinReadFmt failed - nothing read\n"); return FMT_FAIL; } ID feattabinRead TY static MO ajfeatread LB core XX DE Given data in a feature table input structure, DE tries to read everything needed DE using the specified format or by trial and error. XX PN [1] PA u feattabin AjPFeattabin PD Feature table input object PX PN [2] PA w ftable AjPFeattable PD Feature table object PX RT AjBool RD ajTrue on success RX // static AjBool feattabinRead(AjPFeattabin feattabin, AjPFeattable ftable) { ajuint i; ajuint istat = 0; ajuint jstat = 0; AjPFilebuff buff = feattabin->Input->Filebuff; /* AjBool regfile = ajFalse; */ AjBool ok; AjPTextAccess textaccess = feattabin->Input->Query->TextAccess; AjPFeattabAccess feattabaccess = feattabin->Input->Query->Access; ajFeattableClear(ftable); ajDebug("feattabinRead: cleared\n"); if(feattabin->Input->Single && feattabin->Input->Count) { /* ** One data data item at a time is read. ** The first data data item was read by ACD ** for the following ones we need to reset the AjPFeattabin ** ** Single is set by the access method */ ajDebug("feattabinRead: single access - count %d - call access" " routine again\n", feattabin->Input->Count); /* Calling funclist feattabinAccess() */ if(textaccess) { if(!(*textaccess->Access)(feattabin->Input)) { ajDebug("feattabinRead: (*textaccess->Access)(feattabin->Input) " "*failed*\n"); return ajFalse; } } if(feattabaccess) { if(!(*feattabaccess->Access)(feattabin)) { ajDebug("feattabinRead: (*feattabaccess->Access)(feattabin) " "*failed*\n"); return ajFalse; } } buff = feattabin->Input->Filebuff; } ajDebug("feattabinRead: feattabin format %d '%S'\n", feattabin->Input->Format, feattabin->Input->Formatstr); feattabin->Input->Count++; if(!feattabin->Input->Filebuff && !feattabin->Input->TextData) return ajFalse; ok = ajFilebuffIsBuffered(feattabin->Input->Filebuff); while(ok) { /* skip blank lines */ ok = ajBuffreadLine(feattabin->Input->Filebuff, &featReadLine); if(ok && !ajStrIsWhite(featReadLine)) { ajFilebuffClear(feattabin->Input->Filebuff,1); break; } } if(!feattabin->Input->Format) { /* no format specified, try all defaults */ /* regfile = ajFileIsFile(ajFilebuffGetFile(feattabin->Input->Filebuff)); */ for(i = 1; featinformatDef[i].Name; i++) { if(!featinformatDef[i].Try) /* skip if Try is ajFalse */ continue; if(!featinformatDef[i].Used) { if(featinformatDef[i].InitReg && !(*featinformatDef[i].InitReg)()) { ajDebug("Initialisation failed for %s\n", featinformatDef[i].Name); ajErr("Initialisation failed for feature format %s", featinformatDef[i].Name); } featinformatDef[i].Used = ajTrue; } ajDebug("feattabinRead:try format %d (%s)\n", i, featinformatDef[i].Name); istat = feattabinReadFmt(feattabin, ftable, i); switch(istat) { case FMT_OK: ajDebug("++feattabinRead OK, set format %d\n", feattabin->Input->Format); feattableDefine(ftable, feattabin); return ajTrue; case FMT_BADTYPE: ajDebug("feattabinRead: (a1) " "feattabinReadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("feattabinRead: (b1) " "feattabinReadFmt stat == FAIL *failed*\n"); break; /* we can try next format */ case FMT_NOMATCH: ajDebug("feattabinRead: (c1) " "feattabinReadFmt stat==NOMATCH try again\n"); break; case FMT_EOF: ajDebug("feattabinRead: (d1) " "feattabinReadFmt stat == EOF *failed*\n"); return ajFalse; /* EOF and unbuffered */ case FMT_EMPTY: ajWarn("data data '%S' has zero length, ignored", ajFeattableGetQryS(ftable)); ajDebug("feattabinRead: (e1) " "feattabinReadFmt stat==EMPTY try again\n"); break; default: ajDebug("unknown code %d from feattabinReadFmt\n", stat); } ajFeattableClear(ftable); if(feattabin->Input->Format) break; /* we read something */ ajFilebuffTrace(feattabin->Input->Filebuff); } if(!feattabin->Input->Format) { /* all default formats failed, give up */ ajDebug("feattabinRead:all default formats failed, give up\n"); return ajFalse; } ajDebug("++feattabinRead set format %d\n", feattabin->Input->Format); } else { /* one format specified */ ajDebug("feattabinRead: one format specified\n"); ajFilebuffSetUnbuffered(feattabin->Input->Filebuff); ajDebug("++feattabinRead known format %d\n", feattabin->Input->Format); istat = feattabinReadFmt(feattabin, ftable, feattabin->Input->Format); switch(istat) { case FMT_OK: feattableDefine(ftable, feattabin); return ajTrue; case FMT_BADTYPE: ajDebug("feattabinRead: (a2) " "feattabinReadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("feattabinRead: (b2) " "feattabinReadFmt stat == FAIL *failed*\n"); return ajFalse; case FMT_NOMATCH: ajDebug("feattabinRead: (c2) " "feattabinReadFmt stat == NOMATCH *try again*\n"); break; case FMT_EOF: ajDebug("feattabinRead: (d2) " "feattabinReadFmt stat == EOF *try again*\n"); if(feattabin->Input->Records) ajErr("Error reading file '%F' with format '%s': " "end-of-file before end of data " "(read %u records)", ajFilebuffGetFile(feattabin->Input->Filebuff), featinformatDef[feattabin->Input->Format].Name, feattabin->Input->Records); break; /* simply end-of-file */ case FMT_EMPTY: ajWarn("feature table data '%S' has zero length, ignored", ajFeattableGetQryS(ftable)); ajDebug("feattabinRead: (e2) " "feattabinReadFmt stat == EMPTY *try again*\n"); break; default: ajDebug("unknown code %d from feattabinReadFmt\n", stat); } ajFeattableClear(ftable); /* 1 : read, failed to match id/acc/query */ } /* failed - probably entry/accession query failed. Can we try again? */ ajDebug("feattabinRead failed - try again with format %d '%s' code %d\n", feattabin->Input->Format, featinformatDef[feattabin->Input->Format].Name, istat); ajDebug("Search:%B Chunk:%B Data:%x ajFileBuffEmpty:%B\n", feattabin->Input->Search, feattabin->Input->ChunkEntries, feattabin->Input->TextData, ajFilebuffIsEmpty(buff)); if(ajFilebuffIsEmpty(buff) && feattabin->Input->ChunkEntries) { if(textaccess && !(*textaccess->Access)(feattabin->Input)) return ajFalse; else if(feattabaccess && !(*feattabaccess->Access)(feattabin)) return ajFalse; buff = feattabin->Input->Filebuff; } /* need to check end-of-file to avoid repeats */ while(feattabin->Input->Search && (feattabin->Input->TextData || !ajFilebuffIsEmpty(buff))) { jstat = feattabinReadFmt(feattabin, ftable, feattabin->Input->Format); switch(jstat) { case FMT_OK: feattableDefine(ftable, feattabin); return ajTrue; case FMT_BADTYPE: ajDebug("feattabinRead: (a3) " "feattabinReadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("feattabinRead: (b3) " "feattabinReadFmt stat == FAIL *failed*\n"); return ajFalse; case FMT_NOMATCH: ajDebug("feattabinRead: (c3) " "feattabinReadFmt stat == NOMATCH *try again*\n"); break; case FMT_EOF: ajDebug("feattabinRead: (d3) " "feattabinReadFmt stat == EOF *failed*\n"); return ajFalse; /* we already tried again */ case FMT_EMPTY: if(istat != FMT_EMPTY) ajWarn("assmebly data '%S' has zero length, ignored", ajFeattableGetQryS(ftable)); ajDebug("feattabinRead: (e3) " "feattabinReadFmt stat == EMPTY *try again*\n"); break; default: ajDebug("unknown code %d from feattabinReadFmt\n", stat); } ajFeattableClear(ftable); /* 1 : read, failed to match id/acc/query */ } if(feattabin->Input->Format) ajDebug("feattabinRead: *failed* to read data data %S " "using format %s\n", feattabin->Input->Qry, featinformatDef[feattabin->Input->Format].Name); else ajDebug("feattabinRead: *failed* to read data data %S " "using any format\n", feattabin->Input->Qry); return ajFalse; } ID ajFeattableNewRead TY public MO ajfeatread LB core XX DE Generic interface function for reading in features from a file DE given the file handle. XX PN [1] PA u ftin AjPFeattabin PD Specifies the external source (file) PD of the features to be read in PX RT AjPFeattable RD Pointer to a new feature table containing RD the features read in RX CA new CT AjPFeattable CD Reads in a feature set in a specified format CX // AjPFeattable ajFeattableNewRead(AjPFeattabin ftin) { AjPTextin textin = ftin->Input; AjPFilebuff file; ajint format; AjBool ok = ajTrue; AjPFeattable features = NULL; AjBool result = ajFalse; if(!ftin) return NULL; textin = ftin->Input; file = textin->Filebuff; if(!file) return NULL; format = ftin->Input->Format; if(!format) return NULL; ajDebug("ajFeattableNewRead format %d '%s' file %x type: '%S'\n", format, featinformatDef[format].Name, file, ftin->Type); if(!featinformatDef[format].Used) { /* Calling funclist featinformatDef() */ if(featinformatDef[format].InitReg && !(*featinformatDef[format].InitReg)()) { ajDebug("Initialisation failed for %s\n", featinformatDef[format].Name); ajErr("Initialisation failed for feature format %s", featinformatDef[format].Name); } featinformatDef[format].Used = ajTrue; } features = ajFeattableNew(ftin->Seqname); while(ok) { /* skip blank lines */ ok = ajBuffreadLine(textin->Filebuff, &featReadLine); if(ok && !ajStrIsWhite(featReadLine)) { ajFilebuffClear(textin->Filebuff,1); break; } } /* Calling funclist featinformatDef() */ result = (*featinformatDef[format].Read)(ftin, features); if(result) { if(ftin->Circular) features->Circular = ajTrue; /* ajFeattableTrace(features); */ ajDebug("ajFeattableNewRead read %d features\n", ajFeattableGetSize(features)); return features; } else { ajDebug("ajFeattableNewRead failed, read %d features\n", ajFeattableGetSize(features)); ajFeattableDel(&(features)); } return NULL; } ID ajFeattableNewReadUfo TY public MO ajfeatread LB core XX DE Parses a UFO, opens an input file, and reads a feature table XX PN [1] PA u featin AjPFeattabin PD Feature input object PX PN [2] PA r ufo const AjPStr PD UFO feature spec PX RT AjPFeattable RD Feature table created, (or NULL if failed) RX CA new CT AjPFeattable CD Parses a UFO, opens an input file, and reads a feature table CX // AjPFeattable ajFeattableNewReadUfo(AjPFeattabin featin, const AjPStr ufo) { AjPFeattable ret = NULL; AjBool fmtstat = ajFalse; /* status returns from regex tests */ AjBool filstat = ajFalse; /* status returns from regex tests */ ajint i; ajDebug("ajFeattableNewReadUfo ufo: '%S' filebuff: %x\n", ufo, featin->Input->Filebuff); if(!featinRegUfoFmt) featinRegUfoFmt = ajRegCompC("^([A-Za-z0-9][A-Za-z0-9]+):+(.*)$"); /* \1 format */ /* \2 remainder */ if(!featinRegUfoFile) featinRegUfoFile = ajRegCompC("^(([A-Za-z]:)?[^:]+)$"); /*ajDebug("ajFeattableNewReadUfo UFO '%S'\n", ufo);*/ ajStrAssignS(&featinUfoTest, ufo); if(ajStrGetLen(ufo)) { fmtstat = ajRegExec(featinRegUfoFmt, featinUfoTest); /*ajDebug("feat format regexp: %B\n", fmtstat);*/ } if(fmtstat) { ajRegSubI(featinRegUfoFmt, 1, &featin->Formatstr); ajStrAssignEmptyC(&featin->Formatstr, featinformatDef[0].Name); /* unknown */ ajRegSubI(featinRegUfoFmt, 2, &featinUfoTest); /* trim off the format */ /*ajDebug("found feat format %S\n", featin->Formatstr);*/ if(!featFindInformatS(featin->Formatstr, &featin->Input->Format)) ajErr("unknown input feature table format '%S'\n" "NO Features will be read in", featin->Formatstr); } else { /*ajDebug("no format specified in UFO");*/ } featFormatSet(featin); filstat = ajRegExec(featinRegUfoFile, featinUfoTest); /*ajDebug("featinRegUfoFile: %B\n", filstat);*/ if(filstat) ajRegSubI(featinRegUfoFile, 1, &featin->Filename); else { if(ajStrGetLen(featin->Seqname) && ajStrGetLen(featin->Formatstr)) { ajFmtPrintS(&featinUfoTest, "%S.%S", featin->Seqname, featin->Formatstr); ajStrAssignEmptyS(&featin->Filename, featinUfoTest); /*ajDebug("generate filename '%S'\n", featin->Filename);*/ } else { /*ajDebug("unable to generate filename " "Featin Seqname '%S' Formatstr '%S'\n", featin->Seqname, featin->Formatstr);*/ return NULL; } } /* Open the file so that we can try to read it */ /* ajDebug("trying to open '%S'\n", featin->Filename);*/ ajFilebuffDel(&featin->Input->Filebuff); featin->Input->Filebuff = ajFilebuffNewNameS(featin->Filename); if(!featin->Input->Filebuff) return NULL; /*ajDebug("after opening '%S'\n", featin->Filename);*/ /* OKAY if we have a format specified try this and this ONLY */ if(featin->Input->Format) ret = ajFeattableNewRead(featin); /* else loop through all types and try to read features */ else for(i=1;featinformatDef[i].Name;i++) { featin->Input->Format = i; ret = ajFeattableNewRead(featin); if(ret) break; /* Reset buffer to start */ ajFilebuffReset(featin->Input->Filebuff); } ajFilebuffDel(&featin->Input->Filebuff); return ret; } ID featFormatSet TY static MO ajfeatread LB core XX DE Sets the input format for a feature table using the feature input DE object's defined format. XX PN [1] PA u featin AjPFeattabin PD Feature table input. PX RT AjBool RD ajTrue on success. RX // static AjBool featFormatSet(AjPFeattabin featin) { if(ajStrGetLen(featin->Formatstr)) { /*ajDebug("... input format value '%S'\n", featin->Formatstr);*/ if(featFindInformatS(featin->Formatstr, &featin->Input->Format)) { /* we may need to set feature table format too? */ /* (void) ajStrAssignS(&thys->Formatstr, featin->Formatstr); thys->Format = featin->Format; ajDebug("...format OK '%S' = %d\n", featin->Formatstr, featin->Format); */ } else { /*ajDebug("...format unknown '%S'\n", featin->Formatstr);*/ } return ajTrue; } else { /*ajDebug("...input format not set\n");*/ } return ajFalse; } ID featReadChado TY static MO ajfeatread LB core XX DE Copies features attached to fttabin object to fttab object. XX PN [1] PA u fttabin AjPFeattabIn PD Feature table input PX PN [2] PA u fttab AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // static AjBool featReadChado(AjPFeattabIn fttabin, AjPFeattable fttab) { if(fttabin->Input->TextData && fttabin->Input->Records==0) { AjPFeattable ft = fttabin->Input->TextData; ajFeattableSetDefname(fttab, ft->Seqid); ajListFree(&fttab->Features); fttab->Features = ft->Features; ft->Features = NULL; fttab->Start = ft->Start; fttab->End = ft->End; fttab->Len = ft->Len; ajFeattableDel(&ft); return ajTrue; } return ajFalse; } ID featReadDasgff TY static MO ajfeatread LB core XX DE Reads feature data in DAS 1.5/1.6 features format (XML). XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // static AjBool featReadDasgff(AjPFeattabin feattabin, AjPFeattable ftable) { AjPDomDocument doc = NULL; AjPDomNodeList features = NULL; AjPDomNodeList segments = NULL; AjPDomNodeList notes = NULL; AjPDomNodeList links = NULL; AjPDomNodeList targets = NULL; AjPDomNodeList parents = NULL; AjPDomNodeList parts = NULL; AjPDomNodeList groups = NULL; AjPDomNode dasfeature = NULL; AjPDomNode segment = NULL; AjPDomElement e = NULL; AjPFeature feature = NULL; AjPStr attval = NULL; AjPStr elmtxt = NULL; AjPStr type = NULL; AjPStr label = NULL; AjBool ret = AJTRUE; ajint i; ajint j; ajuint start; ajuint end; AjPFilebuff inf = feattabin->Input->Filebuff; doc = ajDomImplementationCreateDocument(NULL,NULL,NULL); if (ajDomReadFilebuff(doc,inf) == -1) { ajDomDocumentDestroyNode(doc,doc); return AJFALSE; } /* * (required; one or more) * provides information on the reference segment queried. * The id attribute is required. * The version attribute (optional) indicates the version of the reference * object being annotated, * used for coordinate systems which are not themselves versioned. * * The optional label attribute provides a human readable label * for display purposes. * * TODO: here we assume one segment, spec suggests it can be more than one */ segments = ajDomDocumentGetElementsByTagNameC(doc, "SEGMENT"); if(segments==NULL || ajDomNodeListGetLen(segments) == 0) { ajDomDocumentDestroyNodeList(doc,segments,AJDOMKEEP); ajDomDocumentDestroyNode(doc,doc); ajDebug("featReadDasgff: no tag found"); return AJFALSE; } segment = ajDomNodeListItem(segments, 0); attval = ajDomElementGetAttributeC(segment,"id"); if(ajStrGetLen(attval) == 0) { ajStrDel(&attval); ajDomDocumentDestroyNodeList(doc,segments,AJDOMKEEP); ajDomDocumentDestroyNode(doc,doc); return AJFALSE; } ajStrAssignS(&ftable->Seqid,attval); ajStrDel(&attval); attval = ajDomElementGetAttributeC(segment,"start"); ajStrToUint(attval, &ftable->Start); ftable->Start--; ajStrDel(&attval); attval = ajDomElementGetAttributeC(segment,"stop"); ajStrToUint(attval, &ftable->End); ftable->End--; ajStrDel(&attval); ftable->Len = ftable->End - ftable->Start; features = ajDomDocumentGetElementsByTagNameC(doc, "FEATURE"); if(features) { for (i = 0; i < ajDomNodeListGetLen(features); i++) { dasfeature = ajDomNodeListItem(features, i); /* * , (optional; one apiece per feature) * * indicates the start and end of the feature in the coordinate * system of the reference object given in the tag. * If one element is present, the other must be also. * If start and end are zero, or omitted entirely, it is assumed * that the feature is an annotation of the reference object * as a whole rather than a region of sequence. */ e = ajDomElementGetFirstChildByTagNameC(doc, dasfeature, "START"); elmtxt = ajDomElementGetText(e); ajStrToUint(elmtxt, &start); ajDebug(" start = %S\n",elmtxt); e = ajDomElementGetFirstChildByTagNameC(doc, dasfeature, "END"); elmtxt = ajDomElementGetText(e); ajDebug(" end = %S\n",elmtxt); ajStrToUint(elmtxt, &end); feature = ajFeatNewII(ftable, start, end); /* * The id attribute (required) is an identifier for the feature. * it must be unique to the feature across the data source. * * The label attribute (optional) is a suggested label to display * for the feature. If not present, it is assumed the id attribute * is suitable for display. */ attval = ajDomElementGetAttributeC(dasfeature, "id"); if(ajStrGetLen(attval) == 0) { ret = AJFALSE; break; } ajDebug(" id = %S\n",attval); ajFeatGfftagAddCS(feature, "ID", attval); ajStrDel(&attval); /* (required; one per feature) * indicates the type of the annotation. * attributes are id (required), which is a unique ID * for the annotation type, * category (optional, recommended), which provides functional * grouping to related types, and cvId (optional, recommended) * which is the ID of a term from a relevant controlled vocabulary * * gff3: type of the feature (previously called the "method"). * This is constrained to be either: * (a) a term from the "lite" sequence ontology, SOFA; or * (b) a SOFA accession number. * The latter alternative is distinguished using the syntax * SO:000000. This field is required. */ e = ajDomElementGetFirstChildByTagNameC(doc,dasfeature,"TYPE"); if(e == NULL) { ret = AJFALSE; break; } attval = ajDomElementGetAttributeC(e, "id"); if(ajStrGetLen(attval) == 0) { ajStrDel(&attval); ajDebug("featReadDasgff: required 'id' attribute of annotation" " type is empty\n"); /* normally parsing should stop here * but many DAS sources are missing this requirement */ /* ret = AJFALSE; break; */ } ajDebug(" type - id = %S\n",attval); ajStrAssignS(&feature->Type, attval); ajStrDel(&attval); attval = ajDomElementGetAttributeC(e, "cvId"); if(ajStrGetLen(attval)>0) ajFeatGfftagAddCS(feature,"Ontology_term",attval); ajStrDel(&attval); attval = ajDomElementGetAttributeC(e, "category"); if(ajStrGetLen(attval)>0) ajFeatGfftagAddCS(feature,"category",attval); ajStrDel(&attval); /* * - "source" on gff * * method used to identify the feature. * The id attribute is required. * The cvId (optional, recommended) attribute is an ontology term * ID from the Evidence Codes Ontology, and as such is a generic * (potentially less specific) representation of the method. * */ e = ajDomElementGetFirstChildByTagNameC(doc,dasfeature,"METHOD"); if(e == NULL) { ret = AJFALSE; break; } attval = ajDomElementGetAttributeC(e, "id"); ajDebug(" method id = %S\n",attval); ajStrAssignRef(&feature->Source, attval); ajStrDel(&attval); attval = ajDomElementGetAttributeC(e, "cvId"); if(ajStrGetLen(attval) > 0) ajFeatSetSource(feature, attval); ajStrDel(&attval); /* * (optional; one per feature) * * integer or floating point number indicating the "score" of * the method used to find the current feature. * If this field is inapplicable, the contents of the tag can be * replaced with a - symbol. * This is the assumed value if the tag is omitted entirely. */ e = ajDomElementGetFirstChildByTagNameC(doc,dasfeature,"SCORE"); if(e!=NULL) { elmtxt = ajDomElementGetText(e); ajDebug(" score = %S\n",elmtxt); ajStrToFloat(elmtxt, &feature->Score); } /* * (optional; one per feature) * * orientation of the feature relative to the direction of * transcription. It may be 0 for features that are unrelated to * transcription, +, for features that are on the sense strand, * and -, for features on the antisense strand. * If this tag is omitted, a value of 0 is assumed. */ e = ajDomElementGetFirstChildByTagNameC(doc,dasfeature, "ORIENTATION"); if(e != NULL) { elmtxt = ajDomElementGetText(e); ajDebug(" strand = %S\n",elmtxt); feature->Strand = ajStrGetCharFirst(elmtxt); } /* * (optional; one per feature) * * position of the feature relative to open reading frame, if any. * It may be one of the integers 0, 1 or 2, corresponding to * each of the three reading frames, * or - if the feature is unrelated to a reading frame. * If this tag is omitted, a value of - is assumed. */ e = ajDomElementGetFirstChildByTagNameC(doc, dasfeature, "PHASE"); if(e != NULL) { elmtxt = ajDomElementGetText(e); ajDebug(" phase = %S\n",elmtxt); /* TODO: new integer value to represent features * unrelated to a reading frame */ if ( ajStrGetCharFirst(elmtxt) == '-' ) feature->Frame = 0; ajStrToInt(elmtxt, &feature->Frame); } /* * (optional; zero or more per feature) * * A human-readable note in plain text format only * */ notes = ajDomElementGetElementsByTagNameC(dasfeature,"NOTE"); if (notes!=NULL) { for(j=0; j */ if(elmtxt != NULL) ajFeatGfftagAddCS(feature, "Note", elmtxt); } ajDomDocumentDestroyNodeList(doc,notes,AJDOMKEEP); } /* * (optional; zero or more per feature) * * link to a web page somewhere that provides more information * about this feature. * element text is an optional human readable label * for display purposes. * */ links = ajDomElementGetElementsByTagNameC(dasfeature,"LINK"); if (links!=NULL) { for(j=0; j (optional; zero or more per feature) * * target sequence in a sequence similarity match. * The id attribute provides * the reference ID for the target sequence, * and the start and stop attributes indicate the segment * that matched across the target sequence. * All three attributes are required. * content of the tag (optional) is a human readable label. * * gff mapping: attributes column - Target tag */ targets = ajDomElementGetElementsByTagNameC(dasfeature,"TARGET"); if(targets!=NULL) { for(j=0;j, (optional; zero or more per feature) * * A replacement for the tag, * these tags identify other features that are parents or children * of this feature within a hierarchy. * Each has a single required attribute, id, * which refers to a separate tag. * This mechanism means a parent or child feature need * only be defined once and may be referred to multiple times. * This is preferable to the use of tags, * where a parent must be defined separately for every child. * It also allows more than two levels of hierarchy to be defined * (e.g. a gene has parts - transcripts, and transcripts have parts * - exons). * In addition, parent features may have start/end positions. * */ groups = ajDomElementGetElementsByTagNameC(dasfeature,"GROUP"); if (groups!=NULL) { for(j=0; jInput->Filebuff; if(!featReadLine) featReadLine = ajStrNewRes(100); ajFeattableSetNuc(ftable); while(ajBuffreadLinePosStore(file, &featReadLine, &fpos, feattabin->Input->Text, &ftable->TextPtr)) { if(ajStrPrefixC(featReadLine, "//")) { break; } else if(ajStrPrefixC(featReadLine, "ID ")) { isGenbank = ajFalse; ajStrTokenAssignC(&handle, featReadLine, " ;\t\n\r"); ajStrTokenNextParse(&handle, &token); /* 'ID' */ ajStrTokenNextParse(&handle, &ftable->Seqid); /* entry name */ ajStrTokenDel(&handle); ajStrDel(&token); } else if(ajStrPrefixC(featReadLine, "LOCUS ")) { isGenbank = ajTrue; } /* if it's an EMBL feature do stuff */ else if(ajStrPrefixC(featReadLine, "FEATURES ")) { isGenbank = ajTrue; } else if(ajStrPrefixC(featReadLine, "FT ")) { ajStrTrimWhiteEnd(&featReadLine); /* remove newline */ if(featEmblFromLine(ftable, featReadLine, &savefeat, &saveloc, &saveline)) found = ajTrue; } /* if it's a GenBank feature do stuff */ else if(isGenbank && ajStrPrefixC(featReadLine, " ")) { ajStrTrimWhiteEnd(&featReadLine); /* remove newline */ if(featEmblFromLine(ftable, featReadLine, &savefeat, &saveloc, &saveline)) found = ajTrue; } else if(isGenbank) { isGenbank = ajFalse; } } if(featEmblFromLine(ftable, NULL, &savefeat, &saveloc, &saveline)) found = ajTrue; ajStrDel(&saveloc); ajStrDel(&saveline); ajStrDel(&savefeat); return found; } ID featReadGenpept TY static MO ajfeatread LB core XX DE Reads feature data in GENPEPT format XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // static AjBool featReadGenpept(AjPFeattabin feattabin, AjPFeattable ftable) { AjBool found = ajFalse; AjPStr savefeat = NULL; AjPStr saveline = NULL; AjPStr saveloc = NULL; AjPFilebuff file = feattabin->Input->Filebuff; if(!featReadLine) featReadLine = ajStrNewRes(100); ajFeattableSetProt(ftable); while(ajBuffreadLine(file, &featReadLine)) { if(ajStrPrefixC(featReadLine, " ")) { ajStrTrimWhiteEnd(&featReadLine); /* remove newline */ if(featEmblFromLine(ftable, featReadLine, &savefeat, &saveloc, &saveline)) found = ajTrue; } } if(featEmblFromLine(ftable, NULL, &savefeat, &saveloc, &saveline)) found = ajTrue; ajStrDel(&saveloc); ajStrDel(&saveline); ajStrDel(&savefeat); return found; } ID featReadRefseq TY static MO ajfeatread LB core XX DE Reads feature data in REFSEQ format XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // static AjBool featReadRefseq(AjPFeattabin feattabin, AjPFeattable ftable) { AjBool found = ajFalse; AjPStr savefeat = NULL; AjPStr saveline = NULL; AjPStr saveloc = NULL; AjPFilebuff file = feattabin->Input->Filebuff; if(!featReadLine) featReadLine = ajStrNewRes(100); ajFeattableSetNuc(ftable); while(ajBuffreadLine(file, &featReadLine)) { if(ajStrPrefixC(featReadLine, " ")) { ajStrTrimWhiteEnd(&featReadLine); /* remove newline */ if(featEmblFromLine(ftable, featReadLine, &savefeat, &saveloc, &saveline)) found = ajTrue; } } if(featEmblFromLine(ftable, NULL, &savefeat, &saveloc, &saveline)) found = ajTrue; ajStrDel(&saveloc); ajStrDel(&saveline); ajStrDel(&savefeat); return found; } ID featReadRefseqp TY static MO ajfeatread LB core XX DE Reads feature data in REFSEQP format XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // static AjBool featReadRefseqp(AjPFeattabin feattabin, AjPFeattable ftable) { AjBool found = ajFalse; AjPStr savefeat = NULL; AjPStr saveline = NULL; AjPStr saveloc = NULL; AjPFilebuff file = feattabin->Input->Filebuff; if(!featReadLine) featReadLine = ajStrNewRes(100); ajFeattableSetProt(ftable); while(ajBuffreadLine(file, &featReadLine)) { if(ajStrPrefixC(featReadLine, " ")) { ajStrTrimWhiteEnd(&featReadLine); /* remove newline */ if(featRefseqpFromLine(ftable, featReadLine, &savefeat, &saveloc, &saveline)) found = ajTrue; } } if(featRefseqpFromLine(ftable, NULL, &savefeat, &saveloc, &saveline)) found = ajTrue; ajStrDel(&saveloc); ajStrDel(&saveline); ajStrDel(&savefeat); return found; } ID featReadPir TY static MO ajfeatread LB core XX DE Reads feature data in PIR format XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // static AjBool featReadPir(AjPFeattabin feattabin, AjPFeattable ftable) { AjBool found = ajFalse; AjPFilebuff file = feattabin->Input->Filebuff; /*ajDebug("featReadPir..........\n");*/ if(!featReadLine) featReadLine = ajStrNewRes(100); while(ajBuffreadLine(file, &featReadLine)) { ajStrTrimWhite(&featReadLine); /* ajDebug("++ line '%S'\n", line); */ if(ajStrPrefixC(featReadLine, "F;")) { if(featPirFromLine(ftable, featReadLine)) found = ajTrue; } } return found; } ID featPirFromLine TY static MO ajfeatread LB core XX DE Read input file line in PIR format DE DE Format is :- DE F;position/type: note #comment XX PN [1] PA u thys AjPFeattable PD Feature table PX PN [2] PA r origline const AjPStr PD Input line PX RT AjPFeature RD New feature. RX // static AjPFeature featPirFromLine(AjPFeattable thys, const AjPStr origline) { AjPStr temp = NULL; static AjPFeature gf = NULL; /* made static so that it's easy to add second line of description */ /* AjPFeature gfpos = NULL; */ AjPStr locstr = NULL; AjPStr typstr = NULL; AjPStr notestr = NULL; AjPStr comstr = NULL; AjPStr exonstr = NULL; AjPStr posstr = NULL; ajint i = 0; AjBool mother = ajTrue; ajuint Start = 0; ajuint End = 0; ajuint Flags = 0; const AjPStr pirtype = NULL; /*ajDebug("featPirFromLine..........\n'%S'\n", origline);*/ if(!featinTagNote) ajStrAssignC(&featinTagNote, "note"); if(!featinTagComm) ajStrAssignC(&featinTagComm, "comment"); if(!featinSourcePir) featinSourcePir = ajStrNewC("PIR"); if(!ajRegExec(PirRegexAll, origline)) return NULL; ajRegSubI(PirRegexAll, 1, &locstr); ajRegSubI(PirRegexAll, 2, &typstr); ajRegSubI(PirRegexAll, 3, ¬estr); /* remove spaces in feature type so we can look it up */ ajStrExchangeCC(&typstr, " ", "_"); pirtype = ajFeattypeGetInternalPir(typstr); ajStrRemoveWhiteExcess(¬estr); /* decode the position(s) */ while(ajRegExec(PirRegexLoc, locstr)) /* split at ',' */ { ajRegSubI(PirRegexLoc, 1, &exonstr); ajRegPost(PirRegexLoc, &temp); ajStrAssignS(&locstr, temp); i = 0; while(ajRegExec(PirRegexPos, exonstr)) /* split at '-' */ { ajRegSubI(PirRegexPos, 1, &posstr); if(!i++) if(!ajStrToUint(posstr, &Start)) Start = 1; ajRegPost(PirRegexPos, &temp); ajStrAssignS(&exonstr, temp); } if(!ajStrToUint(posstr, &End)) End = 1; if(mother) gf = ajFeatNewProtFlags(thys, featinSourcePir, /* source sequence */ pirtype, Start, End, 0.0, Flags); if(!mother || ajStrGetLen(locstr)) { /* gfpos = */ ajFeatNewProtFlagsSub(thys, gf, featinSourcePir, /* source sequence */ pirtype, Start, End, 0.0, Flags); if(!gf->Start || gf->Start > Start) gf->Start = Start; if(!gf->End || gf->End < End) gf->End = End; } /* for the first feature, process the rest of the tags */ if(mother) { if(ajStrGetLen(notestr)) ajFeatTagAddSS(gf, featinTagNote, notestr); ajRegPost(PirRegexAll, &temp); while(ajRegExec(PirRegexCom, temp)) { ajRegSubI(PirRegexCom, 1, &comstr); ajStrRemoveWhiteExcess(&comstr); ajFeatTagAddSS(gf, featinTagComm, comstr); ajRegPost(PirRegexCom, &temp); } } mother = ajFalse; } if(mother) ajFeatWarn("featPirFromLine: Did not understand location '%S'", locstr); ajStrDel(&temp); ajStrDel(&locstr); ajStrDel(&typstr); ajStrDel(¬estr); ajStrDel(&comstr); ajStrDel(&exonstr); ajStrDel(&posstr); return gf; } ID featReadSwiss TY static MO ajfeatread LB core XX DE Reads feature data in SwissProt format XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // static AjBool featReadSwiss(AjPFeattabin feattabin, AjPFeattable ftable) { AjBool found = ajFalse; AjPStr savefeat = NULL; AjPStr saveline = NULL; AjPStr savefrom = NULL; AjPStr saveto = NULL; AjPStrTok handle = NULL; AjPStr token = NULL; AjPFilebuff file = feattabin->Input->Filebuff; AjPStr defname = NULL; AjBool setname = ajFalse; /*ajDebug("featReadSwiss..........\n");*/ if(!featReadLine) featReadLine = ajStrNewRes(100); while(ajBuffreadLine(file, &featReadLine)) { ajStrTrimWhite(&featReadLine); /* ajDebug("++ line '%S'\n", line); */ if(ajStrPrefixC(featReadLine, "//")) { break; } else if(ajStrPrefixC(featReadLine, "ID ")) { ajStrTokenAssignC(&handle, featReadLine, " \n\r"); ajStrTokenNextParse(&handle, &token); /* 'ID' */ ajStrTokenNextParse(&handle, &defname); /* entry name */ if(!setname) { ajFeattableSetDefname(ftable, defname); setname = ajTrue; } } else if(ajStrPrefixC(featReadLine, "AC ")) { ajStrTokenAssignC(&handle, featReadLine, " \n\r,;"); ajStrTokenNextParse(&handle, &token); /* 'AC' */ ajStrTokenNextParse(&handle, &defname); /* accession */ if(!setname) { ajFeattableSetDefname(ftable, defname); setname = ajTrue; } } else if(ajStrPrefixC(featReadLine, "FT ")) { if(featSwissFromLine(ftable, featReadLine, &savefeat, &savefrom, &saveto, &saveline)) found = ajTrue; } } if(featSwissFromLine(ftable, NULL, &savefeat, &savefrom, &saveto, &saveline)) found = ajTrue; if(found) { ajFeattableSetProt(ftable); } /*ajDebug("featReadSwiss returns %B\n", found);*/ ajStrDel(&savefeat); ajStrDel(&savefrom); ajStrDel(&saveto); ajStrDel(&saveline); ajStrDel(&token); ajStrDel(&defname); ajStrTokenDel(&handle); return found; } ID featGff2ProcessTagval TY static MO ajfeatread LB core XX DE Parses and adds a tag-value set to the specified AjPFeature; DE looked at 'parse_group' method in GFF::GeneFeature.pm Perl module DE for inspiration XX PN [1] PA u gf AjPFeature PD Feature PX PN [2] PA u table AjPFeattable PD Feature table PX PN [3] PA r groupfield const AjPStr PD Group field identifier PX PN [4] PA r version float PD GFF version PX RT void RD RX // static void featGff2ProcessTagval(AjPFeature gf, AjPFeattable table, const AjPStr groupfield, float version) { AjPStr TvString = NULL; AjPStr tmptag = NULL; AjBool grpset = ajFalse; AjBool escapedquote = ajFalse; /*ajDebug("featGff2ProcessTagval version %3.1f '%S'\n", version, groupfield); */ /* Validate arguments */ if(!ajStrGetLen(groupfield)) /* no tags, must be new */ return; if(E_FPEQ(version,1.0F,U_FEPS)) { featGroupSet(gf, table, groupfield); /*ajDebug("V1.0 group: '%S'\n", groupfield);*/ grpset = ajTrue; return; } /* * Version 2 or greater: parse groupfield for semicolon ';' * delimited tag-value structures, taking special care about * double quoted string context. rbsk first version of code was * adapted from GFF.pm (th/rbsk), itself inherited from AceParse.pm, * courtesy of James Gilbert */ ajStrAssignS(&TvString, groupfield); if(ajStrFindC(TvString, "\\\"") >= 0) { escapedquote = ajTrue; ajStrExchangeCC(&TvString, "\\\"", "\001"); } while(ajStrGetLen(TvString)) { if(ajRegExec(GffRegexTvTagval, TvString)) { ajRegSubI(GffRegexTvTagval, 1, &tmptag); ajRegSubI(GffRegexTvTagval, 2, &featinValTmp); ajStrTrimWhite(&featinValTmp); /*ajDebug("GffTv '%S' '%S'\n", tmptag, featinValTmp);*/ ajRegPost(GffRegexTvTagval, &TvString); if(ajStrMatchC(tmptag, "Sequence")) { featGroupSet(gf, table, featinValTmp); grpset = ajTrue; } else if(ajStrMatchC(tmptag, "ID")) { featGroupSet(gf, table, featinValTmp); grpset = ajTrue; } else if(ajStrMatchC(tmptag, "FeatFlags")) featFlagSet(gf, featinValTmp); else if(ajStrMatchC(tmptag, "featflags")) featFlagSet(gf, featinValTmp); else { /*ajDebug("Before QuoteStrip: '%S'\n", featinValTmp);*/ ajStrQuoteStrip(&featinValTmp); /*ajDebug(" After QuoteStrip: '%S'\n", featinValTmp);*/ ajFeatTagAddSS(gf,tmptag,featinValTmp); } } else { if(escapedquote) ajStrExchangeCC(&featinValTmp, "\001", "\""); /*ajDebug("Choked on '%S'\n", TvString);*/ ajFeatWarn("GFF tag parsing failed on '%S' in\n'%S'", TvString, groupfield); } } if(!grpset) { featGroupSet(gf, table, NULL); grpset = ajTrue; } ajStrDel(&TvString); ajStrDel(&tmptag); return; } ID featGff3ProcessTagval TY static MO ajfeatread LB core XX DE Parses and adds a tag-value set to the specified AjPFeature XX PN [1] PA u gf AjPFeature PD Feature PX PN [2] PA u table AjPFeattable PD Feature table PX PN [3] PA r groupfield const AjPStr PD Group field identifier PX PN [4] PA w parent AjBool* PD True if any Parent tag is found PX RT AjPStr RD Identifier if any RX // static AjPStr featGff3ProcessTagval(AjPFeature gf, AjPFeattable table, const AjPStr groupfield, AjBool *parent) { AjPStr TvString = NULL; AjPStr tmptag = NULL; AjBool grpset = ajFalse; char cp; AjPStr idstr = NULL; /*ajDebug("featGff3ProcessTagval '%S'\n", groupfield); */ *parent = ajFalse; /* Validate arguments */ if(!ajStrGetLen(groupfield)) /* no tags, must be new */ return idstr; /* * parse groupfield for semicolon ';' * delimited tag-value structures, taking special care about * double quoted string context. */ ajStrAssignS(&TvString, groupfield); while(ajStrGetLen(TvString)) { if(ajRegExec(Gff3RegexTvTagval, TvString)) { ajRegSubI(Gff3RegexTvTagval, 1, &tmptag); ajRegSubI(Gff3RegexTvTagval, 2, &featinValTmp); ajStrFmtPercentDecode(&featinValTmp); ajStrTrimWhite(&featinValTmp); /*ajDebug("Gff3Tv '%S' '%S'\n", tmptag, featinValTmp);*/ ajRegPost(Gff3RegexTvTagval, &TvString); cp = ajStrGetCharFirst(tmptag); if(isupper((int)cp)) { if(ajStrMatchC(tmptag, "ID")) { ajStrAssignS(&idstr, featinValTmp); } else if(ajStrMatchC(tmptag, "Parent")) { *parent = ajTrue; featGff3GroupSet(gf, table, featinValTmp); grpset = ajTrue; } else if(ajStrMatchC(tmptag, "Is_circular")) { ajFeattableSetCircular(table); } ajFeatGfftagAddSS(gf,tmptag,featinValTmp); } else{ if(ajStrMatchC(tmptag, "featflags")) { featGff3FlagSet(gf, featinValTmp); } else { ajFeatTagAddSS(gf,tmptag,featinValTmp); } } } else { /*ajDebug("GFF3 choked on '%S'\n", TvString);*/ ajFeatWarn("GFF3 tag parsing failed on '%S' in\n'%S'", TvString, groupfield); break; } } if(!grpset) { featGroupSet(gf, table, NULL); grpset = ajTrue; } ajStrDel(&TvString); ajStrDel(&tmptag); return idstr; } ID featGff3oldProcessTagval TY static MO ajfeatread LB core XX DE Parses and adds a tag-value set to the specified AjPFeature fror GFF3 before DE EMBOSS 6.4.0 XX PN [1] PA u gf AjPFeature PD Feature PX PN [2] PA u table AjPFeattable PD Feature table PX PN [3] PA r groupfield const AjPStr PD Group field identifier PX RT void RD RX // static void featGff3oldProcessTagval(AjPFeature gf, AjPFeattable table, const AjPStr groupfield) { AjPStr TvString = NULL; AjPStr tmptag = NULL; AjBool grpset = ajFalse; AjBool escapedquote = ajFalse; /*ajDebug("featGff3oldProcessTagval '%S'\n", groupfield); */ /* Validate arguments */ if(!ajStrGetLen(groupfield)) /* no tags, must be new */ return; /* * parse groupfield for semicolon ';' * delimited tag-value structures, taking special care about * double quoted string context. */ ajStrAssignS(&TvString, groupfield); if(ajStrFindC(TvString, "\\\"") >= 0) { escapedquote = ajTrue; ajStrExchangeCC(&TvString, "\\\"", "\001"); } while(ajStrGetLen(TvString)) { if(ajRegExec(Gff3oldRegexTvTagval, TvString)) { ajRegSubI(Gff3oldRegexTvTagval, 1, &tmptag); ajRegSubI(Gff3oldRegexTvTagval, 2, &featinValTmp); ajStrTrimWhite(&featinValTmp); /*ajDebug("Gff3Tv '%S' '%S'\n", tmptag, featinValTmp);*/ ajRegPost(Gff3oldRegexTvTagval, &TvString); if(ajStrMatchC(tmptag, "Sequence")) { featGroupSet(gf, table, featinValTmp); grpset = ajTrue; } else if(ajStrMatchC(tmptag, "ID")) { featGroupSet(gf, table, featinValTmp); grpset = ajTrue; } else if(ajStrMatchC(tmptag, "FeatFlags")) featFlagSet(gf, featinValTmp); else if(ajStrMatchC(tmptag, "featflags")) featFlagSet(gf, featinValTmp); else { if(escapedquote) ajStrExchangeCC(&featinValTmp, "\001", "\""); /*ajDebug("GFF3 before QuoteStrip: '%S'\n", featinValTmp);*/ ajStrQuoteStrip(&featinValTmp); /*ajDebug(" GFF3 after QuoteStrip: '%S'\n", featinValTmp);*/ ajFeatTagAddSS(gf,tmptag,featinValTmp); } } else { /*ajDebug("GFF3 choked on '%S'\n", TvString);*/ ajFeatWarn("GFF3 tag parsing failed on '%S' in\n'%S'", TvString, groupfield); } } if(!grpset) { featGroupSet(gf, table, NULL); grpset = ajTrue; } ajStrDel(&TvString); ajStrDel(&tmptag); return; } ID featSwissFromLine TY static MO ajfeatread LB core XX DE Read input file in Swiss format DE DE Format is :- DE 0-1 FT DE 5-12 Keyname DE 14-19 From DE 21-26 To DE 34-74 Description XX PN [1] PA u thys AjPFeattable PD Feature table PX PN [2] PA r origline const AjPStr PD Input line PX PN [3] PA w savefeat AjPStr* PD Stored feature type PX PN [4] PA w savefrom AjPStr* PD Continued from position PX PN [5] PA w saveto AjPStr* PD Continued to position PX PN [6] PA w saveline AjPStr* PD Continued tag-value pairs PX RT AjPFeature RD New feature. RX // static AjPFeature featSwissFromLine(AjPFeattable thys, const AjPStr origline, AjPStr* savefeat, AjPStr* savefrom, AjPStr* saveto, AjPStr* saveline) { AjPStr temp = NULL; static AjPFeature gf = NULL; /* made static so that it's easy to add second line of description */ AjBool newft = ajFalse; /*ajDebug("featSwissFromLine..........\n'%S'\n", origline);*/ if(!featinSourceSwiss) featinSourceSwiss = ajStrNewC("SWISSPROT"); if(origline) newft = ajRegExec(SwRegexNew, origline); if(newft || !origline) /* process the last feature */ { /* ajDebug("++ feat+from+to '%S' '%S' '%S'\n+ saveline '%S'\n", *savefeat, *savefrom, *saveto, *saveline); */ if(ajStrGetLen(*savefrom)) /* finish the current feature */ gf = featSwissProcess(thys, *savefeat, *savefrom, *saveto, featinSourceSwiss, *saveline); else /* maybe there were no features */ gf = NULL; ajStrDel(savefeat); ajStrDel(savefrom); ajStrDel(saveto); ajStrDel(saveline); } if(!origline) /* we are only cleaning up */ return gf; if(newft) /* if new feature initialise for it */ { ajRegSubI(SwRegexNew, 2, savefeat); ajRegSubI(SwRegexNew, 3, savefrom); ajRegSubI(SwRegexNew, 4, saveto); ajRegSubI(SwRegexNew, 5, saveline); ajStrTrimWhite(savefeat); /*ajDebug(" newft type '%S' from '%S' to '%S' rest '%S'\n", *savefeat, *savefrom, *saveto, *saveline);*/ return gf; } else /* more tag-values */ { if(ajRegExec(SwRegexNext, origline)) { ajRegSubI(SwRegexNext, 1, &temp); ajStrAppendC(saveline, " "); ajStrAppendS(saveline, temp); } else ajFeatWarn("%S: Bad SwissProt feature line:\n%S", thys->Seqid, origline); } ajStrDel(&temp); return gf; } ID featSwissProcess TY static MO ajfeatread LB core XX DE Processes one feature location and qualifier tags for SwissProt XX PN [1] PA u thys AjPFeattable PD Feature table PX PN [2] PA r feature const AjPStr PD Feature type key PX PN [3] PA r fromstr const AjPStr PD Feature start PX PN [4] PA r tostr const AjPStr PD Feature end PX PN [5] PA r source const AjPStr PD Feature table source PX PN [6] PA r tags const AjPStr PD Feature qualifier tags string PX RT AjPFeature RD Feature as inserted into the feature table RX // static AjPFeature featSwissProcess(AjPFeattable thys, const AjPStr feature, const AjPStr fromstr, const AjPStr tostr, const AjPStr source, const AjPStr tags) { AjPFeature ret; ajint Start = 0; ajint End = 0; ajint flags = 0; AjPStr note = NULL; AjPStr comment = NULL; AjPStr ftid = NULL; AjPStr tagstr = NULL; if(!featinTagNote) ajStrAssignC(&featinTagNote, "note"); if(!featinTagComm) ajStrAssignC(&featinTagComm, "comment"); if(!featinTagFtid) ajStrAssignC(&featinTagFtid, "ftid"); switch(ajStrGetCharFirst(fromstr)) { case '?': flags |= AJFEATFLAG_START_UNSURE; ajStrAssignS(&featinTmpStr, fromstr); ajStrCutStart(&featinTmpStr, 1); if(!ajStrToInt(featinTmpStr, &Start)) Start = 0; break; case '<': case '>': /* just to be sure */ flags |= AJFEATFLAG_START_BEFORE_SEQ; ajStrAssignS(&featinTmpStr, fromstr); ajStrCutStart(&featinTmpStr, 1); if(!ajStrToInt(featinTmpStr, &Start)) Start = 0; break; default: if(!ajStrToInt(fromstr, &Start)) Start = 0; } switch(ajStrGetCharFirst(tostr)) { case '?': flags |= AJFEATFLAG_END_UNSURE; ajStrAssignS(&featinTmpStr, tostr); ajStrCutStart(&featinTmpStr, 1); if(!ajStrToInt(featinTmpStr, &End)) End = 0; break; case '<': /* just to be sure */ case '>': flags |= AJFEATFLAG_END_AFTER_SEQ; ajStrAssignS(&featinTmpStr, tostr); ajStrCutStart(&featinTmpStr, 1); if(!ajStrToInt(featinTmpStr, &End)) End = 0; break; default: if(!ajStrToInt(tostr, &End)) End = 0; } ajStrDelStatic(&featinTmpStr); ret = ajFeatNewProtFlags(thys, source, /* source sequence */ feature, Start, End, 0.0, flags); ajStrAssignS(&tagstr, tags); ajStrTrimC(&tagstr, " ."); if(ajRegExec(SwRegexFtid, tagstr)) { ajRegSubI(SwRegexFtid, 1, ¬e); ajRegSubI(SwRegexFtid, 2, &ftid); /*ajDebug("Swiss ftid found\n");*/ /*ajDebug("ftid: '%S'\n",ftid);*/ ajStrAssignS(&tagstr, note); ajStrTrimC(&tagstr, " ."); } if(ajRegExec(SwRegexComment, tagstr)) { ajRegSubI(SwRegexComment, 1, ¬e); ajRegSubI(SwRegexComment, 2, &comment); /*ajDebug("Swiss comment found\nNote: '%S'\nComment: '%S'\n", note, comment);*/ ajStrTrimC(¬e, " ."); if(ajStrGetLen(note)) ajFeatTagAddSS(ret, featinTagNote, note); if(ajStrGetLen(comment)) ajFeatTagAddSS(ret, featinTagComm, comment); } else { /*ajDebug("Simple swiss note: '%S'\n", tagstr);*/ if(ajStrGetLen(tagstr)) ajFeatTagAddSS(ret, featinTagNote, tagstr); } if(ajStrGetLen(ftid)) ajFeatTagAddSS(ret, featinTagFtid, ftid); ajStrDel(¬e); ajStrDel(&comment); ajStrDel(&ftid); ajStrDel(&tagstr); return ret; } ID featEmblFromLine TY static MO ajfeatread LB core XX DE Converts an input EMBL format line into a feature. DE Starts a new feature by processing any existing feature data. DE Creates or appends the type, location and tag-value pairs. DE With a NULL as the input line, simply processes the type, location DE and tag-values. XX PN [1] PA u thys AjPFeattable PD Feature table PX PN [2] PA r origline const AjPStr PD Input line (NULL to process last PD feature at end of input) PX PN [3] PA w savefeat AjPStr* PD Stored feature type PX PN [4] PA w saveloc AjPStr* PD Continued location PX PN [5] PA w saveline AjPStr* PD Continued tag-value pairs PX RT AjPFeature RD New feature RX // static AjPFeature featEmblFromLine(AjPFeattable thys, const AjPStr origline, AjPStr* savefeat, AjPStr* saveloc, AjPStr* saveline) { static AjPFeature gf = NULL; /* so tag-values can be added LATER */ AjPStr temp = NULL; AjBool newft = ajFalse; AjBool doft = ajFalse; if(!featinSourceEmbl) featinSourceEmbl = ajStrNewC("EMBL"); if(origline) { /* As BufferFile can't be edited */ ajStrAssignS(&featProcessLine,origline); /* chop first 5 characters */ ajStrCutStart(&featProcessLine, 5); /* look for the feature key */ if(ajStrGetCharFirst(featProcessLine) != ' ') { newft = ajTrue; if(ajStrGetLen(*saveloc)) doft = ajTrue; } } else { ajStrAssignClear(&featProcessLine); newft = ajFalse; /* no new data, just process */ if(ajStrGetLen(*saveloc)) doft = ajTrue; } /* ajDebug("+ newft: %B doft: %B\n+ line '%S'\n", newft, doft, featProcessLine); */ if(doft) /* process the last feature */ { /* ajDebug("++ saveloc '%S'\n+ saveline '%S'\n", *saveloc, *saveline); */ gf = featEmblProcess(thys, *savefeat, featinSourceEmbl, saveloc, saveline); ajStrDelStatic(saveloc); ajStrDelStatic(saveline); } if(!origline) /* we are only cleaning up */ { if(doft) return gf; else return NULL; } ajStrRemoveWhiteExcess(&featProcessLine); if(newft) /* if new feature initialise for it */ { ajStrTokenAssignC(&featEmblSplit, featProcessLine, " "); ajStrTokenNextParse(&featEmblSplit, savefeat); if(ajStrTokenNextParseC(&featEmblSplit, " /", saveloc)) ajStrTokenRestParse(&featEmblSplit, saveline); else ajStrAssignResC(saveline, 512, ""); /* location only */ ajStrTokenDel(&featEmblSplit); return gf; } else if(!ajStrGetLen(*saveline)) /* no tag-values yet, more location? */ { if(ajStrGetCharFirst(featProcessLine) != '/') { ajStrTokenAssignC(&featEmblSplit, featProcessLine, " "); ajStrTokenNextParse(&featEmblSplit, &temp); if(ajStrGetLen(temp)) ajStrAppendS(saveloc, temp); ajStrTokenRestParse(&featEmblSplit, &temp); if(ajStrGetLen(temp)) ajStrAppendS(saveline, temp); ajStrDel(&temp); ajStrTokenDel(&featEmblSplit); return gf; } } /* tag-values continued */ ajStrAppendK(saveline, ' '); ajStrAppendS(saveline, featProcessLine); return gf; } ID featEmblProcess TY static MO ajfeatread LB core XX DE Processes one feature location and qualifier tags for EMBL XX PN [1] PA u thys AjPFeattable PD Feature table PX PN [2] PA r feature const AjPStr PD Feature type key PX PN [3] PA r source const AjPStr PD Feature table source PX PN [4] PA w loc AjPStr* PD Feature location PX PN [5] PA w tags AjPStr* PD Feature qualifier tags string PX RT AjPFeature RD Feature as inserted into the feature table RX // static AjPFeature featEmblProcess(AjPFeattable thys, const AjPStr feature, const AjPStr source, AjPStr* loc, AjPStr* tags) { AjPFeature ret = NULL; AjPFeature gf = NULL; /* AjPFeature gfpos = NULL; */ AjPStr tag = NULL; AjPStr val = NULL; AjPStr opnam = NULL; AjPStr opval = NULL; AjPStr prestr = NULL; AjBool Fwd = ajTrue; AjBool LocFwd = ajTrue; AjPStr begstr = NULL; AjPStr delstr = NULL; AjPStr endstr = NULL; AjPStr locstr = NULL; AjPStr rest = NULL; AjPStr tagname = NULL; AjPStr tagrest = NULL; AjBool Simple = ajFalse; /* Simple - single position (see also label) */ AjBool BegBound = ajFalse; AjBool EndBound = ajFalse; ajuint BegNum = 0; ajuint EndNum = 0; ajuint Beg2 = 0; ajuint End2 = 0; ajuint tmpbeg = 0; ajuint tmpend = 0; ajuint Flags = 0; ajuint tmpflags = 0; AjBool Between = ajFalse; AjBool Join = ajFalse; AjBool Order = ajFalse; char Strand = '+'; AjBool Parent = ajTrue; ajint Frame = 0; float Score = 0.0; AjBool HasOper = ajFalse; AjBool RemoteId = ajFalse; AjBool IsLabel = ajFalse; /* uses obsolete label */ ajint Exon = 0; ajlong ipos; ajint itags = 0; const AjPStr tmpft = NULL; const AjPStr tmpfeatid = NULL; ajuint noparentflags = 0; noparentflags = ~(AJFEATFLAG_POINT | AJFEATFLAG_BETWEEN_SEQ | AJFEATFLAG_REMOTEID | AJFEATFLAG_LABEL | AJFEATFLAG_START_TWO | AJFEATFLAG_END_TWO | AJFEATFLAG_START_BEFORE_SEQ | AJFEATFLAG_END_AFTER_SEQ); ajStrRemoveWhite(loc); /* no white space needed */ ajStrRemoveWhiteExcess(tags); /* single spaces only */ /*ajDebug("Clean location '%S'\n", *loc);*/ /*ajDebug("Clean tags '%S'\n", *tags);*/ ajStrAssignS(&opval, *loc); ipos = ajStrFindAnyK(opval, ','); /* multiple locations */ if(ipos >= 0) { /* ajDebug("Multiple locations, test operator(s)\n"); */ while(ajStrGetLen(opval) && featEmblOperOut(opval, &opnam, &featinTmpStr)) { if(!ajStrHasParentheses(featinTmpStr)) break; /* ajDebug("OperOut %S( '%S' )\n", opnam, featinTmpStr); */ if(ajStrMatchCaseC(opnam, "complement")) Fwd = !Fwd; else if(ajStrMatchCaseC(opnam, "one_of")) Order = ajTrue; else if(ajStrMatchCaseC(opnam, "join")) Join = ajTrue; else if(ajStrMatchCaseC(opnam, "order")) Order = ajTrue; else if(ajStrMatchCaseC(opnam, "group")) Order = ajTrue; else ajFeatWarn("%S: unrecognised operator '%S()' in '%S'", thys->Seqid, opnam, opval); ajStrAssignS(&opval, featinTmpStr); } } while(ajStrGetLen(opval)) { LocFwd = Fwd; BegBound = ajFalse; EndBound = ajFalse; Simple = ajFalse; Between = ajFalse; BegNum = EndNum = Beg2 = End2 = 0; HasOper = ajFalse; RemoteId = ajFalse; IsLabel = ajFalse; ajStrDelStatic(&featId); ajStrDelStatic(&featLabel); /* check for complement() */ /* set locstr as the whole (or rest) of the location */ if(featEmblOperIn(opval, &opnam, &locstr, &featinTmpStr)) { /* ajDebug("OperIn %S( '%S' )\n", opnam, locstr); */ if(ajStrMatchCaseC(opnam, "complement")) LocFwd = !LocFwd; ajStrAssignS(&opval, featinTmpStr); /* ajDebug("rest: '%S'\n", opval); */ HasOper = ajTrue; } else { ajStrAssignS(&locstr, opval); /* ajDebug("OperIn simple '%S'\n", locstr); */ } if(featEmblOperNone(locstr, &featId, &featinTmpStr, &rest)) /* one exon */ { /* ajDebug("OperNone '%S' \n", featinTmpStr); */ if(ajStrGetLen(featId)) { /* ajDebug("External entryid '%S'\n", featId); */ RemoteId = ajTrue; } if(!featEmblLoc(featinTmpStr, &begstr, &Between, &Simple, &endstr)) { ajStrAssignS(&begstr, featinTmpStr); ajStrAssignS(&endstr, begstr); Simple = ajTrue; ajDebug("Bad feature numeric location '%S' in '%S' - " "test later for label", begstr, locstr); } ajStrAssignS(&featinTmpStr, rest); if(!HasOper) ajStrAssignS(&opval, featinTmpStr); if(featEmblLocNum(begstr, &BegBound, &BegNum)) { Beg2 = 0; /* ajDebug("Begin '%S' %d Bound: %B\n", begstr, BegNum, BegBound); */ } else if(featEmblLocRange(begstr, &BegNum, &Beg2)) { BegBound = ajFalse; /* ajDebug("Begin range (%d . %d)\n", BegNum, Beg2); */ } else { /* ajDebug("Begin is a label '%S'\n", begstr); */ IsLabel = ajTrue; Simple = ajTrue; ajStrAssignS(&featLabel, begstr); ajFeatWarn("%S: Simple feature location '%S' in '%S'", thys->Seqid, begstr, locstr); } if(featEmblLocNum(endstr, &EndBound, &EndNum)) { End2 = 0; /* ajDebug(" End '%S' %d Bound: %B\n", endstr, EndNum, EndBound); */ } else if(featEmblLocRange(endstr, &End2, &EndNum)) { EndBound = ajFalse; /* ajDebug(" End range (%d . %d)\n", End2, EndNum); */ } else { IsLabel = ajTrue; Simple = ajTrue; ajStrAssignS(&featLabel, endstr); /* ajDebug(" End is a label '%S'\n", endstr); */ ajErr("%S: Simple feature end '%S' in '%S'", thys->Seqid, begstr, locstr); } } else { ajErr("Unable to parse location:\n'%S'", opval); } /* location has been read in, now store it */ Flags = 0; if(LocFwd) Strand = '+'; else Strand = '-'; if(Simple) Flags |= AJFEATFLAG_POINT; if(Between) Flags |= AJFEATFLAG_BETWEEN_SEQ; if(End2) Flags |= AJFEATFLAG_END_TWO; if(Beg2) Flags |= AJFEATFLAG_START_TWO; if(BegBound) Flags |= AJFEATFLAG_START_BEFORE_SEQ; if(EndBound) Flags |= AJFEATFLAG_END_AFTER_SEQ; if(RemoteId) Flags |= AJFEATFLAG_REMOTEID; if(IsLabel) Flags |= AJFEATFLAG_LABEL; if(IsLabel) ajFeatWarn("%S: Feature location with label '%S'", thys->Seqid, locstr); if(Join || Order) Flags |= AJFEATFLAG_MULTIPLE; if(Order) Flags |= AJFEATFLAG_ORDER; if(Parent) { if(!Fwd) Flags |= AJFEATFLAG_COMPLEMENT_MAIN; } /* ajDebug("Calling featFeatNew, Flags: %x\n", Flags); */ tmpft = ajFeattypeGetInternalNuc(feature); if(Parent) { if(ajStrGetLen(opval) && RemoteId) { tmpbeg = 0; tmpend = 0; tmpflags = Flags & noparentflags; tmpfeatid = NULL; } else { tmpbeg = BegNum; tmpend = EndNum; tmpflags = Flags; tmpfeatid = featId; } gf = ajFeatNewNucFlags(thys, source, /* source sequence */ tmpft, tmpbeg, tmpend, Score, Strand, Frame, Exon, Beg2, End2, tmpfeatid, featLabel, tmpflags); /* gfpos = gf; */ } if(!Parent || ajStrGetLen(opval)) { Exon++; /*gfpos = */ ajFeatNewNucFlagsSub(thys, gf, source, /* source sequence */ tmpft, BegNum, EndNum, Score, Strand, Frame, Exon, Beg2, End2, featId, featLabel, Flags); if(!RemoteId) { if(Fwd) { if(!gf->Start || gf->Start > BegNum) gf->Start = BegNum; if(!gf->End || gf->End < EndNum) gf->End = EndNum; } else { if(!gf->End || gf->End > BegNum) gf->End = BegNum; if(!gf->Start || gf->Start < EndNum) gf->Start = EndNum; } } } if(Parent) { ret = gf; Parent = ajFalse; Exon = 1; } } while(ajStrGetLen(*tags)) { itags++; if(featEmblTvTagVal(tags, &tag, &val)) { ajStrQuoteStrip(&val); if(ajStrMatchC(tag, "codon_start")) { ajStrToInt(val, &Frame); gf->Frame = Frame; } if(ajStrMatchC(tag, "note") && ajStrGetCharFirst(val) == '*') { ajUser("special note '%S'", val); ajStrCutStart(&val, 1); ajStrExtractFirst(val, &tagrest, &tagname); if(ajStrMatchC(tagname, "Type")) { ajStrExtractFirst(tagrest, &tagname, &gf->Type); } else { ajFeatGfftagAddSS(gf, tagname, tagrest); } continue; } if(!ajFeatTagAddSS(ret, tag, val)) ajFeatWarn("%S: Bad value '%S' for tag '/%S'", thys->Seqid, val, tag); } else if(featEmblTvRest(tags, &featinTmpStr)) { /* anything non-whitespace up to '/' is bad */ ajFeatWarn("Bad feature syntax %S: skipping '%S'", thys->Seqid, featinTmpStr); } else { ajFeatWarn("Bad feature syntax %S: giving up at '%S'", thys->Seqid, *tags); ajStrAssignClear(tags); } } ajDebug("featEmblProcess found %d feature tags\n", itags); ajStrDelStatic(&featinTmpStr); ajStrDel(&prestr); ajStrDel(&val); ajStrDel(&tag); ajStrDel(&begstr); ajStrDel(&delstr); ajStrDel(&opnam); ajStrDel(&opval); ajStrDel(&locstr); ajStrDel(&endstr); ajStrDel(&rest); ajStrDel(&tagname); ajStrDel(&tagrest); return ret; } ID featEmblOperOut TY static MO ajfeatread LB core XX DE Process operator and contents up to matching right parenthesis at end DE DE Return operator and content of parentheses DE DE Ignores internal operators DE For example: DE DE join(complement(12..15),complement(1..3)) DE returns "join" and "complement(12..15),complement(1..3)" DE DE complement(12..15),complement(1..3) DE fails because both complement() operators cover only part of the string DE and will be processed as separate locations. XX PN [1] PA r loc const AjPStr PD Feature location PX PN [2] PA w opnam AjPStr* PD Operator name PX PN [3] PA w value AjPStr* PD Operator value PX RT AjBool RD ajTrue if an operator was found RX // static AjBool featEmblOperOut(const AjPStr loc, AjPStr* opnam, AjPStr* value) { ajint left=0; ajint right=0; ajuint ipos=0; ajint bracepos = 0; const char* cp = ajStrGetPtr(loc); if(*cp == '(') return ajFalse; /* starts with '(' */ if(!isalpha((ajint)*cp)) return ajFalse; /* starts with location */ if(!islower((ajint)*cp)) return ajFalse; /* starts with location */ while(*cp) { ipos ++; switch(*cp++) { case ',': if(!left) return ajFalse; /* loc,loc,oper(loc),loc */ break; case '(': if(!left) bracepos = ipos-1; left++; break; case ')': right++; if(right == left) { if(ipos != ajStrGetLen(loc)) /* partial operator */ return ajFalse; } else if(right > left) return ajFalse; break; default: break; } } if(!left) return ajFalse; /* no parentheses */ if(right != left) return ajFalse; /* unmatched '(' */ ajStrAssignSubS(opnam, loc, 0, bracepos-1); ajStrAssignSubS(value, loc, bracepos+1, ipos-2); return ajTrue; } ID featEmblOperIn TY static MO ajfeatread LB core XX DE Process operator and contents up to matching right parenthesis at end DE DE Return operator and content of parentheses DE DE For operators with single locations, though the location can include () DE For example: DE DE complement(12..15) DE DE complement((11.12)..(14.15)) XX PN [1] PA r loc const AjPStr PD Feature location PX PN [2] PA w opnam AjPStr* PD Operator name PX PN [3] PA w value AjPStr* PD Operator value PX PN [4] PA w rest AjPStr* PD Remainder of location PX RT AjBool RD ajTrue if an operator was found RX // static AjBool featEmblOperIn(const AjPStr loc, AjPStr* opnam, AjPStr* value, AjPStr* rest) { ajint left=0; ajint right=0; ajint depth; ajuint ipos=0; ajint bracepos = 0; const char* cp = ajStrGetPtr(loc); if(*cp == '(') return ajFalse; /* starts with '(' */ while(*cp) { if(*cp == ',') break; ipos ++; switch(*cp++) { case '(': if(!left) bracepos = ipos-1; left++; if(left>3) return ajFalse; depth = left - right; if(depth > 2) return ajFalse; break; case ')': right++; if(right>3) return ajFalse; if(right > left) return ajFalse; break; default: break; } } if(!left) return ajFalse; /* no parentheses */ if(right != left) return ajFalse; /* unmatched '(' */ ajStrAssignSubS(opnam, loc, 0, bracepos-1); ajStrAssignSubS(value, loc, bracepos+1, ipos-2); if(ipos < ajStrGetLen(loc)) ajStrAssignSubS(rest, loc, ipos+1, -1); else ajStrAssignClear(rest); return ajTrue; } ID featEmblOperNone TY static MO ajfeatread LB core XX DE Process anything up to ':' as an entry ID (a feature in another entry) DE Anything up to ',' is this location XX PN [1] PA r loc const AjPStr PD Feature location PX PN [2] PA w entryid AjPStr* PD Entry ID if found, else empty PX PN [3] PA w value AjPStr* PD Location with entryID removed PX PN [4] PA w rest AjPStr* PD Remainder of location PX RT AjBool RD ajTrue if a match was found RD ajFalse means an error occurred RX // static AjBool featEmblOperNone(const AjPStr loc, AjPStr* entryid, AjPStr* value, AjPStr* rest) { ajuint ipos=0; ajuint idpos = 0; const char* cp = ajStrGetPtr(loc); while(*cp) { if(*cp == ',') break; ipos ++; switch(*cp++) { case ':': idpos = ipos; break; default: break; } } if(idpos) { ajStrAssignSubS(entryid, loc, 0, idpos-2); ajStrAssignSubS(value, loc, idpos, ipos-1); } else { ajStrAssignClear(entryid); ajStrAssignSubS(value, loc, 0, ipos-1); } if(ipos < ajStrGetLen(loc)) ajStrAssignSubS(rest, loc, ipos+1, -1); else ajStrAssignClear(rest); return ajTrue; } ID featEmblLoc TY static MO ajfeatread LB core XX DE Process simple feature location in the forms begin..end or begin^end XX PN [1] PA r loc const AjPStr PD Feature location PX PN [2] PA w begstr AjPStr* PD Start position PX PN [3] PA w between AjBool* PD ajTrue for '^' as a position between 2 bases PX PN [4] PA w simple AjBool* PD ajTrue for single base location PX PN [5] PA w endstr AjPStr* PD End position PX RT AjBool RD ajTrue if a match was found RD ajFalse means an error occurred RX // static AjBool featEmblLoc(const AjPStr loc, AjPStr* begstr, AjBool* between, AjBool* simple, AjPStr* endstr) { ajint ipos=0; ajint bracket=0; AjBool numeric=ajTrue; AjBool dot = ajFalse; AjBool end = ajFalse; ajint iend = 0; ajint ibeg = 0; const char* cp = ajStrGetPtr(loc); *between = ajFalse; *simple = ajFalse; ajStrAssignClear(begstr); ajStrAssignClear(endstr); while(*cp) { ipos ++; switch(*cp) { case '.': if(bracket) break; if(dot) { if(end) return ajFalse; end = ajTrue; ibeg = ipos - 2; iend = ipos - 1; } else dot = ajTrue; break; case '^': ibeg = ipos - 1; iend = ipos - 1; end = ajTrue; *between = ajTrue; break; case '(': bracket++; numeric = ajFalse; break; case ')': if(!bracket) return ajFalse; bracket--; break; default: if(!isdigit((ajint) *cp)) numeric = ajFalse; break; } cp++; } if(dot && !end) return ajFalse; if(end) { ajStrAssignSubS(begstr, loc, 0, ibeg-1); ajStrAssignSubS(endstr, loc, iend+1, ipos-1); } else /* simple location e.g. 43 */ { *simple = ajTrue; ajStrAssignSubS(begstr, loc, 0, -1); if(numeric) ajStrAssignSubS(endstr, loc, 0, -1); else ajStrAssignClear(endstr); } return ajTrue; } ID featEmblLocNum TY static MO ajfeatread LB core XX DE Process a single base position with a possible less than or greater than DE character in front to mark a position beyond the bounds of the sequence. XX PN [1] PA r loc const AjPStr PD Feature location PX PN [2] PA w bound AjBool* PD ajTrue if less than or greater than specified PX PN [3] PA w num ajuint* PD Base position PX RT AjBool RD ajTrue if a match was found RD ajFalse means an error occurred RX // static AjBool featEmblLocNum(const AjPStr loc, AjBool* bound, ajuint* num) { const char* cp = ajStrGetPtr(loc); *bound = ajFalse; *num = 0; if((*cp == '<') || (*cp == '>')) { *bound = ajTrue; cp++; } while(*cp) { if(!isdigit((ajint)*cp)) return ajFalse; *num = 10* (*num) + (*cp - '0'); cp++; } return ajTrue; } ID featEmblLocRange TY static MO ajfeatread LB core XX DE Process a range position (12.14) meaning any base from 12 to 14 as the DE start or end of a feature location. XX PN [1] PA r loc const AjPStr PD Feature location PX PN [2] PA w num1 ajuint* PD First base position PX PN [3] PA w num2 ajuint* PD Last base position PX RT AjBool RD ajTrue if a match was found RD ajFalse means an error occurred RX // static AjBool featEmblLocRange(const AjPStr loc, ajuint* num1, ajuint* num2) { ajuint ipos=0; AjBool dot = ajFalse; const char* cp = ajStrGetPtr(loc); if(*cp != '(') return ajFalse; if(ajStrGetCharLast(loc) != ')') return ajFalse; *num1 = 0; *num2 = 0; while(*cp) { ipos ++; switch(*cp) { case '.': if(dot) return ajFalse; dot = ajTrue; break; case '(': if(ipos != 1) return ajFalse; break; case ')': if(ipos != ajStrGetLen(loc)) return ajFalse; break; default: if(!isdigit((ajint)*cp)) return ajFalse; if(dot) *num2 = 10* (*num2) + (*cp - '0'); else *num1 = 10* (*num1) + (*cp - '0'); break; } cp++; } if(!dot) return ajFalse; return ajTrue; } ID featEmblTvTagVal TY static MO ajfeatread LB core XX DE Tests for a feature tag value next in the input string XX PN [1] PA u tags AjPStr* PD Feature tag-values string, returned with PD current tag-value removed PX PN [2] PA w name AjPStr* PD Feature tag name PX PN [3] PA w value AjPStr* PD Feature tag value PX RT AjBool RD ajTrue if a match was found RD ajFalse means an error occurred RX // static AjBool featEmblTvTagVal(AjPStr* tags, AjPStr* name, AjPStr* value) { const char* cp; const char* cq; ajint i; AjPStr testtags = NULL; AjPStr testname = NULL; AjPStr testvalue = NULL; AjPStr tmptag = NULL; static ajint depth = 0; tmptag = ajStrNewS(*tags); cp = ajStrGetPtr(tmptag); ajStrAssignClear(value); while((*cp == ' ')) cp++; if(*cp++ != '/') { ajStrDel(&tmptag); return ajFalse; } cq = cp; i=0; while(isalpha((ajint)*cp) || (*cp == '_')) { i++; cp++; } ajStrAssignLenC(name, cq, i); while(*cp == ' ') cp++; if(!ajStrGetLen(*name)) { ajStrDel(&tmptag); return ajFalse; } switch(*cp) { case '\0': /* /name is end of input */ ajStrAssignClear(tags); ajStrAssignClear(value); ajStrDel(&tmptag); return ajTrue; case '/': /* /name then next tag, no value */ ajStrAssignC(tags, cp); ajStrAssignClear(value); ajStrDel(&tmptag); return ajTrue; case '=': /* /name=value */ break; default: /* anything else is bad */ ajStrDel(&tmptag); return ajFalse; } cp++; if(*cp == '"') /* /name="... */ { cq = cp; cp++; i=0; while(*cp) { i++; if(*cp == '"') { ajStrAppendLenC(value, cq, i); i = 0; cp++; if(!*cp || (*cp != '"')) /* all done */ { while(*cp == ' ') cp++; if(*cp) ajStrAssignC(tags, cp); else ajStrAssignClear(tags); ajStrDel(&testtags); ajStrDel(&testname); ajStrDel(&testvalue); ajStrDel(&tmptag); return ajTrue; } else /* "" but is it really internal */ { if(depth) { ajStrDel(&testtags); ajStrDel(&testname); ajStrDel(&testvalue); ajStrDel(&tmptag); return ajTrue; } depth++; ajStrAssignC(&testtags, cp); if(featEmblTvTagVal(&testtags, &testname, &testvalue)) { /* looks like an extra closing quote */ depth--; ajStrDel(&testtags); ajStrDel(&testname); ajStrDel(&testvalue); cp++; while(*cp == ' ') cp++; ajStrAssignC(tags, cp); ajStrDel(&tmptag); return ajTrue; } else /* really an internal " */ { depth--; cq = cp-1; i = 1; } } } cp++; } ajStrAssignClear(tags); } else { cq = cp; i=0; while(*cp && (*cp != ' ') && (*cp != '/')) { cp++; i++; } ajStrAssignLenC(value, cq, i); ajStrAssignC(tags, cp); ajStrDel(&tmptag); return ajTrue; } ajStrDel(&testtags); ajStrDel(&testname); ajStrDel(&testvalue); ajStrDel(&tmptag); return ajFalse; } ID featEmblTvRest TY static MO ajfeatread LB core XX DE Process bad tag-value text up to next qualifier XX PN [1] PA u tags AjPStr* PD Feature tag-values string, returned with PD skipped text removed PX PN [2] PA w skip AjPStr* PD Skipped text PX RT AjBool RD ajTrue if a match was found RD ajFalse means an error occurred RX // static AjBool featEmblTvRest(AjPStr* tags, AjPStr* skip) { AjPStr testtags = NULL; AjPStr testname = NULL; AjPStr testvalue = NULL; AjBool ok = ajFalse; const char* cp = ajStrGetPtr(*tags); ajStrAssignClear(skip); while(*cp) { if(*cp == '/') { ajStrAssignC(&testtags,cp); ok = featEmblTvTagVal(&testtags, &testname, &testvalue); ajStrDel(&testtags); ajStrDel(&testname); ajStrDel(&testvalue); if(ok) { ajStrAssignC(tags, cp); return ajTrue; } ajStrAppendK(skip, *cp); } else { ajStrAppendK(skip, *cp); } cp++; } ajStrAssignClear(tags); return ajTrue; } ID featGff2FromLine TY static MO ajfeatread LB core XX DE Converts an input GFF format line into a feature XX PN [1] PA u thys AjPFeattable PD Feature table PX PN [2] PA r line const AjPStr PD Input line PX PN [3] PA r version float PD GFF version (1.0 for old format behaviour) PX RT AjPFeature RD New feature RX // static AjPFeature featGff2FromLine(AjPFeattable thys, const AjPStr line, float version) { AjPFeature gf = NULL; AjPStr token = NULL; ajint Start = 0; ajint End = 0; float fscore = 0.0; ajint itemp; char strand; ajint frame; if(!ajStrGetLen(line)) return NULL; ajStrTokenAssignC(&featGffSplit, line, "\t"); if(!ajStrTokenNextParse(&featGffSplit, &featSeqid)) /* seqname */ goto Error; if(!ajStrTokenNextParse(&featGffSplit, &featSource)) /* source */ goto Error; if(!ajStrTokenNextParse(&featGffSplit, &featFeature)) /* feature */ goto Error; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* start */ goto Error; if(!ajStrToInt(token, &Start)) Start = 0; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* end */ goto Error; if(!ajStrToInt(token, &End)) End = 0; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* score */ goto Error; if(!ajStrToFloat(token, &fscore)) fscore = 0.0; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* strand */ goto Error; if(!ajStrCmpC(token,"+")) strand = '+'; else if(!ajStrCmpC(token,"-")) strand = '-'; else strand = '\0'; /* change to \0 later */ if(!ajStrTokenNextParse(&featGffSplit, &token)) /* frame */ goto Error; if(!ajStrCmpC(token,"0")) frame = 1; else if(!ajStrCmpC(token,"1")) frame = 2; else if(!ajStrCmpC(token,"2")) frame = 3; else frame = 0; if(strand == '-') { if(Start < End) { itemp = Start; Start = End; End = itemp; } } /* feature object construction and group tag */ if(ajStrMatchC(thys->Type, "P")) gf = ajFeatNewProt(thys, featSource, featFeature, Start, End, fscore); else gf = ajFeatNewNuc(thys, featSource, featFeature, Start, End, fscore, strand, frame, 0,0,0, NULL, NULL); if(ajStrTokenRestParse(&featGffSplit, &featGroup)) featGff2ProcessTagval(gf, thys, featGroup, version); ajStrDel(&token); return gf; Error: ajStrTokenDel(&featGffSplit); ajStrDelStatic(&featSeqid); ajStrDelStatic(&featSource); ajStrDelStatic(&featFeature); ajStrDel(&token); return gf; } ID featGff3FromLine TY static MO ajfeatread LB core XX DE Converts an input GFF3 format line into a feature XX PN [1] PA u thys AjPFeattable PD Feature table PX PN [2] PA r line const AjPStr PD Input line PX PN [3] PA u idtable AjPTable PD Table of identifiers and features PX PN [4] PA u childlist AjPList PD List of child features PX RT AjPFeature RD New feature RX // static AjPFeature featGff3FromLine(AjPFeattable thys, const AjPStr line, AjPTable idtable, AjPList childlist) { AjPFeature gf = NULL; AjPFeature gftop = NULL; AjPFeature gfknown = NULL; AjPStr token = NULL; ajint Start = 0; ajint End = 0; float fscore = 0.0; char strand; ajint frame; AjPStr idstr = NULL; AjPStr idtag = NULL; AjBool hasparent = ajFalse; AjIList tagiter = NULL; AjPTagval tagval = NULL; if(!ajStrGetLen(line)) return NULL; ajStrTokenAssignC(&featGffSplit, line, "\t"); if(!ajStrTokenNextParse(&featGffSplit, &featSeqid)) /* seqname */ goto Error; if(!ajStrTokenNextParse(&featGffSplit, &featSource)) /* source */ goto Error; if(!ajStrTokenNextParse(&featGffSplit, &featFeature)) /* feature */ goto Error; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* start */ goto Error; if(!ajStrToInt(token, &Start)) Start = 0; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* end */ goto Error; if(!ajStrToInt(token, &End)) End = 0; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* score */ goto Error; if(!ajStrToFloat(token, &fscore)) fscore = 0.0; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* strand */ goto Error; if(!ajStrCmpC(token,"+")) strand = '+'; else if(!ajStrCmpC(token,"-")) strand = '-'; else strand = '\0'; /* change to \0 later */ if(!ajStrTokenNextParse(&featGffSplit, &token)) /* frame */ goto Error; if(!ajStrCmpC(token,"0")) frame = 1; else if(!ajStrCmpC(token,"1")) frame = 2; else if(!ajStrCmpC(token,"2")) frame = 3; else frame = 0; if(!ajStrGetLen(thys->Seqid)) ajStrAssignS(&thys->Seqid, featSeqid); /* feature object construction and group tag */ if(ajStrMatchC(thys->Type, "P")) gf = ajFeatNewProt(thys, featSource, featFeature, Start, End, fscore); else gf = ajFeatNewNuc(thys, featSource, featFeature, Start, End, fscore, strand, frame, 0,0,0, NULL, NULL); if(!ajStrMatchS(featSeqid, thys->Seqid)) { ajStrAssignS(&gf->Remote, featSeqid); gf->Flags |= AJFEATFLAG_REMOTEID; } if(ajStrTokenRestParse(&featGffSplit, &featGroup)) { idstr = featGff3ProcessTagval(gf, thys, featGroup, &hasparent); if(idstr) { gfknown = ajTableFetchmodS(idtable, idstr); if(gfknown) { if(ajListGetLength(gfknown->Subfeatures)) { gftop = gfknown; ajDebug("known gff3 ID add '%S' %Lu '%S' %u..%u " "match '%S' %u..%u\n", idstr, ajListGetLength(gftop->Subfeatures), gf->Type, gf->Start, gf->End, gfknown->Type, gfknown->Start, gfknown->End); ajStrDel(&idstr); } else { ajDebug("known gff3 ID new top '%S' '%S' %u..%u %p " "match '%S' %u..%u %p\n", idstr, gf->Type, gf->Start, gf->End, gf, gfknown->Type, gfknown->Start, gfknown->End, gfknown); gftop = gfknown; gfknown = ajFeatNewFeat(gftop); gftop->Flags |= AJFEATFLAG_GENERATED; gftop->Flags |= AJFEATFLAG_MULTIPLE; if(!gftop->Subfeatures) gftop->Subfeatures = ajListNew(); idtag = ajStrNewS(idstr); ajStrAppendC(&idtag, ".emboss"); ajFeatGfftagAddCS(gftop, "ID", idtag); ajStrDel(&idtag); ajListPushAppend(gftop->Subfeatures, gfknown); ajTablePut(idtable, idstr, gftop); } if(gf->Start < gftop->Start) gftop->Start = gf->Start; if(gf->End > gftop->End) gftop->End = gf->End; ajListPopLast(thys->Features, (void**) &gf); ajListPushAppend(gftop->Subfeatures, gf); tagiter = ajListIterNewread(gf->GffTags); while(!ajListIterDone(tagiter)) { tagval = ajListIterGet(tagiter); if(ajStrMatchC(MAJTAGVALGETTAG(tagval), "ID")) continue; if(ajStrMatchC(MAJTAGVALGETTAG(tagval), "Parent")) continue; ajFeatGfftagAddSS(gftop, MAJTAGVALGETTAG(tagval), MAJTAGVALGETVALUE(tagval)); } ajListIterDel(&tagiter); } else { ajTablePut(idtable, idstr, gf); if(hasparent) { ajListPopLast(thys->Features, (void**) &gf); ajListPushAppend(childlist, gf); } } } else if(hasparent) { ajListPopLast(thys->Features, (void**) &gf); ajListPushAppend(childlist, gf); } } ajStrDel(&token); return gf; Error: ajStrTokenDel(&featGffSplit); ajStrDelStatic(&featSeqid); ajStrDelStatic(&featSource); ajStrDelStatic(&featFeature); ajStrDel(&token); return gf; } ID featGff3oldFromLine TY static MO ajfeatread LB core XX DE Converts an input GFF3 format line into a feature for EMBOSS before 6.4.0 XX PN [1] PA u thys AjPFeattable PD Feature table PX PN [2] PA r line const AjPStr PD Input line PX RT AjPFeature RD New feature RX // static AjPFeature featGff3oldFromLine(AjPFeattable thys, const AjPStr line) { AjPFeature gf = NULL; AjPStr token = NULL; ajint Start = 0; ajint End = 0; float fscore = 0.0; char strand; ajint frame; if(!ajStrGetLen(line)) return NULL; ajStrTokenAssignC(&featGffSplit, line, "\t"); if(!ajStrTokenNextParse(&featGffSplit, &featSeqid)) /* seqname */ goto Error; if(!ajStrTokenNextParse(&featGffSplit, &featSource)) /* source */ goto Error; if(!ajStrTokenNextParse(&featGffSplit, &featFeature)) /* feature */ goto Error; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* start */ goto Error; if(!ajStrToInt(token, &Start)) Start = 0; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* end */ goto Error; if(!ajStrToInt(token, &End)) End = 0; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* score */ goto Error; if(!ajStrToFloat(token, &fscore)) fscore = 0.0; if(!ajStrTokenNextParse(&featGffSplit, &token)) /* strand */ goto Error; if(!ajStrCmpC(token,"+")) strand = '+'; else if(!ajStrCmpC(token,"-")) strand = '-'; else strand = '\0'; /* change to \0 later */ if(!ajStrTokenNextParse(&featGffSplit, &token)) /* frame */ goto Error; if(!ajStrCmpC(token,"0")) frame = 1; else if(!ajStrCmpC(token,"1")) frame = 2; else if(!ajStrCmpC(token,"2")) frame = 3; else frame = 0; /* feature object construction and group tag */ if(ajStrMatchC(thys->Type, "P")) gf = ajFeatNewProt(thys, featSource, featFeature, Start, End, fscore); else gf = ajFeatNewNuc(thys, featSource, featFeature, Start, End, fscore, strand, frame, 0,0,0, NULL, NULL); if(ajStrTokenRestParse(&featGffSplit, &featGroup)) featGff3oldProcessTagval(gf, thys, featGroup); ajStrDel(&token); return gf; Error: ajStrTokenDel(&featGffSplit); ajStrDelStatic(&featSeqid); ajStrDelStatic(&featSource); ajStrDelStatic(&featFeature); ajStrDel(&token); return gf; } ID featReadGff2 TY static MO ajfeatread LB core XX DE Read input file in GFF 2 format XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // static AjBool featReadGff2(AjPFeattabin feattabin, AjPFeattable ftable) { AjPStr line = NULL; AjPStr verstr = NULL; AjPStr start = NULL; AjPStr end = NULL; AjPStr type = NULL; AjBool found = ajFalse; float version = 2.0; AjPFilebuff file = feattabin->Input->Filebuff; /* ajDebug("featReadGff..........\n"); */ while(ajBuffreadLine(file, &line)) { ajStrTrimWhite(&line); /* Header information */ if(ajRegExec(GffRegexblankline, line)) version = 2.0; else if(ajRegExec(GffRegexversion,line)) { verstr = ajStrNew(); ajRegSubI(GffRegexversion, 1, &verstr); ajStrToFloat(verstr, &version); ajStrDel(&verstr); } /* else if(ajRegExec(GffRegexdate,line)) { AjPStr year = NULL; AjPStr month = NULL; AjPStr day = NULL; ajint nYear, nMonth, nDay; ajRegSubI(GffRegexdate, 1, &year); ajRegSubI(GffRegexdate, 2, &month); ajRegSubI(GffRegexdate, 3, &day); ajStrToInt(year, &nYear); ajStrToInt(month, &nMonth); ajStrToInt(day, &nDay); ajStrDel(&year); ajStrDel(&month); ajStrDel(&day); } */ else if(ajRegExec(GffRegexregion,line)) { start = ajStrNew(); end = ajStrNew(); ajRegSubI(GffRegexregion, 1, &ftable->Seqid); ajRegSubI(GffRegexregion, 2, &start); ajRegSubI(GffRegexregion, 3, &end); ajStrToUint(start, &(ftable->Start)); ajStrToUint(end, &(ftable->End)); ajStrDel(&start); ajStrDel(&end); } else if(ajRegExec(GffRegextype,line)) { ajRegSubI(GffRegextype, 1, &type); if(ajStrMatchCaseC(type, "Protein")) ajFeattableSetProt(ftable); else ajFeattableSetNuc(ftable); ajStrDel(&type); ajRegSubI(GffRegextype, 3, &ftable->Seqid); } else if(ajRegExec(GffRegexcomment,line)) version = 2.0; /* ignore for now... could store them in ajFeattable for future reference though?...*/ /* the real feature stuff */ else /* must be a real feature at last !! */ if(featGff2FromLine(ftable, line, version)) /* for ajFeattableAdd */ found = ajTrue; } ajStrDel(&line); return found; } ID featReadGff3 TY static MO ajfeatread LB core XX DE Read input file in GFF3 format XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // static AjBool featReadGff3(AjPFeattabin feattabin, AjPFeattable ftable) { AjPStr line = NULL; AjPStr verstr = NULL; AjPStr start = NULL; AjPStr end = NULL; ajlong fpos = 0; AjBool found = ajFalse; AjBool oldemboss = ajFalse; float version = 3.0; char cp; AjPFilebuff file = feattabin->Input->Filebuff; AjBool haveversion = ajFalse; AjPTable idtable = NULL; AjPList childlist = NULL; idtable = ajTablestrNew(1000); childlist = ajListNew(); /* ajDebug("featReadGff3..........\n"); */ while(ajBuffreadLinePosStore(file, &line, &fpos, feattabin->Input->Text, &ftable->TextPtr)) { ajStrTrimWhite(&line); /* Header information */ if(ajRegExec(Gff3Regexdirective,line)) { if(ajRegExec(Gff3Regexversion,line)) { if(haveversion) /* starting another feature table */ { ajFilebuffClearStore(file, 1, line, feattabin->Input->Text, &ftable->TextPtr); featGff3Matchtable(ftable, &idtable, &childlist); ajStrDel(&line); return found; } ajRegSubI(Gff3Regexversion, 1, &verstr); ajStrToFloat(verstr, &version); ajStrDel(&verstr); haveversion = ajTrue; if(version < 3.0) { ajStrDel(&line); ajTableDel(&idtable); ajListFree(&childlist); return ajFalse; } } else if(ajRegExec(Gff3Regexregion,line)) { start = ajStrNew(); end = ajStrNew(); ajRegSubI(Gff3Regexregion, 1, &ftable->Seqid); ajRegSubI(Gff3Regexregion, 2, &start); ajRegSubI(Gff3Regexregion, 3, &end); ajStrToUint(start, &(ftable->Start)); ajStrToUint(end, &(ftable->End)); ajStrDel(&start); ajStrDel(&end); } } else if(ajRegExec(Gff3Regexcomment,line)) { if(ajStrPrefixC(line, "#!Source-version EMBOSS 6.")) { cp = ajStrGetCharPos(line, 26); if(cp >= '0'&& cp <= '3') oldemboss = ajTrue; } if(ajStrPrefixC(line, "#!Type Protein")) { ajFeattableSetProt(ftable); } } /* the real feature stuff */ else /* must be a real feature at last !! */ { if(oldemboss) { if(featGff3oldFromLine(ftable, line)) /* does ajFeattableAdd */ found = ajTrue; } else { if(featGff3FromLine(ftable, line, idtable, childlist)) found = ajTrue; } } } featGff3Matchtable(ftable, &idtable, &childlist); ajStrDel(&line); return found; } ID featGff3Matchtable TY static MO ajfeatread LB core XX DE Match GFF3 features with parent values to their parent features XX PN [1] PA u ftable AjPFeattable PD Feature table PX PN [2] PA d idtable AjPTable* PD Pointer to table of features by identifier PX PN [3] PA d childlist AjPList* PD Pointer to list of child features PX RT void RD RX // static void featGff3Matchtable(AjPFeattable ftable, AjPTable *idtable, AjPList *childlist) { AjPFeature gf = NULL; AjPFeature gfid = NULL; const AjPStr idparent = NULL; ajDebug("featGff3Matchtable feats %Lu idtable: %Lu childlist: %Lu\n", ajListGetLength(ftable->Features), ajTableGetLength(*idtable), ajListGetLength(*childlist)); while(ajListGetLength(*childlist)) { ajListPop(*childlist, (void**) &gf); ajListDrop(ftable->Features, gf); idparent = ajFeatGetParent(gf); if(idparent) { ajDebug(" parent '%S' '%S' %u..%u\n", idparent, gf->Type, gf->Start, gf->End); gfid = ajTableFetchmodV(*idtable, idparent); if(gfid) { ajDebug(" parent '%S' matching\n", idparent); if(ajStrMatchS(gf->Type, gfid->Type)) gfid->Flags |= AJFEATFLAG_MULTIPLE; ajListPushAppend(gfid->Subfeatures, gf); } else { ajDebug(" ++ parent '%S' no matching id\n", idparent); ajFeatWarn("featGff3Matchtable GFF3 Parent '%S' no matching id", idparent); ajListPushAppend(ftable->Features, gf); } } else { ajDebug(" ++ child with no parent %S %u..%u\n", gf->Type, gf->Start, gf->End); ajFeatWarn("Child feature with no parent %S %u..%u", gf->Type, gf->Start, gf->End); ajListPushAppend(ftable->Features, gf); } } ajDebug("featGff3Matchtable done feats %Lu\n", ajListGetLength(ftable->Features)); ajTableDel(idtable); ajListFree(childlist); return; } ID featReadGff3old TY static MO ajfeatread LB core XX DE Read input file in GFF3 format from EMBOSS releases before 6.4.0 XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // static AjBool featReadGff3old(AjPFeattabin feattabin, AjPFeattable ftable) { AjPStr line = NULL; AjPStr verstr = NULL; AjPStr start = NULL; AjPStr end = NULL; AjBool found = ajFalse; float version = 3.0; AjPFilebuff file = feattabin->Input->Filebuff; /* ajDebug("featReadGff3..........\n"); */ while(ajBuffreadLine(file, &line)) { ajStrTrimWhite(&line); /* Header information */ if(ajRegExec(Gff3Regexblankline, line)) version = 3.0; else if(ajRegExec(Gff3Regexversion,line)) { verstr = ajStrNew(); ajRegSubI(Gff3Regexversion, 1, &verstr); ajStrToFloat(verstr, &version); ajStrDel(&verstr); if(version < 3.0) { ajStrDel(&line); return ajFalse; } } else if(ajRegExec(Gff3Regexregion,line)) { start = ajStrNew(); end = ajStrNew(); ajRegSubI(Gff3Regexregion, 1, &ftable->Seqid); ajRegSubI(Gff3Regexregion, 2, &start); ajRegSubI(Gff3Regexregion, 3, &end); ajStrToUint(start, &(ftable->Start)); ajStrToUint(end, &(ftable->End)); ajStrDel(&start); ajStrDel(&end); } /* the real feature stuff */ else /* must be a real feature at last !! */ if(featGff3oldFromLine(ftable, line)) /* does ajFeattableAdd */ found = ajTrue; } ajStrDel(&line); return found; } ID featRefseqpFromLine TY static MO ajfeatread LB core XX DE Converts an input RefSeq protein format line into a feature. DE Starts a new feature by processing any existing feature data. DE Creates or appends the type, location and tag-value pairs. DE With a NULL as the input line, simply processes the type, location DE and tag-values. XX PN [1] PA u thys AjPFeattable PD Feature table PX PN [2] PA r origline const AjPStr PD Input line (NULL to process last PD feature at end of input) PX PN [3] PA w savefeat AjPStr* PD Stored feature type PX PN [4] PA w saveloc AjPStr* PD Continued location PX PN [5] PA w saveline AjPStr* PD Continued tag-value pairs PX RT AjPFeature RD New feature RX // static AjPFeature featRefseqpFromLine(AjPFeattable thys, const AjPStr origline, AjPStr* savefeat, AjPStr* saveloc, AjPStr* saveline) { static AjPFeature gf = NULL; /* so tag-values can be added LATER */ AjPStr temp = NULL; AjBool newft = ajFalse; AjBool doft = ajFalse; if(!featinSourceRefseqp) featinSourceRefseqp = ajStrNewC("REFSEQP"); /* ajDebug("featRefseqpFromLine '%S'\n", origline); */ if(origline) { /* As BufferFile can't be edited */ ajStrAssignS(&featProcessLine,origline); /* chop first 5 characters */ ajStrCutStart(&featProcessLine, 5); /* look for the feature key */ if(ajStrGetCharFirst(featProcessLine) != ' ') { newft = ajTrue; if(ajStrGetLen(*saveloc)) doft = ajTrue; } } else { ajStrAssignClear(&featProcessLine); newft = ajFalse; /* no new data, just process */ if(ajStrGetLen(*saveloc)) doft = ajTrue; } /* ajDebug("+ newft: %B doft: %B\n+ line '%S'\n", newft, doft, line); */ if(doft) /* process the last feature */ { /* ** ajDebug("++ saveloc '%S'\n+ saveline '%S'\n", ** *saveloc, *saveline); */ gf = featRefseqpProcess(thys, *savefeat, featinSourceRefseqp, saveloc, saveline); ajStrDelStatic(saveloc); ajStrDelStatic(saveline); } if(!origline) /* we are only cleaning up */ return gf; ajStrRemoveWhiteExcess(&featProcessLine); if(newft) /* if new feature initialise for it */ { ajStrTokenAssignC(&featEmblSplit, featProcessLine, " "); ajStrTokenNextParse(&featEmblSplit, savefeat); if(ajStrTokenNextParseC(&featEmblSplit, " /", saveloc)) ajStrTokenRestParse(&featEmblSplit, saveline); else ajStrAssignResC(saveline, 512, ""); /* location only */ ajStrTokenDel(&featEmblSplit); return gf; } else if(!ajStrGetLen(*saveline)) /* no tag-values yet, more location? */ { if(ajStrGetCharFirst(featProcessLine) != '/') { ajStrTokenAssignC(&featEmblSplit, featProcessLine, " "); ajStrTokenNextParse(&featEmblSplit, &temp); if(ajStrGetLen(temp)) ajStrAppendS(saveloc, temp); ajStrTokenRestParse(&featEmblSplit, &temp); if(ajStrGetLen(temp)) ajStrAppendS(saveline, temp); ajStrDel(&temp); ajStrTokenDel(&featEmblSplit); return gf; } } /* tag-values continued */ ajStrAppendK(saveline, ' '); ajStrAppendS(saveline, featProcessLine); return gf; } ID featRefseqpProcess TY static MO ajfeatread LB core XX DE Processes one feature location and qualifier tags for RefSeq protein XX PN [1] PA u thys AjPFeattable PD Feature table PX PN [2] PA r feature const AjPStr PD Feature type key PX PN [3] PA r source const AjPStr PD Feature table source PX PN [4] PA w loc AjPStr* PD Feature location PX PN [5] PA w tags AjPStr* PD Feature qualifier tags string PX RT AjPFeature RD Feature as inserted into the feature table RX // static AjPFeature featRefseqpProcess(AjPFeattable thys, const AjPStr feature, const AjPStr source, AjPStr* loc, AjPStr* tags) { AjPFeature ret = NULL; AjPFeature gf = NULL; AjPStr tag = NULL; AjPStr val = NULL; AjPStr opnam = NULL; AjPStr opval = NULL; AjPStr prestr = NULL; AjBool Fwd = ajTrue; AjBool LocFwd = ajTrue; AjPStr begstr = NULL; AjPStr delstr = NULL; AjPStr endstr = NULL; AjPStr locstr = NULL; AjPStr rest = NULL; AjBool Simple = ajFalse; /* Simple - single position (see also label) */ AjBool BegBound = ajFalse; AjBool EndBound = ajFalse; ajuint BegNum = 0; ajuint EndNum = 0; ajuint Beg2 = 0; ajuint End2 = 0; AjBool Between = ajFalse; AjBool Join = ajFalse; AjBool Order = ajFalse; ajint Flags; ajint ExonFlags; AjBool Mother = ajTrue; ajint Frame = 0; float Score = 0.0; AjBool HasOper = ajFalse; AjBool RemoteId = ajFalse; AjBool IsLabel = ajFalse; /* uses obsolete label */ ajint Exon = 0; ajlong ipos; ajint itags = 0; const AjPStr tmpft = NULL; ajStrRemoveWhite(loc); /* no white space needed */ ajStrRemoveWhiteExcess(tags); /* single spaces only */ /*ajDebug("Clean location '%S'\n", *loc);*/ /*ajDebug("Clean tags '%S'\n", *tags);*/ ajStrAssignS(&opval, *loc); ipos = ajStrFindAnyK(opval, ','); /* multiple locations */ if(ipos >= 0) { /* ajDebug("Multiple locations, test operator(s)\n"); */ while(ajStrGetLen(opval) && featEmblOperOut(opval, &opnam, &featinTmpStr)) { if(!ajStrHasParentheses(featinTmpStr)) break; /* ajDebug("OperOut %S( '%S' )\n", opnam, featinTmpStr); */ if(ajStrMatchCaseC(opnam, "complement")) Fwd = !Fwd; else if(ajStrMatchCaseC(opnam, "one_of")) Order = ajTrue; else if(ajStrMatchCaseC(opnam, "join")) Join = ajTrue; else if(ajStrMatchCaseC(opnam, "order")) Order = ajTrue; else if(ajStrMatchCaseC(opnam, "group")) Order = ajTrue; else ajFeatWarn("%S: unrecognised operator '%S()' in '%S'", thys->Seqid, opnam, opval); ajStrAssignS(&opval, featinTmpStr); } } while(ajStrGetLen(opval)) { LocFwd = Fwd; BegBound = ajFalse; EndBound = ajFalse; Simple = ajFalse; Between = ajFalse; BegNum = EndNum = Beg2 = End2 = 0; HasOper = ajFalse; RemoteId = ajFalse; IsLabel = ajFalse; ajStrDelStatic(&featId); ajStrDelStatic(&featLabel); /* check for complement() */ /* set locstr as the whole (or rest) of the location */ if(featEmblOperIn(opval, &opnam, &locstr, &featinTmpStr)) { /* ajDebug("OperIn %S( '%S' )\n", opnam, locstr); */ if(ajStrMatchCaseC(opnam, "complement")) LocFwd = !LocFwd; ajStrAssignS(&opval, featinTmpStr); /* ajDebug("rest: '%S'\n", opval); */ HasOper = ajTrue; } else { ajStrAssignS(&locstr, opval); /* ajDebug("OperIn simple '%S'\n", locstr); */ } if(featEmblOperNone(locstr, &featId, &featinTmpStr, &rest)) /* one exon */ { /* ajDebug("OperNone '%S' \n", featinTmpStr); */ if(ajStrGetLen(featId)) { /* ajDebug("External entryid '%S'\n", featId); */ RemoteId = ajTrue; } if(!featEmblLoc(featinTmpStr, &begstr, &Between, &Simple, &endstr)) { ajStrAssignS(&begstr, featinTmpStr); ajStrAssignS(&endstr, begstr); Simple = ajTrue; ajDebug("Bad feature numeric location '%S' in '%S' - " "test later for label", begstr, locstr); } ajStrAssignS(&featinTmpStr, rest); if(!HasOper) ajStrAssignS(&opval, featinTmpStr); if(featEmblLocNum(begstr, &BegBound, &BegNum)) { Beg2 = 0; /* ajDebug("Begin '%S' %d Bound: %B\n", begstr, BegNum, BegBound); */ } else if(featEmblLocRange(begstr, &BegNum, &Beg2)) { BegBound = ajFalse; /* ajDebug("Begin range (%d . %d)\n", BegNum, Beg2); */ } else { /* ajDebug("Begin is a label '%S'\n", begstr); */ IsLabel = ajTrue; Simple = ajTrue; ajStrAssignS(&featLabel, begstr); ajFeatWarn("%S: Simple feature location '%S' in '%S'", thys->Seqid, begstr, locstr); } if(featEmblLocNum(endstr, &EndBound, &EndNum)) { End2 = 0; /* ajDebug(" End '%S' %d Bound: %B\n", endstr, EndNum, EndBound); */ } else if(featEmblLocRange(endstr, &End2, &EndNum)) { EndBound = ajFalse; /* ajDebug(" End range (%d . %d)\n", End2, EndNum); */ } else { IsLabel = ajTrue; Simple = ajTrue; ajStrAssignS(&featLabel, endstr); /* ajDebug(" End is a label '%S'\n", endstr); */ ajErr("%S: Simple feature end '%S' in '%S'", thys->Seqid, begstr, locstr); } } else { ajErr("Unable to parse location:\n'%S'", opval); } /* location has been read in, now store it */ Flags = ExonFlags = 0; if(Simple) Flags |= AJFEATFLAG_POINT; if(Between) Flags |= AJFEATFLAG_BETWEEN_SEQ; if(End2) Flags |= AJFEATFLAG_END_TWO; if(Beg2) Flags |= AJFEATFLAG_START_TWO; if(BegBound) Flags |= AJFEATFLAG_START_BEFORE_SEQ; if(EndBound) Flags |= AJFEATFLAG_END_AFTER_SEQ; if(RemoteId) Flags |= AJFEATFLAG_REMOTEID; if(IsLabel) Flags |= AJFEATFLAG_LABEL; if(IsLabel) ajFeatWarn("%S: Feature location with label '%S'", thys->Seqid, locstr); if(Join || Order) Flags |= AJFEATFLAG_MULTIPLE; if(Order) Flags |= AJFEATFLAG_ORDER; ExonFlags = Flags; if(Mother) { if(!Fwd) Flags |= AJFEATFLAG_COMPLEMENT_MAIN; } /* ajDebug("Calling featFeatNew, Flags: %x\n", Flags); */ tmpft = ajFeattypeGetInternalRefseqp(feature); if(!Mother) Exon++; if(Mother) gf = ajFeatNewProtFlags(thys, source, /* source sequence */ tmpft, BegNum, EndNum, Score, Flags); else gf = ajFeatNewProtFlagsSub(thys, gf, source, /* source sequence */ tmpft, BegNum, EndNum, Score, Flags); if(Mother) { ret = gf; Mother = ajFalse; Exon = 1; } } while(ajStrGetLen(*tags)) { itags++; if(featEmblTvTagVal(tags, &tag, &val)) { ajStrQuoteStrip(&val); if(!ajFeatTagAddSS(ret, tag, val)) ajFeatWarn("%S: Bad value '%S' for tag '/%S'", thys->Seqid, val, tag); if(ajStrMatchC(tag, "codon_start")) { ajStrToInt(val, &Frame); gf->Frame = Frame; } } else if(featEmblTvRest(tags, &featinTmpStr)) { /* anything non-whitespace up to '/' is bad */ ajFeatWarn("Bad feature syntax %S: skipping '%S'", thys->Seqid, featinTmpStr); } else { ajFeatWarn("Bad feature syntax %S: giving up at '%S'", thys->Seqid, *tags); ajStrAssignClear(tags); } } ajDebug("featRefseqpProcess found %d feature tags\n", itags); ajStrDelStatic(&featinTmpStr); ajStrDel(&prestr); ajStrDel(&val); ajStrDel(&tag); ajStrDel(&begstr); ajStrDel(&delstr); ajStrDel(&opnam); ajStrDel(&opval); ajStrDel(&locstr); ajStrDel(&endstr); ajStrDel(&rest); return ret; } ID featRegInitEmbl TY static MO ajfeatread LB core XX DE Initialise regular expressions and data structures for DE EMBL/GenBank/DDBJ format XX RT AjBool RD ajTrue if successful RX // static AjBool featRegInitEmbl(void) { if(FeatInitEmbl) return ajTrue; ajFeatVocabInit("embl"); FeatInitEmbl = ajTrue; return ajTrue; } ID featRegInitRefseqp TY static MO ajfeatread LB core XX DE Initialise regular expressions and data structures for DE RefSeq protein format XX RT AjBool RD ajTrue if successful RX // static AjBool featRegInitRefseqp(void) { if(FeatInitRefseqp) return ajTrue; ajFeatVocabInit("refseqp"); FeatInitRefseqp = ajTrue; return ajTrue; } ID featRegInitSwiss TY static MO ajfeatread LB core XX DE Initialise regular expressions and data structures for DE SwissProt format XX RT AjBool RD ajTrue if successful RX // static AjBool featRegInitSwiss(void) { if(FeatInitSwiss) return ajTrue; ajFeatVocabInit("swiss"); if(!SwRegexNew) SwRegexNew = ajRegCompC("^FT (([^ ]+) +([?<]?[0-9]+|[?]) +" "([?>]?[0-9]+|[?]) *)(.*)$"); if(!SwRegexNext) SwRegexNext = ajRegCompC("^FT +(.*)$"); if(!SwRegexComment) SwRegexComment = ajRegCompC("^(.*)[(]([^)]+)[)]$"); if(!SwRegexFtid) SwRegexFtid = ajRegCompC("^(.*)/FTId=([^ .]+)$"); FeatInitSwiss = ajTrue; return ajTrue; } ID featRegInitPir TY static MO ajfeatread LB core XX DE Initialise regular expressions and data structures for ajFeat in DE PIR format XX RT AjBool RD ajTrue if successful RX // static AjBool featRegInitPir(void) { if(FeatInitPir) return ajTrue; ajFeatVocabInit("pir"); if(!PirRegexAll) PirRegexAll = ajRegCompC("^F;([^/]+)/([^:]+):([^#]*)"); if(!PirRegexCom) PirRegexCom = ajRegCompC("^#([^#]*)"); if(!PirRegexLoc) PirRegexLoc = ajRegCompC("^([^,]+),?"); if(!PirRegexPos) PirRegexPos = ajRegCompC("^([^-]+)-?"); FeatInitPir = ajTrue; return ajTrue; } ID featRegInitGff2 TY static MO ajfeatread LB core XX DE Initialise regular expressions and data structures for ajFeat GFF format XX RT AjBool RD ajTrue if successful RX // static AjBool featRegInitGff2(void) { /* Setup any global static runtime resources here for example, regular expression compilation calls */ if(FeatInitGff2) return ajTrue; ajFeatVocabInit("gff2"); ajFeatVocabInit("gff2protein"); /*ajDebug("featRegInitGff2 Compiling regexps\n");*/ GffRegexNumeric = ajRegCompC("^[\\+-]?[0-9]+\\.?[0-9]*$"); GffRegexblankline = ajRegCompC("^[ ]*$"); GffRegexversion = ajRegCompC("^##gff-version[ ]+([0-9]+)"); GffRegexdate = ajRegCompC("^##date[ ]+([0-9][0-9][0-9][0-9])-" "([0-9][0-9]?)-([0-9][0-9]?)"); GffRegexregion = ajRegCompC("^##sequence-region[ ]+([0-9a-zA-Z]+)" "[ ]+([\\+-]?[0-9]+)[ ]+([\\+-]?[0-9]+)"); GffRegexcomment = ajRegCompC("^#[ ]*(.*)"); GffRegextype = ajRegCompC("^##[Tt]ype +(\\S+)( +(\\S+))?"); GffRegexTvTagval = ajRegCompC(" *([^ =]+)[ =](\"[^\"]*\"|" "[^;]+)(;|$)"); /* "tag name */ FeatInitGff2 = ajTrue; return ajTrue; } ID featRegInitGff3 TY static MO ajfeatread LB core XX DE Initialise regular expressions and data structures for ajFeat GFF3 format XX RT AjBool RD ajTrue if successful RX // static AjBool featRegInitGff3(void) { /* Setup any global static runtime resources here for example, regular expression compilation calls */ if(FeatInitGff3) return ajTrue; ajFeatVocabInit("gff3"); ajFeatVocabInit("gff3protein"); /*ajDebug("featRegInitGff3 Compiling regexps\n");*/ Gff3RegexNumeric = ajRegCompC("^[\\+-]?[0-9]+\\.?[0-9]*$"); Gff3Regexblankline = ajRegCompC("^[ ]*$"); Gff3Regexversion = ajRegCompC("^##gff-version[ ]+([0-9]+)"); Gff3Regexregion = ajRegCompC("^##sequence-region[ ]+([0-9a-zA-Z]+)" "[ ]+([\\+-]?[0-9]+)[ ]+([\\+-]?[0-9]+)"); Gff3Regexdirective = ajRegCompC("^##(.*)"); Gff3Regexcomment = ajRegCompC("^#(.*)"); Gff3RegexTvTagval = ajRegCompC(" *([^ =]+)[ =]([^;]+)*(;|$)"); /* "tag name */ Gff3oldRegexTvTagval = ajRegCompC(" *([^ =]+)[ =](\"[^\"]*\"|" "[^;]+)(;|$)"); /* "tag name */ FeatInitGff3 = ajTrue; if(!featRegInitGff2()) return ajFalse; return ajTrue; } ID featDelRegEmbl TY static MO ajfeatread LB core XX DE Cleanup and exit routines. Free and destroy regular expressions XX RT AjBool RD ajFalse if unsuccessful RX // static AjBool featDelRegEmbl(void) { if(!FeatInitEmbl) return ajTrue; FeatInitEmbl = ajFalse; return ajTrue; } ID featDelRegPir TY static MO ajfeatread LB core XX DE Cleanup and exit routines. Free and destroy regular expressions XX RT AjBool RD ajFalse if unsuccessful RX // static AjBool featDelRegPir(void) { if(!FeatInitPir) return ajTrue; ajRegFree(&PirRegexAll); ajRegFree(&PirRegexCom); ajRegFree(&PirRegexLoc); ajRegFree(&PirRegexPos); FeatInitPir = ajFalse; return ajTrue; } ID featDelRegRefseqp TY static MO ajfeatread LB core XX DE Cleanup and exit routines. Free and destroy regular expressions XX RT AjBool RD ajFalse if unsuccessful RX // static AjBool featDelRegRefseqp(void) { if(!FeatInitRefseqp) return ajTrue; FeatInitRefseqp = ajFalse; return ajTrue; } ID featDelRegSwiss TY static MO ajfeatread LB core XX DE Cleanup and exit routines. Free and destroy regular expressions XX RT AjBool RD ajFalse if unsuccessful RX // static AjBool featDelRegSwiss(void) { if(!FeatInitSwiss) return ajTrue; ajRegFree(&SwRegexComment); ajRegFree(&SwRegexFtid); ajRegFree(&SwRegexNew); ajRegFree(&SwRegexNext); FeatInitSwiss = ajFalse; return ajTrue; } ID featDelRegGff2 TY static MO ajfeatread LB core XX DE Cleanup and exit routines. Free and destroy regular expressions XX RT AjBool RD ajFalse if unsuccessful RX // static AjBool featDelRegGff2(void) { if(!FeatInitGff2) return ajTrue; /* Clean-up any global static runtime resources here for example, regular expression pattern variables */ ajRegFree(&GffRegexNumeric); ajRegFree(&GffRegexblankline); ajRegFree(&GffRegexversion); ajRegFree(&GffRegexdate); ajRegFree(&GffRegexregion); ajRegFree(&GffRegexcomment); ajRegFree(&GffRegextype); ajRegFree(&GffRegexTvTagval); FeatInitGff2 = ajFalse; return ajTrue; } ID featDelRegGff3 TY static MO ajfeatread LB core XX DE Cleanup and exit routines. Free and destroy regular expressions XX RT AjBool RD ajFalse if unsuccessful RX // static AjBool featDelRegGff3(void) { if(!FeatInitGff3) return ajTrue; /* Clean-up any global static runtime resources here for example, regular expression pattern variables */ ajRegFree(&Gff3RegexNumeric); ajRegFree(&Gff3Regexblankline); ajRegFree(&Gff3Regexversion); ajRegFree(&Gff3Regexdate); ajRegFree(&Gff3Regexregion); ajRegFree(&Gff3Regexcomment); ajRegFree(&Gff3Regexdirective); ajRegFree(&Gff3Regextype); ajRegFree(&Gff3RegexTvTagval); ajRegFree(&Gff3oldRegexTvTagval); FeatInitGff3 = ajFalse; featDelRegGff2(); return ajTrue; } ID ajFeatreadExit TY public MO ajfeatread LB core XX DE Cleans up feature table input internal memory XX RT void RD RX // void ajFeatreadExit(void) { ajint i; for(i=1;featinformatDef[i].Name;i++) { if(featinformatDef[i].Used) { /* Calling funclist featinformatDef() */ if(featinformatDef[i].DelReg && !(*featinformatDef[i].DelReg)()) { /*ajDebug("No DelReg yet for %s\n",featinformatDef[i].Name);*/ ajErr("No DelReg yet for %s\n",featinformatDef[i].Name); } } } ajRegFree(&featRegFlag); ajRegFree(&featRegMore); ajRegFree(&featRegGroup); ajRegFree(&featRegGff3Group); ajStrDel(&featinFormatTmp); ajStrDel(&featinValTmp); ajStrDel(&featinUfoTest); ajStrDel(&featReadLine); ajStrDel(&featProcessLine); ajStrDel(&featinTmpStr); ajStrDel(&featGroup); ajStrDel(&featSeqid); ajStrDel(&featSource); ajStrDel(&featFeature); ajStrDel(&featinTagNote); ajStrDel(&featinTagComm); ajStrDel(&featinTagFtid); ajStrDel(&featinSourcePir); ajStrDel(&featinSourceSwiss); ajStrDel(&featinSourceEmbl); ajStrDel(&featinSourceRefseqp); ajStrDel(&featId); ajStrDel(&featLabel); ajStrDel(&featLocStr); ajStrDel(&featLocToken); ajStrDel(&featLocDb); ajStrDel(&featSaveGroupStr); ajStrTokenDel(&featGffSplit); ajStrTokenDel(&featEmblSplit); ajRegFree(&featinRegUfoFmt); ajRegFree(&featinRegUfoFile); ajRegFree(&featTagTrans); ajTableDel(&feattabDbMethods); return; } ID ajFeattabinTypeGetFields TY public MO ajfeatread LB core XX DE Returns the listof known field names for ajFeatRead XX RT const char* RD List of field names RX // const char* ajFeattabinTypeGetFields(void) { return "id acc"; } ID ajFeattabinTypeGetQlinks TY public MO ajfeatread LB core XX DE Returns the listof known query link operators for ajFeatRead XX RT const char* RD List of field names RX // const char* ajFeattabinTypeGetQlinks(void) { return "|"; } ID featFindInformatC TY static MO ajfeatread LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Given a single format, sets iformat. XX PN [1] PA r format const char* PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool featFindInformatC(const char* format, ajint* iformat) { ajint i = 0; /*ajDebug("featFindInformatC '%s'\n", format);*/ if(!*format) return ajFalse; ajStrAssignC(&featinFormatTmp, format); ajStrFmtLower(&featinFormatTmp); for(i=0; featinformatDef[i].Name; i++) { /*ajDebug("test %d '%s' \n", i, featinformatDef[i].Name);*/ if(ajStrMatchC(featinFormatTmp, featinformatDef[i].Name)) { *iformat = i; (void) ajStrDelStatic(&featinFormatTmp); /*ajDebug("found '%s' at %d\n", featinformatDef[i].Name, i);*/ return ajTrue; } } ajErr("Unknown input feat format '%s'", format); ajStrDelStatic(&featinFormatTmp); return ajFalse; } ID featFindInformatS TY static MO ajfeatread LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Given a single format, sets iformat. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool featFindInformatS(const AjPStr format, ajint* iformat) { return featFindInformatC(ajStrGetPtr(format), iformat); } ID ajFeatinPrintFormat TY public MO ajfeatread LB core XX DE Reports the internal data structures for input feature formats XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajFeatinPrintFormat(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Feature input formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Alias Name is an alias\n"); ajFmtPrintF(outf, "# Nuc Valid for nucleotide sequences\n"); ajFmtPrintF(outf, "# Pro Valid for protein sequences\n"); ajFmtPrintF(outf, "# Name Alias Nuc Pro " "Description\n"); ajFmtPrintF(outf, "Informat {\n"); for(i=0; featinformatDef[i].Name; i++) { if(full || !featinformatDef[i].Alias) ajFmtPrintF(outf, " %-12s %5B %5B %5B \"%s\"\n", featinformatDef[i].Name, featinformatDef[i].Alias, featinformatDef[i].Nucleotide, featinformatDef[i].Protein, featinformatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajFeatinPrinthtmlFormat TY public MO ajfeatread LB core XX DE Reports the internal data structures for input feature formats XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajFeatinPrinthtmlFormat(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, "
Assembly FormatDescription
\n%-12s\n" "\"%s\"
"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); for(i=0; featinformatDef[i].Name; i++) { if(!featinformatDef[i].Alias) ajFmtPrintF(outf, "" "\n", featinformatDef[i].Name, featinformatDef[i].Alias, featinformatDef[i].Nucleotide, featinformatDef[i].Protein, featinformatDef[i].Desc); } ajFmtPrintF(outf, "
Features Input FormatAliasNucProDescription
\n%-12s\n%5B\n%5B\n%5B\n\"%s\"
\n"); return; } ID ajFeatinPrintbookFormat TY public MO ajfeatread LB core XX DE Reports the input feature format internals in docbook text format XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajFeatinPrintbookFormat(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; AjPList fmtlist; AjPStr* names; fmtlist = ajListstrNew(); ajFmtPrintF(outf, "The supported feature formats are summarised " "in the table below. The columns are as follows: " "Output format (format name), " "Nuc (\"true\" indicates nucleotide " "sequence data may be represented), Pro " "(\"true\" indicates protein sequence data may be " "represented) and Description " "(short description of the format).\n\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=1; featinformatDef[i].Name; i++) { if(!featinformatDef[i].Alias) { namestr = ajStrNewC(featinformatDef[i].Name); ajListPush(fmtlist, namestr); namestr = NULL; } } ajListSort(fmtlist, &ajStrVcmp); ajListstrToarray(fmtlist, &names); for(i=0; names[i]; i++) { for(j=0; featinformatDef[j].Name; j++) { if(ajStrMatchC(names[i],featinformatDef[j].Name)) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", featinformatDef[j].Name); ajFmtPrintF(outf, " \n", featinformatDef[j].Nucleotide); ajFmtPrintF(outf, " \n", featinformatDef[j].Protein); ajFmtPrintF(outf, " \n", featinformatDef[j].Desc); ajFmtPrintF(outf, " \n"); } } } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Input feature formats
Output FormatNucProDescription
%s%B%B%s
\n"); ajStrDel(&namestr); names = NULL; ajListstrFreeData(&fmtlist); return; } ID ajFeatinPrintwikiFormat TY public MO ajfeatread LB core XX DE Reports the input feature format internals in wiki text format XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajFeatinPrintwikiFormat(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!Nuc!!Pro!!" "class=\"unsortable\"|Description\n"); for(i=1; featinformatDef[i].Name; i++) { if(!featinformatDef[i].Alias) { ajFmtPrintF(outf, "|-\n"); ajStrAssignC(&namestr, featinformatDef[i].Name); for(j=i+1; featinformatDef[j].Name; j++) { if(featinformatDef[j].Read == featinformatDef[i].Read) { ajFmtPrintAppS(&namestr, "
%s", featinformatDef[j].Name); if(!featinformatDef[j].Alias) { ajWarn("Feature input format '%s' same as '%s' " "but not alias", featinformatDef[j].Name, featinformatDef[i].Name); } } } ajFmtPrintF(outf, "|%S||%B||%B||%s\n", namestr, featinformatDef[i].Nucleotide, featinformatDef[i].Protein, featinformatDef[i].Desc); } } ajFmtPrintF(outf, "|}\n\n"); ajStrDel(&namestr); return; } ID ajFeattaballNew TY public MO ajfeatread LB core XX DE Creates a new feature input stream object. XX RT AjPFeattaball RD New feature input stream object. RX // AjPFeattaball ajFeattaballNew(void) { AjPFeattaball pthis; AJNEW0(pthis); pthis->Feattabin = ajFeattabinNew(); pthis->Feattable = ajFeattableNew(NULL); return pthis; } ID ajFeattaballDel TY public MO ajfeatread LB core XX DE Deletes a feature input stream object. XX PN [1] PA d pthis AjPFeattaball* PD Feature input stream PX RT void RD RX // void ajFeattaballDel(AjPFeattaball* pthis) { AjPFeattaball thys; if(!pthis) return; thys = *pthis; if(!thys) return; ajFeattabinDel(&thys->Feattabin); if(!thys->Returned) ajFeattableDel(&thys->Feattable); AJFREE(*pthis); return; } ID ajFeattaballClear TY public MO ajfeatread LB core XX DE Clears a feature input stream object back to "as new" condition, DE except for the query list which must be preserved. XX PN [1] PA w thys AjPFeattaball PD Feature input stream PX RT void RD RX // void ajFeattaballClear(AjPFeattaball thys) { ajDebug("ajFeattaballClear called\n"); if(!thys) return; ajFeattabinClear(thys->Feattabin); if(!thys->Returned) ajFeattableClear(thys->Feattable); return; } ID ajFeattaballGetfeattableId TY public MO ajfeatread LB core XX DE Returns the identifier of the current feature table in an input stream XX PN [1] PA r thys const AjPFeattaball PD Feature input stream PX RT const AjPStr RD Identifier RX // const AjPStr ajFeattaballGetfeattableId(const AjPFeattaball thys) { if(!thys) return NULL; ajDebug("ajFeattaballGetfeattableId called\n"); return ajFeattableGetName(thys->Feattable); } ID ajFeattaballNext TY public MO ajfeatread LB core XX DE Parse a feature query into format, access, file and entry DE DE Split at delimiters. Check for the first part as a valid format DE Check for the remaining first part as a database name or as a file DE that can be opened. DE Anything left is an entryname spec. DE DE Return the results in the AjPFeattable object but leave the file open for DE future calls. XX PN [1] PA w thys AjPFeattaball PD Feature input stream PX PN [2] PA u Pfeattable AjPFeattable* PD Feature table returned PX RT AjBool RD ajTrue on success. RX // AjBool ajFeattaballNext(AjPFeattaball thys, AjPFeattable *Pfeattable) { if(!thys->Count) { thys->Count = 1; thys->Totfeattables++; *Pfeattable= thys->Feattable; thys->Returned = ajTrue; return ajTrue; } if(ajFeattabinRead(thys->Feattabin, thys->Feattable)) { thys->Count++; thys->Totfeattables++; *Pfeattable = thys->Feattable; thys->Returned = ajTrue; ajDebug("ajFeattaballNext success\n"); return ajTrue; } ajDebug("ajFeattaballNext failed\n"); ajFeattaballClear(thys); return ajFalse; } ID ajFeattabaccessGetDb TY public MO ajfeatread LB core XX DE returns the table in which feature table database access details DE are registered XX RT AjPTable RD Access functions hash table RX // AjPTable ajFeattabaccessGetDb(void) { if(!feattabDbMethods) feattabDbMethods = ajCallTableNew(); return feattabDbMethods; } ID ajFeattabaccessMethodGetQlinks TY public MO ajfeatread LB core XX DE Tests for a named method for feature table term reading and returns the DE known query link operators XX PN [1] PA r method const AjPStr PD Method required. PX RT const char* RD Known link operators RX // const char* ajFeattabaccessMethodGetQlinks(const AjPStr method) { AjPFeattabAccess methoddata; methoddata = ajCallTableGetS(feattabDbMethods, method); if(!methoddata) return NULL; return methoddata->Qlink; } ID ajFeattabaccessMethodGetScope TY public MO ajfeatread LB core XX DE Tests for a named method for feature table term reading and returns DE the scope (entry, query or all). XX PN [1] PA r method const AjPStr PD Method required. PX RT ajuint RD Scope flags RX // ajuint ajFeattabaccessMethodGetScope(const AjPStr method) { AjPFeattabAccess methoddata; ajuint ret = 0; methoddata = ajCallTableGetS(feattabDbMethods, method); if(!methoddata) return 0; if(methoddata->Entry) ret |= AJMETHOD_ENTRY; if(methoddata->Query) ret |= AJMETHOD_QUERY; if(methoddata->All) ret |= AJMETHOD_ALL; return ret; } ID ajFeattabaccessMethodTest TY public MO ajfeatread LB core XX DE Tests for a named method for feature table reading. XX PN [1] PA r method const AjPStr PD Method required. PX RT AjBool RD ajTrue on success. RX // AjBool ajFeattabaccessMethodTest(const AjPStr method) { if(ajCallTableGetS(feattabDbMethods, method)) return ajTrue; return ajFalse; } ID feattabinQryRestore TY static MO ajfeatread LB core XX DE Restores a feature input specification from a FeatPListUfo node XX PN [1] PA w feattabin AjPFeattabin PD feature table input object PX PN [2] PA r node const FeatPListUfo PD Feature list node PX RT void RD RX // static void feattabinQryRestore(AjPFeattabin feattabin, const FeatPListUfo node) { feattabin->Input->Format = node->Format; feattabin->Input->Fpos = node->Fpos; ajStrAssignS(&feattabin->Input->Formatstr, node->Formatstr); return; } ID feattabinQrySave TY static MO ajfeatread LB core XX DE Saves a data input specification in a FeatPListUfo node XX PN [1] PA w node FeatPListUfo PD Feature list node PX PN [2] PA r feattabin const AjPFeattabin PD Feature table input object PX RT void RD RX // static void feattabinQrySave(FeatPListUfo node, const AjPFeattabin feattabin) { node->Format = feattabin->Input->Format; node->Fpos = feattabin->Input->Fpos; ajStrAssignS(&node->Formatstr, feattabin->Input->Formatstr); return; } ID feattabinQryProcess TY static MO ajfeatread LB core XX DE Converts a feature table query into an open file. DE DE Tests for "format::" and sets this if it is found DE DE Then tests for "list:" or " XX PN [1] PA u feattabin AjPFeattabin PD feature table input structure. PX PN [2] PA u ftable AjPFeattable PD Feature table data to be read. PD The format will be replaced PD if defined in the query string. PX RT AjBool RD ajTrue on success. RX // static AjBool feattabinQryProcess(AjPFeattabin feattabin, AjPFeattable ftable) { AjBool ret = ajTrue; AjPStr qrystr = NULL; AjBool featmethod = ajFalse; const AjPStr fmtstr = NULL; AjPTextin textin; AjPQuery qry; AjPFeattabAccess feataccess = NULL; textin = feattabin->Input; qry = textin->Query; /* pick up the original query string */ qrystr = ajStrNewS(textin->Qry); ajDebug("feattabinQryProcess '%S'\n", qrystr); /* look for a format:: prefix */ fmtstr = ajQuerystrParseFormat(&qrystr, textin, feattabinformatFind); ajDebug("feattabinQryProcess ... fmtstr '%S' '%S'\n", fmtstr, qrystr); /* (seq/feat) look for a [range] suffix */ ajQuerystrParseRange(&qrystr, &feattabin->Start, &feattabin->End, &feattabin->Rev); ajDebug("feattabinQryProcess ... range %d..%d rev:%B '%S'\n", feattabin->Start, feattabin->End, feattabin->Rev, qrystr); /* look for a list:: or (at):: listfile of queries - process and return */ if(ajQuerystrParseListfile(&qrystr)) { ajDebug("feattabinQryProcess ... listfile '%S'\n", qrystr); ret = feattabinListProcess(feattabin, ftable, qrystr); ajStrDel(&qrystr); return ret; } /* try general text access methods (file, asis, text database access */ ajDebug("feattabinQryProcess ... no listfile '%S'\n", qrystr); if(!ajQuerystrParseRead(&qrystr, textin, feattabinformatFind, &featmethod)) { ajStrDel(&qrystr); return ajFalse; } feattabinFormatSet(feattabin, ftable); ajDebug("feattabinQryProcess ... read nontext: %B '%S'\n", featmethod, qrystr); ajStrDel(&qrystr); /* we found a non-text method */ if(featmethod) { ajDebug("feattabinQryProcess ... call method '%S'\n", qry->Method); ajDebug("feattabinQryProcess ... textin format %d '%S'\n", textin->Format, textin->Formatstr); ajDebug("feattabinQryProcess ... query format '%S'\n", qry->Formatstr); qry->Access = ajCallTableGetS(feattabDbMethods,qry->Method); feataccess = qry->Access; return (*feataccess->Access)(feattabin); } ajDebug("feattabinQryProcess text method '%S' success\n", qry->Method); return ajTrue; } ID feattabinListProcess TY static MO ajfeatread LB core XX DE Processes a file of queries. DE This function is called by, and calls, feattabinQryProcess. There is DE a depth check to avoid infinite loops, for example where a list file DE refers to itself. DE DE This function produces a list (AjPList) of queries with all list references DE expanded into lists of queries. DE DE Because queries in a list can have their own format DE the prior settings are stored with each query in the list node so that they DE can be restored after. XX PN [1] PA u feattabin AjPFeattabin PD Feature table input PX PN [2] PA u ftable AjPFeattable PD Feature table data PX PN [3] PA r listfile const AjPStr PD Name of list file., PX RT AjBool RD ajTrue on success. RX // static AjBool feattabinListProcess(AjPFeattabin feattabin, AjPFeattable ftable, const AjPStr listfile) { AjPList list = NULL; AjPFile file = NULL; AjPStr token = NULL; AjPStrTok handle = NULL; AjBool ret = ajFalse; FeatPListUfo node = NULL; static ajint depth = 0; static ajint MAXDEPTH = 16; depth++; ajDebug("++feattabinListProcess %S depth %d\n", listfile, depth); if(depth > MAXDEPTH) ajFatal("Query list too deep"); if(!feattabin->Input->List) feattabin->Input->List = ajListNew(); list = ajListNew(); file = ajFileNewInNameS(listfile); if(!file) { ajErr("Failed to open list file '%S'", listfile); depth--; return ret; } while(ajReadlineTrim(file, &featReadLine)) { feattabinListNoComment(&featReadLine); if(ajStrGetLen(featReadLine)) { ajStrTokenAssignC(&handle, featReadLine, " \t\n\r"); ajStrTokenNextParse(&handle, &token); /* ajDebug("Line '%S'\n");*/ /* ajDebug("token '%S'\n", featReadLine, token); */ if(ajStrGetLen(token)) { ajDebug("++Add to list: '%S'\n", token); AJNEW0(node); ajStrAssignS(&node->Ufo, token); feattabinQrySave(node, feattabin); ajListPushAppend(list, node); } ajStrDel(&token); token = NULL; } } ajFileClose(&file); ajStrDel(&token); ajDebug("Trace feattabin->Input->List\n"); ajQuerylistTrace(feattabin->Input->List); ajDebug("Trace new list\n"); ajQuerylistTrace(list); ajListPushlist(feattabin->Input->List, &list); ajDebug("Trace combined feattabin->Input->List\n"); ajQuerylistTrace(feattabin->Input->List); /* ** now try the first item on the list ** this can descend recursively if it is also a list ** which is why we check the depth above */ if(ajListPop(feattabin->Input->List, (void**) &node)) { ajDebug("++pop first item '%S'\n", node->Ufo); ajFeattabinQryS(feattabin, node->Ufo); feattabinQryRestore(feattabin, node); ajStrDel(&node->Ufo); ajStrDel(&node->Formatstr); AJFREE(node); ajDebug("descending with query '%S'\n", feattabin->Input->Qry); ret = feattabinQryProcess(feattabin, ftable); } ajStrTokenDel(&handle); depth--; ajDebug("++feattabinListProcess depth: %d returns: %B\n", depth, ret); return ret; } ID feattabinListNoComment TY static MO ajfeatread LB core XX DE Strips comments from a character string (a line from a list file). DE Comments are blank lines or any text following a "#" character. XX PN [1] PA u text AjPStr* PD Line of text from input file. PX RT void RD RX // static void feattabinListNoComment(AjPStr* text) { ajuint i; char *cp; i = ajStrGetLen(*text); if(!i) /* empty string */ return; MAJSTRGETUNIQUESTR(text); cp = strchr(ajStrGetPtr(*text), '#'); if(cp) { /* comment found */ *cp = '\0'; ajStrSetValid(text); } return; } ID feattabinFormatSet TY static MO ajfeatread LB core XX DE Sets the input format for data data using the data data DE input object's defined format XX PN [1] PA u feattabin AjPFeattabin PD Dfeature table input. PX PN [2] PA u ftable AjPFeattable PD Feature table data PX RT AjBool RD ajTrue on success. RX // static AjBool feattabinFormatSet(AjPFeattabin feattabin, AjPFeattable ftable) { if(ajStrGetLen(feattabin->Input->Formatstr)) { ajDebug("... input format value '%S'\n", feattabin->Input->Formatstr); if(feattabinformatFind(feattabin->Input->Formatstr, &feattabin->Input->Format)) { ajStrAssignS(&ftable->Formatstr, feattabin->Input->Formatstr); ftable->Format = feattabin->Input->Format; ajDebug("...format OK '%S' = %d\n", feattabin->Input->Formatstr, feattabin->Input->Format); } else ajDebug("...format unknown '%S'\n", feattabin->Input->Formatstr); return ajTrue; } else ajDebug("...input format not set\n"); return ajFalse; } ID featGff3FlagSet TY static MO ajfeatread LB core XX DE Sets the flags for a GFF3 feature. XX PN [1] PA u gf AjPFeature PD Feature PX PN [2] PA r flagstr const AjPStr PD Flags as a hexadecimal value PX RT void RD RX // static void featGff3FlagSet(AjPFeature gf, const AjPStr flagstr) { AjPStr savstr = NULL; AjPStr typstr = NULL; AjPStr valstr = NULL; ajint flags = 0; ajint num = 0; if(!featRegFlag) featRegFlag = ajRegCompC("[ \"]*(0x[0-9a-f]+)"); if(!featRegMore) featRegMore = ajRegCompC("[,]*([^:]+):([^,]+)"); /*ajDebug("featGff3FlagSet '%S'\n", flagstr);*/ ajStrAssignS(&savstr, flagstr); if(ajRegExec(featRegFlag, savstr)) { ajRegSubI(featRegFlag, 1, &featinTmpStr); if(ajStrToHex(featinTmpStr, &flags)) { if(flags & AJFEATFLAG_GROUP) flags |= AJFEATFLAG_ORDER; if(flags & AJFEATFLAG_ONEOF) flags |= AJFEATFLAG_ORDER; gf->Flags |= flags; } /*ajDebug("flags: %x", gf->Flags);*/ ajRegPost(featRegFlag, &featinTmpStr); ajStrAssignS(&savstr, featinTmpStr); } while(ajRegExec(featRegMore, savstr)) { ajRegSubI(featRegMore, 1, &typstr); ajRegSubI(featRegMore, 2, &valstr); /*ajDebug("flag type '%S' val '%S'\n", typstr, valstr);*/ if(ajStrMatchCaseC(typstr, "start_before")) { if(ajStrMatchC(valstr, "true")) gf->Flags |= AJFEATFLAG_START_BEFORE_SEQ; } else if(ajStrMatchCaseC(typstr, "end_after")) { if(ajStrMatchC(valstr, "true")) gf->Flags |= AJFEATFLAG_END_AFTER_SEQ; } else if(ajStrMatchCaseC(typstr, "between")) { if(ajStrMatchC(valstr, "true")) gf->Flags |= AJFEATFLAG_BETWEEN_SEQ; } else if(ajStrMatchCaseC(typstr, "start2")) { if(ajStrToInt(valstr, &num)) { gf->Start2 = num; gf->Flags |= AJFEATFLAG_START_TWO; } } else if(ajStrMatchCaseC(typstr, "end2")) { if(ajStrToInt(valstr, &num)) { gf->End2 = num; gf->Flags |= AJFEATFLAG_END_TWO; } } else if(ajStrMatchCaseC(typstr, "label")) { ajFeatWarn("GFF3 label '%S' used", valstr); ajStrAssignS(&gf->Label, valstr); } else if(ajStrMatchCaseC(typstr, "start_unsure")) { if(ajStrMatchC(valstr, "true")) gf->Flags |= AJFEATFLAG_START_UNSURE; } else if(ajStrMatchCaseC(typstr, "end_unsure")) { if(ajStrMatchC(valstr, "true")) gf->Flags |= AJFEATFLAG_END_UNSURE; } else if(ajStrMatchCaseC(typstr, "type")) { ajStrAssignS(&gf->Type, ajFeattypeGetInternal(valstr)); } else ajFeatWarn("Unknown GFF3 featflags type '%S:%S'", typstr, valstr); ajRegPost(featRegMore, &featinTmpStr); ajStrAssignS(&savstr, featinTmpStr); } ajStrDel(&savstr); ajStrDel(&typstr); ajStrDel(&valstr); return; } ID featFlagSet TY static MO ajfeatread LB core XX DE Sets the flags for a feature. XX PN [1] PA u gf AjPFeature PD Feature PX PN [2] PA r flagstr const AjPStr PD Flags as a hexadecimal value PX RT void RD RX // static void featFlagSet(AjPFeature gf, const AjPStr flagstr) { AjPStr savstr = NULL; AjPStr typstr = NULL; AjPStr valstr = NULL; ajint flags = 0; ajint num = 0; if(!featRegFlag) featRegFlag = ajRegCompC("[ \"]*(0x[0-9a-f]+)"); if(!featRegMore) featRegMore = ajRegCompC("[ \"]*([^:]+):([^: \"]+)"); /*ajDebug("featFlagSet '%S'\n", flagstr);*/ ajStrAssignS(&savstr, flagstr); if(ajRegExec(featRegFlag, savstr)) { ajRegSubI(featRegFlag, 1, &featinTmpStr); if(ajStrToHex(featinTmpStr, &flags)) { if(flags & AJFEATFLAG_GROUP) flags |= AJFEATFLAG_ORDER; if(flags & AJFEATFLAG_ONEOF) flags |= AJFEATFLAG_ORDER; gf->Flags = flags; } /*ajDebug("flags: %x", gf->Flags);*/ ajRegPost(featRegFlag, &featinTmpStr); ajStrAssignS(&savstr, featinTmpStr); } while(ajRegExec(featRegMore, savstr)) { ajRegSubI(featRegMore, 1, &typstr); ajRegSubI(featRegMore, 2, &valstr); /*ajDebug("flag type '%S' val '%S'\n", typstr, valstr);*/ if(ajStrMatchCaseC(typstr, "start2")) { if(ajStrToInt(valstr, &num)) gf->Start2 = num; } else if(ajStrMatchCaseC(typstr, "end2")) { if(ajStrToInt(valstr, &num)) gf->End2 = num; } else if(ajStrMatchCaseC(typstr, "remoteid")) ajStrAssignS(&gf->Remote, valstr); else if(ajStrMatchCaseC(typstr, "label")) { ajFeatWarn("GFF label '%S' used", valstr); ajStrAssignS(&gf->Label, valstr); } else ajFeatWarn("Unknown GFF FeatFlags type '%S:%S'", typstr, valstr); ajRegPost(featRegMore, &featinTmpStr); ajStrAssignS(&savstr, featinTmpStr); } ajStrDel(&savstr); ajStrDel(&typstr); ajStrDel(&valstr); return; } ID featGroupSet TY static MO ajfeatread LB core XX DE Sets the group tag for a feature. XX PN [1] PA u gf AjPFeature PD Feature PX PN [2] PA u table AjPFeattable PD Feature table PX PN [3] PA r grouptag const AjPStr PD Group field identifier PX RT void RD RX // static void featGroupSet(AjPFeature gf, AjPFeattable table, const AjPStr grouptag) { AjPStr namstr = NULL; AjPStr grpstr = NULL; ajint grpnum; if(!featRegGroup) featRegGroup = ajRegCompC("^\"(([^.]*)[.])?([0-9]+)"); if(ajStrGetLen(grouptag) && ajStrMatchCaseS(grouptag, featSaveGroupStr)) { gf->Group = featSaveGroup; gf->Exon = ++featSaveExon; return; } if(ajStrGetLen(grouptag) && ajRegExec(featRegGroup, grouptag)) { ajStrAssignS(&featSaveGroupStr, grouptag); ajRegSubI(featRegGroup, 2, &namstr); ajRegSubI(featRegGroup, 3, &grpstr); /*ajDebug("featGroupSet '%S' name: '%S' group: '%S'\n", grouptag, namstr, grpstr);*/ if(ajStrToInt(grpstr, &grpnum)) /* true, if the regex worked */ { gf->Group = grpnum; featSaveGroup = grpnum; } else gf->Group = ++(table->Groups); if(ajStrGetLen(namstr)) { if(!ajStrMatchCaseS(namstr, table->Seqid)) { ajDebug("GFF group field '%S' table '%S'\n", grouptag, table->Seqid); ajFeatWarn("GFF group field '%S' for table '%S'", grouptag, table->Seqid); } } } else /* regex failed, make something up */ { ajStrAssignS(&grpstr, grouptag); gf->Group = ++(table->Groups); featSaveGroup = gf->Group; gf->Exon = 0; featSaveExon = 0; } ajStrDel(&namstr); ajStrDel(&grpstr); return; } ID featGff3GroupSet TY static MO ajfeatread LB core XX DE Sets the group tag for a feature. XX PN [1] PA u gf AjPFeature PD Feature PX PN [2] PA u table AjPFeattable PD Feature table PX PN [3] PA r grouptag const AjPStr PD Group field identifier PX RT void RD RX // static void featGff3GroupSet(AjPFeature gf, AjPFeattable table, const AjPStr grouptag) { AjPStr namstr = NULL; AjPStr grpstr = NULL; ajint grpnum; if(!featRegGff3Group) featRegGff3Group = ajRegCompC("^(([^.]*)[.])?([0-9]+)"); if(ajStrGetLen(grouptag) && ajStrMatchCaseS(grouptag, featSaveGroupStr)) { gf->Group = featSaveGroup; gf->Exon = ++featSaveExon; return; } if(ajStrGetLen(grouptag) && ajRegExec(featRegGff3Group, grouptag)) { ajStrAssignS(&featSaveGroupStr, grouptag); ajRegSubI(featRegGff3Group, 2, &namstr); ajRegSubI(featRegGff3Group, 3, &grpstr); /*ajDebug("featGroupSet '%S' name: '%S' group: '%S'\n", grouptag, namstr, grpstr);*/ if(ajStrToInt(grpstr, &grpnum)) /* true, if the regex worked */ { gf->Group = grpnum; featSaveGroup = grpnum; } else gf->Group = ++(table->Groups); /* TODO: this validation should be updated and uncommented back if(ajStrGetLen(namstr)) { if(!ajStrMatchCaseS(namstr, table->Seqid)) { ajDebug("GFF group field '%S' table '%S'\n", grouptag, table->Seqid); ajFeatWarn("GFF group field '%S' for table '%S'", grouptag, table->Seqid); } } */ } else /* regex failed, make something up */ { ajStrAssignS(&grpstr, grouptag); gf->Group = ++(table->Groups); featSaveGroup = gf->Group; gf->Exon = 0; featSaveExon = 0; } ajStrDel(&namstr); ajStrDel(&grpstr); return; } ID feattabinformatFind TY static MO ajfeatread LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Sets iformat as the recognised format, and returns ajTrue. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool feattabinformatFind(const AjPStr format, ajint* iformat) { AjPStr tmpformat = NULL; ajuint i = 0; /* ajDebug("feattabinformatFind '%S'\n", format); */ if(!ajStrGetLen(format)) return ajFalse; ajStrAssignS(&tmpformat, format); ajStrFmtLower(&tmpformat); for(i=0; featinformatDef[i].Name; i++) { /* ajDebug("test %d '%s' \n", i, featinformatDef[i].Name); */ if(ajStrMatchCaseC(tmpformat, featinformatDef[i].Name)) { *iformat = i; ajStrDel(&tmpformat); /* ajDebug("found '%s' at %d\n", featinformatDef[i].Name, i); */ return ajTrue; } } ajErr("Unknown input format '%S'", format); ajStrDel(&tmpformat); return ajFalse; } ID ajFeattabinformatTerm TY public MO ajfeatread LB core XX DE Tests whether a feature table data input format term is known XX PN [1] PA r term const AjPStr PD Format term EDAM ID PX RT AjBool RD ajTrue if term was accepted RX // AjBool ajFeattabinformatTerm(const AjPStr term) { ajuint i; for(i=0; featinformatDef[i].Name; i++) if(ajStrMatchCaseC(term, featinformatDef[i].Obo)) return ajTrue; return ajFalse; } ID ajFeattabinformatTest TY public MO ajfeatread LB core XX DE Tests whether a named feature table data input format is known XX PN [1] PA r format const AjPStr PD Format PX RT AjBool RD ajTrue if format was accepted RX // AjBool ajFeattabinformatTest(const AjPStr format) { ajuint i; for(i=0; featinformatDef[i].Name; i++) if(ajStrMatchCaseC(format, featinformatDef[i].Name)) return ajTrue; return ajFalse; } ID featOutFormatDef TY list MO ajfeatwrite LB core XX DE Feature output formats DE DE Includes functions to write the output file (ajFeattableWrite) XX // static FeatOOutFormat featOutFormatDef[] = { /* Name Nucleotide Prot WriteFunction Description Alias Padding */ {"unknown", AJFALSE, AJFALSE, &ajFeattableWriteGff3, "unknown format", AJFALSE, 0}, {"gff", AJTRUE, AJTRUE, &ajFeattableWriteGff3, "GFF version 3", AJFALSE, 0}, {"gff2", AJTRUE, AJTRUE, &ajFeattableWriteGff2, "GFF version 2", AJFALSE, 0}, {"gff3", AJTRUE, AJTRUE, &ajFeattableWriteGff3, "GFF version 3", AJTRUE, 0}, {"bed", AJTRUE, AJTRUE, &ajFeattableWriteBed, "BED format", AJFALSE, 0}, {"embl", AJTRUE, AJFALSE, &ajFeattableWriteEmbl, "embl format", AJFALSE, 0}, {"em", AJTRUE, AJFALSE, &ajFeattableWriteEmbl, "embl format", AJTRUE, 0}, {"genbank", AJTRUE, AJFALSE, &ajFeattableWriteGenbank, "genbank format", AJFALSE, 0}, {"gb", AJTRUE, AJFALSE, &ajFeattableWriteGenbank, "genbank format", AJTRUE, 0}, {"ddbj", AJTRUE, AJFALSE, &ajFeattableWriteDdbj, "ddbj format", AJFALSE, 0}, {"refseq", AJTRUE, AJFALSE, &ajFeattableWriteGenbank, "genbank format", AJTRUE, 0}, {"refseqp", AJFALSE, AJTRUE, &ajFeattableWriteRefseqp, "genbank format", AJTRUE, 0}, {"pir", AJFALSE, AJTRUE, &ajFeattableWritePir, "PIR format", AJFALSE, 0}, {"nbrf", AJFALSE, AJTRUE, &ajFeattableWritePir, "PIR format", AJTRUE, 0}, {"swiss", AJFALSE, AJTRUE, &ajFeattableWriteSwiss, "SwissProt format", AJFALSE, 0}, {"sw", AJFALSE, AJTRUE, &ajFeattableWriteSwiss, "SwissProt format", AJTRUE, 0}, {"swissprot", AJFALSE, AJTRUE, &ajFeattableWriteSwiss, "SwissProt format", AJTRUE, 0}, {"dasgff", AJTRUE, AJTRUE, &ajFeattableWriteDasgff, "DAS GFF format", AJFALSE, 0}, {"draw", AJTRUE, AJTRUE, &ajFeattableWriteDraw, "DAS GFF format", AJFALSE, 0}, {"debug", AJTRUE, AJTRUE, &ajFeattableWriteDebug, "Debugging trace of full internal data content", AJFALSE, 0}, {NULL, AJFALSE, AJFALSE, NULL, NULL, AJFALSE, 0} }; ID ajFeattabOutOpen TY public MO ajfeatwrite LB core XX DE Processes the specified UFO, and opens the resulting output file. DE DE If the AjPFeattabOut has a filename, this is used in preference. DE If not, the ufo is processed. XX PN [1] PA u thys AjPFeattabOut PD Features table output object PX PN [2] PA r ufo const AjPStr PD UFO feature output specifier PX RT AjBool RD ajTrue on success RX // AjBool ajFeattabOutOpen(AjPFeattabOut thys, const AjPStr ufo) { /*ajDebug("ajFeattabOutOpen ufo:'%S' dir:'%S' file:'%S'\n", ufo, thys->Directory, thys->Filename);*/ if(thys->Handle) return ajTrue; if(ajStrGetLen(thys->Filename)) { if(!featFindOutFormatS(thys->Formatstr, &thys->Format)) ajErr("unknown output feature format '%S' " "will write as %s instead\n", thys->Formatstr, featOutFormatDef[thys->Format].Name); } else { if(!featoutUfoProcess(thys, ufo)) return ajFalse; } /*ajDebug("trying to open dir:'%S' file:'%S' fmt:'%S'\n", thys->Directory, thys->Filename, thys->Formatstr);*/ thys->Handle = ajFileNewOutNamePathS(thys->Filename, thys->Directory); if(!thys->Handle) return ajFalse; /* ajDebug("after opening '%S'\n", thys->Filename);*/ return ajTrue; } ID ajFeattabOutFile TY public MO ajfeatwrite LB core XX DE Returns the feature table output file object XX PN [1] PA r thys const AjPFeattabOut PD Features table output object PX RT AjPFile RD File object RX // AjPFile ajFeattabOutFile(const AjPFeattabOut thys) { /* ajDebug("ajFeattabOutFile\n");*/ return thys->Handle; } ID ajFeattabOutFilename TY public MO ajfeatwrite LB core XX DE Returns the name of a feature table output file XX PN [1] PA r thys const AjPFeattabOut PD Features table output object PX RT AjPStr RD Filename RX // AjPStr ajFeattabOutFilename(const AjPFeattabOut thys) { /* ajDebug("ajFeattabOutFilename\n");*/ if(ajStrGetLen(thys->Filename)) return thys->Filename; return NULL; } ID ajFeattabOutIsOpen TY public MO ajfeatwrite LB core XX DE Checks whether feature output file has already been opened XX PN [1] PA r thys const AjPFeattabOut PD Features table output object PX RT AjBool RD ajTrue if file is open RX // AjBool ajFeattabOutIsOpen(const AjPFeattabOut thys) { /* ajDebug("ajFeattabOutIsOpen\n");*/ if(thys->Handle) return ajTrue; return ajFalse; } ID ajFeattabOutIsLocal TY public MO ajfeatwrite LB core XX DE Checks whether feature output file has already been opened XX PN [1] PA r thys const AjPFeattabOut PD Features table output object PX RT AjBool RD ajTrue if file is open RX // AjBool ajFeattabOutIsLocal(const AjPFeattabOut thys) { /* ajDebug("ajFeattabOutIsLocal Handle %x Local %B\n", thys->Handle, thys->Local);*/ if(thys->Handle && thys->Local) return ajTrue; return ajFalse; } ID ajFeattabOutSet TY public MO ajfeatwrite LB core XX DE Processes the specified UFO, and specifies the resulting output file. XX PN [1] PA u thys AjPFeattabOut PD Features table output object PX PN [2] PA r ufo const AjPStr PD UFO feature output specifier PX RT AjBool RD ajTrue on success RX // AjBool ajFeattabOutSet(AjPFeattabOut thys, const AjPStr ufo) { /* ajDebug("ajFeattabOutSet ufo:'%S' dir:'%S' file:'%S'\n", ufo, thys->Directory, thys->Filename);*/ if(thys->Handle) return ajTrue; if(!featoutUfoProcess(thys, ufo)) return ajFalse; return ajTrue; } ID ajFeattabOutSetBasename TY public MO ajfeatwrite LB core XX DE Sets the base output filename for feature table output XX PN [1] PA u thys AjPFeattabOut PD feature table output PX PN [2] PA r basename const AjPStr PD Output base filename PX RT void RD RX // void ajFeattabOutSetBasename(AjPFeattabOut thys, const AjPStr basename) { AjPStr tmpname = NULL; tmpname = ajStrNewS(basename); ajFilenameTrimAll(&tmpname); ajStrAssignEmptyS(&thys->Basename, tmpname); ajStrDel(&tmpname); /*ajDebug("ajFeattabOutSetBasename '%S' result '%S'\n", basename, thys->Basename);*/ return; } ID ajFeattabOutSetSeqname TY public MO ajfeatwrite LB core XX DE Processes the specified UFO, and specifies the resulting output file. XX PN [1] PA u thys AjPFeattabOut PD Features table output object PX PN [2] PA r name const AjPStr PD UFO feature output specifier PX RT AjBool RD ajTrue on success RX // AjBool ajFeattabOutSetSeqname(AjPFeattabOut thys, const AjPStr name) { /* ajDebug("ajFeattabOutSet ufo:'%S' dir:'%S' file:'%S'\n", ufo, thys->Directory, thys->Filename);*/ ajStrAssignS(&thys->Seqname, name); return ajTrue; } ID ajFeattabOutNewCSF TY public MO ajfeatwrite LB core XX DE Constructor for an empty feature table output object, using an DE existing open output file (the file sequence data is already written to) XX PN [1] PA r fmt const char* PD feature format PX PN [2] PA r name const AjPStr PD sequence name PX PN [3] PA r type const char* PD feature type PX PN [4] PA u file AjPFile PD Output file PX RT AjPFeattabOut RD Feature table output object RX CA new CT AjPFeattabOut CD Constructor with format, name, type and output file CX // AjPFeattabOut ajFeattabOutNewCSF(const char* fmt, const AjPStr name, const char* type, AjPFile file) { AjPFeattabOut pthis; ajint iformat = 0; /*ajDebug("ajFeattabOutNewCSF '%S' '%S' '%s' '%F'\n", fmt, name, type, file);*/ if(!featFindOutFormatC(fmt, &iformat)) return NULL; pthis = ajFeattabOutNew(); ajStrAssignC(&pthis->Formatstr, featOutFormat[iformat].Name); pthis->Format = iformat; ajFeattabOutSetTypeC(pthis, type); ajStrAssignS(&pthis->Seqname, name); pthis->Handle = file; pthis->Local = ajTrue; /*ajDebug("ajFeatTabOutNewSSF %x\n", pthis);*/ return pthis; } ID ajFeattabOutNewSSF TY public MO ajfeatwrite LB core XX DE Constructor for an empty feature table output object, using an DE existing open output file (the file sequence data is already written to) XX PN [1] PA r fmt const AjPStr PD feature format PX PN [2] PA r name const AjPStr PD sequence name PX PN [3] PA r type const char* PD feature type PX PN [4] PA u file AjPFile PD Output file PX RT AjPFeattabOut RD Feature table output object RX CA new CT AjPFeattabOut CD Constructor with format, name, type and output file CX // AjPFeattabOut ajFeattabOutNewSSF(const AjPStr fmt, const AjPStr name, const char* type, AjPFile file) { AjPFeattabOut pthis; ajint iformat = 0; /*ajDebug("ajFeattabOutNewSSF '%S' '%S' '%s' '%F'\n", fmt, name, type, file);*/ if(!featFindOutFormatS(fmt, &iformat)) return NULL; pthis = ajFeattabOutNew(); ajStrAssignC(&pthis->Formatstr, featOutFormat[iformat].Name); pthis->Format = iformat; ajFeattabOutSetTypeC(pthis, type); ajStrAssignS(&pthis->Seqname, name); pthis->Handle = file; pthis->Local = ajTrue; /*ajDebug("ajFeatTabOutNewSSF %x\n", pthis);*/ return pthis; } ID ajFeattabOutNew TY public MO ajfeatwrite LB core XX DE Constructor for an empty feature table output object XX RT AjPFeattabOut RD Feature table input object RX CA new CT AjPFeattabOut CD Constructor CX // AjPFeattabOut ajFeattabOutNew(void) { AjPFeattabOut pthis; AJNEW0(pthis); /*ajDebug("ajFeattabOutNew %x\n", pthis);*/ return pthis; } ID ajFeattabOutClear TY public MO ajfeatwrite LB core XX DE Clears all data for a feature table output object XX PN [1] PA d thys AjPFeattabOut* PD feature format PX RT void RD Feature table output object RX CA delete CT AjPFeattabOut CD Destructor CX // void ajFeattabOutClear(AjPFeattabOut *thys) { AjPFeattabOut pthis; pthis = *thys; if(!pthis) return; ajStrSetClear(&pthis->Ufo); ajStrSetClear(&pthis->Formatstr); ajStrSetClear(&pthis->Filename); ajStrSetClear(&pthis->Directory); ajStrSetClear(&pthis->Seqid); ajStrSetClear(&pthis->Type); ajStrSetClear(&pthis->Seqname); ajStrSetClear(&pthis->Basename); if(pthis->Cleanup) (*pthis->Cleanup)(pthis->Handle); pthis->Cleanup = NULL; pthis->Count = 0; /* "Local" tables have borrowed an open file with NewSSF */ if(!pthis->Local) ajFileClose(&pthis->Handle); return; } ID ajFeattabOutDel TY public MO ajfeatwrite LB core XX DE Destructor for a feature table output object XX PN [1] PA d pthis AjPFeattabOut* PD feature format PX RT void RD Feature table output object RX CA delete CT AjPFeattabOut CD Destructor CX // void ajFeattabOutDel(AjPFeattabOut *pthis) { AjPFeattabOut thys; thys = *pthis; if(!thys) return; if(!thys->Local && thys->Handle && !thys->Count) ajWarn("No features written to output file '%F'", thys->Handle); /* "Local" tables have borrowed an open file with NewSSF */ if(!thys->Local) ajFileClose(&thys->Handle); ajStrDel(&thys->Ufo); ajStrDel(&thys->Formatstr); ajStrDel(&thys->Filename); ajStrDel(&thys->Directory); ajStrDel(&thys->Seqid); ajStrDel(&thys->Type); ajStrDel(&thys->Seqname); ajStrDel(&thys->Basename); if(thys->Cleanup) (*thys->Cleanup)(thys->Handle); thys->Cleanup = NULL; thys->Count = 0; AJFREE(*pthis); return; } ID ajFeattableWriteUfo TY public MO ajfeatwrite LB core XX DE Parses a UFO, opens an output file, and writes a feature table to it. XX PN [1] PA u thys AjPFeattabOut PD Feature output object PX PN [2] PA r ftable const AjPFeattable PD Feature table to be written PX PN [3] PA r ufo const AjPStr PD UFO feature spec (ignored if already open) PX RT AjBool RD ajTrue on success. RX // AjBool ajFeattableWriteUfo(AjPFeattabOut thys, const AjPFeattable ftable, const AjPStr ufo) { if(!ajFeattabOutIsOpen(thys)) ajFeattabOutOpen(thys, ufo); return ajFeattableWrite(thys, ftable); } ID featoutUfoProcess TY static MO ajfeatwrite LB core XX DE Converts a UFO Uniform Feature Object into an open output file. DE DE First tests for "format::" and sets format if it is found DE DE Then checks for a filename, and defaults to the sequence name (as stored DE in the AjPFeattabOut) or "unknown" if there is no name. XX PN [1] PA u thys AjPFeattabOut PD Feature table to be written. PX PN [2] PA r ufo const AjPStr PD UFO. PX RT AjBool RD ajTrue on success. RX // static AjBool featoutUfoProcess(AjPFeattabOut thys, const AjPStr ufo) { AjBool fmtstat = ajFalse; /* status returns from regex tests */ AjBool filstat = ajFalse; /* status returns from regex tests */ AjPFeattabOut featout = thys; if(!featoutRegUfoFmt) featoutRegUfoFmt = ajRegCompC("^([A-Za-z0-9]*):+(.*)$"); /* ** \1 format ** \2 remainder */ if(!featoutRegUfoFile) featoutRegUfoFile = ajRegCompC("^([^:]+)$"); /*ajDebug("featoutUfoProcess UFO '%S'\n", ufo);*/ ajStrAssignS(&featoutUfoTest, ufo); if(ajStrGetLen(ufo)) { fmtstat = ajRegExec(featoutRegUfoFmt, featoutUfoTest); /*ajDebug("feat format regexp: %B\n", fmtstat);*/ } if(fmtstat) { ajRegSubI(featoutRegUfoFmt, 1, &featout->Formatstr); ajStrAssignEmptyC(&featout->Formatstr, featOutFormat[0].Name); /* unknown */ ajRegSubI(featoutRegUfoFmt, 2, &featoutUfoTest); /* trim off format */ /*ajDebug("found feat format %S\n", featout->Formatstr);*/ } else { /*ajDebug("no feat format specified in UFO '%S' try '%S' or 'gff'\n", ufo, featout->Formatstr);*/ ajStrAssignEmptyC(&featout->Formatstr, "gff"); } if(!featFindOutFormatS(featout->Formatstr, &featout->Format)) ajErr("unknown output feature format '%S' " "will write as gff3 instead\n", featout->Formatstr ); /* now go for the filename */ filstat = ajRegExec(featoutRegUfoFile, featoutUfoTest); /*ajDebug("featRegUfoFile: %B\n", filstat);*/ if(filstat) { ajRegSubI(featoutRegUfoFile, 1, &featout->Filename); /*ajDebug("set from UFO featout Filename '%S'\n", featout->Filename);*/ } else { if(ajStrGetLen(featout->Basename)) ajFmtPrintS(&featoutUfoTest, "%S.%S", featout->Basename, featout->Formatstr); else if(ajStrGetLen(featout->Seqname)) ajFmtPrintS(&featoutUfoTest, "%S.%S", featout->Seqname, featout->Formatstr); else ajFmtPrintS(&featoutUfoTest, "unknown.%S", featout->Formatstr); ajStrAssignEmptyS(&featout->Filename, featoutUfoTest); /*ajDebug("generate featout filename '%S' dir '%S'\n", featout->Filename, featout->Directory);*/ } /*ajDebug("\n");*/ return ajTrue; } ID featFindOutFormatC TY static MO ajfeatwrite LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Given a single format, sets iformat. XX PN [1] PA r format const char* PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool featFindOutFormatC(const char* format, ajint* iformat) { ajint i = 0; /*ajDebug("featFindOutFormatC '%s'\n", format);*/ if(!*format) return ajFalse; ajStrAssignC(&featoutFormatTmp, format); ajStrFmtLower(&featoutFormatTmp); for(i=0; featOutFormat[i].Name; i++) { /*ajDebug("test %d '%s' len=%d\n", i, featOutFormat[i].Name,ajStrGetLen(featoutFormatTmp));*/ if(ajStrMatchC(featoutFormatTmp, featOutFormat[i].Name)) { *iformat = i; ajStrDelStatic(&featoutFormatTmp); /*ajDebug("found '%s' at %d\n", featOutFormat[i].Name, i);*/ return ajTrue; } } ajStrDelStatic(&featoutFormatTmp); *iformat = 1; return ajFalse; } ID featFindOutFormatS TY static MO ajfeatwrite LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Given a single format, sets iformat. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool featFindOutFormatS(const AjPStr format, ajint* iformat) { return featFindOutFormatC(ajStrGetPtr(format), iformat); } ID ajFeatOutFormatDefault TY public MO ajfeatwrite LB core XX DE Sets the default output format. DE Checks the _OUTFEATFORMAT variable, DE and uses GFF3 if no other definition is found. XX PN [1] PA w pformat AjPStr* PD Default output feature format. PX RT AjBool RD ajTrue on success. RX // AjBool ajFeatOutFormatDefault(AjPStr* pformat) { if(ajStrGetLen(*pformat)) { /*ajDebug("... output feature format '%S'\n", *pformat);*/ } else { if(ajNamGetValueC("outfeatformat", pformat)) { /*ajDebug("ajFeatOutFormatDefault '%S' from EMBOSS_OUTFEATFORMAT\n", *pformat);*/ } else { ajStrAssignEmptyC(pformat, "gff"); /* use the real name */ /*ajDebug("... output feature format not set, default to '%S'\n", *pformat);*/ } } return ajTrue; } ID ajFeattableWrite TY public MO ajfeatwrite LB core XX DE Generic interface function for writing features to a file DE defined by a feature table output object. XX PN [1] PA u ftout AjPFeattabOut PD Specifies the external source PD (file) of the features to be written PX PN [2] PA r features const AjPFeattable PD Feature set to be written out PX RT AjBool RD Returns ajTrue if successful; ajFalse otherwise RX // AjBool ajFeattableWrite(AjPFeattabOut ftout, const AjPFeattable features) { AjBool result = ajFalse; AjPFile file; ajint format; AjBool isnuc = ajFalse; AjBool isprot = ajFalse; AjBool ok = ajFalse; if(features) { if(!ftout) return ajFalse; if(ajFeattableIsNuc(features)) isnuc = ajTrue; if(ajFeattableIsProt(features)) isprot = ajTrue; ok = ajFalse; if(isnuc && featOutFormat[ftout->Format].Nucleotide) ok = ajTrue; else if(isprot && featOutFormat[ftout->Format].Protein) ok = ajTrue; if(!ok) { if(isnuc) ajErr("Feature format '%S' not supported for nucleotide " "sequence features", ftout->Formatstr); else if(isprot) ajErr("Feature format '%S' not supported for protein " "sequence features", ftout->Formatstr); else ajErr("Feature format '%S' failed: unknown sequence feature " "type", ftout->Formatstr); return ajFalse; } /*ajDebug("ajFeattableWrite Validating arguments\n");*/ file = ftout->Handle; if(!file) return ajFalse; format = ftout->Format; /*ajDebug("ajFeattableWrite format is %d OK\n",ftout->Format);*/ ajFeatVocabInit(featOutFormat[format].Name); result = (*featOutFormat[format].Write)(ftout, features); ftout->Count++; return result; } /* ajDebug(" NO Features to output\n");*/ return AJTRUE; } ID ajFeattableWriteGff2 TY public MO ajfeatwrite LB core XX DE Write feature table in GFF 2.0 format XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r Feattab const AjPFeattable PD feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWriteGff2(AjPFeattabOut ftout, const AjPFeattable Feattab) { AjIList iter = NULL; AjPFeature gf = NULL; AjPFile file = ftout->Handle; /* Check arguments */ /* ajDebug("ajFeattableWriteGff2 Checking arguments\n"); */ if(!file) return ajFalse; /* Print header first */ ajFmtPrintF(file, "##gff-version 2.0\n") ; ajFmtPrintF(file, "##date %D\n", ajTimeRefTodayFmt("GFF")) ; if(ajStrMatchC(Feattab->Type, "N")) ajFmtPrintF(file, "##Type %s %S\n", "DNA", Feattab->Seqid); else if(ajStrMatchC(Feattab->Type, "P")) ajFmtPrintF(file, "##Type %s %S\n", "Protein", Feattab->Seqid); else ajFmtPrintF(file, "##Type unknown <%S> %S\n", Feattab->Type, Feattab->Seqid); /* For all features... relatively simple because internal structures are deliberately styled on GFF */ if(Feattab->Features) { iter = ajListIterNewread(Feattab->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); featDumpGff2(gf, Feattab, file); } ajListIterDel(&iter); } return ajTrue; } ID ajFeattableWriteGff3 TY public MO ajfeatwrite LB core XX DE Write feature table in GFF format XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r Feattab const AjPFeattable PD feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWriteGff3(AjPFeattabOut ftout, const AjPFeattable Feattab) { AjIList iter = NULL; AjPFeature gf = NULL; AjPFile file = ftout->Handle; AjPTagval tagval; /* Check arguments */ /* ajDebug("ajFeattableWriteGff3 Checking arguments\n"); */ if(!file) return ajFalse; ajFeattableTrace(Feattab); /* Print GFF3-specific header first with ## tags */ ajFmtPrintF(file, "##gff-version 3\n") ; ajFmtPrintF(file, "##sequence-region %S %u %u\n", Feattab->Seqid, ajFeattableGetBegin(Feattab), ajFeattableGetEnd(Feattab)); /* EMBOSS specific header with #! tags */ ajFmtPrintF(file, "#!Date %D\n", ajTimeRefTodayFmt("GFF")); if(ajStrMatchC(Feattab->Type, "P")) ajFmtPrintF(file, "#!Type Protein\n"); else ajFmtPrintF(file, "#!Type DNA\n"); ajFmtPrintF(file, "#!Source-version EMBOSS %S\n", ajNamValueVersion()); /* For all features... relatively simple because internal structures are deliberately styled on GFF */ if(Feattab->Features) { iter = ajListIterNewread(Feattab->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); if(Feattab->Circular && gf->Start == 1 && gf->End == Feattab->Len) { tagval = ajTagvalNewC("Is_circular", "true"); ajListPushAppend(gf->GffTags, tagval); tagval = NULL; } featDumpGff3(gf, NULL, Feattab, file); } ajListIterDel(&iter); } return ajTrue; } ID ajFeattableWriteBed TY public MO ajfeatwrite LB core XX DE Write feature table in BED format XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r Feattab const AjPFeattable PD feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWriteBed(AjPFeattabOut ftout, const AjPFeattable Feattab) { AjIList iter = NULL; AjPFeature gf = NULL; AjPFile file = ftout->Handle; AjBool usescore = ajFalse; float minscore=0.0; float maxscore=0.0; /* Check arguments */ /* ajDebug("ajFeattableWriteBed Checking arguments\n"); */ if(!file) return ajFalse; usescore = ajFeattableGetScorerange(Feattab, &minscore, &maxscore); /* Print header first */ if(usescore) ajFmtPrintF(file, "track name=%S description=\"EMBOSS:%S\" useScore=1\n", Feattab->Seqid, ajNamValueVersion()); else ajFmtPrintF(file, "track name=%S description=\"EMBOSS:%S\" useScore=0\n", Feattab->Seqid, ajNamValueVersion()); /* For all features... relatively simple because internal structures are deliberately styled on GFF */ if(Feattab->Features) { iter = ajListIterNewread(Feattab->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); featDumpBed(gf, Feattab, minscore, (maxscore - minscore), file); } ajListIterDel(&iter); } return ajTrue; } ID ajFeattableWriteDdbj TY public MO ajfeatwrite LB core XX DE Write a feature table in DDBJ format. XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r thys const AjPFeattable PD feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWriteDdbj(AjPFeattabOut ftout, const AjPFeattable thys) { return feattableWriteEmbl(thys,ftout->Handle,ajFalse); } ID ajFeattableWriteEmbl TY public MO ajfeatwrite LB core XX DE Write a feature table in EMBL format. XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r thys const AjPFeattable PD feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWriteEmbl(AjPFeattabOut ftout, const AjPFeattable thys) { return feattableWriteEmbl(thys,ftout->Handle,ajTrue); } ID ajFeattableWriteGenbank TY public MO ajfeatwrite LB core XX DE Write a feature table in GenBank format. XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r thys const AjPFeattable PD feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWriteGenbank(AjPFeattabOut ftout, const AjPFeattable thys) { return feattableWriteEmbl(thys,ftout->Handle,ajFalse); } ID ajFeattableWriteRefseq TY public MO ajfeatwrite LB core XX DE Write a feature table in Refseq format. XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r thys const AjPFeattable PD feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWriteRefseq(AjPFeattabOut ftout, const AjPFeattable thys) { return feattableWriteRefseq(thys,ftout->Handle); } ID ajFeattableWriteRefseqp TY public MO ajfeatwrite LB core XX DE Write a feature table in Refseqp format. XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r thys const AjPFeattable PD feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWriteRefseqp(AjPFeattabOut ftout, const AjPFeattable thys) { return feattableWriteRefseqp(thys,ftout->Handle); } ID featWriteEmbl TY static MO ajfeatwrite LB core XX DE Write a feature in EMBL format. Include any subfeatures XX PN [1] PA r thys const AjPFeature PD Feature object PX PN [2] PA u file AjPFile PD Output file PX PN [3] PA r Seqid const AjPStr PD Sequence ID PX PN [4] PA r IsEmbl AjBool PD ajTrue for EMBL (different line prefix) PX RT AjBool RD ajTrue on success RX // static AjBool featWriteEmbl(const AjPFeature thys, AjPFile file, const AjPStr Seqid, AjBool IsEmbl) { AjIList iter = NULL; AjPFeature gf = NULL; AjBool whole = ajFalse; /* has "complement(" been added */ AjPStr location = NULL; /* location list as a string */ AjPStr temp = NULL; AjPStr pos = NULL; ajuint exon = 0; location = ajStrNewRes(80); temp = ajStrNewRes(80); pos = ajStrNewRes(80); if((thys->Flags & AJFEATFLAG_MULTIPLE) && ajListGetLength(thys->Subfeatures)) { if(thys->Flags & AJFEATFLAG_COMPLEMENT_MAIN) whole = ajTrue; if(whole) ajStrAppendC(&location,"complement("); /* location from subfeatures */ if(thys->Flags & AJFEATFLAG_ORDER) ajStrAppendC(&location,"order("); else ajStrAppendC(&location,"join("); iter = ajListIterNewread(thys->Subfeatures); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); if(exon++) ajStrAppendC(&location,","); ajStrSetClear(&temp); ajStrSetClear(&pos); /* location start position */ if(gf->Flags & AJFEATFLAG_REMOTEID) { ajFmtPrintAppS(&pos,"%S:",gf->Remote); /* ajDebug("remote: %S\n", gf->Remote); */ } if(gf->Flags & AJFEATFLAG_LABEL) { ajFmtPrintAppS(&pos,"%S",gf->Label); /* ajDebug("label: %S\n", gf->Label); */ } else if(gf->Flags & AJFEATFLAG_START_BEFORE_SEQ) { ajFmtPrintAppS(&pos,"<%d",gf->Start); /* ajDebug("Flags & AJFEATFLAG_START_TWO) { ajFmtPrintAppS(&pos,"(%d.%d)",gf->Start,gf->Start2); /* ajDebug("start2 (%d.%d)\n", gf->Start, gf->Start2); */ } else { ajFmtPrintAppS(&pos,"%d",gf->Start); /* ajDebug("start\n"); */ } /* location range and end position */ if(!(gf->Flags & AJFEATFLAG_POINT)) { if(gf->Flags & AJFEATFLAG_BETWEEN_SEQ) { ajFmtPrintAppS(&pos,"^%d",gf->End); /* ajDebug("between ^end\n"); */ } else if(gf->Flags & AJFEATFLAG_END_AFTER_SEQ) { ajFmtPrintAppS(&pos,"..>%d",gf->End); /* ajDebug(">end\n"); */ } else if(gf->Flags & AJFEATFLAG_END_TWO) { ajFmtPrintAppS(&pos,"..(%d.%d)",gf->End2,gf->End); /* ajDebug("end2 (%d.%d)\n", gf->End2, gf->End); */ } else { ajFmtPrintAppS(&pos,"..%d",gf->End); /* ajDebug(".. end\n"); */ } } if(gf->Strand == '-' && !whole) { ajStrAssignC(&temp,"complement("); ajStrAppendS(&temp,pos); ajStrAppendC(&temp,")"); /* ajDebug("strand '-', wrap exon with complement()\n"); */ } else { ajStrAssignS(&temp,pos); /* ajDebug("simple exon\n"); */ } ajStrAppendS(&location, temp); } ajListIterDel(&iter); ajStrAppendC(&location,")"); if(whole) ajStrAppendC(&location,")"); featDumpEmbl(thys, location, file, Seqid, IsEmbl); ajStrDel(&location); ajStrDel(&pos); ajStrDel(&temp); return ajTrue; } /* simple feature location */ if(thys->Flags & AJFEATFLAG_COMPLEMENT_MAIN) whole = ajTrue; if(whole) ajStrAppendC(&location,"complement("); /* location start position */ if(thys->Flags & AJFEATFLAG_REMOTEID) { ajFmtPrintAppS(&pos,"%S:",thys->Remote); /* ajDebug("remote: %S\n", thys->Remote); */ } if(thys->Flags & AJFEATFLAG_LABEL) { ajFmtPrintAppS(&pos,"%S",thys->Label); /* ajDebug("label: %S\n", thys->Label); */ } else if(thys->Flags & AJFEATFLAG_START_BEFORE_SEQ) { ajFmtPrintAppS(&pos,"<%d",thys->Start); /* ajDebug("Flags & AJFEATFLAG_START_TWO) { ajFmtPrintAppS(&pos,"(%d.%d)",thys->Start,thys->Start2); /* ajDebug("start2 (%d.%d)\n", thys->Start, thys->Start2); */ } else { ajFmtPrintAppS(&pos,"%d",thys->Start); /* ajDebug("start\n"); */ } /* location range and end position */ if(!(thys->Flags & AJFEATFLAG_POINT)) { if(thys->Flags & AJFEATFLAG_BETWEEN_SEQ) { ajFmtPrintAppS(&pos,"^%d",thys->End); /* ajDebug("between ^end\n"); */ } else if(thys->Flags & AJFEATFLAG_END_AFTER_SEQ) { ajFmtPrintAppS(&pos,"..>%d",thys->End); /* ajDebug(">end\n"); */ } else if(thys->Flags & AJFEATFLAG_END_TWO) { ajFmtPrintAppS(&pos,"..(%d.%d)",thys->End2,thys->End); /* ajDebug("end2 (%d.%d)\n", thys->End2, thys->End); */ } else { ajFmtPrintAppS(&pos,"..%d",thys->End); /* ajDebug(".. end\n"); */ } } if(thys->Strand == '-' && !whole) { ajStrAssignC(&temp,"complement("); ajStrAppendS(&temp,pos); ajStrAppendC(&temp,")"); /* ajDebug("strand '-', wrap exon with complement()\n"); */ } else { ajStrAssignS(&temp,pos); /* ajDebug("simple exon\n"); */ } ajStrAppendS(&location, temp); if(whole) ajStrAppendC(&location,")"); featDumpEmbl(thys, location, file, Seqid, IsEmbl); ajStrDel(&location); ajStrDel(&pos); ajStrDel(&temp); /* any other features grouped under this one - write separately */ if(ajListGetLength(thys->Subfeatures)) { iter = ajListIterNewread(thys->Subfeatures); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); featWriteEmbl(gf, file, Seqid, IsEmbl); } ajListIterDel(&iter); } return ajTrue; } ID feattableWriteEmbl TY static MO ajfeatwrite LB core XX DE Write a feature table in EMBL format. XX PN [1] PA r thys const AjPFeattable PD Feature table PX PN [2] PA u file AjPFile PD Output file PX PN [3] PA r IsEmbl AjBool PD ajTrue for EMBL (different line prefix) PX RT AjBool RD ajTrue on success RX // static AjBool feattableWriteEmbl(const AjPFeattable thys, AjPFile file, AjBool IsEmbl) { AjIList iter = NULL; AjPFeature gf = NULL; /* Check arguments */ /* ajDebug("feattableWriteEmbl Checking arguments\n"); */ if(!file) return ajFalse; if(!ajFeattableIsNuc(thys)) return ajFalse; /* feature table heading */ if(IsEmbl) { ajFmtPrintF(file, "FH Key Location/Qualifiers\n"); ajFmtPrintF(file, "FH\n"); } else ajFmtPrintF(file, "FEATURES Location/Qualifiers\n"); if(!thys->Features) return ajFalse; /* For all features... we need to process a group at a time */ ajListSort(thys->Features, &ajFeatCompByGroup); iter = ajListIterNewread(thys->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); featWriteEmbl(gf, file, thys->Seqid, IsEmbl); } ajListIterDel(&iter); return ajTrue; } ID feattableWriteRefseq TY static MO ajfeatwrite LB core XX DE Write a feature table in REFSEQ format. XX PN [1] PA r thys const AjPFeattable PD Feature table PX PN [2] PA u file AjPFile PD Output file PX RT AjBool RD ajTrue on success RX // static AjBool feattableWriteRefseq(const AjPFeattable thys, AjPFile file) { AjIList iter = NULL; AjPFeature gf = NULL; AjPFeature gfprev = NULL; AjBool join = ajFalse; AjBool whole = ajFalse; /* has "complement(" been added */ AjPStr location = NULL; /* location list as a string */ AjPStr temp = NULL; AjPStr pos = NULL; ajuint oldgroup = UINT_MAX; /* Check arguments */ /* ajDebug("feattableWriteRefseq Checking arguments\n"); */ if(!file) return ajFalse; if(!ajFeattableIsNuc(thys)) return ajFalse; /* feature table heading */ ajFmtPrintF(file, "FEATURES Location/Qualifiers\n"); location = ajStrNewRes(80); temp = ajStrNewRes(80); pos = ajStrNewRes(80); /* For all features... we need to process a group at a time */ ajListSort(thys->Features, &ajFeatCompByGroup); if(thys->Features) { iter = ajListIterNewread(thys->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); if((oldgroup != gf->Group) && gfprev) /* previous location ready */ { if(join) { ajStrAppendC(&location,")"); /* close bracket for join */ /* ajDebug("join: closing ')' appended\n"); */ } if(whole) { ajStrInsertC(&location,0,"complement("); ajStrAppendC(&location,")"); /* ajDebug("wrap with complement(), reset whole %b to N\n", whole); */ whole = ajFalse; } /* ajDebug("calling featDumpRefseq for gfprev\n"); */ /* ajDebug("location: '%S'\n", location); */ featDumpRefseq(gfprev, location, file, thys->Seqid); /* gfprev has tag data */ /* reset the values from previous */ /* ajDebug("reset location\n"); */ ajStrSetClear(&location); /* ajDebug("reset join %b to N\n", join); */ join = ajFalse; } oldgroup = gf->Group; /* process the new gf */ /* ajDebug("\n'%S' group: %d exon: %d flags:%x tags: %Lu\n", gf->Type, gf->Group,gf->Exon, gf->Flags, ajListGetLength(gf->Tags)); */ if(gf->Flags & AJFEATFLAG_COMPLEMENT_MAIN) { /* ajDebug("set2 whole %b to Y\n", whole); */ whole =ajTrue; } if(ajStrGetLen(location)) /* one location already there */ { if(!join) { /* ajDebug("insert 'join(', set join Y\n"); */ if(gf->Flags & AJFEATFLAG_ORDER) ajStrInsertC(&location,0,"order("); else ajStrInsertC(&location,0,"join("); join = ajTrue; } ajStrAppendC(&location,","); /* ajDebug("append ','\n"); */ } ajStrSetClear(&temp); ajStrSetClear(&pos); if(gf->Flags & AJFEATFLAG_REMOTEID) { ajFmtPrintAppS(&pos,"%S:",gf->Remote); /* ajDebug("remote: %S\n", gf->Remote); */ } if(gf->Flags & AJFEATFLAG_LABEL) { ajFmtPrintAppS(&pos,"%S",gf->Label); /* ajDebug("label: %S\n", gf->Label); */ } else if(gf->Flags & AJFEATFLAG_START_BEFORE_SEQ) { ajFmtPrintAppS(&pos,"<%d",gf->Start); /* ajDebug("Flags & AJFEATFLAG_START_TWO) { ajFmtPrintAppS(&pos,"(%d.%d)",gf->Start,gf->Start2); /* ajDebug("start2 (%d.%d)\n", gf->Start, gf->Start2); */ } else { ajFmtPrintAppS(&pos,"%d",gf->Start); /* ajDebug("start\n"); */ } if(!(gf->Flags & AJFEATFLAG_POINT)) { if(gf->Flags & AJFEATFLAG_BETWEEN_SEQ) { ajFmtPrintAppS(&pos,"^%d",gf->End); /* ajDebug("between ^end\n"); */ } else if(gf->Flags & AJFEATFLAG_END_AFTER_SEQ) { ajFmtPrintAppS(&pos,"..>%d",gf->End); /* ajDebug(">end\n"); */ } else if(gf->Flags & AJFEATFLAG_END_TWO) { ajFmtPrintAppS(&pos,"..(%d.%d)",gf->End2,gf->End); /* ajDebug("end2 (%d.%d)\n", gf->End2, gf->End); */ } else { ajFmtPrintAppS(&pos,"..%d",gf->End); /* ajDebug(".. end\n"); */ } } if(gf->Strand == '-' && !whole) { ajStrAssignC(&temp,"complement("); ajStrAppendS(&temp,pos); ajStrAppendC(&temp,")"); /* ajDebug("strand '-', wrap exon with complement()\n"); */ } else { ajStrAssignS(&temp,pos); /* ajDebug("simple exon\n"); */ } ajStrSetClear(&pos); ajStrAppendS(&location,temp); /* this is the parent/only feature */ gfprev=gf; } ajListIterDel(&iter); if(gfprev) { /* Don't forget the last one !!! */ if(join) { ajStrAppendC(&location,")"); /* close bracket for join */ /* ajDebug("last: join: closing ')' appended\n"); */ } if(whole) { ajStrInsertC(&location,0,"complement("); ajStrAppendC(&location,")"); /* ** ajDebug("last: wrap with complement(), reset whole %b " ** "to N\n", whole); */ whole = ajFalse; } /* ajDebug("last: calling featDumpRefseq for gfprev\n"); */ /* ajDebug("location: '%S'\n", location); */ featDumpRefseq(gfprev, location, file, thys->Seqid) ; /* gfprev has tag data */ } ajStrDel(&location); ajStrDel(&pos); ajStrDel(&temp); } /* ajDebug("ajFeattableWriteRefseq Done\n"); */ return ajTrue; } ID feattableWriteRefseqp TY static MO ajfeatwrite LB core XX DE Write a feature table in REFSEQP format. XX PN [1] PA r thys const AjPFeattable PD Feature table PX PN [2] PA u file AjPFile PD Output file PX RT AjBool RD ajTrue on success RX // static AjBool feattableWriteRefseqp(const AjPFeattable thys, AjPFile file) { AjIList iter = NULL; AjPFeature gf = NULL; AjPFeature gfprev = NULL; AjBool join = ajFalse; AjBool whole = ajFalse; /* has "complement(" been added */ AjPStr location = NULL; /* location list as a string */ AjPStr temp = NULL; AjPStr pos = NULL; ajuint oldgroup = UINT_MAX; /* Check arguments */ /* ajDebug("feattableWriteRefseqp Checking arguments\n"); */ if(!file) return ajFalse; if(!ajFeattableIsNuc(thys)) return ajFalse; /* feature table heading */ ajFmtPrintF(file, "FEATURES Location/Qualifiers\n"); location = ajStrNewRes(80); temp = ajStrNewRes(80); pos = ajStrNewRes(80); /* For all features... we need to process a group at a time */ ajListSort(thys->Features, &ajFeatCompByGroup); if(thys->Features) { iter = ajListIterNewread(thys->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); if((oldgroup != gf->Group) && gfprev) /* previous location ready */ { if(join) { ajStrAppendC(&location,")"); /* close bracket for join */ /* ajDebug("join: closing ')' appended\n"); */ } if(whole) { ajStrInsertC(&location,0,"complement("); ajStrAppendC(&location,")"); /* ajDebug("wrap with complement(), reset whole %b to N\n", whole); */ whole = ajFalse; } /* ajDebug("calling featDumpRefseqp for gfprev\n"); */ /* ajDebug("location: '%S'\n", location); */ featDumpRefseqp(gfprev, location, file, thys->Seqid); /* gfprev has tag data */ /* reset the values from previous */ /* ajDebug("reset location\n"); */ ajStrSetClear(&location); /* ajDebug("reset join %b to N\n", join); */ join = ajFalse; } oldgroup = gf->Group; /* process the new gf */ /* ajDebug("\n'%S' group: %d exon: %d flags:%x tags: %Lu\n", gf->Type, gf->Group,gf->Exon, gf->Flags, ajListGetLength(gf->Tags)); */ if(gf->Flags & AJFEATFLAG_COMPLEMENT_MAIN) { /* ajDebug("set2 whole %b to Y\n", whole); */ whole =ajTrue; } if(ajStrGetLen(location)) /* one location already there */ { if(!join) { /* ajDebug("insert 'join(', set join Y\n"); */ if(gf->Flags & AJFEATFLAG_ORDER) ajStrInsertC(&location,0,"order("); else ajStrInsertC(&location,0,"join("); join = ajTrue; } ajStrAppendC(&location,","); /* ajDebug("append ','\n"); */ } ajStrSetClear(&temp); ajStrSetClear(&pos); if(gf->Flags & AJFEATFLAG_REMOTEID) { ajFmtPrintAppS(&pos,"%S:",gf->Remote); /* ajDebug("remote: %S\n", gf->Remote); */ } if(gf->Flags & AJFEATFLAG_LABEL) { ajFmtPrintAppS(&pos,"%S",gf->Label); /* ajDebug("label: %S\n", gf->Label); */ } else if(gf->Flags & AJFEATFLAG_START_BEFORE_SEQ) { ajFmtPrintAppS(&pos,"<%d",gf->Start); /* ajDebug("Flags & AJFEATFLAG_START_TWO) { ajFmtPrintAppS(&pos,"(%d.%d)",gf->Start,gf->Start2); /* ajDebug("start2 (%d.%d)\n", gf->Start, gf->Start2); */ } else { ajFmtPrintAppS(&pos,"%d",gf->Start); /* ajDebug("start\n"); */ } if(!(gf->Flags & AJFEATFLAG_POINT)) { if(gf->Flags & AJFEATFLAG_BETWEEN_SEQ) { ajFmtPrintAppS(&pos,"^%d",gf->End); /* ajDebug("between ^end\n"); */ } else if(gf->Flags & AJFEATFLAG_END_AFTER_SEQ) { ajFmtPrintAppS(&pos,"..>%d",gf->End); /* ajDebug(">end\n"); */ } else if(gf->Flags & AJFEATFLAG_END_TWO) { ajFmtPrintAppS(&pos,"..(%d.%d)",gf->End2,gf->End); /* ajDebug("end2 (%d.%d)\n", gf->End2, gf->End); */ } else { ajFmtPrintAppS(&pos,"..%d",gf->End); /* ajDebug(".. end\n"); */ } } if(gf->Strand == '-' && !whole) { ajStrAssignC(&temp,"complement("); ajStrAppendS(&temp,pos); ajStrAppendC(&temp,")"); /* ajDebug("strand '-', wrap exon with complement()\n"); */ } else { ajStrAssignS(&temp,pos); /* ajDebug("simple exon\n"); */ } ajStrSetClear(&pos); ajStrAppendS(&location,temp); /* this is the parent/only feature */ gfprev=gf; } ajListIterDel(&iter); if(gfprev) { /* Don't forget the last one !!! */ if(join) { ajStrAppendC(&location,")"); /* close bracket for join */ /* ajDebug("last: join: closing ')' appended\n"); */ } if(whole) { ajStrInsertC(&location,0,"complement("); ajStrAppendC(&location,")"); /* ** ajDebug("last: wrap with complement(), reset whole %b " ** "to N\n", whole); */ whole = ajFalse; } /* ajDebug("last: calling featDumpRefseqp for gfprev\n"); */ /* ajDebug("location: '%S'\n", location); */ featDumpRefseqp(gfprev, location, file, thys->Seqid) ; /* gfprev has tag data */ } ajStrDel(&location); ajStrDel(&pos); ajStrDel(&temp); } /* ajDebug("ajFeattableWriteRefseqp Done\n"); */ return ajTrue; } ID ajFeattableWriteSwiss TY public MO ajfeatwrite LB core XX DE Write a feature table in SwissProt format. XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r thys const AjPFeattable PD feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWriteSwiss(AjPFeattabOut ftout, const AjPFeattable thys) { AjIList iter = NULL; AjIList itersub = NULL; AjPFeature gf = NULL; AjPFeature gfsub = NULL; AjPFile file = ftout->Handle; /* Check arguments */ ajDebug("ajFeattableWriteSwiss Checking arguments\n"); if(!file) return ajFalse; if(!ajFeattableIsProt(thys)) return ajFalse; /* no FH header in SwissProt */ /* For all features... */ if(!thys->Features) return ajTrue; iter = ajListIterNewread(thys->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); if(ajListGetLength(gf->Subfeatures)) { itersub = ajListIterNewread(gf->Subfeatures); while(!ajListIterDone(itersub)) { gfsub = ajListIterGet(itersub); featDumpSwiss(gfsub, file, gf); } ajListIterDel(&itersub); } else { featDumpSwiss(gf, file, gf); } } ajListIterDel(&iter); return ajTrue; } ID ajFeattableWritePir TY public MO ajfeatwrite LB core XX DE Write a feature table in PIR format. XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r thys const AjPFeattable PD feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWritePir(AjPFeattabOut ftout, const AjPFeattable thys) { AjIList iter = NULL; AjIList itersub = NULL; AjPFeature gf = NULL; AjPFeature gfsub = NULL; AjPStr location = NULL; /* location list as a string */ AjPStr pos = NULL; AjPFile file = ftout->Handle; /* const AjPTable tagstable = NULL; const AjPTable typetable = NULL; */ /* Check arguments */ /*ajDebug("ajFeattableWritePir Checking arguments\n");*/ if(!file) return ajFalse; if(!ajFeattableIsProt(thys)) return ajFalse; if(!thys->Features) return ajTrue; /* tagstable = ajFeatVocabGetTagsProt("pir"); */ /* typetable = ajFeatVocabGetTypesProt("pir"); */ location = ajStrNewRes(80); pos = ajStrNewRes(80); /* For all features... we need to process a group at a time */ ajListSort(thys->Features, &ajFeatCompByGroup); iter = ajListIterNewread(thys->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); ajStrSetClear(&location); if(ajListGetLength(gf->Subfeatures)) { itersub = ajListIterNewread(gf->Subfeatures); while(!ajListIterDone(itersub)) { gfsub = ajListIterGet(itersub); if(ajStrGetLen(location)) ajStrAppendC(&location,","); ajFmtPrintS(&pos,"%d",gfsub->Start); if(gfsub->End != gfsub->Start) ajFmtPrintAppS(&pos,"-%d",gfsub->End); ajStrAppendS(&location,pos); } featDumpPir(gf, location, file); ajListIterDel(&itersub); } else { ajFmtPrintS(&pos,"%d",gf->Start); if(gf->End != gf->Start) ajFmtPrintAppS(&pos,"-%d",gf->End); ajStrAppendS(&location,pos); featDumpPir(gf, location, file); } } ajListIterDel(&iter); ajStrDel(&location); ajStrDel(&pos); /*ajDebug("ajFeattableWritePir Done\n");*/ return ajTrue; } ID ajFeattableWriteDasgff TY public MO ajfeatwrite LB core XX DE Write a feature table in DAS GFF format. XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r thys const AjPFeattable PD Feature table PX RT AjBool RD ajTrue on success RX // AjBool ajFeattableWriteDasgff(AjPFeattabOut ftout, const AjPFeattable thys) { /* ** Things to do: ** ** Where do the tag-values go? In for now ** ** Check standards for id, type etc. for table and features */ /*#define MULTINOTE 1*/ AjPFile file = ftout->Handle; AjIList iter = NULL; AjIList tagiter = NULL; const AjPStr outtyp = NULL; /* these come from AjPTable */ const AjPStr outtag = NULL; /* so please, please */ AjPTagval item = NULL; AjPFeature gf = NULL; /* AjPFeature gftop = NULL; */ const AjPTable tagstable = NULL; const AjPTable typetable = NULL; ajuint oldgroup = UINT_MAX; ajuint nfeat = 0; char frame; AjBool knowntag = ajTrue; AjPStr mytagname = NULL; AjPStr mytagval = NULL; #ifdef MULTINOTE ajuint ntags; ajuint itag; #endif const char* cp; /* Check arguments */ ajDebug("ajFeattableWriteDasgff Checking arguments\n"); if(!file) return ajFalse; if(!ftout->Count) { ftout->Cleanup = featCleanDasgff; ajFmtPrintF(file, "\n"); /* deleted in DAS 1.6 ajFmtPrintF(file, "\n"); */ ajFmtPrintF(file, "\n"); ajFmtPrintF(file, " \n"); } ajFmtPrintF(file, " Seqid, 1+thys->Offset+thys->Start, 1+thys->Offset+thys->End); /* label is optional */ /* ajFmtPrintF(outseq->File, " label=\"%s\">", ""); */ /* type attribute is optional and may not mean this */ /* if(ajFeattableIsProt(thys)) ajFmtPrintF(outseq->File, " type=\"Protein\">"); else ajFmtPrintF(outseq->File, " type=\"DNA\">"); */ ajFmtPrintF(file, " version=\"%s\">\n", "0.0"); /* For all features... */ /* SEGMENT requires at least one feature - if none, make one up */ if(thys->Features) { iter = ajListIterNewread(thys->Features); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); nfeat++; if(gf->Protein) { typetable = ajFeatVocabGetTypesProt("gff3protein"); tagstable = ajFeatVocabGetTagsProt("gff3protein"); ajDebug("ajFeattableWriteDasgff GFF3 protein table\n"); } else { typetable = ajFeatVocabGetTypesNuc("gff3"); tagstable = ajFeatVocabGetTagsNuc("gff3"); ajDebug("ajFeattableWriteDasgff GFF3 nucleotide table\n"); } outtyp = ajFeattypeGetExternal(gf->Type, typetable); if(oldgroup != gf->Group) { oldgroup = gf->Group; /* gftop = gf; */ } else { #if 0 gftop = gf; /* this is the parent/only feature */ #endif } ajFmtPrintF(file, " \n", thys->Seqid, gf->Source, gf->Group); ajFeatGetTagC(gf,"category",1, &mytagval); if(ajStrGetLen(mytagval)==0) ajStrAssignS(&mytagval, ajFeatTypeGetCategory(gf->Type)); ajFmtPrintF(file, " %S\n", gf->Type, mytagval, outtyp); ajStrDel(&mytagval); /* ** METHOD id should be something the server can return ** For application results this is the application name */ if(ajStrGetLen(gf->Source)) ajFmtPrintF(file, " \n", gf->Source); else ajFmtPrintF(file, " \n", gf->Source); /* ** START and END are optional in DAS 1.6 but we always have them */ ajFmtPrintF(file, " %d\n", gf->Start); ajFmtPrintF(file, " %d\n", gf->End); /* ** SCORE is optional in DAS 1.6 ** ** we don't know a value 'zero' means actual 'zero' ** or 'inapplicable', ** looks safer not printing the tag when the score is zero */ if(!E_FPZERO(gf->Score,U_FEPS)) ajFmtPrintF(file, " %.f\n", gf->Score); /* ** ORIENTATION is optional in DAS 1.6 ** for proteins or non-transcriptional features */ if(gf->Strand) ajFmtPrintF(file, " %c\n", gf->Strand); /* 0 or - or + */ else ajFmtPrintF(file, " 0\n"); /* ** PHASE is optional in DAS 1.6 ** for proteins or non-translational features */ if(ajFeatTypeIsCds(gf)) frame = ajFeatframeGetFrameNuc(gf->Frame); else frame = ajFeatframeGetFrame(gf->Frame); if(gf->Frame == 0) ajFmtPrintF(file, " -\n"); else ajFmtPrintF(file, " %c\n", frame); /* ** tag-value pairs are written as NOTES ** We write one note for each ** This displays well in various DAS client browsers */ if(gf->Tags) { #ifdef MULTINOTE ntags = ajListGetLength(gf->Tags); itag = 0; #endif tagiter = ajListIterNewread(gf->Tags); while(!ajListIterDone(tagiter)) { item = (AjPTagval)ajListIterGet(tagiter); outtag = ajFeattagGetNameS(MAJTAGVALGETTAG(item), tagstable, &knowntag); if(!outtag) { ajFeatWarn("Unknown GFF3 feature tag '%S'", ajTagvalGetTag(item)); continue; } ajFeattagFormat(outtag, tagstable, &featoutFmtTmp); #ifdef MULTINOTE if(ntags == 1) { ajFmtPrintF(file, " "); } else { if(!itag) ajFmtPrintF(file, " \n"); } #else ajFmtPrintF(file, " "); #endif ajFmtPrintS(&featoutStr, "%S", outtag); ajStrAssignS(&featoutValTmp, MAJTAGVALGETVALUE(item)); cp = ajStrGetPtr(featoutFmtTmp); switch(CASE2(cp[0], cp[1])) { case CASE2('L','I') : /* limited */ /*ajDebug("case limited\n");*/ ajFeattagGetLimit(outtag, tagstable, &featoutLimTmp); featTagAllLimit(&featoutValTmp, featoutLimTmp); ajFmtPrintAppS(&featoutStr, ":%S", featoutValTmp); break; case CASE2('Q', 'L') : /* limited, escape quotes */ /*ajDebug("case qlimited\n");*/ ajFeattagGetLimit(outtag, tagstable, &featoutLimTmp); featTagAllLimit(&featoutValTmp, featoutLimTmp); /*featTagQuoteGff3(&featoutValTmp);*/ ajFmtPrintAppS(&featoutStr, ":%S", featoutValTmp); break; case CASE2('T','E') :/* no space/quotes, wrap margin */ /*ajDebug("case text\n");*/ ajStrRemoveWhite(&featoutValTmp); ajFmtPrintAppS(&featoutStr, ":%S", featoutValTmp); break; case CASE2('Q','T') :/* escape quotes, wrap at space */ /*ajDebug("case qtext\n");*/ /*featTagQuoteGff3(&featoutValTmp);*/ if(ajStrMatchC(outtag, "note") && ajStrGetCharFirst(featoutValTmp) == '*') { ajStrCutStart(&featoutValTmp,1); ajStrExtractWord(featoutValTmp, &mytagval, &mytagname); ajFmtPrintS(&featoutStr, "%S:%S", mytagname, mytagval); ajStrDel(&mytagname); ajStrDel(&mytagval); } else ajFmtPrintAppS(&featoutStr, ":%S", featoutValTmp); break; case CASE2('Q','W') :/* escape quotes, remove space */ /*ajDebug("case qtext\n");*/ /*featTagQuoteGff3(&featoutValTmp);*/ ajStrRemoveWhite(&featoutValTmp); ajFmtPrintAppS(&featoutStr, ":%S", featoutValTmp); break; case CASE2('Q', 'S') : /* special regexp, quoted */ /*ajDebug("case qspecial\n");*/ if(!ajFeattagSpecialGff3(outtag, &featoutValTmp)) featTagDasgffDefault(&featoutStr, outtag, &featoutValTmp); else { /*featTagQuoteGff3(&featoutValTmp);*/ ajFmtPrintAppS(&featoutStr, ":%S", featoutValTmp); } break; case CASE2('S','P') : /* special regexp */ /*ajDebug("case special\n");*/ if(!ajFeattagSpecialGff3(outtag, &featoutValTmp)) featTagDasgffDefault(&featoutStr, outtag, &featoutValTmp); else ajFmtPrintAppS(&featoutStr, ":%S", featoutValTmp); break; case CASE2('V','O') : /* no value, so an error here */ /*ajDebug("case void\n");*/ break; default: ajFeatWarn("Unknown GFF3 feature tag type '%S' " "for '%S'", featoutFmtTmp, outtag); } /* TESTING: for single NOTE write ';' after each tag-value pair */ #ifdef MULTINOTE if(++itag < ntags) ajFmtPrintAppS(&featoutStr,";"); if(ntags == 1) { ajFmtPrintF(file, "%S\n",featoutStr); } else { ajFmtPrintF(file, " %S\n", featoutStr); if(itag == ntags) ajFmtPrintF(file, " \n"); } #else ajFmtPrintF(file, "%S\n",featoutStr); #endif } ajListIterDel(&tagiter); } /* link to more information about the feature */ /* ajFmtPrintF(file, " %s\n", "url-here", "text-here); */ /* target in an alignment */ /* ajFmtPrintF(file, " %s\n", "target-id", target->start, target->stop,"target-name"); */ /* GROUP is deprecated in DAS 1.6 - replaced by PARENT and PART */ /* ajFmtPrintF(file, " \n", thys->Seqid, gf->Group, thys->Seqid, gf->Group, thys->Seqid, gf->Group);*/ /* link to more GROUP information */ /* ajFmtPrintF(file, " %s\n", "url-here", "text-here); */ /* target in a GROUP alignment */ /* ajFmtPrintF(file, " %s\n", "target-id", target->start, target->stop,"target-name"); ajFmtPrintF(file, " \n"); */ ajFmtPrintF(file, " \n"); } ajListIterDel(&iter) ; } /* it seems unclear whether DASGFF allows empty segments ** this block can be used if some dummy features is mandatory ** but note that any feature will appear in a DASGFF viewer ** unless there is some official dummy type available ** ** Common practice is to ignore the DTD and write an empty SEGMENT ** so this section should not be needed. DAS 1.6 will correct the spec */ /* if(!nfeat) { ajFmtPrintF(file, " \n", thys->Seqid); ajFmtPrintF(file, " %s\n", "dummy", "feature", "dummy)"; if(ajStrGetLen(gf->Source)) ajFmtPrintF(file, " \n", gf->Source); else ajFmtPrintF(file, " \n"); ajFmtPrintF(file, " 1\n"); ajFmtPrintF(file, " %d\n", thys->Len); ajFmtPrintF(file, " 0.0\n"); ajFmtPrintF(file, " 0\n"); ajFmtPrintF(file, " -\n"); ajFmtPrintF(file, " %s\n", "Written by EMBOSS"); ajFmtPrintF(file, " \n"); } */ ajFmtPrintF(file, " \n"); return ajTrue; } ID featCleanDasgff TY static MO ajfeatwrite LB core XX DE Writes the remaining lines to complete and close a DASGFF XML file XX PN [1] PA u file AjPFile PD Output file PX RT void RD RX // static void featCleanDasgff(AjPFile file) { ajFmtPrintF(file, " \n"); ajFmtPrintF(file, "\n"); return; } ID ajFeattableWriteDraw TY public MO ajfeatwrite LB core XX DE Writes a report in Draw format, for use as input to cirdna or lindna DE DE Format:<br> DE group<br> DE <br> DE label<br> DE tick [tagvalue] [start] 8<br> DE endlabel<br> DE label<br> DE tick [tagvalue] [end] 3<br> DE endlabel<br> DE <br> DE endgroup<br> DE DE Data reported: DE DE Tags required: DE DE Tags used: DE DE Tags reported: XX PN [1] PA u ftout AjPFeattabOut PD Features table output object PX PN [2] PA r thys const AjPFeattable PD Feature table object PX RT AjBool RD True on success RX // AjBool ajFeattableWriteDraw(AjPFeattabOut ftout, const AjPFeattable thys) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; AjPStr subseq = NULL; AjPStr tagval = NULL; ajint istart = 0; ajint iend = 0; outf = ftout->Handle; ajFmtPrintF(outf, "Start %d\n", ajFeattableGetBegin(thys)); ajFmtPrintF(outf, "End %d\n", ajFeattableGetEnd(thys)); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "group\n"); iterft = ajListIterNewread(thys->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); /*strand = ajFeatGetStrand(feature);*/ istart = feature->Start; iend = feature->End; ajFmtPrintF(outf, "label\n"); ajFmtPrintF(outf, "Range %d %d | | 9 H\n", istart, iend); ajFmtPrintF(outf, "%S\n", ajFeatGetType(feature)); ajFmtPrintF(outf, "endlabel\n"); ajFmtPrintF(outf, "\n"); } ajFmtPrintF(outf, "endgroup\n"); ajStrDel(&subseq); ajStrDel(&tagval); ajListIterDel(&iterft); return ajTrue; } ID ajFeattabOutSetType TY public MO ajfeatwrite LB core XX DE Sets the type for feature output XX PN [1] PA u thys AjPFeattabOut PD Feature output object PX PN [2] PA r type const AjPStr PD Feature type "nucleotide" "protein" PX RT AjBool RD ajTrue on success RX // AjBool ajFeattabOutSetType(AjPFeattabOut thys, const AjPStr type) { return ajFeattabOutSetTypeC(thys, ajStrGetPtr(type)); } ID ajFeattabOutSetTypeC TY public MO ajfeatwrite LB core XX DE Sets the type for feature output XX PN [1] PA u thys AjPFeattabOut PD Feature output object PX PN [2] PA r type const char* PD Feature type "nucleotide" "protein" PX RT AjBool RD ajTrue on success RX // AjBool ajFeattabOutSetTypeC(AjPFeattabOut thys, const char* type) { ajint i = 0; if(!*type) return ajTrue; for(i=0; featOutTypes[i].Name; i++) { if(ajCharMatchCaseC(featOutTypes[i].Name, type)) { if(featOutTypes[i].Value) ajStrAssignC(&thys->Type, featOutTypes[i].Value); return ajTrue; } } ajErr("Unrecognised feature output type '%s'", type); return ajFalse; } ID featLocEmblWrapC TY static MO ajfeatwrite LB core XX DE Splits EMBL feature location at the last possible comma DE and adds the appropriate prefix (e.g. the EMBL FT line type) XX PN [1] PA u Ploc AjPStr* PD location as a string PX PN [2] PA r margin ajuint PD Right margin PX PN [3] PA r prefix const char* PD Left margin prefix string PX PN [4] PA r preftyp const char* PD Left margin prefix string for first line PD (includes the feature key) PX PN [5] PA w retstr AjPStr* PD string with formatted value. PX RT void RD RX // static void featLocEmblWrapC(AjPStr *Ploc, ajuint margin, const char* prefix, const char* preftyp, AjPStr* retstr) { ajint left = 0; ajint width = 0; ajlong len = 0L; ajlong i; ajlong j; ajlong k; ajlong last; left = strlen(prefix); width = margin - left; /* available width for printing */ ajStrRemoveWhite(Ploc); /* no white space in locations */ len = ajStrGetLen(*Ploc); k = width; /* for safety - will be set in time */ ajDebug("featLocEmblWrapC %Ld <%d> '%S'\n", len, width, *Ploc); for(i=0; i < len; i+= k) { last = i + width - 1; ajDebug("try %Ld to %Ld (len %Ld)\n", i, last, len); if((last+1) >= len) /* no need to split */ { ajStrAssignSubS(&featoutTmpStr, *Ploc, i, len-1); ajDebug("last %Ld >= len %Ld\n", last, len); j = 0; } else { ajStrAssignSubS(&featoutTmpStr, *Ploc, i, last); /* save max string */ j = ajStrFindlastC(featoutTmpStr, ","); /* last comma in featoutTmpStr */ ajDebug("comma at %Ld\n", j); } if(j < 1) /* no comma found */ { ajDebug("no comma j=%Ld k=%u '%S'\n", j, ajStrGetLen(featoutTmpStr), featoutTmpStr); j = ajStrGetLen(featoutTmpStr); k = j; } else { /* print up to last comma */ j++; k = j; /* start after the comma */ } ajDebug("%Ld +%Ld k=%Ld featoutTmpStr: '%.*S'\n", i, j, k, (ajuint) j, featoutTmpStr); if(!i) ajFmtPrintAppS(retstr, "%s%.*S\n", preftyp,(ajuint)j, featoutTmpStr); else ajFmtPrintAppS(retstr, "%s%.*S\n", prefix,(ajuint)j, featoutTmpStr); } return; } ID featTagEmblWrapC TY static MO ajfeatwrite LB core XX DE Splits feature table output at the last possible space (or DE the last column if there are no spaces) and adds the appropriate DE prefix (e.g. the EMBL FT line type) XX PN [1] PA u pval AjPStr* PD parameter value PX PN [2] PA r margin ajuint PD Right margin PX PN [3] PA r prefix const char* PD Left margin prefix string PX PN [4] PA w retstr AjPStr* PD string with formatted value. PX RT void RD RX // static void featTagEmblWrapC(AjPStr *pval, ajuint margin, const char* prefix, AjPStr* retstr) { ajint left = 0; ajint width = 0; ajlong len = 0L; ajlong i; ajlong j; ajlong k; ajlong last; left = strlen(prefix); width = margin - left; /* available width for printing */ ajStrRemoveWhiteExcess(pval); /* single spaces only */ len = ajStrGetLen(*pval); k = width; /* for safety - will be set in time */ /* ajDebug("featTagEmblWrapC %Ld <%Ld> '%S'\n", len, width, *pval); */ for(i=0; i < len; i+= k) { last = i + width - 1; /* ajDebug("try %Ld to %Ld (len %Ld)\n", i, last, len); */ if((last+1) >= len) /* no need to split */ { ajStrAssignSubS(&featoutTmpStr, *pval, i, len-1); /* ajDebug("last %Ld >= len %Ld\n", last, len); */ j = 0; } else if(ajStrGetCharPos(*pval, (last+1)) == ' ') /* split at max width */ { ajStrAssignSubS(&featoutTmpStr, *pval, i, last); j = last + 1 - i; } else { ajStrAssignSubS(&featoutTmpStr, *pval, i, last); /* save max string */ j = ajStrFindlastC(featoutTmpStr, " "); /* last space in featoutTmpStr */ /* ajDebug("space at %Ld\n", j); */ } if(j < 1) /* no space found */ { j = ajStrGetLen(featoutTmpStr); k = j; } else /* print up to last space */ { k = j + 1; /* start after the space */ } /* ajDebug("%Ld +%Ld '%.*S'\n", i, j, (ajuint)j, featoutTmpStr); */ ajFmtPrintAppS(retstr, "%s%.*S\n", prefix,(ajuint)j, featoutTmpStr); } return; } ID featTagSwissWrapC TY static MO ajfeatwrite LB core XX DE Splits feature table output at the last possible space (or DE the last column if there are no spaces) and adds the appropriate DE prefix (e.g. the SwissProt FT line type) XX PN [1] PA u pval AjPStr* PD parameter value PX PN [2] PA r margin ajuint PD Right margin PX PN [3] PA r prefix const char* PD Left margin prefix string PX PN [4] PA w retstr AjPStr* PD string with formatted value. PX RT void RD RX // static void featTagSwissWrapC(AjPStr *pval, ajuint margin, const char* prefix, AjPStr* retstr) { ajuint left = 0; ajuint width = 0; ajuint len = 0; ajulong i; ajulong j; ajulong k; ajlong iftid = -1; AjBool isftidstart = ajFalse; /* AjBool isftid = ajFalse; */ AjPStr valstr = NULL; ajulong last; left = strlen(prefix); width = margin - left; /* available width for printing */ k = width; /* will be reset in the loop */ ajDebug("featTagSwissWrapC %d <%d> '%S'\n", ajStrGetLen(*pval), width, *pval); if(ajStrGetLen(*pval) <= left) /* no need to wrap */ { ajStrAssignS(retstr, *pval); ajStrAppendK(retstr, '\n'); /*ajDebug("simple '%S'\n", *retstr);*/ return; } ajStrAssignSubS(retstr, *pval, 0, left-1); ajStrAssignSubS(&valstr, *pval, left, -1); len = ajStrGetLen(valstr); /*ajDebug("rest '%S'\n", valstr);*/ if(ajStrPrefixC(valstr, "/FTId=")) { ajStrTrimEndC(retstr, " "); ajStrAppendK(retstr, '\n'); isftidstart = ajTrue; } for(i=0; i < len; i+= k) { last = i + width - 1; ajStrAssignSubS(&featoutTmpStr, valstr, (ajlong) i, len-1); iftid = ajStrFindC(featoutTmpStr, " /FTId="); /* isftid = ajFalse; */ /*ajDebug("try %d to %d (len %d) iftid:%d\n", i, last, len, iftid);*/ if(iftid >= 0 && iftid+(ajint)i <= (ajint)last) { if(iftid) { /* isftid = ajTrue; */ ajStrAssignSubS(&featoutTmpStr, valstr, (ajlong) i, (ajlong) i+iftid-1); j = iftid; } else { ajStrAssignSubS(&featoutTmpStr, valstr, (ajlong) i, (ajlong) i+iftid-2); j = 0; } /*ajDebug("' /FTId=' found iftid:%d isftid:%B, j:%d\n", iftid, isftid, j);*/ } else if((last+1) >= len) /* no need to split */ { ajStrAssignSubS(&featoutTmpStr, valstr, (ajlong) i, len-1); /*ajDebug("last %d >= len %d\n", last, len);*/ j = 0; } /* split at max width */ else if(ajStrGetCharPos(valstr, (ajlong) (last+1)) == ' ') { ajStrAssignSubS(&featoutTmpStr, valstr, (ajlong) i, (ajlong) last); j = width; /*ajDebug("split at max width last+1 %d\n", last+1);*/ } else { /* save max string */ ajStrAssignSubS(&featoutTmpStr, valstr, (ajlong) i, (ajlong) last); /* last space in featoutTmpStr */ j = ajStrFindlastC(featoutTmpStr, " "); /*ajDebug("space at %d\n", j);*/ } if(j < 1) /* no space found */ { j = ajStrGetLen(featoutTmpStr); k = j; } else /* print up to last space */ { k = j + 1; /* start after the space */ } /*ajDebug("%Ld +%Ld (%Ld) '%.*S'\n", i, j, k, (ajuint)j, featoutTmpStr);*/ if(i || isftidstart) ajFmtPrintAppS(retstr, "%s%.*S\n", prefix,(ajuint)j, featoutTmpStr); else ajFmtPrintAppS(retstr, "%.*S\n", (ajuint)j, featoutTmpStr); } ajStrDel(&valstr); return; } ID featDumpEmbl TY static MO ajfeatwrite LB core XX DE Write details of single feature to file in EMBL/GenBank/DDBJ format XX PN [1] PA r feat const AjPFeature PD Feature PX PN [2] PA r location const AjPStr PD location list PX PN [3] PA u file AjPFile PD Output file PX PN [4] PA r Seqid const AjPStr PD Sequence ID PX PN [5] PA r IsEmbl AjBool PD ajTrue if writing EMBL format (FT prefix) PX RT void RD RX // static void featDumpEmbl(const AjPFeature feat, const AjPStr location, AjPFile file, const AjPStr Seqid, AjBool IsEmbl) { AjIList iter = NULL; ajint i = 0; AjPTagval tv = NULL; const AjPStr tmptyp = NULL; /* these come from AjPTable */ const AjPStr tmptag = NULL; /* so please, please */ /* don't delete them */ AjPStr tmplim = NULL; AjBool knowntag = ajTrue; const char* cp; AjPStr wrapstr = NULL; AjPStr preftyploc = NULL; AjPStr preftyptag = NULL; AjPStr tmploc = NULL; const AjPTable tagstable; const AjPTable typetable; AjPOboin oboin = NULL; AjPObo obo = NULL; ajDebug("featDumpEmbl '%S'\n", location); /* print the location */ tagstable = ajFeatVocabGetTagsNuc("embl"); typetable = ajFeatVocabGetTypesNuc("embl"); ajStrAssignS(&tmploc, location); tmptyp = ajFeattypeGetExternal(feat->Type, typetable); if(IsEmbl) { ajFmtPrintS(&preftyploc, "%s %-15.15S ", "FT", tmptyp); ajFmtPrintS(&preftyptag, "%s ", "FT"); } else { ajFmtPrintS(&preftyploc, "%s %-15.15S ", " ", tmptyp); ajFmtPrintS(&preftyptag, "%s ", " "); } if(IsEmbl) featLocEmblWrapC(&tmploc, 80, ajStrGetPtr(preftyptag), ajStrGetPtr(preftyploc), &wrapstr); else featLocEmblWrapC(&tmploc, 79, ajStrGetPtr(preftyptag), ajStrGetPtr(preftyploc), &wrapstr); /*ajDebug("tmploc: '%S' wrapstr; '%S'\n", tmploc, wrapstr);*/ ajFmtPrintF(file, "%S", wrapstr); ajStrDel(&wrapstr); /* print the qualifiers */ if(!ajTablestrFetchkeyS(typetable, feat->Type)) { ajUser("unknown type '%S'", feat->Type); oboin = ajOboinNew(); obo = ajOboNew(); ajOboinQryS(oboin, feat->Type); while(ajOboinRead(oboin, obo)) { if(ajOboIsObsolete(obo)) continue; ajFmtPrintS(&featoutStr, "/note=\"*Type %S %S\"\n", feat->Type, ajOboGetName(obo)); if(IsEmbl) featTagEmblWrapC(&featoutStr, 80, ajStrGetPtr(preftyptag), &wrapstr); else featTagEmblWrapC(&featoutStr, 79, ajStrGetPtr(preftyptag), &wrapstr); ajFmtPrintF(file, "%S", wrapstr); ajStrDel(&wrapstr); break; } ajOboDel(&obo); ajOboinDel(&oboin); } iter = ajListIterNewread(feat->GffTags); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); if(ajStrMatchC(MAJTAGVALGETTAG(tv), "Dbxref") || ajStrMatchC(MAJTAGVALGETTAG(tv), "Ontology_term")) { ++i; tmptag = ajFeattagGetNameC("db_xref", tagstable, &knowntag); ajFeattagFormat(tmptag, tagstable, &featoutFmtTmp); ajFmtPrintS(&featoutStr, "/%S", tmptag); ajStrAssignS(&featoutValTmp, MAJTAGVALGETVALUE(tv)); featTagQuoteEmbl(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); } else if(ajStrMatchC(MAJTAGVALGETTAG(tv), "ID") || ajStrMatchC(MAJTAGVALGETTAG(tv), "Parent")) { continue; } else { ajFmtPrintS(&featoutStr, "/note=\"*%S %S\"\n", MAJTAGVALGETTAG(tv), MAJTAGVALGETVALUE(tv)); } if(IsEmbl) featTagEmblWrapC(&featoutStr, 80, ajStrGetPtr(preftyptag), &wrapstr); else featTagEmblWrapC(&featoutStr, 79, ajStrGetPtr(preftyptag), &wrapstr); ajFmtPrintF(file, "%S", wrapstr); ajStrDel(&wrapstr); } ajListIterDel(&iter); iter = ajListIterNewread(feat->Tags); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); ++i; tmptag = ajFeattagGetNameS(MAJTAGVALGETTAG(tv), tagstable, &knowntag); ajFeattagFormat(tmptag, tagstable, &featoutFmtTmp); ajFmtPrintS(&featoutStr, "/%S", tmptag); if(MAJTAGVALGETVALUE(tv)) { ajStrAssignS(&featoutValTmp, MAJTAGVALGETVALUE(tv)); cp = ajStrGetPtr(featoutFmtTmp); switch(CASE2(cp[0], cp[1])) { case CASE2('L','I') : /* limited */ /* ajDebug("case limited\n"); */ ajFeattagGetLimit(tmptag, tagstable, &tmplim); featTagAllLimit(&featoutValTmp, tmplim); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); ajStrDel(&tmplim); break; case CASE2('Q', 'L') : /* limited, escape quotes */ /* ajDebug("case qlimited\n"); */ ajFeattagGetLimit(tmptag, tagstable, &tmplim); featTagAllLimit(&featoutValTmp, tmplim); featTagQuoteEmbl(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); ajStrDel(&tmplim); break; case CASE2('Q', 'S') : /* special regexp, quoted */ /* ajDebug("case qspecial\n"); */ if(!ajFeattagSpecial(tmptag, &featoutValTmp)) { ajFeatWarn("%S: Bad special tag value", Seqid); featTagEmblDefault(&featoutStr, tmptag, &featoutValTmp); } else { featTagQuoteEmbl(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); } break; case CASE2('S','P') : /* special regexp */ /* ajDebug("case special\n"); */ if(!ajFeattagSpecial(tmptag, &featoutValTmp)) { ajFeatWarn("%S: Bad special tag value", Seqid); featTagEmblDefault(&featoutStr, tmptag, &featoutValTmp); } else ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; case CASE2('T','E') : /* no space, no quotes, wrap at margin */ /* ajDebug("case text\n"); */ ajStrRemoveWhite(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; case CASE2('V','O') : /* no value, so an error here */ /*ajDebug("case void\n");*/ break; case CASE2('Q','T') : /* escape quotes, wrap at space */ /* ajDebug("case qtext\n"); */ featTagQuoteEmbl(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; case CASE2('Q','W') : /* escape quotes, remove space */ /* ajDebug("case qword\n"); */ featTagQuoteEmbl(&featoutValTmp); ajStrRemoveWhite(&featoutValTmp); /* no white space needed */ ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; default: ajFeatWarn("Unknown EMBL feature tag type '%S' for '%S'", featoutFmtTmp, tmptag); } } else { /*ajDebug("no value, hope it is void: '%S'\n", featoutFmtTmp);*/ } if(IsEmbl) featTagEmblWrapC(&featoutStr, 80, ajStrGetPtr(preftyptag), &wrapstr); else featTagEmblWrapC(&featoutStr, 79, ajStrGetPtr(preftyptag), &wrapstr); ajFmtPrintF(file, "%S", wrapstr); ajStrDel(&wrapstr); } /* ajDebug("featDumpEmbl Done %d tags\n", i); */ ajListIterDel(&iter); ajStrDel(&preftyploc); ajStrDel(&preftyptag); ajStrDel(&tmploc); ajStrDel(&tmplim); return; } ID featDumpRefseq TY static MO ajfeatwrite LB core XX DE Write details of single feature to file in Refseq format XX PN [1] PA r feat const AjPFeature PD Feature PX PN [2] PA r location const AjPStr PD location list PX PN [3] PA u file AjPFile PD Output file PX PN [4] PA r Seqid const AjPStr PD Sequence ID PX RT void RD RX // static void featDumpRefseq(const AjPFeature feat, const AjPStr location, AjPFile file, const AjPStr Seqid) { AjIList iter = NULL; ajint i = 0; AjPTagval tv = NULL; const AjPStr tmptyp = NULL; /* these come from AjPTable */ const AjPStr tmptag = NULL; /* so please, please */ /* don't delete them */ AjPStr tmplim = NULL; AjBool knowntag = ajTrue; const char* cp; AjPStr wrapstr = NULL; AjPStr preftyploc = NULL; AjPStr preftyptag = NULL; AjPStr tmploc = NULL; const AjPTable tagstable; const AjPTable typetable; /* ajDebug("featDumpRefseq Start\n"); */ /* print the location */ tagstable = ajFeatVocabGetTagsNuc("embl"); typetable = ajFeatVocabGetTypesNuc("embl"); ajStrAssignS(&tmploc, location); tmptyp = ajFeattypeGetExternal(feat->Type, typetable); ajFmtPrintS(&preftyploc, "%s %-15.15S ", " ", tmptyp); ajFmtPrintS(&preftyptag, "%s ", " "); featLocEmblWrapC(&tmploc, 79, ajStrGetPtr(preftyptag), ajStrGetPtr(preftyploc), &wrapstr); ajFmtPrintF(file, "%S", wrapstr); ajStrDel(&wrapstr); /* print the qualifiers */ iter = ajListIterNewread(feat->Tags); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); ++i; tmptag = ajFeattagGetNameS(MAJTAGVALGETTAG(tv), tagstable, &knowntag); ajFeattagFormat(tmptag, tagstable, &featoutFmtTmp); ajFmtPrintS(&featoutStr, "/%S", tmptag); if(MAJTAGVALGETVALUE(tv)) { ajStrAssignS(&featoutValTmp, MAJTAGVALGETVALUE(tv)); cp = ajStrGetPtr(featoutFmtTmp); switch(CASE2(cp[0], cp[1])) { case CASE2('L','I') : /* limited */ /* ajDebug("case limited\n"); */ ajFeattagGetLimit(tmptag, tagstable, &tmplim); featTagAllLimit(&featoutValTmp, tmplim); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); ajStrDel(&tmplim); break; case CASE2('Q', 'L') : /* limited, escape quotes */ /* ajDebug("case qlimited\n"); */ ajFeattagGetLimit(tmptag, tagstable, &tmplim); featTagAllLimit(&featoutValTmp, tmplim); featTagQuoteEmbl(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); ajStrDel(&tmplim); break; case CASE2('Q', 'S') : /* special regexp, quoted */ /* ajDebug("case qspecial\n"); */ if(!ajFeattagSpecial(tmptag, &featoutValTmp)) { ajFeatWarn("%S: Bad special tag value", Seqid); featTagEmblDefault(&featoutStr, tmptag, &featoutValTmp); } else { featTagQuoteEmbl(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); } break; case CASE2('S','P') : /* special regexp */ /* ajDebug("case special\n"); */ if(!ajFeattagSpecial(tmptag, &featoutValTmp)) { ajFeatWarn("%S: Bad special tag value", Seqid); featTagEmblDefault(&featoutStr, tmptag, &featoutValTmp); } else ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; case CASE2('T','E') : /* no space, no quotes, wrap at margin */ /* ajDebug("case text\n"); */ ajStrRemoveWhite(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; case CASE2('V','O') : /* no value, so an error here */ /*ajDebug("case void\n");*/ break; case CASE2('Q','T') : /* escape quotes, wrap at space */ /* ajDebug("case qtext\n"); */ featTagQuoteEmbl(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; case CASE2('Q','W') : /* escape quotes, remove space */ /* ajDebug("case qword\n"); */ featTagQuoteEmbl(&featoutValTmp); ajStrRemoveWhite(&featoutValTmp); /* no white space needed */ ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; default: ajFeatWarn("Unknown EMBL feature tag type '%S' for '%S'", featoutFmtTmp, tmptag); } } else { /*ajDebug("no value, hope it is void: '%S'\n", featoutFmtTmp);*/ } featTagEmblWrapC(&featoutStr, 79, ajStrGetPtr(preftyptag), &wrapstr); ajFmtPrintF(file, "%S", wrapstr); ajStrDel(&wrapstr); } /* ajDebug("featDumpRefseq Done %d tags\n", i); */ ajListIterDel(&iter); ajStrDel(&preftyploc); ajStrDel(&preftyptag); ajStrDel(&tmploc); ajStrDel(&tmplim); return; } ID featDumpRefseqp TY static MO ajfeatwrite LB core XX DE Write details of single feature to file in RefSeq protein format XX PN [1] PA r feat const AjPFeature PD Feature PX PN [2] PA r location const AjPStr PD location list PX PN [3] PA u file AjPFile PD Output file PX PN [4] PA r Seqid const AjPStr PD Sequence ID PX RT void RD RX // static void featDumpRefseqp(const AjPFeature feat, const AjPStr location, AjPFile file, const AjPStr Seqid) { AjIList iter = NULL; ajint i = 0; AjPTagval tv = NULL; const AjPStr tmptyp = NULL; /* these come from AjPTable */ const AjPStr tmptag = NULL; /* so please, please */ /* don't delete them */ AjPStr tmplim = NULL; AjBool knowntag = ajTrue; const char* cp; AjPStr wrapstr = NULL; AjPStr preftyploc = NULL; AjPStr preftyptag = NULL; AjPStr tmploc = NULL; const AjPTable tagstable; const AjPTable typetable; /* ajDebug("featDumpRefseqp Start\n"); */ /* print the location */ tagstable = ajFeatVocabGetTagsProt("refseqp"); typetable = ajFeatVocabGetTypesProt("refseqp"); ajStrAssignS(&tmploc, location); tmptyp = ajFeattypeGetExternal(feat->Type, typetable); ajFmtPrintS(&preftyploc, "%s %-15.15S ", " ", tmptyp); ajFmtPrintS(&preftyptag, "%s ", " "); featLocEmblWrapC(&tmploc, 79, ajStrGetPtr(preftyptag), ajStrGetPtr(preftyploc), &wrapstr); ajFmtPrintF(file, "%S", wrapstr); ajStrDel(&wrapstr); /* print the qualifiers */ iter = ajListIterNewread(feat->Tags); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); ++i; tmptag = ajFeattagGetNameS(MAJTAGVALGETTAG(tv), tagstable, &knowntag); ajFeattagFormat(tmptag, tagstable, &featoutFmtTmp); ajFmtPrintS(&featoutStr, "/%S", tmptag); if(MAJTAGVALGETVALUE(tv)) { ajStrAssignS(&featoutValTmp, MAJTAGVALGETVALUE(tv)); cp = ajStrGetPtr(featoutFmtTmp); switch(CASE2(cp[0], cp[1])) { case CASE2('L','I') : /* limited */ /* ajDebug("case limited\n"); */ ajFeattagGetLimit(tmptag, tagstable, &tmplim); featTagAllLimit(&featoutValTmp, tmplim); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); ajStrDel(&tmplim); break; case CASE2('Q', 'L') : /* limited, escape quotes */ /* ajDebug("case qlimited\n"); */ ajFeattagGetLimit(tmptag, tagstable, &tmplim); featTagAllLimit(&featoutValTmp, tmplim); featTagQuoteEmbl(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); ajStrDel(&tmplim); break; case CASE2('Q', 'S') : /* special regexp, quoted */ /* ajDebug("case qspecial\n"); */ if(!ajFeattagSpecial(tmptag, &featoutValTmp)) { ajFeatWarn("%S: Bad special tag value", Seqid); featTagEmblDefault(&featoutStr, tmptag, &featoutValTmp); } else { featTagQuoteEmbl(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); } break; case CASE2('S','P') : /* special regexp */ /* ajDebug("case special\n"); */ if(!ajFeattagSpecial(tmptag, &featoutValTmp)) { ajFeatWarn("%S: Bad special tag value", Seqid); featTagEmblDefault(&featoutStr, tmptag, &featoutValTmp); } else ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; case CASE2('T','E') : /* no space, no quotes, wrap at margin */ /* ajDebug("case text\n"); */ ajStrRemoveWhite(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; case CASE2('V','O') : /* no value, so an error here */ /*ajDebug("case void\n");*/ break; case CASE2('Q','T') : /* escape quotes, wrap at space */ /* ajDebug("case qtext\n"); */ featTagQuoteEmbl(&featoutValTmp); ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; case CASE2('Q','W') : /* escape quotes, remove space */ /* ajDebug("case qword\n"); */ featTagQuoteEmbl(&featoutValTmp); ajStrRemoveWhite(&featoutValTmp); /* no white space needed */ ajFmtPrintAppS(&featoutStr, "=%S\n", featoutValTmp); break; default: ajFeatWarn("Unknown EMBL feature tag type '%S' for '%S'", featoutFmtTmp, tmptag); } } else { /*ajDebug("no value, hope it is void: '%S'\n", featoutFmtTmp);*/ } featTagEmblWrapC(&featoutStr, 79, ajStrGetPtr(preftyptag), &wrapstr); ajFmtPrintF(file, "%S", wrapstr); ajStrDel(&wrapstr); } /* ajDebug("featDumpRefseqp Done %d tags\n", i); */ ajListIterDel(&iter); ajStrDel(&preftyploc); ajStrDel(&preftyptag); ajStrDel(&tmploc); ajStrDel(&tmplim); return; } ID featDumpPir TY static MO ajfeatwrite LB core XX DE Write details of single feature to output file XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA r location const AjPStr PD Location as a string PX PN [3] PA u file AjPFile PD Output file PX RT void RD RX // static void featDumpPir(const AjPFeature thys, const AjPStr location, AjPFile file) { AjIList iter = NULL; AjPStr outtyp = NULL; /* these come from AjPTable */ const AjPStr outtag = NULL; /* so please, please */ /* don't delete them */ AjPStr outcomm = NULL; AjPStr outfmt = NULL; AjPTagval tv = NULL; AjBool knowntag = ajTrue; const char* cp; AjBool typmod; AjPFeature copy = NULL; const AjPTable tagstable; tagstable = ajFeatVocabGetTagsProt("pir"); copy = ajFeatNewFeat(thys); ajStrAssignClear(&outcomm); ajStrAssignS(&outtyp, copy->Type); typmod = featTypePirOut(&outtyp); /* try to pick the best type if any */ /* if changed, we append the original internal type */ if(typmod) { ajDebug("typmod %B append ', %S'\n", typmod, ajFeatTypeProt(copy->Type)); ajFeatSetDescApp(copy, ajFeatTypeProt(copy->Type)); } /* make sure it's PIR */ ajStrFmtTitle(&outtyp); ajStrExchangeCC(&outtyp, "_", " "); ajFmtPrintF(file, "F;%S/%S:", location, outtyp); /* For all tag-values... */ iter = ajFeatTagIter(copy); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); outtag = ajFeattagGetNameS(MAJTAGVALGETTAG(tv), tagstable, &knowntag); ajFeattagFormat(outtag, tagstable, &outfmt); if(MAJTAGVALGETVALUE(tv)) { ajStrAssignS(&featoutValTmp, MAJTAGVALGETVALUE(tv)); if(ajStrMatchCaseC(outtag, "comment")) { ajFmtPrintAppS(&outcomm, " #%S", featoutValTmp); continue; } cp = ajStrGetPtr(outfmt); switch(CASE2(cp[0], cp[1])) { default: ajFmtPrintAppS(&featoutStr, " %S", featoutValTmp); } } else { /*ajDebug("no value, hope it is void: '%S'\n", outfmt);*/ } ajFmtPrintF(file, "%S", featoutStr); ajStrDelStatic(&featoutStr); } ajListIterDel(&iter); ajFeatDel(©); ajFmtPrintF(file, "%S\n", outcomm); ajStrDel(&outcomm); ajStrDel(&outfmt); ajStrDel(&outtyp); return; } ID featDumpSwiss TY static MO ajfeatwrite LB core XX DE Write details of single feature to output file XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA u file AjPFile PD Output file PX PN [3] PA r gftop const AjPFeature PD Parent feature PX RT void RD RX // static void featDumpSwiss(const AjPFeature thys, AjPFile file, const AjPFeature gftop) { AjIList iter = NULL; const AjPStr outtyp = NULL; /* these come from AjPTable */ const AjPStr outtag = NULL; /* so please, please */ /* don't delete them */ AjPStr outfmt = NULL; AjPStr tmplim = NULL; AjPTagval tv = NULL; ajint i =0; AjBool knowntag = ajTrue; const char* cp; AjPStr wrapstr = NULL; AjPStr fromstr = NULL; AjPStr tostr = NULL; AjBool wasnote = ajFalse; const AjPTable tagstable; const AjPTable typetable; tagstable = ajFeatVocabGetTagsProt("swiss"); typetable = ajFeatVocabGetTypesProt("swiss"); outtyp = ajFeattypeGetExternal(thys->Type, typetable); if(thys->Flags & AJFEATFLAG_START_UNSURE) { if(thys->Start) ajFmtPrintS(&fromstr, "?%d", thys->Start); else ajFmtPrintS(&fromstr, "?"); } else if(thys->Flags & AJFEATFLAG_START_BEFORE_SEQ) ajFmtPrintS(&fromstr, "<%d", thys->Start); else ajFmtPrintS(&fromstr, "%d", thys->Start); if(thys->Flags & AJFEATFLAG_END_UNSURE) { if(thys->End) ajFmtPrintS(&tostr, "?%d", thys->End); else ajFmtPrintS(&tostr, "?"); } else if(thys->Flags & AJFEATFLAG_END_AFTER_SEQ) ajFmtPrintS(&tostr, ">%d", thys->End); else ajFmtPrintS(&tostr, "%d", thys->End); ajFmtPrintS(&featoutStr, "FT %-8.8S %6.6S %6.6S", outtyp, fromstr, tostr); /* For all tag-values... from gftop which could be the same as thys */ iter = ajFeatTagIter(gftop); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); outtag = ajFeattagGetNameS(MAJTAGVALGETTAG(tv), tagstable, &knowntag); ajFeattagFormat(outtag, tagstable, &outfmt); ajDebug("Tag '%S' => '%S' %S '%S'\n", ajTagvalGetTag(tv), outtag, outfmt, ajTagvalGetValue(tv)); if(i++) ajFmtPrintAppS(&featoutStr, " ") ; else ajFmtPrintAppS(&featoutStr, " ") ; /* ajFmtPrintAppS(&featoutStr, "%S", outtag); */ /* tag type is silent */ if(MAJTAGVALGETVALUE(tv)) { ajStrAssignS(&featoutValTmp, MAJTAGVALGETVALUE(tv)); cp = ajStrGetPtr(outfmt); switch(CASE2(cp[0], cp[1])) { case CASE2('L','I') : /* limited */ case CASE2('Q', 'L') : /* limited, escape quotes */ /*ajDebug("case limited\n");*/ ajFeattagGetLimit(outtag, tagstable, &tmplim); featTagAllLimit(&featoutValTmp, tmplim); ajFmtPrintAppS(&featoutStr, "%S.", featoutValTmp); ajStrDel(&tmplim); wasnote = ajFalse; break; case CASE2('T','A') : /* tag=text */ /*ajDebug("case tagval\n");*/ if(ajStrMatchCaseC(outtag, "ftid")) /* fix case for tag */ ajFmtPrintAppS(&featoutStr, "/FTId=%S.",featoutValTmp); else /* lower case is fine */ ajFmtPrintAppS(&featoutStr, "/%S=%S",outtag, featoutValTmp); wasnote = ajFalse; break; case CASE2('T','E') : /* simple text, wrap at space */ /*ajDebug("case text\n");*/ ajFmtPrintAppS(&featoutStr, "%S.", featoutValTmp); wasnote = ajTrue; break; case CASE2('B','T') : /* bracketed, wrap at space */ /*ajDebug("case btext\n");*/ if(wasnote) { ajStrCutEnd(&featoutStr, 2); /* remove ". " */ ajStrAppendK(&featoutStr, ' '); /* replace the space */ } ajFmtPrintAppS(&featoutStr, "(%S).", featoutValTmp); wasnote = ajFalse; break; default: ajFeatWarn("Unknown SWISS feature tag type '%S' for '%S'", outfmt, outtag); wasnote = ajFalse; } } else { /*ajDebug("no value, hope it is void: '%S'\n", outfmt);*/ } } ajListIterDel(&iter); featTagSwissWrapC(&featoutStr, 75, "FT ", &wrapstr); ajFmtPrintF(file, "%S", wrapstr); ajStrDelStatic(&featoutStr); ajStrDel(&wrapstr); ajStrDel(&tostr); ajStrDel(&fromstr); ajStrDel(&outfmt); ajStrDel(&tmplim); return; } ID featDumpBed TY static MO ajfeatwrite LB core XX DE Write details of single feature to BED output file XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA r owner const AjPFeattable PD Feature table PD (used for the sequence name) PX PN [3] PA r minscore float PD Minimum score for whole table PX PN [4] PA r range float PD Score range for whole table PX PN [5] PA u file AjPFile PD Output file PX RT void RD RX // static void featDumpBed(const AjPFeature thys, const AjPFeattable owner, float minscore, float range, AjPFile file) { AjIList iter = NULL; const AjPStr outtyp = NULL; /* comes from AjPTable */ const AjPTable typetable = NULL; ajint iscore = 0; ajuint istart; ajuint iend; ajulong isub = ajListGetLength(thys->Subfeatures); ajuint nsub = 0; ajuint irgb = 0; ajint ilen; ajint ioff; AjPStr offsets = NULL; AjPStr lengths = NULL; AjPFeature gf = NULL; const char* comma = ","; const char* punc = ""; if(thys->Protein) { typetable = ajFeatVocabGetTypesProt("gff2protein"); } else { typetable = ajFeatVocabGetTypesNuc("gff2"); } if(range > 0.0) iscore = (ajint) (1000.0 * (thys->Score - minscore)/range); /* header done by calling routine */ /*ajDebug("featDumpGff...\n");*/ /* simple line-by line with Gff tags */ outtyp = ajFeattypeGetExternal(thys->Type, typetable); /*ajDebug("Type '%S' => '%S'\n", thys->Type, outtyp);*/ if(ajFeatGetStrand(thys) == '-' && thys->End < thys->Start) { iend = thys->Start - 1; istart = thys->End - 1; } else { istart = thys->Start - 1; iend = thys->End - 1; } ajFmtPrintF(file, "%S\t%d\t%d\t%S\t%d\t%c\t%d\t%d\t%d", owner->Seqid, istart, iend, outtyp, iscore, ajFeatGetStrand(thys), istart, iend, irgb); if(isub) { iter = ajListIterNewread(thys->Subfeatures); while(!ajListIterDone(iter)) { gf = ajListIterGet(iter); if(ajStrGetLen(gf->Remote)) continue; nsub++; ilen = gf->End - gf->Start + 1; ioff = gf->Start - istart - 1; ajFmtPrintAppS(&lengths, "%s%d", punc, ilen); ajFmtPrintAppS(&offsets, "%s%d", punc, ioff); punc = comma; } ajListIterDel(&iter); ajFmtPrintF(file, "\t%u\t%S\t%S\n", nsub, lengths, offsets) ; } else { nsub++; ajFmtPrintF(file, "\t%u\t%d\t%d\n", nsub, 0, (thys->End - thys->Start + 1)) ; } ajListIterDel(&iter); ajStrDel(&offsets); ajStrDel(&lengths); return; } ID featDumpGff2 TY static MO ajfeatwrite LB core XX DE Write details of single feature to GFF 2.0 output file XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA r owner const AjPFeattable PD Feature table PD (used for the sequence name) PX PN [3] PA u file AjPFile PD Output file PX RT void RD RX // static void featDumpGff2(const AjPFeature thys, const AjPFeattable owner, AjPFile file) { AjIList iter = NULL; const AjPStr outtyp = NULL; /* these come from AjPTable */ const AjPStr outtag = NULL; /* so please, please */ /* don't delete them */ AjPTagval tv = NULL; ajint i = 0; AjBool knowntag = ajTrue; const char* cp; AjPStr flagdata = NULL; const AjPTable tagstable = NULL; const AjPTable typetable = NULL; if(thys->Protein) { tagstable = ajFeatVocabGetTagsProt("gff2protein"); typetable = ajFeatVocabGetTypesProt("gff2protein"); } else { tagstable = ajFeatVocabGetTagsNuc("gff2"); typetable = ajFeatVocabGetTypesNuc("gff2"); } /* header done by calling routine */ /*ajDebug("featDumpGff...\n");*/ /* simple line-by line with Gff tags */ outtyp = ajFeattypeGetExternal(thys->Type, typetable); /*ajDebug("Type '%S' => '%S'\n", thys->Type, outtyp);*/ if(thys->Score) { if(ajFeatGetStrand(thys) == '-' && thys->End < thys->Start) ajFmtPrintF(file, "%S\t%S\t%S\t%d\t%d\t%.4g\t%c\t%c\t", owner->Seqid, thys->Source, outtyp, thys->End, thys->Start, thys->Score, ajFeatGetStrand(thys), ajFeatframeGetFrame(thys->Frame)); else ajFmtPrintF(file, "%S\t%S\t%S\t%d\t%d\t%.4g\t%c\t%c\t", owner->Seqid, thys->Source, outtyp, thys->Start, thys->End, thys->Score, ajFeatGetStrand(thys), ajFeatframeGetFrame(thys->Frame)); } else { if(ajFeatGetStrand(thys) == '-' && thys->End < thys->Start) ajFmtPrintF(file, "%S\t%S\t%S\t%d\t%d\t.\t%c\t%c\t", owner->Seqid, thys->Source, outtyp, thys->End, thys->Start, ajFeatGetStrand(thys), ajFeatframeGetFrame(thys->Frame)); else ajFmtPrintF(file, "%S\t%S\t%S\t%d\t%d\t.\t%c\t%c\t", owner->Seqid, thys->Source, outtyp, thys->Start, thys->End, thys->Score, ajFeatGetStrand(thys), ajFeatframeGetFrame(thys->Frame)); } if(thys->Flags) ajFmtPrintS(&flagdata, "0x%x", thys->Flags); if(thys->Start2) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, " "); ajFmtPrintAppS(&flagdata, "start2:%d", thys->Start2); } if(thys->End2) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, " "); ajFmtPrintAppS(&flagdata, "end2:%d", thys->End2); } if(ajStrGetLen(thys->Remote)) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, " "); ajFmtPrintAppS(&flagdata, "remoteid:%S", thys->Remote); } if(ajStrGetLen(thys->Label)) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, " "); ajFmtPrintAppS(&flagdata, "label:%S", thys->Label); } /* group and flags */ ajFmtPrintF(file, "Sequence \"%S.%d\"", owner->Seqid, thys->Group) ; i++; if(ajStrGetLen(flagdata)) { /* ** Move this code up to run for all features - to preserve the order ** when rewriting in EMBL format if(AJFEATFLAG_MULTIPLE) { (void) ajFmtPrintF(file, "Sequence \"%S.%d\" ; ", owner->Seqid, thys->Group) ; i++; } */ if(i++) ajFmtPrintF(file, " ; ") ; ajFmtPrintF(file, "FeatFlags \"%S\"", flagdata) ; } /* For all tag-values... */ iter = ajFeatTagIter(thys); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); outtag = ajFeattagGetNameS(MAJTAGVALGETTAG(tv), tagstable, &knowntag); ajFeattagFormat(outtag, tagstable, &featoutFmtTmp); if(i++) ajFmtPrintF(file, " ; ") ; ajFmtPrintAppS(&featoutStr, "%S", outtag); if(MAJTAGVALGETVALUE(tv)) { ajStrAssignS(&featoutValTmp, MAJTAGVALGETVALUE(tv)); cp = ajStrGetPtr(featoutFmtTmp); switch(CASE2(cp[0], cp[1])) { case CASE2('L','I') : /* limited */ /*ajDebug("case limited\n");*/ ajFeattagGetLimit(outtag, tagstable, &featoutLimTmp); featTagAllLimit(&featoutValTmp, featoutLimTmp); ajFmtPrintAppS(&featoutStr, " %S", featoutValTmp); break; case CASE2('Q', 'L') : /* limited, escape quotes */ /*ajDebug("case qlimited\n");*/ ajFeattagGetLimit(outtag, tagstable, &featoutLimTmp); featTagAllLimit(&featoutValTmp, featoutLimTmp); featTagQuoteGff2(&featoutValTmp); ajFmtPrintAppS(&featoutStr, " %S", featoutValTmp); break; case CASE2('T','E') : /* no space, no quotes, wrap at margin */ /*ajDebug("case text\n");*/ ajStrRemoveWhite(&featoutValTmp); ajFmtPrintAppS(&featoutStr, " %S", featoutValTmp); break; case CASE2('Q','T') : /* escape quotes, wrap at space */ /*ajDebug("case qtext\n");*/ featTagQuoteGff2(&featoutValTmp); ajFmtPrintAppS(&featoutStr, " %S", featoutValTmp); break; case CASE2('Q','W') : /* escape quotes, remove space */ /*ajDebug("case qtext\n");*/ featTagQuoteGff2(&featoutValTmp); ajStrRemoveWhite(&featoutValTmp); ajFmtPrintAppS(&featoutStr, " %S", featoutValTmp); break; case CASE2('Q', 'S') : /* special regexp, quoted */ /*ajDebug("case qspecial\n");*/ if(!ajFeattagSpecialGff2(outtag, &featoutValTmp)) featTagGff2Default(&featoutStr, outtag, &featoutValTmp); else { featTagQuoteGff2(&featoutValTmp); ajFmtPrintAppS(&featoutStr, " %S", featoutValTmp); } break; case CASE2('S','P') : /* special regexp */ /*ajDebug("case special\n");*/ if(!ajFeattagSpecialGff2(outtag, &featoutValTmp)) featTagGff2Default(&featoutStr, outtag, &featoutValTmp); else ajFmtPrintAppS(&featoutStr, " %S", featoutValTmp); break; case CASE2('V','O') : /* no value, so an error here */ /*ajDebug("case void\n");*/ break; default: ajFeatWarn("Unknown GFF 2.0 feature tag type '%S' for '%S'", featoutFmtTmp, outtag); } } else { /*ajDebug("no value, hope it is void: '%S'\n", featoutFmtTmp);*/ } ajFmtPrintF(file, "%S", featoutStr); ajStrDelStatic(&featoutStr); } ajFmtPrintF(file, "\n") ; ajListIterDel(&iter); ajStrDel(&flagdata); return; } ID featDumpGff3Tag TY static MO ajfeatwrite LB core XX DE Write tag-value pairs of features to GFF 3.0 output file XX PN [1] PA u file AjPFile PD Output file PX PN [2] PA r tag const char* PD Tag name PX PN [3] PA r value const AjPStr PD Tag value PX PN [4] PA r firsttag AjBool PD whether this is the first tag to be printed PX RT AjBool RD always returns ajFalse RX // static AjBool featDumpGff3Tag(AjPFile file, const char* tag, const AjPStr value, AjBool firsttag) { if(strlen(tag)==0) { ajFeatWarn("empty tag name for tag value '%S'", value); tag="Note"; } if(firsttag) ajFmtPrintF(file, "%s=%S", tag, value); else ajFmtPrintF(file, ";%s=%S", tag, value); (void)firsttag; return ajFalse; } ID featDumpGff3 TY static MO ajfeatwrite LB core XX DE Write details of single feature to GFF 3.0 output file XX PN [1] PA r thys const AjPFeature PD Feature PX PN [2] PA r parent const AjPFeature PD Parent feature PX PN [3] PA r owner const AjPFeattable PD Feature table PD (used for the sequence name) PX PN [4] PA u file AjPFile PD Output file PX RT void RD RX // static void featDumpGff3(const AjPFeature thys, const AjPFeature parent, const AjPFeattable owner, AjPFile file) { AjIList iter = NULL; AjIList ftiter = NULL; AjPFeature gf = NULL; const AjPStr outtyp = NULL; const AjPStr subtyp = NULL; const AjPStr outtag = NULL; const char* tagname = NULL; AjPTagval tv = NULL; ajint i = 0; const char* cp; AjPStr flagdata = NULL; const AjPTable tagstable = NULL; const AjPTable typetable = NULL; char frame; ajuint tmpbeg = 0; ajuint tmpend = 0; const AjPStr tmpid; AjPFeatGfftags gfftags = NULL; AjBool firsttag = ajTrue; AjPStr tmptyp = NULL; const AjPStr idparent = NULL; /*ajDebug("featDumpGff3 prot: %B type '%S'\n", thys->Protein, thys->Type);*/ if((thys->Flags & AJFEATFLAG_GENERATED) && ajListGetLength(thys->Subfeatures)) { ajDebug("featDumpGff3 generated - subfeatures only\n", thys->Protein, thys->Type); ftiter = ajListIterNewread(thys->Subfeatures); while(!ajListIterDone(ftiter)) { gf = ajListIterGet(ftiter); featDumpGff3(gf, thys, owner, file); } ajListIterDel(&ftiter); return; } if(thys->Protein) { tagstable = ajFeatVocabGetTagsProt("gff3protein"); typetable = ajFeatVocabGetTypesProt("gff3protein"); } else { tagstable = ajFeatVocabGetTagsNuc("gff3"); typetable = ajFeatVocabGetTypesNuc("gff3"); } /* header done by calling routine */ /*ajDebug("featDumpGff3...\n");*/ /* simple line-by line with Gff3 tags */ outtyp = ajFeattypeGetExternal(thys->Type, typetable); /*ajDebug("Type '%S' => '%S'\n", thys->Type, outtyp);*/ if(ajFeatTypeIsCds(thys)) frame = ajFeatframeGetFrameNuc(thys->Frame); else frame = ajFeatframeGetFrame(thys->Frame); if(ajListGetLength(thys->Subfeatures)) { subtyp = ajFeattypeGetExternal(ajFeatGetSubtype(thys), typetable); if(ajStrMatchS(outtyp, subtyp)) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, ","); ajFmtPrintAppS(&flagdata, "type:%S", outtyp); tmptyp = ajStrNewC("biological_region"); outtyp = ajFeattypeGetExternal(tmptyp, typetable); ajStrDel(&tmptyp); } } if(thys->Flags & AJFEATFLAG_START_BEFORE_SEQ) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, ","); ajFmtPrintAppS(&flagdata, "start_before:true"); } if(thys->Flags & AJFEATFLAG_END_AFTER_SEQ) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, ","); ajFmtPrintAppS(&flagdata, "end_after:true"); } if(thys->Flags & AJFEATFLAG_BETWEEN_SEQ) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, ","); ajFmtPrintAppS(&flagdata, "between:true"); } if(thys->Start2) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, ","); ajFmtPrintAppS(&flagdata, "start2:%d", thys->Start2); } if(thys->End2) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, ","); ajFmtPrintAppS(&flagdata, "end2:%d", thys->End2); } /* AJFEATFLAG_POINT known from start=end */ /* AJFEATFLAG_COMPLEMENT_MAIN known from parent feature strand */ /* AJFEATFLAG_MULTIPLE known from subfeatures */ /* AJFEATFLAG_GROUP obsolete, known from subfeatures */ /* AJFEATFLAG_ORDER obsolete, known from subfeatures */ /* AJFEATFLAG_ONEOF obsolete, known from subfeatures */ /* AJFEATFLAG_REMOTEID known from non-matching seqid for a subfeature */ if(ajStrGetLen(thys->Label)) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, ","); ajFmtPrintAppS(&flagdata, "label:%S", thys->Label); } if(thys->Flags & AJFEATFLAG_START_UNSURE) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, ","); ajFmtPrintAppS(&flagdata, "start_unsure:true"); } if(thys->Flags & AJFEATFLAG_END_UNSURE) { if(ajStrGetLen(flagdata)) ajStrAppendC(&flagdata, ","); ajFmtPrintAppS(&flagdata, "end_unsure:true"); } if(ajFeatGetStrand(thys) == '-' && thys->End < thys->Start) { tmpbeg = thys->End; tmpend = thys->Start; } else { tmpbeg = thys->Start; tmpend = thys->End; } if(MAJSTRGETLEN(thys->Remote)) tmpid = thys->Remote; else tmpid = owner->Seqid; if(thys->Score) ajFmtPrintF(file, "%S\t%S\t%S\t%d\t%d\t%.4g\t%c\t%c\t", tmpid, thys->Source, outtyp, tmpbeg, tmpend, thys->Score, ajFeatGetStrand(thys), frame); else ajFmtPrintF(file, "%S\t%S\t%S\t%d\t%d\t%c\t%c\t%c\t", tmpid, thys->Source, outtyp, tmpbeg, tmpend, '.', ajFeatGetStrand(thys), frame); gfftags = ajFeatGetGfftags(thys); /* For all GFF tag-values... */ if(ajStrGetLen(gfftags->Id)) firsttag=featDumpGff3Tag(file, "ID", gfftags->Id, firsttag); else if(!parent && !ajStrGetLen(gfftags->Name)) { ajFmtPrintF(file, "%sID=%S.%d", (firsttag?"":";"), owner->Seqid, thys->Group); firsttag = ajFalse; } if(ajStrGetLen(gfftags->Parent)) firsttag=featDumpGff3Tag(file, "Parent", gfftags->Parent, firsttag); else if(parent && !(parent->Flags & AJFEATFLAG_GENERATED)) { idparent = ajFeatGetId(parent); if(idparent) ajFmtPrintF(file, "%sParent=.%d", (firsttag?"":";"), idparent); else ajFmtPrintF(file, "%sParent=%S.%d", (firsttag?"":";"), owner->Seqid, parent->Group); firsttag = ajFalse; } if(ajStrGetLen(gfftags->Name)) firsttag=featDumpGff3Tag(file, "Name", gfftags->Name, firsttag); if(ajStrGetLen(gfftags->Alias)) firsttag=featDumpGff3Tag(file, "Alias", gfftags->Alias, firsttag); if(ajStrGetLen(gfftags->Target)) firsttag=featDumpGff3Tag(file, "Target", gfftags->Target, firsttag); if(ajStrGetLen(gfftags->Gap)) firsttag=featDumpGff3Tag(file, "Gap", gfftags->Gap, firsttag); if(ajStrGetLen(gfftags->DerivesFrom)) firsttag=featDumpGff3Tag(file, "Derives_from", gfftags->DerivesFrom, firsttag); if(ajStrGetLen(gfftags->Note)) firsttag=featDumpGff3Tag(file, "Note", gfftags->Note, firsttag); if(ajStrGetLen(gfftags->Dbxref)) firsttag=featDumpGff3Tag(file, "Dbxref", gfftags->Dbxref, firsttag); if(ajStrGetLen(gfftags->OntologyTerm)) firsttag=featDumpGff3Tag(file, "Ontology_term", gfftags->OntologyTerm, firsttag); if(ajStrGetLen(gfftags->IsCircular)) firsttag=featDumpGff3Tag(file, "Is_circular", gfftags->IsCircular, firsttag); ajFeatGfftagsDel(&gfftags); i++; if(ajStrGetLen(flagdata)) firsttag=featDumpGff3Tag(file, "featflags", flagdata, firsttag); /* For all other tag-values... */ iter = ajFeatTagIter(thys); while(!ajListIterDone(iter)) { tv = ajListIterGet(iter); ajTagvalFmtLower(tv); outtag = ajTagvalGetTag(tv); if(!outtag) { ajFeatWarn("Unknown GFF3 feature tag '%S'", ajTagvalGetTag(tv)); continue; } ajFeattagFormat(outtag, tagstable, &featoutFmtTmp); tagname = ajStrGetPtr(outtag); if(MAJTAGVALGETVALUE(tv)) { ajStrAssignS(&featoutValTmp, MAJTAGVALGETVALUE(tv)); cp = ajStrGetPtr(featoutFmtTmp); if(strlen(cp)>0) switch(CASE2(cp[0], cp[1])) { case CASE2('L','I') : /* limited */ /*ajDebug("case limited\n");*/ ajFeattagGetLimit(outtag, tagstable, &featoutLimTmp); featTagAllLimit(&featoutValTmp, featoutLimTmp); break; case CASE2('Q', 'L') : /* limited, escape quotes */ /*ajDebug("case qlimited\n");*/ ajFeattagGetLimit(outtag, tagstable, &featoutLimTmp); featTagAllLimit(&featoutValTmp, featoutLimTmp); break; case CASE2('T','E') : /* no space, no quotes, wrap at margin */ /*ajDebug("case text\n");*/ ajStrRemoveWhite(&featoutValTmp); break; case CASE2('Q','T') : /* escape quotes, wrap at space */ /*ajDebug("case qtext\n");*/ break; case CASE2('Q','W') : /* escape quotes, remove space */ /*ajDebug("case qtext\n");*/ ajStrRemoveWhite(&featoutValTmp); break; case CASE2('Q', 'S') : /* special regexp, quoted */ /*ajDebug("case qspecial\n");*/ /* todo: double check whether we need following line */ ajFeattagSpecialGff3(outtag, &featoutValTmp); break; case CASE2('S','P') : /* special regexp */ /*ajDebug("case special\n");*/ /* todo: double check whether we need following lines */ ajFeattagSpecialGff3(outtag, &featoutValTmp); break; case CASE2('V','O') : /* no value, so append 'true' */ /*ajDebug("case void\n");*/ ajStrAssignC(&featoutValTmp, "true"); break; default: ajFeatWarn("Unknown GFF3 feature tag type '%S' for '%S'", featoutFmtTmp, outtag); continue; } } else { /*ajDebug("no value, hope it is void: '%S'\n", featoutFmtTmp);*/ ajStrAssignC(&featoutValTmp, "true"); } featTagQuoteGff3(&featoutValTmp); firsttag=featDumpGff3Tag(file, tagname, featoutValTmp, firsttag); } ajFmtPrintF(file, "\n") ; ajListIterDel(&iter); ajStrDel(&flagdata); if(ajListGetLength(thys->Subfeatures)) { ftiter = ajListIterNewread(thys->Subfeatures); while(!ajListIterDone(ftiter)) { gf = ajListIterGet(ftiter); featDumpGff3(gf, thys, owner, file); } ajListIterDel(&ftiter); return; } return; } ID ajFeattableWriteDebug TY public MO ajfeatwrite LB core XX DE Print contents of a feature table to a file XX PN [1] PA u ftout AjPFeattabOut PD Feature table output object PX PN [2] PA r ftable const AjPFeattable PD Feature table PX RT AjBool RD Always true RX // AjBool ajFeattableWriteDebug(AjPFeattabOut ftout, const AjPFeattable ftable) { return ajFeattablePrint(ftable, ftout->Handle); } ID ajFeattablePrint TY public MO ajfeatwrite LB core XX DE Print contents of a feature table to a file XX PN [1] PA r ftable const AjPFeattable PD Feature table PX PN [2] PA u outf AjPFile PD Output file object PX RT AjBool RD Always true RX // AjBool ajFeattablePrint(const AjPFeattable ftable, AjPFile outf) { AjIList iterft = NULL; AjPFeature feature = NULL; AjIList itertag = NULL; AjPTagval tv = NULL; ajint i=0; ajint j=0; ajFmtPrintF(outf, " Format: %u (%S)", ftable->Format, ftable->Formatstr); ajFmtPrintF(outf, " Start: %u\n", ftable->Start); ajFmtPrintF(outf, " End: %u\n", ftable->End); ajFmtPrintF(outf, " Len: %u\n", ftable->Len); ajFmtPrintF(outf, " Offset: %u\n", ftable->Offset); ajFmtPrintF(outf, " Groups: %u\n", ftable->Groups); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); i++; ajFmtPrintF(outf, "\n Feature %d\n", i); ajFmtPrintF(outf, " Source: '%S'\n", feature->Source); ajFmtPrintF(outf, " Type: '%S'\n", feature->Type); ajFmtPrintF(outf, " Score: %.6f\n", feature->Score); ajFmtPrintF(outf, " Protein: %B\n", feature->Protein); ajFmtPrintF(outf, " Strand: '%c'\n", feature->Strand); ajFmtPrintF(outf, " Start: %d\n", feature->Start); ajFmtPrintF(outf, " End: %d\n", feature->End); ajFmtPrintF(outf, " Start2: %d\n", feature->Start2); ajFmtPrintF(outf, " End2: %d\n", feature->End2); ajFmtPrintF(outf, " Remote: '%S'\n", feature->Remote); ajFmtPrintF(outf, " Label: '%S'\n", feature->Label); ajFmtPrintF(outf, " Frame: %d\n", feature->Frame); ajFmtPrintF(outf, " Exon: %u\n", feature->Exon); ajFmtPrintF(outf, " Group: %u\n", feature->Group); ajFmtPrintF(outf, " Flags: %x\n", feature->Flags); if(feature->Flags & AJFEATFLAG_START_BEFORE_SEQ) ajFmtPrintF(outf, " START_BEFORE_SEQ\n"); if(feature->Flags & AJFEATFLAG_END_AFTER_SEQ) ajFmtPrintF(outf, " END_AFTER_SEQ\n"); if(feature->Flags & AJFEATFLAG_GENERATED) ajFmtPrintF(outf, " GENERATED\n"); if(feature->Flags & AJFEATFLAG_BETWEEN_SEQ) ajFmtPrintF(outf, " BETWEEN_SEQ\n"); if(feature->Flags & AJFEATFLAG_START_TWO) ajFmtPrintF(outf, " START_TWO\n"); if(feature->Flags & AJFEATFLAG_END_TWO) ajFmtPrintF(outf, " END_TWO\n"); if(feature->Flags & AJFEATFLAG_POINT) ajFmtPrintF(outf, " POINT\n"); if(feature->Flags & AJFEATFLAG_COMPLEMENT_MAIN) ajFmtPrintF(outf, " COMPLEMENT_MAIN\n"); if(feature->Flags & AJFEATFLAG_MULTIPLE) ajFmtPrintF(outf, " MULTIPLE\n"); if(feature->Flags & AJFEATFLAG_ORDER) ajFmtPrintF(outf, " ORDER\n"); if(feature->Flags & AJFEATFLAG_REMOTEID) ajFmtPrintF(outf, " REMOTEID\n"); if(feature->Flags & AJFEATFLAG_LABEL) ajFmtPrintF(outf, " LABEL\n"); if(feature->Flags & AJFEATFLAG_START_UNSURE) ajFmtPrintF(outf, " START_UNSURE\n"); if(feature->Flags & AJFEATFLAG_END_UNSURE) ajFmtPrintF(outf, " END_UNSURE\n"); ajFmtPrintF(outf, " Tags: %Lu tags\n", ajListGetLength(feature->Tags)); j=0; itertag = ajListIterNewread(feature->Tags); while(!ajListIterDone(itertag)) { tv = ajListIterGet(itertag); ajFmtPrintF(outf, " Tag %3d %S : '%S'\n", ++j, ajTagvalGetTag(tv), ajTagvalGetValue(tv)); } ajListIterDel(&itertag); } ajListIterDel(&iterft); return ajTrue; } ID ajFeatoutPrintFormat TY public MO ajfeatwrite LB core XX DE Reports the internal data structures for output feature formats XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajFeatoutPrintFormat(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "# Feature output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Alias Name is an alias\n"); ajFmtPrintF(outf, "# Nuc Valid for nucleotide sequences\n"); ajFmtPrintF(outf, "# Pro Valid for protein sequences\n"); ajFmtPrintF(outf, "# Name Alias Nuc Pro " "Description\n"); ajFmtPrintF(outf, "OutFormat {\n"); for(i=0; featOutFormatDef[i].Name; i++) { if(full || !featOutFormatDef[i].Alias) ajFmtPrintF(outf, " %-12s %5B %5B %5B \"%s\"\n", featOutFormatDef[i].Name, featOutFormatDef[i].Alias, featOutFormatDef[i].Nucleotide, featOutFormatDef[i].Protein, featOutFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajFeatoutPrinthtmlFormat TY public MO ajfeatwrite LB core XX DE Reports the internal data structures for output feature formats XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajFeatoutPrinthtmlFormat(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, ""); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); for(i=0; featOutFormatDef[i].Name; i++) { if(!featOutFormatDef[i].Alias) ajFmtPrintF(outf, "" "\n", featOutFormatDef[i].Name, featOutFormatDef[i].Alias, featOutFormatDef[i].Nucleotide, featOutFormatDef[i].Protein, featOutFormatDef[i].Desc); } ajFmtPrintF(outf, "
Features Output FormatAliasNucProDescription
\n%-12s\n%5B\n%5B\n%5B\n\"%s\"
\n"); return; } ID ajFeatoutPrintbookFormat TY public MO ajfeatwrite LB core XX DE Reports the output feature format internals in docbook format XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajFeatoutPrintbookFormat(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; AjPList fmtlist; AjPStr* names; fmtlist = ajListstrNew(); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=1; featOutFormatDef[i].Name; i++) { if(!featOutFormatDef[i].Alias) { namestr = ajStrNewC(featOutFormatDef[i].Name); ajListPush(fmtlist, namestr); namestr = NULL; } } ajListSort(fmtlist, &ajStrVcmp); ajListstrToarray(fmtlist, &names); for(i=0; names[i]; i++) { for(j=0; featOutFormatDef[j].Name; j++) { if(ajStrMatchC(names[i],featOutFormatDef[j].Name)) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", featOutFormatDef[j].Name); ajFmtPrintF(outf, " \n", featOutFormatDef[j].Nucleotide); ajFmtPrintF(outf, " \n", featOutFormatDef[j].Protein); ajFmtPrintF(outf, " \n", featOutFormatDef[j].Desc); ajFmtPrintF(outf, " \n"); } } } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Output feature formats
Output FormatNucProDescription
%s%B%B%s
\n"); ajStrDel(&namestr); names = NULL; ajListstrFreeData(&fmtlist); return; } ID ajFeatoutPrintwikiFormat TY public MO ajfeatwrite LB core XX DE Reports the output feature format internals in wiki text format XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajFeatoutPrintwikiFormat(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!Nuc!!Pro!!" "class=\"unsortable\"|Description\n"); for(i=1; featOutFormatDef[i].Name; i++) { if(!featOutFormatDef[i].Alias) { ajFmtPrintF(outf, "|-\n"); ajStrAssignC(&namestr, featOutFormatDef[i].Name); for(j=i+1; featOutFormatDef[j].Name; j++) { if(featOutFormatDef[j].Write == featOutFormatDef[i].Write) { ajFmtPrintAppS(&namestr, " %s", featOutFormatDef[j].Name); if(!featOutFormatDef[j].Alias) { ajWarn("Feature output format '%s' same as '%s' " "but not alias", featOutFormatDef[j].Name, featOutFormatDef[i].Name); } } } ajFmtPrintF(outf, "|%S||%B||%B||%s\n", namestr, featOutFormatDef[i].Nucleotide, featOutFormatDef[i].Protein, featOutFormatDef[i].Desc); } } ajFmtPrintF(outf, "|}\n\n"); ajStrDel(&namestr); return; } ID featTagAllLimit TY static MO ajfeatwrite LB core XX DE Tests a string as a valid feature value, given a DE list of possible values. XX PN [1] PA u pval AjPStr* PD parameter value PX PN [2] PA r values const AjPStr PD comma delimited list of values PX RT AjBool RD ajTrue for a valid value, possibly corrected RD ajFalse if invalid, to be converted to default (note) type RX // static AjBool featTagAllLimit(AjPStr* pval, const AjPStr values) { AjPStr limstr = NULL; const char* cp = NULL; const char* cq = NULL; ajint i=0; AjBool ret = ajFalse; /* ajDebug("featTagAllLimit '%S' '%S'\n", *pval, values); */ ajStrAssignResC(&limstr, 16, ""); cp = ajStrGetPtr(values); cq = cp; while(*cp) { if(*cp == ',') { ajStrAssignLenC(&limstr, cq, i); if(ajStrMatchCaseS(*pval, limstr)) { if(!ajStrMatchS(*pval, limstr)) { ajStrAssignS(pval, limstr); } ret = ajTrue; break; } ajStrAssignClear(&limstr); cq = cp+1; i=0; } else i++; cp++; } ajStrDel(&limstr); return ret; } ID featTagQuoteEmbl TY static MO ajfeatwrite LB core XX DE Internal quotes converted to two double quotes DE for EMBL feature tag values. DE DE Quotes added around the outside. XX PN [1] PA u pval AjPStr* PD parameter value PX RT void RD RX // static void featTagQuoteEmbl(AjPStr* pval) { const char* cp; /* ajDebug("featTagQuoteEmbl '%S'\n", *pval); */ if(ajStrFindAnyK(*pval, '"') < 0) { ajStrFmtQuote(pval); } else /* double up internal quotes */ { ajStrAssignS(&featoutTmpStr, *pval); ajStrAssignC(pval, "\""); cp = ajStrGetPtr(featoutTmpStr); while(*cp) { if(*cp == '"') ajStrAppendK(pval, '"'); ajStrAppendK(pval, *cp++); } ajStrAppendK(pval, '"'); } ajStrDelStatic(&featoutTmpStr); return; } ID featTagQuoteGff2 TY static MO ajfeatwrite LB core XX DE Internal quotes converted to escaped quotes DE for EMBL feature tag values XX PN [1] PA u pval AjPStr* PD parameter value PX RT void RD RX // static void featTagQuoteGff2(AjPStr* pval) { if(!featoutRegQuote) featoutRegQuote = ajRegCompC("([^\"]*)\""); /* ajDebug("featTagQuoteGff2 '%S'\n", *pval); */ ajStrAssignS(&featoutValCopy, *pval); ajStrDelStatic(pval); while(ajRegExec(featoutRegQuote, featoutValCopy)) { ajRegSubI(featoutRegQuote, 1, &featoutSubStr); /* ajDebug("part '%S'\n", substr); */ ajStrAppendS(pval, featoutSubStr); ajStrAppendC(pval, "\\\""); ajRegPost(featoutRegQuote, &featoutTmpStr); ajStrAssignS(&featoutValCopy, featoutTmpStr); } /* ajDebug("rest '%S'\n", featoutValCopy); */ ajStrAppendS(pval, featoutValCopy); ajStrFmtQuote(pval); return; } ID featTagQuoteGff3 TY static MO ajfeatwrite LB core XX DE Quoted text values XX PN [1] PA u pval AjPStr* PD parameter value PX RT void RD RX // static void featTagQuoteGff3(AjPStr* pval) { /*ajDebug("featTagQuoteGff3 '%S'\n", *pval);*/ ajStrFmtPercentEncodeC(pval, ";=%&,\t"); return; } ID featTagEmblDefault TY static MO ajfeatwrite LB core XX DE Give up, and generate a default EMBL/Genbank feature tag XX PN [1] PA w pout AjPStr* PD Output string PX PN [2] PA r tag const AjPStr PD original tag name PX PN [3] PA u pval AjPStr* PD parameter value PX RT void RD RX // static void featTagEmblDefault(AjPStr* pout, const AjPStr tag, AjPStr* pval) { /*ajDebug("featTagEmblDefault '%S' '%S'\n", tag, *pval);*/ featTagQuoteEmbl(pval); ajFmtPrintS(pout, "/note=\"%S: %S\"", tag, *pval); return; } ID featTagGff2Default TY static MO ajfeatwrite LB core XX DE Give up, and generate a default GFF feature tag XX PN [1] PA w pout AjPStr* PD Output string PX PN [2] PA r tag const AjPStr PD original tag name PX PN [3] PA u pval AjPStr* PD parameter value PX RT void RD RX // static void featTagGff2Default(AjPStr* pout, const AjPStr tag, AjPStr* pval) { /*ajDebug("featTagGff2Default '%S' '%S'\n", tag, *pval);*/ featTagQuoteGff2(pval); ajFmtPrintS(pout, "note \"%S: %S\"", tag, *pval); return; } ID featTagDasgffDefault TY static MO ajfeatwrite LB core XX DE Give up, and generate a default DASGFF feature tag XX PN [1] PA w pout AjPStr* PD Output string PX PN [2] PA r tag const AjPStr PD original tag name PX PN [3] PA u pval AjPStr* PD parameter value PX RT void RD RX // static void featTagDasgffDefault(AjPStr* pout, const AjPStr tag, AjPStr* pval) { /*ajDebug("featTagDasgffDefault '%S' '%S'\n", tag, *pval);*/ featTagQuoteGff3(pval); ajFmtPrintS(pout, "%S:%S", tag, *pval); return; } ID featTypePirOut TY static MO ajfeatwrite LB core XX DE Converts an internal feature type into the corresponding PIR type, DE because internal types are based on GFF3. XX PN [1] PA u type AjPStr* PD PIR feature type in, returned as internal type PX RT AjBool RD ajTrue if the type name was found and changed RX // static AjBool featTypePirOut(AjPStr* type) { const AjPStr pirtype = NULL; const AjPStr pirintype = NULL; AjBool ret = ajFalse; const AjPTable typetable; typetable = ajFeatVocabGetTypesProt("pir"); pirtype = ajFeattypeGetExternal(*type, typetable); pirintype = ajFeattypeGetInternalPir(pirtype); ajDebug("featTypePirOut '%S' in '%S' ext '%S'\n", *type, pirintype, pirtype); if(!ajStrMatchCaseS(*type, pirintype)) ret = ajTrue; ajStrAssignS(type, pirtype); return ret; } ID ajFeatwriteExit TY public MO ajfeatwrite LB core XX DE Cleans up feature table output internal memory XX RT void RD RX // void ajFeatwriteExit(void) { ajRegFree(&featoutRegUfoFmt); ajRegFree(&featoutRegUfoFile); ajRegFree(&featoutRegQuote); ajStrDel(&featoutFmtTmp); ajStrDel(&featoutFormatTmp); ajStrDel(&featoutUfoTest); ajStrDel(&featoutValCopy); ajStrDel(&featoutValTmp); ajStrDel(&featoutTmpStr); ajStrDel(&featoutStr); ajStrDel(&featoutSubStr); ajStrDel(&featoutLimTmp); return; } ID ajDirNewPath TY public MO ajfile LB core XX DE Creates a new directory object. XX PN [1] PA r path const AjPStr PD Directory name PX RT AjPDir RD New directory object. RX // AjPDir ajDirNewPath(const AjPStr path) { AjPDir thys; AJNEW0(thys); ajStrAssignS(&thys->Name, path); filePrintname(thys->Name, &thys->Printname); thys->Prefix = NULL; thys->Extension = NULL; return thys; } ID ajDirNewPathExt TY public MO ajfile LB core XX DE Creates a new directory object. XX PN [1] PA r path const AjPStr PD Directory name PX PN [2] PA r ext const AjPStr PD File extension PX RT AjPDir RD New directory object. RX // AjPDir ajDirNewPathExt(const AjPStr path, const AjPStr ext) { AjPDir thys; AJNEW0(thys); ajStrAssignS(&thys->Name, path); filePrintname(thys->Name, &thys->Printname); if (ajStrMatchC(ext, " ")) ajStrAssignC(&thys->Extension, ""); else if (ajStrGetLen(ext)) ajStrAssignS(&thys->Extension, ext); return thys; } ID ajDirNewPathPreExt TY public MO ajfile LB core XX DE Creates a new directory object. XX PN [1] PA r path const AjPStr PD Directory name PX PN [2] PA r prefix const AjPStr PD Filename prefix PX PN [3] PA r ext const AjPStr PD Filename extension PX RT AjPDir RD New directory object. RX // AjPDir ajDirNewPathPreExt(const AjPStr path, const AjPStr prefix, const AjPStr ext) { AjPDir thys; AJNEW0(thys); ajStrAssignS(&thys->Name, path); filePrintname(thys->Name, &thys->Printname); if (ajStrMatchC(prefix, " ")) ajStrAssignC(&thys->Prefix, ""); else if (ajStrGetLen(prefix)) ajStrAssignS(&thys->Prefix, prefix); if (ajStrMatchC(ext, " ")) ajStrAssignC(&thys->Extension, ""); else if (ajStrGetLen(ext)) ajStrAssignS(&thys->Extension, ext); return thys; } ID ajDirDel TY public MO ajfile LB core XX DE Close and free a directory object. XX PN [1] PA d Pdir AjPDir* PD Directory object. PX RT void RD RX // void ajDirDel(AjPDir* Pdir) { AjPDir thys; thys = *Pdir; if(!thys) return; ajStrDel(&thys->Name); ajStrDel(&thys->Printname); ajStrDel(&thys->Prefix); ajStrDel(&thys->Extension); AJFREE(*Pdir); return; } ID ajDirGetExt TY public MO ajfile LB core XX DE Returns the extension(s) of a directory object XX PN [1] PA r thys const AjPDir PD Directory object. PX RT const AjPStr RD Directory name RX // const AjPStr ajDirGetExt(const AjPDir thys) { if (!thys) return NULL; return thys->Extension; } ID ajDirGetPath TY public MO ajfile LB core XX DE Returns the full path of a directory object XX PN [1] PA r thys const AjPDir PD Directory object. PX RT const AjPStr RD Directory name RX // const AjPStr ajDirGetPath(const AjPDir thys) { if (!thys) return NULL; return thys->Name; } ID ajDirGetPrefix TY public MO ajfile LB core XX DE Returns the filename prefix(es) of a directory object XX PN [1] PA r thys const AjPDir PD Directory object. PX RT const AjPStr RD Directory name RX // const AjPStr ajDirGetPrefix(const AjPDir thys) { if (!thys) return NULL; return thys->Prefix; } ID ajDirGetPrintpath TY public MO ajfile LB core XX DE Returns the full printable path of a directory object XX PN [1] PA r thys const AjPDir PD Directory object. PX RT const AjPStr RD Directory printable name RX // const AjPStr ajDirGetPrintpath(const AjPDir thys) { if (!thys) return NULL; return thys->Printname; } ID ajDiroutNewPath TY public MO ajfile LB core XX DE Creates a new directory output object. XX PN [1] PA r path const AjPStr PD Directory name PX RT AjPDirout RD New directory object. RX // AjPDirout ajDiroutNewPath(const AjPStr path) { AjPDirout thys; AJNEW0(thys); ajStrAssignS(&thys->Name, path); filePrintname(thys->Name, &thys->Printname); thys->Extension = NULL; return thys; } ID ajDiroutNewPathExt TY public MO ajfile LB core XX DE Creates a new directory output object. XX PN [1] PA r path const AjPStr PD Directory name PX PN [2] PA r ext const AjPStr PD File extension PX RT AjPDirout RD New directory object. RX // AjPDirout ajDiroutNewPathExt(const AjPStr path, const AjPStr ext) { AjPDirout thys; AJNEW0(thys); ajStrAssignS(&thys->Name, path); filePrintname(thys->Name, &thys->Printname); if (ajStrGetLen(ext)) ajStrAssignS(&thys->Extension, ext); return thys; } ID ajDiroutDel TY public MO ajfile LB core XX DE Close and free a directory object. XX PN [1] PA d Pdir AjPDirout* PD Directory object. PX RT void RD RX // void ajDiroutDel(AjPDirout* Pdir) { AjPDirout thys; thys = *Pdir; if(!thys) return; ajStrDel(&thys->Name); ajStrDel(&thys->Printname); ajStrDel(&thys->Extension); AJFREE(*Pdir); return; } ID ajDiroutGetExt TY public MO ajfile LB core XX DE Returns the extension of an output directory object XX PN [1] PA r thys const AjPDirout PD Directory object. PX RT const AjPStr RD Directory name RX // const AjPStr ajDiroutGetExt(const AjPDirout thys) { if (!thys) return NULL; return thys->Extension; } ID ajDiroutGetPath TY public MO ajfile LB core XX DE Returns the name of an output directory object XX PN [1] PA r thys const AjPDirout PD Directory object. PX RT const AjPStr RD Directory name RX // const AjPStr ajDiroutGetPath(const AjPDirout thys) { if (!thys) return NULL; return thys->Name; } ID ajDiroutGetPrintpath TY public MO ajfile LB core XX DE Returns the printable name of an output directory object XX PN [1] PA r thys const AjPDirout PD Directory object. PX RT const AjPStr RD Directory printable name RX // const AjPStr ajDiroutGetPrintpath(const AjPDirout thys) { if (!thys) return NULL; return thys->Printname; } ID ajDiroutCreated TY public MO ajfile LB core XX DE Tests if an output directory was created when it did not already exist XX PN [1] PA u thys AjPDirout PD Directory name PX RT AjBool RD True if a newly cteated directory RX // AjBool ajDiroutCreated(AjPDirout thys) { if(!thys) return ajFalse; return thys->Created; } ID ajDiroutExists TY public MO ajfile LB core XX DE Tests a directory output object is for an existing directory XX PN [1] PA u thys AjPDirout PD Directory name PX RT AjBool RD True on success. RX // AjBool ajDiroutExists(AjPDirout thys) { #ifdef WIN32 if(ajStrMatchC(thys->Name, ".")) { ajStrAssignC(&thys->Name, ""); ajStrAssignC(&thys->Printname, ""); return ajTrue; } #endif /* WIN32 */ if(ajStrGetCharLast(thys->Name) != SLASH_CHAR) { ajStrAppendC(&thys->Name, SLASH_STRING); filePrintname(thys->Name, &thys->Printname); } if(!ajFilenameExistsDir(thys->Name)) return ajFalse; return ajTrue; } ID ajDiroutOpen TY public MO ajfile LB core XX DE Opens a directory output object, creating it if it does not already exist XX PN [1] PA u thys AjPDirout PD Directory name PX RT AjBool RD True on success. RX // AjBool ajDiroutOpen(AjPDirout thys) { #ifdef WIN32 if(ajStrMatchC(thys->Name, ".")) { ajStrAssignC(&thys->Name, ""); ajStrAssignC(&thys->Printname, ""); return ajTrue; } #endif /* WIN32 */ if(ajStrGetCharLast(thys->Name) != SLASH_CHAR) { ajStrAppendC(&thys->Name, SLASH_STRING); filePrintname(thys->Name, &thys->Printname); } if(!ajFilenameExists(thys->Name)) { ajSysCommandMakedirS(thys->Name); thys->Created = ajTrue; } if(!ajFilenameExistsDir(thys->Name)) return ajFalse; return ajTrue; } ID ajFileNewFromCfile TY public MO ajfile LB core XX DE Creates a new file object from an open C file. DE DE The file is for input, output, or append - depending on how the C FILE DE pointer was opened. XX PN [1] PA u file FILE* PD C file. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewFromCfile(FILE* file) { AjPFile thys; if(!file) ajFatal("Trying to create an AJAX file from a bad C RTL FILE*"); AJNEW0(thys); thys->fp = file; thys->Handle = ++fileHandle; if(file == stdout) thys->Name = ajStrNewC("stdout"); else if(file == stderr) thys->Name = ajStrNewC("stderr"); else if(file == stdin) thys->Name = ajStrNewC("stdin"); else thys->Name = ajStrNew(); ajStrAssignS(&thys->Printname, thys->Name); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; if(file == stdin) fileUsedStdin = ajTrue; else if(file == stdout) fileUsedStdout = ajTrue; else if(file == stderr) fileUsedStderr = ajTrue; ajDebug("Created file from C FILE %p\n", file); return thys; } ID fileNew TY static MO ajfile LB core XX DE Creates a new file object. XX RT AjPFile RD New file object. RX // static AjPFile fileNew(void) { AjPFile thys; AJNEW0(thys); thys->fp = NULL; thys->Handle = 0; thys->Name = ajStrNew(); thys->Printname = ajStrNew(); thys->Buff = ajStrNewRes(fileBuffSize); thys->List = NULL; thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } ID ajFileNewInBlockS TY public MO ajfile LB core XX DE Creates a new file object to read a named file using blocked fread calls. DE DE If the filename ends with a pipe character then a pipe is opened DE using ajFileNewInPipe. XX PN [1] PA r name const AjPStr PD File name. PX PN [2] PA r blocksize ajuint PD Block size PX RT AjPFile RD New file object. RX // AjPFile ajFileNewInBlockS(const AjPStr name, ajuint blocksize) { AjPFile ret; ret = ajFileNewInNameS(name); ret->Blocksize = blocksize; if(blocksize) { ret->Workbuffer = ajCharNewRes(blocksize); /*setvbuf(ret->fp, ret->Workbuffer, _IOFBF, blocksize);*/ ret->Readblock = ajCharNewRes(blocksize+1); ret->Blockpos = 0; ret->Blocklen = 0; ret->Blocksize = blocksize; } ajDebug("ajFileNewInBlock '%S' blocksize:%u\n", name, blocksize); return ret; } ID ajFileNewInNameC TY public MO ajfile LB core XX DE Creates a new file object to read a named file. DE DE If the filename begins with a pipe character then a pipe is opened DE using ajFileNewInPipe. XX PN [1] PA r name const char* PD File name. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewInNameC(const char *name) { ajStrAssignC(&fileNameStrTmp, name); return ajFileNewInNameS(fileNameStrTmp); } ID ajFileNewInNameS TY public MO ajfile LB core XX DE Creates a new file object to read a named file. DE DE If the filename ends with a pipe character then a pipe is opened DE using ajFileNewInPipe. XX PN [1] PA r name const AjPStr PD File name. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewInNameS(const AjPStr name) { AjPFile thys = NULL; AjPStr userstr = NULL; AjPStr reststr = NULL; AjPStr dirname = NULL; AjPStr wildname = NULL; AjPFile ptr; char *hdir = NULL; ajDebug("ajFileNewInNameS '%S'\n", name); if(ajStrMatchC(name, "stdin")) { thys = ajFileNewFromCfile(stdin); ajStrAssignC(&thys->Name, "stdin"); ajStrAssignS(&thys->Printname, thys->Name); return thys; } ajStrAssignS(&fileNameTmp, name); if(ajStrGetCharLast(name) == '|') /* pipe character at end */ return ajFileNewInPipe(name); if(ajStrGetCharFirst(fileNameTmp) == '~') { ajDebug("starts with '~'\n"); if(!fileUserExp) fileUserExp = ajRegCompC("^~([^/\\\\]*)"); ajRegExec(fileUserExp, fileNameTmp); ajRegSubI(fileUserExp, 1, &userstr); ajRegPost(fileUserExp, &reststr); ajDebug(" user: '%S' rest: '%S'\n", userstr, reststr); if(ajStrGetLen(userstr)) { /* username specified */ hdir = ajSysGetHomedirFromName(ajStrGetPtr(userstr)); if(!hdir) { ajStrDel(&userstr); ajStrDelStatic(&fileNameTmp); ajStrDel(&reststr); return NULL; } ajFmtPrintS(&fileNameTmp, "%s%S", hdir, reststr); AJFREE(hdir); ajDebug("use getpwnam: '%S'\n", fileNameTmp); } else { /* just ~/ */ hdir = ajSysGetHomedir(); if(hdir) { ajFmtPrintS(&fileNameTmp, "%s%S", hdir, reststr); AJFREE(hdir); } else ajFmtPrintS(&fileNameTmp,"%S",reststr); ajDebug("use HOME: '%S'\n", fileNameTmp); } } ajStrDel(&userstr); ajStrDel(&reststr); if(!fileWildExp) fileWildExp = ajRegCompC("(.*/)?([^/]*[*?][^/]*)$"); if(ajRegExec(fileWildExp, fileNameTmp)) { /* wildcard file names */ ajRegSubI(fileWildExp, 1, &dirname); ajRegSubI(fileWildExp, 2, &wildname); ajDebug("wild dir '%S' files '%S'\n", dirname, wildname); ptr = ajFileNewListinPathWild(dirname, wildname); ajStrDelStatic(&fileNameTmp); ajStrDel(&dirname); ajStrDel(&wildname); return ptr; } AJNEW0(thys); ajStrAssignS(&thys->Name, fileNameTmp); ajStrDelStatic(&fileNameTmp); ajNamResolve(&thys->Name); #ifdef WIN32 if(ajStrMatchC(thys->Name, "/dev/null")) thys->fp = fopen("NUL", "rb"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(thys->Name), "rb"); if(!thys->fp) { ajStrDel(&thys->Name); AJFREE(thys); /* thys->Handle = 0;*/ return NULL; } filePrintname(thys->Name, &thys->Printname); thys->Handle = ++fileHandle; thys->List = NULL; thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } ID ajFileNewInNamePathC TY public MO ajfile LB core XX DE Opens directory "dir". DE Looks for file "file" XX PN [1] PA r name const char* PD Filename. PX PN [2] PA r path const AjPStr PD Directory PX RT AjPFile RD New file object. RX // AjPFile ajFileNewInNamePathC(const char* name, const AjPStr path) { ajStrAssignC(&fileNameStrTmp, name); return ajFileNewInNamePathS(fileNameStrTmp, path); } ID ajFileNewInNamePathS TY public MO ajfile LB core XX DE Opens directory "dir". DE Looks for file "file" XX PN [1] PA r name const AjPStr PD Filename. PX PN [2] PA r path const AjPStr PD Directory PX RT AjPFile RD New file object. RX // AjPFile ajFileNewInNamePathS(const AjPStr name, const AjPStr path) { if(ajStrGetLen(path)) ajStrAssignS(&fileNameFix, path); else ajStrAssignC(&fileNameFix, CURRENT_DIR); if(ajStrGetCharLast(fileNameFix) != SLASH_CHAR) ajStrAppendC(&fileNameFix, SLASH_STRING); ajStrAppendS(&fileNameFix, name); return ajFileNewInNameS(fileNameFix); } ID ajFileNewInPipe TY public MO ajfile LB core XX DE Creates a new file object to read the output from a command. XX PN [1] PA r command const AjPStr PD Command string. PD The string may end with a trailing pipe character. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewInPipe(const AjPStr command) { AjPFile thys = NULL; thys = ajSysCreateInpipeS(command); if(!thys) return NULL; thys->Handle = ++fileHandle; ajStrAssignS(&thys->Name, command); ajStrAssignS(&thys->Printname, thys->Name); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } ID ajFileNewListinDirPre TY public MO ajfile LB core XX DE Opens directory "dir". DE Looks for file "file" with the extension (if any) specified DE for the directory XX PN [1] PA r dir const AjPDir PD Directory PX PN [2] PA r prefix const AjPStr PD Wildcard Filename. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewListinDirPre(const AjPDir dir, const AjPStr prefix) { if(ajStrGetLen(dir->Name)) ajStrAssignS(&fileNameFix, dir->Name); else ajStrAssignC(&fileNameFix, CURRENT_DIR); if(ajStrGetCharLast(fileNameFix) != SLASH_CHAR) ajStrAppendC(&fileNameFix, SLASH_STRING); ajStrAppendS(&fileNameFix, prefix); ajFilenameReplaceExtS(&fileNameFix, dir->Extension); return ajFileNewInNameS(fileNameFix); } ID ajFileNewListinList TY public MO ajfile LB core XX DE Creates a new file object with a list of input file names. XX PN [1] PA u list AjPList PD List of input filenames as strings. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewListinList(AjPList list) { AjPFile thys; AJNEW0(thys); thys->List = list; thys->Name = NULL; ajListstrTrace(thys->List); ajListstrPop(thys->List, &thys->Name); ajDebug("ajFileNewListinList pop '%S'\n", thys->Name); ajListstrTrace(thys->List); ajNamResolve(&thys->Name); #ifdef WIN32 if(ajStrMatchC(thys->Name, "/dev/null")) thys->fp = fopen("NUL", "rb"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(thys->Name), "rb"); if(!thys->fp) { ajDebug("ajFileNewListinList fopen failed\n"); ajStrDel(&thys->Name); thys->Handle = 0; AJFREE(thys); return NULL; } filePrintname(thys->Name, &thys->Printname); thys->Handle = ++fileHandle; thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } ID ajFileNewListinNameDirS TY public MO ajfile LB core XX DE Opens directory "dir" and looks for file "filename". XX PN [1] PA r name const AjPStr PD Wildcard Filename. PX PN [2] PA r dir const AjPDir PD Directory PX RT AjPFile RD New file object. RX // AjPFile ajFileNewListinNameDirS(const AjPStr name, const AjPDir dir) { if(ajStrGetLen(dir->Name)) ajStrAssignS(&fileNameFix, dir->Name); else ajStrAssignC(&fileNameFix, CURRENT_DIR); if(ajStrGetCharLast(fileNameFix) != SLASH_CHAR) ajStrAppendC(&fileNameFix, SLASH_STRING); ajStrAppendS(&fileNameFix, name); return ajFileNewInNameS(fileNameFix); } ID ajFileNewListinPathWild TY public MO ajfile LB core XX DE Opens directory "dir" DE Looks for file(s) matching "file" DE Opens them as a list of files using a simple file object. XX PN [1] PA r path const AjPStr PD Directory PX PN [2] PA r wildname const AjPStr PD Wildcard filename. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewListinPathWild(const AjPStr path, const AjPStr wildname) { DIR* dp; #if defined(AJ_IRIXLF) struct dirent64 *de; #else /* !AJ_IRIXLF */ struct dirent* de; #endif /* AJ_IRIXLF */ ajint dirsize; AjPList list = NULL; AjPStr name = NULL; #ifdef _POSIX_C_SOURCE char buf[sizeof(struct dirent)+MAXNAMLEN]; #endif /* _POSIX_C_SOURCE */ if(ajStrGetLen(path)) ajStrAssignS(&fileDirfixTmp, path); else ajStrAssignC(&fileDirfixTmp, CURRENT_DIR); if(ajStrGetCharLast(fileDirfixTmp) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); dp = fileOpenDir(&fileDirfixTmp); if(!dp) return NULL; dirsize = 0; list = ajListstrNew(); while( #if defined(AJ_IRIXLF) #ifdef _POSIX_C_SOURCE !readdir64_r(dp,(struct dirent64 *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir64(dp)) #endif /* _POSIX_C_SOURCE */ #else /* !AJ_IRIXLF */ #ifdef _POSIX_C_SOURCE !readdir_r(dp,(struct dirent *)buf,&de) #else /* _POSIX_C_SOURCE */ (de=readdir(dp)) #endif /* _POSIX_C_SOURCE */ #endif /* AJ_IRIXLF */ ) { #ifdef _POSIX_C_SOURCE if(!de) break; #endif /* _POSIX_C_SOURCE */ /* skip deleted files with inode zero */ #ifndef __CYGWIN__ if(!de->d_ino) continue; #endif /* !__CYGWIN__ */ if(ajCharMatchC(de->d_name, ".")) continue; if(ajCharMatchC(de->d_name, "..")) continue; if(!ajCharMatchWildS(de->d_name, wildname)) continue; dirsize++; ajDebug("accept '%s'\n", de->d_name); name = NULL; ajFmtPrintS(&name, "%S%s", fileDirfixTmp, de->d_name); ajListstrPushAppend(list, name); } closedir(dp); ajDebug("%d files for '%S' '%S'\n", dirsize, path, wildname); return ajFileNewListinList(list); } ID ajFileNewListinPathWildExclude TY public MO ajfile LB core XX DE Opens directory "dir". DE Looks for file(s) matching "file". DE Skip files matching excluded files wildcard. DE Opens them as a list of files using a simple file object. XX PN [1] PA r path const AjPStr PD Directory path PX PN [2] PA r wildname const AjPStr PD Wildcard filename. PX PN [3] PA r exclude const AjPStr PD Wildcard excluded filename. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewListinPathWildExclude(const AjPStr path, const AjPStr wildname, const AjPStr exclude) { DIR* dp; #if defined(AJ_IRIXLF) struct dirent64 *de; #else /* !AJ_IRIXLF */ struct dirent* de; #endif /* AJ_IRIXLF */ ajint dirsize; AjPList list = NULL; AjPStr name = NULL; #ifdef _POSIX_C_SOURCE char buf[sizeof(struct dirent)+MAXNAMLEN]; #endif /* _POSIX_C_SOURCE */ ajDebug("ajFileNewListinPathWildExclude " "path '%S' wildname '%S' exclude '%S\n", path, wildname, exclude); if(ajStrGetLen(path)) ajStrAssignS(&fileDirfixTmp, path); else ajStrAssignC(&fileDirfixTmp, CURRENT_DIR); if(ajStrGetCharLast(fileDirfixTmp) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); dp = fileOpenDir(&fileDirfixTmp); if(!dp) return NULL; dirsize = 0; list = ajListstrNew(); while( #if defined(AJ_IRIXLF) #ifdef _POSIX_C_SOURCE !readdir64_r(dp,(struct dirent64 *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir64(dp)) #endif /* _POSIX_C_SOURCE */ #else /* !AJ_IRIXLF */ #ifdef _POSIX_C_SOURCE !readdir_r(dp,(struct dirent *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir(dp)) #endif /* _POSIX_C_SOURCE */ #endif /* AJ_IRIXLF */ ) { #ifdef _POSIX_C_SOURCE if(!de) break; #endif /* _POSIX_C_SOURCE */ /* skip deleted files with inode zero */ #ifndef __CYGWIN__ if(!de->d_ino) continue; #endif /* !__CYGWIN__ */ if(ajCharMatchC(de->d_name, ".")) continue; if(ajCharMatchC(de->d_name, "..")) continue; ajStrAssignC(&fileNameTmp, de->d_name); if(!ajFilenameTestExclude(fileNameTmp, exclude, wildname)) continue; dirsize++; ajDebug("accept '%s'\n", de->d_name); name = NULL; ajFmtPrintS(&name, "%S%s", fileDirfixTmp, de->d_name); ajListstrPushAppend(list, name); } closedir(dp); ajDebug("%d files for '%S' '%S'\n", dirsize, path, wildname); return ajFileNewListinList(list); } ID ajFileNewOutNameC TY public MO ajfile LB core XX DE Creates a new output file object with a specified name. DE DE 'stdout' and 'stderr' are special names for standard output and DE standard error respectively. XX PN [1] PA r name const char* PD File name. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewOutNameC(const char* name) { ajStrAssignC(&fileNameStrTmp, name); return ajFileNewOutNameS(fileNameStrTmp); } ID ajFileNewOutNameS TY public MO ajfile LB core XX DE Creates a new output file object with a specified name. DE DE 'stdout' and 'stderr' are special names for standard output and DE standard error respectively. XX PN [1] PA r name const AjPStr PD File name. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewOutNameS(const AjPStr name) { AjPFile thys; if(ajStrMatchC(name, "stdout")) { thys = ajFileNewFromCfile(stdout); ajStrAssignC(&thys->Name, "stdout"); ajStrAssignS(&thys->Printname, thys->Name); return thys; } if(ajStrMatchC(name, "stderr")) { thys = ajFileNewFromCfile(stderr); ajStrAssignC(&thys->Name, "stderr"); ajStrAssignS(&thys->Printname, thys->Name); return thys; } AJNEW0(thys); #ifdef WIN32 if(ajStrMatchC(name, "/dev/null")) thys->fp = fopen(ajStrGetPtr(name), "wb"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(name), "wb"); if(!thys->fp) { ajWarn("Failed to open output file '%S' error:%d '%s'", name, errno, strerror(errno)); thys->Handle = 0; return NULL; } thys->Handle = ++fileHandle; ajStrAssignS(&thys->Name, name); filePrintname(thys->Name, &thys->Printname); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; thys->App = ajFalse; return thys; } ID ajFileNewOutNameDirS TY public MO ajfile LB core XX DE Creates a new output file object with a specified directory and name. DE Uses the default extension (if any) specified for the directory. DE DE 'stdout' and 'stderr' are special names for standard output and DE standard error respectively. DE DE If the filename already has a directory specified, DE the "dir" argument is ignored. XX PN [1] PA r name const AjPStr PD File name. PX PN [2] PA rN dir const AjPDirout PD Directory PD (optional, can be empty or NULL). PX RT AjPFile RD New file object. RX // AjPFile ajFileNewOutNameDirS(const AjPStr name, const AjPDirout dir) { AjPFile thys; ajDebug("ajFileNewOutNameDirS('%S' '%S')\n", dir->Name, name); AJNEW0(thys); if(!dir) { #ifdef WIN32 if(ajStrMatchC(name, "/dev/null")) thys->fp = fopen("NUL", "wb"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(name), "wb"); ajDebug("ajFileNewOutNameDirS open name '%S'\n", name); } else { if(ajFilenameHasPath(name)) ajStrAssignS(&fileDirfixTmp, name); else { ajStrAssignS(&fileDirfixTmp, dir->Name); if(ajStrGetCharLast(dir->Name) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); ajStrAppendS(&fileDirfixTmp, name); } ajFilenameSetExtS(&fileDirfixTmp, dir->Extension); thys->fp = fopen(ajStrGetPtr(fileDirfixTmp), "wb"); ajDebug("ajFileNewOutNameDirS open dirfix '%S'\n", fileDirfixTmp); } if(!thys->fp) { ajWarn("Failed to open output file '%S' in directory '%S' " "error:%d '%s'", name, dir, errno, strerror(errno)); thys->Handle = 0; return NULL; } thys->Handle = ++fileHandle; ajStrAssignS(&thys->Name, name); filePrintname(thys->Name, &thys->Printname); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } ID ajFileNewOutNamePathS TY public MO ajfile LB core XX DE Creates a new output file object with a specified directory and name. DE DE 'stdout' and 'stderr' are special names for standard output and DE standard error respectively. DE DE If the filename already has a directory specified, DE the "dir" argument is ignored. XX PN [1] PA r name const AjPStr PD File name. PX PN [2] PA rN path const AjPStr PD Directory (optional, can be empty or NULL). PX RT AjPFile RD New file object. RX // AjPFile ajFileNewOutNamePathS(const AjPStr name, const AjPStr path) { AjPFile thys; ajDebug("ajFileNewOutNamePathS('%S' '%S')\n", path, name); if(ajStrMatchC(name, "stdout")) return ajFileNewFromCfile(stdout); if(ajStrMatchC(name, "stderr")) return ajFileNewFromCfile(stderr); AJNEW0(thys); if(!ajStrGetLen(path)) { #ifdef WIN32 if(ajStrMatchC(name, "/dev/null")) thys->fp = fopen("NUL", "wb"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(name), "wb"); ajDebug("ajFileNewOutNamePathS open name '%S'\n", name); } else { if(ajFilenameHasPath(name)) ajStrAssignS(&fileDirfixTmp, name); else { ajStrAssignS(&fileDirfixTmp, path); if(ajStrGetCharLast(path) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); ajStrAppendS(&fileDirfixTmp, name); } thys->fp = fopen(ajStrGetPtr(fileDirfixTmp), "wb"); ajDebug("ajFileNewOutNamePathS open dirfix '%S'\n", fileDirfixTmp); } if(!thys->fp) { ajWarn("Failed to open output file '%S' in directory '%S' " "error:%d '%s'", name, path, errno, strerror(errno)); thys->Handle = 0; return NULL; } thys->Handle = ++fileHandle; ajStrAssignS(&thys->Name, name); filePrintname(thys->Name, &thys->Printname); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; return thys; } ID ajFileNewOutappendNameS TY public MO ajfile LB core XX DE Creates an output file object with a specified name. DE The file is opened for append so it either appends to an existing file DE or opens a new one. XX PN [1] PA r name const AjPStr PD File name. PX RT AjPFile RD New file object. RX // AjPFile ajFileNewOutappendNameS(const AjPStr name) { AjPFile thys; AJNEW0(thys); #ifdef WIN32 if(ajStrMatchC(name, "/dev/null")) thys->fp = fopen("NUL", "ab"); else #endif /* WIN32 */ thys->fp = fopen(ajStrGetPtr(name), "ab"); if(!thys->fp) { thys->Handle = 0; return NULL; } thys->Handle = ++fileHandle; ajStrAssignS(&thys->Name, name); filePrintname(thys->Name, &thys->Printname); thys->End = ajFalse; fileOpenCnt++; fileOpenTot++; if(fileOpenCnt > fileOpenMax) fileOpenMax = fileOpenCnt; thys->App = ajTrue; return thys; } ID ajFileClose TY public MO ajfile LB core XX DE Close and free a file object. XX PN [1] PA d Pfile AjPFile* PD File. PX RT void RD RX // void ajFileClose(AjPFile* Pfile) { AjPFile thys; thys = Pfile ? *Pfile : 0; if(!Pfile) return; if(!*Pfile) return; fileClose(thys); AJFREE(*Pfile); return; } ID fileClose TY static MO ajfile LB core XX DE Closes a file object. Used as part of the public destructor and DE other public functions. XX PN [1] PA w thys AjPFile PD File. PX RT void RD RX // static void fileClose(AjPFile thys) { int sleepcount = 0; int status = 0; int maxsleep = 60; #ifndef WIN32 pid_t retval; #endif /* !WIN32 */ if(!thys) return; #ifndef WIN32 /* ** Only wait for the PID to close if we have read everything. ** Otherwise ... we will wait for ever as it will not ** go away until its output is read or closed */ if (thys->Pid && thys->End) { while((retval=waitpid(thys->Pid,&status,WNOHANG))!= thys->Pid) { if(sleepcount > maxsleep) break; sleepcount++; sleep(1); /*ajDebug("fileClose waitpid returns %d status %d\n", retval, status);*/ if(retval == -1) if(errno != EINTR) break; status = 0; } } #else /* WIN32 */ if(thys->Process && thys->End) { WaitForSingleObject(thys->Process,INFINITE); CloseHandle(thys->Process); CloseHandle(thys->Thread); } #endif /* !WIN32 */ if(thys->Handle) { if(thys->fp) { if(thys->fp == stdout) fileUsedStdout = ajFalse; else if(thys->fp == stderr) fileUsedStderr = ajFalse; else if(thys->fp == stdin) fileUsedStdin = ajFalse; else { if(fclose(thys->fp)) ajFatal("File close problem in fileClose error:%d '%s'", errno, strerror(errno)); } } thys->Handle = 0; fileCloseCnt++; fileOpenCnt--; } else ajDebug("file already closed\n"); ajStrDel(&thys->Name); ajStrDel(&thys->Printname); ajStrDel(&thys->Buff); ajListstrFreeData(&thys->List); AJFREE(thys->Workbuffer); AJFREE(thys->Readblock); return; } ID ajFileReopenName TY public MO ajfile LB core XX DE Reopens an input file with a new name XX PN [1] PA u file AjPFile PD Input file. PX PN [2] PA r name const AjPStr PD name of file. PX RT AjBool RD True on success RX // AjBool ajFileReopenName(AjPFile file, const AjPStr name) { ajStrAssignS(&file->Name, name); filePrintname(file->Name, &file->Printname); if(!freopen(MAJSTRGETPTR(file->Name), "rb", file->fp)) return ajFalse; return ajTrue; } ID ajFileReopenNext TY public MO ajfile LB core XX DE Given a file object that includes a list of input files, closes the DE current input file and opens the next one. XX PN [1] PA u file AjPFile PD File object. PX RT AjBool RD ajTrue on success. RX // AjBool ajFileReopenNext(AjPFile file) { static AjPStr name = NULL; if(!file->List) { ajDebug("ajFileReopenNext for non-list file %F\n", file); return ajFalse; } ajDebug("ajFileReopenNext for non-list file %F name '%S'\n", file, file->Name); /*ajListTrace(file->List);*/ if(!ajListPop(file->List, (void*) &name)) { /* end of list */ ajDebug("ajFileReopenNext failed - list completed\n"); return ajFalse; } ajDebug("ajFileReopenNext filename '%S'\n", name); if(!ajFileReopenName(file, name)) { /* popped from the list */ ajStrDel(&name); return ajFalse; } /* popped from the list */ ajStrDel(&name); file->End = ajFalse; ajDebug("ajFileNext success\n"); return ajTrue; } ID ajFileFix TY public MO ajfile LB core XX DE Resets internal file attribute after non-AJAX operations. XX PN [1] PA u file AjPFile PD File. PX RT AjBool RD True if end of file is reached RX // AjBool ajFileFix(AjPFile file) { ajFileResetPos(file); return ajFileResetEof(file); } ID ajFileResetEof TY public MO ajfile LB core XX DE Resets the end of file attribute after non-AJAX operations. XX PN [1] PA u file AjPFile PD File. PX RT AjBool RD True if end of file is set RX // AjBool ajFileResetEof(AjPFile file) { if(feof(file->fp)) file->End = ajTrue; else file->End = ajFalse; return file->End; } ID ajFileResetPos TY public MO ajfile LB core XX DE Resets and returns the current position in an open file. XX PN [1] PA u file AjPFile PD File. PX RT ajlong RD Result of 'ftell' RX // ajlong ajFileResetPos(AjPFile file) { if(!file->fp) return 0; file->Filepos = ftell(file->fp); return file->Filepos; } ID ajFileSeek TY public MO ajfile LB core XX DE Sets the current position in an open file. DE DE Resets the end-of-file flag End for cases where end-of-file was DE reached and then we seek back somewhere in the file. XX PN [1] PA u file AjPFile PD File. PX PN [2] PA r offset ajlong PD Offset PX PN [3] PA r wherefrom ajint PD Start of offset, as defined for 'fseek'. PX RT ajint RD Result of 'fseek' RX // ajint ajFileSeek(AjPFile file, ajlong offset, ajint wherefrom) { ajint ret; clearerr(file->fp); ret = fseek(file->fp, offset, wherefrom); if(feof(file->fp)) { file->End = ajTrue; ajDebug("EOF ajFileSeek file %F\n", file); } else file->End = ajFalse; return ret; } ID ajFileSetEof TY public MO ajfile LB core XX DE Ensures a binary file has reached the end of file and sets the DE end of file attribute XX PN [1] PA u file AjPFile PD File. PX RT AjBool RD True if end of file was already RX // AjBool ajFileSetEof(AjPFile file) { AjBool ret = file->End; char c; fseek(file->fp, 0, SEEK_END); fread(&c, 1, 1, file->fp); if(feof(file->fp)) file->End = ajTrue; else file->End = ajFalse; return ret; } ID ajFileSetUnbuffer TY public MO ajfile LB core XX DE Turns off system buffering of an output file, for example to allow DE debug output to appear even in the event of a program abort. XX PN [1] PA u file AjPFile PD File object. PX RT void RD RX // void ajFileSetUnbuffer(AjPFile file) { setbuf(file->fp, NULL); return; } ID ajFileGetFileptr TY public MO ajfile LB core XX DE Returns the C file pointer for an open file. DE DE Warning: Using the C file pointer will make internals of the file DE object invalid. The file position can be reset with ajFileResetPos. XX PN [1] PA r file const AjPFile PD File. PX RT FILE* RD C file pointer for the file. RX // FILE* ajFileGetFileptr(const AjPFile file) { if(!file) return NULL; return file->fp; } ID ajFileGetNameC TY public MO ajfile LB core XX DE Returns the file name for a file object. The filename returned is a pointer DE to the real string internally, so the user must take care not to change DE it and cannot trust the value if the file object is deleted. XX PN [1] PA r file const AjPFile PD File. PX RT const char* RD Filename as a C character string. RX // const char* ajFileGetNameC(const AjPFile file) { if(!file) return ""; return ajStrGetPtr(file->Name); } ID ajFileGetNameS TY public MO ajfile LB core XX DE Returns the file name for a file object. The filename returned is a pointer DE to the real string internally, so the user must take care not to change DE it and cannot trust the value if the file object is deleted. XX PN [1] PA r file const AjPFile PD File. PX RT const AjPStr RD Filename as a C character string. RX // const AjPStr ajFileGetNameS(const AjPFile file) { if(!file) return NULL; return file->Name; } ID ajFileGetPrintnameC TY public MO ajfile LB core XX DE Returns the file name for a file object. The filename returned is a pointer DE to the real string internally, so the user must take care not to change DE it and cannot trust the value if the file object is deleted. XX PN [1] PA r file const AjPFile PD File. PX RT const char* RD Printable filename as a C character string. RX // const char* ajFileGetPrintnameC(const AjPFile file) { if(!file) return ""; return ajStrGetPtr(file->Printname); } ID ajFileGetPrintnameS TY public MO ajfile LB core XX DE Returns the file name for a file object. The filename returned is a pointer DE to the real string internally, so the user must take care not to change DE it and cannot trust the value if the file object is deleted. XX PN [1] PA r file const AjPFile PD File. PX RT const AjPStr RD Printable filename as a C character string. RX // const AjPStr ajFileGetPrintnameS(const AjPFile file) { if(!file) return NULL; return file->Printname; } ID ajFileIsAppend TY public MO ajfile LB core XX DE Returns the App element for a file object. The App element is True if the DE file was opened for appending to, False otherwise. XX PN [1] PA r file const AjPFile PD File. PX RT AjBool RD App element, True if if file was opened for appending to, RD False otherwise. RX // AjBool ajFileIsAppend(const AjPFile file) { if(!file) return ajFalse; return file->App; } ID ajFileIsEof TY public MO ajfile LB core XX DE Tests whether we have reached end of file already XX PN [1] PA r file const AjPFile PD File PX RT AjBool RD ajTrue if we already set end-of-file RX // AjBool ajFileIsEof(const AjPFile file) { if(!file) return ajTrue; return file->End; } ID ajFileIsFile TY public MO ajfile LB core XX DE Tests whether a file object is really a regular file. DE DE Used to test for character devices, for example standard input from DE a terminal. XX PN [1] PA r file const AjPFile PD File object. PX RT AjBool RD ajTrue if the file matches stderr. RX // AjBool ajFileIsFile(const AjPFile file) { #if defined(AJ_IRIXLF) struct stat64 buf; #else /* !AJ_IRIXLF */ struct stat buf; #endif /* AJ_IRIXLF */ if( #if defined(AJ_IRIXLF) !fstat64(fileno(file->fp), &buf) #else /* !AJ_IRIXLF */ !fstat(fileno(file->fp), &buf) #endif /* AJ_IRIXLF */ ) if((ajuint)buf.st_mode & AJ_FILE_R) return ajTrue; return ajFalse; } ID ajFileIsStderr TY public MO ajfile LB core XX DE Tests whether a file object is really stderr. XX PN [1] PA r file const AjPFile PD File object. PX RT AjBool RD ajTrue if the file matches stderr. RX // AjBool ajFileIsStderr(const AjPFile file) { if(!file) return ajFalse; if(file->fp == stderr) return ajTrue; return ajFalse; } ID ajFileIsStdin TY public MO ajfile LB core XX DE Tests whether a file object is really stdin. XX PN [1] PA r file const AjPFile PD File object. PX RT AjBool RD ajTrue if the file matches stdin. RX // AjBool ajFileIsStdin(const AjPFile file) { if(!file) return ajFalse; if(file->fp == stdin) return ajTrue; return ajFalse; } ID ajFileIsStdout TY public MO ajfile LB core XX DE Tests whether a file object is really stdout. XX PN [1] PA r file const AjPFile PD File object. PX RT AjBool RD ajTrue if the file matches stdout. RX // AjBool ajFileIsStdout(const AjPFile file) { if(!file) return ajFalse; if(file->fp == stdout) return ajTrue; return ajFalse; } ID ajFileTrace TY public MO ajfile LB core XX DE Writes debug messages to trace the contents of a file object. XX PN [1] PA r file const AjPFile PD File. PX RT void RD RX // void ajFileTrace(const AjPFile file) { ajulong i; ajulong j; AjIList iter; ajDebug("File: '%S'\n", file->Name); ajDebug("Print: '%S'\n", file->Printname); ajDebug(" Handle: %d\n", file->Handle); ajDebug(" End: %B\n", file->End); ajDebug(" Append: %B\n", file->App); ajDebug(" Filepos: %ld\n", file->Filepos); #ifndef WIN32 ajDebug(" PID: %d\n", file->Pid); #endif /* !WIN32 */ ajDebug(" feof: %d\n", feof(file->fp)); ajDebug(" ftell: %ld\n", ftell(file->fp)); i = ajListGetLength(file->List); ajDebug(" List: %Lu\n", i); if(i) { j = 0; iter = ajListIterNewread(file->List); while (!ajListIterDone(iter)) ajDebug(" %3Ld: '%S'\n", ++j, ajListstrIterGet(iter)); } return; } ID ajFileExit TY public MO ajfile LB core XX DE Prints a summary of file usage with debug calls XX RT void RD RX // void ajFileExit(void) { ajDebug("File usage : %d opened, %d closed, %d max, %d total\n", fileOpenCnt, fileCloseCnt, fileOpenMax, fileOpenTot); ajStrDel(&fileNameFix); ajStrDel(&fileNameTmp); ajStrDel(&fileNameStrTmp); ajStrDel(&fileDirfixTmp); ajStrDel(&fileCwd); ajStrDel(&fileTmpStr); ajStrDel(&fileTempFilename); ajStrDel(&fileDirectory); ajRegFree(&fileUserExp); ajRegFree(&fileWildExp); ajRegFree(&fileEntryExp); ajRegFree(&fileFileExp); ajRegFree(&fileRestExp); ajRegFree(&fileDirExp); ajRegFree(&fileFilenameExp); return; } ID ajFilebuffNewFromCfile TY public MO ajfile LB core XX DE Creates a new buffered input file from an already open C file. XX PN [1] PA u file FILE* PD Open C file. PX RT AjPFilebuff RD New buffered file object. RX // AjPFilebuff ajFilebuffNewFromCfile(FILE* file) { AjPFile tmpcfile; tmpcfile = ajFileNewFromCfile(file); if(!tmpcfile) return NULL; return ajFilebuffNewFromFile(tmpcfile); } ID ajFilebuffNewFromFile TY public MO ajfile LB core XX DE Creates a new buffered input file object from an open file. XX PN [1] PA u file AjPFile PD File object to be buffered. PX RT AjPFilebuff RD New buffered file object. RX // AjPFilebuff ajFilebuffNewFromFile(AjPFile file) { AjPFilebuff thys; if(!file) return NULL; AJNEW0(thys); thys->File = file; thys->Last = thys->Curr = thys->Prev = thys->Lines = NULL; thys->Freelines = thys->Freelast = NULL; thys->Pos = thys->Size = 0; return thys; } ID ajFilebuffNewLine TY public MO ajfile LB core XX DE Creates a new buffered input file object with no file but with DE one line of buffered data provided. XX PN [1] PA r line const AjPStr PD One line of buffered data. PX RT AjPFilebuff RD New buffered file object. RX // AjPFilebuff ajFilebuffNewLine(const AjPStr line) { AjPFilebuff thys; AjPFile file; file = fileNew(); file->End = ajTrue; ajDebug("EOF ajFilebuffNewLine file \n"); thys = ajFilebuffNewFromFile(file); thys->Lines = AJNEW0(thys->Last); ajStrAssignS(&thys->Last->Line,line); thys->Curr = thys->Lines; thys->Pos = 0; thys->Size = 1; return thys; } ID ajFilebuffNewListinList TY public MO ajfile LB core XX DE Creates a new buffered file object from a list of filenames. XX PN [1] PA u list AjPList PD List of filenames as strings. PX RT AjPFilebuff RD New buffered file object. RX // AjPFilebuff ajFilebuffNewListinList(AjPList list) { AjPFile file; file = ajFileNewListinList(list); if(!file) return NULL; return ajFilebuffNewFromFile(file); } ID ajFilebuffNewNameS TY public MO ajfile LB core XX DE Creates a new buffered input file object with an opened named file. XX PN [1] PA r name const AjPStr PD File name. PX RT AjPFilebuff RD New buffered file object. RX // AjPFilebuff ajFilebuffNewNameS(const AjPStr name) { AjPFile file; file = ajFileNewInNameS(name); return ajFilebuffNewFromFile(file); } ID ajFilebuffNewNamePathC TY public MO ajfile LB core XX DE Opens directory "dir", finds and opens file "name" XX PN [1] PA r name const char* PD Filename. PX PN [2] PA r path const AjPStr PD Directory. If empty uses current directory. PX RT AjPFilebuff RD New buffered file object. RX // AjPFilebuff ajFilebuffNewNamePathC(const char* name, const AjPStr path) { if(ajStrGetLen(path)) ajStrAssignS(&fileNameFix, path); else ajStrAssignC(&fileNameFix, CURRENT_DIR); if(ajStrGetCharLast(fileNameFix) != SLASH_CHAR) ajStrAppendC(&fileNameFix, SLASH_STRING); ajStrAppendC(&fileNameFix, name); return ajFilebuffNewNameS(fileNameFix); } ID ajFilebuffNewNamePathS TY public MO ajfile LB core XX DE Opens directory "dir", finds and opens file "name" XX PN [1] PA r name const AjPStr PD Filename. PX PN [2] PA r path const AjPStr PD Directory. If empty uses current directory. PX RT AjPFilebuff RD New buffered file object. RX // AjPFilebuff ajFilebuffNewNamePathS(const AjPStr name, const AjPStr path) { if(ajStrGetLen(path)) ajStrAssignS(&fileNameFix, path); else ajStrAssignC(&fileNameFix, CURRENT_DIR); if(ajStrGetCharLast(fileNameFix) != SLASH_CHAR) ajStrAppendC(&fileNameFix, SLASH_STRING); ajStrAppendS(&fileNameFix, name); return ajFilebuffNewNameS(fileNameFix); } ID ajFilebuffNewNofile TY public MO ajfile LB core XX DE Creates a new buffered input file object with an undefined file DE to be used as a text buffer. XX RT AjPFilebuff RD New buffered file object. RX // AjPFilebuff ajFilebuffNewNofile(void) { AjPFile file; file = fileNew(); /* dummy file */ return ajFilebuffNewFromFile(file); } ID ajFilebuffNewPathWild TY public MO ajfile LB core XX DE Opens directory "dir" DE Looks for file(s) matching "file" DE Opens them as a list of files using a buffered file object. XX PN [1] PA r path const AjPStr PD Directory PX PN [2] PA r wildname const AjPStr PD Wildcard filename. PX RT AjPFilebuff RD New buffered file object. RX // AjPFilebuff ajFilebuffNewPathWild(const AjPStr path, const AjPStr wildname) { DIR* dp; #if defined(AJ_IRIXLF) struct dirent64 *de; #else /* !AJ_IRIXLF */ struct dirent* de; #endif /* AJ_IRIXLF */ ajint dirsize; AjPList list = NULL; AjPStr name = NULL; #ifdef _POSIX_C_SOURCE char buf[sizeof(struct dirent)+MAXNAMLEN]; #endif /* _POSIX_C_SOURCE */ if(ajStrGetLen(path)) ajStrAssignS(&fileDirfixTmp, path); else ajStrAssignC(&fileDirfixTmp, CURRENT_DIR); if(ajStrGetCharLast(fileDirfixTmp) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); dp = fileOpenDir(&fileDirfixTmp); if(!dp) return NULL; dirsize = 0; list = ajListstrNew(); while( #if defined(AJ_IRIXLF) #ifdef _POSIX_C_SOURCE !readdir64_r(dp,(struct dirent64 *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir64(dp)) #endif /* _POSIX_C_SOURCE */ #else /* !AJ_IRIXLF */ #ifdef _POSIX_C_SOURCE !readdir_r(dp,(struct dirent *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir(dp)) #endif /* _POSIX_C_SOURCE */ #endif /* AJ_IRIXLF */ ) { #ifdef _POSIX_C_SOURCE if(!de) break; #endif /* _POSIX_C_SOURCE */ /* skip deleted files with inode zero */ #ifndef __CYGWIN__ if(!de->d_ino) continue; #endif /* !__CYGWIN__ */ if(ajCharMatchC(de->d_name, ".")) continue; if(ajCharMatchC(de->d_name, "..")) continue; if(!ajCharMatchWildS(de->d_name, wildname)) continue; dirsize++; ajDebug("accept '%s'\n", de->d_name); name = NULL; ajFmtPrintS(&name, "%S%s", fileDirfixTmp, de->d_name); ajListstrPushAppend(list, name); } closedir(dp); ajDebug("%d files for '%S' '%S'\n", dirsize, path, wildname); return ajFilebuffNewListinList(list); } ID ajFilebuffNewPathWildExclude TY public MO ajfile LB core XX DE Opens directory "dir" DE Looks for file(s) matching "file" DE Skip files matching excluded files wildcard DE Opens them as a list of files using a buffered file object. XX PN [1] PA r path const AjPStr PD Directory PX PN [2] PA r wildname const AjPStr PD Wildcard filename. PX PN [3] PA r exclude const AjPStr PD Wildcard excluded filename. PX RT AjPFilebuff RD New buffered file object. RX // AjPFilebuff ajFilebuffNewPathWildExclude(const AjPStr path, const AjPStr wildname, const AjPStr exclude) { DIR* dp; #if defined(AJ_IRIXLF) struct dirent64 *de; #else /* !AJ_IRIXLF */ struct dirent* de; #endif /* AJ_IRIXLF */ ajint dirsize; AjPList list = NULL; AjPStr name = NULL; #ifdef _POSIX_C_SOURCE char buf[sizeof(struct dirent)+MAXNAMLEN]; #endif /* _POSIX_C_SOURCE */ ajDebug("ajFilebuffNewPathWildExclude " "path '%S' wildname '%S' exclude '%S\n", path, wildname, exclude); if(ajStrGetLen(path)) ajStrAssignS(&fileDirfixTmp, path); else ajStrAssignC(&fileDirfixTmp, CURRENT_DIR); if(ajStrGetCharLast(fileDirfixTmp) != SLASH_CHAR) ajStrAppendC(&fileDirfixTmp, SLASH_STRING); dp = fileOpenDir(&fileDirfixTmp); if(!dp) return NULL; dirsize = 0; list = ajListstrNew(); while( #if defined(AJ_IRIXLF) #ifdef _POSIX_C_SOURCE !readdir64_r(dp,(struct dirent64 *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir64(dp)) #endif /* _POSIX_C_SOURCE */ #else /* !AJ_IRIXLF */ #ifdef _POSIX_C_SOURCE !readdir_r(dp,(struct dirent *)buf,&de) #else /* !_POSIX_C_SOURCE */ (de=readdir(dp)) #endif /* _POSIX_C_SOURCE */ #endif /* AJ_IRIXLF */ ) { #ifdef _POSIX_C_SOURCE if(!de) break; #endif /* _POSIX_C_SOURCE */ /* skip deleted files with inode zero */ #ifndef __CYGWIN__ if(!de->d_ino) continue; #endif /* !__CYGWIN__ */ if(ajCharMatchC(de->d_name, ".")) continue; if(ajCharMatchC(de->d_name, "..")) continue; ajStrAssignC(&fileNameTmp, de->d_name); ajDebug("testing '%s'\n", de->d_name); if(!ajFilenameTestExclude(fileNameTmp, exclude, wildname)) continue; dirsize++; ajDebug("accept '%s'\n", de->d_name); name = NULL; ajFmtPrintS(&name, "%S%s", fileDirfixTmp, de->d_name); ajListstrPushAppend(list, name); } closedir(dp); ajDebug("%d files for '%S' '%S'\n", dirsize, path, wildname); ajStrDelStatic(&fileNameTmp); return ajFilebuffNewListinList(list); } ID fileOpenDir TY static MO ajfile LB core XX DE Runs 'opendir' on the specified directory. If the directory name DE has no trailing slash (on Unix) then one is added. This is why the DE directory name must be writable. XX PN [1] PA u dir AjPStr* PD Directory name. PX RT DIR* RD result of the opendir call. RX // static DIR* fileOpenDir(AjPStr* dir) { AjBool moved = ajFalse; AjPStr cwdpath = NULL; AjPStr userstr = NULL; AjPStr reststr = NULL; char *hdir = NULL; if(ajStrGetCharLast(*dir) != SLASH_CHAR) ajStrAppendC(dir, SLASH_STRING); /* going up */ while(ajStrPrefixC(*dir, UP_DIR)) { if(!moved) cwdpath = ajStrNewS(ajFileValueCwd()); moved = ajTrue; ajDirnameUp(&cwdpath); ajStrKeepRange(dir, 3, -1); ajDebug("Going up '%S' '%S'\n", *dir, fileCwd); } if(moved) ajStrInsertS(dir, 0, cwdpath); if(moved) ajStrDel(&cwdpath); ajStrAssignS(&fileNameTmp, *dir); if(ajStrGetCharFirst(fileNameTmp) == '~') { ajDebug("starts with '~'\n"); if(!fileUserExp) fileUserExp = ajRegCompC("^~([^/\\\\]*)"); ajRegExec(fileUserExp, fileNameTmp); ajRegSubI(fileUserExp, 1, &userstr); ajRegPost(fileUserExp, &reststr); ajDebug(" user: '%S' rest: '%S'\n", userstr, reststr); if(ajStrGetLen(userstr)) { /* username specified */ hdir = ajSysGetHomedirFromName(ajStrGetPtr(userstr)); if(!hdir) { ajStrDel(&userstr); ajStrDelStatic(&fileNameTmp); ajStrDel(&reststr); return NULL; } ajFmtPrintS(&fileNameTmp, "%s%S", hdir, reststr); AJFREE(hdir); ajDebug("use getpwnam: '%S'\n", fileNameTmp); } else { /* just ~/ */ hdir = ajSysGetHomedir(); if(hdir) { ajFmtPrintS(&fileNameTmp, "%s%S", hdir, reststr); AJFREE(hdir); } else ajFmtPrintS(&fileNameTmp,"%S",reststr); ajDebug("use HOME: '%S'\n", fileNameTmp); } ajStrAssignS(dir, fileNameTmp); } ajStrDel(&userstr); ajStrDel(&reststr); ajDebug("fileOpenDir opened '%S'\n", *dir); return opendir(ajStrGetPtr(*dir)); } ID ajFilebuffDel TY public MO ajfile LB core XX DE Destructor for a buffered file object. XX PN [1] PA d Pbuff AjPFilebuff* PD Buffered file object. PX RT void RD RX // void ajFilebuffDel(AjPFilebuff* Pbuff) { AjPFilebuff thys; if(!Pbuff) return; thys = *Pbuff; if(!thys) return; if(thys->File) ajDebug("ajFilebuffDel '%F' fp: %p size:%u\n", thys->File, thys->File->fp, thys->Size); ajFilebuffClear(thys, -1); filebuffFreeClear(thys); ajFileClose(&thys->File); AJFREE(*Pbuff); return; } ID ajFilebuffReopenFile TY public MO ajfile LB core XX DE Sets buffered input file to use a new open file. DE DE The AjPFile pointer is a clone, so we should simply overwrite DE whatever was there before, but we do need to clear the previous buffer DE contents. XX PN [1] PA w Pbuff AjPFilebuff* PD Buffered file object. PX PN [2] PA u file AjPFile PD File object to be buffered. PX RT AjBool RD ajTrue on success RX // AjBool ajFilebuffReopenFile(AjPFilebuff* Pbuff, AjPFile file) { AjPFilebuff thys; if(!file) { ajFatal("used ajFilebuffReopenFile with deleted file buffer"); ajFilebuffDel(Pbuff); return ajFalse; } if(!*Pbuff) { *Pbuff = ajFilebuffNewFromFile(file); thys = *Pbuff; return ajTrue; } thys = *Pbuff; ajFilebuffClear(thys, -1); thys->File = file; return ajTrue; } ID ajFilebuffClear TY public MO ajfile LB core XX DE Deletes processed lines from a file buffer. The buffer has a record DE (Pos) of the next unprocessed line in the buffer. DE DE Unbuffered files need special handling. The buffer can be turned off DE while it still contains data. If so, we have to carefully run it down. DE If this runs it to zero, we may want to save the last line read. XX PN [1] PA u buff AjPFilebuff PD File buffer PX PN [2] PA r lines ajint PD Number of lines to retain. -1 deletes everything. PX RT void RD RX // void ajFilebuffClear(AjPFilebuff buff, ajint lines) { ajint i = 0; AjPFilebufflist list; AjPFilebufflist next; ajint first; ajint ifree = 0; ajDebug("ajFilebuffClear (%d) Nobuff: %B size: %u\n", lines, buff->Nobuff, buff->Size); /*FilebuffTraceFull(buff, buff->Size, 100);*/ if(!buff) return; if(!buff->File) return; if(lines < 0) first = buff->Size; else first = buff->Pos - lines; if(first < 0) first = 0; /* nobuff, and all read */ if(buff->Nobuff && buff->Pos == buff->Size) /* delete any old saved line */ first = buff->Pos; list = buff->Lines; for(i=0; i < first; i++) { /* we save one line at a time */ next = list->Next; /* so keep a note of the next one for later */ /*ajDebug("Try to reuse %x size: %d use: %d\n", list->Line, ajStrGetRes(list->Line), ajStrGetUse(list->Line));*/ if(buff->Nobuff) { ajStrDel(&list->Line); AJFREE(list); /* deleted, kill the list item */ } else { if(ajStrDelStatic(&list->Line)) { /* move free line to the end */ /*ajDebug("can save to free list %x %d bytes\n", list->Line, ajStrGetRes(list->Line));*/ ifree++; /* just save the one line */ list->Next = NULL; if(!buff->Freelines) { /* start a new free list */ buff->Freelines = list; buff->Freelast = list; /*ajDebug("start list Free %x Freelast %x \n", buff->Freelines, buff->Freelast);*/ } else { /* append to free list */ buff->Freelast->Next = list; buff->Freelast = buff->Freelast->Next; /*ajDebug("append list Free %x Freelast %x \n", buff->Freelines, buff->Freelast);*/ } } else { ajDebug("ajStrDelReuse was false\n"); } } list = next; } buff->Pos = 0; buff->Size -= i; /* // if(!buff->Size) // { // ajDebug("size 0: Lines: %x Curr: %x Prev: %x Last: %x Free: " // "%x Freelast: %x\n", // buff->Lines, buff->Curr, buff->Prev, buff->Last, // buff->Freelines, buff->Freelast); // } */ buff->Lines = buff->Curr = list; /* // ajDebug("ajFilebuffClear '%F' (%d lines)\n" // " %b size: %d pos: %d removed %d lines add to free: %d\n", // buff->File, lines, buff->Nobuff, buff->Size, buff->Pos, i, ifree); */ /* ajFilebuffTrace(buff);*/ if(buff->Nobuff && !buff->Size && lines == 1) { /* unbuffered - can only save last line */ if(buff->Lines) ajFatal("Buffer error clearing unbuffered file " "in ajFilebuffClear\n"); buff->Lines = AJNEW0(buff->Last); ajStrAssignS(&buff->Last->Line, buff->File->Buff); buff->Curr = buff->Last; buff->Curr->Fpos = buff->Fpos; buff->Last->Next = NULL; buff->Pos = 0; buff->Size = 1; } return; } ID ajFilebuffClearStore TY public MO ajfile LB core XX DE Deletes processed lines from a file buffer. The buffer has a record DE (Pos) of the next unprocessed line in the buffer. DE DE Unbuffered files need special handling. The buffer can be turned off DE while it still contains data. If so, we have to carefully run it down. DE If this runs it to zero, we may want to save the last line read. XX PN [1] PA u buff AjPFilebuff PD File buffer PX PN [2] PA r lines ajint PD Number of lines to retain. -1 deletes everything. PX PN [3] PA r lastline const AjPStr PD Last line of input. PD Used to count characters to be saved PX PN [4] PA r dostore AjBool PD append if true PX PN [5] PA w Pstore AjPStr* PD Caller's record of the processed lines PX RT void RD RX // void ajFilebuffClearStore(AjPFilebuff buff, ajint lines, const AjPStr lastline, AjBool dostore, AjPStr *Pstore) { ajFilebuffClear(buff, lines); if(dostore && ajStrGetLen(lastline)) ajStrCutEnd(Pstore, ajStrGetLen(lastline)); return; } ID ajFilebuffFix TY public MO ajfile LB core XX DE Resets the pointer and current record of a file buffer so the next DE read starts at the first buffered line. Fixes buffer size after the DE buffer has been edited. XX PN [1] PA u buff AjPFilebuff PD File buffer PX RT void RD RX // void ajFilebuffFix(AjPFilebuff buff) { AjPFilebufflist list; ajint i = 1; ajFilebuffReset(buff); buff->Pos = 0; buff->Curr = buff->Lines; list = buff->Lines; if(!list) { buff->Size = 0; return; } while(list->Next) { i++; list = list->Next; } if(i != buff->Size) ajDebug("ajFilebuffFix size was %d now %d\n", buff->Size, i); buff->Size=i; return; } ID ajFilebuffReset TY public MO ajfile LB core XX DE Resets the pointer and current record of a file buffer so the next read DE starts at the first buffered line. XX PN [1] PA u buff AjPFilebuff PD File buffer PX RT void RD RX // void ajFilebuffReset(AjPFilebuff buff) { ajDebug("ajFilebuffReset '%F' size: %u\n", buff->File, buff->Size); buff->Pos = 0; buff->Curr = buff->Lines; buff->Prev = NULL; return; } ID ajFilebuffResetPos TY public MO ajfile LB core XX DE Resets the pointer and current record of a file buffer so the next read DE starts at the first buffered line. DE DE Also resets the file position to the last known read, to undo the DE damage done by (for example) ajseqabi functions. XX PN [1] PA u buff AjPFilebuff PD File buffer PX RT void RD RX // void ajFilebuffResetPos(AjPFilebuff buff) { ajDebug("ajFilebuffResetPos End: %B Fpos: %ld ftell: %ld\n", buff->File->End, buff->Fpos, ftell(buff->File->fp)); /*ajFilebuffTraceFull(buff, 10, 10);*/ buff->Pos = 0; buff->Curr = buff->Lines; if(!buff->File->End && (buff->File->fp != stdin)) ajFileSeek(buff->File, buff->File->Filepos, SEEK_SET); buff->File->Filepos = buff->Fpos; /*ajFilebuffTraceFull(buff,10,10);*/ return; } ID ajFilebuffResetStore TY public MO ajfile LB core XX DE Resets the pointer and current record of a file buffer so the next read DE starts at the first buffered line. XX PN [1] PA u buff AjPFilebuff PD File buffer PX PN [2] PA r dostore AjBool PD True if text is stored PX PN [3] PA w Pstore AjPStr* PD Stored string cleared if store is true PX RT void RD RX // void ajFilebuffResetStore(AjPFilebuff buff, AjBool dostore, AjPStr *Pstore) { ajFilebuffReset(buff); if(dostore) ajStrAssignClear(Pstore); return; } ID filebuffFreeClear TY static MO ajfile LB core XX DE Deletes freed lines from a file buffer. The free list is used to avoid DE reallocating space for new records and must be deleted as part of DE the destructor. XX PN [1] PA u buff AjPFilebuff PD File buffer PX RT void RD RX // static void filebuffFreeClear(AjPFilebuff buff) { AjPFilebufflist list; if(!buff) return; /*ajDebug("filebuffFreeClear %x\n", buff->Freelines);*/ while(buff->Freelines) { list = buff->Freelines; buff->Freelines = buff->Freelines->Next; ajStrDel(&list->Line); AJFREE(list); } return; } ID ajFilebuffSetBuffered TY public MO ajfile LB core XX DE Sets file to be buffered. If it already has buffered data, we have to DE first run down the buffer. XX PN [1] PA u buff AjPFilebuff PD Buffered file object. PX RT AjBool RD ajTrue if the file was unbuffered before RX // AjBool ajFilebuffSetBuffered(AjPFilebuff buff) { AjBool ret; if(!buff) return ajFalse; ret = buff->Nobuff; ajDebug("ajFilebuffSetBuffered %F buffsize: %d\n", buff->File, buff->Size); buff->Nobuff = ajFalse; return ret; } ID ajFilebuffSetUnbuffered TY public MO ajfile LB core XX DE Sets file to be unbuffered. If it already has buffered data, we have to DE first run down the buffer. XX PN [1] PA u buff AjPFilebuff PD Buffered file object. PX RT AjBool RD ajTrue if the file was unbuffered before RX // AjBool ajFilebuffSetUnbuffered(AjPFilebuff buff) { AjBool ret; if(!buff) return ajFalse; ret = buff->Nobuff; ajDebug("ajFilebuffSetUnbuffered '%F' fsize: %d\n", buff->File, buff->Size); buff->Nobuff = ajTrue; return ret; } ID ajFilebuffHtmlNoheader TY public MO ajfile LB core XX DE Processes data in the file buffer, removing HTML titles and DE decoding possible chunked input XX PN [1] PA u buff AjPFilebuff PD Buffered file with data loaded PD in the buffer. PX RT ajuint RD HTTP error code RX // ajuint ajFilebuffHtmlNoheader(AjPFilebuff buff) { AjPRegexp httpexp = NULL; AjPRegexp nullexp = NULL; AjPRegexp chunkexp = NULL; AjPRegexp hexexp = NULL; AjPRegexp redirexp = NULL; AjBool doChunk = ajFalse; ajint ichunk; ajint chunkSize; ajint iline; AjPStr saveLine = NULL; AjPStr hexstr = NULL; AjPStr codestr = NULL; AjPStr newurl = NULL; ajuint httpcode = 0; AjBool isheader = ajFalse; /* first take out the HTTP header (HTTP 1.0 onwards) */ if(!buff || !buff->Size) return 0; httpexp = ajRegCompC("^HTTP/\\S+\\s+(\\d+)"); nullexp = ajRegCompC("^\r?\n?$"); /*ajFilebuffTraceTitle(buff, "Before ajFilebuffHtmlNoheader");*/ ajDebug("ajFilebuffHtmlNoheader: First line [%d] '%S' \n", ajStrGetUse(buff->Curr->Line), buff->Curr->Line); if(ajRegExec(httpexp, buff->Curr->Line)) { isheader = ajTrue; ajRegSubI(httpexp, 1, &codestr); ajStrToUint(codestr, &httpcode); ajStrDel(&codestr); fileBuffLineDel(buff); } if(isheader) { /* * ajHttpRedirect() function should be called * before calling this function to follow redirections * rather than using http redirection support here */ if(httpcode >= 301 && httpcode <= 302) { redirexp = ajRegCompC("^Location: (\\S+)"); while(buff->Pos < buff->Size && !ajRegExec(nullexp, buff->Curr->Line)) { /* to empty line */ if(ajRegExec(redirexp, buff->Curr->Line)) { ajRegSubI(redirexp, 1, &newurl); ajRegFree(&httpexp); ajRegFree(&nullexp); ajRegFree(&redirexp); ajFilebuffClear(buff, -1); ajFileClose(&buff->File); ajFilebuffLoadReadurl(buff, newurl); ajStrDel(&newurl); return ajFilebuffHtmlNoheader(buff); } fileBuffLineDel(buff); } /* blank line after header */ fileBuffLineDel(buff); ajRegFree(&redirexp); } else { /* HTTP header processing */ if(httpcode >= 300) { ajWarn("HTTP code %u", httpcode); ajRegFree(&httpexp); ajRegFree(&nullexp); return httpcode; } chunkexp = ajRegCompC("^Transfer-Encoding: +chunked"); while(buff->Pos < buff->Size && !ajRegExec(nullexp, buff->Curr->Line)) { if(ajRegExec(chunkexp, buff->Curr->Line)) { ajDebug("Chunk encoding: %S", buff->Curr->Line); /* chunked - see later */ doChunk = ajTrue; } fileBuffLineDel(buff); } /* blank line after header */ fileBuffLineDel(buff); ajRegFree(&chunkexp); } } if(doChunk) { /*ajFilebuffTraceFull(buff, 999999, 0);*/ hexexp = ajRegCompC("^([0-9a-fA-F]+) *\r?\n?$"); if(!ajRegExec(hexexp, buff->Curr->Line)) { ajFatal("Bad chunk data from HTTP, expect chunk size got '%S'", buff->Curr->Line); } ajRegSubI(hexexp, 1, &hexstr); ajStrToHex(hexstr, &chunkSize); ajDebug("chunkSize hex:%x %d\n", chunkSize, chunkSize); fileBuffLineDel(buff); /* chunk size */ ichunk = 0; iline = 0; while(chunkSize && buff->Curr) { iline++; /* get the chunk size - zero is the end */ /* process the chunk */ ichunk += ajStrGetLen(buff->Curr->Line); /*ajDebug("++input line [%d] ichunk=%d:%d %d:%S", iline, ichunk, chunkSize, ajStrGetLen(buff->Curr->Line), buff->Curr->Line);*/ if(ichunk >= chunkSize) /* end of chunk */ { if(ichunk == chunkSize) { /* end-of-chunk at end-of-line */ fileBuffLineNext(buff); ajStrAssignClear(&saveLine); /*ajDebug("end-of-chunk at end-of-line: '%S'\n", saveLine);*/ } else { /* end-of-chunk in mid-line, patch up the input */ ajDebug("end-of-chunk in mid-line, %d:%d have input size:" " %d\n", ichunk, chunkSize, ajStrGetLen(buff->Curr->Line)); ajStrAssignSubS(&saveLine, buff->Curr->Line, 0, -(ichunk-chunkSize+1)); ajStrKeepRange(&buff->Curr->Line, -(ichunk-chunkSize), -1); } /* skip a blank line */ if(!ajRegExec(nullexp, buff->Curr->Line)) { ajFilebuffTraceTitle(buff, "Blank line not found"); ajFatal("Bad chunk data from HTTP, expect blank line" " got '%S'", buff->Curr->Line); } fileBuffLineDel(buff); /** read the next chunk size */ if(!ajRegExec(hexexp, buff->Curr->Line)) { ajFilebuffTraceTitle(buff, "Chunk size not found"); ajFatal("Bad chunk data from HTTP, expect chunk size " "got '%S'", buff->Curr->Line); } ajRegSubI(hexexp, 1, &hexstr); ajStrToHex(hexstr, &chunkSize); ichunk = 0; fileBuffLineDel(buff); } if(saveLine) { if(ajStrGetLen(saveLine)) { ichunk = ajStrGetLen(buff->Curr->Line); ajDebug("curr-line/chunk: '%S'\n", buff->Curr->Line); /* preserve the line split by chunk size */ ajStrInsertS(&buff->Curr->Line, 0, saveLine); if(ichunk < chunkSize) { /* process the next line */ fileBuffLineNext(buff); /* after restored line */ } else { /* we already have the whole chunk! */ ichunk -= ajStrGetLen(buff->Curr->Line); } } else { /* just a chunk size, skip */ if(buff->Curr && chunkSize) { /*fileBuffLineDel(buff);*/ } else if (chunkSize)/* final non-zero chunk size */ { fileBuffLineDel(buff); } } ajStrDel(&saveLine); } else { /* next line */ fileBuffLineNext(buff); } } ajFilebuffFix(buff); /*ajFilebuffTraceFull(buff, 999999, 0);*/ ajStrDel(&hexstr); /*ajFilebuffTraceTitle(buff, "Chunks resolved");*/ } ajFilebuffReset(buff); ajRegFree(&httpexp); ajRegFree(&nullexp); ajRegFree(&hexexp); return 0; } ID ajFilebuffHtmlPre TY public MO ajfile LB core XX DE If we only have one pre-formatted section in HTML, that is all we keep. XX PN [1] PA u buff AjPFilebuff PD buffer PX RT AjBool RD ajTrue=cleaned ajFalse=unchanged RX // AjBool ajFilebuffHtmlPre(AjPFilebuff buff) { AjPFilebufflist lptr = NULL; AjPFilebufflist tptr = NULL; AjPRegexp preexp = NULL; AjPRegexp endexp = NULL; ajint ifound = 0; lptr = buff->Curr; preexp = ajRegCompC("<[Pp][Rr][Ee]>"); lptr=buff->Curr; ajDebug("ajFilebuffHtmlPre testing for
 line(s)\n");

    while(lptr)
    {
        if(ajRegExec(preexp, lptr->Line))
            ifound++;

        lptr = lptr->Next;
    }

    if(!ifound)
    {
        ajRegFree(&preexp);

        return ajFalse;
    }

    if (ifound > 1)
    {
        ajRegFree(&preexp);

        return ajFalse;
    }

    lptr=buff->Curr;


    while(lptr && !ajRegExec(preexp, lptr->Line))
    {
        tptr = lptr;
        lptr = lptr->Next;
        ajStrDel(&tptr->Line);
        AJFREE(tptr);
        buff->Size--;
    }

    buff->Lines = buff->Curr = lptr;
    ajRegPost(preexp, &lptr->Line);
    ajRegFree(&preexp);

    endexp = ajRegCompC("");

    while(lptr && !ajRegExec(endexp,lptr->Line))
    {
        lptr    = lptr->Next;
    }

    ajRegPre(endexp, &lptr->Line);
    buff->Last = lptr;
    lptr = lptr->Next;
    ajRegFree(&endexp);

    while(lptr)
    {
        tptr = lptr;
        lptr = lptr->Next;
        ajStrDel(&tptr->Line);
        AJFREE(tptr);
        buff->Size--;
    }

    buff->Last->Next = NULL;
    ajFilebuffReset(buff);
    ajFilebuffTraceTitle(buff, "ajFilebuffHtmlPre completed");

    return ajTrue;
}
ID ajFilebuffHtmlStrip
TY public
MO ajfile
LB core
XX
DE Processes data in the file buffer, removing HTML tokens between
DE angle brackets, plus any TITLE. This seems to be enough to make HTML
DE output readable.
XX
PN [1]
PA u buff AjPFilebuff
PD Buffered file with data loaded
PD in the buffer.
PX
RT void
RD 
RX
//


void ajFilebuffHtmlStrip(AjPFilebuff buff)
{
    AjPRegexp tagexp   = NULL;
    AjPRegexp fullexp  = NULL;
    AjPRegexp httpexp  = NULL;
    AjPRegexp nullexp  = NULL;
    AjPRegexp chunkexp = NULL;
    AjPRegexp hexexp   = NULL;
    AjPRegexp ncbiexp  = NULL;
    AjPRegexp ncbiexp2 = NULL;
    AjPRegexp srsdbexp = NULL;

    AjPStr s1 = NULL;
    AjPStr s2 = NULL;
    AjPStr s3 = NULL;
    ajint i;
    AjBool doChunk = ajFalse;
    ajint ichunk;
    ajint chunkSize;
    ajint iline;
    AjPStr saveLine = NULL;
    AjPStr hexstr   = NULL;

    /* first take out the HTTP header (HTTP 1.0 onwards) */
    if(!buff->Size)
        return;

    tagexp   = ajRegCompC("^(.*)(<[!/A-Za-z][^>]*>)(.*)$");
    fullexp  = ajRegCompC("^(.*)(<([A-Za-z]+)[^>]*>.*)(.*)$");
    httpexp  = ajRegCompC("^HTTP/");
    nullexp  = ajRegCompC("^\r?\n?$");
    chunkexp = ajRegCompC("^Transfer-Encoding: +chunked");
    hexexp   = ajRegCompC("^([0-9a-fA-F]+) *\r?\n?$");
    ncbiexp  = ajRegCompC("^Entrez Reports\r?\n$");
    ncbiexp2 = ajRegCompC("^----------------\r?\n$");
    srsdbexp = ajRegCompC("^([A-Za-z0-9_-]+)(:)([A-Za-z0-9_-]+)");

/*    ajFilebuffTraceTitle(buff, "Before ajFilebuffHtmlStrip");*/

    i = 0;

    /* ajDebug("First line [%d] '%S' \n",
       ajStrGetUse(buff->Curr->Line), buff->Curr->Line);*/

    if(ajRegExec(httpexp, buff->Curr->Line))
    {
        /* ^HTTP  header processing */
        while(buff->Pos < buff->Size &&
              !ajRegExec(nullexp, buff->Curr->Line))
        {
            /* to empty line */
            if(ajRegExec(chunkexp, buff->Curr->Line))
            {
                /*ajDebug("Chunk encoding: %S", buff->Curr->Line);*/
                /* chunked - see later */
                doChunk = ajTrue;
            }
            fileBuffLineDel(buff);
        }

        /* blank line after header */
        fileBuffLineDel(buff);
    }

    if(doChunk)
    {
        /*ajFilebuffTraceFull(buff, 999999, 0);*/

        if(!ajRegExec(hexexp, buff->Curr->Line))
        {
            ajFatal("Bad chunk data from HTTP, expect chunk size got '%S'",
                    buff->Curr->Line);
        }

        ajRegSubI(hexexp, 1, &hexstr);
        ajStrToHex(hexstr, &chunkSize);

        /*ajDebug("chunkSize hex:%x %d\n", chunkSize, chunkSize);*/
        fileBuffLineDel(buff);  /* chunk size */

        ichunk = 0;
        iline = 0;

        while(chunkSize && buff->Curr)
        {
            iline++;
            /* get the chunk size - zero is the end */
            /* process the chunk */
            ichunk += ajStrGetLen(buff->Curr->Line);

            /*ajDebug("++input line [%d] ichunk=%d:%d %d:%S",
              iline, ichunk, chunkSize,
              ajStrGetLen(buff->Curr->Line), buff->Curr->Line);*/

            if(ichunk >= chunkSize)     /* end of chunk */
            {
                if(ichunk == chunkSize)
                {
                    /* end-of-chunk at end-of-line */
                    fileBuffLineNext(buff);
                    ajStrAssignClear(&saveLine);
                    /*ajDebug("end-of-chunk at end-of-line: '%S'\n",
                      saveLine); */
                }
                else
                {
                    /* end-of-chunk in mid-line, patch up the input */
                    /*ajDebug("end-of-chunk in mid-line, %d:%d have input: "
                      "%d '%S'\n",
                      ichunk, chunkSize,
                      ajStrGetLen(buff->Curr->Line),
                      buff->Curr->Line);*/
                    ajStrAssignSubS(&saveLine, buff->Curr->Line, 0,
                                    -(ichunk-chunkSize+1));
                    ajStrKeepRange(&buff->Curr->Line, -(ichunk-chunkSize), -1);
                }

                /* skip a blank line */

                if(!ajRegExec(nullexp, buff->Curr->Line))
                {
                    ajFilebuffTraceTitle(buff, "Blank line not found");
                    ajFatal("Bad chunk data from HTTP, expect blank line"
                            " got '%S'", buff->Curr->Line);
                }

                fileBuffLineDel(buff);

                /** read the next chunk size */

                if(!ajRegExec(hexexp, buff->Curr->Line))
                {
                    ajFilebuffTraceTitle(buff, "Chunk size not found");
                    ajFatal("Bad chunk data from HTTP, expect chunk size "
                            "got '%S'",
                            buff->Curr->Line);
                }

                ajRegSubI(hexexp, 1, &hexstr);
                ajStrToHex(hexstr, &chunkSize);
                ichunk = 0;
                fileBuffLineDel(buff);
            }

            if(saveLine)
            {
                if(ajStrGetLen(saveLine))
                {
                    ichunk = ajStrGetLen(buff->Curr->Line);
                    /* preserve the line split by chunk size */
                    ajStrInsertS(&buff->Curr->Line, 0, saveLine);

                    if(ichunk < chunkSize)
                    {
                        /* process the next line */
                        fileBuffLineNext(buff); /* after restored line */
                    }
                    else
                    {
                        /* we already have the whole chunk! */
                        ichunk -= ajStrGetLen(buff->Curr->Line);
                    }
                }
                else
                {
                    /* just a chunk size, skip */
                    if(buff->Curr && chunkSize)
                    {
                        /*fileBuffLineDel(buff);*/
                    }
                    else if (chunkSize)/* final non-zero chunk size */
                    {
                        fileBuffLineDel(buff);
                    }
                }

                ajStrDel(&saveLine);
            }
            else
            {
                /* next line */
                fileBuffLineNext(buff);
            }
        }

        ajFilebuffFix(buff);
        /*ajFilebuffTraceFull(buff, 999999, 0);*/
        ajStrDel(&hexstr);
        /*ajFilebuffTraceTitle(buff, "Chunks resolved");*/
    }

    ajFilebuffReset(buff);

    /*
    ** Now we have a clean single file to process
    */

    /*ajFilebuffTraceTitle(buff, "About to preprocess");*/
    ajFilebuffHtmlPre(buff);

    while(buff->Curr)
    {
        if(ajRegExec(ncbiexp, buff->Curr->Line))
            ajStrAssignC(&buff->Curr->Line, "\n");

        if(ajRegExec(ncbiexp2, buff->Curr->Line))
            ajStrAssignC(&buff->Curr->Line, "\n");

        while(ajRegExec(fullexp, buff->Curr->Line))
        {
            ajRegSubI(fullexp, 1, &s1);
            ajRegSubI(fullexp, 2, &s2);
            ajRegSubI(fullexp, 4, &s3);
            ajFmtPrintS(&buff->Curr->Line, "%S%S", s1, s3);
        }

        while(ajRegExec(tagexp, buff->Curr->Line))
        {
            ajRegSubI(tagexp, 1, &s1);
            ajRegSubI(tagexp, 2, &s2);
            ajRegSubI(tagexp, 3, &s3);
            ajFmtPrintS(&buff->Curr->Line, "%S%S", s1, s3);
        }

        if(ajRegExec(srsdbexp, buff->Curr->Line))
        {
            ajRegSubI(srsdbexp,1,&s1);
            ajRegSubI(srsdbexp,2,&s2);
            ajRegSubI(srsdbexp,3,&s3);
            fileBuffLineDel(buff);
            ++i;
            continue;
        }

        if(ajRegExec(nullexp, buff->Curr->Line))
        {
            /* allow for newline */
            fileBuffLineDel(buff);
        }
        else
            fileBuffLineNext(buff);

        i++;
    }

    ajFilebuffReset(buff);

    ajStrDel(&s1);
    ajStrDel(&s2);
    ajStrDel(&s3);

    /* free the regular expressions - we expect to use them once only */

    ajRegFree(&tagexp);
    ajRegFree(&fullexp);
    ajRegFree(&httpexp);
    ajRegFree(&nullexp);
    ajRegFree(&chunkexp);
    ajRegFree(&hexexp);
    ajRegFree(&ncbiexp);
    ajRegFree(&ncbiexp2);
    ajRegFree(&srsdbexp);

    /*ajFilebuffTraceTitle(buff, "After ajFilebuffHtmlStrip");*/

    return;
}
ID ajFilebuffLoadC
TY public
MO ajfile
LB core
XX
DE Adds a line to the buffer.
DE
DE Intended for cases where the file data must be pre-processed before
DE being seen by the sequence reading routines. The first case was
DE for stripping HTML tags after reading via HTTP.
XX
PN [1]
PA u buff AjPFilebuff
PD Buffered file.
PX
PN [2]
PA r line const char*
PD Line of input.
PX
RT void
RD 
RX
//


void ajFilebuffLoadC(AjPFilebuff buff, const char* line)
{
    if(!buff->Lines)
        buff->Curr = buff->Lines = AJNEW0(buff->Last);
    else
        buff->Last = AJNEW0(buff->Last->Next);

    ajStrAssignC(&buff->Last->Line,line);
    buff->Last->Next = NULL;
    buff->Size++;

    return;
}
ID ajFilebuffLoadS
TY public
MO ajfile
LB core
XX
DE Adds a copy of a line to the buffer.
DE
DE Intended for cases where the file data must be pre processed before
DE being seen by the sequence reading routines. The first case was
DE for stripping HTML tags after reading via HTTP.
XX
PN [1]
PA u buff AjPFilebuff
PD Buffered file.
PX
PN [2]
PA r line const AjPStr
PD Line of input.
PX
RT void
RD 
RX
//


void ajFilebuffLoadS(AjPFilebuff buff, const AjPStr line)
{
    if(!buff->Lines)
        buff->Curr = buff->Lines = AJNEW0(buff->Last);
    else
        buff->Last = AJNEW0(buff->Last->Next);

    ajStrAssignS(&buff->Last->Line,line);
    buff->Last->Next = NULL;
    buff->Size++;

    return;
}
ID ajFilebuffLoadAll
TY public
MO ajfile
LB core
XX
DE Reads all input lines from a file into the buffer.
DE
DE Intended for cases where the file data must be pre-processed before
DE being seen by the sequence reading routines. The first case was
DE for stripping HTML tags after reading via HTTP.
XX
PN [1]
PA u buff AjPFilebuff
PD Buffered file.
PX
RT void
RD 
RX
//


void ajFilebuffLoadAll(AjPFilebuff buff)
{
    AjPStr rdline = NULL;
    AjBool status = ajTrue;

    while(status)
    {
        status = ajBuffreadLine(buff, &rdline);
        /*ajDebug("read: <%S>\n", rdline);*/
    }

    ajFilebuffReset(buff);
    ajStrDel(&rdline);

    /*ajFilebuffTrace(buff);*/

    return;
}
ID ajFilebuffLoadReadurl
TY public
MO ajfile
LB core
XX
DE Reads from a URL and loads all input lines into the buffer.
XX
PN [1]
PA u buff AjPFilebuff
PD Buffered file.
PX
PN [2]
PA r url const AjPStr
PD URL
PX
RT AjBool
RD True on success
RX
//


AjBool ajFilebuffLoadReadurl(AjPFilebuff buff, const AjPStr url)
{
    AjPStr host      = NULL;
    AjPStr urlget    = NULL;
    AjPStr get       = NULL;
    AjPStr proxyName = NULL;            /* host for proxy access.*/
    AjPStr proxyAuth = NULL;
    AjPStr proxyCred = NULL;
    AjPStr httpver   = NULL;          /* HTTP version 1.0, 1.1, ... */
    ajint iport = 80;
    ajint proxyPort;
    FILE *fp;
    AjOSysTimeout timo;
    AjOSysSocket sock;

    AjPStr dbproxy = ajStrNew();

    ajHttpUrlDeconstruct(url, &iport, &host, &urlget);

    ajHttpGetVersion(NULL, &httpver);

    if(ajHttpGetProxyinfo(dbproxy, &proxyPort, &proxyName,
                          &proxyAuth, &proxyCred))
        ajFmtPrintS(&get, "GET http://%S:%d%S HTTP/%S\n",
                    host, iport, urlget, httpver);
    else
        ajFmtPrintS(&get, "GET %S HTTP/%S\n", urlget, httpver);

    if(ajStrGetLen(proxyName))
        fp = ajHttpOpenProxy(dbproxy, proxyName, proxyPort,
                             proxyAuth, proxyCred,
                             host, iport,
                             get, &sock);
    else
        fp = ajHttpOpen(NULL, host, iport, get, &sock);

    buff->File = ajFileNewFromCfile(fp);

    if(!buff)
    {
        ajDebug("socket buffer attach failed\n");
        ajErr("socket buffer attach failed for '%S'", url);

        return ajFalse;
    }

    ajDebug("Ready to read errno %d msg '%s'\n",
            errno, ajMessGetSysmessageC());

    timo.seconds = 180;
    ajSysTimeoutSet(&timo);

    ajFilebuffLoadAll(buff);

    ajSysTimeoutUnset(&timo);

    return ajTrue;
}
ID ajFilebuffGetFile
TY public
MO ajfile
LB core
XX
DE Returns the file object from a buffered file object.
XX
PN [1]
PA r buff const AjPFilebuff
PD Buffered file.
PX
RT AjPFile
RD File object.
RX
//


AjPFile ajFilebuffGetFile(const AjPFilebuff buff)
{
    if(!buff)
        return NULL;

    return buff->File;
}
ID ajFilebuffGetFileptr
TY public
MO ajfile
LB core
XX
DE Returns the C file pointer for an open buffered file.
XX
PN [1]
PA r buff const AjPFilebuff
PD Buffered file.
PX
RT FILE*
RD C file pointer for the file.
RX
//


FILE* ajFilebuffGetFileptr(const AjPFilebuff buff)
{
    if(!buff)
        return NULL;

    return buff->File->fp;
}
ID ajFilebuffGetFirst
TY public
MO ajfile
LB core
XX
DE Returns the first line of a file buffer
XX
PN [1]
PA r buff const AjPFilebuff
PD File buffer
PX
RT const AjPStr
RD First line
RX
//


const AjPStr ajFilebuffGetFirst(const AjPFilebuff buff)
{
    if(!buff)
        return NULL;

    if(!buff->Lines)
        return NULL;

    return buff->Lines->Line;
}
ID ajFilebuffIsBuffered
TY public
MO ajfile
LB core
XX
DE Tests whether an input file is buffered.
XX
PN [1]
PA r buff const AjPFilebuff
PD Buffered file object.
PX
RT AjBool
RD ajTrue if the file was unbuffered before
RX
//


AjBool ajFilebuffIsBuffered(const AjPFilebuff buff)
{
    if(!buff)
        return ajFalse;

    if(buff->Nobuff)
        return ajFalse;

    return ajTrue;
}
ID ajFilebuffIsEmpty
TY public
MO ajfile
LB core
XX
DE Tests whether a file buffer is empty, including testing for a
DE possible next file
XX
PN [1]
PA r buff const AjPFilebuff
PD Buffered file.
PX
RT AjBool
RD ajTrue if the buffer is empty.
RX
//


AjBool ajFilebuffIsEmpty(const AjPFilebuff buff)
{
    /*ajDebug("ajFilebuffIsEmpty Size: %d Pos: %d End: %b Handle: %d "
      "Fp: %x List; %d\n",
      buff->Size, buff->Pos, buff->File->End, buff->File->Handle,
      buff->File->fp, ajListstrGetLength(buff->File->List));*/

    if(!buff)
        return ajTrue;

    if(!buff->File)
        return ajTrue;

    if(buff->Pos < buff->Size)
        return ajFalse;

    /* not open */
    if(!buff->File->fp || !buff->File->Handle)
        return ajTrue;

    if(buff->File->End && !ajListstrGetLength(buff->File->List))
        /* EOF and done */
        return ajTrue;

    ajDebug("ajFilebuffIsEmpty false\n");

    return ajFalse;
}
ID ajFilebuffIsEnded
TY public
MO ajfile
LB core
XX
DE Tests whether the current file is exhausted.
DE This means end of file is reached and the buffer is empty.
DE
DE It is possible that further files are defined in the input list
DE (see ajFilebuffIsEmpty)
XX
PN [1]
PA r buff const AjPFilebuff
PD File buffer
PX
RT AjBool
RD ajTrue if we already set end-of-file
RX
//


AjBool ajFilebuffIsEnded(const AjPFilebuff buff)
{
    if(!buff)
        return ajTrue;

    ajDebug("ajFilebuffIsEnded End: %B Size: %d\n",
            buff->File->End, buff->Size);

    /* not reached EOF yet */
    if(!buff->File->End)
        return ajFalse;

    /* Something in the buffer*/
    if(buff->Size != 0)
        return ajFalse;

    return ajTrue;
}
ID ajFilebuffIsEof
TY public
MO ajfile
LB core
XX
DE Tests whether we have reached end of file already
XX
PN [1]
PA r buff const AjPFilebuff
PD File buffer
PX
RT AjBool
RD ajTrue if we already set end-of-file
RX
//


AjBool ajFilebuffIsEof(const AjPFilebuff buff)
{
    if(!buff)
        return ajTrue;

    if(!buff->File)
        return ajTrue;

    return buff->File->End;
}
ID ajFilebuffTrace
TY public
MO ajfile
LB core
XX
DE Writes debug messages to indicate the contents of a buffered file.
XX
PN [1]
PA r buff const AjPFilebuff
PD Buffered file.
PX
RT void
RD 
RX
//


void ajFilebuffTrace(const AjPFilebuff buff)
{
    AjPFilebufflist test;
    ajint i = 0;
    ajint j = -1;

    ajDebug("Trace buffer file '%S'\n"
            "             Pos: %d Size: %d FreeSize: %d Fpos: %Ld End: %b\n",
            buff->File->Name, buff->Pos, buff->Size,buff->FreeSize,
            buff->Fpos, buff->File->End);

    if(buff->Size)
    {
        ajDebug(" Lines: %u\n", buff->Size);

        if(buff->Curr)
            ajDebug("  Curr: %8Ld %x %x %u <%S>\n",
                    buff->Curr->Fpos,
                    buff->Curr->Line, buff->Curr->Next,
                    ajStrGetLen(buff->Curr->Line),
                    buff->Curr->Line);
        else
            ajDebug("  Curr: \n");

        if(buff->Lines)
            ajDebug("  From: %8Ld %x %x %u <%S>\n",
                    buff->Lines->Fpos,
                    buff->Lines->Line, buff->Lines->Next,
                    ajStrGetLen(buff->Lines->Line),
                    buff->Lines->Line);
        else
            ajDebug("  From: \n");

        if(buff->Last)
            ajDebug("    To: %8Ld %x %x %u <%S>\n",
                    buff->Last->Fpos,
                    buff->Last->Line, buff->Last->Next,
                    ajStrGetLen(buff->Last->Line),
                    buff->Last->Line);
        else
            ajDebug("    To: \n");
    }

    if(buff->Freelines)
        for(test = buff->Freelines; test; test=test->Next)
        {
            i++;

            if(test == buff->Freelast)
                j = i;
        }

    ajDebug(" Free: %d Last: %d\n", i, j);

    return;
}
ID ajFilebuffTraceFull
TY public
MO ajfile
LB core
XX
DE Writes debug messages to show the full contents of a buffered file.
XX
PN [1]
PA r buff const AjPFilebuff
PD Buffered file.
PX
PN [2]
PA r nlines size_t
PD Maximum number of lines to trace.
PX
PN [3]
PA r nfree size_t
PD Maximum number of free lines to trace.
PX
RT void
RD 
RX
//


void ajFilebuffTraceFull(const AjPFilebuff buff, size_t nlines,
                         size_t nfree)
{
    ajint i;
    AjPFilebufflist line;
    AjBool last = ajFalse;

    ajDebug("Trace buffer file '%S' End: %B\n"
            "             Pos: %d Size: %d Nobuff: %B Fpos: %Ld\n",
            buff->File->Name, buff->File->End,
            buff->Pos, buff->Size, buff->Nobuff, buff->Fpos);

    line = buff->Lines;

    for(i=1; line && (i <= (ajint)nlines); i++)
    {
        if(line == buff->Curr)
            ajDebug("*Line %x %d: %5d %5d <%-20S>\n",
                    line->Line, i,
                    ajStrGetLen(line->Line), strlen(ajStrGetPtr(line->Line)),
                    line->Line);
        else
            ajDebug(" Line %x %d: %5d %5d <%-20S>\n",
                    line->Line, i,
                    ajStrGetLen(line->Line), strlen(ajStrGetPtr(line->Line)),
                    line->Line);
        line = line->Next;
    }

    line = buff->Freelines;

    for(i=1; line && (i <= (ajint)nfree);  i++)
    {
        if(line == buff->Freelast)
            last = ajTrue;

        ajDebug(" Free %x %d: %d bytes %B\n",
                line->Line, i, ajStrGetRes(line->Line), last);
        line = line->Next;
    }

    return;
}
ID ajFilebuffTraceTitle
TY public
MO ajfile
LB core
XX
DE Writes the full contents of a buffered file to the debug file
XX
PN [1]
PA r buff const AjPFilebuff
PD Buffered file.
PX
PN [2]
PA r title const char*
PD Report title
PX
RT void
RD 
RX
//


void ajFilebuffTraceTitle(const AjPFilebuff buff, const char* title)
{
    ajint i;
    AjPFilebufflist line;
    ajint last = 0;

    ajDebug("=== File Buffer: %s ===\n", title);
    line = buff->Lines;

    for(i=1; line; i++)
    {
        ajStrAssignS(&fileTmpStr, line->Line);
        ajStrRemoveLastNewline(&fileTmpStr);

        if(line == buff->Curr)
            ajDebug("* %x %S\n", line->Line, fileTmpStr);
        else
            ajDebug("  %x %S\n", line->Line, fileTmpStr);

        line = line->Next;
    }

    line = buff->Freelines;

    for(i=1; line;  i++)
    {
        if(line == buff->Freelast)
            last = i;

        if(line == buff->Freelast)
            ajDebug("F %x %S\n", line->Line, fileTmpStr);
        else
            ajDebug("f %x %S\n", line->Line, fileTmpStr);

        line = line->Next;
    }

    if (!last)
        last = i;
    ajDebug("=== end of file, free list %d lines ===\n", last);

    return;
}
ID fileBuffLineDel
TY static
MO ajfile
LB core
XX
DE Delete a line from a buffer.
XX
PN [1]
PA u buff AjPFilebuff
PD File buffer
PX
RT void
RD 
RX
//


static void fileBuffLineDel(AjPFilebuff buff)
{
    AjPFilebufflist saveprev;

    if(!buff->Curr)
        return;

    /*ajDebug("fileBuffLineDel removing line [%d%s%d], '%S' len %d\n",
      buff->Pos, SLASH_STRING, buff->Size, buff->Curr->Line,
      ajStrGetLen(buff->Curr->Line));*/

    /* first line */
    if(!buff->Prev)
    {
        buff->Prev = buff->Lines;
        buff->Curr = buff->Lines = buff->Lines->Next;
        ajStrDel(&buff->Prev->Line);
        AJFREE(buff->Prev);
        --buff->Size;

        return;
    }

    /* last line */
    if(!buff->Curr->Next)
    {
        saveprev = buff->Prev;
        buff->Prev = buff->Lines;

        while(buff->Prev && buff->Prev->Next != saveprev)
            buff->Prev = buff->Prev->Next;

        saveprev->Next = NULL;

        ajStrDel(&buff->Curr->Line);
        AJFREE(buff->Curr);
        buff->Curr = NULL;
        buff->Last = saveprev;
        --buff->Size;
        buff->Pos = buff->Size;

        return;
    }

    buff->Prev->Next = buff->Curr->Next;
    ajStrDel(&buff->Curr->Line);
    AJFREE(buff->Curr);
    buff->Curr = buff->Prev->Next;
    --buff->Size;

    return;
}
ID fileBuffLineNext
TY static
MO ajfile
LB core
XX
DE Steps the Curr pointer to the next line in a buffer.
DE
DE Not for use when reading from a file. This steps through the buffer
XX
PN [1]
PA u buff AjPFilebuff
PD File buffer
PX
RT AjBool
RD ajTrue if there was another line
RX
//


static AjBool fileBuffLineNext(AjPFilebuff buff)
{
    if(buff->Pos < buff->Size)
    {
        buff->Prev = buff->Curr;
        buff->Curr = buff->Curr->Next;
        buff->Pos++;

        return ajTrue;
    }

    return ajFalse;
}
ID ajOutfileNewNameS
TY public
MO ajfile
LB core
XX
DE Creates a new formatted output file object with a specified name.
DE
DE 'stdout' and 'stderr' are special names for standard output and
DE standard error respectively.
XX
PN [1]
PA r name const AjPStr
PD File name.
PX
RT AjPOutfile
RD New output file object.
RX
//


AjPOutfile ajOutfileNewNameS(const AjPStr name)
{
    AjPOutfile thys;
    ajlong ipos;
    AjPStr tmpname = NULL;

    AJNEW0(thys);

    ipos = ajStrFindC(name, "::");
    if(ipos > 0)
    {
        ajStrAssignSubS(&thys->Formatstr, name, 0, ipos-1);
        ajStrAssignSubS(&tmpname, name, ipos+2, -1);
        thys->File = ajFileNewOutNameS(tmpname);
        ajStrDel(&tmpname);
    }
    else
    {
        thys->File = ajFileNewOutNameS(name);
    }

    if(!thys->File)
        return NULL;

    return thys;
}
ID ajOutfileClose
TY public
MO ajfile
LB core
XX
DE Close and free an outfile object.
XX
PN [1]
PA d Pfile AjPOutfile*
PD Output file.
PX
RT void
RD 
RX
//


void ajOutfileClose(AjPOutfile* Pfile)
{
    AjPOutfile thys;

    thys = Pfile ? *Pfile : 0;

    if(!Pfile)
        return;

    if(!*Pfile)
        return;

    fileClose(thys->File);
    AJFREE(thys->File);
    ajStrDel(&thys->Type);
    ajStrDel(&thys->Formatstr);
    AJFREE(*Pfile);

    return;
}
ID ajOutfileGetFile
TY public
MO ajfile
LB core
XX
DE Returns the AjPFile for an AjPOutfile object
XX
PN [1]
PA r file const AjPOutfile
PD Outfile object
PX
RT AjPFile
RD AjPFile object
RX
//


AjPFile ajOutfileGetFile (const AjPOutfile file)
{
    return file->File;
}
ID ajOutfileGetFileptr
TY public
MO ajfile
LB core
XX
DE Returns the C FILE* for an AjPOutfile object
XX
PN [1]
PA r file const AjPOutfile
PD Outfile object
PX
RT FILE*
RD C file pointer
RX
//


FILE* ajOutfileGetFileptr (const AjPOutfile file)
{
    return file->File->fp;
}
ID ajOutfileGetFormat
TY public
MO ajfile
LB core
XX
DE Returns the file format name for an AjPOutfile object
XX
PN [1]
PA r file const AjPOutfile
PD Outfile object
PX
RT const AjPStr
RD Format name
RX
//


const AjPStr ajOutfileGetFormat (const AjPOutfile file)
{
    return file->Formatstr;
}
ID ajOutfileGetFormatindex
TY public
MO ajfile
LB core
XX
DE Returns the file format name for an AjPOutfile object
XX
PN [1]
PA r file const AjPOutfile
PD Outfile object
PX
RT ajuint
RD Format name
RX
//


ajuint ajOutfileGetFormatindex (const AjPOutfile file)
{
    return file->Format;
}
ID ajOutfileReset
TY public
MO ajfile
LB core
XX
DE Close and free an outfile object.
XX
PN [1]
PA u outf AjPOutfile
PD Output file.
PX
RT void
RD 
RX
//


void ajOutfileReset(AjPOutfile outf)
{
    if(!outf)
        return;

    if(!outf->Cleanup)
        return;

    outf->Cleanup(outf);

    return;
}
ID ajFilenameExists
TY public
MO ajfile
LB core
XX
DE Returns true if file exists and is read or write or executable by the user
DE as determined by AJ_FILE_R AJ_FILE_W AJ_FILE_X file modes
XX
PN [1]
PA r filename const AjPStr
PD Filename.
PX
RT AjBool
RD ajTrue on success
RX
//


AjBool ajFilenameExists(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

    if(ajStrMatchC(filename, "stdin"))
            return ajTrue;

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(filename), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(filename), &buf)
#endif /* AJ_IRIXLF */
       )
        return ajTrue;

    return ajFalse;
}
ID ajFilenameExistsDir
TY public
MO ajfile
LB core
XX
DE Returns true if file exists and is a directory
XX
PN [1]
PA r filename const AjPStr
PD Filename.
PX
RT AjBool
RD ajTrue on success
RX
//


AjBool ajFilenameExistsDir(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

#ifdef WIN32
    if(!ajStrGetLen(filename))
        return ajTrue;
#endif /* WIN32 */

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(filename), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(filename), &buf)
#endif /* AJ_IRIXLF */
       )
        if((ajuint)buf.st_mode & AJ_FILE_DIR)
            return ajTrue;

    return ajFalse;
}
ID ajFilenameExistsExec
TY public
MO ajfile
LB core
XX
DE Returns true if file exists and is executable by the user
XX
PN [1]
PA r filename const AjPStr
PD Filename.
PX
RT AjBool
RD ajTrue on success
RX
//


AjBool ajFilenameExistsExec(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(filename), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(filename), &buf)
#endif /* AJ_IRIXLF */
       )
        if((ajuint)buf.st_mode & AJ_FILE_X)
            return ajTrue;

    return ajFalse;
}
ID ajFilenameExistsRead
TY public
MO ajfile
LB core
XX
DE Returns true if file exists and is readable by the user
XX
PN [1]
PA r filename const AjPStr
PD Filename.
PX
RT AjBool
RD ajTrue on success
RX
//


AjBool ajFilenameExistsRead(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(filename), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(filename), &buf)
#endif /* AJ_IRIXLF */
       )
        if((ajuint)buf.st_mode & AJ_FILE_R)
            return ajTrue;

    return ajFalse;
}
ID ajFilenameExistsWrite
TY public
MO ajfile
LB core
XX
DE Returns true if file exists and is writable by the user
XX
PN [1]
PA r filename const AjPStr
PD Filename.
PX
RT AjBool
RD ajTrue on success
RX
//


AjBool ajFilenameExistsWrite(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

    if(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(filename), &buf)
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(filename), &buf)
#endif /* AJ_IRIXLF */
       )
        if((ajuint)buf.st_mode & AJ_FILE_W)
            return ajTrue;

    return ajFalse;
}
ID ajFilenameGetSize
TY public
MO ajfile
LB core
XX
DE Returns the length of a file
XX
PN [1]
PA r filename const AjPStr
PD Filename.
PX
RT ajlong
RD length or -1 if file doesn't exist
RX
//


ajlong ajFilenameGetSize(const AjPStr filename)
{
#if defined(AJ_IRIXLF)
    struct stat64 buf;
#else /* !AJ_IRIXLF */
    struct stat buf;
#endif /* AJ_IRIXLF */

#if defined(AJ_IRIXLF)
    if(!stat64(ajStrGetPtr(filename), &buf))
        return (ajlong)buf.st_size;
#else /* !AJ_IRIXLF */
    if(!stat(ajStrGetPtr(filename), &buf))
        return (ajlong)buf.st_size;
#endif /* AJ_IRIXLF */

    return -1;
}
ID ajFilenameHasPath
TY public
MO ajfile
LB core
XX
DE Tests whether a filename includes a directory specification.
XX
PN [1]
PA r filename const AjPStr
PD File name.
PX
RT AjBool
RD ajTrue if directory filename syntax was found
RX
//


AjBool ajFilenameHasPath(const AjPStr filename)
{
    if(ajStrFindC(filename, SLASH_STRING) < 0)
        return ajFalse;

    return ajTrue;
}
ID ajFilenameTestExclude
TY public
MO ajfile
LB core
XX
DE Tests a filename against wildcard
DE lists of file names to be included and excluded.
DE
DE The path (if any) is removed before checking.
DE
DE By default files are excluded. The inclusion list is used to select
DE files, and the exclusion list is then used to exclude selected
DE files again.
XX
PN [1]
PA r filename const AjPStr
PD File to test
PX
PN [2]
PA r exclude const AjPStr
PD List of wildcard names to exclude
PX
PN [3]
PA r include const AjPStr
PD List of wildcard names to include
PX
RT AjBool
RD ajTrue if the filename is accepted.
RX
//


AjBool ajFilenameTestExclude(const AjPStr filename,
                             const AjPStr exclude,
                             const AjPStr include)
{
    AjBool ret = ajFalse;

    AjPStrTok handle = NULL;
    AjPStr token     = NULL;

    ajDebug("ajFilenameTestExclude "
            "filename '%S' exclude '%S' include '%S'\n",
            filename, exclude, include);

    ajStrAssignS(&fileNameTmp, filename);
    ajFilenameTrimPath(&fileNameTmp);

    if(ajStrGetLen(include))
    {
        ajStrTokenAssignC(&handle, include, " \t,;\n");

        while(ajStrTokenNextParse(&handle, &token))
            if(ajStrMatchWildS(filename, token) ||
               ajStrMatchWildS(fileNameTmp, token))
                ret = ajTrue;

        ajStrTokenReset(&handle);
    }

    if(ajStrGetLen(exclude))
    {   /* nokeep, test exclude last */
        ajStrTokenAssignC(&handle, exclude, " \t,;\n");

        while(ajStrTokenNextParse(&handle, &token))
            if(ajStrMatchWildS(filename, token) ||
               ajStrMatchWildS(fileNameTmp, token))
                ret = ajFalse;

        ajStrTokenReset(&handle);
    }

    ajStrTokenDel(&handle);
    ajStrDel(&token);

    return ret;
}
ID ajFilenameTestExcludePath
TY public
MO ajfile
LB core
XX
DE Tests a full path filename against wildcard
DE lists of file names to be included and excluded.
DE
DE The full path is retained and included in the tests.
DE
DE By default files are excluded. The inclusion list is used to select
DE files, and the exclusion list is then used to exclude selected
DE files again.
XX
PN [1]
PA r filename const AjPStr
PD File to test
PX
PN [2]
PA r exclude const AjPStr
PD List of wildcard names to exclude
PX
PN [3]
PA r include const AjPStr
PD List of wildcard names to include
PX
RT AjBool
RD ajTrue if the filename is accepted.
RX
//


AjBool ajFilenameTestExcludePath(const AjPStr filename,
                                 const AjPStr exclude,
                                 const AjPStr include)
{
    AjBool ret = ajFalse;

    AjPStrTok handle = NULL;
    AjPStr token     = NULL;

    ajDebug("ajFilenameTestExcludePath "
            "filename '%S' exclude '%S' include '%S'\n",
            filename, exclude, include);

    if(ajStrGetLen(include))
    {
        ajStrTokenAssignC(&handle, include, " \t,;\n");

        while(ajStrTokenNextParse(&handle, &token))
            if(ajStrMatchWildS(filename, token))
                ret = ajTrue;

        ajStrTokenReset(&handle);
    }

    if(ajStrGetLen(exclude))
    {   /* nokeep, test exclude last */
        ajStrTokenAssignC(&handle, exclude, " \t,;\n");

        while(ajStrTokenNextParse(&handle, &token))
            if(ajStrMatchWildS(filename, token))
                ret = ajFalse;

        ajStrTokenReset(&handle);
    }

    ajStrTokenDel(&handle);
    ajStrDel(&token);

    return ret;
}
ID ajFilenameTestInclude
TY public
MO ajfile
LB core
XX
DE Tests a filename against wildcard
DE lists of file names to be included and excluded.
DE
DE By default files are included. The exclusion list is used to trim
DE out files, and the inclusion list is then used to add selected
DE files again.
XX
PN [1]
PA r filename const AjPStr
PD File to test
PX
PN [2]
PA r exclude const AjPStr
PD List of wildcard names to exclude
PX
PN [3]
PA r include const AjPStr
PD List of wildcard names to include
PX
RT AjBool
RD ajTrue if the filename is accepted.
RX
//


AjBool ajFilenameTestInclude(const AjPStr filename,
                             const AjPStr exclude,
                             const AjPStr include)
{
    AjBool ret = ajTrue;

    AjPStrTok handle = NULL;
    AjPStr token     = NULL;

    ajStrAssignS(&fileNameTmp, filename);
    ajFilenameTrimPath(&fileNameTmp);

    ajDebug("ajFilenameTestInclude "
            "filename '%S' exclude '%S' include '%S'\n",
            filename, exclude, include);

    if(ajStrGetLen(exclude))
    {
        /* keep, so test exclude first */
        ajStrTokenAssignC(&handle, exclude, " \t,;\n");

        while(ajStrTokenNextParse(&handle, &token))
            if(ajStrMatchWildS(filename, token) ||
               ajStrMatchWildS(fileNameTmp, token))
                ret = ajFalse;

        ajStrTokenReset(&handle);
    }

    if(ajStrGetLen(include))
    {
        ajStrTokenAssignC(&handle, include, " \t,;\n");

        while(ajStrTokenNextParse(&handle, &token))
            if(ajStrMatchWildS(filename, token) ||
               ajStrMatchWildS(fileNameTmp, token))
                ret = ajTrue;

        ajStrTokenReset(&handle);
    }

    ajStrTokenDel(&handle);
    ajStrDel(&token);

    return ret;
}
ID ajFilenameTestIncludePath
TY public
MO ajfile
LB core
XX
DE Tests a filename against wildcard
DE lists of file names to be included and excluded.
DE
DE By default files are included. The exclusion list is used to trim
DE out files, and the inclusion list is then used to add selected
DE files again.
XX
PN [1]
PA r filename const AjPStr
PD File to test
PX
PN [2]
PA r exclude const AjPStr
PD List of wildcard names to exclude
PX
PN [3]
PA r include const AjPStr
PD List of wildcard names to include
PX
RT AjBool
RD ajTrue if the filename is accepted.
RX
//


AjBool ajFilenameTestIncludePath(const AjPStr filename,
                                 const AjPStr exclude,
                                 const AjPStr include)
{
    AjBool ret = ajTrue;

    AjPStrTok handle = NULL;
    AjPStr token     = NULL;

    ajDebug("ajFilenameTestIncludePath "
            "filename '%S' exclukde '%S' include '%S\n",
            filename, exclude, include);

    if(ajStrGetLen(exclude))
    {
        /* keep, so test exclude first */
        ajStrTokenAssignC(&handle, exclude, " \t,;\n");

        while(ajStrTokenNextParse(&handle, &token))
            if(ajStrMatchWildS(filename, token))
                ret = ajFalse;

        ajStrTokenReset(&handle);
    }

    if(ajStrGetLen(include))
    {
        ajStrTokenAssignC(&handle, include, " \t,;\n");

        while(ajStrTokenNextParse(&handle, &token))
            if(ajStrMatchWildS(filename, token))
                ret = ajTrue;

        ajStrTokenReset(&handle);
    }

    ajStrTokenDel(&handle);
    ajStrDel(&token);

    return ret;
}
ID ajFilenameReplaceExtC
TY public
MO ajfile
LB core
XX
DE Replaces the extension part of a filename
XX
PN [1]
PA u Pfilename AjPStr*
PD Filename.
PX
PN [2]
PA r txt const char*
PD New file extension
PX
RT AjBool
RD ajTrue if the replacement succeeded.
RX
//


AjBool ajFilenameReplaceExtC(AjPStr* Pfilename, const char* txt)
{
    AjBool doext;
    char *p = NULL;

    doext = ajTrue;
    if(!txt || !*txt)
        doext = ajFalse;


    /*ajDebug("ajFilenameReplaceExtC '%S' '%s'\n", *Pfilename, txt);*/

    ajStrAssignS(&fileTmpStr,*Pfilename);

    /* Skip any directory path */
    p = strrchr(ajStrGetuniquePtr(&fileTmpStr),SLASH_CHAR);

    if (!p)
        p = ajStrGetuniquePtr(&fileTmpStr);

    p = strrchr(p,'.');

    if(p)
    {
        *p='\0';
        fileTmpStr->Len = p - ajStrGetPtr(fileTmpStr);
    }


    if(doext)
    {
        ajStrAppendC(&fileTmpStr,".");
        ajStrAppendC(&fileTmpStr,txt);
    }

    ajStrAssignS(Pfilename,fileTmpStr);

    /*ajDebug("result '%S'\n", *Pfilename);*/

    return ajTrue;
}
ID ajFilenameReplaceExtS
TY public
MO ajfile
LB core
XX
DE Replaces the extension part of a filename
XX
PN [1]
PA u Pfilename AjPStr*
PD Filename.
PX
PN [2]
PA r str const AjPStr
PD New file extension
PX
RT AjBool
RD ajTrue if the replacement succeeded.
RX
//


AjBool ajFilenameReplaceExtS(AjPStr* Pfilename, const AjPStr str)
{
    if(!str)
        return ajFilenameReplaceExtC(Pfilename, NULL);

    return ajFilenameReplaceExtC(Pfilename, ajStrGetPtr(str));
}
ID ajFilenameReplacePathC
TY public
MO ajfile
LB core
XX
DE Sets the directory part of a filename
XX
PN [1]
PA u Pfilename AjPStr*
PD Filename.
PX
PN [2]
PA r txt const char*
PD Directory
PX
RT AjBool
RD ajTrue if the replacement succeeded.
RX
//


AjBool ajFilenameReplacePathC(AjPStr* Pfilename, const char* txt)
{
    AjPStr tmpname = NULL;
    AjPStr tmpdir = NULL;

    if(!txt)
        return ajFalse;

    if(!fileFilenameExp)
#ifndef WIN32
        fileFilenameExp = ajRegCompC("(.*/)?([^/]+)$");
#else /* WIN32 */
    fileFilenameExp = ajRegCompC("(.*[/\\\\])?([^/\\\\]+)$");
#endif /* !WIN32 */

    if(ajRegExec(fileFilenameExp, *Pfilename))
    {
        ajRegSubI(fileFilenameExp, 1, &tmpdir);

        /* we already have a directory */
        if(ajStrGetLen(tmpdir))
        {
            ajStrDel(&tmpdir);
            return ajFalse;
        }

        ajRegSubI(fileFilenameExp, 2, &tmpname);

        if(txt[strlen(txt)-1] == SLASH_CHAR)
            ajFmtPrintS(Pfilename, "%s%S", txt, tmpname);
        else
            ajFmtPrintS(Pfilename, "%s%s%S", txt, SLASH_STRING,tmpname);

        ajStrDel(&tmpname);
    }

    return ajTrue;
}
ID ajFilenameReplacePathS
TY public
MO ajfile
LB core
XX
DE Sets the directory part of a filename
XX
PN [1]
PA u Pfilename AjPStr*
PD Filename.
PX
PN [2]
PA r str const AjPStr
PD New directory
PX
RT AjBool
RD ajTrue if the replacement succeeded.
RX
//


AjBool ajFilenameReplacePathS(AjPStr* Pfilename, const AjPStr str)
{
    if(!ajStrGetLen(str))
        return ajFalse;

    return ajFilenameReplacePathC(Pfilename, ajStrGetPtr(str));
}
ID ajFilenameSetExtC
TY public
MO ajfile
LB core
XX
DE Sets the extension part of a filename
XX
PN [1]
PA u Pfilename AjPStr*
PD Filename.
PX
PN [2]
PA r txt const char*
PD New file extension
PX
RT AjBool
RD ajTrue if the replacement succeeded.
RX
//


AjBool ajFilenameSetExtC(AjPStr* Pfilename, const char* txt)
{
    char *p = NULL;

    if(!txt || !*txt)
        return ajFalse;

    /*ajDebug("ajFilenameSetExtC '%S' '%s'\n", *Pfilename, txt);*/

    ajStrAssignS(&fileTmpStr,*Pfilename);

    /* Skip any directory path */
    p = strrchr(ajStrGetuniquePtr(&fileTmpStr),SLASH_CHAR);

    if (!p)
        p = ajStrGetuniquePtr(&fileTmpStr);

    ajStrAppendC(&fileTmpStr,".");
    ajStrAppendC(&fileTmpStr,txt);

    ajStrAssignS(Pfilename,fileTmpStr);

    /*ajDebug("result '%S'\n", *Pfilename);*/

    return ajTrue;
}
ID ajFilenameSetExtS
TY public
MO ajfile
LB core
XX
DE Sets the extension part of a base filename
XX
PN [1]
PA u Pfilename AjPStr*
PD Filename.
PX
PN [2]
PA r str const AjPStr
PD New file extension
PX
RT AjBool
RD ajTrue if the replacement succeeded.
RX
//


AjBool ajFilenameSetExtS(AjPStr* Pfilename, const AjPStr str)
{
    if(!str)
        return ajFalse;

    return ajFilenameSetExtC(Pfilename, ajStrGetPtr(str));
}
ID ajFilenameSetTempname
TY public
MO ajfile
LB core
XX
DE Returns an available temporary filename that can be opened for writing
DE Filename will be of the form progname-time.randomnumber
DE Tries 5 times to find a new filename. Returns ajFalse if not
DE successful or the file cannot be opened for writing.
DE This function returns only the filename, not a file pointer.
XX
PN [1]
PA w Pfilename AjPStr*
PD Filename of new temporary file
PX
RT AjBool
RD True on success
RX
//


AjBool ajFilenameSetTempname(AjPStr* Pfilename)
{
#if defined(AJ_IRIXLF)
    struct  stat64 buf;
#else /* !AJ_IRIXLF */
    struct  stat buf;
#endif /* AJ_IRIXLF */
    ajint   retry;
    AjBool  ok;
    AjPFile outf;

    if(!fileTempFilename)
        fileTempFilename = ajStrNew();

    ajStrAssignC(&fileDirectory,".");
    ajDirnameFix(&fileDirectory);

    ajFmtPrintS(&fileTempFilename,
                "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                ajRandomNumber());

    retry = 5;
    ok    = ajTrue;

    while(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(fileTempFilename),&buf) && retry
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(fileTempFilename),&buf) && retry
#endif /* AJ_IRIXLF */
          )
    {
        ajFmtPrintS(&fileTempFilename,
                    "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                    ajRandomNumber());
        --retry;
    }

    if(!retry)
    {
        ajDebug("Cannot find a unique filename [last try %S]\n",
                fileTempFilename);
        ok = ajFalse;
    }

    if(!(outf = ajFileNewOutNameS(fileTempFilename)))
    {
        ajDebug("Cannot write to file %S\n",fileTempFilename);
        ok = ajFalse;
    }
    else
    {
        ajFileClose(&outf);
        ajSysFileUnlinkS(fileTempFilename);
    }

    ajStrAssignS(Pfilename,fileTempFilename);

    return ok;
}
ID ajFilenameSetTempnamePathC
TY public
MO ajfile
LB core
XX
DE Returns an available temporary filename that can be opened for writing
DE Filename will be of the form progname-time.randomnumber
DE Tries 5 times to find a new filename. Returns ajFalse if not
DE successful or the file cannot be opened for writing.
DE This function returns only the filename, not a file pointer.
XX
PN [1]
PA w Pfilename AjPStr*
PD Filename of new temporary file
PX
PN [2]
PA r txt const char*
PD Directory path
PX
RT AjBool
RD True on success
RX
//


AjBool ajFilenameSetTempnamePathC(AjPStr* Pfilename, const char* txt)
{
#if defined(AJ_IRIXLF)
    struct  stat64 buf;
#else /* !AJ_IRIXLF */
    struct  stat buf;
#endif /* AJ_IRIXLF */
    ajint   retry;
    AjBool  ok;
    AjPFile outf;

    if(!fileTempFilename)
        fileTempFilename = ajStrNew();

    ajStrAssignC(&fileDirectory,txt);
    ajDirnameFix(&fileDirectory);

    ajFmtPrintS(&fileTempFilename,
                "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                ajRandomNumber());

    retry = 5;
    ok    = ajTrue;

    while(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(fileTempFilename),&buf) && retry
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(fileTempFilename),&buf) && retry
#endif /* AJ_IRIXLF */
          )
    {
        ajFmtPrintS(&fileTempFilename,
                    "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                    ajRandomNumber());
        --retry;
    }

    if(!retry)
    {
        ajDebug("Cannot find a unique filename [last try %S]\n",
                fileTempFilename);
        ok = ajFalse;
    }

    if(!(outf = ajFileNewOutNameS(fileTempFilename)))
    {
        ajDebug("Cannot write to file %S\n",fileTempFilename);
        ok = ajFalse;
    }
    else
    {
        ajFileClose(&outf);
        ajSysFileUnlinkS(fileTempFilename);
    }

    ajStrAssignS(Pfilename,fileTempFilename);

    return ok;
}
ID ajFilenameSetTempnamePathS
TY public
MO ajfile
LB core
XX
DE Returns an available temporary filename that can be opened for writing
DE Filename will be of the form progname-time.randomnumber
DE Tries 5 times to find a new filename. Returns ajFalse if not
DE successful or the file cannot be opened for writing.
DE This function returns only the filename, not a file pointer.
XX
PN [1]
PA w Pfilename AjPStr*
PD Filename of new temporary file
PX
PN [2]
PA r str const AjPStr
PD Directory path
PX
RT AjBool
RD True on success
RX
//


AjBool ajFilenameSetTempnamePathS(AjPStr* Pfilename, const AjPStr str)
{
#if defined(AJ_IRIXLF)
    struct  stat64 buf;
#else /* !AJ_IRIXLF */
    struct  stat buf;
#endif /* AJ_IRIXLF */
    ajint   retry;
    AjBool  ok;
    AjPFile outf;

    if(!fileTempFilename)
        fileTempFilename = ajStrNew();

    ajStrAssignS(&fileDirectory,str);
    ajDirnameFix(&fileDirectory);

    ajFmtPrintS(&fileTempFilename,
                "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                ajRandomNumber());

    retry = 5;
    ok    = ajTrue;

    while(
#if defined(AJ_IRIXLF)
        !stat64(ajStrGetPtr(fileTempFilename),&buf) && retry
#else /* !AJ_IRIXLF */
        !stat(ajStrGetPtr(fileTempFilename),&buf) && retry
#endif /* AJ_IRIXLF */
          )
    {
        ajFmtPrintS(&fileTempFilename,
                    "%S%S-%d.%d",fileDirectory,ajUtilGetProgram(),time(0),
                    ajRandomNumber());
        --retry;
    }

    if(!retry)
    {
        ajDebug("Cannot find a unique filename [last try %S]\n",
                fileTempFilename);
        ok = ajFalse;
    }

    if(!(outf = ajFileNewOutNameS(fileTempFilename)))
    {
        ajDebug("Cannot write to file %S\n",fileTempFilename);
        ok = ajFalse;
    }
    else
    {
        ajFileClose(&outf);
        ajSysFileUnlinkS(fileTempFilename);
    }

    ajStrAssignS(Pfilename,fileTempFilename);

    return ok;
}
ID ajFilenameTrimAll
TY public
MO ajfile
LB core
XX
DE Truncates a filename to a basic file name.
XX
PN [1]
PA uP Pfilename AjPStr*
PD File name
PX
RT AjBool
RD ajTrue on success, and returns a filename.
RD ajFalse on failure, and returns an empty string.
RX
//


AjBool ajFilenameTrimAll(AjPStr* Pfilename)
{
    /* entryname at end */
    if(!fileEntryExp)
        fileEntryExp = ajRegCompC(":([A-Za-z0-9_-]+)$");

    if(ajRegExec(fileEntryExp, *Pfilename))
    {
        ajRegSubI(fileEntryExp, 1, Pfilename);
        return ajTrue;
    }

    /* name.ext */
    if(!fileFileExp)
        fileFileExp = ajRegCompC("([A-Za-z0-9_-]+)[.][A-Za-z0-9_-]+$");

    if(ajRegExec(fileFileExp, *Pfilename))
    {
        ajRegSubI(fileFileExp, 1, Pfilename);
        return ajTrue;
    }

    /* last valid word */
    if(!fileRestExp)
        fileRestExp = ajRegCompC("([A-Za-z0-9_-]+)[^A-Za-z0-9_-]*$");

    if(ajRegExec(fileRestExp, *Pfilename))
    {
        ajRegSubI(fileRestExp, 1, Pfilename);
        return ajTrue;
    }

    ajStrAssignClear(Pfilename);

    return ajFalse;
}
ID ajFilenameTrimExt
TY public
MO ajfile
LB core
XX
DE Trims the extension (if any) from a filename
XX
PN [1]
PA u Pfilename AjPStr*
PD Filename
PX
RT AjBool
RD ajTrue is there was an extension
RX
//


AjBool ajFilenameTrimExt(AjPStr* Pfilename)
{
    ajlong i;
    ajlong len;

    if(!ajStrGetLen(*Pfilename))
        return ajFalse;

    len = ajStrGetLen(*Pfilename);
    i = ajStrFindlastC(*Pfilename, ".");

    if(i < 0)
        return ajFalse;

    ajStrCutEnd(Pfilename, (size_t) (len-i));

    return ajTrue;
}
ID ajFilenameTrimPath
TY public
MO ajfile
LB core
XX
DE Trims the directory path (if any) from a filename
XX
PN [1]
PA u Pfilename AjPStr*
PD Filename
PX
RT AjBool
RD ajTrue is there was a directory
RX
//


AjBool ajFilenameTrimPath(AjPStr* Pfilename)
{
    ajlong i;

    if(!ajStrGetLen(*Pfilename))
        return ajFalse;

    i = ajStrFindlastC(*Pfilename, SLASH_STRING);

    if(i < 0)
        return ajFalse;

    ajStrCutStart(Pfilename, (size_t) (i+1));

    return ajTrue;
}
ID ajFilenameTrimPathExt
TY public
MO ajfile
LB core
XX
DE Trims the directory path (if any) and extension (if any) from a filename.
XX
PN [1]
PA u Pfilename AjPStr*
PD Filename
PX
RT AjBool
RD ajTrue is there was a directory path or extension.
RX
//


AjBool ajFilenameTrimPathExt(AjPStr* Pfilename)
{
    ajlong i;
    ajlong j;
    ajlong len;

    if(!ajStrGetLen(*Pfilename))
        return ajFalse;

    i = ajStrFindlastC(*Pfilename, SLASH_STRING);

    if(i >= 0)
        ajStrCutStart(Pfilename, (size_t) (i+1));

    len = ajStrGetLen(*Pfilename);
    j = ajStrFindlastC(*Pfilename, ".");

    if(j >= 0)
        ajStrCutEnd(Pfilename, (size_t) (len-j));

    if((i < 0) && (j < 0))
        return ajFalse;

    return ajTrue;
}
ID ajDirnameFillPath
TY public
MO ajfile
LB core
XX
DE Checks that a string is a valid directory, and makes sure it has the
DE full path definition.
XX
PN [1]
PA u Pdirname AjPStr*
PD Directory path
PX
RT AjBool
RD true if a valid directory.
RX
//


AjBool ajDirnameFillPath(AjPStr* Pdirname)
{
    DIR* odir;
    AjPStr cwdpath = NULL;

    ajDebug("ajDirnameFillPath '%S'\n", *Pdirname);

    /* appends trailing slash if needed */
    odir = fileOpenDir(Pdirname);

    if(!odir)
        return ajFalse;

    free(odir);

    ajDebug("So far '%S'\n", *Pdirname);

    /* full path already */
    if(*ajStrGetPtr(*Pdirname) == SLASH_CHAR)
        return ajTrue;

    /* current directory */
    if(ajStrMatchC(*Pdirname, CURRENT_DIR))
    {
        ajStrAssignS(Pdirname, ajFileValueCwd());
        ajDebug("Current '%S'\n", *Pdirname);

        return ajTrue;
    }

    /*  going up */
    ajStrAssignS(&cwdpath, ajFileValueCwd());

    while(ajStrPrefixC(*Pdirname, UP_DIR))
    {
        ajDirnameUp(&cwdpath);
        ajStrKeepRange(Pdirname, 3, -1);
        ajDebug("Going up '%S' '%S'\n", *Pdirname, cwdpath);
    }

    ajStrInsertS(Pdirname, 0, cwdpath);

    ajDebug("Full path '%S'\n", *Pdirname);

    ajStrDel(&cwdpath);

    return ajTrue;
}
ID ajDirnameFix
TY public
MO ajfile
LB core
XX
DE If the directory name has no trailing slash (on Unix) then one is
DE added. This is why the directory name must be writable.
XX
PN [1]
PA u Pdirname AjPStr*
PD Directory name.
PX
RT void
RD 
RX
//


void ajDirnameFix(AjPStr* Pdirname)
{
    if(ajStrGetCharLast(*Pdirname) != SLASH_CHAR)
        ajStrAppendC(Pdirname, SLASH_STRING);

    return;
}
ID ajDirnameFixExists
TY public
MO ajfile
LB core
XX
DE Checks that a string is a valid existing directory, and appends a
DE trailing '/' if it is missing.
XX
PN [1]
PA u Pdirname AjPStr*
PD Directory path
PX
RT AjBool
RD true if a valid directory.
RX
//


AjBool ajDirnameFixExists(AjPStr* Pdirname)
{
    DIR* odir;

    odir = fileOpenDir(Pdirname);       /* appends trailing slash if needed */

    if(!odir)
        return ajFalse;

    closedir(odir);

    return ajTrue;
}
ID ajDirnameUp
TY public
MO ajfile
LB core
XX
DE Changes directory name to one level up
XX
PN [1]
PA u Pdirname AjPStr*
PD Directory name.
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajDirnameUp(AjPStr* Pdirname)
{
    AjPStr tmpdir = NULL;
    AjBool modded = ajFalse;

    const char *p;
    const char *q;
    ajint len;

    ajStrAssignS(&tmpdir, *Pdirname);
    len = ajStrGetLen(tmpdir);
    p   = ajStrGetPtr(tmpdir);
    q   = p + len -2;

    if(q > p)
    {
        while(q!=p && *q==SLASH_CHAR)
            --q;

        while(q!=p && *q!=SLASH_CHAR)
            --q;

        if(q-p > 1)
        {
            ajStrAssignSubC(Pdirname,p,0,q-p);
            modded = ajTrue;
        }
    }

    ajStrDel(&tmpdir);

    return modded;
}
ID ajDirnamePrintRecursiveIgnore
TY public
MO ajfile
LB core
XX
DE Recursively scan through a directory, printing directory and file names
XX
PN [1]
PA r path const AjPStr
PD Directory to scan
PX
PN [2]
PA u ignorelist AjPList
PD List of directories to ignore
PX
PN [3]
PA u outfile AjPFile
PD File for "show" results (or NULL)
PX
RT void
RD 
RX
//


void ajDirnamePrintRecursiveIgnore(const AjPStr path,
                                   AjPList ignorelist, AjPFile outfile)
{
    AjPList dirs = NULL;
    AjIList iter = NULL;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr s = NULL;
    AjPStr t = NULL;

    AjPStr tstr = NULL;

    AjBool flag;
    AjPStr tpath = NULL;
    AjPList plist = NULL;

#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    tpath = ajStrNewS(path);

    ajFmtPrintF(outfile,"\n\nDIRECTORY: %S\n\n",path);

    if(!ajDirnameFixExists(&tpath))
    {
        ajStrDel(&tpath);

        return;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajStrDel(&tpath);

        return;
    }


    s = ajStrNew();
    dirs = ajListNew();
    plist = ajListNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        /*      ajStrAppendC(&s,SLASH_STRING);*/
        ajStrAppendC(&s,dp->d_name);

        /* Its a directory */
        if(ajFilenameExistsDir(s))
        {
            /* Ignore selected directories */
            if(ajListGetLength(ignorelist))
            {
                flag = ajFalse;
                iter = ajListIterNewread(ignorelist);

                while(!ajListIterDone(iter))
                {
                    t = ajListIterGet(iter);

                    if(!strcmp(ajStrGetPtr(t),dp->d_name))
                    {
                        flag = ajTrue;
                        break;
                    }
                }

                ajListIterDel(&iter);

                if(flag)
                    continue;
            }

            if(!ajFilenameExistsRead(s) || !ajFilenameExistsExec(s))
                continue;

            t = ajStrNewC(ajStrGetPtr(s));
            ajListPushAppend(dirs,(void *)t);
        }
        else if(ajFilenameExistsRead(s))
        {
            tstr = ajStrNew();
            ajStrAssignC(&tstr,dp->d_name);
            ajListPush(plist, (void *)tstr);


        }
    }

    closedir(indir);

    ajListSort(plist, &ajStrVcmp);
    while(ajListPop(plist,(void **)&tstr))
    {
        ajFmtPrintF(outfile,"  %S\n",tstr);
        ajStrDel(&tstr);
    }

    ajListFree(&plist);

    while(ajListPop(dirs,(void **)&t))
    {
        ajDirnamePrintRecursiveIgnore(t, ignorelist, outfile);
        ajStrDel(&t);
    }

    ajStrDel(&s);
    ajStrDel(&tpath);
    ajListFree(&dirs);


    return;
}
ID ajFilelistAddDirectory
TY public
MO ajfile
LB core
XX
DE Scan through a directory object returning all filenames that are
DE not directories. Uses the file extension and any other attributes
DE of the directory object. An empty string as a file extension accepts
DE only files that have no extension.
XX
PN [1]
PA u list AjPList
PD List for matching entries
PX
PN [2]
PA r dir const AjPDir
PD Directory to scan
PX
RT ajint
RD number of entries in list
RX
//


ajint ajFilelistAddDirectory(AjPList list,
                             const AjPDir dir)
{
    ajulong oldsize;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr f = NULL;
    AjPStr s = NULL;
    AjPStr t = NULL;
    AjPStr e = NULL;

    AjPStr tpath = NULL;
#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    AjBool doprefix = AJFALSE;
    AjBool doextension = AJFALSE;

    if(!dir)
        return 0;

    if(dir->Prefix)
        doprefix = ajTrue;
    if(dir->Extension)
    {
        if(ajStrGetCharFirst(dir->Extension) == '.')
            e = ajStrNewS(dir->Extension);
        else if(ajStrGetLen(dir->Extension))
            ajFmtPrintS(&e, ".%S", dir->Extension);

        doextension = ajTrue;
    }

    oldsize = ajListGetLength(list);

    tpath = ajStrNewS(dir->Name);

    ajDebug("ajFilelistAddDir '%S' oldsize:%Lu\n",
            tpath, oldsize);

    if(!ajDirnameFixExists(&tpath))
    {
        ajDebug("... not a directory '%S'\n", tpath);
        ajStrDel(&tpath);

        return 0;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajDebug("... failed to open directory '%S'\n", tpath);
        ajStrDel(&tpath);

        return 0;
    }


    s = ajStrNew();
    f = ajStrNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */

        ajStrAssignC(&f,dp->d_name);

        if(doprefix && !ajStrPrefixS(f, dir->Prefix))
            continue;

        if(doextension)
        {
            if(e)
            {
                if(!ajStrSuffixS(f, e))
                    continue;
            }
            else
            {
                if(ajStrFindAnyK(f,'.') >= 0)
                    continue;
            }
        }

        ajStrAssignS(&s,tpath);
        ajStrAppendS(&s,f);

        if(ajFilenameExistsDir(s))
            continue;

        ajDebug("... add to list '%S'\n",
                s);

        t = ajStrNewS(s);
        ajListPushAppend(list,(void *)t);

    }
    closedir(indir);


    ajStrDel(&e);
    ajStrDel(&f);
    ajStrDel(&s);
    ajStrDel(&tpath);

    return (ajuint) (ajListGetLength(list) - oldsize);
}
ID ajFilelistAddListname
TY public
MO ajfile
LB core
XX
DE Adds to a list of files that match a comma-separated string of
DE filenames which can include wildcards or listfiles
XX
PN [1]
PA u list AjPList
PD List of filenames
PX
PN [2]
PA r listname const AjPStr
PD comma-separated filename list
PX
RT ajint
RD Number of entries added to list
RX
//


ajint ajFilelistAddListname(AjPList list, const AjPStr listname)
{
    ajulong oldsize;
    AjPStr *fstr = NULL;
    ajint  ncl;
    ajint  i;
    ajint  rlevel = 0;

    oldsize = ajListGetLength(list);
    ncl = ajArrCommaList(listname,&fstr);

    for(i=0;id_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        ajStrAppendC(&s,dp->d_name);

        ajDebug("... testing '%S'\n",
                s);

        if(ajFilenameExistsDir(s))
        {
            ajDebug("... rejected: directory '%S'\n",
                    s);
            continue;
        }

        ajDebug("... add to list '%S'\n",
                s);

        t = ajStrNewS(s);
        ajListPushAppend(list,(void *)t);

    }

    closedir(indir);


    ajStrDel(&s);
    ajStrDel(&tpath);

    return (ajuint) (ajListGetLength(list) - oldsize);
}
ID ajFilelistAddPathDir
TY public
MO ajfile
LB core
XX
DE Scan through a directory returning all filenames and directory names
DE except '.' and '..', including directories
XX
PN [1]
PA u list AjPList
PD List for matching entries
PX
PN [2]
PA r path const AjPStr
PD Directory to scan
PX
RT ajint
RD number of entries in list
RX
//


ajint ajFilelistAddPathDir(AjPList list,
                           const AjPStr path)
{
    ajulong oldsize;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr s = NULL;
    AjPStr t = NULL;
    AjPStr tpath = NULL;
#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    oldsize = ajListGetLength(list);

    tpath = ajStrNewS(path);

    ajDebug("ajFilelistAddPath '%S' oldsize:%Lu\n",
            path, oldsize);

    if(!ajDirnameFixExists(&tpath))
    {
        ajDebug("... not a directory '%S'\n", tpath);
        ajStrDel(&tpath);

        return 0;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajDebug("... failed to open directory '%S'\n", tpath);
        ajStrDel(&tpath);

        return 0;
    }


    s = ajStrNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        ajStrAppendC(&s,dp->d_name);

        ajDebug("... add to list '%S'\n",
                s);

        t = ajStrNewS(s);
        ajListPushAppend(list,(void *)t);

    }
    closedir(indir);


    ajStrDel(&s);
    ajStrDel(&tpath);

    return (ajuint) (ajListGetLength(list) - oldsize);
}
ID ajFilelistAddPathWild
TY public
MO ajfile
LB core
XX
DE Scan through a directory returning all filenames matching a
DE wildcard filename
XX
PN [1]
PA w list AjPList
PD List for matching entries
PX
PN [2]
PA r path const AjPStr
PD Directory to scan
PX
PN [3]
PA r wildname const AjPStr
PD Filename to search for
PX
RT ajint
RD number of entries in list
RX
//


ajint ajFilelistAddPathWild(AjPList list,
                            const AjPStr path,
                            const AjPStr wildname)
{
    ajulong oldsize;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr s = NULL;
    AjPStr t = NULL;
    AjPStr tpath = NULL;
#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    oldsize = ajListGetLength(list);

    tpath = ajStrNew();
    ajStrAssignS(&tpath,path);


    if(!ajDirnameFixExists(&tpath))
    {
        ajStrDel(&tpath);

        return 0;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajStrDel(&tpath);

        return 0;
    }


    s = ajStrNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        ajStrAppendC(&s,dp->d_name);

        if(ajFilenameExistsDir(s))
            continue;

        if(ajCharMatchWildS(dp->d_name,wildname))
        {
            t = ajStrNewS(s);
            ajListPushAppend(list,(void *)t);
        }
    }
    closedir(indir);


    ajStrDel(&s);
    ajStrDel(&tpath);

    return (ajuint) (ajListGetLength(list) - oldsize);
}
ID ajFilelistAddPathWildDir
TY public
MO ajfile
LB core
XX
DE Scan through a directory returning all filenames matching a
DE wildcard filename, including directories
XX
PN [1]
PA w list AjPList
PD List for matching entries
PX
PN [2]
PA r path const AjPStr
PD Directory to scan
PX
PN [3]
PA r wildname const AjPStr
PD Filename to search for
PX
RT ajint
RD number of entries in list
RX
//


ajint ajFilelistAddPathWildDir(AjPList list,
                               const AjPStr path,
                               const AjPStr wildname)
{
    ajulong oldsize;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr s = NULL;
    AjPStr t = NULL;
    AjPStr tpath = NULL;
#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    oldsize = ajListGetLength(list);

    tpath = ajStrNew();
    ajStrAssignS(&tpath,path);


    if(!ajDirnameFixExists(&tpath))
    {
        ajStrDel(&tpath);

        return 0;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajStrDel(&tpath);

        return 0;
    }


    s = ajStrNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        ajStrAppendC(&s,dp->d_name);

        if(ajCharMatchWildS(dp->d_name,wildname))
        {
            t = ajStrNewS(s);
            ajListPushAppend(list,(void *)t);
        }
    }

    closedir(indir);


    ajStrDel(&s);
    ajStrDel(&tpath);

    return (ajuint) (ajListGetLength(list) - oldsize);
}
ID fileListRecurs
TY static
MO ajfile
LB core
XX
DE Add a filename, expanded wildcard filenames and list file contents to
DE a list
XX
PN [1]
PA r srcfile const AjPStr
PD filename, wild filename or list filename
PX
PN [2]
PA u list AjPList
PD result filename list
PX
PN [3]
PA u recurs ajint*
PD recursion level counter
PX
RT void
RD 
RX
//


static void fileListRecurs(const AjPStr srcfile, AjPList list, ajint *recurs)
{
    char c;
    AjPStr ptr = NULL;
    AjPStr dir = NULL;
    char   *p;
    AjPList dlist;
    AjPFile inf;
    AjPStr  line = NULL;
    AjPStr file  = NULL;

    ++(*recurs);

    if(*recurs > FILERECURSLV)
        ajFatal("Filelist maximum recursion level reached");

    ajStrAssignS(&file, srcfile);
    ajStrTrimWhite(&file);
    c = *ajStrGetPtr(file);

    dir   = ajStrNew();
    line  = ajStrNew();
    dlist = ajListNew();


    if(ajStrIsWild(file))
    {
        if(!(p=strrchr(ajStrGetPtr(file),(int)SLASH_CHAR)))
            ajStrAssignC(&dir,CURRENT_DIR);
        else
            ajStrAssignSubC(&dir,ajStrGetPtr(file),0,p-ajStrGetPtr(file));

        ajFilelistAddPathWild(dlist, dir, file);

        while(ajListPop(dlist,(void **)&ptr))
        {
            if(ajStrPrefixC(ptr,CURRENT_DIR))
                ajStrCutStart(&ptr,2);

            ajListPushAppend(list,(void *)ptr);
        }
    }
    else if(c=='@')
    {
        if((inf=ajFileNewInNameC(ajStrGetPtr(file)+1)))
            while(ajReadlineTrim(inf,&line))
                fileListRecurs(line,list,recurs);
        if(inf)
            ajFileClose(&inf);
    }
    else if(ajStrPrefixC(file,"list::"))
    {
        if((inf=ajFileNewInNameC(ajStrGetPtr(file)+6)))
            while(ajReadlineTrim(inf,&line))
                fileListRecurs(line,list,recurs);
        if(inf)
            ajFileClose(&inf);
    }
    else
    {
        ptr = ajStrNewC(ajStrGetPtr(file));
        ajListPushAppend(list,(void *)ptr);
    }


    ajListFree(&dlist);
    ajStrDel(&dir);
    ajStrDel(&line);
    ajStrDel(&file);

    --(*recurs);

    return;
}
ID ajFilelistAddPathWildRecursiveIgnore
TY public
MO ajfile
LB core
XX
DE Recursively scan through a directory
XX
PN [1]
PA u list AjPList
PD List for matching filenames
PX
PN [2]
PA r path const AjPStr
PD Directory to scan
PX
PN [3]
PA r wildname const AjPStr
PD Filename to search for (or NULL)
PX
PN [4]
PA u ignorelist AjPList
PD List of directories to ignore
PX
RT ajint
RD number of new entries in list
RX
//


ajint ajFilelistAddPathWildRecursiveIgnore(AjPList list,
                                           const AjPStr path,
                                           const AjPStr wildname,
                                           AjPList ignorelist)
{
    ajulong oldsize;
    AjPList dirs = NULL;
    AjIList iter = NULL;
    DIR *indir;
#if defined(AJ_IRIXLF)
    struct dirent64 *dp;
#else /* !AJ_IRIXLF */
    struct dirent *dp;
#endif /* AJ_IRIXLF */
    AjPStr s = NULL;
    AjPStr t = NULL;
    AjBool flag;
    AjPStr tpath = NULL;
#ifdef _POSIX_C_SOURCE
    char buf[sizeof(struct dirent)+MAXNAMLEN];
#endif /* _POSIX_C_SOURCE */

    oldsize = ajListGetLength(list);

    tpath = ajStrNew();
    ajStrAssignS(&tpath,path);

    if(!ajDirnameFixExists(&tpath))
    {
        ajStrDel(&tpath);

        return 0;
    }


    if(!(indir=opendir(ajStrGetPtr(tpath))))
    {
        ajStrDel(&tpath);

        return 0;
    }


    s = ajStrNew();
    dirs = ajListNew();

    while(
#if defined(AJ_IRIXLF)
#ifdef _POSIX_C_SOURCE
        !readdir64_r(indir,(struct dirent64 *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir64(indir))
#endif /* _POSIX_C_SOURCE */
#else /* !AJ_IRIXLF */
#ifdef _POSIX_C_SOURCE
        !readdir_r(indir,(struct dirent *)buf,&dp)
#else /* !_POSIX_C_SOURCE */
        (dp=readdir(indir))
#endif /* _POSIX_C_SOURCE */
#endif /* AJ_IRIXLF */
          )
    {
#ifdef _POSIX_C_SOURCE
        if(!dp)
            break;
#endif /* _POSIX_C_SOURCE */
#ifndef __CYGWIN__
        if(!dp->d_ino ||
           !strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#else /* __CYGWIN__ */
        if(!strcmp(dp->d_name,".") ||
           !strcmp(dp->d_name,".."))
            continue;
#endif /* !__CYGWIN__ */
        ajStrAssignS(&s,tpath);
        /*      ajStrAppendC(&s,SLASH_STRING);*/
        ajStrAppendC(&s,dp->d_name);

        /* Its a directory */
        if(ajFilenameExistsDir(s))
        {
            /* Ignore selected directories */
            if(ajListGetLength(ignorelist))
            {
                flag = ajFalse;
                iter = ajListIterNewread(ignorelist);

                while(!ajListIterDone(iter))
                {
                    t = ajListIterGet(iter);

                    if(!strcmp(ajStrGetPtr(t),dp->d_name))
                    {
                        flag = ajTrue;
                        break;
                    }
                }

                ajListIterDel(&iter);

                if(flag)
                    continue;
            }

            if(!ajFilenameExistsRead(s) || !ajFilenameExistsExec(s))
                continue;

            t = ajStrNewC(ajStrGetPtr(s));
            ajListPushAppend(dirs,(void *)t);
        }
        else if(ajFilenameExistsRead(s))
        {
            if(ajStrGetLen(wildname))
                if(ajCharMatchWildS(dp->d_name,wildname))
                {
                    t = ajStrNewS(s);
                    ajListPushAppend(list,(void *)t);
                }

            /*ajDebug("  %s\n",dp->d_name);*/
        }
    }

    closedir(indir);

    while(ajListPop(dirs,(void **)&t))
    {
        ajFilelistAddPathWildRecursiveIgnore(list, t,wildname, ignorelist);
        ajStrDel(&t);
    }

    ajStrDel(&s);
    ajStrDel(&tpath);
    ajListFree(&dirs);

    return (ajuint) (ajListGetLength(list) - oldsize);
}
ID ajFileValueBuffsize
TY public
MO ajfile
LB core
XX
DE Returns the default buffer size for a file
XX
RT ajuint
RD Buffer size default value
RX
//


ajuint ajFileValueBuffsize(void)
{
    return fileBuffSize;
}
ID ajFileValueCwd
TY public
MO ajfile
LB core
XX
DE Returns the current directory
XX
RT const AjPStr
RD Directory name.
RX
//


const AjPStr ajFileValueCwd(void)
{
    char cwd[PATH_MAX+1];

    if(!getcwd(cwd,PATH_MAX))
    {
        ajStrAssignClear(&fileCwd);

        return fileCwd;
    }


    ajStrAssignC(&fileCwd, cwd);

    if(!ajStrSuffixC(fileCwd, SLASH_STRING))
        ajStrAppendC(&fileCwd, SLASH_STRING);

    return fileCwd;
}
ID ajFileValueRedirectStderr
TY public
MO ajfile
LB core
XX
DE Tests whether stderr is in use by an internal file
XX
RT AjBool
RD ajTrue if the file matches stderr.
RX
//


AjBool ajFileValueRedirectStderr(void)
{
    return fileUsedStderr;
}
ID ajFileValueRedirectStdin
TY public
MO ajfile
LB core
XX
DE Tests whether stdin is in use by an internal file
XX
RT AjBool
RD ajTrue if the file matches stdin.
RX
//


AjBool ajFileValueRedirectStdin(void)
{
    return fileUsedStdin;
}
ID ajFileValueRedirectStdout
TY public
MO ajfile
LB core
XX
DE Tests whether stdout is in use by an internal file
XX
RT AjBool
RD ajTrue if the file matches stdout.
RX
//


AjBool ajFileValueRedirectStdout(void)
{
    return fileUsedStdout;
}
ID filePrintname
TY static
MO ajfile
LB core
XX
DE Create a printable version of a filename
XX
PN [1]
PA r name const AjPStr
PD File name
PX
PN [2]
PA w Pprintname AjPStr*
PD Printable filename
PX
RT void
RD 
RX
//


static void filePrintname(const AjPStr name, AjPStr* Pprintname)
{

    ajStrAssignS(Pprintname, name);
#ifdef WIN32
    ajStrExchangeKK(Pprintname, '/', '\\');
    if(ajStrPrefixC(*Pprintname, ".\\"))
        ajStrCutStart(Pprintname, 2);
#else /* !WIN32 */
    ajStrExchangeCC(Pprintname, "//", "/");
#endif /* WIN32 */

    return;
}
ID ajDatafileNewInNameC
TY public
MO ajfiledata
LB core
XX
DE Returns an allocated data file pointer (AjPFile) if file exists
DE a) in .   b) in ./.embossdata c) ~/ d) ~/.embossdata e) $DATA
XX
PN [1]
PA r name const char*
PD Filename.
PX
RT AjPFile
RD file pointer
RX
//


AjPFile ajDatafileNewInNameC(const char *name)
{
    AjPFile fnew = NULL;

    ajStrAssignC(&filedataTmpstr, name);
    fnew = ajDatafileNewInNameS(filedataTmpstr);
    ajStrDelStatic(&filedataTmpstr);

    return fnew;
}
ID ajDatafileNewInNameS
TY public
MO ajfiledata
LB core
XX
DE Returns an allocated data file pointer (AjPFile) if file exists
DE a) in .   b) in ./.embossdata c) ~/ d) ~/.embossdata e) $DATA
XX
PN [1]
PA r name const AjPStr
PD Filename
PX
RT AjPFile
RD file pointer
RX
//


AjPFile ajDatafileNewInNameS(const AjPStr name)
{
    AjPFile fnew = NULL;
    char *p;
    
    if(!ajStrGetLen(name))
	return NULL;
    
    ajStrAssignS(&filedataBaseTmp, name);
    ajDebug("ajDatafileNewInNameS trying '%S'\n", filedataBaseTmp);

    if(ajFilenameExistsRead(filedataBaseTmp))
    {
	fnew = ajFileNewInNameS(filedataBaseTmp);
	ajStrDelStatic(&filedataBaseTmp);

	return fnew;
    }
    
    ajStrAssignC(&filedataNameDataTmp, ".embossdata");
    ajStrAppendC(&filedataNameDataTmp, SLASH_STRING);
    ajStrAppendS(&filedataNameDataTmp, filedataBaseTmp);
    ajDebug("ajDatafileNewInNameS trying '%S'\n", filedataNameDataTmp);

    if(ajFilenameExistsRead(filedataNameDataTmp))
    {
	fnew = ajFileNewInNameS(filedataNameDataTmp);
	ajStrDelStatic(&filedataBaseTmp);
	ajStrDelStatic(&filedataNameDataTmp);

	return fnew;
    }
    
    
    if((p=getenv("HOME")))
    {
	ajStrAssignC(&filedataHomeTmp,p);
	ajStrAppendC(&filedataHomeTmp,SLASH_STRING);
	ajStrAppendS(&filedataHomeTmp,filedataBaseTmp);
	ajDebug("ajDatafileNewInNameS trying '%S'\n", filedataHomeTmp);

	if(ajFilenameExistsRead(filedataHomeTmp))
	{
	    fnew = ajFileNewInNameS(filedataHomeTmp);
	    ajStrDelStatic(&filedataHomeTmp);
	    ajStrDelStatic(&filedataBaseTmp);
	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}

	ajStrAssignC(&filedataHomeTmp,p);
	ajStrAppendC(&filedataHomeTmp, "/.embossdata");
	ajStrAppendC(&filedataHomeTmp, SLASH_STRING);
	ajStrAppendS(&filedataHomeTmp,filedataBaseTmp);
	ajDebug("ajDatafileNewInNameS trying '%S'\n", filedataHomeTmp);

	if(ajFilenameExistsRead(filedataHomeTmp))
	{
	    fnew = ajFileNewInNameS(filedataHomeTmp);
	    ajStrDelStatic(&filedataHomeTmp);
	    ajStrDelStatic(&filedataBaseTmp);
	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}

	ajStrDelStatic(&filedataHomeTmp);
    }
    
    if(ajNamGetValueC("DATA", &filedataNameDataTmp))
    {
        ajDirnameFix(&filedataNameDataTmp);
	ajStrAppendS(&filedataNameDataTmp,filedataBaseTmp);
	ajDebug("ajDatafileNewInNameS trying '%S'\n", filedataNameDataTmp);

	if(ajFilenameExistsRead(filedataNameDataTmp))
	{
	    fnew = ajFileNewInNameS(filedataNameDataTmp);
	    ajStrDelStatic(&filedataBaseTmp);
	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}
    }
    
    /* just EMBOSS/data under installation */
    ajStrAssignS(&filedataNameDataTmp, ajNamValueInstalldir());

    if(ajStrGetLen(filedataNameDataTmp))
    {
	ajDirnameFix(&filedataNameDataTmp);
	ajStrAppendC(&filedataNameDataTmp,"share/");
	ajStrAppendS(&filedataNameDataTmp,ajNamValuePackage());
	ajStrAppendC(&filedataNameDataTmp,"/data/");
	ajStrAppendS(&filedataNameDataTmp,filedataBaseTmp);
	ajDebug("ajDatafileNewInNameS trying '%S'\n", filedataNameDataTmp);

	if(ajFilenameExistsRead(filedataNameDataTmp))
	{
	    fnew = ajFileNewInNameS(filedataNameDataTmp);
	    ajStrDelStatic(&filedataBaseTmp);
	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}
    }
    
    /* just emboss/data under source */
    ajStrAssignS(&filedataNameDataTmp, ajNamValueRootdir());

    if(ajStrGetLen(filedataNameDataTmp))
    {
	ajStrAppendC(&filedataNameDataTmp,"/data/");
	ajStrAppendS(&filedataNameDataTmp,filedataBaseTmp);
	ajDebug("ajDatafileNewInNameS trying '%S'\n", filedataNameDataTmp);

	if(ajFilenameExistsRead(filedataNameDataTmp))
	{
	    fnew = ajFileNewInNameS(filedataNameDataTmp);
	    ajStrDelStatic(&filedataBaseTmp);
	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}
    }
    
    ajStrDelStatic(&filedataBaseTmp);
    ajStrDelStatic(&filedataNameDataTmp);
    
    ajDebug("ajDatafileNewInNameS failed to find '%S'\n", filedataNameDataTmp);
    
    return NULL;
}
ID ajDatafileNewInNamePathS
TY public
MO ajfiledata
LB core
XX
DE Returns an allocated AjFileInNew pointer (AjPFile) if file exists
DE in the EMBOSS/data/(dir) directory, or is found in the usual directories
DE by ajDatafileNewInNameS
XX
PN [1]
PA r name const AjPStr
PD Filename in AjStr.
PX
PN [2]
PA r path const AjPStr
PD Data directory name in AjStr.
PX
RT AjPFile
RD file pointer
RX
//


AjPFile ajDatafileNewInNamePathS(const AjPStr name, const AjPStr path)
{
    AjPFile fnew = NULL;
    char *p;

    if(!ajStrGetLen(name))
	return NULL;
    if(ajStrGetLen(path))
    {
        ajStrAppendS(&filedataNameDataTmp,path);
        ajDirnameFix(&filedataNameDataTmp);
        ajStrAppendS(&filedataBaseTmp, name);
    }
    else
    {
        ajStrAssignS(&filedataBaseTmp, name);
    }
    
    ajDebug("ajDatafileNewInNamePathS trying '%S'\n", filedataBaseTmp);

    if(ajFilenameExistsRead(filedataBaseTmp))
    {
	fnew = ajFileNewInNameS(filedataBaseTmp);
	ajStrDelStatic(&filedataBaseTmp);

	return fnew;
    }
    
    ajStrAssignC(&filedataNameDataTmp, ".embossdata");
    ajStrAppendC(&filedataNameDataTmp, SLASH_STRING);
    if(ajStrGetLen(path))
    {
        ajStrAppendS(&filedataNameDataTmp,path);
        ajDirnameFix(&filedataNameDataTmp);
    }
    ajStrAppendS(&filedataNameDataTmp, filedataBaseTmp);
    ajDebug("ajDatafileNewInNamePathS trying '%S'\n", filedataNameDataTmp);

    if(ajFilenameExistsRead(filedataNameDataTmp))
    {
	fnew = ajFileNewInNameS(filedataNameDataTmp);
	ajStrDelStatic(&filedataBaseTmp);
	ajStrDelStatic(&filedataNameDataTmp);

	return fnew;
    }
    
    
    if((p=getenv("HOME")))
    {
	ajStrAssignC(&filedataHomeTmp,p);
	ajStrAppendC(&filedataHomeTmp,SLASH_STRING);
        if(ajStrGetLen(path))
        {
            ajStrAppendS(&filedataNameDataTmp,path);
            ajDirnameFix(&filedataNameDataTmp);
        }
	ajStrAppendS(&filedataHomeTmp,filedataBaseTmp);
	ajDebug("ajDatafileNewInNamePathS trying '%S'\n", filedataHomeTmp);

	if(ajFilenameExistsRead(filedataHomeTmp))
	{
	    fnew = ajFileNewInNameS(filedataHomeTmp);
	    ajStrDelStatic(&filedataHomeTmp);
	    ajStrDelStatic(&filedataBaseTmp);
	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}

	ajStrAssignC(&filedataHomeTmp,p);
	ajStrAppendC(&filedataHomeTmp, "/.embossdata");
	ajStrAppendC(&filedataHomeTmp, SLASH_STRING);
        if(ajStrGetLen(path))
        {
            ajStrAppendS(&filedataNameDataTmp,path);
            ajDirnameFix(&filedataNameDataTmp);
        }
	ajStrAppendS(&filedataHomeTmp,filedataBaseTmp);
	ajDebug("ajDatafileNewInNamePathS trying '%S'\n", filedataHomeTmp);

	if(ajFilenameExistsRead(filedataHomeTmp))
	{
	    fnew = ajFileNewInNameS(filedataHomeTmp);
	    ajStrDelStatic(&filedataHomeTmp);
	    ajStrDelStatic(&filedataBaseTmp);
	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}

	ajStrDelStatic(&filedataHomeTmp);
    }
    
    if(ajNamGetValueC("DATA", &filedataNameDataTmp))
    {
        ajDirnameFix(&filedataNameDataTmp);

	if(ajStrGetLen(path))
	{
	    ajStrAppendS(&filedataNameDataTmp,path);
	    ajDirnameFix(&filedataNameDataTmp);
	}

	ajStrAppendS(&filedataNameDataTmp,name);
	ajDebug("ajDatafileNewInNamePathS trying '%S'\n", filedataNameDataTmp);

	if(ajFilenameExistsRead(filedataNameDataTmp))
	{
	    fnew = ajFileNewInNameS(filedataNameDataTmp);
	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}
    }
    
    /* just EMBOSS/data under installation */
    ajStrAssignS(&filedataNameDataTmp, ajNamValueInstalldir());

    if(ajStrGetLen(filedataNameDataTmp))
    {
	/* just EMBOSS */
	ajDirnameFix(&filedataNameDataTmp);
	ajStrAppendC(&filedataNameDataTmp,"share/");
	ajStrAppendS(&filedataNameDataTmp,ajNamValuePackage());
	ajStrAppendC(&filedataNameDataTmp,"/data/");

	if(ajStrGetLen(path))
	{
	    ajStrAppendS(&filedataNameDataTmp,path);
	    ajDirnameFix(&filedataNameDataTmp);
	}

	ajStrAppendS(&filedataNameDataTmp,name);
	ajDebug("ajDatafileNewInNamePathS trying '%S'\n", filedataNameDataTmp);

	if(ajFilenameExistsRead(filedataNameDataTmp))
	{
	    fnew = ajFileNewInNameS(filedataNameDataTmp);
	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}
    }

    /* just emboss/data under source */
    ajStrAssignS(&filedataNameDataTmp, ajNamValueRootdir());

    if(ajStrGetLen(filedataNameDataTmp))
    {
	ajStrAppendC(&filedataNameDataTmp,"/data/");

	if(ajStrGetLen(path))
	{
	    ajStrAppendS(&filedataNameDataTmp,path);
	    ajDirnameFix(&filedataNameDataTmp);
	}

	ajStrAppendS(&filedataNameDataTmp,name);
	ajDebug("ajDatafileNewInNamePathS trying '%S'\n", filedataNameDataTmp);

	if(ajFilenameExistsRead(filedataNameDataTmp))
	{
	    fnew = ajFileNewInNameS(filedataNameDataTmp);
	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}
    }
    
    ajStrDelStatic(&filedataNameDataTmp);
    
    return ajDatafileNewInNameS(name);
    
}
ID ajDatafileNewOutNameS
TY public
MO ajfiledata
LB core
XX
DE Returns an allocated output data file pointer (AjPFile) to a file in the
DE emboss_DATA area
XX
PN [1]
PA r name const AjPStr
PD Filename in AjStr.
PX
RT AjPFile
RD file pointer.
RX
//


AjPFile ajDatafileNewOutNameS(const AjPStr name)
{
    AjPFile fnew = NULL;

    if(!ajStrGetLen(name))
        return NULL;

    if(ajNamGetValueC("DATA", &filedataNameDataTmp))
    {
	/* also does ajDirnameFix */
	if(!ajDirnameFixExists(&filedataNameDataTmp))
	{
	    ajFatal("%S_DATA directory not found: %S\n",
		    ajNamValuePackage(), filedataNameDataTmp);
	}

	ajStrAppendS(&filedataNameDataTmp,name);

	if(!(fnew = ajFileNewOutNameS(filedataNameDataTmp)))
	    ajFatal("Cannot write to file %S\n",filedataNameDataTmp);

	ajStrDelStatic(&filedataNameDataTmp);

	return fnew;
    }
    
    /* just emboss/data under installation */
    ajStrAssignS(&filedataNameDataTmp, ajNamValueInstalldir());

    if(ajStrGetLen(filedataNameDataTmp))
    {
	/* just EMBOSS */
	ajDirnameFix(&filedataNameDataTmp);
	ajStrAppendC(&filedataNameDataTmp,"share/");
	ajStrAppendS(&filedataNameDataTmp,ajNamValuePackage());
	ajStrAppendC(&filedataNameDataTmp,"/data/");

	/* if we are installed, else see below */
	if(ajDirnameFixExists(&filedataNameDataTmp))
	{
	    ajStrAppendS(&filedataNameDataTmp,name);

	    if(!(fnew = ajFileNewOutNameS(filedataNameDataTmp)))
		ajFatal("Cannot write to file %S\n",filedataNameDataTmp);

	    ajStrDelStatic(&filedataNameDataTmp);

	    return fnew;
	}
    }
    
    /* just emboss/data under source */
    ajStrAssignS(&filedataNameDataTmp, ajNamValueRootdir());

    if(ajStrGetLen(filedataNameDataTmp))
    {
	ajStrAppendC(&filedataNameDataTmp,"/data/");

	if(!ajDirnameFixExists(&filedataNameDataTmp))
	    ajFatal("Not installed, and source data directory not found: %S\n",
		    filedataNameDataTmp);

	ajStrAppendS(&filedataNameDataTmp,name);

	if(!(fnew = ajFileNewOutNameS(filedataNameDataTmp)))
	    ajFatal("Cannot write to file %S\n",filedataNameDataTmp);

	ajStrDelStatic(&filedataNameDataTmp);

	return fnew;
    }


    ajFatal("No install or source data directory, and %S_DATA not defined\n",
	    ajNamValuePackage());
    ajStrDelStatic(&filedataNameDataTmp);
    
    return NULL;
}
ID ajDatafileValuePath
TY public
MO ajfiledata
LB core
XX
DE Get the path to the active data directory
XX
RT const AjPStr
RD path.
RX
//


const AjPStr ajDatafileValuePath(void)
{
    if(filedataPath)
	return filedataPath;

    if(ajNamGetValueC("DATA", &filedataPath))
    {
	ajDirnameFix(&filedataPath);

	return filedataPath;
    }

#ifndef WIN32
    ajStrAssignS(&filedataPath,ajNamValueInstalldir()); 

    if(ajStrGetLen(filedataPath))
    {
	ajDirnameFix(&filedataPath);
	ajFmtPrintS(&filedataTmpstr,"%Sshare%c%S%cdata%c",
                    filedataPath,SLASH_CHAR,ajNamValuePackage(),
		    SLASH_CHAR,SLASH_CHAR);
	ajStrAssignS(&filedataPath,filedataTmpstr);

	if(ajDirnameFixExists(&filedataPath))
	    return filedataPath;
    }

    ajStrAssignS(&filedataPath, ajNamValueRootdir());

    if(ajStrGetLen(filedataPath))
    {
	ajStrAppendC(&filedataPath,SLASH_STRING);
	ajStrAppendC(&filedataPath,"data");
	ajStrAppendC(&filedataPath,SLASH_STRING);

	return filedataPath;
    }	

#endif

    return NULL;
}
ID ajDatafileExit
TY public
MO ajfiledata
LB core
XX
DE Prints a summary of file usage with debug calls
XX
RT void
RD 
RX
//


void ajDatafileExit(void)
{
    ajStrDel(&filedataBaseTmp);
    ajStrDel(&filedataHomeTmp);
    ajStrDel(&filedataNameDataTmp);
    ajStrDel(&filedataTmpstr);
    ajStrDel(&filedataPath);

    return;
}
ID ajReadline
TY public
MO ajfileio
LB core
XX
DE Read a line from a file.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pdest AjPStr*
PD Buffer to hold the current line.
PX
RT AjBool
RD ajTrue on success.
RX
CA modify
CT AjPFile
CD Reads a record from a file
CX
//


AjBool ajReadline(AjPFile file, AjPStr* Pdest)
{
    ajlong fpos = 0;

    return ajReadlinePos(file, Pdest, &fpos);
}
ID ajReadlineAppend
TY public
MO ajfileio
LB core
XX
DE Reads a record from a file and appends it to the user supplied buffer.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA u Pdest AjPStr*
PD Buffer to hold results.
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajReadlineAppend(AjPFile file, AjPStr* Pdest)
{
    static AjPStr locbuff = 0;
    AjBool ok;

    if(!locbuff)
	locbuff = ajStrNewRes(512);

    ok = ajReadline(file, &locbuff);

    if(ok)
	ajStrAppendS(Pdest, locbuff);

    ajStrDel(&locbuff);

    return ok;
}
ID ajReadlinePos
TY public
MO ajfileio
LB core
XX
DE Reads a line from a file.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pdest AjPStr*
PD Buffer to hold the current line.
PX
PN [3]
PA w Ppos ajlong*
PD File position before the read.
PX
RT AjBool
RD ajTrue on success.
RX
CA modify
CT AjPFile
CD Reads a record from a file
CX
//


AjBool ajReadlinePos(AjPFile file, AjPStr* Pdest, ajlong* Ppos)
{
    const char *cp;
    char *buff;
    ajint isize;
    ajint ilen;
    ajint jlen;
    ajint ipos;
    ajuint buffsize;
    const char* pnewline = NULL;
#ifndef __ppc__
    size_t iread;
#endif
    
    MAJSTRDEL(Pdest);

    if(file->Buffsize)
        buffsize = file->Buffsize;
    else
        buffsize = ajFileValueBuffsize();

    if(!file->Buff)
      ajStrAssignResC(&file->Buff, buffsize, "");
    else if(buffsize > MAJSTRGETRES(file->Buff))
      ajStrSetRes(&file->Buff, buffsize);

    if(MAJSTRGETUSE(file->Buff) == 1)
      buff = MAJSTRGETPTR(file->Buff);
    else
      buff  = ajStrGetuniquePtr(&file->Buff);

    isize = MAJSTRGETRES(file->Buff);
    ilen  = 0;
    ipos  = 0;
    
    if(!file->fp)
	ajWarn("ajReadlinePos file not found");
    
    *Ppos = file->Filepos;

    while(buff)
    {
	if(file->End)
	{
	    ajStrAssignClear(Pdest);
	    ajDebug("at EOF: File already read to end %F\n", file);

	    return ajFalse;
	}
	

#ifndef __ppc__
        if(file->Readblock)
        {
            if(file->Blockpos >= file->Blocklen)
            {
                iread = fread(file->Readblock,
                              1, file->Blocksize,
                              file->fp);

                if(!iread && ferror(file->fp))
                    ajFatal("fread failed with error:%d '%s'",
                            ferror(file->fp), strerror(ferror(file->fp)));

                file->Blockpos = 0;
                file->Blocklen = iread;
                file->Readblock[iread] = '\0';
                /*ajDebug("++ fread %u Ppos:%Ld\n", iread, *Ppos);*/
             }

            if(file->Blockpos < file->Blocklen)
            {

                /* we know we have something in Readblock to process */

                pnewline = strchr(&file->Readblock[file->Blockpos], '\n');

                if(pnewline)
                    jlen = pnewline - &file->Readblock[file->Blockpos] + 1;
                else
                    jlen = file->Blocklen - file->Blockpos;

                /*ajDebug("ipos:%d jlen:%d pnewline:%p "
                          "Readblock:%p blockpos:%d blocklen:%d\n",
                          ipos, jlen, pnewline, file->Readblock,
                          file->Blockpos, file->Blocklen);*/
                memmove(&buff[ipos], &file->Readblock[file->Blockpos], jlen);
                buff[ipos+jlen]='\0';
                cp = &buff[ipos];
                file->Blockpos += jlen;
            }
            else
            {
                jlen = 0;
                cp = NULL;
            }
        }
        else
        {
            cp = fgets(&buff[ipos], isize, file->fp);
            jlen = strlen(&buff[ipos]);
        }
        
#else
	cp = ajSysFuncFgets(&buff[ipos], isize, file->fp);
	jlen = strlen(&buff[ipos]);
#endif

        if(!cp && !ipos)
	{
	    if(feof(file->fp))
	    {
		file->End = ajTrue;
		ajStrAssignClear(Pdest);
		ajDebug("EOF ajFileGetsL file %F\n", file);

		return ajFalse;
	    }
	    else
            {
		ajFatal("Error reading from file '%S' ferror:%d %d: %s\n",
                        ajFileGetNameS(file),
                        ferror(file->fp),
                        errno, strerror(errno));
            }
	}

	ilen += jlen;
        file->Filepos += jlen;

	/*
	 ** We need to read again if:
	 ** We have read the entire buffer
	 ** and we don't have a newline at the end
	 ** (must be careful about that - we may just have read enough)
	 */

	if(((file->Readblock && !pnewline) ||(jlen == (isize-1))) &&
	   (buff[ilen-1] != '\n'))
	{
            MAJSTRSETVALIDLEN(&file->Buff, ilen); /* fix before resizing! */
	    ajStrSetResRound(&file->Buff, ilen+buffsize+1);
	    /*ajDebug("more to do: jlen: %d ipos: %d isize: %d ilen: %d "
		    "Size: %d\n",
		    jlen, ipos, isize, ilen, ajStrGetRes(file->Buff));*/
	    ipos += jlen;
	    buff = ajStrGetuniquePtr(&file->Buff);
	    isize = ajStrGetRes(file->Buff) - ipos;
	    /*ajDebug("expand to: ipos: %d isize: %d Size: %d\n",
              ipos, isize, ajStrGetRes(file>Buff));*/
	}
	else
	{
            buff = NULL;
        }
    }
    
    MAJSTRSETVALIDLEN(&file->Buff, ilen);
    if (ajStrGetCharLast(file->Buff) != '\n')
    {
	/*ajDebug("Appending missing newline to '%S'\n", file->Buff);*/
	ajStrAppendK(&file->Buff, '\n');
    }
    ajStrAssignRef(Pdest, file->Buff);

/*
  if(file->Readblock)
        ajDebug("ajFileGetsL done blocklen:%d blockpos:%d readlen:%u\n",
                file->Blocklen, file->Blockpos, ajStrGetLen(file->Buff));
*/
    return ajTrue;
}
ID ajReadlineTrim
TY public
MO ajfileio
LB core
XX
DE Reads a line from a file and removes any trailing newline.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pdest AjPStr*
PD Buffer to hold the current line.
PX
RT AjBool
RD ajTrue on success.
RX
CA modify
CT AjPFile
CD Reads a record from a file and removes newline characters
CX
//


AjBool ajReadlineTrim(AjPFile file, AjPStr* Pdest)
{
    ajlong fpos=0;

    return ajReadlineTrimPos(file, Pdest, &fpos);
}
ID ajReadlineTrimPos
TY public
MO ajfileio
LB core
XX
DE Reads a line from a file and removes any trailing newline.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pdest AjPStr*
PD Buffer to hold the current line.
PX
PN [3]
PA w Ppos ajlong*
PD File position before the read.
PX
RT AjBool
RD ajTrue on success.
RX
CA modify
CT AjPFile
CD Reads a record from a file and removes newline characters
CX
//


AjBool ajReadlineTrimPos(AjPFile file, AjPStr* Pdest, ajlong* Ppos)
{
    AjBool ok;

    ok = ajReadlinePos(file, Pdest, Ppos);

    if(!ok)
	return ajFalse;

    MAJSTRDEL(Pdest);

    /* trim any trailing newline */


    /*ajDebug("Remove carriage-return characters from PC-style files\n");*/
    if(ajStrGetCharLast(file->Buff) == '\n')
	ajStrCutEnd(&file->Buff, 1);

    /* PC files have \r\n Macintosh files have just \r : this fixes both */
    if(ajStrGetCharLast(file->Buff) == '\r')
	ajStrCutEnd(&file->Buff, 1);

    ajStrAssignRef(Pdest, file->Buff);

    return ajTrue;
}
ID ajReadbinBinary
TY public
MO ajfileio
LB core
XX
DE Binary read from an input file object using the C 'fread' function.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA r count size_t
PD Number of elements to read.
PX
PN [3]
PA r size size_t
PD Number of bytes per element.
PX
PN [4]
PA w buffer void*
PD Buffer for output.
PX
RT size_t
RD Return value from 'fread'
RX
//


size_t ajReadbinBinary(AjPFile file, size_t count,size_t size,
		   void* buffer)
{
    return fread(buffer, size, count, file->fp);
}
ID ajReadbinChar
TY public
MO ajfileio
LB core
XX
DE Reads a character string from a file
XX
PN [1]
PA u file AjPFile
PD File object.
PX
PN [2]
PA r size size_t
PD Number of bytes to read from index file.
PX
PN [3]
PA w buffer char*
PD Buffer to read into
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinChar(AjPFile file, size_t size, 
                     char* buffer)
{
    size_t ret;

    ret = fread(buffer, 1, size, file->fp);

    return ret;
}
ID ajReadbinCharTrim
TY public
MO ajfileio
LB core
XX
DE Reads a character string from a file and trims trailing spaces
XX
PN [1]
PA u file AjPFile
PD File object.
PX
PN [2]
PA r size size_t
PD Number of bytes to read from index file.
PX
PN [3]
PA w buffer char*
PD Buffer to read into
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinCharTrim(AjPFile file, size_t size,
                             char* buffer)
{
    size_t ret;
    char* sp;

    ret = fread(buffer, 1, size, file->fp);

    buffer[size] = '\0';
    sp = &buffer[strlen(buffer)];

    while(sp > buffer)
    {
        sp--;

	if(*sp != ' ')
	    break;

	*sp = '\0';
    }

    return ret;
}
ID ajReadbinInt
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from little-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi ajint*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinInt(AjPFile file, ajint *Pi)
{
    size_t ret;

    ret = fread(Pi, 4, 1, file->fp);

#ifdef WORDS_BIGENDIAN
    ajByteRevLen4(Pi);
#endif
    
    return ret;
}
ID ajReadbinIntEndian
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from big-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi ajint*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinIntEndian(AjPFile file, ajint *Pi)
{
    size_t ret;

    
    ret = fread(Pi, 4, 1, file->fp);

#ifndef WORDS_BIGENDIAN
    ajByteRevLen4(Pi);
#endif

    return ret;
}
ID ajReadbinIntLocal
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. No conversion (assumes integer was written
DE on the same system).
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi ajint*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinIntLocal(AjPFile file, ajint *Pi)
{
    size_t ret;

    ret = fread(Pi, 4, 1, file->fp);

    return ret;
}
ID ajReadbinInt2
TY public
MO ajfileio
LB core
XX
DE Binary read of a 2 byte integer from an input file object using
DE the C 'fread' function. Converts from little-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi2 ajshort*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinInt2(AjPFile file, ajshort *Pi2)
{
    size_t ret;

    ret = fread(Pi2, 2, 1, file->fp);

#ifdef WORDS_BIGENDIAN
    ajByteRevLen2(Pi2);
#endif
    
    return ret;
}
ID ajReadbinInt2Endian
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from big-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi2 ajshort*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinInt2Endian(AjPFile file, ajshort *Pi2)
{
    size_t ret;

    ret = fread(Pi2, 2, 1, file->fp);

#ifndef WORDS_BIGENDIAN
    ajByteRevLen2(Pi2);
#endif
    
    return ret;
}
ID ajReadbinInt2Local
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. No conversion (assumes integer was written
DE on the same system).
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi2 ajshort*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinInt2Local(AjPFile file, ajshort *Pi2)
{
    size_t ret;

    ret = fread(Pi2, 2, 1, file->fp);

    return ret;
}
ID ajReadbinInt4
TY public
MO ajfileio
LB core
XX
DE Binary read of a 4 byte integer from an input file object using
DE the C 'fread' function.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi4 ajint*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinInt4(AjPFile file, ajint *Pi4)
{
    size_t ret;

    ret = fread(Pi4, 4, 1, file->fp);

#ifdef WORDS_BIGENDIAN
    ajByteRevLen4(Pi4);
#endif
    
    return ret;
}
ID ajReadbinInt4Endian
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from big-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi4 ajint*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinInt4Endian(AjPFile file, ajint *Pi4)
{
    size_t ret;

    ret = fread(Pi4, 4, 1, file->fp);

#ifndef WORDS_BIGENDIAN
    ajByteRevLen4(Pi4);
#endif

    return ret;
}
ID ajReadbinInt4Local
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. No conversion (assumes integer was written
DE on the same system).
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi4 ajint*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinInt4Local(AjPFile file, ajint *Pi4)
{
    size_t ret;

    ret = fread(Pi4, 4, 1, file->fp);

    return ret;
}
ID ajReadbinInt8
TY public
MO ajfileio
LB core
XX
DE Binary read of an 8 byte integer from an input file object using
DE the C 'fread' function. Converts from little-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi8 ajlong*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinInt8(AjPFile file, ajlong *Pi8)
{
    long ret;

    ret = fread(Pi8, 8, 1, file->fp);

#ifdef WORDS_BIGENDIAN
    ajByteRevLen8(Pi8);
#endif
    
    return ret;
}
ID ajReadbinInt8Endian
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from big-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi8 ajlong*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinInt8Endian(AjPFile file, ajlong *Pi8)
{
    size_t ret;

    ret = fread(Pi8, 8, 1, file->fp);

#ifndef WORDS_BIGENDIAN
    ajByteRevLen8(Pi8);
#endif
    
    return ret;
}
ID ajReadbinInt8Local
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. No conversion (assumes integer was written
DE on the same system).
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pi8 ajlong*
PD Integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinInt8Local(AjPFile file, ajlong *Pi8)
{
    size_t ret;

    ret = fread(Pi8, 8, 1, file->fp);

    return ret;
}
ID ajReadbinStr
TY public
MO ajfileio
LB core
XX
DE Reads a string from a file
XX
PN [1]
PA u file AjPFile
PD File object.
PX
PN [2]
PA r size size_t
PD Number of bytes to read from index file.
PX
PN [3]
PA w Pstr AjPStr*
PD Buffer to read into
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinStr(AjPFile file, size_t size, 
                    AjPStr* Pstr)
{
    size_t ret;
    char* cp;

    ajStrSetRes(Pstr, size+1);
    cp = ajStrGetuniquePtr(Pstr);

    ret = fread(cp, 1, size, file->fp);
    cp[size] = '\0';

    ajStrSetValid(Pstr);

    return ret;
}
ID ajReadbinStrTrim
TY public
MO ajfileio
LB core
XX
DE Reads a character string from a file and trims trailing spaces
XX
PN [1]
PA u file AjPFile
PD File object.
PX
PN [2]
PA r size size_t
PD Number of bytes to read from index file.
PX
PN [3]
PA w Pstr AjPStr*
PD Buffer to read into
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinStrTrim(AjPFile file, size_t size,
                        AjPStr* Pstr)
{
    size_t ret;
    char* cp;

    ajStrSetRes(Pstr, size+1);
    cp = ajStrGetuniquePtr(Pstr);

    ret = fread(cp, 1, size, file->fp);
    cp[size] = '\0';

    ajStrSetValid(Pstr);

    ajStrTrimEndC(Pstr, " ");

    return ret;
}
ID ajReadbinUint
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu ajuint*
PD Unsigned integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUint(AjPFile file, ajuint *Pu)
{
    size_t ret;
#ifdef WORDS_BIGENDIAN
    ajint val2;
#endif

    ret = fread(Pu, 4, 1, file->fp);

#ifdef WORDS_BIGENDIAN
    val2 = *Pu;
    ajByteRevLen4(&val2);
    *Pu = val2;
#endif
    
    return ret;
}
ID ajReadbinUintEndian
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from big-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu ajuint*
PD Unsigned integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUintEndian(AjPFile file, ajuint *Pu)
{
    ajuint ret;
#ifndef WORDS_BIGENDIAN
    ajint val2;
#endif

    ret = fread(Pu, 4, 1, file->fp);

#ifndef WORDS_BIGENDIAN
    val2 = *Pu;
    ajByteRevLen4(&val2);
    *Pu = val2;
#endif
    
    return ret;
}
ID ajReadbinUintLocal
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. No conversion (assumes integer was written
DE on the same system).
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu ajuint*
PD Unsigned integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUintLocal(AjPFile file, ajuint *Pu)
{
    ajuint ret;

    ret = fread(Pu, 4, 1, file->fp);
    
    return ret;
}
ID ajReadbinUint2
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from little-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu2 ajushort*
PD Unsigned integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUint2(AjPFile file, ajushort *Pu2)
{
    size_t ret;
#ifdef WORDS_BIGENDIAN
    ajshort val2;
#endif

    ret = fread(Pu2, 2, 1, file->fp);

#ifdef WORDS_BIGENDIAN
    val2 = *Pu2;
    ajByteRevLen2(&val2);
    *Pu2 = val2;
#endif
    
    return ret;
}
ID ajReadbinUint2Endian
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from big-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu2 ajushort*
PD Unsigned integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUint2Endian(AjPFile file, ajushort *Pu2)
{
    size_t ret;
#ifndef WORDS_BIGENDIAN
    ajshort val2;
#endif

    ret = fread(Pu2, 2, 1, file->fp);

#ifndef WORDS_BIGENDIAN
    val2 = *Pu2;
    ajByteRevLen2(&val2);
    *Pu2 = val2;
#endif
    
    return ret;
}
ID ajReadbinUint2Local
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. No conversion (assumes integer was written
DE on the same system).
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu2 ajushort*
PD Unsigned integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUint2Local(AjPFile file, ajushort *Pu2)
{
    size_t ret;

    ret = fread(Pu2, 2, 1, file->fp);

    return ret;
}
ID ajReadbinUint4
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from little-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu4 ajuint*
PD Unsigned integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUint4(AjPFile file, ajuint *Pu4)
{
    size_t ret;
#ifdef WORDS_BIGENDIAN
    ajint val2;
#endif

    ret = fread(Pu4, 4, 1, file->fp);

#ifdef WORDS_BIGENDIAN
    val2 = *Pu4;
    ajByteRevLen4(&val2);
    *Pu4 = val2;
#endif
    
    return ret;
}
ID ajReadbinUint4Endian
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from a big-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu4 ajuint*
PD Unsigned integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUint4Endian(AjPFile file, ajuint *Pu4)
{
    size_t ret;
#ifndef WORDS_BIGENDIAN
    ajint val2;
#endif

    ret = fread(Pu4, 4, 1, file->fp);

#ifndef WORDS_BIGENDIAN
    val2 = *Pu4;
    ajByteRevLen4(&val2);
    *Pu4 = val2;
#endif
    
    return ret;
}
ID ajReadbinUint4Local
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. No conversion (assumes integer was written
DE on the same system).
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu4 ajuint*
PD Unsigned integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUint4Local(AjPFile file, ajuint *Pu4)
{
    size_t ret;

    ret = fread(Pu4, 4, 1, file->fp);

    return ret;
}
ID ajReadbinUint8
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from little-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu8 ajulong*
PD Unsigned long integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUint8(AjPFile file, ajulong *Pu8)
{
    size_t ret;
#ifdef WORDS_BIGENDIAN
    ajlong val2;
#endif

    ret = fread(Pu8, 8, 1, file->fp);

#ifdef WORDS_BIGENDIAN
    val2 = *Pu8;
    ajByteRevLen8(&val2);
    *Pu8 = val2;
#endif
    
    return ret;
}
ID ajReadbinUint8Endian
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. Converts from big-endian.
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu8 ajulong*
PD Unsigned long integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUint8Endian(AjPFile file, ajulong *Pu8)
{
    size_t ret;
#ifndef WORDS_BIGENDIAN
    ajlong val2;
#endif

    ret = fread(Pu8, 8, 1, file->fp);

#ifndef WORDS_BIGENDIAN
    val2 = (ajlong) *Pu8;
    ajByteRevLen8(&val2);
    *Pu8 = val2;
#endif

    return ret;
}
ID ajReadbinUint8Local
TY public
MO ajfileio
LB core
XX
DE Binary read of an unsigned integer from an input file object using
DE the C 'fread' function. No conversion (assumes integer was written
DE on the same system).
XX
PN [1]
PA u file AjPFile
PD Input file.
PX
PN [2]
PA w Pu8 ajulong*
PD Unsigned long integer value
PX
RT size_t
RD Number of bytes read.
RX
//


size_t ajReadbinUint8Local(AjPFile file, ajulong *Pu8)
{
    size_t ret;

    ret = fread(Pu8, 8, 1, file->fp);

    return ret;
}
ID ajWritebinBinary
TY public
MO ajfileio
LB core
XX
DE Binary write to an output file object using the C 'fwrite' function.
XX
PN [1]
PA u file AjPFile
PD Output file.
PX
PN [2]
PA r count size_t
PD Number of elements to write.
PX
PN [3]
PA r size size_t
PD Number of bytes per element.
PX
PN [4]
PA r buffer const void*
PD Buffer for output.
PX
RT size_t
RD Return value from 'fwrite'
RX
//


size_t ajWritebinBinary(AjPFile file, size_t count,
		   size_t size, const void* buffer)
{
    return fwrite(buffer, size, count, file->fp);
}
ID ajWritebinByte
TY public
MO ajfileio
LB core
XX
DE Writes a single byte to a binary file
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r ch char
PD Character
PX
RT size_t
RD Return value from fwrite
RX
//


size_t ajWritebinByte(AjPFile file, char ch)
{
    return fwrite(&ch, 1, 1, file->fp);
}
ID ajWritebinChar
TY public
MO ajfileio
LB core
XX
DE Writes a text string to a binary file
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r txt const char*
PD Text string
PX
PN [3]
PA r len size_t
PD Length (padded) to write to the file
PX
RT size_t
RD Return value from fwrite
RX
//


size_t ajWritebinChar(AjPFile file, const char* txt, size_t len)
{
    size_t ret;
    size_t i;
    size_t j;
    
    i = strlen(txt) + 1;

    if(i >= len)
        return fwrite(txt, len, 1, file->fp);

    ret = fwrite(txt, i, 1, file->fp);

    j = len-i;

    for(i=0;ifp);

    return ret;
}
ID ajWritebinInt2
TY public
MO ajfileio
LB core
XX
DE Writes a 2 byte integer to a binary file, with the correct byte orientation
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r i2 ajshort
PD Integer
PX
RT size_t
RD Return value from fwrite
RX
//


size_t ajWritebinInt2(AjPFile file, ajshort i2)
{
#ifdef WORDS_BIGENDIAN
    short j;

    j = i2;
    ajByteRevLen2(&j);

    return fwrite(&j, 2, 1, file->fp);
#else
    return fwrite(&i2, 2, 1, file->fp);
#endif

}
ID ajWritebinInt4
TY public
MO ajfileio
LB core
XX
DE Writes a 4 byte integer to a binary file, with the correct byte orientation
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r i4 ajint
PD Integer
PX
RT size_t
RD Return value from fwrite
RX
//


size_t ajWritebinInt4(AjPFile file, ajint i4)
{
#ifdef WORDS_BIGENDIAN
    ajint j;

    j = i4;

    ajByteRevLen4(&j);

    return fwrite(&j, 4, 1, file->fp);
#else
    return fwrite(&i4, 4, 1, file->fp);
#endif
}
ID ajWritebinInt8
TY public
MO ajfileio
LB core
XX
DE Writes an 8 byte long to a binary file, with the correct byte orientation
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r i8 ajlong
PD Integer
PX
RT size_t
RD Return value from fwrite
RX
//


size_t ajWritebinInt8(AjPFile file, ajlong i8)
{
#ifdef WORDS_BIGENDIAN
    ajlong j;

    j = i8;

    ajByteRevLen8(&j);

    return fwrite(&j, 8, 1, file->fp);
#else
    return fwrite(&i8, 8, 1, file->fp);
#endif
}
ID ajWritebinNewline
TY public
MO ajfileio
LB core
XX
DE Writes newline character(s) to a file
XX
PN [1]
PA u file AjPFile
PD Output file
PX
RT size_t
RD Return value from fwrite
RX
//


size_t ajWritebinNewline(AjPFile file)
{
    size_t ret;

#ifdef WIN32
    /*ret = fwrite("\r\n", 1, 2, file->fp);*/
    ret = fwrite("\n", 1, 1, file->fp);
#else
    ret = fwrite("\n", 1, 1, file->fp);
#endif

    return ret;
}
ID ajWritebinStr
TY public
MO ajfileio
LB core
XX
DE Writes a string to a binary file
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r str const AjPStr
PD String
PX
PN [3]
PA r len size_t
PD Length (padded) to use in the file
PX
RT size_t
RD Return value from fwrite
RX
//


size_t ajWritebinStr(AjPFile file, const AjPStr str, size_t len)
{
    size_t ret;
    ajuint ilen;
    ajuint i;
    ajuint j;
    ajuint k;

    ilen = 1+ajStrGetLen(str);

    if(ilen >= len)
        return fwrite(ajStrGetPtr(str), len, 1, file->fp);

    ret = fwrite(ajStrGetPtr(str), ilen, 1, file->fp);

    j = len - ilen;

    k = 20;

    for(i=0;ifp);
    }

    return ret;
}
ID ajWritebinUint2
TY public
MO ajfileio
LB core
XX
DE Writes a 2 byte unsigned integer to a binary file, with the correct
DE byte orientation
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r u2 ajushort
PD Unsigned integer
PX
RT size_t
RD Return value from fwrite
RX
//


size_t ajWritebinUint2(AjPFile file, ajushort u2)
{
#ifdef WORDS_BIGENDIAN
    unsigned short j;

    j = u2;

    ajByteRevLen2u(&j);

    return fwrite(&j, 2, 1, file->fp);
#else
    return fwrite(&u2, 2, 1, file->fp);
#endif

}
ID ajWritebinUint4
TY public
MO ajfileio
LB core
XX
DE Writes a 4 byte unsigned integer to a binary file, with the correct
DE byte orientation
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r u4 ajuint
PD Unsigned integer
PX
RT size_t
RD Return value from fwrite
RX
//


size_t ajWritebinUint4(AjPFile file, ajuint u4)
{
#ifdef WORDS_BIGENDIAN
    ajuint j;

    j = u4;

    ajByteRevLen4u(&j);

    return fwrite(&j, 4, 1, file->fp);
#else
    return fwrite(&u4, 4, 1, file->fp);
#endif
}
ID ajWritebinUint8
TY public
MO ajfileio
LB core
XX
DE Writes an 8 byte unsigned long to a binary file, with the correct
DE byte orientation
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r u8 ajulong
PD Unsigned integer
PX
RT size_t
RD Return value from fwrite
RX
//


size_t ajWritebinUint8(AjPFile file, ajulong u8)
{
#ifdef WORDS_BIGENDIAN
    ajulong j;

    j = u8;

    ajByteRevLen8u(&j);

    return fwrite(&j, 8, 1, file->fp);
#else
    return fwrite(&u8, 8, 1, file->fp);
#endif
}
ID ajWriteline
TY public
MO ajfileio
LB core
XX
DE Writes a string to a file, including any newline characters
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r line const AjPStr
PD String to be written
PX
RT AjBool
RD True on success
RX
//


AjBool ajWriteline(AjPFile file, const AjPStr line)
{
    if(!fwrite(MAJSTRGETPTR(line), MAJSTRGETLEN(line), 1, file->fp))
        return ajFalse;

    return ajTrue;
}
ID ajWritelineNewline
TY public
MO ajfileio
LB core
XX
DE Writes a string to a file, including any newline characters
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r line const AjPStr
PD String to be written
PX
RT AjBool
RD True on success
RX
//


AjBool ajWritelineNewline(AjPFile file, const AjPStr line)
{
    if(!fwrite(MAJSTRGETPTR(line), 1, MAJSTRGETLEN(line), file->fp))
        return ajFalse;

#ifdef WIN32
    /*if(!fwrite("\r\n", 1, 2, file->fp))
      return ajFalse;*/
    if(!fwrite("\n", 1, 1, file->fp))
        return ajFalse;
#else
    if(!fwrite("\n", 1, 1, file->fp))
        return ajFalse;
#endif

    return ajTrue;
}
ID ajWritelineSpace
TY public
MO ajfileio
LB core
XX
DE Writes a string to a file, with a leading space
XX
PN [1]
PA u file AjPFile
PD Output file
PX
PN [2]
PA r line const AjPStr
PD String to be written
PX
RT AjBool
RD True on success
RX
//


AjBool ajWritelineSpace(AjPFile file, const AjPStr line)
{
    if(!fwrite(" ", 1, 1, file->fp))
        return ajFalse;

    if(!fwrite(MAJSTRGETPTR(line), MAJSTRGETLEN(line), 1, file->fp))
        return ajFalse;

    return ajTrue;
}
ID ajBuffreadLine
TY public
MO ajfileio
LB core
XX
DE Reads a line from a buffered file. If the buffer has data, reads from the
DE buffer. If the buffer is exhausted, reads from the file. If the file is
DE exhausted, sets end of file and returns. If end of file was already set,
DE looks for another file to open.
XX
PN [1]
PA u buff AjPFilebuff
PD Buffered input file.
PX
PN [2]
PA w Pdest AjPStr*
PD Buffer to hold results.
PX
RT AjBool
RD ajTrue if data was read.
RX
//


AjBool ajBuffreadLine(AjPFilebuff buff, AjPStr* Pdest)
{
    ajlong fpos = 0;

    return ajBuffreadLinePos(buff, Pdest, &fpos);
}
ID ajBuffreadLinePos
TY public
MO ajfileio
LB core
XX
DE Reads a line from a buffered file. If the buffer has data, reads from the
DE buffer. If the buffer is exhausted, reads from the file. If the file is
DE exhausted, sets end of file and returns. If end of file was already set,
DE looks for another file to open.
XX
PN [1]
PA u buff AjPFilebuff
PD Buffered input file.
PX
PN [2]
PA w Pdest AjPStr*
PD Buffer to hold results.
PX
PN [3]
PA w Ppos ajlong*
PD File position before the read.
PX
RT AjBool
RD ajTrue if data was read.
RX
//


AjBool ajBuffreadLinePos(AjPFilebuff buff, AjPStr* Pdest, ajlong* Ppos)
{
    AjBool ok;
    
    /* read from the buffer if it is not empty */
    
    *Ppos = 0;
    
    if(buff->Pos < buff->Size)
    {
	ajStrAssignS(Pdest, buff->Curr->Line);
	*Ppos = buff->Curr->Fpos;
	buff->Prev = buff->Curr;
	buff->Curr = buff->Curr->Next;
	buff->Pos++;

	return ajTrue;
    }
    
    /* file has been closed */
    if(!buff->File->Handle)
	return ajFalse;
    
    /* buffer used up - try reading from the file */
    
    ok = ajReadlinePos(buff->File, Pdest, &buff->Fpos);
    
    if(!ok)
    {
	if(buff->File->End)
	{
	    if(buff->Size)
	    {
		/* we have data in the buffer - fail */
		ajDebug("End of file - data in buffer - return ajFalse\n");
		return ajFalse;
	    }
	    else
	    {
		/* buffer clear - try another file */
		if(ajFileReopenNext(buff->File))
		{
		    /* OK - read the new file */
		    ok = ajBuffreadLinePos(buff, Pdest, Ppos);
		    ajDebug("End of file - trying next file ok: %B "
			    "fpos: %Ld %Ld\n",
			    ok, *Ppos, buff->Fpos);

		    return ok;
		}
		else
		{
		    /* no new file, fail again */
		    ajDebug("End of file - no new file to read - "
			    "return ajFalse\n");

		    return ajFalse;
		}
	    }
	}
	else
        {
	    ajWarn("Error reading from file '%S'",
		    ajFileGetNameS(buff->File));
            return ajFalse;
        }
    }
    
    if(buff->Nobuff)
    {
	*Ppos = buff->Fpos;
	/*ajDebug("ajBuffreadLinePos unbuffered fpos: %Ld\n", *Ppos);*/

	return ajTrue;
    }
    
    filebuffLineAdd(buff, *Pdest);
    *Ppos = buff->Fpos;
    
    return ajTrue;
}
ID ajBuffreadLinePosStore
TY public
MO ajfileio
LB core
XX
DE Reads a line from a buffered file. Also appends the line to
DE a given string if the append flag is true. A double NULL character
DE is added afterwards. If the buffer has data, reads from the
DE buffer. If the buffer is exhausted, reads from the file. If the file is
DE exhausted, sets end of file and returns. If end of file was already set,
DE looks for another file to open.
XX
PN [1]
PA u buff AjPFilebuff
PD Buffered input file.
PX
PN [2]
PA w Pdest AjPStr*
PD Buffer to hold results.
PX
PN [3]
PA w Ppos ajlong*
PD File position before the read.
PX
PN [4]
PA r dostore AjBool
PD append if true
PX
PN [5]
PA w Pstore AjPStr*
PD string to append to
PX
RT AjBool
RD ajTrue if data was read.
RX
CA modify
CT AjPFilebuff
CD Reads a line from a buffered file with append.
CX
//


AjBool ajBuffreadLinePosStore(AjPFilebuff buff, AjPStr* Pdest, ajlong* Ppos,
                              AjBool dostore, AjPStr *Pstore)
{
    AjBool ret;

    ret =  ajBuffreadLinePos(buff, Pdest, Ppos);

    if(dostore && ret)
    {
	ajDebug("ajBuffreadLinePosStore:\n%S", *Pdest);
	ajStrAppendS(Pstore,*Pdest);
    }

    return ret;
}
ID ajBuffreadLineStore
TY public
MO ajfileio
LB core
XX
DE Reads a line from a buffered file. Also appends the line to
DE a given string if the append flag is true. A double NULL character
DE is added afterwards. If the buffer has data, reads from the
DE buffer. If the buffer is exhausted, reads from the file. If the file is
DE exhausted, sets end of file and returns. If end of file was already set,
DE looks for another file to open.
XX
PN [1]
PA u buff AjPFilebuff
PD Buffered input file.
PX
PN [2]
PA w Pdest AjPStr*
PD Buffer to hold results.
PX
PN [3]
PA r dostore AjBool
PD append if true
PX
PN [4]
PA w Pstore AjPStr*
PD string to append to
PX
RT AjBool
RD ajTrue if data was read.
RX
CA modify
CT AjPFilebuff
CD Reads a line from a buffered file with append.
CX
//


AjBool ajBuffreadLineStore(AjPFilebuff buff, AjPStr* Pdest,
                           AjBool dostore, AjPStr *Pstore)
{
    ajlong fpos = 0;
    AjBool ret;

    ret =  ajBuffreadLinePos(buff, Pdest, &fpos);

    if(dostore && ret)
    {
	ajDebug("ajBuffreadLineStore: '%S'", *Pdest);
	ajStrAppendS(Pstore,*Pdest);
    }

    return ret;
}
ID ajBuffreadLineTrim
TY public
MO ajfileio
LB core
XX
DE Reads a line from a buffered file. If the buffer has data, reads from the
DE buffer. If the buffer is exhausted, reads from the file. If the file is
DE exhausted, sets end of file and returns. If end of file was already set,
DE looks for another file to open.
XX
PN [1]
PA u buff AjPFilebuff
PD Buffered input file.
PX
PN [2]
PA w Pdest AjPStr*
PD Buffer to hold results.
PX
RT AjBool
RD ajTrue if data was read.
RX
CA modify
CT AjPFilebuff
CD Reads a line from a buffered file.
CX
//


AjBool ajBuffreadLineTrim(AjPFilebuff buff, AjPStr* Pdest)
{
    AjBool ret;
    ajlong fpos = 0;

    ret = ajBuffreadLinePos(buff, Pdest, &fpos);
    
    /* trim any trailing newline */

    /*ajDebug("Remove carriage-return characters from PC-style files\n");*/
    if(ajStrGetCharLast(*Pdest) == '\n')
	ajStrCutEnd(Pdest, 1);

    /* PC files have \r\n Macintosh files have just \r : this fixes both */
    if(ajStrGetCharLast(*Pdest) == '\r')
	ajStrCutEnd(Pdest, 1);

    return ret;
}
ID filebuffLineAdd
TY static
MO ajfileio
LB core
XX
DE Appends a line to a buffer.
XX
PN [1]
PA u buff AjPFilebuff
PD File buffer
PX
PN [2]
PA r line const AjPStr
PD Line
PX
RT void
RD 
RX
//


static void filebuffLineAdd(AjPFilebuff buff, const AjPStr line)
{
    /* ajDebug("fileBuffLineAdd '%S'\n", line);*/
    if(buff->Freelines)
    {
	if(!buff->Lines)
	{
	    /* Need to set first line in list */
	    buff->Lines = buff->Freelines;
	}
	else
	    buff->Last->Next = buff->Freelines;

	buff->Last = buff->Freelines;
	buff->Freelines = buff->Freelines->Next;

	if(!buff->Freelines)
	{
	    /* Free list now empty */
	    buff->Freelast = NULL;
	}
    }
    else
    {
	/* No Free list, make a new string */
	if(!buff->Lines)
	    buff->Lines = AJNEW0(buff->Last);
	else
	    buff->Last = AJNEW0(buff->Last->Next);
    }
    
    ajStrAssignS(&buff->Last->Line, line);
    buff->Prev = buff->Curr;
    buff->Curr = buff->Last;
    buff->Last->Next = NULL;
    buff->Last->Fpos = buff->Fpos;
    buff->Pos++;
    buff->Size++;
        
    return;
}
ID c_isin
TY static
MO ajfmt
LB core
XX
DE Checks whether a character is in a set
XX
PN [1]
PA r c ajint
PD character
PX
PN [2]
PA r list const char*
PD set of characters
PX
RT AjBool
RD ajTrue if character is in the set
RX
//


static AjBool c_isin(ajint c, const char *list)
{
    while(*list)
        if(c == (ajint) *(list++))
            return ajTrue;

    return ajFalse;
}
ID c_notin
TY static
MO ajfmt
LB core
XX
DE Checks whether a character is not in a set
XX
PN [1]
PA r c ajint
PD character
PX
PN [2]
PA r list const char*
PD set of characters
PX
RT AjBool
RD ajTrue if character is not in the set
RX
//


static AjBool c_notin(ajint c, const char *list)
{
    while(*list)
        if(c == (ajint) *(list++))
            return ajFalse;

    return ajTrue;
}
ID cvt_s
TY static
MO ajfmt
LB core
XX
DE Conversion for %s to print char* text
DE
DE As for C RTL but prints null if given a null pointer.
XX
PN [1]
PA r code ajint
PD Format code specified (usually s)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_s(ajint code, VALIST ap, int (*put) (int c, void *cl),
                  void *cl, const ajuint *flags, ajint width,
                  ajint precision)
{
    char *str = va_arg(VA_V(ap), char *);

    (void) code;

    if(str)
        ajFmtPuts(str, strlen(str), put, cl, flags,
                  width, precision);
    else
        ajFmtPuts("", 6, put, cl, flags,
                  width, precision);

    return;
}
ID cvt_d
TY static
MO ajfmt
LB core
XX
DE Conversion for %d to print integer
XX
PN [1]
PA r code ajint
PD Format code specified (usually d)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_d(ajint code, VALIST ap, int (*put) (int c, void *cl),
                  void *cl, const ajuint *flags, ajint width,
                  ajint precision)
{
    long val = 0;
#if defined(HAVE64)
    ajlong hval = 0;
#endif /* HAVE64 */
    ajulong m = 0;

    char buf[43];
    char *p = buf + sizeof buf;

    (void) code;

    if(flags['l'])
    {
        val  = (long) va_arg(VA_V(ap), long);
#if defined(HAVE64)
        hval = val;
#endif /* HAVE64 */
    }
    else if(flags['L'])
    {
#if defined(HAVE64)
        hval = (ajlong) va_arg(VA_V(ap), ajlong);
        val  = hval;
#else /* !HAVE64 */
        val = (long) va_arg(VA_V(ap), ajlong);
        /*ajDebug("Warning: Use of %%Ld on a 32 bit model");*/
#endif /* HAVE64 */
    }
    else if(flags['h'])
    {
        /* ANSI C converts short to ajint */
        val  = (long) va_arg(VA_V(ap), int);
#if defined(HAVE64)
        hval = val;
#endif /* HAVE64 */
    }
    else
    {
        val  = (long) va_arg(VA_V(ap), int);
#if defined(HAVE64)
        hval = val;
#endif /* HAVE64 */
    }


#if defined(HAVE64)
    if(hval == INT_MIN)
        m = INT_MAX + 1U;
    else if(hval < 0)
        m = -hval;
    else
        m = hval;

    do
        *--p = ajSysCastItoc((ajint) (m % 10 + '0'));
    while((m /= 10) > 0);

    if(hval < 0)
        *--p = '-';
#else /* !HAVE64 */
    if(val == INT_MIN)
        m = INT_MAX + 1U;
    else if(val < 0)
        m = -val;
    else
        m = val;

    do
        *--p = ajSysCastItoc((ajint) (m % 10 + '0'));
    while((m /= 10) > 0);

    if(val < 0)
        *--p = '-';
#endif /* HAVE64 */

    ajFmtPutd(p, (buf + sizeof buf) - p, put, cl, flags,
              width, precision);

    return;
}
ID cvt_u
TY static
MO ajfmt
LB core
XX
DE Conversion for %u to print unsigned integer
XX
PN [1]
PA r code ajint
PD Format code specified (usually u)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_u(ajint code, VALIST ap, int (*put) (int c, void *cl),
                  void *cl, const ajuint *flags, ajint width,
                  ajint precision)
{
    unsigned long val = 0;
#if defined(HAVE64)
    ajulong  hval = 0;
#endif /* HAVE64 */
    char buf[43];
    char *p;

    (void) code;

    p = buf + sizeof buf;

    if(flags['l'])
    {
        val  = va_arg(VA_V(ap), unsigned long);
#if defined(HAVE64)
        hval = val;
#endif /* HAVE64 */
    }
    else if(flags['L'])
    {
#if defined(HAVE64)
        hval = (ajulong) va_arg(VA_V(ap), ajulong);
        val = hval;
#else /* !HAVE64 */
        val = (unsigned long) va_arg(VA_V(ap), ajulong);
        /*ajDebug("Warning: Use of %%Lu on 32 bit model");*/
#endif /* HAVE64 */
    }
    else if(flags['h'])
    {   /* ANSI C converts short to int */
        val = (unsigned long) va_arg(VA_V(ap), unsigned int);
#if defined(HAVE64)
        hval = val;
#endif /* HAVE64 */
    }
    else
    {
        val = (unsigned long) va_arg(VA_V(ap), unsigned int);
#if defined(HAVE64)
        hval = val;
#endif /* HAVE64 */
    }

#if !defined(HAVE64)
    do
        *--p = ajSysCastItoc(val % 10 + '0');
    while((val /= (ajulong) 10) > 0);
#else /* HAVE64 */
    do
        *--p = ajSysCastItoc((int) (hval % (ajulong) 10 + '0'));
    while((hval /= (ajulong) 10) > 0);
#endif /* !HAVE64 */
    ajFmtPutd(p, (buf + sizeof buf) - p, put, cl, flags,
              width, precision);

    return;
}
ID cvt_o
TY static
MO ajfmt
LB core
XX
DE Conversion for %o to print unsigned integer as octal
XX
PN [1]
PA r code ajint
PD Format code specified (usually o)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_o(ajint code, VALIST ap, int (*put) (int c, void *cl),
                  void *cl, const ajuint *flags, ajint width,
                  ajint precision)
{
    unsigned long m = 0;
    char buf[43];
    char *p;
#if defined(HAVE64)
    ajulong hm = 0;
#endif /* HAVE64 */

    (void) code;

    p = buf + sizeof buf;

    if(flags['l'])
        m = va_arg(VA_V(ap), unsigned long);
    if(flags['h'])
        /* ANSI C converts short to ajint */
        m = va_arg(VA_V(ap), unsigned int);
    else if(flags['L'])
    {
#if defined(HAVE64)
        hm = (ajulong) va_arg(VA_V(ap), ajulong);
#else /* !HAVE64 */
        m = (unsigned long) va_arg(VA_V(ap), ajulong);
        /*ajDebug("Warning: Use of %%Lo on a 32 bit model");*/
#endif /* HAVE64 */
    }
    else
    {
        m = va_arg(VA_V(ap), unsigned int);
#if defined(HAVE64)
        hm = m;
#endif /* HAVE64 */
    }

#if !defined(HAVE64)
    do
        *--p = ajSysCastItoc((m & 0x7) + '0');
    while((m >>= 3) != 0);
#else /* HAVE64 */
    do
        *--p = ajSysCastItoc((int) ((hm & 0x7) + '0'));
    while((hm >>= 3) != 0);
#endif /* !HAVE64 */

    if(flags['#'])
        *--p = '0';

    ajFmtPutd(p, (buf + sizeof buf) - p, put, cl, flags,
              width, precision);

    return;
}
ID cvt_x
TY static
MO ajfmt
LB core
XX
DE Conversion for %x to print unsigned integer as hexadecimal
XX
PN [1]
PA r code ajint
PD Format code specified (usually x)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_x(ajint code, VALIST ap, int (*put) (int c, void *cl),
                  void *cl, const ajuint *flags, ajint width,
                  ajint precision)
{
    unsigned long m = 0;
#if defined(HAVE64)
    ajulong hm = 0;
#endif /* HAVE64 */
    char buf[43];
    char *p;

    p = buf + sizeof buf;

    if(flags['l'])
    {
        m = va_arg(VA_V(ap), unsigned long);
#if defined(HAVE64)
        hm = m;
#endif /* HAVE64 */
    }
    else if(flags['h'])
    {
        /* ANSI C converts short to int */
        m = va_arg(VA_V(ap), unsigned int);
#if defined(HAVE64)
        hm = m;
#endif /* HAVE64 */
    }
    else if(flags['L'])
    {
#if defined(HAVE64)
        hm = va_arg(VA_V(ap), ajulong);
#else /* !HAVE64 */
        m = (unsigned long) va_arg(VA_V(ap), ajulong);
        /* ajDebug("Warning: Use of %%Lx on a 32 bit model"); */
#endif /* HAVE64 */
    }
    else
    {
        m = va_arg(VA_V(ap), unsigned int);
#if defined(HAVE64)
        hm = m;
#endif /* HAVE64 */
    }
    if(code == 'X')
    {
#if !defined(HAVE64)
        do
            *--p = "0123456789ABCDEF"[m & 0xf];
        while((m >>= 4) != 0);
#else /* HAVE64 */
        do
            *--p = "0123456789ABCDEF"[hm & 0xf];
        while((hm >>= 4) != 0);
#endif /* !HAVE64 */
    }
    else
    {
#if !defined(HAVE64)
        do
            *--p = "0123456789abcdef"[m & 0xf];
        while((m >>= 4) != 0);
#else /* HAVE64 */
        do
            *--p = "0123456789abcdef"[hm & 0xf];
        while((hm >>= 4) != 0);
#endif /* !HAVE64 */
    }

    while(precision > buf + sizeof buf - p)
        *--p = '0';

    if(flags['#'])
    {
        *--p = 'x';
        *--p = '0';
    }

    ajFmtPutd(p, (buf + sizeof buf) - p, put, cl, flags,
              width, precision);

    return;
}
ID cvt_p
TY static
MO ajfmt
LB core
XX
DE Conversion for %p to print pointers of type void* as hexadecimal
XX
PN [1]
PA r code ajint
PD Format code specified (usually p)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_p(ajint code, VALIST ap, int (*put) (int c, void *cl),
                  void *cl, const ajuint *flags, ajint width,
                  ajint precision)
{
    unsigned long m;
    char buf[43];
    char *p;
    precision = INT_MIN;

    (void) code;

    m = (unsigned long) va_arg(VA_V(ap), void *);
    p = buf + sizeof buf;

    do
        *--p = "0123456789abcdef"[m & 0xf];
    while((m >>= 4) != 0);

    ajFmtPutd(p, (buf + sizeof buf) - p, put, cl, flags,
              width, precision);

    return;
}
ID cvt_c
TY static
MO ajfmt
LB core
XX
DE Conversion for %c to print an integer (or a character)
DE as a single character.
DE
DE Arguments passed in the variable part of an argument list are promoted
DE by default, so char is always promoted to ajint by the time it reaches here.
XX
PN [1]
PA r code ajint
PD Format code specified (usually c)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_c(ajint code, VALIST ap, int (*put) (int c, void *cl),
                  void *cl, const ajuint *flags, ajint width,
                  ajint precision)
{
    ajuint minusflag = flags['-'];
    ajuint upperflag = flags['U'];
    ajuint lowerflag = flags['L'];

    (void) code;
    (void) precision;

    if(width == INT_MIN)
        width = 0;

    if(width < 0)
    {
        minusflag = 1;
        width      = -width;
    }

    if(!minusflag)
        pad(width - 1, ' ');

    if(upperflag)
        (*put) ((unsigned char) toupper(va_arg(VA_V(ap), int)), cl);
    else if(lowerflag)
        (*put) ((unsigned char) tolower(va_arg(VA_V(ap), int)), cl);
    else
        (*put) (ajSysCastItouc(va_arg(VA_V(ap), int)), cl);

    if(minusflag)
        pad(width - 1, ' ');

    return;
}
ID cvt_f
TY static
MO ajfmt
LB core
XX
DE Conversion for %f to print a floating point number.
DE
DE Because it is generally faster than hand crafted code, the standard
DE conversion in sprintf is used, and the resulting string is then
DE written out.
DE
DE Precision is limited to 99 decimal places so it will fit in 2 characters
DE of the format for sprintf.
XX
PN [1]
PA r code ajint
PD Format code specified (usually f)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_f(ajint code, VALIST ap, int (*put) (int c, void *cl),
                  void *cl, const ajuint *flags, ajint width,
                  ajint precision)
{
    char buf[DBL_MAX_10_EXP + 1 + 1 + 99 + 1];

    if(precision < 0)
    {
        if(code == 'f') precision = 6;
        else if(code == 'g') precision = 6;
        else if(code == 'e') precision = 6;
        else precision = DBL_DIG;
    }

    if(code == 'g' && precision == 0)
        precision = 1;

    {
        /* use sprintf to convert to string */
        /* using code and precision */
        static char fmt[12] = "%.dd";
        ajint i = 2;

        assert(precision <= 99);

        if(precision > 9)
            fmt[i++] = ajSysCastItoc((precision / 10) % 10 + '0');
        fmt[i++] = ajSysCastItoc(precision % 10 + '0');
        fmt[i++] = ajSysCastItoc(code);
        fmt[i]   = '\0';

        sprintf(buf, fmt, va_arg(VA_V(ap), double));

        if(code == 'g')
            precision = 0;
    }

    /* now write string and support width */
    ajFmtPutd(buf, strlen(buf), put, cl, flags,
              width, precision);

    return;
}
ID cvt_uS
TY static
MO ajfmt
LB core
XX
DE Conversion for %S to print a string
XX
PN [1]
PA r code ajint
PD Format code specified (usually S)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_uS(ajint code, VALIST ap, int (*put) (int c, void *cl),
                   void *cl, const ajuint *flags, ajint width,
                   ajint precision)
{
    AjPStr str1;

    (void) code;

    str1 = va_arg(VA_V(ap), AjPStr);

    if(str1)
        ajFmtPuts(str1->Ptr, str1->Len, put, cl, flags,
                  width, precision);
    else
        ajFmtPuts("", 6, put, cl, flags,
                  width, precision);

    return;
}
ID cvt_b
TY static
MO ajfmt
LB core
XX
DE Conversion for %b to print a boolean as a 1 letter code (Y or N)
XX
PN [1]
PA r code ajint
PD Format code specified (usually b)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_b(ajint code, VALIST ap, int (*put) (int c, void *cl),
                  void *cl, const ajuint *flags, ajint width,
                  ajint precision)
{
    AjBool bl;

    (void) code;

    bl = va_arg(VA_V(ap), AjBool);

    if(bl)
        ajFmtPuts("Y", 1, put, cl, flags,
                  width, precision);
    else
        ajFmtPuts("N", 1, put, cl, flags,
                  width, precision);

    return;
}
ID cvt_uB
TY static
MO ajfmt
LB core
XX
DE Conversion for %B to print a boolean as text (Yes or No)
XX
PN [1]
PA r code ajint
PD Format code specified (usually B)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_uB(ajint code, VALIST ap, int (*put) (int c, void *cl),
                   void *cl, const ajuint *flags, ajint width,
                   ajint precision)
{
    AjBool bl;

    (void) code;

    bl = va_arg(VA_V(ap), AjBool);

    if(bl)
        ajFmtPuts("Yes", 3, put, cl, flags,
                  width, precision);
    else
        ajFmtPuts("No", 2, put, cl, flags,
                  width, precision);

    return;
}
ID cvt_uD
TY static
MO ajfmt
LB core
XX
DE Conversion for %D to print a datetime value
XX
PN [1]
PA r code ajint
PD Format code specified (usually D)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_uD(ajint code, VALIST ap, int (*put) (int c, void *cl),
                   void *cl, const ajuint *flags, ajint width,
                   ajint precision)
{
    AjPTime timeobj;
    struct tm *mytime;
    int lenyr;

    char buf[280];
    char yr[280];

    (void) code;

    timeobj   =  va_arg(VA_V(ap), AjPTime);
    mytime = &timeobj->time;

    if(!timeobj)
    {
        ajFmtPuts("", 6, put, cl, flags,
                  width, precision);
        return;
    }

    if(timeobj->format)
        strftime(buf, 280, timeobj->format, mytime);
    else
    {
        /* Long-winded but gets around some compilers' %y warnings */
        strftime(yr, 280, "%Y", mytime);
        lenyr = strlen(yr);
        memmove(yr, &yr[lenyr - 2], 3);
        strftime(buf, 280, "%d/%m/", mytime);
        strcat(buf, yr);
    }

    if(timeobj->uppercase)
        ajCharFmtUpper(buf);

    ajFmtPuts(&buf[0], strlen(buf), put, cl, flags,
              width, precision);

    return;
}
ID cvt_uF
TY static
MO ajfmt
LB core
XX
DE Conversion for %F to print a file object
XX
PN [1]
PA r code ajint
PD Format code specified (usually F)
PX
PN [2]
PA r ap VALIST
PD Original arguments at current position
PX
PN [3]
PA f put int function
PD Standard function
PX
PN [4]
PA u cl void*
PD Standard - where to write results
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


static void cvt_uF(ajint code, VALIST ap, int (*put) (int c, void *cl),
                   void *cl, const ajuint *flags, ajint width,
                   ajint precision)
{
    AjPFile fil;

    (void) code;

    fil = va_arg(VA_V(ap), AjPFile);

    if(fil && fil->Name)
        ajFmtPuts(fil->Printname->Ptr, fil->Printname->Len, put, cl, flags,
                  width, precision);
    else
        ajFmtPuts("", 6, put, cl, flags,
                  width, precision);

    return;
}
ID Fmt_T
TY list
MO ajfmt
LB core
XX
DE Conversion functions called for each conversion code.
DE
DE Usually, code "x" will call "cvt_x" but there are exceptions. For example,
DE floating point conversions all use cvt_f which sends everything to
DE the standard C library. Also, cvt_d is used by alternative codes.
XX
RT void
RD 
RX
//


static Fmt_T cvt[256] =
{
    /*   0 -   7 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*   8 -  15 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  16 -  23 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  24 -  31 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  32 -  39 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  40 -  47 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  48 -  55 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  56 -  63 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  64 -  71 */
    0,      0,&cvt_uB,      0,&cvt_uD,      0,&cvt_uF,      0,
    /*  72 -  79 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  80 -  87 */
    0,      0,      0, cvt_uS,      0,      0,      0,      0,
    /*  88 -  95 */
    &cvt_x, 0,      0,      0,      0,      0,      0,      0,
    /*  96 - 103 */
    0,      0, &cvt_b, &cvt_c, &cvt_d, &cvt_f, &cvt_f, &cvt_f,
    /* 104 - 111 */
    0,      0,      0,      0,      0,      0, &cvt_d, &cvt_o,
    /* 112 - 119 */
    &cvt_p, 0,      0, &cvt_s,      0, &cvt_u,      0,      0,
    /* 120 - 127 */
    &cvt_x, 0,      0,      0,      0,      0,      0,      0
};
ID Fmt_S
TY list
MO ajfmt
LB core
XX
DE Conversion functions called for each scan conversion code.
DE
DE Usually, code "x" will call "cvt_x" but there are exceptions. For example,
DE floating point conversions all use cvt_f which sends everything to
DE the standard C library. Also, cvt_d is used by alternative codes.
XX
RT void
RD 
RX
//


static Fmt_S scvt[256] =
{
    /*   0-  7 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*   8- 15 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  16- 23 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  24- 31 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  32- 39 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  40- 47 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  48- 55 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  56- 63 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  64- 71 */
    0,      0,  &scvt_uB,   0,      0,      0,      0,      0,
    /*  72- 79 */
    0,      0,      0,      0,      0,      0,      0,      0,
    /*  80- 87 */
    0,      0,      0,  &scvt_uS,   0,      0,      0,      0,
    /*  88- 95 */
    &scvt_x,0,      0,      0,      0,      0,      0,      0,
    /*  96-103 */
    0,      0,&scvt_b,&scvt_c,&scvt_d,&scvt_f,&scvt_f,&scvt_f,
    /* 104-111 */
    0,      0,      0,      0,      0,      0,&scvt_d,&scvt_o,
    /* 112-119 */
    &scvt_p,0,      0,&scvt_s,      0,&scvt_u,      0,      0,
    /* 120-127 */
    &scvt_x,0,&scvt_z,      0,      0,      0,      0,      0
};
ID fmtOutC
TY static
MO ajfmt
LB core
XX
DE General output function to print a single character to a file
XX
PN [1]
PA r c int
PD Character to be written
PX
PN [2]
PA u cl void*
PD Output file - will be cast to FILE* internally
PX
RT ajint
RD Character written
RX
//


static ajint fmtOutC(int c, void *cl)
{
    FILE *f = cl;
    ajint ret;

    ret = putc(c, f);

#ifdef WIN32
    /*    if((char) c == '\n')
          putc((int) '\r', f);*/
#endif /* WIN32 */

    return ret;
}
ID fmtInsert
TY static
MO ajfmt
LB core
XX
DE Inserts a character in a buffer, raises a Fmt_Overflow exception if
DE the buffer is too small.
XX
PN [1]
PA r c int
PD Character to be written
PX
PN [2]
PA u cl void*
PD Output file - will be cast to FmtPBuf internally
PX
RT ajint
RD Character written
RX
//


static ajint fmtInsert(int c, void *cl)
{
    FmtPBuf p;

    p = cl;

    if(p->bp >= p->buf + p->size)
    {
        if(p->fixed)
            AJRAISE(Fmt_Overflow);

        AJRESIZE(p->buf, 2 * p->size);
        p->bp = p->buf + p->size;
        p->size *= 2;
    }

    *p->bp++ = ajSysCastItoc(c);

    return c;
}
ID fmtAppend
TY static
MO ajfmt
LB core
XX
DE Appends a character to a buffer, resizing it if necessary
XX
PN [1]
PA r c ajint
PD Character to be written
PX
PN [2]
PA u cl void*
PD Output file - will be cast to FmtPBuf internally
PX
RT ajint
RD kCharacter written
RX
//


static ajint fmtAppend(ajint c, void *cl)
{
    FmtPBuf p;

    p = cl;

    if(p->bp >= p->buf + p->size)
    {
        if(p->fixed)
            AJRAISE(Fmt_Overflow);

        AJRESIZE(p->buf, 2 * p->size);
        p->bp = p->buf + p->size;
        p->size *= 2;
    }

    *p->bp++ = ajSysCastItoc(c);

    return c;
}
ID ajFmtPuts
TY public
MO ajfmt
LB core
XX
DE Format and emit the converted numeric (ajFmtPutd) or string
DE (ajFmtPuts) in str[0..len - 1] according to Fmt's defaults
DE and the values of flags, width and precision. It is a c.r.e
DE for str = null, len less than 0 or flags = null.
XX
PN [1]
PA r str const char*
PD Text to write.
PX
PN [2]
PA r len ajint
PD Text length.
PX
PN [3]
PA f put int function
PD Standard function.
PX
PN [4]
PA u cl void*
PD Standard - where to write the output
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


void ajFmtPuts(const char *str, ajint len, int (*put) (int c, void *cl),
               void *cl, const ajuint *flags, ajint width,
               ajint precision)
{
    ajuint minusflag = flags['-'];
    ajuint upperflag = flags['U'];
    ajuint lowerflag = flags['L'];
    ajint i;

    assert(len >= 0);
    assert(flags);

    if(width == INT_MIN)
        width = 0;

    if(width < 0)
    {
        minusflag = 1;
        width = -width;
    }

    if(precision >= 0 && precision < len)
        len = precision;

    if(!minusflag)
        pad(width - len, ' ');

    if(upperflag)
    {
        for(i = 0; i < len; i++)
            (*put) ((unsigned char) toupper((int) *str++), cl);
    }
    else if(lowerflag)
    {
        for(i = 0; i < len; i++)
            (*put) ((unsigned char) tolower((int) *str++), cl);
    }
    else
    {
        for(i = 0; i < len; i++)
            (*put) ((unsigned char) *str++, cl);
    }

    if(minusflag)
        pad(width - len, ' ');

    return;
}
ID ajFmtFmt
TY public
MO ajfmt
LB core
XX
DE formats and emits the "..." arguments according to the format string fmt
XX
PN [1]
PA f put ajint function
PD Standard function.
PX
PN [2]
PA u cl void*
PD Standard - where to write the output
PX
PN [3]
PA r fmt const char*
PD Format string
PX
PN [4]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajFmtFmt(ajint (*put) (ajint c, void *cl), void *cl,
              const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    ajFmtVfmt(put, cl, fmt, ap);
    va_end(ap);

    return;
}
ID ajFmtPrint
TY public
MO ajfmt
LB core
XX
DE format and emit the "..." arguments according to fmt;writes to stdout.
XX
PN [1]
PA r fmt const char*
PD Format string.
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajFmtPrint(const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    ajFmtVfmt(fmtOutC, stdout, fmt, ap);
    va_end(ap);

    return;
}
ID ajFmtVPrint
TY public
MO ajfmt
LB core
XX
DE format and emit the "..." arguments according to fmt;writes to stdout.
XX
PN [1]
PA r fmt const char*
PD Format string.
PX
PN [2]
PA v ap va_list
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajFmtVPrint(const char *fmt, va_list ap)
{
    ajFmtVfmt(fmtOutC, stdout, fmt, ap);

    return;
}
ID ajFmtError
TY public
MO ajfmt
LB core
XX
DE format and emit the "..." arguments according to fmt;writes to stderr.
XX
PN [1]
PA r fmt const char*
PD Format string.
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajFmtError(const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    ajFmtVfmt(fmtOutC, stderr, fmt, ap);
    va_end(ap);

    return;
}
ID ajFmtVError
TY public
MO ajfmt
LB core
XX
DE format and emit the "..." arguments according to fmt. Writes to stderr.
XX
PN [1]
PA r fmt const char*
PD Format string.
PX
PN [2]
PA v ap va_list
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajFmtVError(const char *fmt, va_list ap)
{
    ajFmtVfmt(fmtOutC, stderr, fmt, ap);

    return;
}
ID ajFmtPrintF
TY public
MO ajfmt
LB core
XX
DE format and emit the "..." arguments according to fmt;writes to stream..
XX
PN [1]
PA u file AjPFile
PD Output file.
PX
PN [2]
PA r fmt const char*
PD Format string.
PX
PN [3]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajFmtPrintF(AjPFile file, const char *fmt, ...)
{
    va_list ap;

    if(!file)
        return;

    va_start(ap, fmt);
    ajFmtVfmt(fmtOutC, file->fp, fmt, ap);
    va_end(ap);

    return;
}
ID ajFmtVPrintF
TY public
MO ajfmt
LB core
XX
DE format and emit the "..." arguments according to fmt;writes to stream..
XX
PN [1]
PA u file AjPFile
PD Output file.
PX
PN [2]
PA r fmt const char*
PD Format string.
PX
PN [3]
PA v ap va_list
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajFmtVPrintF(AjPFile file, const char *fmt, va_list ap)
{
    if(!file)
        return;

    ajFmtVfmt(fmtOutC, file->fp, fmt, ap);

    return;
}
ID ajFmtVPrintFp
TY public
MO ajfmt
LB core
XX
DE Format and emit the "..." arguments according to fmt;writes to stream..
XX
PN [1]
PA u stream FILE*
PD Output file.
PX
PN [2]
PA r fmt const char*
PD Format string.
PX
PN [3]
PA v ap va_list
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajFmtVPrintFp(FILE *stream, const char *fmt, va_list ap)
{
    ajFmtVfmt(fmtOutC, stream, fmt, ap);

    return;
}
ID ajFmtPrintFp
TY public
MO ajfmt
LB core
XX
DE format and emit the "..." arguments according to fmt;writes to stream..
XX
PN [1]
PA u stream FILE*
PD Output file.
PX
PN [2]
PA r fmt const char*
PD Format string.
PX
PN [3]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajFmtPrintFp(FILE *stream, const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    ajFmtVfmt(fmtOutC, stream, fmt, ap);
    va_end(ap);

    return;
}
ID ajFmtVPrintCL
TY public
MO ajfmt
LB core
XX
DE formats the "..." arguments into buf[1...size - 1] according to fmt,
DE appends a num character, and returns the length of buf. It is a
DE c.r.e for size to be less than or equal to 0. Raises Fmt_Overflow
DE if more than size - 1 characters are emitted.
XX
PN [1]
PA w buf char*
PD char string to be written to.
PX
PN [2]
PA r size ajint
PD length of buffer
PX
PN [3]
PA r fmt const char*
PD Format string.
PX
PN [4]
PA v ap va_list
PD Variable length argument list
PX
RT ajint
RD number of characters written to buf.
RX
//


ajint ajFmtVPrintCL(char *buf, ajint size, const char *fmt, va_list ap)
{
    ajint len;

    len = ajFmtVfmtCL(buf, size, fmt, ap);

    return len;
}
ID ajFmtPrintCL
TY public
MO ajfmt
LB core
XX
DE formats the "..." arguments into buf[1...size - 1] according to fmt,
DE appends a num character, and returns the length of buf. It is a
DE c.r.e for size to be lass than or equal to 0. Raises Fmt_Overflow
DE if more than size - 1 characters are emitted.
XX
PN [1]
PA w buf char*
PD char string to be written to.
PX
PN [2]
PA r size ajint
PD length of buffer
PX
PN [3]
PA r fmt const char*
PD Format string
PX
PN [4]
PA v vararg ...
PD Variable length argument list
PX
RT ajint
RD number of characters written to buf.
RX
//


ajint ajFmtPrintCL(char *buf, ajint size, const char *fmt, ...)
{
    va_list ap;
    ajint len;

    va_start(ap, fmt);
    len = ajFmtVfmtCL(buf, size, fmt, ap);
    va_end(ap);

    return len;
}
ID ajFmtStr
TY public
MO ajfmt
LB core
XX
DE Formats the "..." arguments into a New AjPStr according to fmt.
DE It starts with an initial size of 20 then doubles until the
DE fmt output fits.
DE
DE The caller is responsible for deleting the AjPStr afterwards.
XX
PN [1]
PA r fmt const char*
PD Format string.
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT AjPStr
RD new AjPStr with Ptr holding formatted chars
RX
//


AjPStr ajFmtStr(const char *fmt, ...)
{
    va_list ap;
    ajint len = 32;
    AjPStr fnew;

    fnew = ajStrNewRes(len);
    va_start(ap, fmt);
    fnew->Len = ajFmtVfmtStrCL(&fnew->Ptr, 0, &fnew->Res, fmt, ap);
    va_end(ap);

    return fnew;
}
ID ajFmtPrintS
TY public
MO ajfmt
LB core
XX
DE Formats the "..." arguments into an AjPStr according to fmt.
DE If AjPStr is not large enough then if it is the only one i.e
DE Use = 1 then increase till it fits. Else return 0 if it does not
DE fit. If it fits return the address of the new AjPStr.
XX
PN [1]
PA u pthis AjPStr*
PD String to be written too.
PX
PN [2]
PA r fmt const char*
PD Format for string.
PX
PN [3]
PA v vararg ...
PD Variable length argument list
PX
RT AjPStr
RD Output string
RX
//


AjPStr ajFmtPrintS(AjPStr *pthis, const char *fmt, ...)
{
    AjPStr thys;
    va_list ap;

    va_start(ap, fmt);

    ajStrSetRes(pthis, 32);
    thys = *pthis;

    thys->Len = ajFmtVfmtStrCL(&thys->Ptr, 0, &thys->Res, fmt, ap);

    va_end(ap);

    return thys;
}
ID ajFmtVPrintS
TY public
MO ajfmt
LB core
XX
DE Formats the "..." arguments into an AjPStr according to fmt.
DE If AjPStr is not large enough then if it is the only one i.e
DE Use = 1 then increase till it fits. Else return 0 if it does not
DE fit. If it fits return the address of the new AjPStr.
XX
PN [1]
PA u pthis AjPStr*
PD String to be written too.
PX
PN [2]
PA r fmt const char*
PD Format for string.
PX
PN [3]
PA v ap va_list
PD Variable length argument list
PX
RT AjPStr
RD Output string
RX
//


AjPStr ajFmtVPrintS(AjPStr *pthis, const char *fmt, va_list ap)
{
    AjPStr thys;

    ajStrSetRes(pthis, 32);
    thys = *pthis;

    thys->Len = ajFmtVfmtStrCL(&thys->Ptr, 0, &thys->Res, fmt, ap);

    return thys;
}
ID ajFmtPrintAppS
TY public
MO ajfmt
LB core
XX
DE Formats the "..." arguments and appends to an AjPStr according to fmt.
DE If AjPStr is not large enough then if it is the only one i.e
DE Use = 1 then increase till it fits. Else return 0 if it does not
DE fit. If it fits return the address of the new AjPStr.
XX
PN [1]
PA u pthis AjPStr*
PD String to be written too.
PX
PN [2]
PA r fmt const char*
PD Format for string.
PX
PN [3]
PA v vararg ...
PD Variable length argument list
PX
RT AjPStr
RD Output string.
RX
//


AjPStr ajFmtPrintAppS(AjPStr *pthis, const char *fmt, ...)
{
    AjPStr thys;
    va_list ap;
    ajint len;

    va_start(ap, fmt);

    ajStrSetRes(pthis, 32);
    thys = *pthis;

    len = ajFmtVfmtStrCL(&thys->Ptr, thys->Len, &thys->Res, fmt, ap);

    thys->Len += len;

    va_end(ap);

    return thys;
}
ID ajFmtVfmtStrCL
TY public
MO ajfmt
LB core
XX
DE Same as ajFmtPrintCL but takes arguments from the list ap.
XX
PN [1]
PA w pbuf char**
PD char string to be written to.
PX
PN [2]
PA r pos ajint
PD position in buffer to start writing
PX
PN [3]
PA u size size_t*
PD allocated size of the buffer
PX
PN [4]
PA r fmt const char*
PD Format string.
PX
PN [5]
PA v ap va_list
PD Variable length argument list.
PX
RT ajint
RD number of characters written to buf.
RX
//


ajint ajFmtVfmtStrCL(char **pbuf, ajint pos, size_t *size,
                     const char *fmt, va_list ap)
{
    FmtOBuf cl;

    assert(*pbuf);
    assert(*size > 0);
    assert(fmt);

    cl.buf   = *pbuf;
    cl.bp    = cl.buf + pos;
    cl.size  = *size;
    cl.fixed = ajFalse;

    ajFmtVfmt(fmtAppend, &cl, fmt, ap);
    fmtAppend(0, &cl);

    *size = cl.size;
    *pbuf = cl.buf;

    return cl.bp - cl.buf - 1 - pos;
}
ID ajFmtVfmtCL
TY public
MO ajfmt
LB core
XX
DE Same as ajFmtPrintCL but takes arguments from the list ap.
XX
PN [1]
PA w buf char*
PD char string to be written to.
PX
PN [2]
PA r size ajint
PD length of buffer
PX
PN [3]
PA r fmt const char*
PD Format string.
PX
PN [4]
PA v ap va_list
PD Variable length argument list.
PX
RT ajint
RD number of characters written to buf.
RX
//


ajint ajFmtVfmtCL(char *buf, ajint size, const char *fmt, va_list ap)
{
    FmtOBuf cl;

    assert(buf);
    assert(size > 0);
    assert(fmt);

    cl.buf   = cl.bp = buf;
    cl.size  = size;
    cl.fixed = ajTrue;

    ajFmtVfmt(fmtInsert, &cl, fmt, ap);
    fmtInsert(0, &cl);

    return cl.bp - cl.buf - 1;
}
ID ajFmtString
TY public
MO ajfmt
LB core
XX
DE formats the "..." arguments into a null-terminated string according to
DE fmt and returns that string.
XX
PN [1]
PA r fmt const char*
PD Format string
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT char*
RD Output string.
RX
//


char* ajFmtString(const char *fmt, ...)
{
    char *str;
    va_list ap;

    assert(fmt);
    va_start(ap, fmt);
    str = ajFmtVString(fmt, ap);
    va_end(ap);

    return str;
}
ID ajFmtVString
TY public
MO ajfmt
LB core
XX
DE as ajFmtString but takes arguments from the list ap.
XX
PN [1]
PA r fmt const char*
PD Format string.
PX
PN [2]
PA v ap va_list
PD Variable length argument list.
PX
RT char*
RD Output string.
RX
//


char* ajFmtVString(const char *fmt, va_list ap)
{
    FmtOBuf cl;

    assert(fmt);

    cl.size = 256;
    cl.buf = cl.bp = AJALLOC(cl.size);
    cl.fixed = ajFalse;

    ajFmtVfmt(fmtAppend, &cl, fmt, ap);
    fmtAppend(0, &cl);

    return AJRESIZE(cl.buf, cl.bp - cl.buf);
}
ID ajFmtVfmt
TY public
MO ajfmt
LB core
XX
DE as ajFmtPrint but takes arguments from the list ap.
XX
PN [1]
PA f put int function
PD Standard function
PX
PN [2]
PA u cl void*
PD Where we are going to write the results
PX
PN [3]
PA r fmt const char*
PD Format string
PX
PN [4]
PA v ap va_list
PD Variable argument list
PX
RT void
RD 
RX
//


void ajFmtVfmt(int (*put) (int c, void *cl), void *cl, const char *fmt,
               va_list ap)
{
    assert(put);
    assert(fmt);
    assert(cl);

    while(*fmt)
    {
        if(*fmt != '%' || *++fmt == '%') /* %% just outputs '%' */
            (*put) ((unsigned char) *fmt++, cl);
        else
        {
            /* we have a % - get working on the format */
            unsigned char c;
            ajint flags[256];
            ajint width = INT_MIN, precision = INT_MIN;

            memset(flags, '\0', sizeof flags);

            if(Fmt_flags)
            {
                /* look for any conversion flags */
                unsigned char cc = *fmt;

                for( ; (int) cc && strchr(Fmt_flags, cc); cc = *++fmt)
                {
                    assert(flags[(int) cc] < 255);
                    flags[(int) cc]++;
                }
            }

            if(*fmt == '*' || isdigit((int) *fmt))
            {
                ajint n;

                if(*fmt == '*')
                {
                    /* '*' width = ajint arg */
                    n = va_arg(ap, int);
                    assert(n != INT_MIN);
                    fmt++;
                }
                else
                    for(n = 0; isdigit((int) *fmt); fmt++)
                    {
                        ajint d = *fmt - '0';
                        assert(n <= (INT_MAX - d) / 10);
                        n = 10 * n + d;
                    }

                width = n;
            }

            if(*fmt == '.' && (*++fmt == '*' || isdigit((int) *fmt)))
            {
                ajint n;

                if(*fmt == '*')
                {                       /* '*' precision = ajint arg */
                    n = va_arg(ap, int);
                    assert(n != INT_MIN);
                    fmt++;
                }
                else
                    for(n = 0; isdigit((int) *fmt); fmt++)
                    {
                        ajint d = *fmt - '0';
                        assert(n <= (INT_MAX - d) / 10);
                        n = 10 * n + d;
                    }

                precision = n;
            }

            if(*fmt == 'l' || *fmt == 'L'|| *fmt == 'h')
            {
                /* size modifiers, L is also lower case string */
                assert(flags[(int) *fmt] < 255); /* store as flags - */
                /* values do not clash */
                flags[(int) *fmt]++;
                fmt++;
            }

            if(*fmt == 'U')
            {
                /* upper case */
                assert(flags[(int) *fmt] < 255); /* store as flags - */
                flags[(int) *fmt]++;
                fmt++;
            }

            /* finally, next character is the code */
            c = *fmt++;

            /* Calling funclist Fmt_T() */

            if(!cvt[(int) c])
                ajDie("Bad format %%%c", c);
            (*cvt[(int) c]) (c, VA_P(ap), put, cl, (ajuint *) flags, width,
                             precision);
        }
    }

    return;
}
ID ajFmtPutd
TY public
MO ajfmt
LB core
XX
DE Given a string containing a number in full, converts it using the width
DE and precision values.
XX
PN [1]
PA r str const char*
PD Text to write.
PX
PN [2]
PA r len ajint
PD Text length.
PX
PN [3]
PA f put int function
PD Standard function.
PX
PN [4]
PA u cl void*
PD Standard - where to write the output
PX
PN [5]
PA r flags const ajuint*
PD Flags (after the %)
PX
PN [6]
PA r width ajint
PD Width (before the dot)
PX
PN [7]
PA r precision ajint
PD Precision (after the dot)
PX
RT void
RD 
RX
//


void ajFmtPutd(const char *str, ajint len, int (*put) (int c, void *cl),
               void *cl, const ajuint *flags, ajint width,
               ajint precision)
{
    ajint sign;

    ajuint minusflag = flags['-'];

    assert(str);
    assert(len >= 0);
    assert(flags);

    if(width == INT_MIN)
        width = 0;

    if(width < 0)
    {
        minusflag = 1;
        width = -width;
    }


    if(len > 0 && (*str == '-' || *str == '+'))
    {
        sign = *str++;
        len--;
    }
    else if(flags['+'])
        sign = '+';
    else if(flags[' '])
        sign = ' ';
    else
        sign = 0;

    {
        ajint n;
        ajint j = 0;

        if(precision < 0)
            precision = 1;

        if(len < precision)
            n = precision;
        /* else if(precision == 0 && len == 1 && str[0] == '0')
           n = 0; */
        else
            n = len;

        if(sign)
            n++;

        if(flags['#'] && flags['0'])
        {
            /* make space for the padding */
            if(*str == '0' && *(str + 1) == 'x')
            {
                (*put) ((unsigned char) *str++, cl);
                (*put) ((unsigned char) *str++, cl);
                j += 2;
            }
        }

        if(flags['-'])
        {
            if(sign)
                (*put) (sign, cl);
        }
        else if(flags['0'])
        {
            if(sign)
                (*put) (sign, cl);
            pad(width - n, '0');
        }
        else
        {
            pad(width - n, ' ');
            if(sign)
                (*put) (sign, cl);
        }

        /* pad for precision - should be turned off for %g */
        pad(precision - len, '0');

        {
            ajint i;

            for(i = j; i < len; i++)
                (*put) ((unsigned char) *str++, cl);
        }

        if(minusflag)
            pad(width - n, ' ');
    }

    return;
}
ID ajFmtPrintSplit
TY public
MO ajfmt
LB core
XX
DE Block and print a string. String is split at given delimiters
XX
PN [1]
PA u outf AjPFile
PD output stream
PX
PN [2]
PA r str const AjPStr
PD text to write
PX
PN [3]
PA r prefix const char*
PD prefix string
PX
PN [4]
PA r len ajint
PD maximum span
PX
PN [5]
PA r delim const char*
PD delimiter string
PX
RT void
RD 
RX
//


void ajFmtPrintSplit(AjPFile outf, const AjPStr str,
                     const char *prefix, ajint len,
                     const char *delim)
{
    AjPStrTok handle = NULL;
    AjPStr token     = NULL;
    AjPStr tmp       = NULL;
    AjPStr tmp2      = NULL;

    ajint    n = 0;
    ajint    l = 0;
    ajint    c = 0;

    if(!outf)
        return;

    token = ajStrNew();
    tmp   = ajStrNewC("");

    handle = ajStrTokenNewC(str, delim);

    while(ajStrTokenNextParse(&handle, &token))
    {
        if(!c)
            ajFmtPrintF(outf, "%s", prefix);

        if((l = n + ajStrGetLen(token)) < len)
        {
            if(c++)
                ajStrAppendC(&tmp, " ");

            ajStrAppendS(&tmp, token);

            if(c != 1)
                n = ++l;
            else
                n = l;
        }
        else
        {
            ajFmtPrintF(outf, "%S\n", tmp);
            ajStrAssignS(&tmp, token);
            ajStrAppendC(&tmp, " ");
            n = ajStrGetLen(token) + 1;
            c = 0;
        }
    }

    if(c)
        ajFmtPrintF(outf, "%S\n", tmp);
    else
    {
        n = ajStrGetLen(tmp);
        ajStrAssignSubS(&tmp2, tmp, 0, n - 2);
        ajFmtPrintF(outf, "%s%S\n", prefix, tmp2);
        ajStrDel(&tmp2);
    }


    ajStrTokenDel(&handle);
    ajStrDel(&token);
    ajStrDel(&tmp);

    return;
}
ID ajFmtScanS
TY public
MO ajfmt
LB core
XX
DE Scan a string according to fmt and load the ... variable pointers
DE Like C function sscanf.
XX
PN [1]
PA r thys const AjPStr
PD String.
PX
PN [2]
PA r fmt const char*
PD Format string.
PX
PN [3]
PA v vararg ...
PD Variable length argument list
PX
RT ajint
RD number of successful conversions
RX
//


ajint ajFmtScanS(const AjPStr thys, const char *fmt, ...)
{
    va_list ap;
    ajint   n;

    va_start(ap, fmt);
    n = fmtVscan(thys->Ptr, fmt, ap);
    va_end(ap);

    return n;
}
ID ajFmtScanC
TY public
MO ajfmt
LB core
XX
DE Scan a string according to fmt and load the ... variable pointers
DE Like C function sscanf.
XX
PN [1]
PA r thys const char*
PD String.
PX
PN [2]
PA r fmt const char*
PD Format string.
PX
PN [3]
PA v vararg ...
PD Variable length argument list
PX
RT ajint
RD number of successful conversions
RX
//


ajint ajFmtScanC(const char *thys, const char *fmt, ...)
{
    va_list ap;
    ajint   n;

    va_start(ap, fmt);
    n = fmtVscan(thys, fmt, ap);
    va_end(ap);

    return n;
}
ID ajFmtScanF
TY public
MO ajfmt
LB core
XX
DE Scan an AjPFile object according to fmt and load the ... variable pointers
DE Like C function fscanf.
XX
PN [1]
PA u thys AjPFile
PD Input file object
PX
PN [2]
PA r fmt const char*
PD Format string.
PX
PN [3]
PA v vararg ...
PD Variable length argument list
PX
RT ajint
RD number of successful conversions
RX
//


ajint ajFmtScanF(AjPFile thys, const char *fmt, ...)
{
    va_list ap;
    ajint   n;
    FILE *file;

    if(!thys)
        return 0;

    file = ajFileGetFileptr(thys);

    va_start(ap, fmt);
    n = fmtVfscanf(file, fmt, ap);
    va_end(ap);

    return n;
}
ID fmtVfscanf
TY static
MO ajfmt
LB core
XX
DE EMBOSS version of C99 vfscanf() including %S conversion
XX
PN [1]
PA u stream FILE*
PD file pointer.
PX
PN [2]
PA r fmt const char*
PD Format string.
PX
PN [3]
PA v ap va_list
PD Variable length argument list
PX
RT ajint
RD number of successful conversions
RX
//


static ajint fmtVfscanf(FILE *stream, const char *fmt, va_list ap)
{
    char fch = '\0';
    int  ipc = 0;
    int  tpc = 0;

    ajuint uflags = 0;
    ajint  nconv  = 0;
    ajint  width  = 0;
    AjBool fin    = ajFalse;
    AjBool idone  = ajFalse;

    ajulong sum = 0L;

    ajuint nread = 0;
    ajint  ret   = EOF;
    ajint  base  = 10;

    char *p       = NULL;
    char fchars[] = "0123456789Ee.";

    char buf[EVF_BUF];

    AjPStr *str = NULL;

    union
    {
        ajlong  L;
        long    l;
        unsigned long ul;
        ajulong uL;
        ajint   i;
        ajuint  u;
        char    *c;
        double  d;
    } val;


    if(feof(stream))
        return EOF;

    val.ul = 0L;

    while((fch = *fmt))
    {
        ++fmt;


        if(uflags & EVF_PERCENT)
        {
            /* If %% has been given then do a simple match */
            if(fch == '%')
            {
                uflags &= ~EVF_PERCENT;

                if((ipc = getc(stream)) == EOF)
                    break;

                ++sum;

                if(ipc != (int) fch)
                    break;

                continue;
            }

            /* Now in the realm of format specifiers */
            /* First test for any width */

            if(fch >= '0' && fch <= '0')
            {
                fch -= '0';

                if(width == INT_MAX)
                    width = 0;
                else
                    width *= 10;

                width += (ajint) fch;

                continue;
            }

            if((char) fch != 'S')
	      fch = tolower((int)fch);

            switch(fch)
            {
                case '*':
                    uflags |= EVF_NOCON;
                    continue;

                case 'n':
                    if(!(uflags & EVF_NOCON))
                        *(va_arg(ap, ajint *)) = (ajint) sum;

                    break;

                case 'c':
                    if(!(uflags & EVF_NOCON))
                        val.c = va_arg(ap, char *);

                    if(width == INT_MAX)
                        width = 1;

                    while(width-- > 0)
                    {
                        if((ipc = getc(stream)) == EOF)
                        {
                            fin = ajTrue;
                            break;
                        }

                        ++nread;

                        if(!(uflags & EVF_NOCON))
                            *val.c++ = (char) ipc;
                    }

                    break;

                case 'h':      /* ANSI C converts shorts to ints */
                    continue;

                case 'l':
                    uflags |= EVF_LONG;
                    continue;

                case 'L':
                    uflags |= EVF_AJLONG;
                    continue;

                case 's':
                    if(!(uflags & EVF_NOCON))
                        val.c = va_arg(ap, char *);

                    do
                    {
                        ipc = getc(stream);
                        ++sum;
                    } while(isspace(ipc));

                    if(ipc == EOF)
                    {
                        fin = ajTrue;
                        break;
                    }

                    while(width-- > 0)
                    {
                        if(isspace(ipc))
                        {
                            ungetc(ipc, stream);
                            --sum;
                            break;
                        }

                        if(!(uflags & EVF_NOCON))
                            *val.c++ = (char) ipc;

                        if((ipc = getc(stream)) == EOF)
                            break;

                        ++nread;
                    }

                    if(!(uflags & EVF_NOCON))
                        *val.c = '\0';

                    break;

                case 'S':
                    if(!(uflags & EVF_NOCON))
                    {
                        str = (AjPStr *) va_arg(ap, AjPStr *);
                        ajStrAssignC(str, "");
                    }

                    do
                    {
                        ipc = getc(stream);
                        ++sum;
                    } while(isspace(ipc));

                    if(ipc == EOF)
                    {
                        fin = ajTrue;
                        break;
                    }

                    while(width-- > 0)
                    {
                        if(isspace(ipc))
                        {
                            ungetc(ipc, stream);
                            --sum;
                            break;
                        }

                        if(!(uflags & EVF_NOCON))
                            ajStrAppendK(str, (char) ipc);

                        if((ipc = getc(stream)) == EOF)
                            break;

                        ++nread;
                    }

                    break;

                case 'o':
                    base = 8;
                    /* Fallthrough */
                case 'x':
                    if(base == 10)  /* To protect fallthrough from octal */
                        base = 16;
                    /* Fallthrough */
                case 'u':
                    uflags |= EVF_UNSIGNED;
                    /* Fallthrough */
                case 'd':
                case 'i':
                    do
                    {
                        ipc = getc(stream);
                        ++sum;
                    } while(isspace(ipc));

                if(ipc == EOF)
                {
                    fin = ajTrue;
                    break;
                }

                if((char) ipc == '+' || (char) ipc == '-')
                {
                    if(--width <= 0)
                    {
                        fin = ajTrue;
                        break;
                    }

                    if((char) ipc == '-')
                        uflags |= EVF_NEGATE;

                    if((ipc = getc(stream)) == EOF)
                    {
                        fin = ajTrue;
                        break;
                    }

                    ++sum;
                }

                idone = ajFalse;
                val.uL = 0;

                if((char) ipc == '0')
                {
                    if(--width <= 0)
                        idone = ajTrue;

                    if(!idone)
                        if((ipc = getc(stream)) == EOF)
                            idone = ajTrue;

                    if((char) tolower(ipc) == 'x' && !idone)
                    {
                        if(fch == 'o' || fch == 'd' || fch == 'u')
                        {
                            /* Invalid 0x */
                            ungetc(ipc, stream);
                            idone = ajTrue;
                        }

                        if(!idone)
                            base = 16;

                        if(!idone)
                        {
                            if((ipc = getc(stream)) == EOF)
                                idone = ajTrue;
                            else
                                ++sum;
                        }
                    }
                    else if(fch == 'i' && !idone)
                        base = 8;
                }

                ++nread;

                for(; !idone;)
                {
                    tpc = tolower(ipc);

                    tpc -= '0';

                    if(tpc > 9)
                    {
                        tpc += '0';
                        tpc -= 'a';

                        if(tpc >= 0)
                            tpc += 10;
                    }

                    if(tpc < 0 || tpc >= base)
                    {
                        ungetc(ipc, stream);
                        --nread;
                        break;
                    }

                    val.uL *= base;
                    val.uL += tpc;

                    if(--width <= 0)
                        break;

                    if((ipc = getc(stream)) == EOF)
                        break;

                    ++nread;
                }

                if((uflags & EVF_NEGATE) && !idone)
                    val.L = -val.L;

                if(!(uflags & EVF_NOCON))
                {
                    if((uflags & (EVF_UNSIGNED | EVF_AJLONG)) ==
                       (EVF_UNSIGNED | EVF_AJLONG))
                        *(va_arg(ap, ajulong *)) = val.uL;
                    else if(uflags & (EVF_AJLONG))
                        *(va_arg(ap, ajlong *)) = val.L;
                    else if((uflags & (EVF_UNSIGNED | EVF_LONG)) ==
                            (EVF_UNSIGNED | EVF_LONG))
                        *(va_arg(ap, unsigned long *)) = val.ul;
                    else if(uflags & (EVF_LONG))
                        *(va_arg(ap, long *)) = val.l;
                    else if(uflags & (EVF_UNSIGNED))
                        *(va_arg(ap, ajuint *)) = val.u;
                    else
                        *(va_arg(ap, ajint *)) = val.i;
                }

                break;

                case 'e':
                case 'f':
                case 'g':
                    do
                    {
                        ipc = getc(stream);
                        ++sum;
                    } while(isspace(ipc));

                if(ipc == EOF)
                {
                    --sum;
                    fin = ajTrue;
                    break;
                }

                if((char) ipc == '+' || (char) ipc == '-')
                {
                    if((char) ipc == '-')
                        uflags |= EVF_NEGATE;

                    if((ipc = getc(stream)) == EOF)
                    {
                        fin = ajTrue;
                        break;
                    }

                    ++sum;
                }

                val.d = 0.0;

                ++nread;

                for(p = buf; p < buf + EVF_BUF - 1 && width > 0; --width)
                {
                    if(!strchr(fchars, ipc))
                    {
                        ungetc(ipc, stream);
                        --nread;
                        break;
                    }

                    if((char) ipc == 'e' || (char) ipc == 'E')
                    {
                        fchars[10] = '\0';
                        *p++ = (char) ipc;

                        if((ipc = getc(stream)) == EOF)
                            break;

                        ++nread;

                        if((char) ipc != '+' && (char) ipc != '-')
                            continue;
                    }
                    else if((char) ipc == '.')
                        fchars[12] = '\0';

                    *p++ = (char) ipc;

                    if((ipc = getc(stream)) == EOF)
                        break;

                    ++nread;
                }

                *p++ = '\0';

                val.d = strtod(buf, 0);

                if(uflags & EVF_NEGATE)
                    val.d = -val.d;

                fchars[10] = 'E';
                fchars[12] = '.';

                *(va_arg(ap, double *)) = val.d;

                break;
            }

            if(fin)
                break;

            if(nread)
            {
                if(!(uflags & EVF_NOCON))
                    ++nconv;

                ret = 0;
                sum += nread;
            }
            else if(fch != 'n' || ipc == EOF)
                break;

            uflags = 0;
        }
        else if(fch == '%')
        {
            uflags = EVF_PERCENT;
            base = 10;
            nread = 0;
            width = INT_MAX;
        }
        else if(isspace((int)fch))
        {
            do
            {
                ipc = getc(stream);
                ++sum;
            } while(isspace(ipc));

            --sum;

            if(ipc == EOF)
                break;

            ungetc(ipc, stream);
        }
        else
        {
            if((ipc = getc(stream)) == EOF)
                break;

            ++sum;

            if(ipc != (int) fch)
                break;
        }

    }

    if((uflags & EVF_PERCENT) && nread)
    {
        if(!(uflags & EVF_NOCON))
            ++nconv;

        ret = 0;
    }

    if(ipc == EOF && nconv == 0)
        return ret;


    return nconv;
}
ID fmtVscan
TY static
MO ajfmt
LB core
XX
DE Scan a string according to fmt and load the va_list variable pointers
XX
PN [1]
PA r thys const char*
PD String.
PX
PN [2]
PA r fmt const char*
PD Format string.
PX
PN [3]
PA v ap va_list
PD Variable length argument list
PX
RT ajint
RD number of successful conversions
RX
//


static ajint fmtVscan(const char *thys, const char *fmt, va_list ap)
{
    ajint n;
    const char *p;
    const char *q;
    static const char *wspace = " \n\t";
    AjBool convert      = ajTrue;
    AjBool ok           = ajTrue;
    ajint width = 0;
    ajint v     = 0;
    ajint d     = 0;

    n = 0;

    /*  we update it as a pointer */
    p = (const char *) thys;
    q = fmt;

    while(*p && *q)
    {
        /* Ignore all whitespace */
        if(c_isin((ajint) *p, wspace))
        {
            ++p;
            continue;
        }

        if(c_isin((ajint) *q, wspace))
        {
            ++q;
            continue;
        }

        /* If *q isn't '%' then it must match *p */
        if(*q != '%')
        {
            if(*q != *p)
                break;
            else
            {
                ++p;
                ++q;
                continue;
            }
        }

        /* Check for %% */
        if(*(++q) == '%')
        {
            if(*p != '%')
                break;
            else
            {
                ++p;
                ++q;
                continue;
            }
        }


        /*
        **  *p now points to a string character to be matched
        **  *q points to first character after fmt '%'
        */

        /* Check for %* format */
        convert = ajTrue;
        if(*q == '*')
        {
            ++q;
            convert = ajFalse;
        }

        /* If *q is a numeral then calculate the width else set to INT_MIN */
        if(isdigit((int) *q))
        {
            for(v = 0; isdigit((int) *q); ++q)
            {
                d = *q - '0';
                v = 10 * v + d;
            }
            width = v;
        }
        else
            width = INT_MIN;

        /* Just ignore size modifier for now */
        if(*q == 'l' || *q == 'L' || *q == 'h')
            ++q;

        /* *q is the conversion function to call */
        ok = ajTrue;

        /* Calling funclist Fmt_S() */
        if(!scvt[(int) *q])
            ajDie("Bad scan format %%%c", q);
        (*scvt[(int) *q]) (q, &p, VA_P(ap), width, convert, &ok);

        if(!ok)
            break;

        if(convert)
            ++n;

        /*
        **  p will already have been incremented by the convert function
        **  so just increment q
        */
        ++q;
    }

    return n;
}
ID scvt_uS
TY static
MO ajfmt
LB core
XX
DE Conversion for %S to load a string
XX
PN [1]
PA r fmt const char*
PD Format string at conv char position
PX
PN [2]
PA w pos const char**
PD Input string current position
PX
PN [3]
PA r ap VALIST
PD Original arguments at current position
PX
PN [4]
PA r width ajint
PD Width
PX
PN [5]
PA r convert AjBool
PD ajFalse if %* was specified
PX
PN [6]
PA w ok AjBool*
PD set for a successful conversion
PX
RT void
RD 
RX
//


static void scvt_uS(const char *fmt, const char **pos, VALIST ap, ajint width,
                    AjBool convert, AjBool *ok)
{
    const char *p;
    const char *q;
    AjPStr *val = NULL;
    static const char *wspace = " \n\t";
    ajint c = 0;

    (void) fmt;                         /* make it used */

    p = *pos;

    *ok = ajFalse;

    if(width != INT_MIN)
        for(q = p; *q && c_notin((int) *q, wspace) && c < width; ++q, ++c);
    else
        for(q = p; *q && c_notin((int) *q, wspace); ++q);

    if(q - p)
    {
        if(convert)
        {
            val = (AjPStr *) va_arg(VA_V(ap), AjPStr *);
            ajStrAssignSubC(val, p, 0, q - p - 1);
        }

        *pos = q;
        *ok = ajTrue;
    }

    return;
}
ID scvt_d
TY static
MO ajfmt
LB core
XX
DE Conversion for %d to load an integer
XX
PN [1]
PA r fmt const char*
PD Format string at conv char position
PX
PN [2]
PA w pos const char**
PD Input string current position
PX
PN [3]
PA r ap VALIST
PD Original arguments at current position
PX
PN [4]
PA r width ajint
PD Width
PX
PN [5]
PA r convert AjBool
PD ajFalse if %* was specified
PX
PN [6]
PA w ok AjBool*
PD set for a successful conversion
PX
RT void
RD 
RX
//


static void scvt_d(const char *fmt, const char **pos, VALIST ap, ajint width,
                   AjBool convert, AjBool *ok)
{
    const char *p;
    const char *q;
    long *val    = NULL;
    ajlong *hval = NULL;
    static const char *wspace = " \n\t";
    static const char *dig1 = "+-0123456789";
    ajint c = 0;
    AjPStr t = NULL;
    long  n   = 0;
    ajlong hn = 0;
    char  flag;
    ajint beg = 1;

    p = *pos;
    flag = *(fmt - 1);

    *ok = ajFalse;

    if(width != INT_MIN)
        for(q = p; *q && c_notin((int) *q, wspace) && c= '0' && c <= '9')
            d = c - '0';
        else
            d = c - 'A' + 10;

        v = (ajulong) 16 * v + (ajulong) d;
    }

    return v;
}
ID sc_octal
TY static
MO ajfmt
LB core
XX
DE Load a 64 bit unsigned long from a char* octal
XX
PN [1]
PA r str const char*
PD long hex number
PX
RT ajulong
RD result
RX
//


static ajulong sc_octal(const char *str)
{
    ajulong v = 0;
    ajint d;
    const char *p;
    char c;

    p = str + 1;

    while((c = toupper((int) *(p++))))
    {
        d = c - '0';
        v = (ajulong) 8 * v + (ajulong) d;
    }

    return v;
}
ID ajFtpOpen
TY public
MO ajftp
LB core
XX
DE Opens an FTP connection
XX
PN [1]
PA r dbname const AjPStr
PD Database name (for error reporting)
PX
PN [2]
PA r host const AjPStr
PD Host name
PX
PN [3]
PA r iport ajint
PD Port
PX
PN [4]
PA r get const AjPStr
PD GET string
PX
PN [5]
PA r fpos ajlong
PD Start position offset
PX
PN [6]
PA u Psock AjPSysSocket
PD Socket returned to caller
PX
RT FILE*
RD Open file on success, NULL on failure
RX
//


FILE* ajFtpOpen(const AjPStr dbname, const AjPStr host, ajint iport,
                const AjPStr get, ajlong fpos, AjPSysSocket Psock)
{
    FILE* fp;
    struct addrinfo hints;
    struct addrinfo *add = NULL;
    struct addrinfo *addinit = NULL;

    AjPStr portstr = NULL;

    const char *phost = NULL;
    const char *pport = NULL;

    AjOSysSocket sock = *Psock;

    AjPStr errstr = NULL;
    int ret;

    phost = ajStrGetPtr(host);
    ajDebug("ajFtpOpen db: '%S' host '%S' port: %u get: '%S'\n",
	    dbname, host, iport, get);

    memset(&hints,0,sizeof(hints));

    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    portstr = ajStrNew();
    ajFmtPrintS(&portstr,"%d",iport);
    pport =  ajStrGetPtr(portstr);

    ret = getaddrinfo(phost, pport, &hints, &addinit);

    ajStrDel(&portstr);
    
    if(ret)
    {
	ajErr("[%s] Failed to find host '%S' for database '%S'",
	      gai_strerror(ret), host, dbname);

	return NULL;
    }

    sock.sock = AJBADSOCK;
    
    for(add = addinit; add; add = add->ai_next)
    {
        sock.sock = ajSysFuncSocket(add->ai_family, add->ai_socktype,
                                    add->ai_protocol);

        if(sock.sock == AJBADSOCK)
            continue;

        if(connect(sock.sock, add->ai_addr, add->ai_addrlen))
        {
            ajSysSocketclose(sock);
            sock.sock = AJBADSOCK;
            continue;
        }

        break;
    }

    freeaddrinfo(addinit);

    if(sock.sock == AJBADSOCK)
    {
	ajDebug("Socket connect failed\n");
	ajFmtPrintS(&errstr, "socket connect failed for database '%S'",
		    dbname);
	ajErr("%S", errstr);
	perror(ajStrGetPtr(errstr));
	ajStrDel(&errstr);

	return NULL;
    }


/*
** read welcome messages and other responses all with \r\n at the end of line
220- first line
220- middle line(s)
220 last line has no -
*/

    fp = ftpSend(dbname, sock, get, fpos);

    return fp;
}
ID ftpSend
TY static
MO ajftp
LB core
XX
DE Send FTP request to an open socket
XX
PN [1]
PA r dbname const AjPStr
PD Database name (for error reporting)
PX
PN [2]
PA u sock AjOSysSocket
PD Socket structure
PX
PN [3]
PA r get const AjPStr
PD GET string
PX
PN [4]
PA r fpos ajlong
PD Start position offset
PX
RT FILE*
RD Open file on success, NULL on failure
RX
//


static FILE* ftpSend(const AjPStr dbname,
                     AjOSysSocket sock,
                     const AjPStr get,
                     ajlong fpos)
{
    FILE* fp       = NULL;
    AjPStr ftpcmd = NULL;
    AjPStr ftpresponse = NULL;
    AjPStr pasvresponse = NULL;
    ajuint ia=0, ib=0, ic=0, id=0;
    ajuint ip1=0, ip2=0;
    ajuint iport=21;
    ajint ipos;
    ajint iscan;
    AjPStr hoststr = NULL;
    AjPStr portstr = NULL;
    AjPStr errstr = NULL;
    struct addrinfo hints;
    struct addrinfo *add = NULL;
    struct addrinfo *addinit = NULL;
    AjOSysSocket filesock;
    int ret;
    AjPFile ftpfile = NULL;

/*    AjPStr cred = NULL;*/
    
    ajint isendlen;

    ajDebug("ftpSend: Sending to socket '%S'\n", get);

    ftpcmd = ajStrNew();

/*
    if(ajStrGetLen(proxyauth))
    {
        if(!ajStrMatchCaseC(proxyauth,"Basic"))
            ajErr("Only 'Basic' proxy authentication currently implemented,\n"
                  "no 'Digest' or 'NTLM' [%S]",proxyauth);

        cred = ajStrNew();
        
        ajUtilBase64EncodeC(&cred, ajStrGetLen(proxycreds),
                            (const unsigned char *) ajStrGetPtr(proxycreds));

        ajFmtPrintS(&ftpcmd,"Proxy-Authorization: Basic %S\r\n",cred);

        isendlen =  send(sock.sock, ajStrGetPtr(ftpcmd),
                         ajStrGetLen(ftpcmd), 0);

        if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(ftpcmd))
            ajErr("send failure, expected %u bytes returned %d : %s\n",
                  ajStrGetLen(ftpcmd), isendlen, ajMessGetSysmessageC());
        ajDebug("sending: '%S'\n", ftpcmd);
        if(isendlen < 0)
            ajDebug("send for host errno %d msg '%s'\n",
                    errno, ajMessGetSysmessageC());

        ajStrDel(&cred);
    }
*/

    ftpfile = ajFileNewFromCfile(ajSysFdFromSocket(sock, "r"));

    ftpResponse(ftpfile, &ftpresponse);

    ajFmtPrintS(&ftpcmd, "USER anonymous\r\n");
    isendlen =  send(sock.sock, ajStrGetPtr(ftpcmd),
                     ajStrGetLen(ftpcmd), 0);
    if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(ftpcmd))
	ajErr("send failure, expected %u bytes returned %d : %s\n",
	      ajStrGetLen(ftpcmd), isendlen, ajMessGetSysmessageC());
    ajDebug("sending: '%S'\n", ftpcmd);
    ftpResponse(ftpfile, &ftpresponse);

    ajFmtPrintS(&ftpcmd, "PASS emboss-bug@emboss.open-bio.org\r\n");
    isendlen =  send(sock.sock, ajStrGetPtr(ftpcmd),
                     ajStrGetLen(ftpcmd), 0);
    if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(ftpcmd))
	ajErr("send failure, expected %u bytes returned %d : %s\n",
	      ajStrGetLen(ftpcmd), isendlen, ajMessGetSysmessageC());
    ajDebug("sending: '%S'\n", ftpcmd);
    ftpResponse(ftpfile, &ftpresponse);


    ajFmtPrintS(&ftpcmd, "TYPE I\r\n");
    isendlen =  send(sock.sock, ajStrGetPtr(ftpcmd),
                     ajStrGetLen(ftpcmd), 0);
    if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(ftpcmd))
	ajErr("send failure, expected %u bytes returned %d : %s\n",
	      ajStrGetLen(ftpcmd), isendlen, ajMessGetSysmessageC());
    ajDebug("sending: '%S'\n", ftpcmd);
    ftpResponse(ftpfile, &ftpresponse);

    ajFmtPrintS(&ftpcmd, "PASV\r\n");
    isendlen =  send(sock.sock, ajStrGetPtr(ftpcmd),
                     ajStrGetLen(ftpcmd), 0);
    if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(ftpcmd))
	ajErr("send failure, expected %u bytes returned %d : %s\n",
	      ajStrGetLen(ftpcmd), isendlen, ajMessGetSysmessageC());
    ajDebug("sending: '%S'\n", ftpcmd);
    ftpResponse(ftpfile, &pasvresponse);

    ajFmtPrintS(&ftpcmd, "SIZE %S\r\n", get);
    isendlen =  send(sock.sock, ajStrGetPtr(ftpcmd),
                     ajStrGetLen(ftpcmd), 0);
    if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(ftpcmd))
	ajErr("send failure, expected %u bytes returned %d : %s\n",
	      ajStrGetLen(ftpcmd), isendlen, ajMessGetSysmessageC());
    ajDebug("sending: '%S'\n", ftpcmd);
    ftpResponse(ftpfile, &ftpresponse);
    if(ajStrGetCharFirst(ftpresponse) != '2')
    {
        ajDebug("FTP failure: File not found\nResponse: %S",
               ftpresponse);
        return NULL;
    }

    if(fpos)
    {
        ajFmtPrintS(&ftpcmd, "REST %Ld\r\n", fpos);
        isendlen =  send(sock.sock, ajStrGetPtr(ftpcmd),
                         ajStrGetLen(ftpcmd), 0);
        if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(ftpcmd))
            ajErr("send failure, expected %u bytes returned %d : %s\n",
                  ajStrGetLen(ftpcmd), isendlen, ajMessGetSysmessageC());
        ajDebug("sending: '%S'\n", ftpcmd);
        ftpResponse(ftpfile, &ftpresponse);
    }

    ajFmtPrintS(&ftpcmd, "RETR %S\r\n", get);
    isendlen =  send(sock.sock, ajStrGetPtr(ftpcmd),
                     ajStrGetLen(ftpcmd), 0);
    if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(ftpcmd))
	ajErr("send failure, expected %u bytes returned %d : %s\n",
	      ajStrGetLen(ftpcmd), isendlen, ajMessGetSysmessageC());
    ajDebug("sending: '%S'\n", ftpcmd);
    /*ftpResponse(sock, &ftpresponse);*/

    ipos = (ajint) ajStrFindAnyK(pasvresponse, '(');
    if(ipos < 4)
        return NULL;

    if(ajStrPrefixC(pasvresponse, "227 "))
    {
        ajStrCutStart(&pasvresponse, ipos);
        iscan = ajFmtScanS(pasvresponse, "(%u,%u,%u,%u,%u,%u)",
                           &ia, &ib, &ic, &id, &ip1, &ip2);
        
        iport = ip1*256+ip2;
        if(iscan != 6)
        {
            ajWarn("FTP failure: Passive mode unexpected reply\nResponse: %S",
               pasvresponse);
            return NULL;
        }
    }
    else if(ajStrPrefixC(pasvresponse, "228 "))
    {
        ajWarn("FTP failure: Long passive mode not supported\nResponse: %S",
               pasvresponse);
        return NULL;
    }
    else if(ajStrPrefixC(pasvresponse, "229 "))
    {
        ajWarn("FTP failure: Extended passive mode not supported\nResponse: %S",
               pasvresponse);
        return NULL;
    }

    ajFmtPrintS(&hoststr, "%u.%u.%u.%u", ia, ib, ic, id);
    ajFmtPrintS(&portstr, "%u", iport);

    ajDebug("PASV mode %S:%S '%S'\n", hoststr, portstr, pasvresponse);

    memset(&hints,0,sizeof(hints));

    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    ret = getaddrinfo(MAJSTRGETPTR(hoststr), MAJSTRGETPTR(portstr),
                      &hints, &addinit);

    if(ret)
    {
	ajErr("[%s] Failed to find host '%S' for database '%S'",
	      gai_strerror(ret), hoststr, dbname);

        ajStrDel(&hoststr);
        ajStrDel(&portstr);

	return NULL;
    }

    ajStrDel(&hoststr);
    ajStrDel(&portstr);

    filesock.sock = AJBADSOCK;
    
    for(add = addinit; add; add = add->ai_next)
    {
        filesock.sock = ajSysFuncSocket(add->ai_family, add->ai_socktype,
                                        add->ai_protocol);

        if(filesock.sock == AJBADSOCK)
            continue;

        if(connect(filesock.sock, add->ai_addr, add->ai_addrlen))
        {
            ajSysSocketclose(filesock);
            filesock.sock = AJBADSOCK;
            continue;
        }

        break;
    }

    freeaddrinfo(addinit);

    if(filesock.sock == AJBADSOCK)
    {
	ajDebug("Socket connect failed\n");
	ajFmtPrintS(&errstr, "socket connect failed for database '%S'",
		    dbname);
	ajErr("%S", errstr);
	perror(ajStrGetPtr(errstr));
	ajStrDel(&errstr);

	return NULL;
    }


    /*
    ** need to check response for the IP address and port to use for
    ** the passive connection
    227 Entering Passive Mode (10,42,5,11,146,64)
    10.42.5.11:(146.46)
    */

/*    ajFmtPrintS(&ftpcmd, "PASV\r\n");*/

/*    ajFmtPrintS(&ftpcmd, "REST %Ld\r\n", 0L);*/

    ajStrDel(&ftpcmd);

    /*ajFileClose(&ftpfile);*/

    fp = ajSysFdFromSocket(filesock, "r");
    
    if(!fp)
    {
	ajDebug("ftpSend socket open failed\n");
	ajErr("ftpSend: socket open failed for database '%S'", dbname);

	return NULL;
    }

    ajStrDel(&ftpresponse);
    ajStrDel(&pasvresponse);

    ajFileClose(&ftpfile);

    return fp;
}
ID ajFtpRead
TY public
MO ajftp
LB core
XX
DE Reads the header of ftp response in given buffer buff,
XX
PN [1]
PA r dbname const AjPStr
PD DB name
PX
PN [2]
PA r host const AjPStr
PD Host name
PX
PN [3]
PA r port ajint
PD Port
PX
PN [4]
PA r fpos ajlong
PD Start position offset
PX
PN [5]
PA r dbpath const AjPStr
PD part of URL after port number
PX
RT AjPFilebuff
RD http response
RX
//


AjPFilebuff ajFtpRead(const AjPStr dbname,
                      const AjPStr host,
                      ajint port, ajlong fpos, const AjPStr dbpath)
{
    AjPStr get       = NULL;
    AjPStr newhost   = NULL;
    AjPStr path      = NULL;
    AjPFilebuff buff = NULL;
    FILE* fp         = NULL;

    AjOSysSocket sock;
    AjOSysTimeout timo;

    get       = ajStrNew();
    newhost   = ajStrNew();

    ajDebug("ajFtpRead db: '%S' host '%S' port: %u dbpath: '%S'\n",
	    dbname, host, port, dbpath);

    ajStrAssignS(&newhost,host);
    ajStrAssignS(&path, dbpath);


    if(ajStrGetCharFirst(path)!='/')
        ajStrInsertK(&path, 0, '/');

    /*ajFmtPrintS(&get, "GET %S HTTP/%S\r\n", path, httpver);*/
        
    fp = ajFtpOpen(dbname, newhost, port, path, fpos, &sock);

    if(!fp)
    {
        if(port == 21)
            ajErr("Cannot open FTP connection 'ftp://%S%S'",
                  host, dbpath);
        else
            ajErr("Cannot open FTP connection 'ftp://%S:%d%S'",
                  host, port, dbpath);
        buff = NULL;
        return NULL;
    }

    buff = ajFilebuffNewFromCfile(fp);

    if(!buff)
    {
        ajErr("ajFtpRead: socket buffer attach failed for host '%S'"
              ", FTP command was '%S'", host, get);
        return NULL;
    }

    ajDebug("ajFtpRead created file %S:%d%S\n", host, port, dbpath);
    timo.seconds = 180;
    ajSysTimeoutSet(&timo);
    ajSysTimeoutUnset(&timo);

    ajStrDel(&get);
    ajStrDel(&newhost);
    ajStrDel(&path);

    return buff;
}
ID ftpResponse
TY static
MO ajftp
LB core
XX
DE Read the response from the FTP server
XX
PN [1]
PA u ftpfile AjPFile
PD File to read from open socket
PX
PN [2]
PA u Presponse AjPStr*
PD Response from server
PX
RT AjBool
RD True on success
RX
//


static AjBool ftpResponse(AjPFile ftpfile, AjPStr* Presponse)
{
    AjBool done = ajFalse;
    AjPStr ftpline = NULL;

    ajStrAssignC(Presponse, "");

    while(!done)
    {
        if(!ajReadline(ftpfile, &ftpline))
            return ajFalse;

        ajDebug("FTP read line: '%S'\n", ftpline);
        ajStrAppendS(Presponse, ftpline);

        if(ajStrGetCharPos(ftpline, 3) != '-')
            done = ajTrue;
    }
    ajDebug("FTP success\n");

    ajStrDel(&ftpline);

    return ajTrue;
}
ID graphType
TY list
MO ajgraph
LB graphics
XX
DE Graph functions to open and display a graph with the PLPLOT
DE device drivers
XX
//


static GraphOType graphType[] =
{
    /*Name         Device     Extension Width Height
     PlPlot   Alias    Screen   Onefile  Fixedsize Interactive
     DisplayFunction       OpenFunction
     Description*/
    {"ps",         "ps",      ".ps",   720,  540,
     AJTRUE,  AJFALSE, AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "Postscript"},
    {"postscript", "ps",      ".ps",    720,  540,
     AJTRUE,  AJTRUE,  AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "Postscript"},

#ifndef WIN32
    {"hpgl",       "lj_hpgl", ".hpgl",  11000,7200,
     AJTRUE,  AJFALSE, AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "HPGL general"},

    {"hp7470",     "hp7470",  ".hpgl",  10300,7650,
     AJTRUE,  AJFALSE,  AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "HPGL 7470 pen plotter"},

    {"hp7580",     "hp7580",  ".hpgl",  9000, 5580,
     AJTRUE,  AJFALSE,  AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "HPGL 7580 pen plotter"},
#endif

    {"meta",       "plmeta",  ".meta",  0,    0,
     AJTRUE,  AJFALSE,  AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "Metafile"},

    {"cps",        "psc",     ".ps",    720,  540,
     AJTRUE,  AJFALSE, AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "Colour postscript"},

    {"colourps",   "psc",     ".ps",    720,  540,
     AJTRUE,  AJTRUE,  AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "Colour postscript"},

#ifndef WIN32
#ifndef X_DISPLAY_MISSING /* X11 is  available */
    {"x11",        "xwin",    "null",   1440, 900,
     AJTRUE,  AJFALSE, AJTRUE,  AJTRUE,  AJFALSE,  AJTRUE,
     &GraphxyDisplayXwin,   &GraphOpenXwin,
     "X11 in new window"},

    {"xwindows",   "xwin",    "null",   1440, 900,
     AJTRUE,  AJTRUE,  AJTRUE,  AJTRUE,  AJFALSE,  AJTRUE,
     &GraphxyDisplayXwin,   &GraphOpenXwin,
     "X11 in new window"},
#endif

    {"tek",        "tek4107t","null",   1024, 780,
     AJTRUE,  AJFALSE, AJTRUE,  AJTRUE,  AJFALSE,  AJTRUE,
     &GraphxyDisplayXwin,   &GraphOpenSimple,
     "Tektronix screen graphics"},

    {"tek4107t",   "tek4107t","null",   1024, 780,
     AJTRUE,  AJTRUE,  AJTRUE,  AJTRUE,  AJFALSE,  AJTRUE,
     &GraphxyDisplayXwin,   &GraphOpenSimple,
     "Tektronix model 4107 screen graphics"},

    {"tekt",       "tekt",    "null",   1024, 780,
     AJTRUE,  AJFALSE, AJTRUE,  AJTRUE,  AJFALSE,  AJTRUE,
     &GraphxyDisplayXwin,   &GraphOpenSimple,
     "Tektronix screen graphics"},

    {"tektronics", "tekt",    "null",   1024, 780,
     AJTRUE,  AJTRUE,  AJTRUE,  AJTRUE,  AJFALSE,  AJTRUE,
     &GraphxyDisplayXwin,   &GraphOpenSimple,
     "Tektronix screen graphics"},

#endif

    {"none",       "null",    "null",  0,    0,
     AJTRUE,  AJFALSE, AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayXwin,   &GraphOpenSimple,
     "No output"},

    {"null",       "null",    "null",  0,    0,
     AJTRUE,  AJTRUE,  AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayXwin,   &GraphOpenSimple,
     "No output"},

    {"text",       "null",    "null",  0,    0,
     AJTRUE,  AJTRUE,  AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayXwin,   &GraphOpenSimple,
     "Text file"},
/* data file output */
  
    {"data",       "data",    ".dat",  960,  768,
     AJTRUE,  AJFALSE, AJFALSE, AJTRUE,  AJTRUE,  AJFALSE,
     &GraphxyDisplayToData, &GraphOpenData,
     "Data file for Staden package"},

    {"das",        "das",     ".das",  0,    0,
     AJTRUE,  AJFALSE, AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToDas,  NULL,
     "Data file for DAS server"},

    {"wig",        "wig",     ".wig",  0,    0,
     AJTRUE,  AJFALSE, AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     GraphxyDisplayToWiggle,  NULL,
     "Wiggle data for genome browsers"},

    {"bedgraph",   "bedgraph", ".bedgraph",  0,    0,
     AJTRUE,  AJFALSE, AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToBedgraph,  NULL,
     "BedGraph data for genome browsers"},

#ifndef WIN32
#ifndef X_DISPLAY_MISSING /* X11 is available */
    {"xterm",      "xterm",   "null",   1024, 780,
     AJTRUE,  AJFALSE, AJTRUE,  AJTRUE,  AJFALSE,  AJTRUE,
     &GraphxyDisplayXwin,   &GraphOpenXwin,
     "Xterm screen graphics"},
#endif
#endif

#ifdef PLD_png          /* if png/gd/zlib libraries available for png driver */
    {"png",        "png",     ".png",   800,  600,
     AJTRUE,  AJFALSE, AJFALSE, AJFALSE, AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "PNG graphics files"},
#ifndef WIN32
    {"gif",        "gif",     ".gif",   640,  480,
     AJTRUE,  AJFALSE, AJFALSE, AJFALSE, AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "GIF graphics files"},
#endif
#endif

#ifdef WIN32
    {"win3",        "win3",     "null", 720,  540,
     AJTRUE, AJFALSE, AJTRUE,  AJTRUE,  AJFALSE,  AJTRUE,
     &GraphxyDisplayXwin,     &GraphOpenXwin,
     "Windows screen graphics"},
#endif

#ifdef PLD_pdf          /* if png/gd/zlib libraries available for png driver */
    {"pdf",        "pdf",     ".pdf",   1683,  1190,
     AJTRUE,  AJFALSE, AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "PDF graphics files"},
#endif

    {"svg",        "svg",     ".svg",   720,  540,
     AJTRUE,  AJFALSE, AJFALSE, AJTRUE,  AJFALSE,  AJFALSE,
     &GraphxyDisplayToFile, &GraphOpenFile,
     "SVG graphics files"},
    
    {NULL, NULL, NULL, 0, 0,
     AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE,
     NULL, NULL,
     NULL}
};
ID ajGraphicsClose
TY public
MO ajgraph
LB graphics
XX
DE Close current Plot.
XX
RT void
RD 
RX
//


void ajGraphicsClose(void)
{
    ajDebug("ajGraphicsClose\n");
    GraphClose();

    return;
}
ID ajGraphicsCloseWin
TY public
MO ajgraph
LB graphics
XX
DE Close current window Plot
XX
RT void
RD 
RX
//


void ajGraphicsCloseWin(void)
{
    ajDebug("ajGraphicsCloseWin\n");
    GraphClose();

    return;
}
ID ajGraphicsPlenv
TY public
MO ajgraph
LB graphics
XX
DE Defines a plot environment. i.e. tells plplot whether the graph is boxed,
DE whether it has tick marks, whether it has labels etc. These should already
DE be set in the flags.
XX
PN [1]
PA r xmin float
PD X axis start
PX
PN [2]
PA r xmax float
PD X axis end
PX
PN [3]
PA r ymin float
PD Y axis start
PX
PN [4]
PA r ymax float
PD Y axis end
PX
PN [5]
PA r flags ajint
PD flag bit settings
PX
RT void
RD 
RX
//


void ajGraphicsPlenv(float xmin, float xmax, float ymin, float ymax,
                     ajint flags)
{
    char xopt[15] = " ";
    char yopt[15] = " ";
    ajuint i = 0;
    ajuint j = 0;

    /* ignore 'a' axis at zero value */
    /* ignore 'd' label plot as date/time */
    /* ignore 'f' fixed point labels */
    /* ignore 'h' minor interval grid */
    /* ignore 'l' logarithmic axis */

    ajDebug("ajGraphicsPlenv(%.3f, %.3f, %.3f, %.3f, flags:%x)\n",
	     xmin, xmax, ymin, ymax, flags);
    GraphSubPage(0);
    GraphSetWin2(xmin,xmax,ymin,ymax);
    
    if(flags & AJGRAPH_X_BOTTOM)
	xopt[i++] = 'b';
    
    if(flags & AJGRAPH_Y_LEFT)
	yopt[j++] = 'b';
    
    if(flags & AJGRAPH_X_TOP)
	xopt[i++] = 'c';
    
    if(flags & AJGRAPH_Y_RIGHT)
	yopt[j++] = 'c';
    
    if(flags & AJGRAPH_X_TICK)
    {
	xopt[i++] = 't';		/* do ticks */
	xopt[i++] = 's';		/* do subticks */

	if(flags & AJGRAPH_X_INVERT_TICK)
	    xopt[i++] = 'i';

	if(flags & AJGRAPH_X_NUMLABEL_ABOVE)
	    xopt[i++] = 'm';
	else
	    xopt[i++] = 'n';		/* write numeric labels */
    }
    
    if(flags & AJGRAPH_Y_TICK)
    {
	yopt[j++] = 't';
	yopt[j++] = 's';

	if(flags & AJGRAPH_Y_INVERT_TICK)
	    yopt[j++] = 'i';

	if(flags & AJGRAPH_Y_NUMLABEL_LEFT)
	    yopt[j++] = 'm';
	else
	    yopt[j++] = 'n';
    }
    
    if(flags & AJGRAPH_X_GRID)
	xopt[i++] = 'g';
    
    if(flags & AJGRAPH_Y_GRID)
	yopt[j++] = 'g';

    /* ignore minor interval grid option 'h' */

    xopt[i] = '\0';
    yopt[j] = '\0';

#if GRAPH_DEBUG
    ajDebug("=g= plbox('%s', 0.0, 0, '%s', 0.0, 0) "
      "[xopt, 0.0, 0, yopt, 0.0, 0]\n", xopt, yopt);
#endif
    plbox(xopt, 0.0, 0, yopt, 0.0, 0);
    
    return;
}
ID ajGraphicsResetBgcolour
TY public
MO ajgraph
LB graphics
XX
DE Reset the background colour to either black or white depending on the
DE current settings.
XX
RT void
RD 
RX
//


void ajGraphicsResetBgcolour(void)
{
    GraphColourBack();
    return;
}
ID ajGraphicsResetFgcolour
TY public
MO ajgraph
LB graphics
XX
DE Reset the foreground plotting colour using current stored colour.
XX
RT void
RD 
RX
//


void ajGraphicsResetFgcolour(void)
{
    GraphColourFore();
    return;
}
ID ajGraphicsSetBgcolourBlack
TY public
MO ajgraph
LB graphics
XX
DE Set the background colour to black
XX
RT void
RD 
RX
//


void ajGraphicsSetBgcolourBlack(void)
{
    ajDebug("ajGraphicsSetBgcolourBlack currentbgwhite: %B\n", currentbgwhite);

    if(currentbgwhite)
    {
	currentbgwhite = ajFalse;

	GraphPen(0, 0, 0, 0);
	GraphPen(MAXCOL, 255, 255, 255);

	graphColourName[0] = "BLACK";
	graphColourName[MAXCOL] = "WHITE";

	ajDebug("ajGraphicsSetBgcolourBlack pen 0 '%s' pen %d '%s'\n",
		graphColourName[0], MAXCOL, graphColourName[MAXCOL]);
    }

    return;
}
ID ajGraphicsSetBgcolourWhite
TY public
MO ajgraph
LB graphics
XX
DE Set the background colour to white (the default colour)
XX
RT void
RD 
RX
//


void ajGraphicsSetBgcolourWhite(void)
{
    ajDebug("ajGraphicsSetBgcolourWhite currentbgwhite: %B\n", currentbgwhite);

    if(!currentbgwhite)
    {
	currentbgwhite = ajTrue;

	GraphPen(0, 255, 255, 255);
	GraphPen(MAXCOL, 0, 0, 0);

	graphColourName[0] = "WHITE";
	graphColourName[MAXCOL] = "BLACK";

	ajDebug("ajGraphicsSetBgcolourWhite pen 0 '%s' pen %d '%s'\n",
		graphColourName[0], MAXCOL, graphColourName[MAXCOL]);
    }

    return;
}
ID ajGraphicsSetCharscale
TY public
MO ajgraph
LB graphics
XX
DE Set the character scale factor
XX
PN [1]
PA r scale float
PD character scale.
PX
RT float
RD the previous character scale factor.
RX
//


float ajGraphicsSetCharscale(float scale)
{
    float oldscale;

    oldscale = currentcharscale;

    if(scale)
    {
	currentcharscale = scale;
	GraphCharScale(currentcharscale);
    }

    return oldscale;
}
ID ajGraphicsSetCharsize
TY public
MO ajgraph
LB graphics
XX
DE Set the character size factor
XX
PN [1]
PA r size float
PD character size.
PX
RT float
RD the previous character size factor.
RX
//


float ajGraphicsSetCharsize(float size)
{
    float oldsize;

    oldsize = currentcharsize;

    if(size)
    {
	currentcharsize = size;
#if 0
	currentcharht = size;
#endif
        currentcharscale = 1.0;
	plschr(currentcharsize, currentcharscale);
    }

    return oldsize;
}
ID ajGraphicsSetDefcharsize
TY public
MO ajgraph
LB graphics
XX
DE Set the default character size in mm.
XX
PN [1]
PA r size float
PD character size in mm.
PX
RT float
RD the previous character size in mm.
RX
//


float ajGraphicsSetDefcharsize(float size)
{
    float oldsize = 0.0;
    float oldscale = 0.0;

    ajGraphicsGetCharsize(&oldsize, &oldscale);
    GraphDefCharSize((PLFLT)size);

    return oldsize;
}
ID ajGraphicsSetDevice
TY public
MO ajgraph
LB graphics
XX
DE Set internal graph device to that selected for a graph object
XX
PN [1]
PA r graph const AjPGraph
PD Graph object with display type set
PX
RT void
RD 
RX
//


void ajGraphicsSetDevice(const AjPGraph graph)
{
#if GRAPH_DEBUG
    ajDebug("=g= plsdev ('%s') [graphType[%d].Device] ready: %B\n",
            graphType[graph->displaytype].Device,
            graph->displaytype, graph->ready);
#endif

    if(!graph->ready)
        plsdev(graphType[graph->displaytype].Device);

    return;
}
ID ajGraphicsSetFgcolour
TY public
MO ajgraph
LB graphics
XX
DE Set the foreground plotting colour
XX
PN [1]
PA r colour ajint
PD colour to set drawing to.
PX
RT ajint
RD the previous colour.
RX
//


ajint ajGraphicsSetFgcolour(ajint colour)
{
    ajint oldcolour;
    ajint col;

    col = colour;

    /* in case of looping through colours */
    /* start at the beginning once past end */
    while(col > MAXCOL)
	col -= (MAXCOL+1);

    /*
    if(col < 0)
	ajDebug("ajGraphicsSetFgcolour give up and use "
                "currentfgcolour %d '%s'\n",
		 currentfgcolour, graphColourName[currentfgcolour]);
    */

    if(col < 0)
	return currentfgcolour;

    oldcolour = currentfgcolour;
    currentfgcolour = col;

    if(currentbgwhite)          /* swap black and white foreground */
    {
	if(col == 0)
	    col = MAXCOL;
	else if(col == MAXCOL)
	    col = 0;
    }

    /*ajDebug("ajGraphicsSetFgcolour (%d '%s') currentbgwhite: %B, "
	    "oldcolour: %d '%s'\n",
	    colour, graphColourName[colour], currentbgwhite,
	    oldcolour, graphColourName[oldcolour]);*/

    GraphColourFore();

    return oldcolour;
}
ID GraphDefCharSize
TY static
MO ajgraph
LB graphics
XX
DE Set the default char size in mm.
XX
PN [1]
PA r size float
PD Character size in mm. See PLPLOT.
PX
RT void
RD 
RX
//


static void GraphDefCharSize(float size)
{
    currentcharscale = 1.0;
    currentcharsize = size;
#if 0
    currentcharht = size;
#endif
    plschr(currentcharsize, currentcharscale);

    return;
}
ID GraphSetName
TY static
MO ajgraph
LB graphics
XX
DE Set BaseName and extension.
XX
PN [1]
PA r thys const AjPGraph
PD Graph object.
PX
PN [2]
PA r txt const AjPStr
PD base name for files
PX
PN [3]
PA r ext const char*
PD extension for files
PX
RT void
RD 
RX
//


static void GraphSetName(const AjPGraph thys,
			 const AjPStr txt, const char *ext)
{
    if(!thys->ready)
    {
#if GRAPH_DEBUG
        ajDebug("=g= plxsfnam ('%S', '%s')\n", txt, ext);
#endif

        plxsfnam(ajStrGetPtr(txt), ext);
        ajStrAssignS(&graphBasename, txt);
        ajStrAssignC(&graphExtension, ext);
        if(ajStrGetCharFirst(graphExtension) == '.')
            ajStrCutStart(&graphExtension, 1);
    }

    return;
}
ID ajGraphicsSetFilename
TY public
MO ajgraph
LB graphics
XX
DE set BaseName and extension.
XX
PN [1]
PA r graph const AjPGraph
PD Graph object.
PX
RT void
RD 
RX
//


void ajGraphicsSetFilename(const AjPGraph graph)
{
    if(!ajCharMatchCaseC(graphType[graph->displaytype].Ext, "null"))
        GraphSetName(graph, graph->outputfile,
                     graphType[graph->displaytype].Ext);

    return;
}
ID ajGraphicsSetFillpat
TY public
MO ajgraph
LB graphics
XX
DE Set the Fill Pattern type.
XX
PN [1]
PA r patstyle ajint
PD line style to set drawing to.
PX
RT ajint
RD the previous line style.
RX
//


ajint ajGraphicsSetFillpat(ajint patstyle)
{
    ajint oldstyle;

    if(patstyle < 0 || patstyle > 8)
	patstyle = 1;

    oldstyle = currentfillpattern;
    currentfillpattern = patstyle;
    GraphFillPat((PLINT)patstyle);

    return oldstyle;
}
ID ajGraphicsSetLabelsC
TY public
MO ajgraph
LB graphics
XX
DE Label current Plot.
XX
PN [1]
PA r x const char*
PD text for x axis labelling.
PX
PN [2]
PA r y const char*
PD text for y axis labelling.
PX
PN [3]
PA r title const char*
PD text for title of plot.
PX
PN [4]
PA r subtitle const char*
PD text for subtitle of plot.
PX
RT void
RD 
RX
//


void ajGraphicsSetLabelsC(const char *x, const char *y,
                          const char *title, const char *subtitle)
{
    float fold;

#if GRAPH_DEBUG
    ajDebug("=g= plmtex('t', 2.5, 0.5, 0.5, '%s') [title]\n", title);
#endif

    plmtex("t", (PLFLT) 2.5, (PLFLT) 0.5, (PLFLT) 0.5, title);

#if GRAPH_DEBUG
    ajDebug("=g= plmtex('b', 3.2, 0.5, 0.5, '%s') [x-title]\n", x);
#endif

    plmtex("b", (PLFLT) 3.2, (PLFLT) 0.5, (PLFLT) 0.5, x);

#if GRAPH_DEBUG
    ajDebug("=g= plmtex('l', 5.0, 0.5, 0.5, '%s') [y-title]\n", y);
#endif

    plmtex("l", (PLFLT) 5.0, (PLFLT) 0.5, (PLFLT) 0.5, y);

    fold = ajGraphicsSetCharscale(0.0);
    ajGraphicsSetCharscale(fold/(float)2.0);

#if GRAPH_DEBUG
    ajDebug("=g= plmtex('t', 1.5, 0.5, 0.5, '%s') [subtitle]\n", subtitle);
#endif

    plmtex("t", (PLFLT) 1.5, (PLFLT) 0.5, (PLFLT) 0.5, subtitle);

    fold = ajGraphicsSetCharscale(fold);

    /*  pllab(x,y,title);*/

    return;
}
ID ajGraphicsSetLabelsS
TY public
MO ajgraph
LB graphics
XX
DE Label current Plot.
XX
PN [1]
PA r strx const AjPStr
PD text for x axis labelling.
PX
PN [2]
PA r stry const AjPStr
PD text for y axis labelling.
PX
PN [3]
PA r strtitle const AjPStr
PD text for title of plot.
PX
PN [4]
PA r strsubtitle const AjPStr
PD text for subtitle of plot.
PX
RT void
RD 
RX
//


void ajGraphicsSetLabelsS(const AjPStr strx, const AjPStr stry,
                          const AjPStr strtitle, const AjPStr strsubtitle)
{
    float fold;

#if GRAPH_DEBUG
    ajDebug("=g= plmtex('t', 2.5, 0.5, 0.5, '%S') [title]\n", strtitle);
#endif

    plmtex("t", (PLFLT) 2.5, (PLFLT) 0.5, (PLFLT) 0.5, MAJSTRGETPTR(strtitle));

#if GRAPH_DEBUG
    ajDebug("=g= plmtex('b', 3.2, 0.5, 0.5, '%S') [x-title]\n", strx);
#endif

    plmtex("b", (PLFLT) 3.2, (PLFLT) 0.5, (PLFLT) 0.5,  MAJSTRGETPTR(strx));


#if GRAPH_DEBUG
    ajDebug("=g= plmtex('l', 5.0, 0.5, 0.5, '%S') [y-title]\n", stry);
#endif

    plmtex("l", (PLFLT) 5.0, (PLFLT) 0.5, (PLFLT) 0.5,  MAJSTRGETPTR(stry));

    fold = ajGraphicsSetCharscale(0.0);
    ajGraphicsSetCharscale(fold/(float)2.0);

#if GRAPH_DEBUG
    ajDebug("=g= plmtex('t', 1.5, 0.5, 0.5, '%s') [subtitle]\n",
            MAJSTRGETPTR(strsubtitle));
#endif

    plmtex("t", (PLFLT) 1.5, (PLFLT) 0.5, (PLFLT) 0.5,
           MAJSTRGETPTR(strsubtitle));

    fold = ajGraphicsSetCharscale(fold);

    /*  pllab(x,y,strtitle);*/

    return;
}
ID ajGraphicsSetLinestyle
TY public
MO ajgraph
LB graphics
XX
DE Set the line style.
XX
PN [1]
PA r linestyle ajint
PD line style to set drawing to.
PX
RT ajint
RD the previous line style.
RX
//


ajint ajGraphicsSetLinestyle(ajint linestyle)
{
    ajint oldstyle;

    if(linestyle < 1 || linestyle > 8)
	linestyle = 1;

    oldstyle = currentlinestyle;
    currentlinestyle = linestyle;
    GraphLineStyle((PLINT)linestyle);

    return oldstyle;
}
ID ajGraphicsSetPagesize
TY public
MO ajgraph
LB graphics
XX
DE Set the width and height of the plot page.
DE
DE Ignored by most plplot drivers, but will be used by PNG
XX
PN [1]
PA r plotwidth ajuint
PD Page width in pixels or mm
PX
PN [2]
PA r plotheight ajuint
PD Page height in pixels or mm
PX
RT void
RD 
RX
//


void ajGraphicsSetPagesize(ajuint plotwidth, ajuint plotheight)
{
    plspage(0.0, 0.0, plotwidth, plotheight, 0, 0);

    return;
}
ID ajGraphicsSetPenwidth
TY public
MO ajgraph
LB graphics
XX
DE Set the current pen width.
XX
PN [1]
PA r penwidth float
PD width for the pen.
PX
RT void
RD 
RX
//


void ajGraphicsSetPenwidth(float penwidth)
{
#if GRAPH_DEBUG
    ajDebug("=g= plwid(%.2f) [width]\n", penwidth);
#endif

    plwid((PLINT)penwidth);

    return;
}
ID ajGraphicsSetPortrait
TY public
MO ajgraph
LB graphics
XX
DE Set graph orientation
XX
PN [1]
PA r set AjBool
PD portrait orientation if true, else landscape
PX
RT void
RD 
RX
//


void ajGraphicsSetPortrait(AjBool set)
{
#if GRAPH_DEBUG
    ajDebug("=g= plsori(%d) [ori]\n", set);
#endif

    if(set)
        plsori(1);
    else
        plsori(0);

    return;
}
ID ajGraphicsSetRlabelC
TY public
MO ajgraph
LB graphics
XX
DE Label the right hand y axis.
XX
PN [1]
PA r txt const char*
PD text for label of right y axis.
PX
RT void
RD 
RX
//

void ajGraphicsSetRlabelC(const char *txt)
{
#if GRAPH_DEBUG
    ajDebug("=g= plmtex('r', 2.0, 0.5, 0.5, '%s') [ajGraphicsDrawRlabelC]\n",
            txt);
#endif

    plmtex("r",2.0,0.5,0.5,txt);

    return;
}
ID ajGraphicsSetRlabelS
TY public
MO ajgraph
LB graphics
XX
DE Label the right hand y axis.
XX
PN [1]
PA r str const AjPStr
PD text for label of right y axis.
PX
RT void
RD 
RX
//

void ajGraphicsSetRlabelS(const AjPStr str)
{
#if GRAPH_DEBUG
    ajDebug("=g= plmtex('r', 2.0, 0.5, 0.5, '%S') [ajGraphicsDrawRlabelS]\n",
            str);
#endif

    plmtex("r",2.0,0.5,0.5,MAJSTRGETPTR(str));

    return;
}
ID GraphColourFore
TY static
MO ajgraph
LB graphics
XX
DE Set the foreground plotting colour using current stored colour.
XX
RT void
RD 
RX
//


static void GraphColourFore(void)
{
    ajint colour;

    colour = currentfgcolour;

    if(currentbgwhite)	 /* OKAY!! when we swap backgrounds we also */
    {
	if(colour == 0)	/* swap pens. User does not know this so switch */
	    colour = MAXCOL;		/* for them */
	else if(colour == MAXCOL)
	    colour = 0;
    }

    GraphSetPen(colour);
    /*ajDebug("GraphColourFore currentbgwhite: %B '%s' "
	    "currentfgcolour: %d (%s) => %d (%s)\n",
	    currentbgwhite, graphColourName[0],
	    currentfgcolour, graphColourName[currentfgcolour],
	    colour, graphColourName[colour]);*/

    return;
}
ID GraphTextLength
TY static
MO ajgraph
LB graphics
XX
DE Compute the length of a string in millimetres.
XX
PN [1]
PA u txt const char*
PD text string
PX
RT float
RD The length of the string in user coordinates
RX
//


static float GraphTextLength(const char *txt)
{
    return plstrl(txt);
}
ID GraphFillPat
TY static
MO ajgraph
LB graphics
XX
DE Set the pattern to fill with.
XX
PN [1]
PA r pat ajint
PD Pattern code. See PLPLOT.
PX
RT void
RD 
RX
//


static void GraphFillPat(ajint pat)
{
#if GRAPH_DEBUG
    ajDebug("=g= plpsty(%d) [pattern style]\n", pat);
#endif

    plpsty((PLINT)pat);

    return;
}
ID GraphLineStyle
TY static
MO ajgraph
LB graphics
XX
DE Set the Line style. i.e. dots dashes unbroken.
XX
PN [1]
PA r style ajint
PD Line style. See PLPLOT.
PX
RT void
RD 
RX
//


static void GraphLineStyle(ajint style)
{
#if GRAPH_DEBUG
    ajDebug("=g= pllsty(%d) [line style]\n", style);
#endif

    pllsty((PLINT)style);

    return;
}
ID GraphCharScale
TY static
MO ajgraph
LB graphics
XX
DE Set the char size.
XX
PN [1]
PA r scale float
PD Character scale factor. See PLPLOT.
PX
RT void
RD 
RX
//


static void GraphCharScale(float scale)
{
#if GRAPH_DEBUG
    ajDebug("=g= plschr(0.0, %.2f) [0.0 charscale]\n", scale);
#endif

    plschr(0.0,(PLFLT)scale);		/* use current size */

#if 0
    if(scale)
        currentcharht = currentcharsize*scale;
#endif

    return;
}
ID GraphLabelTitle
TY static
MO ajgraph
LB graphics
XX
DE Label current Plot with title and subtitle
XX
PN [1]
PA r title const char*
PD text for title of plot.
PX
PN [2]
PA r subtitle const char*
PD text for subtitle of plot.
PX
RT void
RD 
RX
//


static void GraphLabelTitle(const char *title, const char *subtitle)
{
    float fold;
    AjPStr tmpstr = NULL;

    tmpstr = ajStrNewC(title);
    if(ajStrGetLen(tmpstr) > 55)
    {
	ajStrKeepRange(&tmpstr, 0, 55);
	ajStrAppendC(&tmpstr, "...");
    }

#if GRAPH_DEBUG
    ajDebug("=g= plmtex('t', 2.5, 0.5, 0.5, '%S') [title]\n", tmpstr);
#endif
    plvsta();                   /* set standard viewport for standard titles */

    if(ajStrGetLen(tmpstr)) {
	fold = ajGraphicsSetCharscale(1.0);
	ajDebug("GraphLabelTitle title fold: %.3f size: %.3f scale %.3f\n",
		fold, currentcharsize, currentcharscale);
	plmtex("t", (PLFLT) 4.0, (PLFLT) 0.5, (PLFLT) 0.5,
	       ajStrGetPtr(tmpstr));
	fold = ajGraphicsSetCharscale(fold);
    }

    ajStrAssignC(&tmpstr, subtitle);

    if(ajStrGetLen(tmpstr) > 95)
    {
	ajStrKeepRange(&tmpstr, 0, 95);
	ajStrAppendC(&tmpstr, "...");
    }

#if GRAPH_DEBUG
    ajDebug("=g= plmtex('t', 1.5, 0.5, 0.5, '%S') [subtitle]\n", tmpstr);
#endif

    if(ajStrGetLen(tmpstr))
    {
	fold = ajGraphicsSetCharscale(1.0);
	ajGraphicsSetCharscale(0.5);
	ajDebug("GraphLabelTitle subtitle fold: %.3f size: %.3f scale %.3f\n",
		fold, currentcharsize, currentcharscale);
	plmtex("t", (PLFLT) 5.0, (PLFLT) 0.5, (PLFLT) 0.5,
	       ajStrGetPtr(tmpstr));
	fold = ajGraphicsSetCharscale(fold);
    }

    if (graphData)
    {
        ajFmtPrintF(graphData->File,"##Maintitle %s\n",title);
        graphData->Lines++;

        if(subtitle)
        {
            ajFmtPrintF(graphData->File,"##Subtitle %s\n",subtitle);
            graphData->Lines++;
        }
    }


    /*  pllab(x,y,title);*/

    ajStrDel(&tmpstr);
    return;
}
ID GraphSetPen
TY static
MO ajgraph
LB graphics
XX
DE Set the pen to the colour specified.
XX
PN [1]
PA r colour ajint
PD Pen colour (see PLPLOT)
PX
RT void
RD 
RX
//


static void GraphSetPen(ajint colour)
{
#if GRAPH_DEBUG
    ajDebug("=g= plcol(%d '%s') [colour]\n", colour, graphColourName[colour]);
#endif

    plcol((PLINT)colour);

    return;
}
ID GraphSetNumSubPage
TY static
MO ajgraph
LB graphics
XX
DE Sets the number of sub pages for a page.
XX
PN [1]
PA r numofsubpages ajuint
PD Number of subpages
PX
RT void
RD 
RX
//


static void GraphSetNumSubPage(ajuint numofsubpages)
{
#if GRAPH_DEBUG
    ajDebug("=g= plssub (1, %d) [numofsubpages]\n", numofsubpages);
#endif

    plssub(1,numofsubpages);

    return;
}
ID ajGraphicsBasecolourNewNuc
TY public
MO ajgraph
LB graphics
XX
DE Initialise a base colours array for a string of nucleotide
DE sequence characters
XX
PN [1]
PA r codes const AjPStr
PD Residue codes for each numbered position
PX
RT ajint*
RD Array of colours (see PLPLOT)
RX
//


ajint* ajGraphicsBasecolourNewNuc(const AjPStr codes)
{
    ajint *ret;
    const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    ajint colours[] =
        {
	GREEN,YELLOW,BLUE,YELLOW,YELLOW,YELLOW,
	BLACK,YELLOW,YELLOW,YELLOW,YELLOW,YELLOW,
	YELLOW,YELLOW,YELLOW,YELLOW,YELLOW,YELLOW,
	YELLOW,RED,RED,YELLOW,YELLOW,YELLOW,
	YELLOW,YELLOW,YELLOW
    };
    const char* cp;
    const char* cq;
    ajint i;
    ajint j;

    ret = (ajint *) AJALLOC0(sizeof(ajint)*AZ);	/* BLACK is zero */

    ajDebug("ajGraphGetBaseColourProt '%S'\n", codes);

    cp = ajStrGetPtr(codes);
    i = 1;

    while(*cp)
    {
	cq = strchr(alphabet, *cp);

	if(cq)
	{
	    j = cq - alphabet;
	    ret[i] = colours[j];
	    ajDebug("ColourProt %d: '%c' %d -> %d %s\n",
		    i, *cp, j, colours[j], graphColourName[colours[j]]);
	}

	i++;
	cp++;
    }

    return ret;
}
ID ajGraphicsBasecolourNewProt
TY public
MO ajgraph
LB graphics
XX
DE Initialize a base colours array for a string of protein sequence characters
DE according to the following colour scheme designed by Toby Gibson (EMBL
DE Heidelberg) to show the chemical properties important in sequence
DE and structure alignment and to be clear to those with common forms
DE of colour blindness.
DE
DE DE: Red (Acidic)
DE HKR: Blue (Basic)
DE NQ: Green (Amide)
DE AILV: Black (Hydrophobic Small)
DE FWY: Wheat (Hydrophobic Large)
DE ST: Cyan (Hydroxyl group)
DE CM: Yellow (Sulphur chemistry)
DE G: Grey (helix breaker I)
DE P: Violet (helix breaker II)
XX
PN [1]
PA r codes const AjPStr
PD Residue codes for each numbered position
PX
RT ajint*
RD Array of colours (see PLPLOT)
RX
//


ajint* ajGraphicsBasecolourNewProt(const AjPStr codes)
{
    ajint *ret;
    const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    ajint colours[] =
        {
	BLACK,BLACK,YELLOW,RED,RED,WHEAT, /* A-F */
	GREY,BLUE,BLACK,BLACK,BLUE,BLACK, /* G-L */
	YELLOW,GREEN,BLACK,BLUEVIOLET,GREEN,BLUE, /* M-R */
	CYAN,CYAN,BLACK,BLACK,WHEAT,BLACK, /* S-X */
	WHEAT,BLACK,BLACK,BLACK};	/* Y-Z plus 2 more */
    const char* cp;
    const char* cq;
    ajint i;
    ajint j;

    ret = (ajint *) AJALLOC0(sizeof(ajint)*AZ);	/* BLACK is zero */

    ajDebug("ajGraphGetBaseColourProt '%S'\n", codes);

    cp = ajStrGetPtr(codes);
    i = 1;

    while(*cp)
    {
	cq = strchr(alphabet, *cp);

	if(cq)
	{
	    j = cq - alphabet;
	    ret[i] = colours[j];
	    ajDebug("ColourProt %d: '%c' %d -> %d %s\n",
		    i, *cp, j, colours[j], graphColourName[colours[j]]);
	}

	i++;
	cp++;
    }

    return ret;
}
ID ajGraphicsCheckColourC
TY public
MO ajgraph
LB graphics
XX
DE Find if the colour is on the list
XX
PN [1]
PA r txt const char*
PD colour name.
PX
RT ajint
RD the colour number if found else -1.
RX
//


ajint ajGraphicsCheckColourC(const char* txt)
{
    ajint i;

    for(i=0;i<=MAXCOL;i++)
	if(ajCharMatchCaseC(txt,graphColourName[i]))
	    return i;

    return -1;
}
ID ajGraphicsCheckColourS
TY public
MO ajgraph
LB graphics
XX
DE Find if the colour is on the list
XX
PN [1]
PA r str const AjPStr
PD colour name.
PX
RT ajint
RD the colour number if found else -1.
RX
//


ajint ajGraphicsCheckColourS(const AjPStr str)
{
    ajint i;

    for(i=0;i<=MAXCOL;i++)
	if(ajStrMatchCaseC(str,graphColourName[i]))
	    return i;

    return -1;
}
ID ajGraphicsGetCharsize
TY public
MO ajgraph
LB graphics
XX
DE Get the char size.
XX
PN [1]
PA u defheight float*
PD where to store the default character height
PX
PN [2]
PA u currentscale float*
PD where to store the current (scaled)
PD character height
PX
RT void
RD 
RX
//


void ajGraphicsGetCharsize(float *defheight, float *currentscale)
{
    if(graphData)
    {
	*defheight = currentcharsize;
	*currentscale = currentcharscale;
    }
    else
    {
#if GRAPH_DEBUG
	ajDebug("=g= plgchr(&f &f) [defht:%f, scale:%f] plplot\n",
                *defheight, *currentscale);
#endif

	plgchr(defheight,currentscale);
    }

    return;
}
ID ajGraphicsGetFgcolour
TY public
MO ajgraph
LB graphics
XX
DE Return current foreground colour
XX
RT ajint
RD colour.
RX
//


ajint ajGraphicsGetFgcolour(void)
{
    return currentfgcolour;
}
ID ajGraphicsGetParamsPage
TY public
MO ajgraph
LB graphics
XX
DE Get the output page parameters for plplot internal use. These can be set by
DE the user and used by some device drivers.
DE
DE For graph data type, sets to zero as these are not applicable.
XX
PN [1]
PA u xp float*
PD where to store the x pixels/inch
PX
PN [2]
PA u yp float*
PD where to store the y pixels/inch
PX
PN [3]
PA u xleng ajint*
PD where to store the x length
PX
PN [4]
PA u yleng ajint*
PD where to the y length
PX
PN [5]
PA u xoff ajint*
PD where to store the x offset
PX
PN [6]
PA u yoff ajint*
PD where to store the y offset
PX
RT void
RD 
RX
//


void ajGraphicsGetParamsPage(float *xp,float *yp, ajint *xleng, ajint *yleng,
                             ajint *xoff, ajint *yoff)
{
#if GRAPH_DEBUG
    ajDebug("=g= plgpage(&f &f) [&xp, &yp, &xleng, &yleng, &xoff, &yoff]\n");
#endif

    if(graphData)
    {
	*xp=0.0;
	*yp=0.0;
	*xleng=0;
	*yleng=0;
	*xoff=0;
	*yoff=0;
    }
    else
    {
	plgpage(xp,yp,xleng,yleng,xoff,yoff);
	ajDebug("returns xp:%.3f yp:%.3f xleng:%d yleng:%d xoff:%d yoff:%d\n",
		 *xp, *yp, *xleng, *yleng, *xoff, *yoff);
    }

    return;
}
ID ajGraphicsDrawarcArc
TY public
MO ajgraph
LB graphics
XX
DE Draw a portion of a circle (an arc).
XX
PN [1]
PA r xcentre PLFLT
PD x coord for centre.
PX
PN [2]
PA r ycentre PLFLT
PD y coord for centre.
PX
PN [3]
PA r radius PLFLT
PD radius of the circle.
PX
PN [4]
PA r startangle PLFLT
PD angle of the start of the arc.
PX
PN [5]
PA r endangle PLFLT
PD angle of the end of the arc.
PX
RT void
RD 
RX
//


void ajGraphicsDrawarcArc(PLFLT xcentre, PLFLT ycentre, PLFLT radius,
                          PLFLT startangle, PLFLT endangle)
{
    PLFLT angle;
    ajuint i;
    PLFLT x[361];
    PLFLT y[361];
    ajuint numofpoints;
    PLFLT stopangle = endangle;

    if(endangle < startangle)
        stopangle += 360.0;

    if((stopangle-startangle)>=360.0)
        stopangle = startangle + (float) 360.0;

    x[0] = xcentre + ( radius*(float)cos(ajCvtDegToRad(startangle)) );
    y[0] = ycentre + ( radius*(float)sin(ajCvtDegToRad(startangle)) );

    for(i=1, angle=startangle+1; angle=360.0)
        stopangle = startangle + (float) 360.0;

    r1Blocks = radius;
    r2Blocks = r1Blocks+height;

    x[0]=xcentre + ( r1Blocks*(float)cos(ajCvtDegToRad(startangle)) );
    y[0]=ycentre + ( r1Blocks*(float)sin(ajCvtDegToRad(startangle)) );
    x[1]=xcentre + ( r2Blocks*(float)cos(ajCvtDegToRad(startangle)) );
    y[1]=ycentre + ( r2Blocks*(float)sin(ajCvtDegToRad(startangle)) );

    for(i=0, angle=startangle+1; angle180.0 && startangle<=360.0) &&
	 (endangle>180.0 && endangle<=360.0)) ||
       ((startangle>540.0 && startangle<=720.0)
	&& (endangle>540.0 && endangle<=720.0)) )
	GraphDrawarcText(xcentre, ycentre, radius, startangle,
                         +1*pos, txt, just);
    else GraphDrawarcText(xcentre, ycentre, radius, endangle,
                          -1*pos, txt, just);

    return;
}
ID ajGraphicsDrawbarsHoriz
TY public
MO ajgraph
LB graphics
XX
DE Draw Horizontal Error Bars.
XX
PN [1]
PA r num ajuint
PD number of error bars to be drawn.
PX
PN [2]
PA r y CONST PLFLT*
PD y positions to draw at.
PX
PN [3]
PA r xmin CONST PLFLT*
PD x positions to start at.
PX
PN [4]
PA r xmax CONST PLFLT*
PD x positions to end at.
PX
RT void
RD 
RX
//

void ajGraphicsDrawbarsHoriz(ajuint num,
                             PLFLT *y, PLFLT *xmin, PLFLT *xmax)
{
#if GRAPH_DEBUG
    if(num)
	ajDebug("=g= plerrx(%d %.2f..%.2f, %.2f, %.2f) "
		 "[num,y..y,xmin,xmax]\n",
		 num, y[0], y[num-1], xmin, xmax);
    else
	ajDebug("=g= plerrx(%d <>..<>, %.2f, %.2f) [num,y..y,xmin,xmax]\n",
		 num, xmin, xmax);
#endif

    plerrx(num,y,xmin,xmax);

    return;
}
ID ajGraphicsDrawbarsVert
TY public
MO ajgraph
LB graphics
XX
DE Draw vertical Error Bars.
XX
PN [1]
PA r num ajuint
PD number of error bars to be drawn.
PX
PN [2]
PA r x CONST PLFLT*
PD x positions to draw at.
PX
PN [3]
PA r ymin CONST PLFLT*
PD y positions to start at.
PX
PN [4]
PA r ymax CONST PLFLT*
PD y positions to end at.
PX
RT void
RD 
RX
//


void ajGraphicsDrawbarsVert(ajuint num,
                            PLFLT *x, PLFLT *ymin, PLFLT *ymax)
{
#if GRAPH_DEBUG
    if(num)
	ajDebug("=g= plerry(%d %.2f..%.2f, %.2f, %.2f) "
		 "[num,x..x,ymin,ymax]\n",
		 num, x[0], x[num-1], ymin, ymax);
    else
	ajDebug("=g= plerry(%d <>..<>, %.2f, %.2f) [num,x..x,ymin,ymax]\n",
		 num, ymin, ymax);
#endif

    plerry(num,x,ymin,ymax);

    return;
}
ID ajGraphicsDrawposBox
TY public
MO ajgraph
LB graphics
XX
DE Draw a box to the plotter device at point xx0,yy0 size big.
XX
PN [1]
PA r x PLFLT
PD x position to draw the box.
PX
PN [2]
PA r y PLFLT
PD y position to draw the box.
PX
PN [3]
PA r size PLFLT
PD how big to draw the box.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposBox(PLFLT x, PLFLT y,PLFLT size)
{
    PLFLT xa[5];
    PLFLT ya[5];

    if(graphData)
    {
	ajFmtPrintF(graphData->File,"Rectangle x1 %f y1 %f x2 %f"
		    " y2 %f colour %d\n",
		    x, y, x+size, y+size, currentfgcolour);
	graphData->Lines++;
    }
    else
    {
	xa[0] = x;
	ya[0] = y;
	xa[1] = x;
	ya[1] = y + size;
	xa[2] = x + size;
	ya[2] = y + size;
	xa[3] = x + size;
	ya[3] = y;
	xa[4] = x;
	ya[4] = y;
	GraphArray(5, xa, ya);
    }

    return;
}
ID ajGraphicsDrawposBoxFill
TY public
MO ajgraph
LB graphics
XX
DE Draw a box to the plotter device at point xx0,yy0 size big and
DE fill it in.
XX
PN [1]
PA r x PLFLT
PD x position to draw the box.
PX
PN [2]
PA r y PLFLT
PD y position to draw the box.
PX
PN [3]
PA r size PLFLT
PD how big to draw the box.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposBoxFill(PLFLT x, PLFLT y, PLFLT size)
{
    PLFLT xa[4];
    PLFLT ya[4];

    if(graphData)
    {
	ajFmtPrintF(graphData->File,"Shaded Rectangle x1 %f y1 %f x2 %f"
		    " y2 %f colour %d\n",
		    x, y, x+size, y+size, currentfgcolour);
	graphData->Lines++;
    }
    else
    {
	xa[0] = x;
	ya[0] = y;
	xa[1] = x;
	ya[1] = y + size;
	xa[2] = x + size;
	ya[2] = y + size;
	xa[3] = x + size;
	ya[3] = y;
	GraphFill(4, xa, ya);
    }

    return;
}
ID ajGraphicsDrawposCircle
TY public
MO ajgraph
LB graphics
XX
DE Draw a circle.
XX
PN [1]
PA r x PLFLT
PD x coord for centre.
PX
PN [2]
PA r y PLFLT
PD y coord for centre.
PX
PN [3]
PA r radius float
PD radius of the circle.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposCircle(PLFLT x, PLFLT y, float radius)
{
    PLFLT xa[361];
    PLFLT ya[361];
    
    ajint i;

    /* graphData : uses GraphArray */

    for(i=0;i<360;i++)
    {
	xa[i] = x + (radius * (float)cos(ajCvtDegToRad((float)i)));
	ya[i] = y + (radius * (float)sin(ajCvtDegToRad((float)i)));
    }

    xa[360] = xa[0];
    ya[360] = ya[0];

    GraphArray(361, xa,ya);

    return;
}
ID ajGraphicsDrawposDia
TY public
MO ajgraph
LB graphics
XX
DE Draw a diamond to the plotter device at point xx0,yy0 size big.
XX
PN [1]
PA r x PLFLT
PD x position to draw the diamond.
PX
PN [2]
PA r y PLFLT
PD y position to draw the diamond.
PX
PN [3]
PA r size PLFLT
PD how big to draw the diamond.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposDia(PLFLT x, PLFLT y, PLFLT size)
{
    PLFLT xa[5];
    PLFLT ya[5];
    PLFLT incr;

    incr = size*(float)0.5;

    /* graphData : uses GraphArray */

    xa[0] = x;
    ya[0] = y + incr;
    xa[1] = x + incr;
    ya[1] = y + size;
    xa[2] = x + size;
    ya[2] = y + incr;
    xa[3] = x + incr;
    ya[3] = y;
    xa[4] = x;
    ya[4] = y + incr;
    GraphArray(5, xa, ya);

    return;
}
ID ajGraphicsDrawposDiaFill
TY public
MO ajgraph
LB graphics
XX
DE Draw a diamond to the plotter device at point xx0,yy0 size big and
DE fill it in.
XX
PN [1]
PA r x PLFLT
PD x position to draw the diamond.
PX
PN [2]
PA r y PLFLT
PD y position to draw the diamond.
PX
PN [3]
PA r size PLFLT
PD how big to draw the diamond.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposDiaFill(PLFLT x, PLFLT y, PLFLT size)
{
    PLFLT xa[4];
    PLFLT ya[4];
    PLFLT incr;

    incr = size*(float)0.5;

    xa[0] = x;
    ya[0] = y + incr;
    xa[1] = x + incr;
    ya[1] = y + size;
    xa[2] = x + size;
    ya[2] = y + incr;
    xa[3] = x + incr;
    ya[3] = y;

    GraphFill(4, xa, ya);

    return;
}
ID ajGraphicsDrawposLine
TY public
MO ajgraph
LB graphics
XX
DE Draw line between 2 points.
XX
PN [1]
PA r x PLFLT
PD x start position.
PX
PN [2]
PA r y PLFLT
PD y start position.
PX
PN [3]
PA r x2 PLFLT
PD x end position.
PX
PN [4]
PA r y2 PLFLT
PD y end position.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposLine(PLFLT x, PLFLT y, PLFLT x2, PLFLT y2)
{
    if(graphData)
    {
	ajFmtPrintF(graphData->File,
		    "Line x1 %f y1 %f x2 %f y2 %f colour %d\n",
		    x, y, x2, y2, currentfgcolour);
	graphData->Lines++;
    }
    else
    {
#if GRAPH_DEBUG
	ajDebug("=g= pljoin(%.2f, %.2f, %.2f, %.2f) [xy xy]\n",
                x, y, x2, y2);
#endif

	pljoin(x,y,x2,y2);
    }

    return;
}
ID ajGraphicsDrawposRect
TY public
MO ajgraph
LB graphics
XX
DE Draw a rectangle with the current pen colour/style.
XX
PN [1]
PA r x PLFLT
PD xx0 coord.
PX
PN [2]
PA r y PLFLT
PD yy0 coord.
PX
PN [3]
PA r x2 PLFLT
PD xx1 coord.
PX
PN [4]
PA r y2 PLFLT
PD yy1 coord.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposRect(PLFLT x, PLFLT y,PLFLT x2, PLFLT y2)
{
    PLFLT xa[5];
    PLFLT ya[5];

    if(graphData)
    {
	ajFmtPrintF(graphData->File,"Rectangle x1 %f y1 %f x2 %f"
		    " y2 %f colour %d\n",
		    x, y, x2, y2, currentfgcolour);
	graphData->Lines++;
    }
    else
    {
	xa[0] = x;
	ya[0] = y;
	xa[1] = x;
	ya[1] = y2;
	xa[2] = x2;
	ya[2] = y2;
	xa[3] = x2;
	ya[3] = y;
	xa[4] = x;
	ya[4] = y;
	GraphArray(5, xa, ya);
    }

    return;
}
ID ajGraphicsDrawposRectFill
TY public
MO ajgraph
LB graphics
XX
DE Draw a rectangle and fill it with the current pen colour/style.
XX
PN [1]
PA r x PLFLT
PD xx0 coord.
PX
PN [2]
PA r y PLFLT
PD yy0 coord.
PX
PN [3]
PA r x2 PLFLT
PD xx1 coord.
PX
PN [4]
PA r y2 PLFLT
PD yy1 coord.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposRectFill(PLFLT x, PLFLT y, PLFLT x2, PLFLT y2)
{
    PLFLT xa[4];
    PLFLT ya[4];

    if(graphData)
    {
	ajFmtPrintF(graphData->File,"Shaded Rectangle x1 %f y1 %f x2 %f"
		    " y2 %f colour %d\n",
		    x, y, x2, y2, currentfgcolour);
	graphData->Lines++;
    }
    else
    {
	xa[0] = x;
	ya[0] = y;
	xa[1] = x;
	ya[1] = y2;
	xa[2] = x2;
	ya[2] = y2;
	xa[3] = x2;
	ya[3] = y;
	GraphFill(4, xa, ya);
    }

    return;
}
ID ajGraphicsDrawposTextAtend
TY public
MO ajgraph
LB graphics
XX
DE Draw text ending at position (xx1,yy1)
XX
PN [1]
PA r x PLFLT
PD xx1 coord.
PX
PN [2]
PA r y PLFLT
PD yy1 coord.
PX
PN [3]
PA r txt const char*
PD The text to be displayed.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposTextAtend(PLFLT x, PLFLT y, const char *txt)
{
    if(graphData)
    {
	ajFmtPrintF(graphData->File,
		    "Text3 x1 %f y1 %f colour %d size %f %s\n",
		    x,y,
		    currentfgcolour, currentcharscale,txt);
	graphData->Lines++;
    }
    else
	GraphText(x,y,1.0,0.0,1.0, txt);

    return;
}
ID ajGraphicsDrawposTextAtlineJustify
TY public
MO ajgraph
LB graphics
XX
DE Draw text along a line.
XX
PN [1]
PA r x PLFLT
PD x1 coord.
PX
PN [2]
PA r y PLFLT
PD y1 coord.
PX
PN [3]
PA r x2 PLFLT
PD x2 coord.
PX
PN [4]
PA r y2 PLFLT
PD y2 coord.
PX
PN [5]
PA r txt const char*
PD The text to be displayed.
PX
PN [6]
PA r just PLFLT
PD justification of the string.
PD (0=left,1=right,0.5=middle etc)
PX
RT void
RD 
RX
//


void ajGraphicsDrawposTextAtlineJustify(PLFLT x, PLFLT y, PLFLT x2, PLFLT y2,
                                        const char *txt, PLFLT just)
{
    if(graphData)
    {
	ajFmtPrintF(graphData->File,
		    "Textline x1 %f y1 %f x2 %f y2 %f colour %d size %f %s\n",
		    x,y,x2, y2,
		    currentfgcolour, currentcharscale,txt);
	graphData->Lines++;
    }
    else
	GraphText(x,y,x2-x,y2-y,just, txt);

    return;
}
ID ajGraphicsDrawposTextAtmid
TY public
MO ajgraph
LB graphics
XX
DE Draw text with Mid point of text at (xx1,yy1).
DE
DE For graph data type, writes directly.
XX
PN [1]
PA r x PLFLT
PD xx1 coord.
PX
PN [2]
PA r y PLFLT
PD yy1 coord.
PX
PN [3]
PA r txt const char*
PD The text to be displayed.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposTextAtmid(PLFLT x, PLFLT y, const char *txt)
{
    if(graphData)
    {
	ajFmtPrintF(graphData->File,
		    "Text2 x1 %f y1 %f colour %d size %f %s\n",
		    x, y,
		    currentfgcolour, currentcharscale, txt);
	graphData->Lines++;
    }
    else
	GraphText(x,y,1.0,0.0,0.5, txt);

    return;
}
ID ajGraphicsDrawposTextAtstart
TY public
MO ajgraph
LB graphics
XX
DE Draw text starting at position (xx1,yy1)
XX
PN [1]
PA r x PLFLT
PD xx1 coordinate
PX
PN [2]
PA r y PLFLT
PD yy1 coordinate
PX
PN [3]
PA r txt const char*
PD The text to be displayed.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposTextAtstart(PLFLT x, PLFLT y, const char *txt)
{
    if(graphData)
    {
	ajFmtPrintF(graphData->File,
		    "Text1 x1 %f y1 %f colour %d size %f %s\n",
		    x,y,
		    currentfgcolour, currentcharscale,txt);
	graphData->Lines++;
    }
    else
	GraphText(x,y,1.0,0.0,0.0, txt);

    return;
}
ID ajGraphicsDrawposTextJustify
TY public
MO ajgraph
LB graphics
XX
DE Draw text, positioning with respect to (xx1,yy1) by justified as
DE defined by just.
XX
PN [1]
PA r x PLFLT
PD xx1 coord.
PX
PN [2]
PA r y PLFLT
PD yy1 coord.
PX
PN [3]
PA r txt const char*
PD The text to be displayed.
PX
PN [4]
PA r just PLFLT
PD justification of the string.
PD (0=left,1=right,0.5=middle etc)
PX
RT void
RD 
RX
//


void ajGraphicsDrawposTextJustify(PLFLT x, PLFLT y, const char *txt,
                                  PLFLT just)
{
    if(graphData)
    {
	ajFmtPrintF(graphData->File,
		    "Text1 x1 %f y1 %f colour %d size %f %s\n",
		    x,y,currentfgcolour, currentcharscale,txt);
	graphData->Lines++;
    }
    else
	GraphText(x,y,1.0,0.0,just, txt);

    return;
}
ID ajGraphicsDrawposTri
TY public
MO ajgraph
LB graphics
XX
DE Draw a Triangle.
XX
PN [1]
PA r x PLFLT
PD xx1 coord of point 1.
PX
PN [2]
PA r y PLFLT
PD yy1 coord of point 1.
PX
PN [3]
PA r x2 PLFLT
PD xx2 coord of point 2.
PX
PN [4]
PA r y2 PLFLT
PD yy2 coord of point 2.
PX
PN [5]
PA r x3 PLFLT
PD x3 coord of point 3.
PX
PN [6]
PA r y3 PLFLT
PD y3 coord of point 3.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposTri(PLFLT x, PLFLT y, PLFLT x2, PLFLT y2,
                          PLFLT x3, PLFLT y3)
{
    PLFLT xa[4];
    PLFLT ya[4];
    
    xa[0]=x; xa[1]=x2; xa[2]=x3; xa[3]=x;
    ya[0]=y; ya[1]=y2; ya[2]=y3; ya[3]=y;

    ajGraphicsDrawsetPoly(4, xa, ya);

    return;
}
ID ajGraphicsDrawposTriFill
TY public
MO ajgraph
LB graphics
XX
DE Draw a Triangle and fill it in.
XX
PN [1]
PA r x PLFLT
PD xx1 coord of point 1.
PX
PN [2]
PA r y PLFLT
PD yy1 coord of point 1.
PX
PN [3]
PA r x2 PLFLT
PD xx2 coord of point 2.
PX
PN [4]
PA r y2 PLFLT
PD yy2 coord of point 2.
PX
PN [5]
PA r x3 PLFLT
PD x3 coord of point 3.
PX
PN [6]
PA r y3 PLFLT
PD y3 coord of point 3.
PX
RT void
RD 
RX
//


void ajGraphicsDrawposTriFill(PLFLT x, PLFLT y, PLFLT x2, PLFLT y2,
                              PLFLT x3, PLFLT y3)
{
    PLFLT xa[3];
    PLFLT ya[3];
    
    xa[0]=x; xa[1]=x2; xa[2]=x3;
    ya[0]=y; ya[1]=y2; ya[2]=y3;

    ajGraphicsDrawsetPolyFill(3, xa, ya);

    return;
}
ID ajGraphicsDrawsetDots
TY public
MO ajgraph
LB graphics
XX
DE Draw a set of dots.
XX
PN [1]
PA r num ajuint
PD The number of dots to be drawn.
PX
PN [2]
PA r xx CONST PLFLT*
PD xx1 coord.
PX
PN [3]
PA r yy CONST PLFLT*
PD yy1 coord.
PX
RT void
RD 
RX
//


void ajGraphicsDrawsetDots(ajuint num, PLFLT *xx, PLFLT *yy)
{
    GraphSymbols(xx, yy, num, 17);

    /* see xx06c in examples for codes e.g. 17 is a dot*/
    return;
}
ID GraphDrawsetLines
TY static
MO ajgraph
LB graphics
XX
DE Draw a number of lines. The points are stored in two array.
XX
PN [1]
PA r num ajuint
PD number of joined points to plot.
PX
PN [2]
PA r xx CONST PLFLT*
PD pointer to x coordinates.
PX
PN [3]
PA r yy CONST PLFLT*
PD pointer to y coordinates.
PX
RT void
RD 
RX
//


static void GraphDrawsetLines(ajuint num, PLFLT *xx, PLFLT *yy)
{
    ajuint i;
    PLFLT xx0;
    PLFLT yy0;
    PLFLT *xx1 = xx;
    PLFLT *yy1 = yy;

    /* graphdata : calls ajGraphicsDrawposLine */

    for(i=1; i distpts)
        newcharsize = newcharsize * distpts/origStringLength;

    ajDebug("ajGraphicsCalcCharsize returns charsize %f\n", newcharsize);
    ajGraphicsSetCharsize(origcharsize);

    return newcharsize;
}
ID ajGraphicsCalcCoord
TY public
MO ajgraph
LB graphics
XX
DE compute the coordinates of a point on a circle knowing the angle.
XX
PN [1]
PA r x PLFLT
PD x coord for centre.
PX
PN [2]
PA r y PLFLT
PD y coord for centre.
PX
PN [3]
PA r radius PLFLT
PD Radius of the circle.
PX
PN [4]
PA r angle PLFLT
PD angle at which the point is.
PX
RT PLFLT*
RD The x and y coordinates of the point.
RX
//


PLFLT* ajGraphicsCalcCoord(PLFLT x, PLFLT y, PLFLT radius, PLFLT angle)
{
    PLFLT *xy;

    xy = (float *)AJALLOC( 2*sizeof(float) );
    xy[0] = x + ( radius*(float)cos(ajCvtDegToRad(angle)) );
    xy[1] = y + ( radius*(float)sin(ajCvtDegToRad(angle)) );

    return xy;
}
ID ajGraphicsCalcDistance
TY public
MO ajgraph
LB graphics
XX
DE Compute the distance between 2 points in user coordinates.
XX
PN [1]
PA r xx1 PLFLT
PD x coord of point 1
PX
PN [2]
PA r yy1 PLFLT
PD y coord of point 1
PX
PN [3]
PA r xx2 PLFLT
PD x coord of point 2
PX
PN [4]
PA r yy2 PLFLT
PD y coord of point 2
PX
RT PLFLT
RD The distance between the 2 points in user coordinates.
RX
//


PLFLT ajGraphicsCalcDistance(PLFLT xx1, PLFLT yy1, PLFLT xx2, PLFLT yy2)
{
    return GraphDistPts(xx1, yy1, xx2, yy2);
}
ID ajGraphicsCalcRange
TY public
MO ajgraph
LB graphics
XX
DE Get the max and min of the data points you wish to display.
XX
PN [1]
PA r array const float*
PD array
PX
PN [2]
PA r npoints ajuint
PD Number of data points
PX
PN [3]
PA w min float*
PD min.
PX
PN [4]
PA w max float*
PD max.
PX
RT void
RD 
RX
//


void ajGraphicsCalcRange(const float *array,
			ajuint npoints, float *min, float *max)
{
    ajuint i;

    *min = FLT_MAX;
    *max = FLT_MIN;

    for(i=0;i array[i]) ? *max : array[i];
    }

    if(*min == *max)
    {
        *min *= (float) 0.9;
        *max *= (float) 1.1;
    }

    return;
}
ID ajGraphicsCalcTextheight
TY public
MO ajgraph
LB graphics
XX
DE Compute the height of a character in millimetres.
XX
RT PLFLT
RD The height of the character in user coordinates.
RX
//


PLFLT ajGraphicsCalcTextheight(void)
{
    return currentcharsize;
}
ID ajGraphicsCalcTextlengthC
TY public
MO ajgraph
LB graphics
XX
DE Compute the length of a string in millimetres.
XX
PN [1]
PA r txt const char*
PD Text
PX
RT PLFLT
RD The length of the string in user coordinates.
RX
//


PLFLT ajGraphicsCalcTextlengthC(const char *txt)
{
    return GraphTextLength(txt);
}
ID ajGraphicsCalcTextlengthS
TY public
MO ajgraph
LB graphics
XX
DE Compute the length of a string in millimetres.
XX
PN [1]
PA r str const AjPStr
PD Text
PX
RT PLFLT
RD The length of the string in user coordinates.
RX
//


PLFLT ajGraphicsCalcTextlengthS(const AjPStr str)
{
    return GraphTextLength(MAJSTRGETPTR(str));
}
ID ajGraphicsUnused
TY public
MO ajgraph
LB graphics
XX
DE Unused functions to avoid compiler warnings
XX
RT void
RD 
RX
//


void ajGraphicsUnused(void)
{
    float f = 0.0;
    ajint i = 0;
    AjPGraph thys = NULL;
    AjPGraphdata graphdata = NULL;

    GraphPrint(thys);
    GraphDataPrint(graphdata);
    GraphCheckPoints(0, &f, &f);
    GraphCheckFlags(0);
    GraphArrayGapsI(0, &i, &i);

    return;
}
ID ajGraphicsExit
TY public
MO ajgraph
LB graphics
XX
DE Cleans up graphics internal memory
XX
RT void
RD 
RX
//


void ajGraphicsExit(void)
{
    ajStrDel(&graphBasename);
    ajStrDel(&graphExtension);

    return;
}
ID ajGraphNew
TY public
MO ajgraph
LB graphics
XX
DE Create a structure to hold a general graph.
XX
RT AjPGraph
RD multiple graph structure.
RX
//


AjPGraph ajGraphNew(void)
{
    AjPGraph graph;

    AJNEW0(graph);
    ajDebug("ajGraphNew - need to call ajGraphSet\n");

    return graph;
}
ID ajGraphInitSeq
TY public
MO ajgraph
LB graphics
XX
DE Initialises a graph using default values based on a sequence.
DE
DE Existing titles and other data are unchanged
XX
PN [1]
PA u thys AjPGraph
PD Graph
PX
PN [2]
PA r seq const AjPSeq
PD Sequence
PX
RT void
RD 
RX
//


void ajGraphInitSeq(AjPGraph thys, const AjPSeq seq)
{
    if(!ajStrGetLen(thys->title))
        ajFmtPrintS(&thys->title, "%S of %S",
                    ajUtilGetProgram(), ajSeqGetNameS(seq));

    ajGraphxySetXrangeII(thys, 1, ajSeqGetLen(seq));

    return;
}
ID ajGraphNewpage
TY public
MO ajgraph
LB graphics
XX
DE Clear Screen if (X) or new page if plotter/postscript. Also pass a boolean
DE to state whether you want the current pen colour character sizes,
DE plot title and subtitle, etc to be reset or stay the same for the next page.
XX
PN [1]
PA w thys AjPGraph
PD Graph object
PX
PN [2]
PA r resetdefaults AjBool
PD reset page setting?
PX
RT void
RD 
RX
//


void ajGraphNewpage(AjPGraph thys, AjBool resetdefaults)
{
    ajint old;
    ajint cold;
    float fold;


    ajDebug("ajGraphNewPage reset:%B\n", resetdefaults);

    if(graphData)
    {
	GraphDatafileNext();
	return;
    }

    GraphSubPage(0);

    if(resetdefaults)
    {
	ajGraphicsSetFgcolour(BLACK);
	ajGraphicsSetCharscale(1.0);
	ajGraphicsSetLinestyle(0);
    }
    else
    {
	/* pladv resets every thing so need */
	/* to get the old copies */
	cold = ajGraphicsSetFgcolour(BLACK);
	fold = ajGraphicsSetCharscale(0.0);
	old = ajGraphicsSetLinestyle(0);
	ajGraphTrace(thys);

	GraphLabelTitle(((thys->flags & AJGRAPH_TITLE) ?
                         ajStrGetPtr(thys->title) : " "),
                        ((thys->flags & AJGRAPH_SUBTITLE) ?
                         ajStrGetPtr(thys->subtitle) : " "));

        if(thys->windowset)
            GraphSetWin(thys->xstart, thys->xend,
                        thys->ystart, thys->yend);
        else
            GraphSetWin(0.0, 480.0,
                        0.0, 640.0);

	/* then set it again */
	ajGraphicsSetFgcolour(cold);
	ajGraphicsSetCharscale(fold);
	ajGraphicsSetLinestyle(old);
    }

    return;
}
ID ajGraphOpenFlags
TY public
MO ajgraph
LB graphics
XX
DE Open a window whose view is defined by the x's and y's min and max
DE values.
XX
PN [1]
PA w thys AjPGraph
PD Graph object
PX
PN [2]
PA r xmin PLFLT
PD minimum x value.(user coordinates)
PX
PN [3]
PA r xmax PLFLT
PD maximum x value.
PX
PN [4]
PA r ymin PLFLT
PD minimum y value.
PX
PN [5]
PA r ymax PLFLT
PD maximum y value.
PX
PN [6]
PA r flags ajint
PD flag bit settings
PX
RT void
RD 
RX
//


void ajGraphOpenFlags(AjPGraph thys, PLFLT xmin, PLFLT xmax,
                      PLFLT ymin, PLFLT ymax, ajint flags)
{
    AjPTime ajtime;

    ajGraphicsSetDevice(thys);

    /* Calling funclist graphType() */
    if(thys)
        (*graphType[thys->displaytype].GOpen)(thys,
                                              graphType[thys->displaytype].Ext);

    if( ajStrGetLen(thys->title) <=1)
    {
        ajtime = ajTimeNewToday();
        ajStrAppendC(&thys->title,
                     ajFmtString("%S (%D)",ajUtilGetProgram(),ajtime));
        ajTimeDel(&ajtime);
    }

    GraphSetName(thys, thys->outputfile,
                 graphType[thys->displaytype].Ext);
    thys->xstart = xmin;
    thys->xend = xmax;
    thys->ystart = ymin;
    thys->yend = ymax;

    GraphColourBack();
    GraphInit(thys);
    GraphColourFore();
    ajGraphicsPlenv(xmin, xmax , ymin, ymax , flags);

    return;
}
ID ajGraphOpenMm
TY public
MO ajgraph
LB graphics
XX
DE Open a window whose view is defined by x and y's size in millimetres
DE to allow easy scaling of characters and strings whose sizes are always
DE in millimetres.
XX
PN [1]
PA w thys AjPGraph
PD Graph object
PX
PN [2]
PA w xmm float*
PD x length in millimetres
PX
PN [3]
PA w ymm float*
PD y length in millimetres
PX
RT void
RD 
RX
//


void ajGraphOpenMm(AjPGraph thys, float * xmm, float * ymm)
{
    PLFLT mmxmin;
    PLFLT mmxmax;
    PLFLT mmymin;
    PLFLT mmymax;

    AjPTime ajtime;

    ajGraphicsSetDevice(thys);

    if (thys)
    {
	/* Calling funclist graphType() */
	(*graphType[thys->displaytype].GOpen)(thys,
                                              graphType[thys->displaytype].Ext);

	if(!ajStrGetLen(thys->title))
	{
	    ajFmtPrintAppS(&thys->title,"%S",
			   ajUtilGetProgram());
	}

	if(!ajStrGetLen(thys->subtitle))
	{
	    ajtime = ajTimeNewTodayFmt("report");
	    ajFmtPrintAppS(&thys->subtitle,"%D",
			   ajtime);
	    ajTimeDel(&ajtime);
	}
    }
    GraphColourBack();
    GraphInit(thys);
    GraphColourFore();
    GraphSubPage(0);

    if(graphData)
    {
	*xmm= (float) 255.9;
	*ymm= (float) 191.9;
    }
    else
    {
        plgspa(&mmxmin, &mmxmax, &mmymin, &mmymax);
        *xmm = mmxmax - mmxmin;
        *ymm = mmymax - mmymin;
    }

    GraphLabelTitle(((thys->flags & AJGRAPH_TITLE) ?
                     ajStrGetPtr(thys->title) : ""),
                    ((thys->flags & AJGRAPH_SUBTITLE) ?
                     ajStrGetPtr(thys->subtitle) : ""));
    thys->xstart = 0.0;
    thys->xend = *xmm;
    thys->ystart = 0.0;
    thys->yend = *ymm;
    thys->windowset = ajTrue;

    GraphSetWin(thys->xstart, thys->xend,
                thys->ystart, thys->yend);

    return;
}
ID ajGraphOpenPlotset
TY public
MO ajgraph
LB graphics
XX
DE Open a window with a set of plots
XX
PN [1]
PA w thys AjPGraph
PD Graph object.
PX
PN [2]
PA r numofsets ajuint
PD number of plots in set.
PX
RT void
RD 
RX
//


void ajGraphOpenPlotset(AjPGraph thys, ajuint numofsets)
{
    ajGraphicsSetDevice(thys);
    GraphSetNumSubPage(numofsets);
    GraphColourBack();
    GraphInit(thys);
    GraphColourFore();

    return;
}
ID ajGraphOpenWin
TY public
MO ajgraph
LB graphics
XX
DE Open a window whose view is defined by x and y's min and max.
XX
PN [1]
PA w thys AjPGraph
PD Graph object
PX
PN [2]
PA r xmin float
PD minimum x value.(user coordinates)
PX
PN [3]
PA r xmax float
PD maximum x value.
PX
PN [4]
PA r ymin float
PD minimum y value.
PX
PN [5]
PA r ymax float
PD maximum y value.
PX
RT void
RD 
RX
//


void ajGraphOpenWin(AjPGraph thys, float xmin, float xmax,
		    float ymin, float ymax)
{
    AjPTime ajtime;

    ajGraphicsSetDevice(thys);

    if (thys)
    {
	/* Calling funclist graphType() */
	(*graphType[thys->displaytype].GOpen)(thys,
                                              graphType[thys->displaytype].Ext);

	if(!ajStrGetLen(thys->title))
	{
	    ajFmtPrintAppS(&thys->title,"%S",
			   ajUtilGetProgram());
	}

	if(!ajStrGetLen(thys->subtitle))
	{
	    ajtime = ajTimeNewTodayFmt("report");
	    ajFmtPrintAppS(&thys->subtitle,"%D",
			   ajtime);
	    ajTimeDel(&ajtime);
	}
    }
    GraphColourBack();
    GraphInit(thys);
    GraphColourFore();
    GraphSubPage(0);

    GraphLabelTitle(((thys->flags & AJGRAPH_TITLE) ?
                     ajStrGetPtr(thys->title) : ""),
                    ((thys->flags & AJGRAPH_SUBTITLE) ?
                     ajStrGetPtr(thys->subtitle) : ""));
    thys->xstart = xmin;
    thys->xend = xmax;
    thys->ystart = ymin;
    thys->yend = ymax;
    thys->windowset = ajTrue;

    GraphSetWin(xmin, xmax, ymin, ymax);

    return;
}
ID ajGraphAppendTitleC
TY public
MO ajgraph
LB graphics
XX
DE Set an empty graph title from the description or program name and extra text
DE 'of ...' supplied by the caller.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD Title.
PX
RT void
RD 
RX
//


void ajGraphAppendTitleC(AjPGraph thys, const char* txt)
{
    ajDebug("ajGraphAppendTitleC '%s'\n", txt);

    if (!thys)
	return;

    if(ajStrGetLen(thys->title))
	return;

    if(ajStrGetLen(thys->desc))
	ajFmtPrintS(&thys->title, "%S of %s",
                    thys->desc, txt);
    else
	ajFmtPrintS(&thys->title, "%S of %s",
                    ajUtilGetProgram(), txt);

    return;
}
ID ajGraphAppendTitleS
TY public
MO ajgraph
LB graphics
XX
DE Set an empty graph title from the description or program name and extra text
DE 'of ...' supplied by the caller.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD Title.
PX
RT void
RD 
RX
//


void ajGraphAppendTitleS(AjPGraph thys, const AjPStr str)
{
    ajDebug("ajGraphAppendTitleS '%S'\n", str);

    if (!thys)
	return;

    if(ajStrGetLen(thys->title))
	return;

    if(ajStrGetLen(thys->desc))
	ajFmtPrintS(&thys->title, "%S of %S",
                    thys->desc, str);
    else
	ajFmtPrintS(&thys->title, "%S of %S",
                    ajUtilGetProgram(), str);

    return;
}
ID ajGraphClear
TY public
MO ajgraph
LB graphics
XX
DE Delete all the drawable objects connected to the graph object.
XX
PN [1]
PA u thys AjPGraph
PD Graph object
PX
RT void
RD 
RX
//


void ajGraphClear(AjPGraph thys)
{
    AjPGraphobj Obj;
    AjPGraphobj anoth;

    if(!thys->Mainobj)
	return;
    else
    {
	Obj = thys->Mainobj;

	while(Obj)
	{
	    anoth = Obj->next;
	    ajStrDel(&Obj->text);
	    AJFREE(Obj);
	    Obj = anoth;
	}
    }

    thys->Mainobj = NULL;
    thys->numofobjects = 0;

    return;
}
ID ajGraphSetDatanameC
TY public
MO ajgraph
LB graphics
XX
DE Set the graph data source name.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD Data source name.
PX
RT void
RD 
RX
//


void ajGraphSetDatanameC(AjPGraph thys, const char* txt)
{
    ajDebug("ajGraphSetDatanameC '%s'\n", txt);

    ajStrAssignC(&thys->dataname,txt);

    return;
}
ID ajGraphSetDatanameS
TY public
MO ajgraph
LB graphics
XX
DE Set the graph data source name.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD Data source name.
PX
RT void
RD 
RX
//


void ajGraphSetDatanameS(AjPGraph thys, const AjPStr str)
{
    ajDebug("ajGraphSetDatanameS '%S'\n", str);

    ajStrAssignS(&thys->dataname,str);

    return;
}
ID ajGraphSetDescC
TY public
MO ajgraph
LB graphics
XX
DE Set the graph description.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD Title.
PX
RT void
RD 
RX
//


void ajGraphSetDescC(AjPGraph thys, const char* txt)
{
    ajDebug("ajGraphSetDescC '%s'\n", txt);

    ajStrAssignC(&thys->desc,txt);

    return;
}
ID ajGraphSetDescS
TY public
MO ajgraph
LB graphics
XX
DE Set the graph description.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD Title.
PX
RT void
RD 
RX
//


void ajGraphSetDescS(AjPGraph thys, const AjPStr str)
{
    ajDebug("ajGraphSetDescS '%S'\n", str);

    ajStrAssignS(&thys->desc,str);

    return;
}
ID ajGraphSetDevicetype
TY public
MO ajgraph
LB graphics
XX
DE Initialize options for a general graph object
XX
PN [1]
PA u thys AjPGraph
PD Graph object
PX
PN [2]
PA r devicetype const AjPStr
PD Graph type
PX
RT AjBool
RD ajTrue on success
RX
//


AjBool ajGraphSetDevicetype(AjPGraph thys, const AjPStr devicetype)
{
    ajint i;
    ajint j=-1;
    ajint k;
    AjPStr aliases = NULL;

    ajDebug("ajGraphSetDevicetype '%S'\n", devicetype);

    for(i=0;graphType[i].Name;i++)
    {
        if(!graphType[i].GOpen)
            continue;

        if(ajStrMatchCaseC(devicetype, graphType[i].Name))
	{
	    j = i;
	    break;
	}

	if(ajCharPrefixCaseS(graphType[i].Name, devicetype))
	{
	    if(j < 0)
		j = i;
	    else
	    {
		for(k=0;graphType[k].Name;k++)
		{
		    if(ajCharPrefixCaseS(graphType[k].Name, devicetype))
		    {
			if(ajStrGetLen(aliases))
			    ajStrAppendC(&aliases, ", ");
			ajStrAppendC(&aliases, graphType[k].Name);
		    }
		}
		    
		ajErr("Ambiguous graph device name '%S' (%S)",
		       devicetype, aliases);
		ajStrDel(&aliases);

		return ajFalse;
	    }
	}
    }

    if(j<0)
	return ajFalse;

    GraphNewPlplot(thys);

    thys->displaytype = j;

    ajDebug("ajGraphSetDevicetype plplot type '%S' displaytype %d '%s'\n",
	    devicetype, j, graphType[j].Name);

    return ajTrue;
}
ID ajGraphSetFlag
TY public
MO ajgraph
LB graphics
XX
DE Set the flags for the graph to add or subtract "flag" depending on set
XX
PN [1]
PA u thys AjPGraph
PD graph to have flags altered.
PX
PN [2]
PA r flag ajint
PD flag to be set.
PX
PN [3]
PA r set AjBool
PD whether to set the flag or remove it.
PX
RT void
RD 
RX
//


void ajGraphSetFlag(AjPGraph thys, ajint flag, AjBool set)
{
    if (!thys)
	return;

    if(thys->flags & flag)
    {
	if(!set)
                thys->flags &= ~flag;
    }
    else
    {
	if(set)
	    thys->flags |= flag;
    }

    return;
}
ID ajGraphSetMulti
TY public
MO ajgraph
LB graphics
XX
DE Create a structure to hold a number of graphs.
XX
PN [1]
PA w thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r numsets ajuint
PD maximum number of graphs that can be stored.
PX
RT void
RD 
RX
//


void ajGraphSetMulti(AjPGraph thys, ajuint numsets)
{
    if (!thys)
	return;

    if (thys->graphs)
        AJFREE(thys->graphs);

    AJCNEW0(thys->graphs,numsets);

    ajDebug("ajGraphSetMulti numsets: %d\n", numsets);

    thys->numofgraphs    = 0;
    thys->numofgraphsmax = numsets;
    thys->minmaxcalc     = 0;
    thys->flags          = GRAPH_XY;

    return;
}
ID ajGraphSetOutdirS
TY public
MO ajgraph
LB graphics
XX
DE Set the directory of the output file. Only used later if the device
DE plotter is capable of writing to a file (e.g. postscript or png)
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to update file name
PX
PN [2]
PA r str const AjPStr
PD Name of the file.
PX
RT void
RD 
RX
//


void ajGraphSetOutdirS(AjPGraph thys, const AjPStr str)
{
    if(!ajStrGetLen(str))
	return;

    if (!thys)
	return;

    if(ajStrGetCharLast(str) != '/')
	ajStrInsertC(&thys->outputfile, 0, "/");
    ajStrInsertS(&thys->outputfile, 0, str);

    return;
}
ID ajGraphSetOutfileC
TY public
MO ajgraph
LB graphics
XX
DE Set the name of the output file. Only used later if the device
DE plotter is capable of postscript output. ps and cps.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to write file name to.
PX
PN [2]
PA r txt const char*
PD Name of the file.
PX
RT void
RD 
RX
//


void ajGraphSetOutfileC(AjPGraph thys, const char* txt)
{
    if(!strlen(txt))
	return;

    if (!thys)
	return;

    ajStrAssignC(&thys->outputfile, txt);

    return;
}
ID ajGraphSetOutfileS
TY public
MO ajgraph
LB graphics
XX
DE Set the name of the output file. Only used later if the device
DE plotter is capable of postscript output. ps and cps.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to write file name to.
PX
PN [2]
PA r str const AjPStr
PD Name of the file.
PX
RT void
RD 
RX
//


void ajGraphSetOutfileS(AjPGraph thys, const AjPStr str)
{
    if(!ajStrGetLen(str))
	return;

    if (!thys)
	return;

    ajStrAssignS(&thys->outputfile, str);

    return;
}
ID ajGraphSetSubtitleC
TY public
MO ajgraph
LB graphics
XX
DE Set the graph subtitle.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD Subtitle.
PX
RT void
RD 
RX
//


void ajGraphSetSubtitleC(AjPGraph thys, const char* txt)
{
    ajStrAssignC(&thys->subtitle,txt);

    return;
}
ID ajGraphSetSubtitleS
TY public
MO ajgraph
LB graphics
XX
DE Set the title for the Y axis.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD Subtitle
PX
RT void
RD 
RX
//


void ajGraphSetSubtitleS(AjPGraph thys, const AjPStr str)
{
    ajStrAssignS(&thys->subtitle,str);

    return;
}
ID ajGraphSetTitleC
TY public
MO ajgraph
LB graphics
XX
DE Set the graph Title.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD title.
PX
RT void
RD 
RX
//


void ajGraphSetTitleC(AjPGraph thys, const char* txt)
{
    ajDebug("ajGraphSetTitleC '%s'\n", txt);

    ajStrAssignC(&thys->title,txt);

    return;
}
ID ajGraphSetTitleS
TY public
MO ajgraph
LB graphics
XX
DE Set the graph title.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD Title.
PX
RT void
RD 
RX
//


void ajGraphSetTitleS(AjPGraph thys, const AjPStr str)
{
    ajDebug("ajGraphSetTitleS '%S'\n", str);

    ajStrAssignS(&thys->title,str);

    return;
}
ID ajGraphSetXlabelC
TY public
MO ajgraph
LB graphics
XX
DE Set the title for the X axis for multiple plot on one graph.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD title for the x axis.
PX
RT void
RD 
RX
//


void ajGraphSetXlabelC(AjPGraph thys, const char* txt)
{
    ajStrAssignC(&thys->xaxis,txt);

    return;
}
ID ajGraphSetXlabelS
TY public
MO ajgraph
LB graphics
XX
DE Set the title for the X axis for multiple plots on one graph.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD title for the x axis.
PX
RT void
RD 
RX
//


void ajGraphSetXlabelS(AjPGraph thys, const AjPStr str)
{
    ajStrAssignS(&thys->xaxis,str);

    return;
}
ID ajGraphSetYlabelC
TY public
MO ajgraph
LB graphics
XX
DE Set the title for the Y axis for multiple plots on one graph.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD title for the y axis.
PX
RT void
RD 
RX
//


void ajGraphSetYlabelC(AjPGraph thys, const char* txt)
{
    ajStrAssignC(&thys->yaxis, txt);

    return;
}
ID ajGraphSetYlabelS
TY public
MO ajgraph
LB graphics
XX
DE Set the title for the Y axis for multiple plots on one graph.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD title for the y axis.
PX
RT void
RD 
RX
//


void ajGraphSetYlabelS(AjPGraph thys, const AjPStr str)
{
    ajStrAssignS(&thys->yaxis, str);

    return;
}
ID ajGraphShowSubtitle
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to display the subtitle.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphShowSubtitle(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_SUBTITLE, set);

    return;
}
ID ajGraphShowTitle
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to display the title.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphShowTitle(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_TITLE, set);

    return;
}
ID ajGraphGetParamsPage
TY public
MO ajgraph
LB graphics
XX
DE Get the output page parameters stored in a graph object
DE
DE For graph data type, sets to zero as these are not applicable.
XX
PN [1]
PA r thys const AjPGraph
PD Graph object.
PX
PN [2]
PA u xp float*
PD where to store the x position
PX
PN [3]
PA u yp float*
PD where to store the y position
PX
PN [4]
PA u xleng ajint*
PD where to store the x length
PX
PN [5]
PA u yleng ajint*
PD where to the y length
PX
PN [6]
PA u xoff ajint*
PD where to store the x offset
PX
PN [7]
PA u yoff ajint*
PD where to store the y offset
PX
RT void
RD 
RX
//


void ajGraphGetParamsPage(const AjPGraph thys,
                          float *xp,float *yp, ajint *xleng, ajint *yleng,
                          ajint *xoff, ajint *yoff)
{
#if GRAPH_DEBUG
    ajDebug("=g= plgpage(&f &f) [&xp, &yp, &xleng, &yleng, &xoff, &yoff]\n");
#endif

    if(graphData)
    {
	*xp=0.0;
	*yp=0.0;
	*xleng=graphType[thys->displaytype].Width;
	*yleng=graphType[thys->displaytype].Height;
	*xoff=0;
	*yoff=0;
    }
    else
    {
	plgpage(xp,yp,xleng,yleng,xoff,yoff);
        if(!*xleng)
            *xleng = graphType[thys->displaytype].Width;
        if(!*yleng)
            *yleng = graphType[thys->displaytype].Height;
	ajDebug("returns xp:%.3f yp:%.3f xleng:%d yleng:%d xoff:%d yoff:%d\n",
		 *xp, *yp, *xleng, *yleng, *xoff, *yoff);
    }

    return;
}
ID ajGraphGetSubtitleC
TY public
MO ajgraph
LB graphics
XX
DE Return plot subtitle
XX
PN [1]
PA r thys const AjPGraph
PD Graph object.
PX
RT const char*
RD Subtitle
RX
//


const char* ajGraphGetSubtitleC(const AjPGraph thys)
{
    if (thys->subtitle)
	    return ajStrGetPtr(thys->subtitle);

    return "";
}
ID ajGraphGetSubtitleS
TY public
MO ajgraph
LB graphics
XX
DE Return plot subtitle
XX
PN [1]
PA r thys const AjPGraph
PD Graph object.
PX
RT const AjPStr
RD Subtitle
RX
//


const AjPStr ajGraphGetSubtitleS(const AjPGraph thys)
{
    return thys->subtitle;

}
ID ajGraphGetTitleC
TY public
MO ajgraph
LB graphics
XX
DE Return plot title
XX
PN [1]
PA r thys const AjPGraph
PD Graph object.
PX
RT const char*
RD Title
RX
//


const char* ajGraphGetTitleC(const AjPGraph thys)
{
    if(thys->title)
        return ajStrGetPtr(thys->title);

    return "";
}
ID ajGraphGetTitleS
TY public
MO ajgraph
LB graphics
XX
DE Return plot title
XX
PN [1]
PA r thys const AjPGraph
PD Graph object.
PX
RT const AjPStr
RD Title
RX
//


const AjPStr ajGraphGetTitleS(const AjPGraph thys)
{
    return thys->title;
}
ID ajGraphGetXlabelC
TY public
MO ajgraph
LB graphics
XX
DE Return plot x-axis label
XX
PN [1]
PA r thys const AjPGraph
PD Graph object.
PX
RT const char*
RD Title of x-axis
RX
//


const char* ajGraphGetXlabelC(const AjPGraph thys)
{
    if (thys->xaxis)
        return ajStrGetPtr(thys->xaxis);

    return "";
}
ID ajGraphGetXlabelS
TY public
MO ajgraph
LB graphics
XX
DE Return plot x-axis label
XX
PN [1]
PA r thys const AjPGraph
PD Graph object.
PX
RT const AjPStr
RD Title of x-axis
RX
//


const AjPStr ajGraphGetXlabelS(const AjPGraph thys)
{
    return thys->xaxis;
}
ID ajGraphGetYlabelC
TY public
MO ajgraph
LB graphics
XX
DE Return plot y-axis label
XX
PN [1]
PA r thys const AjPGraph
PD Graph object.
PX
RT const char*
RD Title of y-axis
RX
//


const char* ajGraphGetYlabelC(const AjPGraph thys)
{
    if (thys->yaxis)
        return ajStrGetPtr(thys->yaxis);

    return "";
}
ID ajGraphGetYlabelS
TY public
MO ajgraph
LB graphics
XX
DE Return plot y-axis label
XX
PN [1]
PA r thys const AjPGraph
PD Graph object.
PX
RT const AjPStr
RD Title of y-axis
RX
//


const AjPStr ajGraphGetYlabelS(const AjPGraph thys)
{
    return thys->yaxis;
}
ID ajGraphIsData
TY public
MO ajgraph
LB graphics
XX
DE returns ajTrue if the graph is creating a data file
XX
PN [1]
PA r thys const AjPGraph
PD Graph object
PX
RT AjBool
RD ajTrue if the graph is of type data
RX
//


AjBool ajGraphIsData(const AjPGraph thys)
{
    return thys->isdata;
}
ID ajGraphAddLine
TY public
MO ajgraph
LB graphics
XX
DE Add a line to be drawn when the graph is plotted.
XX
PN [1]
PA u thys AjPGraph
PD Graph object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r x2 float
PD End x position
PX
PN [5]
PA r y2 float
PD End y position
PX
PN [6]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
RT void
RD 
RX
//


void ajGraphAddLine(AjPGraph thys, float x, float y,
                    float x2, float y2, ajint colour)
{
    AjPGraphobj Obj;

    if(!thys->Mainobj)
    {
	AJNEW(thys->Mainobj);
	Obj = thys->Mainobj;
    }
    else
    {
	Obj = thys->Mainobj;

	while(Obj->next)
	    Obj = Obj->next;

	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++thys->numofobjects;

    Obj->type = LINE;
    Obj->text = 0;
    Obj->xx1 = x;
    Obj->xx2 = x2;
    Obj->yy1 = y;
    Obj->yy2 = y2;
    Obj->colour = colour;
    Obj->next = 0;

    return;
}
ID ajGraphAddRect
TY public
MO ajgraph
LB graphics
XX
DE Add a Rectangle to be drawn when the graph is plotted, fill states whether
DE the rectangle should be filled in.
XX
PN [1]
PA u thys AjPGraph
PD Graph object
PX
PN [2]
PA r x float
PD Start x coordinate
PX
PN [3]
PA r y float
PD Start y coordinate
PX
PN [4]
PA r x2 float
PD End x coordinate
PX
PN [5]
PA r y2 float
PD End y coordinate
PX
PN [6]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
PN [7]
PA r fill ajint
PD Fill code (see PLPLOT)
PX
RT void
RD 
RX
//


void ajGraphAddRect(AjPGraph thys, float x, float y,
		       float x2, float y2, ajint colour, ajint fill)
{
    AjPGraphobj Obj;

    if(!thys->Mainobj)
    {
	AJNEW(thys->Mainobj);
	Obj = thys->Mainobj;
    }
    else
    {
	Obj = thys->Mainobj;

	while(Obj->next)
	    Obj = Obj->next;

	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++thys->numofobjects;


    if(fill)
	Obj->type = RECTANGLEFILL;
    else
	Obj->type = RECTANGLE;

    Obj->text = 0;
    Obj->xx1 = x;
    Obj->xx2 = x2;
    Obj->yy1 = y;
    Obj->yy2 = y2;
    Obj->colour = colour;
    Obj->next = 0;

    return;
}
ID ajGraphAddTextC
TY public
MO ajgraph
LB graphics
XX
DE Add text to be drawn when the graph is plotted.
XX
PN [1]
PA u thys AjPGraph
PD Graph object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
PN [5]
PA r txt const char*
PD Text
PX
RT void
RD 
RX
//


void ajGraphAddTextC(AjPGraph thys, float x, float y,
		       ajint colour, const char *txt)
{
    AjPGraphobj Obj;

    if(!thys->Mainobj)
    {
	AJNEW(thys->Mainobj);
	Obj = thys->Mainobj;
    }
    else
    {
	Obj = thys->Mainobj;
	while(Obj->next)
	    Obj = Obj->next;
	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++thys->numofobjects;


    Obj->type = TEXT;
    Obj->text = ajStrNewC(txt);
    Obj->xx1 = x;
    Obj->xx2 = 0.0;
    Obj->yy1 = y;
    Obj->yy2 = 0.0;
    Obj->colour = colour;
    Obj->scale = 0.0;
    Obj->next = 0;

    return;
}
ID ajGraphAddTextS
TY public
MO ajgraph
LB graphics
XX
DE Add text to be drawn when the graph is plotted.
XX
PN [1]
PA u thys AjPGraph
PD Graph object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
PN [5]
PA r str const AjPStr
PD Text
PX
RT void
RD 
RX
//


void ajGraphAddTextS(AjPGraph thys, float x, float y,
                     ajint colour, const AjPStr str)
{
    AjPGraphobj Obj;

    if(!thys->Mainobj)
    {
	AJNEW(thys->Mainobj);
	Obj = thys->Mainobj;
    }
    else
    {
	Obj = thys->Mainobj;
	while(Obj->next)
	    Obj = Obj->next;
	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++thys->numofobjects;


    Obj->type = TEXT;
    Obj->text = ajStrNewS(str);
    Obj->xx1 = x;
    Obj->xx2 = 0.0;
    Obj->yy1 = y;
    Obj->yy2 = 0.0;
    Obj->colour = colour;
    Obj->scale = 0.0;
    Obj->next = 0;

    return;
}
ID ajGraphAddTextScaleC
TY public
MO ajgraph
LB graphics
XX
DE Add text to be drawn when the graph is plotted.
XX
PN [1]
PA u thys AjPGraph
PD Graph object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
PN [5]
PA r scale float
PD Character scale (0.0 to use the default)
PX
PN [6]
PA r txt const char*
PD Text
PX
RT void
RD 
RX
//


void ajGraphAddTextScaleC(AjPGraph thys, float x, float y,
                          ajint colour, float scale, const char *txt)
{
    AjPGraphobj Obj;

    if(!thys->Mainobj)
    {
	AJNEW(thys->Mainobj);
	Obj = thys->Mainobj;
    }
    else
    {
	Obj = thys->Mainobj;

	while(Obj->next)
	    Obj = Obj->next;

	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++thys->numofobjects;


    Obj->type = TEXT;
    Obj->text = ajStrNewC(txt);
    Obj->xx1 = x;
    Obj->xx2 = 0.0;
    Obj->yy1 = y;
    Obj->yy2 = 0.0;
    Obj->colour = colour;
    Obj->scale = scale;
    Obj->next = 0;

    return;
}
ID ajGraphAddTextScaleS
TY public
MO ajgraph
LB graphics
XX
DE Add text to be drawn when the graph is plotted.
XX
PN [1]
PA u thys AjPGraph
PD Graph object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
PN [5]
PA r scale float
PD Character scale (0.0 to use the default)
PX
PN [6]
PA r str const AjPStr
PD Text
PX
RT void
RD 
RX
//


void ajGraphAddTextScaleS(AjPGraph thys, float x, float y,
                          ajint colour, float scale, const AjPStr str)
{
    AjPGraphobj Obj;

    if(!thys->Mainobj)
    {
	AJNEW(thys->Mainobj);
	Obj = thys->Mainobj;
    }
    else
    {
	Obj = thys->Mainobj;

	while(Obj->next)
	    Obj = Obj->next;

	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++thys->numofobjects;


    Obj->type = TEXT;
    Obj->text = ajStrNewS(str);
    Obj->xx1 = x;
    Obj->xx2 = 0.0;
    Obj->yy1 = y;
    Obj->yy2 = 0.0;
    Obj->colour = colour;
    Obj->scale = scale;
    Obj->next = 0;

    return;
}
ID GraphDraw
TY static
MO ajgraph
LB graphics
XX
DE Display the drawable objects connected to this graph.
XX
PN [1]
PA r thys const AjPGraph
PD Graph object
PX
RT void
RD 
RX
//


static void GraphDraw(const AjPGraph thys)
{
    AjPGraphobj Obj;
    ajint temp;
    float cold = 1.0;
    ajuint nobj = 0;

    /* graphdata : calls ajGraphLine etc. */

    ajDebug("ajGraphDraw\n");
    if(!thys->Mainobj)
	return;

    Obj = thys->Mainobj;

    while(Obj)
    {
        if(Obj->type == RECTANGLE)
        {
            temp = ajGraphicsSetFgcolour(Obj->colour);
            ajGraphicsDrawposRect(Obj->xx1, Obj->yy1,Obj->xx2,
                                  Obj->yy2);
            ajGraphicsSetFgcolour(temp);
        }
        else if(Obj->type == RECTANGLEFILL)
        {
            temp = ajGraphicsSetFgcolour(Obj->colour);
            ajGraphicsDrawposRectFill(Obj->xx1, Obj->yy1,Obj->xx2,
                                      Obj->yy2);
            ajGraphicsSetFgcolour(temp);
        }
        else if(Obj->type == TEXT )
        {
            if(Obj->scale)
                cold = ajGraphicsSetCharscale(Obj->scale);
            temp = ajGraphicsSetFgcolour(Obj->colour);
            ajGraphicsDrawposTextAtstart(Obj->xx1, Obj->yy1,
                                         ajStrGetPtr(Obj->text));
            ajGraphicsSetFgcolour(temp);
            if(Obj->scale)
                ajGraphicsSetCharscale(cold);
        }
        else if(Obj->type == LINE)
        {
            temp = ajGraphicsSetFgcolour(Obj->colour);
            ajGraphicsDrawposLine(Obj->xx1, Obj->yy1,Obj->xx2,
                                  Obj->yy2);
            ajGraphicsSetFgcolour(temp);
        }
        else
            ajUserDumpC("UNDEFINED OBJECT TYPE USED");

        Obj = Obj->next;
        ++nobj;
    }

    ajDebug("drew %u objects in ajGraphDataDraw\n", nobj);

    return;
}
ID GraphPrint
TY static
MO ajgraph
LB graphics
XX
DE Print all the drawable objects in readable form.
XX
PN [1]
PA r thys const AjPGraph
PD Graph object
PX
RT void
RD 
RX
//


static void GraphPrint(const AjPGraph thys)
{
    AjPGraphobj Obj;

    if(!thys->Mainobj)
    {
	ajUserDumpC("No Objects");

	return;
    }
    else			   /* cycle through till NULL found */
    {
	Obj = thys->Mainobj;

	while(Obj)
	{
	    if(Obj->type == RECTANGLE )
		ajUser("type = RECTANGLE, %f %f %f %f col= %d",
		       Obj->xx1, Obj->yy1,
		       Obj->xx2,Obj->yy2,Obj->colour);
	    else if(Obj->type == RECTANGLEFILL )
		ajUser("type = RECTANGLEFILL, %f %f %f %f col= %d",
		       Obj->xx1, Obj->yy1,
		       Obj->xx2,Obj->yy2,Obj->colour);
	    else if(Obj->type == TEXT)
		ajUser("type = TEXT, %f %f col= %d %S",
		       Obj->xx1, Obj->yy1,Obj->colour,
		       Obj->text);
	    else if(Obj->type == LINE )
		ajUser("type = LINE, %f %f %f %f col= %d",
		       Obj->xx1, Obj->yy1,
		       Obj->xx2,Obj->yy2,Obj->colour);

	    Obj = Obj->next;
	}
    }

    return;
}
ID ajGraphTrace
TY public
MO ajgraph
LB graphics
XX
DE Writes debug messages to trace the contents of a graph object.
XX
PN [1]
PA r thys const AjPGraph
PD Graph object
PX
RT void
RD 
RX
//


void ajGraphTrace(const AjPGraph thys)
{
    AjPGraphdata graphdata;
    ajuint i;

    ajDebug("Graph trace\n");

    ajDebug("\n(a) True booleans\n");

    ajDebug("minmaxcalc %B\n", thys->minmaxcalc);


    ajDebug("\n(b) Strings with values\n");

    ajDebug("Desc '%S'\n", thys->desc);
    ajDebug("Title '%S'\n", thys->title);
    ajDebug("Subtitle '%S'\n", thys->subtitle);
    ajDebug("Xaxis '%S'\n", thys->xaxis);
    ajDebug("Yaxis '%S'\n", thys->yaxis);
    ajDebug("outputfile '%S'\n", thys->outputfile);

    ajDebug("\n(c) Other values\n");
    ajDebug("flags %x\n", thys->flags);
    ajDebug("numofgraphs %d\n", thys->numofgraphs);
    ajDebug("numofgraphsmax %d\n", thys->numofgraphsmax);
    ajDebug("minX   %7.3f maxX   %7.3f\n",
	    thys->minX,
	    thys->maxX);
        
    ajDebug("minX   %7.3f maxX   %7.3f\n",
	    thys->minX,
	    thys->maxX);
    ajDebug("minY   %7.3f maxY   %7.3f\n",
	    thys->minY,
	    thys->maxY);
    ajDebug("xstart %7.3f xend   %7.3f\n",
	    thys->xstart,
	    thys->xend);
    ajDebug("ystart %7.3f yend   %7.3f\n",
	    thys->ystart,
	    thys->yend);
    ajDebug("displaytype %d '%s'\n", thys->displaytype,
	    graphType[thys->displaytype].Device);

    for(i=0; i < thys->numofgraphs; i++)
    {
        ajDebug("GraphData[%u]\n", i);
        graphdata = (thys->graphs)[i];
        ajGraphdataTrace(graphdata);
    }
    
    return;
}
ID ajGraphDataAdd
TY public
MO ajgraph
LB graphics
XX
DE Add another graph structure to the multiple graph structure.
DE
DE The graphdata now belongs to the graph - do not delete it while
DE the graph is using it.
XX
PN [1]
PA u thys AjPGraph
PD multiple graph structure.
PX
PN [2]
PA u graphdata AjPGraphdata
PD graph to be added.
PX
RT AjBool
RD True if graph added successfully
RX
//


AjBool ajGraphDataAdd(AjPGraph thys, AjPGraphdata graphdata)
{
    if(thys->numofgraphs)
    {
        ajDebug("ajGraphDataAdd multi \n");
    }

    if(thys->numofgraphs < thys->numofgraphsmax)
    {
        (thys->graphs)[thys->numofgraphs++] = graphdata;
        return ajTrue;
    }

    ajErr("Too many multiple graphs - expected %d graphs",
          thys->numofgraphsmax);

    return ajFalse;
}
ID ajGraphDataReplace
TY public
MO ajgraph
LB graphics
XX
DE Replace graph structure into the multiple graph structure.
DE
DE The graphdata now belongs to the graph - do not delete it while
DE the graph is using it.
XX
PN [1]
PA u thys AjPGraph
PD multiple graph structure.
PX
PN [2]
PA u graphdata AjPGraphdata
PD graph to be added.
PX
RT AjBool
RD True if graph added successfully
RX
//


AjBool ajGraphDataReplace(AjPGraph thys, AjPGraphdata graphdata)
{
    ajGraphdataDel(&(thys->graphs)[0]);
    (thys->graphs)[0] = graphdata;
    thys->numofgraphs=1;

    thys->minmaxcalc = 0;

    return ajTrue;
}
ID ajGraphDataReplaceI
TY public
MO ajgraph
LB graphics
XX
DE Replace one of the graph structures in the multiple graph structure.
DE
DE The graphdata now belongs to the graph - do not delete it while
DE the graph is using it.
XX
PN [1]
PA u thys AjPGraph
PD multiple graph structure.
PX
PN [2]
PA u graphdata AjPGraphdata
PD graph to be added.
PX
PN [3]
PA r num ajuint
PD number within multiple graph.
PX
RT AjBool
RD True if graph added successfully
RX
//


AjBool ajGraphDataReplaceI(AjPGraph thys, AjPGraphdata graphdata, ajuint num)
{
    if(thys->numofgraphs > num) {
        ajGraphdataDel(&(thys->graphs)[num]);
        (thys->graphs)[num] = graphdata;
    }
    else if(thys->numofgraphs < thys->numofgraphsmax)
    {
        (thys->graphs)[thys->numofgraphs++] = graphdata;
    }
    else
    {
        return ajFalse;
    }

    thys->minmaxcalc = 0;

    return ajTrue;
}
ID ajGraphxyNewI
TY public
MO ajgraph
LB graphics
XX
DE Create a structure to hold a number of graphs.
XX
PN [1]
PA r numsets ajuint
PD maximum number of graphs that can be stored.
PX
RT AjPGraph
RD multiple graph structure.
RX
//


AjPGraph ajGraphxyNewI(ajuint numsets)
{
    AjPGraph ret;

    AJNEW0(ret);
    ret->numsets = numsets;

    ajDebug("ajGraphxyNewI numsets: %d\n", numsets);

    return ret;
}
ID ajGraphxyDel
TY public
MO ajgraph
LB graphics
XX
DE Destructor for a graph object
XX
PN [1]
PA w pthis AjPGraph*
PD Graph structure to store info in.
PX
RT void
RD 
RX
//


void ajGraphxyDel(AjPGraph* pthis)
{
    AjPGraphdata graphdata;
    AjPGraph thys;
    ajuint i;

    if(!pthis)
	return;

    if(!*pthis)
	return;

    thys = *pthis;

    ajDebug("ajGraphxyDel numofgraphs:%d\n",
            thys->numofgraphs);

    for(i = 0 ; i < thys->numofgraphs ; i++)
    {
        graphdata = (thys->graphs)[i];

        if (graphdata)
        {
            ajDebug("ajGraphxyDel graphs[%d] xcalc:%B ycalc:%B x:%x y:%x\n",
                    i, graphdata->xcalc, graphdata->ycalc,
                    graphdata->x, graphdata->y);

            if(!graphdata->xcalc)
                AJFREE(graphdata->x);
            if(!graphdata->ycalc)
                AJFREE(graphdata->y);
            ajGraphdataDel(&graphdata);
        }
    }

    ajStrDel(&thys->desc);
    ajStrDel(&thys->title);
    ajStrDel(&thys->subtitle);
    ajStrDel(&thys->xaxis);
    ajStrDel(&thys->yaxis);
    ajStrDel(&thys->dataname);
    ajStrDel(&thys->outputfile);

    ajGraphClear(thys);

    AJFREE(thys->graphs);

    AJFREE(thys);

    *pthis = NULL;

    return;
}
ID ajGraphxyDisplay
TY public
MO ajgraph
LB graphics
XX
DE A general routine for drawing graphs.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r closeit AjBool
PD Whether to close graph at the end.
PX
RT void
RD 
RX
//


void ajGraphxyDisplay(AjPGraph thys, AjBool closeit)
{
    /* Calling funclist graphType() */
    (graphType[thys->displaytype].XYDisplay)
	(thys, closeit, graphType[thys->displaytype].Ext);

    return;
}
ID ajGraphxySetDevicetype
TY public
MO ajgraph
LB graphics
XX
DE Initialize options for a graphxy object
XX
PN [1]
PA u thys AjPGraph
PD Graph object
PX
PN [2]
PA r devicetype const AjPStr
PD Device type
PX
RT AjBool
RD ajTrue on success
RX
//


AjBool ajGraphxySetDevicetype(AjPGraph thys, const AjPStr devicetype)
{
    ajint i;
    ajint j=-1;
    ajint k;
    AjPStr aliases = NULL;

    ajDebug("ajGraphxySetDevicetype '%S'\n", devicetype);

    for(i=0;graphType[i].Name;i++)
    {
        if(!graphType[i].XYDisplay)
            continue;

        if(ajStrMatchCaseC(devicetype, graphType[i].Name))
	{
	    j = i;
	    break;
	}

	if(ajCharPrefixCaseS(graphType[i].Name, devicetype))
	{
	    if(j < 0)
		j = i;
	    else
	    {
		for(k=0;graphType[k].Name;k++)
		{
		    if(ajCharPrefixCaseS(graphType[k].Name, devicetype))
		    {
			if(ajStrGetLen(aliases))
			    ajStrAppendC(&aliases, ", ");
			ajStrAppendC(&aliases, graphType[k].Name);
		    }
		}
		    
		ajErr("Ambiguous graph device name '%S' (%S)",
		       devicetype, aliases);
		ajStrDel(&aliases);

		return ajFalse;
	    }
	}
    }

    if(j<0)
	return ajFalse;

    GraphxyNewPlplot(thys);

    /* Calling funclist graphType() */

    thys->displaytype = j;

    ajDebug("ajGraphxySetDevicetype type '%S' displaytype %d '%s'\n",
	    devicetype, j, graphType[j].Name);

    return ajTrue;

}
ID ajGraphxySetMinmax
TY public
MO ajgraph
LB graphics
XX
DE Set the max and min of the data points for all graphs.
XX
PN [1]
PA u thys AjPGraph
PD multiple graph structure.
PX
PN [2]
PA r xmin float
PD x min.
PX
PN [3]
PA r xmax float
PD x max.
PX
PN [4]
PA r ymin float
PD y min.
PX
PN [5]
PA r ymax float
PD y max.
PX
RT void
RD 
RX
//


void ajGraphxySetMinmax(AjPGraph thys,float xmin,float xmax,
			float ymin,float ymax)
{
    AjPGraphdata graphdata;
    ajuint i;

    if (!thys)
	return;

    thys->minX = xmin;
    thys->minY = ymin;
    thys->maxX = xmax;
    thys->maxY = ymax;

    for(i = 0 ; i < thys->numofgraphs ; i++)
    {
	graphdata = (thys->graphs)[i];
	graphdata->minX = xmin;
	graphdata->minY = ymin;
	graphdata->maxX = xmax;
	graphdata->maxY = ymax;
    }

    return;
}
ID ajGraphxySetRanges
TY public
MO ajgraph
LB graphics
XX
DE Calculate the max and min of the data points and store them.
DE
DE See ajGraphxySetMaxmin for a function that defines explicit ranges
XX
PN [1]
PA u thys AjPGraph
PD multiple graph structure.
PX
RT void
RD 
RX
//


void ajGraphxySetRanges(AjPGraph thys)
{
    AjPGraphdata graphdata = NULL;
    ajuint i;
    ajuint j;

    for(i = 0 ; i < thys->numofgraphs ; i++)
    {
	graphdata = (thys->graphs)[i];

        if(graphdata->truescale)
        {
            thys->minX = graphdata->tminX;
            thys->maxX = graphdata->tmaxX;
            thys->minY = graphdata->tminY;
            thys->maxY = graphdata->tmaxY;

            graphdata->minX = graphdata->tminX;
            graphdata->maxX = graphdata->tmaxX;
            graphdata->minY = graphdata->tminY;
            graphdata->maxY = graphdata->tmaxY;
        }
        else
        {
            if(graphdata->minX == graphdata->maxX ||
               graphdata->minY == graphdata->maxY)
            {
                graphdata->minX = graphdata->minY =  64000;
                graphdata->maxX = graphdata->maxY =  -64000;

                for( j = 0 ; j < graphdata->numofpoints; j++)
                {
                    if(graphdata->maxX < (graphdata->x)[j])
                        graphdata->maxX = (graphdata->x)[j];
                    if(graphdata->maxY < (graphdata->y)[j])
                        graphdata->maxY = (graphdata->y)[j];
                    if(graphdata->minX > (graphdata->x)[j])
                        graphdata->minX = (graphdata->x)[j];
                    if(graphdata->minY > (graphdata->y)[j])
                        graphdata->minY = (graphdata->y)[j];
                }
                if(graphdata->minX == graphdata->maxX)
                {
                    if(graphdata->minX < 0.0)
		      graphdata->minX *= (float) 1.1;
                    else
		      graphdata->minX /= (float) 1.1;

                    if(graphdata->maxX < 0.0)
		      graphdata->maxX /= (float) 1.1;
                    else
		      graphdata->maxX *= (float) 1.1;
                }

                if(graphdata->minY == graphdata->maxY)
                {
                    if(graphdata->minY < 0.0)
		      graphdata->minY *= (float) 1.1;
                    else
		      graphdata->minY /= (float) 1.1;

                    if(graphdata->maxY < 0.0)
		      graphdata->maxY /= (float) 1.1;
                    else
		      graphdata->maxY *= (float) 1.1;
                }
            }

            if (thys->minX > graphdata->minX)
                thys->minX = graphdata->minX;

            if (thys->minY > graphdata->minY)
                thys->minY = graphdata->minY;

            if (thys->maxX < graphdata->maxX)
                thys->maxX = graphdata->maxX;

            if (thys->maxY < graphdata->maxY)
                thys->maxY = graphdata->maxY;
        }
    }
    

    if(!thys->minmaxcalc)
    {
	thys->xstart     = thys->minX;
	thys->xend       = thys->maxX;
	thys->ystart     = thys->minY;
	thys->yend       = thys->maxY;

        thys->minmaxcalc = ajTrue;
    }

    return;
}
ID ajGraphxySetXendF
TY public
MO ajgraph
LB graphics
XX
DE Set the end position for X in the graph.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r val float
PD The end value for x graph coord
PX
RT void
RD 
RX
//


void ajGraphxySetXendF(AjPGraph thys, float val)
{
    if (thys)
    {
	thys->xend = val;
    }

    return;
}
ID ajGraphxySetXrangeII
TY public
MO ajgraph
LB graphics
XX
DE Sets the X axis range with integers
XX
PN [1]
PA u thys AjPGraph
PD Graph
PX
PN [2]
PA r start ajint
PD start position
PX
PN [3]
PA r end ajint
PD end position
PX
RT void
RD 
RX
//


void ajGraphxySetXrangeII(AjPGraph thys, ajint start, ajint end)
{
    if (thys)
    {
	thys->xstart = (float) start;
	thys->xend = (float) end;
    }

    return;
}
ID ajGraphxySetXstartF
TY public
MO ajgraph
LB graphics
XX
DE Set the start position for X in the graph.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r val float
PD The start value for x graph coord.
PX
RT void
RD 
RX
//


void ajGraphxySetXstartF(AjPGraph thys, float val)
{
    if (thys)
    {
	thys->xstart = val;
    }

    return;
}
ID ajGraphxySetYendF
TY public
MO ajgraph
LB graphics
XX
DE Set the end position for Y in the graph.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r val float
PD The start value for y graph coord.
PX
RT void
RD 
RX
//


void ajGraphxySetYendF(AjPGraph thys, float val)
{
    if (thys)
    {
	thys->yend = val;
    }

    return;
}
ID ajGraphxySetYrangeII
TY public
MO ajgraph
LB graphics
XX
DE Sets the Y axis range with integers
XX
PN [1]
PA u thys AjPGraph
PD Graph
PX
PN [2]
PA r start ajint
PD start position
PX
PN [3]
PA r end ajint
PD end position
PX
RT void
RD 
RX
//


void ajGraphxySetYrangeII(AjPGraph thys, ajint start, ajint end)
{
    if (thys)
    {
	thys->ystart = (float) start;
	thys->yend = (float) end;
    }

    return;
}
ID ajGraphxySetYstartF
TY public
MO ajgraph
LB graphics
XX
DE Set the start position for Y in the graph.
XX
PN [1]
PA u thys AjPGraph
PD Graph structure to store info in.
PX
PN [2]
PA r val float
PD The end value for y graph coord.
PX
RT void
RD 
RX
//


void ajGraphxySetYstartF(AjPGraph thys, float val)
{
    if (thys)
    {
	thys->ystart = val;
    }

    return;
}
ID ajGraphxySetflagGaps
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graphs should enable gaps.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph object
PX
PN [2]
PA r set AjBool
PD if true allow gaps else do not.
PX
RT void
RD 
RX
//


void ajGraphxySetflagGaps(AjPGraph thys, AjBool set)
{
    if (!thys)
	return;

    if(thys->flags & AJGRAPH_GAPS)
    {
	if(!set)
	    thys->flags -= AJGRAPH_GAPS;
    }
    else
    {
	if(set)
	    thys->flags += AJGRAPH_GAPS;
    }

    return;
}
ID ajGraphxySetflagOverlay
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graphs should lay on top of each other.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph object
PX
PN [2]
PA r set AjBool
PD if true overlap else do not.
PX
RT void
RD 
RX
//


void ajGraphxySetflagOverlay(AjPGraph thys, AjBool set)
{
    if (!thys)
	return;

    if(thys->flags & AJGRAPH_OVERLAP)
    {
	if(!set)
	    thys->flags -= AJGRAPH_OVERLAP;
    }
    else
    {
	if(set)
	    thys->flags += AJGRAPH_OVERLAP;
    }

    return;
}
ID ajGraphxyShowPointsCircle
TY public
MO ajgraph
LB graphics
XX
DE Set the graph to draw circles at the points.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowPointsCircle(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_CIRCLEPOINTS, set);

    return;
}
ID ajGraphxyShowPointsJoin
TY public
MO ajgraph
LB graphics
XX
DE Set the graph to draw lines between the points.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowPointsJoin(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_JOINPOINTS, set);

    return;
}
ID ajGraphxyShowRaxis
TY public
MO ajgraph
LB graphics
XX
DE Set the graph is to display a right hand Y axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowRaxis(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_Y_RIGHT, set);

    return;
}
ID ajGraphxyShowUaxis
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to display the left X axis at the top.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowUaxis(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_X_TOP, set);

    return;
}
ID ajGraphxyShowUnum
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to display the labels on the upper x axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowUnum(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_X_NUMLABEL_ABOVE, set);

    return;
}
ID ajGraphxyShowXaxis
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to display a bottom x axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowXaxis(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_X_BOTTOM, set);

    return;
}
ID ajGraphxyShowXgrid
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to grid the tick marks on the x axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowXgrid(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_X_GRID, set);

    return;
}
ID ajGraphxyShowXinvert
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to display the tick marks inside the plot on
DE the x axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowXinvert(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_X_INVERT_TICK, set);

    return;
}
ID ajGraphxyShowXlabel
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to label the x axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowXlabel(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_X_LABEL, set);

    return;
}
ID ajGraphxyShowXtick
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to display tick marks on the x axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowXtick(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_X_TICK, set);

    return;
}
ID ajGraphxyShowYaxis
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to display the left Y axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowYaxis(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_Y_LEFT, set);

    return;
}
ID ajGraphxyShowYgrid
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to grid the tick marks on the x axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowYgrid(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_Y_GRID, set);

    return;
}
ID ajGraphxyShowYinvert
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to display the tick marks inside the plot
DE on the y axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowYinvert(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_Y_INVERT_TICK, set);

    return;
}
ID ajGraphxyShowYlabel
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to label the y axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowYlabel(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_Y_LABEL, set);

    return;
}
ID ajGraphxyShowYnum
TY public
MO ajgraph
LB graphics
XX
DE Set whether the graph is to display the numbers on the left Y axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowYnum(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_Y_NUMLABEL_LEFT, set);

    return;
}
ID ajGraphxyShowYtick
TY public
MO ajgraph
LB graphics
XX
DE Set the graph is to display tick marks on the y axis.
XX
PN [1]
PA u thys AjPGraph
PD graph to have flag altered
PX
PN [2]
PA r set AjBool
PD whether to set or turn off.
PX
RT void
RD 
RX
//


void ajGraphxyShowYtick(AjPGraph thys, AjBool set)
{
    ajGraphSetFlag(thys, AJGRAPH_Y_TICK, set);

    return;
}
ID ajGraphdataNew
TY public
MO ajgraph
LB graphics
XX
DE Creates a new empty graph
XX
RT AjPGraphdata
RD New empty graph
RX
//


AjPGraphdata ajGraphdataNew(void)
{
    static AjPGraphdata graphdata;

    AJNEW0(graphdata);
    graphdata->numofpoints = 0;

    return graphdata;
}
ID ajGraphdataNewI
TY public
MO ajgraph
LB graphics
XX
DE Create and initialise the data structure for the graph with a defined
DE number of data points.
XX
PN [1]
PA r numofpoints ajuint
PD Number of points
PX
RT AjPGraphdata
RD Pointer to new graph structure.
RX
//


AjPGraphdata ajGraphdataNewI(ajuint numofpoints)
{
    AjPGraphdata graphdata;

    AJNEW0(graphdata);

    GraphdataInit(graphdata);

    graphdata->numofpoints = numofpoints;

    graphdata->xcalc = ajFalse;
    AJCNEW0(graphdata->x, numofpoints);

    graphdata->ycalc = ajFalse;
    AJCNEW0(graphdata->y, numofpoints);

    return graphdata;
}
ID ajGraphdataDel
TY public
MO ajgraph
LB graphics
XX
DE Destructor for a graph data object
XX
PN [1]
PA d pthys AjPGraphdata*
PD Graph data object
PX
RT void
RD 
RX
//


void ajGraphdataDel(AjPGraphdata *pthys)
{
    AjPGraphobj here = NULL;
    AjPGraphobj p    = NULL;
    AjPGraphdata thys;

    thys = *pthys;

    if (!thys)
	return;

    ajDebug("ajGraphdataDel objects:%d\n", thys->numofobjects);

    here = p = thys->Dataobj;

    while(p)
    {
	p = here->next;
	ajStrDel(&here->text);
	AJFREE(here);
	here = p;
    }

    thys->Dataobj = NULL;

    ajStrDel(&thys->title);
    ajStrDel(&thys->subtitle);
    ajStrDel(&thys->xaxis);
    ajStrDel(&thys->yaxis);
    ajStrDel(&thys->gtype);
    ajStrDel(&thys->dataname);
    AJFREE(thys->x);
    AJFREE(thys->y);
    thys->numofobjects = 0;

    AJFREE(*pthys);

    return;
}
ID ajGraphdataSetColour
TY public
MO ajgraph
LB graphics
XX
DE Set the colour for the plot on one graph.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r colour ajint
PD colour for this plot.
PX
RT void
RD 
RX
//


void ajGraphdataSetColour(AjPGraphdata graphdata, ajint colour)
{
    graphdata->colour = colour;

    return;
}
ID ajGraphdataSetDatanameC
TY public
MO ajgraph
LB graphics
XX
DE Set the Subtitle.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD Data source name
PX
RT void
RD 
RX
//


void ajGraphdataSetDatanameC(AjPGraphdata graphdata, const char *txt)
{
    ajStrAssignEmptyC(&graphdata->dataname,txt);

    return;
}
ID ajGraphdataSetDatanameS
TY public
MO ajgraph
LB graphics
XX
DE Set the Subtitle.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD Data source name
PX
RT void
RD 
RX
//


void ajGraphdataSetDatanameS(AjPGraphdata graphdata, const AjPStr str)
{
    ajStrAssignEmptyS(&graphdata->dataname,str);

    return;
}
ID ajGraphdataSetLinetype
TY public
MO ajgraph
LB graphics
XX
DE Set the line type for this graph.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r type ajint
PD Set the line type.
PX
RT void
RD 
RX
//


void ajGraphdataSetLinetype(AjPGraphdata graphdata, ajint type)
{
    graphdata->lineType = type;

    return;
}
ID ajGraphdataSetMinmax
TY public
MO ajgraph
LB graphics
XX
DE Set the max and min of the data points you wish to display.
XX
PN [1]
PA u graphdata AjPGraphdata
PD multiple graph structure.
PX
PN [2]
PA r xmin float
PD x min.
PX
PN [3]
PA r xmax float
PD x max.
PX
PN [4]
PA r ymin float
PD y min.
PX
PN [5]
PA r ymax float
PD y max.
PX
RT void
RD 
RX
//


void ajGraphdataSetMinmax(AjPGraphdata graphdata, float xmin, float xmax,
                          float ymin, float ymax)
{
    graphdata->minX = xmin;
    graphdata->minY = ymin;
    graphdata->maxX = xmax;
    graphdata->maxY = ymax;

    return;
}
ID ajGraphdataSetSubtitleC
TY public
MO ajgraph
LB graphics
XX
DE Set the subtitle.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD Sub Title
PX
RT void
RD 
RX
//


void ajGraphdataSetSubtitleC(AjPGraphdata graphdata, const char *txt)
{
    ajStrAssignEmptyC(&graphdata->subtitle,txt);

    return;
}
ID ajGraphdataSetSubtitleS
TY public
MO ajgraph
LB graphics
XX
DE Set the Subtitle.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD Sub Title
PX
RT void
RD 
RX
//


void ajGraphdataSetSubtitleS(AjPGraphdata graphdata, const AjPStr str)
{
    ajStrAssignEmptyS(&graphdata->subtitle,str);

    return;
}
ID ajGraphdataSetTitleC
TY public
MO ajgraph
LB graphics
XX
DE Set the title.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD Title
PX
RT void
RD 
RX
//


void ajGraphdataSetTitleC(AjPGraphdata graphdata, const char *txt)
{
    ajStrAssignEmptyC(&graphdata->title,txt);

    return;
}
ID ajGraphdataSetTitleS
TY public
MO ajgraph
LB graphics
XX
DE Set the title.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD Title
PX
RT void
RD 
RX
//


void ajGraphdataSetTitleS(AjPGraphdata graphdata, const AjPStr str)
{
    ajStrAssignEmptyS(&graphdata->title,str);

    return;
}
ID ajGraphdataSetTruescale
TY public
MO ajgraph
LB graphics
XX
DE Set the scale max and min of the data points you wish to display.
XX
PN [1]
PA u graphdata AjPGraphdata
PD multiple graph structure.
PX
PN [2]
PA r xmin float
PD true x min.
PX
PN [3]
PA r xmax float
PD true x max.
PX
PN [4]
PA r ymin float
PD true y min.
PX
PN [5]
PA r ymax float
PD true y max.
PX
RT void
RD 
RX
//


void ajGraphdataSetTruescale(AjPGraphdata graphdata, float xmin, float xmax,
			    float ymin, float ymax)
{
    graphdata->tminX = xmin;
    graphdata->tminY = ymin;
    graphdata->tmaxX = xmax;
    graphdata->tmaxY = ymax;

    graphdata->truescale = ajTrue;

    return;
}
ID ajGraphdataSetTypeC
TY public
MO ajgraph
LB graphics
XX
DE Set the type of the graph for data output.
XX
PN [1]
PA u graphdata AjPGraphdata
PD multiple graph structure.
PX
PN [2]
PA r txt const char*
PD Type e.g. "2D Plot", "Histogram".
PX
RT void
RD 
RX
//


void ajGraphdataSetTypeC(AjPGraphdata graphdata,const  char* txt)
{
    ajStrAssignC(&graphdata->gtype,txt);

    return;
}
ID ajGraphdataSetTypeS
TY public
MO ajgraph
LB graphics
XX
DE Set the type of the graph for data output.
XX
PN [1]
PA u graphdata AjPGraphdata
PD multiple graph structure.
PX
PN [2]
PA r str const AjPStr
PD Type e.g. "2D Plot", "Histogram".
PX
RT void
RD 
RX
//


void ajGraphdataSetTypeS(AjPGraphdata graphdata,const AjPStr str)
{
    ajStrAssignS(&graphdata->gtype,str);

    return;
}
ID ajGraphdataSetXlabelC
TY public
MO ajgraph
LB graphics
XX
DE Set the title for the X axis.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD title for the X axis.
PX
RT void
RD 
RX
//


void ajGraphdataSetXlabelC(AjPGraphdata graphdata, const char* txt)
{
    ajStrAssignEmptyC(&graphdata->xaxis,txt);

    return;
}
ID ajGraphdataSetXlabelS
TY public
MO ajgraph
LB graphics
XX
DE Set the title for the X axis.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD title for the X axis.
PX
RT void
RD 
RX
//


void ajGraphdataSetXlabelS(AjPGraphdata graphdata, const AjPStr str)
{
    ajStrAssignEmptyS(&graphdata->xaxis,str);

    return;
}
ID ajGraphdataSetYlabelC
TY public
MO ajgraph
LB graphics
XX
DE Set the title for the Y axis.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r txt const char*
PD title for the Y axis.
PX
RT void
RD 
RX
//


void ajGraphdataSetYlabelC(AjPGraphdata graphdata, const char* txt)
{
    ajStrAssignEmptyC(&graphdata->yaxis,txt);

    return;
}
ID ajGraphdataSetYlabelS
TY public
MO ajgraph
LB graphics
XX
DE Set the title for the Y axis.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph structure to store info in.
PX
PN [2]
PA r str const AjPStr
PD title for the Y axis.
PX
RT void
RD 
RX
//


void ajGraphdataSetYlabelS(AjPGraphdata graphdata, const AjPStr str)
{
    ajStrAssignEmptyS(&graphdata->yaxis, str);

    return;
}
ID GraphInit
TY static
MO ajgraph
LB graphics
XX
DE calls plinit.
XX
PN [1]
PA w thys AjPGraph
PD Graph object.
PX
RT void
RD 
RX
//


static void GraphInit(AjPGraph thys)
{
    float fold;

    if (thys)
    {
	if(!thys->ready)
	{
#if GRAPH_DEBUG
	  ajDebug("=g= plinit ()\n");
#endif

          plinit();
	}

	thys->ready = ajTrue;
    }

    fold = ajGraphicsSetCharscale(0.0);
    fold = ajGraphicsSetCharscale(fold);

    return;
}
ID GraphSubPage
TY static
MO ajgraph
LB graphics
XX
DE start new graph page.
XX
PN [1]
PA r page ajint
PD Page number
PX
RT void
RD 
RX
//


static void GraphSubPage(ajint page)
{
#if GRAPH_DEBUG
    ajDebug("=g= pladv (%d) [page]\n", page);
#endif

    pladv(page);

    return;
}
ID GraphWind
TY static
MO ajgraph
LB graphics
XX
DE Calls plwind.
XX
PN [1]
PA r xmin float
PD Minimum x axis value
PX
PN [2]
PA r xmax float
PD Maximum x axis value
PX
PN [3]
PA r ymin float
PD Minimum y axis value
PX
PN [4]
PA r ymax float
PD Maximum y axis value
PX
RT void
RD 
RX
//


static void GraphWind(float xmin, float xmax, float ymin, float ymax)
{
#if GRAPH_DEBUG
    ajDebug("=g= plwind (%.2f, %.2f, %.2f, %.2f) [xmin/max ymin/max]\n",
            xmin, xmax, ymin, ymax);
#endif

    plwind(xmin, xmax, ymin, ymax);

    return;
}
ID GraphSetWin
TY static
MO ajgraph
LB graphics
XX
DE Creates a window using the ranges.
XX
PN [1]
PA r xmin float
PD Minimum x axis value
PX
PN [2]
PA r xmax float
PD Maximum x axis value
PX
PN [3]
PA r ymin float
PD Minimum y axis value
PX
PN [4]
PA r ymax float
PD Maximum y axis value
PX
RT void
RD 
RX
//


static void GraphSetWin(float xmin, float xmax, float ymin, float ymax)
{
    ajDebug("GraphSetWin(%.3f, %.3f, %.3f, %.3f)\n",
	   xmin, xmax, ymin, ymax);

    if(graphData)
    {
	graphData->xmin = xmin;
	graphData->ymin = ymin;
	graphData->xmax = xmax;
	graphData->ymax = ymax;

	if(graphData->Num == 1)	/* else GraphDatafileNext does it */
	{
	    ajFmtPrintF(graphData->File,
			"##Graphic\n##Screen x1 %f y1 %f x2 %f y2 %f\n",
			graphData->xmin, graphData->ymin,
			graphData->xmax, graphData->ymax);
	    graphData->Lines++;
	    graphData->Lines++;
	}
    }
    else
    {
	/*
	** Use the whole screen. User may add boundaries by
	** modifying xmin, xmax etc.
	*/
#if GRAPH_DEBUG
    ajDebug("=g= plvpor (0.0,1.0,0.0,1.0) [whole screen]\n");
#endif

        plvpor (0.0,1.0,0.0,1.0);
	GraphWind(xmin, xmax, ymin, ymax);
    }

    return;
}
ID GraphSetWin2
TY static
MO ajgraph
LB graphics
XX
DE Creates a window using the ranges.
XX
PN [1]
PA r xmin float
PD Minimum x axis value
PX
PN [2]
PA r xmax float
PD Maximum x axis value
PX
PN [3]
PA r ymin float
PD Minimum y axis value
PX
PN [4]
PA r ymax float
PD Maximum y axis value
PX
RT void
RD 
RX
//


static void GraphSetWin2(float xmin, float xmax, float ymin, float ymax)
{
#if GRAPH_DEBUG
    ajDebug("=g= plvsta()\n");
#endif

    plvsta();

    /* use the whole screen */
    GraphWind(xmin, xmax, ymin, ymax);

    return;
}
ID GraphArray
TY static
MO ajgraph
LB graphics
XX
DE Draw lines from the array of x and y values.
XX
PN [1]
PA r numofpoints ajuint
PD Number of data points
PX
PN [2]
PA r x CONST float*
PD Array of x axis values
PX
PN [3]
PA r y CONST float*
PD Array of y axis values
PX
RT void
RD 
RX
//


static void GraphArray(ajuint numofpoints, float *x, float *y)
{
    ajuint i;
    float xlast;
    float ylast;

#if GRAPH_DEBUG
    if(numofpoints)
	ajDebug("=g= plline( %d, %.2f .. %.2f, %.2f .. %.2f) "
		"[num x..x y..y]\n",
		numofpoints, x[0], x[numofpoints-1], y[0], y[numofpoints-1] );
    else
	ajDebug("=g= plline( %d, <> .. <>, <> .. <>) [num x..x y..y]\n",
		numofpoints );
#endif

    if(graphData)
    {
	if(numofpoints < 2)
	    return;

	xlast = *x;
	ylast = *y;

	for(i=1; i .. <>, <> .. <>) [num x..x y..y]\n",
		numofpoints );
#endif

    plfill(numofpoints, x, y);

    return;
}
ID GraphPen
TY static
MO ajgraph
LB graphics
XX
DE Change the actual colour of a pen.
XX
PN [1]
PA r pen ajint
PD Pen colour number
PX
PN [2]
PA r red ajint
PD Red value (see PLPLOT)
PX
PN [3]
PA r green ajint
PD Green value (see PLPLOT)
PX
PN [4]
PA r blue ajint
PD Blue value (see PLPLOT)
PX
RT void
RD 
RX
//


static void GraphPen(ajint pen, ajint red, ajint green, ajint blue)
{
#if GRAPH_DEBUG
    ajDebug("=g= plscol0(%d, %d, %d, %d) [pen RGB]\n",
	     pen, red, green, blue);
#endif

    plscol0(pen,red,green,blue);

    return;
}
ID GraphClose
TY static
MO ajgraph
LB graphics
XX
DE Close the graph with the plplot plend command.
XX
RT void
RD 
RX
//


static void GraphClose(void)
{
    AjPList files = NULL;
    AjPStr tmpStr = NULL;

    ajDebug("GraphClose\n");

    if(graphData)
	files = graphData->List;
    else
	ajGraphicsGetOutfiles(&files);

    while(ajListstrGetLength(files))
    {
	ajListstrPop(files, &tmpStr);
	ajDebug("GraphInfo file '%S'\n", tmpStr);
	ajFmtPrint("Created %S\n", tmpStr);
	ajStrDel(&tmpStr);
    }

    if(graphData)
    {
	ajDebug("GraphClose deleting graphData '%F' '%S'.'%S'\n",
		graphData->File, graphData->FName, graphData->Ext);
	ajListstrFree(&graphData->List);
	ajFileClose(&graphData->File);
	ajStrDel(&graphData->FName);
	ajStrDel(&graphData->Ext);
	AJFREE(graphData);

#if GRAPH_DEBUG
	ajDebug("=g= plend()\n");
#endif

 	plend();
    }
    else
    {
	ajListstrFree(&files);

#if GRAPH_DEBUG
	ajDebug("=g= plend()\n");
#endif

	plend();
    }

    return;
}
ID ajGraphdataAddXY
TY public
MO ajgraph
LB graphics
XX
DE Adds (x,y) data points defined in two floating point arrays
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph object
PX
PN [2]
PA r xx const float*
PD X coordinates
PX
PN [3]
PA r yy const float*
PD Y coordinates
PX
RT void
RD 
RX
//


void ajGraphdataAddXY(AjPGraphdata graphdata,
			 const float *xx,const float *yy)
{
    ajuint i;

    for(i=0;inumofpoints; i++)
    {
	graphdata->x[i] = xx[i];
	graphdata->y[i] = yy[i];
    }

    return;
}
ID ajGraphdataAddposLine
TY public
MO ajgraph
LB graphics
XX
DE Add a line to be drawn when the graph is plotted.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph data object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r x2 float
PD End x position
PX
PN [5]
PA r y2 float
PD End y position
PX
PN [6]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
RT void
RD 
RX
//


void ajGraphdataAddposLine(AjPGraphdata graphdata, float x, float y,
			   float x2, float y2, ajint colour)
{
    AjPGraphobj Obj;

    if(!graphdata->Dataobj)
    {
	AJNEW((graphdata->Dataobj));
	Obj = graphdata->Dataobj;
    }
    else
    {
	Obj = graphdata->Dataobj;

	while(Obj->next)
	    Obj = Obj->next;

	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++graphdata->numofobjects;

    Obj->type = LINE;
    Obj->text = 0;
    Obj->xx1 = x;
    Obj->xx2 = x2;
    Obj->yy1 = y;
    Obj->yy2 = y2;
    Obj->colour = colour;
    Obj->next = 0;

    return;
}
ID ajGraphdataAddposRect
TY public
MO ajgraph
LB graphics
XX
DE Add a Rectangle to be drawn when the graph is plotted, fill states whether
DE the rectangle should be filled in.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph data object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r x2 float
PD End x position
PX
PN [5]
PA r y2 float
PD End y position
PX
PN [6]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
PN [7]
PA r fill ajint
PD Fill code (see PLPLOT)
PX
RT void
RD 
RX
//


void ajGraphdataAddposRect(AjPGraphdata graphdata,
			   float x, float y,
			   float x2, float y2,
			   ajint colour, ajint fill)
{
    AjPGraphobj Obj;

    if(!graphdata->Dataobj)
    {
	AJNEW((graphdata->Dataobj));
	Obj = graphdata->Dataobj;
    }
    else
    {
	Obj = graphdata->Dataobj;

	while(Obj->next)
	    Obj = Obj->next;

	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++graphdata->numofobjects;


    if(fill)
	Obj->type = RECTANGLEFILL;
    else
	Obj->type = RECTANGLE;

    Obj->text = 0;
    Obj->xx1 = x;
    Obj->xx2 = x2;
    Obj->yy1 = y;
    Obj->yy2 = y2;
    Obj->colour = colour;
    Obj->next = 0;

    return;
}
ID ajGraphdataAddposTextC
TY public
MO ajgraph
LB graphics
XX
DE Add Text to be drawn when the graph is plotted.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph data object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
PN [5]
PA r txt const char*
PD Text to add
PD 1
PX
RT void
RD 
RX
//


void ajGraphdataAddposTextC(AjPGraphdata graphdata, float x, float y,
                            ajint colour, const char *txt)
{
    AjPGraphobj Obj;

    if(!graphdata->Dataobj)
    {
	AJNEW((graphdata->Dataobj));
	Obj = graphdata->Dataobj;
    }
    else			   /* cycle through till NULL found */
    {
	Obj = graphdata->Dataobj;

	while(Obj->next)
	    Obj = Obj->next;

	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++graphdata->numofobjects;


    Obj->type = TEXT;
    Obj->text = 0;
    ajStrAssignEmptyC(&Obj->text,txt);
    Obj->xx1 = x;
    Obj->xx2 = 0.0;
    Obj->yy1 = y;
    Obj->yy2 = 0.0;
    Obj->colour = colour;
    Obj->scale = 0.0;
    Obj->next = 0;

    return;
}
ID ajGraphdataAddposTextS
TY public
MO ajgraph
LB graphics
XX
DE Add Text to be drawn when the graph is plotted.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph data object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
PN [5]
PA r str const AjPStr
PD Text to add
PX
RT void
RD 
RX
//


void ajGraphdataAddposTextS(AjPGraphdata graphdata, float x, float y,
                         ajint colour, const AjPStr str)
{
    AjPGraphobj Obj;

    if(!graphdata->Dataobj)
    {
	AJNEW((graphdata->Dataobj));
	Obj = graphdata->Dataobj;
    }
    else			   /* cycle through till NULL found */
    {
	Obj = graphdata->Dataobj;

	while(Obj->next)
	    Obj = Obj->next;

	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++graphdata->numofobjects;


    Obj->type = TEXT;
    Obj->text = 0;
    ajStrAssignEmptyS(&Obj->text,str);
    Obj->xx1 = x;
    Obj->xx2 = 0.0;
    Obj->yy1 = y;
    Obj->yy2 = 0.0;
    Obj->colour = colour;
    Obj->scale = 0.0;
    Obj->next = 0;

    return;
}
ID ajGraphdataAddposTextScaleC
TY public
MO ajgraph
LB graphics
XX
DE Add Text to be drawn when the graph is plotted.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph data object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
PN [5]
PA r scale float
PD Character scale (0.0 to use the default)
PX
PN [6]
PA r txt const char*
PD Text to add
PX
RT void
RD 
RX
//


void ajGraphdataAddposTextScaleC(AjPGraphdata graphdata,
                                 float x, float y,
                                 ajint colour, float scale, const char *txt)
{
    AjPGraphobj Obj;

    if(!graphdata->Dataobj)
    {
	AJNEW((graphdata->Dataobj));
	Obj = graphdata->Dataobj;
    }
    else			   /* cycle through till NULL found */
    {
	Obj = graphdata->Dataobj;

	while(Obj->next)
	    Obj = Obj->next;

	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++graphdata->numofobjects;


    Obj->type = TEXT;
    Obj->text = 0;
    ajStrAssignEmptyC(&Obj->text,txt);
    Obj->xx1 = x;
    Obj->xx2 = 0.0;
    Obj->yy1 = y;
    Obj->yy2 = 0.0;
    Obj->colour = colour;
    Obj->scale = scale;
    Obj->next = 0;

    return;
}
ID ajGraphdataAddposTextScaleS
TY public
MO ajgraph
LB graphics
XX
DE Add Text to be drawn when the graph is plotted.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph data object
PX
PN [2]
PA r x float
PD Start x position
PX
PN [3]
PA r y float
PD Start y position
PX
PN [4]
PA r colour ajint
PD Colour code (see PLPLOT)
PX
PN [5]
PA r scale float
PD Character scale (0.0 to use the default)
PX
PN [6]
PA r str const AjPStr
PD Text to add
PX
RT void
RD 
RX
//


void ajGraphdataAddposTextScaleS(AjPGraphdata graphdata,
                                 float x, float y,
                                 ajint colour, float scale, const AjPStr str)
{
    AjPGraphobj Obj;

    if(!graphdata->Dataobj)
    {
	AJNEW((graphdata->Dataobj));
	Obj = graphdata->Dataobj;
    }
    else			   /* cycle through till NULL found */
    {
	Obj = graphdata->Dataobj;

	while(Obj->next)
	    Obj = Obj->next;

	AJNEW(Obj->next);
	Obj = Obj->next;
    }

    ++graphdata->numofobjects;


    Obj->type = TEXT;
    Obj->text = 0;
    ajStrAssignEmptyS(&Obj->text,str);
    Obj->xx1 = x;
    Obj->xx2 = 0.0;
    Obj->yy1 = y;
    Obj->yy2 = 0.0;
    Obj->colour = colour;
    Obj->scale = scale;
    Obj->next = 0;

    return;
}
ID ajGraphdataCalcXY
TY public
MO ajgraph
LB graphics
XX
DE Adds (x,y) data points defined by an x-axis start and increment
DE and a floating point array of y-axis values.
XX
PN [1]
PA u graphdata AjPGraphdata
PD Graph object
PX
PN [2]
PA r numofpoints ajuint
PD Number of points in array
PX
PN [3]
PA r start float
PD Start position
PX
PN [4]
PA r incr float
PD Increment
PX
PN [5]
PA r y const float*
PD Y coordinates
PX
RT void
RD 
RX
//


void ajGraphdataCalcXY(AjPGraphdata graphdata, ajuint numofpoints,
                       float start, float incr, const float* y)
{
    ajuint i;
    PLFLT x = 0.0;

    if(!graphdata->xcalc)
	AJFREE(graphdata->x);

    if(!graphdata->ycalc)
	AJFREE(graphdata->y);

    graphdata->xcalc = ajFalse;	 /* i.e. OK to delete at the end as it */
    graphdata->ycalc = ajFalse;	 /* is our own copy */

    AJCNEW(graphdata->x, numofpoints);
    AJCNEW(graphdata->y, numofpoints);

    for(i=0;ix[i] = (start+x);
	graphdata->y[i] = y[i];
	x += incr;
    }

    graphdata->numofpoints = numofpoints;

    return;
}
ID GraphdataInit
TY static
MO ajgraph
LB graphics
XX
DE Initialise the contents of a graph object
XX
PN [1]
PA w graphdata AjPGraphdata
PD Graph structure to store info in.
PX
RT void
RD 
RX
//


static void GraphdataInit(AjPGraphdata graphdata)
{
    ajDebug("GraphdataInit title: %x subtitle: %x xaxis: %x yaxis: %x\n",
	    graphdata->title, graphdata->subtitle,
	    graphdata->xaxis, graphdata->yaxis);

    graphdata->x = NULL;
    graphdata->y = NULL;
    graphdata->xcalc = ajTrue;
    graphdata->ycalc = ajTrue;
    ajStrAssignEmptyC(&graphdata->title,"");
    ajStrAssignEmptyC(&graphdata->subtitle,"");
    ajStrAssignEmptyC(&graphdata->xaxis,"");
    ajStrAssignEmptyC(&graphdata->yaxis,"");
    ajStrAssignEmptyC(&graphdata->gtype,"");
    graphdata->minX = graphdata->maxX = 0;
    graphdata->minY = graphdata->maxY = 0;
    graphdata->lineType = 1;
    graphdata->colour = BLACK;
    graphdata->Dataobj = 0;

    return;
}
ID ajGraphdataTrace
TY public
MO ajgraph
LB graphics
XX
DE Writes debug messages to trace the contents of a graphdata object.
XX
PN [1]
PA r thys const AjPGraphdata
PD Graphdata object
PX
RT void
RD 
RX
//


void ajGraphdataTrace(const AjPGraphdata thys)
{
    ajint i = 0;
    AjPGraphobj obj;

    ajDebug("GraphData trace\n");
    ajDebug("\n(a) True booleans\n");

    if(thys->xcalc)
	ajDebug("xcalc %B\n", thys->xcalc);

    if(thys->ycalc)
	ajDebug("ycalc %B\n", thys->ycalc);

    ajDebug("\n(b) Strings with values\n");

    ajDebug("Title '%S'\n", thys->title);
    ajDebug("Subtitle '%S'\n", thys->subtitle);
    ajDebug("Xaxis '%S'\n", thys->xaxis);
    ajDebug("Yaxis '%S'\n", thys->yaxis);
    ajDebug("gtype '%S'\n", thys->gtype);

    ajDebug("\n(c) Other values\n");
    ajDebug("numofpoints %d\n", thys->numofpoints);
    ajDebug("numofobjects %d\n", thys->numofobjects);
    ajDebug("minX   %7.3f maxX   %7.3f\n", thys->minX, thys->maxX);
    ajDebug("minY   %7.3f maxY   %7.3f\n", thys->minY, thys->maxY);
    ajDebug("tminX   %7.3f tmaxX   %7.3f\n", thys->tminX, thys->tmaxX);
    ajDebug("tminY   %7.3f tmaxY   %7.3f\n", thys->tminY, thys->tmaxY);
    ajDebug("colour %d\n", thys->colour);
    ajDebug("lineType %d\n", thys->lineType);

    ajDebug("obj list: %x\n", thys->Dataobj);

    if(thys->Dataobj)
    {
	obj=thys->Dataobj;

	while(obj->next)
	{
	    i++;
	    obj = obj->next;
	}
    }

    ajDebug("obj list length: %d/%d\n",
	     i, thys->numofobjects);
 
    return;
}
ID ajGraphicsDumpDevices
TY public
MO ajgraph
LB graphics
XX
DE Dump device options for a graph object
XX
RT void
RD 
RX
//


void ajGraphicsDumpDevices(void)
{
    ajint i;
    ajint j;
    AjPStr aliases = NULL;

    ajUserDumpC("Devices allowed (with alternative names) are:-");

    for(i=0;graphType[i].Name;i++)
    {
	if(!graphType[i].GOpen)
            continue;

	if(!graphType[i].Alias)
	{
	    ajStrAssignClear(&aliases);

	    for(j=0;graphType[j].Name;j++)
	    {
		if(graphType[j].Alias &&
		   ajCharMatchC(graphType[i].Device, graphType[j].Device))
		{
		    if(ajStrGetLen(aliases))
			ajStrAppendC(&aliases, ", ");
		    ajStrAppendC(&aliases, graphType[j].Name);
		}
	    }

	    if(ajStrGetLen(aliases))
		ajUser("%s (%S)",graphType[i].Name, aliases);
	    else
		ajUserDumpC(graphType[i].Name);
	}
    }

    ajStrDel(&aliases);

    return;
}
ID ajGraphicsGetOutfiles
TY public
MO ajgraph
LB graphics
XX
DE Information on files created for graph output
XX
PN [1]
PA w files AjPList*
PD List of graph files
PX
RT ajuint
RD Number of files (will match length of list)
RX
//


ajuint ajGraphicsGetOutfiles(AjPList* files)
{
    ajint i= -1;
    ajint j;
    AjPStr tmpStr = NULL;

    PLINT family = 0;
    PLINT filenum = 0;
    PLINT bmax = 0;
    char filename[1024];

    plgfam(&family, &filenum, &bmax);

    if(filenum)
        i = filenum;
    else
        i = -1;

    ajDebug("ajGraphicsGetOutfiles i:%d\n", i);

    *files = ajListstrNew();

    ajDebug("ajGraphicsGetOutfiles new list\n");

    if(!i)
	return 0;

    if(i < 0)				/* single filename - or none */
    {
        plgfnam(filename);
        if(!*filename)
            return 0;
	ajStrAssignC(&tmpStr, filename);
	ajListstrPushAppend(*files, tmpStr);
	tmpStr=NULL;
	return 1;
    }

    for(j=1; j<=i; j++) /* family: tmp has format, i is no. of files */
    {
	ajDebug("ajGraphicsGetOutfiles printing file %d\n", j);
	ajFmtPrintS(&tmpStr, "%S.%d.%S", graphBasename, j, graphExtension);
	ajDebug("ajGraphicsGetOutfiles storing file '%S'\n", tmpStr);
	ajListstrPushAppend(*files, tmpStr);
	tmpStr=NULL;
    }

    return i;
}
ID ajGraphicsListDevices
TY public
MO ajgraph
LB graphics
XX
DE Store device names for a graph object in a list
XX
PN [1]
PA w list AjPList
PD List to write device names to.
PX
RT void
RD 
RX
//


void ajGraphicsListDevices (AjPList list)
{
    ajint i;
    AjPStr devicename;

    for(i=0;graphType[i].Name;i++)
    {
	if(!graphType[i].GOpen)
            continue;

	if(!graphType[i].Alias)
	{
	    devicename = ajStrNewC(graphType[i].Name);
	    ajListstrPushAppend(list, devicename);
	}
    }

    return;
}
ID ajGraphicsPrintType
TY public
MO ajgraph
LB graphics
XX
DE Print graph types
XX
PN [1]
PA u outf AjPFile
PD Output file
PX
PN [2]
PA r full AjBool
PD Full report (usually ajFalse)
PX
RT void
RD 
RX
//

void ajGraphicsPrintType(AjPFile outf, AjBool full)
{
    GraphPType gt;
    ajint i;

    ajFmtPrintF(outf, "\n");
    ajFmtPrintF(outf, "# Graphics devices\n");
    ajFmtPrintF(outf, "# Name     Name\n");
    ajFmtPrintF(outf, "# Alias    Alias name\n");
    ajFmtPrintF(outf, "# Device   Device name\n");
    ajFmtPrintF(outf, "# Extension Filename extension (null if no file "
                "created)\n");
    ajFmtPrintF(outf, "# Description\n");
    ajFmtPrintF(outf, "# Name       Alias Device     Extension Description\n");
    ajFmtPrintF(outf, "GraphType {\n");

    for(i=0; graphType[i].Name; i++)
    {
	gt = &graphType[i];

	if(full || !gt->Alias)
        {
	    ajFmtPrintF(outf, "  %-10s", gt->Name);
	    ajFmtPrintF(outf, " %5B", gt->Alias);
	    ajFmtPrintF(outf, " %-10s", gt->Device);
	    ajFmtPrintF(outf, " %-10s", gt->Ext);
	    ajFmtPrintF(outf, " \"%s\"", gt->Desc);
	    ajFmtPrintF(outf, "\n");
	}
    }

    ajFmtPrintF(outf, "}\n");

    return;
}
ID ajGraphicsxyDumpDevices
TY public
MO ajgraph
LB graphics
XX
DE Dump device options for a graph object
XX
RT void
RD 
RX
//


void ajGraphicsxyDumpDevices(void)
{
    ajint i;
    ajint j;
    AjPStr aliases = NULL;

    ajUserDumpC("Devices allowed (with alternative names) are:-");

    for(i=0;graphType[i].Name;i++)
    {
	if(!graphType[i].XYDisplay)
            continue;

	if(!graphType[i].Alias)
	{
	    ajStrAssignClear(&aliases);

	    for(j=0;graphType[j].Name;j++)
	    {
		if(graphType[j].Alias &&
		   ajCharMatchC(graphType[i].Device, graphType[j].Device))
		{
		    if(ajStrGetLen(aliases))
			ajStrAppendC(&aliases, ", ");
		    ajStrAppendC(&aliases, graphType[j].Name);
		}
	    }

	    if(ajStrGetLen(aliases))
		ajUser("%s (%S)",graphType[i].Name, aliases);
	    else
		ajUserDumpC(graphType[i].Name);
	}
    }

    ajStrDel(&aliases);

    return;
}
ID ajGraphicsxyListDevices
TY public
MO ajgraph
LB graphics
XX
DE Store device names for a graph object in a list
XX
PN [1]
PA w list AjPList
PD List to write device names to.
PX
RT void
RD 
RX
//


void ajGraphicsxyListDevices (AjPList list)
{
    ajint i;
    AjPStr devicename;

    for(i=0;graphType[i].Name;i++)
    {
	if(!graphType[i].XYDisplay)
            continue;

	if(!graphType[i].Alias)
	{
	    devicename = ajStrNewC(graphType[i].Name);
	    ajListstrPushAppend(list, devicename);
	}
    }

    return;
}
ID GraphCheckFlags
TY static
MO ajgraph
LB graphics
XX
DE Prints the flags defined by bits in the input integer value.
XX
PN [1]
PA r flags ajint
PD flag bits
PX
RT void
RD 
RX
//


static void GraphCheckFlags(ajint flags)
{
    ajFmtPrint("flag = %d\n",flags);
    if(flags & AJGRAPH_X_BOTTOM)
	ajFmtPrint("AJGRAPH_X_BOTTOM \n");

    if(flags & AJGRAPH_Y_LEFT)
	ajFmtPrint("AJGRAPH_Y_LEFT \n");

    if(flags & AJGRAPH_X_TOP)
	ajFmtPrint("AJGRAPH_X_TOP \n");

    if(flags & AJGRAPH_Y_RIGHT)
	ajFmtPrint(" AJGRAPH_Y_RIGHT\n");

    if(flags & AJGRAPH_X_TICK)
	ajFmtPrint("AJGRAPH_X_TICK \n");

    if(flags & AJGRAPH_Y_TICK)
	ajFmtPrint("AJGRAPH_Y_TICK \n");

    if(flags & AJGRAPH_X_LABEL)
	ajFmtPrint("AJGRAPH_X_LABEL \n");

    if(flags & AJGRAPH_Y_LABEL)
	ajFmtPrint("AJGRAPH_Y_LABEL \n");

    if(flags & AJGRAPH_TITLE)
	ajFmtPrint("AJGRAPH_TITLE \n");

    if(flags & AJGRAPH_JOINPOINTS)
	ajFmtPrint("AJGRAPH_JOINPOINTS \n");

    if(flags & AJGRAPH_OVERLAP)
	ajFmtPrint("AJGRAPH_OVERLAP \n");

    if(flags & AJGRAPH_Y_NUMLABEL_LEFT)
	ajFmtPrint("AJGRAPH_Y_NUMLABEL_LEFT");

    if(flags & AJGRAPH_Y_INVERT_TICK)
	ajFmtPrint("AJGRAPH_Y_INVERT_TICK");

    if(flags & AJGRAPH_Y_GRID)
	ajFmtPrint("AJGRAPH_Y_GRID");

    if(flags & AJGRAPH_X_NUMLABEL_ABOVE)
	ajFmtPrint("AJGRAPH_X_NUMLABEL_ABOVE");

    if(flags & AJGRAPH_X_INVERT_TICK)
	ajFmtPrint("AJGRAPH_X_INVERT_TICK");

    if(flags & AJGRAPH_X_GRID)
	ajFmtPrint("AJGRAPH_X_GRID");
    
    return;
}
ID GraphCheckPoints
TY static
MO ajgraph
LB graphics
XX
DE Prints a list of data points from two floating point arrays.
XX
PN [1]
PA r n ajint
PD Number of points to print
PX
PN [2]
PA r x const PLFLT*
PD X coordinates
PX
PN [3]
PA r y const PLFLT*
PD Y coordinates
PX
RT void
RD 
RX
//


static void GraphCheckPoints(ajint n, const PLFLT *x, const PLFLT *y)
{
    ajint i;

    for(i=0;ioutputfile);

    GraphSetName(thys, thys->outputfile,ext);

    return;
}
ID GraphOpenData
TY static
MO ajgraph
LB graphics
XX
DE A general routine for setting BaseName and extension in plplot
DE for -graph data output.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r ext const char*
PD file extension
PX
RT void
RD 
RX
//


static void GraphOpenData(AjPGraph thys, const char *ext)
{
    ajDebug("GraphOpenData '%S' '%s\n", thys->outputfile, ext);

    if(!graphData)
    {
        AJNEW0(graphData);
        graphData->List = ajListstrNew();
    }

    GraphSetName(thys, thys->outputfile,ext);
    thys->isdata = ajTrue;

    ajStrAssignS(&graphData->FName, thys->outputfile);
    ajStrAssignC(&graphData->Ext, ext);

    GraphDatafileNext();

    return;
}
ID GraphOpenSimple
TY static
MO ajgraph
LB graphics
XX
DE A general routine for drawing graphs. Does nothing.
DE
DE This is called when initialised. Graph files are opened when
DE each graph is written.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r ext const char*
PD file extension
PX
RT void
RD 
RX
//


static void GraphOpenSimple(AjPGraph thys, const char *ext )
{
    (void) thys;			/* make it used */
    (void) ext;				/* make it used */

    return;
}
ID GraphOpenXwin
TY static
MO ajgraph
LB graphics
XX
DE A general routine for drawing graphs to an xwin. Only creates the window.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r ext const char*
PD file extension
PX
RT void
RD 
RX
//


static void GraphOpenXwin(AjPGraph thys, const char *ext )
{
    char* plargv[2] = {NULL,NULL};
    ajint plargc = 2;
    char argname[] = "-plwindow";

    (void) thys;			/* make it used */
    (void) ext;				/* make it used */

    plargv[0] = argname;
    plargv[1] = MAJSTRGETPTR(ajUtilGetProgram());

#if GRAPH_DEBUG
    ajDebug("=g= plparseopts('%s', '%s') ajUtilGetProgram\n",
            plargv[0], plargv[1]);
#endif

    plparseopts(&plargc, plargv,
                PL_PARSE_NOPROGRAM | PL_PARSE_QUIET |
                PL_PARSE_NODELETE | PL_PARSE_SKIP);

/*
    GraphLabelTitle(((thys->flags & AJGRAPH_TITLE) ?
		     ajStrGetPtr(thys->title) : " "),
		    ((thys->lags & AJGRAPH_SUBTITLE) ?
		     ajStrGetPtr(thys->subtitle) : " "));
	
*/

    return;
}
ID GraphxyDisplayToFile
TY static
MO ajgraph
LB graphics
XX
DE A general routine for drawing graphs to a file.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r closeit AjBool
PD Close file if true
PX
PN [3]
PA r ext const char*
PD file extension
PX
RT void
RD 
RX
//


static void GraphxyDisplayToFile(AjPGraph thys, AjBool closeit,
				 const char *ext)
{
    	ajDebug("GraphxyDisplayToFile '%S'\n", thys->outputfile);

	GraphSetName(thys, thys->outputfile,ext);
	ajGraphxySetRanges(thys);
	GraphxyGeneral(thys, closeit);

    return;
}
ID GraphxyDisplayToDas
TY static
MO ajgraph
LB graphics
XX
DE A general routine for drawing graphs to DAS output file as points.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r closeit AjBool
PD Close file if true
PX
PN [3]
PA r ext const char*
PD file extension
PX
RT void
RD 
RX
//


static void GraphxyDisplayToDas(AjPGraph thys, AjBool closeit,
                                const char *ext)
{
    AjPFile outf    = NULL;
    AjPGraphdata graphdata  = NULL;
    AjPStr temp;
    AjPTime ajtime;
    ajuint i,j;
    float minxa = 64000.;
    float minya = 64000.;
    float maxxa = -64000.;
    float maxya = -64000.;
    ajint nfeat = 0;
    ajint istart;
    ajint iend;

    /*
    ** Things to do:
    **
    ** Test this output
    ** Check how it handles multiple sequences
    ** Try to catch the sequence information
    **
    ** Do we want end=start or end=start+1
    **
    */

     ajDebug("GraphxyDisplayToDas '%S' '%s' %d graphs\n",
	     thys->outputfile, ext, thys->numofgraphs);
        
    if(!graphData)
    {
	AJNEW0(graphData);
	graphData->List = ajListstrNew();
    }

    /* Calculate maxima and minima */
    for(i=0;inumofgraphs;i++)
    {
	graphdata = (thys->graphs)[i];
	minxa = (minxaminX) ? minxa : graphdata->minX;
	minya = (minyaminY) ? minya : graphdata->minY;
	maxxa = (maxxa>graphdata->maxX) ? maxxa : graphdata->maxX;
	maxya = (maxya>graphdata->maxY) ? maxya : graphdata->maxY;
    }
    
    temp = ajFmtStr("%S%s",thys->outputfile,ext);
    outf = ajFileNewOutNameS(temp);

    ajFmtPrintF(outf,"\n");
    ajFmtPrintF(outf,"  \n");
    ajFmtPrintF(outf,"\n");
    ajFmtPrintF(outf,"\n");
    ajFmtPrintF(outf,"  \n");

    ajFmtPrintF(outf,"\n", thys->title);
    ajFmtPrintF(outf,"\n",thys->numofgraphs);
    ajFmtPrintF(outf,"\n",i+1);
    ajFmtPrintF(outf,"\n",graphdata->numofpoints);
	
    for(i=0;inumofgraphs;i++)
    {
	graphdata = (thys->graphs)[i];
	
	ajListstrPushAppend(graphData->List, temp);
	if(!outf)
	{
	    ajErr("Could not open graph file %S\n",temp);
	    return;
	}
	else
	    ajDebug("Writing graph %d data to %S\n",i+1,temp);

	if( ajStrGetLen(thys->title) <=1)
	{
	    ajFmtPrintAppS(&thys->title,"%S",
			   ajUtilGetProgram());
	}
	if( ajStrGetLen(thys->subtitle) <=1)
	{
	    ajtime = ajTimeNewTodayFmt("report");
	    ajFmtPrintAppS(&thys->subtitle,"%D",
			   ajtime);
	    ajTimeDel(&ajtime);
	}

        istart = (ajint) graphdata->minX;
        if(istart < 1)
            istart = 1;
        iend = (ajint) graphdata->maxX;
        if(iend < istart)
            iend = istart;
	ajFmtPrintF(outf,"    \n",
                    "0.0");

	
	/* Dump out the data points */
	for(j=0;jnumofpoints;j++)
        {
            nfeat++;
	    ajFmtPrintF(outf,"      \n",
                        "featid", nfeat);
            ajFmtPrintF(outf,"        %d\n",
                        (ajint) graphdata->x[j]);
            ajFmtPrintF(outf,"        %d\n",
                        (ajint) graphdata->x[j]);
            ajFmtPrintF(outf,"        %f\n",
                        graphdata->y[j]);
	    ajFmtPrintF(outf,"      \n");
	}
        
	
	ajFmtPrintF(outf,"    \n");
    }
    
    ajFmtPrintF(outf,
                "  \n");
    ajFmtPrintF(outf,
                "\n");

    ajFileClose(&outf);
    if(closeit)
	GraphClose();

    return;
}
ID GraphxyDisplayToWiggle
TY static
MO ajgraph
LB graphics
XX
DE A general routine for drawing graphs to wiggle output file as points.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r closeit AjBool
PD Close file if true
PX
PN [3]
PA r ext const char*
PD file extension
PX
RT void
RD 
RX
//


static void GraphxyDisplayToWiggle(AjPGraph thys, AjBool closeit,
                                   const char *ext)
{
    AjPFile outf    = NULL;
    AjPGraphdata graphdata  = NULL;
    AjPStr temp;
    AjPTime ajtime;
    ajuint i,j;
    float minxa = 64000.;
    float minya = 64000.;
    float maxxa = -64000.;
    float maxya = -64000.;
    ajint istart;
    ajint iend;

     ajDebug("GraphxyDisplayToWiggle '%S' '%s' %d graphs\n",
	     thys->outputfile, ext, thys->numofgraphs);
        
    if(!graphData)
    {
	AJNEW0(graphData);
	graphData->List = ajListstrNew();
    }

    /* Calculate maxima and minima */
    for(i=0;inumofgraphs;i++)
    {
	graphdata = (thys->graphs)[i];
	minxa = (minxaminX) ? minxa : graphdata->minX;
	minya = (minyaminY) ? minya : graphdata->minY;
	maxxa = (maxxa>graphdata->maxX) ? maxxa : graphdata->maxX;
	maxya = (maxya>graphdata->maxY) ? maxya : graphdata->maxY;
    }
    
    temp = ajFmtStr("%S%s",thys->outputfile,ext);
    outf = ajFileNewOutNameS(temp);

    for(i=0;inumofgraphs;i++)
    {
	graphdata = (thys->graphs)[i];
	
	/* open a file for dumping the data points */
	ajListstrPushAppend(graphData->List, temp);
	if(!outf)
	{
	    ajErr("Could not open graph file %S\n",temp);
	    return;
	}
	else
	    ajDebug("Writing graph %d data to %S\n",i+1,temp);

	if( ajStrGetLen(thys->title) <=1)
	{
	    ajFmtPrintAppS(&thys->title,"%S",
			   ajUtilGetProgram());
	}
	if( ajStrGetLen(thys->subtitle) <=1)
	{
	    ajtime = ajTimeNewTodayFmt("report");
	    ajFmtPrintAppS(&thys->subtitle,"%D",
			   ajtime);
	    ajTimeDel(&ajtime);
	}

        istart = (ajint) graphdata->minX;
        if(istart < 1)
            istart = 1;
        iend = (ajint) graphdata->maxX;
        if(iend < istart)
            iend = istart;
	
	/* Dump out the data points graphdata->x[j] graphdata->y[j] */
	for(j=0;jnumofpoints;j++)
        {
 	}
        
    }
    
    ajFileClose(&outf);

    if(closeit)
	GraphClose();

    return;
}
ID GraphxyDisplayToBedgraph
TY static
MO ajgraph
LB graphics
XX
DE A general routine for drawing graphs to bedgraph output file as points.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r closeit AjBool
PD Close file if true
PX
PN [3]
PA r ext const char*
PD file extension
PX
RT void
RD 
RX
//


static void GraphxyDisplayToBedgraph(AjPGraph thys, AjBool closeit,
                                     const char *ext)
{
    AjPFile outf    = NULL;
    AjPGraphdata graphdata  = NULL;
    AjPStr temp;
    AjPTime ajtime;
    ajuint i,j;
    float minxa = 64000.;
    float minya = 64000.;
    float maxxa = -64000.;
    float maxya = -64000.;
    ajint istart;
    ajint iend;
    ajint ipos;

     ajDebug("GraphxyDisplayToBedgraph '%S' '%s' %d graphs\n",
	     thys->outputfile, ext, thys->numofgraphs);
        
    if(!graphData)
    {
	AJNEW0(graphData);
	graphData->List = ajListstrNew();
    }

    /* Calculate maxima and minima */
    for(i=0;inumofgraphs;i++)
    {
	graphdata = (thys->graphs)[i];
	minxa = (minxaminX) ? minxa : graphdata->minX;
	minya = (minyaminY) ? minya : graphdata->minY;
	maxxa = (maxxa>graphdata->maxX) ? maxxa : graphdata->maxX;
	maxya = (maxya>graphdata->maxY) ? maxya : graphdata->maxY;
    }
    
    temp = ajFmtStr("%S%s",thys->outputfile,ext);
    outf = ajFileNewOutNameS(temp);

    ajFmtPrintF(outf, "track type=bedGraph graphType=points\n");
    /* can also document:
  name              trackLabel           # default is "User Track"
  description       centerlabel          # default is "User Supplied Track"
  visibility        full|dense|hide      # default is hide (also values 2|1|0)
  color             RRR,GGG,BBB          # default is 255,255,255
  altColor          RRR,GGG,BBB          # default is 128,128,128
  priority          N                    # default is 100
  autoScale         on|off               # default is on
  alwaysZero        on|off               # default is off
  gridDefault       on|off               # default is off
  maxHeightPixels   max:default:min      # default is 128:128:11
  graphType         bar|points           # default is bar
  viewLimits        lower:upper          # default is range found in data
  yLineMark         real-value           # default is 0.0
  yLineOnOff        on|off               # default is off
  windowingFunction maximum|mean|minimum # default is maximum
  smoothingWindow   off|[2-16]           # default is off
    */

    for(i=0;inumofgraphs;i++)
    {
	graphdata = (thys->graphs)[i];
	
	/* open a file for dumping the data points */
	ajListstrPushAppend(graphData->List, temp);
	if(!outf)
	{
	    ajErr("Could not open graph file %S\n",temp);
	    return;
	}
	else
	    ajDebug("Writing graph %d data to %S\n",i+1,temp);

	if( ajStrGetLen(thys->title) <=1)
	{
	    ajFmtPrintAppS(&thys->title,"%S",
			   ajUtilGetProgram());
	}
	if( ajStrGetLen(thys->subtitle) <=1)
	{
	    ajtime = ajTimeNewTodayFmt("report");
	    ajFmtPrintAppS(&thys->subtitle,"%D",
			   ajtime);
	    ajTimeDel(&ajtime);
	}

        istart = (ajint) graphdata->minX;
        if(istart < 1)
            istart = 1;
        iend = (ajint) graphdata->maxX;
        if(iend < istart)
            iend = istart;
	
	/* Dump out the data points graphdata->x[j] graphdata->y[j] */
	for(j=0;jnumofpoints;j++)
        {
            ipos = ((int) graphdata->x[j]) - 1;
            if(ajStrGetLen(graphdata->dataname))
                ajFmtPrintF(outf, "%S %d %d %f\n",
                            graphdata->dataname, ipos, ipos,
                            graphdata->y[j]);
            else if(ajStrGetLen(thys->dataname))
                ajFmtPrintF(outf, "%S %d %d %f\n",
                            thys->dataname, ipos, ipos,
                            graphdata->y[j]);
            else
                ajFmtPrintF(outf, "%s %d %d %f\n",
                            "unknown", ipos, ipos,
                            graphdata->y[j]);
 	}

    }
    
    ajFileClose(&outf);

    if(closeit)
	GraphClose();

    return;
}
ID GraphxyDisplayToData
TY static
MO ajgraph
LB graphics
XX
DE A general routine for drawing graphs to file as points.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r closeit AjBool
PD Close file if true
PX
PN [3]
PA r ext const char*
PD file extension
PX
RT void
RD 
RX
//


static void GraphxyDisplayToData(AjPGraph thys, AjBool closeit,
				 const char *ext)
{
    AjPFile outf    = NULL;
    AjPGraphdata graphdata  = NULL;
    AjPGraphobj ptr = NULL;
    AjPStr temp;
    AjPTime ajtime;
    ajuint i,j;
    float minxa = 64000.;
    float minya = 64000.;
    float maxxa = -64000.;
    float maxya = -64000.;
    ajint type  = 0;
    
    ajDebug("GraphxyDisplayToData '%S' '%s' %d graphs\n",
	     thys->outputfile, ext, thys->numofgraphs);
        
    if(!graphData)
    {
	AJNEW0(graphData);
	graphData->List = ajListstrNew();
    }

    /* Calculate maxima and minima */
    for(i=0;inumofgraphs;i++)
    {
	graphdata = (thys->graphs)[i];
	minxa = (minxaminX) ? minxa : graphdata->minX;
	minya = (minyaminY) ? minya : graphdata->minY;
	maxxa = (maxxa>graphdata->maxX) ? maxxa : graphdata->maxX;
	maxya = (maxya>graphdata->maxY) ? maxya : graphdata->maxY;
    }
    
    for(i=0;inumofgraphs;i++)
    {
	graphdata = (thys->graphs)[i];
	
	/* open a file for dumping the data points */
	temp = ajFmtStr("%S%d%s",thys->outputfile,i+1,ext);
	outf = ajFileNewOutNameS(temp);
	ajListstrPushAppend(graphData->List, temp);

	if(!outf)
	{
	    ajErr("Could not open graph file %S\n",temp);

	    return;
	}
	else
	    ajDebug("Writing graph %d data to %S\n",i+1,temp);

	if( ajStrGetLen(thys->title) <=1)
	{
	    ajFmtPrintAppS(&thys->title,"%S",
			   ajUtilGetProgram());
	}

	if( ajStrGetLen(thys->subtitle) <=1)
	{
	    ajtime = ajTimeNewTodayFmt("report");
	    ajFmtPrintAppS(&thys->subtitle,"%D",
			   ajtime);
	    ajTimeDel(&ajtime);
	}

	ajFmtPrintF(outf,"##%S\n",graphdata->gtype);
	ajFmtPrintF(outf,"##Title %S\n",thys->title);
	ajFmtPrintF(outf,"##Graphs %d\n",thys->numofgraphs);
	ajFmtPrintF(outf,"##Number %d\n",i+1);
	ajFmtPrintF(outf,"##Points %d\n",graphdata->numofpoints);
	
	
	ajFmtPrintF(outf,"##XminA %f XmaxA %f YminA %f YmaxA %f\n",
			   minxa,maxxa,minya,maxya);
	ajFmtPrintF(outf,"##Xmin %f Xmax %f Ymin %f Ymax %f\n",
		    graphdata->tminX,graphdata->tmaxX,
		    graphdata->tminY,graphdata->tmaxY);
	ajFmtPrintF(outf,"##ScaleXmin %f ScaleXmax %f ScaleYmin %f "
			   "ScaleYmax %f\n",
		    graphdata->minX,graphdata->maxX,
		    graphdata->minY,graphdata->maxY);
	
	ajFmtPrintF(outf,"##Maintitle %S\n",graphdata->title);
	
	if(thys->numofgraphs == 1)
	{
	    ajFmtPrintF(outf,"##Xtitle %S\n",thys->xaxis);
	    ajFmtPrintF(outf,"##Ytitle %S\n",thys->yaxis);
	}
	else
	{
	    if(graphdata->subtitle)
	        ajFmtPrintF(outf,"##Subtitle %S\n",graphdata->subtitle);
	    ajFmtPrintF(outf,"##Xtitle %S\n",graphdata->xaxis);
	    ajFmtPrintF(outf,"##Ytitle %S\n",graphdata->yaxis);
	}
	
	/* Dump out the data points */
	for(j=0;jnumofpoints;j++)
	    ajFmtPrintF(outf,"%f\t%f\n",graphdata->x[j],graphdata->y[j]);
	
	
	/* Now for the Data graph objects */
	ajFmtPrintF(outf,"##DataObjects\n##Number %d\n",
		    graphdata->numofobjects);
	
	if(graphdata->numofobjects)
	{
	    ptr = graphdata->Dataobj;

	    for(j=0;jnumofobjects;++j)
	    {
		type = ptr->type;

		if(type==LINE || type==RECTANGLE || type==RECTANGLEFILL)
		{
		    if(type==LINE)
			ajFmtPrintF(outf,"Line ");
		    else if(type==RECTANGLE)
			ajFmtPrintF(outf,"Rectangle ");
		    else
			ajFmtPrintF(outf,"Filled Rectangle ");
		    ajFmtPrintF(outf,"x1 %f y1 %f x2 %f y2 %f colour %d\n",
				ptr->xx1,ptr->yy1,
				ptr->xx2,ptr->yy2,ptr->colour);
		}
		else if(type==TEXT)
		{
		    ajFmtPrintF(outf,"Textline ");
		    ajFmtPrintF(outf,"x1 %f y1 %f x2 %f y2 %f colour %d "
				"size 1.0 %S\n",
				ptr->xx1,ptr->yy1,ptr->xx2,
				ptr->yy2,ptr->colour,
				ptr->text);
		}

		ptr = ptr->next;
	    }
	}
	
	
	/* Now for the Graph graph objects */
	ajFmtPrintF(outf,"##GraphObjects\n##Number %d\n",
		    thys->numofobjects);
	
	if(thys->numofobjects)
	{
	    ptr = thys->Mainobj;

	    for(j=0;jnumofobjects;++j)
	    {
		type = ptr->type;

		if(type==LINE || type==RECTANGLE || type==RECTANGLEFILL)
		{
		    if(type==LINE)
			ajFmtPrintF(outf,"Line ");
		    else if(type==RECTANGLE)
			ajFmtPrintF(outf,"Rectangle ");
		    else
			ajFmtPrintF(outf,"Filled Rectangle ");

		    ajFmtPrintF(outf,"x1 %f y1 %f x2 %f y2 %f colour %d\n",
				ptr->xx1,ptr->yy1,
				ptr->xx2,ptr->yy2,ptr->colour);
		}
		else if(type==TEXT)
		{
		    ajFmtPrintF(outf,"Textline ");
		    ajFmtPrintF(outf,"x1 %f y1 %f x2 %f y2 %f colour %d "
				"size 1.0 %S\n",
				ptr->xx1,ptr->yy1,
				ptr->xx2,ptr->yy2,ptr->colour,
				ptr->text);
		}

		ptr = ptr->next;
	    }
	}
	
	
	ajFileClose(&outf);
    }
    
    if(closeit)
	GraphClose();

    return;
}
ID GraphxyDisplayXwin
TY static
MO ajgraph
LB graphics
XX
DE A general routine for drawing graphs to an xwin.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r closeit AjBool
PD Close file if true
PX
PN [3]
PA r ext const char*
PD file extension
PX
RT void
RD 
RX
//


static void GraphxyDisplayXwin(AjPGraph thys, AjBool closeit, const char *ext )
{
    (void) ext;				/* make it used */

    ajGraphxySetRanges(thys);
    GraphxyGeneral(thys, closeit);

    return;
}
ID GraphNewPlplot
TY static
MO ajgraph
LB graphics
XX
DE Populate a plplot structure to hold a general graph.
XX
PN [1]
PA w graph AjPGraph
PD multiple graph structure.
PX
RT void
RD 
RX
//


static void GraphNewPlplot(AjPGraph graph)
{
    AJCNEW0(graph->graphs,1);

    ajDebug("GraphNewPlplot\n");

    graph->numofgraphs    = 0;
    graph->numofgraphsmax = 1;
    graph->flags          = GRAPH_XY;
    graph->minmaxcalc     = 0;
    ajFmtPrintS(&graph->outputfile,"%S", ajUtilGetProgram());

    return;
}
ID GraphxyNewPlplot
TY static
MO ajgraph
LB graphics
XX
DE Create a structure to hold a number of graphs.
XX
PN [1]
PA w thys AjPGraph
PD Empty graph object
PX
RT void
RD 
RX
//


static void GraphxyNewPlplot(AjPGraph thys)
{
    AJCNEW0(thys->graphs,thys->numsets);

    ajDebug("GraphxyNewPlplot numsets: %d\n", thys->numsets);

    thys->numofgraphs       = 0;
    thys->numofgraphsmax    = thys->numsets;
    thys->minX = thys->minY = 64000;
    thys->maxX = thys->maxY = -64000;
    thys->flags             = GRAPH_XY;
    thys->minmaxcalc          = 0;
    thys->xstart = thys->xend = 0;
    thys->ystart = thys->yend = 0;
    thys->Mainobj             = NULL;
    ajFmtPrintS(&thys->outputfile,"%S", ajUtilGetProgram());

    return;
}
ID GraphxyGeneral
TY static
MO ajgraph
LB graphics
XX
DE A general routine for drawing graphs.
XX
PN [1]
PA u thys AjPGraph
PD Multiple graph pointer.
PX
PN [2]
PA r closeit AjBool
PD Close at end if true.
PX
RT void
RD 
RX
//


static void GraphxyGeneral(AjPGraph thys, AjBool closeit)
{
    AjPGraphdata graphdata;
    ajuint i;
    ajint old;
    ajint old2;
    AjPTime ajtime;

    ajGraphicsSetDevice(thys);
    
    ajDebug("GraphxyGeneral flags %x\n", thys->flags);
    ajDebug("... title '%S'\n", thys->title);
    
    if(thys->flags & AJGRAPH_OVERLAP)
    {
	ajDebug("... AJGRAPH_OVERLAP\n");
	GraphColourBack();
	GraphInit(thys);
	GraphColourFore();
	/*    GraphSubPage(0);         Done in ajGraphicsplenv*/
	
	if(thys->numofgraphs)
	{
	    graphdata = (thys->graphs)[0];
	    thys->xstart = graphdata->minX;
	    thys->xend   = graphdata->maxX;
	    thys->ystart = graphdata->minY;
	    thys->yend   = graphdata->maxY;
	}

	ajGraphicsPlenv(thys->xstart, thys->xend,
                        thys->ystart, thys->yend,
                        thys->flags);
	
	if((thys->flags & AJGRAPH_TITLE) &&
	   ajStrGetLen(thys->title) <=1)
	{
	    ajtime = ajTimeNewToday();
	    ajStrAppendC(&thys->title,
			     ajFmtString("%S (%D)",
					 ajUtilGetProgram(),
					 ajtime));
	    ajTimeDel(&ajtime);
	}

	ajGraphicsSetLabelsS(((thys->flags & AJGRAPH_X_LABEL) ?
                              thys->xaxis : NULL),
                             ((thys->flags & AJGRAPH_Y_LABEL) ?
                              thys->yaxis : NULL),
                             ((thys->flags & AJGRAPH_TITLE) ?
                              thys->title : NULL),
                             (thys->flags & AJGRAPH_SUBTITLE) ?
                             thys->subtitle : NULL);
	
	for(i=0;inumofgraphs;i++)
	{
	    graphdata = (thys->graphs)[i];

	    /* Draw the line through the data */
	    old = ajGraphicsSetFgcolour(graphdata->colour);

	    if(thys->flags & AJGRAPH_CIRCLEPOINTS)
		ajGraphicsDrawsetSymbols(graphdata->numofpoints,
                                         (graphdata->x), (graphdata->y), 9);

	    old2 = ajGraphicsSetLinestyle(graphdata->lineType);

	    if(thys->flags & AJGRAPH_JOINPOINTS)
	    {
		if(thys->flags & AJGRAPH_GAPS)
		    GraphArrayGaps(graphdata->numofpoints,
				   (graphdata->x),
				   (graphdata->y));
		else
		    GraphArray(graphdata->numofpoints,
			       (graphdata->x), (graphdata->y));
	    }

	    ajGraphicsSetLinestyle(old2);
	    ajGraphicsSetFgcolour(old);
	}

	GraphDraw(thys);	
    }
    else
    {
	ajDebug("... else not AJGRAPH_OVERLAP\n");
	GraphColourBack();
	GraphInit(thys);
	GraphColourFore();
        GraphSubPage(0);
	GraphSetNumSubPage(thys->numofgraphs);

	/* first do the main plot title */
	if((thys->flags & AJGRAPH_TITLE) &&
	   ajStrGetLen(thys->title) <=1)
	{
	    ajtime = ajTimeNewToday();
	    ajStrAppendC(&thys->title,
			     ajFmtString("%S (%D)",
					 ajUtilGetProgram(),
					 ajtime));
	    ajTimeDel(&ajtime);
	}

	for(i=0;inumofgraphs;i++)
	{
	    graphdata = (thys->graphs)[i];
	    ajGraphicsPlenv(graphdata->minX, graphdata->maxX,
                            graphdata->minY, graphdata->maxY,
                            thys->flags);

	    if(!ajStrGetLen(graphdata->title) &&
	       (thys->flags & AJGRAPH_TITLE))
		ajStrAssignS(&graphdata->title, thys->title);

	    if(!ajStrGetLen(graphdata->subtitle) &&
	       (thys->flags & AJGRAPH_SUBTITLE))
		ajStrAssignS(&graphdata->subtitle, thys->subtitle);

	    ajGraphicsSetLabelsS(((thys->flags & AJGRAPH_X_LABEL) ?
                                  graphdata->xaxis : NULL),
                                 ((thys->flags & AJGRAPH_Y_LABEL) ?
                                  graphdata->yaxis : NULL),
                                 ((thys->flags & AJGRAPH_TITLE) ?
                                  graphdata->title : NULL),
                                 (thys->flags & AJGRAPH_SUBTITLE) ?
                                 graphdata->subtitle : NULL);
	    old = ajGraphicsSetFgcolour(graphdata->colour);

	    if(thys->flags & AJGRAPH_CIRCLEPOINTS)
		ajGraphicsDrawsetSymbols(graphdata->numofpoints,
                                         (graphdata->x), (graphdata->y), 9);
	    
	    /* Draw the line through the data */
	    /*    old = ajGraphicsSetFgcolour(i+2);*/
	    old2 = ajGraphicsSetLinestyle(graphdata->lineType);

	    if(thys->flags & AJGRAPH_JOINPOINTS)
	    {
		if(thys->flags & AJGRAPH_GAPS)
		    GraphArrayGaps(graphdata->numofpoints,
				   (graphdata->x), (graphdata->y));
		else
		    GraphArray(graphdata->numofpoints,
			       (graphdata->x), (graphdata->y));
	    }

	    ajGraphicsSetLinestyle(old2);
	    ajGraphicsSetFgcolour(old);
	    GraphDataDraw(graphdata);
	}	
    }

    if(closeit)
	GraphClose();

    return;
}
ID GraphDataPrint
TY static
MO ajgraph
LB graphics
XX
DE Print all the drawable objects in readable form.
XX
PN [1]
PA r graphdata const AjPGraphdata
PD Graph data object
PX
RT void
RD 
RX
//


static void GraphDataPrint(const AjPGraphdata graphdata)
{
    AjPGraphobj Obj;

    if(!graphdata->Dataobj)
    {
	ajUserDumpC("No Objects");

	return;
    }
    else
    {
	Obj = graphdata->Dataobj;

	while(Obj)
	{
	    if(Obj->type == RECTANGLE )
		ajUser("type = RECTANGLE, %f %f %f %f col= %d",
		       Obj->xx1,Obj->yy1,
		       Obj->xx2,Obj->yy2,Obj->colour);
	    else if(Obj->type == RECTANGLEFILL )
		ajUser("type = RECTANGLEFILL, %f %f %f %f col= %d",
		       Obj->xx1, Obj->yy1,
		       Obj->xx2,Obj->yy2,Obj->colour);
	    else if(Obj->type == TEXT)
		ajUser("type = TEXT, %f %f col= %d %S",Obj->xx1,
		       Obj->yy1,Obj->colour,
		       Obj->text);
	    else if(Obj->type == LINE )
		ajUser("type = LINE, %f %f %f %f col= %d",Obj->xx1,
		       Obj->yy1,
		       Obj->xx2,Obj->yy2,Obj->colour);

	    Obj = Obj->next;
	}
    }

    return;
}
ID GraphDataDraw
TY static
MO ajgraph
LB graphics
XX
DE Display the drawable objects connected to this graph.
XX
PN [1]
PA r graphdata const AjPGraphdata
PD Graph data object
PX
RT void
RD 
RX
//


static void GraphDataDraw(const AjPGraphdata graphdata)
{
    AjPGraphobj Obj;
    ajint temp;
    ajuint nobj = 0;

    /* graphdata : calls ajGraphLine */

    if(!graphdata->Dataobj)
	return;

    ajDebug("ajGraphDataDraw\n");

    Obj = graphdata->Dataobj;
    while(Obj)
    {
        if(Obj->type == RECTANGLE)
        {
            temp = ajGraphicsSetFgcolour(Obj->colour);
            ajGraphicsDrawposRect(Obj->xx1, Obj->yy1,
                                  Obj->xx2,Obj->yy2);
            ajGraphicsSetFgcolour(temp);
        }
        else if(Obj->type == RECTANGLEFILL)
        {
            temp = ajGraphicsSetFgcolour(Obj->colour);
            ajGraphicsDrawposRectFill(Obj->xx1, Obj->yy1,
                                      Obj->xx2,Obj->yy2);
            ajGraphicsSetFgcolour(temp);
        }
        else if(Obj->type == TEXT )
        {
            temp = ajGraphicsSetFgcolour(Obj->colour);
            ajGraphicsDrawposTextAtstart(Obj->xx1, Obj->yy1,
                                         ajStrGetPtr(Obj->text));
            ajGraphicsSetFgcolour(temp);
        }
        else if(Obj->type == LINE)
        {
            temp = ajGraphicsSetFgcolour(Obj->colour);
            ajGraphicsDrawposLine(Obj->xx1, Obj->yy1,
                                  Obj->xx2,Obj->yy2);
            ajGraphicsSetFgcolour(temp);
        }
        else
            ajUserDumpC("UNDEFINED OBJECT TYPE USED");

        Obj = Obj->next;
        ++nobj;
    }

    ajDebug("drew %u objects in ajGraphDataDraw\n", nobj);

    return;
}
ID GraphDistPts
TY static
MO ajgraph
LB graphics
XX
DE Compute the distance between 2 points in user coordinates.
XX
PN [1]
PA r xx1 float
PD x coord of point 1
PX
PN [2]
PA r yy1 float
PD y coord of point 1
PX
PN [3]
PA r xx2 float
PD x coord of point 2
PX
PN [4]
PA r yy2 float
PD y coord of point 2
PX
RT float
RD The distance between the 2 points in user coordinates
RX
//


static float GraphDistPts(float xx1, float yy1, float xx2, float yy2)
{
    PLFLT diag;

    diag = (PLFLT) sqrt( (xx2-xx1) * (xx2-xx1) + (yy2-yy1) * (yy2-yy1) );

    return diag;
}
ID GraphDrawarcText
TY static
MO ajgraph
LB graphics
XX
DE Draw text along a curve (i.e., an arc of a circle).
DE The text is written character by character.
XX
PN [1]
PA r xcentre PLFLT
PD x coord for centre.
PX
PN [2]
PA r ycentre PLFLT
PD y coord for centre.
PX
PN [3]
PA r Radius PLFLT
PD Radius of the circle.
PX
PN [4]
PA r Angle PLFLT
PD angle at which a particular character
PD will be written (in deg).
PX
PN [5]
PA r pos PLFLT
PD index for incrementing the angle for the
PD next character in the text.
PX
PN [6]
PA r txt const char*
PD The text to be displayed.
PX
PN [7]
PA r just PLFLT
PD justification of the string.
PD (0=left,1=right,0.5=middle etc)
PX
RT void
RD 
RX
//


static void GraphDrawarcText(PLFLT xcentre, PLFLT ycentre,
                             PLFLT Radius, PLFLT Angle, PLFLT pos,
                             const char *txt, PLFLT just)
{
    ajuint i;
    ajuint numchar;
    PLFLT *xy1;
    PLFLT *xy2;
    char textchar[2];

    numchar = strlen(txt);

    textchar[1] = '\0';
    for(i=0; iLines,
	     graphData->xmin, graphData->xmax,
	     graphData->ymin, graphData->ymax);

    if(graphData)
    {
	tempstr = NULL;
	ajFileClose(&graphData->File);
	graphData->Lines = 0;
	tempstr = ajFmtStr("%S%d%S",
			   graphData->FName,++graphData->Num,graphData->Ext);
	graphData->File = ajFileNewOutNameS(tempstr);

	if(!graphData->File)
	{
	    ajErr("Could not open graph file %S\n",tempstr);
	    return;
	}

	ajDebug("Writing graph %d data to %S\n",graphData->Num,tempstr);

	if(graphData->Num > 1)		/* ajGraphOpenWin will do the first */
	{
	    ajFmtPrintF(graphData->File,
			"##Graphic\n##Screen x1 %f y1 %f x2 %f y2 %f\n",
			graphData->xmin, graphData->ymin,
			graphData->xmax, graphData->ymax);
	    graphData->Lines++;
	    graphData->Lines++;
	}

	ajListstrPushAppend(graphData->List, tempstr);
    }

    return;
}
ID GraphColourBack
TY static
MO ajgraph
LB graphics
XX
DE Set the background colour to either black or white depending on the
DE current settings.
XX
RT void
RD 
RX
//


static void GraphColourBack(void)
{
  /*ajDebug("GraphColourBack currentbgwhite: %B\n", currentbgwhite);*/

    if(currentbgwhite)
    {
	GraphPen(0, 255, 255, 255);
	GraphPen(MAXCOL, 0, 0, 0);

	graphColourName[0] = "WHITE";
	graphColourName[MAXCOL] = "BLACK";

	/*ajDebug("GraphColourBack pens 0 '%s' and pen %d '%s'\n",
	  graphColourName[0], MAXCOL, graphColourName[MAXCOL]);*/
    }

    return;
}
ID ajHistogramClose
TY public
MO ajhist
LB graphics
XX
DE Closes the histograms window.
XX
RT void
RD 
RX
//


void ajHistogramClose(void)
{
    ajGraphicsCloseWin();

    return;
}
ID ajHistogramSetMark
TY public
MO ajhist
LB graphics
XX
DE Set the histogram mark internal value
XX
PN [1]
PA r mark ajint
PD Mark value
PX
RT void
RD 
RX
//


void ajHistogramSetMark(ajint mark)
{
  aj_hist_mark = mark;
  return;
}
ID ajHistNew
TY public
MO ajhist
LB graphics
XX
DE Create a histogram Object. Which can hold "numofsets" set of data of which
DE all must have "numofpoints" data points in them.
XX
PN [1]
PA r numofsets ajuint
PD Number of sets of data.
PX
PN [2]
PA r numofpoints ajuint
PD Number of data points per set.
PX
RT AjPHist
RD histogram structure.
RX
//


AjPHist ajHistNew(ajuint numofsets, ajuint numofpoints)
{
    static AjPHist hist = NULL;
    ajuint i;

    AJNEW0(hist);

    hist->numofsets = 0;
    hist->numofsetsmax    = numofsets;
    hist->numofdatapoints = numofpoints;
    hist->xmin = 0;
    hist->xmax = 0;
    hist->displaytype = HIST_SIDEBYSIDE; /* default draw
					    multiple histograms
					    side by side */
    hist->bins = 0;
    hist->BaW  = AJFALSE;
    ajStrAssignEmptyC(&hist->title,"");
    ajStrAssignEmptyC(&hist->xaxis,"");
    ajStrAssignEmptyC(&hist->yaxisleft,"");
    ajStrAssignEmptyC(&hist->yaxisright,"");

    AJCNEW0(hist->hists,numofsets);

    for(i=0;ihists[i]));
	(hist->hists)[i]->data       = NULL;
	(hist->hists)[i]->deletedata = AJFALSE;
	(hist->hists)[i]->colour  = i+2;
	(hist->hists)[i]->pattern = 0;
	(hist->hists)[i]->title = NULL;
	(hist->hists)[i]->xaxis = NULL;
	(hist->hists)[i]->yaxis = NULL;
    }

    return hist;
}
ID ajHistNewG
TY public
MO ajhist
LB graphics
XX
DE Create a histogram Object which has the histogram data and graph data
DE storage capacity.
XX
PN [1]
PA r numofsets ajuint
PD Number of sets of data.
PX
PN [2]
PA r numofpoints ajuint
PD Number of data points per set.
PX
PN [3]
PA u graph AjPGraph
PD Graph object, device, multi and name are set.
PD The original AjPGraph object will be used
PD by the AjPHist
PX
RT AjPHist
RD histogram structure.
RX
//


AjPHist ajHistNewG(ajuint numofsets, ajuint numofpoints, AjPGraph graph)
{
    AjPHist hist = ajHistNew(numofsets, numofpoints);

    hist->graph = graph;
    ajGraphicsSetDevice(graph);
    ajGraphSetMulti(graph, numofsets);
    ajGraphicsSetFilename(graph);

    return hist;
}
ID ajHistDel
TY public
MO ajhist
LB graphics
XX
DE Delete and free all memory associated with the histogram.
DE Does not delete the graph.
XX
PN [1]
PA d phist AjPHist*
PD Histogram to be deleted.
PX
RT void
RD 
RX
//


void ajHistDel(AjPHist* phist)
{
    ajuint i;
    AjPHist hist;

    if(!phist) return;

    hist = *phist;

    if (!hist)
	return;

    for(i=0;inumofsets; i++)
    {
	if(hist->hists[i]->deletedata)
	{
	    ajStrDel(&hist->hists[i]->title);
	    ajStrDel(&hist->hists[i]->xaxis);
	    ajStrDel(&hist->hists[i]->yaxis);
	    AJFREE(hist->hists[i]->data);
	}
	AJFREE((hist->hists[i]));
    }

    AJFREE(hist->hists);

    ajStrDel(&hist->title);
    ajStrDel(&hist->xaxis);
    ajStrDel(&hist->yaxisleft);
    ajStrDel(&hist->yaxisright);

    AJFREE(*phist);
    return;
}
ID ajHistDisplay
TY public
MO ajhist
LB graphics
XX
DE Display the histogram.
XX
PN [1]
PA r thys const AjPHist
PD Histogram Structure.
PX
RT void
RD 
RX
//


void ajHistDisplay(const AjPHist thys)
{
    PLFLT *data    = NULL;		/* points to data in hist */
    PLFLT *totals  = NULL;
    PLFLT *totals2 = NULL;
    float ptsperbin;
    float max = FLT_MIN;
    float min = 0.0;
    ajuint i;
    ajuint j;
    ajint ratioint;
    ajint num;
    ajint old;
    float bin_range;
    float bar_width;
    float offset;
    float start;
    float tot;
    float percent5;
    
    /* Sanity check */
    if(thys->numofdatapoints < 1 || thys->numofsets < 1 || thys->bins < 1)
    {
	ajErr("points =%d, sets = %d, bins = %d !!! "
	      "Must all be Greater than 1 ",
	      thys->numofdatapoints,thys->numofsets,thys->bins);

	return;
    }

    /* what multiple is the bins to numofdatasets */
    /* as i may have to take an average if not identical */
    ptsperbin = (float)thys->numofdatapoints/(float)thys->bins;
    
    if(ptsperbin < 1.0)
    {
	ajErr("You cannot more have bins than datapoints!!");

	return;
    }
    /* is the ratio a whole number? */

    ratioint = (ajint)ptsperbin;

    if((ptsperbin - (float)ratioint) != 0.0)
    {
	ajErr("number of data points needs to be a multiple of bins");

	return;
    }
    /* end Sanity check */
    
    /* Add spacing either side */
    percent5 = (thys->xmax - thys->xmin)*(float)0.025;
    
    
    /* calculate max and min for each set */
    if(thys->numofsets != 1)
    {	
	/* if NOT side by side max and min as the sets added together */
	if(thys->displaytype == HIST_SIDEBYSIDE)
	{
	    /* find the max value */
	    max = INT_MIN;
	    min = 0;

	    for(j=0; j < thys->numofsets; j++)
	    {
		data = thys->hists[j]->data;

		for(i=0;inumofdatapoints;i++)
		{
		    if(data[i] > max)
			max = data[i];

		    if(data[i] < min)
			min = data[i];
		}
	    }
	}
	else if(thys->displaytype == HIST_ONTOP)
	{
	    totals = AJALLOC(thys->numofdatapoints*(sizeof(PLFLT)));

	    /* set all memory to 0.0 */
	    for(i=0;inumofdatapoints;i++)
	    {
		totals[i] = 0.0;
	    }

	    min = 0;
	    max = 0;

	    for(j=0; j < thys->numofsets; j++)
	    {
		data = thys->hists[j]->data;
		for(i=0;inumofdatapoints;i++)
		{
		    totals[i] += data[i];
		    if(totals[i] > max)
			max = totals[i];

		    if(totals[i] < min)
			min = totals[i];
		    /*	  ajDebug("%d %d\t%f",j,i,totals[i]);*/
		}
	    }
	}
	else if(thys->displaytype == HIST_SEPARATE)
	{
	    totals = AJALLOC(thys->numofsets*(sizeof(PLFLT)));
	    totals2 = AJALLOC(thys->numofsets*(sizeof(PLFLT)));

	    for(j=0; j < thys->numofsets; j++)
	    {
		data = thys->hists[j]->data;
		totals[j] = 0;
		totals2[j] = 0;

		for(i=0;inumofdatapoints;i++)
		{
		    if(totals[j] < data[i])
			totals[j] = data[i];

		    if(totals2[j] > data[i])
			totals2[j] = data[i];
		}
	    }
	}
    }
    else
    {
	data = thys->hists[0]->data;
	max = data[0];
	min = 0;

	for(i=1; i < thys->numofdatapoints; i++)
	{
	    if(data[i] > max)
		max = data[i];

	    if(data[i] < min)
		min = data[i];
	}

	if(thys->displaytype == HIST_ONTOP /*!thys->sidebyside*/)
	{
	    totals = AJALLOC(thys->numofdatapoints*(sizeof(PLFLT)));

	    /* set all memory to 0.0 */
	    for(i=0; i < thys->numofdatapoints; i++)
		totals[i] = 0.0;
	}
	else if(thys->displaytype == HIST_SEPARATE)
	{
	    totals = AJALLOC((sizeof(PLFLT)));
	    totals[0]= max;
	    totals2 = AJALLOC((sizeof(PLFLT)));
	    totals2[0]= min;
	}
    }
    
    bin_range = (thys->xmax - thys->xmin)/(float)thys->bins;
    
    if(thys->displaytype != HIST_SEPARATE)
    {
	if(max <= 0.01)
	{
	    if(max < 0.0)
		max = 0.0;
	    else
		max = 1.0;
	}

	ajGraphOpenPlotset(thys->graph, 1);
	ajGraphicsPlenv(thys->xmin-percent5, thys->xmax+percent5, min,
		     max*((float)1.025), aj_hist_mark);
	ajGraphicsSetLabelsS(thys->xaxis ,
                             thys->yaxisleft ,
                             thys->title,
                             thys->subtitle);

	ajGraphicsSetRlabelS(thys->yaxisright);
    }
    else 
	ajGraphOpenPlotset(thys->graph, thys->numofsets);
    
    if(thys->displaytype == HIST_SIDEBYSIDE)
    {
	bar_width = bin_range/thys->numofsets;

	for(i=0; i < thys->numofsets; i++)
	{
	    offset = i*bar_width;
	    start = thys->xmin;
	    num = 0;
	    tot=0.0;
	    data = thys->hists[i]->data;

	    for(j=0; j < thys->numofdatapoints; j++)
	    {
		tot += data[j];
		num++;

		if(num >= ptsperbin)
		{
		    tot = tot / (float)num;

		    if(thys->BaW)
			old = ajGraphicsSetFillpat(thys->hists[i]->pattern);
		    else
			old = ajGraphicsSetFgcolour(thys->hists[i]->colour);
		    ajGraphicsDrawposRectFill(start+offset,0.0,
                                              start+offset+bar_width,tot);

		    if(thys->BaW)
			ajGraphicsSetFillpat(old);
		    else
			ajGraphicsSetFgcolour(old);

		    ajGraphicsDrawposRect(start+offset,0.0,
                                          start+offset+bar_width,tot);
		    num = 0;
		    tot = 0;
		    start +=bin_range;
		}		
	    }
	}
    }
    else if(thys->displaytype == HIST_SEPARATE)
    {
	bar_width = bin_range;

	for(i=0; i < thys->numofsets; i++)
	{	    
	    if(totals[i] <= 0.01)
	    {			       /* apparently the ymin value */
		if(totals[i] < 0.0)
		    totals[i] = 0.0;
		else
		    totals[i] = 1.0;
	    }

	    ajGraphicsPlenv(thys->xmin - percent5, thys->xmax + percent5,
                            totals2[i]*((float)1.025), totals[i]*((float)1.025),
                            aj_hist_mark);
	    offset = /*bar_width*/0.0;
	    start = thys->xmin;
	    num = 0;
	    tot=0.0;
	    data = thys->hists[i]->data;
	    ajGraphicsSetLabelsS(thys->hists[i]->xaxis,
                                 thys->hists[i]->yaxis,
                                 thys->hists[i]->title,
                                 thys->hists[i]->subtitle);
	    
	    for(j=0; j < thys->numofdatapoints; j++)
	    {
		tot += data[j];
		num++;

		if(num >= ptsperbin)
		{
		    tot = tot / (float)num;

		    if(thys->BaW)
			old = ajGraphicsSetFillpat(thys->hists[i]->pattern);
		    else
			old = ajGraphicsSetFgcolour(thys->hists[i]->colour);

		    ajGraphicsDrawposRectFill(start+offset,0.0,
                                              start+offset+bar_width,tot);

		    if(thys->BaW)
			ajGraphicsSetFillpat(old);
		    else
			ajGraphicsSetFgcolour(old);

		    ajGraphicsDrawposRect(start+offset,0.0,
                                          start+offset+bar_width,tot);
		    num = 0;
		    tot = 0;
		    start +=bin_range;
		}		
	    }
	}
    }
    else if(thys->displaytype == HIST_ONTOP)
    {
	for(i=0; i < thys->numofdatapoints; i++)
	    totals[i] = 0.0;

	for(i=0; i < thys->numofsets; i++)
	{
	    data = thys->hists[i]->data;
	    start = thys->xmin;
	    num = 0;
	    tot=0.0;

	    for(j=0; j < thys->numofdatapoints; j++)
	    {
		tot += data[j];
		num++;

		if(num >= ptsperbin)
		{
		    tot = tot / (float)num;

		    if(thys->BaW)
			old = ajGraphicsSetFillpat(thys->hists[i]->pattern);
		    else
			old = ajGraphicsSetFgcolour(thys->hists[i]->colour);

		    ajGraphicsDrawposRectFill(start,totals[j],
                                              start+bin_range,tot+totals[j]);
		    if(thys->BaW)
			ajGraphicsSetFillpat(old);
		    else
			ajGraphicsSetFgcolour(old);

		    ajGraphicsDrawposRect(start,totals[j],
                                          start+bin_range,tot+totals[j]);
		    totals[j] += tot;
		    tot = 0;
		    /*	  ajDebug("num = %d",num);*/
		    num = 0;
		    start +=bin_range;
		}
	    }
	}
    }

    AJFREE(totals);
    AJFREE(totals2);

    return;
}
ID ajHistSetMono
TY public
MO ajhist
LB graphics
XX
DE Set patterns instead of colours for printing to B/W printers etc.
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r set AjBool
PD Set to use patterns or colour for filling,
PD false to reset to colour.
PX
RT void
RD 
RX
//


void ajHistSetMono(AjPHist thys, AjBool set)
{
    thys->BaW = set;

    return;
}
ID ajHistSetRlabelC
TY public
MO ajhist
LB graphics
XX
DE Store Y Axis Right Label for the histogram
XX
PN [1]
PA u thys AjPHist
PD histogram to set string in.
PX
PN [2]
PA r txt const char*
PD text to be copied.
PX
RT void
RD 
RX
//


void ajHistSetRlabelC(AjPHist thys, const char* txt)
{
    ajStrAssignC(&thys->yaxisright,txt);

    return;
}
ID ajHistSetTitleC
TY public
MO ajhist
LB graphics
XX
DE Copy Title for the histogram.
XX
PN [1]
PA u thys AjPHist
PD histogram to set string in.
PX
PN [2]
PA r txt const char*
PD text to be copied.
PX
RT void
RD 
RX
//


void ajHistSetTitleC(AjPHist thys, const char* txt)
{
    ajStrAssignC(&thys->title,txt);

    return;
}
ID ajHistSetXlabelC
TY public
MO ajhist
LB graphics
XX
DE Store X axis label for the histogram
XX
PN [1]
PA u thys AjPHist
PD histogram to set string in.
PX
PN [2]
PA r txt const char*
PD text to be copied.
PX
RT void
RD 
RX
//


void ajHistSetXlabelC(AjPHist thys, const char* txt)
{
    ajStrAssignC(&thys->xaxis,txt);

    return;
}
ID ajHistSetYlabelC
TY public
MO ajhist
LB graphics
XX
DE Store Y Axis Left Label for the histogram
XX
PN [1]
PA u thys AjPHist
PD histogram to set string in.
PX
PN [2]
PA r txt const char*
PD text to be copied.
PX
RT void
RD 
RX
//


void ajHistSetYlabelC(AjPHist thys, const char* txt)
{
    ajStrAssignC(&thys->yaxisleft,txt);

    return;
}
ID ajHistSetmultiColour
TY public
MO ajhist
LB graphics
XX
DE Set colour for bars in histogram for index'th set.
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r indexnum ajuint
PD Index for the set number.
PX
PN [3]
PA r colour ajint
PD Colour for bar set.
PX
RT void
RD 
RX
//


void ajHistSetmultiColour(AjPHist thys, ajuint indexnum, ajint colour)
{
    if(indexnum >= thys->numofdatapoints)
    {
	ajErr("Histograms can only be allocated from 0 to %d. NOT %u",
	      thys->numofdatapoints-1,indexnum);

	return;
    }

    thys->hists[indexnum]->colour = colour;

    return;
}
ID ajHistSetmultiPattern
TY public
MO ajhist
LB graphics
XX
DE Set colour for bars in histogram for one set.
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r indexnum ajuint
PD Index for the set number.
PX
PN [3]
PA r style ajint
PD Line style number for bar set.
PX
RT void
RD 
RX
//


void ajHistSetmultiPattern(AjPHist thys, ajuint indexnum, ajint style)
{
    if(indexnum >= thys->numofdatapoints)
    {
	ajErr("Histograms can only be allocated from 0 to %d. NOT %u",
	      thys->numofdatapoints-1,indexnum);

	return;
    }

    thys->hists[indexnum]->pattern = style;

    return;
}
ID ajHistSetmultiTitleC
TY public
MO ajhist
LB graphics
XX
DE Store title for the index'th set.
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r indexnum ajuint
PD Index for the set number.
PX
PN [3]
PA r txt const char*
PD Title.
PX
RT void
RD 
RX
//


void ajHistSetmultiTitleC(AjPHist thys, ajuint indexnum, const char *txt)
{
    if(indexnum >= thys->numofdatapoints)
    {
	ajErr("Histograms can only be allocated from 0 to %d. NOT %u",
	      thys->numofdatapoints-1,indexnum);

	return;
    }

    ajStrAssignC(&thys->hists[indexnum]->title,txt);

    return;
}
ID ajHistSetmultiTitleS
TY public
MO ajhist
LB graphics
XX
DE Set ptr for title for index'th set..
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r indexnum ajuint
PD Index for the set number.
PX
PN [3]
PA r str const AjPStr
PD Title.
PX
RT void
RD 
RX
//


void ajHistSetmultiTitleS(AjPHist thys, ajuint indexnum, const AjPStr str)
{
    if(indexnum >= thys->numofdatapoints)
    {
	ajErr("Histograms can only be allocated from 0 to %d. NOT %u",
	      thys->numofdatapoints-1,indexnum);
	return;
    }

    ajStrAssignS(&thys->hists[indexnum]->title, str);

    return;
}
ID ajHistSetmultiXlabelC
TY public
MO ajhist
LB graphics
XX
DE Store X axis title for the index'th set.
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r indexnum ajuint
PD Index for the set number.
PX
PN [3]
PA r txt const char*
PD x Title.
PX
RT void
RD 
RX
//


void ajHistSetmultiXlabelC(AjPHist thys, ajuint indexnum, const char *txt)
{
    if(indexnum >= thys->numofdatapoints)
    {
	ajErr("Histograms can only be allocated from 0 to %d. NOT %u",
	      thys->numofdatapoints-1,indexnum);

	return;
    }

    ajStrAssignC(&thys->hists[indexnum]->xaxis,txt);

    return;
}
ID ajHistSetmultiXlabelS
TY public
MO ajhist
LB graphics
XX
DE Set ptr for X axis title for index'th set..
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r indexnum ajuint
PD Index for the set number.
PX
PN [3]
PA r str const AjPStr
PD x Title.
PX
RT void
RD 
RX
//


void ajHistSetmultiXlabelS(AjPHist thys, ajuint indexnum, const AjPStr str)
{
    if(indexnum >= thys->numofdatapoints)
    {
	ajErr("Histograms can only be allocated from 0 to %d. NOT %u",
	      thys->numofdatapoints-1,indexnum);
	return;
    }

    ajStrAssignS(&thys->hists[indexnum]->xaxis, str);

    return;
}
ID ajHistSetmultiYlabelC
TY public
MO ajhist
LB graphics
XX
DE Store Y axis title for the index'th set.
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r indexnum ajuint
PD Index for the set number.
PX
PN [3]
PA r txt const char*
PD Y Title.
PX
RT void
RD 
RX
//


void ajHistSetmultiYlabelC(AjPHist thys, ajuint indexnum, const char *txt)
{
    if(indexnum >= thys->numofdatapoints)
    {
	ajErr("Histograms can only be allocated from 0 to %d. NOT %u",
	      thys->numofdatapoints-1,indexnum);
	return;
    }

    ajStrAssignC(&thys->hists[indexnum]->yaxis,txt);

    return;
}
ID ajHistSetmultiYlabelS
TY public
MO ajhist
LB graphics
XX
DE Set ptr for Y axis title for index'th set..
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r indexnum ajuint
PD Index for the set number.
PX
PN [3]
PA r str const AjPStr
PD Y Title.
PX
RT void
RD 
RX
//


void ajHistSetmultiYlabelS(AjPHist thys, ajuint indexnum, const AjPStr str)
{
    if(indexnum >= thys->numofdatapoints)
    {
	ajErr("Histograms can only be allocated from 0 to %d. NOT %u",
	      thys->numofdatapoints-1,indexnum);
	return;
    }

    ajStrAssignS(&thys->hists[indexnum]->yaxis, str);

    return;
}
ID ajHistDataAdd
TY public
MO ajhist
LB graphics
XX
DE Set internal pointer to data for a set of data points for one set.
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r indexnum ajuint
PD Index for the set number.
PX
PN [3]
PA u data PLFLT*
PD Data values to be owned by the histogram object
PX
RT void
RD 
RX
//


void ajHistDataAdd(AjPHist thys, ajuint indexnum, PLFLT *data)
{
    if(indexnum >= thys->numofdatapoints)
    {
	ajErr("Histograms can only be allocated from 0 to %d. NOT %u",
	      thys->numofdatapoints-1,indexnum);

	return;
    }

    if(!thys->hists[indexnum]->data)
	thys->numofsets++;

    thys->hists[indexnum]->data = data;

    return;
}
ID ajHistDataCopy
TY public
MO ajhist
LB graphics
XX
DE Copy data from data array to histogram for one set.
XX
PN [1]
PA u thys AjPHist
PD Histogram to have ptr set.
PX
PN [2]
PA r indexnum ajuint
PD Index for the set number.
PX
PN [3]
PA r srcdata PLFLT const*
PD Data to be copied
PX
RT void
RD 
RX
//


void ajHistDataCopy(AjPHist thys, ajuint indexnum, PLFLT const *srcdata)
{
    ajuint i;

    if(indexnum >= thys->numofdatapoints)
    {
	ajErr("Histograms can only be allocated from 0 to %d. NOT %u",
	      thys->numofdatapoints-1,indexnum);

	return;
    }

    thys->hists[indexnum]->data = AJALLOC(thys->numofdatapoints*sizeof(PLFLT));

    for(i=0; i < thys->numofdatapoints; i++)
	thys->hists[indexnum]->data[i] = srcdata[i];

    thys->hists[indexnum]->deletedata = AJTRUE;
    thys->numofsets++;

    return;
}
ID ajHttpGetProxyinfo
TY public
MO ajhttp
LB core
XX
DE Returns a proxy definition (if any). Any proxy string given as an
DE argument overrides any EMBOSS_PROXY definition. This
DE allows strings from DB definitions to override such an envvar.
XX
PN [1]
PA r dbproxy const AjPStr
PD Primary proxy string (if any)
PX
PN [2]
PA w proxyport ajint*
PD Proxy port
PX
PN [3]
PA w proxyname AjPStr*
PD Proxy name
PX
PN [4]
PA w proxyauth AjPStr*
PD Proxy authentication type (if any)
PX
PN [5]
PA w proxycreds AjPStr*
PD Proxy auth credentials (if any)
PX
RT AjBool
RD ajTrue if a proxy was defined
RX
//


AjBool ajHttpGetProxyinfo(const AjPStr dbproxy, ajint* proxyport,
                          AjPStr* proxyname, AjPStr* proxyauth,
                          AjPStr* proxycreds)
{
    AjPStr proxy     = NULL;
    AjPStr portStr   = NULL;
    const char *p    = NULL;
    const char *q    = NULL;
    AjPStrTok handle = NULL;

    AjPStr serv  = NULL;
    AjPStr token = NULL;
    AjBool ret = ajFalse;
    

    ajStrDel(proxyname);
    *proxyport = 0;
    ajStrDel(proxyauth);
    ajStrDel(proxycreds);

    
    ajNamGetValueC("proxy", &proxy);

    if(ajStrGetLen(dbproxy))
	ajStrAssignS(&proxy, dbproxy);


    token = ajStrNew();
    
    ajStrTokenAssignC(&handle, proxy, " \n\r\t,");
    ret = ajStrTokenNextParse(&handle, &token);
    if(!ret || ajStrMatchC(token, ":"))
    {
        ajStrDel(&proxy);
        ajStrDel(&token);
        ajStrTokenDel(&handle);

        return ajFalse;
    }


    serv    = ajStrNew();
    portStr = ajStrNew();

    ajStrAssignS(&serv, token);

    if(ajStrMatchC(serv, ":"))
	ajStrAssignClear(&serv);


    
    p = ajStrGetPtr(serv);
    if((q = strchr(p, (int)':')) && p != q)
    {
        ajStrAssignSubC(proxyname,p,0,q-p-1);
        ajStrAssignC(&portStr,q+1);
    }
    
    
    if(!ajStrGetLen(*proxyname) || !ajStrGetLen(portStr))
    {
        ajStrTokenDel(&handle);
        ajStrDel(&token);
        ajStrDel(&serv);
        ajStrDel(&proxy);
        ajStrDel(&portStr);
        
        return ajFalse;
    }

    ajStrToInt(portStr, proxyport);

    ajStrDel(&portStr);
    ajStrDel(&serv);

    /* Check for authentication type */

    ret = ajStrTokenNextParse(&handle, &token);
    if(!ret)
    {
        ajStrTokenDel(&handle);
        ajStrDel(&token);
        ajStrDel(&proxy);
        
        return ajTrue;
    }
        
    ajStrAssignS(proxyauth, token);
    
    /* Check for authentication credentials */

    ret = ajStrTokenNextParse(&handle, &token);
    if(!ret)
    {
        ajWarn("ajHttpGetProxyinfo: No credentials specified in proxy "
               "definition (%S)",proxy);
        ajStrTokenDel(&handle);
        ajStrDel(&token);
        ajStrDel(&proxy);
        
        return ajFalse;
    }
    
    ajStrAssignS(proxycreds,token);
    
    ajStrTokenDel(&handle);
    ajStrDel(&token);
    ajStrDel(&proxy);
    
    return ajTrue;
}
ID ajHttpGetVersion
TY public
MO ajhttp
LB core
XX
DE Returns the HTTP version. Any supplied version takes precedence over
DE an EMBOSS_HTTPVERSION definition so allowing DB entries to
DE override such a setting.
XX
PN [1]
PA r version const AjPStr
PD Version or NULL (or zero-length string)
PX
PN [2]
PA w httpver AjPStr*
PD HTTP version
PX
RT AjBool
RD ajTrue if a version was defined
RX
//


AjBool ajHttpGetVersion(const AjPStr version, AjPStr* httpver)
{
    ajNamGetValueC("httpversion", httpver);
    ajDebug("httpver getValueC '%S'\n", *httpver);

    if(ajStrGetLen(version))
	ajStrAssignS(httpver, version);

    ajDebug("httpver after qry '%S'\n", *httpver);

    if(!ajStrGetLen(*httpver))
    {
	ajStrAssignC(httpver, "1.1");

	return ajFalse;
    }

    if(!ajStrIsFloat(*httpver))
    {
	ajWarn("Invalid HTTPVERSION '%S', reset to 1.1", *httpver);
	ajStrAssignC(httpver, "1.1");

	return ajFalse;
    }

    ajDebug("httpver final '%S'\n", *httpver);

    return ajTrue;
}
ID ajHttpOpen
TY public
MO ajhttp
LB core
XX
DE Opens an HTTP connection
XX
PN [1]
PA r dbname const AjPStr
PD Database name (for error reporting)
PX
PN [2]
PA r host const AjPStr
PD Host name
PX
PN [3]
PA r iport ajint
PD Port
PX
PN [4]
PA r get const AjPStr
PD GET string
PX
PN [5]
PA u Psock AjPSysSocket
PD Socket returned to caller
PX
RT FILE*
RD Open file on success, NULL on failure
RX
//


FILE* ajHttpOpen(const AjPStr dbname, const AjPStr host, ajint iport,
                 const AjPStr get, AjPSysSocket Psock)
{
    FILE* fp;
    struct addrinfo hints;
    struct addrinfo *add = NULL;
    struct addrinfo *addinit = NULL;

    AjPStr portstr = NULL;

    const char *phost = NULL;
    const char *pport = NULL;

    AjOSysSocket sock = *Psock;

    AjPStr errstr = NULL;
    int ret;

    phost = ajStrGetPtr(host);
    ajDebug("ajHttpOpen db: '%S' host '%S' port: %u get: '%S'\n",
	    dbname, host, iport, get);

    memset(&hints,0,sizeof(hints));

    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    portstr = ajStrNew();
    ajFmtPrintS(&portstr,"%d",iport);
    pport =  ajStrGetPtr(portstr);

    ret = getaddrinfo(phost, pport, &hints, &addinit);

    ajStrDel(&portstr);
    
    if(ret)
    {
	ajErr("[%s] Failed to find host '%S' for database '%S'",
	      gai_strerror(ret), host, dbname);

	return NULL;
    }

    sock.sock = AJBADSOCK;
    
    for(add = addinit; add; add = add->ai_next)
    {
        sock.sock = ajSysFuncSocket(add->ai_family, add->ai_socktype,
                                    add->ai_protocol);

        if(sock.sock == AJBADSOCK)
            continue;

        if(connect(sock.sock, add->ai_addr, add->ai_addrlen))
        {
            ajSysSocketclose(sock);
            sock.sock = AJBADSOCK;
            continue;
        }

        break;
    }

    freeaddrinfo(addinit);

    if(sock.sock == AJBADSOCK)
    {
	ajDebug("Socket connect failed\n");
	ajFmtPrintS(&errstr, "socket connect failed for database '%S'",
		    dbname);
	ajErr("%S", errstr);
	perror(ajStrGetPtr(errstr));
	ajStrDel(&errstr);

	return NULL;
    }

    fp = httpSend(dbname, sock, host, iport, NULL, NULL, get);

    return fp;
}
ID ajHttpOpenProxy
TY public
MO ajhttp
LB core
XX
DE Opens an HTTP connection via a proxy
XX
PN [1]
PA r dbname const AjPStr
PD Databse name (for error reporting)
PX
PN [2]
PA r proxyname const AjPStr
PD Proxy name
PX
PN [3]
PA r proxyport ajint
PD Proxy port
PX
PN [4]
PA r proxyauth const AjPStr
PD Proxy auth type (if any)
PX
PN [5]
PA r proxycreds const AjPStr
PD Proxy auth credentials (if any)
PX
PN [6]
PA r host const AjPStr
PD Host name
PX
PN [7]
PA r iport ajint
PD Port
PX
PN [8]
PA r get const AjPStr
PD GET string
PX
PN [9]
PA u Psock AjPSysSocket
PD Socket returned to caller
PX
RT FILE*
RD Open file on success, NULL on failure
RX
//


FILE* ajHttpOpenProxy(const AjPStr dbname, const AjPStr proxyname,
                      ajint proxyport, const AjPStr proxyauth,
                      const AjPStr proxycreds, const AjPStr host,
                      ajint iport, const AjPStr get, AjPSysSocket Psock)
{
    FILE* fp;
    struct addrinfo hints;
    struct addrinfo *add;
    struct addrinfo *addinit;

    AjPStr portstr = NULL;

    const char *phost = NULL;
    const char *pport = NULL;

    AjOSysSocket sock = *Psock;
    
    AjPStr errstr = NULL;
    int ret;

    phost = ajStrGetPtr(proxyname);
    ajDebug("ajHttpOpenProxy db: '%S' host '%s' get: '%S'\n",
	    dbname, phost, get);

    memset(&hints,0,sizeof(hints));

    hints.ai_socktype = SOCK_STREAM;

    portstr = ajStrNew();
    ajFmtPrintS(&portstr,"%d",proxyport);
    pport =  ajStrGetPtr(portstr);
    
    ret = getaddrinfo(phost, pport, &hints, &addinit);


    
    if(ret)
    {
	ajErr("[%s] Failed to find host '%S' for service '%S'",
	      gai_strerror(ret), proxyname, portstr);
        ajStrDel(&portstr);

	return NULL;
    }

    ajStrDel(&portstr);

    sock.sock = AJBADSOCK;
    
    for(add = addinit; add; add = add->ai_next)
    {
        sock.sock = ajSysFuncSocket(add->ai_family, add->ai_socktype,
                                    add->ai_protocol);

        if(sock.sock == AJBADSOCK)
            continue;

        if(connect(sock.sock, add->ai_addr, add->ai_addrlen))
        {
            ajSysSocketclose(sock);
            sock.sock = AJBADSOCK;
            continue;
        }

        break;
    }

    freeaddrinfo(addinit);
    
    if(sock.sock == AJBADSOCK)
    {
	ajDebug("Socket connect failed\n");
	ajFmtPrintS(&errstr, "socket connect failed for database '%S'",
		    dbname);
	ajErr("%S", errstr);
	perror(ajStrGetPtr(errstr));
	ajStrDel(&errstr);

	return NULL;
    }

    fp = httpSend(dbname, sock, host, iport, proxyauth, proxycreds, get);

    return fp;
}
ID httpSend
TY static
MO ajhttp
LB core
XX
DE Send HTTP GET request to an open socket
XX
PN [1]
PA r dbname const AjPStr
PD Database name (for error reporting)
PX
PN [2]
PA u sock AjOSysSocket
PD Socket structure
PX
PN [3]
PA r host const AjPStr
PD Host name for Host header line
PX
PN [4]
PA r iport ajint
PD Port for Host header line
PX
PN [5]
PA r proxyauth const AjPStr
PD Proxy auth type (if any)
PX
PN [6]
PA r proxycreds const AjPStr
PD Proxy auth credentials (if any)
PX
PN [7]
PA r get const AjPStr
PD GET string
PX
RT FILE*
RD Open file on success, NULL on failure
RX
//


static FILE* httpSend(const AjPStr dbname,
                      AjOSysSocket sock,
                      const AjPStr host, ajint iport,
                      const AjPStr proxyauth, const AjPStr proxycreds,
                      const AjPStr get)
{
    FILE* fp       = NULL;
    AjPStr gethead = NULL;
    AjPStr cred = NULL;
    
    ajint isendlen;

    ajDebug("httpSend: Sending to socket\n");

    gethead = ajStrNew();


    isendlen = send(sock.sock, ajStrGetPtr(get), ajStrGetLen(get), 0);

    if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(get))
	ajErr("send failure, expected %u bytes returned %d : %s\n",
	      ajStrGetLen(get), isendlen, ajMessGetSysmessageC());

    ajDebug("sending: '%S'\n", get);
    if(isendlen < 0)
            ajDebug("send for GET errno %d msg '%s'\n",
                    errno, ajMessGetSysmessageC());

    if(ajStrGetLen(proxyauth))
    {
        if(!ajStrMatchCaseC(proxyauth,"Basic"))
            ajErr("Only 'Basic' proxy authentication currently implemented,\n"
                  "no 'Digest' or 'NTLM' [%S]",proxyauth);

        cred = ajStrNew();
        
        ajUtilBase64EncodeC(&cred, ajStrGetLen(proxycreds),
                            (const unsigned char *) ajStrGetPtr(proxycreds));

        ajFmtPrintS(&gethead,"Proxy-Authorization: Basic %S\r\n",cred);

        isendlen =  send(sock.sock, ajStrGetPtr(gethead),
                         ajStrGetLen(gethead), 0);

        if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(gethead))
            ajErr("send failure, expected %u bytes returned %d : %s\n",
                  ajStrGetLen(gethead), isendlen, ajMessGetSysmessageC());
        ajDebug("sending: '%S'\n", gethead);
        if(isendlen < 0)
            ajDebug("send for host errno %d msg '%s'\n",
                    errno, ajMessGetSysmessageC());

        ajStrDel(&cred);
    }



    ajFmtPrintS(&gethead, "User-Agent: EMBOSS/%S (%S)\r\n",
                ajNamValueVersion(), ajNamValueSystem());
    isendlen = send(sock.sock, ajStrGetPtr(gethead), ajStrGetLen(gethead), 0);

    if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(gethead))
	ajErr("send failure, expected %u bytes returned %d : %s\n",
	      ajStrGetLen(gethead), isendlen, ajMessGetSysmessageC());
    ajDebug("sending: '%S'\n", gethead);

    ajFmtPrintS(&gethead, "Host: %S:%d\r\n", host, iport);
    isendlen =  send(sock.sock, ajStrGetPtr(gethead), ajStrGetLen(gethead), 0);

    if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(gethead))
	ajErr("send failure, expected %u bytes returned %d : %s\n",
	      ajStrGetLen(gethead), isendlen, ajMessGetSysmessageC());
    ajDebug("sending: '%S'\n", gethead);
    if(isendlen < 0)
            ajDebug("send for host errno %d msg '%s'\n",
                    errno, ajMessGetSysmessageC());

    if(ajStrFindC(get,"HTTP/1.1") != -1)
    {
        ajFmtPrintS(&gethead, "Connection: close\r\n");
        isendlen =  send(sock.sock, ajStrGetPtr(gethead),ajStrGetLen(gethead),
                         0);

        if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(gethead))
            ajErr("send failure, expected %u bytes returned %d : %s\n",
                  ajStrGetLen(gethead), isendlen, ajMessGetSysmessageC());

        ajDebug("sending: '%S'\n", gethead);
        if(isendlen < 0)
            ajDebug("send for host errno %d msg '%s'\n",
                    errno, ajMessGetSysmessageC());
    }

    ajFmtPrintS(&gethead, "\r\n");
    isendlen =  send(sock.sock, ajStrGetPtr(gethead), ajStrGetLen(gethead), 0);

    if(isendlen < 0 || isendlen != (ajint) ajStrGetLen(gethead))
	ajErr("send failure, expected %u bytes returned %d : %s\n",
	      ajStrGetLen(gethead), isendlen, ajMessGetSysmessageC());
    ajDebug("sending: '%S'\n", gethead);
    if(isendlen < 0)
        ajDebug("send for blankline errno %d msg '%s'\n",
                errno, ajMessGetSysmessageC());

    ajStrDel(&gethead);


    fp = ajSysFdFromSocket(sock, "r");
    
    if(!fp)
    {
	ajDebug("httpSend socket open failed\n");
	ajErr("httpSend: socket open failed for database '%S'", dbname);

	return NULL;
    }

    return fp;
}
ID ajHttpUrlrefNew
TY public
MO ajhttp
LB core
XX
DE Initialise a URL components object
XX
RT AjPUrlref
RD URL Components
RX
//


AjPUrlref ajHttpUrlrefNew(void)
{
    AjPUrlref ret = NULL;

    AJNEW0(ret);
    
    ret->Method   = ajStrNew();
    ret->Host     = ajStrNew();
    ret->Port     = ajStrNew();
    ret->Absolute = ajStrNew();
    ret->Relative = ajStrNew();
    ret->Fragment = ajStrNew();
    ret->Username = ajStrNew();
    ret->Password = ajStrNew();

    return ret;
}
ID ajHttpUrlrefDel
TY public
MO ajhttp
LB core
XX
DE Delete URL components object
XX
PN [1]
PA u thys AjPUrlref*
PD URL components object
PX
RT void
RD 
RX
//


void ajHttpUrlrefDel(AjPUrlref *thys)
{
    AjPUrlref pthis = NULL;

    if(!thys)
        return;

    if(!*thys)
        return;

    pthis = *thys;

    ajStrDel(&pthis->Method);
    ajStrDel(&pthis->Host);
    ajStrDel(&pthis->Port);
    ajStrDel(&pthis->Absolute);
    ajStrDel(&pthis->Relative);
    ajStrDel(&pthis->Fragment);
    ajStrDel(&pthis->Username);
    ajStrDel(&pthis->Password);

    AJFREE(pthis);
    
    *thys = NULL;
    
    return;
}
ID ajHttpUrlrefParseC
TY public
MO ajhttp
LB core
XX
DE Parse an IPV4/6 URL into its components
XX
PN [1]
PA u parts AjPUrlref*
PD URL components object
PX
PN [2]
PA u url const char*
PD URL
PX
RT void
RD 
RX
//


void ajHttpUrlrefParseC(AjPUrlref *parts, const char *url)
{
    char *ucopy = NULL;
    char *p = NULL;
    char *post = NULL;

    char *dest = NULL;
    char *src  = NULL;
    
    AjPUrlref comp = NULL;
    
    char *pmethod = NULL;
    char *phost   = NULL;
    char *pabs    = NULL;
    char *prel    = NULL;
    
    if(!parts || !url)
        return;
    
    if(!*parts)
        return;
    
    ucopy = ajCharNewC(url);

    post = ucopy;
    comp = *parts;
    
    /* Get any fragment */
    if ((p = strchr(ucopy, '#')))
    {
	*p++ = '\0';
        ajStrAssignC(&comp->Fragment,p);
    }

    if((p = strchr(ucopy, ' ')))
        *p++ = '\0';


    for(p = ucopy; *p; ++p)
    {
	if (isspace((int) *p))
        {
            dest = p;
            src  = p+1;
            
	    while ((*dest++ = *src++));

	    p = p-1;
	}

	if (*p == '/' || *p == '#' || *p == '?')
	    break;

	if (*p == ':')
        {
		*p = '\0';
                pmethod = post;
		post = p+1;

                if(ajCharPrefixCaseC(pmethod,"URL"))
                    pmethod = NULL;
                else
                    break;
	}
    }

    p = post;

    if(*p == '/')
    {
	if(p[1] == '/')
        {
	    phost = p+2;		/* There is a host   */
	    *p = '\0';
	    p = strchr(phost,'/');	/* Find end of host */

	    if(p)
            {
	        *p=0;			/* and terminate it */
	        pabs = p+1;		/* Path found */
	    }
	}
        else
	    pabs = p+1;		/* Path found but not host */
    }
    else
        prel = (*post) ? post : NULL;


    if(pmethod)
        ajStrAssignC(&comp->Method,pmethod);

    if(phost)
        ajStrAssignC(&comp->Host,phost);

    if(pabs)
        ajStrAssignC(&comp->Absolute,pabs);

    if(prel)
        ajStrAssignC(&comp->Relative,prel);

    AJFREE(ucopy);

    return;
}
ID ajHttpUrlrefSplitPort
TY public
MO ajhttp
LB core
XX
DE Separate any port from a host specification (IPV4/6)
XX
PN [1]
PA u urli AjPUrlref
PD URL components object
PX
RT void
RD 
RX
//


void ajHttpUrlrefSplitPort(AjPUrlref urli)
{
    const char *p   = NULL;
    const char *end = NULL;
    const char *start = NULL;
    
    ajint len;

    if(!(len = ajStrGetLen(urli->Host)))
        return;

    end = (start = ajStrGetPtr(urli->Host)) + len - 1;

    p = end;

    if(!isdigit((int) *p))
        return;

    while(isdigit((int) *p) && p != start)
        --p;

    if(p == start)
        return;

    if(*p != ':')
        return;
    
    ajStrAssignC(&urli->Port,p+1);

    ajStrAssignSubC(&urli->Host,start,0,p-start-1);
    
    return;
}
ID ajHttpUrlrefParseS
TY public
MO ajhttp
LB core
XX
DE Parse an IPV4/6 URL into its components
XX
PN [1]
PA u parts AjPUrlref*
PD URL components object
PX
PN [2]
PA r surl const AjPStr
PD URL
PX
RT void
RD 
RX
//


void ajHttpUrlrefParseS(AjPUrlref *parts, const AjPStr surl)
{
    ajHttpUrlrefParseC(parts, MAJSTRGETPTR(surl));
}
ID ajHttpUrlrefSplitUsername
TY public
MO ajhttp
LB core
XX
DE Separate any username[:password] from a host specification (IPV4/6)
XX
PN [1]
PA u urli AjPUrlref
PD URL components object
PX
RT void
RD 
RX
//


void ajHttpUrlrefSplitUsername(AjPUrlref urli)
{
    const char *p   = NULL;
    const char *end = NULL;
    AjPStr userpass = NULL;
    AjPStr host = NULL;
    
    ajint len;

    if(!ajStrGetLen(urli->Host))
        return;

    if(!(end = strchr(ajStrGetPtr(urli->Host), (int)'@')))
        return;

    p = ajStrGetPtr(urli->Host);
    len = end - p;

    if(!len)
        return;
    
    userpass = ajStrNew();
    ajStrAssignSubC(&userpass, p, 0, end - p - 1);
    
    host = ajStrNew();
    ajStrAssignC(&host,end + 1);
    ajStrAssignS(&urli->Host,host);

    

    if(!(end = strchr(ajStrGetPtr(userpass), (int)':')))
    {
        ajStrAssignS(&urli->Username,userpass);
        ajStrDel(&userpass);
        ajStrDel(&host);

        return;
    }

    p = ajStrGetPtr(userpass);
    len = end - p;

    if(!len)
        ajWarn("ajHttpUrlrefSplitUsername: Missing username in URL [%S@%S]",
               userpass,host);
    else
        ajStrAssignSubC(&urli->Username,p,0,len - 1);

    ajStrAssignC(&urli->Password, end + 1);

    ajStrDel(&userpass);
    ajStrDel(&host);

    return;
}
ID ajHttpQueryUrl
TY public
MO ajhttp
LB core
XX
DE Returns the components of a URL (IPV4/6)
DE An equivalent for seqHttpUrl().
XX
PN [1]
PA r qry const AjPQuery
PD Database query
PX
PN [2]
PA w iport ajint*
PD Port
PX
PN [3]
PA w host AjPStr*
PD Host name
PX
PN [4]
PA w urlget AjPStr*
PD URL for the HTTP header GET
PX
RT AjBool
RD ajTrue if the URL was parsed
RX
//


AjBool ajHttpQueryUrl(const AjPQuery qry, ajint* iport, AjPStr* host,
                      AjPStr* urlget)
{
    const AjPStr url = NULL;
    AjPUrlref uo = NULL;
    
    url = qry->DbUrl;
    if(!url)
    {
	ajErr("no URL defined for database %S", qry->DbName);

	return ajFalse;
    }

    uo = ajHttpUrlrefNew();
    
    ajHttpUrlrefParseC(&uo, ajStrGetPtr(url));
    ajHttpUrlrefSplitPort(uo);
    
    ajStrAssignS(host,uo->Host);
    ajFmtPrintS(urlget,"/%S",uo->Absolute);

    if(ajStrGetLen(uo->Port))
        ajStrToInt(uo->Port,iport);

    ajHttpUrlrefDel(&uo);

    return ajTrue;
}
ID ajHttpUrlDeconstruct
TY public
MO ajhttp
LB core
XX
DE Deconstruct a URL (IPV4/6)
XX
PN [1]
PA r url const AjPStr
PD url
PX
PN [2]
PA w iport ajint*
PD Port
PX
PN [3]
PA w host AjPStr*
PD Host name
PX
PN [4]
PA w urlget AjPStr*
PD URL for the HTTP header GET
PX
RT void
RD 
RX
//


void ajHttpUrlDeconstruct(const AjPStr url, ajint* iport, AjPStr* host,
                          AjPStr* urlget)
{
    AjPUrlref uo = NULL;
    
    uo = ajHttpUrlrefNew();
    
    ajHttpUrlrefParseC(&uo, ajStrGetPtr(url));
    ajHttpUrlrefSplitPort(uo);
    
    ajStrAssignS(host,uo->Host);
    ajFmtPrintS(urlget,"/%S",uo->Absolute);

    if(ajStrGetLen(uo->Port))
        ajStrToInt(uo->Port,iport);

    ajHttpUrlrefDel(&uo);

    return;
}
ID ajHttpRedirect
TY public
MO ajhttp
LB core
XX
DE Reads the header of http response in given buffer buff,
DE if it includes a redirection response updates the host, port and get
DE parameters using the 'Location' header
XX
PN [1]
PA u buff AjPFilebuff
PD file buffer
PX
PN [2]
PA w host AjPStr*
PD Host name
PX
PN [3]
PA w port ajint*
PD Port
PX
PN [4]
PA w path AjPStr*
PD part of URL after port number
PX
PN [5]
PA w httpcode ajuint*
PD HTTP protocol return code
PX
RT AjBool
RD returns true if the header includes a redirection response
RX
//


AjBool ajHttpRedirect(AjPFilebuff buff, AjPStr* host, ajint* port, AjPStr* path,
                      ajuint *httpcode)
{

    AjPRegexp httpexp  = NULL;
    AjPRegexp nullexp  = NULL;
    AjPRegexp redirexp = NULL;

    AjPStr codestr  = NULL;
    AjPStr newurl   = NULL;
    AjPStr newhost  = NULL;
    AjPStr currline = NULL;

    AjBool isheader = ajFalse;
    AjBool ret = ajFalse;

    httpexp  = ajRegCompC("^HTTP/\\S+\\s+(\\d+)");

    if(!buff->Size)
	return 0;

    ajBuffreadLine(buff, &currline);

    ajDebug("ajHttpRedirect: First line: '%S'\n", currline);

    if(ajRegExec(httpexp, currline))
    {
        isheader = ajTrue;
        ajRegSubI(httpexp, 1, &codestr);
        ajStrToUint(codestr, httpcode);
        ajDebug("Header: codestr '%S' code '%u'\n", codestr, *httpcode);
        ajStrDel(&codestr);
    }

    if(isheader)
    {
        if(*httpcode == 301 || *httpcode == 302 || *httpcode==307)
        {
            redirexp = ajRegCompC("^Location: (\\S+)");
            nullexp  = ajRegCompC("^\r?\n?$");

            while( ajBuffreadLine(buff, &currline) &&
                  !ajRegExec(nullexp, currline))
            {
        	ajDebug("ajHttpRedirect: header line: '%S'\n", currline);

        	if(ajRegExec(redirexp, currline))
                {
                    ajRegSubI(redirexp, 1, &newurl);
                    ajHttpUrlDeconstruct(newurl, port, &newhost, path);

                    if(ajStrGetLen(newhost))
                	ajStrAssignS(host, newhost);

                    ajStrDel(&newurl);
                    ajStrDel(&newhost);
                    ret = ajTrue;
                    break;
                }
            }

            ajRegFree(&redirexp);
            ajRegFree(&nullexp);
        }
    }

    if(!ret)
	ajFilebuffReset(buff);

    ajRegFree(&httpexp);
    ajStrDel(&currline);

    return ret;
}
ID ajHttpRead
TY public
MO ajhttp
LB core
XX
DE Reads the header of http response in given buffer buff,
DE if it includes a redirection response updates the host, port and get
DE parameters using the 'Location' header
XX
PN [1]
PA r dbhttpver const AjPStr
PD DB http version
PX
PN [2]
PA r dbname const AjPStr
PD DB name
PX
PN [3]
PA r dbproxy const AjPStr
PD DB proxy
PX
PN [4]
PA r host const AjPStr
PD Host name
PX
PN [5]
PA r port ajint
PD Port
PX
PN [6]
PA r dbpath const AjPStr
PD part of URL after port number
PX
RT AjPFilebuff
RD http response
RX
//


AjPFilebuff ajHttpRead(const AjPStr dbhttpver, const AjPStr dbname,
                       const AjPStr dbproxy, const AjPStr host,
                       ajint port, const AjPStr dbpath)
{
    return ajHttpReadPos(dbhttpver, dbname, dbproxy, host, port, dbpath, 0L);
}
ID ajHttpReadPos
TY public
MO ajhttp
LB core
XX
DE Reads the header of http response in given buffer buff,
DE if it includes a redirection response updates the host, port and get
DE parameters using the 'Location' header
XX
PN [1]
PA r dbhttpver const AjPStr
PD DB http version
PX
PN [2]
PA r dbname const AjPStr
PD DB name
PX
PN [3]
PA r dbproxy const AjPStr
PD DB proxy
PX
PN [4]
PA r host const AjPStr
PD Host name
PX
PN [5]
PA r port ajint
PD Port
PX
PN [6]
PA r dbpath const AjPStr
PD part of URL after port number
PX
PN [7]
PA r fpos ajlong
PD File start offset
PX
RT AjPFilebuff
RD http response
RX
//


AjPFilebuff ajHttpReadPos(const AjPStr dbhttpver, const AjPStr dbname,
                          const AjPStr dbproxy, const AjPStr host,
                          ajint port, const AjPStr dbpath, ajlong fpos)
{
    AjPStr get       = NULL;
    AjPStr httpver   = NULL;
    AjPStr proxyname = NULL;
    AjPStr proxyauth = NULL;
    AjPStr proxycred = NULL;
    AjPStr newhost   = NULL;
    AjPStr path      = NULL;
    AjPFilebuff buff = NULL;
    FILE *fp         = NULL;

    AjOSysSocket sock;
    AjOSysTimeout timo;
    ajint proxyport = 0;
    ajuint httpcode = 0;

    httpver   = ajStrNew();
    proxyname = ajStrNew();
    proxyauth = ajStrNew();
    proxycred = ajStrNew();
    get       = ajStrNew();
    newhost   = ajStrNew();

    ajDebug("ajHttpRead db: '%S' host '%S' port: %u dbpath: '%S'\n",
	    dbname, host, port, dbpath);

    ajStrAssignS(&newhost,host);
    ajStrAssignS(&path, dbpath);


    ajHttpGetVersion(dbhttpver, &httpver);

    ajHttpGetProxyinfo(dbproxy, &proxyport, &proxyname, &proxyauth, &proxycred);


    while (buff==NULL || ajHttpRedirect(buff, &newhost, &port, &path,
                                        &httpcode))
    {
	if(buff) /* means buff includes http redirect response*/
	    ajFilebuffDel(&buff);

	if(ajStrGetCharFirst(path)!='/')
	    ajStrInsertK(&path, 0, '/');

	if(ajStrGetLen(proxyname))
	{
	    ajFmtPrintS(&get, "GET http://%S:%d%S HTTP/%S\r\n",
	                host, port, path, httpver);
	    fp = ajHttpOpenProxy(dbname, proxyname, proxyport, proxyauth,
	                         proxycred, newhost, port, get, &sock);
	}
	else
	{
	    ajFmtPrintS(&get, "GET %S HTTP/%S\r\n", path, httpver);
	    if(fpos)
                ajFmtPrintAppS(&get, "Range: bytes=%Ld-\r\n", fpos);

	    fp = ajHttpOpen(dbname, newhost, port, get, &sock);
	}

	if(!fp)
	{
	    ajErr("ajHttpRead: cannot open HTTP connection '%S'\n", get);
	    buff = NULL;
	    break;
	}

	buff = ajFilebuffNewFromCfile(fp);

	if(!buff)
	{
	    ajErr("ajHttpRead: socket buffer attach failed for host '%S'"
		    ", HTTP get command was '%S'", host, get);
	    break;
	}

	timo.seconds = 180;
	ajSysTimeoutSet(&timo);
	ajFilebuffLoadAll(buff);
	ajSysTimeoutUnset(&timo);
    }

    if(httpcode >= 400)
    {
        ajFilebuffDel(&buff);
    }

    ajStrDel(&get);
    ajStrDel(&httpver);
    ajStrDel(&proxyname);
    ajStrDel(&newhost);
    ajStrDel(&path);

    return buff;
}
ID btreeFieldInit
TY static
MO ajindex
LB core
XX
DE Initialises the table for named fields
XX
RT void
RD 
RX
//


static void btreeFieldInit(void)
{
    ajuint i;
    BtreePField field = NULL;

    if(btreeFieldsTable)
        return;

    btreeFieldsTable = ajTablestrNewConst(30);

    for(i=0; btreeFields[i].Name; i++)
    {
        field = btreeFieldNewField(&btreeFields[i]);
        ajTablePut(btreeFieldsTable,
                   field->Name,
                   (void*) field);
        field = btreeFieldNewField(&btreeFields[i]);
        ajTablePut(btreeFieldsTable,
                   field->Extension,
                   (void*) field);
    }

    return;
}
ID btreeFieldNewC
TY static
MO ajindex
LB core
XX
DE Construct a new named field with default values
XX
PN [1]
PA r nametxt const char*
PD Field name
PX
RT BtreePField
RD New field object
RX
//


static BtreePField btreeFieldNewC(const char* nametxt)
{
    BtreePField ret;
    ajuint ilen;

    ilen = strlen(nametxt);

    AJNEW0(ret);

    ret->Name = ajStrNewC(nametxt);
    ret->Extension = ajStrNewRes(ilen+2);
    ajStrAssignK(&ret->Extension, 'x');
    ajStrAppendC(&ret->Extension, nametxt);
    ret->Secondary = ajTrue;
    ret->Len = 15;

    return ret;
}
ID btreeFieldNewS
TY static
MO ajindex
LB core
XX
DE Construct a new named field with default values
XX
PN [1]
PA r name const AjPStr
PD Field name
PX
RT BtreePField
RD New field object
RX
//


static BtreePField btreeFieldNewS(const AjPStr name)
{
    BtreePField ret;
    ajuint ilen;

    ilen = MAJSTRGETLEN(name);

    AJNEW0(ret);

    ret->Name = ajStrNewS(name);
    ret->Extension = ajStrNewRes(ilen+2);
    ajStrAssignK(&ret->Extension, 'x');
    ajStrAppendS(&ret->Extension, name);
    ret->Secondary = ajTrue;
    ret->Len = 15;

    return ret;
}
ID btreeFieldNewField
TY static
MO ajindex
LB core
XX
DE Construct copy of a known field
XX
PN [1]
PA r field const BtreePFielddef
PD Source field
PX
RT BtreePField
RD New field object
RX
//


static BtreePField btreeFieldNewField(const BtreePFielddef field)
{
    BtreePField ret = NULL;

    AJNEW0(ret);

    ret->Name = ajStrNewC(field->Name);
    ret->Extension = ajStrNewC(field->Extension);
    ret->Secondary = field->Secondary;
    ret->Len = field->Len;

    return ret;
}
ID btreeFieldDel
TY static
MO ajindex
LB core
XX
DE Destructor for a named field
XX
PN [1]
PA d Pfield BtreePField*
PD Field object
PX
RT void
RD 
RX
//


static void btreeFieldDel(BtreePField *Pfield)
{
    BtreePField field = *Pfield;

    ajStrDel(&field->Name);
    ajStrDel(&field->Extension);
    AJFREE(field);
    *Pfield = NULL;
    
    return;
}
ID btreeFieldMapDel
TY static
MO ajindex
LB core
XX
DE Deletes entries from the MSF internal table. Called for each entry in turn.
XX
PN [1]
PA d key void**
PD Standard argument, table key.
PX
PN [2]
PA d value void**
PD Standard argument, table data item.
PX
PN [3]
PA r cl void*
PD Standard argument, usually NULL
PX
RT void
RD 
RX
//


static void btreeFieldMapDel(void** key, void** value, void* cl)
{
    BtreePField field;

    (void) cl;

    field = (BtreePField) *value;

    btreeFieldDel(&field);

    *key = NULL;
    *value = NULL;

    return;
}
ID ajBtreeFieldGetLenC
TY public
MO ajindex
LB core
XX
DE Returns the default indexed name length for a named field
XX
PN [1]
PA r nametxt const char*
PD Field name
PX
RT ajuint
RD Field name length
RX
//


ajuint ajBtreeFieldGetLenC(const char* nametxt)
{
    ajuint ret = 15;
    const BtreePField field;

    btreeFieldInit();

    field = ajTableFetchC(btreeFieldsTable, nametxt);

    if(field)
        return field->Len;

    return ret;
}
ID ajBtreeFieldGetLenS
TY public
MO ajindex
LB core
XX
DE Returns the default indexed name length for a named field
XX
PN [1]
PA r name const AjPStr
PD Field name
PX
RT ajuint
RD Field name length
RX
//


ajuint ajBtreeFieldGetLenS(const AjPStr name)
{
    ajuint ret = 15;
    const BtreePField field;

    btreeFieldInit();

    field = ajTableFetchS(btreeFieldsTable, name);

    if(field)
        return field->Len;

    return ret;
}
ID ajBtreeFieldGetExtensionC
TY public
MO ajindex
LB core
XX
DE Returns the default index file extension for a named field
XX
PN [1]
PA r nametxt const char*
PD Field name
PX
RT const AjPStr
RD Filename extension
RX
//


const AjPStr ajBtreeFieldGetExtensionC(const char* nametxt)
{
    const BtreePField field;
    BtreePField newfield;

    btreeFieldInit();

    field = ajTableFetchC(btreeFieldsTable, nametxt);
    if(field)
        return field->Extension;
        
    newfield = btreeFieldNewC(nametxt);
    ajTablePut(btreeFieldsTable,
               newfield->Name,
               (void*) newfield);

    return newfield->Extension;
}
ID ajBtreeFieldGetExtensionS
TY public
MO ajindex
LB core
XX
DE Returns the default index file extension for a named field
XX
PN [1]
PA r name const AjPStr
PD Field name
PX
RT const AjPStr
RD Filename extension
RX
//


const AjPStr ajBtreeFieldGetExtensionS(const AjPStr name)
{
    const BtreePField field;
    BtreePField newfield;

    btreeFieldInit();

    field = ajTableFetchS(btreeFieldsTable, name);
    if(field)
        return field->Extension;

    newfield = btreeFieldNewS(name);
    ajTablePut(btreeFieldsTable,
               newfield->Name,
               (void*) newfield);

    return newfield->Extension;
}
ID ajBtreeFieldGetSecondaryC
TY public
MO ajindex
LB core
XX
DE Returns whether a named field uses a secondary index
XX
PN [1]
PA r nametxt const char*
PD Field name
PX
RT AjBool
RD True for a secondary index where indexed terms treturn IDs
RD False for an ID index where terms return file positions.
RX
//


AjBool ajBtreeFieldGetSecondaryC(const char* nametxt)
{
    AjBool ret = ajTrue;
    const BtreePField field;

    btreeFieldInit();

    ajStrAssignC(&btreeFieldnameTmp, nametxt);
    field = ajTableFetchC(btreeFieldsTable, nametxt);

    if(field)
        return field->Secondary;

    return ret;
}
ID ajBtreeFieldGetSecondaryS
TY public
MO ajindex
LB core
XX
DE Returns whether a named field uses a secondary index
XX
PN [1]
PA r name const AjPStr
PD Field name
PX
RT AjBool
RD True for a secondary index where indexed terms treturn IDs
RD False for an ID index where terms return file positions.
RX
//


AjBool ajBtreeFieldGetSecondaryS(const AjPStr name)
{
    AjBool ret = ajTrue;
    const BtreePField field;

    btreeFieldInit();
    
    field = ajTableFetchS(btreeFieldsTable, name);
    if(field)
        return field->Secondary;

    return ret;
}
ID ajBtreeIdcacheNewC
TY public
MO ajindex
LB core
XX
DE s** Open a b+tree index file and initialise a cache object
XX
PN [1]
PA r filetxt const char*
PD name of file
PX
PN [2]
PA r exttxt const char*
PD extension of file
PX
PN [3]
PA r idirtxt const char*
PD index file directory
PX
PN [4]
PA r mode const char*
PD opening mode
PX
PN [5]
PA r compressed AjBool
PD Compressed index flag
PX
PN [6]
PA r kwlimit ajuint
PD Max key size
PX
PN [7]
PA r refcount ajuint
PD Extra files for each entry
PX
PN [8]
PA r pripagesize ajuint
PD Primary pagesize
PX
PN [9]
PA r secpagesize ajuint
PD Secondary pagesize
PX
PN [10]
PA r pricachesize ajuint
PD size of primary cache
PX
PN [11]
PA r seccachesize ajuint
PD size of secondary cache
PX
PN [12]
PA r pripagecount ajulong
PD Primary page count
PX
PN [13]
PA r secpagecount ajulong
PD Secondary page count
PX
PN [14]
PA r order ajuint
PD Tree order
PX
PN [15]
PA r fill ajuint
PD Number of entries per bucket
PX
PN [16]
PA r level ajuint
PD level of tree
PX
PN [17]
PA r sorder ajuint
PD order of secondary tree
PX
PN [18]
PA r sfill ajuint
PD Number of entries per secondary bucket
PX
PN [19]
PA r count ajulong
PD Number of entries in the index
PX
PN [20]
PA r countall ajulong
PD Number of total entries in the index
PX
RT AjPBtcache
RD initialised disc block cache structure
RX
//


AjPBtcache ajBtreeIdcacheNewC(const char *filetxt, const char *exttxt,
                              const char *idirtxt, const char *mode,
                              AjBool compressed,
                              ajuint kwlimit, ajuint refcount,
                              ajuint pripagesize, ajuint secpagesize,
                              ajuint pricachesize, ajuint seccachesize,
                              ajulong pripagecount, ajulong secpagecount,
                              ajuint order, ajuint fill, ajuint level,
                              ajuint sorder, ajuint sfill,
                              ajulong count, ajulong countall)
{
    AjPBtcache cache = NULL;
    AjPBtpage  page = NULL;
#if defined (usestat64)
    struct stat64 buf;
#else
    struct stat buf;
#endif

    ajulong filelen = 0L;
    AjBool douncompress = ajFalse;
    AjBool writemode = ajFalse;
    AjBool okcache = ajTrue;

    AJNEW0(cache);

    cache->prilistLength = 0;
    cache->seclistLength = 0;

    cache->plru   = NULL;
    cache->pmru   = NULL;
    cache->slru   = NULL;
    cache->smru   = NULL;
    
    cache->replace    = ajStrNew();
    cache->numreplace = 0L;
    
    if(pripagesize>0)
	cache->pripagesize = pripagesize;
    else
	cache->pripagesize = BT_PAGESIZE;

    cache->plevel       = level;
    cache->porder       = order;
    cache->pnperbucket  = fill;
    cache->pricachesize = pricachesize;
    cache->refcount = refcount;

    cache->pripagecount = pripagecount;
    cache->secpagecount = secpagecount;

    cache->sorder = sorder;
    cache->slevel = 0;
    cache->snperbucket = sfill;

    if(secpagesize > 0)
	cache->secpagesize = secpagesize;
    else
	cache->secpagesize = cache->pripagesize;

    if(seccachesize > 0)
        cache->seccachesize = seccachesize;
    else
        cache->seccachesize = cache->pricachesize;

    cache->countunique = count;
    cache->countall = countall;
    cache->keylimit = kwlimit;

    cache->compressed = compressed;


    cache->bmem = NULL;
    cache->tmem = NULL;

    cache->bsmem = NULL;
    cache->tsmem = NULL;

    cache->pripagetable = ajTableulongNewConst(cache->pricachesize);
    cache->secpagetable = ajTableulongNewConst(cache->seccachesize);

    cache->filename = ajStrNew();

    if(!*idirtxt)
        ajFmtPrintS(&cache->filename,"%s.%s",filetxt,exttxt);
    else if(idirtxt[strlen(idirtxt)-1] == SLASH_CHAR)
        ajFmtPrintS(&cache->filename,"%s%s.%s",idirtxt,filetxt,exttxt);
    else
        ajFmtPrintS(&cache->filename,"%s%s%s.%s",idirtxt,SLASH_STRING,
		    filetxt,exttxt);

    if(cache->porder < 4)
    {
        ajErr("cache '%S' pagesize %u order %u too small, increase pagesize",
              cache->filename, cache->pripagesize, cache->porder);
        okcache = ajFalse;
    }

    if(cache->pnperbucket < 4)
    {
        ajErr("cache '%S' pagesize %u fill %u too small, increase pagesize",
              cache->filename, cache->pripagesize, cache->pnperbucket);
        okcache = ajFalse;
    }

    if(cache->sorder < 4)
    {
        ajErr("cache '%S' pagesize %u srder %u too small, increase pagesize",
              cache->filename, cache->pripagesize, cache->sorder);
        okcache = ajFalse;
    }

    if(cache->snperbucket < 4)
    {
        ajErr("cache '%S' pagesize %u sfill %u too small, increase pagesize",
              cache->filename, cache->secpagesize, cache->snperbucket);
        okcache = ajFalse;
    }

    if(!okcache)
        return NULL;
        

    cache->fp = fopen(MAJSTRGETPTR(cache->filename),mode);

    if(!cache->fp)
	return NULL;


    if(ajCharMatchC(mode, "rb")) /* read-only */
    {
#if defined (usestat64)
	if(!stat64(MAJSTRGETPTR(cache->filename), &buf))
#else
        if(!stat(MAJSTRGETPTR(cache->filename), &buf))
#endif
            filelen = buf.st_size;

        cache->readonly = ajTrue;
    }
    else if(ajCharMatchC(mode, "rb+")) /* update */
    {
#if defined (usestat64)
	if(!stat64(MAJSTRGETPTR(cache->filename), &buf))
#else
        if(!stat(MAJSTRGETPTR(cache->filename), &buf))
#endif
            filelen = buf.st_size;
        if(compressed)
            douncompress = ajTrue;
    }
    else if(ajCharMatchC(mode, "wb+")) /* create */
    {
        writemode = ajTrue;
    }
    else
    {
        ajWarn("ajBtreeIdcacheNewC unknown mode '%s'", mode);
    }
    
    cache->totsize     = filelen;
    cache->filesize    = filelen;

    if(douncompress)
        btreeCacheUncompress(cache);

    /* create or lock the root page */

    if(writemode)
        btreePrirootCreate(cache);
    else
    {
        page = btreePricacheRead(cache,0L);
        page->dirty = BT_LOCK;
    }

    return cache;
}
ID ajBtreeIdcacheNewS
TY public
MO ajindex
LB core
XX
DE Open a b+tree index file and initialise a cache object
XX
PN [1]
PA r file const AjPStr
PD name of file
PX
PN [2]
PA r ext const AjPStr
PD extension of file
PX
PN [3]
PA r idir const AjPStr
PD index file directory
PX
PN [4]
PA r mode const char*
PD opening mode
PX
PN [5]
PA r compressed AjBool
PD Compressed index flag
PX
PN [6]
PA r refcount ajuint
PD Extra files for each entry
PX
PN [7]
PA r kwlimit ajuint
PD Max key size
PX
PN [8]
PA r pripagesize ajuint
PD Primary pagesize
PX
PN [9]
PA r secpagesize ajuint
PD Secondary pagesize
PX
PN [10]
PA r pricachesize ajuint
PD size of primary cache
PX
PN [11]
PA r seccachesize ajuint
PD size of secondary cache
PX
PN [12]
PA r pripagecount ajulong
PD Primary page count
PX
PN [13]
PA r secpagecount ajulong
PD Secondary page count
PX
PN [14]
PA r order ajuint
PD Tree order
PX
PN [15]
PA r fill ajuint
PD Number of entries per bucket
PX
PN [16]
PA r level ajuint
PD level of tree
PX
PN [17]
PA r sorder ajuint
PD order of secondary tree
PX
PN [18]
PA r sfill ajuint
PD Number of entries per secondary bucket
PX
PN [19]
PA r count ajulong
PD Number of entries in the index
PX
PN [20]
PA r countall ajulong
PD Number of total entries in the index
PX
RT AjPBtcache
RD initialised disc block cache structure
RX
//


AjPBtcache ajBtreeIdcacheNewS(const AjPStr file, const AjPStr ext,
                              const AjPStr idir, const char *mode,
                              AjBool compressed,
                              ajuint refcount,
                              ajuint kwlimit,
                              ajuint pripagesize, ajuint secpagesize,
                              ajuint pricachesize, ajuint seccachesize,
                              ajulong pripagecount, ajulong secpagecount,
                              ajuint order, ajuint fill, ajuint level,
                              ajuint sorder, ajuint sfill,
                              ajulong count, ajulong countall)
{
    return ajBtreeIdcacheNewC(MAJSTRGETPTR(file), MAJSTRGETPTR(ext),
                              MAJSTRGETPTR(idir), mode,
                              compressed,  refcount, kwlimit,
                              pripagesize, secpagesize,
                              pricachesize, seccachesize,
                              pripagecount, secpagecount,
                              order, fill, level,
                              sorder, sfill,
                              count, countall);
}
ID ajBtreeCacheNewReadC
TY public
MO ajindex
LB core
XX
DE Open an existing b+tree index file and initialise a cache object
XX
PN [1]
PA r filetxt const char*
PD name of file
PX
PN [2]
PA r exttxt const char*
PD extension of file
PX
PN [3]
PA r idirtxt const char*
PD index file directory
PX
RT AjPBtcache
RD initialised disc block cache structure
RX
//


AjPBtcache ajBtreeCacheNewReadC(const char *filetxt, const char *exttxt,
                                const char *idirtxt)
{
    AjPBtcache cache = NULL;

    ajuint pripagesize;
    ajuint pricachesize;
    ajuint secpagesize;
    ajuint seccachesize;
    ajuint order;
    ajuint sorder;
    ajuint fill;
    ajuint sfill;
    ajuint level;
    ajulong pripagecount;
    ajulong secpagecount;
    ajulong count;
    ajulong countall;
    ajuint kwlimit;
    ajuint idlimit;
    ajuint refcount;
    AjBool secondary = ajTrue;
    AjBool compressed = ajFalse;
    
/* first read the parameter file */
   
    if(!ajBtreeReadParamsC(filetxt, exttxt, idirtxt,
                           &secondary, &compressed,
                           &kwlimit, &idlimit, &refcount,
                           &pripagesize, &secpagesize,
                           &pricachesize, &seccachesize,
                           &pripagecount, &secpagecount,
                           &order, &fill, &level,
                           &sorder, &sfill,
                           &count, &countall))
    {
         return ajFalse;
    }

    if(secondary)
        cache = ajBtreeSeccacheNewC(filetxt, exttxt, idirtxt, "rb",
                                    compressed,
                                    kwlimit, idlimit,
                                    pripagesize, secpagesize,
                                    pricachesize, seccachesize,
                                    pripagecount, secpagecount,
                                    order, fill, level,
                                    sorder, sfill,
                                    count, countall);
    else
        cache = ajBtreeIdcacheNewC(filetxt, exttxt, idirtxt, "rb",
                                   compressed,
                                   kwlimit, refcount,
                                   pripagesize, secpagesize,
                                   pricachesize, seccachesize,
                                   pripagecount, secpagecount,
                                   order, fill, level,
                                   sorder, sfill,
                                   count, countall);

    if(cache)
        cache->secondary = secondary;
    
    return cache;
}
ID ajBtreeCacheNewReadS
TY public
MO ajindex
LB core
XX
DE Open an existing b+tree index file and initialise a cache object
XX
PN [1]
PA r file const AjPStr
PD name of file
PX
PN [2]
PA r ext const AjPStr
PD extension of file
PX
PN [3]
PA r idir const AjPStr
PD index file directory
PX
RT AjPBtcache
RD initialised disc block cache structure
RX
//


AjPBtcache ajBtreeCacheNewReadS(const AjPStr file, const AjPStr ext,
                                const AjPStr idir)
{
    return ajBtreeCacheNewReadC(MAJSTRGETPTR(file), MAJSTRGETPTR(ext),
                                MAJSTRGETPTR(idir));
}
ID ajBtreeCacheNewUpdateC
TY public
MO ajindex
LB core
XX
DE Open an existing b+tree index file for update and initialise a cache object
XX
PN [1]
PA r filetxt const char*
PD name of file
PX
PN [2]
PA r exttxt const char*
PD extension of file
PX
PN [3]
PA r idirtxt const char*
PD index file directory
PX
RT AjPBtcache
RD initialised disc block cache structure
RX
//


AjPBtcache ajBtreeCacheNewUpdateC(const char *filetxt, const char *exttxt,
                                  const char *idirtxt)
{
    AjPBtcache cache = NULL;

    ajuint pripagesize;
    ajuint secpagesize;
    ajuint pricachesize;
    ajuint seccachesize;
    ajuint order;
    ajuint sorder;
    ajuint fill;
    ajuint sfill;
    ajuint level;
    ajulong pripagecount;
    ajulong secpagecount;
    ajulong count;
    ajulong countall;
    ajuint kwlimit;
    ajuint idlimit;
    ajuint refcount;
    AjBool secondary = ajTrue;
    AjBool compressed = ajFalse;
    
/* first read the parameter file */
   
    if(!ajBtreeReadParamsC(filetxt, exttxt, idirtxt,
                           &secondary, &compressed,
                           &kwlimit, &idlimit, &refcount,
                           &pripagesize, &secpagesize,
                           &pricachesize, &seccachesize,
                           &pripagecount, &secpagecount,
                           &order, &fill, &level,
                           &sorder, &sfill,
                           &count, &countall))
    {
         return ajFalse;
    }

    if(secondary)
        cache = ajBtreeSeccacheNewC(filetxt, exttxt, idirtxt, "rb+",
                                    compressed, kwlimit, idlimit,
                                    pripagesize, secpagesize,
                                    pricachesize, seccachesize,
                                    pripagecount, secpagecount,
                                    order, fill, level,
                                    sorder, sfill,
                                    count, countall);
    else
        cache = ajBtreeIdcacheNewC(filetxt, exttxt, idirtxt, "rb+",
                                   compressed, kwlimit, refcount,
                                   pripagesize, secpagesize,
                                   pricachesize, seccachesize,
                                   pripagecount, secpagecount,
                                   order, fill, level,
                                   sorder, sfill,
                                   count, countall);

    if(cache)
        cache->secondary = secondary;
    
    return cache;
}
ID ajBtreeCacheNewUpdateS
TY public
MO ajindex
LB core
XX
DE Open an existing b+tree index file for update and initialise a cache object
XX
PN [1]
PA r file const AjPStr
PD name of file
PX
PN [2]
PA r ext const AjPStr
PD extension of file
PX
PN [3]
PA r idir const AjPStr
PD index file directory
PX
RT AjPBtcache
RD initialised disc block cache structure
RX
//


AjPBtcache ajBtreeCacheNewUpdateS(const AjPStr file, const AjPStr ext,
                                  const AjPStr idir)
{
    return ajBtreeCacheNewUpdateC(MAJSTRGETPTR(file), MAJSTRGETPTR(ext),
                                  MAJSTRGETPTR(idir));
}
ID btreePripageNew
TY static
MO ajindex
LB core
XX
DE Construct a cache page object
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT AjPBtpage
RD initialised disc block cache structure
RX
//


static AjPBtpage btreePripageNew(AjPBtcache cache)
{
    AjPBtpage thys = NULL;
    ajuint nodetype;

    /* ajDebug("In btreePripageNew\n"); */

    AJNEW0(thys);
    AJCNEW0(thys->buf,cache->pripagesize);
    nodetype     = BT_FREEPAGE;
    SBT_NODETYPE(thys->buf,nodetype);

    thys->next = NULL;
    thys->prev = NULL;
    
    ++cache->prilistLength;

    
    return thys;
}
ID btreeSecpageNew
TY static
MO ajindex
LB core
XX
DE Construct a cache secondary page object
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT AjPBtpage
RD initialised disc block cache structure
RX
//


static AjPBtpage btreeSecpageNew(AjPBtcache cache)
{
    AjPBtpage thys = NULL;
    ajuint nodetype;

    /* ajDebug("In btreeSecpageNew\n"); */

    AJNEW0(thys);
    AJCNEW0(thys->buf,cache->secpagesize);

    nodetype     = BT_SECFREEPAGE;
    SBT_NODETYPE(thys->buf,nodetype);

    thys->next = NULL;
    thys->prev = NULL;
    
    ++cache->seclistLength;

    
    return thys;
}
ID btreeIdbucketNew
TY static
MO ajindex
LB core
XX
DE Construct an id bucket object
XX
PN [1]
PA r n ajuint
PD Number of IDs
PX
PN [2]
PA r refcount ajuint
PD Number pf reference offsets
PX
RT AjPIdbucket
RD initialised id bucket
RX
//


static AjPIdbucket btreeIdbucketNew(ajuint n, ajuint refcount)
{
    AjPIdbucket bucket = NULL;
    ajuint i;

    /* ajDebug("In btreeIdbucketNew\n"); */
    
    if(n)
    {
        if(statSaveIdbucketNext)
        {
            bucket = statSaveIdbucket[--statSaveIdbucketNext];
            for(i=0;iMaxentries;++i)
                ajStrAssignClear(&bucket->Ids[i]->id);
            if(n > bucket->Maxentries)
            {
                AJCRESIZE0(bucket->keylen,bucket->Maxentries,n);
                AJCRESIZE0(bucket->Ids,bucket->Maxentries,n);
                for(i=bucket->Maxentries;iIds[i] = ajBtreeIdNew(refcount);
                bucket->Maxentries = n;
            }
        }
        else
        {
            AJNEW0(bucket);
            AJCNEW0(bucket->Ids,n);
            AJCNEW0(bucket->keylen,n);
            for(i=0;iIds[i] = ajBtreeIdNew(refcount);
            bucket->Maxentries = n;
            
        }
        
    }
    else 
    {
        if(statSaveIdbucketEmptyNext)
            bucket = statSaveIdbucketEmpty[--statSaveIdbucketEmptyNext];
        else
            AJNEW0(bucket);
    }

    bucket->NodeType = BT_IDBUCKET;
    bucket->Nentries = n;
    bucket->Overflow = 0L;
    
    return bucket;
}
ID btreePricacheLocate
TY static
MO ajindex
LB core
XX
DE Search for a page in the primary cache
XX
PN [1]
PA u cache AjPBtcache
PD cache structure
PX
PN [2]
PA r page ajulong
PD page number to locate
PX
RT AjPBtpage
RD pointer to page or NULL if not found
RX
//


static AjPBtpage btreePricacheLocate(AjPBtcache cache, ajulong page)
{
    AjPBtpage cpage = NULL;

    /*ajDebug("In btreePricacheLocate %Lu\n", page);*/
    

    cpage = ajTableFetchmodV(cache->pripagetable, (const void*) &page);

    if(!cpage)
        return NULL;

    cache->pricachehits++;

    if(!ajBtreePageIsPrimary(cpage))
        ajWarn("btreePricacheLocate secondary page %Lu '%s'",
               cpage->pagepos, btreeNodetype(cpage->buf));

    return cpage;
}
ID btreeSeccacheLocate
TY static
MO ajindex
LB core
XX
DE Search for a page in the secondary cache
XX
PN [1]
PA u cache AjPBtcache
PD cache structure
PX
PN [2]
PA r page ajulong
PD page number to locate
PX
RT AjPBtpage
RD pointer to page or NULL if not found
RX
//


static AjPBtpage btreeSeccacheLocate(AjPBtcache cache, ajulong page)
{
    AjPBtpage cpage = NULL;

    /*ajDebug("In btreeSeccacheLocate %Lu\n", page);*/
    

    cpage = ajTableFetchmodV(cache->secpagetable, (const void*) &page);

    if(!cpage)
        return NULL;

    cache->seccachehits++;

    if(ajBtreePageIsPrimary(cpage))
        ajWarn("btreeSeccacheLocate primary page %Lu '%s'",
               cpage->pagepos, btreeNodetype(cpage->buf));

    return cpage;
}
ID btreePricacheUnlink
TY static
MO ajindex
LB core
XX
DE Remove links to a primary cache page and return the address of the page
XX
PN [1]
PA w cache AjPBtcache
PD cache structure
PX
PN [2]
PA u cpage AjPBtpage
PD cache page
PX
RT void
RD 
RX
//


static void btreePricacheUnlink(AjPBtcache cache, AjPBtpage cpage)
{
    /* ajDebug("In btreePricacheUnlink\n"); */

    if(!ajBtreePageIsPrimary(cpage))
        ajWarn("btreePricacheUnlink secondary page %Lu '%s'",
               cpage->pagepos, btreeNodetype(cpage->buf));

    ajTableRemove(cache->pripagetable, (void*) &cpage->pagepos);

    if(cache->pmru == cpage)
    {
	cache->pmru = cpage->prev;

	if(cpage->prev)
	    cpage->prev->next = NULL;

	if(cache->plru == cpage)
	    cache->plru = NULL;
    }
    else if(cache->plru == cpage)
    {
	cache->plru = cpage->next;

	if(cpage->next)
	    cpage->next->prev = NULL;
    }
    else
    {
	cpage->prev->next = cpage->next;
	cpage->next->prev = cpage->prev;
    }

    return;
}
ID btreeSeccacheUnlink
TY static
MO ajindex
LB core
XX
DE Remove links to a secondary cache page and return the address of the page
XX
PN [1]
PA w cache AjPBtcache
PD cache structure
PX
PN [2]
PA u cpage AjPBtpage
PD cache page
PX
RT void
RD 
RX
//


static void btreeSeccacheUnlink(AjPBtcache cache, AjPBtpage cpage)
{
    /* ajDebug("In btreeSeccacheUnlink\n"); */

    if(ajBtreePageIsPrimary(cpage))
        ajWarn("btreeSeccacheUnlink primary page %Lu '%s'",
               cpage->pagepos, btreeNodetype(cpage->buf));

    ajTableRemove(cache->secpagetable, (void*) &cpage->pagepos);

    if(cache->smru == cpage)
    {
	cache->smru = cpage->prev;

	if(cpage->prev)
	    cpage->prev->next = NULL;

	if(cache->slru == cpage)
	    cache->slru = NULL;
    }
    else if(cache->slru == cpage)
    {
	cache->slru = cpage->next;

	if(cpage->next)
	    cpage->next->prev = NULL;
    }
    else
    {
	cpage->prev->next = cpage->next;
	cpage->next->prev = cpage->prev;
    }

    return;
}
ID btreePricacheMruAdd
TY static
MO ajindex
LB core
XX
DE Insert a primary cache page at the mru position
XX
PN [1]
PA w cache AjPBtcache
PD cache structure
PX
PN [2]
PA u cpage AjPBtpage
PD cache page
PX
RT void
RD 
RX
//


static void btreePricacheMruAdd(AjPBtcache cache, AjPBtpage cpage)
{

    /* ajDebug("In btreeCacheMruAdd\n"); */

    if(!ajBtreePageIsPrimary(cpage))
        ajWarn("btreePricacheMruAdd secondary page %Lu '%s'",
               cpage->pagepos, btreeNodetype(cpage->buf));

    ajTablePut(cache->pripagetable,
               (void*) &cpage->pagepos, (void*) cpage);

    cpage->prev = cache->pmru;
    cpage->next = NULL;

    if(cache->pmru)
	cache->pmru->next = cpage;

    if(!cache->plru)
	cache->plru = cpage;

    cache->pmru = cpage;

    return;
}
ID btreeSeccacheMruAdd
TY static
MO ajindex
LB core
XX
DE Insert a secondary cache page at the mru position
XX
PN [1]
PA w cache AjPBtcache
PD cache structure
PX
PN [2]
PA u cpage AjPBtpage
PD cache page
PX
RT void
RD 
RX
//


static void btreeSeccacheMruAdd(AjPBtcache cache, AjPBtpage cpage)
{

    /* ajDebug("In btreeSeccacheMruAdd\n"); */

    if(ajBtreePageIsPrimary(cpage))
        ajWarn("btreeSeccacheMruAdd primary page %Lu '%s'",
               cpage->pagepos, btreeNodetype(cpage->buf));

    ajTablePut(cache->secpagetable,
               (void*) &cpage->pagepos, (void*) cpage);

    cpage->prev = cache->smru;
    cpage->next = NULL;

    if(cache->smru)
	cache->smru->next = cpage;

    if(!cache->slru)
	cache->slru = cpage;

    cache->smru = cpage;

    return;
}
ID btreePricacheLruUnlink
TY static
MO ajindex
LB core
XX
DE Remove links to an LRU primary cache page
XX
PN [1]
PA w cache AjPBtcache
PD cache structure
PX
RT AjPBtpage
RD pointer to unlinked cache page
RX
//


static AjPBtpage btreePricacheLruUnlink(AjPBtcache cache)
{
    AjPBtpage ret;

    /* ajDebug("In btreePricacheLruUnlink\n"); */

    if(cache->plru->dirty != BT_LOCK)
    {
	if(!cache->plru)
	    ajFatal("btreePricacheLruUnlink: No pages nodes found in cache %S",
                    cache->filename);

	ret = cache->plru;
	ret->next->prev = NULL;
	cache->plru = ret->next;
        ajTableRemove(cache->pripagetable, (void*) &ret->pagepos);

        if(!ajBtreePageIsPrimary(ret))
            ajWarn("btreePricacheLruUnlink secondary page %Lu '%s'",
                   ret->pagepos, btreeNodetype(ret->buf));

        return ret;
    }
    
    for(ret=cache->plru; ret; ret=ret->next)
	if(ret->dirty != BT_LOCK)
	    break;

    if(!ret)
	ajFatal("Too many locked cache page in primary cache %S. "
                "Try increasing cachesize (%u)",
                cache->filename, cache->pricachesize);

    btreePricacheUnlink(cache,ret);
    
    return ret;
}
ID btreeSeccacheLruUnlink
TY static
MO ajindex
LB core
XX
DE Remove links to an LRU secondary cache page
XX
PN [1]
PA w cache AjPBtcache
PD cache structure
PX
RT AjPBtpage
RD pointer to unlinked cache page
RX
//


static AjPBtpage btreeSeccacheLruUnlink(AjPBtcache cache)
{
    AjPBtpage ret;

    /* ajDebug("In btreeSeccacheLruUnlink\n"); */

    if(cache->slru->dirty != BT_LOCK)
    {
	if(!cache->slru)
	    ajFatal("btreeSeccacheLruUnlink: No pages nodes found in cache %S",
                    cache->filename);

	ret = cache->slru;
	ret->next->prev = NULL;
	cache->slru = ret->next;

        ajTableRemove(cache->secpagetable, (void*) &ret->pagepos);

        if(ajBtreePageIsPrimary(ret))
            ajWarn("btreeSeccacheLruUnlink primary page %Lu '%s'",
                   ret->pagepos, btreeNodetype(ret->buf));

        return ret;
    }
    
    for(ret=cache->slru; ret; ret=ret->next)
	if(ret->dirty != BT_LOCK)
	    break;

    if(!ret)
	ajFatal("Too many locked cache page in secondary cache %S. "
                "Try increasing cachesize (%u)",
                cache->filename, cache->seccachesize);

    btreeSeccacheUnlink(cache,ret);
    
    return ret;
}
ID btreePricacheDestage
TY static
MO ajindex
LB core
XX
DE Destage a primary cache page
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u cpage AjPBtpage
PD cache page
PX
RT void
RD 
RX
//


static void btreePricacheDestage(AjPBtcache cache, AjPBtpage cpage)
{
    ajuint written = 0;
    ajuint retries = 0;

    /* ajDebug("In btreePricacheDestage\n");*/

    if(!ajBtreePageIsPrimary(cpage))
        ajWarn("btreePricacheDestage secondary page %Lu '%s'",
               cpage->pagepos, btreeNodetype(cpage->buf));

    if(fseek(cache->fp, cpage->pagepos,SEEK_SET))
    {
        ajWarn("fseek %Lu failed for cache '%S' %d: '%s'",
               cpage->pagepos, cache->filename,
               ferror(cache->fp), strerror(ferror(cache->fp)));
	fseek(cache->fp,0L,SEEK_END);
    }

    while(written != cache->pripagesize && retries != BT_MAXRETRIES)
    {
	written += fwrite((void *)(cpage->buf+written),1,
                          cache->pripagesize-written,
			  cache->fp);
	++retries;
    }

    if(retries == BT_MAXRETRIES)
	ajFatal("Maximum retries (%u) reached in btreePricacheDestage "
                "cache %S error: '%s'",
		BT_MAXRETRIES, cache->filename, strerror(ferror(cache->fp)));

    cpage->dirty = BT_CLEAN;
    
    cache->priwrites++;

    return;
}
ID btreeSeccacheDestage
TY static
MO ajindex
LB core
XX
DE Destage a secondary cache page
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u cpage AjPBtpage
PD cache page
PX
RT void
RD 
RX
//


static void btreeSeccacheDestage(AjPBtcache cache, AjPBtpage cpage)
{
    ajuint written = 0;
    ajuint retries = 0;

    /* ajDebug("In btreeSeccacheDestage\n");*/

    if(ajBtreePageIsPrimary(cpage))
        ajWarn("btreeSeccacheDestage primary page %Lu '%s'",
               cpage->pagepos, btreeNodetype(cpage->buf));

    if(fseek(cache->fp, cpage->pagepos,SEEK_SET))
    {
        ajWarn("fseek %Lu failed for cache '%S' %d: '%s'",
               cpage->pagepos, cache->filename,
               ferror(cache->fp), strerror(ferror(cache->fp)));
	fseek(cache->fp,0L,SEEK_END);
    }

    while(written != cache->secpagesize && retries != BT_MAXRETRIES)
    {
	written += fwrite((void *)(cpage->buf+written),1,
                          cache->secpagesize-written,
			  cache->fp);
	++retries;
    }

    if(retries == BT_MAXRETRIES)
	ajFatal("Maximum retries (%u) reached in btreeSeccacheDestage "
                "cache %S error: '%s'",
		BT_MAXRETRIES, cache->filename, strerror(ferror(cache->fp)));

    cpage->dirty = BT_CLEAN;
    
    cache->secwrites++;

    return;
}
ID btreeCacheFetchSize
TY static
MO ajindex
LB core
XX
DE Fetch a cache page from disc
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA w cpage AjPBtpage
PD cache page
PX
PN [3]
PA r pagepos ajulong
PD page number
PX
PN [4]
PA r pagesize ajuint
PD page size
PX
RT void
RD 
RX
//


static void btreeCacheFetchSize(AjPBtcache cache, AjPBtpage cpage,
                                ajulong pagepos, ajuint pagesize)
{
    ajuint sum = 0;
    ajuint retries = 0;

    /* ajDebug("In btreeCacheFetchSize\n"); */

    if(fseek(cache->fp,pagepos,SEEK_SET))
	ajFatal("Seek error %d: '%s' in ajBtreeCacheFetchSize file %S",
                ferror(cache->fp),
                strerror(ferror(cache->fp)),
                cache->filename);

    while(sum != pagesize && retries != BT_MAXRETRIES)
    {
	sum += fread((void *)(cpage->buf+sum),1,pagesize-sum,
		     cache->fp);

	++retries;
    }
    
    if(retries == BT_MAXRETRIES)
	ajFatal("Maximum retries (%u) reached in btreeCacheFetchSize "
                "for page %Lu cache '%S'",
		BT_MAXRETRIES,pagepos, cache->filename);

    cpage->pagepos = pagepos;
    
    return;
}
ID btreePricacheFetch
TY static
MO ajindex
LB core
XX
DE Fetch a cache primary page from disc
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA w cpage AjPBtpage
PD cache page
PX
PN [3]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreePricacheFetch(AjPBtcache cache, AjPBtpage cpage,
                               ajulong pagepos)
{
    ajuint sum = 0;
    ajuint retries = 0;

    /* ajDebug("In btreePricacheFetch\n"); */

    if(fseek(cache->fp,pagepos,SEEK_SET))
	ajFatal("Seek error %d: '%s' in ajBtreePricacheFetch file %S",
                ferror(cache->fp),
                strerror(ferror(cache->fp)),
                cache->filename);

    while(sum != cache->pripagesize && retries != BT_MAXRETRIES)
    {
	sum += fread((void *)(cpage->buf+sum),1,cache->pripagesize-sum,
		     cache->fp);

	++retries;
    }
    
    if(retries == BT_MAXRETRIES)
	ajFatal("Maximum retries (%u) reached in btreePricacheFetch "
                "for page %Lu cache '%S'",
		BT_MAXRETRIES,pagepos, cache->filename);

    cpage->pagepos = pagepos;
    cache->prireads++;
    
    return;
}
ID btreeSeccacheFetch
TY static
MO ajindex
LB core
XX
DE Fetch a cache secondary page from disc
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA w cpage AjPBtpage
PD cache page
PX
PN [3]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreeSeccacheFetch(AjPBtcache cache, AjPBtpage cpage,
                               ajulong pagepos)
{
    ajuint sum = 0;
    ajuint retries = 0;

    /* ajDebug("In btreeSeccacheFetch\n"); */

    if(fseek(cache->fp,pagepos,SEEK_SET))
	ajFatal("Seek error %d: '%s' in ajBtreeSeccacheFetch file %S",
                ferror(cache->fp),
                strerror(ferror(cache->fp)),
                cache->filename);

    while(sum != cache->secpagesize && retries != BT_MAXRETRIES)
    {
	sum += fread((void *)(cpage->buf+sum),1,cache->secpagesize-sum,
		     cache->fp);

	++retries;
    }
    
    if(retries == BT_MAXRETRIES)
	ajFatal("Maximum retries (%u) reached in btreeSeccacheFetch "
                "for page %Lu cache '%S'",
		BT_MAXRETRIES,pagepos, cache->filename);

    cpage->pagepos = pagepos;
    cache->secreads++;
    
    return;
}
ID btreeCacheWriteCompress
TY static
MO ajindex
LB core
XX
DE Write a compressed cache page
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u cpage AjPBtpage
PD cache page
PX
PN [3]
PA r pagepos ajulong
PD Cache file position
PX
PN [4]
PA r pagesize ajuint
PD Compressed page size
PX
RT void
RD 
RX
//


static void btreeCacheWriteCompress(AjPBtcache cache, AjPBtpage cpage,
                                    ajulong pagepos, ajuint pagesize)
{
    ajuint written = 0;
    ajuint retries = 0;

    /* ajDebug("In btreeCacheWriteCompress\n");*/

    if(fseek(cache->fp, pagepos,SEEK_SET))
    {
        ajWarn("fseek %Lu failed for cache '%S' %d: '%s'",
               pagepos, cache->filename, errno, strerror(errno));
	fseek(cache->fp,0L,SEEK_END);
    }

    while(written != pagesize && retries != BT_MAXRETRIES)
    {
	written += fwrite((void *)(cpage->buf+written),1,pagesize-written,
			  cache->fp);
	++retries;
    }

    if(retries == BT_MAXRETRIES)
	ajFatal("Maximum retries (%u) reached in btreeCacheWriteCompress "
                "cache %S error: '%s'",
		BT_MAXRETRIES, cache->filename, strerror(ferror(cache->fp)));

    return;
}
ID btreeCacheWriteUncompress
TY static
MO ajindex
LB core
XX
DE Rewrite an uncompressed cache page
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u cpage AjPBtpage
PD cache page
PX
PN [3]
PA r pagepos ajulong
PD Cache file position
PX
PN [4]
PA r pagesize ajuint
PD Page size
PX
RT void
RD 
RX
//


static void btreeCacheWriteUncompress(AjPBtcache cache, AjPBtpage cpage,
                                      ajulong pagepos, ajuint pagesize)
{
    ajuint written = 0;
    ajuint retries = 0;
    
    /* ajDebug("In btreeCacheWriteCompress\n");*/

    if(fseek(cache->fp, pagepos,SEEK_SET))
    {
        ajWarn("fseek %Lu failed for cache '%S' %d: '%s'",
               pagepos, cache->filename, errno, strerror(errno));
	fseek(cache->fp,0L,SEEK_END);
    }

    while(written != pagesize && retries != BT_MAXRETRIES)
    {
	written += fwrite((void *)(cpage->buf+written),1,pagesize-written,
			  cache->fp);
	++retries;
    }

    if(retries == BT_MAXRETRIES)
	ajFatal("Maximum retries (%u) reached in btreeCacheWriteUncompress "
                "cache %S error: '%s'",
		BT_MAXRETRIES, cache->filename, strerror(ferror(cache->fp)));

    return;
}
ID btreeCacheCompress
TY static
MO ajindex
LB core
XX
DE Compress a b+tree cache on closing
XX
PN [1]
PA u thys AjPBtcache
PD Expanded cache to be compressed
PX
RT ajulong
RD File size after compression
RX
//


static ajulong btreeCacheCompress(AjPBtcache thys)
{
    AjPTable  newpostable;
    ajulong  *oldpagepos = NULL;
    ajulong  *newpagepos = NULL;
    ajuint    oldpagesize;
    ajuint    newpagesize;
    AjPBtpage page = NULL;
    ajuint    freepages = 0;
    ajulong   oldpos = 0L;
    ajulong   newpos = 0L;
    ajulong   newsize = 0L;

    ajuint   *newpagesizes = NULL;
    ajulong   i;
    ajuint    maxpagesize = 0;
    ajuint    minpagesize = 0;

    ajulong   pagecount = thys->pripagecount + thys->secpagecount;

    if(thys->secpagesize > thys->pripagesize)
    {
        page = btreeSecpageNew(thys);
        maxpagesize = thys->secpagesize;
        minpagesize = thys->pripagesize;
    }
    else
    {
        page = btreePripageNew(thys);
        maxpagesize = thys->pripagesize;
        minpagesize = thys->secpagesize;
    }

    AJCNEW(oldpagepos, pagecount);
    AJCNEW(newpagepos, pagecount);
    AJCNEW(newpagesizes, pagecount);

    newpostable = ajTableulongNewConst((ajint) pagecount);

    oldpos = 0L;
    newpos = 0L;
    newsize = 0L;

    for(i=0; i < pagecount; i++)
    {
        btreeCacheFetchSize(thys, page, oldpos, minpagesize);

        if(ajBtreePageIsPrimary(page))
            oldpagesize = thys->pripagesize;
        else
            oldpagesize = thys->secpagesize;

        btreeCacheFetchSize(thys, page, oldpos, oldpagesize);

        newpagesize = ajBtreePageGetSize(page, thys->refcount);

        oldpagepos[i] = oldpos;
        newpagepos[i] = newpos;
        newpagesizes[i] = newpagesize;

        ajTablePut(newpostable, (void*) &oldpagepos[i],
                   (void*) &newpagepos[i]);

        oldpos += oldpagesize;
        newpos += newpagesize;
    }

    newsize = newpos;
    newsize += maxpagesize - newpagesizes[pagecount-1];
    newpagesizes[pagecount-1] = maxpagesize;

    oldpos = 0L;

    for(i=0; i < pagecount; i++)
    {
        btreeCacheFetchSize(thys, page, oldpos, minpagesize);
        if(ajBtreePageIsPrimary(page))
            oldpagesize = thys->pripagesize;
        else
            oldpagesize = thys->secpagesize;

        btreeCacheFetchSize(thys, page, oldpos, oldpagesize);

        oldpos += oldpagesize;

        if(!btreePageCompress(page, newpostable, thys->refcount))
        {
            freepages++;
            continue;
        }

        btreeCacheWriteCompress(thys, page, newpagepos[i], newpagesizes[i]);
    }

    thys->filesize = newsize;

    AJFREE(oldpagepos);
    AJFREE(newpagepos);
    AJFREE(newpagesizes);

    ajTableFree(&newpostable);

    AJFREE(page->buf);
    AJFREE(page);

    return newsize;
}
ID btreeCacheUncompress
TY static
MO ajindex
LB core
XX
DE Uncompress a b+tree cache on opening
XX
PN [1]
PA u thys AjPBtcache
PD Compressed cache to be expanded
PX
RT ajulong
RD File size compressed
RX
//


static ajulong btreeCacheUncompress(AjPBtcache thys)
{
    AjPTable  newpostable;
    ajulong  *oldpagepos = NULL;
    ajulong  *newpagepos = NULL;
    ajuint    oldpagesize;
    ajuint    newpagesize;
    AjPBtpage page = NULL;
    ajuint    freepages = 0;
    ajulong   newsize = 0L;
    ajulong   oldpos = 0L;
    ajulong   newpos = 0L;

    ajulong   i;
    ajuint    icnt;
    ajulong   writepos;
    ajulong   oldsize;
    ajuint    pagesize;
    ajuint    writepagesize;
    AjBool    isprimary;

    ajulong   pagecount = thys->pripagecount + thys->secpagecount;

    ajDebug("btreeCacheUncompress %S size: %Lu pages: %Lu + %Lu\n",
            thys->filename, thys->totsize,
            thys->pripagecount, thys->secpagecount);

    if(thys->secpagesize > thys->pripagesize)
    {
        page = btreeSecpageNew(thys);
        pagesize = thys->secpagesize;
    }
    else
    {
        page = btreePripageNew(thys);
        pagesize = thys->pripagesize;
    }

    oldsize = thys->totsize;

    AJCNEW0(oldpagepos, pagecount);
    AJCNEW0(newpagepos, pagecount);
    newpostable = ajTableulongNewConst((ajint) pagecount);

    oldpos = 0L;
    newpos = 0L;

    for(i=0; i < pagecount; i++)
    {
        btreeCacheFetchSize(thys, page, oldpos, pagesize);
        oldpagesize = ajBtreePageGetSize(page, thys->refcount);
        if(ajBtreePageIsPrimary(page))
            newpagesize = thys->pripagesize;
        else
            newpagesize = thys->secpagesize;

        oldpagepos[i] = oldpos;
        newpagepos[i] = newpos;

        ajTablePut(newpostable, (void*) &oldpagepos[i],
                   (void*) &newpagepos[i]);

        oldpos += oldpagesize;
        newpos += newpagesize;
    }

    thys->totsize = (ajulong) (thys->pripagesize * thys->pripagecount) +
        (ajulong) (thys->secpagesize * thys->secpagecount);

    writepos = thys->totsize;

    oldpos = 0L;
    newpos = 0L;

    for(i=pagecount; i > 0; i--)
    {
        btreeCacheFetchSize(thys, page, oldpagepos[i-1], pagesize);

        if(!btreePageUncompress(page, newpostable,
                                thys->refcount))
        {
            freepages++;
            continue;
        }

        oldpagesize = ajBtreePageGetSize(page, thys->refcount);

        isprimary = ajBtreePageIsPrimary(page);
        if(isprimary)
            writepagesize = thys->pripagesize;
        else
            writepagesize = thys->secpagesize;

        icnt=writepagesize - oldpagesize;
        if(icnt)
            memset(page->buf+oldpagesize, 0, icnt);
        
        if(writepos < writepagesize)
            ajErr("in btreeCacheWriteUncompress writepos %Lu writepagesize %u",
                  writepos, writepagesize);

        writepos -= writepagesize;

        btreeCacheWriteUncompress(thys, page,
                                  writepos, writepagesize);

    }

    thys->filesize = newsize;

    AJFREE(oldpagepos);
    AJFREE(newpagepos);

    ajTableFree(&newpostable);

    AJFREE(page->buf);
    AJFREE(page);

    return oldsize;
}
ID ajBtreeCacheDel
TY public
MO ajindex
LB core
XX
DE Close a b+tree cache
XX
PN [1]
PA w Pthis AjPBtcache*
PD Cache object
PX
RT ajulong
RD Index file size
RX
//


ajulong ajBtreeCacheDel(AjPBtcache *Pthis)
{
    AjPBtcache thys;
    AjPBtpage  page  = NULL;
    AjPBtpage  temp  = NULL;
    ajulong ret = 0L;

    if(!Pthis || !*Pthis) return ret;
    thys = *Pthis;

    /* ajDebug("In ajBtreeCacheDel\n"); */

    /* clear the dirty / locked pages */

    if(!thys->readonly)
        btreeCacheSync(thys, 0L);

    if(thys->bmem)
        btreeFreePriArray(thys);
    if(thys->bsmem)
        btreeFreeSecArray(thys);

    /* tidy up clean (read not written or locked) cached pages */

    for(page=thys->plru;page;page=temp)
    {
	temp = page->next;
        ajTableRemove(thys->pripagetable, (void*) &page->pagepos);
	AJFREE(page->buf);
	AJFREE(page);
    }

    for(page=thys->slru;page;page=temp)
    {
	temp = page->next;
        ajTableRemove(thys->secpagetable, (void*) &page->pagepos);
	AJFREE(page->buf);
	AJFREE(page);
    }

    if(!thys->readonly)
    {
        /* Ubuntu warns if ftruncate return is not used */
        if(ftruncate(fileno(thys->fp), thys->totsize) == -1)
        {
        }

        if(thys->compressed)
        {
            ret = btreeCacheCompress(thys);
            /* Ubuntu warns if ftruncate return is not used */
            if(ftruncate(fileno(thys->fp), ret) == -1)
            {
            }
        }
    }
    else
        ret = thys->totsize;

    ajStrDel(&thys->filename);
    ajStrDel(&thys->replace);

    fclose(thys->fp);

    ajTableFree(&thys->pripagetable);
    ajTableFree(&thys->secpagetable);

    AJFREE(*Pthis);
    *Pthis = NULL;
    
    return ret;
}
ID ajBtreeCacheIsCompressed
TY public
MO ajindex
LB core
XX
DE Returns true if the index is compressed
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
RT AjBool
RD True if index is compressed
RX
//


AjBool ajBtreeCacheIsCompressed(const AjPBtcache cache)
{
    return cache->compressed;
}
ID ajBtreeCacheIsSecondary
TY public
MO ajindex
LB core
XX
DE Test whether a cache is a secondary index
XX
PN [1]
PA r thys const AjPBtcache
PD Cache object
PX
RT AjBool
RD True if cache is secondary
RX
//


AjBool ajBtreeCacheIsSecondary(const AjPBtcache thys)
{
    return thys->secondary;
}
ID ajBtreeCacheStatsOut
TY public
MO ajindex
LB core
XX
DE Cache statistics for writing a new index
XX
PN [1]
PA u outf AjPFile
PD output file file
PX
PN [2]
PA r cache const AjPBtcache
PD cache object
PX
PN [3]
PA u Ppricache ajulong*
PD Number of cache reads to date
PX
PN [4]
PA u Pseccache ajulong*
PD Number of cache reads to date
PX
PN [5]
PA u Pprireads ajulong*
PD Number of disk reads to date
PX
PN [6]
PA u Psecreads ajulong*
PD Number of disk reads to date
PX
PN [7]
PA u Ppriwrites ajulong*
PD Number of disk writes to date
PX
PN [8]
PA u Psecwrites ajulong*
PD Number of disk writes to date
PX
PN [9]
PA u Pprisize ajulong*
PD Number of primary cache pages to date
PX
PN [10]
PA u Psecsize ajulong*
PD Number of secondary cache pages to date
PX
RT void
RD 
RX
//


void ajBtreeCacheStatsOut(AjPFile outf, const AjPBtcache cache,
                          ajulong* Ppricache, ajulong* Pseccache,
                          ajulong* Pprireads, ajulong* Psecreads,
                          ajulong* Ppriwrites, ajulong* Psecwrites,
                          ajulong* Pprisize, ajulong* Psecsize)
{
    ajulong lpriread;
    ajulong lsecread;
    ajulong lpricache;
    ajulong lseccache;
    ajulong lpriwrite;
    ajulong lsecwrite;
    ajulong lprinew;
    ajulong lsecnew;
    ajulong lpripages;
    ajulong lsecpages;

    ajulong lprirewrite = 0;
    ajulong lsecrewrite = 0;

    if(!cache) return;

    lpripages = cache->pripagecount;
    lsecpages = cache->secpagecount;
    lpricache = cache->pricachehits - *Ppricache;
    lseccache = cache->seccachehits - *Pseccache;
    lpriread = cache->prireads - *Pprireads;
    lsecread = cache->secreads - *Psecreads;
    lpriwrite = cache->priwrites - *Ppriwrites;
    lsecwrite = cache->secwrites - *Psecwrites;

    if(lpripages > *Pprisize)
        lprinew = lpripages - *Pprisize;
    else
        lprinew = 0L;

    if(lsecpages > *Psecsize)
        lsecnew = lsecpages - *Psecsize;
    else
        lsecnew = 0L;

    /*
    ** writes counts those committed to disk
    ** newpages counts growth in index size
    ** pending writes may give a negative count for rewrites
    */
    if(lpriwrite >= lprinew)
        lprirewrite = (ajlong)lpriwrite - (ajlong)lprinew;
    if(lsecwrite >= lsecnew)
        lsecrewrite = (ajlong)lsecwrite - (ajlong)lsecnew;

    ajFmtPrintF(outf,
                "ajBtreeCacheStatsOut '%S' cached: %8Lu/%-8Lu "
                "reads: %8Lu/%-8Lu writes: %8Lu/%-8Lu "
                "rewrite: %8Lu/%-8Lu pages: %8Lu/%-8Lu\n",
                cache->filename,  lpricache,  lseccache,
                lpriread, lsecread, lpriwrite, lsecwrite,
                lprirewrite, lsecrewrite, lprinew, lsecnew);

    *Ppricache = cache->pricachehits;
    *Pseccache = cache->seccachehits;
    *Pprireads = cache->prireads;
    *Psecreads = cache->secreads;
    *Ppriwrites = cache->priwrites;
    *Psecwrites = cache->secwrites;
    *Pprisize = lpripages;
    *Psecsize = lsecpages;

    return;
}
ID btreePricacheControl
TY static
MO ajindex
LB core
XX
DE Master control function for primary cache read/write
XX
PN [1]
PA w cache AjPBtcache
PD name of file
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA r isread AjBool
PD is this a read operation?
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


static AjPBtpage btreePricacheControl(AjPBtcache cache, ajulong pagepos,
                                      AjBool isread)
{
    AjPBtpage ret      = NULL;

    /* ajDebug("In btreePricacheControl\n"); */

    if(pagepos < cache->totsize)
        ret = btreePricacheLocate(cache,pagepos);

    if(ret)
    {
	btreePricacheUnlink(cache,ret);
    }
    else
    {
	if(cache->prilistLength == cache->pricachesize)
	{
	    ret = btreePricacheLruUnlink(cache);

	    if(ret->dirty == BT_DIRTY)
		btreePricacheDestage(cache,ret);

	    if(isread || pagepos!=cache->totsize)
		btreePricacheFetch(cache,ret,pagepos);
            else
                btreePripageClear(cache, ret);
	}
	else
	{
	    ret = btreePripageNew(cache);

	    if(isread || pagepos!=cache->totsize)
		btreePricacheFetch(cache,ret,pagepos);
	}

	if(!isread)
	    ret->pagepos = pagepos;
	else
	    ret->dirty = BT_CLEAN;
    }

    btreePricacheMruAdd(cache,ret);

    if(!ajBtreePageIsPrimary(ret))
        ajWarn("btreePricacheControl secondary page %Lu '%s'",
               ret->pagepos, btreeNodetype(ret->buf));

    return ret;
}
ID btreeSeccacheControl
TY static
MO ajindex
LB core
XX
DE Master control function for secondary cache read/write
XX
PN [1]
PA w cache AjPBtcache
PD name of file
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA r isread AjBool
PD is this a read operation?
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


static AjPBtpage btreeSeccacheControl(AjPBtcache cache, ajulong pagepos,
                                      AjBool isread)
{
    AjPBtpage ret      = NULL;

    /* ajDebug("In btreeSeccacheControl\n"); */

    if(pagepos < cache->totsize)
        ret = btreeSeccacheLocate(cache,pagepos);

    if(ret)
    {
	btreeSeccacheUnlink(cache,ret);
    }
    else
    {
	if(cache->seclistLength == cache->seccachesize)
	{
	    ret = btreeSeccacheLruUnlink(cache);

	    if(ret->dirty == BT_DIRTY)
		btreeSeccacheDestage(cache,ret);

	    if(isread || pagepos!=cache->totsize)
		btreeSeccacheFetch(cache,ret,pagepos);
            else
                btreeSecpageClear(cache, ret);
	}
	else
	{
	    ret = btreeSecpageNew(cache);

	    if(isread || pagepos!=cache->totsize)
		btreeSeccacheFetch(cache,ret,pagepos);
	}

	if(!isread)
	    ret->pagepos = pagepos;
	else
	    ret->dirty = BT_CLEAN;
    }

    btreeSeccacheMruAdd(cache,ret);

    if(ajBtreePageIsPrimary(ret))
        ajWarn("btreeSeccacheControl primary page %Lu '%s'",
               ret->pagepos, btreeNodetype(ret->buf));

    return ret;
}
ID ajBtreeCacheRead
TY public
MO ajindex
LB core
XX
DE Get a pointer to a disc cache page
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


AjPBtpage ajBtreeCacheRead(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage ret = NULL;

    if(!btreeTestpage)
    {
        if(cache->secpagesize > cache->pripagesize)
            btreeTestpage = btreeSecpageNew(cache);
        else        
            btreeTestpage = btreePripageNew(cache);
    }
    
    /* ajDebug("In ajBtreeCacheRead\n"); */

    btreeNocacheFetch(cache, btreeTestpage, pagepos);

    if(ajBtreePageIsPrimary(btreeTestpage))
        ret = btreePricacheControl(cache,pagepos,BT_READ);
    else
        ret = btreeSeccacheControl(cache,pagepos,BT_READ);

    return ret;
}
ID btreePricacheRead
TY static
MO ajindex
LB core
XX
DE Get a pointer to a disc primary cache page
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


static AjPBtpage btreePricacheRead(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage ret = NULL;

    /* ajDebug("In btreePricacheRead\n"); */

    ret = btreePricacheControl(cache,pagepos,BT_READ);

    return ret;
}
ID btreeSeccacheRead
TY static
MO ajindex
LB core
XX
DE Get a pointer to a disc secondary cache page
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


static AjPBtpage btreeSeccacheRead(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage ret = NULL;

    /* ajDebug("In btreeSeccacheRead\n"); */

    ret = btreeSeccacheControl(cache,pagepos,BT_READ);

    return ret;
}
ID btreeCacheSync
TY static
MO ajindex
LB core
XX
DE Sync all dirty cache pages
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r rootpage ajulong
PD root page
PX
RT void
RD 
RX
//


static void btreeCacheSync(AjPBtcache cache, ajulong rootpage)
{
    AjPBtpage page = NULL;
    ajuint numlocked = 0;
    ajuint numwrite = 0;

    /* ajDebug("In btreeCacheSync\n");*/

    if(!cache->pmru && !cache->smru) /* cache unused*/
        return;

    statCallSync++;

    if(cache->pmru)
    {
        for(page=cache->plru;page;page=page->next)
        {
            if(page->dirty)         /* BT_LOCK or BT_DIRTY */
            {
                numwrite++;
                if(page->dirty == BT_LOCK)
                    numlocked++;
                btreePricacheDestage(cache,page);
            }
        }
    }

    if(cache->smru)
    {
        for(page=cache->slru;page;page=page->next)
        {
            if(page->dirty)         /* BT_LOCK or BT_DIRTY */
            {
                numwrite++;
                if(page->dirty == BT_LOCK)
                    numlocked++;
                btreeSeccacheDestage(cache,page);
            }
        }
    }

    if(rootpage)
    {
        if(numlocked > 2)
            ajWarn("CacheSync cache '%S' root: %Lu locked: %u",
                   cache->filename, rootpage, numlocked);
    }
    else
    {
        if(numlocked > 1)
            ajWarn("CacheSync cache '%S' root: zero locked: %u",
                   cache->filename, numlocked);
    }
    
                
    page = btreePricacheLocate(cache,rootpage);
    page->dirty = BT_LOCK;
    page->lockfor = 1001;

    if(rootpage)
    {
        page = btreePricacheLocate(cache,0L);
        page->dirty = BT_LOCK;
        page->lockfor = 1002;
    }

    statSyncLocked += numlocked;
    statSyncWrite += numwrite;
    return;
}
ID btreeCacheRootSync
TY static
MO ajindex
LB core
XX
DE Reset locked pages to dirty and relock the specified secondary root page.
DE
DE Pages will be written only when they are removed from the cache.
DE
DE This function clears all locks.
DE The caller must relock the root page (zero) if needed.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r rootpage ajulong
PD secondary (locked) root page
PX
RT void
RD 
RX
//


static void btreeCacheRootSync(AjPBtcache cache, ajulong rootpage)
{
    AjPBtpage page = NULL;
    ajuint numlocked = 0;
    ajuint numunlocked = 0;

    /*ajDebug("In btreeCacheRootSync '%S' rootpage:%Lu\n",
      cache->filename, rootpage);*/

    statCallRootSync++;

    for(page=cache->plru;page;page=page->next)
    {
        if(page->dirty == BT_LOCK)
        {
            numlocked++;
            if(page->pagepos)
            {
                if(page->pagepos != rootpage)
                {
                    numunlocked++;
                    page->dirty = BT_DIRTY;
                    ajWarn("btreeCacheRootSync locked '%Lu' lockfor: %u",
                            page->pagepos, page->lockfor);
#if AJINDEX_DEBUG                    
                    ajDebug("btreeCacheRootSync locked '%Lu' lockfor: %u\n",
                            page->pagepos, page->lockfor);
#endif
                }
            }
        }
    }
    
    for(page=cache->slru;page;page=page->next)
    {
        if(page->dirty == BT_LOCK)
        {
            numlocked++;
            if(page->pagepos)
            {
                if(page->pagepos != rootpage)
                {
                    numunlocked++;
                    page->dirty = BT_DIRTY;
                    ajWarn("btreeCacheRootSync locked '%Lu' lockfor: %u",
                            page->pagepos, page->lockfor);
#if AJINDEX_DEBUG                    
                    ajDebug("btreeCacheRootSync locked '%Lu' lockfor: %u\n",
                            page->pagepos, page->lockfor);
#endif
                }
            }
        }
    }
    
    if(numunlocked > statRootSyncMaxUnlocked)
        statRootSyncMaxUnlocked = numunlocked;

    if(numlocked > statRootSyncMaxLocked)
        statRootSyncMaxLocked = numlocked;

    statRootSyncLocked += numlocked;
    statRootSyncUnlocked += numunlocked;

    page = btreeSeccacheLocate(cache,rootpage);
    page->dirty = BT_LOCK;      /* (re)lock the secondary root page */
    page->lockfor = 1011;

    return;
}
ID btreePricacheWrite
TY static
MO ajindex
LB core
XX
DE Get a pointer to a disc primary cache page for writing
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


static AjPBtpage btreePricacheWrite(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage ret = NULL;

#if AJINDEX_DEBUG
    ajDebug("btreePricacheWrite %Lu\n", pagepos);
#endif

    ret = btreePricacheControl(cache,pagepos,BT_WRITE);

    return ret;
}
ID btreeSeccacheWrite
TY static
MO ajindex
LB core
XX
DE Get a pointer to a disc secondary cache page for writing
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


static AjPBtpage btreeSeccacheWrite(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage ret = NULL;

#if AJINDEX_DEBUG
    ajDebug("btreeSeccacheWrite %Lu\n", pagepos);
#endif

    ret = btreeSeccacheControl(cache,pagepos,BT_WRITE);

    return ret;
}
ID btreePricacheNodenew
TY static
MO ajindex
LB core
XX
DE Get a pointer to a new disc primary cache page for writing a bucket.
DE
DE Clears the page and sets the block number.
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


static AjPBtpage btreePricacheNodenew(AjPBtcache cache)
{
    AjPBtpage ret = NULL;
    unsigned char *p;
    ajuint nodetype;

#if AJINDEX_DEBUG
    ajDebug("btreePricacheNodeenew pagepos:%Lu\n", cache->totsize);
#endif

    ret = btreePricacheControl(cache,cache->totsize,BT_WRITE);
    p = ret->buf;
    AJCSET0(p, cache->pripagesize); /* clear buffer to zeros */

    /* keep pointers to other cached pages */
/*
    ret->next = NULL;
    ret->prev = NULL;
*/

    ret->dirty = BT_CLEAN;

    ret->pagepos = cache->totsize;

    nodetype     = BT_FREEPAGE;
    SBT_NODETYPE(p,nodetype);

    SBT_BLOCKNUMBER(p,ret->pagepos);

    cache->totsize += cache->pripagesize;
    cache->pripagecount++;

    return ret;
}
ID btreeSeccacheNodenew
TY static
MO ajindex
LB core
XX
DE Get a pointer to a new disc secondary cache page for writing
DE
DE Clears the page and sets the block number.
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


static AjPBtpage btreeSeccacheNodenew(AjPBtcache cache)
{
    AjPBtpage ret = NULL;
    unsigned char *p;
    ajuint nodetype;

#if AJINDEX_DEBUG
    ajDebug("btreeSeccacheNodenew pagepos:%Lu\n", cache->totsize);
#endif

    ret = btreeSeccacheControl(cache,cache->totsize,BT_WRITE);
    p = ret->buf;
    AJCSET0(p, cache->secpagesize); /* clear buffer to zeros */

    /* keep pointers to other cached pages */
/*
    ret->next = NULL;
    ret->prev = NULL;
*/

    ret->dirty = BT_CLEAN;

    ret->pagepos = cache->totsize;

    nodetype     = BT_SECFREEPAGE;
    SBT_NODETYPE(p,nodetype);

    SBT_BLOCKNUMBER(p,ret->pagepos);

    cache->totsize += cache->secpagesize;
    cache->secpagecount++;

    return ret;
}
ID btreePricacheBucketnew
TY static
MO ajindex
LB core
XX
DE Get a pointer to a new disc primary cache page for writing a bucket.
DE
DE Clears the page.
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


static AjPBtpage btreePricacheBucketnew(AjPBtcache cache)
{
    AjPBtpage ret = NULL;
    unsigned char *p;
    ajuint nodetype;

#if AJINDEX_DEBUG
    ajDebug("btreePricacheBucketnew pagepos:%Lu\n", cache->totsize);
#endif

    ret = btreePricacheControl(cache,cache->totsize,BT_WRITE);
    p = ret->buf;
    AJCSET0(p, cache->pripagesize); /* clear buffer to zeros */

    /* keep pointers to other cached pages */
/*
    ret->next = NULL;
    ret->prev = NULL;
*/

    ret->dirty = BT_CLEAN;

    ret->pagepos = cache->totsize;

    nodetype     = BT_FREEPAGE;
    SBT_NODETYPE(p,nodetype);

    cache->totsize += cache->pripagesize;
    cache->pripagecount++;

    return ret;
}
ID btreeSeccacheBucketnew
TY static
MO ajindex
LB core
XX
DE Get a pointer to a new disc secondary cache page for writing a bucket
DE
DE Clears the page.
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
RT AjPBtpage
RD disc cache page pointer
RX
//


static AjPBtpage btreeSeccacheBucketnew(AjPBtcache cache)
{
    AjPBtpage ret = NULL;
    unsigned char *p;
    ajuint nodetype;

#if AJINDEX_DEBUG
    ajDebug("btreeSeccacheBucketnew pagepos:%Lu\n", cache->totsize);
#endif

    ret = btreeSeccacheControl(cache,cache->totsize,BT_WRITE);
    p = ret->buf;
    AJCSET0(p, cache->secpagesize); /* clear buffer to zeros */

    /* keep pointers to other cached pages */
/*
    ret->next = NULL;
    ret->prev = NULL;
*/

    ret->dirty = BT_CLEAN;

    ret->pagepos = cache->totsize;

    nodetype     = BT_SECFREEPAGE;
    SBT_NODETYPE(p,nodetype);

    cache->totsize += cache->secpagesize;
    cache->secpagecount++;

    return ret;
}
ID btreePrirootCreate
TY static
MO ajindex
LB core
XX
DE Create and write an empty primary root node. Set it as root, write it to
DE disc and then lock the page in the disc cache.
DE
DE The root node is at block 0L
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
RT void
RD 
RX
//


static void btreePrirootCreate(AjPBtcache cache)
{
    AjPBtpage page = NULL;
    unsigned char *p;
    ajuint nodetype;
    ajulong blocknumber;
    ajuint nkeys;
    ajuint totlen;
    ajulong left;
    ajulong right;
    ajulong overflow;
    ajulong prev;
    ajulong rootpage = 0L;

#if AJINDEX_DEBUG
    ajDebug("btreePrirootCreate\n"); 
#endif


    if(!cache->totsize)
        page = btreePricacheNodenew(cache); /* new index */
    else
        page = btreePricacheWrite(cache, rootpage); /* existing index? */

    page->pagepos    = rootpage;
    
    p = page->buf;

    nodetype    = BT_ROOT;
    blocknumber = rootpage;
    nkeys       = 0;
    totlen      = 0;
    left        = 0L;
    right       = 0L;
    prev        = 0L;
    overflow    = 0L;

    /* Don't reuse the variables. Endian-ness may be changed */
    SBT_NODETYPE(p,nodetype);
    SBT_BLOCKNUMBER(p,blocknumber);
    SBT_NKEYS(p,nkeys);
    SBT_TOTLEN(p,totlen);
    SBT_LEFT(p,left);
    SBT_RIGHT(p,right);
    SBT_PREV(p,prev);
    SBT_OVERFLOW(p,overflow);

    page->dirty = BT_DIRTY;

    if(btreeDoRootSync)
        btreeCacheRootSync(cache,rootpage);
    page->dirty = BT_LOCK;
    page->lockfor = 1021;

    return;
}
ID btreeSecrootCreate
TY static
MO ajindex
LB core
XX
DE Create and write an empty secondary root node. Set it as root, write it to
DE disc and then lock the page in the disc cache.
DE
DE The root node is at block 0L, secondary roots are on other pages
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r rootpage ajulong
PD root block
PX
RT void
RD 
RX
//


static void btreeSecrootCreate(AjPBtcache cache, ajulong rootpage)
{
    AjPBtpage page = NULL;
    unsigned char *p;
    ajuint nodetype;
    ajulong blocknumber;
    ajuint nkeys;
    ajuint totlen;
    ajulong left;
    ajulong right;
    ajulong overflow;
    ajulong prev;

#if AJINDEX_DEBUG
    ajDebug("btreeSecrootCreate %Lu\n", rootpage); 
#endif


    if(!rootpage)
    {
        if(rootpage == cache->totsize)
            page = btreeSeccacheNodenew(cache);
        else
            page = btreeSeccacheWrite(cache, rootpage);
    }
    else 
    {
        if(rootpage == cache->totsize)
            page = btreeSeccacheNodenew(cache);
        else
            page = btreeSeccacheWrite(cache, rootpage);
    }
           
    page->pagepos    = rootpage;
    
    p = page->buf;

    nodetype    = BT_SECROOT;
    blocknumber = rootpage;
    nkeys       = 0;
    totlen      = 0;
    left        = 0L;
    right       = 0L;
    prev        = 0L;
    overflow    = 0L;

    /* Don't reuse the variables. Endian-ness may be changed */
    SBT_NODETYPE(p,nodetype);
    SBT_BLOCKNUMBER(p,blocknumber);
    SBT_NKEYS(p,nkeys);
    SBT_TOTLEN(p,totlen);
    SBT_LEFT(p,left);
    SBT_RIGHT(p,right);
    SBT_PREV(p,prev);
    SBT_OVERFLOW(p,overflow);

    page->dirty = BT_DIRTY;

    if(btreeDoRootSync)
        btreeCacheRootSync(cache,rootpage);
    page->dirty = BT_LOCK;
    page->lockfor = 1021;

    return;
}
ID btreePrimaryFindInode
TY static
MO ajindex
LB core
XX
DE Recursive search for insert node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD page
PX
PN [3]
PA r item const AjPStr
PD key to search for
PX
RT AjPBtpage
RD leaf node where item should be inserted
RX
//


static AjPBtpage btreePrimaryFindInode(AjPBtcache cache, AjPBtpage page,
                                       const AjPStr item)
{
    AjPBtpage ret = NULL;
    AjPBtpage pg  = NULL;

    unsigned char *buf = NULL;
    ajuint status       = 0;
    ajuint ival         = 0;

    /* ajDebug("In btreePrimaryFindInode\n"); */
    
    ret = page;
    buf = page->buf;
    GBT_NODETYPE(buf,&ival);

    if(ival != BT_LEAF)
    {
	status = ret->dirty;
	ret->dirty = BT_LOCK;	/* Lock in case of lots of overflow pages */
        ret->lockfor = 1031;
	pg = btreePrimaryPageDown(cache,buf,item);
	ret->dirty = status;
	ret = btreePrimaryFindInode(cache,pg,item);
    }

    return ret;
}
ID btreeKeyidFindINode
TY static
MO ajindex
LB core
XX
DE Recursive search for insert node in a secondary tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD page
PX
PN [3]
PA r item const AjPStr
PD key to search for
PX
RT AjPBtpage
RD leaf node where item should be inserted
RX
//


static AjPBtpage btreeKeyidFindINode(AjPBtcache cache, AjPBtpage page,
                                   const AjPStr item)
{
    AjPBtpage ret = NULL;
    AjPBtpage pg  = NULL;

    unsigned char *buf = NULL;
    ajuint status       = 0;
    ajuint ival         = 0;

    /* ajDebug("In btreeKeyidFindINode\n"); */
    
    ret = page;
    buf = page->buf;
    GBT_NODETYPE(buf,&ival);

    if(ival != BT_SECLEAF)
    {
	status = ret->dirty;
	ret->dirty = BT_LOCK;	/* Lock in case of lots of overflow pages */
        ret->lockfor = 1041;
	pg = btreeSecondaryPageDown(cache,buf,item);
	ret->dirty = status;
	ret = btreeKeyidFindINode(cache,pg,item);
    }
    
    return ret;
}
ID btreePrimaryPageDown
TY static
MO ajindex
LB core
XX
DE Return next lower index page given a key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA r key const AjPStr
PD key to search for
PX
RT AjPBtpage
RD pointer to a page
RX
//


static AjPBtpage btreePrimaryPageDown(AjPBtcache cache, unsigned char *buf,
                                         const AjPStr key)
{
    ajulong blockno = 0L;
    AjPBtpage page = NULL;
    
    /* ajDebug("In btreePrimaryPageDown\n"); */

    blockno = btreeGetBlockS(cache,buf,key);
    page = btreePricacheRead(cache,blockno);

    return page;
}
ID btreeSecondaryPageDown
TY static
MO ajindex
LB core
XX
DE Return next lower index page given a key in a secondary tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA r key const AjPStr
PD key to search for
PX
RT AjPBtpage
RD pointer to a page
RX
//


static AjPBtpage btreeSecondaryPageDown(AjPBtcache cache, unsigned char *buf,
                                        const AjPStr key)
{
    ajulong blockno = 0L;
    AjPBtpage page = NULL;
    
    /* ajDebug("In btreeSecondaryPageDown\n"); */
    
    blockno = btreeGetBlockS(cache, buf, key);

    page =  btreeSeccacheRead(cache,blockno);

    return page;
}
ID ajBtreeIdNew
TY public
MO ajindex
LB core
XX
DE Constructor for index bucket ID information
XX
PN [1]
PA r refcount ajuint
PD Number of reference files for each entry
PX
RT AjPBtId
RD Index ID object
RX
//


AjPBtId ajBtreeIdNew(ajuint refcount)
{
    AjPBtId Id = NULL;

    /* ajDebug("In ajBtreeIdNew\n"); */

    if(statSaveBtreeIdNext)
    {
        Id = statSaveBtreeId[--statSaveBtreeIdNext];
        MAJSTRASSIGNCLEAR(&Id->id);
        if(Id->refcount != refcount)
        {
            if(refcount)
                AJCRESIZE0(Id->refoffsets, Id->refcount, refcount);
            else
                AJFREE(Id->refoffsets);

            Id->refcount = refcount;
        }
    }
    else
    {
        AJNEW0(Id);
        Id->id = ajStrNew();
        Id->refcount = refcount;

        if(refcount)
            AJCNEW(Id->refoffsets, refcount);
    }

    Id->dbno = 0;
    Id->dups = 0;
    Id->offset = 0L;

    return Id;
}
ID ajBtreeIdDel
TY public
MO ajindex
LB core
XX
DE Destructor for index bucket ID information
XX
PN [1]
PA w thys AjPBtId*
PD index ID object
PX
RT void
RD 
RX
//


void ajBtreeIdDel(AjPBtId *thys)
{
    ajuint newmax;

    /* ajDebug("In ajBtreeIdDel\n"); */

    if(!statSaveBtreeId)
    {
        statSaveBtreeIdMax = 2048;
        AJCNEW0(statSaveBtreeId, statSaveBtreeIdMax);
        statSaveBtreeIdNext = 0;
    }

    if(!thys || !*thys)
	return;

    if(statSaveBtreeIdNext >= statSaveBtreeIdMax) 
    {
        newmax = statSaveBtreeIdMax + statSaveBtreeIdMax;
        AJCRESIZE0(statSaveBtreeId,statSaveBtreeIdMax,newmax);
        statSaveBtreeIdMax = newmax;
    }
    
    statSaveBtreeId[statSaveBtreeIdNext++] = *thys;
    
    *thys = NULL;

    return;
}
ID ajBtreeIdDelVoid
TY public
MO ajindex
LB core
XX
DE Destructor for index bucket ID information
XX
PN [1]
PA w voidarg void**
PD Index ID object, passed as void for use by
PD list and table destructors.
PX
RT void
RD 
RX
//


void ajBtreeIdDelVoid(void **voidarg)
{
    AjPBtId *thys = (AjPBtId *)voidarg;

    ajBtreeIdDel(thys);

    return;
}
ID ajBtreeIdIsMulti
TY public
MO ajindex
LB core
XX
DE Tests whether an ID object links to multiple entries
XX
PN [1]
PA r thys const AjPBtId
PD index ID object
PX
RT AjBool
RD True if ID object points to duplicated identifiers
RX
//


AjBool ajBtreeIdIsMulti(const AjPBtId thys)
{
    if(!thys)
	return ajFalse;

    if(thys->dups)
        return ajTrue;

    return ajFalse;
}
ID btreeIdFree
TY static
MO ajindex
LB core
XX
DE Destructor for index bucket ID information
XX
PN [1]
PA w thys AjPBtId*
PD index ID object
PX
RT void
RD 
RX
//


static void btreeIdFree(AjPBtId *thys)
{
    AjPBtId Id = NULL;

    /* ajDebug("In btreeIdFree\n"); */

    if(!thys || !*thys)
	return;

    Id = *thys;

    ajStrDel(&Id->id);

    if(Id->refoffsets)
        AJFREE(Id->refoffsets);

    AJFREE(Id);
    *thys = NULL;

    return;
}
ID ajBtreeHitNew
TY public
MO ajindex
LB core
XX
DE Constructor for index hit information
XX
RT AjPBtHit
RD Index hit object
RX
//


AjPBtHit ajBtreeHitNew(void)
{
    AjPBtHit Hit = NULL;

    /* ajDebug("In ajBtreeHitNew\n"); */

    if(statSaveBtreeHitNext)
    {
        Hit = statSaveBtreeHit[--statSaveBtreeHitNext];
        Hit->dbno = 0;
        Hit->refcount = 0;
        Hit->offset = 0L;
    }
    else
    {
        AJNEW0(Hit);
    }

    return Hit;
}
ID ajBtreeHitNewId
TY public
MO ajindex
LB core
XX
DE Constructor for index hit information from an existing Btid object
XX
PN [1]
PA r btid const AjPBtId
PD Index id object
PX
RT AjPBtHit
RD Index hit object
RX
//


AjPBtHit ajBtreeHitNewId(const AjPBtId btid)
{
    AjPBtHit Hit = NULL;

    /* ajDebug("In ajBtreeHitNewId\n"); */

    if(statSaveBtreeHitNext)
    {
        Hit = statSaveBtreeHit[--statSaveBtreeHitNext];
    }
    else
    {
        AJNEW0(Hit);
    }

    Hit->dbno = btid->dbno;
    Hit->refcount = 0;
    Hit->offset = btid->offset;

    return Hit;
}
ID ajBtreeHitDel
TY public
MO ajindex
LB core
XX
DE Destructor for index hit information
XX
PN [1]
PA w thys AjPBtHit*
PD index hit object
PX
RT void
RD 
RX
//


void ajBtreeHitDel(AjPBtHit *thys)
{
    ajuint newmax;

    /* ajDebug("In ajBtreeHitDel\n"); */

    if(!statSaveBtreeHit)
    {
        statSaveBtreeHitMax = 2048;
        AJCNEW0(statSaveBtreeHit, statSaveBtreeHitMax);
        statSaveBtreeHitNext = 0;
    }

    if(!thys || !*thys)
	return;

    if(statSaveBtreeHitNext >= statSaveBtreeHitMax) 
    {
        newmax = statSaveBtreeHitMax + statSaveBtreeHitMax;
        AJCRESIZE0(statSaveBtreeHit,statSaveBtreeHitMax,newmax);
        statSaveBtreeHitMax = newmax;
    }
    
    statSaveBtreeHit[statSaveBtreeHitNext++] = *thys;
    
    *thys = NULL;

    return;
}
ID ajBtreeHitDelVoid
TY public
MO ajindex
LB core
XX
DE Destructor for index hit information
XX
PN [1]
PA w voidarg void**
PD Index hit object, passed as void for use by
PD list and table destructors.
PX
RT void
RD 
RX
//


void ajBtreeHitDelVoid(void **voidarg)
{
    AjPBtHit *thys = (AjPBtHit *)voidarg;

    ajBtreeHitDel(thys);

    return;
}
ID btreeHitFree
TY static
MO ajindex
LB core
XX
DE Destructor for index hit information
XX
PN [1]
PA w thys AjPBtHit*
PD index hit object
PX
RT void
RD 
RX
//


static void btreeHitFree(AjPBtHit *thys)
{
    AjPBtHit Hit = NULL;

    /* ajDebug("In btreeHitFree\n"); */

    if(!thys || !*thys)
	return;

    Hit = *thys;

    AJFREE(Hit);
    *thys = NULL;

    return;
}
ID ajBtreeHitrefNew
TY public
MO ajindex
LB core
XX
DE Constructor for index reference hit information
XX
RT AjPBtHitref
RD Index reference hit object
RX
//


AjPBtHitref ajBtreeHitrefNew(void)
{
    AjPBtHitref Hitref = NULL;

    /* ajDebug("In ajBtreeHitrefNew\n"); */

    if(statSaveBtreeHitrefNext)
    {
        Hitref = statSaveBtreeHitref[--statSaveBtreeHitrefNext];
        Hitref->dbno = 0;
        Hitref->refcount = 0;
        Hitref->offset = 0L;
    }
    else
    {
        AJNEW0(Hitref);
    }

    return Hitref;
}
ID ajBtreeHitrefNewId
TY public
MO ajindex
LB core
XX
DE Constructor for index reference hit information from an existing
DE Btid object
XX
PN [1]
PA r btid const AjPBtId
PD Index id object
PX
RT AjPBtHitref
RD Index hit object
RX
//


AjPBtHitref ajBtreeHitrefNewId(const AjPBtId btid)
{
    AjPBtHitref Hitref = NULL;

    /* ajDebug("In ajBtreeHitrefNewId\n"); */

    if(statSaveBtreeHitrefNext)
    {
        Hitref = statSaveBtreeHitref[--statSaveBtreeHitrefNext];
    }
    else
    {
        AJNEW0(Hitref);
    }

    Hitref->dbno = btid->dbno;
    Hitref->refcount = 0;
    Hitref->offset = btid->offset;
    if(btid->refcount)
        Hitref->refoffset = btid->refoffsets[0];
    else
        Hitref->refoffset = 0L;

    return Hitref;
}
ID ajBtreeHitrefDel
TY public
MO ajindex
LB core
XX
DE Destructor for index reference hit information
XX
PN [1]
PA w thys AjPBtHitref*
PD index reference hit object
PX
RT void
RD 
RX
//


void ajBtreeHitrefDel(AjPBtHitref *thys)
{
    ajuint newmax;

    /* ajDebug("In ajBtreeHitrefDel\n"); */

    if(!statSaveBtreeHitref)
    {
        statSaveBtreeHitrefMax = 2048;
        AJCNEW0(statSaveBtreeHitref, statSaveBtreeHitrefMax);
        statSaveBtreeHitrefNext = 0;
    }

    if(!thys || !*thys)
	return;

    if(statSaveBtreeHitrefNext >= statSaveBtreeHitrefMax) 
    {
        newmax = statSaveBtreeHitrefMax + statSaveBtreeHitrefMax;
        AJCRESIZE0(statSaveBtreeHitref,statSaveBtreeHitrefMax,newmax);
        statSaveBtreeHitrefMax = newmax;
    }
    
    statSaveBtreeHitref[statSaveBtreeHitrefNext++] = *thys;
    
    *thys = NULL;

    return;
}
ID ajBtreeHitrefDelVoid
TY public
MO ajindex
LB core
XX
DE Destructor for index reference hit information
XX
PN [1]
PA w voidarg void**
PD Index reference hit object, passed as
PD void for use by list and table
PD destructors.
PX
RT void
RD 
RX
//


void ajBtreeHitrefDelVoid(void **voidarg)
{
    AjPBtHitref *thys = (AjPBtHitref *)voidarg;

    ajBtreeHitrefDel(thys);

    return;
}
ID btreeHitrefFree
TY static
MO ajindex
LB core
XX
DE Destructor for index reference hit information
XX
PN [1]
PA w thys AjPBtHitref*
PD index reference hit object
PX
RT void
RD 
RX
//


static void btreeHitrefFree(AjPBtHitref *thys)
{
    AjPBtHitref Hitref = NULL;

    /* ajDebug("In btreeHitrefFree\n"); */

    if(!thys || !*thys)
	return;

    Hitref = *thys;

    AJFREE(Hitref);
    *thys = NULL;

    return;
}
ID btreeIdbucketIdlist
TY static
MO ajindex
LB core
XX
DE Copies all IDs into a list
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA u idlist AjPList
PD list to hold IDs
PX
RT ajulong
RD Overflow
RX
//


static ajulong btreeIdbucketIdlist(AjPBtcache cache, ajulong pagepos,
                                   AjPList idlist)
{
    AjPBtpage page      = NULL;
    AjPBtpage lpage     = NULL;
    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    AjPBtId id          = NULL;
    
    unsigned char *idptr = NULL;
    
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajulong pageoverflow = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  iref;
    ajuint  len = 0;
    ajuint idlen;
    ajuint keyskip = BT_DDOFF + cache->refcount*BT_EXTRA + 1;

    /* ajDebug("In btreeIdbucketIdlist\n"); */
    
    if(!pagepos)
	ajFatal("IdbucketIdlist: cannot read bucket from root page cache %S",
                cache->filename);

    page  = btreePricacheRead(cache,pagepos);
    lpage = page;
    dirtysave = lpage->dirty;
    lpage->dirty = BT_LOCK;     /* reset on return */
    lpage->lockfor = 1051;

    buf = lpage->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_IDBUCKET)
	ajFatal("BucketIdlist: NodeType mismatch. "
                "Not bucket (%u) cache %S",
                nodetype, cache->filename);
    
    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->pnperbucket)
	ajFatal("BucketIdlist: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->pnperbucket, cache->filename);
    

    GBT_BUCKOVERFLOW(buf,&overflow);
    pageoverflow = overflow;
    
    kptr  = PBT_BUCKKEYLEN(buf);
    idptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//	    /# ajDebug("IdbucketIdlist: Overflow\n"); #/
//	    page  = btreePricacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//	    if(nodetype != BT_IDBUCKET)
//		ajFatal("BucketIdlist: NodeType mismatch. "
//                        "Not bucket (%u) cache %S",
//			nodetype, cache->filename);
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    idptr = PBT_BUCKKEYLEN(buf);
//	}
*/

	id = ajBtreeIdNew(cache->refcount);
        
	/* Fill ID objects */
	ajStrAssignLenC(&id->id,(const char *)idptr,idlen);
	idptr += (idlen + 1);
	BT_GETAJUINT(idptr,&id->dbno);
	idptr += sizeof(ajuint);
	BT_GETAJUINT(idptr,&id->dups);
	idptr += sizeof(ajuint);	
	BT_GETAJULONG(idptr,&id->offset);
	idptr += sizeof(ajulong);
        if(cache->refcount)
        {
            for(iref=0; iref< cache->refcount; iref++)
            {
                BT_GETAJULONG(idptr,&id->refoffsets[iref]);
                idptr += sizeof(ajulong);
            }
        }

	kptr += sizeof(ajuint);
        ajListPushAppend(idlist, id);
    }

    lpage->dirty = dirtysave;
    
    return pageoverflow;
}
ID btreeReadIdbucket
TY static
MO ajindex
LB core
XX
DE Constructor for id bucket given a disc page number
DE Creates one empty key slot for possible addition
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT AjPIdbucket
RD Id bucket
RX
//


static AjPIdbucket btreeReadIdbucket(AjPBtcache cache, ajulong pagepos)
{
    AjPIdbucket bucket    = NULL;
    AjPBtpage page      = NULL;
    AjPBtpage lpage     = NULL;
    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    AjPBtId id          = NULL;
    
    unsigned char *idptr = NULL;
    
    ajuint nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  iref;
    ajuint  len = 0;
    ajuint idlen;
    ajuint keyskip = BT_DDOFF + cache->refcount*BT_EXTRA + 1;

    /* ajDebug("In btreeReadIdbucket\n"); */
    
    if(!pagepos)
	ajFatal("BucketRead: cannot read bucket from root page");

    page  = btreePricacheRead(cache,pagepos);
    lpage = page;
    dirtysave = lpage->dirty;
    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1061;

    buf = lpage->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);

    if(nodetype != BT_IDBUCKET)
	ajFatal("ReadBucket: NodeType mismatch. Not bucket (%u)", nodetype);
    
    GBT_BUCKNENTRIES(buf,&nentries);

    if(nentries > cache->pnperbucket)
	ajFatal("ReadBucket: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->pnperbucket, cache->filename);
    

    GBT_BUCKOVERFLOW(buf,&overflow);

    bucket = btreeIdbucketNew(cache->pnperbucket, cache->refcount);
    bucket->Nentries = nentries;
    
    kptr  = PBT_BUCKKEYLEN(buf);
    idptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//#if AJINDEX_DEBUG
//	    ajDebug("ReadIdbucket: Overflow\n");
//#endif
//	    page  = btreePricacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//	    if(nodetype != BT_IDBUCKET)
//		ajFatal("ReadBucket: NodeType mismatch. Not bucket (%u)",
//			nodetype);
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    idptr = PBT_BUCKKEYLEN(buf);
//	}
*/

        id = bucket->Ids[i];

	/* Fill ID objects */
	ajStrAssignLenC(&id->id,(const char *)idptr, idlen);
	idptr += (idlen + 1);
	BT_GETAJUINT(idptr,&id->dbno);
	idptr += sizeof(ajuint);
	BT_GETAJUINT(idptr,&id->dups);
	idptr += sizeof(ajuint);	
	BT_GETAJULONG(idptr,&id->offset);
	idptr += sizeof(ajulong);
        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
            {
                BT_GETAJULONG(idptr,&id->refoffsets[iref]);
                idptr += sizeof(ajulong);
            }
        }

	kptr += sizeof(ajuint);
    }

    lpage->dirty = dirtysave;
    
    return bucket;
}
ID btreeIdbucketFindDupId
TY static
MO ajindex
LB core
XX
DE Tests for an ID in an id bucket.
DE If found, returns the ID and its count in the bucket.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA r id const AjPStr
PD id to search for
PX
PN [4]
PA w ientry ajuint*
PD entry number matched
PX
RT AjPBtId
RD ID found
RX
//


static AjPBtId btreeIdbucketFindDupId(AjPBtcache cache, ajulong pagepos,
                                      const AjPStr id, ajuint* ientry)
{
    AjPBtId bid = NULL;
    AjPBtpage page      = NULL;
    AjPBtpage lpage     = NULL;
    unsigned char *buf  = NULL;
    unsigned char *lbuf  = NULL;
    unsigned char *kptr = NULL;
    
    unsigned char *idptr = NULL;
    
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajuint  dirtysave = 0;

    ajuint  i;
    ajuint  iref;
    ajuint  len = 0;
    ajuint  idlen;
    ajuint keyskip = BT_DDOFF + cache->refcount*BT_EXTRA + 1;

    /* ajDebug("In btreeIdbucketFindDupId\n"); */
    
    if(!pagepos)
	ajFatal("btreeIdbucketFindDupId: "
                "cannot read bucket from root page cache %S",
                cache->filename);

    page  = btreePricacheRead(cache,pagepos);
    lpage = page;
    dirtysave = lpage->dirty;
    lpage->dirty = BT_LOCK;     /* reset on return */
    lpage->lockfor = 1071;

    buf = lpage->buf;
    lbuf = buf;

    GBT_BUCKNODETYPE(lbuf,&nodetype);
    if(nodetype != BT_IDBUCKET)
	ajFatal("BucketIdFindDupId: NodeType mismatch. "
                "Not bucket (%u) cache %S",
                nodetype, cache->filename);
    
    GBT_BUCKNENTRIES(lbuf,&nentries);
    if(nentries > cache->pnperbucket)
	ajFatal("BucketIdFindDupId: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->pnperbucket, cache->filename);
    

    GBT_BUCKOVERFLOW(lbuf,&overflow);
    
    kptr  = PBT_BUCKKEYLEN(buf);
    idptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//	    /# ajDebug("BucketFindId: Overflow\n"); #/
//	    page  = btreePricacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//	    if(nodetype != BT_IDBUCKET)
//		ajFatal("BucketFindId: NodeType mismatch. "
//                        "Not bucket (%u) cache %S",
//			nodetype, cache->filename);
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    idptr = PBT_BUCKKEYLEN(buf);
//	}
*/

	if(!MAJSTRCMPC(id,(const char*)idptr))
        {
            bid = ajBtreeIdNew(cache->refcount);
            idlen = len - keyskip;
            ajStrAssignLenC(&bid->id,(const char *)idptr, idlen);
            idptr += (idlen + 1);
            BT_GETAJUINT(idptr,&bid->dbno);
            idptr += sizeof(ajuint);
            BT_GETAJUINT(idptr,&bid->dups);
            idptr += sizeof(ajuint);	
            BT_GETAJULONG(idptr,&bid->offset);
            idptr += sizeof(ajulong);

            if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                {
                    BT_GETAJULONG(idptr,&bid->refoffsets[iref]);
                    idptr += sizeof(ajulong);
                }
            }

            *ientry = i;
            lpage->dirty = dirtysave;
            return bid;
        }

/* Step through ID object data */

	idptr += len;
        kptr += sizeof(ajuint);
    }

    lpage->dirty = dirtysave;

    *ientry = nentries;
    return NULL;
}
ID btreeIdbucketSort
TY static
MO ajindex
LB core
XX
DE Sorts IDs in an id bucket
XX
PN [1]
PA u thys AjPIdbucket
PD Id bucket
PX
RT void
RD 
RX
//


static void btreeIdbucketSort(AjPIdbucket thys)
{
    qsort(thys->Ids, thys->Nentries, sizeof(AjPBtId), btreeIdCompare);

    return;
}
ID btreeWriteIdbucketId
TY static
MO ajindex
LB core
XX
DE Tests for an ID in a bucket.
DE If found, returns the ID and its count in the bucket.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA r btid const AjPBtId
PD Bucket id to update
PX
PN [4]
PA r ientry ajuint
PD entry number for this ID
PX
RT void
RD 
RX
//


static void btreeWriteIdbucketId(AjPBtcache cache, ajulong pagepos,
                                 const AjPBtId btid, ajuint ientry)
{
    AjPBtpage page      = NULL;
    AjPBtpage lpage     = NULL;
    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;
    unsigned char *kptr = NULL;
    
    unsigned char *idptr = NULL;
    
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;

    ajuint  i;
    ajuint  len = 0;
    ajuint  idlen;
    ajuint  v;
    ajulong lv;
    ajuint iref;
    ajuint keyskip = BT_DDOFF + cache->refcount*BT_EXTRA + 1;

    /* ajDebug("In btreeWriteIdbucketId\n"); */
    
    if(!pagepos)
	ajFatal("WriteIdbucketId: cannot read bucket from root page cache %S",
                cache->filename);

    page  = btreePricacheRead(cache,pagepos);
    lpage = page;
    lpage->dirty = BT_LOCK;     /* reset on return */
    lpage->lockfor = 1081;

    buf = lpage->buf;
    lbuf= buf;

    GBT_BUCKNODETYPE(lbuf,&nodetype);
    if(nodetype != BT_IDBUCKET)
	ajFatal("WriteIdbucketId: NodeType mismatch. "
                "Not bucket (%u) cache %S",
                nodetype, cache->filename);
    
    GBT_BUCKNENTRIES(lbuf,&nentries);
    if(nentries > cache->pnperbucket)
	ajFatal("WriteIdbucketId: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->pnperbucket, cache->filename);
    

    GBT_BUCKOVERFLOW(lbuf,&overflow);
    
    kptr  = PBT_BUCKKEYLEN(lbuf);
    idptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//	    /# ajDebug("BucketFindId: Overflow\n"); #/
//	    page  = btreePricacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//	    if(nodetype != BT_IDBUCKET)
//		ajFatal("WriteIdbucketId: NodeType mismatch. "
//                        "Not bucket (%u) cache %S",
//			nodetype, cache->filename);
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    idptr = PBT_BUCKKEYLEN(buf);
//	}
*/

/* Step through ID object data */
	idptr += len;
        kptr += sizeof(ajuint);
    }

    BT_GETAJUINT(kptr,&len);
    idlen = len - keyskip;
    idptr += (idlen + 1);

    v = btid->dbno;
    BT_SETAJUINT(idptr,v);
    idptr += sizeof(ajuint);

    v = btid->dups;
    BT_SETAJUINT(idptr,v);
    idptr += sizeof(ajuint);	

    lv = btid->offset;
    BT_SETAJULONG(idptr,lv);
    idptr += sizeof(ajulong);

    if(cache->refcount)
    {
        for(iref=0; iref < cache->refcount; iref++)
        {
            lv = btid->refoffsets[iref];
            BT_SETAJULONG(idptr,lv);
            idptr += sizeof(ajulong);
        }

    }

    lpage->dirty = BT_DIRTY;

    return;
}
ID btreeWriteIdbucket
TY static
MO ajindex
LB core
XX
DE Write index bucket object to the cache given a disc page number
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r bucket const AjPIdbucket
PD Id bucket
PX
PN [3]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreeWriteIdbucket(AjPBtcache cache, const AjPIdbucket bucket,
                               ajulong pagepos)
{
    AjPBtpage page      = NULL;
    AjPBtpage lpage     = NULL;
    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;
    ajuint  uv  = 0;
    ajuint   v   = 0;
    ajuint  i    = 0;
    ajuint  iref = 0;
    ajuint   len = 0;
    ajulong lv   = 0L;
    AjPBtId id  = NULL;

    ajuint nentries = 0;
    ajulong overflow = 0L;
    unsigned char *keyptr = NULL;
    unsigned char *lptr   = NULL;
    ajuint refskip = cache->refcount*BT_EXTRA;

/*    ajuint keyskip = BT_DDOFF + cache->refcount*BT_EXTRA + 1;*/

    if(pagepos == cache->totsize)	/* Create a new page */
    {
	page = btreePricacheBucketnew(cache);
	buf = page->buf;
    }
    else
    {
	page = btreePricacheRead(cache,pagepos);
	buf = page->buf;
	GBT_BUCKOVERFLOW(buf,&overflow);
    }

    v = BT_IDBUCKET;
    SBT_BUCKNODETYPE(buf,v);

    lbuf = buf;
    page->dirty = BT_LOCK;
    page->lockfor = 1091;
    lpage = page;

    nentries = bucket->Nentries;
    v = nentries;
    SBT_BUCKNENTRIES(buf,v);

    /* Write out key lengths */
    keyptr = PBT_BUCKKEYLEN(lbuf);

    for(i=0;i cache->pagesize)
//	    ajFatal("WriteIdbucket: Bucket cannot hold more than %u keys",
//		    i-1);
*/

	id = bucket->Ids[i];
	/* Need to alter this if bucket ID structure changes */
	len = BT_BUCKIDLEN(id->id) + refskip;
        v = len;
	BT_SETAJUINT(keyptr,v);
	keyptr += sizeof(ajuint);
    }


    /* Write out IDs using overflow if necessary */
    lptr = keyptr;

    for(i=0;iIds[i];
	len = BT_BUCKIDLEN(id->id) + refskip;

/*
//        /# overflow #/        
//	if((lptr-buf+1) + (len+keyskip)) > (ajuint) cache->pagesize)
//	{
//	    if(!overflow)		/# No overflow buckets yet #/
//	    {
//                page = btreePricacheBucketnew(cache);
//		buf = page->buf;
//		v = BT_IDBUCKET;
//		SBT_BUCKNODETYPE(buf,v);
//	    }
//	    else
//	    {
//		page = btreePricacheRead(cache,overflow);
//		buf  = page->buf;
//		GBT_BUCKOVERFLOW(buf,&overflow);
//	    }
//
//	    page->dirty = BT_DIRTY;
//
//	    lptr = PBT_BUCKKEYLEN(buf);	    
//	}
*/	

	sprintf((char *)lptr,"%s",ajStrGetPtr(id->id));
	lptr += (ajStrGetLen(id->id) + 1);
        uv = id->dbno;
	BT_SETAJUINT(lptr,uv);
	lptr += sizeof(ajuint);
        uv = id->dups;
	BT_SETAJUINT(lptr,uv);
	lptr += sizeof(ajuint);
        lv = id->offset;
	BT_SETAJULONG(lptr,lv);
	lptr += sizeof(ajulong);

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
            {
                lv = id->refoffsets[iref];
                BT_SETAJULONG(lptr,lv);
                lptr += sizeof(ajulong);
            }
        }
    }

    lv = 0L;
    SBT_BUCKOVERFLOW(buf,lv);

    lpage->dirty = BT_DIRTY;

    return;
}
ID btreeWriteIdbucketEmpty
TY static
MO ajindex
LB core
XX
DE Write empty index id bucket object to the cache given a disc page number
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreeWriteIdbucketEmpty(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage page      = NULL;
    unsigned char *buf  = NULL;
    ajuint   v   = 0;
    ajulong lv   = 0L;

    ajuint  nentries = 0;
    ajulong overflow = 0L;

    /* ajDebug("In btreeWriteIdbucketEmpty\n"); */

    if(pagepos == cache->totsize)	/* Create a new page */
    {
	page = btreePricacheBucketnew(cache);
	buf = page->buf;
	overflow = 0L;
	lv = overflow;
	SBT_BUCKOVERFLOW(buf,lv);
    }
    else
    {
	page = btreePricacheRead(cache,pagepos);
	buf = page->buf;
	GBT_BUCKOVERFLOW(buf,&overflow);
    }
    
    v = BT_IDBUCKET;
    SBT_BUCKNODETYPE(buf,v);

    nentries = 0;
    v = nentries;
    SBT_BUCKNENTRIES(buf,v);

    lv = 0L;
    SBT_BUCKOVERFLOW(buf,lv);

    page->dirty = BT_DIRTY;

    return;
}
ID btreeIdbucketDel
TY static
MO ajindex
LB core
XX
DE Delete an id bucket object
XX
PN [1]
PA w thys AjPIdbucket*
PD bucket
PX
RT void
RD bucket
RX
//


static void btreeIdbucketDel(AjPIdbucket *thys)
{
    AjPIdbucket pthis = NULL;
    ajuint newmax;

    /* ajDebug("In btreeIdbucketDel\n"); */

    if(!thys || !*thys)
	return;

 
    pthis = *thys;

    if(!statSaveIdbucket)
    {
        statSaveIdbucketMax=2048;
        statSaveIdbucketNext=0;
        AJCNEW0(statSaveIdbucket,statSaveIdbucketMax);
    }
    
    if(!statSaveIdbucketEmpty)
    {
        statSaveIdbucketEmptyMax=2048;
        statSaveIdbucketEmptyNext=0;
        AJCNEW0(statSaveIdbucketEmpty,statSaveIdbucketEmptyMax);
    }
    
    if(pthis->Maxentries)
    {
        if(statSaveIdbucketNext >= statSaveIdbucketMax)
        {
            newmax = statSaveIdbucketMax + statSaveIdbucketMax;
            AJCRESIZE0(statSaveIdbucket,statSaveIdbucketMax,newmax);
            statSaveIdbucketMax = newmax;
        }
        
        statSaveIdbucket[statSaveIdbucketNext++] = pthis;
    }
    else
    {
        if(statSaveIdbucketEmptyNext >= statSaveIdbucketEmptyMax)
        {
            newmax = statSaveIdbucketEmptyMax + statSaveIdbucketEmptyMax;
            AJCRESIZE0(statSaveIdbucketEmpty,statSaveIdbucketEmptyMax,newmax);
            statSaveIdbucketEmptyMax = newmax;
        }
        statSaveIdbucketEmpty[statSaveIdbucketEmptyNext++] = pthis;
    }
    
    *thys = NULL;

    return;
}
ID btreeIdbucketFree
TY static
MO ajindex
LB core
XX
DE Delete a keyword id bucket object
XX
PN [1]
PA w thys AjPIdbucket*
PD bucket
PX
RT void
RD 
RX
//


static void btreeIdbucketFree(AjPIdbucket *thys)
{
    AjPIdbucket pthis = NULL;
    ajuint n;
    ajuint i;
    
    /*ajDebug("In btreeIdbucketFree\n"); */

    if(!thys || !*thys)
	return;

    pthis = *thys;

    n = pthis->Maxentries;

    for(i=0;iIds[i]);
    
    AJFREE(pthis->keylen);
    AJFREE(pthis->Ids);
    
    AJFREE(pthis);

    *thys = NULL;

    return;
}
ID btreeIdbucketAdd
TY static
MO ajindex
LB core
XX
DE Add an ID to an id bucket
DE Only called if there is room in the bucket
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number of bucket
PX
PN [3]
PA r id const AjPBtId
PD ID info
PX
RT void
RD 
RX
//


static void btreeIdbucketAdd(AjPBtcache cache, ajulong pagepos,
                             const AjPBtId id)
{
    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    unsigned char *src  = NULL;
    unsigned char *dest = NULL;

    unsigned char *endptr  = NULL;
    
    ajuint nentries = 0;
    ajuint nodetype = 0;
    
    ajuint sum = 0;
    ajuint len = 0;
    ajuint i;
    ajuint v;
    ajuint uv;
    ajulong lv;

    ajuint iref;

    AjPBtpage page = NULL;
    static ajuint calls = 0;
    ajuint refskip = cache->refcount*BT_EXTRA;
    
/*  ajuint keyskip = BT_DDOFF + cache->refcount*BT_EXTRA + 1;*/

/*    unsigned char *lastptr = NULL;*/
/*    ajuint idlen    = 0;*/
/*    static ajuint overflowcalls=0;*/
   
    calls++;

    page = btreePricacheRead(cache,pagepos);
    buf  = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_IDBUCKET)
        ajFatal("Wrong nodetype in IdbucketAdd cache %S", cache->filename);

    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries == cache->pnperbucket)
        ajFatal("Bucket too full in IdbucketAdd page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->snperbucket, cache->filename);    

    kptr = PBT_BUCKKEYLEN(buf);
    src  = kptr + (nentries * sizeof(ajuint));

    sum = 0;
    for(i=0;iid);
//    lastptr = endptr + sizeof(ajuint) + idlen + keyskip;
//
//    if((ajuint) (lastptr - buf) >= cache->pripagesize)
//    {
//        overflowcalls++;
//        ajWarn("\nOverflow in IdbucketAdd nentries:%u fails %u/%u cache %S",
//               nentries, overflowcalls,calls, cache->filename);
//        btreeIdbucketAddFull(cache,pagepos,id);
//        return;
//    }
*/    

    dest = src + sizeof(ajuint);
    memmove((void *)dest, (void *)src, sum);

    v = BT_BUCKIDLEN(id->id) + refskip;
    BT_SETAJUINT(src,v);

    endptr += sizeof(ajuint) + 1;
    strcpy((char *)endptr,MAJSTRGETPTR(id->id));
    endptr += (MAJSTRGETLEN(id->id) + 1);
    uv = id->dbno;
    BT_SETAJUINT(endptr,uv);
    endptr += sizeof(ajuint);
    uv = id->dups;
    BT_SETAJUINT(endptr,uv);
    endptr += sizeof(ajuint);
    lv = id->offset;
    BT_SETAJULONG(endptr,lv);
    endptr += sizeof(ajulong);

    if(cache->refcount)
    {
        for(iref=0; iref < cache->refcount; iref++)
        {
            lv = id->refoffsets[iref];
            BT_SETAJULONG(endptr,lv);
            endptr += sizeof(ajulong);
        }
    }

    v = nentries + 1;
    SBT_BUCKNENTRIES(buf,v);

    page->dirty = BT_DIRTY;

    return;
}
ID btreeIdbucketCount
TY static
MO ajindex
LB core
XX
DE Return number of entries in an id bucket
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT ajuint
RD Number of entries in bucket
RX
//


static ajuint btreeIdbucketCount(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage page     = NULL;
    unsigned char *buf = NULL;
    ajuint nodetype    = 0;
    ajuint nentries    = 0;
    
    /* ajDebug("In btreeIdbucketCount\n"); */
    
    if(!pagepos)
	ajFatal("btreeIdbucketCount: Attempt to read bucket from root page\n");

    page  = btreePricacheRead(cache,pagepos);

    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);

    if(nodetype != BT_IDBUCKET)
	ajFatal("btreeIdbucketCount: NodeType mismatch. Not id bucket (%u)",
                nodetype);
    
    GBT_BUCKNENTRIES(buf,&nentries);

    return nentries;
}
ID btreeIdbucketsReorder
TY static
MO ajindex
LB core
XX
DE Re-order leaf id buckets
DE Must only be called if one of the buckets is full
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u leaf AjPBtpage
PD leaf page
PX
RT AjBool
RD true if reorder was successful i.e. leaf not full
RX
//


static AjBool btreeIdbucketsReorder(AjPBtcache cache, AjPBtpage leaf)
{
    ajuint nkeys = 0;
    unsigned char *lbuf = NULL;

    ajulong *ptrs        = NULL;
    ajulong *overflows   = NULL;
    AjPStr *newkeys     = NULL;
    ajulong *newptrs     = NULL;
    AjPBtMem arrays = NULL;
    AjPBtMem newarrays = NULL;
    
    ajuint i = 0;
    ajuint iref = 0;

    ajuint order;
    ajuint totalkeys     = 0;
    ajuint maxnperbucket = 0;
    ajuint count         = 0;
    ajuint keylimit      = 0;
    ajuint bucketlimit   = 0;
    ajuint nodetype      = 0;
    
    AjPList idlist    = NULL;
    ajuint  dirtysave = 0;
    AjPBtId bid       = NULL;
    AjPIdbucket cbucket = NULL;
    AjPBtId cid       = NULL;

    ajuint iold = 0;
    ajuint refskip = cache->refcount*BT_EXTRA;
    
#if AJINDEX_DEBUG
    ajDebug("btreeIdbucketsReorder %Lu\n", leaf->pagepos);
#endif

    /* ajDebug("In btreeIdbucketsReorder\n"); */

    dirtysave = leaf->dirty;

    leaf->dirty = BT_LOCK;
    leaf->lockfor = 1101;
    lbuf = leaf->buf;

    GBT_NODETYPE(lbuf,&nodetype);

    order = cache->porder;    

    /* Read keys/ptrs */
    arrays = btreeAllocPriArray(cache);
    ptrs = arrays->parray;

    btreeGetPointers(cache,lbuf,&ptrs);

    GBT_NKEYS(lbuf,&nkeys);


    if(!nkeys)
	ajFatal("IdbucketsReorder: Attempt to reorder empty leaf");

    keylimit = nkeys + 1;
    for(i=0;i<=nkeys;++i)
	totalkeys += btreeIdbucketCount(cache,ptrs[i]);

    btreeBucketCalc(totalkeys, keylimit, cache->pnperbucket,
                    &bucketlimit, &maxnperbucket);

    if(bucketlimit >= order)
    {
        btreeDeallocPriArray(cache, arrays);
	
	leaf->dirty = dirtysave;
	return ajFalse;
    }
    
    ++statCallIdbucketsReorder;

    newarrays = btreeAllocPriArray(cache);
    newkeys = newarrays->karray;
    newptrs = newarrays->parray;
    overflows = newarrays->overflows;
    
    /* Read IDs from all buckets and push to list and sort (increasing id) */
    idlist  = ajListNew();
    
    for(i=0;irefcount);
    iold=0;
    for(i=0;iOverflow = overflows[i];
	cbucket->Nentries = 0;

	count = 0;

	while(count!=maxnperbucket)
	{
	    ajListPop(idlist,(void **)&bid);
	    
	    cid = cbucket->Ids[count];
	    ajStrAssignS(&cid->id,bid->id);
	    cid->dbno = bid->dbno;
	    cid->dups = bid->dups;
	    cid->offset = bid->offset;

            if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                    cid->refoffsets[iref] = bid->refoffsets[iref];
	    }

	    cbucket->keylen[count] = BT_BUCKIDLEN(bid->id) + refskip;
	    ++cbucket->Nentries;
	    ++count;
	    ajBtreeIdDel(&bid);
	}


	ajListPeek(idlist,(void **)&bid);
	ajStrAssignS(&newkeys[i],bid->id);

	if((iold < order) && ptrs[iold])
            newptrs[i] = ptrs[iold++];
        else
	    newptrs[i] = cache->totsize;
	btreeWriteIdbucket(cache,cbucket,newptrs[i]);
    }


    /* Deal with greater-than bucket */

    cbucket->Overflow = overflows[i];
    cbucket->Nentries = 0;

    count = 0;

    while(ajListPop(idlist,(void **)&bid))
    {
	cid = cbucket->Ids[count];
	ajStrAssignS(&cid->id,bid->id);
	cid->dbno = bid->dbno;
	cid->dups = bid->dups;
	cid->offset = bid->offset;

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
                cid->refoffsets[iref] = bid->refoffsets[iref];
	}

	++cbucket->Nentries;
	++count;
	ajBtreeIdDel(&bid);
    }
    
    if((iold < order) && ptrs[iold])
        newptrs[i] = ptrs[iold++];
    else
        newptrs[i] = cache->totsize;
     btreeWriteIdbucket(cache,cbucket,newptrs[i]);

    btreeIdbucketDel(&cbucket);

#if AJINDEX_DEBUG
    if(iold < keylimit)
        ajDebug("btreeIdbucketsReorder '%S' %u -> %u",
                cache->filename, keylimit, iold);
#endif

    for(i = iold+1; i <= keylimit; i++)
    {
        btreePripageSetfree(cache, ptrs[i]);
    }

    /* Now write out a modified leaf with new keys/ptrs */

    nkeys = bucketlimit;
    btreeWriteNode(cache,leaf,newkeys,newptrs,nkeys);

    leaf->dirty = BT_DIRTY;

    if(nodetype == BT_ROOT)
    {
	leaf->dirty = BT_LOCK;
        leaf->lockfor = 1102;
    }

    btreeDeallocPriArray(cache, arrays);
    btreeDeallocPriArray(cache, newarrays);

    btreeIdbucketDel(&cbucket);
    ajListFree(&idlist);

    return ajTrue;
}
ID btreeNodeIsFull
TY static
MO ajindex
LB core
XX
DE Tests whether a node is full of keys
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD original page
PX
RT AjBool
RD true if full
RX
//


static AjBool btreeNodeIsFull(const AjPBtcache cache, AjPBtpage page)
{
    unsigned char *buf = NULL;
    ajuint nkeys = 0;

    /* ajDebug("In btreeNodeIsFull\n"); */

    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);

    if(nkeys == cache->porder - 1)
	return ajTrue;

    return ajFalse;
}
ID ajBtreeDumpKeywords
TY public
MO ajindex
LB core
XX
DE Read the leaves of a complete keywords tree, returning all identifiers
DE found within a range of total occurrences
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r dmin ajuint
PD minimum number of times the key should appear
PX
PN [3]
PA r dmax ajuint
PD maximum number of times the key should appear
PX
PN [4]
PA u outf AjPFile
PD output file
PX
RT void
RD 
RX
//


void ajBtreeDumpKeywords(AjPBtcache cache,
                         ajuint dmin, ajuint dmax, AjPFile outf)
{
    ajulong *parray;
    AjPBtpage page;
    unsigned char *buf;
    ajuint nodetype;
    ajuint i;
    ajuint j;
    ajulong kdups;
    
    AjPPribucket bucket;
    ajuint nkeys;
    ajulong right;
    AjPBtMem array = NULL;
    AjPList tlist  = NULL;
    ajuint dirtysave = 0;
    ajulong totkeys = 0L;
    ajulong totdups = 0L;

    if(!cache->secondary)
    {
        ajBtreeDumpIdentifiers(cache, dmin, dmax, outf);
        return;
    }

    array = btreeAllocPriArray(cache);
    parray = array->parray;

    page = btreePricacheRead(cache, 0);
    buf = page->buf;
    dirtysave  = page->dirty;
    page->dirty = BT_LOCK;
    page->lockfor = 1111;

    btreeGetPointers(cache,buf,&parray);
    GBT_NODETYPE(buf,&nodetype);

    while(nodetype != BT_LEAF && cache->plevel != 0)
    {
        page->dirty = dirtysave;
	page = btreePricacheRead(cache,parray[0]);
        dirtysave  =page->dirty;
        page->dirty = BT_LOCK;
        page->lockfor = 1112;
	buf = page->buf;
	btreeGetPointers(cache,buf,&parray);
	GBT_NODETYPE(buf,&nodetype);
    }

    do
    {
	GBT_NKEYS(buf,&nkeys);
	for(i=0;i<=nkeys;++i)
	{
	    bucket = btreePribucketRead(cache,parray[i]);
            btreePribucketSort(bucket);

	    for(j=0;jNentries;++j)
	    {
                totkeys++;

		kdups = btreeSecTreeCount(cache, bucket->codes[j]->treeblock);
                totdups += kdups;

		ajListstrFreeData(&tlist);

                if(kdups < dmin)
                    continue;
                if(dmax && kdups > dmax)
                    continue;
                ajFmtPrintF(outf,"%10Ld %S\n",
                            kdups, bucket->codes[j]->keyword);
	    }
	    btreePribucketDel(&bucket);
	}

	right = 0L;
	if(cache->plevel)
	{
	    GBT_RIGHT(buf,&right);
	    if(right)
	    {
                page->dirty = dirtysave;
		page = btreePricacheRead(cache,right);
                dirtysave  =page->dirty;
                page->dirty = BT_LOCK;
                page->lockfor = 1113;
		buf = page->buf;
		btreeGetPointers(cache,buf,&parray);
	    }
	}
    } while(right);

    btreeDeallocPriArray(cache,array);
    
    ajFmtPrintF(outf,"\nTotal keywords: %Lu IDs: %Lu\n", totkeys, totdups);
    return;
}
ID btreeNodeIsFullSec
TY static
MO ajindex
LB core
XX
DE Tests whether a secondary node is full of keys
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD original page
PX
RT AjBool
RD true if full
RX
//


static AjBool btreeNodeIsFullSec(const AjPBtcache cache, AjPBtpage page)
{
    unsigned char *buf = NULL;
    ajuint nkeys = 0;

    /* ajDebug("In btreeNodeIsFull\n"); */

    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);

    if(nkeys == cache->sorder - 1)
	return ajTrue;

    return ajFalse;
}
ID btreePrimaryInsertNonfull
TY static
MO ajindex
LB core
XX
DE Insert a key into a non-full primary node (identifiers or keywords)
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD original page
PX
PN [3]
PA r key const AjPStr
PD key to insert
PX
PN [4]
PA r less ajulong
PD less-than pointer
PX
PN [5]
PA r greater ajulong
PD greater-than pointer
PX
RT void
RD 
RX
//


static void btreePrimaryInsertNonfull(AjPBtcache cache, AjPBtpage page,
                                      const AjPStr key,
                                      ajulong less, ajulong greater)
{
    unsigned char *buf = NULL;

    AjPStr *karray     = NULL;
    ajulong *parray     = NULL;
    AjPBtMem arrays1 = NULL;

    ajuint nkeys  = 0;
    ajuint ipos   = 0;
    ajuint i;
    ajint  ii;
    ajuint count  = 0;

    ajulong lv = 0L;

    AjPBtpage ppage = NULL;
    ajulong pagepos   = 0L;

    ajuint nodetype = 0;
    
    /* ajDebug("In btreeInsertNonfull\n"); */

    arrays1 = btreeAllocPriArray(cache);
    karray = arrays1->karray;
    parray = arrays1->parray;
 
    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&nodetype);
    
    btreeGetKeys(cache,buf,&karray,&parray);

    i = 0;

    while(i!=nkeys && MAJSTRCMPS(key,karray[i]) >= 0)
	++i;

    ipos = i;

    count = nkeys - ipos;
    

    if(ipos == nkeys)
    {
	ajStrAssignS(&karray[ipos],key);
	parray[ipos+1] = greater;
	parray[ipos]   = less;
    }
    else
    {
	parray[nkeys+1] = parray[nkeys];

	for(ii=nkeys-1; count>0; --count, --ii)
	{
	    ajStrAssignS(&karray[ii+1],karray[ii]);
	    parray[ii+1] = parray[ii];
	}

	ajStrAssignS(&karray[ipos],key);
	parray[ipos] = less;
	parray[ipos+1] = greater;
    }

    ++nkeys;
    btreeWriteNode(cache,page,karray,parray,nkeys);

    if(nodetype == BT_ROOT)
    {
	page->dirty = BT_LOCK;
        page->lockfor = 1121;
    }
    
    pagepos = page->pagepos;
    ppage = btreePricacheRead(cache,less);
    lv = pagepos;
    SBT_PREV(ppage->buf,lv);
    ppage->dirty = BT_DIRTY;

    ppage = btreePricacheRead(cache,greater);
    lv = pagepos;
    SBT_PREV(ppage->buf,lv);
    ppage->dirty = BT_DIRTY;
    
    btreeDeallocPriArray(cache,arrays1);

    if(nodetype != BT_ROOT)
	btreePrimaryShift(cache,page);

    return;
}
ID btreePriInsertKey
TY static
MO ajindex
LB core
XX
DE Insert a key into a potentially full primary node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD original page
PX
PN [3]
PA r key const AjPStr
PD key to insert
PX
PN [4]
PA r less ajulong
PD less-than pointer
PX
PN [5]
PA r greater ajulong
PD greater-than pointer
PX
RT void
RD 
RX
//


static void btreePriInsertKey(AjPBtcache cache, AjPBtpage page,
                              const AjPStr key, ajulong less, ajulong greater)
{
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;
    unsigned char *tbuf = NULL;

    AjPStr *karray      = NULL;
    ajulong *parray      = NULL;
    AjPStr *tkarray     = NULL;
    ajulong *tparray     = NULL;
    AjPBtMem savekeyarrays = NULL;
    AjPBtMem arrays2 = NULL;

    ajuint nkeys    = 0;
    ajuint order    = 0;
    ajuint keypos   = 0;
    ajuint rkeyno   = 0;
    
    ajuint i = 0;
    ajuint n = 0;
    
    ajuint nodetype  = 0;
    AjPBtpage ipage = NULL;
    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage tpage = NULL;

    ajulong blockno  = 0L;
    ajulong rblockno = 0L;
    ajulong lblockno = 0L;
    ajulong ibn      = 0L;
    

    AjPStr mediankey  = NULL;
    ajulong medianless = 0L;
    ajulong mediangtr  = 0L;
    ajulong overflow   = 0L;
    ajulong prev       = 0L;
    ajuint totlen     = 0;
    
    ajulong lv = 0L;
    ajuint  v  = 0;
    
    if(!btreeNodeIsFull(cache,page))
    {
	btreePrimaryInsertNonfull(cache,page,key,less,greater);
	return;
    }
    
    order = cache->porder;
    lbuf = page->buf;
    GBT_NODETYPE(lbuf,&nodetype);
    page->dirty = BT_LOCK;
    page->lockfor = 1131;

    if(nodetype == BT_ROOT)
    {
	btreePriSplitroot(cache);

	if(page->pagepos)
	    page->dirty = BT_DIRTY;
        
        blockno = btreeGetBlockFirstS(cache,lbuf,key);

	ipage = btreePricacheRead(cache,blockno);
	btreePrimaryInsertNonfull(cache,ipage,key,less,greater);

	return;
    }


    savekeyarrays = btreeAllocPriArray(cache);
    arrays2 = btreeAllocPriArray(cache);
    karray = savekeyarrays->karray;
    parray = savekeyarrays->parray;
    tkarray = arrays2->karray;
    tparray = arrays2->parray;
    
    lpage = page;
    lbuf = lpage->buf;
    
    btreeGetKeys(cache,lbuf,&karray,&parray);

    GBT_BLOCKNUMBER(lbuf,&lblockno);
    rblockno = cache->totsize;
    rpage = btreePricacheNodenew(cache);
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1132;
    rbuf = rpage->buf;

    
    GBT_PREV(lbuf,&prev);
    lv = prev;
    SBT_PREV(rbuf,lv);

    nkeys = order - 1;
    keypos = nkeys / 2;

    if(!(nkeys % 2))
	--keypos;

    mediankey = ajStrNewS(karray[keypos]);
    medianless = lblockno;
    mediangtr  = rblockno;


    GBT_NODETYPE(lbuf,&nodetype);
    v = nodetype;
    SBT_NODETYPE(rbuf,v);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);


    totlen = 0;

    for(i=0;ibuf;
	lv = lblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }


    totlen = 0;

    for(i=keypos+1;idirty = BT_DIRTY;

    btreeWriteNode(cache,rpage,tkarray,tparray,rkeyno);

    for(i=0;ibuf;
	lv = rblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }


    ibn = rblockno;

    if(MAJSTRCMPS(key,mediankey)<0)
	ibn = lblockno;

    ipage = btreePricacheRead(cache,ibn);
    
    btreePrimaryInsertNonfull(cache,ipage,key,less,greater);

    btreeDeallocPriArray(cache, savekeyarrays);
    btreeDeallocPriArray(cache, arrays2);

    ipage = btreePricacheRead(cache,prev);

    btreePriInsertKey(cache,ipage,mediankey,medianless,mediangtr);
    ajStrDel(&mediankey);

    return;
}
ID btreePriSplitroot
TY static
MO ajindex
LB core
XX
DE Split the root node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT void
RD 
RX
//


static void btreePriSplitroot(AjPBtcache cache)
{
    AjPBtpage rootpage = NULL;
    AjPBtpage rpage    = NULL;
    AjPBtpage lpage    = NULL;
    AjPBtpage tpage    = NULL;

    AjPBtMem arrays1 = NULL;
    AjPBtMem arrays2 = NULL;
    AjPStr *karray     = NULL;
    AjPStr *tkarray    = NULL;
    ajulong *parray     = NULL;
    ajulong *tparray    = NULL;

    ajuint order     = 0;
    ajuint nkeys     = 0;
    ajuint keypos    = 0;
    
    ajulong rblockno = 0L;
    ajulong lblockno = 0L;
    
    AjPStr key = NULL;
    ajuint  i;
    ajuint  j;

    unsigned char *rootbuf = NULL;
    unsigned char *rbuf    = NULL;
    unsigned char *lbuf    = NULL;
    unsigned char *tbuf    = NULL;
    
    ajuint nodetype  = 0;
    ajulong overflow = 0L;
    ajulong zero     = 0L;
    ajuint rkeyno    = 0;

    ajulong lv = 0L;
    ajuint  v  = 0;
    
#if AJINDEX_DEBUG
    ajDebug("btreePriSplitroot zero\n");
#endif
    ++statCallPriSplitroot;

    /* ajDebug("In btreePriSplitroot\n"); */

    order = cache->porder;

    arrays1 = btreeAllocPriArray(cache);
    arrays2 = btreeAllocPriArray(cache);
    karray = arrays1->karray;
    parray = arrays1->parray;
    tkarray = arrays2->karray;
    tparray = arrays2->parray;

    key = ajStrNew();

    rootpage = btreePricacheLocate(cache,0L);

    if(!rootpage)
	ajFatal("Root page has been unlocked 1 cache %S", cache->filename);
    
    rootbuf = rootpage->buf;

    nkeys = order - 1;

    keypos = nkeys / 2;

    if(!(nkeys % 2))
	--keypos;

    rblockno = cache->totsize;
    rpage = btreePricacheNodenew(cache);
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1141;

    lblockno = cache->totsize;
    lpage = btreePricacheNodenew(cache);

    if(!cache->plevel)
    {
	lv = zero;
	SBT_LEFT(lpage->buf,lv);
	lv = rblockno;
	SBT_RIGHT(lpage->buf,lv);
	lv = lblockno;
	SBT_LEFT(rpage->buf,lv);
	lv = zero;
	SBT_RIGHT(rpage->buf,lv);
    }

    btreeGetKeys(cache,rootbuf,&karray,&parray);
    
    btreeWriteNodeSingle(cache,rootpage,karray[keypos],lblockno,rblockno);

    rootpage->dirty = BT_LOCK;
    rootpage->lockfor = 1142;

    rbuf = rpage->buf;
    lbuf = lpage->buf;
    
    if(cache->plevel)
	nodetype = BT_INTERNAL;
    else
	nodetype = BT_LEAF;

    v = nodetype;
    SBT_NODETYPE(rbuf,v);
    v = nodetype;
    SBT_NODETYPE(lbuf,v);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);
    lv = zero;
    SBT_PREV(rbuf,lv);
    lv = overflow;
    SBT_OVERFLOW(lbuf,lv);
    lv = zero;
    SBT_PREV(lbuf,lv);

    for(i=0;ibuf;
	lv = lblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }

    j = 0;

    for(i=keypos+1;idirty = BT_DIRTY;

    btreeWriteNode(cache,rpage,tkarray,tparray,rkeyno);

    for(i=0;ibuf;
	lv = rblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }


    for(i=0;iplevel;

    ajStrDel(&key);
    
    return;
}
ID btreeGetKeys
TY static
MO ajindex
LB core
XX
DE Get Keys and Pointers from an internal node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA w keys AjPStr**
PD keys
PX
PN [4]
PA w ptrs ajulong**
PD ptrs
PX
RT void
RD 
RX
//


static void btreeGetKeys(AjPBtcache cache, unsigned char *buf,
			 AjPStr **keys, ajulong **ptrs)
{
    AjPStr *karray = NULL;
    ajulong *parray = NULL;
    
    ajuint  nkeys;
    unsigned char *lenptr = NULL;
    unsigned char *keyptr = NULL;
    unsigned char *tbuf   = NULL;

    ajuint    i;
    ajuint    ival = 0;
    
    ajuint    len;

/*    ajulong   overflow = 0L;*/
/*    ajuint    pagesize = 0;*/
/*    AjPBtpage page = NULL;*/

    karray = *keys;
    parray = *ptrs;
    
    tbuf    = buf;

/*    pagesize = cache->pagesize;*/
    
    GBT_NKEYS(tbuf,&ival);
    nkeys = ival;
    if(!nkeys)
	ajFatal("GetKeys: No keys in node cache %S", cache->filename);

    lenptr =  PBT_KEYLEN(tbuf);
    keyptr = lenptr + nkeys * sizeof(ajuint);


    for(i=0; i pagesize)
//	{
//#if AJINDEX_DEBUG
//    	    ajDebug("GetKeys: Overflow\n");
//#endif
//	    GBT_OVERFLOW(tbuf,&overflow);
//	    page = btreePricacheRead(cache,overflow);
//	    tbuf = page->buf;
//	    GBT_NODETYPE(tbuf,&ival);
//
//	    if(ival != BT_OVERFLOW)
//		ajFatal("Overflow node expected but not found for cache %S",
//                        cache->filename);
//	    /#
//	     ** The length pointer is restricted to the initial page.
//	     ** The keyptr in overflow pages starts at the Key Lengths
//	     ** position!
//	     #/
//	    keyptr = PBT_KEYLEN(tbuf);
//	}
*/

	ajStrAssignC(&karray[i],(const char *)keyptr);
	keyptr += len;

	BT_GETAJULONG(keyptr,&parray[i]);
	keyptr += sizeof(ajulong);
	lenptr += sizeof(ajuint);
    }
    
/*
//    if((ajuint)((keyptr-tbuf+1) + sizeof(ajulong)) > pagesize)
//    {
//	GBT_OVERFLOW(tbuf,&overflow);
//	page = btreePricacheRead(cache,overflow);
//	tbuf = page->buf;
//	GBT_NODETYPE(tbuf,&ival);
//
//	if(ival != BT_OVERFLOW)
//	    ajFatal("Overflow node expected but not found at end for cache %S",
//                    cache->filename);
//	/#
//	 ** The length pointer is restricted to the initial page.
//	 ** The keyptr in overflow pages starts at the Key Lengths
//	 ** position!
//	 #/
//	keyptr = PBT_KEYLEN(tbuf);
//    }
*/    

    BT_GETAJULONG(keyptr,&parray[i]);

    return;
}
ID btreeGetPointers
TY static
MO ajindex
LB core
XX
DE Get Pointers from an internal node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA w ptrs ajulong**
PD ptrs
PX
RT ajuint
RD Number of pointers
RX
//


static ajuint btreeGetPointers(AjPBtcache cache, unsigned char *buf,
                               ajulong **ptrs)
{
    ajulong *parray = NULL;
    
    ajuint  nkeys;
    unsigned char *lenptr = NULL;
    unsigned char *keyptr = NULL;
    unsigned char *tbuf   = NULL;

    ajuint    i;
    ajuint    ival = 0;
    
    ajuint    len;
/*    ajuint    pagesize = 0;*/
/*    ajulong   overflow = 0L;*/
/*    AjPBtpage page = NULL;*/

    parray = *ptrs;
    
    tbuf    = buf;

/*    pagesize = cache->pagesize;*/
    
    GBT_NKEYS(tbuf,&ival);
    nkeys = ival;
    if(!nkeys)
	ajFatal("btreeGetPointers: No keys in node cache %S",
                cache->filename);

    lenptr =  PBT_KEYLEN(tbuf);
    keyptr = lenptr + nkeys * sizeof(ajuint);

    GBT_NODETYPE(tbuf,&ival);

    for(i=0; i pagesize)
//	{
//	    GBT_OVERFLOW(tbuf,&overflow);
//	    page = ajBtreeCacheRead(cache,overflow); /# pri or sec possible #/
//	    tbuf = page->buf;
//	    GBT_NODETYPE(tbuf,&ival);
//	    if(ival != BT_OVERFLOW)
//		ajFatal("Overflow node expected but not found cache %S",
//                        cache->filename);
//	    /#
//	     ** The length pointer is restricted to the initial page.
//	     ** The keyptr in overflow pages starts at the Key Lengths
//	     ** position!
//	     #/
//	    keyptr = PBT_KEYLEN(tbuf);
//	}
*/

	keyptr += len;

	BT_GETAJULONG(keyptr,&parray[i]);
	keyptr += sizeof(ajulong);
	lenptr += sizeof(ajuint);
    }
    
/*
//    if((ajuint)((keyptr-tbuf+1) + sizeof(ajulong)) > pagesize)
//    {
//	/# ajDebug("GetPointers: Overflow\n"); #/
//	GBT_OVERFLOW(tbuf,&overflow);
//	page = ajBtreeCacheRead(cache,overflow); /# pri or sec possible #/
//	tbuf = page->buf;
//	GBT_NODETYPE(tbuf,&ival);
//	if(ival != BT_OVERFLOW)
//	    ajFatal("Overflow node expected but not found cache %S",
//                    cache->filename);
//	/#
//	 ** The length pointer is restricted to the initial page.
//	 ** The keyptr in overflow pages starts at the Key Lengths
//	 ** position!
//	 #/
//	keyptr = PBT_KEYLEN(tbuf);
//    }
*/    

    BT_GETAJULONG(keyptr,&parray[i]);

    return i;
}
ID btreeGetBlockC
TY static
MO ajindex
LB core
XX
DE Get Block number matching a key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA r ckey const char*
PD key
PX
RT ajulong
RD Block number
RX
//


static ajulong btreeGetBlockC(AjPBtcache cache, unsigned char *buf,
                              const char* ckey)
{
    ajulong blockno = 0L;

    ajuint m;
    unsigned char *lenptr = NULL;
    unsigned char *keyptr = NULL;
    unsigned char *tbuf   = NULL;

    ajuint    i;
    ajuint    ival = 0;
    ajuint    len;
    
/*    AjPBtpage page = NULL;*/
/*    ajuint    pagesize = 0;*/
/*    ajulong   overflow = 0L;*/

/*    pagesize = cache->pagesize;*/

    tbuf = buf;

    GBT_NKEYS(tbuf,&m);
    if(!m)
	ajFatal("btreeGetBlockC: No keys in node cache %S",
                cache->filename);

    lenptr =  PBT_KEYLEN(tbuf);
    keyptr = lenptr + m * sizeof(ajuint);

    for(i=0; i pagesize)
//	{
//    	    /# ajDebug("btreeGetBlockC: Overflow\n"); #/
//	    GBT_OVERFLOW(tbuf,&overflow);
//	    page = ajBtreeCacheRead(cache,overflow); /# pri or sec possible #/
//	    tbuf = page->buf;
//	    GBT_NODETYPE(tbuf,&ival);
//	    if(ival != BT_OVERFLOW)
//		ajFatal("Overflow node expected but not found cache %S",
//                        cache->filename);
//	    /#
//	     ** The length pointer is restricted to the initial page.
//	     ** The keyptr in overflow pages starts at the Key Lengths
//	     ** position!
//	     #/
//	    keyptr = PBT_KEYLEN(tbuf);
//	}
*/

        if(strcmp((const char*)keyptr, ckey) > 0)
        {
            keyptr += len;

            BT_GETAJULONG(keyptr,&blockno);
            return blockno;
        }

        keyptr += len;
	keyptr += sizeof(ajulong);
	lenptr += sizeof(ajuint);
    }
    
/*
//    if((ajuint)((keyptr-tbuf+1) + sizeof(ajulong)) > pagesize)
//    {
//	/# ajDebug("btreeGetBlockC: Overflow\n"); #/
//	GBT_OVERFLOW(tbuf,&overflow);
//	page = ajBtreeCacheRead(cache,overflow); /# pri or sec possible #/
//	tbuf = page->buf;
//	GBT_NODETYPE(tbuf,&ival);
//	if(ival != BT_OVERFLOW)
//	    ajFatal("Overflow node expected but not found cache %S",
//                    cache->filename);
//	/#
//	 ** The length pointer is restricted to the initial page.
//	 ** The keyptr in overflow pages starts at the Key Lengths
//	 ** position!
//	 #/
//	keyptr = PBT_KEYLEN(tbuf);
//    }
*/    

    BT_GETAJULONG(keyptr,&blockno);
    
    return blockno;
}
ID btreeGetBlockN
TY static
MO ajindex
LB core
XX
DE Get Block number matching a numeric key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA r numkey ajulong
PD key
PX
RT ajulong
RD Block number
RX
//


static ajulong btreeGetBlockN(AjPBtcache cache, unsigned char *buf,
                              ajulong numkey)
{
    ajulong blockno = 0L;

    ajuint m;
    unsigned char *pptr = NULL;
    unsigned char *kptr = NULL;

    ajuint    i;
    ajulong   kval = 0L;

    GBT_NKEYS(buf,&m);
    if(!m)
	ajFatal("btreeGetBlockN: No keys in node cache %S", cache->filename);

    kptr = PBT_KEYLEN(buf);
    pptr = kptr + m * sizeof(ajulong);

    for(i=0; i numkey)
        {
            BT_GETAJULONG(pptr,&blockno);
            return blockno;
        }

	kptr += sizeof(ajulong);
	pptr += sizeof(ajulong);
    }
    

    BT_GETAJULONG(pptr,&blockno);

    return blockno;
}
ID btreeGetBlockFirstN
TY static
MO ajindex
LB core
XX
DE Get Block number of first block if more than numeric key
DE or second block
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA r numkey ajulong
PD key
PX
RT ajulong
RD Block number
RX
//


static ajulong btreeGetBlockFirstN(AjPBtcache cache, unsigned char *buf,
                                   ajulong numkey)
{
    ajulong blockno = 0L;

    ajuint m;
    unsigned char *pptr = NULL;
    unsigned char *kptr = NULL;

    ajulong   kval = 0L;
    
    GBT_NKEYS(buf,&m);
    if(!m)
	ajFatal("btreeGetBlockFirstN: No keys in node cache %S",
                cache->filename);

    kptr = PBT_KEYLEN(buf);
    pptr = kptr + m * sizeof(ajulong);

    BT_GETAJULONG(kptr,&kval);
	
    if(kval > numkey)
    {
        BT_GETAJULONG(pptr,&blockno);
        return blockno;
    }

    kptr += sizeof(ajulong);
    pptr += sizeof(ajulong);
    

    BT_GETAJULONG(pptr,&blockno);

    return blockno;
}
ID btreeGetBlockS
TY static
MO ajindex
LB core
XX
DE Get Block number matching a key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA r key const AjPStr
PD key
PX
RT ajulong
RD Block number
RX
//


static ajulong btreeGetBlockS(AjPBtcache cache, unsigned char *buf,
                             const AjPStr key)
{
    return btreeGetBlockC(cache, buf, MAJSTRGETPTR(key));
}
ID btreeGetBlockFirstC
TY static
MO ajindex
LB core
XX
DE Get Block number of first block if more than key
DE or second block
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA r ckey const char*
PD key
PX
PN [4]
PA r clen ajuint
PD Maximum length of key to test (e.g. for wildcards)
PD or zero to test full key
PX
RT ajulong
RD Block number
RX
//


static ajulong btreeGetBlockFirstC(AjPBtcache cache, unsigned char *buf,
                                  const char* ckey, ajuint clen)
{
    ajulong blockno = 0L;

    ajuint m;
    unsigned char *lenptr = NULL;
    unsigned char *keyptr = NULL;
    unsigned char *tbuf   = NULL;

    ajuint    ival = 0;
    ajuint    len;
    ajuint    klen = 0;
    
    tbuf    = buf;

    GBT_NKEYS(tbuf,&m);
    if(!m)
	ajFatal("btreeGetBlockFirstC: No keys in node cache %S",
                cache->filename);

    lenptr =  PBT_KEYLEN(tbuf);
    keyptr = lenptr + m * sizeof(ajuint);

    BT_GETAJUINT(lenptr,&ival);
    len = ival+1;
    klen = ival;
    if(clen && (klen > clen))
        klen = clen;
	
    if(strncmp((const char*)keyptr, ckey, klen) > 0)
    {
        keyptr += len;
        
        BT_GETAJULONG(keyptr,&blockno);
        return blockno;
    }

    keyptr += len;
    keyptr += sizeof(ajulong);
    lenptr += sizeof(ajuint);
    
    BT_GETAJULONG(keyptr,&blockno);

    return blockno;
}
ID btreeGetBlockFirstS
TY static
MO ajindex
LB core
XX
DE Get Block number matching a key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA r key const AjPStr
PD key
PX
RT ajulong
RD Block number
RX
//


static ajulong btreeGetBlockFirstS(AjPBtcache cache, unsigned char *buf,
                                  const AjPStr key)
{
    return btreeGetBlockFirstC(cache, buf, MAJSTRGETPTR(key),0);
}
ID btreeIdCompare
TY static
MO ajindex
LB core
XX
DE Comparison function for ajListSort
XX
PN [1]
PA r a const void*
PD ID 1
PX
PN [2]
PA r b const void*
PD ID 2
PX
RT ajint
RD 0 = bases match
RX
//


static ajint btreeIdCompare(const void *a, const void *b)
{
    return MAJSTRCMPS((*(AjPBtId const *)a)->id,
                      (*(AjPBtId const *)b)->id);
}
ID btreeNumIdCompare
TY static
MO ajindex
LB core
XX
DE Comparison function for ajListSort
XX
PN [1]
PA r a const void*
PD ID 1
PX
PN [2]
PA r b const void*
PD ID 2
PX
RT ajint
RD 0 = bases match
RX
//


static ajint btreeNumIdCompare(const void *a, const void *b)
{
    ajlong val;

    val = (ajlong) ((*(AjPBtNumId const *)a)->offset -
                    (*(AjPBtNumId const *)b)->offset);

    if(!val)
      return 0;

    return (val < 0L) ? -1 : 1;
}
ID btreeWriteNode
TY static
MO ajindex
LB core
XX
DE Write an internal node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u spage AjPBtpage
PD buffer
PX
PN [3]
PA r keys AjPStr const*
PD keys
PX
PN [4]
PA r ptrs const ajulong*
PD page pointers
PX
PN [5]
PA r nkeys ajuint
PD number of keys
PX
RT void
RD 
RX
//


static void btreeWriteNode(AjPBtcache cache, AjPBtpage spage,
			   AjPStr const *keys, const ajulong *ptrs,
                           ajuint nkeys)
{
    unsigned char *lbuf   = NULL;
    unsigned char *tbuf   = NULL;
    unsigned char *lenptr = NULL;
    unsigned char *keyptr = NULL;
    AjPBtpage page        = NULL;
    
    ajulong   overflow = 0L;
    ajulong   blockno  = 0L;

    ajuint   i;
    ajuint   len;
    ajuint    v  = 0;
    ajulong   lv = 0L;
    ajuint totlen = 0;

    (void) cache;               /* make it used */

    lbuf = spage->buf;
    tbuf = lbuf;
    page = spage;

    v = nkeys;
    SBT_NKEYS(lbuf,v);
    
    lenptr = PBT_KEYLEN(lbuf);
    keyptr = lenptr + nkeys * sizeof(ajuint);

    for(i=0;i cache->pagesize)
//	    ajFatal("WriteNode: Too many key lengths for available pagesize");
*/

	len = ajStrGetLen(keys[i]);
	v = len;
	BT_SETAJUINT(lenptr,v);
        totlen += len;
	lenptr += sizeof(ajuint);
    }

    v = totlen;
    SBT_TOTLEN(lbuf,v);

    GBT_OVERFLOW(lbuf,&overflow);
    GBT_BLOCKNUMBER(lbuf, &blockno);

    for(i=0;i cache->pagesize)
//	{
//	    if(!overflow)		/# No overflow buckets yet #/
//	    {
//		page->dirty = BT_DIRTY;
//		blockno = cache->totsize;
//		lv = blockno;
//		SBT_OVERFLOW(tbuf,lv);
//		page = ajBtreeCacheWriteNodenew(cache); /# pri/sec possible #/
//		tbuf = page->buf;
//		v = BT_OVERFLOW;
//		SBT_NODETYPE(tbuf,v);
//		lv = blockno;
//		SBT_BLOCKNUMBER(tbuf,lv);
//	    }
//	    else
//	    {
//		page = ajBtreeCacheRead(cache,overflow); /# pri/sec possible #/
//		tbuf = page->buf;
//		GBT_OVERFLOW(tbuf,&overflow);
//	    }
//
//	    keyptr = PBT_KEYLEN(tbuf);
//	    page->dirty = BT_DIRTY;
//	}
*/

	sprintf((char *)keyptr,"%s",ajStrGetPtr(keys[i]));
	keyptr += len;
	lv = ptrs[i];
	BT_SETAJULONG(keyptr,lv);
	keyptr += sizeof(ajulong);
    }
    


/*
//    if((ajuint)((keyptr-tbuf+1) + sizeof(ajulong)) > cache->pagesize)
//    {
//	page->dirty = BT_DIRTY;
//
//	if(!overflow)			/# No overflow buckets yet #/
//	{
//	    blockno = cache->totsize;
//	    lv = blockno;
//	    SBT_OVERFLOW(tbuf,lv);
//	    page = ajBtreeCacheWriteNodenew(cache); /# pri or sec possible #/
//	    tbuf = page->buf;
//	    v = BT_OVERFLOW;
//	    SBT_NODETYPE(tbuf,v);
//	}
//	else
//	{
//	    page = ajBtreeCacheRead(cache,overflow); /# pri or sec possible #/
//	    tbuf = page->buf;
//	}
//
//	keyptr = PBT_KEYLEN(tbuf);
//    }
*/    

    page->dirty = BT_DIRTY;

    overflow = 0L;
    SBT_OVERFLOW(tbuf,overflow);

    lv = ptrs[i];
    BT_SETAJULONG(keyptr,lv);
    keyptr += sizeof(ajulong);

    return;
}
ID btreeWriteNodeSingle
TY static
MO ajindex
LB core
XX
DE Write an internal node with a single key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u spage AjPBtpage
PD buffer
PX
PN [3]
PA r key const AjPStr
PD key
PX
PN [4]
PA r lptr ajulong
PD left page pointer
PX
PN [5]
PA r rptr ajulong
PD right page pointer
PX
RT void
RD 
RX
//


static void btreeWriteNodeSingle(AjPBtcache cache, AjPBtpage spage,
                                 const AjPStr key, ajulong lptr,
                                 ajulong rptr)
{
    unsigned char *lbuf   = NULL;
    unsigned char *tbuf   = NULL;
    unsigned char *lenptr = NULL;
    unsigned char *keyptr = NULL;
    AjPBtpage page        = NULL;
    
    ajulong   overflow = 0L;

    ajuint   len;
    ajuint    v  = 0;
    ajulong   lv = 0L;

    ajuint nkeys = 1;

    /* ajDebug("In btreeWriteNodeSingle\n"); */

    (void) cache;               /* make it used */

    lbuf = spage->buf;
    tbuf = lbuf;
    page = spage;

    v = 1;
    SBT_NKEYS(lbuf,v);
    lenptr = PBT_KEYLEN(lbuf);
    keyptr = lenptr + nkeys * sizeof(ajuint);

    len = MAJSTRGETLEN(key);
    v = len;
    BT_SETAJUINT(lenptr,v);
    lenptr += sizeof(ajuint);
    v = len;
    SBT_TOTLEN(lbuf,v);

    GBT_OVERFLOW(lbuf,&overflow);

    len = MAJSTRGETLEN(key) + 1;

    sprintf((char *)keyptr,"%s",MAJSTRGETPTR(key));
    keyptr += len;
    lv = lptr;
    BT_SETAJULONG(keyptr,lv);
    keyptr += sizeof(ajulong);
    
    overflow = 0L;
    SBT_OVERFLOW(tbuf,overflow);

    lv = rptr;
    BT_SETAJULONG(keyptr,lv);
    keyptr += sizeof(ajulong);

    page->dirty = BT_DIRTY;

    return;
}
ID btreeIdentQueryId
TY static
MO ajindex
LB core
XX
DE Get an ID structure from a leaf node given an identifier
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key
PX
RT AjPBtId
RD Btree ID object
RX
//


static AjPBtId btreeIdentQueryId(AjPBtcache cache, const AjPStr key)
{
    AjPBtpage page   = NULL;
    AjPIdbucket bucket = NULL;
    AjPBtId   id     = NULL;
    AjPBtId   tid    = NULL;
    
    unsigned char *buf = NULL;

    ajuint nentries = 0;
    
    ajuint i;
    ajuint iref;

    ajulong blockno = 0L;
    AjBool found   = ajFalse;

    AjPStr keystr = NULL;

    /* ajDebug("In ajBtreeIdentQueryId\n"); */

    if(!cache->countunique && !cache->countall)
        return NULL;

    keystr = ajStrNewS(key);
    ajStrFmtQuery(&keystr);

    page = btreeIdentFind(cache,keystr);
    buf = page->buf;

    blockno = btreeGetBlockS(cache,buf,keystr);

    bucket = btreeReadIdbucket(cache,blockno);
    
    nentries = bucket->Nentries;

    found = ajFalse;

    for(i=0;iIds[i]->id, keystr))
	{
	    found = ajTrue;
	    break;
	}

    if(found)
    {
	id  = ajBtreeIdNew(cache->refcount);
	tid = bucket->Ids[i];
	ajStrAssignS(&id->id,tid->id);
	id->dups = tid->dups;
	id->dbno = tid->dbno;
	id->offset = tid->offset;

        if(cache->refcount)
	{
            for(iref=0; iref < cache->refcount; iref++)
                id->refoffsets[iref] = tid->refoffsets[iref];
        }
    }

    btreeIdbucketDel(&bucket);
    ajStrDel(&keystr);

    if(!found)
	return NULL;

    return id;
}
ID btreeIdentQueryHit
TY static
MO ajindex
LB core
XX
DE Get a hit structure from a leaf node given an identifier
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key
PX
RT AjPBtHit
RD Btree hit object
RX
//


static AjPBtHit btreeIdentQueryHit(AjPBtcache cache, const AjPStr key)
{
    AjPBtpage page   = NULL;
    AjPIdbucket bucket = NULL;
    AjPBtHit  hit    = NULL;
    AjPBtId   tid    = NULL;
    
    unsigned char *buf = NULL;

    ajuint nentries = 0;
    
    ajuint i;

    ajulong blockno = 0L;
    AjBool found   = ajFalse;

    AjPStr keystr = NULL;

    /* ajDebug("In ajBtreeIdentQueryHit\n"); */

    if(!cache->countunique && !cache->countall)
        return NULL;

    keystr = ajStrNewS(key);
    ajStrFmtQuery(&keystr);

    page = btreeIdentFind(cache,keystr);
    buf = page->buf;

    blockno = btreeGetBlockS(cache,buf,keystr);

    bucket = btreeReadIdbucket(cache,blockno);
    
    nentries = bucket->Nentries;

    found = ajFalse;

    for(i=0;iIds[i]->id, keystr))
	{
	    found = ajTrue;
	    break;
	}

    if(found)
    {
	tid = bucket->Ids[i];
	hit = ajBtreeHitNewId(tid);
    }

    btreeIdbucketDel(&bucket);
    ajStrDel(&keystr);

    if(!found)
	return NULL;

    return hit;
}
ID btreeIdentQueryHitref
TY static
MO ajindex
LB core
XX
DE Get a reference hit structure from a leaf node given an identifier
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key
PX
RT AjPBtHitref
RD Btree hit object
RX
//


static AjPBtHitref btreeIdentQueryHitref(AjPBtcache cache, const AjPStr key)
{
    AjPBtpage page   = NULL;
    AjPIdbucket bucket = NULL;
    AjPBtHitref hitref = NULL;
    AjPBtId   tid    = NULL;
    
    unsigned char *buf = NULL;

    ajuint nentries = 0;
    
    ajuint i;

    ajulong blockno = 0L;
    AjBool found   = ajFalse;

    AjPStr keystr = NULL;

    /* ajDebug("In ajBtreeIdentQueryHit\n"); */

    if(!cache->countunique && !cache->countall)
        return NULL;

    keystr = ajStrNewS(key);
    ajStrFmtQuery(&keystr);

    page = btreeIdentFind(cache,keystr);
    buf = page->buf;

    blockno = btreeGetBlockS(cache,buf,keystr);

    bucket = btreeReadIdbucket(cache,blockno);
    
    nentries = bucket->Nentries;

    found = ajFalse;

    for(i=0;iIds[i]->id, keystr))
	{
	    found = ajTrue;
	    break;
	}

    if(found)
    {
	tid = bucket->Ids[i];
	hitref = ajBtreeHitrefNewId(tid);
    }

    btreeIdbucketDel(&bucket);
    ajStrDel(&keystr);

    if(!found)
	return NULL;

    return hitref;
}
ID ajBtreeIdentFetchId
TY public
MO ajindex
LB core
XX
DE Get an ID structure from a leaf node given an identifier
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key
PX
PN [3]
PA w btidlist AjPList
PD List of ID objects
PX
RT void
RD 
RX
//


void ajBtreeIdentFetchId(AjPBtcache cache, const AjPStr key,
                         AjPList btidlist)
{
    AjPBtpage page   = NULL;
    AjPIdbucket bucket = NULL;
    AjPBtId   id     = NULL;
    AjPBtId   tid    = NULL;
    
    unsigned char *buf = NULL;

    ajuint nentries = 0;
    
    ajuint i;
    ajuint iref;

    ajulong blockno = 0L;
    AjBool found   = ajFalse;

    AjPStr keystr = NULL;

    /* ajDebug("In ajBtreeIdentFetch\n"); */

    if(!cache->countunique && !cache->countall)
        return;

    keystr = ajStrNewS(key);
    ajStrFmtQuery(&keystr);

    page = btreeIdentFind(cache,keystr);
    buf = page->buf;

    blockno = btreeGetBlockS(cache,buf,keystr);

    bucket = btreeReadIdbucket(cache,blockno);
    
    nentries = bucket->Nentries;

    found = ajFalse;

    for(i=0;iIds[i]->id, keystr))
	{
	    found = ajTrue;
	    break;
	}

    if(found)
    {
	id  = ajBtreeIdNew(cache->refcount);
	tid = bucket->Ids[i];
	ajStrAssignS(&id->id,tid->id);
	id->dups = tid->dups;
	id->dbno = tid->dbno;
	id->offset = tid->offset;

        if(cache->refcount)
	{
            for(iref=0; iref < cache->refcount; iref++)
                id->refoffsets[iref] = tid->refoffsets[iref];
        }
    }

    btreeIdbucketDel(&bucket);
    ajStrDel(&keystr);

    if(!found)
	return;

    if(!id->dups)
    {
        ajListPushAppend(btidlist,(void *)id);
        id = NULL;
    }
    else
    {
        btreeIdentFetchMulti(cache, id->id, id->offset,
                             btidlist);
        ajBtreeIdDel(&id);
    }

    return;
}
ID ajBtreeIdentFetchHit
TY public
MO ajindex
LB core
XX
DE Get an ID hit structure from a leaf node given an identifier
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key
PX
PN [3]
PA w hitlist AjPList
PD List of hit objects
PX
RT void
RD 
RX
//


void ajBtreeIdentFetchHit(AjPBtcache cache, const AjPStr key,
                          AjPList hitlist)
{
    AjPBtpage page   = NULL;
    AjPIdbucket bucket = NULL;
    AjPBtHit  hit    = NULL;
    AjPBtId   tid    = NULL;
    
    unsigned char *buf = NULL;

    ajuint nentries = 0;
    
    ajuint i;

    ajulong blockno = 0L;
    AjBool found   = ajFalse;

    AjPStr keystr = NULL;

    /* ajDebug("In ajBtreeIdentFetch\n"); */

    if(!cache->countunique && !cache->countall)
        return;

    if(cache->refcount)
        ajWarn("ajBtreeIdentFetchHit called for cache '%S' "
               "with %u reference files",
               cache->filename, cache->refcount);

    keystr = ajStrNewS(key);
    ajStrFmtQuery(&keystr);

    page = btreeIdentFind(cache,keystr);
    buf = page->buf;

    blockno = btreeGetBlockS(cache,buf,keystr);

    bucket = btreeReadIdbucket(cache,blockno);
    
    nentries = bucket->Nentries;

    found = ajFalse;

    for(i=0;iIds[i]->id, keystr))
	{
	    found = ajTrue;
	    break;
	}

    if(found)
    {
	tid = bucket->Ids[i];
	hit = ajBtreeHitNewId(tid);
    }

    ajStrDel(&keystr);

    if(!found)
    {
        btreeIdbucketDel(&bucket);
	return;
    }
    
    if(!tid->dups)
    {
        ajListPushAppend(hitlist,(void *)hit);
        hit = NULL;
    }
    else
    {
        btreeIdentFetchMultiHit(cache, tid->offset,
                                hitlist);
        ajBtreeHitDel(&hit);
    }

    btreeIdbucketDel(&bucket);

    return;
}
ID ajBtreeIdentFetchHitref
TY public
MO ajindex
LB core
XX
DE Get an ID reference hit structure from a leaf node given an identifier
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key
PX
PN [3]
PA w hitlist AjPList
PD List of reference hit objects
PX
RT void
RD 
RX
//


void ajBtreeIdentFetchHitref(AjPBtcache cache, const AjPStr key,
                          AjPList hitlist)
{
    AjPBtpage page   = NULL;
    AjPIdbucket bucket = NULL;
    AjPBtHitref hitref = NULL;
    AjPBtId   tid    = NULL;
    
    unsigned char *buf = NULL;

    ajuint nentries = 0;
    
    ajuint i;

    ajulong blockno = 0L;
    AjBool found   = ajFalse;

    AjPStr keystr = NULL;

    /* ajDebug("In ajBtreeIdentFetch\n"); */

    if(!cache->countunique && !cache->countall)
        return;

    if(cache->refcount != 1)
        ajWarn("ajBtreeIdentFetchHitref called for cache '%S' "
               "with %u reference files",
               cache->filename, cache->refcount);

    keystr = ajStrNewS(key);
    ajStrFmtQuery(&keystr);

    page = btreeIdentFind(cache,keystr);
    buf = page->buf;

    blockno = btreeGetBlockS(cache,buf,keystr);

    bucket = btreeReadIdbucket(cache,blockno);
    
    nentries = bucket->Nentries;

    found = ajFalse;

    for(i=0;iIds[i]->id, keystr))
	{
	    found = ajTrue;
	    break;
	}

    if(found)
    {
	tid = bucket->Ids[i];
	hitref = ajBtreeHitrefNewId(tid);
    }

    ajStrDel(&keystr);

    if(!found)
    {
        btreeIdbucketDel(&bucket);
	return;
    }
    
    if(!tid->dups)
    {
        ajListPushAppend(hitlist,(void *)hitref);
        hitref = NULL;
    }
    else
    {
        btreeIdentFetchMultiHitref(cache, tid->offset,
                                hitlist);
        ajBtreeHitrefDel(&hitref);
    }

    btreeIdbucketDel(&bucket);

    return;
}
ID ajBtreeWriteParamsC
TY public
MO ajindex
LB core
XX
DE Write B+ tree parameters to file
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
PN [2]
PA r fntxt const char*
PD file name
PX
PN [3]
PA r exttxt const char*
PD index file extension name
PX
PN [4]
PA r idirtxt const char*
PD index file directory
PX
RT void
RD 
RX
//


void ajBtreeWriteParamsC(const AjPBtcache cache, const char *fntxt,
			 const char *exttxt, const char *idirtxt)
{
    AjPStr  fname = NULL;
    AjPFile outf  = NULL;

    fname = ajStrNewRes(128);

    if(!*idirtxt)
      ajFmtPrintS(&fname,"%s.p%s",fntxt,exttxt);
    else
      ajFmtPrintS(&fname,"%s%s%s.p%s",idirtxt,SLASH_STRING,fntxt,exttxt);

    if(!(outf = ajFileNewOutNameS(fname)))
	ajFatal("Cannot open param file %S\n",fname);

    if(cache->secondary)
        ajFmtPrintF(outf,"Type      %s\n","Secondary");
    else
        ajFmtPrintF(outf,"Type         %s\n","Identifier");
    ajFmtPrintF(outf,"Compress     %B\n",cache->compressed);
    ajFmtPrintF(outf,"Pages        %Lu\n",cache->pripagecount);
    ajFmtPrintF(outf,"Secpages     %Lu\n",cache->secpagecount);
    ajFmtPrintF(outf,"Order        %u\n",cache->porder);
    ajFmtPrintF(outf,"Fill         %u\n",cache->pnperbucket);
    ajFmtPrintF(outf,"Level        %u\n",cache->plevel);
    ajFmtPrintF(outf,"Pagesize     %u\n",cache->pripagesize);
    ajFmtPrintF(outf,"Cachesize    %u\n",cache->pricachesize);
    ajFmtPrintF(outf,"Order2       %u\n",cache->sorder);
    ajFmtPrintF(outf,"Fill2        %u\n",cache->snperbucket);
    ajFmtPrintF(outf,"Secpagesize  %u\n",cache->secpagesize);
    ajFmtPrintF(outf,"Seccachesize %u\n",cache->seccachesize);
    ajFmtPrintF(outf,"Count        %Lu\n",cache->countunique);
    ajFmtPrintF(outf,"Fullcount    %Lu\n",cache->countall);
    ajFmtPrintF(outf,"Kwlimit      %u\n",cache->keylimit);
    if(cache->secondary)
        ajFmtPrintF(outf,"Idlimit      %u\n",cache->idlimit);
    else
        ajFmtPrintF(outf,"Reffiles     %u\n",cache->refcount);

    ajFileClose(&outf);
    ajStrDel(&fname);

    return;
}
ID ajBtreeWriteParamsS
TY public
MO ajindex
LB core
XX
DE Write B+ tree parameters to file
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
PN [2]
PA r fn const AjPStr
PD file name
PX
PN [3]
PA r ext const AjPStr
PD index file extension name
PX
PN [4]
PA r idir const AjPStr
PD index file directory
PX
RT void
RD 
RX
//


void ajBtreeWriteParamsS(const AjPBtcache cache, const AjPStr fn,
                         const AjPStr ext, const AjPStr idir)
{
    AjPStr  fname = NULL;
    AjPFile outf  = NULL;

    fname = ajStrNewRes(128);

    if(ajStrGetLen(idir))
      ajFmtPrintS(&fname,"%S%s%S.p%S",idir,SLASH_STRING,fn,ext);
    else
        ajFmtPrintS(&fname,"%S.p%S",fn,ext);

    if(!(outf = ajFileNewOutNameS(fname)))
	ajFatal("Cannot open param file %S\n",fname);

    if(cache->secondary)
        ajFmtPrintF(outf,"Type         %s\n","Secondary");
    else
        ajFmtPrintF(outf,"Type         %s\n","Identifier");
    ajFmtPrintF(outf,"Compress     %B\n",cache->compressed);
    ajFmtPrintF(outf,"Pages        %Lu\n",cache->pripagecount);
    ajFmtPrintF(outf,"Secpages     %Lu\n",cache->secpagecount);
    ajFmtPrintF(outf,"Order        %u\n",cache->porder);
    ajFmtPrintF(outf,"Fill         %u\n",cache->pnperbucket);
    ajFmtPrintF(outf,"Level        %u\n",cache->plevel);
    ajFmtPrintF(outf,"Pagesize     %u\n",cache->pripagesize);
    ajFmtPrintF(outf,"Cachesize    %u\n",cache->pricachesize);
    ajFmtPrintF(outf,"Order2       %u\n",cache->sorder);
    ajFmtPrintF(outf,"Fill2        %u\n",cache->snperbucket);
    ajFmtPrintF(outf,"Secpagesize  %u\n",cache->secpagesize);
    ajFmtPrintF(outf,"Seccachesize %u\n",cache->seccachesize);
    ajFmtPrintF(outf,"Count        %Lu\n",cache->countunique);
    ajFmtPrintF(outf,"Fullcount    %Lu\n",cache->countall);
    ajFmtPrintF(outf,"Kwlimit      %u\n",cache->keylimit);
    if(cache->secondary)
        ajFmtPrintF(outf,"Idlimit      %u\n",cache->idlimit);
    else
        ajFmtPrintF(outf,"Reffiles     %u\n",cache->refcount);

    ajFileClose(&outf);
    ajStrDel(&fname);

    return;
}
ID ajBtreeReadParamsC
TY public
MO ajindex
LB core
XX
DE Read B+ tree parameters from file
XX
PN [1]
PA r filetxt const char*
PD file
PX
PN [2]
PA r exttxt const char*
PD file extension
PX
PN [3]
PA r idirtxt const char*
PD index directory
PX
PN [4]
PA w secondary AjBool*
PD true for a secondary index
PX
PN [5]
PA w compressed AjBool*
PD true for a compressed index
PX
PN [6]
PA w kwlimit ajuint*
PD maximum length of a keyword
PX
PN [7]
PA w idlimit ajuint*
PD maximum length of a secondary id
PX
PN [8]
PA w refcount ajuint*
PD reference file(s) per entry
PX
PN [9]
PA w pripagesize ajuint*
PD size of primary pages
PX
PN [10]
PA w secpagesize ajuint*
PD size of secondary pages
PX
PN [11]
PA w pricachesize ajuint*
PD Primary cachesize
PX
PN [12]
PA w seccachesize ajuint*
PD Secondary cachesize
PX
PN [13]
PA w pripagecount ajulong*
PD Primary page count
PX
PN [14]
PA w secpagecount ajulong*
PD Secondary page count
PX
PN [15]
PA w order ajuint*
PD tree order
PX
PN [16]
PA w nperbucket ajuint*
PD bucket fill
PX
PN [17]
PA w level ajuint*
PD depth of tree (0 = root leaf)
PX
PN [18]
PA w sorder ajuint*
PD secondary tree order
PX
PN [19]
PA w snperbucket ajuint*
PD secondary bucket fill
PX
PN [20]
PA w count ajulong*
PD number of primary keywords in the index
PX
PN [21]
PA w countall ajulong*
PD number of total keywords in the index
PX
RT AjBool
RD True on success
RX
//


AjBool ajBtreeReadParamsC(const char *filetxt, const char *exttxt,
                          const char *idirtxt,
                          AjBool *secondary, AjBool *compressed,
                          ajuint *kwlimit, ajuint *idlimit,
                          ajuint *refcount,
                          ajuint *pripagesize, ajuint *secpagesize,
                          ajuint *pricachesize, ajuint *seccachesize,
                          ajulong *pripagecount, ajulong *secpagecount,
                          ajuint *order, ajuint *nperbucket, ajuint *level,
                          ajuint *sorder, ajuint *snperbucket,
                          ajulong *count, ajulong *countall)
{
    AjPStr fname = NULL;
    AjPStr line  = NULL;
    AjPFile inf  = NULL;
    AjPStr type = NULL;
    AjBool setsecond = ajFalse;

    *countall = 0L;
    *compressed = ajFalse;
    *pripagecount = 0L;
    *secpagecount = 0L;
    *refcount = 1;
    *secpagesize = 0;
    *seccachesize = 0;
    *idlimit = 0;

    line  = ajStrNew();

    fname = ajStrNew();

    if(!*idirtxt)
        ajFmtPrintS(&fname,"%s.p%s",filetxt,exttxt);
    else if(idirtxt[strlen(idirtxt)-1] == SLASH_CHAR)
        ajFmtPrintS(&fname,"%s%s.p%s",idirtxt,filetxt,exttxt);
    else
      ajFmtPrintS(&fname,"%s%s%s.p%s",idirtxt,SLASH_STRING,filetxt,exttxt);
    
    if(!(inf = ajFileNewInNameS(fname)))
    {
	ajErr("Cannot open param file %S %d: '%s'\n",
              fname, errno, strerror(errno));
        return ajFalse;
    }

    while(ajReadlineTrim(inf,&line))
    {
	if(ajStrPrefixC(line,"Order2 "))
	{
	    ajFmtScanS(line,"%*s%u",sorder);
	    continue;
	}

	if(ajStrPrefixC(line,"Fill2 "))
	{
	    ajFmtScanS(line,"%*s%u",snperbucket);
	    continue;
	}

	if(ajStrPrefixC(line,"Type "))
        {
	    ajFmtScanS(line,"%*s%S",&type);
            if(ajStrMatchC(type, "Secondary"))
                *secondary = ajTrue;
            else if(ajStrMatchC(type, "Identifier"))
                *secondary = ajFalse;
            else
                ajErr("param file %S unknown Type value '%S'",
                      fname, type);
            setsecond = ajTrue;
        }

	if(ajStrPrefixC(line,"Compress "))
	    ajFmtScanS(line,"%*s%b",compressed);

	if(ajStrPrefixC(line,"Pages "))
	    ajFmtScanS(line,"%*s%Lu",pripagecount);

	if(ajStrPrefixC(line,"Secpages "))
	    ajFmtScanS(line,"%*s%Lu",secpagecount);

	if(ajStrPrefixC(line,"Order "))
	    ajFmtScanS(line,"%*s%u",order);

	if(ajStrPrefixC(line,"Fill "))
	    ajFmtScanS(line,"%*s%u",nperbucket);

	if(ajStrPrefixC(line,"Pagesize "))
	    ajFmtScanS(line,"%*s%u",pripagesize);

	if(ajStrPrefixC(line,"Secpagesize "))
	    ajFmtScanS(line,"%*s%u",secpagesize);

	if(ajStrPrefixC(line,"Level "))
	    ajFmtScanS(line,"%*s%u",level);

	if(ajStrPrefixC(line,"Cachesize "))
	    ajFmtScanS(line,"%*s%u",pricachesize);

	if(ajStrPrefixC(line,"Seccachesize "))
	    ajFmtScanS(line,"%*s%u",seccachesize);

	if(ajStrPrefixC(line,"Count "))
	    ajFmtScanS(line,"%*s%Lu",count);

	if(ajStrPrefixC(line,"Fullcount "))
	    ajFmtScanS(line,"%*s%Lu",countall);

	if(ajStrPrefixC(line,"Kwlimit "))
	    ajFmtScanS(line,"%*s%u",kwlimit);

	if(ajStrPrefixC(line,"Idlimit "))
            ajFmtScanS(line,"%*s%u",idlimit);
        
	if(ajStrPrefixC(line,"Reffiles "))
	    ajFmtScanS(line,"%*s%u",refcount);
    }

    if(!setsecond)
    {
        *secondary = ajBtreeFieldGetSecondaryC(exttxt);
    }

    if(!*pripagecount && !*compressed)
    {
        if(!*idirtxt)
            ajFmtPrintS(&fname,"%s.%s",filetxt,exttxt);
        else if(idirtxt[strlen(idirtxt)-1] == SLASH_CHAR)
            ajFmtPrintS(&fname,"%s%s.%s",idirtxt,filetxt,exttxt);
        else
	    ajFmtPrintS(&fname,"%s%s%s.%s",
			idirtxt,SLASH_STRING, filetxt,exttxt);
        *pripagecount = ajFilenameGetSize(fname) / *pripagesize;
        *secpagecount = 0;
    }

    if(!*secpagesize)
        *secpagesize = *pripagesize;

    if(!*seccachesize)
        *seccachesize = *pricachesize;

    if(*secondary && !*idlimit)
        *idlimit = *kwlimit;

    ajFileClose(&inf);
    ajStrDel(&fname);
    ajStrDel(&line);
    ajStrDel(&type);
    
    return ajTrue;
}
ID ajBtreeReadParamsS
TY public
MO ajindex
LB core
XX
DE Read B+ tree parameters from file
XX
PN [1]
PA r file const AjPStr
PD file
PX
PN [2]
PA r ext const AjPStr
PD file extension
PX
PN [3]
PA r idir const AjPStr
PD index directory
PX
PN [4]
PA w secondary AjBool*
PD true for a secondary index
PX
PN [5]
PA w compressed AjBool*
PD true for a compressed index
PX
PN [6]
PA w kwlimit ajuint*
PD maximum length of a keyword
PX
PN [7]
PA w idlimit ajuint*
PD maximum length of a secondary id
PX
PN [8]
PA w refcount ajuint*
PD reference file(s) per entry
PX
PN [9]
PA w pripagesize ajuint*
PD size of primary pages
PX
PN [10]
PA w secpagesize ajuint*
PD size of secondary pages
PX
PN [11]
PA w pricachesize ajuint*
PD Primary cachesize
PX
PN [12]
PA w seccachesize ajuint*
PD Secondary cachesize
PX
PN [13]
PA w pripagecount ajulong*
PD Primary page count
PX
PN [14]
PA w secpagecount ajulong*
PD Secondary page count
PX
PN [15]
PA w order ajuint*
PD tree order
PX
PN [16]
PA w nperbucket ajuint*
PD bucket fill
PX
PN [17]
PA w level ajuint*
PD depth of tree (0 = root leaf)
PX
PN [18]
PA w sorder ajuint*
PD secondary tree order
PX
PN [19]
PA w snperbucket ajuint*
PD secondary bucket fill
PX
PN [20]
PA w count ajulong*
PD number of primary keywords in the index
PX
PN [21]
PA w countall ajulong*
PD number of total keywords in the index
PX
RT AjBool
RD True on success
RX
//


AjBool ajBtreeReadParamsS(const AjPStr file, const AjPStr ext,
                          const AjPStr idir,
                          AjBool *secondary, AjBool *compressed,
                          ajuint *kwlimit, ajuint *idlimit,
                          ajuint *refcount,
                          ajuint *pripagesize, ajuint *secpagesize,
                          ajuint *pricachesize, ajuint *seccachesize,
                          ajulong *pripagecount, ajulong *secpagecount,
                          ajuint *order, ajuint *nperbucket, ajuint *level,
                          ajuint *sorder, ajuint *snperbucket,
                          ajulong *count, ajulong *countall)
{
    return ajBtreeReadParamsC(MAJSTRGETPTR(file), MAJSTRGETPTR(ext),
                              MAJSTRGETPTR(idir), secondary, compressed,
                              kwlimit, idlimit, refcount,
                              pripagesize, secpagesize,
                              pricachesize, seccachesize,
                              pripagecount, secpagecount,
                              order, nperbucket, level,
                              sorder, snperbucket,
                              count, countall);
}
ID btreeKeyInsertShift
TY static
MO ajindex
LB core
XX
DE Rebalance buckets on insertion of a key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u retpage AjPBtpage*
PD page
PX
PN [3]
PA r key const AjPStr
PD key
PX
RT ajulong
RD bucket block or 0L if shift not possible
RX
//


static ajulong btreeKeyInsertShift(AjPBtcache cache, AjPBtpage *retpage,
                                   const AjPStr key)
{
    unsigned char *tbuf = NULL;
    unsigned char *pbuf = NULL;
    unsigned char *sbuf = NULL;

    AjPBtpage ppage = NULL;
    AjPBtpage spage = NULL;
    AjPBtpage tpage = NULL;

    ajuint tkeys = 0;
    ajuint pkeys = 0;
    ajuint skeys = 0;
    ajuint order = 0;
    
    ajint ii;
    ajuint i;
    ajuint n;
    
    ajulong parent  = 0L;
    ajulong blockno = 0L;
    
    AjPBtMem arrays1 = NULL;
    AjPBtMem arrays2 = NULL;
    AjPBtMem arrays3 = NULL;
    AjPStr *kTarray = NULL;
    AjPStr *kParray = NULL;
    AjPStr *kSarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pParray = NULL;
    ajulong *pSarray = NULL;

    AjPStr *karray = NULL;
    ajulong *parray = NULL;

    ajuint ppos    = 0;
    ajuint pkeypos = 0;
    ajuint minsize = 0;
    
    /* ajDebug("In btreeKeyInsertShift\n"); */


    tpage = *retpage;

    tbuf = tpage->buf;

    GBT_PREV(tbuf,&parent);
    GBT_NKEYS(tbuf,&tkeys);

    order = cache->porder;
    minsize = order / 2;

    if(order % 2)
	++minsize;

    if(tkeys <= minsize)
	return 0L;

    ppage = btreePricacheRead(cache,parent);
    
    pbuf = ppage->buf;
    GBT_NKEYS(pbuf,&pkeys);
    
    
    arrays1 = btreeAllocPriArray(cache);
    kParray = arrays1->karray;
    pParray = arrays1->parray;

    arrays2 = btreeAllocPriArray(cache);
    kSarray = arrays2->karray;
    pSarray = arrays2->parray;

    arrays3 = btreeAllocPriArray(cache);
    kTarray = arrays3->karray;
    pTarray = arrays3->parray;
    
    btreeGetKeys(cache,pbuf,&kParray,&pParray);

    i=0;

    while(i!=pkeys && MAJSTRCMPS(kParray[i],key)<=0)
	++i;

    pkeypos = i;
    
    if(i==pkeys)
    {
	if(MAJSTRCMPS(kParray[i-1],key)>0)
	    ppos = i-1;
	else
	    ppos = i;
    }
    else
	ppos = i;

    
    if(ppos) /* There is another leaf to the left */
    {
	spage = btreePricacheRead(cache,pParray[ppos-1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
    }

    if(i && skeys != order-1) /* There is space in the left leaf */
    {
	/* ajDebug("Left shift\n"); */
	btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
	if(skeys)
	    btreeGetKeys(cache,sbuf,&kSarray,&pSarray);

	i = 0;

	while(pParray[i] != tpage->pagepos)
	    ++i;

	--i;

	pkeypos = i;

	ajStrAssignS(&kSarray[skeys],kParray[pkeypos]);
	pSarray[skeys+1] = pTarray[0];
	++skeys;
	--tkeys;
	ajStrAssignS(&kParray[pkeypos],kTarray[0]);

	for(i=0;ipagepos)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1161;
        }

	i = 0;

	while(i!=pkeys && MAJSTRCMPS(kParray[i],key)<=0)
	    ++i;

	if(i==pkeys)
	{
	    if(MAJSTRCMPS(kParray[i-1],key)>0)
		blockno = pParray[i-1];
	    else
		blockno = pParray[i];
	}
	else
	    blockno = pParray[i];

	if(blockno == spage->pagepos)
	{
	    *retpage = spage;
	    karray = kSarray;
	    parray = pSarray;
	    n = skeys;
	}
	else
	{
	    karray = kTarray;
	    parray = pTarray;
	    n = tkeys;
	}
	

	i = 0;

	while(i!=n && MAJSTRCMPS(karray[i],key)<=0)
	    ++i;

	if(i==n)
	{
	    if(MAJSTRCMPS(karray[i-1],key)>0)
		blockno = parray[i-1];
	    else
		blockno = parray[i];
	}
	else
	    blockno = parray[i];

        btreeDeallocPriArray(cache,arrays1);
        btreeDeallocPriArray(cache,arrays2);
        btreeDeallocPriArray(cache,arrays3);

	/* ajDebug("... returns blockno (a) %Lu\n",blockno); */

	return blockno;
    }
    

    if(ppos != pkeys)	/* There is a right node */
    {
	spage = btreePricacheRead(cache,pParray[ppos+1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
    }


    /* Space in the right leaf */
    if(ppos != pkeys && skeys != order-1)
    {
	/* ajDebug("Right shift\n"); */
	btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
	btreeGetKeys(cache,sbuf,&kSarray,&pSarray);

	i = 0;

	while(pParray[i] != tpage->pagepos)
	    ++i;

	pkeypos = i;
	
	pSarray[skeys+1] = pSarray[skeys];
	for(ii=skeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kSarray[ii+1],kSarray[ii]);
	    pSarray[ii+1] = pSarray[ii];
	}
	ajStrAssignS(&kSarray[0],kParray[pkeypos]);
	pSarray[0] = pTarray[tkeys];
	ajStrAssignS(&kParray[pkeypos],kTarray[tkeys-1]);
	++skeys;
	--tkeys;
	pTarray[tkeys+1] = 0L;
	
	btreeWriteNode(cache,spage,kSarray,pSarray,skeys);
	btreeWriteNode(cache,tpage,kTarray,pTarray,tkeys);
	btreeWriteNode(cache,ppage,kParray,pParray,pkeys);
	if(!ppage->pagepos)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1162;
        }

	i = 0;

	while(i!=pkeys && MAJSTRCMPS(kParray[i],key)<=0)
	    ++i;

	if(i==pkeys)
	{
	    if(MAJSTRCMPS(kParray[i-1],key)>0)
		blockno = pParray[i-1];
	    else
		blockno = pParray[i];
	}
	else
	    blockno = pParray[i];

	if(blockno == spage->pagepos)
	{
	    *retpage = spage;
	    karray = kSarray;
	    parray = pSarray;
	    n = skeys;
	}
	else
	{
	    karray = kTarray;
	    parray = pTarray;
	    n = tkeys;
	}
	
	i = 0;

	while(i!=n && MAJSTRCMPS(karray[i],key)<=0)
	    ++i;

	if(i==n)
	{
	    if(MAJSTRCMPS(karray[i-1],key)>0)
		blockno = parray[i-1];
	    else
		blockno = parray[i];
	}
	else
	    blockno = parray[i];

        btreeDeallocPriArray(cache,arrays1);
        btreeDeallocPriArray(cache,arrays2);
        btreeDeallocPriArray(cache,arrays3);

	/* ajDebug("... returns blockno (b) %Lu\n",blockno); */
	
	return blockno;
    }


    btreeDeallocPriArray(cache,arrays1);
    btreeDeallocPriArray(cache,arrays2);
    btreeDeallocPriArray(cache,arrays3);

    /* ajDebug("... returns 0L\n"); */

    return 0L;
}
ID btreePrimaryShift
TY static
MO ajindex
LB core
XX
DE Rebalance primary nodes (identifiers or keywords) on insertion
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u tpage AjPBtpage
PD page
PX
RT void
RD 
RX
//


static void btreePrimaryShift(AjPBtcache cache, AjPBtpage tpage)
{
    unsigned char *tbuf = NULL;
    unsigned char *pbuf = NULL;
    unsigned char *sbuf = NULL;
    unsigned char *buf  = NULL;

    AjPBtpage ppage = NULL;
    AjPBtpage spage = NULL;
    AjPBtpage page  = NULL;

    ajuint tkeys = 0;
    ajuint pkeys = 0;
    ajuint skeys = 0;
    ajuint order = 0;

    ajuint i;
    ajint  ii;

    ajulong parent  = 0L;
    
    AjPBtMem Parrays = NULL;
    AjPBtMem Sarrays = NULL;
    AjPBtMem Tarrays = NULL;
    AjPStr *kTarray = NULL;
    AjPStr *kParray = NULL;
    AjPStr *kSarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pParray = NULL;
    ajulong *pSarray = NULL;

    ajuint pkeypos = 0;
    ajuint minsize = 0;

    ajulong lv = 0L;
    
    /* ajDebug("In btreePrimaryShift\n"); */
    
    tbuf = tpage->buf;

    GBT_PREV(tbuf,&parent);
    GBT_NKEYS(tbuf,&tkeys);

    order = cache->porder;
    minsize = order / 2;

    if(order % 2)
	++minsize;

    if(tkeys <= minsize)
	return;

    
    ppage = btreePricacheRead(cache,parent);
    pbuf = ppage->buf;
    GBT_NKEYS(pbuf,&pkeys);

    Parrays = btreeAllocPriArray(cache);
    Sarrays = btreeAllocPriArray(cache);
    Tarrays = btreeAllocPriArray(cache);
    kParray = Parrays->karray;
    pParray = Parrays->parray;
    kSarray = Sarrays->karray;
    pSarray = Sarrays->parray;
    kTarray = Tarrays->karray;
    pTarray = Tarrays->parray;
    
    btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
    GBT_NKEYS(tbuf,&tkeys);

    btreeGetKeys(cache,pbuf,&kParray,&pParray);

    i=0;

    while(pParray[i] != tpage->pagepos)
	++i;

    if(i) /* There is another leaf to the left */
    {
	pkeypos = i-1;
	spage = btreePricacheRead(cache,pParray[pkeypos]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
	
    }

    if(i && skeys != order-1) /* There is space in the left leaf */
    {
	if(skeys)
	    btreeGetKeys(cache,sbuf,&kSarray,&pSarray);

	ajStrAssignS(&kSarray[skeys],kParray[pkeypos]);
	pSarray[skeys+1] = pTarray[0];
	++skeys;
	--tkeys;
	ajStrAssignS(&kParray[pkeypos],kTarray[0]);

	for(i=0;ipagepos)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1171;
        }

	page = btreePricacheRead(cache,pSarray[skeys]);
	buf = page->buf;
	lv = spage->pagepos;
	SBT_PREV(buf,lv);
	page->dirty = BT_DIRTY;

        btreeDeallocPriArray(cache,Parrays);
        btreeDeallocPriArray(cache,Sarrays);
        btreeDeallocPriArray(cache,Tarrays);

	return;
    }



    if(i != pkeys)	/* There is a right node */
    {
	pkeypos = i;
	spage = btreePricacheRead(cache,pParray[pkeypos+1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
    }


    if(i != pkeys && skeys != order-1) /* Space in the right node */
    {
	if(skeys)
	    btreeGetKeys(cache,sbuf,&kSarray,&pSarray);

	pSarray[skeys+1] = pSarray[skeys];

	for(ii=skeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kSarray[ii+1],kSarray[ii]);
	    pSarray[ii+1] = pSarray[ii];
	}

	ajStrAssignS(&kSarray[0],kParray[pkeypos]);
	pSarray[0] = pTarray[tkeys];
	ajStrAssignS(&kParray[pkeypos],kTarray[tkeys-1]);
	++skeys;
	--tkeys;
	pTarray[tkeys+1] = 0L;
	
	btreeWriteNode(cache,spage,kSarray,pSarray,skeys);
	btreeWriteNode(cache,tpage,kTarray,pTarray,tkeys);
	btreeWriteNode(cache,ppage,kParray,pParray,pkeys);
	if(!ppage->pagepos)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1172;
        }
        
	page = btreePricacheRead(cache,pSarray[0]);
	buf = page->buf;
	lv = spage->pagepos;
	SBT_PREV(buf,lv);
	page->dirty = BT_DIRTY;

        btreeDeallocPriArray(cache,Parrays);
        btreeDeallocPriArray(cache,Sarrays);
        btreeDeallocPriArray(cache,Tarrays);
        
	return;
    }

    btreeDeallocPriArray(cache,Parrays);
    btreeDeallocPriArray(cache,Sarrays);
    btreeDeallocPriArray(cache,Tarrays);

    return;
}
ID btreeTraverseLeaves
TY static
MO ajindex
LB core
XX
DE Find the next leaf by traversing the tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u thys AjPBtpage
PD current leaf page
PX
RT AjPBtpage
RD next leaf or NULL
RX
//


static AjPBtpage btreeTraverseLeaves(AjPBtcache cache, AjPBtpage thys)
{
    AjPBtpage page = NULL;
    
    ajulong pagepos = 0L;
    ajulong prev   = 0L;
    
    AjPBtMem arrays = NULL;
    ajulong *parray = NULL;

    ajuint nodetype = 0;
    ajuint nkeys    = 0;
    ajuint apos     = 0;
    ajuint order    = 0;
    ajuint i;

    unsigned char *buf = NULL;    

    if(!cache->plevel)
	return NULL;

    order = cache->porder;
    arrays = btreeAllocPriArray(cache);
    parray = arrays->Parray;

    pagepos = thys->pagepos;
    buf = thys->buf;
    GBT_PREV(buf,&prev);

    page = btreePricacheRead(cache,prev);
    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&nodetype);
    btreeGetPointers(cache,buf,&parray);

    apos = 0;

    while(parray[apos] != pagepos)
	++apos;

    while(apos == nkeys)
    {
	if(nodetype == BT_ROOT)
	{
            btreeDeallocPriArray(cache,arrays);

	    return NULL;
	}

	GBT_PREV(buf,&prev);
	pagepos = page->pagepos;
	page = btreePricacheRead(cache,prev);
	buf = page->buf;
	GBT_NKEYS(buf,&nkeys);
	GBT_NODETYPE(buf,&nodetype);
	btreeGetPointers(cache,buf,&parray);

	apos = 0;

	while(parray[apos] != pagepos)
	    ++apos;
    }

    page = btreePricacheRead(cache,parray[apos+1]);
    buf = page->buf;
    GBT_NODETYPE(buf,&nodetype);
    btreeGetPointers(cache,buf,&parray);
    
    while(nodetype != BT_LEAF)
    {
	page = btreePricacheRead(cache,parray[0]);
	buf = page->buf;
	btreeGetPointers(cache,buf,&parray);
	GBT_NODETYPE(buf,&nodetype);
    }

    btreeDeallocPriArray(cache,arrays);

    return page;
}
ID btreeJoinLeaves
TY static
MO ajindex
LB core
XX
DE Update all Left/Right Leaf Pointers
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT void
RD next leaf or NULL
RX
//


static void btreeJoinLeaves(AjPBtcache cache)
{
    unsigned char *buf = NULL;
    AjPBtpage page     = NULL;
    AjPBtpage newpage  = NULL;
    
    ajuint nodetype = 0;
    ajuint order    = 0;
    ajuint i;

    AjPBtMem arrays = NULL;
    ajulong *parray = NULL;

    ajulong left    = 0L;
    ajulong right   = 0L;

    ajulong lv = 0L;
    
    if(!cache->plevel)
	return;

    order = cache->porder;
    arrays = btreeAllocPriArray(cache);
    parray = arrays->Parray;

    page = btreePricacheLocate(cache,0L);
    buf = page->buf;
    btreeGetPointers(cache,buf,&parray);
    GBT_NODETYPE(buf,&nodetype);

    while(nodetype != BT_LEAF)
    {
	page = btreePricacheRead(cache,parray[0]);
	buf = page->buf;
	btreeGetKeys(cache,buf,&karray,&parray);
	GBT_NODETYPE(buf,&nodetype);
    }
    
    lv = left;
    SBT_LEFT(buf,lv);
    
    while((newpage = btreeTraverseLeaves(cache,page)))
    {
	right = newpage->pagepos;
	lv = right;
	SBT_RIGHT(buf,lv);
	page->dirty = BT_DIRTY;
	left = page->pagepos;
	buf = newpage->buf;
	lv = left;
	SBT_LEFT(buf,lv);
	page = newpage;
    }

    right = 0L;
    SBT_RIGHT(buf,right);
    page->dirty = BT_DIRTY;

    btreeDeallocPriArray(cache,arrays);

    return;
}
ID ajBtreeIdwildNew
TY public
MO ajindex
LB core
XX
DE Construct a wildcard id search object
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r wild const AjPStr
PD wildcard id prefix (without asterisk)
PX
RT AjPBtIdwild
RD b+ tree wildcard object
RX
//


AjPBtIdwild ajBtreeIdwildNew(AjPBtcache cache, const AjPStr wild)
{
    AjPBtIdwild thys = NULL;

    (void) cache;			/* make it used */

    AJNEW0(thys);

    thys->id   = ajStrNewS(wild);
    ajStrTrimC(&thys->id,"*"); /* Need to revisit this */
    thys->list = ajListNew();

    thys->first = ajTrue;

    return thys;
}
ID ajBtreeIdwildDel
TY public
MO ajindex
LB core
XX
DE Destroy a wildcard search object
XX
PN [1]
PA u Pthis AjPBtIdwild*
PD b+ tree identifier wildcard structure
PX
RT void
RD 
RX
//


void ajBtreeIdwildDel(AjPBtIdwild *Pthis)
{
    AjPBtIdwild thys = NULL;
    AjPBtId   id    = NULL;
    
    if(!Pthis || !*Pthis)
	return;

    thys = *Pthis;

    ajStrDel(&thys->id);

    while(ajListPop(thys->list,(void **)&id))
	ajBtreeIdDel(&id);

    ajListFree(&thys->list);

    AJFREE(thys);
    *Pthis = NULL;

    return;
}
ID ajBtreeKeywildNew
TY public
MO ajindex
LB core
XX
DE Construct a wildcard keyword search object
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r wild const AjPStr
PD wildcard keyword prefix (without asterisk)
PX
RT AjPBtKeywild
RD b+ tree keyword wildcard object
RX
//


AjPBtKeywild ajBtreeKeywildNew(AjPBtcache cache, const AjPStr wild)
{
    AjPBtKeywild thys = NULL;

    char *p;
    char *cp;

    AJNEW0(thys);

    thys->keyword = ajStrNewS(wild);
    ajStrFmtQuery(&thys->keyword);
    if(MAJSTRGETLEN(thys->keyword) > cache->keylimit)
        ajStrTruncateLen(&thys->keyword, cache->keylimit);

    cp = MAJSTRGETPTR(thys->keyword);
    p = strpbrk(cp,"*?");

    if(p)
    {
	if(p-cp)
	    ajStrAssignSubS(&thys->prefix,thys->keyword,0,p-cp-1);
	else
            ajStrAssignC(&thys->prefix, "");
    }
    else
	ajStrAssignS(&thys->prefix,thys->keyword);

    thys->list   = ajListNew();
    thys->idlist = ajListNew();

    thys->first = ajTrue;

    return thys;
}
ID ajBtreeKeywildDel
TY public
MO ajindex
LB core
XX
DE Destroy a wildcard keyword search object
XX
PN [1]
PA u Pthis AjPBtKeywild*
PD b+ tree wildcard keyword structure
PX
RT void
RD 
RX
//


void ajBtreeKeywildDel(AjPBtKeywild *Pthis)
{
    AjPBtKeywild thys = NULL;
    AjPStr id = NULL;
    AjPBtPri pri = NULL;
    
    if(!Pthis || !*Pthis)
	return;

    thys = *Pthis;

    ajStrDel(&thys->keyword);
    ajStrDel(&thys->prefix);

    while(ajListPop(thys->idlist,(void **)&id))
	ajStrDel(&id);

    while(ajListPop(thys->list,(void **)&pri))
	ajBtreePriDel(&pri);

    ajListFree(&thys->list);

    AJFREE(thys);
    *Pthis = NULL;

    return;
}
ID btreePrimaryFetchFindleafWild
TY static
MO ajindex
LB core
XX
DE Find the node that should contain a wildcard identifier or keyword
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key to search for
PX
RT AjPBtpage
RD leaf node where item should be inserted
RX
//


static AjPBtpage btreePrimaryFetchFindleafWild(AjPBtcache cache,
                                               const AjPStr key)
{
    AjPBtpage root = NULL;
    AjPBtpage ret  = NULL;

    /* ajDebug("In btreePrimaryFetchFindleafWild\n"); */

    /* The root node should always be in the cache (BT_LOCKed) */
    root = btreePricacheLocate(cache,0L);
    
    if(!cache->plevel)
	return root;
    
    ret = btreePrimaryFindInodeWild(cache,root,key);

    return ret;
}
ID btreePrimaryFindInodeWild
TY static
MO ajindex
LB core
XX
DE Recursive search for node (wild)
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD page
PX
PN [3]
PA r item const AjPStr
PD key to search for
PX
RT AjPBtpage
RD leaf node where item should be inserted
RX
//


static AjPBtpage btreePrimaryFindInodeWild(AjPBtcache cache, AjPBtpage page,
                                           const AjPStr item)
{
    AjPBtpage ret = NULL;
    AjPBtpage pg  = NULL;

    unsigned char *buf = NULL;

    ajuint status = 0;
    ajuint ival   = 0;

    /* ajDebug("In btreePrimaryFindInodeWild\n"); */
    
    ret = page;
    buf = page->buf;
    GBT_NODETYPE(buf,&ival);

    if(ival != BT_LEAF)
    {
	status = ret->dirty;
	ret->dirty = BT_LOCK;	/* Lock in case of lots of overflow pages */
        ret->lockfor = 1181;
	pg = btreePrimaryPageDownWild(cache,buf,item);
	ret->dirty = status;
	ret = btreePrimaryFindInodeWild(cache,pg,item);
    }
    
    return ret;
}
ID btreePrimaryPageDownWild
TY static
MO ajindex
LB core
XX
DE Return next lower index page given a wildcard key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA r key const AjPStr
PD key to search for
PX
RT AjPBtpage
RD pointer to a page
RX
//


static AjPBtpage btreePrimaryPageDownWild(AjPBtcache cache, unsigned char *buf,
                                          const AjPStr key)
{
    ajulong blockno = 0L;
    AjPBtpage page = NULL;

    unsigned char *rootbuf = NULL;
    
    /* ajDebug("In btreePrimaryPageDownWild\n"); */
    
    rootbuf = buf;

    blockno = btreeGetBlockS(cache,rootbuf,key);
    page =  btreePricacheRead(cache,blockno);

    return page;
}
ID btreeIdleafFetch
TY static
MO ajindex
LB core
XX
DE Read all leaf identifiers into a list
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD page
PX
PN [3]
PA w list AjPList
PD list of AjPBtIds
PX
RT void
RD 
RX
//


static void btreeIdleafFetch(AjPBtcache cache, AjPBtpage page,
                               AjPList list)
{
    unsigned char *buf = NULL;
    AjPBtMem arrays    = NULL;
    ajulong *parray     = NULL;

    ajuint keylimit = 0;
    ajuint nkeys    = 0;

    ajuint i;
    
    /* ajDebug("In btreeIdleafFetch\n"); */
    
    buf = page->buf;

    GBT_NKEYS(buf,&nkeys);

    if(!nkeys)
        return;

    arrays = btreeAllocPriArray(cache);
    parray = arrays->parray;

    btreeGetPointers(cache,buf,&parray);

    GBT_NKEYS(buf,&nkeys);
    
    keylimit = nkeys+1;

    for(i=0;iid;
    list = wild->list;

    found = ajFalse;
    
    if(wild->first)
    {
	page = btreePrimaryFetchFindleafWild(cache,key);
	page->dirty = BT_LOCK;
        page->lockfor = 1191;
	wild->pagepos = page->pagepos;
	
	btreeIdleafFetch(cache,page,list);

	page->dirty = BT_CLEAN;
	
	if(!ajListGetLength(list))
	    return NULL;

	while(ajListPop(list,(void **)&id))
	{
	    if(ajStrPrefixS(id->id,key))
	    {
		found = ajTrue;
		break;
	    }

	    ajBtreeIdDel(&id);
	}

	wild->first = ajFalse;


	if(!found)	/* Check the next leaf just in case key==internal */
	{
	    buf = page->buf;
	    GBT_RIGHT(buf,&pagepos);

	    if(!pagepos)
		return NULL;

	    page = btreePricacheRead(cache,pagepos);
	    wild->pagepos = pagepos;
	    page->dirty = BT_LOCK;
            page->lockfor = 1192;

	    btreeIdleafFetch(cache,page,list);	
	    
	    page->dirty = BT_CLEAN;
	    
	    if(!ajListGetLength(list))
		return NULL;
	    
	    found = ajFalse;

	    while(ajListPop(list,(void **)&id))
	    {
		if(ajStrPrefixS(id->id,key))
		{
		    found = ajTrue;
		    break;
		}

		ajBtreeIdDel(&id);
	    }
	}


	if(!found)
	    return NULL;

	return id;
    }


    if(!ajListGetLength(list))
    {
	page = btreePricacheRead(cache,wild->pagepos); 
	buf = page->buf;
	GBT_RIGHT(buf,&pagepos);

	if(!pagepos)
	    return NULL;

	page = btreePricacheRead(cache,pagepos);
	wild->pagepos = pagepos;
	page->dirty = BT_LOCK;
        page->lockfor = 1193;

	btreeIdleafFetch(cache,page,list);	

	page->dirty = BT_CLEAN;
	
	if(!ajListGetLength(list))
	    return NULL;
    }
    


    while(ajListPop(list,(void **)&id))
    {
	if(ajStrPrefixS(id->id,key))
	{
	    found = ajTrue;
	    break;
	}

	ajBtreeIdDel(&id);
    }
    
    
    if(!found)
	return NULL;
    
    return id;
}
ID ajBtreeIdentFetchwildId
TY public
MO ajindex
LB core
XX
DE Wildcard retrieval of entries
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD Wildcard key
PX
PN [3]
PA u idlist AjPList
PD list of matching AjPBtIds
PX
RT void
RD 
RX
//


void ajBtreeIdentFetchwildId(AjPBtcache cache, const AjPStr key,
                             AjPList idlist)
{

    AjPBtId id     = NULL;
    AjPBtpage page = NULL;
    AjPList list   = NULL;
    AjBool found   = ajFalse;

    ajulong pripagepos = 0L;
    ajulong right = 0L;
    
    unsigned char *buf = NULL;    
    AjBool finished = ajFalse;

    AjPStr prefix = NULL;
    AjPStr keystr = NULL;
    
    char *p;
    char *cp;

    ajDebug("ajBtreeIdentFetchwildId '%S' list: %Lu\n",
            key, ajListGetLength(list));

    if(!cache->countunique && !cache->countall)
        return;

    keystr = ajStrNewS(key);
    ajStrFmtQuery(&keystr);

    if(MAJSTRGETLEN(keystr) > cache->keylimit)
        ajStrTruncateLen(&keystr, cache->keylimit);
    
    cp = MAJSTRGETPTR(keystr);
    p = strpbrk(cp,"*?");

    if(p)
    {
	if(p-cp)
	    ajStrAssignSubS(&prefix,keystr,0,p-cp-1);
	else
	{
	    btreeKeyFullSearchId(cache,keystr,idlist);
            ajStrDel(&keystr);
	    ajStrDel(&prefix);
	    return;
	}
    }
    else
	ajStrAssignS(&prefix,keystr);

    ajStrFmtQuery(&prefix);

    if(MAJSTRGETLEN(prefix) > cache->keylimit)
        ajStrTruncateLen(&prefix, cache->keylimit);

    ajDebug("ajBtreeIdentFetchwild '%S' prefix: '%S'\n",
            keystr, prefix);

    list = ajListNew();

    found = ajFalse;

    page = btreePrimaryFetchFindleafWild(cache,prefix);
    page->dirty = BT_LOCK;
    page->lockfor = 1201;
    pripagepos = page->pagepos;
    
    btreeIdleafFetch(cache,page,list);
    page->dirty = BT_CLEAN;
    

    while(ajListPop(list,(void **)&id))
    {
	if(ajStrPrefixS(id->id,prefix))
	{
	    found = ajTrue;
	    break;
	}
	else
	    ajBtreeIdDel(&id);
    }
    
    ajDebug("ajBtreeIdentFetchwild first leaf prefix found: %B\n", found);
    
    if(!found)	/* Check the next leaf just in case key==internal */
    {
	buf = page->buf;
	GBT_RIGHT(buf,&right);

	if(!right)
	{
            ajStrDel(&keystr);
	    ajStrDel(&prefix);
	    ajListFree(&list);

	    return;
	}

	page = btreePricacheRead(cache,right);
	pripagepos = right;
	page->dirty = BT_LOCK;
        page->lockfor = 1202;

	btreeIdleafFetch(cache,page,list);	
	    
	page->dirty = BT_CLEAN;
	    
	if(!ajListGetLength(list))
	{
            ajStrDel(&keystr);
	    ajStrDel(&prefix);
	    ajListFree(&list);

	    return;
	}
	
	    
	found = ajFalse;

	while(ajListPop(list,(void **)&id))
	{
	    if(ajStrPrefixS(id->id,prefix))
	    {
		found = ajTrue;
		break;
	    }
	    else
		ajBtreeIdDel(&id);
	}
        ajDebug("ajBtreeIdentFetchwild check next prefix found: %B\n", found);
    }
    
    
    if(!found)
    {
        ajStrDel(&keystr);
	ajStrDel(&prefix);
	ajListFree(&list);

	return;
    }
    

    finished = ajFalse;

    /*
    ** check current ID against full wildcard query
    ** read next ID (new list when list is empty)
    ** stop when no more to read or prefix no longer matches
    */

    while(!finished)
    {
	if(ajStrMatchWildS(id->id,keystr))
        {
	    ajListPush(idlist,(void *)id);
            id = NULL;
        }
	else
	    ajBtreeIdDel(&id);
	
	if(!ajListGetLength(list))
	{
	    page = btreePricacheRead(cache,pripagepos);
	    buf = page->buf;
	    GBT_RIGHT(buf,&right);

	    if(!right)
	    {
		finished = ajTrue;
		continue;
	    }

	    page = btreePricacheRead(cache,right);
	    page->dirty = BT_LOCK;
            page->lockfor = 1203;
	    buf = page->buf;
	    pripagepos = right;
	    
	    btreeIdleafFetch(cache,page,list);	
	    
	    page->dirty = BT_CLEAN;
	    
	    if(!ajListGetLength(list))
	    {
		finished = ajTrue;
		continue;
	    }
	}

	ajListPop(list,(void **)&id);

	if(!ajStrPrefixS(id->id,prefix))
	{
	    finished = ajTrue;
	    ajBtreeIdDel(&id);
	}
    }

    /*
    ** clear remaining (non-matching) ids in the latest list
    */

    while(ajListPop(list,(void **)&id))
	ajBtreeIdDel(&id);
    ajListFree(&list);

    ajListSortTwoUnique(idlist, &btreeIdDbnoCompare, &btreeIdOffsetCompare,
                        &btreeIdDelFromList);

    ajStrDel(&prefix);
    ajStrDel(&keystr);

    return;
}
ID ajBtreeIdentFetchwildHit
TY public
MO ajindex
LB core
XX
DE Wildcard retrieval of entries
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD Wildcard key
PX
PN [3]
PA u hitlist AjPList
PD list of matching AjPBtHits
PX
RT void
RD 
RX
//


void ajBtreeIdentFetchwildHit(AjPBtcache cache, const AjPStr key,
                              AjPList hitlist)
{

    AjPBtId id     = NULL;
    AjPBtHit hit   = NULL;
    AjPBtpage page = NULL;
    AjPList list   = NULL;
    AjBool found   = ajFalse;

    ajulong pripagepos = 0L;
    ajulong right = 0L;
    
    unsigned char *buf = NULL;    
    AjBool finished = ajFalse;

    AjPStr prefix = NULL;
    AjPStr keystr = NULL;
    
    char *p;
    char *cp;

    ajDebug("ajBtreeIdentFetchwildHit '%S' list: %Lu\n",
            key, ajListGetLength(list));

    if(!cache->countunique && !cache->countall)
        return;

    keystr = ajStrNewS(key);
    ajStrFmtQuery(&keystr);

    if(MAJSTRGETLEN(keystr) > cache->keylimit)
        ajStrTruncateLen(&keystr, cache->keylimit);
    
    cp = MAJSTRGETPTR(keystr);
    p = strpbrk(cp,"*?");

    if(p)
    {
	if(p-cp)
	    ajStrAssignSubS(&prefix,keystr,0,p-cp-1);
	else
	{
	    btreeKeyFullSearchHit(cache,keystr,hitlist);
            ajStrDel(&keystr);
	    ajStrDel(&prefix);
	    return;
	}
    }
    else
	ajStrAssignS(&prefix,keystr);

    ajStrFmtQuery(&prefix);

    if(MAJSTRGETLEN(prefix) > cache->keylimit)
        ajStrTruncateLen(&prefix, cache->keylimit);

    ajDebug("ajBtreeIdentFetchwildHit '%S' prefix: '%S'\n",
            keystr, prefix);

    list = ajListNew();

    found = ajFalse;

    page = btreePrimaryFetchFindleafWild(cache,prefix);
    page->dirty = BT_LOCK;
    page->lockfor = 1201;
    pripagepos = page->pagepos;
    
    btreeIdleafFetch(cache,page,list);
    page->dirty = BT_CLEAN;
    

    while(ajListPop(list,(void **)&id))
    {
	if(ajStrPrefixS(id->id,prefix))
	{
	    found = ajTrue;
	    break;
	}
	else
	    ajBtreeIdDel(&id);
    }
    
    ajDebug("ajBtreeIdentFetchwildHit first leaf prefix found: %B\n", found);
    
    if(!found)	/* Check the next leaf just in case key==internal */
    {
	buf = page->buf;
	GBT_RIGHT(buf,&right);

	if(!right)
	{
            ajStrDel(&keystr);
	    ajStrDel(&prefix);
	    ajListFree(&list);

	    return;
	}

	page = btreePricacheRead(cache,right);
	pripagepos = right;
	page->dirty = BT_LOCK;
        page->lockfor = 1202;

	btreeIdleafFetch(cache,page,list);	
	    
	page->dirty = BT_CLEAN;
	    
	if(!ajListGetLength(list))
	{
            ajStrDel(&keystr);
	    ajStrDel(&prefix);
	    ajListFree(&list);

	    return;
	}
	
	    
	found = ajFalse;

	while(ajListPop(list,(void **)&id))
	{
	    if(ajStrPrefixS(id->id,prefix))
	    {
		found = ajTrue;
		break;
	    }
	    else
		ajBtreeIdDel(&id);
	}
        ajDebug("ajBtreeIdentFetchwildHit check next prefix found: %B\n",
                found);
    }
    
    
    if(!found)
    {
        ajStrDel(&keystr);
	ajStrDel(&prefix);
	ajListFree(&list);

	return;
    }
    

    finished = ajFalse;

    /*
    ** check current ID against full wildcard query
    ** read next ID (new list when list is empty)
    ** stop when no more to read or prefix no longer matches
    */

    while(!finished)
    {
	if(ajStrMatchWildS(id->id,keystr))
        {
            hit = ajBtreeHitNewId(id);
	    ajListPush(hitlist,(void *)hit);
            hit = NULL;
        }
        ajBtreeIdDel(&id);
	
	if(!ajListGetLength(list))
	{
	    page = btreePricacheRead(cache,pripagepos);
	    buf = page->buf;
	    GBT_RIGHT(buf,&right);

	    if(!right)
	    {
		finished = ajTrue;
		continue;
	    }

	    page = btreePricacheRead(cache,right);
	    page->dirty = BT_LOCK;
            page->lockfor = 1203;
	    buf = page->buf;
	    pripagepos = right;
	    
	    btreeIdleafFetch(cache,page,list);	
	    
	    page->dirty = BT_CLEAN;
	    
	    if(!ajListGetLength(list))
	    {
		finished = ajTrue;
		continue;
	    }
	}

	ajListPop(list,(void **)&id);

	if(!ajStrPrefixS(id->id,prefix))
	{
	    finished = ajTrue;
	    ajBtreeIdDel(&id);
	}
    }

    /*
    ** clear remaining (non-matching) ids in the latest list
    */

    while(ajListPop(list,(void **)&id))
	ajBtreeIdDel(&id);
    ajListFree(&list);

    ajListSortTwoUnique(hitlist,
                        &btreeHitDbnoCompare, &btreeHitOffsetCompare,
                        &btreeHitDelFromList);

    ajStrDel(&prefix);
    ajStrDel(&keystr);

    return;
}
ID ajBtreeIdentFetchwildHitref
TY public
MO ajindex
LB core
XX
DE Wildcard retrieval of entries
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD Wildcard key
PX
PN [3]
PA u hitlist AjPList
PD list of matching AjPBtHitrefs
PX
RT void
RD 
RX
//


void ajBtreeIdentFetchwildHitref(AjPBtcache cache, const AjPStr key,
                                 AjPList hitlist)
{

    AjPBtId id     = NULL;
    AjPBtHitref hitref = NULL;
    AjPBtpage page = NULL;
    AjPList list   = NULL;
    AjBool found   = ajFalse;

    ajulong pripagepos = 0L;
    ajulong right = 0L;
    
    unsigned char *buf = NULL;    
    AjBool finished = ajFalse;

    AjPStr prefix = NULL;
    AjPStr keystr = NULL;
    
    char *p;
    char *cp;

    ajDebug("ajBtreeIdentFetchwildHitref '%S' list: %Lu\n",
            key, ajListGetLength(list));

    if(!cache->countunique && !cache->countall)
        return;

    keystr = ajStrNewS(key);
    ajStrFmtQuery(&keystr);

    if(MAJSTRGETLEN(keystr) > cache->keylimit)
        ajStrTruncateLen(&keystr, cache->keylimit);
    
    cp = MAJSTRGETPTR(keystr);
    p = strpbrk(cp,"*?");

    if(p)
    {
	if(p-cp)
	    ajStrAssignSubS(&prefix,keystr,0,p-cp-1);
	else
	{
	    btreeKeyFullSearchHitref(cache,keystr,hitlist);
            ajStrDel(&keystr);
	    ajStrDel(&prefix);
	    return;
	}
    }
    else
	ajStrAssignS(&prefix,keystr);

    ajStrFmtQuery(&prefix);

    if(MAJSTRGETLEN(prefix) > cache->keylimit)
        ajStrTruncateLen(&prefix, cache->keylimit);

    ajDebug("ajBtreeIdentFetchwildHitref '%S' prefix: '%S'\n",
            keystr, prefix);

    list = ajListNew();

    found = ajFalse;

    page = btreePrimaryFetchFindleafWild(cache,prefix);
    page->dirty = BT_LOCK;
    page->lockfor = 1201;
    pripagepos = page->pagepos;
    
    btreeIdleafFetch(cache,page,list);
    page->dirty = BT_CLEAN;
    

    while(ajListPop(list,(void **)&id))
    {
	if(ajStrPrefixS(id->id,prefix))
	{
	    found = ajTrue;
	    break;
	}
	else
	    ajBtreeIdDel(&id);
    }
    
    ajDebug("ajBtreeIdentFetchwildHitref first leaf prefix found: %B\n", found);
    
    if(!found)	/* Check the next leaf just in case key==internal */
    {
	buf = page->buf;
	GBT_RIGHT(buf,&right);

	if(!right)
	{
            ajStrDel(&keystr);
	    ajStrDel(&prefix);
	    ajListFree(&list);

	    return;
	}

	page = btreePricacheRead(cache,right);
	pripagepos = right;
	page->dirty = BT_LOCK;
        page->lockfor = 1202;

	btreeIdleafFetch(cache,page,list);	
	    
	page->dirty = BT_CLEAN;
	    
	if(!ajListGetLength(list))
	{
            ajStrDel(&keystr);
	    ajStrDel(&prefix);
	    ajListFree(&list);

	    return;
	}
	
	    
	found = ajFalse;

	while(ajListPop(list,(void **)&id))
	{
	    if(ajStrPrefixS(id->id,prefix))
	    {
		found = ajTrue;
		break;
	    }
	    else
		ajBtreeIdDel(&id);
	}
        ajDebug("ajBtreeIdentFetchwildHitref check next prefix found: %B\n",
                found);
    }
    
    
    if(!found)
    {
        ajStrDel(&keystr);
	ajStrDel(&prefix);
	ajListFree(&list);

	return;
    }
    

    finished = ajFalse;

    /*
    ** check current ID against full wildcard query
    ** read next ID (new list when list is empty)
    ** stop when no more to read or prefix no longer matches
    */

    while(!finished)
    {
	if(ajStrMatchWildS(id->id,keystr))
        {
            hitref = ajBtreeHitrefNewId(id);
	    ajListPush(hitlist,(void *)hitref);
            hitref = NULL;
        }
        ajBtreeIdDel(&id);
	
	if(!ajListGetLength(list))
	{
	    page = btreePricacheRead(cache,pripagepos);
	    buf = page->buf;
	    GBT_RIGHT(buf,&right);

	    if(!right)
	    {
		finished = ajTrue;
		continue;
	    }

	    page = btreePricacheRead(cache,right);
	    page->dirty = BT_LOCK;
            page->lockfor = 1203;
	    buf = page->buf;
	    pripagepos = right;
	    
	    btreeIdleafFetch(cache,page,list);	
	    
	    page->dirty = BT_CLEAN;
	    
	    if(!ajListGetLength(list))
	    {
		finished = ajTrue;
		continue;
	    }
	}

	ajListPop(list,(void **)&id);

	if(!ajStrPrefixS(id->id,prefix))
	{
	    finished = ajTrue;
	    ajBtreeIdDel(&id);
	}
    }

    /*
    ** clear remaining (non-matching) ids in the latest list
    */

    while(ajListPop(list,(void **)&id))
	ajBtreeIdDel(&id);
    ajListFree(&list);

    ajListSortTwoUnique(hitlist,
                        &btreeHitrefDbnoCompare, &btreeHitrefOffsetCompare,
                        &btreeHitrefDelFromList);

    ajStrDel(&prefix);
    ajStrDel(&keystr);

    return;
}
ID btreeKeyFullSearchId
TY static
MO ajindex
LB core
XX
DE Wildcard retrieval of id/acc/sv entries. Whole index scan. Only used for
DE wildcard searches with keys beginning with '?' or '*'
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD Wildcard key
PX
PN [3]
PA u idlist AjPList
PD list of matching AjPBtIds
PX
RT void
RD 
RX
//


static void btreeKeyFullSearchId(AjPBtcache cache, const AjPStr key,
                                 AjPList idlist)
{
    AjPBtId id     = NULL;
    AjPBtpage root = NULL;
    AjPBtpage page = NULL;
    ajulong right   = 0L;
    ajuint nodetype = 0;
    
    AjPBtMem arrays = NULL;
    ajulong *parray = NULL;

    AjPList list   = NULL;
    
    unsigned char *buf = NULL;    

    ajulong idlen = 0UL;

    ajDebug("btreeKeyFullSearchId key '%S'\n", key);

    list   = ajListNew();

    root = btreePricacheLocate(cache, 0L);
    page = root;
    
    buf = root->buf;
    GBT_NODETYPE(buf,&nodetype);
    
    if(cache->plevel)
    {
        arrays = btreeAllocPriArray(cache);
        parray = arrays->parray;

        /* down to the leftmost leaf node */

	while(nodetype != BT_LEAF)
	{
	    btreeGetPointers(cache,buf,&parray);
	    page = btreePricacheRead(cache, parray[0]);
	    buf = page->buf;
	    GBT_NODETYPE(buf,&nodetype);
	    page->dirty = BT_CLEAN;
	}

        btreeDeallocPriArray(cache,arrays);
    }

    right = 99L;

    /* read left-to-right through all leaf nodes */

    while(right)
    {
	btreeIdleafFetch(cache,page,list);

	while(ajListPop(list,(void **)&id))
	{
	    if(ajStrMatchWildS(id->id,key))
		ajListPushAppend(idlist,(void *)id);
	    else
		ajBtreeIdDel(&id);
	}

        if(idlen != ajListGetLength(idlist))
        {
            ajDebug("Page %Lu found %Lu\n", page->pagepos,
                    ajListGetLength(idlist) - idlen);
            idlen = ajListGetLength(idlist);
        }

	GBT_RIGHT(buf,&right);

	if(right)
	{
	    page = btreePricacheRead(cache,right);
	    buf = page->buf;
	}
    }
    

    ajListSortTwoUnique(idlist, &btreeIdDbnoCompare, &btreeIdOffsetCompare,
                        &btreeIdDelFromList);

    ajListFree(&list);

    return;
}
ID btreeKeyFullSearchHit
TY static
MO ajindex
LB core
XX
DE Wildcard retrieval of id/acc/sv entries. Whole index scan. Only used for
DE wildcard searches with keys beginning with '?' or '*'
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD Wildcard key
PX
PN [3]
PA u hitlist AjPList
PD list of matching AjPBtHits
PX
RT void
RD 
RX
//


static void btreeKeyFullSearchHit(AjPBtcache cache, const AjPStr key,
                                  AjPList hitlist)
{
    AjPBtId  id    = NULL;
    AjPBtHit hit   = NULL;
    AjPBtpage root = NULL;
    AjPBtpage page = NULL;
    ajulong right   = 0L;
    ajuint nodetype = 0;
    
    AjPBtMem arrays = NULL;
    ajulong *parray = NULL;

    AjPList list   = NULL;
    
    unsigned char *buf = NULL;    

    ajulong hitlen = 0UL;

    ajDebug("btreeKeyFullSearchId key '%S'\n", key);

    list   = ajListNew();

    root = btreePricacheLocate(cache, 0L);
    page = root;
    
    buf = root->buf;
    GBT_NODETYPE(buf,&nodetype);
    
    if(cache->plevel)
    {
        arrays = btreeAllocPriArray(cache);
        parray = arrays->parray;

        /* down to the leftmost leaf node */

	while(nodetype != BT_LEAF)
	{
	    btreeGetPointers(cache,buf,&parray);
	    page = btreePricacheRead(cache, parray[0]);
	    buf = page->buf;
	    GBT_NODETYPE(buf,&nodetype);
	    page->dirty = BT_CLEAN;
	}

        btreeDeallocPriArray(cache,arrays);
    }

    right = 99L;

    /* read left-to-right through all leaf nodes */

    while(right)
    {
	btreeIdleafFetch(cache,page,list);

	while(ajListPop(list,(void **)&id))
	{
	    if(ajStrMatchWildS(id->id,key))
            {
                hit = ajBtreeHitNewId(id);
		ajListPushAppend(hitlist,(void *)hit);
                hit = NULL;
            }
            ajBtreeIdDel(&id);
	}

        if(hitlen != ajListGetLength(hitlist))
        {
            ajDebug("Page %Lu found %Lu\n", page->pagepos,
                    ajListGetLength(hitlist) - hitlen);
            hitlen = ajListGetLength(hitlist);
        }

	GBT_RIGHT(buf,&right);

	if(right)
	{
	    page = btreePricacheRead(cache,right);
	    buf = page->buf;
	}
    }
    

    ajListSortTwoUnique(hitlist, &btreeHitDbnoCompare, &btreeHitOffsetCompare,
                        &btreeHitDelFromList);

    ajListFree(&list);

    return;
}
ID btreeKeyFullSearchHitref
TY static
MO ajindex
LB core
XX
DE Wildcard retrieval of id/acc/sv entries. Whole index scan. Only used for
DE wildcard searches with keys beginning with '?' or '*'
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD Wildcard key
PX
PN [3]
PA u hitlist AjPList
PD list of matching AjPBtHitrefs
PX
RT void
RD 
RX
//


static void btreeKeyFullSearchHitref(AjPBtcache cache, const AjPStr key,
                                     AjPList hitlist)
{
    AjPBtId  id    = NULL;
    AjPBtHitref hitref = NULL;
    AjPBtpage root = NULL;
    AjPBtpage page = NULL;
    ajulong right   = 0L;
    ajuint nodetype = 0;
    
    AjPBtMem arrays = NULL;
    ajulong *parray = NULL;

    AjPList list   = NULL;
    
    unsigned char *buf = NULL;    

    ajulong hitlen = 0UL;

    ajDebug("btreeKeyFullSearchId key '%S'\n", key);

    list   = ajListNew();

    root = btreePricacheLocate(cache, 0L);
    page = root;
    
    buf = root->buf;
    GBT_NODETYPE(buf,&nodetype);
    
    if(cache->plevel)
    {
        arrays = btreeAllocPriArray(cache);
        parray = arrays->parray;

        /* down to the leftmost leaf node */

	while(nodetype != BT_LEAF)
	{
	    btreeGetPointers(cache,buf,&parray);
	    page = btreePricacheRead(cache, parray[0]);
	    buf = page->buf;
	    GBT_NODETYPE(buf,&nodetype);
	    page->dirty = BT_CLEAN;
	}

        btreeDeallocPriArray(cache,arrays);
    }

    right = 99L;

    /* read left-to-right through all leaf nodes */

    while(right)
    {
	btreeIdleafFetch(cache,page,list);

	while(ajListPop(list,(void **)&id))
	{
	    if(ajStrMatchWildS(id->id,key))
            {
                hitref = ajBtreeHitrefNewId(id);
		ajListPushAppend(hitlist,(void *)hitref);
                hitref = NULL;
            }
            ajBtreeIdDel(&id);
	}

        if(hitlen != ajListGetLength(hitlist))
        {
            ajDebug("Page %Lu found %Lu\n", page->pagepos,
                    ajListGetLength(hitlist) - hitlen);
            hitlen = ajListGetLength(hitlist);
        }

	GBT_RIGHT(buf,&right);

	if(right)
	{
	    page = btreePricacheRead(cache,right);
	    buf = page->buf;
	}
    }
    

    ajListSortTwoUnique(hitlist,
                        &btreeHitrefDbnoCompare, &btreeHitrefOffsetCompare,
                        &btreeHitrefDelFromList);

    ajListFree(&list);

    return;
}
ID ajBtreeReplaceId
TY public
MO ajindex
LB core
XX
DE Replace an ID structure in a leaf node given a key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r rid const AjPBtId
PD replacement id object
PX
RT AjBool
RD true if success
RX
//


AjBool ajBtreeReplaceId(AjPBtcache cache, const AjPBtId rid)
{
    AjPBtpage page   = NULL;
    AjPIdbucket bucket = NULL;
    AjPBtId   id     = NULL;
    const AjPStr key = NULL;
    
    unsigned char *buf = NULL;

    ajuint nentries = 0;
    
    ajuint i;
    ajuint iref;

    ajulong blockno = 0L;
    AjBool found   = ajFalse;


    key = rid->id;

    page = btreeIdentFind(cache,key);
    buf = page->buf;

    blockno = btreeGetBlockS(cache,buf,key);
    bucket = btreeReadIdbucket(cache,blockno);
    
    nentries = bucket->Nentries;

    found = ajFalse;

    for(i=0;iIds[i]->id))
	{
	    found = ajTrue;
	    break;
	}
    }

    if(found)
    {
	id->dbno = rid->dbno;
	id->dups = rid->dups;
	id->offset = rid->offset;

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
                id->refoffsets[iref] = rid->refoffsets[iref];
        }
        
	btreeWriteIdbucket(cache,bucket,blockno);
    }

    btreeIdbucketDel(&bucket);

    return ajTrue;
}
ID ajBtreeReadEntriesC
TY public
MO ajindex
LB core
XX
DE Read B+ tree entries from file
XX
PN [1]
PA r filename const char*
PD file name
PX
PN [2]
PA r indexdir const char*
PD index file directory
PX
PN [3]
PA r directory const char*
PD file directory
PX
PN [4]
PA w seqfiles AjPStr**
PD sequence file names
PX
PN [5]
PA w reffiles AjPStr***
PD reference file names (if any)
PX
PN [6]
PA w refcount ajuint*
PD Number of reference file(s) per entry
PX
RT ajuint
RD number of entries
RX
//


ajuint ajBtreeReadEntriesC(const char *filename, const char *indexdir,
                           const char *directory,
                           AjPStr **seqfiles, AjPStr ***reffiles,
                           ajuint *refcount)
{
    AjPStr line = NULL;
    AjPStr fn   = NULL;
    
    AjPList list;
    AjPList *reflist;

    AjPStr seqname = NULL;
    AjPStr refname = NULL;
    AjPStr tseqname = NULL;
    AjPStr trefname = NULL;
    
    AjPFile inf   = NULL;
    char p;
    ajulong entries = 0UL;
    ajuint iref;

    AjPStrTok handle = NULL;

    *refcount = 0;

    line    = ajStrNew();
    list    = ajListNew();

    tseqname = ajStrNew();
    trefname = ajStrNew();
    
    fn = ajStrNew();

    if(!*indexdir)
        ajFmtPrintS(&fn, "%s", filename);
    else
        ajFmtPrintS(&fn, "%s%s%s", indexdir, SLASH_STRING, filename);

    ajStrAppendC(&fn,".ent");

    inf = ajFileNewInNameS(fn);

    if(!inf)
	ajFatal("Cannot open database entries file %S",fn);

    while(ajReadlineTrim(inf, &line))
    {
	p = ajStrGetCharFirst(line);

	if(p == '#' || !ajStrGetLen(line))
	    continue;

	if(ajStrPrefixC(line,"Dual"))
	    *refcount = 1;
        
	if(ajStrPrefixC(line,"Reference "))
        {
	    ajFmtScanS(line,"%S%d",&trefname, refcount);
            --*refcount;
        }
        
	break;
    }
    

    if(!*refcount)
    {
	while(ajReadlineTrim(inf, &line))
	{
	    seqname = ajStrNew();
	    ajFmtScanS(line,"%S",&tseqname);
	    ajFmtPrintS(&seqname,"%s%s%S",directory,SLASH_STRING,tseqname);
	    ajListPushAppend(list,(void *)seqname);
	}

	ajListToarray(list,(void ***)&(*seqfiles));
	entries = ajListGetLength(list);
    }
    else
    {
        AJCNEW(reflist, *refcount);
        for(iref=0; iref < *refcount; iref++)
            reflist[iref] = ajListNew();

	while(ajReadlineTrim(inf, &line))
	{
	    seqname = ajStrNew();
	    refname = ajStrNew();
            handle = ajStrTokenNewC(line, " \t");
            ajStrTokenNextParse(&handle, &tseqname);
	    ajFmtPrintS(&seqname,"%s%s%S",directory,SLASH_STRING,tseqname);
	    ajListPushAppend(list,(void *)seqname);

            for(iref=0; iref < *refcount; iref++)
            {
                ajStrTokenNextParse(&handle, &trefname);
                ajFmtPrintS(&refname,"%s%s%S",directory,SLASH_STRING,trefname);
                ajListPushAppend(reflist[iref],(void *)refname);
            }
        }

	ajListToarray(list,(void ***)&(*seqfiles));

        for(iref=0; iref < *refcount; iref++)
            ajListToarray(reflist[iref],(void ***)&(*reffiles[iref]));

	entries = ajListGetLength(list);
    }
    
    
    ajStrTokenDel(&handle);
    ajListFree(&list);

    if(*refcount)
    {
        for(iref=0; iref < *refcount; iref++)
            ajListFree(&reflist[iref]);

        AJFREE(reflist);
    }
    
    ajStrDel(&line);
    ajStrDel(&fn);

    ajStrDel(&tseqname);
    ajStrDel(&trefname);
    

    ajFileClose(&inf);

    return (ajuint) entries;
}
ID ajBtreeReadEntriesS
TY public
MO ajindex
LB core
XX
DE Read B+ tree entries from file
XX
PN [1]
PA r filename const AjPStr
PD file name
PX
PN [2]
PA r indexdir const AjPStr
PD index file directory
PX
PN [3]
PA r directory const AjPStr
PD file directory
PX
PN [4]
PA w seqfiles AjPStr**
PD sequence file names
PX
PN [5]
PA w reffiles AjPStr***
PD reference file names (if any)
PX
PN [6]
PA w refcount ajuint*
PD Number of reference file(s) per entry
PX
RT ajuint
RD number of entries
RX
//


ajuint ajBtreeReadEntriesS(const AjPStr filename, const AjPStr indexdir,
                           const AjPStr directory,
                           AjPStr **seqfiles, AjPStr ***reffiles,
                           ajuint *refcount)
{
    AjPStr line = NULL;
    AjPStr fn   = NULL;
    
    AjPList list;
    AjPList *reflist;

    AjPStr seqname = NULL;
    AjPStr refname = NULL;
    AjPStr tseqname = NULL;
    AjPStr trefname = NULL;
    
    AjPFile inf   = NULL;
    char p;
    ajulong entries = 0UL;
    ajuint iref;

    AjPStrTok handle = NULL;

    *refcount = 0;

    line    = ajStrNew();
    list    = ajListNew();

    tseqname = ajStrNew();
    trefname = ajStrNew();
    
    fn = ajStrNew();

    if(!ajStrGetLen(indexdir))
        ajFmtPrintS(&fn,"%S.ent",filename);
    else
        ajFmtPrintS(&fn,"%S%s%S.ent",indexdir,SLASH_STRING,filename);
    
    inf = ajFileNewInNameS(fn);

    if(!inf)
	ajFatal("Cannot open database entries file %S", fn);

    while(ajReadlineTrim(inf, &line))
    {
	p = ajStrGetCharFirst(line);

	if(p == '#' || !ajStrGetLen(line))
	    continue;

	if(ajStrPrefixC(line,"Dual"))
	    *refcount = 1;
        
	if(ajStrPrefixC(line,"Reference "))
        {
	    ajFmtScanS(line,"%S%d",&trefname, refcount);
            --*refcount;
        }
    
	break;
    }
    

    if(!*refcount)
    {
	while(ajReadlineTrim(inf, &line))
	{
	    seqname = ajStrNew();
	    ajFmtScanS(line,"%S",&tseqname);
	    ajFmtPrintS(&seqname,"%S%s%S",directory,SLASH_STRING,tseqname);
	    ajListPushAppend(list,(void *)seqname);
	}

	ajListToarray(list,(void ***)&(*seqfiles));
	entries = ajListGetLength(list);
    }
    else
    {
        AJCNEW(reflist, *refcount);
        for(iref=0; iref < *refcount; iref++)
            reflist[iref] = ajListNew();

	while(ajReadlineTrim(inf, &line))
	{
	    seqname = ajStrNew();
	    refname = ajStrNew();
            ajStrTokenAssignC(&handle, line, " \t");
            ajStrTokenNextParse(&handle, &tseqname);
	    ajFmtPrintS(&seqname,"%S%s%S",directory,SLASH_STRING,tseqname);
	    ajListPushAppend(list,(void *)seqname);

            for(iref=0; iref < *refcount; iref++)
            {
                ajStrTokenNextParse(&handle, &trefname);
                ajFmtPrintS(&refname,"%S%s%S",directory,SLASH_STRING,trefname);
                ajListPushAppend(reflist[iref],(void *)refname);
            }
        }

	ajListToarray(list,(void ***)&(*seqfiles));

        if(reffiles)
        {
            AJCNEW0(*reffiles, *refcount);
            for(iref=0; iref < *refcount; iref++)
                ajListToarray(reflist[iref],(void ***)&(*reffiles[iref]));
        }

	entries = ajListGetLength(list);
    }
    
    ajStrTokenDel(&handle);
    ajListFree(&list);

    if(*refcount)
    {
        for(iref=0; iref < *refcount; iref++)
            ajListFree(&reflist[iref]);

        AJFREE(reflist);
    }
    
    ajStrDel(&line);
    ajStrDel(&fn);

    ajStrDel(&tseqname);
    ajStrDel(&trefname);
    

    ajFileClose(&inf);

    return (ajuint) entries;
}
ID btreePribucketNew
TY static
MO ajindex
LB core
XX
DE Construct a primary keyword bucket object
XX
PN [1]
PA r n ajuint
PD Number of IDs
PX
RT AjPPribucket
RD initialised disc block cache structure
RX
//


static AjPPribucket btreePribucketNew(ajuint n)
{
    AjPPribucket bucket = NULL;
    ajuint i;

    /*ajDebug("In btreePribucketNew %u statsave: %u empty: %u\n",
      n, statSavePribucketNext, statSavePribucketEmptyNext);*/
    

    if(n)
    {
        if(statSavePribucketNext)
        {
            bucket = statSavePribucket[--statSavePribucketNext];
            for(i=0;iMaxentries;++i)
            {
                ajStrAssignClear(&bucket->codes[i]->id);
                ajStrAssignClear(&bucket->codes[i]->keyword);
                bucket->codes[i]->treeblock = 0L;
            }
            if(n > bucket->Maxentries)
            {
                AJCRESIZE0(bucket->keylen,bucket->Maxentries,n);
                AJCRESIZE0(bucket->codes,bucket->Maxentries,n);
                for(i=bucket->Maxentries;icodes[i] = ajBtreePriNew();
                bucket->Maxentries = n;
            }
        }
        else
        {
            AJNEW0(bucket);
            AJCNEW0(bucket->codes,n);
            AJCNEW0(bucket->keylen,n);
            for(i=0;icodes[i] = ajBtreePriNew();
            bucket->Maxentries = n;
        }
        
    }
    else 
    {
        if(statSavePribucketEmptyNext)
            bucket = statSavePribucketEmpty[--statSavePribucketEmptyNext];
        else
            AJNEW0(bucket);
    }
        
    bucket->NodeType = BT_PRIBUCKET;
    bucket->Nentries = n;
    bucket->Overflow = 0L;
    
    return bucket;
}
ID btreePribucketDel
TY static
MO ajindex
LB core
XX
DE Delete a keyword primary bucket object
XX
PN [1]
PA w thys AjPPribucket*
PD bucket
PX
RT void
RD 
RX
//


static void btreePribucketDel(AjPPribucket *thys)
{
    AjPPribucket pthis = NULL;
    ajuint newmax;
    
    if(!thys || !*thys)
	return;

    pthis = *thys;

    /*ajDebug("In btreePribucketDel maxentries: %u savepribucket %u empty %u\n",
            pthis->Maxentries, statSavePribucketNext,
            statSavePribucketEmptyNext);*/

    if(!statSavePribucket)
    {
        statSavePribucketMax=2048;
        statSavePribucketNext=0;
        AJCNEW0(statSavePribucket,statSavePribucketMax);
    }
    
    if(!statSavePribucketEmpty)
    {
        statSavePribucketEmptyMax=2048;
        statSavePribucketEmptyNext=0;
        AJCNEW0(statSavePribucketEmpty,statSavePribucketEmptyMax);
    }
    
    if(pthis->Maxentries)
    {
        if(statSavePribucketNext >= statSavePribucketMax)
        {
            newmax = statSavePribucketMax + statSavePribucketMax;
            AJCRESIZE0(statSavePribucket,statSavePribucketMax,newmax);
            statSavePribucketMax = newmax;
        }
        
        statSavePribucket[statSavePribucketNext++] = pthis;
    }
    else
    {
        if(statSavePribucketEmptyNext >= statSavePribucketEmptyMax)
        {
            newmax = statSavePribucketEmptyMax + statSavePribucketEmptyMax;
            AJCRESIZE0(statSavePribucketEmpty,statSavePribucketEmptyMax,newmax);
            statSavePribucketEmptyMax = newmax;
        }
        statSavePribucketEmpty[statSavePribucketEmptyNext++] = pthis;
    }

    *thys = NULL;

    return;
}
ID btreePribucketFree
TY static
MO ajindex
LB core
XX
DE Delete a keyword primary bucket object
XX
PN [1]
PA w thys AjPPribucket*
PD bucket
PX
RT void
RD 
RX
//


static void btreePribucketFree(AjPPribucket *thys)
{
    AjPPribucket pthis = NULL;
    ajuint n;
    ajuint i;

    if(!thys || !*thys)
	return;

    pthis = *thys;
    n = pthis->Maxentries;

    /*ajDebug("In btreePribucketFree %u\n",pthis->Maxentries);*/

    for(i=0;icodes[i]);
    
    AJFREE(pthis->keylen);
    AJFREE(pthis->codes);
    
    AJFREE(pthis);

    *thys = NULL;

    return;
}
ID ajBtreePriNew
TY public
MO ajindex
LB core
XX
DE Constructor for index bucket keyword information
XX
RT AjPBtPri
RD Index ID object
RX
//


AjPBtPri ajBtreePriNew(void)
{
    AjPBtPri pri = NULL;

    /* ajDebug("In ajBtreePriNew\n"); */

    if(statSaveBtreePriNext)
    {
        pri = statSaveBtreePri[--statSaveBtreePriNext];
        MAJSTRASSIGNCLEAR(&pri->id);
        MAJSTRASSIGNCLEAR(&pri->keyword);
        pri->treeblock = 0L;
    }
    else
    {
        AJNEW0(pri);
        pri->keyword   = ajStrNew();
        pri->id        = ajStrNew();
        pri->treeblock = 0L;
    }
    
    return pri;
}
ID ajBtreePriDel
TY public
MO ajindex
LB core
XX
DE Destructor for keyword index primary bucket information
XX
PN [1]
PA w thys AjPBtPri*
PD index keyword primary object
PX
RT void
RD 
RX
//


void ajBtreePriDel(AjPBtPri *thys)
{
    ajuint newmax;

    /* ajDebug("In ajBtreePriDel\n"); */

    if(!statSaveBtreePri)
    {
        statSaveBtreePriMax = 2048;
        AJCNEW0(statSaveBtreePri, statSaveBtreePriMax);
        statSaveBtreePriNext = 0;
    }

    if(!thys || !*thys)
	return;
    
    if(statSaveBtreePriNext >= statSaveBtreePriMax) 
    {
        newmax = statSaveBtreePriMax + statSaveBtreePriMax;
        AJCRESIZE0(statSaveBtreePri,statSaveBtreePriMax,newmax);
        statSaveBtreePriMax = newmax;
    }
    
    statSaveBtreePri[statSaveBtreePriNext++] = *thys;
    
    *thys = NULL;

    return;
}
ID btreePriFree
TY static
MO ajindex
LB core
XX
DE Destructor for index primary bucket information
XX
PN [1]
PA w thys AjPBtPri*
PD index keyword primary object
PX
RT void
RD 
RX
//


static void btreePriFree(AjPBtPri *thys)
{
    AjPBtPri pri = NULL;

    if(!thys || !*thys)
	return;
    pri = *thys;
    
    /*ajDebug("In ajBtreePriFree id '%S' key '%S'\n",
      pri->id, pri->keyword);*/

    ajStrDel(&pri->id);
    ajStrDel(&pri->keyword);
    AJFREE(pri);
    *thys = NULL;

    return;
}
ID btreePribucketIdlist
TY static
MO ajindex
LB core
XX
DE Copies all primary keys into a list
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA u idlist AjPList
PD list to hold keys
PX
RT ajulong
RD Overflow
RX
//


static ajulong btreePribucketIdlist(AjPBtcache cache, ajulong pagepos,
                                   AjPList idlist)
{
    AjPBtpage page      = NULL;
    AjPBtpage lpage     = NULL;
    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    AjPBtPri pri        = NULL;
    
    unsigned char *codeptr = NULL;
    
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajulong pageoverflow = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  len  = 0;
    ajuint idlen = 0;
    
    /* ajDebug("In btreePribucketIdlist\n"); */
    
    if(!pagepos)
	ajFatal("PribucketIdlist: cannot read bucket from root page cache %S",
                cache->filename);

    page  = btreePricacheRead(cache,pagepos);
    lpage = page;
    dirtysave = lpage->dirty;
    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1211;

    buf = lpage->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_PRIBUCKET)
	ajFatal("PribucketIdlist: NodeType mismatch. "
                "Not primary bucket (%u) cache %S",
		nodetype, cache->filename);
    
    GBT_BUCKNENTRIES(buf,&nentries);
    

    GBT_BUCKOVERFLOW(buf,&overflow);
    pageoverflow = overflow;
    
    kptr  = PBT_BUCKKEYLEN(buf);
    codeptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//	    /# ajDebug("PribucketRead: Overflow\n"); #/
//	    page  = btreePricacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//	    if(nodetype != BT_PRIBUCKET)
//		ajFatal("PribucketIdlist: NodeType mismatch. Not primary "
//			"bucket (%u) cache %S",
//                        nodetype, cache->filename);
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    codeptr = PBT_BUCKKEYLEN(buf);
//	}
*/

	pri = ajBtreePriNew();

	/* Fill ID objects */
	ajStrAssignLenC(&pri->keyword,(const char *)codeptr,idlen);
	codeptr += (idlen + 1);
	BT_GETAJULONG(codeptr,&pri->treeblock);
	codeptr += sizeof(ajulong);

	kptr += sizeof(ajuint);
        ajListPushAppend(idlist, pri);
    }

    lpage->dirty = dirtysave;
    
    return pageoverflow;
}
ID btreePribucketRead
TY static
MO ajindex
LB core
XX
DE Constructor for keyword index primary bucket given a disc page number
DE Creates one empty key slot for possible addition
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT AjPPribucket
RD bucket
RX
//


static AjPPribucket btreePribucketRead(AjPBtcache cache, ajulong pagepos)
{
    AjPPribucket bucket = NULL;
    AjPBtpage page      = NULL;
    AjPBtpage lpage     = NULL;
    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    AjPBtPri pri        = NULL;
    
    unsigned char *codeptr = NULL;
    
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  len  = 0;
    
    /* ajDebug("In btreePribucketRead\n"); */
    
    if(!pagepos)
	ajFatal("PribucketRead: cannot read bucket from root page cache %S",
                cache->filename);

    page  = btreePricacheRead(cache,pagepos);
    lpage = page;
    dirtysave = lpage->dirty;
    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1221;

    buf = lpage->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);

    if(nodetype != BT_PRIBUCKET)
	ajFatal("PriReadBucket: NodeType mismatch. "
                "Not primary bucket (%u) page %Lu cache %S",
		nodetype, pagepos, cache->filename);
    
    GBT_BUCKNENTRIES(buf,&nentries);

    if(nentries > cache->pnperbucket)
	ajFatal("PriReadBucket: Bucket too full page: %Lu "
                "entries: %u max: %u page %Lu cache %S",
                pagepos, nentries, cache->pnperbucket,
                pagepos, cache->filename);
    

    GBT_BUCKOVERFLOW(buf,&overflow);

    bucket = btreePribucketNew(cache->pnperbucket);
    bucket->Nentries = nentries;
    
    kptr  = PBT_BUCKKEYLEN(buf);
    codeptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//#if AJINDEX_DEBUG
//	    ajDebug("PriReadBucket: Overflow\n");
//#endif
//	    page  = btreePricacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//
//	    if(nodetype != BT_PRIBUCKET)
//		ajFatal("PriReadBucket: NodeType mismatch. Not primary "
//			"bucket (%u) page %Lu", nodetype, pagepos);
//
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    codeptr = PBT_BUCKKEYLEN(buf);
//	}
*/

	pri = bucket->codes[i];

	/* Fill ID objects */
	ajStrAssignC(&pri->keyword,(const char *)codeptr);
	codeptr += (strlen((const char *)codeptr) + 1);
	BT_GETAJULONG(codeptr,&pri->treeblock);
	codeptr += sizeof(ajulong);

	kptr += sizeof(ajuint);
    }

    lpage->dirty = dirtysave;
    
    return bucket;
}
ID btreePribucketSort
TY static
MO ajindex
LB core
XX
DE Sorts IDs in a primary keyword bucket
XX
PN [1]
PA u thys AjPPribucket
PD cache
PX
RT void
RD 
RX
//


static void btreePribucketSort(AjPPribucket thys)
{
    qsort(thys->codes, thys->Nentries, sizeof(AjPBtPri), btreeKeywordCompare);

    return;
}
ID btreeWritePribucket
TY static
MO ajindex
LB core
XX
DE Write primary keyword index bucket object to the cache given a disc page
DE number
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r bucket const AjPPribucket
PD bucket
PX
PN [3]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreeWritePribucket(AjPBtcache cache, const AjPPribucket bucket,
				ajulong pagepos)
{
    AjPBtpage page  = NULL;
    AjPBtpage lpage = NULL;

    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;

    ajuint  v   = 0;
    ajuint i   = 0;
    ajuint len = 0;
    ajulong lv  = 0L;

    AjPBtPri pri    = NULL;
    ajuint nentries = 0;
    ajulong overflow = 0L;

    unsigned char *keyptr = NULL;
    unsigned char *lptr   = NULL;

/*    ajulong pno = 0L;*/


    /* ajDebug("In btreeWritePribucket\n"); */

    if(pagepos == cache->totsize)	/* Create a new page */
    {
	/* pno = pagepos; */
	page = btreePricacheBucketnew(cache);
	buf = page->buf;
        overflow = 0L;
    }
    else
    {
	page = btreePricacheRead(cache,pagepos);
	buf = page->buf;
	GBT_BUCKOVERFLOW(buf,&overflow);
    }

    v = BT_PRIBUCKET;
    SBT_BUCKNODETYPE(buf,v);

    lbuf = buf;
    page->dirty = BT_LOCK;
    page->lockfor = 1231;
    lpage = page;

    nentries = bucket->Nentries;
    v = nentries;
    SBT_BUCKNENTRIES(buf,v);

    /* Write out key lengths */
    keyptr = PBT_BUCKKEYLEN(lbuf);

    for(i=0;i cache->pagesize)
//	    ajFatal("WritePribucket: Bucket cannot hold more than %u keys",
//		    i-1);
*/

	pri = bucket->codes[i];
	/* Need to alter this if bucket primary keyword structure changes */
	len = BT_BUCKPRILEN(pri->keyword);
        v = len;
	BT_SETAJUINT(keyptr,v);
	keyptr += sizeof(ajuint);
    }


    /* Write out IDs using overflow if necessary */
    lptr = keyptr;
    for(i=0;icodes[i];
	len = BT_BUCKPRILEN(pri->keyword);

/*
//	if((lptr-buf+1)+len > cache->pagesize) /# overflow #/
//	{
//    	    /#ajDebug("WritePribucket: Overflow\n");#/
//
//	    if(!overflow)		/# No overflow buckets yet #/
//	    {
//		pno = cache->totsize;
//                lv = pno;
//		SBT_BUCKOVERFLOW(buf,lv);
//		page = btreePricacheBucketnew(cache);
//                buf = page->buf;
//		v = BT_PRIBUCKET;
//		SBT_BUCKNODETYPE(buf,v);
//	    }
//	    else
//	    {
//		page = btreePricacheRead(cache,overflow);
//		buf  = page->buf;
//		GBT_BUCKOVERFLOW(buf,&overflow);
//	    }
//
//	    page->dirty = BT_DIRTY;
//
//	    lptr = PBT_BUCKKEYLEN(buf);	    
//	}
*/	

	sprintf((char *)lptr,"%s",ajStrGetPtr(pri->keyword));
	lptr += (ajStrGetLen(pri->keyword) + 1);
        lv = pri->treeblock;
	BT_SETAJULONG(lptr,lv);
	lptr += sizeof(ajulong);
    }

    lv = 0L;
    SBT_BUCKOVERFLOW(buf,lv);

    lpage->dirty = BT_DIRTY;

    return;
}
ID btreeWritePribucketEmpty
TY static
MO ajindex
LB core
XX
DE Write empty primary keyword index bucket object to the cache given a
DE disc page number
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreeWritePribucketEmpty(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage page  = NULL;
    AjPBtpage lpage = NULL;

    unsigned char *buf  = NULL;

    ajuint  v   = 0;
    ajulong lv  = 0L;

    ajulong overflow = 0L;

    /* ajDebug("In btreeWritePribucketEmpty\n"); */

    if(pagepos == cache->totsize)	/* Create a new page */
    {
	page = btreePricacheBucketnew(cache);
	buf = page->buf;
	overflow = 0L;
	lv = overflow;
	SBT_BUCKOVERFLOW(buf,lv);
    }
    else
    {
	page = btreePricacheRead(cache,pagepos);
	buf = page->buf;
	GBT_BUCKOVERFLOW(buf,&overflow);
    }

    v = BT_PRIBUCKET;
    SBT_BUCKNODETYPE(buf,v);

    page->dirty = BT_LOCK;      /* cleared at end */
    page->lockfor = 1241;
    lpage = page;

    v = 0;
    SBT_BUCKNENTRIES(buf,v);

    lv = 0L;
    SBT_BUCKOVERFLOW(buf,lv);

    lpage->dirty = BT_DIRTY;    /* clear the lock */

    return;
}
ID btreePribucketAdd
TY static
MO ajindex
LB core
XX
DE Add a keyword ID to a primary bucket
DE Only called if there is room in the bucket
DE
DE The primary bucket entry for the keyword stores the root page of
DE the secondary tree of ids
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number of bucket
PX
PN [3]
PA r keyword const AjPStr
PD Keyword
PX
PN [4]
PA r id const AjPStr
PD Identifier
PX
RT void
RD 
RX
//


static void btreePribucketAdd(AjPBtcache cache, ajulong pagepos,
                              const AjPStr keyword, const AjPStr id)
{
    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    unsigned char *src  = NULL;
    unsigned char *dest = NULL;

/*    unsigned char *lastptr = NULL;*/
    unsigned char *endptr  = NULL;
    
    ajuint nentries = 0;
    ajuint nodetype = 0;
    ajuint idlen    = 0;

    ajuint sum = 0;
    ajuint len = 0;
    ajuint i;
    ajuint v;
    ajulong lv;
    
    ajulong secrootpage = 0L;
    ajulong treeblock  = 0L;

    AjPBtpage page = NULL;
    static ajuint calls = 0;
/*    static ajuint overflowcalls=0;*/

    calls++;

    treeblock = 0;

    /* secondary tree does not exist, can use secbucket */

    secrootpage = cache->totsize;

    btreeWriteSecbucketEmpty(cache,secrootpage);
    btreeSecbucketAdd(cache,secrootpage,id);

    treeblock = secrootpage;
    cache->secrootblock = secrootpage;
    cache->slevel = 0;

    /* add to the Pribucket page */

    page = btreePricacheRead(cache,pagepos);
    buf  = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);

    if(nodetype != BT_PRIBUCKET)
        ajFatal("Wrong nodetype in PribucketAdd cache %S",
                cache->filename);

    GBT_BUCKNENTRIES(buf,&nentries);

    if(nentries >= cache->pnperbucket)
        ajFatal("Bucket too full in PribucketAdd page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->pnperbucket,
                cache->filename);    

    kptr = PBT_BUCKKEYLEN(buf);
    src  = kptr + (nentries * sizeof(ajuint));

    for(i=0;i= cache->pripagesize)
//    {
//        overflowcalls++;
//
//        ajWarn("\nOverflow in PribucketAdd nentries:%u fails %u/%u cache %S",
//               nentries, overflowcalls, calls, cache->filename);
//
//        btreePribucketAddFull(cache,pagepos,keyword, id);
//        return;
//    }
*/    

    dest = src + sizeof(ajuint);
    memmove((void *)dest, (void *)src, sum);

    v = BT_BUCKPRILEN(keyword);
    BT_SETAJUINT(src,v);

    endptr += sizeof(ajuint) + 1;
    strcpy((char *)endptr,MAJSTRGETPTR(keyword));

    endptr += idlen + 1;
    lv = treeblock;
    BT_SETAJULONG(endptr,lv);
    endptr += sizeof(ajulong);

    v = nentries + 1;
    SBT_BUCKNENTRIES(buf,v);

    page->dirty = BT_DIRTY;

    return;
}
ID btreeNumInPribucket
TY static
MO ajindex
LB core
XX
DE Return number of entries in a primary keyword bucket
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT ajuint
RD Number of entries in bucket
RX
//


static ajuint btreeNumInPribucket(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage page     = NULL;
    unsigned char *buf = NULL;

    ajuint  nodetype = 0;
    ajuint nentries = 0;
    
    /* ajDebug("In btreeNumInPribucket\n"); */
    
    if(!pagepos)
	ajFatal("NumInPribucket: Attempt to read bucket from root page\n");

    page  = btreePricacheRead(cache,pagepos);

    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);

    if(nodetype != BT_PRIBUCKET)
	ajFatal("PriReadBucket: NodeType mismatch. Not primary bucket (%u)",
		nodetype);
    
    GBT_BUCKNENTRIES(buf,&nentries);

    return nentries;
}
ID btreeKeywordCompare
TY static
MO ajindex
LB core
XX
DE Comparison function for ajListSort
XX
PN [1]
PA r a const void*
PD ID 1
PX
PN [2]
PA r b const void*
PD ID 2
PX
RT ajint
RD 0 = bases match
RX
//


static ajint btreeKeywordCompare(const void *a, const void *b)
{
    return MAJSTRCMPS((*(AjPBtPri const *)a)->keyword,
                      (*(AjPBtPri const *)b)->keyword);
}
ID btreePribucketsReorder
TY static
MO ajindex
LB core
XX
DE Re-order primary keyword leaf buckets
DE Must only be called if one of the buckets is full
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u leaf AjPBtpage
PD leaf page
PX
RT AjBool
RD true if reorder was successful i.e. leaf not full
RX
//


static AjBool btreePribucketsReorder(AjPBtcache cache, AjPBtpage leaf)
{
    ajuint nkeys = 0;
    unsigned char *lbuf   = NULL;

    ajulong *ptrs      = NULL;
    AjPStr *newkeys      = NULL;
    ajulong *newptrs      = NULL;
    ajulong *overflows = NULL;
    AjPBtMem arrays1 = NULL;
    AjPBtMem arrays2 = NULL;
    
    ajuint i = 0;
    
    ajuint order;
    ajuint totalkeys     = 0;
    ajuint maxnperbucket = 0;
    ajuint count         = 0;
    ajuint keylimit      = 0;
    ajuint bucketlimit   = 0;
    ajuint nodetype      = 0;
    ajuint dirtysave     = 0;    

    AjPList idlist       = NULL;
    AjPBtPri bid         = NULL;
    AjPPribucket cbucket = NULL;
    AjPBtPri cid         = NULL;

    ajuint iold = 0;
    
#if AJINDEX_DEBUG
    ajDebug("btreePribucketsReorder %Lu\n", leaf->pagepos);
#endif

    /* ajDebug("In btreePribucketsReorder\n"); */

    dirtysave = leaf->dirty;

    leaf->dirty = BT_LOCK;
    leaf->lockfor = 1271;
    lbuf = leaf->buf;

    GBT_NODETYPE(lbuf,&nodetype);

    order = cache->porder;
    
    /* Read keys/ptrs */
    arrays1 = btreeAllocPriArray(cache);
    ptrs = arrays1->parray;
    arrays2 = btreeAllocPriArray(cache);
    newkeys = arrays2->karray;
    newptrs = arrays2->parray;
    overflows = arrays2->overflows;
    
    btreeGetPointers(cache,lbuf,&ptrs);

    GBT_NKEYS(lbuf,&nkeys);
    keylimit = nkeys + 1;

    if(!nkeys)
	ajFatal("PribucketsReorder: Attempt to reorder empty leaf");

    for(i=0;ipnperbucket,
                    &bucketlimit, &maxnperbucket);

    if(bucketlimit >= order)
    {
        btreeDeallocPriArray(cache,arrays1);
        btreeDeallocPriArray(cache,arrays2);
	leaf->dirty = dirtysave;

	return ajFalse;
    }
    
    ++statCallPribucketsReorder;

    /* Read IDs from all buckets and push to list and sort (increasing kw) */
    idlist  = ajListNew();
    
    for(i=0;ipnperbucket);
    
    iold = 0;
    for(i=0;iOverflow = overflows[i];
	cbucket->Nentries = 0;

        count = 0;
	while(count!=maxnperbucket)
	{
	    ajListPop(idlist,(void **)&bid);
	    
	    cid = cbucket->codes[count];
	    ajStrAssignS(&cid->keyword,bid->keyword);
	    cid->treeblock = bid->treeblock;

	    cbucket->keylen[count] = BT_BUCKPRILEN(bid->keyword);
	    ++cbucket->Nentries;
	    ++count;
	    ajBtreePriDel(&bid);
	}


	ajListPeek(idlist,(void **)&bid);
	ajStrAssignS(&newkeys[i],bid->keyword);

	if((iold < order) && ptrs[iold])
            newptrs[i] = ptrs[iold++];
        else
	    newptrs[i] = cache->totsize;
	btreeWritePribucket(cache,cbucket,newptrs[i]);
    }


    /* Deal with greater-than bucket */

    cbucket->Overflow = overflows[i];
    cbucket->Nentries = 0;

    count = 0;

    while(ajListPop(idlist,(void **)&bid))
    {
	cid = cbucket->codes[count];
	ajStrAssignS(&cid->keyword,bid->keyword);
	cid->treeblock = bid->treeblock;

	++cbucket->Nentries;
	++count;
	ajBtreePriDel(&bid);
    }
    
    
    if((iold < order) && ptrs[iold])
        newptrs[i] = ptrs[iold++];
    else
        newptrs[i] = cache->totsize;
    btreeWritePribucket(cache,cbucket,newptrs[i]);

    btreePribucketDel(&cbucket);

#if AJINDEX_DEBUG
    if(bucketlimit <= keylimit)
        ajDebug("btreePribucketsReorder '%S' %u -> %u",
                cache->filename, keylimit, bucketlimit);
#endif

    for(i = bucketlimit + 1; i <= keylimit; i++)
    {
        btreePripageSetfree(cache, ptrs[i]);
    }

    /* Now write out a modified leaf with new keys/ptrs */

    nkeys = bucketlimit;
    btreeWriteNode(cache,leaf,newkeys,newptrs,nkeys);
    leaf->dirty = BT_DIRTY;

    if(nodetype == BT_ROOT)
    {
	leaf->dirty = BT_LOCK;
        leaf->lockfor = 1272;
    }
    
    btreeDeallocPriArray(cache,arrays1);
    btreeDeallocPriArray(cache,arrays2);

    ajListFree(&idlist);

    return ajTrue;
}
ID ajBtreeKeyIndex
TY public
MO ajindex
LB core
XX
DE Insert a keyword structure into the tree.
DE
DE This is the primary function for adding a keyword and identifier to
DE a secondary keyword index
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r keyword const AjPStr
PD keyword
PX
PN [3]
PA r id const AjPStr
PD entry identifier
PX
RT AjBool
RD True if keyword and ID combination was inserted
RD False if keyword exists already for ID
RX
//


AjBool ajBtreeKeyIndex(AjPBtcache cache, const AjPStr keyword, const AjPStr id)
{
    AjPBtpage spage   = NULL;
    AjPBtpage page    = NULL;
    ajulong lblockno = 0L;
    ajulong rblockno = 0L;
    ajulong blockno  = 0L;
    ajulong shift    = 0L;

    ajuint nkeys = 0;

    ajuint nodetype = 0;
    ajulong right   = 0L;
    
    ajuint n;
    ajuint savedirty;
    unsigned char *buf   = NULL;

    ajulong treeblock = 0L;
    AjBool newid = ajTrue;

#if AJINDEX_DEBUG
    /*ajDebug("ajBtreeKeyIndex '%S' '%S'\n", pri->keyword, pri->id);*/
#endif

    if(!MAJSTRGETLEN(keyword))
	return ajFalse;

    ajStrAssignS(&indexKeyword, keyword);
    ajStrAssignS(&indexId, id);
    ajStrFmtQuery(&indexId);
    ajStrFmtQuery(&indexKeyword);

    if(MAJSTRGETLEN(indexKeyword) > cache->keylimit)
        ajStrTruncateLen(&indexKeyword, cache->keylimit);

    if(MAJSTRGETLEN(indexId) > cache->idlimit)
        ajStrTruncateLen(&indexId, cache->idlimit);

    /*
    ** Only insert a primary key if that key doesn't exist
    **
    ** We always have a secondary identifier if there is a match
    ** (for Identifiers there may be no secondary data so their code differs)
    */

    if(btreeKeyFind(cache, indexKeyword, &treeblock))
    {
        /* we have the keyword in the index */

	cache->secrootblock = treeblock;
	page = btreeSeccacheWrite(cache,cache->secrootblock);
        savedirty = page->dirty;
	page->dirty = BT_LOCK;
        page->lockfor = 1281;
	buf = page->buf;
	GBT_RIGHT(buf,&right);
	cache->slevel = (ajuint) right;

	if(btreeKeyidInsert(cache,indexId))
        {
            ++cache->countall;
            GBT_NODETYPE(buf,&nodetype);
            if(nodetype != BT_SECBUCKET)
            {
                right = (ajulong) cache->slevel;
                SBT_RIGHT(buf,right);
                page->dirty = BT_DIRTY;
            }
        }
        else
        {
            newid = ajFalse;
            page->dirty = savedirty;
        }
        
	return newid;
    }

    /* this is a new key */

    spage = btreeIdentFind(cache,indexKeyword);
    buf = spage->buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&nodetype);
    
    if(!nkeys)
    {
        /* new primary leaf node with no keys yet */

        /* create two buckets, add to the right-hand one */

	lblockno = cache->totsize;
	btreeWritePribucketEmpty(cache,lblockno);

	rblockno = cache->totsize;
	btreeWritePribucketEmpty(cache,rblockno);	

	btreeWriteNodeSingle(cache,spage,indexKeyword,lblockno,rblockno);

	GBT_BLOCKNUMBER(buf,&blockno);

	if(!blockno)
        {
	    spage->dirty = BT_LOCK; /* root page */
            spage->lockfor = 1282;
        }

	btreePribucketAdd(cache,rblockno,indexKeyword, indexId);
	++cache->countunique;
	++cache->countall;

	return ajTrue;
    }
    
    blockno = btreeGetBlockS(cache,buf,indexKeyword);
    if(nodetype != BT_ROOT)
	if((shift = btreeKeyInsertShift(cache,&spage,indexKeyword)))
	    blockno = shift;

    buf = spage->buf;

    n = btreeNumInPribucket(cache,blockno);

    if(n == cache->pnperbucket)
    {
	if(btreePribucketsReorder(cache,spage))
	{
            blockno = btreeGetBlockS(cache,buf,indexKeyword);
	}
	else
	{
	    btreeKeySplitleaf(cache,spage);
	    spage = btreeIdentFind(cache,indexKeyword);
	    buf = spage->buf;

            blockno = btreeGetBlockS(cache,buf,indexKeyword);
	}
    }

    btreePribucketAdd(cache, blockno, indexKeyword, indexId);
    ++cache->countunique;
    ++cache->countall;

    return ajTrue;
}
ID btreeKeyFind
TY static
MO ajindex
LB core
XX
DE Get secondary root block matching a keyword
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key
PX
PN [3]
PA w treeblock ajulong*
PD Tree block number for id
PX
RT AjBool
RD ajTrue if found
RX
//


static AjBool btreeKeyFind(AjPBtcache cache, const AjPStr key,
                           ajulong* treeblock)
{
    AjPBtpage page      = NULL;
    
    unsigned char *buf = NULL;

    ajulong blockno = 0L;

    /*ajDebug("btreeKeyFind '%s'\n", ckey);*/

    if(!cache->countunique)
	return ajFalse;

    page = btreeIdentFind(cache,key);

    buf = page->buf;

    blockno = btreeGetBlockS(cache, buf, key);

    return btreePribucketFindId(cache,blockno,key,treeblock);
}
ID ajBtreeKeyFindLen
TY public
MO ajindex
LB core
XX
DE Get keyword structure matching a keyword truncated to maximum indexed length
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key
PX
PN [3]
PA w treeblock ajulong*
PD Tree block number for id
PX
RT AjBool
RD ajTrue if found
RX
//


AjBool ajBtreeKeyFindLen(AjPBtcache cache, const AjPStr key,
                         ajulong* treeblock)
{
    AjBool ret = ajFalse;

    AjPBtpage page      = NULL;
    unsigned char *buf = NULL;
    ajulong blockno = 0L;

    AjPStr keystr = NULL;
    const AjPStr keytest = key;

#if AJINDEX_DEBUG
    ajDebug("ajBtreeKeyFindLen '%S'\n", key);
#endif

    if(!cache->countunique)
	return ajFalse;

    keystr = ajStrNewS(key);
    ajStrFmtQuery(&keystr);
    if(MAJSTRGETLEN(keystr) > cache->keylimit)
        ajStrTruncateLen(&keystr, cache->keylimit);
    keytest = keystr;

    ajDebug("ajBtreeKeyFindLen '%S'\n", keystr);

    page = btreeIdentFind(cache,keytest);

    buf = page->buf;

    blockno = btreeGetBlockS(cache, buf, keytest);

    ret = btreePribucketFindId(cache,blockno,keytest,treeblock);

    ajStrDel(&keystr);

    return ret;
}
ID btreePribucketFindId
TY static
MO ajindex
LB core
XX
DE Tests for an ID in a primary bucket
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA r id const AjPStr
PD id to search for
PX
PN [4]
PA w treeblock ajulong*
PD Tree block number for id
PX
RT AjBool
RD ajTrue if found
RX
//


static AjBool btreePribucketFindId(AjPBtcache cache, ajulong pagepos,
                                   const AjPStr id, ajulong* treeblock)
{
    AjPBtpage page      = NULL;
    AjPBtpage lpage     = NULL;
    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    
    unsigned char *codeptr = NULL;
    
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  len  = 0;
    ajuint idlen = 0;
    
    /* ajDebug("In btreePribucketFindId\n"); */
    
    if(!pagepos)
	ajFatal("PribucketFindId: cannot read bucket from root page cache %S",
                cache->filename);

    page  = btreePricacheRead(cache,pagepos);
    lpage = page;
    dirtysave = lpage->dirty;
    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1291;

    buf = lpage->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_PRIBUCKET)
	ajFatal("PribucketFindId: NodeType mismatch. "
                "Not primary bucket (%u) cache %S",
		nodetype, cache->filename);
    
    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->pnperbucket)
	ajFatal("PribucketFindId: Bucket too full  page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->pnperbucket,
                cache->filename);
    

    GBT_BUCKOVERFLOW(buf,&overflow);
    
    kptr  = PBT_BUCKKEYLEN(buf);
    codeptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//	    /# ajDebug("PribucketFindId: Overflow\n"); #/
//	    page  = btreePricacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//	    if(nodetype != BT_PRIBUCKET)
//		ajFatal("PribucketFindId: NodeType mismatch. "
//                        "Not primary bucket (%u) cache %S",
//                        nodetype, cache->filename);
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    codeptr = PBT_BUCKKEYLEN(buf);
//	}
*/

	/* Fill ID objects */
	if(ajStrMatchC(id, (const char *)codeptr))
        {
            codeptr += (idlen + 1);
            BT_GETAJULONG(codeptr,treeblock);
            lpage->dirty = dirtysave;
            return ajTrue;
        }
        
	codeptr += (idlen + 1);
	codeptr += sizeof(ajulong);

	kptr += sizeof(ajuint);
    }

    lpage->dirty = dirtysave;
    
    return ajFalse;
}
ID btreeKeySplitleaf
TY static
MO ajindex
LB core
XX
DE Split a keyword leaf and propagate up if necessary
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u spage AjPBtpage
PD page
PX
RT AjPBtpage
RD pointer to a parent page
RX
//


static AjPBtpage btreeKeySplitleaf(AjPBtcache cache, AjPBtpage spage)
{
    ajuint nkeys     = 0;
    ajuint order     = 0;
    ajuint totalkeys = 0;
    ajuint keylimit  = 0;
    ajuint nodetype  = 0;

    ajuint rootnodetype = 0;
    
    ajuint i;
    ajuint j;
    
    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage page  = NULL;
    
    AjPStr mediankey  = NULL;
    ajulong mediangtr  = 0L;
    ajulong medianless = 0L;
    

    AjPBtPri bid       = NULL;
    AjPBtPri cid       = NULL;
    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;

    AjPList idlist = NULL;

    AjPPribucket cbucket  = NULL;
    
    ajulong *parray = NULL;
    AjPStr *newkarray = NULL;
    ajulong *newparray = NULL;
    AjPBtMem arrays = NULL;
    AjPBtMem newarrays = NULL;
    
    ajuint lno    = 0;
    ajuint rno    = 0;

    ajuint lbucketlimit   = 0;
    ajuint rbucketlimit   = 0;
    ajuint lmaxnperbucket = 0;
    ajuint rmaxnperbucket = 0;
    ajuint count         = 0;
    
    ajulong lblockno = 0L;
    ajulong rblockno = 0L;
    ajulong prev     = 0L;
    ajulong overflow = 0L;
    ajulong prevsave = 0L;
    
    ajulong zero = 0L;
    ajulong join = 0L;
    
    ajulong lv = 0L;
    ajuint  v  = 0;

    ajuint iold = 0;
    
#if AJINDEX_DEBUG
    ajDebug("btreeKeySplitleaf %p\n", spage->pagepos);
#endif
    ++statCallKeySplitleaf;

    /* ajDebug("In btreeKeySplitleaf\n"); */

    order = cache->porder;

    mediankey = ajStrNew();
    arrays = btreeAllocPriArray(cache);
    parray = arrays->parray;

    newarrays = btreeAllocPriArray(cache);
    newkarray = newarrays->karray;
    newparray = newarrays->parray;

    buf = spage->buf;
    lbuf = buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&rootnodetype);

    if(rootnodetype == BT_ROOT)
    {
	lblockno = cache->totsize;
	lpage = btreePricacheNodenew(cache);
	lbuf = lpage->buf;
	lv = prev;
	SBT_PREV(lbuf,lv);
    }
    else
    {
	lblockno = spage->pagepos;
	lpage = spage;
    }

    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1301;

    rblockno = cache->totsize;
    rpage = btreePricacheNodenew(cache);
    rbuf = rpage->buf;
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1302;

    if(rootnodetype == BT_ROOT)
    {
	lv = zero;
	SBT_RIGHT(rbuf,lv);
	lv = zero;
	SBT_LEFT(lbuf,lv);
    }
    else
    {
	GBT_RIGHT(lbuf,&join);
	lv = join;
	SBT_RIGHT(rbuf,lv);
    }

    lv = lblockno;
    SBT_LEFT(rbuf,lv);
    lv = rblockno;
    SBT_RIGHT(lbuf,lv);


    btreeGetPointers(cache,buf,&parray);


    keylimit = nkeys+1;
    idlist = ajListNew();

    for(i=0;ipnperbucket,
                         &lbucketlimit,&lmaxnperbucket,&lno,
                         &rbucketlimit,&rmaxnperbucket,&rno);

    cbucket = btreePribucketNew(cache->pnperbucket);

    count = 0;
    iold = 0;

    for(i=0;iNentries = 0;

	for(j=0;jcodes[j];
	    ajStrAssignS(&cid->keyword,bid->keyword);
	    cid->treeblock = bid->treeblock;

	    cbucket->keylen[j] = BT_BUCKPRILEN(bid->keyword);
	    ++count;
	    ++cbucket->Nentries;
	    ajBtreePriDel(&bid);
	}

	ajListPeek(idlist,(void **)&bid);
	
	ajStrAssignS(&newkarray[i],bid->keyword);

	if((iold < order) && parray[iold])
            newparray[i] = parray[iold++];
        else
	    newparray[i] = cache->totsize;
	btreeWritePribucket(cache,cbucket,newparray[i]);
    }

    cbucket->Nentries = 0;

    j = 0;

    while(count != lno)
    {
	ajListPop(idlist,(void **)&bid);
	cid = cbucket->codes[j];
	++j;
	++count;

	ajStrAssignS(&cid->keyword,bid->keyword);
	cid->treeblock = bid->treeblock;
	++cbucket->Nentries;
	ajBtreePriDel(&bid);
    }

    if((iold < order) && parray[iold])
        newparray[i] = parray[iold++];
    else
        newparray[i] = cache->totsize;
    btreeWritePribucket(cache,cbucket,newparray[i]);

    nkeys = lbucketlimit;
    nodetype = BT_LEAF;
    v = nodetype;
    SBT_NODETYPE(lbuf,v);

    GBT_PREV(lbuf,&prevsave);
    lpage->dirty = BT_DIRTY;

    btreeWriteNode(cache,lpage,newkarray,newparray,nkeys);

    ajListPeek(idlist,(void **)&bid);
    ajStrAssignS(&mediankey,bid->keyword);

    for(i=0;iNentries = 0;

	for(j=0;jcodes[j];
	    ajStrAssignS(&cid->keyword,bid->keyword);
	    cid->treeblock = bid->treeblock;

	    cbucket->keylen[j] = BT_BUCKPRILEN(bid->keyword);
	    ++cbucket->Nentries;
	    ajBtreePriDel(&bid);
	}

	ajListPeek(idlist,(void **)&bid);
	ajStrAssignS(&newkarray[i],bid->keyword);

	if((iold < order) && parray[iold])
            newparray[i] = parray[iold++];
        else
	    newparray[i] = cache->totsize;
	btreeWritePribucket(cache,cbucket,newparray[i]);
    }

    cbucket->Nentries = 0;

    j = 0;

    while(ajListPop(idlist,(void**)&bid))
    {
	cid = cbucket->codes[j];
	++j;

	ajStrAssignS(&cid->keyword,bid->keyword);
	cid->treeblock = bid->treeblock;
	++cbucket->Nentries;
	ajBtreePriDel(&bid);
    }
    
    if((iold < order) && parray[iold])
        newparray[i] = parray[iold++];
    else
        newparray[i] = cache->totsize;
    btreeWritePribucket(cache,cbucket,newparray[i]);

    nkeys = rbucketlimit;

    v = nkeys;
    nodetype = BT_LEAF;
    v = nodetype;
    SBT_NODETYPE(rbuf,v);

    lv = prevsave;
    SBT_PREV(rbuf,lv);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);
    
    btreeWriteNode(cache,rpage,newkarray,newparray,nkeys);
    rpage->dirty = BT_DIRTY;

    btreePribucketDel(&cbucket);
    ajListFree(&idlist);



    medianless = lblockno;
    mediangtr  = rblockno;

    btreeDeallocPriArray(cache,arrays);
    btreeDeallocPriArray(cache,newarrays);

    if(rootnodetype == BT_ROOT)
    {
	btreeWriteNodeSingle(cache,spage,mediankey,lblockno,rblockno);	
	spage->dirty = BT_LOCK;
        spage->lockfor = 1303;
	ajStrDel(&mediankey);
	++cache->plevel;

	return spage;
    }

    page = btreePricacheRead(cache,prevsave);
    btreePriInsertKey(cache,page,mediankey,medianless,mediangtr);
    ajStrDel(&mediankey);

    page = btreePricacheRead(cache,prevsave);

    return page;
}
ID btreeSecbucketFindId
TY static
MO ajindex
LB core
XX
DE Tests for an ID in a secondary bucket
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA r id const AjPStr
PD id to search for
PX
RT AjBool
RD ajTrue if found
RX
//


static AjBool btreeSecbucketFindId(AjPBtcache cache, ajulong pagepos,
                                   const AjPStr id)
{
    AjPBtpage page  = NULL;
    AjPBtpage lpage = NULL;

    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    
    unsigned char *codeptr = NULL;
    
    ajuint  nodetype = 0;
    ajuint  nentries = 0;
    ajulong overflow = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  len  = 0;
    
    /* ajDebug("In btreeSecbucketFindId\n"); */

    /* Put in test here for secondary root page read
       instead of !pagepos - done */

/*
  if(pagepos == cache->secrootblock)
	ajFatal("SecbucketFindId: cannot read bucket from root page cache %S",
                cache->filename);
*/

    page  = btreeSeccacheRead(cache,pagepos);
    lpage = page;
    dirtysave = lpage->dirty;
    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1311;

    buf = lpage->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_SECBUCKET)
	ajFatal("SecbucketFindId: NodeType mismatch. "
                "Not secondary bucket (%u) page %Lu cache %S",
		nodetype, pagepos, cache->filename);

    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->snperbucket)
	ajFatal("SecbucketFindId: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->snperbucket,
                cache->filename);
    

    GBT_BUCKOVERFLOW(buf,&overflow);

    kptr  = PBT_BUCKKEYLEN(buf);
    codeptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//	    /# ajDebug("SecbucketFindId: Overflow\n"); #/
//	    page  = btreeSeccacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//	    if(nodetype != BT_SECBUCKET)
//		ajFatal("SecbucketFindId: NodeType mismatch. "
//                        "Not secondary bucket (%u) page: %Lu cache %S",
//                        nodetype, pagepos, cache->filename);
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    codeptr = PBT_BUCKKEYLEN(buf);
//	}
*/

	/* Fill ID objects */
        if(ajStrMatchC(id, (const char *)codeptr))
        {
            lpage->dirty = dirtysave;
            return ajTrue;
        }
        
	codeptr += len;

	kptr += sizeof(ajuint);
    }

    lpage->dirty = dirtysave;
    
    return ajFalse;
}
ID btreeSecbucketIdcount
TY static
MO ajindex
LB core
XX
DE Counts secondary IDs in a bucket
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT ajulong
RD Number of IDs
RX
//


static ajulong btreeSecbucketIdcount(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage page  = NULL;

    unsigned char *buf  = NULL;
    
    ajuint  nodetype = 0;
    ajuint  nentries = 0;
    
    ajulong lcount = 0L;
    
/*
    if(pagepos == cache->secrootblock)
	ajFatal("SecbucketIdcount: cannot read bucket from root page cache %S",
               cache->filename);
*/

    page  = btreeSeccacheRead(cache,pagepos);

    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_SECBUCKET)
	ajFatal("SecbucketIdcount: NodeType mismatch. "
                "Not secondary bucket (%u) cache %S",
		nodetype, cache->filename);
    
    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->snperbucket)
	ajFatal("SecbucketIdcount: Bucket too full page: %Lu "
                "entries: %u max: %u full cache %S",
                pagepos, nentries, cache->snperbucket, cache->filename);
    
    lcount = nentries;

    return lcount;
}
ID btreeSecbucketIdlist
TY static
MO ajindex
LB core
XX
DE Copies all secondary IDs into a list
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA u idlist AjPList
PD list to hold secondary ID strings
PX
RT ajulong
RD Overflow
RX
//


static ajulong btreeSecbucketIdlist(AjPBtcache cache, ajulong pagepos,
                                    AjPList idlist)
{
    AjPStr tmpstr = NULL;
    
    AjPBtpage page  = NULL;
    AjPBtpage lpage = NULL;

    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    
    unsigned char *codeptr = NULL;
    
    ajuint  nodetype = 0;
    ajuint  nentries = 0;
    ajulong overflow = 0L;
    ajulong pageoverflow = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  len  = 0;
    
    /* Put in test here for secondary root page read
       instead of !pagepos - done*/

/*
    if(pagepos == cache->secrootblock)
	ajFatal("SecbucketIdlist: cannot read bucket from root page cache %S",
               cache->filename);
*/

    page  = btreeSeccacheRead(cache,pagepos);
    lpage = page;
    dirtysave = lpage->dirty;
    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1321;

    buf = lpage->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_SECBUCKET)
	ajFatal("SecbucketIdlist: NodeType mismatch. "
                "Not secondary bucket (%u) cache %S",
		nodetype, cache->filename);
    
    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->snperbucket)
	ajFatal("SecbucketIdlist: Bucket too full page: %Lu "
                "entries: %u max: %u full cache %S",
                pagepos, nentries, cache->snperbucket, cache->filename);
    

    GBT_BUCKOVERFLOW(buf,&overflow);
    pageoverflow = overflow;
    
    kptr  = PBT_BUCKKEYLEN(buf);
    codeptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//	    page  = btreeSeccacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//	    if(nodetype != BT_SECBUCKET)
//		ajFatal("SecbucketIdlist: NodeType mismatch. "
//                        "Not secondary bucket (%u) cache %S",
//                        nodetype, cache->filename);
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    codeptr = PBT_BUCKKEYLEN(buf);
//	}
*/

	/* Fill ID objects */
        if(statSaveSecIdNext)
            tmpstr = statSaveSecId[--statSaveSecIdNext];
	ajStrAssignLenC(&tmpstr,(const char *)codeptr, len-1);
        /*ajStrFmtLower(&tmpstr);*/ /* already lowercase */
        ajListPushAppend(idlist, tmpstr);
        tmpstr = NULL;
	codeptr += len;

	kptr += sizeof(ajuint);
    }

    lpage->dirty = dirtysave;
    
    return pageoverflow;
}
ID btreeReadSecbucket
TY static
MO ajindex
LB core
XX
DE Constructor for keyword index secondary bucket given a disc page number
DE Creates one empty key slot for possible addition
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT AjPSecbucket
RD bucket
RX
//


static AjPSecbucket btreeReadSecbucket(AjPBtcache cache, ajulong pagepos)
{
    AjPSecbucket bucket = NULL;

    AjPBtpage page  = NULL;
    AjPBtpage lpage = NULL;

    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    
    unsigned char *codeptr = NULL;
    
    ajuint  nodetype = 0;
    ajuint nentries = 0;
    ajulong overflow = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  len  = 0;
    
    /* ajDebug("In btreeReadSecbucket\n"); */

    if(pagepos == cache->secrootblock)
	ajFatal("btreeReadSecbucket: cannot read bucket from "
                "root page cache %S",
                cache->filename);

    page  = btreeSeccacheRead(cache,pagepos);
    lpage = page;
    dirtysave = lpage->dirty;
    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1331;

    buf = lpage->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);

    if(nodetype != BT_SECBUCKET)
	ajFatal("SecReadBucket: NodeType mismatch. "
                "Not secondary bucket (%u) cache %S",
		nodetype, cache->filename);
    
    GBT_BUCKNENTRIES(buf,&nentries);

    if(nentries > cache->snperbucket)
	ajFatal("SecReadBucket: Bucket too full page: %Lu "
                "entries: %u max: %u secondary: %B cache %S\n",
                pagepos, nentries, cache->snperbucket,
                cache->secondary, cache->filename);
    

    GBT_BUCKOVERFLOW(buf,&overflow);

    bucket = btreeSecbucketNew(cache->snperbucket,cache->idlimit);
    bucket->Nentries = nentries;

    kptr  = PBT_BUCKKEYLEN(buf);
    codeptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//#if AJINDEX_DEBUG
//	    ajDebug("SecReadBucket: Overflow\n");
//#endif
//	    page  = btreeSeccacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//
//	    if(nodetype != BT_SECBUCKET)
//		ajFatal("SecReadBucket: NodeType mismatch. Not secondary "
//			"bucket (%u)",nodetype);
//
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    codeptr = PBT_BUCKKEYLEN(buf);
//	}
*/

	/* Fill ID objects */
	ajStrAssignLenC(&bucket->SecIds[i],(const char *)codeptr,len-1);
	codeptr += len;

	kptr += sizeof(ajuint);
    }

    lpage->dirty = dirtysave;
    
    return bucket;
}
ID btreeWriteSecbucket
TY static
MO ajindex
LB core
XX
DE Write primary keyword index bucket object to the cache given a disc page
DE number
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r bucket const AjPSecbucket
PD bucket
PX
PN [3]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreeWriteSecbucket(AjPBtcache cache, const AjPSecbucket bucket,
				ajulong pagepos)
{
    AjPBtpage page  = NULL;
    AjPBtpage lpage = NULL;

    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;

    ajuint  v   = 0;
    ajuint i   = 0;
    ajuint len = 0;
    ajulong lv  = 0L;

    AjPStr sec = NULL;
    ajuint nentries = 0;
    ajulong overflow = 0L;
    unsigned char *keyptr = NULL;
    unsigned char *lptr   = NULL;

/*  ajulong   pno = 0L;*/

    /* ajDebug("In btreeWriteSecbucket\n"); */

    if(pagepos == cache->totsize)	/* Create a new page */
    {
	/* pno = pagepos; */
	page = btreeSeccacheBucketnew(cache);
	buf = page->buf;
	overflow = 0L;
    }
    else
    {
	page = btreeSeccacheRead(cache,pagepos);
	buf = page->buf;
	GBT_BUCKOVERFLOW(buf,&overflow);
    }

    v = BT_SECBUCKET;
    SBT_BUCKNODETYPE(buf,v);

    lbuf = buf;
    page->dirty = BT_LOCK;
    page->lockfor = 1341;
    lpage = page;

    nentries = bucket->Nentries;
    v = nentries;
    SBT_BUCKNENTRIES(buf,v);

    /* Write out key lengths */
    keyptr = PBT_BUCKKEYLEN(lbuf);

    for(i=0;i cache->pagesize)
//	    ajFatal("btreeWriteSecbucket: Bucket cannot hold more than %u keys",
//		    i-1);
*/

	sec = bucket->SecIds[i];
	/* Need to alter this if bucket primary keyword structure changes */
	len = BT_BUCKSECLEN(sec);
        v = len;
	BT_SETAJUINT(keyptr,v);
	keyptr += sizeof(ajuint);
    }


    /* Write out IDs using overflow if necessary */
    lptr = keyptr;

    for(i=0;iSecIds[i];
	len = BT_BUCKSECLEN(sec);

/*
//	if((lptr-buf+1)+len > cache->pagesize) /# overflow #/
//	{
//#if AJINDEX_DEBUG
//    	    ajDebug("btreeWriteSecbucket: Overflow\n");
//#endif
//
//	    if(!overflow)		/# No overflow buckets yet #/
//	    {
//		pno = cache->totsize;
//                lv = pno;
//		SBT_BUCKOVERFLOW(buf,lv);
//		page = btreeSeccacheBucketnew(cache);
//		buf = page->buf;
//		v = BT_SECBUCKET;
//		SBT_BUCKNODETYPE(buf,v);
//	    }
//	    else
//	    {
//		page = btreeSeccacheRead(cache,overflow);
//		buf  = page->buf;
//		GBT_BUCKOVERFLOW(buf,&overflow);
//	    }
//
//	    page->dirty = BT_DIRTY;
//
//	    lptr = PBT_BUCKKEYLEN(buf);	    
//	}
*/	

	sprintf((char *)lptr,"%s",MAJSTRGETPTR(sec));
	lptr += len;
    }

    lv = 0L;
    SBT_BUCKOVERFLOW(buf,lv);

    lpage->dirty = BT_DIRTY;

    return;
}
ID btreeWriteSecbucketEmpty
TY static
MO ajindex
LB core
XX
DE Write empty secondary keyword index bucket object to the cache given a
DE disc page number
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreeWriteSecbucketEmpty(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage page  = NULL;
    AjPBtpage lpage = NULL;

    unsigned char *buf  = NULL;

    ajuint  v   = 0;
    ajulong lv  = 0L;

    ajulong overflow = 0L;

    if(pagepos == cache->totsize)	/* Create a new page */
    {
	page = btreeSeccacheBucketnew(cache);
	buf = page->buf;
	overflow = 0L;
	lv = overflow;
	SBT_BUCKOVERFLOW(buf,lv);
    }
    else
    {
	page = btreeSeccacheRead(cache,pagepos);
	buf = page->buf;
	GBT_BUCKOVERFLOW(buf,&overflow);
    }

    v = BT_SECBUCKET;
    SBT_BUCKNODETYPE(buf,v);

    page->dirty = BT_LOCK;      /* cleared at end */
    page->lockfor = 1351;
    lpage = page;

    v = 0;
    SBT_BUCKNENTRIES(buf,v);

    lv = 0L;
    SBT_BUCKOVERFLOW(buf,lv);

    lpage->dirty = BT_DIRTY;    /* clear the lock */

    return;
}
ID ajBtreeSeccacheNewC
TY public
MO ajindex
LB core
XX
DE Open a b+tree index file and initialise a cache object for keyword index
XX
PN [1]
PA r filetxt const char*
PD name of file
PX
PN [2]
PA r exttxt const char*
PD extension of file
PX
PN [3]
PA r idirtxt const char*
PD index file directory
PX
PN [4]
PA r mode const char*
PD opening mode
PX
PN [5]
PA r compressed AjBool
PD Compressed index flag
PX
PN [6]
PA r kwlimit ajuint
PD Max key size
PX
PN [7]
PA r idlimit ajuint
PD Max secondary id size
PX
PN [8]
PA r pripagesize ajuint
PD pagesize
PX
PN [9]
PA r secpagesize ajuint
PD secondary pagesize
PX
PN [10]
PA r pricachesize ajuint
PD size of cache
PX
PN [11]
PA r seccachesize ajuint
PD size of secondary cache
PX
PN [12]
PA r pripagecount ajulong
PD page count
PX
PN [13]
PA r secpagecount ajulong
PD page count
PX
PN [14]
PA r order ajuint
PD Tree order
PX
PN [15]
PA r fill ajuint
PD Number of entries per bucket
PX
PN [16]
PA r level ajuint
PD level of tree
PX
PN [17]
PA r sorder ajuint
PD order of secondary tree
PX
PN [18]
PA r sfill ajuint
PD Number of entries per secondary bucket
PX
PN [19]
PA r count ajulong
PD Number of entries in the index
PX
PN [20]
PA r countall ajulong
PD Number of total entries in the index
PX
RT AjPBtcache
RD initialised disc block cache structure
RX
//


AjPBtcache ajBtreeSeccacheNewC(const char *filetxt, const char *exttxt,
			       const char *idirtxt, const char *mode,
                               AjBool compressed,
                               ajuint kwlimit, ajuint idlimit,
                               ajuint pripagesize, ajuint secpagesize,
                               ajuint pricachesize, ajuint seccachesize,
                               ajulong pripagecount, ajulong secpagecount,
                               ajuint order, ajuint fill, ajuint level,
			       ajuint sorder, ajuint sfill,
			       ajulong count, ajulong countall)
{
    AjPBtcache cache = NULL;
    AjPBtpage  page = NULL;
#if defined (usestat64)
    struct stat64 buf;
#else
    struct stat buf;
#endif
    ajulong filelen = 0L;
    AjBool douncompress = ajFalse;
    AjBool writemode = ajFalse;
    AjBool okcache = ajTrue;

    AJNEW0(cache);

    cache->prilistLength = 0;
    cache->seclistLength = 0;

    cache->plru   = NULL;
    cache->pmru   = NULL;
    cache->slru   = NULL;
    cache->smru   = NULL;
    
    cache->replace    = ajStrNew();
    cache->numreplace = 0L;
    
    if(pripagesize>0)
	cache->pripagesize = pripagesize;
    else
	cache->pripagesize = BT_PAGESIZE;

    if(secpagesize>0)
	cache->secpagesize = secpagesize;
    else
	cache->secpagesize = cache->pripagesize;

    cache->plevel       = level;
    cache->porder       = order;
    cache->pnperbucket  = fill;

    if(pricachesize > 0)
        cache->pricachesize = pricachesize;
    else
        cache->pricachesize = BT_CACHESIZE;

    cache->pripagecount = pripagecount;
    cache->secpagecount = secpagecount;

    cache->slevel = 0;
    cache->sorder = sorder;
    cache->snperbucket = sfill;
    if(seccachesize > 0)
        cache->seccachesize = seccachesize;
    else
        cache->seccachesize = cache->pricachesize;

    cache->countunique = count;
    cache->countall = countall;
    cache->keylimit = kwlimit;
    cache->idlimit = idlimit;
    cache->compressed = compressed;

    cache->bmem = NULL;
    cache->tmem = NULL;

    cache->bsmem = NULL;
    cache->tsmem = NULL;

    cache->secondary = ajTrue;
    
    cache->pripagetable = ajTablelongNewConst(cache->pricachesize);
    cache->secpagetable = ajTablelongNewConst(cache->seccachesize);

    cache->filename = ajStrNew();
    if(!*idirtxt)
        ajFmtPrintS(&cache->filename,"%s.%s",filetxt,exttxt);
    else if(idirtxt[strlen(idirtxt)-1] == SLASH_CHAR)
        ajFmtPrintS(&cache->filename,"%s%s.%s",idirtxt,filetxt,exttxt);
    else
        ajFmtPrintS(&cache->filename,"%s%s%s.%s",
		    idirtxt,SLASH_STRING,filetxt,exttxt);
    
    if(cache->porder < 4)
    {
        ajErr("cache '%S' pagesize %u order %u too small, increase pagesize",
              cache->filename, cache->pripagesize, cache->porder);
        okcache = ajFalse;
    }

    if(cache->pnperbucket < 4)
    {
        ajErr("cache '%S' pagesize %u fill %u too small, increase pagesize",
              cache->filename, cache->pripagesize, cache->pnperbucket);
        okcache = ajFalse;
    }

    if(cache->sorder < 4)
    {
        ajErr("cache '%S' pagesize %u sorder %u too small, increase pagesize",
              cache->filename, cache->pripagesize, cache->sorder);
        okcache = ajFalse;
    }

    if(cache->snperbucket < 4)
    {
        ajErr("cache '%S' pagesize %u sfill %u too small, increase pagesize",
              cache->filename, cache->secpagesize, cache->snperbucket);
        okcache = ajFalse;
    }

    if(!okcache)
        return NULL;

    cache->fp = fopen(MAJSTRGETPTR(cache->filename),mode);
    if(!cache->fp)
	return NULL;

    /* Commented out pending database updating */
    if(ajCharMatchC(mode, "rb"))
    {
#if defined (usestat64)
	if(!stat64(MAJSTRGETPTR(cache->filename), &buf))
#else
        if(!stat(MAJSTRGETPTR(cache->filename), &buf))
#endif
            filelen = buf.st_size;

        cache->readonly = ajTrue;
    }
    else if(ajCharMatchC(mode, "rb+"))
    {
#if defined (usestat64)
	if(!stat64(MAJSTRGETPTR(cache->filename), &buf))
#else
        if(!stat(MAJSTRGETPTR(cache->filename), &buf))
#endif
            filelen = buf.st_size;
        if(compressed)
            douncompress = ajTrue;
    }
    else if(ajCharMatchC(mode, "wb+")) /* create */
    {
        writemode = ajTrue;
    }
    else 
    {
        ajWarn("ajBtreeSecCacheNewC unknown mode '%s'", mode);
    }
    
    cache->totsize    = filelen;
    cache->filesize  = filelen;

    if(douncompress)
        btreeCacheUncompress(cache);

    /* create or lock the root page */

    if(writemode)
        btreePrirootCreate(cache);
    else
    {
        page = btreePricacheRead(cache,0L);
        page->dirty = BT_LOCK;
    }

    return cache;
}
ID ajBtreeSeccacheNewS
TY public
MO ajindex
LB core
XX
DE Open a b+tree index file and initialise a cache object for keyword index
XX
PN [1]
PA r file const AjPStr
PD name of file
PX
PN [2]
PA r ext const AjPStr
PD extension of file
PX
PN [3]
PA r idir const AjPStr
PD index file directory
PX
PN [4]
PA r mode const char*
PD opening mode
PX
PN [5]
PA r compressed AjBool
PD Compressed index flag
PX
PN [6]
PA r kwlimit ajuint
PD Max key size
PX
PN [7]
PA r idlimit ajuint
PD Max secondary id size
PX
PN [8]
PA r pripagesize ajuint
PD Primary pagesize
PX
PN [9]
PA r secpagesize ajuint
PD Secondary pagesize
PX
PN [10]
PA r pricachesize ajuint
PD size of primary cache
PX
PN [11]
PA r seccachesize ajuint
PD size of secondary cache
PX
PN [12]
PA r pripagecount ajulong
PD Primary page count
PX
PN [13]
PA r secpagecount ajulong
PD Secondary page count
PX
PN [14]
PA r order ajuint
PD Tree order
PX
PN [15]
PA r fill ajuint
PD Number of entries per bucket
PX
PN [16]
PA r level ajuint
PD level of tree
PX
PN [17]
PA r sorder ajuint
PD order of secondary tree
PX
PN [18]
PA r sfill ajuint
PD Number of entries per secondary bucket
PX
PN [19]
PA r count ajulong
PD Number of entries in the index
PX
PN [20]
PA r countall ajulong
PD Number of total entries in the index
PX
RT AjPBtcache
RD initialised disc block cache structure
RX
//


AjPBtcache ajBtreeSeccacheNewS(const AjPStr file, const AjPStr ext,
			       const AjPStr idir, const char *mode,
                               AjBool compressed,
                               ajuint kwlimit, ajuint idlimit,
                               ajuint pripagesize, ajuint secpagesize,
                               ajuint pricachesize, ajuint seccachesize,
                               ajulong pripagecount, ajulong secpagecount,
                               ajuint order, ajuint fill, ajuint level,
			       ajuint sorder, ajuint sfill,
			       ajulong count, ajulong countall)
{
    return ajBtreeSeccacheNewC(MAJSTRGETPTR(file), MAJSTRGETPTR(ext),
                               MAJSTRGETPTR(idir), mode,
                               compressed, kwlimit, idlimit,
                               pripagesize, secpagesize,
                               pricachesize, seccachesize,
                               pripagecount, secpagecount,
                               order, fill, level,
                               sorder, sfill,
                               count, countall);
}
ID btreeSecSplitleaf
TY static
MO ajindex
LB core
XX
DE Split a leaf and propagate up if necessary
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u spage AjPBtpage
PD page
PX
RT AjPBtpage
RD pointer to a parent page
RX
//


static AjPBtpage btreeSecSplitleaf(AjPBtcache cache, AjPBtpage spage)
{
    ajuint nkeys     = 0;
    ajuint order     = 0;
    ajuint totalkeys = 0;
    ajuint keylimit  = 0;
    ajuint nodetype  = 0;

    ajuint rootnodetype = 0;
    
    ajuint i;
    ajuint j;
    
    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage page  = NULL;
    
    AjPStr mediankey  = NULL;
    ajulong mediangtr  = 0L;
    ajulong medianless = 0L;

    AjPStr bid = NULL;

    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;

    AjPList idlist = NULL;

    AjPSecbucket cbucket  = NULL;
    
    ajulong *parray = NULL;
    AjPBtMem arrays = NULL;
    AjPBtMem newarrays = NULL;
    AjPStr *newkarray = NULL;
    ajulong *newparray = NULL;
    
    ajuint lno    = 0;
    ajuint rno    = 0;

    ajuint lbucketlimit   = 0;
    ajuint rbucketlimit   = 0;
    ajuint lmaxnperbucket = 0;
    ajuint rmaxnperbucket = 0;
    ajuint count         = 0;
    
    ajulong lblockno = 0L;
    ajulong rblockno = 0L;
    ajulong overflow = 0L;
    ajulong prevsave = 0L;
    
    ajulong zero = 0L;
    ajulong join = 0L;
    
    ajulong lv = 0L;
    ajuint  v  = 0;
    ajuint newmax;
    ajuint iold=0;
    
#if AJINDEX_DEBUG
    ajDebug("btreeSecSplitleaf %Lu\n", spage->pagepos);
#endif
    ++statCallSecSplitleaf;

    if(!statSaveSecId)
    {
        statSaveSecIdMax = 2048;
        AJCNEW0(statSaveSecId, statSaveSecIdMax);
        statSaveSecIdNext = 0;
    }

    order = cache->sorder;

    mediankey = ajStrNew();

    arrays = btreeAllocSecArray(cache);
    parray = arrays->parray;
    newarrays = btreeAllocSecArray(cache);
    newkarray = newarrays->karray;
    newparray = newarrays->parray;

    buf = spage->buf;
    lbuf = buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&rootnodetype);

    if(rootnodetype == BT_SECROOT)
    {
	lblockno = cache->totsize;
	lpage = btreeSeccacheNodenew(cache);
	lbuf = lpage->buf;
	lv = cache->secrootblock;
	SBT_PREV(lbuf,lv);
    }
    else
    {
	lblockno = spage->pagepos;
	lpage = spage;
    }

    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1361;

    rblockno = cache->totsize;
    rpage = btreeSeccacheNodenew(cache);
    rbuf = rpage->buf;
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1362;

    if(rootnodetype == BT_SECROOT)
    {
	lv = zero;
	SBT_RIGHT(rbuf,lv);
	lv = zero;
	SBT_LEFT(lbuf,lv);
    }
    else
    {
	GBT_RIGHT(lbuf,&join);
	lv = join;
	SBT_RIGHT(rbuf,lv);
    }

    lv = lblockno;
    SBT_LEFT(rbuf,lv);
    lv = rblockno;
    SBT_RIGHT(lbuf,lv);


    btreeGetPointers(cache,buf,&parray);


    keylimit = nkeys+1;
    
    idlist = ajListNew();

    for(i=0;isnperbucket,
                         &lbucketlimit,&lmaxnperbucket,&lno,
                         &rbucketlimit,&rmaxnperbucket,&rno);

    cbucket = btreeSecbucketNew(cache->snperbucket,cache->idlimit);

    count = 0;
    iold=0;

    for(i=0;iNentries = 0;

	for(j=0;jSecIds[j],bid);

	    cbucket->keylen[j] = BT_BUCKSECLEN(bid);
	    ++count;
	    ++cbucket->Nentries;
            if(statSaveSecIdNext >= statSaveSecIdMax) 
            {
                newmax = statSaveSecIdMax + statSaveSecIdMax;
                AJCRESIZE0(statSaveSecId,statSaveSecIdMax,newmax);
                statSaveSecIdMax = newmax;
            }
            statSaveSecId[statSaveSecIdNext++] = bid;
            bid = NULL;
	}

	ajListPeek(idlist,(void **)&bid);
	
	ajStrAssignS(&newkarray[i],bid);

	if((iold < order) && parray[iold])
            newparray[i] = parray[iold++];
        else
	    newparray[i] = cache->totsize;
	btreeWriteSecbucket(cache,cbucket,newparray[i]);
    }

    cbucket->Nentries = 0;

    j = 0;

    while(count != lno)
    {
	ajListPop(idlist,(void **)&bid);

	ajStrAssignS(&cbucket->SecIds[j],bid);
	++j;
	++count;


	++cbucket->Nentries;
	ajStrDel(&bid);
    }

    if((iold < order) && parray[iold])
        newparray[i] = parray[iold++];
    else
        newparray[i] = cache->totsize;

    btreeWriteSecbucket(cache,cbucket,newparray[i]);

    nkeys = lbucketlimit;

    nodetype = BT_SECLEAF;
    v = nodetype;
    SBT_NODETYPE(lbuf,v);

    GBT_PREV(lbuf,&prevsave);
    lpage->dirty = BT_DIRTY;

    btreeWriteNode(cache,lpage,newkarray,newparray,nkeys);

    ajListPeek(idlist,(void **)&bid);
    ajStrAssignS(&mediankey,bid);

    for(i=0;iNentries = 0;

	for(j=0;jSecIds[j],bid);

	    cbucket->keylen[j] = BT_BUCKSECLEN(bid);
	    ++cbucket->Nentries;
	    ajStrDel(&bid);
	}

	ajListPeek(idlist,(void **)&bid);
	ajStrAssignS(&newkarray[i],bid);

	if((iold < order) && parray[iold])
            newparray[i] = parray[iold++];
        else
            newparray[i] = cache->totsize;

	btreeWriteSecbucket(cache,cbucket,newparray[i]);
    }

    cbucket->Nentries = 0;

    j = 0;

    while(ajListPop(idlist,(void**)&bid))
    {
	ajStrAssignS(&cbucket->SecIds[j],bid);
	++j;


	++cbucket->Nentries;
	ajStrDel(&bid);
    }
    
    if((iold < order) && parray[iold])
        newparray[i] = parray[iold++];
    else
        newparray[i] = cache->totsize;
    btreeWriteSecbucket(cache,cbucket,newparray[i]);

    nkeys = rbucketlimit;

    nodetype = BT_SECLEAF;
    v = nodetype;
    SBT_NODETYPE(rbuf,v);

    lv = prevsave;
    SBT_PREV(rbuf,lv);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);
    
    btreeWriteNode(cache,rpage,newkarray,newparray,nkeys);
    rpage->dirty = BT_DIRTY;

    btreeSecbucketDel(&cbucket);
    ajListFree(&idlist);



    medianless = lblockno;
    mediangtr  = rblockno;

    btreeDeallocSecArray(cache,arrays);
    btreeDeallocSecArray(cache,newarrays);

    if(rootnodetype == BT_SECROOT)
    {
	spage->dirty = BT_DIRTY;

	btreeWriteNodeSingle(cache,spage,mediankey,lblockno,rblockno);	

	++cache->slevel;
	lv = cache->slevel;
	SBT_RIGHT(buf,lv);
	spage->dirty = BT_LOCK;
        spage->lockfor = 1363;

	ajStrDel(&mediankey);

	return spage;
    }


    page = btreeSeccacheRead(cache,prevsave);
    btreeInsertKeySec(cache,page,mediankey,medianless,mediangtr);
    ajStrDel(&mediankey);

    page = btreeSeccacheRead(cache,prevsave);

    return page;
}
ID btreeKeywordIdCompare
TY static
MO ajindex
LB core
XX
DE Comparison function for ajListSort
XX
PN [1]
PA r a const void*
PD ID 1
PX
PN [2]
PA r b const void*
PD ID 2
PX
RT ajint
RD 0 = bases match
RX
//


static ajint btreeKeywordIdCompare(const void *a, const void *b)
{
    return MAJSTRCMPS((*(AjPStr const *)a),
                      (*(AjPStr const *)b));
}
ID btreeSecbucketNew
TY static
MO ajindex
LB core
XX
DE Construct a primary keyword secondary bucket object
XX
PN [1]
PA r n ajuint
PD Number of IDs
PX
PN [2]
PA r idlen ajuint
PD Maximum size of IDs
PX
RT AjPSecbucket
RD initialised disc block cache structure
RX
//


static AjPSecbucket btreeSecbucketNew(ajuint n, ajuint idlen)
{
    AjPSecbucket bucket = NULL;
    ajuint i;

    /* ajDebug("In btreeSecbucketNew\n"); */
    
    if(n)
    {
        if(statSaveSecbucketNext)
        {
            bucket = statSaveSecbucket[--statSaveSecbucketNext];
            for(i=0;iMaxentries;++i)
                MAJSTRASSIGNCLEAR(&bucket->SecIds[i]);
            if(n > bucket->Maxentries)
            {
                AJCRESIZE0(bucket->keylen,bucket->Maxentries,n);
                AJCRESIZE0(bucket->SecIds,bucket->Maxentries,n);
                for(i=bucket->Maxentries;iSecIds[i] = ajStrNewRes(idlen+1);
                bucket->Maxentries = n;
            }
            
        }
        else
        {
            AJNEW0(bucket);

            AJCNEW0(bucket->SecIds,n);
            AJCNEW0(bucket->keylen,n);
            for(i=0;iSecIds[i] = ajStrNewRes(idlen+1);
            bucket->Maxentries = n;
        }
    }
    else 
    {
        if(statSaveSecbucketEmptyNext)
            bucket = statSaveSecbucketEmpty[--statSaveSecbucketEmptyNext];
        else            
            AJNEW0(bucket);
    }
    
    bucket->NodeType = BT_SECBUCKET;
    bucket->Nentries = n;
    bucket->Overflow = 0L;
    
    return bucket;
}
ID btreeSecbucketDel
TY static
MO ajindex
LB core
XX
DE Delete a keyword primary bucket object
XX
PN [1]
PA w thys AjPSecbucket*
PD bucket
PX
RT void
RD 
RX
//


static void btreeSecbucketDel(AjPSecbucket *thys)
{
    AjPSecbucket pthis = NULL;
    ajuint newmax;
    
    /* ajDebug("In btreeSecbucketDel\n"); */

    if(!thys || !*thys)
	return;

    pthis = *thys;

    if(!statSaveSecbucket)
    {
        statSaveSecbucketMax=2048;
        statSaveSecbucketNext=0;
        AJCNEW0(statSaveSecbucket,statSaveSecbucketMax);
    }
    
    if(!statSaveSecbucketEmpty)
    {
        statSaveSecbucketEmptyMax=2048;
        statSaveSecbucketEmptyNext=0;
        AJCNEW0(statSaveSecbucketEmpty,statSaveSecbucketEmptyMax);
    }
    

    /*
    statCountSecbucketDel++;
    statCountSecbucket--;
    if(pthis->Nentries > statMaxSecbucket)
      statMaxSecbucket = pthis->Nentries;
    */

    if(pthis->Maxentries)
    {
        /*statReusedSecbucket++;
          statUsedSecbucket--;*/
        if(statSaveSecbucketNext >= statSaveSecbucketMax)
        {
            newmax = statSaveSecbucketMax + statSaveSecbucketMax;
            AJCRESIZE0(statSaveSecbucket,statSaveSecbucketMax,newmax);
            statSaveSecbucketMax = newmax;
        }
        
        statSaveSecbucket[statSaveSecbucketNext++] = pthis;
        /*if(ajListGetLength(statListSecbucket) > statMaxFreeSecbucket)
          statMaxFreeSecbucket = ajListGetLength(statListSecbucket);*/
    }
    else
    {
        if(statSaveSecbucketEmptyNext >= statSaveSecbucketEmptyMax)
        {
            newmax = statSaveSecbucketEmptyMax + statSaveSecbucketEmptyMax;
            AJCRESIZE0(statSaveSecbucketEmpty,statSaveSecbucketEmptyMax,newmax);
            statSaveSecbucketEmptyMax = newmax;
        }
        statSaveSecbucketEmpty[statSaveSecbucketEmptyNext++] = pthis;
        /*statReusedSecbucketEmpty++;
          statUsedSecbucketEmpty--;*/
        /*if(ajListGetLength(statListSecbucketEmpty) > statMaxFreeSecbucketEmpty)
          statMaxFreeSecbucketEmpty = ajListGetLength(statListSecbucketEmpty);*/
    }
    
    pthis = NULL;

    *thys = NULL;

    return;
}
ID btreeSecbucketFree
TY static
MO ajindex
LB core
XX
DE Delete a keyword primary bucket object
XX
PN [1]
PA w thys AjPSecbucket*
PD bucket
PX
RT void
RD 
RX
//


static void btreeSecbucketFree(AjPSecbucket *thys)
{
    AjPSecbucket pthis = NULL;
    ajuint n;
    ajuint i;
    
    /* ajDebug("In btreeSecbucketFree\n"); */

    if(!thys || !*thys)
	return;

    pthis = *thys;
    n = pthis->Maxentries;

    for(i=0;iSecIds[i]);
    
    AJFREE(pthis->keylen);
    AJFREE(pthis->SecIds);
    
    AJFREE(pthis);

    *thys = NULL;

    return;
}
ID btreeKeyidInsert
TY static
MO ajindex
LB core
XX
DE Insert an ID into the secondary keyword tree
DE
DE Depends on secrootblock being set before the function is called
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r id const AjPStr
PD Id
PX
RT AjBool
RD True if ID was inserted
RD False if ID already exists
RX
//


static AjBool btreeKeyidInsert(AjPBtcache cache, const AjPStr id)
{
    AjPBtpage spage  = NULL;

    ajulong lblockno = 0L;
    ajulong rblockno = 0L;
    ajulong blockno  = 0L;
    ajulong shift    = 0L;

    ajuint nkeys = 0;

    ajuint nodetype = 0;
    
    unsigned char *buf = NULL;
 
    ajuint n;

    AjBool exists  = ajFalse;
    
    /* ajDebug("In btreeKeyidInsert\n"); */


    if(!MAJSTRGETLEN(id))
        return ajFalse;

    /* Only insert an ID if it doesn't exist */
    exists = btreeKeyidExists(cache,id);

    if(exists)
	return ajFalse;
    
    spage = btreeKeyidFind(cache,id);
    buf = spage->buf;

    GBT_NODETYPE(buf,&nodetype);
    if(nodetype == BT_SECBUCKET)
    {
        blockno = spage->pagepos;
        n = btreeNumInSecbucket(cache,blockno);
        if(n < cache->snperbucket)
        {
            btreeSecbucketAdd(cache,blockno,id);
            return ajTrue;
        }

        /*
        ** make a list of the IDs in the bucket
        ** make a new root
        ** split the IDs into the two buckets
        */

        btreeKeyidMakeroot(cache, spage);
    }

    GBT_NKEYS(buf,&nkeys);
    if(!nkeys)
    {
        lblockno = cache->totsize;
        btreeWriteSecbucketEmpty(cache,lblockno);

        rblockno = cache->totsize;
        btreeWriteSecbucketEmpty(cache,rblockno);	

        btreeWriteNodeSingle(cache,spage,id,lblockno,rblockno);

        btreeSecbucketAdd(cache,rblockno,id);

        return ajTrue;
    }
    
    blockno = btreeGetBlockS(cache,buf,id);
    
    if(nodetype != BT_SECROOT)
        if((shift = btreeKeyidInsertShift(cache,&spage,id)))
            blockno = shift;

    buf = spage->buf;

    n = btreeNumInSecbucket(cache,blockno);

    if(n == cache->snperbucket)
    {
	if(btreeSecbucketsReorder(cache,spage))
	{
            blockno = btreeGetBlockS(cache,buf,id);
	}
	else
	{
	    btreeSecSplitleaf(cache,spage);
	    
	    spage = btreeKeyidFind(cache,id);
	    buf = spage->buf;

            blockno = btreeGetBlockS(cache,buf,id);
	}
    }


    btreeSecbucketAdd(cache,blockno,id);

    return ajTrue;
}
ID btreeKeyidExists
TY static
MO ajindex
LB core
XX
DE See whether ID already exists in the tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key
PX
RT AjBool
RD true if ID already added
RX
//


static AjBool btreeKeyidExists(AjPBtcache cache, const AjPStr key)
{
    AjPBtpage page      = NULL;
    unsigned char *buf = NULL;

    ajuint nkeys    = 0;
    ajuint nodetype;

    ajulong blockno = 0L;
    AjBool found   = ajFalse;

    /* ajDebug("In btreeKeyidExists %u\n",cache->count); */

    if(!cache->countunique)
	return ajFalse;
    
    page = btreeKeyidFind(cache,key);
    buf = page->buf;
    GBT_NODETYPE(buf, &nodetype);

    if(nodetype == BT_SECBUCKET)
    {
        blockno = page->pagepos;
    }
    else
    {
        GBT_NKEYS(buf,&nkeys);

        if(!nkeys)
            return ajFalse;
    
        blockno = btreeGetBlockS(cache,buf,key);
    }

    found = btreeSecbucketFindId(cache, blockno, key);

    return found;
}
ID btreeKeyidFind
TY public
MO ajindex
LB core
XX
DE Find the node that should contain a new key for insertion
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key to search for
PX
RT AjPBtpage
RD leaf node where item should be inserted
RX
//


AjPBtpage btreeKeyidFind(AjPBtcache cache, const AjPStr key)
{
    AjPBtpage root = NULL;
    AjPBtpage ret  = NULL;
    ajuint nodetype;

    /* ajDebug("In btreeKeyidFind\n"); */

    /* The root node should always be in the cache (BT_LOCKed) */
    root = btreeSeccacheLocate(cache,cache->secrootblock);

    /* ajDebug("cache->slevel = %u root=%u\n",cache->slevel,(ajuint)root); */
    
    GBT_NODETYPE(root->buf, &nodetype);

    if(nodetype == BT_SECBUCKET)
        return root;

    if(!cache->slevel)
	return root;
    
    ret = btreeKeyidFindINode(cache,root,key);

    return ret;
}
ID btreeNumInSecbucket
TY static
MO ajindex
LB core
XX
DE Return number of entries in a secondary id bucket
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT ajuint
RD Number of entries in bucket
RX
//


static ajuint btreeNumInSecbucket(AjPBtcache cache, ajulong pagepos)
{
    unsigned char *buf = NULL;

    AjPBtpage page = NULL;
    ajuint  nodetype = 0;
    ajuint nentries = 0;
    
    /* ajDebug("In btreeNumInPribucket\n"); */

/*        
    if(pagepos == cache->secrootblock)
	ajFatal("NumInSecbucket: Attempt to read bucket from root page\n");
*/

    page  = btreeSeccacheRead(cache,pagepos);

    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);

    if(nodetype != BT_SECBUCKET)
	ajFatal("SecReadBucket: NodeType mismatch. Not secondary bucket (%u)",
		nodetype);
    
    GBT_BUCKNENTRIES(buf,&nentries);

    return nentries;
}
ID btreeSecbucketAdd
TY static
MO ajindex
LB core
XX
DE Add a keyword ID to a secondary bucket
DE Only called if there is room in the bucket
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number of bucket
PX
PN [3]
PA r id const AjPStr
PD ID
PX
RT void
RD 
RX
//


static void btreeSecbucketAdd(AjPBtcache cache, ajulong pagepos,
                              const AjPStr id)
{
    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    unsigned char *src  = NULL;
    unsigned char *dest = NULL;

/*    unsigned char *lastptr = NULL;*/
    unsigned char *endptr  = NULL;
    
    ajuint nentries = 0;
    ajuint nodetype = 0;
    ajuint idlen    = 0;
    
    ajuint sum = 0;
    ajuint len = 0;
    ajuint i;

    ajuint v;
    
    AjPBtpage page = NULL;
    static ajuint calls = 0;
/*    static ajuint overflowcalls=0;*/
   
    calls++;
    page = btreeSeccacheRead(cache,pagepos);
    buf  = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_SECBUCKET)
        ajFatal("Wrong nodetype in SecbucketAdd cache %S",
                cache->filename);

    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries >= cache->snperbucket)
        ajFatal("Bucket too full in SecbucketAdd page %Lu %u %u cache %S",
                pagepos, nentries, cache->snperbucket, cache->filename);    

    kptr = PBT_BUCKKEYLEN(buf);
    src  = kptr + (nentries * sizeof(ajuint));

    sum = 0;
    for(i=0;i= cache->pagesize)
//    {
//        overflowcalls++;
//        ajWarn("\nOverflow in SecbucketAdd nentries:%u fails %u/%u '%S' "
//               "cache %S",
//               nentries, overflowcalls,calls, id, cache->filename);
//        btreeSecbucketAddFull(cache,pagepos,id);
//        return;
//    }
*/    

    dest = src + sizeof(ajuint);
    memmove((void *)dest, (void *)src, sum);

    v = idlen+1;
    BT_SETAJUINT(src,v);

    endptr += sizeof(ajuint);
    strcpy((char *)endptr,MAJSTRGETPTR(id));

    v = nentries + 1;
    SBT_BUCKNENTRIES(buf,v);

    page->dirty = BT_DIRTY;

    return;
}
ID btreeKeyidMakeroot
TY static
MO ajindex
LB core
XX
DE Re-write root secondary id buckets as a root node with two buckets
DE Must only be called if the buckets is full
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u bucket AjPBtpage
PD secbucket page
PX
RT AjBool
RD true if reorder was successful i.e. leaf not full
RX
//


static AjBool btreeKeyidMakeroot(AjPBtcache cache, AjPBtpage bucket)
{
    AjPList idlist;
    ajulong pagepos;
    ajulong lblockno;
    ajulong rblockno;
    AjPBtpage page;
    AjPSecbucket cbucket = NULL;
    AjPStr bid      = NULL;
    ajuint count;
    ajuint maxleft;
    ajuint newmax;

    pagepos = bucket->pagepos;

    if(!statSaveSecId)
    {
        statSaveSecIdMax = 2048;
        AJCNEW0(statSaveSecId, statSaveSecIdMax);
        statSaveSecIdNext = 0;
    }

    /* save the ids in the bucket ... a simple list of strings */

    idlist = ajListNew();
    btreeSecbucketIdlist(cache, bucket->pagepos, idlist);
    ajListSort(idlist, &btreeKeywordIdCompare);

    /* create a pair of buckets linked to the root */

    lblockno = cache->totsize;
    btreeWriteSecbucketEmpty(cache,lblockno);

    rblockno = cache->totsize;
    btreeWriteSecbucketEmpty(cache,rblockno);	
  
    /* save half the ids to the left, half to the right */

    cbucket = btreeSecbucketNew(cache->snperbucket,cache->idlimit);
    cbucket->Overflow = 0L;
    cbucket->Nentries = 0;
    count = 0;
    maxleft = (ajuint) ajListGetLength(idlist)/2;

    while(count < maxleft)
    {
	    ajListPop(idlist,(void **)&bid);
	    ajStrAssignS(&cbucket->SecIds[count],bid);
	    cbucket->keylen[count] = BT_BUCKSECLEN(bid);
	    ++cbucket->Nentries;
	    ++count;

            if(statSaveSecIdNext >= statSaveSecIdMax) 
            {
                newmax = statSaveSecIdMax + statSaveSecIdMax;
                AJCRESIZE0(statSaveSecId,statSaveSecIdMax,newmax);
                statSaveSecIdMax = newmax;
            }
            statSaveSecId[statSaveSecIdNext++] = bid;
            bid = NULL;        
    }

    btreeWriteSecbucket(cache,cbucket,lblockno);

    /* make the bucket into the new secondary root */

    cache->secrootblock = pagepos;
    btreeSecrootCreate(cache, pagepos);

    page = btreeSeccacheWrite(cache,pagepos);
    page->dirty = BT_LOCK;
    page->lockfor = 1251;

    if(btreeDoRootSync)
        btreeCacheRootSync(cache,pagepos);

    cache->slevel = 0;

    ajListPeek(idlist,(void **)&bid); /* mid-range key to save in root node */
    btreeWriteNodeSingle(cache,page,bid,lblockno,rblockno);

    cbucket->Overflow = 0L;
    cbucket->Nentries = 0;

    count = 0;

    while(ajListGetLength(idlist))
    {
	    ajListPop(idlist,(void **)&bid);
	    ajStrAssignS(&cbucket->SecIds[count],bid);
	    cbucket->keylen[count] = BT_BUCKSECLEN(bid);
	    ++cbucket->Nentries;
	    ++count;

            if(statSaveSecIdNext >= statSaveSecIdMax) 
            {
                newmax = statSaveSecIdMax + statSaveSecIdMax;
                AJCRESIZE0(statSaveSecId,statSaveSecIdMax,newmax);
                statSaveSecIdMax = newmax;
            }
            statSaveSecId[statSaveSecIdNext++] = bid;
            bid = NULL;        
    }

    btreeWriteSecbucket(cache,cbucket,rblockno);

    btreeSecbucketDel(&cbucket);
    ajListFree(&idlist);

    return ajTrue;
}
ID btreeSecbucketsReorder
TY static
MO ajindex
LB core
XX
DE Re-order secondary id leaf buckets
DE Must only be called if one of the buckets is full
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u leaf AjPBtpage
PD leaf page
PX
RT AjBool
RD true if reorder was successful i.e. leaf not full
RX
//


static AjBool btreeSecbucketsReorder(AjPBtcache cache, AjPBtpage leaf)
{
    ajuint nkeys = 0;
    AjPSecbucket cbucket  = NULL;
    unsigned char *lbuf   = NULL;

    AjPBtMem arrays1 = NULL;
    AjPBtMem arrays2 = NULL;
    AjPStr *newkeys = NULL;
    ajulong *newptrs = NULL;
    ajulong *ptrs = NULL;
    ajulong *overflows = NULL;

    ajuint i = 0;
    
    ajuint order;
    ajuint totalkeys     = 0;
    ajuint maxnperbucket = 0;
    ajuint count         = 0;
    ajuint keylimit      = 0;
    ajuint bucketlimit   = 0;
    ajuint nodetype      = 0;
    
    AjPList idlist  = NULL;
    ajuint dirtysave = 0;
    AjPStr bid      = NULL;


    ajuint newmax;
    ajuint iold = 0;
    
#if AJINDEX_DEBUG
    ajDebug("btreeSecbucketsReorder %Lu\n", leaf->pagepos);
#endif

    /* ajDebug("In btreeSecbucketsReorder\n"); */

    if(!statSaveSecId)
    {
        statSaveSecIdMax = 2048;
        AJCNEW0(statSaveSecId, statSaveSecIdMax);
        statSaveSecIdNext = 0;
    }

    dirtysave = leaf->dirty;

    leaf->dirty = BT_LOCK;
    leaf->lockfor = 1371;
    lbuf = leaf->buf;

    GBT_NODETYPE(lbuf,&nodetype);

    order = cache->sorder;

    /* Read keys/ptrs */
    arrays1 = btreeAllocSecArray(cache);
    ptrs = arrays1->parray;
    arrays2 = btreeAllocSecArray(cache);
    newkeys = arrays2->karray;
    newptrs = arrays2->parray;
    overflows = arrays2->overflows;
    btreeGetPointers(cache,lbuf,&ptrs);


    GBT_NKEYS(lbuf,&nkeys);


    if(!nkeys)
	ajFatal("SecbucketsReorder: Attempt to reorder empty lea cache %Sf",
                cache->filename);

    if(nkeys > order)
    {
        ajErr("SecbucketsReorder: nkeys %u > order %u cache %S",
              nkeys, order, cache->filename);
    }
    
    for(i=0;isnperbucket,
                    &bucketlimit, &maxnperbucket);

    if(bucketlimit >= order)
    {
        btreeDeallocSecArray(cache,arrays1);
        btreeDeallocSecArray(cache,arrays2);
	leaf->dirty = dirtysave;

	return ajFalse;
    }
    
    ++statCallSecbucketsReorder;

    /* Read IDs from all buckets and push to list and sort (increasing kw) */
    idlist  = ajListNew();
    
    for(i=0;isnperbucket,cache->idlimit);
    iold = 0;
    
    for(i=0;iOverflow = overflows[i];
	cbucket->Nentries = 0;

	count = 0;
	while(count!=maxnperbucket)
	{
	    ajListPop(idlist,(void **)&bid);
	    
	    ajStrAssignS(&cbucket->SecIds[count],bid);

	    cbucket->keylen[count] = BT_BUCKSECLEN(bid);
	    ++cbucket->Nentries;
	    ++count;
            if(statSaveSecIdNext >= statSaveSecIdMax) 
            {
                newmax = statSaveSecIdMax + statSaveSecIdMax;
                AJCRESIZE0(statSaveSecId,statSaveSecIdMax,newmax);
                statSaveSecIdMax = newmax;
            }
            statSaveSecId[statSaveSecIdNext++] = bid;
            bid = NULL;
	}


	ajListPeek(idlist,(void **)&bid);
	ajStrAssignS(&newkeys[i],bid);

	if((iold < order) && ptrs[iold])
            newptrs[i]=ptrs[iold++];
        else
	    newptrs[i] = cache->totsize;
	btreeWriteSecbucket(cache,cbucket,newptrs[i]);
    }


    /* Deal with greater-than bucket */

    cbucket->Overflow = overflows[i];
    cbucket->Nentries = 0;

    count = 0;

    while(ajListPop(idlist,(void **)&bid))
    {
	ajStrAssignS(&cbucket->SecIds[count],bid);

	++cbucket->Nentries;
	++count;
	ajStrDel(&bid);
    }
    
    if((iold < order) && ptrs[iold])
        newptrs[i]=ptrs[iold++];
    else
        newptrs[i] = cache->totsize;
    btreeWriteSecbucket(cache,cbucket,newptrs[i]);

    btreeSecbucketDel(&cbucket);

#if AJINDEX_DEBUG
    if(bucketlimit <= keylimit)
        ajDebug("btreeSecbucketsReorder '%S' %u -> %u",
                cache->filename, keylimit, bucketlimit);
#endif

    for(i = bucketlimit + 1; i <= keylimit; i++)
    {
        btreeSecpageSetfree(cache, ptrs[i]);
    }

    /* Now write out a modified leaf with new keys/ptrs */

    nkeys = bucketlimit;
    btreeWriteNode(cache,leaf,newkeys,newptrs,nkeys);
    leaf->dirty = BT_DIRTY;

    if(nodetype == BT_SECROOT)
    {
	leaf->dirty = BT_LOCK;
        leaf->lockfor = 1372;
    }

    btreeDeallocSecArray(cache,arrays1);
    btreeDeallocSecArray(cache,arrays2);

    ajListFree(&idlist);

    return ajTrue;
}
ID btreeInsertIdOnly
TY static
MO ajindex
LB core
XX
DE Add only a secondary ID: the primary keyword already exists
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pri const AjPBtPri
PD keyword/id
PX
RT void
RD 
RX
//


static void btreeInsertIdOnly(AjPBtcache cache, const AjPBtPri pri)
{
    unsigned char *buf;
    AjPBtpage page = NULL;
    ajuint nodetype;

    ajulong right = 0L;

    /* ajDebug("In btreeInsertIdOnly\n"); */
    
    if(!pri->treeblock)
    {
	fprintf(stderr,"btreeInsertIdOnly: root page doesn't exist\n");
	exit(-1);
    }
    
    cache->secrootblock = pri->treeblock;
    page = btreeSeccacheWrite(cache,cache->secrootblock);
    page->dirty = BT_LOCK;
    page->lockfor = 1381;
    buf = page->buf;
    GBT_RIGHT(buf, &right);
    cache->slevel = (ajuint) right;

    btreeKeyidInsert(cache, pri->id);

    GBT_NODETYPE(buf, &nodetype);
    if(nodetype != BT_SECBUCKET)
    {
        right = (ajulong) cache->slevel;
        SBT_RIGHT(buf, right);
        page->dirty = BT_DIRTY;
    }

    return;
}
ID btreeSecSplitroot
TY static
MO ajindex
LB core
XX
DE Split a secondary root node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT void
RD 
RX
//


static void btreeSecSplitroot(AjPBtcache cache)
{
    AjPBtpage rootpage = NULL;
    AjPBtpage rpage    = NULL;
    AjPBtpage lpage    = NULL;
    AjPBtpage tpage    = NULL;

    AjPBtMem arrays = NULL;
    AjPBtMem tarrays = NULL;
    AjPStr *karray  = NULL;
    AjPStr *tkarray = NULL;
    ajulong *parray  = NULL;
    ajulong *tparray = NULL;

    ajuint order  = 0;
    ajuint nkeys  = 0;
    ajuint keypos = 0;
    
    ajulong rblockno = 0L;
    ajulong lblockno = 0L;

    ajulong right;
    
    AjPStr key = NULL;
    ajuint i;

    unsigned char *rootbuf = NULL;
    unsigned char *rbuf    = NULL;
    unsigned char *lbuf    = NULL;
    unsigned char *tbuf    = NULL;
    
    ajuint nodetype  = 0;
    ajulong overflow = 0L;
    ajulong zero     = 0L;
    ajuint totlen    = 0;
    ajuint rkeyno    = 0;
    ajuint n         = 0;

    ajulong lv = 0L;
    ajuint  v  = 0;
    
    
#if AJINDEX_DEBUG
    ajDebug("btreeSecSplitroot %Lu\n", cache->secrootblock);
#endif
    ++statCallSecSplitroot;

    /* ajDebug("In btreeSecSplitroot\n"); */

    order = cache->sorder;

    arrays = btreeAllocSecArray(cache);
    tarrays = btreeAllocSecArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;
    tkarray = tarrays->karray;
    tparray = tarrays->parray;

    rootpage = btreeSeccacheLocate(cache,cache->secrootblock);
    rootbuf = rootpage->buf;

    nkeys = order - 1;

    keypos = nkeys / 2;

    if(!(nkeys % 2))
	--keypos;


    rblockno = cache->totsize;
    rpage = btreeSeccacheNodenew(cache);
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1391;

    lblockno = cache->totsize;
    lpage = btreeSeccacheNodenew(cache);

    /* Comment this next block out after the beta test */
    if(!cache->slevel)
    {
	fprintf(stderr,"btreeSecSplitroot Shouldn't get here\n");
	exit(0);
	lv = zero;
	SBT_LEFT(lpage->buf,lv);
	lv = rblockno;
	SBT_RIGHT(lpage->buf,lv);
	lv = lblockno;
	SBT_LEFT(rpage->buf,lv);
	lv = zero;
	SBT_RIGHT(rpage->buf,lv);
    }

    btreeGetKeys(cache,rootbuf,&karray,&parray);

    /* Get key for root node and write new root node */
    ajStrAssignS(&tkarray[0],karray[keypos]);
    tparray[0] = lblockno;
    tparray[1] = rblockno;
    

    n = 1;
    v = n;
    SBT_NKEYS(rootbuf,v);
    btreeWriteNode(cache,rootpage,tkarray,tparray,1);
    right = (ajulong)(cache->slevel + 1);
    SBT_RIGHT(rootbuf,right);

    rootpage->dirty = BT_LOCK;
    rootpage->lockfor = 1392;

    rbuf = rpage->buf;
    lbuf = lpage->buf;
    
    if(cache->slevel)
	nodetype = BT_SECINTERNAL;
    else
	nodetype = BT_SECLEAF;

    v = nodetype;
    SBT_NODETYPE(rbuf,v);
    v = nodetype;
    SBT_NODETYPE(lbuf,v);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);
    lv = cache->secrootblock;
    SBT_PREV(rbuf,lv);
    lv = overflow;
    SBT_OVERFLOW(lbuf,lv);
    lv = cache->secrootblock;
    SBT_PREV(lbuf,lv);

    totlen = 0;

    for(i=0;ibuf;
	lv = lblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }

    totlen = 0;

    for(i=keypos+1;idirty = BT_DIRTY;

    btreeWriteNode(cache,rpage,tkarray,tparray,rkeyno);

    for(i=0;ibuf;
	lv = rblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }


    ++cache->slevel;

    btreeDeallocSecArray(cache,arrays);
    btreeDeallocSecArray(cache,tarrays);

    ajStrDel(&key);
    
    return;
}
ID btreeInsertKeySec
TY static
MO ajindex
LB core
XX
DE Insert a secondary key into a potentially full node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD original page
PX
PN [3]
PA r key const AjPStr
PD key to insert
PX
PN [4]
PA r less ajulong
PD less-than pointer
PX
PN [5]
PA r greater ajulong
PD greater-than pointer
PX
RT void
RD 
RX
//


static void btreeInsertKeySec(AjPBtcache cache, AjPBtpage page,
			      const AjPStr key, ajulong less, ajulong greater)
{
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;
    unsigned char *tbuf = NULL;

    AjPStr *karray  = NULL;
    ajulong *parray  = NULL;
    AjPStr *tkarray = NULL;
    ajulong *tparray = NULL;
    AjPBtMem savekeyarrays = NULL;
    AjPBtMem tarrays = NULL;

    ajuint nkeys  = 0;
    ajuint order  = 0;
    ajuint keypos = 0;
    ajuint rkeyno = 0;
    
    ajuint i = 0;
    ajuint n = 0;
    
    ajuint nodetype = 0;
    AjPBtpage ipage = NULL;
    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage tpage = NULL;

    ajulong blockno  = 0L;
    ajulong rblockno = 0L;
    ajulong lblockno = 0L;
    ajulong ibn      = 0L;
    
    AjPStr mediankey  = NULL;
    ajulong medianless = 0L;
    ajulong mediangtr  = 0L;
    ajulong overflow   = 0L;
    ajulong prev       = 0L;
    ajuint  totlen     = 0;
    
    ajulong lv = 0L;
    ajuint  v  = 0;
    
    /* ajDebug("In btreeInsertKeySec\n"); */

    if(!btreeNodeIsFullSec(cache,page))
    {
	btreeInsertNonfullSec(cache,page,key,less,greater);

	return;
    }
    
    order = cache->sorder;
    lbuf = page->buf;
    GBT_NODETYPE(lbuf,&nodetype);
    page->dirty = BT_LOCK;
    page->lockfor = 1401;
    
    if(nodetype == BT_SECROOT)
    {
	btreeSecSplitroot(cache);
	page->dirty = BT_DIRTY;
	
	blockno = btreeGetBlockFirstS(cache,lbuf,key);

	ipage = btreeSeccacheRead(cache,blockno);
	btreeInsertNonfullSec(cache,ipage,key,less,greater);

	return;
    }

    savekeyarrays = btreeAllocSecArray(cache);
    tarrays = btreeAllocSecArray(cache);
    karray = savekeyarrays->karray;
    parray = savekeyarrays->parray;
    tkarray = tarrays->karray;
    tparray = tarrays->parray;

    lpage = page;
    lbuf = lpage->buf;
    
    btreeGetKeys(cache,lbuf,&karray,&parray);

    GBT_BLOCKNUMBER(lbuf,&lblockno);

    rblockno = cache->totsize;
    rpage = btreeSeccacheNodenew(cache);
    rbuf = rpage->buf;
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1402;
    
    GBT_PREV(lbuf,&prev);
    lv = prev;
    SBT_PREV(rbuf,lv);

    nkeys = order - 1;
    keypos = nkeys / 2;

    if(!(nkeys % 2))
	--keypos;

    mediankey = ajStrNewS(karray[keypos]);
    medianless = lblockno;
    mediangtr  = rblockno;


    GBT_NODETYPE(lbuf,&nodetype);
    v = nodetype;
    SBT_NODETYPE(rbuf,v);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);


    totlen = 0;

    for(i=0;ibuf;
	lv = lblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }


    totlen = 0;

    for(i=keypos+1;idirty = BT_DIRTY;

    btreeWriteNode(cache,rpage,tkarray,tparray,rkeyno);


    for(i=0;ibuf;
	lv = rblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }


    ibn = rblockno;

    if(MAJSTRCMPS(key,mediankey)<0)
	ibn = lblockno;

    ipage = btreeSeccacheRead(cache,ibn);
    
    btreeInsertNonfullSec(cache,ipage,key,less,greater);



    ipage = btreeSeccacheRead(cache,prev);

    btreeInsertKeySec(cache,ipage,mediankey,medianless,mediangtr);
    btreeDeallocSecArray(cache,savekeyarrays); /* mediankey points here */
    btreeDeallocSecArray(cache,tarrays);

    ajStrDel(&mediankey);

    return;
}
ID btreeKeyidInsertShift
TY static
MO ajindex
LB core
XX
DE Rebalance buckets on insertion of a secondary identifier
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u retpage AjPBtpage*
PD page
PX
PN [3]
PA r key const AjPStr
PD key
PX
RT ajulong
RD bucket block or 0L if shift not possible
RX
//


static ajulong btreeKeyidInsertShift(AjPBtcache cache, AjPBtpage *retpage,
                                     const AjPStr key)
{
    unsigned char *tbuf = NULL;
    unsigned char *pbuf = NULL;
    unsigned char *sbuf = NULL;

    AjPBtpage ppage = NULL;
    AjPBtpage spage = NULL;
    AjPBtpage tpage = NULL;

    ajuint tkeys = 0;
    ajuint pkeys = 0;
    ajuint skeys = 0;
    ajuint order = 0;
    
    ajuint i;
    ajuint n;
    ajint ii;
    
    ajulong parent  = 0L;
    ajulong blockno = 0L;
    
    AjPBtMem arrays1 = NULL;
    AjPBtMem arrays2 = NULL;
    AjPBtMem arrays3 = NULL;
    
    AjPStr *kTarray = NULL;
    AjPStr *kParray = NULL;
    AjPStr *kSarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pParray = NULL;
    ajulong *pSarray = NULL;

    AjPStr *karray = NULL;
    ajulong *parray = NULL;

    ajuint ppos    = 0;
    ajuint pkeypos = 0;
    ajuint minsize = 0;
    
    /* ajDebug("In btreeKeyidInsertShift\n"); */


    tpage = *retpage;

    tbuf = tpage->buf;

    GBT_PREV(tbuf,&parent);
    GBT_NKEYS(tbuf,&tkeys);

    order = cache->sorder;
    minsize = order / 2;

    if(order % 2)
	++minsize;

    if(tkeys <= minsize)
	return 0L;

    
    ppage = btreeSeccacheRead(cache,parent);
    pbuf = ppage->buf;
    GBT_NKEYS(pbuf,&pkeys);

    arrays1 = btreeAllocSecArray(cache);
    kParray = arrays1->karray;
    pParray = arrays1->parray;

    arrays2 = btreeAllocSecArray(cache);
    kSarray = arrays2->karray;
    pSarray = arrays2->parray;

    arrays3 = btreeAllocSecArray(cache);
    kTarray = arrays3->karray;
    pTarray = arrays3->parray;
    
    btreeGetKeys(cache,pbuf,&kParray,&pParray);

    i=0;

    while(i!=pkeys && MAJSTRCMPS(key,kParray[i])>=0)
	++i;

    pkeypos = i;
    
    if(i==pkeys)
    {
	if(MAJSTRCMPS(key,kParray[i-1])<0)
	    ppos = i-1;
	else
	    ppos = i;
    }
    else
	ppos = i;

    
    if(ppos) /* There is another leaf to the left */
    {
	spage = btreeSeccacheRead(cache,pParray[ppos-1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
    }

    if(i && skeys != order-1) /* There is space in the left leaf */
    {
	/* ajDebug("Left shift\n"); */
	btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
	if(skeys)
	    btreeGetKeys(cache,sbuf,&kSarray,&pSarray);

	i = 0;

	while(pParray[i] != tpage->pagepos)
	    ++i;

	--i;

	pkeypos = i;

	ajStrAssignS(&kSarray[skeys],kParray[pkeypos]);
	pSarray[skeys+1] = pTarray[0];
	++skeys;
	--tkeys;
	ajStrAssignS(&kParray[pkeypos],kTarray[0]);

	for(i=0;ipagepos == cache->secrootblock)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1411;
        }

	i = 0;

	while(i!=pkeys && MAJSTRCMPS(key,kParray[i])>=0)
	    ++i;

	if(i==pkeys)
	{
	    if(MAJSTRCMPS(key,kParray[i-1])<0)
		blockno = pParray[i-1];
	    else
		blockno = pParray[i];
	}
	else
	    blockno = pParray[i];

	if(blockno == spage->pagepos)
	{
	    *retpage = spage;
	    karray = kSarray;
	    parray = pSarray;
	    n = skeys;
	}
	else
	{
	    karray = kTarray;
	    parray = pTarray;
	    n = tkeys;
	}
	

	i = 0;

	while(i!=n && MAJSTRCMPS(key,karray[i])>=0)
	    ++i;

	if(i==n)
	{
	    if(MAJSTRCMPS(key,karray[i-1])<0)
		blockno = parray[i-1];
	    else
		blockno = parray[i];
	}
	else
	    blockno = parray[i];

        btreeDeallocSecArray(cache,arrays1);
        btreeDeallocSecArray(cache,arrays2);
        btreeDeallocSecArray(cache,arrays3);

	return blockno;
    }
    

    if(ppos != pkeys)	/* There is a right node */
    {
	spage = btreeSeccacheRead(cache,pParray[ppos+1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
    }


    /* Space in the right leaf */
    if(ppos != pkeys && skeys != order-1)
    {
	/* ajDebug("Right shift\n");*/
	btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
	btreeGetKeys(cache,sbuf,&kSarray,&pSarray);

	i = 0;

	while(pParray[i] != tpage->pagepos)
	    ++i;

	pkeypos = i;
	
	pSarray[skeys+1] = pSarray[skeys];

	for(ii=skeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kSarray[ii+1],kSarray[ii]);
	    pSarray[ii+1] = pSarray[ii];
	}

	ajStrAssignS(&kSarray[0],kParray[pkeypos]);
	pSarray[0] = pTarray[tkeys];
	ajStrAssignS(&kParray[pkeypos],kTarray[tkeys-1]);
	++skeys;
	--tkeys;
	pTarray[tkeys+1] = 0L;
	
	btreeWriteNode(cache,spage,kSarray,pSarray,skeys);
	btreeWriteNode(cache,tpage,kTarray,pTarray,tkeys);
	btreeWriteNode(cache,ppage,kParray,pParray,pkeys);

	if(ppage->pagepos == cache->secrootblock)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1412;
        }

	i = 0;

	while(i!=pkeys && MAJSTRCMPS(key,kParray[i])>=0)
	    ++i;

	if(i==pkeys)
	{
	    if(MAJSTRCMPS(key,kParray[i-1])<0)
		blockno = pParray[i-1];
	    else
		blockno = pParray[i];
	}
	else
	    blockno = pParray[i];

	if(blockno == spage->pagepos)
	{
	    *retpage = spage;
	    karray = kSarray;
	    parray = pSarray;
	    n = skeys;
	}
	else
	{
	    karray = kTarray;
	    parray = pTarray;
	    n = tkeys;
	}
	
	i = 0;

	while(i!=n && MAJSTRCMPS(key,karray[i])>=0)
	    ++i;

	if(i==n)
	{
	    if(MAJSTRCMPS(key,karray[i-1])<0)
		blockno = parray[i-1];
	    else
		blockno = parray[i];
	}
	else
	    blockno = parray[i];

        btreeDeallocSecArray(cache,arrays1);
        btreeDeallocSecArray(cache,arrays2);
        btreeDeallocSecArray(cache,arrays3);

	return blockno;
    }

    btreeDeallocSecArray(cache,arrays1);
    btreeDeallocSecArray(cache,arrays2);
    btreeDeallocSecArray(cache,arrays3);

    return 0L;
}
ID btreeInsertNonfullSec
TY static
MO ajindex
LB core
XX
DE Insert a key into a non-full node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD original page
PX
PN [3]
PA r key const AjPStr
PD key to insert
PX
PN [4]
PA r less ajulong
PD less-than pointer
PX
PN [5]
PA r greater ajulong
PD greater-than pointer
PX
RT void
RD 
RX
//


static void btreeInsertNonfullSec(AjPBtcache cache, AjPBtpage page,
				  const AjPStr key, ajulong less,
				  ajulong greater)
{
    unsigned char *buf = NULL;

    AjPBtMem arrays = NULL;
    AjPStr *karray = NULL;
    ajulong *parray = NULL;

    ajuint nkeys = 0;
    ajuint ipos  = 0;
    ajuint i;
    ajuint count = 0;

    ajulong lv = 0L;
    ajuint  v  = 0;
    

    AjPBtpage ppage = NULL;
    ajulong pagepos   = 0L;

    ajuint nodetype = 0;
    
    /* ajDebug("In btreeInsertNonfullSec\n"); */

    arrays = btreeAllocSecArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;

    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&nodetype);
    
    btreeGetKeys(cache,buf,&karray,&parray);

    i = 0;

    while(i!=nkeys && MAJSTRCMPS(key,karray[i]) >= 0)
	++i;

    ipos = i;

    count = nkeys - ipos;
    

    if(ipos == nkeys)
    {
	ajStrAssignS(&karray[ipos],key);
	parray[ipos+1] = greater;
	parray[ipos]   = less;
    }
    else
    {
	parray[nkeys+1] = parray[nkeys];

	for(i=nkeys-1; count>0; --count, --i)
	{
	    ajStrAssignS(&karray[i+1],karray[i]);
	    parray[i+1] = parray[i];
	}

	ajStrAssignS(&karray[ipos],key);
	parray[ipos] = less;
	parray[ipos+1] = greater;
    }

    ++nkeys;
    v = nkeys;
    SBT_NKEYS(buf,v);

    btreeWriteNode(cache,page,karray,parray,nkeys);

    if(nodetype == BT_SECROOT)
    {
	page->dirty = BT_LOCK;
        page->lockfor = 1421;
    }

    pagepos = page->pagepos;
    ppage = btreeSeccacheRead(cache,less);
    lv = pagepos;
    SBT_PREV(ppage->buf,lv);
    ppage->dirty = BT_DIRTY;

    ppage = btreeSeccacheRead(cache,greater);
    lv = pagepos;
    SBT_PREV(ppage->buf,lv);
    ppage->dirty = BT_DIRTY;

    btreeDeallocSecArray(cache,arrays);

    if(nodetype != BT_SECROOT)
	btreeKeyShiftSec(cache,page);

    return;
}
ID btreeKeyShiftSec
TY static
MO ajindex
LB core
XX
DE Rebalance secondary Nodes on insertion
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u tpage AjPBtpage
PD page
PX
RT void
RD 
RX
//


static void btreeKeyShiftSec(AjPBtcache cache, AjPBtpage tpage)
{
    unsigned char *tbuf = NULL;
    unsigned char *pbuf = NULL;
    unsigned char *sbuf = NULL;
    unsigned char *buf  = NULL;

    AjPBtpage ppage = NULL;
    AjPBtpage spage = NULL;
    AjPBtpage page  = NULL;

    ajuint tkeys = 0;
    ajuint pkeys = 0;
    ajuint skeys = 0;
    ajuint order = 0;
    
    ajuint i;
    ajint ii;
    
    ajulong parent = 0L;
    
    AjPBtMem Parrays = NULL;
    AjPBtMem Sarrays = NULL;
    AjPBtMem Tarrays = NULL;
    AjPStr *kTarray = NULL;
    AjPStr *kParray = NULL;
    AjPStr *kSarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pParray = NULL;
    ajulong *pSarray = NULL;

    ajuint pkeypos = 0;
    ajuint minsize = 0;

    ajulong lv = 0L;
    
    /* ajDebug("In btreeKeyShiftSec\n"); */
    
    tbuf = tpage->buf;

    GBT_PREV(tbuf,&parent);
    GBT_NKEYS(tbuf,&tkeys);

    order = cache->sorder;
    minsize = order / 2;

    if(order % 2)
	++minsize;

    if(tkeys <= minsize)
	return;

    
    ppage = btreeSeccacheRead(cache,parent);
    pbuf = ppage->buf;
    GBT_NKEYS(pbuf,&pkeys);
    
    Parrays = btreeAllocSecArray(cache);
    Sarrays = btreeAllocSecArray(cache);
    Tarrays = btreeAllocSecArray(cache);
    kParray = Parrays->karray;
    pParray = Parrays->parray;
    kSarray = Sarrays->karray;
    pSarray = Sarrays->parray;
    kTarray = Tarrays->karray;
    pTarray = Tarrays->parray;

    btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
    GBT_NKEYS(tbuf,&tkeys);


    btreeGetKeys(cache,pbuf,&kParray,&pParray);

    i=0;

    while(pParray[i] != tpage->pagepos)
	++i;

    if(i) /* There is another leaf to the left */
    {
	pkeypos = i-1;
	spage = btreeSeccacheRead(cache,pParray[pkeypos]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
	
    }

    if(i && skeys != order-1) /* There is space in the left leaf */
    {
	if(skeys)
	    btreeGetKeys(cache,sbuf,&kSarray,&pSarray);

	ajStrAssignS(&kSarray[skeys],kParray[pkeypos]);
	pSarray[skeys+1] = pTarray[0];
	++skeys;
	--tkeys;
	ajStrAssignS(&kParray[pkeypos],kTarray[0]);

	for(i=0;ipagepos == cache->secrootblock)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1431;
        }

	page = btreeSeccacheRead(cache,pSarray[skeys]);
	buf = page->buf;
	lv = spage->pagepos;
	SBT_PREV(buf,lv);
	page->dirty = BT_DIRTY;


        btreeDeallocSecArray(cache,Parrays);
        btreeDeallocSecArray(cache,Sarrays);
        btreeDeallocSecArray(cache,Tarrays);

	return;
    }



    if(i != pkeys)	/* There is a right node */
    {
	pkeypos = i;
	spage = btreeSeccacheRead(cache,pParray[pkeypos+1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
    }


    if(i != pkeys && skeys != order-1) /* Space in the right node */
    {
	if(skeys)
	    btreeGetKeys(cache,sbuf,&kSarray,&pSarray);

	pSarray[skeys+1] = pSarray[skeys];

	for(ii=skeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kSarray[ii+1],kSarray[ii]);
	    pSarray[ii+1] = pSarray[ii];
	}

	ajStrAssignS(&kSarray[0],kParray[pkeypos]);
	pSarray[0] = pTarray[tkeys];
	ajStrAssignS(&kParray[pkeypos],kTarray[tkeys-1]);
	++skeys;
	--tkeys;
	pTarray[tkeys+1] = 0L;
	
	btreeWriteNode(cache,spage,kSarray,pSarray,skeys);
	btreeWriteNode(cache,tpage,kTarray,pTarray,tkeys);
	btreeWriteNode(cache,ppage,kParray,pParray,pkeys);

	if(ppage->pagepos == cache->secrootblock)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1432;
        }

	page = btreeSeccacheRead(cache,pSarray[0]);
	buf = page->buf;
	lv = spage->pagepos;
	SBT_PREV(buf,lv);
	page->dirty = BT_DIRTY;


        btreeDeallocSecArray(cache,Parrays);
        btreeDeallocSecArray(cache,Sarrays);
        btreeDeallocSecArray(cache,Tarrays);

	return;
    }

    btreeDeallocSecArray(cache,Parrays);
    btreeDeallocSecArray(cache,Sarrays);
    btreeDeallocSecArray(cache,Tarrays);

    return;
}
ID btreeSecTreeCount
TY static
MO ajindex
LB core
XX
DE Count the IDs in a secondary tree of identifiers for primary keywords
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r rootblock ajulong
PD root page of secondary tree
PX
RT ajulong
RD Number of IDs in secondary tree
RX
//


static ajulong btreeSecTreeCount(AjPBtcache cache, ajulong rootblock)
{
    AjPBtMem arrays = NULL;
    ajulong *parray;

    AjPBtpage page;
    unsigned char *buf;
    ajuint nodetype;
    ajuint j;
    ajulong level = 0L;
    
    ajuint nkeys;
    ajulong right;

    ajulong lcount = 0L;

    page = btreeSeccacheRead(cache,rootblock);
    buf = page->buf;
    GBT_NODETYPE(buf,&nodetype);

    if(nodetype == BT_SECBUCKET)
    {
        return btreeSecbucketIdcount(cache, page->pagepos);
    }

    GBT_RIGHT(buf,&level);
    cache->slevel = (ajuint)level;
    
    arrays = btreeAllocSecArray(cache);
    parray = arrays->parray;
    
    btreeGetPointers(cache,buf,&parray);

    while(nodetype != BT_SECLEAF && cache->slevel!=0)
    {
	page = btreeSeccacheRead(cache,parray[0]);
	buf = page->buf;
	btreeGetPointers(cache,buf,&parray);
	GBT_NODETYPE(buf,&nodetype);
    }

    do
    {
	GBT_NKEYS(buf,&nkeys);

        page->dirty = BT_LOCK;
        page->lockfor = 9901;

	for(j=0;j<=nkeys;++j)
	    lcount += btreeNumInSecbucket(cache, parray[j]);

        page->dirty = BT_CLEAN;
	right = 0L;

	if(cache->slevel)
	{
	    GBT_RIGHT(buf,&right);

	    if(right)
	    {
		page = btreeSeccacheRead(cache,right);	    
		buf = page->buf;
		btreeGetPointers(cache,buf,&parray);
	    }
	}
	
    } while(right);

    btreeDeallocSecArray(cache,arrays);

    return lcount;
}
ID btreeSecTreeList
TY static
MO ajindex
LB core
XX
DE Read the leaves of a secondary tree, storing the entry secondary
DE identifiers in a list
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r rootblock ajulong
PD root page of secondary tree
PX
RT AjPList
RD List of IDs matching a keyword
RX
//


static AjPList btreeSecTreeList(AjPBtcache cache, ajulong rootblock)
{
    AjPList list;

    AjPBtMem arrays = NULL;
    ajulong *parray;

    AjPBtpage page;
    unsigned char *buf;
    ajuint nodetype;
    ajuint j;
    ajuint k;
    ajulong level = 0L;
    
    AjPSecbucket bucket;
    ajuint nkeys;
    AjPStr id = NULL;
    ajulong right;

    list = ajListNew();

    page = btreeSeccacheRead(cache,rootblock);
    buf = page->buf;
    GBT_NODETYPE(buf,&nodetype);

    if(nodetype == BT_SECBUCKET)
    {
        btreeSecbucketIdlist(cache, page->pagepos, list);
        return list;
    }

    GBT_RIGHT(buf,&level);
    cache->slevel = (ajuint)level;
    
    arrays = btreeAllocSecArray(cache);
    parray = arrays->parray;
    
    btreeGetPointers(cache,buf,&parray);

    while(nodetype != BT_SECLEAF && cache->slevel!=0)
    {
	page = btreeSeccacheRead(cache,parray[0]);
	buf = page->buf;
	btreeGetPointers(cache,buf,&parray);
	GBT_NODETYPE(buf,&nodetype);
    }

    do
    {
	GBT_NKEYS(buf,&nkeys);

        page->dirty = BT_LOCK;
        page->lockfor = 9901;

	for(j=0;j<=nkeys;++j)
	{
	    bucket = btreeReadSecbucket(cache, parray[j]);
	    
	    for(k=0;kNentries;++k)
	    {
		id = ajStrNewS(bucket->SecIds[k]);
		ajListPushAppend(list, (void *)id);
	    }

	    btreeSecbucketDel(&bucket);
	}

        page->dirty = BT_CLEAN;
	right = 0L;

	if(cache->slevel)
	{
	    GBT_RIGHT(buf,&right);

	    if(right)
	    {
		page = btreeSeccacheRead(cache,right);	    
		buf = page->buf;
		btreeGetPointers(cache,buf,&parray);
	    }
	}
	
    } while(right);

    btreeDeallocSecArray(cache,arrays);

    return list;
}
ID ajBtreeKeywildQuery
TY public
MO ajindex
LB core
XX
DE Wildcard retrieval of keyword index entries
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u wild AjPBtKeywild
PD Wildcard
PX
PN [3]
PA u idcache AjPBtcache
PD id cache
PX
RT AjPBtId
RD next matching Id or NULL
RX
//


AjPBtId ajBtreeKeywildQuery(AjPBtcache cache, AjPBtKeywild wild,
                            AjPBtcache idcache)
{

    AjPBtPri pri   = NULL;
    AjPBtpage page = NULL;
    AjPStr key      = NULL;
    AjPList list   = NULL;
    AjBool found   = ajFalse;
    AjPBtId btid   = NULL;
    AjPStr  id     = NULL;
    
    ajulong pagepos = 0L;

    unsigned char *buf = NULL;    

    key  = ajStrNewS(wild->prefix);
    
    list = wild->list;

    found = ajFalse;

    /*
    ** note prefix may be empty if query started with a wildcard
    */

    if(wild->first)
    {
	page = btreePrimaryFetchFindleafWild(cache,key);
	page->dirty = BT_LOCK;
        page->lockfor = 1451;
	wild->pagepos = page->pagepos;
	
	btreePrileafFetch(cache,page,list);

	page->dirty = BT_CLEAN;
	
	if(!ajListGetLength(list))
        {
            ajStrDel(&key);
	    return NULL;
        }

	while(ajListPop(list,(void **)&pri))
	{
	    if(ajStrPrefixS(pri->keyword,key))
	    {
		found = ajTrue;
		break;
	    }
	    else
		ajBtreePriDel(&pri);
	}


	wild->first = ajFalse;

	if(found)
	{
	    cache->secrootblock = pri->treeblock;
	    btreeSecLeftLeaf(cache, wild);
	}
	else /* Check the next leaf just in case key==internal */
	{
	    buf = page->buf;
	    GBT_RIGHT(buf,&pagepos);

	    if(!pagepos)
            {
                ajStrDel(&key);
		return NULL;
            }

	    page = btreePricacheRead(cache,pagepos);
	    wild->pagepos = pagepos;
	    page->dirty = BT_LOCK;
            page->lockfor = 1452;
	    
	    btreePrileafFetch(cache,page,list);	
	    
	    page->dirty = BT_CLEAN;
	    
	    if(!ajListGetLength(list))
            {
                ajStrDel(&key);
		return NULL;
	    }

	    found = ajFalse;

	    while(ajListPop(list,(void **)&pri))
	    {
		if(ajStrPrefixS(pri->keyword,key))
		{
		    found = ajTrue;
		    break;
		}
		else
		    ajBtreePriDel(&pri);
	    }

	    if(!found)
            {
                ajStrDel(&key);
		return NULL;
            }

	    cache->secrootblock = pri->treeblock;
	    btreeSecLeftLeaf(cache, wild);
	}


    }


    if(ajListGetLength(wild->idlist))
    {
	ajListPop(wild->idlist,(void **)&id);
	btid = btreeIdentQueryId(idcache,id);
	ajStrDel(&id);
        ajStrDel(&key);

	return btid;
    }
    else if((btreeKeywildNextList(cache,wild)))
    {
	if(ajListGetLength(wild->idlist))
	{
	    ajListPop(wild->idlist,(void **)&id);
	    btid = btreeIdentQueryId(idcache,id);
	    ajStrDel(&id);
            ajStrDel(&key);

	    return btid;
	}

        ajStrDel(&key);
	return NULL;
    }
    

    /* Done for the current keyword so get the next one */

    if(!ajListGetLength(list))
    {
	page = btreePricacheRead(cache,wild->pagepos); 
	buf = page->buf;
	GBT_RIGHT(buf,&pagepos);

	if(!pagepos)
        {
            ajStrDel(&key);
            return NULL;
        }

	page = btreePricacheRead(cache,pagepos);
	wild->pagepos = pagepos;
	page->dirty = BT_LOCK;
        page->lockfor = 1453;

	btreePrileafFetch(cache,page,list);	

	page->dirty = BT_CLEAN;
	
	if(!ajListGetLength(list))
        {
            ajStrDel(&key);
	    return NULL;
        }
    }

    while(ajListPop(list,(void **)&pri))
    {
	if(ajStrPrefixS(pri->keyword,key))
	{
	    found = ajTrue;
	    break;
	}

	ajBtreePriDel(&pri);
    }

    ajStrDel(&key);

    if(!found)
	return NULL;

    cache->secrootblock = pri->treeblock;
    btreeSecLeftLeaf(cache, wild);
    
    if(ajListGetLength(wild->idlist))
    {
	ajListPop(wild->idlist,(void **)&id);
	btid = btreeIdentQueryId(idcache,id);
	ajStrDel(&id);
    }
    else
	return NULL;
    
    return btid;
}
ID ajBtreeKeyFetchId
TY public
MO ajindex
LB core
XX
DE Retrieval of keyword index entries with a perfect match to the keyword
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u idcache AjPBtcache
PD id cache
PX
PN [3]
PA r key const AjPStr
PD key
PX
PN [4]
PA u btidlist AjPList
PD List of matching AjPBtId entries
PX
RT void
RD 
RX
//


void ajBtreeKeyFetchId(AjPBtcache cache, AjPBtcache idcache,
                       const AjPStr key, AjPList btidlist)
{
    AjPBtId   id   = NULL;
    AjPList tlist = NULL;
    AjPStr   kwid  = NULL;
    ajulong treeblock = 0L;

    if(ajBtreeKeyFindLen(cache, key,
                         &treeblock))
    {
        tlist = btreeSecTreeList(cache, treeblock);

        while(ajListPop(tlist,(void **)&kwid))
        {
            id = btreeIdentQueryId(idcache, kwid);
            ajDebug("id '%S' entry: %p\n", kwid, id);

            if(id)
            {
                ajDebug("entry id: '%S' dups: %u offset: %Ld\n",
                        id->id, id->dups, id->offset);
                if(!id->dups)
                {
                    ajListPushAppend(btidlist,(void *)id);
                    id = NULL;
                }
                else
                {
                    btreeIdentFetchMulti(idcache, id->id, id->offset,
                                           btidlist);
                    ajBtreeIdDel(&id);
                }
            }

            ajStrDel(&kwid);
        }

        ajListFree(&tlist);
    }
}
ID ajBtreeKeyFetchHit
TY public
MO ajindex
LB core
XX
DE Retrieval of keyword hits with a perfect match to the keyword
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u idcache AjPBtcache
PD id cache
PX
PN [3]
PA r key const AjPStr
PD key
PX
PN [4]
PA u hitlist AjPList
PD List of matching AjPBtHit entries
PX
RT void
RD 
RX
//


void ajBtreeKeyFetchHit(AjPBtcache cache, AjPBtcache idcache,
                        const AjPStr key, AjPList hitlist)
{
    AjPBtId id  = NULL;
    AjPBtHit hit  = NULL;
    AjPList tlist = NULL;
    AjPStr   kwid  = NULL;
    ajulong treeblock = 0L;

    ajDebug("ajBtreeKeyFetchHit '%S'\n", key);

    if(ajBtreeKeyFindLen(cache, key,
                         &treeblock))
    {
        tlist = btreeSecTreeList(cache, treeblock);

        while(ajListPop(tlist,(void **)&kwid))
        {
            id = btreeIdentQueryId(idcache, kwid);
            ajDebug("id '%S' entry: %p\n", kwid, id);

            if(id)
            {
                ajDebug("entry id: '%S' dups: %u offset: %Ld\n",
                        id->id, id->dups, id->offset);
                if(!id->dups)
                {
                    hit = ajBtreeHitNewId(id);
                    ajListPushAppend(hitlist,(void *)hit);
                    hit = NULL;
                    ajBtreeIdDel(&id);
                }
                else
                {
                    btreeIdentFetchMultiHit(idcache, id->offset,
                                            hitlist);
                    ajBtreeIdDel(&id);
                }
            }

            ajStrDel(&kwid);
        }

        ajListFree(&tlist);
    }
}
ID ajBtreeKeyFetchHitref
TY public
MO ajindex
LB core
XX
DE Retrieval of keyword hits with a perfect match to the keyword
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u idcache AjPBtcache
PD id cache
PX
PN [3]
PA r key const AjPStr
PD key
PX
PN [4]
PA u hitlist AjPList
PD List of matching AjPBtHitref entries
PX
RT void
RD 
RX
//


void ajBtreeKeyFetchHitref(AjPBtcache cache, AjPBtcache idcache,
                           const AjPStr key, AjPList hitlist)
{
    AjPBtId id  = NULL;
    AjPBtHitref hitref = NULL;
    AjPList tlist = NULL;
    AjPStr   kwid  = NULL;
    ajulong treeblock = 0L;

    ajDebug("ajBtreeKeyFetchHitref '%S'\n", key);

    if(ajBtreeKeyFindLen(cache, key,
                         &treeblock))
    {
        tlist = btreeSecTreeList(cache, treeblock);

        while(ajListPop(tlist,(void **)&kwid))
        {
            id = btreeIdentQueryId(idcache, kwid);
            ajDebug("id '%S' entry: %p\n", kwid, id);

            if(id)
            {
                ajDebug("entry id: '%S' dups: %u offset: %Ld\n",
                        id->id, id->dups, id->offset);
                if(!id->dups)
                {
                    hitref = ajBtreeHitrefNewId(id);
                    ajListPushAppend(hitlist,(void *)hitref);
                    hitref = NULL;
                    ajBtreeIdDel(&id);
                }
                else
                {
                    btreeIdentFetchMultiHitref(idcache, id->offset,
                                            hitlist);
                    ajBtreeIdDel(&id);
                }
            }

            ajStrDel(&kwid);
        }

        ajListFree(&tlist);
    }
}
ID ajBtreeKeyFetchwildId
TY public
MO ajindex
LB core
XX
DE Wildcard retrieval of keyword index entries
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u idcache AjPBtcache
PD id cache
PX
PN [3]
PA r key const AjPStr
PD key
PX
PN [4]
PA u btidlist AjPList
PD List of matching AjPBtId entries
PX
RT void
RD 
RX
//


void ajBtreeKeyFetchwildId(AjPBtcache cache, AjPBtcache idcache,
                           const AjPStr key, AjPList btidlist)
{
    AjPBtPri pri     = NULL;
    AjPBtpage page   = NULL;
    AjPList prilist  = NULL;
    AjPList strlist  = NULL;

    AjPBtId btid = NULL;
    AjPStr id    = NULL;
    
    AjBool found     = ajFalse;
    AjBool finished  = ajFalse;
    
    ajulong pripagepossave = 0L;
    ajulong pagepos        = 0L;
    ajulong right         = 0L;
    
    unsigned char *buf = NULL;    

    AjPStr prefix = NULL;
    
    char *p;
    char *cp;
    
    ajDebug("ajBtreeKeyFetchwildId '%S' list: %Lu\n",
            key, ajListGetLength(btidlist));

    prefix = ajStrNew();
    cp = MAJSTRGETPTR(key);
    p = strpbrk(cp,"*?");

    if(p)
    {
	if(p-cp)
	    ajStrAssignSubS(&prefix,key,0,p-cp-1);
	else
	{
	    ajStrDel(&prefix);
	    btreeKeywordFullSearchId(cache,key,idcache,btidlist);

	    return;
	}
    }
    else
	ajStrAssignS(&prefix,key);
    
    ajDebug("ajBtreeKeyFetchwildId prefix '%S' list: %Lu\n",
            prefix, ajListGetLength(btidlist));

    prilist  = ajListNew();
    
    found   = ajFalse;
    
    page = btreePrimaryFetchFindleafWild(cache,prefix);
    page->dirty = BT_LOCK;
    page->lockfor = 1461;
    pripagepossave = page->pagepos;
    btreePrileafFetch(cache,page,prilist);
    page->dirty = BT_CLEAN;

    if(!ajListGetLength(prilist))
    {
	ajStrDel(&prefix);
	ajListFree(&prilist);

	return;
    }


    while(ajListPop(prilist,(void **)&pri))
    {
	if(ajStrPrefixS(pri->keyword,prefix))
	{
	    found = ajTrue;
	    break;
	}
	else
	    ajBtreePriDel(&pri);
    }

    if(!found)	/* check next leaf in case key == internal */
    {
	buf = page->buf;
	GBT_RIGHT(buf,&pagepos);

	if(!pagepos)
	{
	    ajStrDel(&prefix);
	    ajListFree(&prilist);

	    return;
	}
	page = btreePricacheRead(cache,pagepos);
	page->dirty = BT_LOCK;
        page->lockfor = 1462;
	pripagepossave = pagepos;
	btreePrileafFetch(cache,page,prilist);
	page->dirty = BT_CLEAN;

	if(!ajListGetLength(prilist))
	{
	    ajStrDel(&prefix);
	    ajListFree(&prilist);

	    return;
	}

	while(ajListPop(prilist,(void **)&pri))
	{
	    if(ajStrPrefixS(pri->keyword,prefix))
	    {
		found = ajTrue;
		break;
	    }
	    else
		ajBtreePriDel(&pri);
	}
    }
    
    if(!found)
    {
	ajStrDel(&prefix);
	ajListFree(&prilist);
	return;
    }

    finished = ajFalse;

    strlist  = ajListNew();

    while(!finished)
    {
	if(ajStrMatchWildS(pri->keyword,key))
	{
	    cache->secrootblock = pri->treeblock;
	    btreeReadAllSecLeaves(cache,strlist);
	}

	ajBtreePriDel(&pri);

	if(!ajListGetLength(prilist))
	{
	    page = btreePricacheRead(cache,pripagepossave);

	    buf = page->buf;
	    GBT_RIGHT(buf,&right);

	    if(!right)
	    {
		finished = ajTrue;
		continue;
	    }
	    
	    page->dirty = BT_LOCK;
            page->lockfor = 1463;
	    btreePrileafFetch(cache,page,prilist);
	    page->dirty = BT_CLEAN;
	    pripagepossave = right;	    

	    if(!ajListGetLength(prilist))
	    {
		finished = ajTrue;
		continue;
	    }
	}
	

	ajListPop(prilist,(void **)&pri);

	if(!ajStrPrefixS(pri->keyword,prefix))
	{
	    ajBtreePriDel(&pri);
	    finished = ajTrue;
	}
    }
    

    while(ajListPop(prilist,(void **)&pri))
	ajBtreePriDel(&pri);

    ajListFree(&prilist);


    if(ajListGetLength(strlist))
    {
	ajListSortUnique(strlist, &ajStrVcmp, &btreeStrDel);

	while(ajListPop(strlist,(void **)&id))
	{
	    btid = btreeIdentQueryId(idcache,id);
            if(btid)
            {
                ajDebug("ajBtreeKeyFetchwildId id: %S btid: '%S'\n",
                        id, btid->id);
                ajListPushAppend(btidlist,(void *)btid);
            }
            else
            {
                ajDebug("ajBtreeKeyFetchwildId id: %S not found\n",
                        id);
            }
	    ajStrDel(&id);
	}
    }

    ajListFree(&strlist);

    ajStrDel(&prefix);

    return;
}
ID ajBtreeKeyFetchwildHit
TY public
MO ajindex
LB core
XX
DE Wildcard retrieval of keyword hit entries
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u idcache AjPBtcache
PD id cache
PX
PN [3]
PA r key const AjPStr
PD key
PX
PN [4]
PA u hitlist AjPList
PD List of matching AjPBtHit entries
PX
RT void
RD 
RX
//


void ajBtreeKeyFetchwildHit(AjPBtcache cache, AjPBtcache idcache,
                            const AjPStr key, AjPList hitlist)
{
    AjPBtPri pri     = NULL;
    AjPBtpage page   = NULL;
    AjPList prilist  = NULL;
    AjPList strlist  = NULL;

    AjPBtId btid = NULL;
    AjPBtHit hit = NULL;
    AjPStr id    = NULL;
    
    AjBool found     = ajFalse;
    AjBool finished  = ajFalse;
    
    ajulong pripagepossave = 0L;
    ajulong pagepos        = 0L;
    ajulong right         = 0L;
    
    unsigned char *buf = NULL;    

    AjPStr prefix = NULL;
    
    char *p;
    char *cp;

    ajDebug("ajBtreeKeyFetchwildHit '%S' list: %Lu\n",
            key, ajListGetLength(hitlist));

    prefix = ajStrNew();
    cp = MAJSTRGETPTR(key);
    p = strpbrk(cp,"*?");

    if(p)
    {
	if(p-cp)
	    ajStrAssignSubS(&prefix,key,0,p-cp-1);
	else
	{
	    ajStrDel(&prefix);
	    btreeKeywordFullSearchHit(cache,key,idcache,hitlist);

	    return;
	}
    }
    else
	ajStrAssignS(&prefix,key);
    
    ajDebug("ajBtreeKeyFetchwildHit prefix '%S' list: %Lu\n",
            prefix, ajListGetLength(hitlist));

    prilist  = ajListNew();
    
    found   = ajFalse;
    
    page = btreePrimaryFetchFindleafWild(cache,prefix);
    page->dirty = BT_LOCK;
    page->lockfor = 1461;
    pripagepossave = page->pagepos;
    btreePrileafFetch(cache,page,prilist);
    page->dirty = BT_CLEAN;

    if(!ajListGetLength(prilist))
    {
	ajStrDel(&prefix);
	ajListFree(&prilist);

	return;
    }


    while(ajListPop(prilist,(void **)&pri))
    {
	if(ajStrPrefixS(pri->keyword,prefix))
	{
	    found = ajTrue;
	    break;
	}
	else
	    ajBtreePriDel(&pri);
    }

    if(!found)	/* check next leaf in case key == internal */
    {
	buf = page->buf;
	GBT_RIGHT(buf,&pagepos);

	if(!pagepos)
	{
	    ajStrDel(&prefix);
	    ajListFree(&prilist);

	    return;
	}
	page = btreePricacheRead(cache,pagepos);
	page->dirty = BT_LOCK;
        page->lockfor = 1462;
	pripagepossave = pagepos;
	btreePrileafFetch(cache,page,prilist);
	page->dirty = BT_CLEAN;

	if(!ajListGetLength(prilist))
	{
	    ajStrDel(&prefix);
	    ajListFree(&prilist);

	    return;
	}

	while(ajListPop(prilist,(void **)&pri))
	{
	    if(ajStrPrefixS(pri->keyword,prefix))
	    {
		found = ajTrue;
		break;
	    }
	    else
		ajBtreePriDel(&pri);
	}
    }
    
    if(!found)
    {
	ajStrDel(&prefix);
	ajListFree(&prilist);
	return;
    }

    finished = ajFalse;

    strlist  = ajListNew();

    while(!finished)
    {
	if(ajStrMatchWildS(pri->keyword,key))
	{
	    cache->secrootblock = pri->treeblock;
	    btreeReadAllSecLeaves(cache,strlist);
	}

	ajBtreePriDel(&pri);

	if(!ajListGetLength(prilist))
	{
	    page = btreePricacheRead(cache,pripagepossave);

	    buf = page->buf;
	    GBT_RIGHT(buf,&right);

	    if(!right)
	    {
		finished = ajTrue;
		continue;
	    }
	    
	    page->dirty = BT_LOCK;
            page->lockfor = 1463;
	    btreePrileafFetch(cache,page,prilist);
	    page->dirty = BT_CLEAN;
	    pripagepossave = right;	    

	    if(!ajListGetLength(prilist))
	    {
		finished = ajTrue;
		continue;
	    }
	}
	

	ajListPop(prilist,(void **)&pri);

	if(!ajStrPrefixS(pri->keyword,prefix))
	{
	    ajBtreePriDel(&pri);
	    finished = ajTrue;
	}
    }
    

    while(ajListPop(prilist,(void **)&pri))
	ajBtreePriDel(&pri);

    ajListFree(&prilist);


    if(ajListGetLength(strlist))
    {
	ajListSortUnique(strlist, &ajStrVcmp, &btreeStrDel);

	while(ajListPop(strlist,(void **)&id))
	{
	    btid = btreeIdentQueryId(idcache,id);
            if(btid)
            {
                ajDebug("ajBtreeKeyFetchwildHit id: %S btid: '%S'\n",
                        id, btid->id);
                hit = ajBtreeHitNewId(btid);
                ajListPushAppend(hitlist,(void *)hit);
                hit = NULL;
                ajBtreeIdDel(&btid);
            }
            else
            {
                ajDebug("ajBtreeKeyFetchwildHit id: %S not found\n",
                        id);
            }
	    ajStrDel(&id);
	}
    }

    ajListFree(&strlist);

    ajStrDel(&prefix);

    return;
}
ID ajBtreeKeyFetchwildHitref
TY public
MO ajindex
LB core
XX
DE Wildcard retrieval of keyword reference hit entries
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u idcache AjPBtcache
PD id cache
PX
PN [3]
PA r key const AjPStr
PD key
PX
PN [4]
PA u hitlist AjPList
PD List of matching AjPBtHitref entries
PX
RT void
RD 
RX
//


void ajBtreeKeyFetchwildHitref(AjPBtcache cache, AjPBtcache idcache,
                               const AjPStr key, AjPList hitlist)
{
    AjPBtPri pri     = NULL;
    AjPBtpage page   = NULL;
    AjPList prilist  = NULL;
    AjPList strlist  = NULL;

    AjPBtId btid = NULL;
    AjPBtHitref hitref = NULL;
    AjPStr id    = NULL;
    
    AjBool found     = ajFalse;
    AjBool finished  = ajFalse;
    
    ajulong pripagepossave = 0L;
    ajulong pagepos        = 0L;
    ajulong right         = 0L;
    
    unsigned char *buf = NULL;    

    AjPStr prefix = NULL;
    
    char *p;
    char *cp;
    
    ajDebug("ajBtreeKeyFetchwildHitref '%S' list: %Lu\n",
            key, ajListGetLength(hitlist));

    prefix = ajStrNew();
    cp = MAJSTRGETPTR(key);
    p = strpbrk(cp,"*?");

    if(p)
    {
	if(p-cp)
	    ajStrAssignSubS(&prefix,key,0,p-cp-1);
	else
	{
	    ajStrDel(&prefix);
	    btreeKeywordFullSearchHitref(cache,key,idcache,hitlist);

	    return;
	}
    }
    else
	ajStrAssignS(&prefix,key);
    
    ajDebug("ajBtreeKeyFetchwildHitref prefix '%S' list: %Lu\n",
            prefix, ajListGetLength(hitlist));

    prilist  = ajListNew();
    
    found   = ajFalse;
    
    page = btreePrimaryFetchFindleafWild(cache,prefix);
    page->dirty = BT_LOCK;
    page->lockfor = 1461;
    pripagepossave = page->pagepos;
    btreePrileafFetch(cache,page,prilist);
    page->dirty = BT_CLEAN;

    if(!ajListGetLength(prilist))
    {
	ajStrDel(&prefix);
	ajListFree(&prilist);

	return;
    }


    while(ajListPop(prilist,(void **)&pri))
    {
	if(ajStrPrefixS(pri->keyword,prefix))
	{
	    found = ajTrue;
	    break;
	}
	else
	    ajBtreePriDel(&pri);
    }

    if(!found)	/* check next leaf in case key == internal */
    {
	buf = page->buf;
	GBT_RIGHT(buf,&pagepos);

	if(!pagepos)
	{
	    ajStrDel(&prefix);
	    ajListFree(&prilist);

	    return;
	}
	page = btreePricacheRead(cache,pagepos);
	page->dirty = BT_LOCK;
        page->lockfor = 1462;
	pripagepossave = pagepos;
	btreePrileafFetch(cache,page,prilist);
	page->dirty = BT_CLEAN;

	if(!ajListGetLength(prilist))
	{
	    ajStrDel(&prefix);
	    ajListFree(&prilist);

	    return;
	}

	while(ajListPop(prilist,(void **)&pri))
	{
	    if(ajStrPrefixS(pri->keyword,prefix))
	    {
		found = ajTrue;
		break;
	    }
	    else
		ajBtreePriDel(&pri);
	}
    }
    
    if(!found)
    {
	ajStrDel(&prefix);
	ajListFree(&prilist);
	return;
    }

    finished = ajFalse;

    strlist  = ajListNew();

    while(!finished)
    {
	if(ajStrMatchWildS(pri->keyword,key))
	{
	    cache->secrootblock = pri->treeblock;
	    btreeReadAllSecLeaves(cache,strlist);
	}

	ajBtreePriDel(&pri);

	if(!ajListGetLength(prilist))
	{
	    page = btreePricacheRead(cache,pripagepossave);

	    buf = page->buf;
	    GBT_RIGHT(buf,&right);

	    if(!right)
	    {
		finished = ajTrue;
		continue;
	    }
	    
	    page->dirty = BT_LOCK;
            page->lockfor = 1463;
	    btreePrileafFetch(cache,page,prilist);
	    page->dirty = BT_CLEAN;
	    pripagepossave = right;	    

	    if(!ajListGetLength(prilist))
	    {
		finished = ajTrue;
		continue;
	    }
	}
	

	ajListPop(prilist,(void **)&pri);

	if(!ajStrPrefixS(pri->keyword,prefix))
	{
	    ajBtreePriDel(&pri);
	    finished = ajTrue;
	}
    }
    

    while(ajListPop(prilist,(void **)&pri))
	ajBtreePriDel(&pri);

    ajListFree(&prilist);


    if(ajListGetLength(strlist))
    {
	ajListSortUnique(strlist, &ajStrVcmp, &btreeStrDel);

	while(ajListPop(strlist,(void **)&id))
	{
	    btid = btreeIdentQueryId(idcache,id);
            if(btid)
            {
                ajDebug("ajBtreeKeyFetchwildHitref id: %S btid: '%S'\n",
                        id, btid->id);
                hitref = ajBtreeHitrefNewId(btid);
                ajListPushAppend(hitlist,(void *)hitref);
                hitref = NULL;
                ajBtreeIdDel(&btid);
            }
            else
            {
                ajDebug("ajBtreeKeyFetchwildHitref id: %S not found\n",
                        id);
            }
	    ajStrDel(&id);
	}
    }

    ajListFree(&strlist);

    ajStrDel(&prefix);

    return;
}
ID btreeKeywordFullSearchId
TY static
MO ajindex
LB core
XX
DE Wildcard retrieval of key/des/org entries. Whole index scan. Only used for
DE wildcard searches with keys beginning with '?' or '*'
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD Wildcard key
PX
PN [3]
PA u idcache AjPBtcache
PD id index cache
PX
PN [4]
PA u idlist AjPList
PD list of matching AjPBtIds
PX
RT void
RD 
RX
//


static void btreeKeywordFullSearchId(AjPBtcache cache, const AjPStr key,
                                     AjPBtcache idcache, AjPList idlist)
{
    AjPBtPri pri   = NULL;
    AjPBtpage root = NULL;
    AjPBtpage page = NULL;
    AjPBtId btid   = NULL;
    ajulong right   = 0L;
    ajuint nodetype = 0;
    
    AjPBtMem arrays = NULL;
    ajulong *parray = NULL;

    AjPList list   = NULL;
    AjPList strlist = NULL;
    AjPStr id = NULL;
    
    unsigned char *buf = NULL;    

    ajulong nids = 0;
    ajulong i;

    AjPTable strtable = NULL;
    AjPStr *idarray = NULL;

    list    = ajListNew();
    strlist = ajListNew();
    strtable = ajTablestrNew(idcache->countunique);
    
    root = btreePricacheLocate(cache, 0L);
    page = root;
    
    buf = root->buf;
    GBT_NODETYPE(buf,&nodetype);
    
    if(cache->plevel)
    {
        arrays = btreeAllocPriArray(cache);
        parray = arrays->parray;
	while(nodetype != BT_LEAF)
	{
	    btreeGetPointers(cache,buf,&parray);
	    page = btreePricacheRead(cache, parray[0]);
	    buf = page->buf;
	    GBT_NODETYPE(buf,&nodetype);
	    page->dirty = BT_CLEAN;
	}
        btreeDeallocPriArray(cache,arrays);
    }

    right = 99L;

    while(right)
    {
	btreePrileafFetch(cache,page,list);

	while(ajListPop(list,(void **)&pri))
	{
	    if(ajStrMatchWildS(pri->keyword,key))
	    {
		cache->secrootblock = pri->treeblock;
		btreeReadAllSecLeaves(cache,strlist);

                while(ajListPop(strlist,(void **)&id))
                    ajTablePut(strtable, id, NULL);
	    }

	    ajBtreePriDel(&pri);
	}

	GBT_RIGHT(buf,&right);

	if(right)
	{
	    page = btreePricacheRead(cache,right);
	    buf = page->buf;
	}
    }

    if(ajTableGetLength(strtable))
    {
        nids = ajTableToarrayKeys(strtable, (void***)&idarray);

        for(i=0; i < nids; i++)
	{
	    btid = btreeIdentQueryId(idcache,idarray[i]);
            if(btid)
                ajListPushAppend(idlist,(void *)btid);
	}
    }

    AJFREE(idarray);
    ajListFree(&list);
    ajListFree(&strlist);
    ajTableDel(&strtable);

    return;
}
ID btreeKeywordFullSearchHit
TY static
MO ajindex
LB core
XX
DE Wildcard retrieval of key/des/org entries. Whole index scan. Only used for
DE wildcard searches with keys beginning with '?' or '*'
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD Wildcard key
PX
PN [3]
PA u idcache AjPBtcache
PD id index cache
PX
PN [4]
PA u hitlist AjPList
PD list of matching AjPBtHits
PX
RT void
RD 
RX
//


static void btreeKeywordFullSearchHit(AjPBtcache cache, const AjPStr key,
                                      AjPBtcache idcache, AjPList hitlist)
{
    AjPBtPri pri   = NULL;
    AjPBtpage root = NULL;
    AjPBtpage page = NULL;
    AjPBtHit  hit  = NULL;
    ajulong right   = 0L;
    ajuint nodetype = 0;
    
    AjPBtMem arrays = NULL;
    ajulong *parray = NULL;

    AjPList list   = NULL;
    AjPList strlist = NULL;
    AjPStr id = NULL;

    unsigned char *buf = NULL;

    ajulong nids = 0;
    ajulong i;

    AjPTable strtable = NULL;
    AjPStr *idarray = NULL;

    list    = ajListNew();
    strlist = ajListNew();
    strtable = ajTablestrNew(idcache->countunique);

    root = btreePricacheLocate(cache, 0L);
    page = root;
    
    buf = root->buf;
    GBT_NODETYPE(buf,&nodetype);
    
    if(cache->plevel)
    {
        arrays = btreeAllocPriArray(cache);
        parray = arrays->parray;
	while(nodetype != BT_LEAF)
	{
	    btreeGetPointers(cache,buf,&parray);
	    page = btreePricacheRead(cache, parray[0]);
	    buf = page->buf;
	    GBT_NODETYPE(buf,&nodetype);
	    page->dirty = BT_CLEAN;
	}
        btreeDeallocPriArray(cache,arrays);
    }

    right = 99L;

    while(right)
    {
	btreePrileafFetch(cache,page,list);

	while(ajListPop(list,(void **)&pri))
	{
	    if(ajStrMatchWildS(pri->keyword,key))
	    {
		cache->secrootblock = pri->treeblock;
		btreeReadAllSecLeaves(cache,strlist);

                while(ajListPop(strlist,(void **)&id))
                    ajTablePut(strtable, id, NULL);
            }

	    ajBtreePriDel(&pri);
	}

	GBT_RIGHT(buf,&right);

	if(right)
	{
	    page = btreePricacheRead(cache,right);
	    buf = page->buf;
	}
    }

    if(ajTableGetLength(strtable))
    {
        nids = ajTableToarrayKeys(strtable, (void***)&idarray);
        for(i=0; i < nids; i++)
	{
	    hit = btreeIdentQueryHit(idcache,idarray[i]);
	    if(hit)
                ajListPushAppend(hitlist,(void *)hit);
	}
    }

    AJFREE(idarray);
    ajListFree(&list);
    ajListFree(&strlist);
    ajTableDel(&strtable);

    return;
}
ID btreeKeywordFullSearchHitref
TY static
MO ajindex
LB core
XX
DE Wildcard retrieval of key/des/org entries. Whole index scan. Only used for
DE wildcard searches with keys beginning with '?' or '*'
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD Wildcard key
PX
PN [3]
PA u idcache AjPBtcache
PD id index cache
PX
PN [4]
PA u hitlist AjPList
PD list of matching AjPBtHitrefs
PX
RT void
RD 
RX
//


static void btreeKeywordFullSearchHitref(AjPBtcache cache, const AjPStr key,
                                         AjPBtcache idcache, AjPList hitlist)
{
    AjPBtPri pri   = NULL;
    AjPBtpage root = NULL;
    AjPBtpage page = NULL;
    AjPBtHitref hitref = NULL;
    ajulong right   = 0L;
    ajuint nodetype = 0;
    
    AjPBtMem arrays = NULL;
    ajulong *parray = NULL;

    AjPList list   = NULL;
    AjPList strlist = NULL;
    AjPStr id = NULL;
    
    unsigned char *buf = NULL;    

    ajulong nids = 0;
    ajulong i;

    AjPTable strtable = NULL;
    AjPStr *idarray = NULL;

    list    = ajListNew();
    strlist = ajListNew();
    strtable = ajTablestrNew(idcache->countunique);
    
    root = btreePricacheLocate(cache, 0L);
    page = root;
    
    buf = root->buf;
    GBT_NODETYPE(buf,&nodetype);
    
    if(cache->plevel)
    {
        arrays = btreeAllocPriArray(cache);
        parray = arrays->parray;
	while(nodetype != BT_LEAF)
	{
	    btreeGetPointers(cache,buf,&parray);
	    page = btreePricacheRead(cache, parray[0]);
	    buf = page->buf;
	    GBT_NODETYPE(buf,&nodetype);
	    page->dirty = BT_CLEAN;
	}
        btreeDeallocPriArray(cache,arrays);
    }

    right = 99L;

    while(right)
    {
	btreePrileafFetch(cache,page,list);

	while(ajListPop(list,(void **)&pri))
	{
	    if(ajStrMatchWildS(pri->keyword,key))
	    {
		cache->secrootblock = pri->treeblock;
		btreeReadAllSecLeaves(cache,strlist);

                while(ajListPop(strlist,(void **)&id))
                    ajTablePut(strtable, id, NULL);
	    }

	    ajBtreePriDel(&pri);
	}

	GBT_RIGHT(buf,&right);

	if(right)
	{
	    page = btreePricacheRead(cache,right);
	    buf = page->buf;
	}
    }


    if(ajTableGetLength(strtable))
    {
        nids = ajTableToarrayKeys(strtable, (void***)&idarray);

        for(i=0; i < nids; i++)
	{
	    hitref = btreeIdentQueryHitref(idcache,idarray[i]);
            if(hitref)
                ajListPushAppend(hitlist,(void *)hitref);
	}
    }

    AJFREE(idarray);
    ajListFree(&list);
    ajListFree(&strlist);
    ajTableDel(&strtable);

    return;
}
ID btreePrileafFetch
TY static
MO ajindex
LB core
XX
DE Read all primary index leaf keywords into a list
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD page
PX
PN [3]
PA w list AjPList
PD list of AjPBtPri objects
PX
RT void
RD 
RX
//


static void btreePrileafFetch(AjPBtcache cache, AjPBtpage page, AjPList list)
{
    unsigned char *buf = NULL;
    AjPBtMem arrays    = NULL;
    ajulong *parray     = NULL;

    ajuint keylimit = 0;
    ajuint nkeys    = 0;
    
    ajuint i;
    
    /* ajDebug("In PrileafFetch\n"); */
    
    buf = page->buf;

    arrays = btreeAllocPriArray(cache);
    parray = arrays->parray;

    btreeGetPointers(cache,buf,&parray);

    GBT_NKEYS(buf,&nkeys);
    
    keylimit = nkeys+1;

    for(i=0;isecrootblock);
    root->dirty = BT_LOCK;
    root->lockfor = 1471;
   
    buf = root->buf;
    GBT_RIGHT(buf,&right);
    cache->slevel = (ajuint) right;
    GBT_NODETYPE(buf,&nodetype);
    
    arrays = btreeAllocSecArray(cache);
    parray = arrays->parray;

    btreeGetPointers(cache,buf,&parray);

    GBT_NODETYPE(buf,&nodetype);

    if(cache->slevel)
    {
	while(nodetype != BT_SECLEAF)
	{
	    btreeGetPointers(cache,buf,&parray);
	    page = btreeSeccacheRead(cache, parray[0]);
	    buf = page->buf;
	    GBT_NODETYPE(buf,&nodetype);
	    page->dirty = BT_CLEAN;
	}

	wild->secpagepos = parray[0];
	btreeGetPointers(cache,buf,&parray);
    }
    else
	wild->secpagepos = cache->secrootblock;
    


    GBT_NKEYS(buf,&nkeys);
	

    keylimit = nkeys+1;
    for(i=0;iidlist);

    ajListSort(wild->idlist, &ajStrVcmp);
    
    root->dirty = BT_CLEAN;
    
    btreeDeallocSecArray(cache,arrays);

    cache->secrootblock = 0L;

    return;
}
ID btreeKeywildNextList
TY static
MO ajindex
LB core
XX
DE Get next wadge of secondary index leaf IDs into a list after a successful
DE wildcard keyword search
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u wild AjPBtKeywild
PD wildcard keyword object
PX
RT AjBool
RD true if a wadge was successfully read
RX
//


static AjBool btreeKeywildNextList(AjPBtcache cache, AjPBtKeywild wild)
{
    AjPBtpage page = NULL;
    unsigned char *buf;
    ajulong right = 0L;
    ajuint nkeys = 0;
    ajuint keylimit = 0;
    ajuint i;
    
    AjPBtMem arrays = NULL;
    ajulong *parray = NULL;

    if(cache->secrootblock == wild->secpagepos)
	return ajFalse;

    
    page = btreeSeccacheRead(cache,wild->secpagepos);
    buf = page->buf;
    GBT_RIGHT(buf,&right);
    page->dirty = BT_CLEAN;

    if(!right)
	return ajFalse;

    page = btreeSeccacheRead(cache,right);
    wild->secpagepos = right;

    arrays = btreeAllocSecArray(cache);
    parray = arrays->parray;

    btreeGetPointers(cache,buf,&parray);
    GBT_NKEYS(buf,&nkeys);

    keylimit = nkeys + 1;
    
    for(i=0;iidlist);
    
    ajListSort(wild->idlist, &ajStrVcmp);
    
    btreeDeallocSecArray(cache,arrays);

    return ajTrue;
}
ID btreeReadAllSecLeaves
TY static
MO ajindex
LB core
XX
DE Read all the IDs from a secondary tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u list AjPList
PD list of IDs to return
PX
RT void
RD 
RX
//


static void btreeReadAllSecLeaves(AjPBtcache cache, AjPList list)
{
    AjPBtpage root = NULL;
    AjPBtpage page = NULL;
    ajulong right = 0L;
    ajuint nodetype = 0;
    ajuint nkeys = 0;
    ajuint keylimit = 0;
    
    ajuint i;
    
    AjPBtMem arrays = NULL;
    ajulong *parray = NULL;
    
    unsigned char *buf;
    
    ajulong secpagepos = 0L;
    

    root = btreeSeccacheRead(cache,cache->secrootblock);
    buf = root->buf;
    GBT_NODETYPE(buf,&nodetype);

    if(nodetype == BT_SECBUCKET)
    {
        btreeSecbucketIdlist(cache, root->pagepos, list);
        return;
    }

    root->dirty = BT_LOCK;
    root->lockfor = 1481;
    GBT_RIGHT(buf,&right);
    cache->slevel = (ajuint) right;

    arrays = btreeAllocSecArray(cache);
    parray = arrays->parray;

    if(cache->slevel)
    {
	while(nodetype != BT_SECLEAF)
	{
	    btreeGetPointers(cache,buf,&parray);
	    page = btreeSeccacheRead(cache,parray[0]);
	    buf = page->buf;
	    GBT_NODETYPE(buf,&nodetype);
	    page->dirty = BT_CLEAN;
	}

	secpagepos = parray[0];
    }
    else
	secpagepos = cache->secrootblock;


    btreeGetPointers(cache,buf,&parray);

    GBT_NKEYS(buf,&nkeys);
    keylimit = nkeys + 1;

    for(i=0;isorder)
    {
	page = btreeSeccacheRead(cache,secpagepos);
	buf  = page->buf;
	GBT_RIGHT(buf,&right);
	page->dirty = BT_CLEAN;
    }


    while(right && secpagepos != cache->secrootblock)
    {
	secpagepos = right;
	page = btreeSeccacheRead(cache,secpagepos);
	page->dirty = BT_LOCK;
        page->lockfor = 1482;
	buf = page->buf;

	btreeGetPointers(cache,buf,&parray);
	GBT_NKEYS(buf,&nkeys);
	keylimit = nkeys + 1;
	for(i=0; i < keylimit;++i)
            btreeSecbucketIdlist(cache,parray[i],list);

	GBT_RIGHT(buf,&right);
	page->dirty = BT_CLEAN;
    }


    root->dirty = BT_CLEAN;

    btreeDeallocSecArray(cache,arrays);

    return;
}
ID btreeStrDel
TY static
MO ajindex
LB core
XX
DE Deletes an AjPStr entry from a list
XX
PN [1]
PA r Pstr void**
PD Address of an AjPStr object
PX
PN [2]
PA r cl void*
PD Standard unused argument, usually NULL.
PX
RT void
RD 
RX
//


static void btreeStrDel(void** Pstr, void* cl)
{
    AjPStr str = NULL;

    (void) cl;				/* make it used */

    str = *((AjPStr *)Pstr);
    ajStrDel(&str);

    return;
}
ID btreeIdDelFromList
TY static
MO ajindex
LB core
XX
DE Deletes an AjPBtId entry from a list
XX
PN [1]
PA r pentry void**
PD Address of an AjPBtId object
PX
PN [2]
PA r cl void*
PD Standard unused argument, usually NULL.
PX
RT void
RD 
RX
//


static void btreeIdDelFromList(void** pentry, void* cl)
{
    AjPBtId id = NULL;

    (void) cl;				/* make it used */

    id = *((AjPBtId *)pentry);

    ajDebug("btreeIdDelFromList '%S'\n", id->id);
    ajBtreeIdDel(&id);

    return;
}
ID btreeHitDelFromList
TY static
MO ajindex
LB core
XX
DE Deletes an AjPBtHit entry from a list
XX
PN [1]
PA r pentry void**
PD Address of an AjPBtHit object
PX
PN [2]
PA r cl void*
PD Standard unused argument, usually NULL.
PX
RT void
RD 
RX
//


static void btreeHitDelFromList(void** pentry, void* cl)
{
    AjPBtHit hit = NULL;

    (void) cl;				/* make it used */

    hit = *((AjPBtHit *)pentry);

    ajBtreeHitDel(&hit);

    return;
}
ID btreeHitrefDelFromList
TY static
MO ajindex
LB core
XX
DE Deletes an AjPBtHitref entry from a list
XX
PN [1]
PA r pentry void**
PD Address of an AjPBtHitref object
PX
PN [2]
PA r cl void*
PD Standard unused argument, usually NULL.
PX
RT void
RD 
RX
//


static void btreeHitrefDelFromList(void** pentry, void* cl)
{
    AjPBtHitref hitref = NULL;

    (void) cl;				/* make it used */

    hitref = *((AjPBtHitref *)pentry);

    ajBtreeHitrefDel(&hitref);

    return;
}
ID btreeIdOffsetCompare
TY static
MO ajindex
LB core
XX
DE Comparison function for ajListUnique2
XX
PN [1]
PA r a const void*
PD ID 1
PX
PN [2]
PA r b const void*
PD ID 2
PX
RT ajint
RD 0 = bases match
RX
//


static ajint btreeIdOffsetCompare(const void *a, const void *b)
{
    ajlong val;

    val = (ajlong) ((*(AjPBtId const *)a)->offset -
                    (*(AjPBtId const *)b)->offset);

    if(!val)
      return 0;

    return (val < 0L) ? -1 : 1;
}
ID btreeHitOffsetCompare
TY static
MO ajindex
LB core
XX
DE Comparison function for ajListUnique2
XX
PN [1]
PA r a const void*
PD ID 1
PX
PN [2]
PA r b const void*
PD ID 2
PX
RT ajint
RD 0 = offsets match
RX
//


static ajint btreeHitOffsetCompare(const void *a, const void *b)
{
    ajlong val;

    val = (ajlong) ((*(AjPBtHit const *)a)->offset -
                    (*(AjPBtHit const *)b)->offset);

    if(!val)
      return 0;

    return (val < 0L) ? -1 : 1;
}
ID btreeHitrefOffsetCompare
TY static
MO ajindex
LB core
XX
DE Comparison function for ajListUnique2
XX
PN [1]
PA r a const void*
PD ID 1
PX
PN [2]
PA r b const void*
PD ID 2
PX
RT ajint
RD 0 = offsets match
RX
//


static ajint btreeHitrefOffsetCompare(const void *a, const void *b)
{
    ajlong val;

    val = (ajlong) ((*(AjPBtHitref const *)a)->offset -
                    (*(AjPBtHitref const *)b)->offset);

    if(!val)
      return 0;

    return (val < 0L) ? -1 : 1;
}
ID btreeHitDbnoCompare
TY static
MO ajindex
LB core
XX
DE Second comparison function for ajListUnique2
XX
PN [1]
PA r a const void*
PD ID 1
PX
PN [2]
PA r b const void*
PD ID 2
PX
RT ajint
RD 0 = values match
RX
//


static ajint btreeHitDbnoCompare(const void *a, const void *b)
{
    return (ajuint) (*(AjPBtHit const *)a)->dbno -
           (ajuint) (*(AjPBtHit const *)b)->dbno;
}
ID btreeHitrefDbnoCompare
TY static
MO ajindex
LB core
XX
DE Second comparison function for ajListUnique2
XX
PN [1]
PA r a const void*
PD ID 1
PX
PN [2]
PA r b const void*
PD ID 2
PX
RT ajint
RD 0 = values match
RX
//


static ajint btreeHitrefDbnoCompare(const void *a, const void *b)
{
    return (ajuint) (*(AjPBtHitref const *)a)->dbno -
           (ajuint) (*(AjPBtHitref const *)b)->dbno;
}
ID btreeIdDbnoCompare
TY static
MO ajindex
LB core
XX
DE Second comparison function for ajListUnique2
XX
PN [1]
PA r a const void*
PD ID 1
PX
PN [2]
PA r b const void*
PD ID 2
PX
RT ajint
RD 0 = values match
RX
//


static ajint btreeIdDbnoCompare(const void *a, const void *b)
{
    return (ajuint) (*(AjPBtId const *)a)->dbno -
           (ajuint) (*(AjPBtId const *)b)->dbno;
}
ID btreeAllocPriArray
TY static
MO ajindex
LB core
XX
DE Allocate karray and parray arrays for a primary key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT AjPBtMem
RD memory node
RX
//


static AjPBtMem btreeAllocPriArray(AjPBtcache cache)
{
    AjPBtMem node = NULL;
    ajuint i;
    ajuint limit;
    AjPBtMem p = NULL;
    ajuint klen;

    limit = cache->porder;

    if(!cache->bmem)
    {
        statCountAllocPriArrayNew++;

        AJNEW0(node);
        cache->bmem = node;
        cache->tmem = node;
        node->prev = NULL;
        node->next = NULL;
        node->used = ajTrue;
        AJCNEW0(node->karray,limit);
        AJCNEW0(node->parray,limit);
        AJCNEW0(node->overflows,limit);

        klen = cache->keylimit+1;
        for(i=0;ikarray[i] = ajStrNewRes(klen);

        return node;
    }

    if(!cache->bmem->used)
    {
        statCountAllocPriArrayReuse++;

        cache->bmem->used = ajTrue;

        if(cache->bmem->next)
        {
            p = cache->bmem->next;

            cache->tmem->next = cache->bmem;
            cache->bmem->next = NULL;

            cache->bmem->prev = cache->tmem;

            cache->tmem = cache->bmem;

            cache->bmem = p;
            cache->bmem->prev = NULL;

	    memset(cache->tmem->parray,0,sizeof(ajulong)*limit);
	    
	    return cache->tmem;
        }

	memset(cache->bmem->parray,0,sizeof(ajulong)*limit);
	
        return cache->bmem;
    }


    statCountAllocPriArrayNew++;

    AJNEW0(node);
    node->used = ajTrue;
    node->next = NULL;
    node->prev = cache->tmem;
    cache->tmem->next = node;
    cache->tmem = node;

    AJCNEW0(node->karray,limit);
    AJCNEW0(node->parray,limit);
    AJCNEW0(node->overflows,limit);

    klen = cache->keylimit+1;
    for(i=0;ikarray[i] = ajStrNewRes(klen);

    return node;
}
ID btreeDeallocPriArray
TY static
MO ajindex
LB core
XX
DE Deallocate karray and parray arrays for a primary key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u node AjPBtMem
PD node
PX
RT void
RD 
RX
//


static void btreeDeallocPriArray(AjPBtcache cache, AjPBtMem node)
{
    statCountAllocPriArrayDel++;

    node->used = ajFalse;

    if(!node->prev)
        return;

    node->prev->next = node->next;

    if(node->next)
        node->next->prev = node->prev;
    else
        cache->tmem = node->prev;

    node->next = cache->bmem;
    cache->bmem->prev = node;
    cache->bmem = node;
    node->prev = NULL;

    return;
}
ID btreeAllocSecArray
TY static
MO ajindex
LB core
XX
DE Allocate karray and parray arrays for a primary key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT AjPBtMem
RD memory node
RX
//


static AjPBtMem btreeAllocSecArray(AjPBtcache cache)
{
    AjPBtMem node = NULL;
    ajuint i;
    ajuint limit;
    AjPBtMem p = NULL;
    ajuint klen;

    limit = cache->sorder;

    if(!cache->bsmem)
    {
        statCountAllocSecArrayNew++;

        AJNEW0(node);
        cache->bsmem = node;
        cache->tsmem = node;
        node->prev = NULL;
        node->next = NULL;
        node->used = ajTrue;
        AJCNEW0(node->karray,limit);
        AJCNEW0(node->parray,limit);
        AJCNEW0(node->overflows,limit);

        klen = cache->idlimit + 1;
        for(i=0;ikarray[i] = ajStrNewRes(klen);

        return node;
    }

    if(!cache->bsmem->used)
    {
        statCountAllocSecArrayReuse++;

        cache->bsmem->used = ajTrue;

        if(cache->bsmem->next)
        {
            p = cache->bsmem->next;

            cache->tsmem->next = cache->bsmem;
            cache->bsmem->next = NULL;

            cache->bsmem->prev = cache->tsmem;

            cache->tsmem = cache->bsmem;

            cache->bsmem = p;
            cache->bsmem->prev = NULL;

	    memset(cache->tsmem->parray,0,sizeof(ajulong)*limit);

	    return cache->tsmem;
        }

	memset(cache->bsmem->parray,0,sizeof(ajulong)*limit);

        return cache->bsmem;
    }

    statCountAllocSecArrayNew++;

    AJNEW0(node);
    node->used = ajTrue;
    node->next = NULL;
    node->prev = cache->tsmem;
    cache->tsmem->next = node;
    cache->tsmem = node;

    AJCNEW0(node->karray,limit);
    AJCNEW0(node->parray,limit);
    AJCNEW0(node->overflows,limit);

    klen = cache->idlimit + 1;
    for(i=0;ikarray[i] = ajStrNewRes(klen);

    return node;
}
ID btreeDeallocSecArray
TY static
MO ajindex
LB core
XX
DE Deallocate karray and parray arrays for a primary key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u node AjPBtMem
PD node
PX
RT void
RD 
RX
//


static void btreeDeallocSecArray(AjPBtcache cache, AjPBtMem node)
{
    node->used = ajFalse;

    if(!node->prev)
        return;

    node->prev->next = node->next;

    if(node->next)
        node->next->prev = node->prev;
    else
        cache->tsmem = node->prev;

    node->next = cache->bsmem;
    cache->bsmem->prev = node;
    cache->bsmem = node;
    node->prev = NULL;

    return;
}
ID btreeIdentFind
TY static
MO ajindex
LB core
XX
DE Find the node that should contain a new key for insertion
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const AjPStr
PD key to search for
PX
RT AjPBtpage
RD leaf node where item should be inserted
RX
//


static AjPBtpage btreeIdentFind(AjPBtcache cache, const AjPStr key)
{
    AjPBtpage root = NULL;
    AjPBtpage ret  = NULL;

    /* ajDebug("In btreeIdentFind\n"); */

    /* The root node should always be in the cache (BT_LOCKed) */
    root = btreePricacheLocate(cache,0L);

    if(!root)
	ajFatal("The master root cache page has been unlocked\n");
    
    if(!cache->plevel)
	return root;
    
    ret = btreePrimaryFindInode(cache,root,key);
    
    return ret;
}
ID btreeIdentInsertShift
TY static
MO ajindex
LB core
XX
DE Rebalance buckets on insertion of an identifier
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u retpage AjPBtpage*
PD page
PX
PN [3]
PA r key const AjPStr
PD key
PX
RT ajulong
RD bucket block or 0L if shift not possible
RX
//


static ajulong btreeIdentInsertShift(AjPBtcache cache, AjPBtpage *retpage,
                                     const AjPStr key)
{
    unsigned char *tbuf = NULL;
    unsigned char *pbuf = NULL;
    unsigned char *sbuf = NULL;

    AjPBtpage ppage = NULL;
    AjPBtpage spage = NULL;
    AjPBtpage tpage = NULL;

    ajuint tkeys = 0;
    ajuint pkeys = 0;
    ajuint skeys = 0;
    ajuint order = 0;
    
    ajuint i;
    ajuint n;
    ajint ii;

    ajulong parent  = 0L;
    ajulong blockno = 0L;
    
    AjPStr *kTarray = NULL;
    AjPStr *kParray = NULL;
    AjPStr *kSarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pParray = NULL;
    ajulong *pSarray = NULL;

    AjPStr *karray = NULL;
    ajulong *parray = NULL;

    ajuint ppos    = 0;
    ajuint pkeypos = 0;
    ajuint minsize = 0;

    AjPBtMem arrays1 = NULL;
    AjPBtMem arrays2 = NULL;
    AjPBtMem arrays3 = NULL;
    
    tpage = *retpage;

    tbuf = tpage->buf;

    GBT_PREV(tbuf,&parent);
    GBT_NKEYS(tbuf,&tkeys);

#if AJINDEX_DEBUG
    ajDebug("btreeIdentInsertShift %Lu parent: %Lu '%S'\n",
            (*retpage)->pagepos, parent, key);
#endif

    order = cache->porder;
    minsize = order / 2;

    if(order % 2)
	++minsize;

    if(tkeys <= minsize)
	return 0L;

    ppage = btreePricacheRead(cache,parent);
    
    pbuf = ppage->buf;
    GBT_NKEYS(pbuf,&pkeys);
    

    arrays1 = btreeAllocPriArray(cache);
    kParray = arrays1->karray;
    pParray = arrays1->parray;

    arrays2 = btreeAllocPriArray(cache);
    kSarray = arrays2->karray;
    pSarray = arrays2->parray;

    arrays3 = btreeAllocPriArray(cache);
    kTarray = arrays3->karray;
    pTarray = arrays3->parray;
    

    btreeGetKeys(cache,pbuf,&kParray,&pParray);

    i=0;

    while(i!=pkeys && MAJSTRCMPS(key,kParray[i])>=0)
	++i;

    pkeypos = i;
    
    if(i==pkeys)
    {
	if(MAJSTRCMPS(key,kParray[i-1])<0)
	    ppos = i-1;
	else
	    ppos = i;
    }
    else
	ppos = i;

    
#if AJINDEX_DEBUG
    ajDebug("    InsertShift tpage: %Lu ppage: %Lu ppos: %u i: %u "
            "skeys: %u order: %u\n",
            tpage->pagepos, ppage->pagepos, ppos, i, skeys, order);
#endif

    if(ppos) /* There is another leaf to the left */
    {
	spage = btreePricacheRead(cache,pParray[ppos-1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
    }

    if(i && skeys != order-1) /* There is space in the left leaf */
    {

#if AJINDEX_DEBUG
    ajDebug("    InsertShift left leaf\n");
#endif

	/* ajDebug("Left shift\n"); */
	btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
	if(skeys)
	    btreeGetKeys(cache,sbuf,&kSarray,&pSarray);

	i = 0;

	while(pParray[i] != tpage->pagepos)
	    ++i;
	--i;

	pkeypos = i;

	ajStrAssignS(&kSarray[skeys],kParray[pkeypos]);
	pSarray[skeys+1] = pTarray[0];
	++skeys;
	--tkeys;
	ajStrAssignS(&kParray[pkeypos],kTarray[0]);
	for(i=0;ipagepos)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1501;
        }

	i = 0;

	while(i!=pkeys && MAJSTRCMPS(key,kParray[i])>=0)
	    ++i;

	if(i==pkeys)
	{
	    if(MAJSTRCMPS(key,kParray[i-1])<0)
		blockno = pParray[i-1];
	    else
		blockno = pParray[i];
	}
	else
	    blockno = pParray[i];

	if(blockno == spage->pagepos)
	{
	    *retpage = spage;
	    karray = kSarray;
	    parray = pSarray;
	    n = skeys;
	}
	else
	{
	    karray = kTarray;
	    parray = pTarray;
	    n = tkeys;
	}
	

	i = 0;

	while(i!=n && MAJSTRCMPS(key,karray[i])>=0)
	    ++i;

	if(i==n)
	{
	    if(MAJSTRCMPS(key,karray[i-1])<0)
		blockno = parray[i-1];
	    else
		blockno = parray[i];
	}
	else
	    blockno = parray[i];

	btreeDeallocPriArray(cache,arrays1);
	btreeDeallocPriArray(cache,arrays2);
	btreeDeallocPriArray(cache,arrays3);

	/* ajDebug("... returns blockno (a) %Lu\n",blockno); */

	return blockno;
    }
    

    if(ppos != pkeys)	/* There is a right node */
    {
	spage = btreePricacheRead(cache,pParray[ppos+1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);

#if AJINDEX_DEBUG
        ajDebug("    InsertShift right, new spage: %Lu skeys: %u\n",
                spage->pagepos, skeys);
#endif

    }


#if AJINDEX_DEBUG
    ajDebug("    InsertShift test again tpage: %Lu ppage: %Lu spage: %Lu "
            "ppos: %u i: %u skeys: %u order: %u\n",
            tpage->pagepos, ppage->pagepos, spage->pagepos,
            ppos, i, skeys, order);
#endif

    /* Space in the right leaf */
    if(ppos != pkeys && skeys != order-1)
    {

#if AJINDEX_DEBUG
        ajDebug("    InsertShift right leaf tpage: %Lu\n", tpage->pagepos);
#endif

	/* ajDebug("Right shift\n"); */
	btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
	btreeGetKeys(cache,sbuf,&kSarray,&pSarray);

	i = 0;

	while(pParray[i] != tpage->pagepos) /* fails here */
        {

#if AJINDEX_DEBUG
            ajDebug("    InsertShift right pParray[%u] %Lu\n",
                    i, pParray[i]);
#endif
	    ++i;
        }
        
	pkeypos = i;
	
	pSarray[skeys+1] = pSarray[skeys];

	for(ii=skeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kSarray[ii+1],kSarray[ii]);
	    pSarray[ii+1] = pSarray[ii];
	}

	ajStrAssignS(&kSarray[0],kParray[pkeypos]);
	pSarray[0] = pTarray[tkeys];
	ajStrAssignS(&kParray[pkeypos],kTarray[tkeys-1]);
	++skeys;
	--tkeys;
	pTarray[tkeys+1] = 0L;
	
	btreeWriteNode(cache,spage,kSarray,pSarray,skeys);
	btreeWriteNode(cache,tpage,kTarray,pTarray,tkeys);
	btreeWriteNode(cache,ppage,kParray,pParray,pkeys);

	if(!ppage->pagepos)
	{
            ppage->dirty = BT_LOCK;
            ppage->lockfor = 1502;
        }

	i = 0;

	while(i!=pkeys && MAJSTRCMPS(key,kParray[i])>=0)
	    ++i;

	if(i==pkeys)
	{
	    if(MAJSTRCMPS(key,kParray[i-1])<0)
		blockno = pParray[i-1];
	    else
		blockno = pParray[i];
	}
	else
	    blockno = pParray[i];

	if(blockno == spage->pagepos)
	{
	    *retpage = spage;
	    karray = kSarray;
	    parray = pSarray;
	    n = skeys;
	}
	else
	{
	    karray = kTarray;
	    parray = pTarray;
	    n = tkeys;
	}
	
	i = 0;

	while(i!=n && MAJSTRCMPS(key,karray[i])>=0)
	    ++i;

	if(i==n)
	{
	    if(MAJSTRCMPS(key,karray[i-1])<0)
		blockno = parray[i-1];
	    else
		blockno = parray[i];
	}
	else
	    blockno = parray[i];

	btreeDeallocPriArray(cache,arrays1);
	btreeDeallocPriArray(cache,arrays2);
	btreeDeallocPriArray(cache,arrays3);

	/* ajDebug("... returns blockno (b) %Lu\n",blockno); */
	
	return blockno;
    }


    btreeDeallocPriArray(cache,arrays1);
    btreeDeallocPriArray(cache,arrays2);
    btreeDeallocPriArray(cache,arrays3);


#if AJINDEX_DEBUG
    ajDebug("    InsertShift returning zero ...\n");
#endif

    /* ajDebug("... returns 0L\n"); */

    return 0L;
}
ID btreeIdSplitleaf
TY static
MO ajindex
LB core
XX
DE Split a leaf and propagate up if necessary
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u spage AjPBtpage
PD page
PX
RT AjPBtpage
RD pointer to a parent page
RX
//


static AjPBtpage btreeIdSplitleaf(AjPBtcache cache, AjPBtpage spage)
{
    ajuint nkeys     = 0;
    ajuint order     = 0;
    ajuint totalkeys = 0;
    ajuint keylimit  = 0;
    ajuint nodetype  = 0;

    ajuint rootnodetype  = 0;
    
    ajuint i;
    ajuint j;
    ajuint iref;

    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage page  = NULL;
    
    AjPStr mediankey  = NULL;
    ajulong mediangtr  = 0L;
    ajulong medianless = 0L;
    

    AjPBtId bid = NULL;
    AjPBtId cid = NULL;

    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;

    AjPList idlist = NULL;

    AjPIdbucket cbucket  = NULL;
    
    ajulong *parray = NULL;
    AjPStr *newkarray  = NULL;
    ajulong *newparray  = NULL;
    AjPBtMem arrays     = NULL;
    AjPBtMem newarrays = NULL;
    
    ajuint lno    = 0;
    ajuint rno    = 0;

    ajuint lbucketlimit   = 0;
    ajuint lmaxnperbucket = 0;
    ajuint rbucketlimit   = 0;
    ajuint rmaxnperbucket = 0;
    ajuint count         = 0;
    
    ajulong lblockno = 0L;
    ajulong rblockno = 0L;
    ajulong prev     = 0L;
    ajulong overflow = 0L;
    ajulong prevsave = 0L;

    ajulong zero = 0L;
    ajulong join = 0L;
    
    ajulong lv = 0L;
    ajuint  v  = 0;
    
    ajuint iold=0;
    ajuint refskip = cache->refcount*BT_EXTRA;

#if AJINDEX_DEBUG
    ajDebug("btreeIdSplitleaf %Lu\n", spage->pagepos);
#endif
    ++statCallIdSplitleaf;

    /*ajDebug("In btreeIdSplitleaf page:%Lu\n", spage->pagepos);*/

    order = cache->porder;

    arrays    = btreeAllocPriArray(cache);
    parray    = arrays->parray;

    newarrays    = btreeAllocPriArray(cache);
    newkarray    = newarrays->karray;
    newparray    = newarrays->parray;

    buf = spage->buf;
    lbuf = buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&rootnodetype);

    /*ajDebug("btreeIdSplitleaf start page %Lu nkeys:%u\n",
      spage->pagepos,  nkeys);*/

    for(i=nkeys+1; ipagepos, cache->totsize);
#endif
	lblockno = cache->totsize;
	lpage = btreePricacheNodenew(cache);
	lbuf = lpage->buf;
	lv = prev;
	SBT_PREV(lbuf,lv);
    }
    else
    {
	lblockno = spage->pagepos;
	lpage = spage;
#if AJINDEX_DEBUG
        ajDebug("    keeping lpage %Lu\n", lpage->pagepos);
#endif
    }

    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1521;


#if AJINDEX_DEBUG
        ajDebug("    new right node %Lu\n",
                cache->totsize);
#endif

    rblockno = cache->totsize;
    rpage = btreePricacheNodenew(cache);
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1522;
    rbuf = rpage->buf;

    if(rootnodetype == BT_ROOT)
    {
	lv = zero;
	SBT_RIGHT(rbuf,lv);
	lv = zero;
	SBT_LEFT(lbuf,lv);
    }
    else
    {
	GBT_RIGHT(lbuf,&join);
	lv = join;
	SBT_RIGHT(rbuf,lv);
    }

    lv = lblockno;
    SBT_LEFT(rbuf,lv);
    lv = rblockno;
    SBT_RIGHT(lbuf,lv);


    btreeGetPointers(cache,buf,&parray);

    keylimit = nkeys+1;

    idlist = ajListNew();

    for(i=0;ipnperbucket,
                         &lbucketlimit,&lmaxnperbucket,&lno,
                         &rbucketlimit,&rmaxnperbucket,&rno);

    cbucket = btreeIdbucketNew(cache->pnperbucket, cache->refcount);

    count = 0;
    iold=0;

    for(i=0;iNentries = 0;

	for(j=0;jIds[j];
	    ajStrAssignS(&cid->id,bid->id);
	    cid->dbno = bid->dbno;
	    cid->dups = bid->dups;
	    cid->offset = bid->offset;

            if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                    cid->refoffsets[iref] = bid->refoffsets[iref];
	    }

	    cbucket->keylen[j] = BT_BUCKIDLEN(bid->id) + refskip;
	    ++count;
	    ++cbucket->Nentries;
	    ajBtreeIdDel(&bid);
	}

	ajListPeek(idlist,(void **)&bid);
	
	ajStrAssignS(&newkarray[i],bid->id);

	if(iold < keylimit)
            newparray[i] = parray[iold++];
        else
	    newparray[i] = cache->totsize;
	btreeWriteIdbucket(cache,cbucket,newparray[i]);
    }

    cbucket->Nentries = 0;

    j = 0;

    while(count != lno)
    {
	ajListPop(idlist,(void **)&bid);
	cid = cbucket->Ids[j];
	++j;
	++count;

	ajStrAssignS(&cid->id,bid->id);
	cid->dbno = bid->dbno;
	cid->dups = bid->dups;
	cid->offset = bid->offset;

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
                cid->refoffsets[iref] = bid->refoffsets[iref];
	}

	++cbucket->Nentries;
	ajBtreeIdDel(&bid);
    }

    if(iold < keylimit)
        newparray[i] = parray[iold++];
    else
        newparray[i] = cache->totsize;
    btreeWriteIdbucket(cache,cbucket,newparray[i]);

    nkeys = lbucketlimit;

    nodetype = BT_LEAF;
    v = nodetype;
    SBT_NODETYPE(lbuf,v);
    lpage->dirty = BT_DIRTY;

    GBT_PREV(lbuf,&prevsave);
#if AJINDEX_DEBUG
    ajDebug("    lpage %Lu prevsave %Lu\n",
            lpage->pagepos, prevsave);
#endif
    btreeWriteNode(cache,lpage,newkarray,newparray,nkeys);

    ajListPeek(idlist,(void **)&bid);
    mediankey = ajStrNewS(bid->id);

    for(i=0;iNentries = 0;
	for(j=0;jIds[j];
	    ajStrAssignS(&cid->id,bid->id);
	    cid->dbno = bid->dbno;
	    cid->dups = bid->dups;
	    cid->offset = bid->offset;

            if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                    cid->refoffsets[iref] = bid->refoffsets[iref];
	    }

	    cbucket->keylen[j] = BT_BUCKIDLEN(bid->id) + refskip;
	    ++cbucket->Nentries;
	    ajBtreeIdDel(&bid);
	}

	ajListPeek(idlist,(void **)&bid);
	ajStrAssignS(&newkarray[i],bid->id);

	if(iold < keylimit)
            newparray[i] = parray[iold++];
        else
            newparray[i] = cache->totsize;
	btreeWriteIdbucket(cache,cbucket,newparray[i]);
    }

    cbucket->Nentries = 0;

    j = 0;

    while(ajListPop(idlist,(void**)&bid))
    {
	cid = cbucket->Ids[j];
	++j;

	ajStrAssignS(&cid->id,bid->id);
	cid->dbno = bid->dbno;
	cid->dups = bid->dups;
	cid->offset = bid->offset;

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
                cid->refoffsets[iref] = bid->refoffsets[iref];
	}

	++cbucket->Nentries;
	ajBtreeIdDel(&bid);
    }
    
    if(iold < keylimit)
        newparray[i] = parray[iold++];
    else
        newparray[i] = cache->totsize;
    btreeWriteIdbucket(cache,cbucket,newparray[i]);

    nkeys = rbucketlimit;

    nodetype = BT_LEAF;
    v = nodetype;
    SBT_NODETYPE(rbuf,v);
    lv = prevsave;
    SBT_PREV(rbuf,lv);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);

#if AJINDEX_DEBUG
    ajDebug("    right %Lu prev: %Lu\n",
            rpage->pagepos, prevsave);
#endif
    btreeWriteNode(cache,rpage,newkarray,newparray,nkeys);
    rpage->dirty = BT_DIRTY;

    btreeIdbucketDel(&cbucket);
    ajListFree(&idlist);



    medianless = lblockno;
    mediangtr  = rblockno;

    btreeDeallocPriArray(cache,arrays);
    btreeDeallocPriArray(cache,newarrays);

    if(rootnodetype == BT_ROOT)
    {
#if AJINDEX_DEBUG
        ajDebug("    rootnode new node for mediankey '%S' %Lu\n",
                mediankey, spage->pagepos);
#endif
	btreeWriteNodeSingle(cache,spage,mediankey,lblockno,rblockno);	
	spage->dirty = BT_LOCK;
        spage->lockfor = 1523;

	ajStrDel(&mediankey);
	++cache->plevel;

	return spage;
    }


    page = btreePricacheRead(cache,prevsave);
    btreeIdInsertKey(cache,page,mediankey,medianless,mediangtr);
    ajStrDel(&mediankey);

    page = btreePricacheRead(cache,prevsave);

    return page;
}
ID btreeIdInsertKey
TY static
MO ajindex
LB core
XX
DE Insert an identifier into a potentially full node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD original page
PX
PN [3]
PA r key const AjPStr
PD key to insert
PX
PN [4]
PA r less ajulong
PD less-than pointer
PX
PN [5]
PA r greater ajulong
PD greater-than pointer
PX
RT void
RD 
RX
//


static void btreeIdInsertKey(AjPBtcache cache, AjPBtpage page,
                             const AjPStr key, ajulong less, ajulong greater)
{
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;
    unsigned char *tbuf = NULL;
    AjPStr *karray      = NULL;
    ajulong *parray      = NULL;
    AjPStr *tkarray     = NULL;
    ajulong *tparray     = NULL;

    AjPBtMem arrays1    = NULL;
    AjPBtMem arrays2    = NULL;

    ajuint nkeys    = 0;
    ajuint order    = 0;
    ajuint keypos   = 0;
    ajuint rkeyno   = 0;
    
    ajuint i = 0;
    ajuint n = 0;
    
    ajuint nodetype  = 0;
    AjPBtpage ipage = NULL;
    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage tpage = NULL;

    ajulong blockno  = 0L;
    ajulong rblockno = 0L;
    ajulong lblockno = 0L;
    ajulong ibn      = 0L;

    AjPStr mediankey  = NULL;
    ajulong medianless = 0L;
    ajulong mediangtr  = 0L;
    ajulong overflow   = 0L;
    ajulong prev       = 0L;
    ajuint totlen     = 0;
    
    ajulong lv = 0L;
    ajuint  v  = 0;

    if(!btreeNodeIsFull(cache,page))
    {
	btreePrimaryInsertNonfull(cache,page,key,less,greater);

	return;
    }
    
    order = cache->porder;
    lbuf = page->buf;
    GBT_NODETYPE(lbuf,&nodetype);
    page->dirty = BT_LOCK;
    page->lockfor = 1531;

    if(nodetype == BT_ROOT)
    {
	arrays1   = btreeAllocPriArray(cache);
	karray    = arrays1->karray;
	parray    = arrays1->parray;

	btreeIdSplitroot(cache);

	if(page->pagepos)
	    page->dirty = BT_DIRTY;

	btreeGetKeys(cache,lbuf,&karray,&parray);

	if(MAJSTRCMPS(key,karray[0])<0)
	    blockno = parray[0];
	else
	    blockno = parray[1];

	ipage = btreePricacheRead(cache,blockno);
	btreePrimaryInsertNonfull(cache,ipage,key,less,greater);

	btreeDeallocPriArray(cache,arrays1);

	return;
    }


    arrays1   = btreeAllocPriArray(cache);
    karray    = arrays1->karray;
    parray    = arrays1->parray;

    arrays2   = btreeAllocPriArray(cache);
    tkarray   = arrays2->karray;
    tparray   = arrays2->parray;

    mediankey = ajStrNew();
    
    lpage = page;
    lbuf = lpage->buf;
    
    btreeGetKeys(cache,lbuf,&karray,&parray);

    GBT_BLOCKNUMBER(lbuf,&lblockno);

    rblockno = cache->totsize;
    rpage = btreePricacheNodenew(cache);
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1532;
    rbuf = rpage->buf;


    GBT_PREV(lbuf,&prev);
    lv = prev;
    SBT_PREV(rbuf,lv);

    nkeys = order - 1;
    keypos = nkeys / 2;

    if(!(nkeys % 2))
	--keypos;

    ajStrAssignS(&mediankey,karray[keypos]);
    medianless = lblockno;
    mediangtr  = rblockno;


    GBT_NODETYPE(lbuf,&nodetype);
    v = nodetype;
    SBT_NODETYPE(rbuf,v);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);


    totlen = 0;

    for(i=0;idirty = BT_LOCK;
    lpage->lockfor = 1533;


    for(i=0;ibuf;
	lv = lblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }


    totlen = 0;

    for(i=keypos+1;idirty = BT_DIRTY;

    btreeWriteNode(cache,rpage,tkarray,tparray,rkeyno);
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1534;

    for(i=0;ibuf;
	lv = rblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }


    ibn = rblockno;

    if(MAJSTRCMPS(key,mediankey)<0)
	ibn = lblockno;

    lpage->dirty = BT_DIRTY;
    rpage->dirty = BT_DIRTY;

    ipage = btreePricacheRead(cache,ibn);
    
    btreePrimaryInsertNonfull(cache,ipage,key,less,greater);


    btreeDeallocPriArray(cache,arrays1);
    btreeDeallocPriArray(cache,arrays2);

    ipage = btreePricacheRead(cache,prev);

    btreeIdInsertKey(cache,ipage,mediankey,medianless,mediangtr);
    ajStrDel(&mediankey);

    return;
}
ID btreeIdSplitroot
TY static
MO ajindex
LB core
XX
DE Split the root node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT void
RD 
RX
//


static void btreeIdSplitroot(AjPBtcache cache)
{
    AjPBtpage rootpage = NULL;
    AjPBtpage rpage    = NULL;
    AjPBtpage lpage    = NULL;
    AjPBtpage tpage    = NULL;

    AjPStr *karray     = NULL;
    AjPStr *tkarray    = NULL;
    ajulong *parray     = NULL;
    ajulong *tparray    = NULL;
    AjPBtMem arrays1   = NULL;
    AjPBtMem arrays2   = NULL;

    ajuint order     = 0;
    ajuint nkeys     = 0;
    ajuint keypos    = 0;
    
    ajulong rblockno = 0L;
    ajulong lblockno = 0L;
    
    AjPStr key = NULL;
    ajuint  i;
    ajuint  j;

    unsigned char *rootbuf = NULL;
    unsigned char *rbuf    = NULL;
    unsigned char *lbuf    = NULL;
    unsigned char *tbuf    = NULL;
    
    ajuint nodetype  = 0;
    ajulong overflow = 0L;
    ajulong zero     = 0L;
    ajuint rkeyno    = 0;

    ajulong lv = 0L;
    ajuint  v  = 0;
    
    
#if AJINDEX_DEBUG
    ajDebug("btreeIdSplitroot zero\n");
#endif
    ++statCallIdSplitroot;

    /* ajDebug("In btreeIdSplitroot\n"); */

    order = cache->porder;

    arrays1   = btreeAllocPriArray(cache);
    karray    = arrays1->karray;
    parray    = arrays1->parray;

    arrays2   = btreeAllocPriArray(cache);
    tkarray   = arrays2->karray;
    tparray   = arrays2->parray;

    key = ajStrNew();

    rootpage = btreePricacheLocate(cache,0L);

    if(!rootpage)
	ajFatal("Root page has been unlocked 1");
    
    rootbuf = rootpage->buf;
    nkeys   = order - 1;
    keypos  = nkeys / 2;

    if(!(nkeys % 2))
	--keypos;


    rblockno = cache->totsize;
    rpage = btreePricacheNodenew(cache);
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1541;

    lblockno = cache->totsize;
    lpage = btreePricacheNodenew(cache);

    if(!cache->plevel)
    {
	lv = zero;
	SBT_LEFT(lpage->buf,lv);
	lv = rblockno;
	SBT_RIGHT(lpage->buf,lv);
	lv = lblockno;
	SBT_LEFT(rpage->buf,lv);
	lv = zero;
	SBT_RIGHT(rpage->buf,lv);
    }

    btreeGetKeys(cache,rootbuf,&karray,&parray);

    /* write new root node */

    btreeWriteNodeSingle(cache,rootpage,karray[keypos],lblockno,rblockno);
    rootpage->dirty = BT_LOCK;
    rootpage->lockfor = 1542;

    rbuf = rpage->buf;
    lbuf = lpage->buf;
    
    if(cache->plevel)
	nodetype = BT_INTERNAL;
    else
	nodetype = BT_LEAF;

    v = nodetype;
    SBT_NODETYPE(rbuf,v);
    v = nodetype;
    SBT_NODETYPE(lbuf,v);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);
    lv = overflow;
    SBT_PREV(rbuf,lv);
    lv = overflow;
    SBT_OVERFLOW(lbuf,lv);
    lv = overflow;
    SBT_PREV(lbuf,lv);

    for(i=0;ibuf;
	lv = lblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
#if AJINDEX_DEBUG
        ajDebug("    new lpage %Lu prev %Lu\n",
                tpage->pagepos, lblockno);
#endif
    }

    j=0;

    for(i=keypos+1;idirty = BT_DIRTY;

    btreeWriteNode(cache,rpage,tkarray,tparray,rkeyno);

    for(i=0;i<=rkeyno;++i)
    {
	tpage = btreePricacheRead(cache,tparray[i]);
	tbuf = tpage->buf;
	lv = rblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
#if AJINDEX_DEBUG
        ajDebug("    new rpage %Lu prev %Lu\n",
                tpage->pagepos, rblockno);
#endif
    }


    btreeDeallocPriArray(cache,arrays1);
    btreeDeallocPriArray(cache,arrays2);

    ++cache->plevel;

    ajStrDel(&key);
    
    return;
}
ID ajBtreeIdentIndex
TY public
MO ajindex
LB core
XX
DE Insert an ID structure into the tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u id AjPBtId
PD Id object
PX
RT void
RD pointer to a page
RX
//


void ajBtreeIdentIndex(AjPBtcache cache, AjPBtId id)
{
    AjPBtpage spage   = NULL;
    ajulong lblockno = 0L;
    ajulong rblockno = 0L;
    ajulong blockno  = 0L;
    ajulong shift    = 0L;

    ajuint nkeys = 0;

    ajuint nodetype = 0;
    
    AjPBtId curid = NULL;
    
    ajuint n;
    ajuint ientry;

    unsigned char *buf = NULL;

#if AJINDEX_DEBUG
    ajDebug("btreeIdentIndex '%S'\n", id->id);
#endif

    /* ajDebug("In ajBtreeIdentIndex\n"); */

    ajStrFmtQuery(&id->id);

    if(!MAJSTRGETLEN(id->id))
	return;

    if(MAJSTRGETLEN(id->id) > cache->keylimit)
        ajStrTruncateLen(&id->id, cache->keylimit);

    spage = btreeIdentFind(cache,id->id);
    buf = spage->buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&nodetype);
    
    if(!nkeys)
    {
	lblockno = cache->totsize;
	btreeWriteIdbucketEmpty(cache,lblockno);

	rblockno = cache->totsize;
	btreeWriteIdbucketEmpty(cache,rblockno);	

	btreeWriteNodeSingle(cache,spage,id->id,lblockno,rblockno);

	btreeIdbucketAdd(cache,rblockno,id);
        ++cache->countunique;
        ++cache->countall;

	return;
    }


    /* Search to see whether entry exists */
    
    blockno = btreeGetBlockS(cache,buf,id->id);

    curid = btreeIdbucketFindDupId(cache,blockno,id->id,&ientry);

    if(curid)
    {
        ++cache->countall;

	btreeIdentDupInsert(cache,id,curid);
        btreeWriteIdbucketId(cache,blockno,curid,ientry);
        ajBtreeIdDel(&curid);

	return;
    }

    if(nodetype != BT_ROOT)
	if((shift = btreeIdentInsertShift(cache,&spage,id->id)))
	    blockno = shift;

    buf = spage->buf;

    n = btreeIdbucketCount(cache,blockno);

    if(n == cache->pnperbucket)
    {
	if(btreeIdbucketsReorder(cache,spage))
	{
            blockno = btreeGetBlockS(cache,buf,id->id);
	}
	else
	{
	    btreeIdSplitleaf(cache,spage);
	    spage  = btreeIdentFind(cache,id->id);
	    buf = spage->buf;

            blockno = btreeGetBlockS(cache,buf,id->id);
	}
    }


    btreeIdbucketAdd(cache,blockno,id);

    ++cache->countunique;
    ++cache->countall;

    return;
}
ID btreeIdentDupInsert
TY static
MO ajindex
LB core
XX
DE Insert a known duplicate ID structure into the tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r newid const AjPBtId
PD New ID object
PX
PN [3]
PA u curid AjPBtId
PD Id object from current index
PX
RT void
RD 
RX
//


static void btreeIdentDupInsert(AjPBtcache cache, const AjPBtId newid,
                                AjPBtId curid)
{
    AjPBtpage page;
    AjPBtpage rpage;
    ajulong secrootpage = 0L;
    unsigned char *buf;
    ajulong right = 0L;
    ajulong refoffsets[1000];
    AjPBtNumId num = NULL;
    AjOBtNumId numobj = {0L, refoffsets, 0, 0};

    ajuint iref;

    /* ajDebug("In btreeIdentDupInsert\n"); */

    if(!curid->dups)
    {
        /* first duplicate: could use SIMPLEBUCKET */

	curid->dups = 1;
	num = &numobj;
	
	num->dbno      = curid->dbno;
	num->offset    = curid->offset;

        if(cache->refcount)
        {
            if(cache->refcount > 1000)
                ajFatal("btreeIdentDupInsert refcount %u", cache->refcount);
            num->refcount = cache->refcount;
            for(iref=0; iref < cache->refcount; iref++)
                num->refoffsets[iref] = curid->refoffsets[iref];
        }

	secrootpage = cache->totsize;

	curid->offset = secrootpage;

	btreeSecrootCreate(cache,secrootpage);
	cache->secrootblock = secrootpage;
	page = btreeSeccacheWrite(cache,secrootpage);
	page->dirty = BT_DIRTY;

        if(btreeDoRootSync)
            btreeCacheRootSync(cache,secrootpage);
	page->dirty = BT_LOCK;
	page->lockfor = 1551;

	rpage = btreePricacheLocate(cache, 0L);
	rpage->dirty = BT_LOCK;
        rpage->lockfor = 1552;

	cache->slevel = 0;

        btreeNumInsert(cache,num,page);

	num->dbno      = newid->dbno;
	num->offset    = newid->offset;

        if(cache->refcount)
        {
            num->refcount = cache->refcount;
            for(iref=0; iref < cache->refcount; iref++)
                num->refoffsets[iref] = newid->refoffsets[iref];
        }

	btreeNumInsert(cache,num,page);
	++curid->dups;

	return;
    }
    else
    {
	cache->secrootblock = curid->offset;
	page = btreeSeccacheWrite(cache,cache->secrootblock);
	page->dirty = BT_LOCK;
        page->lockfor = 1553;
	buf = page->buf;
	GBT_RIGHT(buf,&right);
	cache->slevel = (ajuint) right;

	num = &numobj;
	
	num->dbno      = newid->dbno;
	num->offset    = newid->offset;

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
                num->refoffsets[iref] = newid->refoffsets[iref];
	}

	btreeNumInsert(cache,num,page);

	++curid->dups;
    }
    

    page->dirty = BT_DIRTY;

    return;
}
ID btreeIdbucketIdlistAll
TY static
MO ajindex
LB core
XX
DE Copies all ID objects into a list, following duplicates
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA u idlist AjPList
PD list to hold hybrid IDs
PX
RT ajulong
RD Overflow
RX
//


static ajulong btreeIdbucketIdlistAll(AjPBtcache cache, ajulong pagepos,
                                      AjPList idlist)
{
    AjPBtpage page      = NULL;
    AjPBtpage lpage     = NULL;
    unsigned char *buf  = NULL;
    unsigned char *kptr = NULL;
    AjPBtId id          = NULL;
    
    unsigned char *idptr = NULL;
    
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajulong pageoverflow = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  iref;
    ajuint  len = 0;
    ajuint idlen;
    ajuint keyskip = BT_DDOFF + cache->refcount*BT_EXTRA + 1;

    /* ajDebug("In btreeIdbucketIdlistAll\n"); */
    
    if(!pagepos)
	ajFatal("IdbucketIdlistAll: cannot read bucket from root page cache %S",
                cache->filename);

    page  = btreePricacheRead(cache,pagepos);
    lpage = page;
    dirtysave = lpage->dirty;
    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1561;

    buf = lpage->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_IDBUCKET)
	ajFatal("IdbucketIdlistAll: NodeType mismatch. "
                "Not bucket (%u) cache %S",
                nodetype, cache->filename);
    
    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->pnperbucket)
	ajFatal("IdbucketIdlistAll: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->pnperbucket, cache->filename);
    

    GBT_BUCKOVERFLOW(buf,&overflow);
    pageoverflow = overflow;
    
    kptr  = PBT_BUCKKEYLEN(buf);
    idptr = kptr + (nentries * sizeof(ajuint));

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//	    /# ajDebug("IdbucketIdlistAll: Overflow\n"); #/
//	    page  = btreePricacheRead(cache,overflow);
//	    buf = page->buf;
//	    GBT_BUCKNODETYPE(buf,&nodetype);
//	    if(nodetype != BT_IDBUCKET)
//		ajFatal("IdbucketIdlistAll: NodeType mismatch. "
//                        "Not bucket (%u) cache %S",
//			nodetype, cache->filename);
//	    GBT_BUCKOVERFLOW(buf,&overflow);
//	    /# overflow bucket ids start at the keylen position #/
//	    idptr = PBT_BUCKKEYLEN(buf);
//	}
*/

	id = ajBtreeIdNew(cache->refcount);
        
	/* Fill ID objects */
	ajStrAssignLenC(&id->id,(const char *)idptr,idlen);
	idptr += (idlen + 1);
	BT_GETAJUINT(idptr,&id->dbno);
	idptr += sizeof(ajuint);
	BT_GETAJUINT(idptr,&id->dups);
	idptr += sizeof(ajuint);	
	BT_GETAJULONG(idptr,&id->offset);
	idptr += sizeof(ajulong);

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
            {
                BT_GETAJULONG(idptr,&id->refoffsets[iref]);
                idptr += sizeof(ajulong);
            }
        }

	kptr += sizeof(ajuint);
        if(!id->dups)
            ajListPushAppend(idlist, id);
        else
        {
            btreeIdentFetchMulti(cache, id->id, id->offset, idlist);
            ajBtreeIdDel(&id);
        }
    }

    lpage->dirty = dirtysave;
    
    return pageoverflow;
}
ID btreeGetNumKeys
TY static
MO ajindex
LB core
XX
DE Get Keys and Pointers from an internal node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA w keys ajulong**
PD keys
PX
PN [4]
PA w ptrs ajulong**
PD ptrs
PX
RT void
RD 
RX
//


static void btreeGetNumKeys(AjPBtcache cache, unsigned char *buf,
			    ajulong **keys, ajulong **ptrs)
{
    ajulong *karray = NULL;
    ajulong *parray = NULL;
    
    ajuint nkeys = 0;
    unsigned char *pptr = NULL;
    ajuint    i;

    /* ajDebug("In btreeGetNumKeys\n"); */

    (void) cache;			/* make it used */

    karray = *keys;
    parray = *ptrs;

    pptr = PBT_KEYLEN(buf);
    GBT_NKEYS(buf,&nkeys);

    if(!nkeys)
	ajFatal("GetNumKeys: No keys in node");

    for(i=0;ifilename);

    pptr += nkeys*sizeof(ajulong);
    for(i=0;ipagepos, nkeys);
#endif

    buf = spage->buf;

    tnkeys = nkeys;
    SBT_NKEYS(buf,tnkeys);
    totlen = 0;
    SBT_TOTLEN(buf, totlen);

    pptr = PBT_KEYLEN(buf);

    /* drop check - tested before node write - page size dependency */
/*
//    aspace = 2 * nkeys * sizeof(ajulong) + sizeof(ajulong);
//
//    if((ajuint) ((pptr+aspace)-buf) > cache->pagesize)
//	ajFatal("WriteNumNode: too many keys for available pagesize");
*/

    for(i=0;idirty = BT_DIRTY;

    return;
}
ID btreeWriteNumNodeSingle
TY static
MO ajindex
LB core
XX
DE Write an internal numeric node with a single key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u spage AjPBtpage
PD buffer
PX
PN [3]
PA r numkey ajulong
PD key
PX
PN [4]
PA r lptr ajulong
PD left page pointer
PX
PN [5]
PA r rptr ajulong
PD rightpage pointer
PX
RT void
RD 
RX
//


static void btreeWriteNumNodeSingle(AjPBtcache cache, AjPBtpage spage,
                                    ajulong numkey, ajulong lptr,
                                    ajulong rptr)
{
    unsigned char *pptr   = NULL;
    unsigned char *buf;
    
    ajuint tnkeys = 0;
    ajuint totlen = 0;

    ajulong lv       = 0L;
    ajulong overflow = 0L;

/*  ajulong aspace   = 0L;*/

    (void) cache;             /* make it used */

    /* ajDebug("In btreeWriteNumNode\n"); */

    buf = spage->buf;

    tnkeys = 1;
    SBT_NKEYS(buf,tnkeys);
    totlen = 0;
    SBT_TOTLEN(buf,totlen);

    pptr = PBT_KEYLEN(buf);

/* drop check - tested before call - pagesize dependency */
/*
//    aspace = 2 * sizeof(ajulong) + sizeof(ajulong);
//
//    if((ajuint) ((pptr+aspace)-buf) > cache->pagesize)
//	ajFatal("WriteNumNodeSingle: "
//                "too many keys for available pagesize cache %S",
//                cache->filename);
*/

    lv = numkey;
    BT_SETAJULONG(pptr,lv);
    pptr += sizeof(ajulong);

    lv = lptr;
    BT_SETAJULONG(pptr,lv);
    pptr += sizeof(ajulong);
    
    lv = rptr;
    BT_SETAJULONG(pptr,lv);
    pptr += sizeof(ajulong);

    overflow = 0L;
    SBT_OVERFLOW(buf,overflow);

    spage->dirty = BT_DIRTY;    /* test in caller */

    return;
}
ID btreeWriteNumbucket
TY static
MO ajindex
LB core
XX
DE Write index bucket object to the cache given a disc page number
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r bucket const AjPNumbucket
PD bucket
PX
PN [3]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreeWriteNumbucket(AjPBtcache cache, const AjPNumbucket bucket,
                                ajulong pagepos)
{
    AjPBtpage page      = NULL;
    unsigned char *buf  = NULL;
    unsigned char *pptr = NULL;

    ajuint  uv  = 0;
    ajuint   v   = 0;
    ajuint  i    = 0;
    ajuint  iref = 0;
    ajulong lv   = 0L;
    ajuint nentries = 0;
    ajulong overflow = 0L;

    /* ajDebug("In btreeWriteNumbucket\n"); */

    if(pagepos == cache->totsize)
    {
	page = btreeSeccacheBucketnew(cache);
	buf = page->buf;
	overflow = 0L;
    }
    else
    {
	page = btreeSeccacheRead(cache,pagepos);
	buf = page->buf;
	GBT_BUCKOVERFLOW(buf,&overflow);
    }

    v = BT_NUMBUCKET;
    SBT_BUCKNODETYPE(buf,v);
    page->dirty = BT_LOCK;
    page->lockfor = 1571;

    nentries = bucket->Nentries;
    v = nentries;
    SBT_BUCKNENTRIES(buf,v);
    
    pptr = PBT_BUCKKEYLEN(buf);

    for(i=0;iNumId[i]->offset;
	BT_SETAJULONG(pptr,lv);
	pptr += sizeof(ajulong);

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
            {
                lv = bucket->NumId[i]->refoffsets[iref];
                BT_SETAJULONG(pptr,lv);
                pptr += sizeof(ajulong);
            }
        }

	uv = bucket->NumId[i]->dbno;
	BT_SETAJUINT(pptr,uv);
	pptr += sizeof(ajuint);
    }
    
    lv = 0L;
    SBT_BUCKOVERFLOW(buf,lv);

    page->dirty = BT_DIRTY;

    return;
}
ID btreeWriteNumbucketEmpty
TY static
MO ajindex
LB core
XX
DE Write empty index numeric bucket object to the cache given a disc page number
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreeWriteNumbucketEmpty(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage page      = NULL;
    unsigned char *buf  = NULL;

    ajuint   v   = 0;
    ajulong lv   = 0L;
    ajulong overflow = 0L;

    /* ajDebug("In btreeWriteNumbucketEmpty\n"); */

    if(pagepos == cache->totsize)
    {
	page = btreeSeccacheBucketnew(cache);
	buf = page->buf;
	overflow = 0L;
	lv = overflow;
	SBT_BUCKOVERFLOW(buf,lv);
    }
    else
    {
	page = btreeSeccacheRead(cache,pagepos);
	buf = page->buf;
	GBT_BUCKOVERFLOW(buf,&overflow);
    }

    v = BT_NUMBUCKET;
    SBT_BUCKNODETYPE(buf,v);
    page->dirty = BT_LOCK;      /* clear at end */
    page->lockfor = 1581;

    v = 0;
    SBT_BUCKNENTRIES(buf,v);
    
    lv = 0L;
    SBT_BUCKOVERFLOW(buf,lv);
 
    page->dirty = BT_DIRTY;     /* clear the lock */

    return;
}
ID btreeNumbucketIdlist
TY static
MO ajindex
LB core
XX
DE Copies all numeric IDs into a list
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA u idlist AjPList
PD list to hold numeric ID strings
PX
RT ajulong
RD Overflow
RX
//


static ajulong btreeNumbucketIdlist(AjPBtcache cache, ajulong pagepos,
                                    AjPList idlist)
{
    AjPBtNumId num      = NULL;
    AjPBtpage page      = NULL;
    unsigned char *buf  = NULL;
    unsigned char *pptr = NULL;
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajulong pageoverflow = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint iref;

    /* ajDebug("In btreeNumbucketIdlist\n"); */

    if(pagepos == cache->secrootblock)
	ajFatal("NumbucketIdlist: cannot read bucket from root page cache %S",
                cache->filename);

    page = btreeSeccacheRead(cache,pagepos);
    dirtysave = page->dirty;
    page->dirty = BT_LOCK;
    page->lockfor = 1591;
    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_NUMBUCKET && nodetype != BT_IDBUCKET)
	ajFatal("NumbucketIdlist: Nodetype mismatch. "
                "Not bucket (%u) cache %S",
                nodetype, cache->filename);

    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->snperbucket)
	ajFatal("NumbucketIdlist: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->snperbucket, cache->filename);

    GBT_BUCKOVERFLOW(buf,&overflow);
    pageoverflow = overflow;

    pptr = PBT_BUCKKEYLEN(buf);

    for(i=0;irefcount)
        {
            AJCNEW0(num->refoffsets, cache->refcount);
            num->refcount = cache->refcount;
        }

	BT_GETAJULONG(pptr,&num->offset);
	pptr += sizeof(ajulong);

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
            {
                BT_GETAJULONG(pptr,&num->refoffsets[iref]);
                pptr += sizeof(ajulong);
            }
        }

	BT_GETAJUINT(pptr,&num->dbno);
	pptr += sizeof(ajuint);
        ajListPushAppend(idlist, num);
        num = NULL;
    }
    
    page->dirty = dirtysave;

    return pageoverflow;
}
ID btreeNumbucketBtidlist
TY static
MO ajindex
LB core
XX
DE Copies all numeric IDs into a list of btree Ids
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA r idname const AjPStr
PD id name
PX
PN [4]
PA u idlist AjPList
PD list to hold numeric ID strings
PX
RT ajulong
RD Overflow
RX
//


static ajulong btreeNumbucketBtidlist(AjPBtcache cache, ajulong pagepos,
                                      const AjPStr idname, AjPList idlist)
{
    AjPBtId btid      = NULL;
    AjPBtpage page      = NULL;
    unsigned char *buf  = NULL;
    unsigned char *pptr = NULL;
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajulong pageoverflow = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  iref;
    
    /* ajDebug("In btreeNumbucketBtidlist\n"); */

    if(pagepos == cache->secrootblock)
	ajFatal("NumbucketBtidlist: cannot read bucket "
                "from root page cache %S",
                cache->filename);

    page = btreeSeccacheRead(cache,pagepos);
    dirtysave = page->dirty;
    page->dirty = BT_LOCK;
    page->lockfor = 1601;
    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_NUMBUCKET && nodetype != BT_IDBUCKET)
	ajFatal("NumbucketBtidlist: Nodetype mismatch. "
                "Not bucket (%u) cache %S",
                nodetype, cache->filename);

    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->snperbucket)
	ajFatal("NumbucketBtidlist: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->snperbucket,
                cache->filename);

    GBT_BUCKOVERFLOW(buf,&overflow);
    pageoverflow = overflow;

    pptr = PBT_BUCKKEYLEN(buf);

    for(i=0;irefcount);
        ajStrAssignS(&btid->id,idname);
	BT_GETAJULONG(pptr,&btid->offset);
	pptr += sizeof(ajulong);

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
            {
                BT_GETAJULONG(pptr,&btid->refoffsets[iref]);
                pptr += sizeof(ajulong);
            }
        }

	BT_GETAJUINT(pptr,&btid->dbno);
	pptr += sizeof(ajuint);
        ajListPushAppend(idlist, btid);
        btid = NULL;
    }
    
    page->dirty = dirtysave;

    return pageoverflow;
}
ID btreeNumbucketBthitlist
TY static
MO ajindex
LB core
XX
DE Copies all numeric IDs into a list of btree Hits
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA u hitlist AjPList
PD list to hold numeric hits
PX
RT ajulong
RD Overflow
RX
//


static ajulong btreeNumbucketBthitlist(AjPBtcache cache, ajulong pagepos,
                                       AjPList hitlist)
{
    AjPBtHit bthit      = NULL;
    AjPBtpage page      = NULL;
    unsigned char *buf  = NULL;
    unsigned char *pptr = NULL;
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajulong pageoverflow = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    
    /* ajDebug("In btreeNumbucketBthitlist\n"); */

    if(cache->refcount)
        ajWarn("btreeNumbucketbthitlist called for cache '%S' "
               "with %u reference files",
               cache->filename, cache->refcount);

    if(pagepos == cache->secrootblock)
	ajFatal("NumbucketBthitlist: cannot read bucket "
                "from root page cache %S",
                cache->filename);

    page = btreeSeccacheRead(cache,pagepos);
    dirtysave = page->dirty;
    page->dirty = BT_LOCK;
    page->lockfor = 1601;
    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_NUMBUCKET && nodetype != BT_IDBUCKET)
	ajFatal("NumbucketBthitlist: Nodetype mismatch. "
                "Not bucket (%u) cache %S",
                nodetype, cache->filename);

    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->snperbucket)
	ajFatal("NumbucketBthitlist: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->snperbucket,
                cache->filename);

    GBT_BUCKOVERFLOW(buf,&overflow);
    pageoverflow = overflow;

    pptr = PBT_BUCKKEYLEN(buf);

    for(i=0;ioffset);
	pptr += sizeof(ajulong);

        if(cache->refcount)
            pptr += cache->refcount*sizeof(ajulong);

	BT_GETAJUINT(pptr,&bthit->dbno);
	pptr += sizeof(ajuint);
        ajListPushAppend(hitlist, bthit);
        bthit = NULL;
    }
    
    page->dirty = dirtysave;

    return pageoverflow;
}
ID btreeNumbucketBthitreflist
TY static
MO ajindex
LB core
XX
DE Copies all numeric IDs into a list of btree Hitrefs
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
PN [3]
PA u hitlist AjPList
PD list to hold numeric hitrefs
PX
RT ajulong
RD Overflow
RX
//


static ajulong btreeNumbucketBthitreflist(AjPBtcache cache, ajulong pagepos,
                                          AjPList hitlist)
{
    AjPBtHitref bthitref = NULL;
    AjPBtpage page      = NULL;
    unsigned char *buf  = NULL;
    unsigned char *pptr = NULL;
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;
    ajulong pageoverflow = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    
    /* ajDebug("In btreeNumbucketBthitreflist\n"); */

    if(cache->refcount != 1)
        ajWarn("btreeNumbucketbthitreflist called for cache '%S' "
               "with %u reference files",
               cache->filename, cache->refcount);

    if(pagepos == cache->secrootblock)
	ajFatal("NumbucketBthitreflist: cannot read bucket "
                "from root page cache %S",
                cache->filename);

    page = btreeSeccacheRead(cache,pagepos);
    dirtysave = page->dirty;
    page->dirty = BT_LOCK;
    page->lockfor = 1601;
    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_NUMBUCKET && nodetype != BT_IDBUCKET)
	ajFatal("NumbucketBthitreflist: Nodetype mismatch. "
                "Not bucket (%u) cache %S",
                nodetype, cache->filename);

    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->snperbucket)
	ajFatal("NumbucketBthitreflist: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->snperbucket,
                cache->filename);

    GBT_BUCKOVERFLOW(buf,&overflow);
    pageoverflow = overflow;

    pptr = PBT_BUCKKEYLEN(buf);

    for(i=0;ioffset);
	pptr += sizeof(ajulong);

        if(cache->refcount)
        {
            BT_GETAJULONG(pptr,&bthitref->refoffset);
            pptr += cache->refcount*sizeof(ajulong);
        }
        
	BT_GETAJUINT(pptr,&bthitref->dbno);
	pptr += sizeof(ajuint);
        ajListPushAppend(hitlist, bthitref);
        bthitref = NULL;
    }
    
    page->dirty = dirtysave;

    return pageoverflow;
}
ID btreeReadNumbucket
TY static
MO ajindex
LB core
XX
DE Constructor for index bucket given a disc page number
DE Creates one empty key slot for possible addition
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT AjPNumbucket
RD bucket
RX
//


static AjPNumbucket btreeReadNumbucket(AjPBtcache cache, ajulong pagepos)
{
    AjPNumbucket bucket    = NULL;
    AjPBtpage page      = NULL;
    unsigned char *buf  = NULL;
    unsigned char *pptr = NULL;
    ajuint  nodetype  = 0;
    ajuint nentries  = 0;
    ajulong overflow  = 0L;
    ajuint  dirtysave = 0;
    
    ajuint  i;
    ajuint  iref;
    
    /* ajDebug("In btreeReadNumbucket\n"); */

    if(pagepos == cache->secrootblock)
	ajFatal("ReadNumbucket: cannot read bucket from a root page");

    page = btreeSeccacheRead(cache,pagepos);
    dirtysave = page->dirty;
    page->dirty = BT_LOCK;
    page->lockfor = 1611;
    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);

    if(nodetype != BT_NUMBUCKET && nodetype !=  BT_IDBUCKET)
	ajFatal("ReadNumbucket: Nodetype mismatch. Not bucket (%u) cache %S",
                nodetype, cache->filename);

    GBT_BUCKNENTRIES(buf,&nentries);

    if(nentries > cache->snperbucket)
	ajFatal("ReadNumbucket: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->snperbucket,
                cache->filename);

    GBT_BUCKOVERFLOW(buf,&overflow);

    bucket = btreeNumbucketNew(cache->snperbucket, cache->refcount);
    bucket->Nentries = nentries;

    pptr = PBT_BUCKKEYLEN(buf);

    for(i=0;iNumId[i]->offset);
	pptr += sizeof(ajulong);

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
            {
                BT_GETAJULONG(pptr,&bucket->NumId[i]->refoffsets[iref]);
                pptr += sizeof(ajulong);
            }
        }

	BT_GETAJUINT(pptr,&bucket->NumId[i]->dbno);
	pptr += sizeof(ajuint);
    }
    
    page->dirty = dirtysave;

    return bucket;
}
ID btreeNumbucketDel
TY static
MO ajindex
LB core
XX
DE Delete a bucket object
XX
PN [1]
PA w thys AjPNumbucket*
PD bucket
PX
RT void
RD 
RX
//


static void btreeNumbucketDel(AjPNumbucket *thys)
{
    AjPNumbucket pthis = NULL;
    ajuint newmax;
    
    /* ajDebug("In btreeNumbucketDel\n"); */

    if(!thys || !*thys)
	return;

    pthis = *thys;

    if(!statSaveNumbucket)
    {
        statSaveNumbucketMax=2048;
        statSaveNumbucketNext=0;
        AJCNEW0(statSaveNumbucket,statSaveNumbucketMax);
    }
    
    if(statSaveNumbucketNext >= statSaveNumbucketMax)
    {
        newmax = statSaveNumbucketMax + statSaveNumbucketMax;
        AJCRESIZE0(statSaveNumbucket,statSaveNumbucketMax,newmax);
        statSaveNumbucketMax = newmax;
    }
        
    statSaveNumbucket[statSaveNumbucketNext++] = pthis;
 
    *thys = NULL;

    return;
}
ID btreeNumbucketFree
TY static
MO ajindex
LB core
XX
DE Delete a bucket object
XX
PN [1]
PA w thys AjPNumbucket*
PD bucket
PX
RT void
RD 
RX
//


static void btreeNumbucketFree(AjPNumbucket *thys)
{
    AjPNumbucket pthis = NULL;
    ajuint i;
    
    
    /* ajDebug("In btreeNumbucketFree\n"); */

    if(!thys || !*thys)
	return;

    pthis = *thys;

    if(pthis->NumId)
    {
	for(i=0;iMaxentries;++i)
	    AJFREE(pthis->NumId[i]);
	AJFREE(pthis->NumId);
    }

    AJFREE(pthis);

    *thys = NULL;

    return;
}
ID btreeNumFind
TY static
MO ajindex
LB core
XX
DE Find the node that should contain a new key for insertion
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r key const ajulong
PD key to search for
PX
RT AjPBtpage
RD leaf node where item should be inserted
RX
//


static AjPBtpage btreeNumFind(AjPBtcache cache, const ajulong key)
{
    AjPBtpage root = NULL;
    AjPBtpage ret  = NULL;

    /* ajDebug("In btreeNumFind\n"); */

    /* The root node should always be in the cache (BT_LOCKed) */
    root = btreeSeccacheLocate(cache,cache->secrootblock);

    /* ajDebug("cache->slevel = %u root=%u\n",cache->slevel,(ajuint)root); */
    

    if(!cache->slevel)
	return root;
    
    ret = btreeNumFindINode(cache,root,key);

    return ret;
}
ID btreeNumFindINode
TY static
MO ajindex
LB core
XX
DE Recursive search for insert node in a secondary tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD page
PX
PN [3]
PA r item ajulong
PD key to search for
PX
RT AjPBtpage
RD leaf node where item should be inserted
RX
//


static AjPBtpage btreeNumFindINode(AjPBtcache cache, AjPBtpage page,
				   ajulong item)
{
    AjPBtpage ret = NULL;
    AjPBtpage pg  = NULL;

    unsigned char *buf = NULL;
    ajuint status       = 0;
    ajuint ival         = 0;

    /* ajDebug("In btreeNumFindINode\n"); */
    
    ret = page;
    buf = page->buf;
    GBT_NODETYPE(buf,&ival);

    if(ival != BT_SECLEAF)
    {
	status = ret->dirty;
	ret->dirty = BT_LOCK;	/* Lock in case of lots of overflow pages */
        ret->lockfor = 1621;
	pg = btreeNumPageFromKey(cache,buf,item);
	ret->dirty = status;
	ret = btreeNumFindINode(cache,pg,item);
    }
    
    return ret;
}
ID btreeNumPageFromKey
TY static
MO ajindex
LB core
XX
DE Return next lower index page given a key in a numeric secondary tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u buf unsigned char*
PD page buffer
PX
PN [3]
PA r key ajulong
PD key to search for
PX
RT AjPBtpage
RD pointer to a page
RX
//


static AjPBtpage btreeNumPageFromKey(AjPBtcache cache, unsigned char *buf,
				     ajulong key)
{
    unsigned char *rootbuf = NULL;
    ajuint nkeys = 0;
    ajuint i;
    
    ajulong blockno = 0L;
    ajulong *karray = NULL;
    ajulong *parray = NULL;
    AjPBtpage page = NULL;
    AjPBtMem array = NULL;

    /* ajDebug("In btreeNumPageFromKey\n"); */
    
    rootbuf = buf;


    GBT_NKEYS(rootbuf,&nkeys);

    array = btreeAllocSecArray(cache);
    karray = array->overflows;
    parray = array->parray;
    
    btreeGetNumKeys(cache,rootbuf,&karray,&parray);

    i = 0;

    while(i!=nkeys && key >= karray[i])
	++i;

    if(i==nkeys)
    {
	if(key < karray[i-1])
	    blockno = parray[i-1];
	else
	    blockno = parray[i];
    }
    else
	blockno = parray[i];

    btreeDeallocSecArray(cache,array);

    page =  btreeSeccacheRead(cache,blockno);

    return page;
}
ID btreeNumbucketAdd
TY static
MO ajindex
LB core
XX
DE Add offset info to a numeric bucket
DE Only called if there is room in the bucket
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number of bucket
PX
PN [3]
PA r num const AjPBtNumId
PD ID info
PX
RT void
RD 
RX
//


static void btreeNumbucketAdd(AjPBtcache cache, ajulong pagepos,
                              const AjPBtNumId num)
{
    AjPBtpage page      = NULL;
    unsigned char *buf  = NULL;
    unsigned char *pptr = NULL;
    ajuint  nodetype  = 0;
    ajuint  nentries  = 0;
    ajulong overflow  = 0L;

    ajulong lv = 0L;
    ajuint uv = 0;

    ajuint  i;
    ajuint  iref;

    /* ajDebug("In btreeReadNumbucket\n"); */

    if(pagepos == cache->secrootblock)
	ajFatal("NumbucketAdd: cannot read bucket from root page cache %S",
                cache->filename);

    page = btreeSeccacheRead(cache,pagepos);

    page->dirty = BT_LOCK;      /* reset at end */
    page->lockfor = 1631;
    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);
    if(nodetype != BT_NUMBUCKET)
	ajFatal("NumbucketAdd: Nodetype mismatch. "
                "Not bucket (%u) cache %S",
                nodetype, cache->filename);

    GBT_BUCKNENTRIES(buf,&nentries);
    if(nentries > cache->snperbucket)
	ajFatal("NumbucketAdd: Bucket too full page: %Lu "
                "entries: %u max: %u cache %S",
                pagepos, nentries, cache->snperbucket, cache->filename);

    GBT_BUCKOVERFLOW(buf,&overflow);

    pptr = PBT_BUCKKEYLEN(buf);

    for(i=0;irefcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
                pptr += sizeof(ajulong);
        }
	pptr += sizeof(ajuint);
    }
    
    lv = num->offset;
    BT_SETAJULONG(pptr,lv);
    pptr += sizeof(ajulong);

    if(cache->refcount)
    {
        for(iref=0; iref < cache->refcount; iref++)
        {
            lv = num->refoffsets[iref];
            BT_SETAJULONG(pptr,lv);
            pptr += sizeof(ajulong);
        }
    }

    uv = num->dbno;
    BT_SETAJUINT(pptr,uv);
    pptr += sizeof(ajuint);

    nentries++;
    SBT_BUCKNENTRIES(buf,nentries);

    page->dirty = BT_DIRTY;

    return;
}
ID btreeNumInNumbucket
TY static
MO ajindex
LB core
XX
DE Return number of entries in a bucket
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number
PX
RT ajuint
RD Number of entries in bucket
RX
//


static ajuint btreeNumInNumbucket(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage page     = NULL;
    unsigned char *buf = NULL;
    ajuint  nodetype    = 0;
    ajuint nentries    = 0;
    
    /* ajDebug("In btreeNumInNumbucket\n"); */
    
    if(pagepos == cache->secrootblock)
	ajFatal("NumInNumbucket: Attempt to read bucket from root page\n");

    page  = btreeSeccacheRead(cache,pagepos);

    buf = page->buf;

    GBT_BUCKNODETYPE(buf,&nodetype);

    if(nodetype != BT_NUMBUCKET)
	ajFatal("NumInNumbucket: NodeType mismatch. Not numbucket (%u) "
                "for cache '%S'",
		nodetype, cache->filename);
    
    GBT_BUCKNENTRIES(buf,&nentries);

    return nentries;
}
ID btreeNumbucketNew
TY static
MO ajindex
LB core
XX
DE Construct a bucket object
XX
PN [1]
PA r n ajuint
PD Number of IDs
PX
PN [2]
PA r refcount ajuint
PD Number of reference files per entry
PX
RT AjPNumbucket
RD initialised disc block cache structure
RX
//


static AjPNumbucket btreeNumbucketNew(ajuint n, ajuint refcount)
{
    AjPNumbucket bucket = NULL;
    ajuint i;
    ajuint iref;

    /* ajDebug("In btreeNumbucketNew\n"); */
    if(statSaveNumbucketNext)
    {
       bucket = statSaveNumbucket[--statSaveNumbucketNext];
       for(i=0;iMaxentries;++i)
       {
           bucket->NumId[i]->offset = 0L;
           bucket->NumId[i]->dbno = 0;
           if(refcount != bucket->NumId[i]->refcount)
           {
               if(refcount)
                   AJCRESIZE0(bucket->NumId[i]->refoffsets,
                             bucket->NumId[i]->refcount, refcount);
               else
                   AJFREE(bucket->NumId[i]->refoffsets);

               bucket->NumId[i]->refcount = refcount;
           }
           else if(refcount)
           {
               for(iref=0; iref < refcount; iref++)
                   bucket->NumId[i]->refoffsets[iref] = 0L;
           }
       }
       if(n > bucket->Maxentries)
       {
           if(bucket->Maxentries)
               AJCRESIZE0(bucket->NumId,bucket->Maxentries+1,n+1);
           else
               AJCNEW0(bucket->NumId,n+1);

           for(i=bucket->Maxentries;iNumId[i]);
               if(refcount)
                   AJCNEW0(bucket->NumId[i]->refoffsets, refcount);
           }

           bucket->Maxentries = n;
       }
   }
   else
   {
       AJNEW0(bucket);
       AJCNEW0(bucket->NumId,n+1);

       for(i=0;iNumId[i]);
           if(refcount)
               AJCNEW0(bucket->NumId[i]->refoffsets, refcount);
       }

       bucket->Maxentries = n;
   }

    bucket->NodeType = BT_NUMBUCKET;
    bucket->Nentries = n;
    bucket->Overflow = 0L;
    
    return bucket;
}
ID btreeReorderNumbuckets
TY static
MO ajindex
LB core
XX
DE Re-order leaf buckets
DE Must only be called if one of the buckets is full
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u leaf AjPBtpage
PD leaf page
PX
RT AjBool
RD true if reorder was successful i.e. leaf not full
RX
//


static AjBool btreeReorderNumbuckets(AjPBtcache cache, AjPBtpage leaf)
{
    ajuint nkeys = 0;
    unsigned char *lbuf = NULL;

    AjPBtMem array = NULL;
    AjPBtMem newarray = NULL;
    ajulong *ptrs        = NULL;
    ajulong *newkeys        = NULL;
    ajulong *newptrs        = NULL;

    ajuint i = 0;
    ajuint iref = 0;

    ajuint order;
    ajuint totalkeys     = 0;
    ajuint nperbucket    = 0;
    ajuint maxnperbucket = 0;
    ajuint count         = 0;
    ajuint keylimit      = 0;
    ajuint bucketlimit   = 0;
    ajuint nodetype      = 0;
    
    AjPList idlist    = NULL;
    ajuint   dirtysave = 0;
    AjPBtNumId bid       = NULL;
    AjPNumbucket cbucket = NULL;
    AjPBtNumId cid       = NULL;

    ajuint   iold = 0;
    
#if AJINDEX_DEBUG
    ajDebug("btreeReorderNumbuckets %Lu\n", leaf->pagepos);
#endif
    ++statCallNumbucketsReorder;

    /* ajDebug("In btreeReorderNumbuckets\n"); */

    dirtysave = leaf->dirty;

    leaf->dirty = BT_LOCK;
    leaf->lockfor = 1641;
    lbuf = leaf->buf;

    GBT_NODETYPE(lbuf,&nodetype);

    order = cache->sorder;
    nperbucket = cache->snperbucket;
    

    array = btreeAllocSecArray(cache);
    ptrs  = array->parray;

    /* Read keys/ptrs */

    btreeGetNumPointers(cache,lbuf,&ptrs);

    GBT_NKEYS(lbuf,&nkeys);
    keylimit = nkeys + 1;


    if(!nkeys)
	ajFatal("ReorderNumbuckets: Attempt to reorder empty leaf");

    for(i=0;i= order)
    {
	btreeDeallocSecArray(cache,array);
	
	leaf->dirty = dirtysave;

#if AJINDEX_DEBUG
    ajDebug("   return ajFalse\n");
#endif
	return ajFalse;
    }
    

    newarray = btreeAllocSecArray(cache);
    newkeys  = newarray->overflows;
    newptrs  = newarray->parray;

    /* Read buckets */
    
    idlist  = ajListNew();

    /* Read IDs from all buckets and push to list and sort (increasing id) */
    for(i=0;irefcount);
    iold = 0;
    for(i=0;iNentries = 0;

	count = 0;

	while(count!=maxnperbucket)
	{
	    ajListPop(idlist,(void **)&bid);
	    
	    cid = cbucket->NumId[count];
	    cid->dbno = bid->dbno;
	    cid->offset = bid->offset;

            if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                    cid->refoffsets[iref] = bid->refoffsets[iref];
	    }

	    ++cbucket->Nentries;
	    ++count;
	    AJFREE(bid);
	}


	ajListPeek(idlist,(void **)&bid);
	newkeys[i] = bid->offset;

	if((iold < order) && ptrs[iold])
            newptrs[i] = ptrs[iold++];
        else
	    newptrs[i] = cache->totsize;
	btreeWriteNumbucket(cache,cbucket,newptrs[i]);
    }


    /* Deal with greater-than bucket */

    cbucket->Nentries = 0;

    count = 0;

    while(ajListPop(idlist,(void **)&bid))
    {
	cid = cbucket->NumId[count];
	cid->dbno = bid->dbno;
	cid->offset = bid->offset;

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
                cid->refoffsets[iref] = bid->refoffsets[iref];
	}

	++cbucket->Nentries;
	++count;
	AJFREE(bid);
    }
    
    if((iold < order) && ptrs[iold])
        newptrs[i] = ptrs[iold++];
    else
        newptrs[i] = cache->totsize;
    btreeWriteNumbucket(cache,cbucket,newptrs[i]);

    btreeNumbucketDel(&cbucket);

#if AJINDEX_DEBUG
    if(iold < keylimit)
        ajDebug("btreeReorderNumbuckets '%S' %u -> %u",
                cache->filename, keylimit, iold);
#endif

    for(i = iold+1; i <= keylimit; i++)
    {
        btreeSecpageSetfree(cache, ptrs[i]);
    }

    /* Now write out a modified leaf with new keys/ptrs */

    nkeys = bucketlimit;

    btreeWriteNumNode(cache,leaf,newkeys,newptrs,nkeys);

    leaf->dirty = BT_DIRTY;

    if(nodetype == BT_SECROOT)
    {
	leaf->dirty = BT_LOCK;
        leaf->lockfor = 1642;
    }

    btreeDeallocSecArray(cache,array);
    btreeDeallocSecArray(cache,newarray);

    ajListFree(&idlist);

#if AJINDEX_DEBUG
    ajDebug("   return ajTrue\n");
#endif

    return ajTrue;
}
ID btreeNumNodeIsFull
TY static
MO ajindex
LB core
XX
DE Tests whether a node is full of keys
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD original page
PX
RT AjBool
RD true if full
RX
//


static AjBool btreeNumNodeIsFull(const AjPBtcache cache, AjPBtpage page)
{
    unsigned char *buf = NULL;
    ajuint nkeys = 0;

    /* ajDebug("In btreeNumNodeIsFull\n"); */

    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);

    if(nkeys == cache->sorder - 1)
	return ajTrue;

    return ajFalse;
}
ID btreeNumInsertNonfull
TY static
MO ajindex
LB core
XX
DE Insert a key into a non-full numeric secondary node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD original page
PX
PN [3]
PA r key ajulong
PD key to insert
PX
PN [4]
PA r less ajulong
PD less-than pointer
PX
PN [5]
PA r greater ajulong
PD greater-than pointer
PX
RT void
RD 
RX
//


static void btreeNumInsertNonfull(AjPBtcache cache, AjPBtpage page,
				  ajulong key, ajulong less,
				  ajulong greater)
{
    unsigned char *buf = NULL;
    ajulong *karray     = NULL;
    ajulong *parray     = NULL;
    ajuint nkeys  = 0;
    ajuint ipos   = 0;
    ajuint i;
    ajuint count  = 0;

    ajulong lv = 0L;
    ajuint  v  = 0;
    AjPBtMem array = NULL;    

    AjPBtpage ppage = NULL;
    ajulong pagepos   = 0L;

    ajuint nodetype = 0;
    
    /* ajDebug("In btreeNumInsertNonfull\n"); */

    array = btreeAllocSecArray(cache);
    karray  = array->overflows;
    parray  = array->parray;


    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&nodetype);
    
    btreeGetNumKeys(cache,buf,&karray,&parray);

    i = 0;

    while(i!=nkeys && key >= karray[i])
	++i;

    ipos = i;

    count = nkeys - ipos;
    

    if(ipos == nkeys)
    {
	karray[ipos] = key;
	parray[ipos+1] = greater;
	parray[ipos]   = less;
    }
    else
    {
	parray[nkeys+1] = parray[nkeys];

	for(i=nkeys-1; count>0; --count, --i)
	{
	    karray[i+1] = karray[i];
	    parray[i+1] = parray[i];
	}

	karray[ipos] = key;
	parray[ipos] = less;
	parray[ipos+1] = greater;
    }

    ++nkeys;
    v = nkeys;
    SBT_NKEYS(buf,v);

    btreeWriteNumNode(cache,page,karray,parray,nkeys);
    if(nodetype == BT_SECROOT)
    {
	page->dirty = BT_LOCK;
        page->lockfor = 1651;
    }

    pagepos = page->pagepos;
    ppage = btreeSeccacheRead(cache,less);
    lv = pagepos;
    SBT_PREV(ppage->buf,lv);
    ppage->dirty = BT_DIRTY;

    ppage = btreeSeccacheRead(cache,greater);
    lv = pagepos;
    SBT_PREV(ppage->buf,lv);
    ppage->dirty = BT_DIRTY;

    btreeDeallocSecArray(cache,array);

    if(nodetype != BT_SECROOT)
	btreeNumKeyShift(cache,page);

    return;
}
ID btreeNumInsertKey
TY static
MO ajindex
LB core
XX
DE Insert a key into a potentially full node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD original page
PX
PN [3]
PA r key ajulong
PD key to insert
PX
PN [4]
PA r less ajulong
PD less-than pointer
PX
PN [5]
PA r greater ajulong
PD greater-than pointer
PX
RT void
RD 
RX
//


static void btreeNumInsertKey(AjPBtcache cache, AjPBtpage page,
			      ajulong key, ajulong less, ajulong greater)
{
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;
    unsigned char *tbuf = NULL;
    ajulong *narray      = NULL;
    ajulong *parray      = NULL;
    ajulong *tnarray     = NULL;
    ajulong *tparray     = NULL;
    ajuint nkeys    = 0;
    ajuint order    = 0;
    ajuint keypos   = 0;
    ajuint rkeyno   = 0;
    
    ajuint i = 0;
    ajuint n = 0;
    
    ajuint nodetype  = 0;
    AjPBtpage ipage = NULL;
    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage tpage = NULL;

    ajulong blockno  = 0L;
    ajulong rblockno = 0L;
    ajulong lblockno = 0L;
    ajulong ibn      = 0L;

    ajulong mediankey  = 0L;
    ajulong medianless = 0L;
    ajulong mediangtr  = 0L;
    ajulong overflow   = 0L;
    ajulong prev       = 0L;
    
    ajulong lv = 0L;
    ajuint  v  = 0;
    AjPBtMem savekeyarray  = NULL;    
    AjPBtMem array2 = NULL;    
    
    /* ajDebug("In btreeNumInsertKey\n"); */

    if(!btreeNumNodeIsFull(cache,page))
    {
	btreeNumInsertNonfull(cache,page,key,less,greater);
	return;
    }
    
    order = cache->sorder;
    lbuf = page->buf;
    GBT_NODETYPE(lbuf,&nodetype);
    page->dirty = BT_LOCK;
    page->lockfor = 1661;

    if(nodetype == BT_SECROOT)
    {
	btreeNumSplitroot(cache);

	if(page->pagepos)
	    page->dirty = BT_DIRTY;

        blockno = btreeGetBlockFirstN(cache,lbuf,key);

	ipage = btreeSeccacheRead(cache,blockno);
	btreeNumInsertNonfull(cache,ipage,key,less,greater);

	return;
    }


    savekeyarray = btreeAllocSecArray(cache);
    narray  = savekeyarray->overflows;
    parray  = savekeyarray->parray;
    
    array2 = btreeAllocSecArray(cache);
    tnarray  = array2->overflows;
    tparray  = array2->parray;

    
    lpage = page;
    lbuf = lpage->buf;
    
    btreeGetNumKeys(cache,lbuf,&narray,&parray);

    GBT_BLOCKNUMBER(lbuf,&lblockno);

    rblockno = cache->totsize;
    rpage = btreeSeccacheNodenew(cache);
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1662;
    rbuf = rpage->buf;

    
    GBT_PREV(lbuf,&prev);
    lv = prev;
    SBT_PREV(rbuf,lv);

    nkeys = order - 1;
    keypos = nkeys / 2;

    if(!(nkeys % 2))
	--keypos;

    mediankey = narray[keypos];
    medianless = lblockno;
    mediangtr  = rblockno;


    GBT_NODETYPE(lbuf,&nodetype);
    v = nodetype;
    SBT_NODETYPE(rbuf,v);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);


    for(i=0;idirty = BT_LOCK;
    lpage->lockfor = 1663;


    for(i=0;ibuf;
	lv = lblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }


    for(i=keypos+1;idirty = BT_DIRTY;

    btreeWriteNumNode(cache,rpage,tnarray,tparray,rkeyno);
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1664;

    for(i=0;ibuf;
	lv = rblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }

    btreeDeallocSecArray(cache,array2);

    ibn = rblockno;
    if(key < mediankey)
	ibn = lblockno;

    lpage->dirty = BT_DIRTY;
    rpage->dirty = BT_DIRTY;

    ipage = btreeSeccacheRead(cache,ibn);
    
    btreeNumInsertNonfull(cache,ipage,key,less,greater);

    ipage = btreeSeccacheRead(cache,prev);

    btreeNumInsertKey(cache,ipage,mediankey,medianless,mediangtr);

    btreeDeallocSecArray(cache,savekeyarray);

    return;
}
ID btreeNumSplitroot
TY static
MO ajindex
LB core
XX
DE Split a numeric root node
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT void
RD 
RX
//


static void btreeNumSplitroot(AjPBtcache cache)
{
    AjPBtpage rootpage = NULL;
    AjPBtpage rpage    = NULL;
    AjPBtpage lpage    = NULL;
    AjPBtpage tpage    = NULL;

    ajulong *karray  = NULL;
    ajulong *tkarray = NULL;
    ajulong *parray  = NULL;
    ajulong *tparray = NULL;

    ajuint order  = 0;
    ajuint nkeys  = 0;
    ajuint keypos = 0;
    
    ajulong rblockno = 0L;
    ajulong lblockno = 0L;

    ajulong right;
    
    ajuint i;

    unsigned char *rootbuf = NULL;
    unsigned char *rbuf    = NULL;
    unsigned char *lbuf    = NULL;
    unsigned char *tbuf    = NULL;
    
    ajuint nodetype  = 0;
    ajulong overflow = 0L;
    ajulong zero     = 0L;
    ajuint rkeyno   = 0;
    ajuint n        = 0;

    ajulong lv = 0L;
    ajuint  v  = 0;

    AjPBtMem array  = NULL;    
    AjPBtMem array2 = NULL;    
    
    
#if AJINDEX_DEBUG
    ajDebug("btreeNumSplitroot %Lu\n", cache->secrootblock);
#endif
    ++statCallNumSplitroot;

    /* ajDebug("In btreeNumSplitroot\n"); */

    order = cache->sorder;

    array = btreeAllocSecArray(cache);
    karray  = array->overflows;
    parray  = array->parray;
    
    array2 = btreeAllocSecArray(cache);
    tkarray  = array2->overflows;
    tparray  = array2->parray;


    rootpage = btreeSeccacheLocate(cache,cache->secrootblock);
    rootbuf = rootpage->buf;

    nkeys = order - 1;

    keypos = nkeys / 2;

    if(!(nkeys % 2))
	--keypos;


    rblockno = cache->totsize;
    rpage = btreeSeccacheNodenew(cache);
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1671;

    lblockno = cache->totsize;
    lpage = btreeSeccacheNodenew(cache);


    /* Comment this next block out after the beta test */
    if(!cache->slevel)
    {
	fprintf(stderr,"btreeNumSplitroot: Shouldn't get here\n");
	exit(0);
	lv = zero;
	SBT_LEFT(lpage->buf,lv);
	lv = rblockno;
	SBT_RIGHT(lpage->buf,lv);
	lv = lblockno;
	SBT_LEFT(rpage->buf,lv);
	lv = zero;
	SBT_RIGHT(rpage->buf,lv);
    }

    btreeGetNumKeys(cache,rootbuf,&karray,&parray);

    /* Get key for root node and write new root node */
    tkarray[0] = karray[keypos];
    tparray[0] = lblockno;
    tparray[1] = rblockno;
    

    n = 1;
    v = n;
    SBT_NKEYS(rootbuf,v);
    btreeWriteNumNode(cache,rootpage,tkarray,tparray,1);
    right = (ajulong)(cache->slevel + 1);
    SBT_RIGHT(rootbuf,right);

    rootpage->dirty = BT_LOCK;
    rootpage->lockfor = 1672;

    rbuf = rpage->buf;
    lbuf = lpage->buf;
    
    if(cache->slevel)
	nodetype = BT_SECINTERNAL;
    else
	nodetype = BT_SECLEAF;

    v = nodetype;
    SBT_NODETYPE(rbuf,v);
    v = nodetype;
    SBT_NODETYPE(lbuf,v);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);
    lv = cache->secrootblock;
    SBT_PREV(rbuf,lv);
    lv = overflow;
    SBT_OVERFLOW(lbuf,lv);
    lv = cache->secrootblock;
    SBT_PREV(lbuf,lv);

    for(i=0;ibuf;
	lv = lblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }

    for(i=keypos+1;idirty = BT_DIRTY;

    btreeWriteNumNode(cache,rpage,tkarray,tparray,rkeyno);

    for(i=0;ibuf;
	lv = rblockno;
	SBT_PREV(tbuf,lv);
	tpage->dirty = BT_DIRTY;
    }


    ++cache->slevel;

    btreeDeallocSecArray(cache,array);
    btreeDeallocSecArray(cache,array2);
    
    return;
}
ID btreeNumKeyShift
TY static
MO ajindex
LB core
XX
DE Rebalance Nodes on insertion
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u tpage AjPBtpage
PD page
PX
RT void
RD 
RX
//


static void btreeNumKeyShift(AjPBtcache cache, AjPBtpage tpage)
{
    unsigned char *tbuf = NULL;
    unsigned char *pbuf = NULL;
    unsigned char *sbuf = NULL;
    unsigned char *buf  = NULL;

    AjPBtpage ppage = NULL;
    AjPBtpage spage = NULL;
    AjPBtpage page  = NULL;

    ajuint tkeys = 0;
    ajuint pkeys = 0;
    ajuint skeys = 0;
    ajuint order = 0;
    
    ajuint i;
    ajint ii;

    ajulong parent  = 0L;
    
    AjPBtMem array  = NULL;    
    AjPBtMem array2 = NULL;    
    AjPBtMem array3 = NULL;    
    ajulong *kTarray = NULL;
    ajulong *kParray = NULL;
    ajulong *kSarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pParray = NULL;
    ajulong *pSarray = NULL;

    ajuint pkeypos = 0;
    ajuint minsize = 0;

    ajulong lv = 0L;
    
    /* ajDebug("In btreeNumKeyShift\n"); */
    
    tbuf = tpage->buf;

    GBT_PREV(tbuf,&parent);
    GBT_NKEYS(tbuf,&tkeys);

    order = cache->sorder;
    minsize = order / 2;

    if(order % 2)
	++minsize;

    if(tkeys <= minsize)
	return;

    
    ppage = btreeSeccacheRead(cache,parent);
    pbuf = ppage->buf;
    GBT_NKEYS(pbuf,&pkeys);
    
    array = btreeAllocSecArray(cache);
    kParray  = array->overflows;
    pParray  = array->parray;
    
    array2 = btreeAllocSecArray(cache);
    kSarray  = array2->overflows;
    pSarray  = array2->parray;

    array3 = btreeAllocSecArray(cache);
    kTarray  = array3->overflows;
    pTarray  = array3->parray;


    btreeGetNumKeys(cache,tbuf,&kTarray,&pTarray);
    GBT_NKEYS(tbuf,&tkeys);


    btreeGetNumKeys(cache,pbuf,&kParray,&pParray);

    i=0;

    while(pParray[i] != tpage->pagepos)
	++i;

    if(i) /* There is another leaf to the left */
    {
	pkeypos = i-1;
	spage = btreeSeccacheRead(cache,pParray[pkeypos]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
	
    }

    if(i && skeys != order-1) /* There is space in the left leaf */
    {
	if(skeys)
	    btreeGetNumKeys(cache,sbuf,&kSarray,&pSarray);

	kSarray[skeys] = kParray[pkeypos];
	pSarray[skeys+1] = pTarray[0];
	++skeys;
	--tkeys;
	kParray[pkeypos] = kTarray[0];

	for(i=0;ipagepos == cache->secrootblock)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1681;
        }

	page = btreeSeccacheRead(cache,pSarray[skeys]);
	buf = page->buf;
	lv = spage->pagepos;
	SBT_PREV(buf,lv);
	page->dirty = BT_DIRTY;

	btreeDeallocSecArray(cache,array);
	btreeDeallocSecArray(cache,array2);
	btreeDeallocSecArray(cache,array3);

	return;
    }



    if(i != pkeys)	/* There is a right node */
    {
	pkeypos = i;
	spage = btreeSeccacheRead(cache,pParray[pkeypos+1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
    }


    if(i != pkeys && skeys != order-1) /* Space in the right node */
    {
	if(skeys)
	    btreeGetNumKeys(cache,sbuf,&kSarray,&pSarray);

	pSarray[skeys+1] = pSarray[skeys];

	for(ii=skeys-1;ii>-1;--ii)
	{
	    kSarray[ii+1] = kSarray[ii];
	    pSarray[ii+1] = pSarray[ii];
	}

	kSarray[0] = kParray[pkeypos];
	pSarray[0] = pTarray[tkeys];
	kParray[pkeypos] = kTarray[tkeys-1];
	++skeys;
	--tkeys;
	pTarray[tkeys+1] = 0L;
	
	btreeWriteNumNode(cache,spage,kSarray,pSarray,skeys);
	btreeWriteNumNode(cache,tpage,kTarray,pTarray,tkeys);
	btreeWriteNumNode(cache,ppage,kParray,pParray,pkeys);

	if(ppage->pagepos == cache->secrootblock)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1682;
        }

	page = btreeSeccacheRead(cache,pSarray[0]);
	buf = page->buf;
	lv = spage->pagepos;
	SBT_PREV(buf,lv);
	page->dirty = BT_DIRTY;

	btreeDeallocSecArray(cache,array);
	btreeDeallocSecArray(cache,array2);
	btreeDeallocSecArray(cache,array3);

	return;
    }


    btreeDeallocSecArray(cache,array);
    btreeDeallocSecArray(cache,array2);
    btreeDeallocSecArray(cache,array3);

    return;
}
ID btreeNumInsertShift
TY static
MO ajindex
LB core
XX
DE Rebalance buckets on insertion
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u retpage AjPBtpage*
PD page
PX
PN [3]
PA r key ajulong
PD key
PX
RT ajulong
RD bucket block or 0L if shift not possible
RX
//


static ajulong btreeNumInsertShift(AjPBtcache cache, AjPBtpage *retpage,
                                   ajulong key)
{
    unsigned char *tbuf = NULL;
    unsigned char *pbuf = NULL;
    unsigned char *sbuf = NULL;

    AjPBtpage ppage = NULL;
    AjPBtpage spage = NULL;
    AjPBtpage tpage = NULL;

    ajuint tkeys = 0;
    ajuint pkeys = 0;
    ajuint skeys = 0;
    ajuint order = 0;
    
    ajuint i;
    ajuint n;
    ajint ii;
    
    ajulong parent  = 0L;
    ajulong blockno = 0L;
    
    ajulong *kTarray = NULL;
    ajulong *kParray = NULL;
    ajulong *kSarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pParray = NULL;
    ajulong *pSarray = NULL;

    ajulong *karray = NULL;
    ajulong *parray = NULL;

    ajuint ppos    = 0;
    ajuint pkeypos = 0;
    ajuint minsize = 0;

    AjPBtMem array  = NULL;    
    AjPBtMem array2 = NULL;    
    AjPBtMem array3 = NULL;    


    
    /* ajDebug("In btreeNumInsertShift\n"); */


    tpage = *retpage;

    tbuf = tpage->buf;

    GBT_PREV(tbuf,&parent);
    GBT_NKEYS(tbuf,&tkeys);


    order = cache->sorder;
    minsize = order / 2;

    if(order % 2)
	++minsize;

    if(tkeys <= minsize)
	return 0L;

    ppage = btreeSeccacheRead(cache,parent);


    
    pbuf = ppage->buf;
    GBT_NKEYS(pbuf,&pkeys);

    
    array = btreeAllocSecArray(cache);
    kParray  = array->overflows;
    pParray  = array->parray;
    
    array2 = btreeAllocSecArray(cache);
    kSarray  = array2->overflows;
    pSarray  = array2->parray;

    array3 = btreeAllocSecArray(cache);
    kTarray  = array3->overflows;
    pTarray  = array3->parray;


    btreeGetNumKeys(cache,pbuf,&kParray,&pParray);

    i=0;

    while(i!=pkeys && key >= kParray[i])
	++i;

    pkeypos = i;
    
    if(i==pkeys)
    {
	if(key < kParray[i-1])
	    ppos = i-1;
	else
	    ppos = i;
    }
    else
	ppos = i;

    
    if(ppos) /* There is another leaf to the left */
    {
	spage = btreeSeccacheRead(cache,pParray[ppos-1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
    }

    if(i && skeys != order-1) /* There is space in the left leaf */
    {
	/* ajDebug("Left shift\n"); */
	btreeGetNumKeys(cache,tbuf,&kTarray,&pTarray);
	if(skeys)
	    btreeGetNumKeys(cache,sbuf,&kSarray,&pSarray);

	i = 0;

	while(pParray[i] != tpage->pagepos)
	    ++i;

	--i;

	pkeypos = i;

	kSarray[skeys] = kParray[pkeypos];
	pSarray[skeys+1] = pTarray[0];
	++skeys;
	--tkeys;
	kParray[pkeypos] = kTarray[0];

	for(i=0;ipagepos == cache->secrootblock)
        {
	    ppage->dirty = BT_LOCK;
            ppage->lockfor = 1691;
       }

	i = 0;

	while(i!=pkeys && key >= kParray[i])
	    ++i;

	if(i==pkeys)
	{
	    if(key < kParray[i-1])
		blockno = pParray[i-1];
	    else
		blockno = pParray[i];
	}
	else
	    blockno = pParray[i];

	if(blockno == spage->pagepos)
	{
	    *retpage = spage;
	    karray = kSarray;
	    parray = pSarray;
	    n = skeys;
	}
	else
	{
	    karray = kTarray;
	    parray = pTarray;
	    n = tkeys;
	}
	

	i = 0;

	while(i!=n && key >= karray[i])
	    ++i;

	if(i==n)
	{
	    if(key < karray[i-1])
		blockno = parray[i-1];
	    else
		blockno = parray[i];
	}
	else
	    blockno = parray[i];

	btreeDeallocSecArray(cache,array);
	btreeDeallocSecArray(cache,array2);
	btreeDeallocSecArray(cache,array3);

	/* ajDebug("... returns blockno (a) %Lu\n",blockno); */

	return blockno;
    }
    

    if(ppos != pkeys)	/* There is a right node */
    {
	spage = btreeSeccacheRead(cache,pParray[ppos+1]);
	sbuf = spage->buf;
	GBT_NKEYS(sbuf,&skeys);
    }


    /* Space in the right leaf */
    if(ppos != pkeys && skeys != order-1)
    {
	/* ajDebug("Right shift\n"); */
	btreeGetNumKeys(cache,tbuf,&kTarray,&pTarray);
	btreeGetNumKeys(cache,sbuf,&kSarray,&pSarray);

	i = 0;

	while(pParray[i] != tpage->pagepos)
	    ++i;

	pkeypos = i;
	
	pSarray[skeys+1] = pSarray[skeys];

	for(ii=skeys-1;ii>-1;--ii)
	{
	    kSarray[ii+1] = kSarray[ii];
	    pSarray[ii+1] = pSarray[ii];
	}

	kSarray[0] = kParray[pkeypos];
	pSarray[0] = pTarray[tkeys];
	kParray[pkeypos] = kTarray[tkeys-1];
	++skeys;
	--tkeys;
	pTarray[tkeys+1] = 0L;
	
	btreeWriteNumNode(cache,spage,kSarray,pSarray,skeys);
	btreeWriteNumNode(cache,tpage,kTarray,pTarray,tkeys);
	btreeWriteNumNode(cache,ppage,kParray,pParray,pkeys);
	if(ppage->pagepos == cache->secrootblock)
        {
            ppage->dirty = BT_LOCK;
            ppage->lockfor = 1692;
        }

	i = 0;

	while(i!=pkeys && key >= kParray[i])
	    ++i;

	if(i==pkeys)
	{
	    if(key < kParray[i-1])
		blockno = pParray[i-1];
	    else
		blockno = pParray[i];
	}
	else
	    blockno = pParray[i];

	if(blockno == spage->pagepos)
	{
	    *retpage = spage;
	    karray = kSarray;
	    parray = pSarray;
	    n = skeys;
	}
	else
	{
	    karray = kTarray;
	    parray = pTarray;
	    n = tkeys;
	}
	
	i = 0;

	while(i!=n && key >= karray[i])
	    ++i;

	if(i==n)
	{
	    if(key < karray[i-1])
		blockno = parray[i-1];
	    else
		blockno = parray[i];
	}
	else
	    blockno = parray[i];

	btreeDeallocSecArray(cache,array);
	btreeDeallocSecArray(cache,array2);
	btreeDeallocSecArray(cache,array3);

	/* ajDebug("... returns blockno (b) %Lu\n",blockno); */
	
	return blockno;
    }


    btreeDeallocSecArray(cache,array);
    btreeDeallocSecArray(cache,array2);
    btreeDeallocSecArray(cache,array3);

    /* ajDebug("... returns 0L\n"); */

    return 0L;
}
ID btreeNumInsert
TY static
MO ajindex
LB core
XX
DE Insert a file offset key into a secondary tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r num const AjPBtNumId
PD Id object
PX
PN [3]
PA w page AjPBtpage
PD cache page
PX
RT void
RD pointer to a page
RX
//


static void btreeNumInsert(AjPBtcache cache, const AjPBtNumId num,
                           AjPBtpage page)
{
    unsigned char *buf = NULL;
    ajulong numkey;
    AjPBtpage spage  = NULL;
    ajuint nkeys = 0;
    ajuint nodetype = 0;

    ajulong lblockno;
    ajulong rblockno;
    ajulong blockno;
    ajulong shift;
    ajuint n;

    /* ajDebug("In btreeNumInsert\n"); */

    (void) page;			/* make it used */

    numkey = num->offset;

    spage = btreeNumFind(cache,numkey);
    buf = spage->buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&nodetype);

    if(!nkeys)
    {
	lblockno = cache->totsize;
	btreeWriteNumbucketEmpty(cache,lblockno);
	rblockno = cache->totsize;
	btreeWriteNumbucketEmpty(cache,rblockno);

	btreeWriteNumNodeSingle(cache,spage,numkey,lblockno,rblockno);

	btreeNumbucketAdd(cache,rblockno,num);

	return;
    }

    blockno = btreeGetBlockN(cache,buf,numkey);

    if(nodetype != BT_SECROOT)
	if((shift = btreeNumInsertShift(cache,&spage,numkey)))
	    blockno = shift;


    buf = spage->buf;
    n = btreeNumInNumbucket(cache,blockno);

    if(n == cache->snperbucket)
    {
	if(btreeReorderNumbuckets(cache,spage))
	{
            blockno = btreeGetBlockN(cache,buf,numkey);
	}
	else
	{
	    btreeNumSplitleaf(cache,spage);
	    spage = btreeNumFind(cache,numkey);
	    buf = spage->buf;

	    blockno = btreeGetBlockN(cache,buf,numkey);
	}
    }

    btreeNumbucketAdd(cache,blockno,num);

    return;
}
ID btreeNumSplitleaf
TY static
MO ajindex
LB core
XX
DE Split a leaf and propagate up if necessary
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u spage AjPBtpage
PD page
PX
RT AjPBtpage
RD pointer to a parent page
RX
//


static AjPBtpage btreeNumSplitleaf(AjPBtcache cache, AjPBtpage spage)
{
    ajuint nkeys     = 0;
    ajuint totalkeys = 0;
    ajuint keylimit  = 0;
    ajuint order     = 0;
    ajuint nodetype  = 0;

    ajuint rootnodetype = 0;
    
    ajuint i;
    ajuint j;
    ajuint iref;

    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage page  = NULL;
    
    ajulong mediankey  = 0L;
    ajulong mediangtr  = 0L;
    ajulong medianless = 0L;

    AjPBtNumId bid = NULL;

    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;

    AjPList idlist = NULL;

    AjPNumbucket cbucket  = NULL;
    
    AjPBtMem array = NULL;
    AjPBtMem newarray = NULL;
    ajulong *newnarray = NULL;
    ajulong *parray = NULL;
    ajulong *newparray = NULL;
    
    ajuint lno    = 0;
    ajuint rno    = 0;

    ajuint lbucketlimit   = 0;
    ajuint rbucketlimit   = 0;
    ajuint lmaxnperbucket = 0;
    ajuint rmaxnperbucket = 0;
    ajuint count         = 0;
    
    ajulong lblockno = 0L;
    ajulong rblockno = 0L;
    ajulong overflow = 0L;
    ajulong prevsave = 0L;
    
    ajulong zero = 0L;
    ajulong join = 0L;

    ajuint iold = 0;

    ajulong lv = 0L;
    ajuint  v  = 0;

    
#if AJINDEX_DEBUG
    ajDebug("btreeNumSplitleaf %Lu dirty:%u lockfor:%u\n",
            spage->pagepos, spage->dirty, spage->lockfor);
#endif
    ++statCallNumSplitleaf;

    /* ajDebug("In btreeNumSplitleaf\n"); */

    order = cache->sorder;

    array = btreeAllocSecArray(cache);
    parray = array->parray;
    newarray = btreeAllocSecArray(cache);
    newparray = newarray->parray;
    newnarray = newarray->overflows;

    buf = spage->buf;
    lbuf = buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&rootnodetype);

#if AJINDEX_DEBUG
    ajDebug("    nkeys:%u nodetype:%u\n", nkeys, rootnodetype);
#endif

    if(rootnodetype == BT_SECROOT)
    {
#if AJINDEX_DEBUG
	ajDebug("   Root leaf splitting page:%Lu secroot:%Lu\n",
                cache->totsize, cache->secrootblock);
#endif
	lblockno = cache->totsize;
	lpage = btreeSeccacheNodenew(cache);
	lbuf = lpage->buf;
	lv = cache->secrootblock;
	SBT_PREV(lbuf,lv);
    }
    else
    {
	lblockno = spage->pagepos;
	lpage = spage;
    }

    lpage->dirty = BT_LOCK;
    lpage->lockfor = 1701;

    rblockno = cache->totsize;
    rpage = btreeSeccacheNodenew(cache);
    rbuf = rpage->buf;
    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1702;

    if(rootnodetype == BT_SECROOT)
    {
	lv = zero;
	SBT_RIGHT(rbuf,lv);
	lv = zero;
	SBT_LEFT(lbuf,lv);
    }
    else
    {
	GBT_RIGHT(lbuf,&join);
	lv = join;
	SBT_RIGHT(rbuf,lv);
    }

    lv = lblockno;
    SBT_LEFT(rbuf,lv);
    lv = rblockno;
    SBT_RIGHT(lbuf,lv);


    btreeGetNumPointers(cache,buf,&parray);


    keylimit = nkeys+1;
    idlist = ajListNew();
    for(i=0;isnperbucket,
                         &lbucketlimit,&lmaxnperbucket,&lno,
                         &rbucketlimit,&rmaxnperbucket,&rno);

    cbucket = btreeNumbucketNew(cache->snperbucket, cache->refcount);

    count = 0;
    iold = 0;

    for(i=0;iNentries = 0;

	for(j=0;jNumId[j]->dbno      = bid->dbno;
	    cbucket->NumId[j]->offset    = bid->offset;

            if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                    cbucket->NumId[j]->refoffsets[iref] = bid->refoffsets[iref];
	    }

	    ++count;
	    ++cbucket->Nentries;
	    AJFREE(bid);
	}

	ajListPeek(idlist,(void **)&bid);

	newnarray[i] = bid->offset;

        if((iold < order) && parray[iold])
            newparray[i] = parray[iold++];
        else
            newparray[i] = cache->totsize;

	btreeWriteNumbucket(cache,cbucket,newparray[i]);
    }

    cbucket->Nentries = 0;

    j = 0;

    while(count != lno)
    {
	ajListPop(idlist,(void **)&bid);

	cbucket->NumId[j]->dbno      = bid->dbno;
	cbucket->NumId[j]->offset    = bid->offset;

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
                cbucket->NumId[j]->refoffsets[iref] = bid->refoffsets[iref];
        }

	++j;
	++count;


	++cbucket->Nentries;
	AJFREE(bid);
    }

    if((iold < order) && parray[iold])
        newparray[i] = parray[iold++];
    else
        newparray[i] = cache->totsize;

    btreeWriteNumbucket(cache,cbucket,newparray[i]);

    nkeys = lbucketlimit;
    nodetype = BT_SECLEAF;
    v = nodetype;
    SBT_NODETYPE(lbuf,v);
    GBT_PREV(lbuf,&prevsave);
    lpage->dirty = BT_DIRTY;

    btreeWriteNumNode(cache,lpage,newnarray,newparray,nkeys);

    ajListPeek(idlist,(void **)&bid);
    mediankey = bid->offset;

    for(i=0;iNentries = 0;

	for(j=0;jNumId[j]->dbno      = bid->dbno;
	    cbucket->NumId[j]->offset    = bid->offset;
	    
            if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                    cbucket->NumId[j]->refoffsets[iref] = bid->refoffsets[iref];
            }

	    ++cbucket->Nentries;
	    AJFREE(bid);
	}

	ajListPeek(idlist,(void **)&bid);
	newnarray[i] = bid->offset;

        if((iold < order) && parray[iold])
            newparray[i] = parray[iold++];
        else
            newparray[i] = cache->totsize;

	btreeWriteNumbucket(cache,cbucket,newparray[i]);
    }

    cbucket->Nentries = 0;

    j = 0;

    while(ajListPop(idlist,(void**)&bid))
    {
	cbucket->NumId[j]->dbno      = bid->dbno;
	cbucket->NumId[j]->offset    = bid->offset;
	
        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
                cbucket->NumId[j]->refoffsets[iref] = bid->refoffsets[iref];
        }

	++j;


	++cbucket->Nentries;
	AJFREE(bid);
    }
    
    if((iold < order) && parray[iold])
        newparray[i] = parray[iold++];
    else
        newparray[i] = cache->totsize;

    btreeWriteNumbucket(cache,cbucket,newparray[i]);

#if AJINDEX_DEBUG
    ajDebug("end of NumSplitleaf i:%u iold:%u, order:%u\n", i, iold, order);
#endif
    nkeys = rbucketlimit;

    nodetype = BT_SECLEAF;
    v = nodetype;
    SBT_NODETYPE(rbuf,v);
    lv = prevsave;
    SBT_PREV(rbuf,lv);
    lv = overflow;
    SBT_OVERFLOW(rbuf,lv);
    
    btreeWriteNumNode(cache,rpage,newnarray,newparray,nkeys);
    rpage->dirty = BT_DIRTY;

    btreeNumbucketDel(&cbucket);
    ajListFree(&idlist);



    medianless = lblockno;
    mediangtr  = rblockno;


    if(rootnodetype == BT_SECROOT)
    {
	spage->dirty = BT_DIRTY;
	btreeWriteNumNodeSingle(cache,spage,mediankey,lblockno,rblockno);	
	++cache->slevel;
	lv = cache->slevel;
	SBT_RIGHT(buf,lv);
	spage->dirty = BT_LOCK;
        spage->lockfor = 1703;

	btreeDeallocSecArray(cache,array);
        btreeDeallocSecArray(cache,newarray);

	return spage;
    }

    btreeDeallocSecArray(cache,array);
    btreeDeallocSecArray(cache,newarray);


    page = btreeSeccacheRead(cache,prevsave);
    btreeNumInsertKey(cache,page,mediankey,medianless,mediangtr);


    page = btreeSeccacheRead(cache,prevsave);

    return page;
}
ID btreeFreePriArray
TY static
MO ajindex
LB core
XX
DE Free karray and parray arrays for a primary key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT void
RD 
RX
//


static void btreeFreePriArray(AjPBtcache cache)
{
    AjPBtMem p;
    AjPBtMem next;
    ajuint i;

    /* ajDebug("In btreeFreePriArray\n"); */
    
    if(!cache->bmem)
	return;

    p = cache->bmem;
    
    while(p)
    {
        next = p->next;

        AJFREE(p->parray);
	AJFREE(p->overflows);

	for(i=0;iporder;++i)
	    ajStrDel(&p->karray[i]);

	AJFREE(p->karray);
	AJFREE(p);
	p = next;
    }
    

    cache->bmem = NULL;
    cache->tmem = NULL;

    return;
}
ID btreeFreeSecArray
TY static
MO ajindex
LB core
XX
DE Free karray and parray arrays for a secondary key
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT void
RD 
RX
//


static void btreeFreeSecArray(AjPBtcache cache)
{
    AjPBtMem p;
    AjPBtMem next;
    ajuint i;

    /* ajDebug("In btreeFreeSecArray\n"); */
    
    if(!cache->bsmem)
	return;

    p = cache->bsmem;
    
    while(p)
    {
        next = p->next;

	AJFREE(p->parray);
	AJFREE(p->overflows);

	for(i=0;isorder;++i)
	    ajStrDel(&p->karray[i]);

	AJFREE(p->karray);
	AJFREE(p);
	
	p = next;
    }
    

    cache->bsmem = NULL;
    cache->tsmem = NULL;

    return;
}
ID btreeIdentFetchMulti
TY static
MO ajindex
LB core
XX
DE Read the leaves of a secondary hybrid tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r idname const AjPStr
PD id name
PX
PN [3]
PA r rootblock ajulong
PD root page of secondary tree
PX
PN [4]
PA u list AjPList
PD list to add BtIDs to
PX
RT void
RD 
RX
//


static void btreeIdentFetchMulti(AjPBtcache cache, const AjPStr idname,
                                 ajulong rootblock,
                                 AjPList list)
{
    AjPBtMem array = NULL;
    ajulong *parray;

    AjPBtpage page;
    unsigned char *buf;

    ajuint nodetype;
    ajuint i;
    ajulong level = 0L;
    
    ajuint nkeys;
    ajulong right;

    array = btreeAllocSecArray(cache);
    parray = array->parray;

    page = btreeSeccacheRead(cache, rootblock);
    buf = page->buf;

    GBT_RIGHT(buf,&level);
    cache->slevel = (ajuint) level;

    btreeGetNumPointers(cache,buf,&parray);
    GBT_NODETYPE(buf,&nodetype);

    while(nodetype != BT_SECLEAF && cache->slevel != 0)
    {
	page = btreeSeccacheRead(cache,parray[0]);
	buf = page->buf;
	btreeGetNumPointers(cache,buf,&parray);
	GBT_NODETYPE(buf,&nodetype);
    }

    do
    {
	GBT_NKEYS(buf,&nkeys);

	for(i=0;i<=nkeys;++i)
	    btreeNumbucketBtidlist(cache,parray[i],idname,list);

	right = 0L;
	if(cache->slevel)
	{
	    GBT_RIGHT(buf,&right);

	    if(right)
	    {
		page = btreeSeccacheRead(cache,right);
		buf = page->buf;
		btreeGetNumPointers(cache,buf,&parray);
	    }
	}
    } while(right);

    btreeDeallocSecArray(cache,array);

    return;
}
ID btreeIdentFetchMultiHit
TY static
MO ajindex
LB core
XX
DE Read the leaves of a secondary hybrid tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r rootblock ajulong
PD root page of secondary tree
PX
PN [3]
PA u list AjPList
PD list to add BtHits to
PX
RT void
RD 
RX
//


static void btreeIdentFetchMultiHit(AjPBtcache cache,
                                    ajulong rootblock,
                                    AjPList list)
{
    AjPBtMem array = NULL;
    ajulong *parray;

    AjPBtpage page;
    unsigned char *buf;

    ajuint nodetype;
    ajuint i;
    ajulong level = 0L;
    
    ajuint nkeys;
    ajulong right;

    array = btreeAllocSecArray(cache);
    parray = array->parray;

    page = btreeSeccacheRead(cache, rootblock);
    buf = page->buf;

    GBT_RIGHT(buf,&level);
    cache->slevel = (ajuint) level;

    btreeGetNumPointers(cache,buf,&parray);
    GBT_NODETYPE(buf,&nodetype);

    while(nodetype != BT_SECLEAF && cache->slevel != 0)
    {
	page = btreeSeccacheRead(cache,parray[0]);
	buf = page->buf;
	btreeGetNumPointers(cache,buf,&parray);
	GBT_NODETYPE(buf,&nodetype);
    }

    do
    {
	GBT_NKEYS(buf,&nkeys);

	for(i=0;i<=nkeys;++i)
	    btreeNumbucketBthitlist(cache,parray[i],list);

	right = 0L;
	if(cache->slevel)
	{
	    GBT_RIGHT(buf,&right);

	    if(right)
	    {
		page = btreeSeccacheRead(cache,right);
		buf = page->buf;
		btreeGetNumPointers(cache,buf,&parray);
	    }
	}
    } while(right);

    btreeDeallocSecArray(cache,array);

    return;
}
ID btreeIdentFetchMultiHitref
TY static
MO ajindex
LB core
XX
DE Read the leaves of a secondary hybrid tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r rootblock ajulong
PD root page of secondary tree
PX
PN [3]
PA u list AjPList
PD list to add BtHitrefs to
PX
RT void
RD 
RX
//


static void btreeIdentFetchMultiHitref(AjPBtcache cache,
                                       ajulong rootblock,
                                       AjPList list)
{
    AjPBtMem array = NULL;
    ajulong *parray;

    AjPBtpage page;
    unsigned char *buf;

    ajuint nodetype;
    ajuint i;
    ajulong level = 0L;
    
    ajuint nkeys;
    ajulong right;

    array = btreeAllocSecArray(cache);
    parray = array->parray;

    page = btreeSeccacheRead(cache, rootblock);
    buf = page->buf;

    GBT_RIGHT(buf,&level);
    cache->slevel = (ajuint) level;

    btreeGetNumPointers(cache,buf,&parray);
    GBT_NODETYPE(buf,&nodetype);

    while(nodetype != BT_SECLEAF && cache->slevel != 0)
    {
	page = btreeSeccacheRead(cache,parray[0]);
	buf = page->buf;
	btreeGetNumPointers(cache,buf,&parray);
	GBT_NODETYPE(buf,&nodetype);
    }

    do
    {
	GBT_NKEYS(buf,&nkeys);

	for(i=0;i<=nkeys;++i)
	    btreeNumbucketBthitreflist(cache,parray[i],list);

	right = 0L;
	if(cache->slevel)
	{
	    GBT_RIGHT(buf,&right);

	    if(right)
	    {
		page = btreeSeccacheRead(cache,right);
		buf = page->buf;
		btreeGetNumPointers(cache,buf,&parray);
	    }
	}
    } while(right);

    btreeDeallocSecArray(cache,array);

    return;
}
ID ajBtreeDumpIdentifiers
TY public
MO ajindex
LB core
XX
DE Read the leaves of an identifier tree
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r dmin ajuint
PD minimum number of times the key should appear
PX
PN [3]
PA r dmax ajuint
PD maximum number of times the key should appear
PX
PN [4]
PA u outf AjPFile
PD output file
PX
RT void
RD 
RX
//


void ajBtreeDumpIdentifiers(AjPBtcache cache, ajuint dmin, ajuint dmax,
                            AjPFile outf)
{
    AjPBtMem array = NULL;
    AjPStr *karray;
    ajulong *parray;

    AjPBtpage page;
    unsigned char *buf;

    ajuint nodetype;
    ajuint i;
    ajuint j;
    ajuint dups;
    
    AjPIdbucket bucket;
    ajuint nkeys;
    ajulong right;

    if(cache->secondary)
    {
        ajBtreeDumpKeywords(cache, dmin, dmax, outf);
        return;
    }

    array = btreeAllocPriArray(cache);
    karray = array->karray;
    parray = array->parray;

    page = btreePricacheRead(cache, 0L);
    buf = page->buf;

    btreeGetKeys(cache,buf,&karray,&parray);
    GBT_NODETYPE(buf,&nodetype);

    while(nodetype != BT_LEAF && cache->plevel != 0)
    {
	page = btreePricacheRead(cache,parray[0]);
	buf = page->buf;
	btreeGetKeys(cache,buf,&karray,&parray);
	GBT_NODETYPE(buf,&nodetype);
    }

    do
    {
	GBT_NKEYS(buf,&nkeys);
	for(i=0;i<=nkeys;++i)
	{
	    bucket = btreeReadIdbucket(cache,parray[i]);
            btreeIdbucketSort(bucket);

	    for(j=0;jNentries;++j)
	    {
		dups = bucket->Ids[j]->dups;

		if(!dups)
		    dups = 1;
		
		if(dups < dmin)
		    continue;

		if(dmax && dups > dmax)
		    continue;

		ajFmtPrintF(outf,"%10d %S\n",
			    dups, bucket->Ids[j]->id);
	    }

	    btreeIdbucketDel(&bucket);
	}

	right = 0L;

	if(cache->plevel)
	{
	    GBT_RIGHT(buf,&right);

	    if(right)
	    {
		page = btreePricacheRead(cache,right);
		buf = page->buf;
		btreeGetKeys(cache,buf,&karray,&parray);
	    }
	}
    } while(right);

    btreeDeallocPriArray(cache,array);

    return;
}
ID ajBtreeDeleteIdent
TY public
MO ajindex
LB core
XX
DE Entry point for hybrid ID deletion.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r btid const AjPBtId
PD hybrid object
PX
RT AjBool
RD True if found and deleted
RX
//


AjBool ajBtreeDeleteIdent(AjPBtcache cache, const AjPBtId btid)
{
    AjPBtpage rootpage = NULL;
    AjPBtpage spage   = NULL;
    AjPStr key        = NULL;
    AjPIdbucket bucket  = NULL;
    ajulong blockno  = 0L;

    ajuint nkeys = 0;

    ajuint nodetype = 0;
    ajuint nentries = 0;
    
    AjPStr *karray = NULL;
    ajulong *parray = NULL;
    AjPBtMem arrays = NULL;
    AjBool found = ajFalse;
    ajuint dups = 0;
    
    ajuint i;
    
    unsigned char *buf = NULL;
    AjPBtId did = NULL;
    ajulong  secrootpage = 0L;
    AjBool ret = ajFalse;
    
    /* ajDebug("In ajBtreeDeleteIdent\n"); */


    if(!ajStrGetLen(btid->id))
	return ajFalse;

    spage = btreeIdentFind(cache,btid->id);
    buf = spage->buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_NODETYPE(buf,&nodetype);
    
    arrays = btreeAllocPriArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;
    
    if(!nkeys)
    {
	btreeDeallocPriArray(cache,arrays);
	ajStrDel(&key);

	return ajFalse;
    }


    /* Search to see whether entry exists */
    
    btreeGetKeys(cache,buf,&karray,&parray);

    i=0;

    while(i!=nkeys && MAJSTRCMPS(btid->id,karray[i])>=0)
	++i;

    blockno = parray[i];


    bucket = btreeReadIdbucket(cache,blockno);
    
    nentries = bucket->Nentries;
    
    found = ajFalse;

    for(i=0;iid,bucket->Ids[i]->id))
	{
	    found = ajTrue;
	    break;
	}
    

    if(!found)
    {
	btreeDeallocPriArray(cache,arrays);

	return ajFalse;
    }
    

    dups = bucket->Ids[i]->dups;
    

    if(!dups)
    {
        /* ajDebug("No secondary tree\n"); */
        rootpage = btreePricacheLocate(cache,0L);

        if(!rootpage)
            ajFatal("Rootpage has been unlocked (ajBtreeDeleteHybId)");
    
        rootpage->dirty = BT_LOCK;
        rootpage->lockfor = 1711;

        btreeFindIdentBalanceOne(cache,0L,BTNO_NODE,BTNO_NODE,BTNO_NODE,
                                 BTNO_NODE,btid);


        btreeDeallocPriArray(cache,arrays);

        if(cache->dodelete)
            ret = ajTrue;
        else
            ret = ajFalse;
    }
    else
    {
        did = bucket->Ids[i];
        secrootpage = did->offset;
        cache->secrootblock = secrootpage;

        ret = btreeDeleteIdentIdTwo(cache,btid,did);

        btreeWriteIdbucket(cache,bucket,blockno);
    }
    

    return ret;
}
ID btreeFindIdentBalanceOne
TY static
MO ajindex
LB core
XX
DE Master routine for entry deletion from level 1 identifier tree.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD Current node
PX
PN [3]
PA r leftNode ajulong
PD Node to left
PX
PN [4]
PA r rightNode ajulong
PD Node to right
PX
PN [5]
PA r lAnchor ajulong
PD Left anchor
PX
PN [6]
PA r rAnchor ajulong
PD Right anchor
PX
PN [7]
PA r btid const AjPBtId
PD Id object
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeFindIdentBalanceOne(AjPBtcache cache, ajulong thisNode,
                                        ajulong leftNode, ajulong rightNode,
                                        ajulong lAnchor, ajulong rAnchor,
                                        const AjPBtId btid)
{
    unsigned char *buf  = NULL;
    unsigned char *buf1 = NULL;
    
    ajulong nextNode   = BTNO_NODE;
    ajulong nextLeft   = BTNO_NODE;
    ajulong nextRight  = BTNO_NODE;
    ajulong nextAncL   = BTNO_NODE;
    ajulong nextAncR   = BTNO_NODE;
    ajulong done       = 0L;

    ajuint  nkeys      = 0;
    ajuint  order      = 0;
    ajuint  minkeys    = 0;
    ajuint  i;
    ajuint   nodetype   = 0;

    ajuint n1keys      = 0;
    
    AjPBtpage page  = NULL;
    AjPBtpage page1 = NULL;

    ajulong balanceNode = 0L;
    ajulong blockno     = 0L;
    ajulong ptrSave     = 0L;

    AjPStr *karray  = NULL;
    ajulong *parray  = NULL;
    AjPStr *k1array = NULL;
    ajulong *p1array = NULL;

    AjPBtMem arrays  = NULL;
    AjPBtMem arrays1 = NULL;
    
    AjBool existed = ajFalse;
    
    /* ajDebug("In btreeFindBalance\n"); */

    if(thisNode)
	page = btreePricacheRead(cache,thisNode);
    else
    {   /* It's the root node of the primary hyb tree */
        /* Needs altering for secondary tree          */
	page = btreePricacheLocate(cache,thisNode);
	page->dirty = BT_LOCK;
        page->lockfor = 1721;
    }

    cache->dodelete = ajFalse;

    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);

    order = cache->porder;
    /* order-1 is the number of keys in the node */
    minkeys = (order-1) / 2;

    if((order-1)%2)
	++minkeys;

    /*
    ** If thisNode contains >= minkeys then it is not a candidate
    ** for balancing
    */
    if(nkeys >= minkeys)
	balanceNode = BTNO_BALANCE;
    else
	balanceNode = page->pagepos;

    arrays  = btreeAllocPriArray(cache);
    arrays1 = btreeAllocPriArray(cache);

    karray = arrays->karray;
    parray = arrays->parray;

    k1array = arrays1->karray;
    p1array = arrays1->parray;

    btreeGetKeys(cache,buf,&karray,&parray);

    i=0;

    while(i!=nkeys && MAJSTRCMPS(btid->id,karray[i])>=0)
	++i;

    blockno = parray[i];

    nextNode = blockno;
    ptrSave = i;

    GBT_NODETYPE(buf,&nodetype);

    if(!(nodetype == BT_LEAF) && !(nodetype == BT_ROOT && !cache->plevel))
    {
	if(nextNode == parray[0])
	{
	    if(leftNode != BTNO_NODE)
	    {
		page1 = btreePricacheRead(cache,leftNode);
		buf1 = page1->buf;
		GBT_NKEYS(buf1,&n1keys);
		btreeGetKeys(cache,buf1,&k1array,&p1array);
		nextLeft = p1array[n1keys];
	    }
	    else
		nextLeft = BTNO_NODE;
	    
	    if(!thisNode)
		nextAncL = thisNode;
	    else
		nextAncL = lAnchor;
	}
	else
	{
	    nextLeft = parray[ptrSave-1];
	    nextAncL = thisNode;
	}

	if(nextNode == parray[nkeys])
	{
	    if(rightNode != BTNO_NODE)
	    {
		page1 = btreePricacheRead(cache,rightNode);
		buf1 = page1->buf;
		GBT_NKEYS(buf1,&n1keys);
		btreeGetKeys(cache,buf1,&k1array,&p1array);
		nextRight = p1array[0];
	    }
	    else
		nextRight = BTNO_NODE;

	    if(!thisNode)
		nextAncR = thisNode;
	    else
		nextAncR = rAnchor;
	}
	else
	{
	    nextRight = parray[ptrSave+1];
	    nextAncR  = thisNode;
	}



	/* Check to see whether key exists in an internal node */
	if(nodetype != BT_LEAF && cache->plevel)
	{
	    i=0;

	    while(i!=nkeys && MAJSTRCMPS(btid->id,karray[i]))
		++i;

	    if(i!=nkeys)
	    {
		btreeFindHybMinOne(cache,parray[i+1],btid->id);
		ajStrAssignS(&karray[i],cache->replace);
		btreeWriteNode(cache,page,karray,parray,nkeys);
	    }
	
	}
	
	btreeFindIdentBalanceOne(cache,nextNode,nextLeft,nextRight,
                                 nextAncL,nextAncR,btid);

	if(thisNode)
	    page = btreePricacheRead(cache,thisNode);
	else
	{
	    page = btreePricacheLocate(cache,thisNode);
	    page->dirty = BT_LOCK;
            page->lockfor = 1722;
	}
	buf = page->buf;

    }
    else
    {
	if(nodetype == BT_LEAF || (nodetype==BT_ROOT && !cache->plevel))
	{
	    existed = btreeRemoveIdentEntryOne(cache,thisNode,btid);

	    if(existed)
		cache->dodelete = ajTrue;

	    GBT_NKEYS(buf,&nkeys);

	    if(nkeys >= minkeys || (nodetype==BT_ROOT && !cache->plevel))
		balanceNode = BTNO_BALANCE;
	    else
		balanceNode = page->pagepos;
	}
    }


    if(balanceNode == BTNO_BALANCE || thisNode == 0L)
	done = BTNO_NODE;
    else
	done = btreeRebalanceHybOne(cache,thisNode,leftNode,rightNode,
                                    lAnchor,rAnchor);
    

    btreeDeallocPriArray(cache,arrays);
    btreeDeallocPriArray(cache,arrays1);

    return done;
}
ID btreeFindHybMinOne
TY static
MO ajindex
LB core
XX
DE Find minimum key in hybrid level 1 subtree and store in cache.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page
PX
PN [3]
PA r key const AjPStr
PD key
PX
RT void
RD 
RX
//


static void btreeFindHybMinOne(AjPBtcache cache, ajulong pagepos,
                               const AjPStr key)
{
    AjPBtpage page   = NULL;
    AjPIdbucket bucket = NULL;
    AjPStr *karray   = NULL;
    ajulong *parray   = NULL;

    ajuint nkeys     = 0;
    ajuint  nodetype = 0;
    ajuint nentries  = 0;
    ajuint i;
    AjPBtMem arrays = NULL;
    
    unsigned char *buf = NULL;

    /* ajDebug("In btreeFindHybMinOne\n"); */

    arrays = btreeAllocPriArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;

    page = btreePricacheRead(cache,pagepos);
    buf  = page->buf;
    GBT_NODETYPE(buf,&nodetype);
    GBT_NKEYS(buf,&nkeys);

    btreeGetKeys(cache,buf,&karray,&parray);

    if(nodetype == BT_LEAF)
    {
	bucket = btreeReadIdbucket(cache,parray[0]);
	nentries = bucket->Nentries;

        /*
        ** If there's only one entry then it must be the key marked
        ** for deletion
        */
        if(nentries<2)
	{
	    btreeIdbucketDel(&bucket);
	    bucket = btreeReadIdbucket(cache,parray[1]);
	    nentries = bucket->Nentries;
	}

        /* Check for empty bucket - shouldn't happen */
        /* Checking solely out of interest */
	if(nentries<1)	
	    ajFatal("FindHybMinOne: Too few entries in bucket Nkeys=%u\n",
                    nkeys);


        /* Find lowest value key in the bucket and store in cache */
        ajStrAssignS(&cache->replace,bucket->Ids[0]->id);
	if(!MAJSTRCMPS(cache->replace,key))
	    ajStrAssignS(&cache->replace,bucket->Ids[1]->id);

	for(i=1;iIds[i]->id,cache->replace)<0 &&
	       MAJSTRCMPS(bucket->Ids[i]->id,key))
		ajStrAssignS(&cache->replace,bucket->Ids[i]->id);
	btreeIdbucketDel(&bucket);
    }
    else
    {
	pagepos = parray[0];
	btreeFindHybMinOne(cache,pagepos,key);
	
    }

    btreeDeallocPriArray(cache,arrays);
    
    return;
}
ID btreeRemoveIdentEntryOne
TY static
MO ajindex
LB core
XX
DE Find and delete an ID from a given identifier tree level 1 leaf node if
DE necessary.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD leaf node page
PX
PN [3]
PA r btid const AjPBtId
PD id object
PX
RT AjBool
RD True if found (and deleted)
RX
//


static AjBool btreeRemoveIdentEntryOne(AjPBtcache cache, ajulong pagepos,
                                       const AjPBtId btid)
{
    AjPBtpage page   = NULL;
    AjPIdbucket bucket = NULL;
    
    AjPStr *karray = NULL;
    ajulong *parray = NULL;
    ajulong blockno = 0L;
    
    ajuint nkeys    = 0;
    ajuint nentries = 0;
    ajuint i;

    ajuint dirtysave = 0;
    
    AjBool found = ajFalse;

    unsigned char *buf = NULL;
    AjPBtMem arrays = NULL;
    
    /* ajDebug("In btreeRemoveIdentEntryOne\n"); */

    page = btreePricacheRead(cache,pagepos);
    buf = page->buf;
    dirtysave = page->dirty;
    page->dirty = BT_LOCK;
    page->lockfor = 1731;

    GBT_NKEYS(buf,&nkeys);

    if(!nkeys)
	return ajFalse;


    arrays = btreeAllocPriArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;

    btreeGetKeys(cache,buf,&karray,&parray);
    
    i=0;

    while(i!=nkeys && MAJSTRCMPS(btid->id,karray[i])>=0)
	++i;

    blockno = parray[i];
    
    bucket = btreeReadIdbucket(cache,blockno);

    nentries = bucket->Nentries;
    found = ajFalse;

    for(i=0;iid,bucket->Ids[i]->id))
	{
	    found = ajTrue;
	    break;
	}
    

    if(found)
    {
	/* Perform the deletion */
	if(nentries == 1)
	{
	    bucket->Nentries = 0;
	    ajBtreeIdDel(&bucket->Ids[0]);
	}
	else
	{
	    ajBtreeIdDel(&bucket->Ids[i]);
	    bucket->Ids[i] = bucket->Ids[nentries-1];
	    --bucket->Nentries;
	}

        
	btreeWriteIdbucket(cache,bucket,blockno);

	btreeAdjustHybbucketsOne(cache,page);

	page->dirty = BT_DIRTY;
    }
    else
	page->dirty = dirtysave;

    btreeIdbucketDel(&bucket);

    btreeDeallocPriArray(cache,arrays);
    
    if(!found)
	return ajFalse;

    return ajTrue;
}
ID btreeAdjustHybbucketsOne
TY static
MO ajindex
LB core
XX
DE Re-order leaf buckets
DE Can be called whatever the state of a leaf.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u leaf AjPBtpage
PD leaf page
PX
RT void
RD 
RX
//


static void btreeAdjustHybbucketsOne(AjPBtcache cache, AjPBtpage leaf)
{
    ajuint nkeys = 0;
    unsigned char *lbuf = NULL;
    AjPIdbucket *buckets  = NULL;

    AjPBtMem arrays = NULL;    
    AjPStr *keys        = NULL;
    ajulong *ptrs        = NULL;
    ajulong *overflows   = NULL;
    
    ajuint i = 0;
    ajuint j = 0;
    ajuint iref = 0;

    ajuint order;
    ajuint bentries      = 0;
    ajuint totalkeys     = 0;
    ajuint nperbucket    = 0;
    ajuint maxnperbucket = 0;
    ajuint count         = 0;
    ajuint totkeylen     = 0;
    ajuint keylimit      = 0;
    ajuint bucketn       = 0;
    ajuint bucketlimit   = 0;
    ajuint nodetype      = 0;
    ajuint nids          = 0;
    ajuint totnids       = 0;
    
    AjPList idlist    = NULL;
    ajuint  dirtysave = 0;
    AjPBtId bid       = NULL;
    AjPIdbucket cbucket = NULL;
    AjPBtId cid       = NULL;

    ajuint v = 0;
    ajuint refskip = cache->refcount*BT_EXTRA;
    
    /* ajDebug("In btreeAdjustHybbucketsOne\n"); */

    dirtysave = leaf->dirty;

    leaf->dirty = BT_LOCK;
    leaf->lockfor = 1741;
    lbuf = leaf->buf;

    GBT_NKEYS(lbuf,&nkeys);

    if(!nkeys)
    {
	leaf->dirty = dirtysave;
	return;
    }


    GBT_NODETYPE(lbuf,&nodetype);

    order = cache->porder;
    nperbucket = cache->pnperbucket;
    

    /* Read keys/ptrs */

    arrays = btreeAllocPriArray(cache);
    keys = arrays->karray;
    ptrs = arrays->parray;
    overflows = arrays->overflows;

    btreeGetKeys(cache,lbuf,&keys,&ptrs);

    for(i=0;i<=nkeys;++i)
	totalkeys += btreeIdbucketCount(cache,ptrs[i]);

    /* Set the number of entries per bucket to approximately half full */
    maxnperbucket = nperbucket >> 1;

    if(!maxnperbucket)
	++maxnperbucket;

    if(!leaf->pagepos)
	maxnperbucket = nperbucket;

    /* Work out the number of new buckets needed */
    bucketn = (totalkeys / maxnperbucket);
    if(totalkeys % maxnperbucket)
	++bucketn;

    if(bucketn == 1)
	++bucketn;

    while(bucketn > order)
    {
        ++maxnperbucket;
        bucketn = (totalkeys / maxnperbucket);

        if(totalkeys % maxnperbucket)
            ++bucketn;
    }

    /* Read buckets */
    AJCNEW0(buckets,nkeys+1);
    keylimit = nkeys + 1;
    
    for(i=0;iOverflow;
	bentries = buckets[i]->Nentries;

	for(j=0;jIds[j]);
	
	AJFREE(buckets[i]->keylen);
	AJFREE(buckets[i]->Ids);
	AJFREE(buckets[i]);
    }

    ajListSort(idlist, &btreeIdCompare);
    AJFREE(buckets);

    cbucket = btreeIdbucketNew(maxnperbucket, cache->refcount);
    bucketlimit = bucketn - 1;

    totnids = 0;
    nids = (ajuint) ajListGetLength(idlist);


    if(!totalkeys)
    {
	v = totalkeys;
	SBT_NKEYS(lbuf,v);

        btreeDeallocPriArray(cache,arrays);

	ajListFree(&idlist);
	leaf->dirty = BT_DIRTY;

	return;
    }
    
    if(nids <= maxnperbucket)
    {
	cbucket->Overflow = overflows[1];
	cbucket->Nentries = 0;
	ajListPeek(idlist,(void **)&bid);
	ajStrAssignS(&keys[0],bid->id);

	count = 0;

	while(count!=maxnperbucket && totnids != nids)
	{
	    ajListPop(idlist,(void **)&bid);
	    
	    cid = cbucket->Ids[count];
	    ajStrAssignS(&cid->id,bid->id);
	    cid->dbno = bid->dbno;
	    cid->dups = bid->dups;
	    cid->offset = bid->offset;

	    if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                    cid->refoffsets[iref] = bid->refoffsets[iref];
            }

	    cbucket->keylen[count] = BT_BUCKIDLEN(bid->id) + refskip;
	    ++cbucket->Nentries;
	    ++count;
	    ++totnids;
	    ajBtreeIdDel(&bid);
	}


	totkeylen += ajStrGetLen(keys[0]);

	if(!ptrs[1])
	    ptrs[1] = cache->totsize;

	btreeWriteIdbucket(cache,cbucket,ptrs[1]);

	cbucket->Overflow = overflows[0];
	cbucket->Nentries = 0;

	if(!ptrs[0])
	    ptrs[0] = cache->totsize;

	btreeWriteIdbucket(cache,cbucket,ptrs[0]);
    }
    else
    {
	for(i=0;iOverflow = overflows[i];
	    cbucket->Nentries = 0;

	    count = 0;

	    while(count!=maxnperbucket && totnids != nids)
	    {
		ajListPop(idlist,(void **)&bid);

		cid = cbucket->Ids[count];
		ajStrAssignS(&cid->id,bid->id);
		cid->dbno = bid->dbno;
		cid->dups = bid->dups;
		cid->offset = bid->offset;
		
                if(cache->refcount)
                {
                    for(iref=0; iref < cache->refcount; iref++)
                        cid->refoffsets[iref] = bid->refoffsets[iref];
                }

		cbucket->keylen[count] = BT_BUCKIDLEN(bid->id) + refskip;
		++cbucket->Nentries;
		++count;
		ajBtreeIdDel(&bid);
	    }


	    ajListPeek(idlist,(void **)&bid);
	    ajStrAssignS(&keys[i],bid->id);


	    totkeylen += ajStrGetLen(bid->id);

	    if(!ptrs[i])
		ptrs[i] = cache->totsize;
	    btreeWriteIdbucket(cache,cbucket,ptrs[i]);
	}
	
	
	/* Deal with greater-than bucket */
	
	cbucket->Overflow = overflows[i];
	cbucket->Nentries = 0;
	
	
	
	count = 0;

	while(ajListPop(idlist,(void **)&bid))
	{
	    cid = cbucket->Ids[count];
	    ajStrAssignS(&cid->id,bid->id);
	    cid->dbno = bid->dbno;
	    cid->dups = bid->dups;
	    cid->offset = bid->offset;

            if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                    cid->refoffsets[iref] = bid->refoffsets[iref];
            }

	    ++cbucket->Nentries;
	    ++count;
	    ajBtreeIdDel(&bid);
	}
	
	
	if(!ptrs[i])
	    ptrs[i] = cache->totsize;
	
	btreeWriteIdbucket(cache,cbucket,ptrs[i]);
    }
    

    cbucket->Nentries = maxnperbucket;
    btreeIdbucketDel(&cbucket);

    /* Now write out a modified leaf with new keys/ptrs */

    nkeys = bucketn - 1;

    btreeWriteNode(cache,leaf,keys,ptrs,nkeys);

    leaf->dirty = dirtysave;
    if(nodetype == BT_ROOT)
    {
	leaf->dirty = BT_LOCK;
        leaf->lockfor = 1742;
    }

    btreeDeallocPriArray(cache,arrays);

    btreeIdbucketDel(&cbucket);
    ajListFree(&idlist);

    return;
}
ID btreeRebalanceHybOne
TY static
MO ajindex
LB core
XX
DE Rebalance Hybrid level 1 tree after deletion
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD Node to rebalance
PX
PN [3]
PA r leftNode ajulong
PD left node
PX
PN [4]
PA r rightNode ajulong
PD right node
PX
PN [5]
PA r lAnchor ajulong
PD left anchor
PX
PN [6]
PA r rAnchor ajulong
PD right anchor
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeRebalanceHybOne(AjPBtcache cache, ajulong thisNode,
                                    ajulong leftNode, ajulong rightNode,
                                    ajulong lAnchor, ajulong rAnchor)
{
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;
    unsigned char *tbuf = NULL;

    ajulong anchorNode   = 0L;
    ajulong balanceNode  = 0L;
    ajulong mergeNode    = 0L;
    ajulong done         = 0L;
    ajulong parent       = 0L;
    
    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage tpage = NULL;
    
    ajuint lnkeys  = 0;
    ajuint rnkeys  = 0;
    ajuint size    = 0;
    ajuint order   = 0;
    ajuint minsize = 0;

    AjBool leftok  = ajFalse;
    AjBool rightok = ajFalse;
    
    
    /* ajDebug("In btreeRebalanceHybOne\n"); */

    if(leftNode!=BTNO_NODE && lAnchor!=BTNO_NODE)
	leftok = ajTrue;

    if(rightNode!=BTNO_NODE && rAnchor!=BTNO_NODE)
	rightok = ajTrue;

    if(!leftok && !rightok)
	return BTNO_NODE;
    

    if(leftok)
    {
	lpage = btreePricacheRead(cache,leftNode);
	lbuf  = lpage->buf;
	GBT_NKEYS(lbuf,&lnkeys);
    }
    

    if(rightok)
    {
	rpage = btreePricacheRead(cache,rightNode);
	rbuf  = rpage->buf;
	GBT_NKEYS(rbuf,&rnkeys);
    }
    


    if(leftok && rightok)
    {
	size = (lnkeys >= rnkeys) ? lnkeys : rnkeys;
	balanceNode = (lnkeys >= rnkeys) ? leftNode : rightNode;
    }
    else if(leftok)
    {
	size = lnkeys;
	balanceNode = leftNode;
    }
    else
    {
	size = rnkeys;
	balanceNode = rightNode;
    }

    
    order = cache->porder;
    minsize = (order-1) / 2;

    if((order-1)%2)
	++minsize;

    if(size >= minsize)
    {
	if(leftok && rightok)
	    anchorNode = (lnkeys >= rnkeys) ? lAnchor : rAnchor;
	else if(leftok)
	    anchorNode = lAnchor;
	else
	    anchorNode = rAnchor;

	done = btreeShiftHybOne(cache,thisNode,balanceNode,anchorNode);
    }
	    
    else
    {
	tpage = btreePricacheRead(cache,thisNode);
	tbuf  = tpage->buf;
	GBT_PREV(tbuf,&parent);

	if(leftok && rightok)
	{
	    anchorNode = (parent == lAnchor) ? lAnchor : rAnchor;
	    mergeNode  = (anchorNode == lAnchor) ? leftNode : rightNode;
	}
	else if(leftok)
	{
	    anchorNode = lAnchor;
	    mergeNode  = leftNode;
	}
	else
	{
	    anchorNode = rAnchor;
	    mergeNode  = rightNode;
	}

	done = btreeMergeHybOne(cache,thisNode,mergeNode,anchorNode);
    }

    return done;
}
ID btreeShiftHybOne
TY static
MO ajindex
LB core
XX
DE Shift spare entries from one hybrid tree level 1 node to another.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD master node
PX
PN [3]
PA r balanceNode ajulong
PD balance node
PX
PN [4]
PA r anchorNode ajulong
PD anchor node
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeShiftHybOne(AjPBtcache cache, ajulong thisNode,
                                ajulong balanceNode, ajulong anchorNode)
{
    unsigned char *tbuf = NULL;
    unsigned char *abuf = NULL;
    unsigned char *bbuf = NULL;
    unsigned char *buf  = NULL;
    
    AjPStr *kTarray = NULL;
    AjPStr *kAarray = NULL;
    AjPStr *kBarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pAarray = NULL;
    ajulong *pBarray = NULL;
    
    ajuint  nAkeys = 0;
    ajuint  nBkeys = 0;
    ajuint  nTkeys = 0;
    ajuint  i;
    ajint ii;
    
    AjPBtpage pageA = NULL;
    AjPBtpage pageB = NULL;
    AjPBtpage pageT = NULL;
    AjPBtpage page  = NULL;
    
    AjPBtpage leftpage = NULL;

    ajuint anchorPos   = 0;
    ajulong prev        = 0L;
    ajuint  nodetype    = 0;

    ajulong lv = 0L;
    
    AjPBtMem arraysA = NULL;    
    AjPBtMem arraysB = NULL;    
    AjPBtMem arraysT = NULL;    

    /* ajDebug("In btreeShiftHybOne\n"); */


    arraysA = btreeAllocPriArray(cache);
    kAarray = arraysA->karray;
    pAarray = arraysA->parray;

    arraysB = btreeAllocPriArray(cache);
    kBarray = arraysB->karray;
    pBarray = arraysB->parray;

    arraysT = btreeAllocPriArray(cache);
    kTarray = arraysT->karray;
    pTarray = arraysT->parray;


    pageA = btreePricacheRead(cache,anchorNode);
    pageA->dirty = BT_LOCK;
    pageA->lockfor = 1751;
    abuf = pageA->buf;
    pageB = btreePricacheRead(cache,balanceNode);
    pageB->dirty = BT_LOCK;
    pageB->lockfor = 1752;
    bbuf = pageB->buf;
    pageT = btreePricacheRead(cache,thisNode);
    pageT->dirty = BT_LOCK;
    pageT->lockfor = 1753;
    tbuf = pageT->buf;

    GBT_NKEYS(abuf,&nAkeys);
    GBT_NKEYS(bbuf,&nBkeys);
    GBT_NKEYS(tbuf,&nTkeys);

    btreeGetKeys(cache,abuf,&kAarray,&pAarray);
    btreeGetKeys(cache,bbuf,&kBarray,&pBarray);
    btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
    
    if(MAJSTRCMPS(kTarray[nTkeys-1],kBarray[nBkeys-1])<0)
	leftpage = pageT;
    else
	leftpage = pageB;


    if(leftpage == pageT)
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && MAJSTRCMPS(kTarray[nTkeys-1],kAarray[i])>=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to thisNode */
	ajStrAssignS(&kTarray[nTkeys],kAarray[anchorPos]);
	++nTkeys;

	/* Shift extra */
	while(nTkeys < nBkeys)
	{
	    ajStrAssignS(&kTarray[nTkeys],kBarray[0]);
	    pTarray[nTkeys] = pBarray[0];
	    ++nTkeys;
	    --nBkeys;

	    for(i=0;i=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to thisNode */
	pTarray[nTkeys+1] = pTarray[nTkeys];

	for(ii=nTkeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kTarray[ii+1],kTarray[ii]);
	    pTarray[ii+1] = pTarray[ii];
	}

	ajStrAssignS(&kTarray[0],kAarray[anchorPos]);
	++nTkeys;

	/* Shift extra */
	while(nTkeys < nBkeys)
	{
	    pTarray[nTkeys+1] = pTarray[nTkeys];

	    for(ii=nTkeys-1;ii>-1;--ii)
	    {
		ajStrAssignS(&kTarray[ii+1],kTarray[ii]);
		pTarray[ii+1] = pTarray[ii];
	    }

	    ajStrAssignS(&kTarray[0],kBarray[nBkeys-1]);
	    pTarray[1] = pBarray[nBkeys];
	    ++nTkeys;
	    --nBkeys;
	}


	/* Adjust anchor key */
	ajStrAssignS(&kAarray[anchorPos],kTarray[0]);
	--nTkeys;

	for(i=0;ipagepos;

    for(i=0;ibuf;
	GBT_NODETYPE(buf,&nodetype);

	if(nodetype != BT_IDBUCKET)
	{
	    lv = prev;
	    SBT_PREV(buf,lv);
	    page->dirty = BT_DIRTY;
	}
    }

    btreeWriteNode(cache,pageA,kAarray,pAarray,nAkeys);
    btreeWriteNode(cache,pageB,kBarray,pBarray,nBkeys);
    btreeWriteNode(cache,pageT,kTarray,pTarray,nTkeys);

    if(!anchorNode)
    {
	pageA->dirty = BT_LOCK;
        pageA->lockfor = 1754;
    }

    btreeDeallocPriArray(cache,arraysA);
    btreeDeallocPriArray(cache,arraysB);
    btreeDeallocPriArray(cache,arraysT);

    return BTNO_NODE;
}
ID btreeMergeHybOne
TY static
MO ajindex
LB core
XX
DE Merge two nodes.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD master node
PX
PN [3]
PA r mergeNode ajulong
PD merge node
PX
PN [4]
PA r anchorNode ajulong
PD anchor node
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeMergeHybOne(AjPBtcache cache, ajulong thisNode,
                                ajulong mergeNode, ajulong anchorNode)
{
    unsigned char *tbuf = NULL;
    unsigned char *abuf = NULL;
    unsigned char *nbuf = NULL;
    unsigned char *buf  = NULL;
    
    AjPStr *kTarray = NULL;
    AjPStr *kAarray = NULL;
    AjPStr *kNarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pAarray = NULL;
    ajulong *pNarray = NULL;

    ajulong thisprev  = 0L;
    ajulong mergeprev = 0L;
    
    
    ajuint  nAkeys = 0;
    ajuint  nNkeys = 0;
    ajuint  nTkeys = 0;
    ajuint  count  = 0;
    ajuint  i;
    ajint ii;

    ajuint   nodetype = 0;
    
    ajuint saveA = 0;
    ajuint saveN = 0;
    ajuint saveT = 0;
    
    AjPBtpage pageA = NULL;
    AjPBtpage pageN = NULL;
    AjPBtpage pageT = NULL;
    AjPBtpage page  = NULL;
    
    AjPBtpage leftpage = NULL;

    ajuint anchorPos = 0;
    ajulong prev      = 0L;

    ajulong lv = 0L;

    AjPBtMem arraysA = NULL;    
    AjPBtMem arraysN = NULL;    
    AjPBtMem arraysT = NULL;    

    AjBool collapse = ajFalse;
    
    /* ajDebug("In btreeMergeHybOne\n"); */

    pageA = btreePricacheRead(cache,anchorNode);
    saveA = pageA->dirty;
    pageA->dirty = BT_LOCK;
    pageA->lockfor = 1761;
    abuf = pageA->buf;
    pageN = btreePricacheRead(cache,mergeNode);
    saveN = pageN->dirty;
    pageN->dirty = BT_LOCK;
    pageN->lockfor = 1762;
    nbuf = pageN->buf;
    pageT = btreePricacheRead(cache,thisNode);
    saveT = pageT->dirty;
    pageT->dirty = BT_LOCK;
    pageT->lockfor = 1763;
    tbuf = pageT->buf;

    GBT_PREV(tbuf,&thisprev);
    GBT_PREV(nbuf,&mergeprev);

    GBT_NKEYS(abuf,&nAkeys);
    GBT_NKEYS(nbuf,&nNkeys);
    GBT_NKEYS(tbuf,&nTkeys);

    GBT_NODETYPE(nbuf,&nodetype);


    if(nAkeys == 1)
    {
	if(!anchorNode && !thisprev && !mergeprev)
	    collapse = ajTrue;
	else
	{
	    pageA->dirty = saveA;
	    pageN->dirty = saveN;
	    pageT->dirty = saveT;
	    return thisNode;
	}
    }

    arraysA = btreeAllocPriArray(cache);
    kAarray = arraysA->karray;
    pAarray = arraysA->parray;

    arraysN = btreeAllocPriArray(cache);
    kNarray = arraysN->karray;
    pNarray = arraysN->parray;

    arraysT = btreeAllocPriArray(cache);
    kTarray = arraysT->karray;
    pTarray = arraysT->parray;

    btreeGetKeys(cache,abuf,&kAarray,&pAarray);
    btreeGetKeys(cache,nbuf,&kNarray,&pNarray);
    btreeGetKeys(cache,tbuf,&kTarray,&pTarray);

    if(MAJSTRCMPS(kTarray[nTkeys-1],kNarray[nNkeys-1])<0)
	leftpage = pageT;
    else
	leftpage = pageN;


    if(leftpage == pageT)
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && MAJSTRCMPS(kTarray[nTkeys-1],kAarray[i])>=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to neighbour Node */
	pNarray[nNkeys+1] = pNarray[nNkeys];

	for(ii=nNkeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kNarray[ii+1],kNarray[ii]);
	    pNarray[ii+1] = pNarray[ii];
	}
	ajStrAssignS(&kNarray[0],kAarray[anchorPos]);
	++nNkeys;


	/* Adjust anchor node keys/ptrs */
	++anchorPos;

	if(anchorPos==nAkeys)
	    pAarray[nAkeys-1] = pAarray[nAkeys];
	else
	{
	    for(i=anchorPos;i-1;--ii)
	    {
		ajStrAssignS(&kNarray[ii+1],kNarray[ii]);
		pNarray[ii+1] = pNarray[ii];
	    }

	    ajStrAssignS(&kNarray[0],kTarray[nTkeys-1]);
	    pNarray[1] = pTarray[nTkeys];
	    pNarray[0] = pTarray[nTkeys-1];
	    --nTkeys;
	    ++nNkeys;
	}

	/* At this point the 'this' node could be added to a free list */
    }
    else
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && MAJSTRCMPS(kNarray[nNkeys-1],kAarray[i])>=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to neighbourNode */
	ajStrAssignS(&kNarray[nNkeys],kAarray[anchorPos]);
	++nNkeys;

	/* Adjust anchor node keys/ptrs */
	++anchorPos;

	if(anchorPos!=nAkeys)
	    for(i=anchorPos;ipagepos;

    for(i=0;i<=nNkeys;++i)
    {
	page = btreePricacheRead(cache,pNarray[i]);
	buf = page->buf;
	GBT_NODETYPE(buf,&nodetype);

	if(nodetype != BT_IDBUCKET)
	{
	    lv = prev;
	    SBT_PREV(buf,lv);
	    page->dirty = BT_DIRTY;
	}
    }

    pageT->dirty = BT_CLEAN;
    btreeWriteNode(cache,pageA,kAarray,pAarray,nAkeys);
    btreeWriteNode(cache,pageN,kNarray,pNarray,nNkeys);

    if(!anchorNode)
    {
	pageA->dirty = BT_LOCK;
        pageA->lockfor = 1764;
    }

    btreeDeallocPriArray(cache,arraysA);
    btreeDeallocPriArray(cache,arraysN);
    btreeDeallocPriArray(cache,arraysT);
    
    if(collapse)
	btreeCollapseRootHybOne(cache,mergeNode);

    return thisNode;
}
ID btreeCollapseRootHybOne
TY static
MO ajindex
LB core
XX
DE Collapse root page for hybrid level 1 tree.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number to make new root
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeCollapseRootHybOne(AjPBtcache cache, ajulong pagepos)
{
    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;

    AjPStr *karray = NULL;
    ajulong *parray = NULL;

    AjPBtpage rootpage = NULL;
    AjPBtpage page     = NULL;
    
    ajuint nodetype = 0;
    ajuint nkeys    = 0;
    ajuint i;

    ajulong prev = 0L;
    AjPBtMem arrays = NULL;
    
    /* ajDebug("In btreeCollapseRootHybOne\n"); */
    
    if(!cache->plevel)
	return BTNO_NODE;

    rootpage = btreePricacheLocate(cache,0L);
    buf = rootpage->buf;
    page = btreePricacheRead(cache,pagepos);


    arrays = btreeAllocPriArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;

    /*
    ** Swap pagepos values to make root the child and child the root
    ** Update node types and mark the original root as a clean page
    */

    /* At this point page->pagepos could be added to a free list */

    rootpage->pagepos = page->pagepos;
    rootpage->dirty = BT_CLEAN;
    nodetype = BT_INTERNAL;
    SBT_NODETYPE(buf,nodetype);

    page->pagepos = 0;
    page->dirty = BT_LOCK;
    page->lockfor = 1771;
    buf = page->buf;
    nodetype = BT_ROOT;
    SBT_NODETYPE(buf,nodetype);
    
    --cache->plevel;

    if(cache->plevel)
    {
	/*
	 ** Update the PREV pointers of the new root's children
	 */
	GBT_NKEYS(buf,&nkeys);
	btreeGetKeys(cache,buf,&karray,&parray);
	for(i=0;i<=nkeys;++i)
	{
	    page = btreePricacheRead(cache,parray[i]);
	    lbuf = page->buf;
	    SBT_PREV(lbuf,prev);
	    page->dirty = BT_DIRTY;
	}
    }

    btreeDeallocPriArray(cache,arrays);
    
    return 0L;
}
ID btreeDeleteIdentIdTwo
TY static
MO ajindex
LB core
XX
DE Entry point for secondary hybrid tree ID deletion.
DE Assumes cache->secrootblock has been initialised.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r btid const AjPBtId
PD ID object
PX
PN [3]
PA u did AjPBtId
PD ID object in primary tree
PX
RT AjBool
RD True if found and deleted
RX
//


static AjBool btreeDeleteIdentIdTwo(AjPBtcache cache, const AjPBtId btid,
                                    AjPBtId did)
{
    AjPBtpage rpage = NULL;
    AjPBtpage page  = NULL;

    ajulong sval = 0L;
    ajulong key  = 0L;
    ajulong *karray = NULL;
    ajulong *parray = NULL;
    ajulong blockno = 0L;
    
    unsigned char *rbuf = NULL;
    unsigned char *buf  = NULL;

    ajuint nkeys    = 0;
    ajuint nentries = 0;
    
    ajuint i;
    ajuint iref;

    AjBool found = ajFalse;
    
    AjPBtNumId num      = NULL;
    AjPNumbucket bucket = NULL;
    
    AjPBtMem array = NULL;
    
    rpage = btreeSeccacheWrite(cache,cache->secrootblock);

    rpage->dirty = BT_LOCK;
    rpage->lockfor = 1781;
    rbuf = rpage->buf;
    GBT_RIGHT(rbuf,&sval);
    cache->slevel = (ajuint) sval;

    key  = btid->offset;

    page = btreeNumFind(cache,key);
    buf  = page->buf;

    GBT_NKEYS(buf,&nkeys);

    array  = btreeAllocSecArray(cache);

    karray = array->overflows;
    parray = array->parray;
    
    if(!nkeys)
    {
        ajWarn("btreeDeleteIdentIdTwo: No keys in findinsert node");
        btreeDeallocSecArray(cache,array);
        rpage->dirty = BT_CLEAN;

        return ajFalse;
    }

    btreeGetNumKeys(cache,buf,&karray,&parray);

    i = 0;

    while(i != nkeys && key >= karray[i])
        ++i;

    blockno = parray[i];

    bucket = btreeReadNumbucket(cache,blockno);
    nentries = bucket->Nentries;

    found = ajFalse;

    for(i=0; i < nentries; ++i)
    {
        num = bucket->NumId[i];
        
        if(num->offset == key)
        {
            found = ajTrue;
            break;
        }
    }

    if(!found)
    {
        ajWarn("btreeDeleteIdentIdTwo: Numeric key not in bucket");
        btreeDeallocSecArray(cache,array);
        rpage->dirty = BT_CLEAN;

        return ajFalse;
    }

    btreeFindHybBalanceTwo(cache,cache->secrootblock,BTNO_NODE,BTNO_NODE,
                           BTNO_NODE,BTNO_NODE,key);

    if(!cache->dodelete)
    {
        ajWarn("btreeDeleteIdentIdTwo: entry %Lu not deleted",key);
        rpage->dirty = BT_CLEAN;
        btreeDeallocSecArray(cache,array);

        return ajFalse;
    }

    --did->dups;

    if(did->dups != 1)
    {
        rpage->dirty = BT_DIRTY;
        btreeDeallocSecArray(cache,array);

        return ajTrue;
    }
    
    /*
    ** Need to find remaining 2ry key here. Should be in root node.
    */
    buf = rpage->buf;
    btreeGetNumKeys(cache,buf,&karray,&parray);
    bucket = btreeReadNumbucket(cache,parray[0]);

    if(!bucket->Nentries)
        bucket = btreeReadNumbucket(cache,parray[1]);

    if(bucket->Nentries != 1)
        ajFatal("Expected only one remaining entry in btreeDeleteIdentIdTwo");
    
    num = bucket->NumId[0];

    if(cache->refcount)
    {
        for(iref=0; iref < cache->refcount; iref++)
            did->refoffsets[iref] = num->refoffsets[iref];
    }
    
    did->offset    = num->offset;
    did->dups      = 0;
    rpage->dirty = BT_CLEAN; /* Doesn't matter as page is now defunct */

    /*
    ** At this point pages parray[0], parray[1] and cache->secrootblock
    ** could be reused.
    **/
    
    
    btreeDeallocSecArray(cache,array);

    return ajTrue;
}
ID btreeFindHybBalanceTwo
TY static
MO ajindex
LB core
XX
DE Master routine for entry deletion from level 2 hybrid tree.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD Current node
PX
PN [3]
PA r leftNode ajulong
PD Node to left
PX
PN [4]
PA r rightNode ajulong
PD Node to right
PX
PN [5]
PA r lAnchor ajulong
PD Left anchor
PX
PN [6]
PA r rAnchor ajulong
PD Right anchor
PX
PN [7]
PA r key ajulong
PD key
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeFindHybBalanceTwo(AjPBtcache cache, ajulong thisNode,
                                      ajulong leftNode, ajulong rightNode,
                                      ajulong lAnchor, ajulong rAnchor,
                                      ajulong key)
{
    unsigned char *buf  = NULL;
    unsigned char *buf1 = NULL;
    
    ajulong nextNode   = BTNO_NODE;
    ajulong nextLeft   = BTNO_NODE;
    ajulong nextRight  = BTNO_NODE;
    ajulong nextAncL   = BTNO_NODE;
    ajulong nextAncR   = BTNO_NODE;
    ajulong done       = 0L;
    
    ajuint  nkeys      = 0;
    ajuint  order      = 0;
    ajuint  minkeys    = 0;
    ajuint  i;
    ajuint   nodetype   = 0;

    ajuint n1keys      = 0;
    
    AjPBtpage page  = NULL;
    AjPBtpage page1 = NULL;

    ajulong balanceNode = 0L;
    ajulong blockno     = 0L;
    ajulong ptrSave     = 0L;

    AjPBtMem arrays  = NULL;
    AjPBtMem arrays1 = NULL;
    ajulong *karray  = NULL;
    ajulong *parray  = NULL;
    ajulong *k1array = NULL;
    ajulong *p1array = NULL;

    AjBool existed = ajFalse;
    
    /* ajDebug("In btreeFindHybBalanceTwo\n"); */

    if(thisNode != cache->secrootblock)
	page = btreeSeccacheRead(cache,thisNode);
    else
    {   /* It's the root node of the primary hyb tree */
	page = btreeSeccacheLocate(cache,thisNode);
	page->dirty = BT_LOCK;
        page->lockfor = 1791;
    }

    cache->dodelete = ajFalse;

    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);
    
    order = cache->sorder;
    /* order-1 is the number of keys in the node */
    minkeys = (order-1) / 2;

    if((order-1)%2)
	++minkeys;

    /*
    ** If thisNode contains >= minkeys then it is not a candidate
    ** for balancing
    */
    if(nkeys >= minkeys)
	balanceNode = BTNO_BALANCE;
    else
	balanceNode = page->pagepos;

    arrays  = btreeAllocSecArray(cache);
    arrays1 = btreeAllocSecArray(cache);

    karray = arrays->overflows;
    parray = arrays->parray;

    k1array = arrays1->overflows;
    p1array = arrays1->parray;

    btreeGetNumKeys(cache,buf,&karray,&parray);

    i=0;

    while(i!=nkeys && key >= karray[i])
	++i;

    blockno = parray[i];

    nextNode = blockno;
    ptrSave = i;

    GBT_NODETYPE(buf,&nodetype);

    if(!(nodetype == BT_SECLEAF) && !(nodetype == BT_SECROOT && !cache->slevel))
    {
	if(nextNode == parray[0])
	{
	    if(leftNode != BTNO_NODE)
	    {
		page1 = btreeSeccacheRead(cache,leftNode);
		buf1 = page1->buf;
		GBT_NKEYS(buf1,&n1keys);
		btreeGetNumKeys(cache,buf1,&k1array,&p1array);
		nextLeft = p1array[n1keys];
	    }
	    else
		nextLeft = BTNO_NODE;
	    
	    if(thisNode == cache->secrootblock)
		nextAncL = thisNode;
	    else
		nextAncL = lAnchor;
	}
	else
	{
	    nextLeft = parray[ptrSave-1];
	    nextAncL = thisNode;
	}

	if(nextNode == parray[nkeys])
	{
	    if(rightNode != BTNO_NODE)
	    {
		page1 = btreeSeccacheRead(cache,rightNode);
		buf1 = page1->buf;
		GBT_NKEYS(buf1,&n1keys);
		btreeGetNumKeys(cache,buf1,&k1array,&p1array);
		nextRight = p1array[0];
	    }
	    else
		nextRight = BTNO_NODE;

	    if(thisNode == cache->secrootblock)
		nextAncR = thisNode;
	    else
		nextAncR = rAnchor;
	}
	else
	{
	    nextRight = parray[ptrSave+1];
	    nextAncR  = thisNode;
	}



	/* Check to see whether key exists in an internal node */
	if(nodetype != BT_SECLEAF && cache->slevel)
	{
	    i=0;

	    while(i!=nkeys && key > karray[i])
		++i;

	    if(i!=nkeys)
	    {
		btreeFindHybMinTwo(cache,parray[i+1],key);
		karray[i] = cache->numreplace;
		btreeWriteNumNode(cache,page,karray,parray,nkeys);
	    }
	
	}
	
	btreeFindHybBalanceTwo(cache,nextNode,nextLeft,nextRight,
                               nextAncL,nextAncR,key);

	if(thisNode != cache->secrootblock)
	    page = btreeSeccacheRead(cache,thisNode);
	else
	{
	    page = btreeSeccacheLocate(cache,thisNode);
	    page->dirty = BT_LOCK;
            page->lockfor = 1792;
	}
	buf = page->buf;

    }
    else
    {
	if(nodetype == BT_SECLEAF || (nodetype==BT_SECROOT && !cache->slevel))
	{
	    existed = btreeRemoveHybEntryTwo(cache,thisNode,key);

	    if(existed)
		cache->dodelete = ajTrue;

	    GBT_NKEYS(buf,&nkeys);

	    if(nkeys >= minkeys || (nodetype==BT_SECROOT && !cache->slevel))
		balanceNode = BTNO_BALANCE;
	    else
		balanceNode = page->pagepos;
	}
    }


    if(balanceNode == BTNO_BALANCE || thisNode == cache->secrootblock)
	done = BTNO_NODE;
    else
	done = btreeRebalanceHybTwo(cache,thisNode,leftNode,rightNode,
                                    lAnchor,rAnchor);
    

    btreeDeallocSecArray(cache,arrays);
    btreeDeallocSecArray(cache,arrays1);

    return done;
}
ID btreeFindHybMinTwo
TY static
MO ajindex
LB core
XX
DE Find minimum key in hybrid level 2 subtree and store in cache.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page
PX
PN [3]
PA r key ajulong
PD key
PX
RT void
RD 
RX
//


static void btreeFindHybMinTwo(AjPBtcache cache, ajulong pagepos,
                               ajulong key)
{
    AjPBtpage page   = NULL;

    AjPBtMem arrays = NULL;
    ajulong *karray   = NULL;
    ajulong *parray   = NULL;

    ajuint nkeys    = 0;
    ajuint nodetype = 0;
    ajuint nentries = 0;
    ajuint i;

    AjPNumbucket bucket = NULL;
    
    unsigned char *buf = NULL;

    /* ajDebug("In btreeFindHybMinTwo\n"); */

    arrays = btreeAllocSecArray(cache);
    karray = arrays->overflows;
    parray = arrays->parray;

    page = btreeSeccacheRead(cache,pagepos);
    buf  = page->buf;
    GBT_NODETYPE(buf,&nodetype);
    GBT_NKEYS(buf,&nkeys);

    btreeGetNumKeys(cache,buf,&karray,&parray);

    if(nodetype == BT_SECLEAF)
    {
	bucket = btreeReadNumbucket(cache,parray[0]);
	nentries = bucket->Nentries;

        /*
        ** If there's only one entry then it must be the key marked
        ** for deletion
        */
        if(nentries<2)
	{
	    btreeNumbucketDel(&bucket);
	    bucket = btreeReadNumbucket(cache,parray[1]);
	    nentries = bucket->Nentries;
	}

        /* Check for empty bucket - shouldn't happen */
        /* Checking solely out of interest */
	if(nentries<1)	
	    ajFatal("FindHybMinTwo: Too few entries in bucket Nkeys=%u\n",
                    nkeys);


        /* Find lowest value key in the bucket and store in cache */
        cache->numreplace = bucket->NumId[0]->offset;

	if(cache->numreplace == key)
	    cache->numreplace = bucket->NumId[1]->offset;

        for(i=1;iNumId[i]->offset < cache->numreplace  &&
               bucket->NumId[i]->offset != key)
                cache->numreplace = bucket->NumId[i]->offset;

	btreeNumbucketDel(&bucket);
    }
    else
    {
	pagepos = parray[0];
	btreeFindHybMinTwo(cache,pagepos,key);
	
    }

    btreeDeallocSecArray(cache,arrays);
    
    return;
}
ID btreeRemoveHybEntryTwo
TY static
MO ajindex
LB core
XX
DE Find and delete an ID from a given hybrid tree level 2 leaf node if
DE necessary.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD leaf node page
PX
PN [3]
PA r key ajulong
PD key
PX
RT AjBool
RD True if found (and deleted)
RX
//


static AjBool btreeRemoveHybEntryTwo(AjPBtcache cache, ajulong pagepos,
                                     ajulong key)
{
    AjPBtpage page   = NULL;
    AjPNumbucket bucket = NULL;
    
    ajulong *karray = NULL;
    ajulong *parray = NULL;
    ajulong blockno = 0L;
    
    ajuint nkeys    = 0;
    ajuint nentries = 0;
    ajuint i;

    ajuint dirtysave = 0;
    
    AjBool found = ajFalse;

    unsigned char *buf = NULL;
    AjPBtMem arrays = NULL;
    
    /* ajDebug("In btreeRemoveHybEntryTwo\n"); */

    page = btreeSeccacheRead(cache,pagepos);
    buf = page->buf;
    dirtysave = page->dirty;
    page->dirty = BT_LOCK;
    page->lockfor = 1801;
   
    GBT_NKEYS(buf,&nkeys);

    if(!nkeys)
	return ajFalse;


    arrays = btreeAllocSecArray(cache);
    karray = arrays->overflows;
    parray = arrays->parray;

    btreeGetNumKeys(cache,buf,&karray,&parray);
    
    i=0;

    while(i!=nkeys && key >= karray[i])
	++i;

    blockno = parray[i];
    
    bucket = btreeReadNumbucket(cache,blockno);


    nentries = bucket->Nentries;
    found = ajFalse;

    for(i=0;iNumId[i]->offset)
	{
	    found = ajTrue;
	    break;
	}
    

    if(found)
    {
	/* Perform the deletion */
	if(nentries == 1)
	{
	    bucket->Nentries = 0;
            AJFREE(bucket->NumId[0]);
	}
	else
	{
            AJFREE(bucket->NumId[i]);
	    bucket->NumId[i] = bucket->NumId[nentries-1];
	    --bucket->Nentries;
	}

	btreeWriteNumbucket(cache,bucket,blockno);
	btreeAdjustHybbucketsTwo(cache,page);
	page->dirty = BT_DIRTY;
    }
    else
	page->dirty = dirtysave;

    btreeNumbucketDel(&bucket);

    btreeDeallocSecArray(cache,arrays);
    
    if(!found)
	return ajFalse;

    return ajTrue;
}
ID btreeAdjustHybbucketsTwo
TY static
MO ajindex
LB core
XX
DE Re-order leaf buckets in 2ry hybrid tree
DE Can be called whatever the state of a leaf.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u leaf AjPBtpage
PD leaf page
PX
RT void
RD 
RX
//


static void btreeAdjustHybbucketsTwo(AjPBtcache cache, AjPBtpage leaf)
{
    ajuint nkeys = 0;
    unsigned char *lbuf = NULL;
    AjPNumbucket *buckets  = NULL;

    AjPBtMem arrays = NULL;    
    AjPBtMem overarrays = NULL;    

    ajulong *keys           = NULL;
    ajulong *ptrs           = NULL;
    ajulong *overflows      = NULL;
    
    ajuint i = 0;
    ajuint j = 0;
    ajuint iref = 0;
    
    ajuint order;
    ajuint bentries      = 0;
    ajuint totalkeys     = 0;
    ajuint nperbucket    = 0;
    ajuint maxnperbucket = 0;
    ajuint count         = 0;
    ajuint keylimit      = 0;
    ajuint bucketn       = 0;
    ajuint bucketlimit   = 0;
    ajuint nodetype      = 0;
    ajuint nids          = 0;
    ajuint totnids       = 0;
    
    AjPList idlist    = NULL;
    ajuint   dirtysave = 0;
    AjPBtNumId bid       = NULL;
    AjPNumbucket cbucket = NULL;
    AjPBtNumId cid       = NULL;

    ajuint v = 0;
    
    /* ajDebug("In btreeAdjustHybbucketsTwo\n"); */

    dirtysave = leaf->dirty;

    leaf->dirty = BT_LOCK;
    leaf->lockfor = 1811;
    lbuf = leaf->buf;

    GBT_NKEYS(lbuf,&nkeys);

    if(!nkeys)
    {
	leaf->dirty = dirtysave;
	return;
    }


    GBT_NODETYPE(lbuf,&nodetype);

    order = cache->sorder;
    nperbucket = cache->snperbucket;
    

    /* Read keys/ptrs */

    arrays = btreeAllocSecArray(cache);
    keys = arrays->overflows;
    ptrs = arrays->parray;

    overarrays = btreeAllocSecArray(cache);
    overflows = overarrays->overflows;
    

    btreeGetNumKeys(cache,lbuf,&keys,&ptrs);


    for(i=0;i> 1;

    if(!maxnperbucket)
	++maxnperbucket;

    if(!leaf->pagepos)
	maxnperbucket = nperbucket;

    /* Work out the number of new buckets needed */
    bucketn = (totalkeys / maxnperbucket);

    if(totalkeys % maxnperbucket)
	++bucketn;

    if(bucketn == 1)
	++bucketn;


    while(bucketn > order)
    {
        ++maxnperbucket;
        bucketn = (totalkeys / maxnperbucket);
        if(totalkeys % maxnperbucket)
            ++bucketn;
    }

    /* Read buckets */
    AJCNEW0(buckets,nkeys+1);
    keylimit = nkeys + 1;
    
    for(i=0;iOverflow;
	bentries = buckets[i]->Nentries;
	for(j=0;jNumId[j]);
	
	AJFREE(buckets[i]->NumId);
	AJFREE(buckets[i]);
    }

    ajListSort(idlist, &btreeNumIdCompare);
    AJFREE(buckets);

    cbucket = btreeNumbucketNew(maxnperbucket, cache->refcount);
    bucketlimit = bucketn - 1;

    totnids = 0;
    nids = (ajuint) ajListGetLength(idlist);


    if(!totalkeys)
    {
	v = totalkeys;
	SBT_NKEYS(lbuf,v);

        btreeDeallocSecArray(cache,arrays);
        btreeDeallocSecArray(cache,overarrays);

	ajListFree(&idlist);
	leaf->dirty = BT_DIRTY;

	return;
    }
    
    if(nids <= maxnperbucket)
    {
	cbucket->Overflow = overflows[1];
	cbucket->Nentries = 0;
	ajListPeek(idlist,(void **)&bid);
	keys[0] = bid->offset;

	count = 0;

	while(count!=maxnperbucket && totnids != nids)
	{
	    ajListPop(idlist,(void **)&bid);
	    
	    cid = cbucket->NumId[count];
	    cid->dbno = bid->dbno;
	    cid->offset = bid->offset;
            if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                    cid->refoffsets[iref] = bid->refoffsets[iref];
            }
	    
	    ++cbucket->Nentries;
	    ++count;
	    ++totnids;
	    AJFREE(bid);
	}


	if(!ptrs[1])
	    ptrs[1] = cache->totsize;

	btreeWriteNumbucket(cache,cbucket,ptrs[1]);

	cbucket->Overflow = overflows[0];
	cbucket->Nentries = 0;

	if(!ptrs[0])
	    ptrs[0] = cache->totsize;

	btreeWriteNumbucket(cache,cbucket,ptrs[0]);
    }
    else
    {
	for(i=0;iOverflow = overflows[i];
	    cbucket->Nentries = 0;

	    count = 0;

	    while(count!=maxnperbucket && totnids != nids)
	    {
		ajListPop(idlist,(void **)&bid);

		cid = cbucket->NumId[count];
		cid->dbno = bid->dbno;
		cid->offset = bid->offset;

                if(cache->refcount)
                {
                    for(iref=0; iref < cache->refcount; iref++)
                        cid->refoffsets[iref] = bid->refoffsets[iref];
		}

		++cbucket->Nentries;
		++count;
		AJFREE(bid);
	    }


	    ajListPeek(idlist,(void **)&bid);
	    keys[i] = bid->offset;


	    if(!ptrs[i])
		ptrs[i] = cache->totsize;

	    btreeWriteNumbucket(cache,cbucket,ptrs[i]);
	}
	
	
	/* Deal with greater-than bucket */
	
	cbucket->Overflow = overflows[i];
	cbucket->Nentries = 0;
	
	
	
	count = 0;

	while(ajListPop(idlist,(void **)&bid))
	{
	    cid = cbucket->NumId[count];
	    cid->dbno = bid->dbno;
	    cid->offset = bid->offset;

            if(cache->refcount)
            {
                for(iref=0; iref < cache->refcount; iref++)
                    cid->refoffsets[iref] = bid->refoffsets[iref];
	    }

	    ++cbucket->Nentries;
	    ++count;
	    AJFREE(bid);
	}
	
	
	if(!ptrs[i])
	    ptrs[i] = cache->totsize;
	
	btreeWriteNumbucket(cache,cbucket,ptrs[i]);
    }
    

    cbucket->Nentries = maxnperbucket;
    btreeNumbucketDel(&cbucket);

    /* Now write out a modified leaf with new keys/ptrs */

    nkeys = bucketn - 1;
    v = nkeys;
    SBT_NKEYS(lbuf,v);

    btreeWriteNumNode(cache,leaf,keys,ptrs,nkeys);

    leaf->dirty = dirtysave;
    if(nodetype == BT_SECROOT)
    {
	leaf->dirty = BT_LOCK;
        leaf->lockfor = 1812;
    }
    
    btreeDeallocSecArray(cache,arrays);
    btreeDeallocSecArray(cache,overarrays);

    btreeNumbucketDel(&cbucket);
    ajListFree(&idlist);

    return;
}
ID btreeRebalanceHybTwo
TY static
MO ajindex
LB core
XX
DE Rebalance Hybrid level 2 tree after deletion
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD Node to rebalance
PX
PN [3]
PA r leftNode ajulong
PD left node
PX
PN [4]
PA r rightNode ajulong
PD right node
PX
PN [5]
PA r lAnchor ajulong
PD left anchor
PX
PN [6]
PA r rAnchor ajulong
PD right anchor
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeRebalanceHybTwo(AjPBtcache cache, ajulong thisNode,
                                   ajulong leftNode, ajulong rightNode,
                                   ajulong lAnchor, ajulong rAnchor)
{
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;
    unsigned char *tbuf = NULL;

    ajulong anchorNode   = 0L;
    ajulong balanceNode  = 0L;
    ajulong mergeNode    = 0L;
    ajulong done         = 0L;
    ajulong parent       = 0L;
    
    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage tpage = NULL;
    
    ajuint lnkeys  = 0;
    ajuint rnkeys  = 0;
    ajuint size    = 0;
    ajuint order   = 0;
    ajuint minsize = 0;

    AjBool leftok  = ajFalse;
    AjBool rightok = ajFalse;
    
    
    /* ajDebug("In btreeRebalanceHybTwo\n"); */

    if(leftNode!=BTNO_NODE && lAnchor!=BTNO_NODE)
	leftok = ajTrue;

    if(rightNode!=BTNO_NODE && rAnchor!=BTNO_NODE)
	rightok = ajTrue;

    if(!leftok && !rightok)
	return BTNO_NODE;
    

    if(leftok)
    {
	lpage = btreeSeccacheRead(cache,leftNode);
	lbuf  = lpage->buf;
	GBT_NKEYS(lbuf,&lnkeys);
    }
    

    if(rightok)
    {
	rpage = btreeSeccacheRead(cache,rightNode);
	rbuf  = rpage->buf;
	GBT_NKEYS(rbuf,&rnkeys);
    }
    


    if(leftok && rightok)
    {
	size = (lnkeys >= rnkeys) ? lnkeys : rnkeys;
	balanceNode = (lnkeys >= rnkeys) ? leftNode : rightNode;
    }
    else if(leftok)
    {
	size = lnkeys;
	balanceNode = leftNode;
    }
    else
    {
	size = rnkeys;
	balanceNode = rightNode;
    }

    
    order = cache->sorder;
    minsize = (order-1) / 2;

    if((order-1)%2)
	++minsize;

    if(size >= minsize)
    {
	if(leftok && rightok)
	    anchorNode = (lnkeys >= rnkeys) ? lAnchor : rAnchor;
	else if(leftok)
	    anchorNode = lAnchor;
	else
	    anchorNode = rAnchor;

	done = btreeShiftHybTwo(cache,thisNode,balanceNode,anchorNode);
    }
	    
    else
    {
	tpage = btreeSeccacheRead(cache,thisNode);
	tbuf  = tpage->buf;
	GBT_PREV(tbuf,&parent);

	if(leftok && rightok)
	{
	    anchorNode = (parent == lAnchor) ? lAnchor : rAnchor;
	    mergeNode  = (anchorNode == lAnchor) ? leftNode : rightNode;
	}
	else if(leftok)
	{
	    anchorNode = lAnchor;
	    mergeNode  = leftNode;
	}
	else
	{
	    anchorNode = rAnchor;
	    mergeNode  = rightNode;
	}

	done = btreeMergeHybTwo(cache,thisNode,mergeNode,anchorNode);
    }

    return done;
}
ID btreeShiftHybTwo
TY static
MO ajindex
LB core
XX
DE Shift spare entries from one hybrid tree level 2 node to another.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD master node
PX
PN [3]
PA r balanceNode ajulong
PD balance node
PX
PN [4]
PA r anchorNode ajulong
PD anchor node
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeShiftHybTwo(AjPBtcache cache, ajulong thisNode,
                                ajulong balanceNode, ajulong anchorNode)
{
    unsigned char *tbuf = NULL;
    unsigned char *abuf = NULL;
    unsigned char *bbuf = NULL;
    unsigned char *buf  = NULL;
    
    ajulong *kTarray = NULL;
    ajulong *kAarray = NULL;
    ajulong *kBarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pAarray = NULL;
    ajulong *pBarray = NULL;
    
    ajuint  nAkeys = 0;
    ajuint  nBkeys = 0;
    ajuint  nTkeys = 0;
    ajuint  i;
    ajint  ii;

    AjPBtpage pageA = NULL;
    AjPBtpage pageB = NULL;
    AjPBtpage pageT = NULL;
    AjPBtpage page  = NULL;
    
    AjPBtpage leftpage = NULL;

    ajuint anchorPos   = 0;
    ajulong prev        = 0L;
    ajuint  nodetype    = 0;

    ajulong lv = 0L;
    
    AjPBtMem arraysA = NULL;    
    AjPBtMem arraysB = NULL;    
    AjPBtMem arraysT = NULL;    

    /* ajDebug("In btreeShiftHybTwo\n"); */


    arraysA = btreeAllocSecArray(cache);
    kAarray = arraysA->overflows;
    pAarray = arraysA->parray;

    arraysB = btreeAllocSecArray(cache);
    kBarray = arraysB->overflows;
    pBarray = arraysB->parray;

    arraysT = btreeAllocSecArray(cache);
    kTarray = arraysT->overflows;
    pTarray = arraysT->parray;


    pageA = btreeSeccacheRead(cache,anchorNode);
    pageA->dirty = BT_LOCK;
    pageA->lockfor = 1821;
    abuf = pageA->buf;
    pageB = btreeSeccacheRead(cache,balanceNode);
    pageB->dirty = BT_LOCK;
    pageB->lockfor = 1822;
    bbuf = pageB->buf;
    pageT = btreeSeccacheRead(cache,thisNode);
    pageT->dirty = BT_LOCK;
    pageT->lockfor = 1823;
    tbuf = pageT->buf;

    GBT_NKEYS(abuf,&nAkeys);
    GBT_NKEYS(bbuf,&nBkeys);
    GBT_NKEYS(tbuf,&nTkeys);

    btreeGetNumKeys(cache,abuf,&kAarray,&pAarray);
    btreeGetNumKeys(cache,bbuf,&kBarray,&pBarray);
    btreeGetNumKeys(cache,tbuf,&kTarray,&pTarray);

    if(kTarray[nTkeys-1] < kBarray[nBkeys-1])
	leftpage = pageT;
    else
	leftpage = pageB;


    if(leftpage == pageT)
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && kTarray[nTkeys-1] >= kAarray[i])
	    ++i;

	anchorPos = i;

	/* Move down anchor key to thisNode */
        kTarray[nTkeys] = kAarray[anchorPos];
	++nTkeys;

	/* Shift extra */
	while(nTkeys < nBkeys)
	{
            kTarray[nTkeys] = kBarray[0];
	    pTarray[nTkeys] = pBarray[0];
	    ++nTkeys;
	    --nBkeys;

	    for(i=0;i= kAarray[i])
	    ++i;

	anchorPos = i;

	/* Move down anchor key to thisNode */
	pTarray[nTkeys+1] = pTarray[nTkeys];

	for(ii=nTkeys-1;ii>-1;--ii)
	{
            kTarray[ii+1] = kTarray[ii];
	    pTarray[ii+1] = pTarray[ii];
	}

        kTarray[0] = kAarray[anchorPos];
	++nTkeys;

	/* Shift extra */
	while(nTkeys < nBkeys)
	{
	    pTarray[nTkeys+1] = pTarray[nTkeys];

	    for(ii=nTkeys-1;ii>-1;--ii)
	    {
                kTarray[ii+1] = kTarray[ii];
		pTarray[ii+1] = pTarray[ii];
	    }

            kTarray[0] = kBarray[nBkeys-1];
	    pTarray[1] = pBarray[nBkeys];
	    ++nTkeys;
	    --nBkeys;
	}


	/* Adjust anchor key */
        kAarray[anchorPos] = kTarray[0];
	--nTkeys;

	for(i=0;ipagepos;

    for(i=0;ibuf;
	GBT_NODETYPE(buf,&nodetype);

	if(nodetype != BT_IDBUCKET)
	{
	    lv = prev;
	    SBT_PREV(buf,lv);
	    page->dirty = BT_DIRTY;
	}
    }

    btreeWriteNumNode(cache,pageA,kAarray,pAarray,nAkeys);
    btreeWriteNumNode(cache,pageB,kBarray,pBarray,nBkeys);
    btreeWriteNumNode(cache,pageT,kTarray,pTarray,nTkeys);

    if(anchorNode == cache->secrootblock)
    {
	pageA->dirty = BT_LOCK;
        pageA->lockfor = 1824;
    }

    btreeDeallocSecArray(cache,arraysA);
    btreeDeallocSecArray(cache,arraysB);
    btreeDeallocSecArray(cache,arraysT);

    return BTNO_NODE;
}
ID btreeMergeHybTwo
TY static
MO ajindex
LB core
XX
DE Merge two nodes in Hybrid 2ry tree
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD master node
PX
PN [3]
PA r mergeNode ajulong
PD merge node
PX
PN [4]
PA r anchorNode ajulong
PD anchor node
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeMergeHybTwo(AjPBtcache cache, ajulong thisNode,
                                ajulong mergeNode, ajulong anchorNode)
{
    unsigned char *tbuf = NULL;
    unsigned char *abuf = NULL;
    unsigned char *nbuf = NULL;
    unsigned char *buf  = NULL;
    
    AjPBtMem arraysA = NULL;    
    AjPBtMem arraysN = NULL;    
    AjPBtMem arraysT = NULL;    

    ajulong *kTarray = NULL;
    ajulong *kAarray = NULL;
    ajulong *kNarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pAarray = NULL;
    ajulong *pNarray = NULL;

    ajulong thisprev  = 0L;
    ajulong mergeprev = 0L;
    
    
    ajuint  nAkeys = 0;
    ajuint  nNkeys = 0;
    ajuint  nTkeys = 0;
    ajuint  count  = 0;
    ajuint  i;
    ajint ii;

    ajuint   nodetype = 0;
    
    ajuint saveA = 0;
    ajuint saveN = 0;
    ajuint saveT = 0;
    
    AjPBtpage pageA = NULL;
    AjPBtpage pageN = NULL;
    AjPBtpage pageT = NULL;
    AjPBtpage page  = NULL;
    
    AjPBtpage leftpage = NULL;

    ajuint anchorPos = 0;
    ajulong prev      = 0L;

    ajulong lv = 0L;

    AjBool collapse = ajFalse;
    ajulong csrb     = 0L;
    
    /* ajDebug("In btreeMergeHybTwo\n"); */

    pageA = btreeSeccacheRead(cache,anchorNode);
    saveA = pageA->dirty;
    pageA->dirty = BT_LOCK;
    pageA->lockfor = 1831;
    abuf = pageA->buf;
    pageN = btreeSeccacheRead(cache,mergeNode);
    saveN = pageN->dirty;
    pageN->dirty = BT_LOCK;
    pageN->lockfor = 1832;
    nbuf = pageN->buf;
    pageT = btreeSeccacheRead(cache,thisNode);
    saveT = pageT->dirty;
    pageT->dirty = BT_LOCK;
    pageT->lockfor = 1833;
    tbuf = pageT->buf;

    GBT_PREV(tbuf,&thisprev);
    GBT_PREV(nbuf,&mergeprev);

    GBT_NKEYS(abuf,&nAkeys);
    GBT_NKEYS(nbuf,&nNkeys);
    GBT_NKEYS(tbuf,&nTkeys);

    GBT_NODETYPE(nbuf,&nodetype);

    csrb = cache->secrootblock;

    if(nAkeys == 1)
    {
	if(anchorNode==csrb && thisprev==csrb && mergeprev==csrb)
	    collapse = ajTrue;
	else
	{
	    pageA->dirty = saveA;
	    pageN->dirty = saveN;
	    pageT->dirty = saveT;

	    return thisNode;
	}
    }

    arraysA = btreeAllocSecArray(cache);
    kAarray = arraysA->overflows;
    pAarray = arraysA->parray;

    arraysN = btreeAllocSecArray(cache);
    kNarray = arraysN->overflows;
    pNarray = arraysN->parray;

    arraysT = btreeAllocSecArray(cache);
    kTarray = arraysT->overflows;
    pTarray = arraysT->parray;

    btreeGetNumKeys(cache,abuf,&kAarray,&pAarray);
    btreeGetNumKeys(cache,nbuf,&kNarray,&pNarray);
    btreeGetNumKeys(cache,tbuf,&kTarray,&pTarray);

    if(kTarray[nTkeys-1] < kNarray[nNkeys-1])
	leftpage = pageT;
    else
	leftpage = pageN;


    if(leftpage == pageT)
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && kTarray[nTkeys-1] >= kAarray[i])
	    ++i;

	anchorPos = i;

	/* Move down anchor key to neighbour Node */
	pNarray[nNkeys+1] = pNarray[nNkeys];

	for(ii=nNkeys-1;ii>-1;--ii)
	{
	    kNarray[ii+1] = kNarray[ii];
	    pNarray[ii+1] = pNarray[ii];
	}

	kNarray[0] = kAarray[anchorPos];
	++nNkeys;


	/* Adjust anchor node keys/ptrs */
	++anchorPos;

	if(anchorPos==nAkeys)
	    pAarray[nAkeys-1] = pAarray[nAkeys];
	else
	{
	    for(i=anchorPos;i-1;--ii)
	    {
		kNarray[ii+1] = kNarray[ii];
		pNarray[ii+1] = pNarray[ii];
	    }

	    kNarray[0] = kTarray[nTkeys-1];
	    pNarray[1] = pTarray[nTkeys];
	    pNarray[0] = pTarray[nTkeys-1];
	    --nTkeys;
	    ++nNkeys;
	}

	/* At this point the 'this' node could be added to a free list */
    }
    else
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && kNarray[nNkeys-1] >= kAarray[i])
	    ++i;

	anchorPos = i;

	/* Move down anchor key to neighbourNode */
	kNarray[nNkeys] = kAarray[anchorPos];
	++nNkeys;

	/* Adjust anchor node keys/ptrs */
	++anchorPos;

	if(anchorPos!=nAkeys)
	    for(i=anchorPos;ipagepos;

    for(i=0;i<=nNkeys;++i)
    {
	page = btreeSeccacheRead(cache,pNarray[i]);
	buf = page->buf;
	GBT_NODETYPE(buf,&nodetype);

	if(nodetype != BT_IDBUCKET)
	{
	    lv = prev;
	    SBT_PREV(buf,lv);
	    page->dirty = BT_DIRTY;
	}
    }

    pageT->dirty = BT_CLEAN;
    btreeWriteNumNode(cache,pageA,kAarray,pAarray,nAkeys);
    btreeWriteNumNode(cache,pageN,kNarray,pNarray,nNkeys);

    if(anchorNode == csrb)
    {
	pageA->dirty = BT_LOCK;
        pageA->lockfor = 1834;
    }

    btreeDeallocSecArray(cache,arraysA);
    btreeDeallocSecArray(cache,arraysN);
    btreeDeallocSecArray(cache,arraysT);

    if(collapse)
	btreeCollapseRootHybTwo(cache,mergeNode);

    return thisNode;
}
ID btreeCollapseRootHybTwo
TY static
MO ajindex
LB core
XX
DE Collapse root page for hybrid level 2 tree.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number to make new root
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeCollapseRootHybTwo(AjPBtcache cache, ajulong pagepos)
{
    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;

    ajulong *karray = NULL;
    ajulong *parray = NULL;

    AjPBtpage rootpage = NULL;
    AjPBtpage page     = NULL;
    
    ajuint nodetype = 0;
    ajuint nkeys    = 0;
    ajuint i;

    ajulong prev = 0L;
    AjPBtMem arrays = NULL;
    
    /* ajDebug("In btreeCollapseRootHybTwo\n"); */
    
    if(!cache->slevel)
	return BTNO_NODE;

    rootpage = btreeSeccacheLocate(cache,cache->secrootblock);
    buf = rootpage->buf;
    page = btreeSeccacheRead(cache,pagepos);


    arrays = btreeAllocSecArray(cache);
    karray = arrays->overflows;
    parray = arrays->parray;

    /*
    ** Swap pagepos values to make root the child and child the root
    ** Update node types and mark the original root as a clean page
    */

    /* At this point page->pagepos could be added to a free list */

    rootpage->pagepos = page->pagepos;
    rootpage->dirty = BT_CLEAN;
    nodetype = BT_SECINTERNAL;
    SBT_NODETYPE(buf,nodetype);

    page->pagepos = cache->secrootblock;
    page->dirty = BT_LOCK;
    page->lockfor = 1841;
    buf = page->buf;
    nodetype = BT_SECROOT;
    SBT_NODETYPE(buf,nodetype);
    
    --cache->slevel;

    if(cache->slevel)
    {
	/*
	 ** Update the PREV pointers of the new root's children
	 */
	GBT_NKEYS(buf,&nkeys);
	btreeGetNumKeys(cache,buf,&karray,&parray);

	for(i=0;i<=nkeys;++i)
	{
	    page = btreeSeccacheRead(cache,parray[i]);
	    lbuf = page->buf;
	    SBT_PREV(lbuf,prev);
	    page->dirty = BT_DIRTY;
	}
    }

    btreeDeallocSecArray(cache,arrays);

    cache->secrootblock = pagepos;
    
    return 0L;
}
ID ajBtreeDeletePriId
TY public
MO ajindex
LB core
XX
DE Entry point for keyword tree ID deletion.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pri const AjPBtPri
PD keyword object
PX
RT AjBool
RD True if found and deleted
RX
//


AjBool ajBtreeDeletePriId(AjPBtcache cache, const AjPBtPri pri)
{
    AjPBtpage rootpage = NULL;
    AjPBtpage spage    = NULL;
    AjPBtpage page     = NULL;
    AjPStr key         = NULL;
    AjPSecbucket bucket  = NULL;
    ajulong blockno  = 0L;

    ajuint nkeys = 0;
    ajulong slevel = 0L;

    ajuint nentries = 0;
    
    AjPStr *karray = NULL;
    ajulong *parray = NULL;
    AjPBtMem arrays = NULL;
    AjBool found = ajFalse;
    
    
    ajuint i;
    
    unsigned char *buf = NULL;
    ajulong  secrootpage = 0L;

    AjBool empty = ajFalse;
    AjBool ret   = ajFalse;

    AjPBtpage prirootpage = NULL;
    
    ajulong treeblock = 0L;
    
    /* ajDebug("In ajBtreeDeletePriId\n"); */

    key = ajStrNew();
    

    ajStrAssignS(&key,pri->keyword);

    if(!ajStrGetLen(key))
    {
	ajStrDel(&key);

	return ajFalse;
    }

    if(!btreeKeyFind(cache,key,&treeblock))
    {
        ajStrDel(&key);
        ajWarn("DeletePriId: Keyword %S not found",pri->keyword);

        return ajFalse;
    }

    secrootpage = treeblock;
    cache->secrootblock = treeblock;
    
    arrays = btreeAllocSecArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;
    
    page = btreePricacheRead(cache,secrootpage);
    page->dirty = BT_LOCK;
    page->lockfor = 1851;
    buf = page->buf;

    GBT_RIGHT(buf,&slevel);
    cache->slevel = (ajuint)slevel;

    spage = btreeKeyidFind(cache,pri->id);
    buf = spage->buf;

    btreeGetKeys(cache,buf,&karray,&parray);


    
    GBT_NKEYS(buf,&nkeys);

    if(!nkeys)
    {
	btreeDeallocSecArray(cache,arrays);
	ajStrDel(&key);
        page->dirty = BT_CLEAN;
        
	return ajFalse;
    }


    i=0;

    while(i!=nkeys && MAJSTRCMPS(pri->id,karray[i])>=0)
        ++i;

    blockno = parray[i];

    bucket = btreeReadSecbucket(cache,blockno);

    nentries = bucket->Nentries;

    found = ajFalse;

    for(i=0;iid,bucket->SecIds[i]))
	{
	    found = ajTrue;
	    break;
	}

    if(!found)
    {
        ajWarn("DeletePriId: ID %S  not found for Keyword %S",pri->id,
               pri->keyword);
	btreeDeallocPriArray(cache,arrays);
	ajStrDel(&key);
        page->dirty = BT_CLEAN;

	return ajFalse;
    }
    

    
    /*
    ** Have to delete ID from secondary tree. If that empties the
    ** tree then have to delete keyword from primary tree.
    ** Needs a little thought. Maybe use cache->dodelete for multiple
    ** purposes i.e. 0=not deleted  1=keyword deleted 2=tree deleted
    */

    rootpage = btreeSeccacheLocate(cache,secrootpage);
    if(!rootpage)
        ajFatal("DeletePriId: secondary root page became unlocked");
    rootpage->dirty = BT_LOCK;
    rootpage->lockfor = 1852;
    buf = rootpage->buf;

    GBT_RIGHT(buf,&slevel);
    cache->slevel = (ajuint)slevel;

    btreeFindPriBalanceTwo(cache,secrootpage,BTNO_NODE,BTNO_NODE,BTNO_NODE,
                           BTNO_NODE,pri);

    ret = cache->dodelete;

    if(!ret)
    {
        btreeDeallocSecArray(cache,arrays);
        ajStrDel(&key);
        page->dirty = BT_CLEAN;

        return ajFalse;
    }
    
    empty = btreeIsSecEmpty(cache);

    
    if(empty)
    {
        prirootpage = btreePricacheLocate(cache,0L);

        if(!prirootpage)
            ajFatal("ajBtreeDeletePriId: prirootpage unlocked");
        
    
        btreeFindPriBalanceOne(cache,0L,BTNO_NODE,BTNO_NODE,BTNO_NODE,
                               BTNO_NODE,pri);

        ret = cache->dodelete;
    }
    

    btreeDeallocSecArray(cache,arrays);
    ajStrDel(&key);

    return ret;
}
ID btreeFindPriBalanceTwo
TY static
MO ajindex
LB core
XX
DE Master routine for entry deletion from level 2 keyword tree.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD Current node
PX
PN [3]
PA r leftNode ajulong
PD Node to left
PX
PN [4]
PA r rightNode ajulong
PD Node to right
PX
PN [5]
PA r lAnchor ajulong
PD Left anchor
PX
PN [6]
PA r rAnchor ajulong
PD Right anchor
PX
PN [7]
PA r pri const AjPBtPri
PD pri
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeFindPriBalanceTwo(AjPBtcache cache, ajulong thisNode,
                                      ajulong leftNode, ajulong rightNode,
                                      ajulong lAnchor, ajulong rAnchor,
                                      const AjPBtPri pri)
{
    unsigned char *buf  = NULL;
    unsigned char *buf1 = NULL;
    
    ajulong nextNode   = BTNO_NODE;
    ajulong nextLeft   = BTNO_NODE;
    ajulong nextRight  = BTNO_NODE;
    ajulong nextAncL   = BTNO_NODE;
    ajulong nextAncR   = BTNO_NODE;
    ajulong done       = 0L;
    
    ajuint  nkeys      = 0;
    ajuint  order      = 0;
    ajuint  minkeys    = 0;
    ajuint  i;
    ajuint  nodetype   = 0;

    ajuint n1keys      = 0;
    
    AjPBtpage page  = NULL;
    AjPBtpage page1 = NULL;

    ajulong balanceNode = 0L;
    ajulong blockno     = 0L;
    ajulong ptrSave     = 0L;

    AjPStr *karray  = NULL;
    ajulong *parray  = NULL;
    AjPStr *k1array = NULL;
    ajulong *p1array = NULL;

    AjPBtMem arrays  = NULL;
    AjPBtMem arrays1 = NULL;
    
    const AjPStr key = NULL;
    AjBool existed = ajFalse;
    
    /* ajDebug("In btreeFindPriBalanceTwo\n"); */

    if(thisNode != cache->secrootblock)
	page = btreeSeccacheRead(cache,thisNode);
    else
    {
	page = btreeSeccacheLocate(cache,thisNode);
	page->dirty = BT_LOCK;
        page->lockfor = 1861;
    }

    cache->dodelete = ajFalse;

    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);
    
    order = cache->sorder;
    /* order-1 is the number of keys in the node */
    minkeys = (order-1) / 2;
    if((order-1)%2)
	++minkeys;

    /*
    ** If thisNode contains >= minkeys then it is not a candidate
    ** for balancing
    */
    if(nkeys >= minkeys)
	balanceNode = BTNO_BALANCE;
    else
	balanceNode = page->pagepos;

    arrays  = btreeAllocSecArray(cache);
    arrays1 = btreeAllocSecArray(cache);

    karray = arrays->karray;
    parray = arrays->parray;

    k1array = arrays1->karray;
    p1array = arrays1->parray;

    key = pri->id;

    btreeGetKeys(cache,buf,&karray,&parray);

    i=0;

    while(i!=nkeys && MAJSTRCMPS(key,karray[i])>=0)
	++i;

    blockno = parray[i];

    nextNode = blockno;
    ptrSave = i;

    GBT_NODETYPE(buf,&nodetype);

    if(!(nodetype == BT_SECLEAF) && !(nodetype == BT_SECROOT && !cache->slevel))
    {
	if(nextNode == parray[0])
	{
	    if(leftNode != BTNO_NODE)
	    {
		page1 = btreeSeccacheRead(cache,leftNode);
		buf1 = page1->buf;
		GBT_NKEYS(buf1,&n1keys);
		btreeGetKeys(cache,buf1,&k1array,&p1array);
		nextLeft = p1array[n1keys];
	    }
	    else
		nextLeft = BTNO_NODE;
	    
	    if(thisNode == cache->secrootblock)
		nextAncL = thisNode;
	    else
		nextAncL = lAnchor;
	}
	else
	{
	    nextLeft = parray[ptrSave-1];
	    nextAncL = thisNode;
	}

	if(nextNode == parray[nkeys])
	{
	    if(rightNode != BTNO_NODE)
	    {
		page1 = btreeSeccacheRead(cache,rightNode);
		buf1 = page1->buf;
		GBT_NKEYS(buf1,&n1keys);
		btreeGetKeys(cache,buf1,&k1array,&p1array);
		nextRight = p1array[0];
	    }
	    else
		nextRight = BTNO_NODE;

	    if(thisNode == cache->secrootblock)
		nextAncR = thisNode;
	    else
		nextAncR = rAnchor;
	}
	else
	{
	    nextRight = parray[ptrSave+1];
	    nextAncR  = thisNode;
	}



	/* Check to see whether key exists in an internal node */
	if(nodetype != BT_SECLEAF && cache->slevel)
	{
	    i=0;

	    while(i!=nkeys && MAJSTRCMPS(key,karray[i]))
		++i;

	    if(i!=nkeys)
	    {
		btreeFindPriMinTwo(cache,parray[i+1],key);
		ajStrAssignS(&karray[i],cache->replace);
		btreeWriteNode(cache,page,karray,parray,nkeys);
	    }
	
	}
	
	btreeFindPriBalanceTwo(cache,nextNode,nextLeft,nextRight,
                               nextAncL,nextAncR,pri);

	if(thisNode != cache->secrootblock)
	    page = btreeSeccacheRead(cache,thisNode);
	else
	{
	    page = btreeSeccacheLocate(cache,thisNode);
	    page->dirty = BT_LOCK;
            page->lockfor = 1862;
	}
	buf = page->buf;

    }
    else
    {
	if(nodetype == BT_SECLEAF || (nodetype==BT_SECROOT && !cache->slevel))
	{
	    existed = btreeRemovePriEntryTwo(cache,thisNode,pri);

	    if(existed)
		cache->dodelete = ajTrue;
	    GBT_NKEYS(buf,&nkeys);

	    if(nkeys >= minkeys || (nodetype==BT_SECROOT && !cache->slevel))
		balanceNode = BTNO_BALANCE;
	    else
		balanceNode = page->pagepos;
	}
    }


    if(balanceNode == BTNO_BALANCE || thisNode == cache->secrootblock)
	done = BTNO_NODE;
    else
	done = btreeRebalancePriTwo(cache,thisNode,leftNode,rightNode,
                                    lAnchor,rAnchor);

    btreeDeallocSecArray(cache,arrays);
    btreeDeallocSecArray(cache,arrays1);

    return done;
}
ID btreeFindPriMinTwo
TY static
MO ajindex
LB core
XX
DE Find minimum key in keyword level 2 subtree and store in cache.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page
PX
PN [3]
PA r key const AjPStr
PD key
PX
RT void
RD 
RX
//


static void btreeFindPriMinTwo(AjPBtcache cache, ajulong pagepos,
                               const AjPStr key)
{
    AjPBtpage page   = NULL;
    AjPSecbucket bucket = NULL;
    AjPStr *karray   = NULL;
    ajulong *parray   = NULL;

    ajuint nkeys    = 0;
    ajuint nodetype = 0;
    ajuint nentries = 0;
    ajuint i;
    AjPBtMem arrays = NULL;
    
    unsigned char *buf = NULL;

    /* ajDebug("In btreeFindPriMinTwo\n"); */

    arrays = btreeAllocSecArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;

    page = btreeSeccacheRead(cache,pagepos);
    buf  = page->buf;
    GBT_NODETYPE(buf,&nodetype);
    GBT_NKEYS(buf,&nkeys);

    btreeGetKeys(cache,buf,&karray,&parray);

    if(nodetype == BT_SECLEAF)
    {
	bucket = btreeReadSecbucket(cache,parray[0]);
	nentries = bucket->Nentries;

        /*
        ** If there's only one entry then it must be the key marked
        ** for deletion
        */
        if(nentries<2)
	{
	    btreeSecbucketDel(&bucket);
	    bucket = btreeReadSecbucket(cache,parray[1]);
	    nentries = bucket->Nentries;
	}

        /* Check for empty bucket - shouldn't happen */
        /* Checking solely out of interest */
	if(nentries<1)	
	    ajFatal("FindPriMinTwo: Too few entries in bucket Nkeys=%u\n",
                    nkeys);


        /* Find lowest value key in the bucket and store in cache */
        ajStrAssignS(&cache->replace,bucket->SecIds[0]);

	if(!MAJSTRCMPS(cache->replace,key))
	    ajStrAssignS(&cache->replace,bucket->SecIds[1]);

	for(i=1;iSecIds[i],cache->replace)<0 &&
	       MAJSTRCMPS(bucket->SecIds[i],key))
		ajStrAssignS(&cache->replace,bucket->SecIds[i]);

	btreeSecbucketDel(&bucket);
    }
    else
    {
	pagepos = parray[0];
	btreeFindPriMinTwo(cache,pagepos,key);
	
    }

    btreeDeallocSecArray(cache,arrays);
    
    return;
}
ID btreeRemovePriEntryTwo
TY static
MO ajindex
LB core
XX
DE Find and delete an ID from a given keyword tree level 2 leaf node if
DE necessary.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD leaf node page
PX
PN [3]
PA r pri const AjPBtPri
PD pri
PX
RT AjBool
RD True if found (and deleted)
RX
//


static AjBool btreeRemovePriEntryTwo(AjPBtcache cache, ajulong pagepos,
                                     const AjPBtPri pri)
{
    AjPBtpage page   = NULL;
    AjPSecbucket bucket = NULL;
    
    AjPStr *karray = NULL;
    ajulong *parray = NULL;
    ajulong blockno = 0L;
    
    ajuint nkeys    = 0;
    ajuint nentries = 0;
    ajuint i;

    ajuint dirtysave = 0;
    
    AjBool found = ajFalse;
    const AjPStr key  = NULL;

    unsigned char *buf = NULL;
    AjPBtMem arrays = NULL;
    
    /* ajDebug("In btreeRemovePriEntryTwo\n"); */

    page = btreeSeccacheRead(cache,pagepos);
    buf = page->buf;
    dirtysave = page->dirty;
    page->dirty = BT_LOCK;
    page->lockfor = 1871;
  
    GBT_NKEYS(buf,&nkeys);

    if(!nkeys)
	return ajFalse;


    arrays = btreeAllocSecArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;

    btreeGetKeys(cache,buf,&karray,&parray);
    
    key = pri->id;

    i=0;

    while(i!=nkeys && MAJSTRCMPS(key,karray[i])>=0)
	++i;

    blockno = parray[i];
    
    bucket = btreeReadSecbucket(cache,blockno);


    nentries = bucket->Nentries;
    found = ajFalse;

    for(i=0;iSecIds[i]))
	{
	    found = ajTrue;
	    break;
	}
    

    if(found)
    {
	/* Perform the deletion */
	if(nentries == 1)
	{
	    bucket->Nentries = 0;
	    AJFREE(bucket->SecIds[0]);
	}
	else
	{
	    AJFREE(bucket->SecIds[i]);
	    bucket->SecIds[i] = bucket->SecIds[nentries-1];
	    --bucket->Nentries;
	}

	btreeWriteSecbucket(cache,bucket,blockno);
	btreeAdjustPribucketsTwo(cache,page);
	page->dirty = BT_DIRTY;
    }
    else
	page->dirty = dirtysave;

    btreeSecbucketDel(&bucket);

    btreeDeallocSecArray(cache,arrays);
    
    if(!found)
	return ajFalse;

    return ajTrue;
}
ID btreeAdjustPribucketsTwo
TY static
MO ajindex
LB core
XX
DE Re-order leaf buckets in keyword level 2 tree
DE Can be called whatever the state of a leaf.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u leaf AjPBtpage
PD leaf page
PX
RT void
RD 
RX
//


static void btreeAdjustPribucketsTwo(AjPBtcache cache, AjPBtpage leaf)
{
    ajuint nkeys = 0;
    unsigned char *lbuf = NULL;
    AjPSecbucket *buckets  = NULL;

    AjPBtMem arrays = NULL;    
    AjPBtMem newarrays = NULL;    
    AjPStr *keys        = NULL;
    ajulong *ptrs        = NULL;
    ajulong *overflows   = NULL;
    
    ajuint i = 0;
    ajuint j = 0;
    
    ajuint order;
    ajuint bentries      = 0;
    ajuint totalkeys     = 0;
    ajuint nperbucket    = 0;
    ajuint maxnperbucket = 0;
    ajuint count         = 0;
    ajuint totkeylen     = 0;
    ajuint keylimit      = 0;
    ajuint bucketn       = 0;
    ajuint bucketlimit   = 0;
    ajuint nodetype      = 0;
    ajuint nids          = 0;
    ajuint totnids       = 0;
    
    AjPList idlist    = NULL;
    ajuint   dirtysave = 0;
    AjPStr bid       = NULL;
    AjPSecbucket cbucket = NULL;
    AjPStr cid       = NULL;

    ajuint v = 0;
    
    /* ajDebug("In btreeAdjustPribucketsTwo\n"); */

    dirtysave = leaf->dirty;

    leaf->dirty = BT_LOCK;
    leaf->lockfor = 1881;
    lbuf = leaf->buf;

    GBT_NKEYS(lbuf,&nkeys);

    if(!nkeys)
    {
	leaf->dirty = dirtysave;

	return;
    }


    GBT_NODETYPE(lbuf,&nodetype);

    order = cache->sorder;
    nperbucket = cache->snperbucket;
    

    /* Read keys/ptrs */

    arrays = btreeAllocSecArray(cache);
    ptrs = arrays->parray;

    btreeGetPointers(cache,lbuf,&ptrs);


    for(i=0;i> 1;

    if(!maxnperbucket)
	++maxnperbucket;

    if(!leaf->pagepos)
	maxnperbucket = nperbucket;

    /* Work out the number of new buckets needed */
    bucketn = (totalkeys / maxnperbucket);

    if(totalkeys % maxnperbucket)
	++bucketn;

    if(bucketn == 1)
	++bucketn;
    
    while(bucketn > order)
    {
        ++maxnperbucket;
        bucketn = (totalkeys / maxnperbucket);

        if(totalkeys % maxnperbucket)
            ++bucketn;
    }

    /* Read buckets */
    AJCNEW0(buckets,nkeys+1);
    keylimit = nkeys + 1;
    
    for(i=0;ikarray;
    overflows = newarrays->overflows;
    
    /* Read IDs from all buckets and push to list and sort (increasing id) */
    idlist  = ajListNew();

    for(i=0;iOverflow;
	bentries = buckets[i]->Nentries;

	for(j=0;jSecIds[j]);
	
	AJFREE(buckets[i]->keylen);
	AJFREE(buckets[i]->SecIds);
	AJFREE(buckets[i]);
    }

/* AJB: check this compares ajStr objects OK */
    ajListSort(idlist, &ajStrVcmp);
    AJFREE(buckets);

    cbucket = btreeSecbucketNew(maxnperbucket,cache->idlimit);
    bucketlimit = bucketn - 1;

    totnids = 0;
    nids = (ajuint) ajListGetLength(idlist);


    if(!totalkeys)
    {
	v = totalkeys;
	SBT_NKEYS(lbuf,v);

        btreeDeallocSecArray(cache,arrays);
        btreeDeallocSecArray(cache,newarrays);

	ajListFree(&idlist);
	leaf->dirty = BT_DIRTY;

	return;
    }
    
    if(nids <= maxnperbucket)
    {
	cbucket->Overflow = overflows[1];
	cbucket->Nentries = 0;
	ajListPeek(idlist,(void **)&bid);
	ajStrAssignS(&keys[0],bid);

	count = 0;

	while(count!=maxnperbucket && totnids != nids)
	{
	    ajListPop(idlist,(void **)&bid);
	    
	    cid = cbucket->SecIds[count];
	    ajStrAssignS(&cid,bid);
	    
	    cbucket->keylen[count] = BT_BUCKSECLEN(bid);
	    ++cbucket->Nentries;
	    ++count;
	    ++totnids;
	    ajStrDel(&bid);
	}


	totkeylen += ajStrGetLen(keys[0]);

	if(!ptrs[1])
	    ptrs[1] = cache->totsize;

	btreeWriteSecbucket(cache,cbucket,ptrs[1]);

	cbucket->Overflow = overflows[0];
	cbucket->Nentries = 0;

	if(!ptrs[0])
	    ptrs[0] = cache->totsize;

	btreeWriteSecbucket(cache,cbucket,ptrs[0]);
    }
    else
    {
	for(i=0;iOverflow = overflows[i];
	    cbucket->Nentries = 0;

	    count = 0;

	    while(count!=maxnperbucket && totnids != nids)
	    {
		ajListPop(idlist,(void **)&bid);

		cid = cbucket->SecIds[count];
		ajStrAssignS(&cid,bid);
		
		cbucket->keylen[count] = BT_BUCKSECLEN(bid);
		++cbucket->Nentries;
		++count;
		ajStrDel(&bid);
	    }


	    ajListPeek(idlist,(void **)&bid);
	    ajStrAssignS(&keys[i],bid);


	    totkeylen += ajStrGetLen(bid);

	    if(!ptrs[i])
		ptrs[i] = cache->totsize;

	    btreeWriteSecbucket(cache,cbucket,ptrs[i]);
	}
	
	
	/* Deal with greater-than bucket */
	
	cbucket->Overflow = overflows[i];
	cbucket->Nentries = 0;

	count = 0;

	while(ajListPop(idlist,(void **)&bid))
	{
	    cid = cbucket->SecIds[count];
	    ajStrAssignS(&cid,bid);
	    
	    ++cbucket->Nentries;
	    ++count;
	    ajStrDel(&bid);
	}
	
	
	if(!ptrs[i])
	    ptrs[i] = cache->totsize;
	
	btreeWriteSecbucket(cache,cbucket,ptrs[i]);
    }
    

    cbucket->Nentries = maxnperbucket;
    btreeSecbucketDel(&cbucket);

    /* Now write out a modified leaf with new keys/ptrs */

    nkeys = bucketn - 1;
    btreeWriteNode(cache,leaf,keys,ptrs,nkeys);

    leaf->dirty = dirtysave;
    if(nodetype == BT_SECROOT)
    {
	leaf->dirty = BT_LOCK;
        leaf->lockfor = 1882;
    }

    btreeDeallocSecArray(cache,arrays);
    btreeDeallocSecArray(cache,newarrays);

    btreeSecbucketDel(&cbucket);
    ajListFree(&idlist);

    return;
}
ID btreeRebalancePriTwo
TY static
MO ajindex
LB core
XX
DE Rebalance keyword level 2 tree after deletion
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD Node to rebalance
PX
PN [3]
PA r leftNode ajulong
PD left node
PX
PN [4]
PA r rightNode ajulong
PD right node
PX
PN [5]
PA r lAnchor ajulong
PD left anchor
PX
PN [6]
PA r rAnchor ajulong
PD right anchor
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeRebalancePriTwo(AjPBtcache cache, ajulong thisNode,
                                   ajulong leftNode, ajulong rightNode,
                                   ajulong lAnchor, ajulong rAnchor)
{
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;
    unsigned char *tbuf = NULL;

    ajulong anchorNode   = 0L;
    ajulong balanceNode  = 0L;
    ajulong mergeNode    = 0L;
    ajulong done         = 0L;
    ajulong parent       = 0L;
    
    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage tpage = NULL;
    
    ajuint lnkeys  = 0;
    ajuint rnkeys  = 0;
    ajuint size    = 0;
    ajuint order   = 0;
    ajuint minsize = 0;

    AjBool leftok  = ajFalse;
    AjBool rightok = ajFalse;
    
    
    /* ajDebug("In btreeRebalancePriTwo\n"); */

    if(leftNode!=BTNO_NODE && lAnchor!=BTNO_NODE)
	leftok = ajTrue;

    if(rightNode!=BTNO_NODE && rAnchor!=BTNO_NODE)
	rightok = ajTrue;

    if(!leftok && !rightok)
	return BTNO_NODE;
    

    if(leftok)
    {
	lpage = btreeSeccacheRead(cache,leftNode);
	lbuf  = lpage->buf;
	GBT_NKEYS(lbuf,&lnkeys);
    }
    

    if(rightok)
    {
	rpage = btreeSeccacheRead(cache,rightNode);
	rbuf  = rpage->buf;
	GBT_NKEYS(rbuf,&rnkeys);
    }
    


    if(leftok && rightok)
    {
	size = (lnkeys >= rnkeys) ? lnkeys : rnkeys;
	balanceNode = (lnkeys >= rnkeys) ? leftNode : rightNode;
    }
    else if(leftok)
    {
	size = lnkeys;
	balanceNode = leftNode;
    }
    else
    {
	size = rnkeys;
	balanceNode = rightNode;
    }

    
    order = cache->sorder;
    minsize = (order-1) / 2;

    if((order-1)%2)
	++minsize;

    if(size >= minsize)
    {
	if(leftok && rightok)
	    anchorNode = (lnkeys >= rnkeys) ? lAnchor : rAnchor;
	else if(leftok)
	    anchorNode = lAnchor;
	else
	    anchorNode = rAnchor;

	done = btreeShiftPriTwo(cache,thisNode,balanceNode,anchorNode);
    }
	    
    else
    {
	tpage = btreeSeccacheRead(cache,thisNode);
	tbuf  = tpage->buf;
	GBT_PREV(tbuf,&parent);
	if(leftok && rightok)
	{
	    anchorNode = (parent == lAnchor) ? lAnchor : rAnchor;
	    mergeNode  = (anchorNode == lAnchor) ? leftNode : rightNode;
	}
	else if(leftok)
	{
	    anchorNode = lAnchor;
	    mergeNode  = leftNode;
	}
	else
	{
	    anchorNode = rAnchor;
	    mergeNode  = rightNode;
	}

	done = btreeMergePriTwo(cache,thisNode,mergeNode,anchorNode);
    }

    return done;
}
ID btreeShiftPriTwo
TY static
MO ajindex
LB core
XX
DE Shift spare entries from one keyword tree level 2 node to another.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD master node
PX
PN [3]
PA r balanceNode ajulong
PD balance node
PX
PN [4]
PA r anchorNode ajulong
PD anchor node
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeShiftPriTwo(AjPBtcache cache, ajulong thisNode,
                               ajulong balanceNode, ajulong anchorNode)
{
    unsigned char *tbuf = NULL;
    unsigned char *abuf = NULL;
    unsigned char *bbuf = NULL;
    unsigned char *buf  = NULL;
    
    AjPStr *kTarray = NULL;
    AjPStr *kAarray = NULL;
    AjPStr *kBarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pAarray = NULL;
    ajulong *pBarray = NULL;
    
    ajuint  nAkeys = 0;
    ajuint  nBkeys = 0;
    ajuint  nTkeys = 0;
    ajuint  i;
    ajint ii;

    AjPBtpage pageA = NULL;
    AjPBtpage pageB = NULL;
    AjPBtpage pageT = NULL;
    AjPBtpage page  = NULL;
    
    AjPBtpage leftpage = NULL;

    ajuint anchorPos   = 0;
    ajulong prev        = 0L;
    ajuint  nodetype    = 0;

    ajulong lv = 0L;
    
    AjPBtMem arraysA = NULL;    
    AjPBtMem arraysB = NULL;    
    AjPBtMem arraysT = NULL;    

    /* ajDebug("In btreeShiftPriTwo\n"); */


    arraysA = btreeAllocSecArray(cache);
    kAarray = arraysA->karray;
    pAarray = arraysA->parray;

    arraysB = btreeAllocSecArray(cache);
    kBarray = arraysB->karray;
    pBarray = arraysB->parray;

    arraysT = btreeAllocSecArray(cache);
    kTarray = arraysT->karray;
    pTarray = arraysT->parray;


    pageA = btreeSeccacheRead(cache,anchorNode);
    pageA->dirty = BT_LOCK;
    pageA->lockfor = 1891;
    abuf = pageA->buf;
    pageB = btreeSeccacheRead(cache,balanceNode);
    pageB->dirty = BT_LOCK;
    pageB->lockfor = 1892;
    bbuf = pageB->buf;
    pageT = btreeSeccacheRead(cache,thisNode);
    pageT->dirty = BT_LOCK;
    pageT->lockfor = 1893;
    tbuf = pageT->buf;

    GBT_NKEYS(abuf,&nAkeys);
    GBT_NKEYS(bbuf,&nBkeys);
    GBT_NKEYS(tbuf,&nTkeys);

    btreeGetKeys(cache,abuf,&kAarray,&pAarray);
    btreeGetKeys(cache,bbuf,&kBarray,&pBarray);
    btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
    
    if(MAJSTRCMPS(kTarray[nTkeys-1],kBarray[nBkeys-1])<0)
	leftpage = pageT;
    else
	leftpage = pageB;


    if(leftpage == pageT)
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && MAJSTRCMPS(kTarray[nTkeys-1],kAarray[i])>=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to thisNode */
	ajStrAssignS(&kTarray[nTkeys],kAarray[anchorPos]);
	++nTkeys;

	/* Shift extra */

	while(nTkeys < nBkeys)
	{
	    ajStrAssignS(&kTarray[nTkeys],kBarray[0]);
	    pTarray[nTkeys] = pBarray[0];
	    ++nTkeys;
	    --nBkeys;

	    for(i=0;i=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to thisNode */
	pTarray[nTkeys+1] = pTarray[nTkeys];

	for(ii=nTkeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kTarray[ii+1],kTarray[ii]);
	    pTarray[ii+1] = pTarray[ii];
	}

	ajStrAssignS(&kTarray[0],kAarray[anchorPos]);
	++nTkeys;

	/* Shift extra */
	while(nTkeys < nBkeys)
	{
	    pTarray[nTkeys+1] = pTarray[nTkeys];

	    for(ii=nTkeys-1;ii>-1;--ii)
	    {
		ajStrAssignS(&kTarray[ii+1],kTarray[ii]);
		pTarray[ii+1] = pTarray[ii];
	    }
	    ajStrAssignS(&kTarray[0],kBarray[nBkeys-1]);
	    pTarray[1] = pBarray[nBkeys];
	    ++nTkeys;
	    --nBkeys;
	}


	/* Adjust anchor key */
	ajStrAssignS(&kAarray[anchorPos],kTarray[0]);
	--nTkeys;

	for(i=0;ipagepos;

    for(i=0;ibuf;
	GBT_NODETYPE(buf,&nodetype);

	if(nodetype != BT_IDBUCKET)
	{
	    lv = prev;
	    SBT_PREV(buf,lv);
	    page->dirty = BT_DIRTY;
	}
    }

    btreeWriteNode(cache,pageA,kAarray,pAarray,nAkeys);
    btreeWriteNode(cache,pageB,kBarray,pBarray,nBkeys);
    btreeWriteNode(cache,pageT,kTarray,pTarray,nTkeys);

    if(anchorNode == cache->secrootblock)
    {
	pageA->dirty = BT_LOCK;
        pageA->lockfor = 1894;
    }

    btreeDeallocSecArray(cache,arraysA);
    btreeDeallocSecArray(cache,arraysB);
    btreeDeallocSecArray(cache,arraysT);

    return BTNO_NODE;
}
ID btreeMergePriTwo
TY static
MO ajindex
LB core
XX
DE Merge two nodes.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD master node
PX
PN [3]
PA r mergeNode ajulong
PD merge node
PX
PN [4]
PA r anchorNode ajulong
PD anchor node
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeMergePriTwo(AjPBtcache cache, ajulong thisNode,
                                ajulong mergeNode, ajulong anchorNode)
{
    unsigned char *tbuf = NULL;
    unsigned char *abuf = NULL;
    unsigned char *nbuf = NULL;
    unsigned char *buf  = NULL;
    
    AjPStr *kTarray = NULL;
    AjPStr *kAarray = NULL;
    AjPStr *kNarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pAarray = NULL;
    ajulong *pNarray = NULL;

    ajulong thisprev  = 0L;
    ajulong mergeprev = 0L;
    
    
    ajuint  nAkeys = 0;
    ajuint  nNkeys = 0;
    ajuint  nTkeys = 0;
    ajuint  count  = 0;
    ajuint  i;
    ajint ii;

    ajuint   nodetype = 0;
    
    ajuint saveA = 0;
    ajuint saveN = 0;
    ajuint saveT = 0;
    
    AjPBtpage pageA = NULL;
    AjPBtpage pageN = NULL;
    AjPBtpage pageT = NULL;
    AjPBtpage page  = NULL;
    
    AjPBtpage leftpage = NULL;

    ajuint anchorPos = 0;
    ajulong prev      = 0L;

    ajulong lv = 0L;

    AjPBtMem arraysA = NULL;    
    AjPBtMem arraysN = NULL;    
    AjPBtMem arraysT = NULL;    

    AjBool collapse = ajFalse;
    ajulong csrb = 0L;
    
    /* ajDebug("In btreeMergePriTwo\n"); */

    pageA = btreeSeccacheRead(cache,anchorNode);
    saveA = pageA->dirty;
    pageA->dirty = BT_LOCK;
    pageA->lockfor = 1901;
    abuf = pageA->buf;
    pageN = btreeSeccacheRead(cache,mergeNode);
    saveN = pageN->dirty;
    pageN->dirty = BT_LOCK;
    pageN->lockfor = 1902;
    nbuf = pageN->buf;
    pageT = btreeSeccacheRead(cache,thisNode);
    saveT = pageT->dirty;
    pageT->dirty = BT_LOCK;
    pageT->lockfor = 1903;
    tbuf = pageT->buf;

    GBT_PREV(tbuf,&thisprev);
    GBT_PREV(nbuf,&mergeprev);

    GBT_NKEYS(abuf,&nAkeys);
    GBT_NKEYS(nbuf,&nNkeys);
    GBT_NKEYS(tbuf,&nTkeys);

    GBT_NODETYPE(nbuf,&nodetype);

    csrb = cache->secrootblock;

    if(nAkeys == 1)
    {
	if(anchorNode==csrb && thisprev==csrb && mergeprev==csrb)
	    collapse = ajTrue;
	else
	{
	    pageA->dirty = saveA;
	    pageN->dirty = saveN;
	    pageT->dirty = saveT;

	    return thisNode;
	}
    }

    arraysA = btreeAllocSecArray(cache);
    kAarray = arraysA->karray;
    pAarray = arraysA->parray;

    arraysN = btreeAllocSecArray(cache);
    kNarray = arraysN->karray;
    pNarray = arraysN->parray;

    arraysT = btreeAllocSecArray(cache);
    kTarray = arraysT->karray;
    pTarray = arraysT->parray;

    btreeGetKeys(cache,abuf,&kAarray,&pAarray);
    btreeGetKeys(cache,nbuf,&kNarray,&pNarray);
    btreeGetKeys(cache,tbuf,&kTarray,&pTarray);

    if(MAJSTRCMPS(kTarray[nTkeys-1],kNarray[nNkeys-1])<0)
	leftpage = pageT;
    else
	leftpage = pageN;


    if(leftpage == pageT)
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && MAJSTRCMPS(kTarray[nTkeys-1],kAarray[i])>=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to neighbour Node */
	pNarray[nNkeys+1] = pNarray[nNkeys];

	for(ii=nNkeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kNarray[ii+1],kNarray[ii]);
	    pNarray[ii+1] = pNarray[ii];
	}

	ajStrAssignS(&kNarray[0],kAarray[anchorPos]);
	++nNkeys;


	/* Adjust anchor node keys/ptrs */
	++anchorPos;
	if(anchorPos==nAkeys)
	    pAarray[nAkeys-1] = pAarray[nAkeys];
	else
	{
	    for(i=anchorPos;i-1;--ii)
	    {
		ajStrAssignS(&kNarray[ii+1],kNarray[ii]);
		pNarray[ii+1] = pNarray[ii];
	    }

	    ajStrAssignS(&kNarray[0],kTarray[nTkeys-1]);
	    pNarray[1] = pTarray[nTkeys];
	    pNarray[0] = pTarray[nTkeys-1];
	    --nTkeys;
	    ++nNkeys;
	}

	/* At this point the 'this' node could be added to a free list */
    }
    else
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && MAJSTRCMPS(kNarray[nNkeys-1],kAarray[i])>=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to neighbourNode */
	ajStrAssignS(&kNarray[nNkeys],kAarray[anchorPos]);
	++nNkeys;

	/* Adjust anchor node keys/ptrs */
	++anchorPos;

	if(anchorPos!=nAkeys)
	    for(i=anchorPos;ipagepos;

    for(i=0;i<=nNkeys;++i)
    {
	page = btreeSeccacheRead(cache,pNarray[i]);
	buf = page->buf;
	GBT_NODETYPE(buf,&nodetype);

	if(nodetype != BT_IDBUCKET)
	{
	    lv = prev;
	    SBT_PREV(buf,lv);
	    page->dirty = BT_DIRTY;
	}
    }

    pageT->dirty = BT_CLEAN;
    btreeWriteNode(cache,pageA,kAarray,pAarray,nAkeys);
    btreeWriteNode(cache,pageN,kNarray,pNarray,nNkeys);

    if(anchorNode == cache->secrootblock)
    {
	pageA->dirty = BT_LOCK;
        pageA->lockfor = 1904;
    }

    btreeDeallocSecArray(cache,arraysA);
    btreeDeallocSecArray(cache,arraysN);
    btreeDeallocSecArray(cache,arraysT);
    
    if(collapse)
	btreeCollapseRootPriTwo(cache,mergeNode);

    return thisNode;
}
ID btreeCollapseRootPriTwo
TY static
MO ajindex
LB core
XX
DE Collapse root page for keyword level 2 tree.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number to make new root
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeCollapseRootPriTwo(AjPBtcache cache, ajulong pagepos)
{
    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;

    AjPStr *karray = NULL;
    ajulong *parray = NULL;

    AjPBtpage rootpage = NULL;
    AjPBtpage page     = NULL;
    
    ajuint nodetype = 0;
    ajuint nkeys    = 0;
    ajuint i;

    ajulong prev = 0L;
    AjPBtMem arrays = NULL;
    
    /* ajDebug("In btreeCollapseRootPriTwo\n"); */
    
    if(!cache->slevel)
	return BTNO_NODE;

    rootpage = btreeSeccacheLocate(cache,cache->secrootblock);
    buf = rootpage->buf;
    page = btreeSeccacheRead(cache,pagepos);


    arrays = btreeAllocSecArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;

    /*
    ** Swap pagepos values to make root the child and child the root
    ** Update node types and mark the original root as a clean page
    */

    /* At this point page->pagepos could be added to a free list */

    rootpage->pagepos = page->pagepos;
    rootpage->dirty = BT_CLEAN;
    nodetype = BT_SECINTERNAL;
    SBT_NODETYPE(buf,nodetype);

    page->pagepos = cache->secrootblock;
    page->dirty = BT_LOCK;
    page->lockfor = 1911;
    buf = page->buf;
    nodetype = BT_SECROOT;
    SBT_NODETYPE(buf,nodetype);
    
    --cache->slevel;

    if(cache->slevel)
    {
	/*
	 ** Update the PREV pointers of the new root's children
	 */
	GBT_NKEYS(buf,&nkeys);
	btreeGetKeys(cache,buf,&karray,&parray);

	for(i=0;i<=nkeys;++i)
	{
	    page = btreeSeccacheRead(cache,parray[i]);
	    lbuf = page->buf;
	    SBT_PREV(lbuf,prev);
	    page->dirty = BT_DIRTY;
	}
    }

    btreeDeallocSecArray(cache,arrays);
    
    return 0L;
}
ID btreeFindPriBalanceOne
TY static
MO ajindex
LB core
XX
DE Master routine for entry deletion from level 1 keyword tree.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD Current node
PX
PN [3]
PA r leftNode ajulong
PD Node to left
PX
PN [4]
PA r rightNode ajulong
PD Node to right
PX
PN [5]
PA r lAnchor ajulong
PD Left anchor
PX
PN [6]
PA r rAnchor ajulong
PD Right anchor
PX
PN [7]
PA r pri const AjPBtPri
PD pri
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeFindPriBalanceOne(AjPBtcache cache, ajulong thisNode,
                                      ajulong leftNode, ajulong rightNode,
                                      ajulong lAnchor, ajulong rAnchor,
                                      const AjPBtPri pri)
{
    unsigned char *buf  = NULL;
    unsigned char *buf1 = NULL;
    
    ajulong nextNode   = BTNO_NODE;
    ajulong nextLeft   = BTNO_NODE;
    ajulong nextRight  = BTNO_NODE;
    ajulong nextAncL   = BTNO_NODE;
    ajulong nextAncR   = BTNO_NODE;
    ajulong done       = 0L;
    
    ajuint  nkeys      = 0;
    ajuint  order      = 0;
    ajuint  minkeys    = 0;
    ajuint  i;
    ajuint  nodetype   = 0;

    ajuint n1keys      = 0;
    
    AjPBtpage page  = NULL;
    AjPBtpage page1 = NULL;

    ajulong balanceNode = 0L;
    ajulong blockno     = 0L;
    ajulong ptrSave     = 0L;

    AjPStr *karray  = NULL;
    ajulong *parray  = NULL;
    AjPStr *k1array = NULL;
    ajulong *p1array = NULL;

    AjPBtMem arrays  = NULL;
    AjPBtMem arrays1 = NULL;
    
    const AjPStr key = NULL;
    AjBool existed = ajFalse;
    
    /* ajDebug("In btreeFindPriBalanceOne\n"); */

    if(thisNode)
	page = btreePricacheRead(cache,thisNode);
    else
    {   /* It's the root node of the primary hyb tree */
        /* Needs altering for secondary tree          */
	page = btreePricacheLocate(cache,thisNode);
	page->dirty = BT_LOCK;
        page->lockfor = 1921;
    }

    cache->dodelete = ajFalse;

    buf = page->buf;
    GBT_NKEYS(buf,&nkeys);
    
    order = cache->porder;
    /* order-1 is the number of keys in the node */
    minkeys = (order-1) / 2;

    if((order-1)%2)
	++minkeys;

    /*
    ** If thisNode contains >= minkeys then it is not a candidate
    ** for balancing
    */
    if(nkeys >= minkeys)
	balanceNode = BTNO_BALANCE;
    else
	balanceNode = page->pagepos;

    arrays  = btreeAllocPriArray(cache);
    arrays1 = btreeAllocPriArray(cache);

    karray = arrays->karray;
    parray = arrays->parray;

    k1array = arrays1->karray;
    p1array = arrays1->parray;

    key = pri->keyword;

    btreeGetKeys(cache,buf,&karray,&parray);

    i=0;

    while(i!=nkeys && MAJSTRCMPS(key,karray[i])>=0)
	++i;

    blockno = parray[i];

    nextNode = blockno;
    ptrSave = i;

    GBT_NODETYPE(buf,&nodetype);

    if(!(nodetype == BT_LEAF) && !(nodetype == BT_ROOT && !cache->plevel))
    {
	if(nextNode == parray[0])
	{
	    if(leftNode != BTNO_NODE)
	    {
		page1 = btreePricacheRead(cache,leftNode);
		buf1 = page1->buf;
		GBT_NKEYS(buf1,&n1keys);
		btreeGetKeys(cache,buf1,&k1array,&p1array);
		nextLeft = p1array[n1keys];
	    }
	    else
		nextLeft = BTNO_NODE;
	    
	    if(!thisNode)
		nextAncL = thisNode;
	    else
		nextAncL = lAnchor;
	}
	else
	{
	    nextLeft = parray[ptrSave-1];
	    nextAncL = thisNode;
	}

	if(nextNode == parray[nkeys])
	{
	    if(rightNode != BTNO_NODE)
	    {
		page1 = btreePricacheRead(cache,rightNode);
		buf1 = page1->buf;
		GBT_NKEYS(buf1,&n1keys);
		btreeGetKeys(cache,buf1,&k1array,&p1array);
		nextRight = p1array[0];
	    }
	    else
		nextRight = BTNO_NODE;

	    if(!thisNode)
		nextAncR = thisNode;
	    else
		nextAncR = rAnchor;
	}
	else
	{
	    nextRight = parray[ptrSave+1];
	    nextAncR  = thisNode;
	}



	/* Check to see whether key exists in an internal node */
	if(nodetype != BT_LEAF && cache->plevel)
	{
	    i=0;

	    while(i!=nkeys && MAJSTRCMPS(key,karray[i]))
		++i;

	    if(i!=nkeys)
	    {
		btreeFindPriMinOne(cache,parray[i+1],key);
		ajStrAssignS(&karray[i],cache->replace);
		btreeWriteNode(cache,page,karray,parray,nkeys);
	    }
	
	}
	
	btreeFindPriBalanceOne(cache,nextNode,nextLeft,nextRight,
                               nextAncL,nextAncR,pri);

	if(thisNode)
	    page = btreePricacheRead(cache,thisNode);
	else
	{
	    page = btreePricacheLocate(cache,thisNode);
	    page->dirty = BT_LOCK;
            page->lockfor = 1922;
	}
	buf = page->buf;

    }
    else
    {
	if(nodetype == BT_LEAF || (nodetype==BT_ROOT && !cache->plevel))
	{
	    existed = btreeRemovePriEntryOne(cache,thisNode,pri);

	    if(existed)
		cache->dodelete = ajTrue;
	    GBT_NKEYS(buf,&nkeys);

	    if(nkeys >= minkeys || (nodetype==BT_ROOT && !cache->plevel))
		balanceNode = BTNO_BALANCE;
	    else
		balanceNode = page->pagepos;
	}
    }


    if(balanceNode == BTNO_BALANCE || thisNode == 0L)
	done = BTNO_NODE;
    else
	done = btreeRebalancePriOne(cache,thisNode,leftNode,rightNode,
                                    lAnchor,rAnchor);
    

    btreeDeallocPriArray(cache,arrays);
    btreeDeallocPriArray(cache,arrays1);

    return done;
}
ID btreeFindPriMinOne
TY static
MO ajindex
LB core
XX
DE Find minimum key in keyword level 1 subtree and store in cache.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page
PX
PN [3]
PA r key const AjPStr
PD key
PX
RT void
RD 
RX
//


static void btreeFindPriMinOne(AjPBtcache cache, ajulong pagepos,
                               const AjPStr key)
{
    AjPBtpage page   = NULL;
    AjPPribucket bucket = NULL;
    AjPStr *karray   = NULL;
    ajulong *parray   = NULL;

    ajuint nkeys    = 0;
    ajuint nodetype = 0;
    ajuint nentries = 0;
    ajuint i;
    AjPBtMem arrays = NULL;
    
    unsigned char *buf = NULL;

    /* ajDebug("In btreeFindPriMinOne\n"); */

    arrays = btreeAllocPriArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;

    page = btreePricacheRead(cache,pagepos);
    buf  = page->buf;
    GBT_NODETYPE(buf,&nodetype);
    GBT_NKEYS(buf,&nkeys);

    btreeGetKeys(cache,buf,&karray,&parray);

    if(nodetype == BT_LEAF)
    {
	bucket = btreePribucketRead(cache,parray[0]);
	nentries = bucket->Nentries;

        /*
        ** If there's only one entry then it must be the key marked
        ** for deletion
        */
        if(nentries<2)
	{
	    btreePribucketDel(&bucket);
	    bucket = btreePribucketRead(cache,parray[1]);
	    nentries = bucket->Nentries;
	}

        /* Check for empty bucket - shouldn't happen */
        /* Checking solely out of interest */
	if(nentries<1)	
	    ajFatal("FindPriMinOne: Too few entries in bucket Nkeys=%u\n",
                    nkeys);


        /* Find lowest value key in the bucket and store in cache */
        ajStrAssignS(&cache->replace,bucket->codes[0]->keyword);

	if(!MAJSTRCMPS(cache->replace,key))
	    ajStrAssignS(&cache->replace,bucket->codes[1]->keyword);

	for(i=1;icodes[i]->keyword,cache->replace)<0 &&
	       MAJSTRCMPS(bucket->codes[i]->keyword,key))
		ajStrAssignS(&cache->replace,bucket->codes[i]->keyword);
	btreePribucketDel(&bucket);
    }
    else
    {
	pagepos = parray[0];
	btreeFindPriMinOne(cache,pagepos,key);
	
    }

    btreeDeallocPriArray(cache,arrays);
    
    return;
}
ID btreeRemovePriEntryOne
TY static
MO ajindex
LB core
XX
DE Find and delete an ID from a given hybrid tree level 1 leaf node if
DE necessary.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD leaf node page
PX
PN [3]
PA r pri const AjPBtPri
PD keyword object
PX
RT AjBool
RD True if found (and deleted)
RX
//


static AjBool btreeRemovePriEntryOne(AjPBtcache cache, ajulong pagepos,
                                     const AjPBtPri pri)
{
    AjPBtpage page   = NULL;
    AjPPribucket bucket = NULL;
    
    AjPStr *karray = NULL;
    ajulong *parray = NULL;
    ajulong blockno = 0L;
    
    ajuint nkeys    = 0;
    ajuint nentries = 0;
    ajuint i;

    ajuint dirtysave = 0;
    
    AjBool found = ajFalse;
    const AjPStr key  = NULL;

    unsigned char *buf = NULL;
    AjPBtMem arrays = NULL;
    
    /* ajDebug("In btreeRemovePriEntryOne\n"); */

    page = btreePricacheRead(cache,pagepos);
    buf = page->buf;
    dirtysave = page->dirty;
    page->dirty = BT_LOCK;
    page->lockfor = 1931;
  
    GBT_NKEYS(buf,&nkeys);

    if(!nkeys)
	return ajFalse;


    arrays = btreeAllocPriArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;

    btreeGetKeys(cache,buf,&karray,&parray);
    
    key = pri->keyword;

    i=0;

    while(i!=nkeys && MAJSTRCMPS(key,karray[i])>=0)
	++i;

    blockno = parray[i];
    
    bucket = btreePribucketRead(cache,blockno);


    nentries = bucket->Nentries;
    found = ajFalse;

    for(i=0;icodes[i]->keyword))
	{
	    found = ajTrue;
	    break;
	}
    

    if(found)
    {
	/* Perform the deletion */
	if(nentries == 1)
	{
	    bucket->Nentries = 0;
	    ajBtreePriDel(&bucket->codes[0]);
	}
	else
	{
	    ajBtreePriDel(&bucket->codes[i]);
	    bucket->codes[i] = bucket->codes[nentries-1];
	    --bucket->Nentries;
	}

	btreeWritePribucket(cache,bucket,blockno);
	btreeAdjustPribucketsOne(cache,page);
	page->dirty = BT_DIRTY;
    }
    else
	page->dirty = dirtysave;

    btreePribucketDel(&bucket);

    btreeDeallocPriArray(cache,arrays);
    
    if(!found)
	return ajFalse;

    return ajTrue;
}
ID btreeAdjustPribucketsOne
TY static
MO ajindex
LB core
XX
DE Re-order leaf buckets
DE Can be called whatever the state of a leaf.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u leaf AjPBtpage
PD leaf page
PX
RT void
RD 
RX
//


static void btreeAdjustPribucketsOne(AjPBtcache cache, AjPBtpage leaf)
{
    ajuint nkeys = 0;
    unsigned char *lbuf = NULL;
    AjPPribucket *buckets  = NULL;

    AjPBtMem arrays = NULL;    

    AjPStr *keys        = NULL;
    ajulong *ptrs        = NULL;
    ajulong *overflows   = NULL;
    
    ajuint i = 0;
    ajuint j = 0;
    
    ajuint order;
    ajuint bentries      = 0;
    ajuint totalkeys     = 0;
    ajuint nperbucket    = 0;
    ajuint maxnperbucket = 0;
    ajuint count         = 0;
    ajuint totkeylen     = 0;
    ajuint keylimit      = 0;
    ajuint bucketn       = 0;
    ajuint bucketlimit   = 0;
    ajuint nodetype      = 0;
    ajuint nids          = 0;
    ajuint totnids       = 0;
    
    AjPList idlist    = NULL;
    ajuint   dirtysave = 0;
    AjPBtPri bid       = NULL;
    AjPPribucket cbucket = NULL;
    AjPBtPri cid       = NULL;

    ajuint v = 0;
    
    /* ajDebug("In btreeAdjustPribucketsOne\n"); */

    dirtysave = leaf->dirty;

    leaf->dirty = BT_LOCK;
    leaf->lockfor = 1941;
    lbuf = leaf->buf;

    GBT_NKEYS(lbuf,&nkeys);

    if(!nkeys)
    {
	leaf->dirty = dirtysave;
	return;
    }

    GBT_NODETYPE(lbuf,&nodetype);

    order = cache->porder;
    nperbucket = cache->pnperbucket;
    

    /* Read keys/ptrs */

    arrays = btreeAllocPriArray(cache);
    keys = arrays->karray;
    ptrs = arrays->parray;
    overflows = arrays->overflows;

    btreeGetPointers(cache,lbuf,&ptrs);


    for(i=0;i> 1;

    if(!maxnperbucket)
	++maxnperbucket;

    if(!leaf->pagepos)
	maxnperbucket = nperbucket;

    /* Work out the number of new buckets needed */
    bucketn = (totalkeys / maxnperbucket);

    if(totalkeys % maxnperbucket)
	++bucketn;

    if(bucketn == 1)
	++bucketn;

    while(bucketn > order)
    {
        ++maxnperbucket;
        bucketn = (totalkeys / maxnperbucket);

        if(totalkeys % maxnperbucket)
            ++bucketn;
    }
    

    /* Read buckets */
    AJCNEW0(buckets,nkeys+1);
    keylimit = nkeys + 1;
    
    for(i=0;iOverflow;
	bentries = buckets[i]->Nentries;

	for(j=0;jcodes[j]);
	
	AJFREE(buckets[i]->keylen);
	AJFREE(buckets[i]->codes);
	AJFREE(buckets[i]);
    }
    ajListSort(idlist, &btreeKeywordCompare);
    AJFREE(buckets);

    cbucket = btreePribucketNew(maxnperbucket);
    bucketlimit = bucketn - 1;

    totnids = 0;
    nids = (ajuint) ajListGetLength(idlist);


    if(!totalkeys)
    {
	v = totalkeys;
	SBT_NKEYS(lbuf,v);

        btreeDeallocPriArray(cache,arrays);

	ajListFree(&idlist);
	leaf->dirty = BT_DIRTY;

	return;
    }
    
    if(nids <= maxnperbucket)
    {
	cbucket->Overflow = overflows[1];
	cbucket->Nentries = 0;
	ajListPeek(idlist,(void **)&bid);
	ajStrAssignS(&keys[0],bid->keyword);

	count = 0;

	while(count!=maxnperbucket && totnids != nids)
	{
	    ajListPop(idlist,(void **)&bid);
	    
	    cid = cbucket->codes[count];
	    ajStrAssignS(&cid->keyword,bid->keyword);
	    cid->treeblock = bid->treeblock;
	    
	    cbucket->keylen[count] = BT_BUCKPRILEN(bid->keyword);
	    ++cbucket->Nentries;
	    ++count;
	    ++totnids;
	    ajBtreePriDel(&bid);
	}


	totkeylen += ajStrGetLen(keys[0]);

	if(!ptrs[1])
	    ptrs[1] = cache->totsize;
	btreeWritePribucket(cache,cbucket,ptrs[1]);

	cbucket->Overflow = overflows[0];
	cbucket->Nentries = 0;
	if(!ptrs[0])
	    ptrs[0] = cache->totsize;
	btreeWritePribucket(cache,cbucket,ptrs[0]);
    }
    else
    {
	for(i=0;iOverflow = overflows[i];
	    cbucket->Nentries = 0;

	    count = 0;

	    while(count!=maxnperbucket && totnids != nids)
	    {
		ajListPop(idlist,(void **)&bid);

		cid = cbucket->codes[count];
		ajStrAssignS(&cid->keyword,bid->keyword);
		cid->treeblock = bid->treeblock;
		
		cbucket->keylen[count] = BT_BUCKPRILEN(bid->id);
		++cbucket->Nentries;
		++count;
		ajBtreePriDel(&bid);
	    }


	    ajListPeek(idlist,(void **)&bid);
	    ajStrAssignS(&keys[i],bid->keyword);


	    totkeylen += ajStrGetLen(bid->keyword);

	    if(!ptrs[i])
		ptrs[i] = cache->totsize;

	    btreeWritePribucket(cache,cbucket,ptrs[i]);
	}
	
	
	/* Deal with greater-than bucket */
	
	cbucket->Overflow = overflows[i];
	cbucket->Nentries = 0;
	
	
	
	count = 0;

	while(ajListPop(idlist,(void **)&bid))
	{
	    cid = cbucket->codes[count];
	    ajStrAssignS(&cid->keyword,bid->keyword);
	    cid->treeblock = bid->treeblock;
	    
	    ++cbucket->Nentries;
	    ++count;
	    ajBtreePriDel(&bid);
	}
	
	
	if(!ptrs[i])
	    ptrs[i] = cache->totsize;
	
	btreeWritePribucket(cache,cbucket,ptrs[i]);
    }
    

    cbucket->Nentries = maxnperbucket;
    btreePribucketDel(&cbucket);

    /* Now write out a modified leaf with new keys/ptrs */

    nkeys = bucketn - 1;
    v = nkeys;
    SBT_NKEYS(lbuf,v);
    v = totkeylen;
    SBT_TOTLEN(lbuf,v);

    btreeWriteNode(cache,leaf,keys,ptrs,nkeys);

    leaf->dirty = dirtysave;

    if(nodetype == BT_ROOT)
    {
	leaf->dirty = BT_LOCK;
        leaf->lockfor = 1942;
    }

    btreeDeallocPriArray(cache,arrays);

    btreePribucketDel(&cbucket);
    ajListFree(&idlist);

    return;
}
ID btreeRebalancePriOne
TY static
MO ajindex
LB core
XX
DE Rebalance keyword level 1 tree after deletion
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD Node to rebalance
PX
PN [3]
PA r leftNode ajulong
PD left node
PX
PN [4]
PA r rightNode ajulong
PD right node
PX
PN [5]
PA r lAnchor ajulong
PD left anchor
PX
PN [6]
PA r rAnchor ajulong
PD right anchor
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeRebalancePriOne(AjPBtcache cache, ajulong thisNode,
                                    ajulong leftNode, ajulong rightNode,
                                    ajulong lAnchor, ajulong rAnchor)
{
    unsigned char *lbuf = NULL;
    unsigned char *rbuf = NULL;
    unsigned char *tbuf = NULL;

    ajulong anchorNode   = 0L;
    ajulong balanceNode  = 0L;
    ajulong mergeNode    = 0L;
    ajulong done         = 0L;
    ajulong parent       = 0L;
    
    AjPBtpage lpage = NULL;
    AjPBtpage rpage = NULL;
    AjPBtpage tpage = NULL;
    
    ajuint lnkeys  = 0;
    ajuint rnkeys  = 0;
    ajuint size    = 0;
    ajuint order   = 0;
    ajuint minsize = 0;

    AjBool leftok  = ajFalse;
    AjBool rightok = ajFalse;
    
    
    /* ajDebug("In btreeRebalancePriOne\n"); */

    if(leftNode!=BTNO_NODE && lAnchor!=BTNO_NODE)
	leftok = ajTrue;

    if(rightNode!=BTNO_NODE && rAnchor!=BTNO_NODE)
	rightok = ajTrue;

    if(!leftok && !rightok)
	return BTNO_NODE;
    

    if(leftok)
    {
	lpage = btreePricacheRead(cache,leftNode);
	lbuf  = lpage->buf;
	GBT_NKEYS(lbuf,&lnkeys);
    }
    

    if(rightok)
    {
	rpage = btreePricacheRead(cache,rightNode);
	rbuf  = rpage->buf;
	GBT_NKEYS(rbuf,&rnkeys);
    }
    


    if(leftok && rightok)
    {
	size = (lnkeys >= rnkeys) ? lnkeys : rnkeys;
	balanceNode = (lnkeys >= rnkeys) ? leftNode : rightNode;
    }
    else if(leftok)
    {
	size = lnkeys;
	balanceNode = leftNode;
    }
    else
    {
	size = rnkeys;
	balanceNode = rightNode;
    }

    
    order = cache->porder;
    minsize = (order-1) / 2;

    if((order-1)%2)
	++minsize;

    if(size >= minsize)
    {
	if(leftok && rightok)
	    anchorNode = (lnkeys >= rnkeys) ? lAnchor : rAnchor;
	else if(leftok)
	    anchorNode = lAnchor;
	else
	    anchorNode = rAnchor;

	done = btreeShiftPriOne(cache,thisNode,balanceNode,anchorNode);
    }
	    
    else
    {
	tpage = btreePricacheRead(cache,thisNode);
	tbuf  = tpage->buf;
	GBT_PREV(tbuf,&parent);

	if(leftok && rightok)
	{
	    anchorNode = (parent == lAnchor) ? lAnchor : rAnchor;
	    mergeNode  = (anchorNode == lAnchor) ? leftNode : rightNode;
	}
	else if(leftok)
	{
	    anchorNode = lAnchor;
	    mergeNode  = leftNode;
	}
	else
	{
	    anchorNode = rAnchor;
	    mergeNode  = rightNode;
	}

	done = btreeMergePriOne(cache,thisNode,mergeNode,anchorNode);
    }

    return done;
}
ID btreeShiftPriOne
TY static
MO ajindex
LB core
XX
DE Shift spare entries from one keyword tree level 1 node to another.
DE Same as btreeShiftHybOne but duplicated for clarity
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD master node
PX
PN [3]
PA r balanceNode ajulong
PD balance node
PX
PN [4]
PA r anchorNode ajulong
PD anchor node
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeShiftPriOne(AjPBtcache cache, ajulong thisNode,
                               ajulong balanceNode, ajulong anchorNode)
{
    unsigned char *tbuf = NULL;
    unsigned char *abuf = NULL;
    unsigned char *bbuf = NULL;
    unsigned char *buf  = NULL;
    
    AjPBtMem arraysA = NULL;    
    AjPBtMem arraysB = NULL;    
    AjPBtMem arraysT = NULL;    

    AjPStr *kTarray = NULL;
    AjPStr *kAarray = NULL;
    AjPStr *kBarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pAarray = NULL;
    ajulong *pBarray = NULL;
    
    ajuint  nAkeys = 0;
    ajuint  nBkeys = 0;
    ajuint  nTkeys = 0;
    ajuint  i;
    ajint   ii;
    
    AjPBtpage pageA = NULL;
    AjPBtpage pageB = NULL;
    AjPBtpage pageT = NULL;
    AjPBtpage page  = NULL;
    
    AjPBtpage leftpage = NULL;

    ajuint anchorPos   = 0;
    ajulong prev        = 0L;
    ajuint  nodetype    = 0;

    ajulong lv = 0L;

    /* ajDebug("In btreeShiftPriOne\n"); */


    arraysA = btreeAllocPriArray(cache);
    kAarray = arraysA->karray;
    pAarray = arraysA->parray;

    arraysB = btreeAllocPriArray(cache);
    kBarray = arraysB->karray;
    pBarray = arraysB->parray;

    arraysT = btreeAllocPriArray(cache);
    kTarray = arraysT->karray;
    pTarray = arraysT->parray;


    pageA = btreePricacheRead(cache,anchorNode);
    pageA->dirty = BT_LOCK;
    pageA->lockfor = 1951;
    abuf = pageA->buf;
    pageB = btreePricacheRead(cache,balanceNode);
    pageB->dirty = BT_LOCK;
    pageB->lockfor = 1952;
    bbuf = pageB->buf;
    pageT = btreePricacheRead(cache,thisNode);
    pageT->dirty = BT_LOCK;
    pageT->lockfor = 1953;
    tbuf = pageT->buf;

    GBT_NKEYS(abuf,&nAkeys);
    GBT_NKEYS(bbuf,&nBkeys);
    GBT_NKEYS(tbuf,&nTkeys);

    btreeGetKeys(cache,abuf,&kAarray,&pAarray);
    btreeGetKeys(cache,bbuf,&kBarray,&pBarray);
    btreeGetKeys(cache,tbuf,&kTarray,&pTarray);
    
    if(MAJSTRCMPS(kTarray[nTkeys-1],kBarray[nBkeys-1])<0)
	leftpage = pageT;
    else
	leftpage = pageB;


    if(leftpage == pageT)
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && MAJSTRCMPS(kTarray[nTkeys-1],kAarray[i])>=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to thisNode */
	ajStrAssignS(&kTarray[nTkeys],kAarray[anchorPos]);
	++nTkeys;

	/* Shift extra */
	while(nTkeys < nBkeys)
	{
	    ajStrAssignS(&kTarray[nTkeys],kBarray[0]);
	    pTarray[nTkeys] = pBarray[0];
	    ++nTkeys;
	    --nBkeys;

	    for(i=0;i=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to thisNode */
	pTarray[nTkeys+1] = pTarray[nTkeys];

	for(ii=nTkeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kTarray[ii+1],kTarray[ii]);
	    pTarray[ii+1] = pTarray[ii];
	}

	ajStrAssignS(&kTarray[0],kAarray[anchorPos]);
	++nTkeys;

	/* Shift extra */
	while(nTkeys < nBkeys)
	{
	    pTarray[nTkeys+1] = pTarray[nTkeys];

	    for(ii=nTkeys-1;ii>-1;--ii)
	    {
		ajStrAssignS(&kTarray[ii+1],kTarray[ii]);
		pTarray[ii+1] = pTarray[ii];
	    }

	    ajStrAssignS(&kTarray[0],kBarray[nBkeys-1]);
	    pTarray[1] = pBarray[nBkeys];
	    ++nTkeys;
	    --nBkeys;
	}


	/* Adjust anchor key */
	ajStrAssignS(&kAarray[anchorPos],kTarray[0]);
	--nTkeys;

	for(i=0;ipagepos;

    for(i=0;ibuf;
	GBT_NODETYPE(buf,&nodetype);
	if(nodetype != BT_IDBUCKET)
	{
	    lv = prev;
	    SBT_PREV(buf,lv);
	    page->dirty = BT_DIRTY;
	}
    }

    btreeWriteNode(cache,pageA,kAarray,pAarray,nAkeys);
    btreeWriteNode(cache,pageB,kBarray,pBarray,nBkeys);
    btreeWriteNode(cache,pageT,kTarray,pTarray,nTkeys);

    if(!anchorNode)
    {
	pageA->dirty = BT_LOCK;
        pageA->lockfor = 1954;
    }

    btreeDeallocPriArray(cache,arraysA);
    btreeDeallocPriArray(cache,arraysB);
    btreeDeallocPriArray(cache,arraysT);

    return BTNO_NODE;
}
ID btreeMergePriOne
TY static
MO ajindex
LB core
XX
DE Merge two nodes.
DE
DE Deletion software
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r thisNode ajulong
PD master node
PX
PN [3]
PA r mergeNode ajulong
PD merge node
PX
PN [4]
PA r anchorNode ajulong
PD anchor node
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeMergePriOne(AjPBtcache cache, ajulong thisNode,
                                ajulong mergeNode, ajulong anchorNode)
{
    unsigned char *tbuf = NULL;
    unsigned char *abuf = NULL;
    unsigned char *nbuf = NULL;
    unsigned char *buf  = NULL;
    
    AjPStr *kTarray = NULL;
    AjPStr *kAarray = NULL;
    AjPStr *kNarray = NULL;
    ajulong *pTarray = NULL;
    ajulong *pAarray = NULL;
    ajulong *pNarray = NULL;

    ajulong thisprev  = 0L;
    ajulong mergeprev = 0L;
    
    
    ajuint  nAkeys = 0;
    ajuint  nNkeys = 0;
    ajuint  nTkeys = 0;
    ajuint  count  = 0;
    ajuint  i;
    ajint   ii;

    ajuint   nodetype = 0;
    
    ajuint saveA = 0;
    ajuint saveN = 0;
    ajuint saveT = 0;
    
    AjPBtpage pageA = NULL;
    AjPBtpage pageN = NULL;
    AjPBtpage pageT = NULL;
    AjPBtpage page  = NULL;
    
    AjPBtpage leftpage = NULL;

    ajuint anchorPos = 0;
    ajulong prev      = 0L;

    ajulong lv = 0L;

    AjPBtMem arraysA = NULL;    
    AjPBtMem arraysN = NULL;    
    AjPBtMem arraysT = NULL;    

    AjBool collapse = ajFalse;
    
    /* ajDebug("In btreeMergePriOne\n"); */

    pageA = btreePricacheRead(cache,anchorNode);
    saveA = pageA->dirty;
    pageA->dirty = BT_LOCK;
    pageA->lockfor = 1961;
    abuf = pageA->buf;
    pageN = btreePricacheRead(cache,mergeNode);
    saveN = pageN->dirty;
    pageN->dirty = BT_LOCK;
    pageN->lockfor = 1962;
    nbuf = pageN->buf;
    pageT = btreePricacheRead(cache,thisNode);
    saveT = pageT->dirty;
    pageT->dirty = BT_LOCK;
    pageT->lockfor = 1963;
    tbuf = pageT->buf;

    GBT_PREV(tbuf,&thisprev);
    GBT_PREV(nbuf,&mergeprev);

    GBT_NKEYS(abuf,&nAkeys);
    GBT_NKEYS(nbuf,&nNkeys);
    GBT_NKEYS(tbuf,&nTkeys);

    GBT_NODETYPE(nbuf,&nodetype);


    if(nAkeys == 1)
    {
	if(!anchorNode && !thisprev && !mergeprev)
	    collapse = ajTrue;
	else
	{
	    pageA->dirty = saveA;
	    pageN->dirty = saveN;
	    pageT->dirty = saveT;

	    return thisNode;
	}
    }

    arraysA = btreeAllocPriArray(cache);
    kAarray = arraysA->karray;
    pAarray = arraysA->parray;

    arraysN = btreeAllocPriArray(cache);
    kNarray = arraysN->karray;
    pNarray = arraysN->parray;

    arraysT = btreeAllocPriArray(cache);
    kTarray = arraysT->karray;
    pTarray = arraysT->parray;

    btreeGetKeys(cache,abuf,&kAarray,&pAarray);
    btreeGetKeys(cache,nbuf,&kNarray,&pNarray);
    btreeGetKeys(cache,tbuf,&kTarray,&pTarray);

    if(MAJSTRCMPS(kTarray[nTkeys-1],kNarray[nNkeys-1])<0)
	leftpage = pageT;
    else
	leftpage = pageN;


    if(leftpage == pageT)
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && MAJSTRCMPS(kTarray[nTkeys-1],kAarray[i])>=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to neighbour Node */
	pNarray[nNkeys+1] = pNarray[nNkeys];

	for(ii=nNkeys-1;ii>-1;--ii)
	{
	    ajStrAssignS(&kNarray[ii+1],kNarray[ii]);
	    pNarray[ii+1] = pNarray[ii];
	}

	ajStrAssignS(&kNarray[0],kAarray[anchorPos]);
	++nNkeys;


	/* Adjust anchor node keys/ptrs */
	++anchorPos;

	if(anchorPos==nAkeys)
	    pAarray[nAkeys-1] = pAarray[nAkeys];
	else
	{
	    for(i=anchorPos;i-1;--ii)
	    {
		ajStrAssignS(&kNarray[ii+1],kNarray[ii]);
		pNarray[ii+1] = pNarray[ii];
	    }

	    ajStrAssignS(&kNarray[0],kTarray[nTkeys-1]);
	    pNarray[1] = pTarray[nTkeys];
	    pNarray[0] = pTarray[nTkeys-1];
	    --nTkeys;
	    ++nNkeys;
	}

	/* At this point the 'this' node could be added to a free list */
    }
    else
    {
	/* Find anchor key position */
	i=0;

	while(i!=nAkeys && MAJSTRCMPS(kNarray[nNkeys-1],kAarray[i])>=0)
	    ++i;

	anchorPos = i;

	/* Move down anchor key to neighbourNode */
	ajStrAssignS(&kNarray[nNkeys],kAarray[anchorPos]);
	++nNkeys;

	/* Adjust anchor node keys/ptrs */
	++anchorPos;

	if(anchorPos!=nAkeys)
	    for(i=anchorPos;ipagepos;

    for(i=0;i<=nNkeys;++i)
    {
	page = btreePricacheRead(cache,pNarray[i]);
	buf = page->buf;
	GBT_NODETYPE(buf,&nodetype);

	if(nodetype != BT_IDBUCKET)
	{
	    lv = prev;
	    SBT_PREV(buf,lv);
	    page->dirty = BT_DIRTY;
	}
    }

    pageT->dirty = BT_CLEAN;
    btreeWriteNode(cache,pageA,kAarray,pAarray,nAkeys);
    btreeWriteNode(cache,pageN,kNarray,pNarray,nNkeys);

    if(!anchorNode)
    {
	pageA->dirty = BT_LOCK;
        pageA->lockfor = 1964;
    }

    btreeDeallocPriArray(cache,arraysA);
    btreeDeallocPriArray(cache,arraysN);
    btreeDeallocPriArray(cache,arraysT);
    
    if(collapse)
	btreeCollapseRootPriOne(cache,mergeNode);

    return thisNode;
}
ID btreeCollapseRootPriOne
TY static
MO ajindex
LB core
XX
DE Collapse root page for keyword level 1 tree.
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD page number to make new root
PX
RT ajulong
RD page number or BTNO_NODE
RX
//


static ajulong btreeCollapseRootPriOne(AjPBtcache cache, ajulong pagepos)
{
    unsigned char *buf  = NULL;
    unsigned char *lbuf = NULL;

    AjPStr *karray = NULL;
    ajulong *parray = NULL;

    AjPBtpage rootpage = NULL;
    AjPBtpage page     = NULL;
    
    ajuint nodetype = 0;
    ajuint nkeys    = 0;
    ajuint i;

    ajulong prev = 0L;
    AjPBtMem arrays = NULL;
    
    /* ajDebug("In btreeCollapseRootPriOne\n"); */
    
    if(!cache->plevel)
	return BTNO_NODE;

    rootpage = btreePricacheLocate(cache,0L);
    buf = rootpage->buf;
    page = btreePricacheRead(cache,pagepos);


    arrays = btreeAllocPriArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;

    /*
    ** Swap pagepos values to make root the child and child the root
    ** Update node types and mark the original root as a clean page
    */

    /* At this point page->pagepos could be added to a free list */

    rootpage->pagepos = page->pagepos;
    rootpage->dirty = BT_CLEAN;
    nodetype = BT_INTERNAL;
    SBT_NODETYPE(buf,nodetype);

    page->pagepos = 0;
    page->dirty = BT_LOCK;
    page->lockfor = 1971;
    buf = page->buf;
    nodetype = BT_ROOT;
    SBT_NODETYPE(buf,nodetype);
    
    --cache->plevel;

    if(cache->plevel)
    {
	/*
	 ** Update the PREV pointers of the new root's children
	 */
	GBT_NKEYS(buf,&nkeys);
	btreeGetKeys(cache,buf,&karray,&parray);

	for(i=0;i<=nkeys;++i)
	{
	    page = btreePricacheRead(cache,parray[i]);
	    lbuf = page->buf;
	    SBT_PREV(lbuf,prev);
	    page->dirty = BT_DIRTY;
	}
    }

    btreeDeallocPriArray(cache,arrays);
    
    return 0L;
}
ID btreeIsSecEmpty
TY static
MO ajindex
LB core
XX
DE Tests whether a secondary tree is empty
DE
DE Deletion software.
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
RT AjBool
RD true if empty, false otherwise
RX
//


static AjBool btreeIsSecEmpty(AjPBtcache cache)
{
    AjPBtpage rootpage  = NULL;
    AjPSecbucket bucket = NULL;
    unsigned char *buf  = NULL;
    AjPStr *karray  = NULL;
    ajulong *parray  = NULL;
    AjPBtMem arrays = NULL;
    ajuint nkeys = 0;
    ajuint tkeys = 0;

    if(cache->slevel)
        return ajFalse;

    rootpage = btreeSeccacheLocate(cache,cache->secrootblock);

    if(!rootpage)
        ajFatal("btreeSecIsEmpty: root page unlocked");

    buf = rootpage->buf;
    GBT_NKEYS(buf,&nkeys);

    if(!nkeys)
        return ajTrue;

    if(nkeys > 1)
        return ajFalse;

    arrays = btreeAllocSecArray(cache);
    karray = arrays->karray;
    parray = arrays->parray;
    btreeGetKeys(cache,buf,&karray,&parray);

    tkeys = 0;

    if(parray[0])
    {
        bucket = btreeReadSecbucket(cache,parray[0]);
        tkeys += bucket->Nentries;
    }

    if(parray[1])
    {
        bucket = btreeReadSecbucket(cache,parray[1]);
        tkeys += bucket->Nentries;
    }

    btreeDeallocSecArray(cache,arrays);

    if(tkeys)
        return ajFalse;

    return ajTrue;
}
ID btreeNodetype
TY static
MO ajindex
LB core
XX
DE Returns a name for the nodetype of a buffer
XX
PN [1]
PA r buf const unsigned char*
PD buffer
PX
RT const char*
RD Name of nodetype
RX
//


static const char* btreeNodetype(const unsigned char* buf)
{
    static AjBool called = AJFALSE;
    static char nodetypebuf[512];
    ajuint nodetype;

    if(!called)
    {
        AJCNEW0(btreeNodetypeNames, BT_MAXTYPE+1);
        btreeNodetypeNames[BT_SECFREEPAGE] = "secfree";
        btreeNodetypeNames[BT_FREEPAGE]    = "free";
        btreeNodetypeNames[BT_ROOT]        = "root";
        btreeNodetypeNames[BT_INTERNAL]    = "internal";
        btreeNodetypeNames[BT_LEAF]        = "leaf";
        btreeNodetypeNames[BT_IDBUCKET]    = "bucket";
        btreeNodetypeNames[BT_OVERFLOW]    = "overflow";
        btreeNodetypeNames[BT_PRIBUCKET]   = "pribucket";
        btreeNodetypeNames[BT_SECBUCKET]   = "secbucket";
        btreeNodetypeNames[BT_NUMBUCKET]   = "numbucket";
        btreeNodetypeNames[BT_SECROOT]     = "secroot";
        btreeNodetypeNames[BT_SECINTERNAL] = "secinternal";
        btreeNodetypeNames[BT_SECLEAF]     = "secleaf";
        called = ajTrue;
    }

    GBT_NODETYPE(buf, &nodetype);

    if(nodetype >= BT_MAXTYPE)
    {
        sprintf(nodetypebuf, "unknown (%u)", nodetype);
        return nodetypebuf;
    }

    if(!btreeNodetypeNames[nodetype])
    {
        sprintf(nodetypebuf, "unknown (%u)", nodetype);
        return nodetypebuf;
    }

    if(nodetype == BT_OVERFLOW || nodetype == BT_SECOVERFLOW)
    {
        ajWarn("Overflow page found");
    }
    

    return btreeNodetypeNames[nodetype];
}
ID ajBtreePageIsPrimary
TY public
MO ajindex
LB core
XX
DE Returns a true is a page is primary
XX
PN [1]
PA r page const AjPBtpage
PD Page
PX
RT AjBool
RD True if page is primary index or bucket
RX
//


AjBool ajBtreePageIsPrimary(const AjPBtpage page)
{
    unsigned char* buf = NULL;
    ajuint nodetype;

    buf = page->buf;

    GBT_NODETYPE(buf, &nodetype);

    switch (nodetype) 
    {
        case BT_FREEPAGE:       /* no need to save the page */
        case BT_ROOT:
        case BT_INTERNAL:
        case BT_LEAF:
        case BT_IDBUCKET:
        case BT_PRIBUCKET:
        case BT_OVERFLOW:
            return ajTrue;
            break;
        case BT_SECFREEPAGE:
        case BT_SECROOT:
        case BT_SECINTERNAL:
        case BT_SECLEAF:
        case BT_SECOVERFLOW:
        case BT_SECBUCKET:
        case BT_NUMBUCKET:
            return ajFalse;
            break;
        default:
            return ajTrue;
    }

    return ajTrue;
}
ID ajBtreeCacheGetPagecount
TY public
MO ajindex
LB core
XX
DE Returns the total number of pages in a tree index
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
RT ajulong
RD number of pages in a tree index
RX
//


ajulong ajBtreeCacheGetPagecount(const AjPBtcache cache)
{
    return cache->pripagecount + cache->secpagecount;
}
ID ajBtreeCacheGetPripagesize
TY public
MO ajindex
LB core
XX
DE Returns the primary page size of a tree index
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
RT ajuint
RD Page size of a tree index
RX
//


ajuint ajBtreeCacheGetPripagesize(const AjPBtcache cache)
{
    return cache->pripagesize;
}
ID ajBtreeCacheGetSecpagesize
TY public
MO ajindex
LB core
XX
DE Returns the secondary page size of a tree index
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
RT ajuint
RD Page size of a tree index
RX
//


ajuint ajBtreeCacheGetSecpagesize(const AjPBtcache cache)
{
    return cache->secpagesize;
}
ID ajBtreeCacheGetTotsize
TY public
MO ajindex
LB core
XX
DE Returns the number of bytes in an uncompressed tree index
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
RT ajulong
RD number of bytes in a tree index
RX
//


ajulong ajBtreeCacheGetTotsize(const AjPBtcache cache)
{
    return (cache->pripagesize * cache->pripagecount) +
        (cache->secpagesize * cache->secpagecount);
}
ID ajBtreePageGetTypename
TY public
MO ajindex
LB core
XX
DE Returns the node type name of a tree index page
XX
PN [1]
PA r page const AjPBtpage
PD Page
PX
RT const char*
RD Name of page type
RX
//


const char* ajBtreePageGetTypename(const AjPBtpage page)
{
    return btreeNodetype(page->buf);
}
ID ajBtreeStatNode
TY public
MO ajindex
LB core
XX
DE Returns number of keys, overflow pages visited and free space unused
DE for a b+ tree node page
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r page const AjPBtpage
PD Page
PX
PN [3]
PA r full AjBool
PD If true, write detailed debug report
PX
PN [4]
PA w nkeys ajuint*
PD Number of keys
PX
PN [5]
PA w overflows ajuint*
PD Number of overflow pages
PX
PN [6]
PA w freespace ajuint*
PD Unused space in this page
PX
PN [7]
PA u refs ajulong*
PD References to pages
PX
PN [8]
PA r newpostable const AjPTable
PD Page numbers for page offsets
PX
RT AjBool
RD True on success
RX
//


AjBool ajBtreeStatNode(AjPBtcache cache, const AjPBtpage page, AjBool full,
                       ajuint* nkeys, ajuint* overflows,
                       ajuint* freespace, ajulong* refs,
                       const AjPTable newpostable)
{
    ajuint i;
    ajuint m;

    ajuint klen;
    const AjPBtpage bpage;
    unsigned char *tbuf;

    ajuint    pagesize = 0;
    ajulong   overflow = 0L;
    ajulong blockno;
    ajuint totlen;
    ajulong left;
    ajulong right;
    ajulong prev;
    unsigned char *lenptr = NULL;
    unsigned char *keyptr = NULL;
    ajulong lv;
    unsigned char* kp;

/*    ajuint freebytes;*/
/*    ajuint nodetype;*/

    if(ajBtreePageIsPrimary(page))
        pagesize = cache->pripagesize;
    else
        pagesize = cache->secpagesize;

    bpage = page;
    tbuf = bpage->buf;
    GBT_TOTLEN(tbuf,&totlen);

    if(!totlen)
      return ajBtreeStatNumnode(cache, page, full,
                                nkeys, overflows, freespace, refs,
                                newpostable);
    
    btreeCheckNode(cache, page);

    GBT_NKEYS(tbuf,&m);
    *nkeys = m;
    *overflows = 0;
    *freespace = 0;

    GBT_OVERFLOW(tbuf,&overflow);

    GBT_LEFT(tbuf,&left);
    GBT_RIGHT(tbuf,&right);
    GBT_PREV(tbuf,&prev);
    GBT_BLOCKNUMBER(tbuf,&blockno);

    if(full)
        ajDebug("\n"
                "StatNode      %12Lu totlen:%u '%s'\n"
                "         left:%12Lu  right:%12Lu  prev:%12Lu\n"
                "        block:%12Lu   over:%12Lu\n",
                page->pagepos, totlen, btreeNodetype(tbuf),
                left, right, prev, blockno, overflow);
    if(full)
        ajDebug("\nStatNode      %12Lu      pointer (%u)\n",
                page->pagepos, m);
    if(full)
        ajDebug("--------      ------------      -------\n");

    lenptr =  PBT_KEYLEN(tbuf);
    keyptr = lenptr + m * sizeof(ajuint);

    for(i=0;i pagesize)
//	{
//            freebytes = pagesize - (keyptr-tbuf);
//            *freespace += freebytes;
//            (*overflows)++;
//    	    /# ajDebug("btreeStatNode: Overflow\n"); #/
//	    bpage = ajBtreeCacheRead(cache,overflow);
//	    tbuf = bpage->buf;
//	    GBT_NODETYPE(tbuf,&nodetype);
//
//	    if(nodetype != BT_OVERFLOW)
//		ajFatal("StatNode Overflow node %Lu expected but not found "
//                        "cache %S page %Lu "
//                        "pagepos:%Lu key:%u/%u free:%u keyptr:%x lenptr:%x "
//                        "tbuf:%x klen:%u old nodetype '%s' newnodetype '%s' "
//                        "blockno:%Lu totlen:%u left:%Lu right:%Lu prev:%Lu",
//                        overflow, cache->filename,
//                        page->pagepos, page->pagepos/pagesize,
//                        i, m, freebytes,
//                        keyptr, lenptr, tbuf, klen,
//                        btreeNodetype(page->buf), btreeNodetype(tbuf),
//                        blockno,totlen, left, right, prev);
//
//            GBT_BLOCKNUMBER(tbuf,&blockno);
//            GBT_TOTLEN(tbuf,&totlen);
//            GBT_LEFT(tbuf,&left);
//            GBT_RIGHT(tbuf,&right);
//            GBT_PREV(tbuf,&prev);
//
//	    /#
//	     ** The length pointer is restricted to the initial page.
//	     ** The keyptr in overflow pages starts at the Key Lengths
//	     ** position!
//	     #/
//            keyptr = PBT_KEYLEN(tbuf);
//	}
*/

        kp = keyptr;
        keyptr += klen+1;
        BT_GETAJULONG(keyptr,&lv);
        ++refs[*ajTableulongFetch(newpostable, &lv)];
	keyptr += sizeof(ajulong);

        if(full)
            ajDebug("#StatNode     %12Lu %12Lu '%*s'\n",
                    page->pagepos, lv, klen, kp);

	lenptr += sizeof(ajuint);
    }

    BT_GETAJULONG(keyptr,&lv);
    keyptr += sizeof(ajulong);
    ++refs[*ajTableulongFetch(newpostable, &lv)];

    if(full)
        ajDebug("#StatNode     %12Lu %12Lu ''\n",
                page->pagepos, lv);

    *freespace += pagesize - (keyptr-tbuf);

    if(full)
        ajDebug("StatNode      %12Lu keys:%6u over:%6u free:%6u\n",
                page->pagepos, *nkeys, *overflows, *freespace);

    return ajTrue;
}
ID ajBtreeStatNumnode
TY public
MO ajindex
LB core
XX
DE Returns number of keys, overflow pages visited and free space unused
DE for a b+ tree numeric node page
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r page const AjPBtpage
PD Page
PX
PN [3]
PA r full AjBool
PD If true, write detailed debug report
PX
PN [4]
PA w nkeys ajuint*
PD Number of keys
PX
PN [5]
PA w overflows ajuint*
PD Number of overflow pages
PX
PN [6]
PA w freespace ajuint*
PD Unused space in this page
PX
PN [7]
PA u refs ajulong*
PD References to pages
PX
PN [8]
PA r newpostable const AjPTable
PD Page numbers for page offsets
PX
RT AjBool
RD True on success
RX
//


AjBool ajBtreeStatNumnode(AjPBtcache cache, const AjPBtpage page, AjBool full,
                          ajuint* nkeys, ajuint* overflows,
                          ajuint* freespace, ajulong *refs,
                          const AjPTable newpostable)
{
    ajuint i;
    ajuint m;
    const AjPBtpage bpage;
    unsigned char *tbuf;

    ajuint   pagesize = 0;
    ajulong   overflow = 0L;
    ajulong blockno;
    ajuint totlen;
    ajulong left;
    ajulong right;
    ajulong prev;
    unsigned char *keyptr = NULL;
    unsigned char *valptr = NULL;
    ajulong lv;
    ajulong lk;

    btreeCheckNumnode(cache, page);

    if(ajBtreePageIsPrimary(page))
        pagesize = cache->pripagesize;
    else
        pagesize = cache->secpagesize;

    bpage = page;
    tbuf = bpage->buf;
    GBT_TOTLEN(tbuf,&totlen);
    
    GBT_NKEYS(tbuf,&m);
    *nkeys = m;
    *overflows = 0;
    *freespace = 0;

    GBT_LEFT(tbuf,&left);
    GBT_RIGHT(tbuf,&right);
    GBT_BLOCKNUMBER(tbuf,&blockno);
    GBT_PREV(tbuf,&prev);
    GBT_OVERFLOW(tbuf,&overflow);

    if(full)
        btreeStatNumnode(cache, page);

    keyptr =  PBT_KEYLEN(tbuf);
    valptr = keyptr + m * sizeof(ajulong);
    for(i=0;ibuf;
    GBT_TOTLEN(tbuf,&totlen);
    
    GBT_NKEYS(tbuf,&m);

    GBT_LEFT(tbuf,&left);
    GBT_RIGHT(tbuf,&right);
    GBT_BLOCKNUMBER(tbuf,&blockno);
    GBT_PREV(tbuf,&prev);
    GBT_OVERFLOW(tbuf,&overflow);

    ajDebug("\n"
                "StatNumNode   %12Lu totlen:%u\n"
                "         left:%12Lu  right:%12Lu  prev:%12Lu\n"
                "        block:%12Lu   over:%12Lu\n",
                page->pagepos, totlen, left, right, prev, blockno, overflow);
    ajDebug("\nStatNumnode   %12Lu   pageoffset (%u)\n",
                page->pagepos, m);
    ajDebug("-----------   ------------ ------------ -------\n");


    keyptr =  PBT_KEYLEN(tbuf);
    valptr = keyptr + m * sizeof(ajulong);

    for(i=0;ipagepos, lv, lk);
	keyptr += sizeof(ajulong);
	valptr += sizeof(ajulong);
    }

    BT_GETAJULONG(valptr,&lv);
    ajDebug("#StatNumnode  %12Lu %12Lu ''\n", page->pagepos, lv);
    ajDebug("StatNumnode   %12Lu keys:%6u\n",
                page->pagepos, m);
    return;
}
ID ajBtreeStatIdbucket
TY public
MO ajindex
LB core
XX
DE Returns number of keys, overflow pages visited and free space unused
DE for a b+ tree bucket page
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r page const AjPBtpage
PD Page
PX
PN [3]
PA r full AjBool
PD If true, write detailed debug report
PX
PN [4]
PA w nentries ajuint*
PD Number of entries
PX
PN [5]
PA w ndups ajuint*
PD Number of entries with duplicates
PX
PN [6]
PA w nextra ajuint*
PD Number of extra entries as duplicates
PX
PN [7]
PA w overflows ajuint*
PD Number of overflow pages
PX
PN [8]
PA w freespace ajuint*
PD Unused space in this page
PX
PN [9]
PA u refs ajulong*
PD References to pages
PX
PN [10]
PA r newpostable const AjPTable
PD Page numbers for page offsets
PX
RT AjBool
RD True on success
RX
//


AjBool ajBtreeStatIdbucket(AjPBtcache cache, const AjPBtpage page, AjBool full,
                           ajuint* nentries, ajuint *ndups, ajuint *nextra,
                           ajuint* overflows, ajuint* freespace, ajulong* refs,
                           const AjPTable newpostable)
{
    ajuint i;
    ajuint iref;
    ajuint m;
    ajuint klen;
    const AjPBtpage bpage;
    unsigned char *tbuf;

    ajuint   pagesize = 0;
    ajulong   overflow = 0L;
    unsigned char *idptr = NULL;
    unsigned char *keyptr = NULL;
    unsigned char *kp = NULL;

    ajuint uv;
    ajuint uv2;
    ajulong lv;
    ajulong lv2;
    ajuint idlen;
    ajuint keyskip = BT_DDOFF + cache->refcount*BT_EXTRA;

/*    ajuint freebytes;*/
/*    ajuint nodetype;*/

    if(ajBtreePageIsPrimary(page))
        pagesize = cache->pripagesize;
    else
        pagesize = cache->secpagesize;

    bpage = page;
    tbuf = bpage->buf;

    GBT_BUCKNENTRIES(tbuf,&m);
    *nentries = m;
    *overflows = 0;
    *freespace = 0;
    *ndups = 0;
    *nextra = 0;

    GBT_BUCKOVERFLOW(tbuf,&overflow);

    keyptr = PBT_BUCKKEYLEN(tbuf);
    idptr = keyptr + (m * sizeof(ajuint));

    if(full)
        ajDebug("\n"
                "StatBucket     %12Lu over:%12Lu\n",
                page->pagepos, overflow);
    if(full)
        ajDebug("\nStatBucket     %12Lu   dbno   dups       offset",
                page->pagepos);

    if(full && cache->refcount)
    {
        for(iref=0; iref < cache->refcount; iref++)
            ajDebug("    refoffset%d", iref+1);
    }

    if(full)
        ajDebug(" (%u)\n", m);

    if(full)
        ajDebug("----------                    ----   ----");

    if(full && cache->refcount)
    {
        for(iref=0; iref < cache->refcount; iref++)
            ajDebug("       ------");
    }

    if(full)
        ajDebug("    ---------\n");

    for(i=0;i cache->pagesize)	/# overflow #/
//	{
//            freebytes = pagesize - (idptr-tbuf);
//            *freespace += freebytes;
//            (*overflows)++;
//    	    /# ajDebug("btreeStatNode: Overflow\n"); #/
//	    GBT_BUCKOVERFLOW(tbuf,&overflow);
//	    bpage = ajBtreeCacheRead(cache,overflow);
//	    tbuf = bpage->buf;
//	    GBT_BUCKNODETYPE(tbuf,&nodetype);
//	    if(nodetype != BT_OVERFLOW)
//		ajFatal("StatBucket Overflow node %Lu expected but not found "
//                        "for cache '%S'",
//                        overflow, cache->filename);
//	    /# overflow bucket ids start at the keylen position #/
//	    idptr = PBT_BUCKKEYLEN(tbuf);
//	}
*/

        kp = idptr;
	idptr += idlen;
        BT_GETAJUINT(idptr, &uv);
        idptr += sizeof(ajuint);
	BT_GETAJUINT(idptr,&uv2);
        if(uv2)
        {
            (*ndups)++;
            *nextra += uv2;
        }
	idptr += sizeof(ajuint);	
	BT_GETAJULONG(idptr,&lv);
        if(uv2)
            ++refs[*ajTableulongFetch(newpostable, &lv)];
	idptr += sizeof(ajulong);

        if(full)
            ajDebug("#StatBucket    %12Lu %6u %6u %12Lu ",
                    page->pagepos, uv, uv2, lv);

        if(cache->refcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
            {
                BT_GETAJULONG(idptr,&lv2);
                idptr += sizeof(ajulong);
                if(full)
                    ajDebug("%12Lu ", lv2);
            }
        }

        if(full)
            ajDebug("'%*s'\n", idlen-1, kp);

	keyptr += sizeof(ajuint);
    }
    *freespace += pagesize - (idptr-tbuf);
    *nextra -= *ndups;
    if(full)
        ajDebug("StatBucket     %12Lu keys:%6u over:%6u free:%6u\n",
                page->pagepos, *nentries, *overflows, *freespace);

    return ajTrue;
}
ID ajBtreeStatNumbucket
TY public
MO ajindex
LB core
XX
DE Returns number of keys, overflow pages visited and free space unused
DE for a b+ tree secondary bucket page
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r page const AjPBtpage
PD Page
PX
PN [3]
PA r full AjBool
PD If true, write detailed debug report
PX
PN [4]
PA w nentries ajuint*
PD Number of entries
PX
PN [5]
PA w overflows ajuint*
PD Number of overflow pages
PX
PN [6]
PA w freespace ajuint*
PD Unused space in this page
PX
RT AjBool
RD True on success
RX
//


AjBool ajBtreeStatNumbucket(AjPBtcache cache, const AjPBtpage page, AjBool full,
                            ajuint* nentries, ajuint* overflows,
                            ajuint* freespace)
{
    ajuint iref;
    ajuint i;
    ajuint m;
    const AjPBtpage bpage;
    unsigned char *tbuf;

    ajulong lv;
    ajulong lv2;
    ajuint v;
    ajuint   pagesize = 0;
    ajulong   overflow = 0L;
    unsigned char *keyptr = NULL;

    pagesize = cache->secpagesize;

    bpage = page;
    tbuf = bpage->buf;
    GBT_BUCKNENTRIES(tbuf,&m);
    *nentries = m;
    *overflows = 0;
    *freespace = 0;

    GBT_BUCKOVERFLOW(tbuf,&overflow);

    keyptr = PBT_BUCKKEYLEN(tbuf);

    if(full)
        ajDebug("\n"
                "StatNumbucket  %12Lu over:%12Lu\n",
                page->pagepos, overflow);
    if(full)
        ajDebug("\nStatNumbucket  %12Lu       offset    ",
                page->pagepos);

    if(full && cache->refcount)
    {
        for(iref=0; iref < cache->refcount; iref++)
            ajDebug("refoffset%d", iref+1);
    }

    if (full)
        ajDebug("dbno (%u)\n",
                page->pagepos, m);

    if(full)
        ajDebug("-------------  ------------      -------");
    
    if(full && cache->refcount)
    {
        for(iref=0; iref < cache->refcount; iref++)
            ajDebug("   ----------");
    }
    
    if(full)
        ajDebug("         ----\n");

    for(i=0;irefcount)
        {
            for(iref=0; iref < cache->refcount; iref++)
            {
                BT_GETAJULONG(keyptr,&lv2);
                keyptr += sizeof(ajulong);
                if(full)
                    ajDebug(" %12Lu", lv2);
            }

        }

        BT_GETAJUINT(keyptr,&v);
	keyptr += sizeof(ajuint);
        if(full)
            ajDebug(" %12u\n", v);
    }
    *freespace += pagesize - (keyptr-tbuf);
    if(full)
        ajDebug("StatNumbucket  %12Lu keys:%6u over:%6u free:%6u\n",
                page->pagepos, *nentries, *overflows, *freespace);
    
    return ajTrue;
}
ID ajBtreeStatPribucket
TY public
MO ajindex
LB core
XX
DE Returns number of keys, overflow pages visited and free space unused
DE for a b+ tree primary bucket page
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r page const AjPBtpage
PD Page
PX
PN [3]
PA r full AjBool
PD If true, write detailed debug report
PX
PN [4]
PA w nentries ajuint*
PD Number of entries
PX
PN [5]
PA w overflows ajuint*
PD Number of overflow pages
PX
PN [6]
PA w freespace ajuint*
PD Unused space in this page
PX
PN [7]
PA u refs ajulong*
PD References to pages
PX
PN [8]
PA r newpostable const AjPTable
PD Page numbers for page offsets
PX
RT AjBool
RD True on success
RX
//


AjBool ajBtreeStatPribucket(AjPBtcache cache, const AjPBtpage page, AjBool full,
                            ajuint* nentries, ajuint* overflows,
                            ajuint* freespace, ajulong *refs,
                            const AjPTable newpostable)
{
    ajuint i;
    ajuint m;
    ajuint klen;
    const AjPBtpage bpage;
    unsigned char *tbuf;

    ajuint   pagesize = 0;
    ajulong   overflow = 0L;
    unsigned char *idptr = NULL;
    unsigned char *keyptr = NULL;

    unsigned char* lp;
    ajulong lv;

/*    ajuint nodetype;*/
/*    ajuint freebytes;*/

    pagesize = cache->pripagesize;

    bpage = page;
    tbuf = bpage->buf;
    GBT_BUCKNENTRIES(tbuf,&m);
    *nentries = m;
    *overflows = 0;
    *freespace = 0;

    GBT_BUCKOVERFLOW(tbuf,&overflow);

    keyptr = PBT_BUCKKEYLEN(tbuf);
    idptr = keyptr + (m * sizeof(ajuint));

    if(full)
        ajDebug("\n"
                "StatPribucket  %12Lu over:%12Lu\n",
                page->pagepos, overflow);
    if(full)
        ajDebug("\nStatPribucket  %12Lu (%u)\n",
                page->pagepos, m);
    if(full)
        ajDebug("-------------  ------------      -------\n");
    for(i=0;i pagesize)	/# overflow #/
//	{
//            freebytes = pagesize - (idptr-tbuf);
//            *freespace += freebytes;
//            (*overflows)++;
//    	    /# ajDebug("btreeStatNode: Overflow\n"); #/
//	    GBT_BUCKOVERFLOW(tbuf,&overflow);
//	    bpage = btreePricacheRead(cache,overflow);
//	    tbuf = bpage->buf;
//	    GBT_BUCKNODETYPE(tbuf,&nodetype);
//	    if(nodetype != BT_OVERFLOW)
//		ajFatal("StatPribucket Overflow node %Lu expected but not found",
//                        overflow, cache->filename);
//	    /# overflow bucket ids start at the keylen position #/
//	    idptr = PBT_BUCKKEYLEN(tbuf);
//	}
*/

        lp = idptr+ klen-8;
        BT_GETAJULONG(lp,&lv);
        ++refs[*ajTableulongFetch(newpostable, &lv)];
        if(full)
            ajDebug("#StatPribucket %12Lu %12Lu '%*s'\n",
                    page->pagepos, lv, klen-9, idptr);
	idptr += klen;    /* string */
	keyptr += sizeof(ajuint);
    }
    *freespace += pagesize - (idptr-tbuf);
    if(full)
        ajDebug("StatPribucket  %12Lu keys:%6u over:%6u free:%6u\n",
                page->pagepos, *nentries, *overflows, *freespace);    
    return ajTrue;
}
ID ajBtreeStatSecbucket
TY public
MO ajindex
LB core
XX
DE Returns number of keys, overflow pages visited and free space unused
DE for a b+ tree secondary bucket page
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r page const AjPBtpage
PD Page
PX
PN [3]
PA r full AjBool
PD If true, write detailed debug report
PX
PN [4]
PA w nentries ajuint*
PD Number of entries
PX
PN [5]
PA w overflows ajuint*
PD Number of overflow pages
PX
PN [6]
PA w freespace ajuint*
PD Unused space in this page
PX
RT AjBool
RD True on success
RX
//


AjBool ajBtreeStatSecbucket(AjPBtcache cache, const AjPBtpage page, AjBool full,
                            ajuint* nentries, ajuint* overflows,
                            ajuint* freespace)
{
    ajuint i;
    ajuint m;
    ajuint klen;
    const AjPBtpage bpage;
    unsigned char *tbuf;

    ajuint   pagesize = 0;
    ajulong   overflow = 0L;
    unsigned char *idptr = NULL;
    unsigned char *keyptr = NULL;

/*    ajuint nodetype;*/
/*    ajuint freebytes;*/

    pagesize = cache->secpagesize;

    bpage = page;
    tbuf = bpage->buf;
    GBT_BUCKNENTRIES(tbuf,&m);
    *nentries = m;
    *overflows = 0;
    *freespace = 0;

    GBT_BUCKOVERFLOW(tbuf,&overflow);

    keyptr = PBT_BUCKKEYLEN(tbuf);
    idptr = keyptr + (m * sizeof(ajuint));

    if(full)
        ajDebug("\n"
                "StatSecbucket  %12Lu over:%12Lu\n",
                page->pagepos, overflow);
    ajDebug("\nStatSecbucket  %12Lu (%u)\n",
            page->pagepos, m);
    if(full)
        ajDebug("-------------  ------------      -------\n");
    for(i=0;i pagesize)	/# overflow #/
//	{
//            freebytes = pagesize - (idptr-tbuf);
//            *freespace += freebytes;
//            (*overflows)++;
//    	    /# ajDebug("btreeStatNode: Overflow\n"); #/
//	    GBT_BUCKOVERFLOW(tbuf,&overflow);
//	    bpage = btreeSeccacheRead(cache,overflow);
//	    tbuf = bpage->buf;
//            GBT_BUCKNODETYPE(tbuf,&nodetype);
//	    if(nodetype != BT_OVERFLOW)
//		ajFatal("StatSecbucket Overflow node %Lu expected "
//                        "but not found",
//                        overflow, cache->filename);
//	    /# overflow bucket ids start at the keylen position #/
//	    idptr = PBT_BUCKKEYLEN(tbuf);
//	}
*/

        if(full)
            ajDebug("#StatSecbucket %12Lu '%*s'\n",
                    page->pagepos, klen-1, idptr);
	idptr += klen;    /* string */

	keyptr += sizeof(ajuint);
    }
    *freespace += pagesize - (idptr-tbuf);
    if(full)
        ajDebug("StatSecbucket  %12Lu keys:%6u over:%6u free:%6u\n",
                page->pagepos, *nentries, *overflows, *freespace);
    
    return ajTrue;
}
ID btreeBucketSplitCalc
TY static
MO ajindex
LB core
XX
DE Calculate new bucket sizes to split a node.
DE Make sure all existing buckets are reused.
XX
PN [1]
PA r totalkeys ajuint
PD Total number of keys in all buckets
PX
PN [2]
PA r totalbuckets ajuint
PD Total number of current buckets
PX
PN [3]
PA r maxbucketsize ajuint
PD Maximum number of keys in a buckets
PX
PN [4]
PA w leftbuckets ajuint*
PD Number of left node buckets
PX
PN [5]
PA w leftmax ajuint*
PD Maximum number of keys per left node bucket
PX
PN [6]
PA w leftkeys ajuint*
PD Maximum number of keys under left node
PX
PN [7]
PA w rightbuckets ajuint*
PD Number of right node buckets
PX
PN [8]
PA w rightmax ajuint*
PD Maximum number of keys per right node bucket
PX
PN [9]
PA w rightkeys ajuint*
PD Maximum number of keys under right node
PX
RT AjBool
RD True if calculated value reused all buckets
RD False if increased to use all buckets
RX
//


static AjBool btreeBucketSplitCalc(ajuint totalkeys,
                                   ajuint totalbuckets,
                                   ajuint maxbucketsize,
                                   ajuint *leftbuckets, ajuint *leftmax,
                                   ajuint *leftkeys,
                                   ajuint *rightbuckets, ajuint *rightmax,
                                   ajuint *rightkeys)
{
    ajuint lbuckets;
    ajuint rbuckets;
    ajuint lmax;
    ajuint rmax;
    ajuint lkeys;
    ajuint rkeys;

    ajuint lmax2;
    ajuint rmax2;
    ajuint lbuckets1;
    ajuint rbuckets1;
    ajuint lbuckets2;
    ajuint rbuckets2;

    ajuint bucketn;
    ajuint lrest;
    ajuint rrest;

    AjBool ret = ajTrue;

/* we always split the keys to left and right */

    lkeys = totalkeys/2;
    rkeys = totalkeys - lkeys;

/* values to reuse all the current buckets */

    lbuckets = totalbuckets/2;
    rbuckets = totalbuckets - lbuckets;

    lmax = lkeys/lbuckets;
    if(!(lkeys % lbuckets))
        lmax--;
    if(!lmax) lmax = 1;

    bucketn = lkeys/lmax;
    if(lkeys % lmax)
        bucketn++;
    lbuckets1 = bucketn - 1;
    
    rmax = rkeys/rbuckets;
    if(!(rkeys % rbuckets))
        rmax--;
    if(!rmax) rmax = 1;
    
    bucketn = rkeys/rmax;
    if(rkeys % rmax)
        bucketn++;
    rbuckets1 = bucketn - 1;

/* minimum needed to fit what we need with half-full buckets */

    lmax2 = maxbucketsize/2;
    lmax2++;

    bucketn = lkeys/lmax2;
    if(lkeys % lmax2)
        bucketn++;
    lbuckets2 = bucketn - 1;

    rmax2 = maxbucketsize/2;
    rmax2++;

    bucketn = rkeys/rmax2;
    if(rkeys % rmax2)
        bucketn++;
    rbuckets2 = bucketn - 1;

/* we always use the same keys split */

    *leftkeys = lkeys;
    *rightkeys = rkeys;

    if((rbuckets2 + lbuckets2) < totalbuckets)
    {
        /* use all the current buckets */

        *leftmax = lmax;
        *leftbuckets = lbuckets1;
        *rightmax = rmax;
        *rightbuckets = rbuckets1;
        ret = ajFalse;
    }
    else
    {
        /* split to same or more half-full buckets */

        *leftmax = lmax2;
        *leftbuckets = lbuckets2;
        *rightmax = rmax2;
        *rightbuckets = rbuckets2;
    }

    lrest = (*leftkeys) - (*leftbuckets)*(*leftmax);
    rrest = (*rightkeys) - (*rightbuckets)*(*rightmax);

#if AJINDEX_DEBUG
    ajDebug("btreeBucketSplitCalc totalkeys:%u  totalbuckets:%u "
            "maxbucketsize:%u => %u*%u + %u (%u) "
            "%u*%u + %u (%u)\n",
            totalkeys, totalbuckets, maxbucketsize,
            *leftbuckets, *leftmax, lrest, *leftkeys,
            *rightbuckets, *rightmax, rrest, *rightkeys);
#endif

    if(lrest < 1 || lrest > (*leftmax))
        ajFatal("Bad btreeBucketSplitCalc lrest:%u", lrest);
    if(rrest < 1 || rrest > (*rightmax))
        ajFatal("Bad btreeBucketSplitCalc rrest:%u", rrest);

    return ret;
}
ID btreeBucketCalc
TY static
MO ajindex
LB core
XX
DE Calculate new bucket sizes to split a node.
DE Make sure all existing buckets are reused.
XX
PN [1]
PA r totalkeys ajuint
PD Total number of keys in all buckets
PX
PN [2]
PA r totalbuckets ajuint
PD Total number of current buckets
PX
PN [3]
PA r maxbucketsize ajuint
PD Maximum number of keys in a buckets
PX
PN [4]
PA w newbuckets ajuint*
PD Number of new buckets
PX
PN [5]
PA w newmax ajuint*
PD Maximum number of keys per new bucket
PX
RT AjBool
RD True if calculated value reused all buckets
RD False if increased to use all buckets
RX
//


static AjBool btreeBucketCalc(ajuint totalkeys, ajuint totalbuckets,
                              ajuint maxbucketsize,
                              ajuint *newbuckets, ajuint *newmax)
{
    ajuint bmax;
    ajuint bmax2;

    ajuint buckets1;
    ajuint buckets2;

    ajuint bucketn;
    ajuint rest;

    AjBool ret = ajTrue;

    /* from the original keys and buckets */

    if(!totalbuckets)
        ajFatal("btreeBucketCalc zero buckets keys:%u", totalkeys);
    bmax = totalkeys/totalbuckets;
    if(!(totalkeys % totalbuckets))
        bmax--;
    if(!bmax) bmax = 1;

    bucketn = totalkeys/bmax;
    if(totalkeys % bmax)
        bucketn++;
    buckets1 = bucketn - 1;

    /* minimum space required */

    bmax2 = maxbucketsize/2;
    if(!bmax2)
        ++bmax2;

    bucketn = totalkeys/bmax2;
    if(totalkeys % bmax2)
        bucketn++;
    buckets2 = bucketn - 1;


    if(buckets2 < totalbuckets)
    {
        *newmax = bmax;
        *newbuckets = buckets1;
        ret = ajFalse;
    }
    else
    {
        *newmax = bmax2;
        *newbuckets = buckets2;
    }
    rest = (totalkeys) - (*newbuckets)*(*newmax);

#if AJINDEX_DEBUG
    ajDebug("btreeBucketCalc totalkeys:%u totalbuckets:%u "
            "maxbucketsize:%u => %u*%u + %u ret:%B\n",
            totalkeys, totalbuckets, maxbucketsize,
            *newbuckets, *newmax, rest, ret);
#endif

    if(rest < 1 || rest > (*newmax))
        ajFatal("Bad ajBtreeBucketCalc rest:%u", rest);

    return ret;
}
ID btreePageposCompress
TY static
MO ajindex
LB core
XX
DE Return the new start position of a compressed page position
XX
PN [1]
PA r oldpos ajulong
PD Page position
PX
PN [2]
PA r newpostable const AjPTable
PD Table of new page positions
PX
PN [3]
PA r where const char*
PD Location in index for reporting in
PD warning message.
PX
RT ajulong
RD New page start position
RX
//


static ajulong btreePageposCompress(ajulong oldpos,
                                    const AjPTable newpostable,
                                    const char* where)
{
    const ajulong *newpos;

    ajDebug("btreePageposCompress oldpos %Lu table size:%Lu\n",
            oldpos, ajTableGetLength(newpostable));

    newpos =  ajTableulongFetch(newpostable, &oldpos);

    if(!newpos)
        ajWarn("oldpos %Lu not found (%s)",
               oldpos, where);
    else
        ajDebug("btreePageposCompress oldpos %Lu newpos %Lu\n",
                oldpos, *newpos);

    return *newpos;
}
ID btreePageposUncompress
TY static
MO ajindex
LB core
XX
DE Return the new start position of a compressed page
XX
PN [1]
PA r oldpos ajulong
PD Page position
PX
PN [2]
PA r newpostable const AjPTable
PD Array of new page positions
PX
PN [3]
PA r where const char*
PD Location in index for reporting in
PD warning message.
PX
RT ajulong
RD new start position
RX
//


static ajulong btreePageposUncompress(ajulong oldpos,
                                      const AjPTable newpostable,
                                      const char* where)
{
    const ajulong *newpos;

    ajDebug("btreePageposUncompress oldpos %Lu table size:%Lu\n",
            oldpos, ajTableGetLength(newpostable));

    newpos =  ajTableulongFetch(newpostable, &oldpos);

    if(!newpos)
        ajWarn("oldpos %Lu not found (%s)",
               oldpos, where);
    else
        ajDebug("btreePageposUncompress oldpos %Lu newpos %Lu\n",
                oldpos, *newpos);

    return *newpos;
}
ID btreePageUncompress
TY static
MO ajindex
LB core
XX
DE Uncompress a cache page using a table of new positions for pages
XX
PN [1]
PA u page AjPBtpage
PD Page
PX
PN [2]
PA r newpostable const AjPTable
PD Table of new page positions
PX
PN [3]
PA r refcount ajuint
PD Reference offset count
PX
RT AjBool
RD True on success
RX
//


static AjBool btreePageUncompress(AjPBtpage page,
                                  const AjPTable newpostable,
                                  ajuint refcount)
{
    const unsigned char* buf = page->buf;
    ajuint nodetype;

    GBT_NODETYPE(buf, &nodetype);

    ajDebug("btreePageUncompress %Lu (%s)\n",
            page->pagepos, btreeNodetype(buf));

    switch (nodetype) 
    {
        case BT_SECFREEPAGE:    /* no need to save the page */
        case BT_FREEPAGE:       /* no need to save the page */
            return ajFalse;
        case BT_ROOT:
        case BT_INTERNAL:
        case BT_LEAF:
            btreePageUncompressNode(page, newpostable);
            return ajTrue;
        case BT_SECROOT:
        case BT_SECINTERNAL:
        case BT_SECLEAF:
            btreePageUncompressNode(page, newpostable);
            return ajTrue;
        case BT_IDBUCKET:
            btreePageUncompressIdbucket(page, newpostable,
                                        refcount);
            return ajTrue;
        case BT_OVERFLOW:
        case BT_SECOVERFLOW:
            ajErr("Overflow page type %u in btreePageUncompress", nodetype);
            return ajTrue;
        case BT_PRIBUCKET:
            btreePageUncompressPribucket(page, newpostable);
            return ajTrue;
        case BT_SECBUCKET:
            /* no index page offsets to adjust */
            return ajTrue;
        case BT_NUMBUCKET:
            /* no index page offsets to adjust */
            return ajTrue;
        default:
            ajErr("Unknown page type %u in btreePageUncompress", nodetype);
            break;
    }
    return ajTrue;
}
ID btreePageUncompressIdbucket
TY static
MO ajindex
LB core
XX
DE Uncompress a bucket cache page using an array of new positions for
DE uncompressed pages
XX
PN [1]
PA u page AjPBtpage
PD Page
PX
PN [2]
PA r newpostable const AjPTable
PD Table of new page positions
PX
PN [3]
PA r refcount ajuint
PD Number of reference offsets
PX
RT void
RD 
RX
//


static void btreePageUncompressIdbucket(AjPBtpage page,
                                        const AjPTable newpostable,
                                        ajuint refcount)
{
    unsigned char* buf = page->buf;
    ajulong   overflow = 0L;
    unsigned char *idptr = NULL;
    unsigned char *keyptr = NULL;
    ajulong lv;
    ajuint klen;
    ajuint idlen;
    ajuint nentries;
    ajuint dups;
    ajuint i;
    ajuint keyskip = BT_DDOFF + refcount*BT_EXTRA;

    buf = page->buf;

    GBT_BUCKNENTRIES(buf,&nentries);
    GBT_BUCKOVERFLOW(buf,&overflow);
/*
//    if(overflow)
//    {
//        overflow = btreePageposUncompress(overflow, newpostable,
//                                          "Bucket overflow");
//        SBT_BUCKOVERFLOW(buf, overflow);
//    }
*/
    keyptr = PBT_BUCKKEYLEN(buf);
    idptr = keyptr + (nentries* sizeof(ajuint));

    /*
    ** ajulong values in bucket are offset and refoffset in files for dups=1
    ** but for dups > 1 offset is the secondary root page for the duplicates
    */

    for(i=0;ibuf;
    ajuint nkeys = 0;
    ajulong overflow = 0L;
    unsigned char *lenptr = NULL;
    unsigned char *keyptr = NULL;

    ajuint klen;
    ajuint totlen;
    ajuint i;
    ajulong blockno;
    ajulong left;
    ajulong right;
    ajulong prev;
    ajulong lv;

    buf = page->buf;

    GBT_TOTLEN(buf,&totlen);
    if(!totlen)
    {
        btreePageUncompressNumnode(page, newpostable);
        return;
    }

    GBT_NKEYS(buf,&nkeys);
    GBT_OVERFLOW(buf,&overflow);

/*
//    if(overflow)
//    {
//        overflow = btreePageposUncompress(overflow, newpostable,
//                                          "Node overflow");
//        SBT_OVERFLOW(buf, overflow);
//    }
*/    

    GBT_LEFT(buf,&left);

    if(left)
    {
        left = btreePageposUncompress(left, newpostable,
                                      "Node left");
        SBT_LEFT(buf, left);
    }
    
    GBT_RIGHT(buf,&right);

    if(right > BT_NODEPREAMBLE)      /* not a level, must be a page position */
    {
        right = btreePageposUncompress(right, newpostable,
                                       "Node right");
        SBT_RIGHT(buf, right);
    }
    
    GBT_PREV(buf,&prev);

    if(prev)
    {
        prev = btreePageposUncompress(prev, newpostable,
                                      "Node prev");
        SBT_PREV(buf, prev);
    }
    
    GBT_BLOCKNUMBER(buf,&blockno);
    if(blockno)
    {
        blockno = btreePageposUncompress(blockno, newpostable,
                                         "Node blockno");
        SBT_BLOCKNUMBER(buf, blockno);
    }
    
    lenptr = PBT_KEYLEN(buf);
    keyptr = lenptr + nkeys * sizeof(ajuint);

    for(i=0;ibuf;
    ajuint nkeys;
    ajulong   overflow = 0L;
    unsigned char *keyptr = NULL;
    unsigned char *valptr = NULL;
    ajuint i;
    ajulong blockno;
    ajulong left;
    ajulong right;
    ajulong prev;
    ajulong lv;

    buf = page->buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_OVERFLOW(buf,&overflow);

/*
//    if(overflow)
//    {
//        overflow = btreePageposUncompress(overflow, newpostable,
//                                          "Numnode overflow");
//        SBT_OVERFLOW(buf, overflow);
//    }
*/

    GBT_LEFT(buf,&left);

    if(left)
    {
        left = btreePageposUncompress(left, newpostable,
                                      "Numnode left");
        SBT_LEFT(buf, left);
    }
    
    GBT_RIGHT(buf,&right);

    if(right > BT_NODEPREAMBLE)      /* not a level, must be a page position */
    {
        right = btreePageposUncompress(right, newpostable,
                                       "Numnode right");
        SBT_RIGHT(buf, right);
    }
    
    GBT_PREV(buf,&prev);

    if(prev)
    {
        prev = btreePageposUncompress(prev, newpostable,
                                      "Numnode prev");
        SBT_PREV(buf, prev);
    }
    
    GBT_BLOCKNUMBER(buf,&blockno);
    if(blockno)
    {
        prev = btreePageposUncompress(blockno, newpostable,
                                      "Numnode blockno");
        SBT_BLOCKNUMBER(buf, blockno);
    }
    
    keyptr =  PBT_KEYLEN(buf);
    valptr = keyptr + nkeys * sizeof(ajulong);

    for(i = 0; i < nkeys; i++)
    {
        BT_GETAJULONG(valptr,&lv); /* pagepos for key */

        if(lv)
        {
            lv = btreePageposUncompress(lv, newpostable,
                                        "Numnode keypos");
            BT_SETAJULONG(valptr, lv);
        }

        valptr += sizeof(ajulong);
    }

    BT_GETAJULONG(valptr,&lv); /* pagepos after last key */

    if(lv)
    {
        lv = btreePageposUncompress(lv, newpostable,
                                    "Numnode last keypos");
        BT_SETAJULONG(valptr, lv);
    }

    return;
}
ID btreePageUncompressPribucket
TY static
MO ajindex
LB core
XX
DE Uncompress a primary bucket cache page using a table of new positions
XX
PN [1]
PA u page AjPBtpage
PD Page
PX
PN [2]
PA r newpostable const AjPTable
PD Table of new page positions
PX
RT void
RD 
RX
//


static void btreePageUncompressPribucket(AjPBtpage page,
                                         const AjPTable newpostable)
{
    unsigned char* buf = page->buf;
    ajuint nentries;
    ajulong overflow = 0L;
    unsigned char *idptr = NULL;
    unsigned char *keyptr = NULL;
    ajuint klen;
    ajuint i;
    ajulong lv;
    unsigned char * lp = NULL;

    buf = page->buf;

    GBT_BUCKNENTRIES(buf,&nentries);
    GBT_BUCKOVERFLOW(buf,&overflow);

/*
//    if(overflow)
//    {
//        overflow = btreePageposUncompress(overflow, newpostable,
//                                          "Pribucket overflow");
//        SBT_BUCKOVERFLOW(buf, overflow);
//    }
*/

    keyptr = PBT_BUCKKEYLEN(buf);
    idptr = keyptr + (nentries * sizeof(ajuint));

    for(i=0;ibuf;
    ajuint nodetype;

    GBT_NODETYPE(buf, &nodetype);

    ajDebug("btreePageCompress %Lu (%s)\n",
            page->pagepos, btreeNodetype(buf));

    switch (nodetype) 
    {
        case BT_SECFREEPAGE:    /* no need to save the page */
        case BT_FREEPAGE:       /* no need to save the page */
            return ajFalse;
        case BT_ROOT:
        case BT_INTERNAL:
        case BT_LEAF:
            btreePageCompressNode(page, newpostable);
            return ajTrue;
        case BT_SECROOT:
        case BT_SECINTERNAL:
        case BT_SECLEAF:
            btreePageCompressNode(page, newpostable);
            return ajTrue;
        case BT_IDBUCKET:
            btreePageCompressIdbucket(page, newpostable, refcount);
            return ajTrue;
        case BT_OVERFLOW:
        case BT_SECOVERFLOW:
            ajErr("Overflow page type %u in btreePageCompress", nodetype);
            return ajTrue;
        case BT_PRIBUCKET:
            btreePageCompressPribucket(page, newpostable);
            return ajTrue;
        case BT_SECBUCKET:
            /* no index page offsets to adjust */
            return ajTrue;
        case BT_NUMBUCKET:
            /* no index page offsets to adjust */
            return ajTrue;
        default:
            ajErr("Unknown page type %u in btreePageCompress", nodetype);
            break;
    }
    return ajTrue;
}
ID btreePageCompressIdbucket
TY static
MO ajindex
LB core
XX
DE Compress a bucket cache page using an array of new positions for
DE uncompressed pages
XX
PN [1]
PA u page AjPBtpage
PD Page
PX
PN [2]
PA r newpostable const AjPTable
PD Table of new page positions
PX
PN [3]
PA r refcount ajuint
PD Number of reference offsets
PX
RT void
RD 
RX
//


static void btreePageCompressIdbucket(AjPBtpage page,
                                      const AjPTable newpostable,
                                      ajuint refcount)
{
    unsigned char* buf = page->buf;
    ajulong   overflow = 0L;
    unsigned char *idptr = NULL;
    unsigned char *keyptr = NULL;
    ajulong lv;
    ajuint klen;
    ajuint idlen;
    ajuint nentries;
    ajuint dups;
    ajuint i;
    ajuint keyskip = BT_DDOFF + refcount*BT_EXTRA;

    buf = page->buf;

    GBT_BUCKNENTRIES(buf,&nentries);
    GBT_BUCKOVERFLOW(buf,&overflow);

/*
//    if(overflow)
//    {
//        overflow = btreePageposCompress(overflow, newpostable,
//                                        "Bucket overflow");
//        SBT_BUCKOVERFLOW(buf, overflow);
//    }
*/

    keyptr = PBT_BUCKKEYLEN(buf);
    idptr = keyptr + (nentries* sizeof(ajuint));

    /*
    ** ajulong values in bucket are offset and refoffset in files for dups=1
    ** but for dups > 1 offset is the secondary root page for the duplicates
    */

    for(i=0;ibuf;
    ajuint nkeys = 0;
    ajulong overflow = 0L;
    unsigned char *lenptr = NULL;
    unsigned char *keyptr = NULL;

    ajuint klen;
    ajuint totlen;
    ajuint i;
    ajulong blockno;
    ajulong left;
    ajulong right;
    ajulong prev;
    ajulong lv;

    buf = page->buf;

    GBT_TOTLEN(buf,&totlen);

    if(!totlen)
    {
        btreePageCompressNumnode(page, newpostable);
        return;
    }

    GBT_NKEYS(buf,&nkeys);
    GBT_OVERFLOW(buf,&overflow);

/*
//    if(overflow)
//    {
//        overflow = btreePageposCompress(overflow, newpostable,
//                                        "Node overflow");
//        SBT_OVERFLOW(buf, overflow);
//    }
*/    

    GBT_LEFT(buf,&left);

    if(left)
    {
        left = btreePageposCompress(left, newpostable,
                                    "Node left");
        SBT_LEFT(buf, left);
    }
    
    GBT_RIGHT(buf,&right);

    if(right > BT_NODEPREAMBLE)      /* not a level, must be a page position */
    {
        right = btreePageposCompress(right, newpostable,
                                     "Node right");
        SBT_RIGHT(buf, right);
    }
    
    GBT_PREV(buf,&prev);

    if(prev)
    {
        prev = btreePageposCompress(prev, newpostable,
                                    "Node prev");
        SBT_PREV(buf, prev);
    }
    
    GBT_BLOCKNUMBER(buf,&blockno);
    if(blockno)
    {
        blockno = btreePageposCompress(blockno, newpostable,
                                       "Node blockno");
        SBT_BLOCKNUMBER(buf, blockno);
    }
    
    lenptr = PBT_KEYLEN(buf);
    keyptr = lenptr + nkeys * sizeof(ajuint);

    for(i=0;ibuf;
    ajuint nkeys;
    ajulong   overflow = 0L;
    unsigned char *keyptr = NULL;
    unsigned char *valptr = NULL;
    ajuint i;
    ajulong blockno;
    ajulong left;
    ajulong right;
    ajulong prev;
    ajulong lv;

    buf = page->buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_OVERFLOW(buf,&overflow);

/*
//    if(overflow)
//    {
//        overflow = btreePageposCompress(overflow, newpostable,
//                                        "Numnode overflow");
//        SBT_OVERFLOW(buf, overflow);
//    }
*/

    GBT_LEFT(buf,&left);

    if(left)
    {
        left = btreePageposCompress(left, newpostable,
                                    "Numnode left");
        SBT_LEFT(buf, left);
    }
    
    GBT_RIGHT(buf,&right);

    if(right > BT_NODEPREAMBLE)      /* not a level, must be a page position */
    {
        right = btreePageposCompress(right, newpostable,
                                     "Numnode right");
        SBT_RIGHT(buf, right);
    }
    
    GBT_PREV(buf,&prev);

    if(prev)
    {
        prev = btreePageposCompress(prev, newpostable,
                                    "Numnode prev");
        SBT_PREV(buf, prev);
    }
    
    GBT_BLOCKNUMBER(buf,&blockno);
    if(blockno)
    {
        blockno = btreePageposCompress(blockno, newpostable,
                                       "Numnode blockno");
        SBT_BLOCKNUMBER(buf, blockno);
    }
    
    keyptr =  PBT_KEYLEN(buf);
    valptr = keyptr + nkeys * sizeof(ajulong);

    for(i = 0; i < nkeys; i++)
    {
        BT_GETAJULONG(valptr,&lv); /* pagepos for key */

        if(lv)
        {
            lv = btreePageposCompress(lv, newpostable,
                                      "Numnode keypos");
            BT_SETAJULONG(valptr, lv);
        }

        valptr += sizeof(ajulong);
    }

    BT_GETAJULONG(valptr,&lv); /* pagepos after last key */

    if(lv)
    {
        lv = btreePageposCompress(lv, newpostable,
                                  "Numnode last keypos");
        BT_SETAJULONG(valptr, lv);
    }

    return;
}
ID btreePageCompressPribucket
TY static
MO ajindex
LB core
XX
DE Compress a primary bucket cache page using an array of new
DE positions for uncompressed pages
XX
PN [1]
PA u page AjPBtpage
PD Page
PX
PN [2]
PA r newpostable const AjPTable
PD Table of new page positions
PX
RT void
RD 
RX
//


static void btreePageCompressPribucket(AjPBtpage page,
                                       const AjPTable newpostable)
{
    unsigned char* buf = page->buf;
    ajuint nentries;
    ajulong overflow = 0L;
    unsigned char *idptr = NULL;
    unsigned char *keyptr = NULL;
    ajuint klen;
    ajuint i;
    ajulong lv;
    unsigned char * lp = NULL;

    buf = page->buf;

    GBT_BUCKNENTRIES(buf,&nentries);
    GBT_BUCKOVERFLOW(buf,&overflow);

/*
//    if(overflow)
//    {
//        overflow = btreePageposCompress(overflow, newpostable,
//                                        "Pribucket overflow");
//        SBT_BUCKOVERFLOW(buf, overflow);
//    }
*/

    keyptr = PBT_BUCKKEYLEN(buf);
    idptr = keyptr + (nentries * sizeof(ajuint));

    for(i=0;ibuf;
    ajuint nodetype;

    GBT_NODETYPE(buf, &nodetype);
    switch (nodetype) 
    {
        case BT_SECFREEPAGE:    /* no need to save the page */
        case BT_FREEPAGE:       /* no need to save the page */
            return 0;
        case BT_ROOT:
        case BT_INTERNAL:
        case BT_LEAF:
        case BT_SECROOT:
        case BT_SECINTERNAL:
        case BT_SECLEAF:
            return btreePageGetSizeNode(page);
        case BT_IDBUCKET:
            return btreePageGetSizeIdbucket(page, refcount);
        case BT_OVERFLOW:
        case BT_SECOVERFLOW:
            ajErr("Overflow page type %u in btreePageGetSize at %Lu",
                  nodetype, page->pagepos);
            return 0;
        case BT_PRIBUCKET:
            return btreePageGetSizePribucket(page);
        case BT_SECBUCKET:
            return btreePageGetSizeSecbucket(page);
        case BT_NUMBUCKET:
            return btreePageGetSizeNumbucket(page, refcount);
        default:
            ajErr("Unknown page type %u in btreePageGetSize at %Lu",
                  nodetype, page->pagepos);
            break;
    }
    return 0;
}
ID btreePageGetSizeIdbucket
TY static
MO ajindex
LB core
XX
DE Return the used size of a bucket cache page
XX
PN [1]
PA r page const AjPBtpage
PD Page
PX
PN [2]
PA r refcount ajuint
PD Number of reference offsets
PX
RT ajuint
RD Used page size
RX
//


static ajuint btreePageGetSizeIdbucket(const AjPBtpage page, ajuint refcount)
{
    const unsigned char* buf = page->buf;
    ajuint nentries;
    ajulong   overflow = 0L;
    const unsigned char *idptr = NULL;
    const unsigned char *keyptr = NULL;
    ajuint klen;
    ajuint idlen;
    ajuint i;
    ajuint keyskip = BT_DDOFF + refcount*BT_EXTRA;

    buf = page->buf;

    GBT_BUCKNENTRIES(buf,&nentries);
    GBT_BUCKOVERFLOW(buf,&overflow);

    keyptr = PBT_BUCKKEYLEN(buf);
    idptr = keyptr + (nentries * sizeof(ajuint));

    for(i=0;ibuf;
    ajuint nkeys = 0;
    ajulong overflow = 0L;
    const unsigned char *lenptr = NULL;
    const unsigned char *keyptr = NULL;

    ajuint klen;
    ajuint totlen;
    ajuint i;

    buf = page->buf;

    GBT_TOTLEN(buf,&totlen);
    if(!totlen)
      return btreePageGetSizeNumnode(page);

    GBT_NKEYS(buf,&nkeys);
    GBT_OVERFLOW(buf,&overflow);

    lenptr = PBT_KEYLEN(buf);
    keyptr = lenptr + nkeys * sizeof(ajuint);

    for(i=0;ibuf;
    ajuint nentries;
    ajulong   overflow = 0L;
    const unsigned char *keyptr = NULL;

    buf = page->buf;

    GBT_BUCKNENTRIES(buf,&nentries);
    GBT_BUCKOVERFLOW(buf,&overflow);

    keyptr = PBT_BUCKKEYLEN(buf);

    keyptr += nentries * (sizeof(ajlong) + refcount*sizeof(ajlong) +
                          sizeof(ajuint));

    return (keyptr-buf);
}
ID btreePageGetSizeNumnode
TY static
MO ajindex
LB core
XX
DE Return the used size of a numeric node cache page
XX
PN [1]
PA r page const AjPBtpage
PD Page
PX
RT ajuint
RD Used page size
RX
//


static ajuint btreePageGetSizeNumnode(const AjPBtpage page)
{
    const unsigned char* buf = page->buf;
    ajuint nkeys;
    ajulong   overflow = 0L;
    const unsigned char *keyptr = NULL;
    const unsigned char *valptr = NULL;

    buf = page->buf;

    GBT_NKEYS(buf,&nkeys);
    GBT_OVERFLOW(buf,&overflow);

    keyptr =  PBT_KEYLEN(buf);
    valptr = keyptr + nkeys * sizeof(ajulong) +
        nkeys * sizeof(ajulong) + sizeof(ajulong);

    return (valptr-buf);
}
ID btreePageGetSizePribucket
TY static
MO ajindex
LB core
XX
DE Return the used size of a primary bucket cache page
XX
PN [1]
PA r page const AjPBtpage
PD Page
PX
RT ajuint
RD Used page size
RX
//


static ajuint btreePageGetSizePribucket(const AjPBtpage page)
{
    const unsigned char* buf = page->buf;
    ajuint nentries;
    ajulong overflow = 0L;
    const unsigned char *idptr = NULL;
    const unsigned char *keyptr = NULL;
    ajuint klen;
    ajuint i;

    buf = page->buf;

    GBT_BUCKNENTRIES(buf,&nentries);
    GBT_BUCKOVERFLOW(buf,&overflow);

    keyptr = PBT_BUCKKEYLEN(buf);
    idptr = keyptr + (nentries * sizeof(ajuint));

    for(i=0;ibuf;
    ajuint nentries;
    ajulong   overflow = 0L;
    const unsigned char *idptr = NULL;
    const unsigned char *keyptr = NULL;
    ajuint klen;
    ajuint i;

    buf = page->buf;

    GBT_BUCKNENTRIES(buf,&nentries);
    GBT_BUCKOVERFLOW(buf,&overflow);

    keyptr = PBT_BUCKKEYLEN(buf);
    idptr = keyptr + (nentries * sizeof(ajuint));

    for(i=0;ipagesize;*/

    bpage = page;
    tbuf = bpage->buf;
    GBT_TOTLEN(tbuf,&totlen);
    
    GBT_NKEYS(tbuf,&m);

    GBT_LEFT(tbuf,&left);
    GBT_RIGHT(tbuf,&right);
    GBT_BLOCKNUMBER(tbuf,&blockno);
    GBT_PREV(tbuf,&prev);
    GBT_OVERFLOW(tbuf,&overflow);


    lenptr =  PBT_KEYLEN(tbuf);
    keyptr = lenptr + m * sizeof(ajuint);

    AJNEW0(ncpage);

    for(i=0;i pagesize)
//	{
//            freebytes = pagesize - (keyptr-tbuf);
//    	    /# ajDebug("btreeStatNode: Overflow\n"); #/
//	    btreeNocacheFetch(cache, ncpage, overflow);
//	    tbuf = bpage->buf;
//	    GBT_NODETYPE(tbuf,&nodetype);
//	    if(nodetype != BT_OVERFLOW)
//		ajFatal("btreeCheckNode "
//                        "Overflow node %Lu expected but not found "
//                        "cache %S page %Lu "
//                        "pagepos:%Lu key:%u/%u free:%u keyptr:%x lenptr:%x "
//                        "tbuf:%x klen:%u old nodetype '%s' newnodetype '%s' "
//                        "blockno:%Lu totlen:%u left:%Lu right:%Lu prev:%Lu",
//                        overflow, cache->filename,
//                        page->pagepos, page->pagepos/pagesize,
//                        i, m, freebytes,
//                        keyptr, lenptr, tbuf, klen,
//                        btreeNodetype(page->buf), btreeNodetype(tbuf),
//                        blockno,totlen, left, right, prev);
//
//            GBT_BLOCKNUMBER(tbuf,&blockno);
//            GBT_TOTLEN(tbuf,&totlen);
//            GBT_LEFT(tbuf,&left);
//            GBT_RIGHT(tbuf,&right);
//            GBT_PREV(tbuf,&prev);
//
//	    /#
//	     ** The length pointer is restricted to the initial page.
//	     ** The keyptr in overflow pages starts at the Key Lengths
//	     ** position!
//	     #/
//            keyptr = PBT_KEYLEN(tbuf);
//	}
*/

        keyptr += klen+1;
        BT_GETAJULONG(keyptr,&lv);
	keyptr += sizeof(ajulong);
        lenptr += sizeof(ajuint);
    }

    BT_GETAJULONG(keyptr,&lv);
    AJFREE(ncpage);

    return ret;
}
ID btreeCheckNumnode
TY static
MO ajindex
LB core
XX
DE Checks consistency of a numeric node page
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r page const AjPBtpage
PD Page
PX
RT AjBool
RD True on success
RX
//


static AjBool btreeCheckNumnode(AjPBtcache cache, const AjPBtpage page)
{
    ajuint i;
    ajuint m;
    const AjPBtpage bpage;
    unsigned char *tbuf;

    ajulong   overflow = 0L;
    ajulong blockno;
    ajuint totlen;
    ajulong left;
    ajulong right;
    ajulong prev;
    unsigned char *keyptr = NULL;
    unsigned char *valptr = NULL;
    ajulong lv;
    ajulong lk;
    ajulong lastk = 0L;

    AjBool ok = ajTrue;
    AjBool ret = ajTrue;

    if(!btreeDoExtra)
        return ret;

    bpage = page;
    tbuf = bpage->buf;
    GBT_TOTLEN(tbuf,&totlen);
    
    GBT_NKEYS(tbuf,&m);

    GBT_LEFT(tbuf,&left);
    GBT_RIGHT(tbuf,&right);
    GBT_BLOCKNUMBER(tbuf,&blockno);
    GBT_PREV(tbuf,&prev);
    GBT_OVERFLOW(tbuf,&overflow);

    if(!btreeCheckNodeHeader(cache, page, "numnode"))
        ret = ajFalse;

    keyptr =  PBT_KEYLEN(tbuf);
    valptr = keyptr + m*sizeof(ajulong);
    for(i=0;ipagepos, i, lk, lastk,
                   cache->filename);
        }
        lastk = lk;
        
	keyptr += sizeof(ajulong);
	valptr += sizeof(ajulong);
    }

    BT_GETAJULONG(valptr,&lv);

    if(!ok)
    {
        ret = ajFalse;
        btreeStatNumnode(cache, page);
    }
    
    return ret;
}
ID btreeCheckNodeHeader
TY static
MO ajindex
LB core
XX
DE Checks consistency of any node header
XX
PN [1]
PA w cache AjPBtcache
PD cache
PX
PN [2]
PA r page const AjPBtpage
PD Page
PX
PN [3]
PA r type const char*
PD Node type
PX
RT AjBool
RD True on success
RX
//


static AjBool btreeCheckNodeHeader(AjPBtcache cache, const AjPBtpage page,
                                   const char* type)
{
    ajuint nkeys;
    unsigned char *tbuf;

    ajulong   overflow = 0L;
    ajulong blockno;
    ajuint totlen;
    ajulong left;
    ajulong right;
    ajulong prev;
    const char* nodetype;

    AjBool ret = ajTrue;
    
    tbuf = page->buf;
    nodetype = btreeNodetype(tbuf);

    GBT_TOTLEN(tbuf,&totlen);
    
    GBT_NKEYS(tbuf,&nkeys);

    GBT_LEFT(tbuf,&left);
    GBT_RIGHT(tbuf,&right);
    GBT_BLOCKNUMBER(tbuf,&blockno);
    GBT_PREV(tbuf,&prev);
    GBT_OVERFLOW(tbuf,&overflow);

    if(!strcmp(type, "node"))
    {
        if(!strcmp(nodetype, "numbucket"))
        {
            ret = ajFalse;
            ajWarn("%s:%Lu expected non-numeric node in %S",
                   nodetype,
                   cache->filename);
        }
    }
    else if(!strcmp(type, "numnode"))
    {
        if(strcmp(nodetype, "numbucket"))
        {
            ret = ajFalse;
            ajWarn("%s:%Lu expected numeric node in %S",
                   nodetype,
                   cache->filename);
        }
    }
    else
    {
        ret = ajFalse;
        ajWarn("%s:%Lu expected '%s' in %S",
               nodetype, type,
               cache->filename);
    }

    if(ajBtreePageIsPrimary(page))
    {
        if(nkeys >= cache->porder)
        {
            ret = ajFalse;
            ajWarn("%s:%Lu nkeys: %u porder:%u in %S",
                   nodetype, page->pagepos, nkeys,
                   cache->porder,
                   cache->filename);
        }
    }
    else
    {
        if(nkeys > cache->sorder)
        {
            ret = ajFalse;
            ajWarn("%s:%Lu nkeys: %u sorder:%u in %S",
                   nodetype, page->pagepos, nkeys,
                   cache->sorder,
                   cache->filename);
        }
    }
    if((blockno != page->pagepos))
    {
        ret = ajFalse;
        ajWarn("%s:%Lu blockno: %Lu in %S",
               nodetype, page->pagepos, blockno,
               cache->filename);
    }

    return ret;
}
ID btreeNocacheFetch
TY static
MO ajindex
LB core
XX
DE Fetch a cache page from disc
XX
PN [1]
PA r cache const AjPBtcache
PD cache
PX
PN [2]
PA w cpage AjPBtpage
PD cache page
PX
PN [3]
PA r pagepos ajulong
PD page number
PX
RT void
RD 
RX
//


static void btreeNocacheFetch(const AjPBtcache cache, AjPBtpage cpage,
                              ajulong pagepos)
{
    ajuint sum = 0;
    ajuint retries = 0;
    ajuint pagesize;

    /* ajDebug("In btreeNoCacheFetch\n"); */
    
    if(cache->secpagesize > cache->pripagesize)
        pagesize = cache->pripagesize;
    else
        pagesize = cache->secpagesize;

    if(fseek(cache->fp,pagepos,SEEK_SET))
	ajFatal("Seek error '%s' in btreeNocacheFetch file %S",
                strerror(ferror(cache->fp)), cache->filename);
    
    while(sum != pagesize && retries != BT_MAXRETRIES)
    {
	sum += fread((void *)(cpage->buf+sum),1,pagesize-sum,
		     cache->fp);
	++retries;
    }
    
    if(retries == BT_MAXRETRIES)
	ajFatal("Maximum retries (%u) reached in btreeNocacheFetch "
                "for page %Lu",
		BT_MAXRETRIES,pagepos);

    if(ajBtreePageIsPrimary(cpage))
    {
        retries = 0;
        while(sum < cache->pripagesize && retries != BT_MAXRETRIES)
        {
            sum += fread((void *)(cpage->buf+sum),1,cache->pripagesize-sum,
                         cache->fp);
            ++retries;
        }
        if(retries == BT_MAXRETRIES)
            ajFatal("Maximum retries (%u) reached in btreeNocacheFetch "
                    "for page %Lu",
                    BT_MAXRETRIES,pagepos);
    }
    else
    {
        retries = 0;
        while(sum < cache->secpagesize && retries != BT_MAXRETRIES)
        {
            sum += fread((void *)(cpage->buf+sum),1,cache->secpagesize-sum,
                         cache->fp);
            ++retries;
        }
        if(retries == BT_MAXRETRIES)
            ajFatal("Maximum retries (%u) reached in btreeNocacheFetch "
                    "for page %Lu",
                    BT_MAXRETRIES,pagepos);
    }

    cpage->pagepos = pagepos;
    
    return;
}
ID btreePripageSetfree
TY static
MO ajindex
LB core
XX
DE Clear a primary cache page and set as free
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD Pahge number
PX
RT void
RD 
RX
//


static void btreePripageSetfree(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage page = NULL;
    unsigned char *p;
    ajuint nodetype;

    page = btreePricacheRead(cache,pagepos);

    if(!ajBtreePageIsPrimary(page))
        ajWarn("btreePripageSetfree secondary page %Lu '%s'",
               pagepos, btreeNodetype(page->buf));

    p = page->buf;
    AJCSET0(p, cache->pripagesize); /* clear buffer to zeros */

    page->next = NULL;
    page->prev = NULL;

    nodetype = BT_FREEPAGE;
    SBT_NODETYPE(p,nodetype);

    page->dirty = BT_DIRTY;
    
    return;
}
ID btreeSecpageSetfree
TY static
MO ajindex
LB core
XX
DE Clear a secondary cache page and set as free
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA r pagepos ajulong
PD Pahge number
PX
RT void
RD 
RX
//


static void btreeSecpageSetfree(AjPBtcache cache, ajulong pagepos)
{
    AjPBtpage page = NULL;
    unsigned char *p;
    ajuint nodetype;

    page = btreeSeccacheRead(cache,pagepos);

    if(ajBtreePageIsPrimary(page))
        ajWarn("btreeSecpageSetfree primary page %Lu '%s'",
               pagepos, btreeNodetype(page->buf));

    p = page->buf;
    AJCSET0(p, cache->secpagesize); /* clear buffer to zeros */

    page->next = NULL;
    page->prev = NULL;

    nodetype = BT_SECFREEPAGE;
    SBT_NODETYPE(p,nodetype);

    page->dirty = BT_DIRTY;
    
    return;
}
ID btreePripageClear
TY static
MO ajindex
LB core
XX
DE Clear a primary cache page
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD Page
PX
RT void
RD 
RX
//


static void btreePripageClear(AjPBtcache cache, AjPBtpage page)
{
    unsigned char *p;
    ajuint nodetype;

    p = page->buf;

    if(!ajBtreePageIsPrimary(page))
        ajWarn("btreePripageClear secondary page %Lu '%s'",
               page->pagepos, btreeNodetype(page->buf));

    AJCSET0(p, cache->pripagesize); /* clear buffer to zeros */

    nodetype     = BT_FREEPAGE;
    SBT_NODETYPE(p,nodetype);

    page->next = NULL;
    page->prev = NULL;

    page->dirty = BT_CLEAN;

    return;
}
ID btreeSecpageClear
TY static
MO ajindex
LB core
XX
DE Clear a secondary cache page
XX
PN [1]
PA u cache AjPBtcache
PD cache
PX
PN [2]
PA u page AjPBtpage
PD Page
PX
RT void
RD 
RX
//


static void btreeSecpageClear(AjPBtcache cache, AjPBtpage page)
{
    unsigned char *p;
    ajuint nodetype;

    p = page->buf;

    if(ajBtreePageIsPrimary(page))
        ajWarn("btreeSecpageClear primary page %Lu '%s'",
               page->pagepos, btreeNodetype(page->buf));

    AJCSET0(p, cache->secpagesize); /* clear buffer to zeros */

    nodetype     = BT_SECFREEPAGE;
    SBT_NODETYPE(p,nodetype);

    page->next = NULL;
    page->prev = NULL;

    page->dirty = BT_CLEAN;

    return;
}
ID ajBtreeStatsOut
TY public
MO ajindex
LB core
XX
DE Reports overall statistics from B+tree indexing since the last call
XX
PN [1]
PA u outf AjPFile
PD output file file
PX
PN [2]
PA u Psplitrootid ajulong*
PD Number of hyb splitroot calls to date
PX
PN [3]
PA u Psplitrootnum ajulong*
PD Number of num splitroot calls to date
PX
PN [4]
PA u Psplitrootkey ajulong*
PD Number of pri splitroot calls to date
PX
PN [5]
PA u Psplitrootsec ajulong*
PD Number of sec splitroot calls to date
PX
PN [6]
PA u Psplitleafid ajulong*
PD Number of id splitleaf calls to date
PX
PN [7]
PA u Psplitleafnum ajulong*
PD Number of num splitleaf calls to date
PX
PN [8]
PA u Psplitleafkey ajulong*
PD Number of pri splitleaf calls to date
PX
PN [9]
PA u Psplitleafsec ajulong*
PD Number of sec splitleaf calls to date
PX
PN [10]
PA u Preorderid ajulong*
PD Number of id reorderings to date
PX
PN [11]
PA u Preordernum ajulong*
PD Number of num reorderings to date
PX
PN [12]
PA u Preorderkey ajulong*
PD Number of pri reorderings to date
PX
PN [13]
PA u Preordersec ajulong*
PD Number of sec reorderings to date
PX
RT void
RD 
RX
//


void ajBtreeStatsOut(AjPFile outf,
                     ajulong* Psplitrootid,  ajulong* Psplitrootnum,
                     ajulong* Psplitrootkey, ajulong* Psplitrootsec,
                     ajulong* Psplitleafid,  ajulong* Psplitleafnum,
                     ajulong* Psplitleafkey, ajulong* Psplitleafsec,
                     ajulong* Preorderid,    ajulong* Preordernum,
                     ajulong* Preorderkey,   ajulong* Preordersec)
{
    ajulong splitrootid;
    ajulong splitrootnum;
    ajulong splitrootkey;
    ajulong splitrootsec;
    ajulong splitleafid;
    ajulong splitleafnum;
    ajulong splitleafkey;
    ajulong splitleafsec;
    ajulong reorderid;
    ajulong reordernum;
    ajulong reorderkey;
    ajulong reordersec;

    splitrootid  = statCallIdSplitroot  - *Psplitrootid;
    splitrootnum = statCallNumSplitroot - *Psplitrootnum;
    splitrootkey = statCallPriSplitroot - *Psplitrootkey;
    splitrootsec = statCallSecSplitroot - *Psplitrootsec;
    splitleafid  = statCallIdSplitleaf  - *Psplitleafid;
    splitleafnum = statCallNumSplitleaf - *Psplitleafnum;
    splitleafkey = statCallKeySplitleaf - *Psplitleafkey;
    splitleafsec = statCallSecSplitleaf - *Psplitleafsec;
    reorderid  = statCallIdbucketsReorder - *Preorderid;
    reordernum = statCallNumbucketsReorder - *Preordernum;
    reorderkey = statCallPribucketsReorder - *Preorderkey;
    reordersec = statCallSecbucketsReorder - *Preordersec;

    ajFmtPrintF(outf,
                "ajBtreeStatsOut splitroot              id: %8Lu "
                "num: %8Lu key: %8Lu sec: %8Lu\n",
                splitrootid, splitrootnum,
                splitrootkey, splitrootsec);

    ajFmtPrintF(outf,
                "ajBtreeStatsOut splitleaf id: %8Lu "
                "num: %8Lu key: %8Lu sec: %8Lu\n",
                splitleafid, splitleafnum,
                splitleafkey, splitleafsec);

    ajFmtPrintF(outf,
                "ajBtreeStatsOut reorder   id: %8Lu "
                "num: %8Lu key: %8Lu sec: %8Lu\n",
                reorderid, reordernum,
                reorderkey, reordersec);

    *Psplitrootid  = statCallIdSplitroot;
    *Psplitrootnum = statCallNumSplitroot;
    *Psplitrootkey = statCallPriSplitroot;
    *Psplitrootsec = statCallSecSplitroot;

    *Psplitleafid  = statCallIdSplitleaf;
    *Psplitleafnum = statCallNumSplitleaf;
    *Psplitleafkey = statCallKeySplitleaf;
    *Psplitleafsec = statCallSecSplitleaf;

    *Preorderid    = statCallIdbucketsReorder;
    *Preordernum   = statCallNumbucketsReorder;
    *Preorderkey   = statCallPribucketsReorder;
    *Preordersec   = statCallSecbucketsReorder;

    return;
}
ID ajBtreeExit
TY public
MO ajindex
LB core
XX
DE Cleans up B+tree indexing internal memory
XX
RT void
RD 
RX
//


void ajBtreeExit(void)
{
    void *tmpfree = NULL;
    
    ajDebug("ajBtreeExit\n");

    ajDebug("sync calls: %Lu Lock:%Lu Write: %Lu\n",
            statCallSync, statSyncLocked, statSyncWrite);

    ajDebug("Rootsync calls: %Lu Lock:%Lu (max %u) Unlock: %Lu (max %u)\n",
            statCallRootSync, statRootSyncLocked, statRootSyncMaxLocked,
            statRootSyncUnlocked, statRootSyncMaxUnlocked);

    ajDebug("   statSaveIdbucketNext: %u\n", statSaveIdbucketNext);
    while(statSaveIdbucketNext)
        btreeIdbucketFree(&statSaveIdbucket[--statSaveIdbucketNext]);
    AJFREE(statSaveIdbucket);
    statSaveIdbucketMax=0;
    statSaveIdbucketNext=0;

    ajDebug("   statSaveIdbucketEmptyNext: %u\n", statSaveIdbucketEmptyNext);
    while(statSaveIdbucketEmptyNext)
        btreeIdbucketFree(&statSaveIdbucketEmpty[--statSaveIdbucketEmptyNext]);
    AJFREE(statSaveIdbucketEmpty);
    statSaveIdbucketEmptyMax=0;
    statSaveIdbucketEmptyNext=0;

    ajDebug("   statSavePribucketNext: %u\n", statSavePribucketNext);
    while(statSavePribucketNext)
        btreePribucketFree(&statSavePribucket[--statSavePribucketNext]);
    AJFREE(statSavePribucket);
    statSavePribucketMax=0;
    statSavePribucketNext=0;

    ajDebug("   statSavePribucketEmptyNext: %u\n", statSavePribucketEmptyNext);
    while(statSavePribucketEmptyNext)
        btreePribucketFree(&statSavePribucketEmpty[--statSavePribucketEmptyNext]);
    AJFREE(statSavePribucketEmpty);
    statSavePribucketEmptyMax=0;
    statSavePribucketEmptyNext=0;

    ajDebug("   statSaveSecbucketNext: %u\n", statSaveSecbucketNext);
    while(statSaveSecbucketNext)
        btreeSecbucketFree(&statSaveSecbucket[--statSaveSecbucketNext]);
    AJFREE(statSaveSecbucket);
    statSaveSecbucketMax=0;
    statSaveSecbucketNext=0;

    ajDebug("   statSaveSecbucketEmptyNext: %u\n", statSaveSecbucketEmptyNext);
    while(statSaveSecbucketEmptyNext)
        btreeSecbucketFree(&statSaveSecbucketEmpty[--statSaveSecbucketEmptyNext]);
    AJFREE(statSaveSecbucketEmpty);
    statSaveSecbucketEmptyMax=0;
    statSaveSecbucketEmptyNext=0;

    ajDebug("   statSaveNumbucketNext: %u\n", statSaveNumbucketNext);
    while(statSaveNumbucketNext)
        btreeNumbucketFree(&statSaveNumbucket[--statSaveNumbucketNext]);
    AJFREE(statSaveNumbucket);
    statSaveNumbucketMax=0;
    statSaveNumbucketNext=0;

    ajDebug("   statSaveSecIdNext: %u\n", statSaveSecIdNext);
    while(statSaveSecIdNext)
        ajStrDel(&statSaveSecId[--statSaveSecIdNext]);
    AJFREE(statSaveSecId);
    statSaveSecIdMax=0;
    statSaveSecIdNext=0;

    ajDebug("   statSaveBtreeIdNext: %u\n", statSaveBtreeIdNext);
    while(statSaveBtreeIdNext)
        btreeIdFree(&statSaveBtreeId[--statSaveBtreeIdNext]);
    AJFREE(statSaveBtreeId);
    statSaveBtreeIdMax=0;
    statSaveBtreeIdNext=0;

    ajDebug("   statSaveBtreeHitrefNext: %u\n", statSaveBtreeHitrefNext);
    while(statSaveBtreeHitrefNext)
        btreeHitrefFree(&statSaveBtreeHitref[--statSaveBtreeHitrefNext]);
    AJFREE(statSaveBtreeHitref);
    statSaveBtreeHitrefMax=0;
    statSaveBtreeHitrefNext=0;

     ajDebug("   statSaveBtreeHitNext: %u\n", statSaveBtreeHitNext);
    while(statSaveBtreeHitNext)
        btreeHitFree(&statSaveBtreeHit[--statSaveBtreeHitNext]);
    AJFREE(statSaveBtreeHit);
    statSaveBtreeHitMax=0;
    statSaveBtreeHitNext=0;

   ajDebug("   statSaveBtreePriNext: %u\n", statSaveBtreePriNext);
    while(statSaveBtreePriNext)
        btreePriFree(&statSaveBtreePri[--statSaveBtreePriNext]);
    AJFREE(statSaveBtreePri);
    statSaveBtreePriMax=0;
    statSaveBtreePriNext=0;

    ajDebug("Primary Arrays: New:%Lu Reuse:%Lu Delete:%Lu Free:%Lu\n",
                statCountAllocPriArrayNew, statCountAllocPriArrayReuse,
                statCountAllocPriArrayDel, statCountAllocPriArrayFree);

    ajDebug("Secondary Arrays: New:%Lu Reuse:%Lu Delete:%Lu Free:%Lu\n",
                statCountAllocSecArrayNew, statCountAllocSecArrayReuse,
                statCountAllocSecArrayDel, statCountAllocSecArrayFree);

    ajDebug("Split root:%Lu Sec:%Lu Hyb:%Lu Num:%Lu\n",
            statCallPriSplitroot,statCallSecSplitroot,
            statCallIdSplitroot,statCallNumSplitroot);

    ajDebug("Split leaf:%Lu Pri:%Lu Sec:%Lu Id:%Lu Num:%Lu\n",
            statCallKeySplitleaf,statCallSecSplitleaf,
            statCallIdSplitleaf,statCallNumSplitleaf);

    ajDebug("Split reorder:%Lu Pri:%Lu Sec:%Lu  Id:%Lu Num:%Lu\n",
            statCallIdbucketsReorder,
            statCallPribucketsReorder,statCallSecbucketsReorder,
            statCallIdbucketsReorder,statCallNumbucketsReorder);

    ajStrDel(&btreeFieldnameTmp);
    ajTableMapDel(btreeFieldsTable, &btreeFieldMapDel, NULL);
    ajTableFree(&btreeFieldsTable);

    tmpfree = (void *) btreeNodetypeNames;
    AJFREE(tmpfree);
    btreeNodetypeNames = NULL;

    if(btreeTestpage)
    {
        AJFREE(btreeTestpage->buf);
        AJFREE(btreeTestpage);
    }

    ajStrDel(&indexKeyword);
    ajStrDel(&indexId);

    return;
}
ID ajBtreeIdCmp
TY public
MO ajindex
LB core
XX
DE Comparison function for sorting B+tree ID objects by database file number
DE and offset.
DE
DE Using the file and position allows unique identifiers to be
DE compared with other primary keys such bas accession number.
XX
PN [1]
PA r x const void*
PD Standard argument. Item value.
PX
PN [2]
PA r y const void*
PD Standard argument. Comparison item value.
PX
RT ajint
RD Comparison result. Zero if equal, non-zero if different.
RX
//


ajint ajBtreeIdCmp(const void* x, const void* y)
{
    const AjPBtId idx;
    const AjPBtId idy;

    idx = (const AjPBtId) x;
    idy = (const AjPBtId) y;

    if(idx->dbno > idy->dbno)
        return 1;

    if(idx->dbno < idy->dbno)
        return -1;

    if(idx->offset > idy->offset)
        return 1;

    if(idx->offset < idy->offset)
        return -1;

    return 0;
}
ID ajBtreeIdHash
TY public
MO ajindex
LB core
XX
DE Hash function for a table with a B+tree primary key
XX
PN [1]
PA r key const void*
PD Standard argument. Table key.
PX
PN [2]
PA r hashsize ajulong
PD Standard argument. Estimated Hash size.
PX
RT ajulong
RD Hash value in range 0 to hashsize-1
RX
//


ajulong ajBtreeIdHash(const void* key, ajulong hashsize)
{
    const AjPBtId id;
    ajulong hash;
    ajulong ia;

    id = (const AjPBtId) key;

     if(!key)
        return 0;

    if(!hashsize)
        return 0;

    ia = id->offset + (ajulong) id->dbno;

    hash = (ia >> 2) % hashsize;

    return hash;
}
ID ajBtreeHitCmp
TY public
MO ajindex
LB core
XX
DE Comparison function for sorting B+tree hit objects by database file number
DE and offset.
DE
DE Using the file and position allows unique identifiers to be
DE compared with other primary keys such bas accession number.
XX
PN [1]
PA r x const void*
PD Standard argument. Item value.
PX
PN [2]
PA r y const void*
PD Standard argument. Comparison item value.
PX
RT ajint
RD Comparison result. Zero if equal, non-zero if different.
RX
//


ajint ajBtreeHitCmp(const void* x, const void* y)
{
    const AjPBtHit hitx;
    const AjPBtHit hity;

    hitx = (const AjPBtHit) x;
    hity = (const AjPBtHit) y;

    if(hitx->dbno > hity->dbno)
        return 1;

    if(hitx->dbno < hity->dbno)
        return -1;

    if(hitx->offset > hity->offset)
        return 1;

    if(hitx->offset < hity->offset)
        return -1;

    return 0;
}
ID ajBtreeHitHash
TY public
MO ajindex
LB core
XX
DE Hash function for a table with a B+tree hit primary key
XX
PN [1]
PA r key const void*
PD Standard argument. Table key.
PX
PN [2]
PA r hashsize ajulong
PD Standard argument. Estimated Hash size.
PX
RT ajulong
RD Hash value in range 0 to hashsize-1
RX
//


ajulong ajBtreeHitHash(const void* key, ajulong hashsize)
{
    const AjPBtHit hit;
    ajulong hash;
    ajulong ia;

    hit = (const AjPBtHit) key;

     if(!key)
        return 0;

    if(!hashsize)
        return 0;

    ia = hit->offset + (ajulong) hit->dbno;

    hash = (ia >> 2) % hashsize;

    return hash;
}
ID ajBtreeHitrefCmp
TY public
MO ajindex
LB core
XX
DE Comparison function for sorting B+tree reference hit objects by
DE database file number and offset.
DE
DE Using the file and position allows unique identifiers to be
DE compared with other primary keys such bas accession number.
XX
PN [1]
PA r x const void*
PD Standard argument. Item value.
PX
PN [2]
PA r y const void*
PD Standard argument. Comparison item value.
PX
RT ajint
RD Comparison result. Zero if equal, non-zero if different.
RX
//


ajint ajBtreeHitrefCmp(const void* x, const void* y)
{
    const AjPBtHitref hitrefx;
    const AjPBtHitref hitrefy;

    hitrefx = (const AjPBtHitref) x;
    hitrefy = (const AjPBtHitref) y;

    if(hitrefx->dbno > hitrefy->dbno)
        return 1;

    if(hitrefx->dbno < hitrefy->dbno)
        return -1;

    if(hitrefx->offset > hitrefy->offset)
        return 1;

    if(hitrefx->offset < hitrefy->offset)
        return -1;

    return 0;
}
ID ajBtreeHitrefHash
TY public
MO ajindex
LB core
XX
DE Hash function for a table with a B+tree reference hit primary key
XX
PN [1]
PA r key const void*
PD Standard argument. Table key.
PX
PN [2]
PA r hashsize ajulong
PD Standard argument. Estimated Hash size.
PX
RT ajulong
RD Hash value in range 0 to hashsize-1
RX
//


ajulong ajBtreeHitrefHash(const void* key, ajulong hashsize)
{
    const AjPBtHitref hitref;
    ajulong hash;
    ajulong ia;

    hitref = (const AjPBtHitref) key;

     if(!key)
        return 0;

    if(!hashsize)
        return 0;

    ia = hitref->offset + (ajulong) hitref->dbno;

    hash = (ia >> 2) % hashsize;

    return hash;
}
ID btreePageDump
TY static
MO ajindex
LB core
XX
DE Dump a page
XX
PN [1]
PA r cache const AjPBtcache
PD Page
PX
PN [2]
PA r page const AjPBtpage
PD Page
PX
RT void
RD 
RX
//


static void btreePageDump(const AjPBtcache cache, const AjPBtpage page)
{
    unsigned char *buf = page->buf;
    AjBool isprimary;
    ajuint i=0;
    ajuint pagesize;

    isprimary =  ajBtreePageIsPrimary(page);
    if(isprimary)
        pagesize = cache->pripagesize;
    else
        pagesize = cache->secpagesize;

    ajUser("PageDump page %Lu '%s' primary: %B",
           page->pagepos, btreeNodetype(buf), isprimary);

    i = 0;
    while(i < pagesize)
    {
        ajUser("%4x %2x%2x%2x%2x %2x%2x%2x%2x %2x%2x%2x%2x %2x%2x%2x%2x",
               i,
               buf[i],buf[i+1],buf[i+2],buf[i+3],
               buf[i+4],buf[i+5],buf[i+6],buf[i+7],
               buf[i+8],buf[i+9],buf[i+10],buf[i+11],
               buf[i+12],buf[i+13],buf[i+14],buf[i+15] );

        i += 16;
    }

    ajUser("pagesize: %u", pagesize);

    return;
}
ID Ajax.seqType
TY public
MO ajjava
LB core
XX
DE Return length, weight and type information for a sequence
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r usa jstring
PD usa
PX
RT jboolean
RD true if the sequence exists
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_seqType
           (JNIEnv *env, jobject obj, jstring usa)
{

    AjPStr name    = NULL;
    const char *javaname = NULL;
    AjBool ok      = ajFalse;
    AjPSeq seq     = NULL;
    AjBool nuc     = ajFalse;
    ajint  len     = 0;
    float  weight  = 0.;
    jclass jvc;
    jfieldID field;

    jvc = (*env)->GetObjectClass(env,obj);

    name = ajStrNew();
    seq  = ajSeqNew();

    javaname = (*env)->GetStringUTFChars(env,usa,0);
    ajStrAssignC(&name,javaname);
    (*env)->ReleaseStringUTFChars(env,usa,javaname);

    ok = ajJavaGetSeqFromUsa(name,&seq);

    if(ok)
    {
	len = ajSeqGetLen(seq);
	nuc = ajSeqIsNuc(seq);
	weight = seq->Weight;

	field = (*env)->GetStaticFieldID(env,jvc,"length","I");
	(*env)->SetStaticIntField(env,jvc,field,len);

	field = (*env)->GetStaticFieldID(env,jvc,"protein","Z");
	(*env)->SetStaticBooleanField(env,jvc,field,(unsigned char)!nuc);

	field = (*env)->GetStaticFieldID(env,jvc,"weight","F");
	(*env)->SetStaticFloatField(env,jvc,field,weight);
    }

    ajStrDel(&name);
    ajSeqDel(&seq);

    return (unsigned char) ok;
}
ID Ajax.seqsetType
TY public
MO ajjava
LB core
XX
DE Return length, weight and type information for a seqset
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r usa jstring
PD usa
PX
RT jboolean
RD true if the sequences exist
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_seqsetType
           (JNIEnv *env, jobject obj, jstring usa)
{
    AjPStr name    = NULL;
    const char *javaname = NULL;
    AjBool ok      = ajFalse;
    AjPSeqset seq  = NULL;
    AjBool nuc     = ajFalse;
    ajint  len     = 0;
    float  weight  = 0.;
    jclass jvc;
    jfieldID field;

    name = ajStrNew();
    seq  = ajSeqsetNew();

    jvc = (*env)->GetObjectClass(env,obj);

    javaname = (*env)->GetStringUTFChars(env,usa,0);
    ajStrAssignC(&name,javaname);
    (*env)->ReleaseStringUTFChars(env,usa,javaname);

    ok = ajJavaGetSeqsetFromUsa(name,&seq);

    if(ok)
    {
	len = ajSeqsetGetLen(seq);
	nuc = ajSeqsetIsNuc(seq);
	weight = ajSeqsetGetTotweight(seq);

	field = (*env)->GetStaticFieldID(env,jvc,"length","I");
	(*env)->SetStaticIntField(env,jvc,field,len);

	field = (*env)->GetStaticFieldID(env,jvc,"protein","Z");
	(*env)->SetStaticBooleanField(env,jvc,field,(unsigned char)!nuc);

	field = (*env)->GetStaticFieldID(env,jvc,"weight","F");
	(*env)->SetStaticFloatField(env,jvc,field,weight);
    }

    ajStrDel(&name);
    ajSeqsetDel(&seq);

    return (unsigned char) ok;
}
ID ajJavaGetSeqFromUsa
TY static
MO ajjava
LB core
XX
DE Return a sequence given a USA
XX
PN [1]
PA r thys const AjPStr
PD usa
PX
PN [2]
PA w seq AjPSeq*
PD sequence
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool ajJavaGetSeqFromUsa(const AjPStr thys, AjPSeq *seq)
{
    AjPSeqin seqin;
    AjBool ok;

    ajNamInit("emboss");

    seqin = ajSeqinNew();
    seqin->Input->Multi = ajFalse;
    seqin->Input->Text  = ajFalse;

    ajSeqinUsa (&seqin, thys);
    ok = ajSeqRead(*seq, seqin);
    ajSeqinDel (&seqin);

    if(!ok)
	return ajFalse;

    return ajTrue;
}
ID ajJavaGetSeqsetFromUsa
TY static
MO ajjava
LB core
XX
DE Return a seqset given a usa
XX
PN [1]
PA r thys const AjPStr
PD usa
PX
PN [2]
PA w seq AjPSeqset*
PD seqset
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool ajJavaGetSeqsetFromUsa(const AjPStr thys, AjPSeqset *seq)
{
    AjPSeqin seqin;
    AjBool ok;

    ajNamInit("emboss");

    seqin = ajSeqinNew();
    seqin->Input->Multi = ajTrue;
    seqin->Input->Text  = ajFalse;

    ajSeqinUsa (&seqin, thys);
    ok = ajSeqsetRead(*seq, seqin);
    ajSeqinDel (&seqin);


    if(!ok)
	return ajFalse;

    return ajTrue;
}
ID Ajax.userInfo
TY public
MO ajjava
LB core
XX
DE Return the uid, gid and home directory of a user
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jstring
PD password
PX
RT jboolean
RD true if the username/password are valid
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_userInfo
(JNIEnv *env, jobject obj, jstring door, jstring key)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr home     = NULL;
    const char *juser = NULL;
    const char *jpass = NULL;
    AjBool ok       = ajFalse;

    jclass jvc;
    jfieldID field;
    jstring  jhpstr;

    ajint uid = 0;
    ajint gid = 0;
    const char  *hp;

    jvc = (*env)->GetObjectClass(env,obj);

    java_core_dump();

    username = ajStrNew();
    password = ajStrNew();
    home     = ajStrNew();

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	ajStrDel(&username);
	ajStrDel(&password);
	ajStrDel(&home);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);

    jpass = (*env)->GetStringUTFChars(env,key,0);

    if(jpass)
	ajStrAssignC(&password,jpass);
    else
    {
	ajStrDel(&username);
	ajStrDel(&password);
	ajStrDel(&home);

	return (unsigned char)ajFalse;
    }
    (*env)->ReleaseStringUTFChars(env,key,jpass);

    if(!ajStrGetLen(username) || !ajStrGetLen(password))
    {
	ajStrDel(&username);
	ajStrDel(&password);
	ajStrDel(&home);
	return (unsigned char)ajFalse;
    }

#ifndef NO_AUTH
    ok = java_pass(username,password,&uid,&gid,&home);
#endif /* !NO_AUTH */

    field = (*env)->GetStaticFieldID(env,jvc,"uid","I");
    (*env)->SetStaticIntField(env,jvc,field,uid);

    field = (*env)->GetStaticFieldID(env,jvc,"gid","I");
    (*env)->SetStaticIntField(env,jvc,field,gid);


    hp = ajStrGetPtr(home);
    field = (*env)->GetFieldID(env,jvc,"home","Ljava/lang/String;");

    jhpstr = (*env)->NewStringUTF(env,hp);
    (*env)->SetObjectField(env,obj,field,jhpstr);

    bzero((void *)username->Ptr,ajStrGetLen(username));
    bzero((void *)password->Ptr,ajStrGetLen(password));
    bzero((void *)home->Ptr,ajStrGetLen(home));

    ajStrDel(&username);
    ajStrDel(&password);
    ajStrDel(&home);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID java_core_dump
TY static
MO ajjava
LB core
XX
DE Set process coredump size to be zero
XX
RT void
RD 
RX
//


static void java_core_dump(void)
{
    struct rlimit limit;

    limit.rlim_cur = 0;
    limit.rlim_max = 0;

    setrlimit(RLIMIT_CORE,&limit);

    return;
}
ID Ajax.setuid
TY public
MO ajjava
LB core
XX
DE Set the uid of the current process
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r j jclass
PD java class
PX
PN [3]
PA r uid jint
PD uid
PX
RT jint
RD 0 on success, -1 if failure
RX
//


JNIEXPORT jint JNICALL Java_org_emboss_jemboss_server_Ajax_setuid
(JNIEnv *env, jclass j, jint uid)
{
    (void) env;
    (void) j;
    
    return((jint)setuid((uid_t)uid));
}
ID Ajax.seteuid
TY public
MO ajjava
LB core
XX
DE Set the effective uid of the current process
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r j jclass
PD java class
PX
PN [3]
PA r uid jint
PD uid
PX
RT jint
RD 0 on success, -1 if failure
RX
//


JNIEXPORT jint JNICALL Java_org_emboss_jemboss_server_Ajax_seteuid
(JNIEnv *env, jclass j, jint uid)
{
    (void) env;
    (void) j;
#ifndef __hpux
    return((jint)seteuid((uid_t)uid));
#else /* !__hpux */
    (void) uid;
    return -1;
#endif /* __hpux */
}
ID Ajax.setgid
TY public
MO ajjava
LB core
XX
DE Set the gid of the current process
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r j jclass
PD java class
PX
PN [3]
PA r gid jint
PD gid
PX
RT jint
RD 0 on success, -1 if failure
RX
//


JNIEXPORT jint JNICALL Java_org_emboss_jemboss_server_Ajax_setgid
(JNIEnv *env, jclass j, jint gid)
{
    (void) env;
    (void) j;
    
    return((jint)setgid((gid_t)gid));
}
ID Ajax.setegid
TY public
MO ajjava
LB core
XX
DE Set the effective gid of the current process
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r j jclass
PD java class
PX
PN [3]
PA r gid jint
PD gid
PX
RT jint
RD 0 on success, -1 if failure
RX
//


JNIEXPORT jint JNICALL Java_org_emboss_jemboss_server_Ajax_setegid
(JNIEnv *env, jclass j, jint gid)
{
    (void) env;
    (void) j;
#ifndef __hpux
    return((jint)setegid((gid_t)gid));
#else /* !__hpux */
    (void) gid;
    return -1;
#endif /* __hpux */
}
ID Ajax.getuid
TY public
MO ajjava
LB core
XX
DE Return the uid of the current process
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r j jclass
PD java class
PX
RT jint
RD uid
RX
//


JNIEXPORT jint JNICALL Java_org_emboss_jemboss_server_Ajax_getuid
(JNIEnv *env, jclass j)
{
    (void) env;
    (void) j;
    
    return((jint)getuid());
}
ID Ajax.getgid
TY public
MO ajjava
LB core
XX
DE Return the gid of the current process
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r j jclass
PD java class
PX
RT jint
RD gid
RX
//


JNIEXPORT jint JNICALL Java_org_emboss_jemboss_server_Ajax_getgid
(JNIEnv *env, jclass j)
{
    (void) env;
    (void) j;
    
    return((jint)getgid());
}
ID Ajax.geteuid
TY public
MO ajjava
LB core
XX
DE Return the effective uid of the current process
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r j jclass
PD java class
PX
RT jint
RD uid
RX
//


JNIEXPORT jint JNICALL Java_org_emboss_jemboss_server_Ajax_geteuid
(JNIEnv *env, jclass j)
{
    (void) env;
    (void) j;
    
    return((jint)geteuid());
}
ID Ajax.getegid
TY public
MO ajjava
LB core
XX
DE Return the effective gid of the current process
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r j jclass
PD java class
PX
RT jint
RD gid
RX
//


JNIEXPORT jint JNICALL Java_org_emboss_jemboss_server_Ajax_getegid
(JNIEnv *env, jclass j)
{
    (void) env;
    (void) j;
    
    return((jint)getegid());
}
ID Ajax.fork
TY public
MO ajjava
LB core
XX
DE Fork off new process with given uid & gid, chdir and execute command
DE return stdout and stderr of the child process
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r commandline jstring
PD command to execute
PX
PN [4]
PA r environment jstring
PD shell environment
PX
PN [5]
PA r directory jstring
PD chdir here before execution
PX
PN [6]
PA r uid jint
PD uid for setuid
PX
PN [7]
PA r gid jint
PD gid for setgid
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_fork
(JNIEnv *env, jobject obj, jstring commandline,
 jstring environment, jstring directory, jint uid, jint gid)
{
    jclass jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;

    const char  *sptr;

    AjPStr prog = NULL;
    AjPStr cl   = NULL;
    AjPStr envi = NULL;
    AjPStr dir  = NULL;


    char **argp = NULL;
    char **envp = NULL;
    int  pid;
    int  status = 0;
    int  i=0;

    int  outpipe[2];
    int  errpipe[2];

#ifdef HAVE_POLL
    struct pollfd ufds[2];
    unsigned int nfds;
#else /* !HAVE_POLL */
    fd_set rec;
    struct timeval t;
#endif /* HAVE_POLL */

    int nread = 0;
    char *buf;
    AjPStr outstd = NULL;
    AjPStr errstd = NULL;
    int retval    = 0;
    const char *save;

    jvc = (*env)->GetObjectClass(env,obj);

    if(!uid || !gid)
	return (unsigned char)ajFalse;

    if(uidGetStringUTFChars(env,commandline,0);
    ajStrAssignC(&cl,sptr);
    (*env)->ReleaseStringUTFChars(env,commandline,sptr);


    ajSysFuncStrtokR(ajStrGetPtr(cl)," \t\n",&save,&prog);


    sptr = (*env)->GetStringUTFChars(env,environment,0);
    ajStrAssignC(&envi,sptr);
    (*env)->ReleaseStringUTFChars(env,environment,sptr);

    sptr = (*env)->GetStringUTFChars(env,directory,0);
    ajStrAssignC(&dir,sptr);
    (*env)->ReleaseStringUTFChars(env,directory,sptr);


    argp = make_array(cl);
    envp = make_array(envi);

    if(!ajSysFileWhichEnv(&prog,envp))
    {
	java_tidy_command(&prog,&cl,&envi,&dir,&outstd,&errstd);
	i = 0;

	while(argp[i])
	    AJFREE(argp[i++]);

	AJFREE(argp);

	i = 0;

	while(envp[i])
	    AJFREE(envp[i++]);

	AJFREE(envp);

	return (unsigned char)ajFalse;
    }


    while(pipe(outpipe)==-1);
    while(pipe(errpipe)==-1);

#if defined (__SVR4) && defined (__sun)
    pid = fork1();
#else /* !(__SVR4 && __sun) */
    pid = fork();
#endif/* __SVR4 && __sun */

    if(pid == -1)
    {
	java_tidy_command(&prog,&cl,&envi,&dir,&outstd,&errstd);
	i = 0;

	while(argp[i])
	    AJFREE(argp[i]);

	AJFREE(argp);

	i = 0;

	while(envp[i])
	    AJFREE(envp[i]);

	AJFREE(envp);

	return (unsigned char)ajFalse;
    }


    if(!pid)			/* Child */
    {
	dup2(outpipe[1],1);
	dup2(errpipe[1],2);

	if(setgid(gid)==-1)
	{
	    fprintf(stderr,"setgid failure");
	    exit(-1);
	}

	if(setuid(uid)==-1)
	{
	    fprintf(stderr,"setuid failure");
	    exit(-1);
	}

	if(chdir(ajStrGetPtr(dir))==-1)
	{
	    fprintf(stderr,"chdir failure");
	    exit(-1);
	}

        ajSysExecprogNowaitS(prog,argp,envp);
    }

    *buf = '\0';

#ifdef HAVE_POLL
    while((retval=waitpid(pid,&status,WNOHANG))!=pid)
    {
	if(retval==-1)
	    if(errno!=EINTR)
		break;

	ufds[0].fd = outpipe[0];
	ufds[1].fd = errpipe[0];
	ufds[0].events = POLLIN | POLLPRI;
	ufds[1].events = POLLIN | POLLPRI;
	nfds = 2;

	if(!(retval=poll(ufds,nfds,1)) || retval==-1)
	    continue;

	if((ufds[0].revents & POLLIN) || (ufds[0].revents & POLLPRI))
	{
	    while((nread = read(outpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    buf[nread]='\0';
	    ajStrAppendC(&outstd,buf);
	}


	if((ufds[1].revents & POLLIN) || (ufds[1].revents & POLLPRI))
	{
	    while((nread = read(errpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    buf[nread]='\0';
	    ajStrAppendC(&errstd,buf);
	}
    }


    ufds[0].fd = outpipe[0];
    ufds[1].fd = errpipe[0];
    ufds[0].events = POLLIN | POLLPRI;
    ufds[1].events = POLLIN | POLLPRI;
    nfds = 2;

    retval=poll(ufds,nfds,1);

    if(retval>0)
	if((ufds[0].revents & POLLIN) || (ufds[0].revents & POLLPRI))
	{
	    while((nread = read(outpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);
	    buf[nread]='\0';
	    ajStrAppendC(&outstd,buf);
	}

    retval=poll(ufds,nfds,1);

    if(retval>0)
	if((ufds[1].revents & POLLIN) || (ufds[1].revents & POLLPRI))
	{
	    while((nread = read(errpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);
	    buf[nread]='\0';
	    ajStrAppendC(&errstd,buf);
	}
#else /* !HAVE_POLL */
    while((retval=waitpid(pid,&status,WNOHANG))!=pid)
    {
	if(retval==-1)
	    if(errno!=EINTR)
		break;

	FD_ZERO(&rec);
	FD_SET(outpipe[0],&rec);
	t.tv_sec = 0;
	t.tv_usec = 1000;
	select(outpipe[0]+1,&rec,NULL,NULL,&t);

	if(FD_ISSET(outpipe[0],&rec))
	{
	    nread = read(outpipe[0],(void *)buf,AJ_OUTBUF);
	    buf[nread]='\0';
	    ajStrAppendC(&outstd,buf);
	}

	FD_ZERO(&rec);
	FD_SET(errpipe[0],&rec);
	t.tv_sec = 0;
	t.tv_usec = 1000;
	select(errpipe[0]+1,&rec,NULL,NULL,&t);

	if(FD_ISSET(errpipe[0],&rec))
	{
	    nread = read(errpipe[0],(void *)buf,AJ_OUTBUF);
	    buf[nread]='\0';
	    ajStrAppendC(&errstd,buf);
	}


    }


    FD_ZERO(&rec);
    FD_SET(outpipe[0],&rec);
    t.tv_sec = 0;
    t.tv_usec = 0;
    select(outpipe[0]+1,&rec,NULL,NULL,&t);

    if(FD_ISSET(outpipe[0],&rec))
    {
	nread = read(outpipe[0],(void *)buf,AJ_OUTBUF);
	buf[nread]='\0';
	ajStrAppendC(&outstd,buf);
    }


    FD_ZERO(&rec);
    FD_SET(errpipe[0],&rec);
    t.tv_sec = 0;
    t.tv_usec = 0;
    select(errpipe[0]+1,&rec,NULL,NULL,&t);

    if(FD_ISSET(errpipe[0],&rec))
    {
	nread = read(errpipe[0],(void *)buf,AJ_OUTBUF);
	buf[nread]='\0';
	ajStrAppendC(&errstd,buf);
    }
#endif /* HAVE_POLL */

    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    close(errpipe[0]);
    close(errpipe[1]);
    close(outpipe[0]);
    close(outpipe[1]);

    i = 0;

    while(argp[i])
	AJFREE(argp[i]);

    AJFREE(argp);

    i = 0;

    while(envp[i])
	AJFREE(envp[i]);

    AJFREE(envp);


    AJFREE(buf);

    ajStrDel(&prog);
    ajStrDel(&cl);
    ajStrDel(&envi);
    ajStrDel(&dir);
    ajStrDel(&outstd);
    ajStrDel(&errstd);

    return (unsigned char)ajTrue;
}
ID make_array
TY static
MO ajjava
LB core
XX
DE Construct argv and env arrays for Ajax.fork
XX
PN [1]
PA r str const AjPStr
PD space separated tokens
PX
RT char**
RD env or argv array
RX
//


static char** make_array(const AjPStr str)
{
    int n;
    char **ptr   = NULL;
    AjPStr buf;
    const char *save = NULL;

    buf = ajStrNew();

    n = ajStrParseCountMultiC(str," \t\n");

    AJCNEW0(ptr,n+1);

    ptr[n] = NULL;

    n = 0;

    if(!ajSysFuncStrtokR(ajStrGetPtr(str)," \t\n",&save,&buf))
	return ptr;

    ptr[n++] = ajCharNewS(buf);

    while(ajSysFuncStrtokR(NULL," \t\n",&save,&buf))
	ptr[n++] = ajCharNewS(buf);

    ajStrDel(&buf);

    return ptr;
}
ID java_send_auth
TY static
MO ajjava
LB core
XX
DE Send verification/homedir request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r rchan int
PD return channel file descriptor
PX
PN [3]
PA r cuser const char*
PD username
PX
PN [4]
PA r cpass const char*
PD password
PX
PN [5]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_send_auth(int tchan, int rchan,
			    const char *cuser, const char *cpass,
			    AjPStr *errstd)
{
    AjPStr cmnd = NULL;
    char   c;
    int    n;

    cmnd = ajStrNew();
    ajFmtPrintS(&cmnd,"%d %s %s",COMM_AUTH,cuser,cpass);

    if(java_snd(tchan,ajStrGetPtr(cmnd),ajStrGetLen(cmnd)+1,errstd)<0)
    {
	ajStrAppendC(errstd,"Error sending (java_send_auth)\n");
	ajStrDel(&cmnd);

	return -1;
    }


    n = java_rcv(rchan,(char *)&c,errstd);

    if(n==-1)
    {
	ajStrAppendC(errstd,"Error receiving (java_send_auth)\n");
	ajStrDel(&cmnd);

	return -1;
    }



    ajStrDel(&cmnd);


    if(!c)
	return -1;

    return 0;
}
ID java_emboss_fork
TY static
MO ajjava
LB core
XX
DE Send an emboss fork request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r cuser const char*
PD username
PX
PN [3]
PA r cpass const char*
PD password
PX
PN [4]
PA r clemboss const AjPStr
PD emboss command line
PX
PN [5]
PA r envi const AjPStr
PD emboss environment
PX
PN [6]
PA r dir const AjPStr
PD directory to invoke emboss from
PX
PN [7]
PA w buf char*
PD socket buffer
PX
PN [8]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_emboss_fork(int tchan, const char *cuser, const char *cpass,
			      const AjPStr clemboss, const AjPStr envi,
			      const AjPStr dir,
			      char *buf, AjPStr *errstd)
{
    char *p = NULL;
    int n;


    n = sprintf(buf,"%d %s %s",EMBOSS_FORK,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(clemboss));
    p += (ajStrGetLen(clemboss)+1);
    sprintf(p,"%s",ajStrGetPtr(envi));
    p += (ajStrGetLen(envi)+1);
    sprintf(p,"%s",ajStrGetPtr(dir));
    n = (p-buf) + ajStrGetLen(dir) +1;

    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_emboss_fork)\n");

	return -1;
    }

    return 0;
}
ID java_batch_fork
TY static
MO ajjava
LB core
XX
DE Send a detached emboss fork request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r cuser const char*
PD username
PX
PN [3]
PA r cpass const char*
PD password
PX
PN [4]
PA r clemboss const AjPStr
PD emboss command line
PX
PN [5]
PA r envi const AjPStr
PD emboss environment
PX
PN [6]
PA r dir const AjPStr
PD directory to invoke emboss from
PX
PN [7]
PA w buf char*
PD socket buffer
PX
PN [8]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_batch_fork(int tchan, const char *cuser,const char *cpass,
			     const AjPStr clemboss, const AjPStr envi,
			     const AjPStr dir,
			     char *buf, AjPStr *errstd)
{
    char *p = NULL;
    int n;


    n = sprintf(buf,"%d %s %s",BATCH_FORK,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(clemboss));
    p += (ajStrGetLen(clemboss)+1);
    sprintf(p,"%s",ajStrGetPtr(envi));
    p += (ajStrGetLen(envi)+1);
    sprintf(p,"%s",ajStrGetPtr(dir));
    n = (p-buf) + ajStrGetLen(dir) +1;

    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_batch_fork)\n");

	return -1;
    }

    return 0;
}
ID java_make_dir
TY static
MO ajjava
LB core
XX
DE Send a make directory request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r cuser const char*
PD username
PX
PN [3]
PA r cpass const char*
PD password
PX
PN [4]
PA r dir const AjPStr
PD directory to create
PX
PN [5]
PA w buf char*
PD socket buffer
PX
PN [6]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_make_dir(int tchan,const char *cuser,const char *cpass,
			   const AjPStr dir,
			   char *buf, AjPStr *errstd)
{
    char *p = NULL;
    int n;


    n = sprintf(buf,"%d %s %s",MAKE_DIRECTORY,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(dir));
    n = (p-buf) + ajStrGetLen(dir) +1;

    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_make_dir)\n");

	return -1;
    }

    return 0;
}
ID java_delete_file
TY static
MO ajjava
LB core
XX
DE Send a delete file request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r cuser const char*
PD username
PX
PN [3]
PA r cpass const char*
PD password
PX
PN [4]
PA r ufile const AjPStr
PD file to delete
PX
PN [5]
PA w buf char*
PD socket buffer
PX
PN [6]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_delete_file(int tchan,const char *cuser,const char *cpass,
			      const AjPStr ufile,
			      char *buf, AjPStr *errstd)
{
    char *p = NULL;
    int n;


    n = sprintf(buf,"%d %s %s",DELETE_FILE,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(ufile));
    n = (p-buf) + ajStrGetLen(ufile) +1;

    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_delete_file)\n");

	return -1;
    }

    return 0;
}
ID java_rename_file
TY static
MO ajjava
LB core
XX
DE Send a rename file request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r cuser const char*
PD username
PX
PN [3]
PA r cpass const char*
PD password
PX
PN [4]
PA r ufile const AjPStr
PD file to rename
PX
PN [5]
PA r u2file const AjPStr
PD new filename
PX
PN [6]
PA w buf char*
PD socket buffer
PX
PN [7]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_rename_file(int tchan,const char *cuser,const char *cpass,
			      const AjPStr ufile,
			      const AjPStr u2file, char *buf, AjPStr *errstd)
{
    char *p = NULL;
    int n;


    n = sprintf(buf,"%d %s %s",RENAME_FILE,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(ufile));
    p += (ajStrGetLen(ufile)+1);
    sprintf(p,"%s",ajStrGetPtr(u2file));
    n = (p-buf) + ajStrGetLen(u2file) +1;

    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_rename_file)\n");

	return -1;
    }

    return 0;
}
ID java_delete_dir
TY static
MO ajjava
LB core
XX
DE Send a delete directory request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r cuser const char*
PD username
PX
PN [3]
PA r cpass const char*
PD password
PX
PN [4]
PA r dir const AjPStr
PD directory to delete
PX
PN [5]
PA w buf char*
PD socket buffer
PX
PN [6]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_delete_dir(int tchan,const char *cuser,const char *cpass,
			     const AjPStr dir,
			     char *buf, AjPStr *errstd)
{
    char *p = NULL;
    int n;


    n = sprintf(buf,"%d %s %s",DELETE_DIR,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(dir));
    n = (p-buf) + ajStrGetLen(dir) +1;

    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_delete_dir)\n");

	return -1;
    }

    return 0;
}
ID java_list_files
TY static
MO ajjava
LB core
XX
DE Send a list files request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r cuser const char*
PD username
PX
PN [3]
PA r cpass const char*
PD password
PX
PN [4]
PA r dir const AjPStr
PD directory to scan
PX
PN [5]
PA w buf char*
PD socket buffer
PX
PN [6]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_list_files(int tchan,const char *cuser,const char *cpass,
			     const AjPStr dir,
			     char *buf, AjPStr *errstd)
{
    char *p = NULL;
    int n;


    n = sprintf(buf,"%d %s %s",LIST_FILES,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(dir));
    n = (p-buf) + ajStrGetLen(dir) +1;

    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_list_files)\n");

	return -1;
    }

    return 0;
}
ID java_list_dirs
TY static
MO ajjava
LB core
XX
DE Send a list directories request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r cuser const char*
PD username
PX
PN [3]
PA r cpass const char*
PD password
PX
PN [4]
PA r dir const AjPStr
PD directory to scan
PX
PN [5]
PA w buf char*
PD socket buffer
PX
PN [6]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_list_dirs(int tchan,const char *cuser,const char *cpass,
			    const AjPStr dir,
			    char *buf, AjPStr *errstd)
{
    char *p = NULL;
    int n;


    n = sprintf(buf,"%d %s %s",LIST_DIRS,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(dir));
    n = (p-buf) + ajStrGetLen(dir) +1;

    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_list_dirs)\n");

	return -1;
    }

    return 0;
}
ID java_get_file
TY static
MO ajjava
LB core
XX
DE Send a get file request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r rchan int
PD return channel file descriptor
PX
PN [3]
PA r cuser const char*
PD username
PX
PN [4]
PA r cpass const char*
PD password
PX
PN [5]
PA r file const AjPStr
PD file to get
PX
PN [6]
PA w buf char*
PD socket buffer
PX
PN [7]
PA w fbuf unsigned char**
PD file buffer
PX
PN [8]
PA w size int*
PD file size
PX
PN [9]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_get_file(int tchan,int rchan,
			   const char *cuser,const char *cpass,
			   const AjPStr file, char *buf,unsigned char **fbuf,
			   int *size, AjPStr *errstd)
{
    char *p = NULL;
    int n;
    int rlen = 0;

    n = sprintf(buf,"%d %s %s",GET_FILE,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(file));
    n = (p-buf) + ajStrGetLen(file) +1;


    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_get_file)\n");

	return -1;
    }


    rlen = java_rcv(rchan,buf,errstd);

    if(rlen==-1)
    {
	*size = 0;
	ajStrAppendC(errstd,"Reading error (java_get_file)\n");

	return -1;
    }


    if(sscanf(buf,"%d",&n)!=1)
    {
	ajStrAppendC(errstd,"Bad size (java_get_file)\n");

	return -1;
    }



    if(n==-1)
    {
	ajStrAppendC(errstd,"Bad size (java_get_file)\n");

	return -1;
    }

    *size = n;

    if(n)
	if(!(*fbuf=(unsigned char *)malloc(n)))
	{
	    ajStrAppendC(errstd,"Malloc error (java_get_file)\n");

	    return -1;
	}

    return 0;
}
ID java_put_file
TY static
MO ajjava
LB core
XX
DE Send a put file request to jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r rchan int
PD return channel file descriptor
PX
PN [3]
PA r cuser const char*
PD username
PX
PN [4]
PA r cpass const char*
PD password
PX
PN [5]
PA r file const AjPStr
PD file to get
PX
PN [6]
PA w buf char*
PD socket buffer
PX
PN [7]
PA r fbuf const unsigned char*
PD file buffer
PX
PN [8]
PA r size int
PD file size
PX
PN [9]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_put_file(int tchan,int rchan,
			   const char *cuser,const char *cpass,
			   const AjPStr file,
			   char *buf,const unsigned char *fbuf,int size,
			   AjPStr *errstd)
{
    char *p  = NULL;
    int n;
    int pos  = 0;
    int rlen = 0;


    n = sprintf(buf,"%d %s %s",PUT_FILE,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(file));
    n = (p-buf) + ajStrGetLen(file) +1;

    /* send command */
    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_put_file)\n");

	return -1;
    }


    rlen = java_rcv(rchan,buf,errstd);

    if(rlen==-1)
    {
	ajStrAppendC(errstd,"Socket Recv error 2\n");

	return -1;
    }




    /* Send file size */
    n = sprintf(buf,"%d",size);

    if(java_snd(tchan,buf,n+1,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_put_file)\n");

	return -1;
    }


    rlen = java_rcv(rchan,buf,errstd);

    if(rlen==-1)
    {
	ajStrAppendC(errstd,"Socket Recv error 2\n");

	return -1;
    }



    while(pos+JBUFFLEN < size)
    {
 	memcpy((void *)buf,(const void *)&fbuf[pos],JBUFFLEN);

	if(java_snd(tchan,buf,JBUFFLEN,errstd) < 0)
	{
	    ajStrAppendC(errstd,"Sending error (java_put_file)\n");

	    return -1;
	}

	pos += JBUFFLEN;
    }

    if(size)
	if(size-pos)
	{
	    memcpy((void *)buf,(const void *)&fbuf[pos],size-pos);

	    if(java_snd(tchan,buf,size-pos,errstd) < 0)
	    {
		ajStrAppendC(errstd,"Sending error (java_put_file)\n");

		return -1;
	    }
	}

    return 0;
}
ID java_seq_attrib
TY static
MO ajjava
LB core
XX
DE Get sequence attributes via jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r cuser const char*
PD username
PX
PN [3]
PA r cpass const char*
PD password
PX
PN [4]
PA r usa const AjPStr
PD usa
PX
PN [5]
PA w buf char*
PD socket buffer
PX
PN [6]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_seq_attrib(int tchan,const char *cuser,const char *cpass,
			     const AjPStr usa,
			     char *buf, AjPStr *errstd)
{
    char *p = NULL;
    int n;


    n = sprintf(buf,"%d %s %s",SEQ_ATTRIB,cuser,cpass);
    p=buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(usa));
    n = (p-buf) + ajStrGetLen(usa) +1;

    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_seq_attrib)\n");

	return -1;
    }

    return 0;
}
ID java_seqset_attrib
TY static
MO ajjava
LB core
XX
DE Get seqset attributes from jembossctl
XX
PN [1]
PA r tchan int
PD to channel file descriptor
PX
PN [2]
PA r cuser const char*
PD username
PX
PN [3]
PA r cpass const char*
PD password
PX
PN [4]
PA r usa const AjPStr
PD usa
PX
PN [5]
PA w buf char*
PD socket buffer
PX
PN [6]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
RT ajint
RD 0=success -1=failure
RX
//


static ajint java_seqset_attrib(int tchan,const char *cuser,const char *cpass,
				const AjPStr usa,
				char *buf, AjPStr *errstd)
{
    char *p = NULL;
    int n;


    n = sprintf(buf,"%d %s %s",SEQSET_ATTRIB,cuser,cpass);
    p = buf+n+1;
    sprintf(p,"%s",ajStrGetPtr(usa));
    n = (p-buf) + ajStrGetLen(usa) +1;

    if(java_snd(tchan,buf,n,errstd) < 0)
    {
	ajStrAppendC(errstd,"Sending error (java_seqset_attrib)\n");

	return -1;
    }

    return 0;
}
ID java_wait_for_term
TY static
MO ajjava
LB core
XX
DE Wait for jembossctl to terminate
XX
PN [1]
PA r pid int
PD jembossctl pid
PX
PN [2]
PA w outstd AjPStr*
PD stdout from jembossctl
PX
PN [3]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
PN [4]
PA w outpipe int*
PD stdout pipe
PX
PN [5]
PA w errpipe int*
PD stderr pipe
PX
PN [6]
PA w buf char*
PD pipe buffer
PX
RT void
RD 
RX
//


static void java_wait_for_term(int pid,AjPStr *outstd, AjPStr *errstd,
			       int *outpipe, int *errpipe, char *buf)
{
#ifdef HAVE_POLL
    struct pollfd ufds[2];
    unsigned int  nfds;
#else /* !HAVE_POLL */
    fd_set rec;
    struct timeval t;
#endif /* HAVE_POLL */

    int nread  = 0;
    int  status;
    int retval = 0;
    unsigned long block = 0;


    block = 1;

    if(java_block(outpipe[0],block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 1a. %d\n",errno);

	return;
    }

    if(java_block(errpipe[0],block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 2a. %d\n",errno);

	return;
    }

    *buf = '\0';


#ifdef HAVE_POLL
    while((retval=waitpid(pid,&status,WNOHANG))!=pid)
    {
	if(retval==-1)
	    if(errno!=EINTR)
		break;

	ufds[0].fd = outpipe[0];
	ufds[1].fd = errpipe[0];
	ufds[0].events = POLLIN | POLLPRI;
	ufds[1].events = POLLIN | POLLPRI;
	nfds = 2;

	if(!(retval=poll(ufds,nfds,1)) || retval==-1)
	    continue;

	if((ufds[0].revents & POLLIN) || (ufds[0].revents & POLLPRI))
	{
	    while((nread = read(outpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    buf[nread]='\0';
	    ajStrAppendC(outstd,buf);
	}


	if((ufds[1].revents & POLLIN) || (ufds[1].revents & POLLPRI))
	{
	    while((nread = read(errpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    buf[nread]='\0';
	    ajStrAppendC(errstd,buf);
	}
    }


    ufds[0].fd = outpipe[0];
    ufds[1].fd = errpipe[0];
    ufds[0].events = POLLIN | POLLPRI;
    ufds[1].events = POLLIN | POLLPRI;
    nfds = 2;

    retval=poll(ufds,nfds,1);

    if(retval>0)
	if((ufds[0].revents & POLLIN) || (ufds[0].revents & POLLPRI))
	{
	    while((nread = read(outpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    buf[nread]='\0';
	    ajStrAppendC(outstd,buf);
	}

    retval=poll(ufds,nfds,1);

    if(retval>0)
	if((ufds[1].revents & POLLIN) || (ufds[1].revents & POLLPRI))
	{
	    while((nread = read(errpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    buf[nread]='\0';
	    ajStrAppendC(errstd,buf);
	}
#else /* !HAVE_POLL */
    while((retval=waitpid(pid,&status,WNOHANG))!=pid)
    {
	if(retval==-1)
	    if(errno!=EINTR)
		break;

	FD_ZERO(&rec);
	FD_SET(outpipe[0],&rec);
	t.tv_sec = 0;
	t.tv_usec = 1000;
	select(outpipe[0]+1,&rec,NULL,NULL,&t);

	if(FD_ISSET(outpipe[0],&rec))
	{
	    *buf = '\0';

	    while((nread = read(outpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    if(nread > 0)
	    {
		buf[nread]='\0';
		ajStrAppendC(outstd,buf);
	    }
	}

	FD_ZERO(&rec);
	FD_SET(errpipe[0],&rec);
	t.tv_sec = 0;
	t.tv_usec = 1000;
	select(errpipe[0]+1,&rec,NULL,NULL,&t);
	if(FD_ISSET(errpipe[0],&rec))
	{
	    *buf = '\0';

	    while((nread = read(errpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    if(nread>0)
	    {
		buf[nread]='\0';
		ajStrAppendC(errstd,buf);
	    }
	}


    }


    FD_ZERO(&rec);
    FD_SET(outpipe[0],&rec);
    t.tv_sec = 0;
    t.tv_usec = 0;
    select(outpipe[0]+1,&rec,NULL,NULL,&t);

    if(FD_ISSET(outpipe[0],&rec))
    {
	*buf = '\0';

	while((nread = read(outpipe[0],(void *)buf,JBUFFLEN))==-1
	      && errno==EINTR);

	if(nread>0)
	{
	    buf[nread]='\0';
	    ajStrAppendC(outstd,buf);
	}
    }


    FD_ZERO(&rec);
    FD_SET(errpipe[0],&rec);
    t.tv_sec = 0;
    t.tv_usec = 0;
    select(errpipe[0]+1,&rec,NULL,NULL,&t);

    if(FD_ISSET(errpipe[0],&rec))
    {
	*buf = '\0';

	while((nread = read(errpipe[0],(void *)buf,JBUFFLEN))==-1
	      && errno==EINTR);

	if(nread >0)
	{
	    buf[nread]='\0';
	    ajStrAppendC(errstd,buf);
	}
    }
#endif /* HAVE_POLL */

    block = 0;

    if(java_block(outpipe[0],block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 3a. %d\n",errno);

	return;
    }

    if(java_block(errpipe[0],block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 4a. %d\n",errno);

	return;
    }


    return;
}
ID java_wait_for_file
TY static
MO ajjava
LB core
XX
DE Wait for jembossctl to terminate. Receive a file from stdout pipe
XX
PN [1]
PA r pid int
PD jembossctl pid
PX
PN [2]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
PN [3]
PA w outpipe int*
PD stdout pipe
PX
PN [4]
PA w errpipe int*
PD stderr pipe
PX
PN [5]
PA w buf char*
PD pipe buffer
PX
PN [6]
PA w fbuf unsigned char*
PD buffer for file
PX
PN [7]
PA r size int
PD size of file to receive
PX
RT void
RD 
RX
//


static void java_wait_for_file(int pid, AjPStr *errstd,
			       int *outpipe, int *errpipe, char *buf,
			       unsigned char *fbuf, int size)
{
#ifdef HAVE_POLL
    struct pollfd ufds[2];
    unsigned int  nfds;
#else /* !HAVE_POLL */
    fd_set rec;
    struct timeval t;
#endif /* HAVE_POLL */

    int nread  = 0;
    int  status;
    int retval = 0;
    unsigned char *ptr  = NULL;
    unsigned long block = 0;

    block = 1;

    if(java_block(outpipe[0],block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 5a. %d\n",errno);

	return;
    }

    if(java_block(errpipe[0],block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 6a. %d\n",errno);

	return;
    }

    *buf = '\0';

    if(size)
	ptr = fbuf;


#ifdef HAVE_POLL
    while((retval=waitpid(pid,&status,WNOHANG))!=pid)
    {
	if(retval==-1)
	    if(errno!=EINTR)
		break;

	ufds[0].fd = outpipe[0];
	ufds[1].fd = errpipe[0];
	ufds[0].events = POLLIN | POLLPRI;
	ufds[1].events = POLLIN | POLLPRI;
	nfds = 2;

	if(!(retval=poll(ufds,nfds,1)) || retval==-1)
	    continue;

	if(size && ((ufds[0].revents & POLLIN) || (ufds[0].revents & POLLPRI)))
	{
	    while((nread = read(outpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    if(nread >0)
	    {
		memcpy((void *)ptr,(const void *)buf,nread);
		ptr += nread;
	    }
	}


	if((ufds[1].revents & POLLIN) || (ufds[1].revents & POLLPRI))
	{
	    while((nread = read(errpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    if(nread > -1)
	    {
		buf[nread]='\0';
		ajStrAppendC(errstd,buf);
	    }
	}
    }


    ufds[0].fd = outpipe[0];
    ufds[1].fd = errpipe[0];
    ufds[0].events = POLLIN | POLLPRI;
    ufds[1].events = POLLIN | POLLPRI;
    nfds = 2;

    retval=poll(ufds,nfds,1);

    if(retval>0)
	if(size && ((ufds[0].revents & POLLIN) || (ufds[0].revents & POLLPRI)))
	{
	    while((nread = read(outpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    if(nread > 0)
	    {
		memcpy((void *)ptr,(const void *)buf,nread);
		ptr += nread;
	    }
	}

    retval=poll(ufds,nfds,1);

    if(retval>0)
	if((ufds[1].revents & POLLIN) || (ufds[1].revents & POLLPRI))
	{
	    while((nread = read(errpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    if(nread > -1)
	    {
		buf[nread]='\0';
		ajStrAppendC(errstd,buf);
	    }
	}
#else /* !HAVE_POLL */
    while((retval=waitpid(pid,&status,WNOHANG))!=pid)
    {
	if(retval==-1)
	    if(errno!=EINTR)
		break;

	FD_ZERO(&rec);
	FD_SET(outpipe[0],&rec);
	t.tv_sec = 0;
	t.tv_usec = 1000;
	select(outpipe[0]+1,&rec,NULL,NULL,&t);

	if(size && FD_ISSET(outpipe[0],&rec))
	{
	    *buf = '\0';
	    while((nread = read(outpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    if(nread>0)
	    {
		memcpy((void *)ptr,(const void *)buf,nread);
		ptr += nread;
	    }
	}

	FD_ZERO(&rec);
	FD_SET(errpipe[0],&rec);
	t.tv_sec = 0;
	t.tv_usec = 1000;
	select(errpipe[0]+1,&rec,NULL,NULL,&t);

	if(FD_ISSET(errpipe[0],&rec))
	{
	    *buf = '\0';

	    while((nread = read(errpipe[0],(void *)buf,JBUFFLEN))==-1
		  && errno==EINTR);

	    if(nread > -1)
	    {
		buf[nread]='\0';
		ajStrAppendC(errstd,buf);
	    }
	}


    }


    FD_ZERO(&rec);
    FD_SET(outpipe[0],&rec);
    t.tv_sec = 0;
    t.tv_usec = 0;
    select(outpipe[0]+1,&rec,NULL,NULL,&t);

    if(size && FD_ISSET(outpipe[0],&rec))
    {
	*buf = '\0';

	while((nread = read(outpipe[0],(void *)buf,JBUFFLEN))==-1
	      && errno==EINTR);

	if(nread>0)
	{
	    memcpy((void *)ptr,(const void *)buf,nread);
	    ptr += nread;
	}
    }


    FD_ZERO(&rec);
    FD_SET(errpipe[0],&rec);
    t.tv_sec = 0;
    t.tv_usec = 0;
    select(errpipe[0]+1,&rec,NULL,NULL,&t);

    if(FD_ISSET(errpipe[0],&rec))
    {
	*buf = '\0';
	while((nread = read(errpipe[0],(void *)buf,JBUFFLEN))==-1
	      && errno==EINTR);

	if(nread>-1)
	{
	    buf[nread]='\0';
	    ajStrAppendC(errstd,buf);
	}
    }
#endif /* HAVE_POLL */

    if(size)
	if(ptr-fbuf != size)
	    ajStrAppendC(errstd,"\nIncomplete file read\n");

    block = 0;

    if(java_block(outpipe[0],block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 7a. %d\n",errno);

	return;
    }

    if(java_block(errpipe[0],block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 8a. %d\n",errno);

	return;
    }

    return;
}
ID java_jembossctl
TY static
MO ajjava
LB core
XX
DE fork jembossctl and broker requests
XX
PN [1]
PA r command ajint
PD command type
PX
PN [2]
PA r username const AjPStr
PD username
PX
PN [3]
PA r password const AjPStr
PD password
PX
PN [4]
PA r environment const AjPStr
PD environment
PX
PN [5]
PA r str1 const AjPStr
PD command string. depends on command
PX
PN [6]
PA r str2 const AjPStr
PD command string. depends on command
PX
PN [7]
PA w outstd AjPStr*
PD stdout from jembossctl
PX
PN [8]
PA w errstd AjPStr*
PD stderr from jembossctl
PX
PN [9]
PA w fbuf unsigned char**
PD file buffer
PX
PN [10]
PA w fsize int*
PD file size
PX
RT int
RD 0=success -1=failure
RX
//


static int java_jembossctl(ajint command,
			   const AjPStr username, const AjPStr password,
			   const AjPStr environment,
			   const AjPStr str1, const AjPStr str2,
			   AjPStr *outstd, AjPStr *errstd,
			   unsigned char **fbuf, int *fsize)
{
    AjPStr dir    = NULL;
    AjPStr unused = NULL;

    AjPStr prog     = NULL;    /* Fork strings */
    AjPStr cl       = NULL;
    AjPStr clemboss = NULL;
    AjPStr envi     = NULL;

    int    i=0;


    const char *cuser;
    const char *cpass;

    char *buff  = NULL;
    int size = 0;

    int rlen = 0;

    char **argp = NULL;
    char **envp = NULL;
    int  pid;

    int  outpipe[2];
    int  errpipe[2];
    int  commpipe[2];
    int  tchan;
    int  rchan;

    int retval = 0;
    char c;
    int  n;

    cuser = ajStrGetPtr(username);
    cpass = ajStrGetPtr(password);

    unused   = ajStrNew();
    clemboss = ajStrNew();
    dir      = ajStrNew();
    cl       = ajStrNew();
    envi     = ajStrNew();
    prog     = ajStrNew();


    if(!(buff=(char*)malloc(JBUFFLEN)))
    {
	ajStrAppendC(errstd,"Malloc error\n");
	java_tidy_command(&unused,&cl,&clemboss,&dir,&envi,&prog);

	return -1;
    }



    /* Set environment for execve */
    ajStrAssignS(&envi,environment);
    envp = make_array(envi);

    /* Set program name */
    ajStrAssignC(&prog,"jembossctl");

    if(!ajSysFileWhichEnv(&prog,envp))
    {
	ajStrAppendC(errstd,"Cannot locate jembossctl\n");
	java_tidy_command2(&unused,&cl,&clemboss,&dir,&envi,&prog,buff);
	i = 0;

	while(envp[i])
	    AJFREE(envp[i++]);

	AJFREE(envp);

	return -1;
    }

    /* Setup commandline */
    ajFmtPrintS(&cl,"jembossctl");
    argp = make_array(cl);

    while(pipe(outpipe)==-1);
    while(pipe(errpipe)==-1);
    while(pipe(commpipe)==-1);

    tchan = commpipe[1];
    rchan = outpipe[0];




#if defined (__SVR4) && defined (__sun)
    pid = fork1();
#else /* !(__SVR4 && __sun) */
    pid = fork();
#endif /* __SVR4 && __sun */
    if(pid == -1)
    {
	ajStrAppendC(errstd,"Fork error\n");
	java_tidy_command3(&unused,&cl,&clemboss,&dir,&envi,&prog,buff,
			   commpipe,outpipe,errpipe);
	i = 0;

	while(argp[i])
	    AJFREE(argp[i]);

	AJFREE(argp);

	i = 0;

	while(envp[i])
	    AJFREE(envp[i]);

	AJFREE(envp);

	return -1;
    }



    if(!pid)			/* Child (jembossctl) prog */
    {
	dup2(commpipe[0],0);
	dup2(outpipe[1],1);
	dup2(errpipe[1],2);

        ajSysExecprogNowaitS(prog,argp,envp);
    }



    /* Don't need the env or cmdline for the parent */
    i = 0;

    while(argp[i])
        AJFREE(argp[i]);

    AJFREE(argp);

    i = 0;

    while(envp[i])
        AJFREE(envp[i]);

    AJFREE(envp);
    /* End of fork code */


    *buff = '\0';
    rlen = java_rcv(rchan,buff,errstd);

    if(rlen==-1)
    {
	ajStrAppendC(errstd,"Pipe Recv error 1\n");
	java_tidy_command3(&unused,&cl,&clemboss,&dir,&envi,&prog,buff,
			   commpipe,outpipe,errpipe);
	return -1;
    }

    if(rlen<2 || strncmp(buff,"OK",2))
    {
	ajStrAppendC(errstd,"Incorrect ACK error\n");
	java_tidy_command3(&unused,&cl,&clemboss,&dir,&envi,&prog,buff,
			   commpipe,outpipe,errpipe);
	return -1;
    }

    /* At this point pipe comms are active and commands can be sent */

    switch(command)
    {
    case COMM_AUTH:
	retval = java_send_auth(tchan,rchan,cuser,cpass,errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    case EMBOSS_FORK:
	ajStrAssignS(&clemboss,str1);
	ajStrAssignS(&dir,str2);
	retval = java_emboss_fork(tchan,cuser,cpass,clemboss,envi,dir,buff,
				  errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    case MAKE_DIRECTORY:
	ajStrAssignS(&dir,str1);
	retval = java_make_dir(tchan,cuser,cpass,dir,buff,errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    case DELETE_FILE:
	ajStrAssignS(&dir,str1);
	retval = java_delete_file(tchan,cuser,cpass,dir,buff,errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    case RENAME_FILE:
	ajStrAssignS(&dir,str1);
	ajStrAssignS(&clemboss,str2);
	retval = java_rename_file(tchan,cuser,cpass,dir,clemboss,buff,errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    case DELETE_DIR:
	ajStrAssignS(&dir,str1);
	retval = java_delete_dir(tchan,cuser,cpass,dir,buff,errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    case LIST_FILES:
	ajStrAssignS(&dir,str1);
	retval = java_list_files(tchan,cuser,cpass,dir,buff,errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    case LIST_DIRS:
	ajStrAssignS(&dir,str1);
	retval = java_list_dirs(tchan,cuser,cpass,dir,buff,errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    case GET_FILE:
	ajStrAssignS(&dir,str1);
	retval = java_get_file(tchan,rchan,cuser,cpass,dir,buff,fbuf,&size,
			       errstd);
	if(retval==-1)
	{
	    size = 0;
	    *fsize = 0;
	}
	else
	    *fsize = size;

	java_wait_for_file(pid,errstd,outpipe,errpipe,buff,*fbuf,size);
	break;

    case PUT_FILE:
	ajStrAssignS(&dir,str1);
	size = *fsize;
	retval = java_put_file(tchan,rchan,cuser,cpass,dir,buff,*fbuf,size,
			       errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    case BATCH_FORK:
	ajStrAssignS(&clemboss,str1);
	ajStrAssignS(&dir,str2);
	retval = java_batch_fork(tchan,cuser,cpass,clemboss,envi,dir,buff,
				  errstd);

	n = java_rcv(rchan,(char *)&c,errstd);

	if(n==-1 || !c)
	    ajStrAppendC(errstd,"Error receiving (java_batch)\n");
#ifdef __hpux
	signal(SIGCLD,SIG_IGN);
#endif /* __hpux */
#ifdef linux
	signal(SIGCLD,SIG_IGN);
#endif /* linux */
#if defined (__SVR4) && defined (__sun)
	signal(SIGCHLD,SIG_IGN);
#endif /* __SVR4 && __sun */
	break;

    case SEQ_ATTRIB:
	ajStrAssignS(&dir,str1);
	retval = java_seq_attrib(tchan,cuser,cpass,dir,buff,errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    case SEQSET_ATTRIB:
	ajStrAssignS(&dir,str1);
	retval = java_seqset_attrib(tchan,cuser,cpass,dir,buff,errstd);
	java_wait_for_term(pid,outstd,errstd,outpipe,errpipe,buff);
	break;

    default:
	break;
    }


    java_tidy_command3(&unused,&cl,&clemboss,&dir,&envi,&prog,buff,
		       commpipe,outpipe,errpipe);


    return retval;
}
ID java_tidy_command
TY static
MO ajjava
LB core
XX
DE Delete allocated memory
XX
PN [1]
PA w str1 AjPStr*
PD any string
PX
PN [2]
PA w str2 AjPStr*
PD any string
PX
PN [3]
PA w str3 AjPStr*
PD any string
PX
PN [4]
PA w str4 AjPStr*
PD any string
PX
PN [5]
PA w str5 AjPStr*
PD any string
PX
PN [6]
PA w str6 AjPStr*
PD any string
PX
RT void
RD 
RX
//


static void java_tidy_command(AjPStr *str1, AjPStr *str2, AjPStr *str3,
			      AjPStr *str4, AjPStr *str5, AjPStr *str6)
{

    ajStrDel(str1);
    ajStrDel(str2);
    ajStrDel(str3);
    ajStrDel(str4);
    ajStrDel(str5);
    ajStrDel(str6);

    return;
}
ID java_tidy_command2
TY static
MO ajjava
LB core
XX
DE Delete allocated memory
XX
PN [1]
PA w uniq AjPStr*
PD unique name
PX
PN [2]
PA w cl AjPStr*
PD command line
PX
PN [3]
PA w clemboss AjPStr*
PD emboss command line
PX
PN [4]
PA w dir AjPStr*
PD directory
PX
PN [5]
PA w envi AjPStr*
PD environment
PX
PN [6]
PA w prog AjPStr*
PD program name
PX
PN [7]
PA w buf char*
PD socket buffer
PX
RT void
RD 
RX
//


static void java_tidy_command2(AjPStr *uniq, AjPStr *cl, AjPStr *clemboss,
			       AjPStr *dir, AjPStr *envi, AjPStr *prog,
			       char *buf)
{

    java_tidy_command(uniq,cl,clemboss,dir,envi,prog);
    AJFREE(buf);

    return;
}
ID java_tidy_command3
TY static
MO ajjava
LB core
XX
DE Delete allocated memory and close pipes
XX
PN [1]
PA w uniq AjPStr*
PD unique name
PX
PN [2]
PA w cl AjPStr*
PD command line
PX
PN [3]
PA w clemboss AjPStr*
PD emboss command line
PX
PN [4]
PA w dir AjPStr*
PD directory
PX
PN [5]
PA w envi AjPStr*
PD environment
PX
PN [6]
PA w prog AjPStr*
PD program name
PX
PN [7]
PA w buf char*
PD socket buffer
PX
PN [8]
PA u commpipe int*
PD command pipe file descriptors
PX
PN [9]
PA u outpipe int*
PD stdout pipe
PX
PN [10]
PA u errpipe int*
PD stderr pipe
PX
RT void
RD 
RX
//


static void java_tidy_command3(AjPStr *uniq, AjPStr *cl, AjPStr *clemboss,
			       AjPStr *dir, AjPStr *envi, AjPStr *prog,
			       char *buf, int *commpipe,
			       int *outpipe, int *errpipe)
{

    close(commpipe[0]);
    close(commpipe[1]);
    close(outpipe[0]);
    close(outpipe[1]);
    close(errpipe[0]);
    close(errpipe[1]);

    java_tidy_command2(uniq,cl,clemboss,dir,envi,prog,buf);

    return;
}
ID Ajax.userAuth
TY public
MO ajjava
LB core
XX
DE Verify user and retrieve home directory
DE Loads outStd,errStd within java.
DE Sets 'home' in Java
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_userAuth
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;


    jclass jvc;
    jfieldID field;
    jstring  hstr;    /* Java returned strings */
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    jsize  plen;
    jbyte  *ca;

    AjPStr home=NULL;

    AjBool ok=ajFalse;
    int i;


    username = ajStrNew();
    password = ajStrNew();
    home     = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    jvc  = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca   = (*env)->GetByteArrayElements(env,key,0);


    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&home,&envi,&outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);


    if(!(jpass=(char *)malloc(plen+1)))
	return (unsigned char)ajFalse;

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);

    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&home,&envi,&outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);

    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&home,&envi,&outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    ok = ajFalse;

    if(!java_jembossctl(COMM_AUTH,username,password,envi,NULL,NULL,&outstd,
			&errstd,NULL,NULL))
    {
	ok = ajTrue;
	ajStrAssignS(&home,outstd);
    }


    field = (*env)->GetFieldID(env,jvc,"home","Ljava/lang/String;");
    hstr = (*env)->NewStringUTF(env,ajStrGetPtr(home));
    (*env)->SetObjectField(env,obj,field,hstr);


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    java_tidy_command(&username,&password,&home,&envi,&outstd,&errstd);


    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.forkBatch
TY public
MO ajjava
LB core
XX
DE Run an EMBOSS program detached
DE Loads outStd,errStd within java.
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r cline jstring
PD command line
PX
PN [7]
PA r direct jstring
PD directory to create
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_forkBatch
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring cline, jstring direct)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;

    jclass jvc = (*env)->GetObjectClass(env,obj);
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jcl   = NULL;
    const char   *jdir  = NULL;

    AjPStr commandline = NULL;
    AjPStr directory   = NULL;

    AjBool ok = ajFalse;
    jsize  plen;
    jbyte  *ca;
    int i;

    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    commandline = ajStrNew();
    directory   = ajStrNew();

    jvc = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca = (*env)->GetByteArrayElements(env,key,0);

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);


    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);

	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);


    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);


    jcl = (*env)->GetStringUTFChars(env,cline,0);

    if(jcl)
	ajStrAssignC(&commandline,jcl);
    else
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,cline,jcl);

    jdir = (*env)->GetStringUTFChars(env,direct,0);

    if(jdir)
	ajStrAssignC(&directory,jdir);
    else
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,direct,jdir);


    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    ok = ajFalse;

    if(!java_jembossctl(BATCH_FORK,username,password,envi,commandline,
			directory,&outstd,&errstd,NULL,NULL))
	ok = ajTrue;


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    java_tidy_command(&username,&password,&envi,&commandline,
		      &outstd,&errstd);
    ajStrDel(&directory);
    AJFREE(jpass);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.forkEmboss
TY public
MO ajjava
LB core
XX
DE Run an EMBOSS program
DE Loads outStd,errStd within java.
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r cline jstring
PD command line
PX
PN [7]
PA r direct jstring
PD directory to create
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_forkEmboss
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring cline, jstring direct)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;


    jclass jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jcl   = NULL;
    const char   *jdir  = NULL;

    AjPStr commandline = NULL;
    AjPStr directory   = NULL;

    AjBool ok = ajFalse;
    jsize  plen;
    jbyte  *ca;
    int i;


    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    commandline = ajStrNew();
    directory   = ajStrNew();

    plen = (*env)->GetArrayLength(env,key);
    ca   = (*env)->GetByteArrayElements(env,key,0);
    jvc  = (*env)->GetObjectClass(env,obj);

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);


    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);

	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);


    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);


    jcl = (*env)->GetStringUTFChars(env,cline,0);

    if(jcl)
	ajStrAssignC(&commandline,jcl);
    else
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,cline,jcl);

    jdir = (*env)->GetStringUTFChars(env,direct,0);

    if(jdir)
	ajStrAssignC(&directory,jdir);
    else
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }
    (*env)->ReleaseStringUTFChars(env,direct,jdir);


    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&commandline,
			  &outstd,&errstd);
	ajStrDel(&directory);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    ok = ajFalse;

    if(!java_jembossctl(EMBOSS_FORK,username,password,envi,commandline,
			directory,&outstd,&errstd,NULL,NULL))
	ok = ajTrue;


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    java_tidy_command(&username,&password,&envi,&commandline,
		      &outstd,&errstd);
    ajStrDel(&directory);
    AJFREE(jpass);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.makeDir
TY public
MO ajjava
LB core
XX
DE Create a directory
DE Loads outStd,errStd within java.
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r direct jstring
PD directory to create
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_makeDir
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring direct)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;


    jclass jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jdir  = NULL;

    AjPStr directory = NULL;

    AjBool ok = ajFalse;
    jsize  plen;
    jbyte  *ca;
    int i;


    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    directory  = ajStrNew();

    jvc  = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca   = (*env)->GetByteArrayElements(env,key,0);

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);


    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);

    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);

    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    jdir = (*env)->GetStringUTFChars(env,direct,0);

    if(jdir)
	ajStrAssignC(&directory,jdir);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,direct,jdir);

    ok = ajFalse;

    if(!java_jembossctl(MAKE_DIRECTORY,username,password,envi,directory,NULL,
			&outstd,&errstd,NULL,NULL))
	ok = ajTrue;


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    java_tidy_command(&username,&password,&envi,&directory,
		      &outstd,&errstd);
    AJFREE(jpass);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.delFile
TY public
MO ajjava
LB core
XX
DE Delete a file
DE Loads outStd,errStd within java.
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r filename jstring
PD file to delete
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_delFile
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring filename)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;

    jclass   jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jfil  = NULL;

    AjPStr file=NULL;

    AjBool ok = ajFalse;
    jsize  plen;
    jbyte  *ca;
    int i;


    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    file     = ajStrNew();

    jvc  = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca   = (*env)->GetByteArrayElements(env,key,0);

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);


    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);


    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);

    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    jfil = (*env)->GetStringUTFChars(env,filename,0);

    if(jfil)
	ajStrAssignC(&file,jfil);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,filename,jfil);




    ok = ajFalse;

    if(!java_jembossctl(DELETE_FILE,username,password,envi,file,NULL,
			&outstd,&errstd,NULL,NULL))
	ok = ajTrue;


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    java_tidy_command(&username,&password,&envi,&file,
		      &outstd,&errstd);
    AJFREE(jpass);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.renameFile
TY public
MO ajjava
LB core
XX
DE Rename a file
DE Loads outStd,errStd within java.
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r filename jstring
PD old filename
PX
PN [7]
PA r filename2 jstring
PD new filename
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_renameFile
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring filename, jstring filename2)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;


    jclass   jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jfil  = NULL;
    const char   *jfil2 = NULL;

    AjPStr file  = NULL;
    AjPStr file2 = NULL;

    AjBool ok = ajFalse;
    jsize  plen;
    jbyte  *ca;
    int i;

    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    file     = ajStrNew();


    jvc = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca = (*env)->GetByteArrayElements(env,key,0);

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);


    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);


    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);

    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    jfil = (*env)->GetStringUTFChars(env,filename,0);

    if(jfil)
	ajStrAssignC(&file,jfil);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,filename,jfil);


    file2 = ajStrNew();

    jfil2 = (*env)->GetStringUTFChars(env,filename2,0);

    if(jfil2)
	ajStrAssignC(&file2,jfil2);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	ajStrDel(&file2);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,filename2,jfil2);



    ok = ajFalse;
    if(!java_jembossctl(RENAME_FILE,username,password,envi,file,file2,
			&outstd,&errstd,NULL,NULL))
	ok = ajTrue;


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    java_tidy_command(&username,&password,&envi,&file,
		      &outstd,&errstd);
    ajStrDel(&file2);
    AJFREE(jpass);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.delDir
TY public
MO ajjava
LB core
XX
DE Recursively delete a directory
DE Loads outStd,errStd within java.
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r direct jstring
PD directory to delete
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_delDir
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring direct)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;


    jclass   jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jdir  = NULL;

    AjPStr directory = NULL;

    AjBool ok = ajFalse;
    jsize  plen;
    jbyte  *ca;
    int i;


    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    directory  = ajStrNew();


    jvc  = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca   = (*env)->GetByteArrayElements(env,key,0);

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);


    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);


    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);

    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    jdir = (*env)->GetStringUTFChars(env,direct,0);

    if(jdir)
	ajStrAssignC(&directory,jdir);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,direct,jdir);


    ok = ajFalse;

    if(!java_jembossctl(DELETE_DIR,username,password,envi,directory,NULL,
			&outstd,&errstd,NULL,NULL))
	ok = ajTrue;


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    java_tidy_command(&username,&password,&envi,&directory,
		      &outstd,&errstd);
    AJFREE(jpass);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.listFiles
TY public
MO ajjava
LB core
XX
DE Lists regular files
DE Loads outStd,errStd within java. outStd contains the list
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r direct jstring
PD directory to scan
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_listFiles
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring direct)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;


    jclass jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jdir  = NULL;

    AjPStr directory = NULL;

    AjBool ok=ajFalse;
    jsize  plen;
    jbyte  *ca;
    int i;


    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    directory  = ajStrNew();


    jvc  = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca   = (*env)->GetByteArrayElements(env,key,0);


    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);


    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);



    jenv = (*env)->GetStringUTFChars(env,environment,0);
    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);
	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);

    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    jdir = (*env)->GetStringUTFChars(env,direct,0);

    if(jdir)
	ajStrAssignC(&directory,jdir);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,direct,jdir);


    ok = ajFalse;

    if(!java_jembossctl(LIST_FILES,username,password,envi,directory,NULL,
			&outstd,&errstd,NULL,NULL))
	ok = ajTrue;


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    java_tidy_command(&username,&password,&envi,&directory,
		      &outstd,&errstd);
    AJFREE(jpass);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.listDirs
TY public
MO ajjava
LB core
XX
DE Lists files of type 'directory'
DE Loads outStd,errStd within java. outStd contains the list
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r direct jstring
PD directory to scan
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_listDirs
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring direct)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;


    jclass   jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jdir  = NULL;

    AjPStr directory = NULL;

    AjBool ok = ajFalse;
    jsize  plen;
    jbyte  *ca;
    int i;


    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    directory  = ajStrNew();

    jvc = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca = (*env)->GetByteArrayElements(env,key,0);

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);

    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);


    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);

    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    jdir = (*env)->GetStringUTFChars(env,direct,0);

    if(jdir)
	ajStrAssignC(&directory,jdir);
    else
    {
	java_tidy_command(&username,&password,&envi,&directory,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }
    (*env)->ReleaseStringUTFChars(env,direct,jdir);


    ok = ajFalse;

    if(!java_jembossctl(LIST_DIRS,username,password,envi,directory,NULL,
			&outstd,&errstd,NULL,NULL))
	ok = ajTrue;


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    if(ajStrGetLen(errstd))
	ok = ajFalse;

    java_tidy_command(&username,&password,&envi,&directory,
		      &outstd,&errstd);
    AJFREE(jpass);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.getFile
TY public
MO ajjava
LB core
XX
DE Get a user file
DE Loads outStd,errStd within java
DE Sets size (of file),fileok (success) & prnt ([non]printable]
DE variables in java
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r filename jstring
PD filename to get
PX
RT jbyteArray
RD contents of file
RX
//


JNIEXPORT jbyteArray JNICALL Java_org_emboss_jemboss_server_Ajax_getFile
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring filename)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;

    jclass jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    jbyteArray jb;

    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jfil  = NULL;

    AjPStr file=NULL;
    unsigned char *fbuf = NULL;
    int size   = 0;
    int prnt   = 1;
    int fileok = 0;
    int c;
    int i;

    AjBool ok=ajFalse;
    jsize  plen = (*env)->GetArrayLength(env,key);
    jbyte  *ca = (*env)->GetByteArrayElements(env,key,0);


    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    file     = ajStrNew();


    jvc  = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca   = (*env)->GetByteArrayElements(env,key,0);

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);

    (*env)->ReleaseStringUTFChars(env,door,juser);

    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);

	return NULL;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);


    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);

    (*env)->ReleaseStringUTFChars(env,environment,jenv);


    jfil = (*env)->GetStringUTFChars(env,filename,0);

    if(jfil)
	ajStrAssignC(&file,jfil);

    (*env)->ReleaseStringUTFChars(env,filename,jfil);




    ok = ajFalse;

    if(!java_jembossctl(GET_FILE,username,password,envi,file,NULL,
			&outstd,&errstd,&fbuf,&size))
	ok = ajTrue;



    fileok = 0;

    if(ok)
	fileok=1;

    if(!size)
    {
	fileok = 0;
	AJCNEW0(fbuf,1);
    }


    for(i=0;iGetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    field = (*env)->GetFieldID(env,jvc,"prnt","I");
    (*env)->SetIntField(env,obj,field,prnt);


    field = (*env)->GetFieldID(env,jvc,"size","I");
    (*env)->SetIntField(env,obj,field,size);



    field = (*env)->GetFieldID(env,jvc,"fileok","I");
    (*env)->SetIntField(env,obj,field,fileok);

    jb = (*env)->NewByteArray(env,size);
    (*env)->SetByteArrayRegion(env,jb,0,size,(jbyte *)fbuf);



    java_tidy_command(&username,&password,&envi,&file,
		      &outstd,&errstd);
    AJFREE(jpass);
    AJFREE(fbuf);

    return jb;
}
ID Ajax.putFile
TY public
MO ajjava
LB core
XX
DE Write a user file
DE Loads outStd,errStd within java
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r filename jstring
PD filename to create
PX
PN [7]
PA r arr jbyteArray
PD contents for the file
PX
RT jboolean
RD true if the file was written
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_putFile
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring filename, jbyteArray arr)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;


    jclass   jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    jsize    len;
    jbyte    *ba;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jfil  = NULL;

    AjPStr file = NULL;
    unsigned char *fbuf;
    int size;
    int i;

    AjBool ok = ajFalse;
    jsize  plen;
    jbyte  *ca;

    len = (*env)->GetArrayLength(env,arr);
    ba  = (*env)->GetByteArrayElements(env,arr,0);

    size = len;

    if(size)
	if(!(fbuf=(unsigned char *)malloc(size)))
	    return (unsigned char)ajFalse;

    for(i=0;iReleaseByteArrayElements(env,arr,ba,0);

    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    file     = ajStrNew();

    jvc  = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca   = (*env)->GetByteArrayElements(env,key,0);


    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	if(size)
	    AJFREE(fbuf);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);

    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	if(size)
	    AJFREE(fbuf);

	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);


    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	if(size)
	    AJFREE(fbuf);

	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);

    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	if(size)
	    AJFREE(fbuf);

	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    jfil = (*env)->GetStringUTFChars(env,filename,0);

    if(jfil)
	ajStrAssignC(&file,jfil);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	if(size)
	    AJFREE(fbuf);

	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,filename,jfil);


    ok = ajFalse;
    if(!java_jembossctl(PUT_FILE,username,password,envi,file,NULL,
			&outstd,&errstd,&fbuf,&size))
	ok = ajTrue;


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    if(size)
	AJFREE(fbuf);

    java_tidy_command(&username,&password,&envi,&file,
		      &outstd,&errstd);
    AJFREE(jpass);


    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.seqAttrib
TY public
MO ajjava
LB core
XX
DE Return sequence attributes
DE Loads outStd,errStd within java.
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r filename jstring
PD file to delete
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_seqAttrib
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring filename)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;


    jclass   jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jfil  = NULL;

    AjPStr file = NULL;

    AjBool ok = ajFalse;
    jsize  plen;
    jbyte  *ca;
    int i;
    ajint len    = 0;
    float weight = 0.;
    AjBool nuc = ajFalse;


    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    file     = ajStrNew();

    jvc  = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca   = (*env)->GetByteArrayElements(env,key,0);

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);


    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);


    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }
    (*env)->ReleaseStringUTFChars(env,environment,jenv);

    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    jfil = (*env)->GetStringUTFChars(env,filename,0);

    if(jfil)
	ajStrAssignC(&file,jfil);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,filename,jfil);




    ok = ajFalse;

    if(!java_jembossctl(SEQ_ATTRIB,username,password,envi,file,NULL,
			&outstd,&errstd,NULL,NULL))
	ok = ajTrue;



    /* Results are returned via a stdout string in outStd */
    ajFmtScanS(outstd,"%d%f%B",&len,&weight,&nuc);

    field = (*env)->GetFieldID(env,jvc,"length_soap","I");
    (*env)->SetIntField(env,obj,field,len);

    field = (*env)->GetFieldID(env,jvc,"protein_soap","Z");
    (*env)->SetBooleanField(env,obj,field,(unsigned char)!nuc);

    field = (*env)->GetFieldID(env,jvc,"weight_soap","F");
    (*env)->SetFloatField(env,obj,field,weight);

    ajStrAssignClear(&outstd);

    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    java_tidy_command(&username,&password,&envi,&file,
		      &outstd,&errstd);
    AJFREE(jpass);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID Ajax.seqsetAttrib
TY public
MO ajjava
LB core
XX
DE Return seqset attributes
DE Loads outStd,errStd within java.
XX
PN [1]
PA u env JNIEnv*
PD java environment
PX
PN [2]
PA r obj jobject
PD java object
PX
PN [3]
PA r door jstring
PD username
PX
PN [4]
PA r key jbyteArray
PD password
PX
PN [5]
PA r environment jstring
PD environment
PX
PN [6]
PA r filename jstring
PD usa
PX
RT jboolean
RD true if success
RX
//


JNIEXPORT jboolean JNICALL Java_org_emboss_jemboss_server_Ajax_seqsetAttrib
(JNIEnv *env, jobject obj, jstring door, jbyteArray key,
 jstring environment, jstring filename)
{
    AjPStr username = NULL;
    AjPStr password = NULL;
    AjPStr envi     = NULL;
    AjPStr outstd   = NULL;
    AjPStr errstd   = NULL;


    jclass   jvc;
    jfieldID field;
    jstring  ostr;
    jstring  estr;
    const char   *juser = NULL;
    char   *jpass = NULL;
    const char   *jenv  = NULL;
    const char   *jfil  = NULL;

    AjPStr file = NULL;

    AjBool ok = ajFalse;
    jsize  plen;
    jbyte  *ca;
    int i;
    ajint  len    = 0;
    float  weight = 0.;
    AjBool nuc = ajFalse;

    username = ajStrNew();
    password = ajStrNew();
    envi     = ajStrNew();
    outstd   = ajStrNew();
    errstd   = ajStrNew();

    file     = ajStrNew();


    jvc  = (*env)->GetObjectClass(env,obj);
    plen = (*env)->GetArrayLength(env,key);
    ca   = (*env)->GetByteArrayElements(env,key,0);

    juser = (*env)->GetStringUTFChars(env,door,0);

    if(juser)
	ajStrAssignC(&username,juser);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,door,juser);


    if(!(jpass=(char *)malloc(plen+1)))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);

	return (unsigned char)ajFalse;
    }

    bzero((void *)jpass,plen+1);

    for(i=0;iReleaseByteArrayElements(env,key,ca,0);


    jenv = (*env)->GetStringUTFChars(env,environment,0);

    if(jenv)
	ajStrAssignC(&envi,jenv);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,environment,jenv);

    if(!ajStrGetLen(username) || !ajStrGetLen(password) || !ajStrGetLen(envi))
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    jfil = (*env)->GetStringUTFChars(env,filename,0);

    if(jfil)
	ajStrAssignC(&file,jfil);
    else
    {
	java_tidy_command(&username,&password,&envi,&file,
			  &outstd,&errstd);
	AJFREE(jpass);

	return (unsigned char)ajFalse;
    }

    (*env)->ReleaseStringUTFChars(env,filename,jfil);




    ok = ajFalse;

    if(!java_jembossctl(SEQSET_ATTRIB,username,password,envi,file,NULL,
			&outstd,&errstd,NULL,NULL))
	ok = ajTrue;



    /* Results are returned via a stdout string in outStd */
    ajFmtScanS(outstd,"%d%f%B",&len,&weight,&nuc);

    field = (*env)->GetFieldID(env,jvc,"length_soap","I");
    (*env)->SetIntField(env,obj,field,len);

    field = (*env)->GetFieldID(env,jvc,"protein_soap","Z");
    (*env)->SetBooleanField(env,obj,field,(unsigned char)!nuc);

    field = (*env)->GetFieldID(env,jvc,"weight_soap","F");
    (*env)->SetFloatField(env,obj,field,weight);

    ajStrAssignClear(&outstd);


    field = (*env)->GetFieldID(env,jvc,"outStd","Ljava/lang/String;");
    ostr = (*env)->NewStringUTF(env,ajStrGetPtr(outstd));
    (*env)->SetObjectField(env,obj,field,ostr);

    field = (*env)->GetFieldID(env,jvc,"errStd","Ljava/lang/String;");
    estr = (*env)->NewStringUTF(env,ajStrGetPtr(errstd));
    (*env)->SetObjectField(env,obj,field,estr);


    java_tidy_command(&username,&password,&envi,&file,
		      &outstd,&errstd);
    AJFREE(jpass);

    if(!ok)
	return (unsigned char)ajFalse;

    return (unsigned char)ajTrue;
}
ID java_pipe_write
TY static
MO ajjava
LB core
XX
DE Write a byte stream down a file descriptor (unblocked)
XX
PN [1]
PA r tchan int
PD file descriptor
PX
PN [2]
PA r buf const char*
PD buffer to write
PX
PN [3]
PA r n int
PD number of bytes to write
PX
PN [4]
PA r seconds int
PD time-out
PX
PN [5]
PA w errstd AjPStr*
PD stderr
PX
RT int
RD 0=success  -1=failure
RX
//


static int java_pipe_write(int tchan, const char *buf, int n, int seconds,
			   AjPStr *errstd)
{
#ifdef HAVE_POLL
    struct pollfd ufds;
    unsigned int  nfds;
#else /* !HAVE_POLL */
    fd_set fdr;
    fd_set fdw;
    struct timeval tfd;
#endif /* HAVE_POLL */

    int  written;
    int  sent = 0;
    int  ret  = 0;
    const char *p;
    unsigned long block = 0;
    long then = 0;
    long now  = 0;
    struct timeval tv;

    gettimeofday(&tv,NULL);
    then = tv.tv_sec;


    block = 1;

    if(java_block(tchan,block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 9a. %d\n",errno);

	return -1;
    }


    p = buf;
    written = 0;

#ifdef HAVE_POLL
    while(written!=n)
    {
	gettimeofday(&tv,NULL);
	now = tv.tv_sec;

	if(now-then >= seconds)
	{
	    ajStrAppendC(errstd,"java_pipe_write timeout\n");

	    return -1;
	}

	/* Check pipe is writable */

	ufds.fd = tchan;
	ufds.events = POLLOUT;
	nfds = 1;
	ret=poll(&ufds,nfds,1);

	if(ret && ret!=-1 && (ufds.revents & POLLOUT))
	{
	    while((sent=write(tchan,p,n-(p-buf)))==-1 && errno==EINTR);

	    if(sent == -1)
	    {
		ajStrAppendC(errstd,"java_pipe_write send error\n");

		return -1;
	    }

	    written += sent;
	    p += sent;
	    gettimeofday(&tv,NULL);
	    then = tv.tv_sec;
	}
    }
#else /* !HAVE_POLL */
    while(written!=n)
    {
	gettimeofday(&tv,NULL);
	now = tv.tv_sec;

	if(now-then >= seconds)
	{
	    ajStrAppendC(errstd,"java_pipe_write timeout\n");

	    return -1;
	}

	/* Check pipe is writable */
	tfd.tv_sec  = 0;
	tfd.tv_usec = 1000;
	FD_ZERO(&fdw);
	FD_SET(tchan,&fdw);
	fdr = fdw;

	ret = select(tchan+1,&fdr,&fdw,NULL,&tfd);

	if(ret && ret!=-1 && FD_ISSET(tchan,&fdw))
	{
	    while((sent=write(tchan,p,n-(p-buf)))==-1 && errno==EINTR);

	    if(sent == -1)
	    {
		ajStrAppendC(errstd,"java_pipe_write send error\n");

		return -1;
	    }

	    written += sent;
	    p += sent;
	    gettimeofday(&tv,NULL);
	    then = tv.tv_sec;
	}
    }
#endif /* HAVE_POLL */

    block = 0;

    if(java_block(tchan,block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot block 10a. %d\n",errno);

	return -1;
    }

    return 0;
}
ID java_pipe_read
TY static
MO ajjava
LB core
XX
DE Read a byte stream from a file descriptor (unblocked)
XX
PN [1]
PA r rchan int
PD file descriptor
PX
PN [2]
PA w buf char*
PD buffer for read
PX
PN [3]
PA r n int
PD number of bytes to read
PX
PN [4]
PA r seconds int
PD time-out
PX
PN [5]
PA w errstd AjPStr*
PD stderr
PX
RT int
RD 0=success  -1=failure
RX
//


static int java_pipe_read(int rchan, char *buf, int n, int seconds,
			  AjPStr *errstd)
{
#ifdef HAVE_POLL
    struct pollfd ufds;
    unsigned int  nfds;
#else /* !HAVE_POLL */
    fd_set fdr;
    fd_set fdw;
    struct timeval tfd;
#endif /* HAVE_POLL */

    int  sum;
    int  got = 0;
    int  ret = 0;
    char *p;
    unsigned long block = 0;
    long then = 0;
    long now  = 0;
    struct timeval tv;

    gettimeofday(&tv,NULL);
    then = tv.tv_sec;


    block = 1;

    if(java_block(rchan,block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 11a. %d\n",errno);

	return -1;
    }

    p = buf;
    sum = 0;

#ifdef HAVE_POLL
    while(sum!=n)
    {
	gettimeofday(&tv,NULL);
	now = tv.tv_sec;

	if(now-then >= seconds)
	{
	    ajStrAppendC(errstd,"java_pipe_read timeout\n");

	    return -1;
	}

	/* Check pipe is readable */
	ufds.fd = rchan;
	ufds.events = POLLIN | POLLPRI;
	nfds = 1;

	ret=poll(&ufds,nfds,1);

	if(ret && ret!=-1)
	{
	    if((ufds.revents & POLLIN) || (ufds.revents & POLLPRI))
	    {
		while((got=read(rchan,p,n-(p-buf)))==-1 && errno==EINTR);

		if(got == -1)
		{
		    ajStrAppendC(errstd,"java_pipe_read read error\n");

		    return -1;
		}

		sum += got;
		p += got;
		gettimeofday(&tv,NULL);
		then = tv.tv_sec;
	    }
	}

    }
#else /* !HAVE_POLL */
    while(sum!=n)
    {
	gettimeofday(&tv,NULL);
	now = tv.tv_sec;

	if(now-then >= seconds)
	{
	    ajStrAppendC(errstd,"java_pipe_read timeout\n");

	    return -1;
	}

	/* Check pipe is readable */
	tfd.tv_sec  = 0;
	tfd.tv_usec = 1000;
	FD_ZERO(&fdr);
	FD_SET(rchan,&fdr);
	fdw = fdr;

	ret = select(rchan+1,&fdr,&fdw,NULL,&tfd);

	if(ret && ret!=-1 && FD_ISSET(rchan,&fdr))
	{
	    while((got=read(rchan,p,n-(p-buf)))==-1 && errno==EINTR);

	    if(got == -1)
	    {
		ajStrAppendC(errstd,"java_pipe_read read error\n");

		return -1;
	    }

	    sum += got;
	    p += got;
	    gettimeofday(&tv,NULL);
	    then = tv.tv_sec;
	}
    }
#endif /* HAVE_POLL */

    block = 0;

    if(java_block(rchan,block)==-1)
    {
	ajFmtPrintAppS(errstd,"Cannot unblock 12a. %d\n",errno);

	return -1;
    }

    return 0;
}
ID java_snd
TY static
MO ajjava
LB core
XX
DE Mimic socket write using pipes
XX
PN [1]
PA r tchan int
PD file descriptor
PX
PN [2]
PA r buf const char*
PD buffer to write
PX
PN [3]
PA r len int
PD number of bytes to write
PX
PN [4]
PA w errstd AjPStr*
PD stderr
PX
RT int
RD 0=success  -1=failure
RX
//


static int java_snd(int tchan,const char *buf,int len,AjPStr *errstd)
{

    if(java_pipe_write(tchan,(char *)&len,sizeof(int),TIMEOUT,errstd)==-1)
    {
	ajStrAppendC(errstd,"java_snd error\n");

	return -1;
    }

    if(java_pipe_write(tchan,buf,len,TIMEOUT,errstd)==-1)
    {
	ajStrAppendC(errstd,"java_snd error\n");

	return -1;
    }

    return 0;
}
ID java_rcv
TY static
MO ajjava
LB core
XX
DE Mimic socket read using pipes
XX
PN [1]
PA r rchan int
PD file descriptor
PX
PN [2]
PA w buf char*
PD buffer to write
PX
PN [3]
PA w errstd AjPStr*
PD stderr
PX
RT int
RD 0=success  -1=failure
RX
//


static int java_rcv(int rchan, char *buf, AjPStr *errstd)
{
    int len;

    if(java_pipe_read(rchan,(char *)&len,sizeof(int),TIMEOUT,errstd)==-1)
    {
	ajStrAppendC(errstd,"java_rcv error\n");

	return -1;
    }

    if(java_pipe_read(rchan,buf,len,TIMEOUT,errstd)==-1)
    {
	ajStrAppendC(errstd,"java_rcv error\n");

	return -1;
    }

    return len;
}
ID java_block
TY static
MO ajjava
LB core
XX
DE File descriptor block/unblock
XX
PN [1]
PA r chan int
PD file descriptor
PX
PN [2]
PA r flag unsigned long
PD block=1 unblock=0
PX
RT int
RD 0=success  -1=failure
RX
//


static int java_block(int chan, unsigned long flag)
{

    if(ioctl(chan,FIONBIO,&flag)==-1)
    {
#ifdef __sgi
	if(errno==ENOSYS)
	    return 0;
#endif /* __sgi */
#ifdef __hpux
	if(errno==ENOTTY)
	    return 0;
#endif /* __hpux */
	return -1;
    }

    return 0;
}
ID ajListNew
TY public
MO ajlist
LB core
XX
DE Creates a new general list.
XX
RT AjPList
RD new list;
RX
//


AjPList ajListNew(void)
{
    return listNew(NULL);
}
ID ajListNewListref
TY public
MO ajlist
LB core
XX
DE Copy a list.
DE
DE WARNING: pointers to the data are copied, NOT the data
DE so be careful when cleaning up after copy.
XX
PN [1]
PA r list const AjPList
PD list to be copied
PX
RT AjPList
RD new copied list.
RX
//


AjPList ajListNewListref(const AjPList list)
{
    AjPList newlist = NULL;

    AjPListNode node = NULL;

    if(!list)
        return NULL;

    newlist = ajListNew();

    for(node = list->First; node->Next; node = node->Next)
        ajListPushAppend(newlist, node->Item);


    return newlist;
}
ID ajListNewRef
TY public
MO ajlist
LB core
XX
DE Makes a reference-counted copy of any list
XX
PN [1]
PA u list AjPList
PD list to be given a new reference
PX
RT AjPList
RD new list;
RX
//


AjPList ajListNewRef(AjPList list)
{
    list->Use++;

    return list;
}
ID listNew
TY static
MO ajlist
LB core
XX
DE Creates a new list.
XX
PN [1]
PA f itemdel void function
PD Data destructor
PX
RT AjPList
RD new list;
RX
//


static AjPList listNew (void (*itemdel)(void** Pitem))
{
    AjPList list = NULL;

    AJNEW0(list);
    list->Fitemdel = itemdel;
    list->Last     = listDummyNode(&list->First);

#ifdef AJ_SAVESTATS
    listNodeCnt--;                      /* dummy */
    listNewCnt++;
#endif

    list->Use = 1;

    return list;
}
ID listInsertNode
TY static
MO ajlist
LB core
XX
DE Inserts a new node in a list at the current node position.
XX
PN [1]
PA u Pnode AjPListNode*
PD Current AJAX List Node address
PX
PN [2]
PA u item void*
PD Data item to insert.
PX
RT void
RD 
RX
//


static void listInsertNode(AjPListNode* Pnode, void* item)
{
    AjPListNode node = NULL;

    if(listFreeNext)
        node = listFreeSet[--listFreeNext];
    else
        AJNEW0(node);

    node->Item = item;
    node->Next = (*Pnode);
    node->Prev = (*Pnode)->Prev;

    node->Next->Prev = node;

    *Pnode = node;

#ifdef AJ_SAVESTATS
    listNodeCnt++;
#endif

    return;
}
ID listDummyNode
TY static
MO ajlist
LB core
XX
DE Creates a new empty node.
XX
PN [1]
PA u Pnode AjPListNode*
PD New AJAX List Node address
PX
RT AjPListNode
RD Copy of current node
RX
//


static AjPListNode listDummyNode(AjPListNode* Pnode)
{
    if(listFreeNext)
    {
        *Pnode = listFreeSet[--listFreeNext];
        (*Pnode)->Item = NULL;
        (*Pnode)->Prev = NULL;
        (*Pnode)->Next = NULL;
    }
    else
        AJNEW0(*Pnode);

#ifdef AJ_SAVESTATS
    listNodeCnt++;
#endif

    return *Pnode;
}
ID ajListPush
TY public
MO ajlist
LB core
XX
DE Add a new node at the start of the list and add the
DE data pointer.
XX
PN [1]
PA u list AjPList
PD list to be changed.
PX
PN [2]
PA u item void*
PD Pointer to data.
PX
RT void
RD 
RX
//


void ajListPush(AjPList list, void* item)
{
    assert(list);

    listInsertNode(&list->First, item);

    if(!list->Count++)
        list->Last->Prev = list->First;

#ifdef AJ_SAVESTATS
    if(list->Count > listMaxNum)
        listMaxNum = list->Count;
#endif

    return;
}
ID ajListPushAppend
TY public
MO ajlist
LB core
XX
DE Add a new node at the end of the list and add the
DE data pointer.
XX
PN [1]
PA u list AjPList
PD List to be changed.
PX
PN [2]
PA u item void*
PD Pointer to data to append.
PX
RT void
RD 
RX
//


void ajListPushAppend(AjPList list, void* item)
{
    /*
    ** cannot use listInsertNode because that needs a pointer to the
    ** penultimate node, so we use the dummy node and make a new dummy node
    ** instead
    */
    AjPListNode tmp = NULL;

    assert(list);

    if(!list->Count)
    {
        ajListPush(list, item);
        return;
    }

    list->Last->Item = item;

    tmp = list->Last;
    list->Last = listDummyNode(&list->Last->Next);
    list->Last->Prev = tmp;

    list->Count++;

#ifdef AJ_SAVESTATS
    if(list->Count > listMaxNum)
        listMaxNum = list->Count;
#endif

    return;
}
ID ajListPushlist
TY public
MO ajlist
LB core
XX
DE Adds a list to the start of the current list, then deletes the second list.
XX
PN [1]
PA u list AjPList
PD List.
PX
PN [2]
PA d Plist AjPList*
PD AJAX List address to be merged.
PX
RT void
RD 
RX
//


void ajListPushlist(AjPList list, AjPList* Plist)
{
    AjPList more = *Plist;

    if(more->Count)
    {
        /* more list has items */
        if(list->Count)
        {
            /* master list has items */
            more->Last->Item = list->First->Item;
            more->Last->Next = list->First->Next;
            list->First->Next->Prev = more->Last;
        }
        else
        {
            /* master list is empty */
            list->Last = more->Last;
        }

        if(listFreeNext >= listFreeMax)
            listFreeSetExpand();

        if(listFreeNext >= listFreeMax)
            AJFREE(list->First);
        else if(list->First)
            listFreeSet[listFreeNext++] = list->First;

        list->First = more->First;
        list->Count += more->Count;
        list->First->Prev = NULL;
        more->First = NULL;
        more->Count=0;

#ifdef AJ_SAVESTATS
        if(list->Count > listMaxNum)
            listMaxNum = list->Count;
#endif
    }

    ajListFree(Plist);  /* free the list but not the nodes */

    return;
}
ID ajListMap
TY public
MO ajlist
LB core
XX
DE For each node in the list call function apply.
XX
PN [1]
PA u list AjPList
PD List.
PX
PN [2]
PA f apply void function
PD Function to call for each list item.
PX
PN [3]
PA u cl void*
PD Standard, usually NULL.
PX
RT void
RD 
RX
//


void ajListMap(AjPList list, void (*apply)(void** Pitem, void* cl), void* cl)
{
    AjPListNode rest;

    assert(apply);

    for(rest = list->First; rest->Next; rest = rest->Next)
        (*apply)(&rest->Item, cl);

    return;
}
ID ajListPurge
TY public
MO ajlist
LB core
XX
DE Garbage collect a list
XX
PN [1]
PA u list AjPList
PD List.
PX
PN [2]
PA f test AjBool function
PD Function to test whether to delete
PX
PN [3]
PA f itemdel void function
PD Wrapper function for item destructor
PX
RT void
RD 
RX
//


void ajListPurge(AjPList list,
                 AjBool (*test)(const void* item),
                 void (*itemdel)(void** Pitem))
{
    void* item = NULL;

    AjIList iter = NULL;


    iter = ajListIterNew(list);

    while((item = ajListIterGet(iter)))
        if((*test)(item))
        {
            (*itemdel)(&item);
            ajListIterRemove(iter);
        }


    ajListIterDel(&iter);

    return;
}
ID ajListReverse
TY public
MO ajlist
LB core
XX
DE Reverse the order of the nodes in an abstract list.
XX
PN [1]
PA u list AjPList
PD List
PX
RT void
RD 
RX
//


void ajListReverse(AjPList list)
{
    AjPListNode head     = NULL;
    AjPListNode savenext = NULL;
    AjPListNode node     = NULL;

    if(!list)
        return;

    if(list->Count <= 1)
        return;

    head = list->Last;

    list->Last->Prev = list->First;

    for(node = list->First; node->Next; node = savenext)
    {
        savenext   = node->Next;
        node->Prev = node->Next;
        node->Next = head;
        head       = node;
    }

    list->First = head;

    list->First->Prev = NULL;

    return;
}
ID ajListSort
TY public
MO ajlist
LB core
XX
DE Sort the items in a list.
XX
PN [1]
PA u list AjPList
PD List.
PX
PN [2]
PA f compar1 int function
PD Function to compare two list items.
PX
RT void
RD 
RX
//


void ajListSort(AjPList list,
                int (*compar1)(const void* item1,
                               const void* item2))
{
    void** array = NULL;

    ajulong i = 0UL;

    AjPListNode node = NULL;

    /*ajDebug("ajListSort %Lu items\n", list->Count);*/
    /*ajListTrace(list);*/

    node = list->First;

    if(list->Count <= 1)
        return;

    ajListToarray(list, &array);
    /* listArrayTrace(array);*/

    qsort(array, (size_t) list->Count, sizeof(void*), compar1);

    while(node->Next)
    {
        node->Item = array[i++];
        node = node->Next;
    }

    AJFREE(array);

    return;
}
ID ajListSortTwo
TY public
MO ajlist
LB core
XX
DE Sort the items in a list using 2 fields in the same object hierarchy.
XX
PN [1]
PA u list AjPList
PD List.
PX
PN [2]
PA f compar1 int function
PD 1st function to compare two list items.
PX
PN [3]
PA f compar2 int function
PD 2nd function to compare two list items.
PX
RT void
RD 
RX
//


void ajListSortTwo(AjPList list,
                   int (*compar1)(const void* item1,
                                  const void* item2),
                   int (*compar2)(const void* item1,
                                  const void* item2))
{
    AjPListNode node = NULL;

    void** items = NULL;

    ajulong len   = 0UL;
    ajulong limit = 0UL;
    ajulong pos   = 0UL;
    ajulong base  = 0UL;
    ajulong n     = 0UL;

    node = list->First;

    ajListSort(list, compar1);

    len = ajListToarray(list, &items);

    if(len < 2)
        return;

    pos = base = 0UL;
    limit = len - 2;

    while(pos < limit)
    {
        while((*compar1)(&items[pos], &items[pos + 1]) == 0)
        {
            ++pos;

            if(pos > limit)
                break;
        }
        ++pos;

        n = pos - base;

        if(n > 1)
	  qsort((void*) &items[base], (size_t) n, sizeof(void*), compar2);

        base = pos;
    }

    pos = 0UL;

    while(node->Next)
    {
        node->Item = items[pos++];
        node = node->Next;
    }

    AJFREE(items);

    return;
}
ID ajListSortTwoThree
TY public
MO ajlist
LB core
XX
DE Sort the items in a list using 3 fields in the same object hierarchy.
XX
PN [1]
PA u list AjPList
PD List.
PX
PN [2]
PA f compar1 int function
PD 1st function to compare two list items.
PX
PN [3]
PA f compar2 int function
PD 2nd function to compare two list items.
PX
PN [4]
PA f compar3 int function
PD 3rd function to compare two list items.
PX
RT void
RD 
RX
//


void ajListSortTwoThree(AjPList list,
                        int (*compar1)(const void* item1,
                                       const void* item2),
                        int (*compar2)(const void* item1,
                                       const void* item2),
                        int (*compar3)(const void* item1,
                                       const void* item2))
{
    AjPListNode node = NULL;

    void** items = NULL;

    ajulong len   = 0UL;
    ajulong limit = 0UL;
    ajulong pos   = 0UL;
    ajulong base  = 0UL;
    ajulong n     = 0UL;

    node = list->First;
    len = ajListGetLength(list);

    if(len < 2)
        return;

    ajListSortTwo(list, compar1, compar2);

    len = ajListToarray(list, &items);

    pos = base = 0UL;
    limit = len - 2;

    while(pos < limit)
    {
        while(((*compar1)(&items[pos],&items[pos+1]) == 0) &&
              ((*compar2)(&items[pos],&items[pos+1]) == 0))
        {
            ++pos;

            if(pos > limit)
                break;
        }

        ++pos;
        n = pos - base;

        if(n > 1)
	  qsort((void*) &items[base], (size_t) n, sizeof(void*), compar3);

        base = pos;
    }

    pos = 0UL;

    while(node->Next)
    {
        node->Item = items[pos++];
        node = node->Next;
    }

    AJFREE(items);

    return;
}
ID ajListSortTwoUnique
TY public
MO ajlist
LB core
XX
DE Double-sort the items in a list, and remove duplicates
XX
PN [1]
PA u list AjPList
PD List.
PX
PN [2]
PA f compar1 int function
PD Function to compare two list items.
PX
PN [3]
PA f compar2 int function
PD Function to compare two list items.
PX
PN [4]
PA f itemdel void function
PD Function to delete an item
PX
RT void
RD 
RX
//


void ajListSortTwoUnique(AjPList list,
                         int (*compar1)(const void* item1,
                                        const void* item2),
                         int (*compar2)(const void* item1,
                                        const void* item2),
                         void (*itemdel)(void** Pitem,
                                         void* cl))
{
    void* item     = NULL;
    void* previtem = NULL;

    AjIList iter = NULL;

    ajDebug("ajListSortTwoUnique %Lu items\n", list->Count);

    if(list->Count <= 1)                /* no duplicates */
        return;

    ajListSortTwo(list, compar1, compar2);
    /*ajListTrace(list);*/

    iter = ajListIterNew(list);

    while(!ajListIterDone(iter))
    {
        item = ajListIterGet(iter);

        if(previtem &&
           ((*compar1)(&item, &previtem) == 0) &&
           ((*compar2)(&item, &previtem) == 0))
        {
            (*itemdel)(&item, NULL);
            ajListIterRemove(iter);
        }
        else
            previtem = item;
    }

    ajListIterDel(&iter);

    ajDebug("ajListUnique result %Lu items\n", list->Count);
    /*ajListTrace(list);*/

    return;
}
ID ajListSortUnique
TY public
MO ajlist
LB core
XX
DE Sort the items in a list, and remove duplicates
XX
PN [1]
PA u list AjPList
PD List.
PX
PN [2]
PA f compar1 int function
PD Function to compare two list items.
PX
PN [3]
PA f itemdel void function
PD Function to delete an item
PX
RT void
RD 
RX
//


void ajListSortUnique(AjPList list,
                      int (*compar1)(const void* item1,
                                     const void* item2),
                      void (*itemdel)(void** Pitem,
                                      void* cl))
{
    void* item     = NULL;
    void* previtem = NULL;

    AjIList iter = NULL;

    ajDebug("ajListSortUnique %Lu items\n", list->Count);

    if(list->Count <= 1)                /* no duplicates */
        return;

    ajListSort(list, compar1);
    /*ajListTrace(list);*/

    iter = ajListIterNew(list);

    while(!ajListIterDone(iter))
    {
        item = ajListIterGet(iter);

        if(previtem && ((*compar1)(&item, &previtem) == 0))
        {
            (*itemdel)(&item, NULL);
            ajListIterRemove(iter);
        }
        else
            previtem = item;
    }

    ajListIterDel(&iter);

    ajDebug("ajListSortUnique result %d items\n", list->Count);
    /*ajListTrace(list);*/

    return;
}
ID listFreeSetExpand
TY static
MO ajlist
LB core
XX
DE Expand the list of free nodes
XX
RT void
RD 
RX
//


static void listFreeSetExpand(void)
{
    ajulong newsize;

    if(!listFreeSet)
    {
        listFreeMax = 1024;
        AJCNEW0(listFreeSet,listFreeMax);

        return;
    }

    if(listFreeMax >= 65536)
        return;

    newsize = listFreeMax + listFreeMax;
    AJCRESIZE0(listFreeSet, (size_t) listFreeMax, (size_t) newsize);
    listFreeMax = newsize;

    return;
}
ID ajListDrop
TY public
MO ajlist
LB core
XX
DE Removes a data value from a list if the address matches data for a node
XX
PN [1]
PA u list AjPList
PD List
PX
PN [2]
PA r item void*
PD Data item
PX
RT void*
RD Dropped data item.
RX
//


void* ajListDrop(AjPList list, void* item)
{
    void* ret = NULL;

    AjIList iter = NULL;

    ajDebug("ajListDrop %p\n", item);

    iter = ajListIterNewBack(list);

    while(!ajListIterDoneBack(iter))
    {
        ret = ajListIterGetBack(iter);

        ajDebug("test %p\n", ret);

        if(ret == item)
        {
            ajListIterRemove(iter);
            ajListIterDel(&iter);
            return ret;
        }
    }
    ajListIterDel(&iter);

    return NULL;
}
ID ajListPop
TY public
MO ajlist
LB core
XX
DE remove the first node but set pointer to data first.
XX
PN [1]
PA u list AjPList
PD List
PX
PN [2]
PA w Pitem void**
PD pointer to pointer to data
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListPop(AjPList list, void** Pitem)
{
    if(!list)
    {
        if(Pitem)
            *Pitem = NULL;

        return ajFalse;
    }

    if(Pitem)
        *Pitem = listNodeItem(list->First);

    if(!listNodeDel(&list->First))
        return ajFalse;

    list->First->Prev = NULL;
    list->Count--;

    return ajTrue;
}
ID ajListPopLast
TY public
MO ajlist
LB core
XX
DE remove the last node but set pointer to data first.
XX
PN [1]
PA u list AjPList
PD List
PX
PN [2]
PA w Pitem void**
PD pointer to pointer to data
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListPopLast(AjPList list, void** Pitem)
{
    AjPListNode node = NULL;

    if(!list)
    {
        if(Pitem)
            *Pitem = NULL;

        return ajFalse;
    }


    if(!list->Count)
        return ajFalse;

    node = list->Last->Prev;

    if(Pitem)
        *Pitem = listNodeItem(node);


    if(list->Count==1)
    {
        list->Last->Prev = NULL;

        if(listFreeNext >= listFreeMax)
            listFreeSetExpand();

        if(listFreeNext >= listFreeMax)
            AJFREE(list->First);
        else if(list->First)
            listFreeSet[listFreeNext++] = list->First;

        list->First = list->Last;
    }
    else
    {
        node->Prev->Next = list->Last;
        list->Last->Prev = node->Prev;

        if(listFreeNext >= listFreeMax)
            listFreeSetExpand();

        if(listFreeNext >= listFreeMax)
            AJFREE(node);
        else if(node)
        {
            listFreeSet[listFreeNext++] = node;
            node = NULL;
        }
    }

    --list->Count;

    return ajTrue;
}
ID ajListGetLength
TY public
MO ajlist
LB core
XX
DE get the number of nodes in the linked list.
XX
PN [1]
PA r list const AjPList
PD List
PX
RT ajulong
RD Number of nodes in list.
RX
//


ajulong ajListGetLength(const AjPList list)
{
    if(!list)
        return 0UL;

    return list->Count;
}
ID ajListMapfind
TY public
MO ajlist
LB core
XX
DE For each node in the list call function 'apply' and return
DE ajTrue when any node is matched by the function.
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA f apply AjBool function
PD Function to call to test each list item.
PX
PN [3]
PA u cl void*
PD Standard, usually NULL.
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListMapfind(const AjPList list,
                     AjBool (*apply)(void** Pitem, void* cl), void* cl)
{
    AjPListNode listnode = NULL;

    assert(list);

    assert(apply);

    for( listnode = list->First; listnode->Next; listnode = listnode->Next)
        if((*apply)(&listnode->Item, cl))
            return ajTrue;

    return ajFalse;
}
ID ajListMapread
TY public
MO ajlist
LB core
XX
DE For each node in the list call function apply.
DE The apply function must not modify the list elements.
XX
PN [1]
PA r list const AjPList
PD List.
PX
PN [2]
PA f apply void function
PD Function to call for each list item.
PX
PN [3]
PA u cl void*
PD Standard, usually NULL.
PX
RT void
RD 
RX
//


void ajListMapread(const AjPList list,
                   void (*apply)(void* item, void* cl), void* cl)
{
    AjPListNode rest = NULL;

    assert(apply);

    for(rest = list->First; rest->Next; rest = rest->Next)
        (*apply)(rest->Item, cl);

    return;
}
ID ajListPeek
TY public
MO ajlist
LB core
XX
DE Return the first node but keep it on the list
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA w Pitem void**
PD pointer to pointer to data
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListPeek(const AjPList list, void** Pitem)
{
    if(!list)
        return ajFalse;

    if(!list->Count)
        return ajFalse;

    if(Pitem)
        *Pitem = listNodeItem(list->First);

    return ajTrue;
}
ID ajListPeekFirst
TY public
MO ajlist
LB core
XX
DE Set pointer to first node's data. Does NOT remove the first node.
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA w Pitem void**
PD pointer to pointer to data
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListPeekFirst(const AjPList list, void** Pitem)
{
    if(!list)
        return ajFalse;

    if(!list->Count)
        return ajFalse;

    if(Pitem)
        *Pitem = listNodeItem(list->First);

    return ajTrue;
}
ID ajListPeekLast
TY public
MO ajlist
LB core
XX
DE Set pointer to last node's data. Does NOT remove the last node.
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA w Pitem void**
PD pointer to pointer to data
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListPeekLast(const AjPList list, void** Pitem)
{
    AjPListNode rest = NULL;

    if(!list)
        return ajFalse;

    if(!list->Count)
        return ajFalse;

    if(list->Last)
        rest = list->Last->Prev;
    else
        rest = list->First;

    if(Pitem)
        *Pitem = listNodeItem(rest);

    return ajTrue;
}
ID ajListPeekNumber
TY public
MO ajlist
LB core
XX
DE Set pointer to last node's nth data item. 0 <= n < number of elements.
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA r ipos ajulong
PD element of the list
PX
PN [3]
PA w Pitem void**
PD pointer to pointer to data
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListPeekNumber(const AjPList list, ajulong ipos, void** Pitem)
{
    ajulong len = 0UL;
    ajulong i   = 0UL;

    AjPListNode rest = NULL;

    if(!list)
        return ajFalse;

    len = ajListGetLength(list);

    if(ipos>=len)
        return ajFalse;

    for(i = 0Ul, rest = list->First; i < ipos ; rest = rest->Next)
        i++;

    if(Pitem)
        *Pitem = listNodeItem(rest);

    return ajTrue;
}
ID ajListToarray
TY public
MO ajlist
LB core
XX
DE Create an array of the pointers to the data.
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA w array void***
PD Array of pointers to list items.
PX
RT ajulong
RD Size of array of pointers.
RX
//


ajulong ajListToarray(const AjPList list, void*** array)
{
    ajulong i = 0UL;
    ajulong n = 0UL;

    AjPListNode rest = NULL;

    n    = list->Count;
    rest = list->First;

    if(!n)
    {
        *array = NULL;

        return 0UL;
    }

    if(*array)
        AJFREE(*array);

    *array = AJALLOC((size_t)(n + 1) * sizeof(array));

    for(i = 0UL; i < n; i++)
    {
        (*array)[i] = rest->Item;
        rest = rest->Next;
    }

    (*array)[n] = NULL;

    return n;
}
ID listArrayTrace
TY static
MO ajlist
LB core
XX
DE Writes debug messages to trace an array generated from a list.
XX
PN [1]
PA r array void**
PD Array to trace
PX
RT void
RD 
RX
//


static void listArrayTrace(void** array)
{
    void** v = array;

    ajuint i = 0UL;

    while(*v)
        ajDebug("array[%Lu] %p\n", i++, *v++);

    return;
}
ID ajListToindex
TY public
MO ajlist
LB core
XX
DE Create an array of the pointers to the data.
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA w lind ajuint*
PD Populated index array to be sorted
PX
PN [3]
PA f compar1 int function
PD Function to compare two list items.
PX
RT ajulong
RD Size of index array.
RX
//


ajulong ajListToindex(const AjPList list, ajuint* lind,
                     int (*compar1)(const void* item1, const void* item2))
{
    ajulong n = 0UL;
    ajulong s = 0UL;
    ajulong i = 0UL;
    ajlong  j = 0L;
    ajuint  it = 0U;
    ajulong lt = 0UL;

    AjPListNode* nodes = NULL;
    ajulong* idx = NULL;

    n = list->Count;

    if(!n)
        return 0UL;

    ajListToarray(list, (void***) &nodes);
    AJCNEW0(idx, n);

    for(i = 0UL; i < n; i++)
        idx[i] = i;

    for(s = n / 2; s > 0; s /= 2)
        for(i = s; i < n; ++i)
        {
            for(j = i - s;
                j >= 0 && ((*compar1)(&nodes[idx[j]], &nodes[idx[j+s]]) > 0);
                j -= s)
            {
                it = lind[j];
                lind[j] = lind[j + s];
                lind[j + s] = it;
                lt = idx[j];
                idx[j] = idx[j + s];
                idx[j + s] = lt;
            }
        }

    AJFREE(nodes);
    AJFREE(idx);

    return n;
}
ID ajListProbe
TY public
MO ajlist
LB core
XX
DE Test list for memory allocation conflicts
XX
PN [1]
PA r Plist AjPList const*
PD List
PX
RT void
RD 
RX
//


void ajListProbe(AjPList const* Plist)
{
    AjPList list = NULL;

    AjPListNode  next = NULL;
    AjPListNode* rest = NULL;

    if(!Plist)
        return;

    if(!*Plist)
        return;

    list = *Plist;
    rest = &list->First;

    /* don't free the data in the list (we don't know how) */
    /* just free the nodes */
    if(list->Count)
        for(; (*rest)->Next; *rest = next)
        {
            next = (*rest)->Next;
            AJMPROBE(*rest);
        }

    AJMPROBE(*rest);
    AJMPROBE(*Plist);

    return;
}
ID ajListProbeData
TY public
MO ajlist
LB core
XX
DE Test list and data for memory allocation conflicts
XX
PN [1]
PA r Plist AjPList const*
PD List
PX
RT void
RD 
RX
//


void ajListProbeData(AjPList const* Plist)
{
    AjPListNode  next = NULL;
    AjPListNode* rest = NULL;

    AjPList list = NULL;

    if(!Plist)
        return;

    if(!*Plist)
        return;

    list = *Plist;
    rest = &list->First;


    /* free the data for each node (just a simple free) */
    /* as we free the nodes */

    if(list->Count)
    {
        for(; (*rest)->Next; *rest = next)
        {
            AJMPROBE((*rest)->Item);
            next = (*rest)->Next;
            AJMPROBE(*rest);
        }
        AJMPROBE((*rest)->Item);
    }

    AJMPROBE(*rest);
    AJMPROBE(*Plist);

    return;
}
ID ajListTrace
TY public
MO ajlist
LB core
XX
DE Traces through a list and validates it
XX
PN [1]
PA r list const AjPList
PD list to be traced.
PX
RT void
RD 
RX
CA output
CT AjPList
CD Traces through a list and validates it
CX
//


void ajListTrace(const AjPList list)
{
    ajulong i = 0UL;

    AjPListNode node = NULL;

    if(!list)
        return;

    ajDebug("\nList Trace %p count %d\n",
            list, list->Count);
    ajDebug("first-> %p last-> %p\n", list->First, list->Last);

    for(node = list->First; node->Next; node = node->Next)
    {
        i++;
        ajDebug("Item[%Lu] item %p (data %p) rest -> %p prev -> %p\n",
                i, node, node->Item, node->Next, node->Prev);
    }

    if(i != list->Count)
    {
        ajErr("*** list error expect %Lu items, found %Lu",
              list->Count, i);
    }

    if(list->Last != node)
    {
        ajDebug("*** list error expect end at %p, found at %p\n",
                list->Last, node);
        ajErr("*** list error expect end at %p, found at %p",
              list->Last, node);
    }

    return;
}
ID listNodesTrace
TY static
MO ajlist
LB core
XX
DE Writes debug messages to trace from the current list node.
XX
PN [1]
PA r node const AjPListNode
PD Current node.
PX
RT void
RD 
RX
//


static void listNodesTrace(const AjPListNode node)
{
    ajDebug("listNodesTrace %p\n", node);

    while(node->Next)
    {
        ajDebug("node %p item %p -> %p\n", node, node->Item, node->Next);
        node = node->Next;
    }

    return;
}
ID listNodeItem
TY static
MO ajlist
LB core
XX
DE Return the data item for a list node.
XX
PN [1]
PA r node const AjPListNode
PD Current node.
PX
RT void*
RD Data item.
RX
//


static void* listNodeItem(const AjPListNode node)
{
    if(!node || !node->Next)
        return NULL;

    return node->Item;
}
ID ajListFree
TY public
MO ajlist
LB core
XX
DE Free all nodes in the list.
DE NOTE: The data is only freed with a specified list type.
DE For undefined data types we recommend you to
DE use ajListMap with a routine to free the memory.
XX
PN [1]
PA d Plist AjPList*
PD AJAX List address
PX
RT void
RD 
RX
//


void ajListFree(AjPList* Plist)
{
    AjPList list = NULL;

    AjPListNode  next = NULL;
    AjPListNode* rest = NULL;

    if(!Plist)
        return;

    if(!*Plist)
        return;

#ifdef AJ_SAVESTATS
    listDelCnt++;
#endif

    list = *Plist;

    if(!list->Use)
        ajErr("trying to delete unused list");

    --list->Use;

    if(!list->Use)
    {                                   /* any other references? */
        rest = &list->First;

        /* don't free the data in the list (we don't know how) */
        /* just free the nodes */
        if(list->Count)
            for(; (*rest)->Next; *rest = next)
            {
                next = (*rest)->Next;

                if(listFreeNext >= listFreeMax)
                    listFreeSetExpand();

                if(listFreeNext >= listFreeMax)
                    AJFREE(*rest);
                else if(*rest)
                {
                    listFreeSet[listFreeNext++] = *rest;
                    *rest = NULL;
                }
            }

        if(listFreeNext >= listFreeMax)
            listFreeSetExpand();

        if(listFreeNext >= listFreeMax)
            AJFREE(*rest);
        else if(*rest)
        {
            listFreeSet[listFreeNext++] = *rest;
            *rest = NULL;
        }

        AJFREE(*Plist);
    }

    *Plist = NULL;

    return;
}
ID ajListFreeData
TY public
MO ajlist
LB core
XX
DE Free all nodes in the list. Free all the data values.
DE For more complex data objects use ajListMap with a routine to
DE free the object memory.
XX
PN [1]
PA d Plist AjPList*
PD AJAX List address
PX
RT void
RD 
RX
//


void ajListFreeData(AjPList* Plist)
{
    AjPList list = NULL;

    AjPListNode  next = NULL;
    AjPListNode* rest = NULL;

    if(!Plist)
        return;

    if(!*Plist)
        return;

#ifdef AJ_SAVESTATS
    listDelCnt++;
#endif

    list = *Plist;

    if(!list->Use)
        ajErr("trying to delete unused list");

    --list->Use;

    if(!list->Use)
    {                                   /* any other references? */
        rest = &list->First;

        /* free the data for each node (just a simple free) */
        /* as we free the nodes */

        if(list->Count)
        {
            for(; (*rest)->Next; *rest = next)
            {
                AJFREE((*rest)->Item);
                next = (*rest)->Next;

                if(listFreeNext >= listFreeMax)
                    listFreeSetExpand();

                if(listFreeNext >= listFreeMax)
                    AJFREE(*rest);
                else if(*rest)
                {
                    listFreeSet[listFreeNext++] = *rest;
                    *rest = NULL;
                }
            }
            AJFREE((*rest)->Item);
        }

        if(listFreeNext >= listFreeMax)
            listFreeSetExpand();

        if(listFreeNext >= listFreeMax)
            AJFREE(*rest);
        else if(*rest)
        {
            listFreeSet[listFreeNext++] = *rest;
            *rest = NULL;
        }

        AJFREE(*Plist);
    }

    *Plist = NULL;

    return;
}
ID listDelStr
TY static
MO ajlist
LB core
XX
DE Delete a string object value
XX
PN [1]
PA d Pstr void**
PD AJAX String object to be deleted
PX
RT void
RD 
RX
//


static void listDelStr(void** Pstr)
{
    if(!Pstr)
        return;

    if(!*Pstr)
        return;

    ajStrDel((AjPStr*) Pstr);

    return;
}
ID listNodeDel
TY static
MO ajlist
LB core
XX
DE Remove a node from the list.
XX
PN [1]
PA d Pnode AjPListNode*
PD Current AJAX List Node address
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool listNodeDel(AjPListNode* Pnode)
{
    AjPListNode node = NULL;
    AjPListNode tmp  = NULL;

    node = *Pnode;

    if(!node || !node->Next)
        return ajFalse;

    tmp = node->Prev;
    node = node->Next;
    node->Prev = tmp;

    if(listFreeNext >= listFreeMax)
        listFreeSetExpand();

    if(listFreeNext >= listFreeMax)
        AJFREE(*Pnode);
    else if(*Pnode)
        listFreeSet[listFreeNext++] = *Pnode;

    *Pnode = node;

    return ajTrue;
}
ID ajListUnused
TY public
MO ajlist
LB core
XX
DE Dummy function to catch all unused functions defined in ajlist
XX
PN [1]
PA r array void**
PD Array needed by ajListArrayTrace
PX
RT void
RD 
RX
//


void ajListUnused(void** array)
{
    const AjPListNode node = NULL;
    listNodesTrace(node);
    listArrayTrace(array);

    return;
}
ID ajListExit
TY public
MO ajlist
LB core
XX
DE Prints a summary of list usage with debug calls
XX
RT void
RD 
RX
//


void ajListExit(void)
{
    ajulong i = 0UL;

#ifdef AJ_SAVESTATS
    ajDebug("List usage : %d opened, %d closed, %Ld maxsize %Ld nodes\n",
            listNewCnt, listDelCnt, listMaxNum, listNodeCnt);
    ajDebug("List iterator usage : %d opened, %d closed\n",
            listIterNewCnt, listIterDelCnt);
#endif
    if(listFreeNext)
        for(i = 0UL; i < listFreeNext; i++)
            AJFREE(listFreeSet[i]);


    if(listFreeSet)
        AJFREE(listFreeSet);

    listFreeNext = 0UL;
    listFreeMax  = 0UL;

    return;
}
ID ajListIterNew
TY public
MO ajlist
LB core
XX
DE Creates an iterator to operate from start to end of list.
XX
PN [1]
PA u list AjPList
PD List
PD Not const in practice - the iterator can insert
PD and delete entries
PX
RT AjIList
RD New list iterator
RX
//


AjIList ajListIterNew(AjPList list)
{
    AjIList iter = NULL;

    if(!list)
        return NULL;

    AJNEW0(iter);
    iter->Head = list;
    iter->Back = ajFalse;
    iter->Here = list->First;
    iter->Modify = ajTrue;

#ifdef AJ_SAVESTATS
    listIterNewCnt++;
#endif

    return iter;
}
ID ajListIterNewBack
TY public
MO ajlist
LB core
XX
DE Creates an iterator to operate from end to start of the list.
XX
PN [1]
PA u list AjPList
PD List
PD Not const - the iterator can insert and delete entries
PX
RT AjIList
RD New list iterator
RX
//


AjIList ajListIterNewBack(AjPList list)
{
    AjIList iter = NULL;

    AjPListNode node = NULL;
    AjPListNode tmp  = NULL;

    if(!list)
        return NULL;

    if(!list->Count)
        return NULL;

    for(node=list->First; node->Next; node = node->Next)
        tmp = node;

    list->Last->Prev = tmp;

    AJNEW0(iter);
    iter->Head = list;
    iter->Back = ajTrue;
    iter->Here = tmp->Next;
    iter->Modify = ajTrue;

#ifdef AJ_SAVESTATS
    listIterNewCnt++;
#endif

    return iter;
}
ID ajListIterNewread
TY public
MO ajlist
LB core
XX
DE Creates an iterator to operate from start to end of read-only list.
XX
PN [1]
PA r list const AjPList
PD List
PD Not const in practice - the iterator can insert
PD and delete entries
PX
RT AjIList
RD New list iterator
RX
//


AjIList ajListIterNewread(const AjPList list)
{
    AjIList iter = NULL;

    if(!list)
        return NULL;

    AJNEW0(iter);
    iter->ReadHead = list;
    iter->Back = ajFalse;
    iter->Here = list->First;
    iter->Modify = ajFalse;

#ifdef AJ_SAVESTATS
    listIterNewCnt++;
#endif

    return iter;
}
ID ajListIterNewreadBack
TY public
MO ajlist
LB core
XX
DE Creates an iterator to operate from end to start of the list.
XX
PN [1]
PA r list const AjPList
PD List
PX
RT AjIList
RD New list iterator
RX
//


AjIList ajListIterNewreadBack(const AjPList list)
{
    AjIList iter = NULL;

    AjPListNode node = NULL;
    AjPListNode tmp  = NULL;

    if(!list)
        return NULL;

    if(!list->Count)
        return NULL;

    for(node=list->First; node->Next; node = node->Next)
        tmp = node;

    list->Last->Prev = tmp;

    AJNEW0(iter);
    iter->ReadHead = list;
    iter->Back = ajTrue;
    iter->Here = tmp->Next;
    iter->Modify = ajFalse;

#ifdef AJ_SAVESTATS
    listIterNewCnt++;
#endif

    return iter;
}
ID ajListIterDone
TY public
MO ajlist
LB core
XX
DE Tests whether an iterator has completed yet.
XX
PN [1]
PA r iter const AjIList
PD List iterator.
PX
RT AjBool
RD ajTrue if the iterator is exhausted.
RX
//


AjBool ajListIterDone(const AjIList iter)
{
    AjPListNode node = NULL;

    if(!iter)
        return ajTrue;

    node = iter->Here;

    if(!iter->Back)
    {
        if(!node->Next)
            return ajTrue;
    }
    else
        if(!node->Next->Next || !node->Next->Next->Next)
            return ajTrue;

    return ajFalse;
}
ID ajListIterDoneBack
TY public
MO ajlist
LB core
XX
DE Tests whether a backwards iterator has completed yet.
XX
PN [1]
PA r iter const AjIList
PD List iterator.
PX
RT AjBool
RD ajTrue if the iterator is exhausted.
RX
//


AjBool ajListIterDoneBack(const AjIList iter)
{
    AjPListNode node = NULL;

    if(!iter)
        return ajTrue;

    node = iter->Here;

    if(!node->Prev)
        return ajTrue;

    return ajFalse;
}
ID ajListIterDel
TY public
MO ajlist
LB core
XX
DE Destructor for a list iterator.
XX
PN [1]
PA d iter AjIList*
PD List iterator.
PX
RT void
RD 
RX
//


void ajListIterDel(AjIList* iter)
{
    AJFREE(*iter);

#ifdef AJ_SAVESTATS
    listIterDelCnt++;
#endif

    return;
}
ID ajListIterGet
TY public
MO ajlist
LB core
XX
DE Returns next item using iterator, or steps off the end.
XX
PN [1]
PA u iter AjIList
PD List iterator.
PX
RT void*
RD Data item returned.
RX
//


void* ajListIterGet(AjIList iter)
{
    void* item = NULL;

    AjPListNode node = NULL;

    if(!iter)
        return NULL;

    node = iter->Here;

    if(!iter->Back)
    {
        if(!node->Next)
            return NULL;

        item = node->Item;
        iter->Here = node->Next;
    }
    else
    {
        if(!node->Next->Next || !node->Next->Next->Next)
            return NULL;

        iter->Back = ajFalse;
        item = node->Next->Item;
        iter->Here = node->Next->Next;
    }

    return item;
}
ID ajListIterGetBack
TY public
MO ajlist
LB core
XX
DE Returns next item using back iterator.
XX
PN [1]
PA u iter AjIList
PD List iterator.
PX
RT void*
RD Data item returned.
RX
//


void* ajListIterGetBack(AjIList iter)
{
    void* item = NULL;

    AjPListNode node = NULL;

    if(!iter)
        return NULL;

    node = iter->Here;

    if(!node->Prev)
        return NULL;

    if(!iter->Back && node->Prev->Prev)
    {
        item = node->Prev->Prev->Item;
        iter->Here = node->Prev->Prev;
        iter->Back = ajTrue;
    }
    else
    {
        item = node->Prev->Item;
        iter->Here = node->Prev;
    }

    return item;
}
ID ajListIterInsert
TY public
MO ajlist
LB core
XX
DE Insert an item in a list, using an iterator (if not null)
DE to show which position to insert. Otherwise, simply push.
XX
PN [1]
PA u iter AjIList
PD List iterator.
PX
PN [2]
PA u item void*
PD Data item to insert.
PX
RT void
RD 
RX
//


void ajListIterInsert(AjIList iter, void* item)
{
    AjPList list = iter->Head;

    AjPListNode node = NULL;

    /* ajDebug("ajListInsert\n");*/

    if(!iter->Modify)
    {
        ajErr("ajListIterInsert called on a read-only iterator");
        return;
    }

    node = iter->Here;

    if(!iter->Back)
    {
        if(!node->Prev)
            listInsertNode(&list->First, item);
        else
            listInsertNode(&node->Prev->Next, item);

        iter->Here = node->Prev;
    }
    else
    {
        if(!node->Next)
            ajFatal("Cannot add a new node for unused back iterator\n");

        if(!node->Prev)
            listInsertNode(&list->First, item);
        else
            listInsertNode(&node->Prev->Next, item);
    }

    list->Count++;
#ifdef AJ_SAVESTATS
    if(list->Count > listMaxNum)
        listMaxNum = list->Count;
#endif

    /*ajListTrace(list);*/
    /*ajListIterTrace(iter);*/

    return;
}
ID ajListIterRemove
TY public
MO ajlist
LB core
XX
DE Remove an item from a list, using an iterator (if not null)
DE to show which item. Otherwise remove the first item.
DE
DE We want to remove the item just fetched by the iterator.
XX
PN [1]
PA u iter AjIList
PD List iterator.
PX
RT void
RD 
RX
//


void ajListIterRemove(AjIList iter)
{
    AjPListNode node = NULL;

    /* ajDebug("ajListIterRemove\n");*/

    if(!iter->Modify)
    {
        ajDie("Attempt to modify read-only iterator with ajListIterRemove\n");
        return;
    }

    node = iter->Here;

    if(!iter->Back)
    {
        if(!node->Prev)
            ajDie("Attempt to delete from unused iterator\n");

        if(!node->Prev->Prev)
            listNodeDel(&(iter->Head->First));
        else
            listNodeDel(&node->Prev->Prev->Next);
    }
    else
        listNodeDel(&node->Prev->Prev->Next);


    iter->Head->Count--;

    return;
}
ID ajListIterRewind
TY public
MO ajlist
LB core
XX
DE Resets iterator to start position
XX
PN [1]
PA u iter AjIList
PD List iterator.
PX
RT void
RD 
RX
//


void ajListIterRewind(AjIList iter)
{
    const AjPList list = NULL;

    AjPListNode node = NULL;
    AjPListNode tmp  = NULL;

    if(!iter)
        return;

    if(iter->Modify)
        list = iter->Head;
    else
        list = iter->ReadHead;

    if(iter->Back)
    {
        for(node=list->First; node->Next; node = node->Next)
            tmp = node;

        iter->Here = tmp->Next;
    }
    else
    {
        iter->Here = list->First;
    }

    return;
}
ID ajListIterTrace
TY public
MO ajlist
LB core
XX
DE Traces a list iterator and validates it.
XX
PN [1]
PA r iter const AjIList
PD list iterator to be traced.
PX
RT void
RD 
RX
//


void ajListIterTrace(const AjIList iter)
{
    ajulong icount = 0;

    if(!iter)
    {
        ajDebug("\nIterator NULL\n");
        return;
    }

    if(iter->Head)
        icount = iter->Head->Count;
    else if(iter->ReadHead)
        icount = iter->ReadHead->Count;

    ajDebug("\nIter Head %p ReadHead %p Here %p Back %B Modify %B Len: %Ld\n",
            iter->Head,iter->ReadHead,iter->Here,iter->Back,
            iter->Modify, icount);

    return;
}
ID ajListstrNew
TY public
MO ajlist
LB core
XX
DE Creates a new string list.
XX
RT AjPList
RD new list;
RX
//


AjPList ajListstrNew(void)
{
    return listNew(&listDelStr);
}
ID ajListstrNewList
TY public
MO ajlist
LB core
XX
DE Copy a list, with copies of all the string values.
DE
DE General data cannot be copied so this is a string-only function
XX
PN [1]
PA r list const AjPList
PD list to be copied
PX
RT AjPList
RD new copied list.
RX
//


AjPList ajListstrNewList(const AjPList list)
{
    AjPList newlist = NULL;

    AjPListNode node = NULL;

    AjPStr tmpstr = NULL;

    if(!list)
        return NULL;

    newlist = listNew(&listDelStr);
    newlist->Fitemdel = list->Fitemdel;

    for( node=list->First; node->Next; node=node->Next)
    {
        tmpstr = ajStrNewS(node->Item);
        ajListstrPushAppend(newlist, tmpstr);
    }


    return newlist;
}
ID ajListstrNewListref
TY public
MO ajlist
LB core
XX
DE Copy a string list.
DE
DE WARNING: pointers to the data are copied, NOT the data
DE so be careful when cleaning up after copy.
XX
PN [1]
PA r list const AjPList
PD List to be copied
PX
RT AjPList
RD New, copied, list.
RX
//


AjPList ajListstrNewListref(const AjPList list)
{
    return ajListNewListref(list);
}
ID ajListstrPush
TY public
MO ajlist
LB core
XX
DE Add a new node at the start of a string list.
XX
PN [1]
PA u list AjPList
PD list to be changed.
PX
PN [2]
PA u str AjPStr
PD String data.
PX
RT void
RD 
RX
//


void ajListstrPush(AjPList list, AjPStr str)
{
    ajListPush(list, (void*) str);

    return;
}
ID ajListstrPushAppend
TY public
MO ajlist
LB core
XX
DE Add a new node at the end of the list and add the
DE data pointer.
XX
PN [1]
PA u list AjPList
PD List to be changed.
PX
PN [2]
PA u str AjPStr
PD String to append.
PX
RT void
RD 
RX
//


void ajListstrPushAppend(AjPList list, AjPStr str)
{
    ajListPushAppend(list, (void*) str);

    return;
}
ID ajListstrPushlist
TY public
MO ajlist
LB core
XX
DE Adds a list to the start of the current list, then deletes the second list.
XX
PN [1]
PA u list AjPList
PD List.
PX
PN [2]
PA d Plist AjPList*
PD AJAX List address to be merged.
PX
RT void
RD 
RX
//


void ajListstrPushlist(AjPList list, AjPList* Plist)
{
    ajListPushlist(list, Plist);

    return;
}
ID ajListstrMap
TY public
MO ajlist
LB core
XX
DE For each node in the list call function apply,
DE with the address of the string and a client pointer.
XX
PN [1]
PA u list AjPList
PD List.
PX
PN [2]
PA f apply void function
PD Function to call for each list item.
PX
PN [3]
PA u cl void*
PD Standard, usually NULL.
PX
RT void
RD 
RX
//


void ajListstrMap(AjPList list,
                  void (*apply)(AjPStr* str, void* cl), void* cl)
{
    AjPListNode rest = NULL;

    assert(apply);

    for(rest = list->First; rest->Next; rest = rest->Next)
        (*apply)((AjPStr*) &rest->Item, cl);

    return;
}
ID ajListstrReverse
TY public
MO ajlist
LB core
XX
DE Reverse the order of the nodes in a string list.
XX
PN [1]
PA u list AjPList
PD List
PX
RT void
RD 
RX
//


void ajListstrReverse(AjPList list)
{
    ajListReverse(list);

    return;
}
ID ajListstrPop
TY public
MO ajlist
LB core
XX
DE Remove the first node but set pointer to data first.
XX
PN [1]
PA u list AjPList
PD List
PX
PN [2]
PA w Pstr AjPStr*
PD String
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListstrPop(AjPList list, AjPStr* Pstr)
{
    if(!list)
        return ajFalse;

    if(Pstr)
    {
        ajStrDel(Pstr);
        *Pstr = (AjPStr) listNodeItem(list->First);
    }

    if(!listNodeDel(&list->First))
        return ajFalse;

    list->First->Prev = NULL;
    list->Count--;

    return ajTrue;
}
ID ajListstrPopLast
TY public
MO ajlist
LB core
XX
DE Remove the last node but set pointer to data first.
XX
PN [1]
PA u list AjPList
PD List
PX
PN [2]
PA w Pstr AjPStr*
PD String
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListstrPopLast(AjPList list, AjPStr* Pstr)
{
    AjPListNode node = NULL;

    if(!list)
        return ajFalse;

    if(!list->Count)
        return ajFalse;

    node = list->Last->Prev;

    if(Pstr)
    {
        ajStrDel(Pstr);
        *Pstr = (AjPStr) listNodeItem(node);
    }

    if(list->Count == 1)
    {
        list->Last->Prev = NULL;

        if(listFreeNext >= listFreeMax)
            listFreeSetExpand();

        if(listFreeNext >= listFreeMax)
            AJFREE(list->First);
        else if(list->First)
            listFreeSet[listFreeNext++] = list->First;

        list->First = list->Last;
    }
    else
    {
        node->Prev->Next = list->Last;
        list->Last->Prev = node->Prev;

        if(listFreeNext >= listFreeMax)
            listFreeSetExpand();

        if(listFreeNext >= listFreeMax)
            AJFREE(node);
        else if(node)
        {
            listFreeSet[listFreeNext++] = node;
            node = NULL;
        }
    }

    --list->Count;

    return ajTrue;
}
ID ajListstrGetLength
TY public
MO ajlist
LB core
XX
DE get the number of nodes in the linked list.
XX
PN [1]
PA r list const AjPList
PD List
PX
RT ajulong
RD Number of nodes in list.
RX
//


ajulong ajListstrGetLength(const AjPList list)
{
    return ajListGetLength(list);
}
ID ajListstrGetMemsize
TY public
MO ajlist
LB core
XX
DE get the memory used nodes in the linked string list.
XX
PN [1]
PA r list const AjPList
PD List
PX
RT ajulong
RD Number of bytes allocated in list.
RX
//


ajulong ajListstrGetMemsize(const AjPList list)
{
    ajulong lbytes = 0L;
    AjPListNode node = NULL;
    AjPStr tmpstr;

    if(!list)
        return lbytes;

    for(node = list->First; node->Next; node=node->Next)
    {
        tmpstr = (AjPStr) listNodeItem(node);
        lbytes += (sizeof(*node) + sizeof(*tmpstr) + tmpstr->Res);
    }

    lbytes += sizeof(*node);
    lbytes += sizeof(*list);

    return lbytes;
}
ID ajListstrMapfind
TY public
MO ajlist
LB core
XX
DE For each node in the list call function apply and return
DE ajTrue when any node is matched by the function.
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA f apply AjBool function
PD Function to call to test each list item.
PX
PN [3]
PA u cl void*
PD Standard, usually NULL.
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListstrMapfind(const AjPList list,
                        AjBool (*apply)(AjPStr* Pstr, void* cl),
                        void* cl)
{
    AjPListNode listnode = NULL;

    assert(list);

    assert(apply);

    for(listnode = list->First; listnode->Next; listnode = listnode->Next)
        if((*apply)((AjPStr*) &listnode->Item, cl))
            return ajTrue;

    return ajFalse;
}
ID ajListstrMapread
TY public
MO ajlist
LB core
XX
DE For each node in the list call function apply,
DE with the address of the string and a client pointer.
DE The apply function must not modify the list elements.
XX
PN [1]
PA r list const AjPList
PD List.
PX
PN [2]
PA f apply void function
PD Function to call for each list item.
PX
PN [3]
PA u cl void*
PD Standard, usually NULL.
PX
RT void
RD 
RX
//


void ajListstrMapread(const AjPList list,
                      void (*apply)(AjPStr str, void* cl), void* cl)
{
    AjPListNode rest = NULL;

    assert(apply);

    for(rest = list->First; rest->Next; rest = rest->Next)
        (*apply)((AjPStr) rest->Item, cl);


    return;
}
ID ajListstrPeek
TY public
MO ajlist
LB core
XX
DE Return the first node but keep it on the list.
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA w Pstr AjPStr*
PD String
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajListstrPeek(const AjPList list, AjPStr* Pstr)
{
    if(!list)
        return ajFalse;

    if(!list->Count)
        return ajFalse;

    if(Pstr)
        *Pstr = (AjPStr) listNodeItem(list->First);

    return ajTrue;
}
ID ajListstrToarray
TY public
MO ajlist
LB core
XX
DE create an array of the pointers to the data.
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA w array AjPStr**
PD Array of Strings.
PX
RT ajulong
RD Size of array of pointers.
RX
//


ajulong ajListstrToarray(const AjPList list, AjPStr** array)
{
    ajulong i = 0UL;
    ajulong n = 0UL;

    AjPListNode rest = NULL;

    n = list->Count;
    rest = list->First;

    if(!n)
    {
        *array = NULL;

        return 0UL;
    }

    *array = AJALLOC((size_t) (n + 1) * sizeof(array));

    for(i = 0UL; i < n; i++)
    {
        (*array)[i] = (AjPStr) rest->Item;
        rest = rest->Next;
    }

    (*array)[n] = (AjPStr) NULL;

    return n;
}
ID ajListstrToarrayAppend
TY public
MO ajlist
LB core
XX
DE append to an array of the pointers to the data.
XX
PN [1]
PA r list const AjPList
PD List
PX
PN [2]
PA w array AjPStr**
PD Array of Strings.
PX
RT ajulong
RD Size of array of pointers.
RX
//


ajulong ajListstrToarrayAppend(const AjPList list, AjPStr** array)
{
    ajulong i = 0UL;
    ajulong n = 0UL;
    ajulong j = 0UL;

    AjPListNode rest = NULL;

    rest = list->First;

    if(*array)
    {
        for(j = 0UL; array[j]; j++)
            continue;
    }
    else
        j = 0UL;

    n = list->Count + j;

    if(!n)
    {
        *array = NULL;

        return 0UL;
    }

    AJCRESIZE(*array, (size_t) (n + 1));

    for(i = j; i < n; i++)
    {
        (*array)[i] = (AjPStr) rest->Item;
        rest = rest->Next;
    }

    (*array)[n] = (AjPStr) NULL;

    return n;
}
ID ajListstrTrace
TY public
MO ajlist
LB core
XX
DE Traces through a string list and validates it
XX
PN [1]
PA r list const AjPList
PD list to be traced.
PX
RT void
RD 
RX
//


void ajListstrTrace(const AjPList list)
{
    ajulong i = 0UL;

    AjPListNode node = NULL;

    if(!list)
        return;

    ajDebug("\nList Trace %p count %Lu\n",
            list, list->Count);
    ajDebug("rest-> %p last-> %p\n",
            list->First, list->Last);

    for(node = list->First; node->Next; node = node->Next)
    {
        i++;
        ajDebug("Item[%Lu] item %p '%S' rest -> %p prev -> %p\n",
                i, node, (AjPStr) node->Item, node->Next, node->Prev);
    }

    if(i != list->Count)
    {
        ajErr("*** list error expect %Lu items, found %Lu",
              list->Count, i);
    }

    if(list->Last != node)
    {
        ajErr("*** list error expect end at %p, found at %p",
              list->Last, node);
    }

    return;
}
ID ajListstrFree
TY public
MO ajlist
LB core
XX
DE Free the list. Do not attempt to free the nodes.
DE For use where the node data has been saved elsewhere, for example
DE by ajListToarray or where the list is a temporary structure
DE referring to permanent data.
XX
PN [1]
PA d Plist AjPList*
PD AJAX List address
PX
RT void
RD 
RX
//


void ajListstrFree(AjPList* Plist)
{
    ajListFree(Plist);

    return;
}
ID ajListstrFreeData
TY public
MO ajlist
LB core
XX
DE Free all nodes in a string list.
DE Also deletes all the strings. If these are to be preserved,
DE use ajListstrDel instead.
XX
PN [1]
PA d Plist AjPList*
PD AJAX List address
PX
RT void
RD 
RX
//


void ajListstrFreeData(AjPList* Plist)
{
    AjPList list = NULL;

    AjPListNode  next = NULL;
    AjPListNode* rest = NULL;

    if(!Plist)
        return;

    if(!*Plist)
        return;

#ifdef AJ_SAVESTATS
    listDelCnt++;
#endif

    list = *Plist;
    rest = &list->First;

    if(list->Count)
    {
        /* free the data in the list (if we know how) */
        for(; (*rest)->Next; *rest = next)
        {
            next = (*rest)->Next;
            ajStrDel((AjPStr*) &(*rest)->Item);

            if(listFreeNext >= listFreeMax)
                listFreeSetExpand();

            if(listFreeNext >= listFreeMax)
                AJFREE(*rest);
            else if(*rest)
            {
                listFreeSet[listFreeNext++] = *rest;
                *rest=NULL;
            }
        }

        ajStrDel((AjPStr*) &(*rest)->Item);
    }


    if(listFreeNext >= listFreeMax)
        listFreeSetExpand();

    if(listFreeNext >= listFreeMax)
        AJFREE(*rest);
    else if(*rest)
    {
        listFreeSet[listFreeNext++] = *rest;
        *rest = NULL;
    }

    AJFREE(*Plist);

    return;
}
ID ajListstrIterGet
TY public
MO ajlist
LB core
XX
DE Returns next item using iterator, or steps off the end.
XX
PN [1]
PA u iter AjIList
PD List iterator.
PX
RT AjPStr
RD Data item returned.
RX
//


AjPStr ajListstrIterGet(AjIList iter)
{
    void* item = NULL;

    AjPListNode node = NULL;

    if(!iter)
        return NULL;

    node = iter->Here;

    if(!iter->Back)
    {
        if(!node->Next)
            return NULL;

        item = node->Item;
        iter->Here = node->Next;
    }
    else
    {
        if(!node->Next->Next || !node->Next->Next->Next)
            return NULL;

        iter->Back = ajFalse;
        item = node->Next->Item;
        iter->Here = node->Next->Next;
    }

    return (AjPStr) item;
}
ID ajListstrIterGetBack
TY public
MO ajlist
LB core
XX
DE Returns next item using back iterator.
XX
PN [1]
PA u iter AjIList
PD List iterator.
PX
RT AjPStr
RD Data item returned.
RX
//


AjPStr ajListstrIterGetBack(AjIList iter)
{
    void* item = NULL;

    AjPListNode node = NULL;

    if(!iter)
        return NULL;

    node = iter->Here;

    if(!node->Prev)
        return NULL;

    if(!iter->Back)
    {
        item = node->Prev->Prev->Item;
        iter->Here = node->Prev->Prev;
        iter->Back = ajTrue;
    }
    else
    {
        item = node->Prev->Item;
        iter->Here = node->Prev;
    }

    return (AjPStr) item;
}
ID ajListstrIterInsert
TY public
MO ajlist
LB core
XX
DE Insert an item in a list, using an iterator (if not null)
DE to show which position to insert. Otherwise, simply push.
XX
PN [1]
PA u iter AjIList
PD List iterator.
PX
PN [2]
PA u str AjPStr
PD String to insert.
PX
RT void
RD 
RX
//


void ajListstrIterInsert(AjIList iter, AjPStr str)
{
    AjPList list = iter->Head;

    AjPListNode node = NULL;

    /*ajDebug("ajListstrInsert\n");*/
    ajListstrTrace(list);
    ajListstrIterTrace(iter);

    if(!iter->Modify)
    {
        ajErr("ajListInsert called on a read-only iterator");
        return;
    }

    node = iter->Here;


    if(!iter->Back)
    {
        if(!node->Prev)
            listInsertNode(&list->First, str);
        else
            listInsertNode(&node->Prev->Next, str);
        iter->Here = node->Prev;
    }
    else
    {
        if(!node->Next)
            ajFatal("Cannot add a new node for unused back iterator\n");

        if(!node->Prev)
            listInsertNode(&list->First, str);
        else
            listInsertNode(&node->Prev->Next, str);
    }

    list->Count++;
#ifdef AJ_SAVESTATS
    if(list->Count > listMaxNum)
        listMaxNum = list->Count;
#endif

    ajListstrTrace(list);
    ajListstrIterTrace(iter);

    return;
}
ID ajListstrIterRemove
TY public
MO ajlist
LB core
XX
DE Remove an item from a list, using an iterator (if not null)
DE to show which item. Otherwise remove the first item.
DE
DE We want to remove the item just fetched by the iterator.
XX
PN [1]
PA u iter AjIList
PD List iterator.
PX
RT void
RD 
RX
//


void ajListstrIterRemove(AjIList iter)
{
    AjPListNode node = NULL;

    /* ajDebug("ajListstrIterRemove\n");*/

    if(!iter->Modify)
    {
        ajErr("ajListstrIterRemove called on a read-only iterator");
        return;
    }

    node = iter->Here;

    if(!iter->Back)
    {
        if(!node->Prev)
            ajFatal("Attempt to delete from unused iterator\n");

        if(!node->Prev->Prev)
        {
            ajStrDel((AjPStr*) &(iter->Head->First->Item));
            listNodeDel(&(iter->Head->First));
        }
        else
        {
            ajStrDel((AjPStr*) &node->Prev->Prev->Next->Item);
            listNodeDel(&node->Prev->Prev->Next);
        }
    }
    else
    {
        ajStrDel((AjPStr*) &node->Prev->Prev->Next->Item);
        listNodeDel(&node->Prev->Prev->Next);
    }

    iter->Head->Count--;

    return;
}
ID ajListstrIterTrace
TY public
MO ajlist
LB core
XX
DE Traces a list iterator and validates it
XX
PN [1]
PA r iter const AjIList
PD List iterator to be traced.
PX
RT void
RD 
RX
//


void ajListstrIterTrace(const AjIList iter)
{
    if(!iter)
        return;

    ajDebug("\nIterator Head %p ReadHead %p Here %p Back %B Item %S\n",
            iter->Head, iter->ReadHead, iter->Here,iter->Back,
            (AjPStr)iter->Here->Item);

    return;
}
ID martGetVirtualSchema
TY static
MO ajmart
LB ajaxdb
XX
DE Return a virtual schema given a dataset name
XX
PN [1]
PA r dataset const AjPStr
PD dataset name
PX
RT const char*
RD virtualSchema value
RX
//


static const char* martGetVirtualSchema(const AjPStr dataset)
{
    static const char *defschema="default";
    const char *ret = NULL;
    ajuint i;

    ret = defschema;
    
    i = 0;
    while(dataset_schemas[i])
    {
        if(ajStrMatchC(dataset,dataset_schemas[i]))
        {
            ret = dataset_schemas[i+1];
            break;
        }

        i += 2;
    }

    return ret;
}
ID ajMartqueryNew
TY public
MO ajmart
LB ajaxdb
XX
DE Initialise mart query object
XX
RT AjPMartquery
RD Mart query object
RX
//


AjPMartquery ajMartqueryNew(void)
{
    AjPMartquery ret = NULL;

    AJNEW0(ret);
    
    ret->Reghost  = ajStrNew();
    ret->Regpath  = ajStrNew();
    ret->Marthost = ajStrNew();
    ret->Martpath = ajStrNew();
    ret->Cachedir = ajStrNew();
    ret->Query    = ajStrNew();
    ret->Mart     = ajStrNew();
    ret->Dsname   = ajStrNew();
    ret->Loc      = ajMartLocNew();

    ret->Dataset = ajMartDatasetNew();
    ret->Atts    = ajMartAttributeNew();
    ret->Filters = ajMartFilterNew();

    ret->Config = NULL;
    
    ret->Regport  =  80;
    ret->Martport  = 80;

    ret->Cacheflag = 0;
    
    return ret;
}
ID ajMartqueryDel
TY public
MO ajmart
LB ajaxdb
XX
DE Delete Mart query object
XX
PN [1]
PA u thys AjPMartquery*
PD Mary query object
PX
RT void
RD 
RX
//


void ajMartqueryDel(AjPMartquery *thys)
{
    AjPMartquery pthis = NULL;

    if(!thys)
        return;

    if(!*thys)
        return;

    pthis = *thys;

    ajMartLocDel(&pthis->Loc);
    ajMartDatasetDel(&pthis->Dataset);
    ajMartAttributeDel(&pthis->Atts);
    ajMartFilterDel(&pthis->Filters);

    if(pthis->Config)
        ajDomDocumentDestroyNode(pthis->Config,pthis->Config);
    
    ajStrDel(&pthis->Reghost);
    ajStrDel(&pthis->Regpath);
    ajStrDel(&pthis->Marthost);
    ajStrDel(&pthis->Martpath);
    ajStrDel(&pthis->Cachedir);
    ajStrDel(&pthis->Mart);
    ajStrDel(&pthis->Dsname);
    ajStrDel(&pthis->Query);

    AJFREE(pthis);
    
    *thys = NULL;
    
    return;
}
ID ajMartquerySeqinFree
TY public
MO ajmart
LB ajaxdb
XX
DE Delete Mart query object given a seqin object
XX
PN [1]
PA u thys AjPSeqin
PD Seqin query object
PX
RT void
RD 
RX
//


void ajMartquerySeqinFree(AjPSeqin thys)
{
    AjPQuery qry = NULL;
    AjPMartquery mq = NULL;

    if(!thys)
        return;

    qry = thys->Input->Query;

    if(!qry)
        return;
    
    mq = (AjPMartquery) qry->QryData;
    
    if(!mq)
        return;

    ajMartqueryDel(&mq);

    qry->QryData = NULL;

    return;
}
ID ajMartLocNew
TY public
MO ajmart
LB ajaxdb
XX
DE Create object for holding Mart location data read from XML
XX
RT AjPMartLoc
RD MartLoc object
RX
//


AjPMartLoc ajMartLocNew(void)
{
    AjPMartLoc ret = NULL;

    AJNEW0(ret);

    ret->Reg_read  = ajListNew();
    ret->Data_read = ajListNew();
    ret->Url_read  = ajListNew();

    ret->Nregistries = 0;
    ret->Ndatabases  = 0;
    ret->Nurls       = 0;

    return ret;
}
ID ajMartLocDel
TY public
MO ajmart
LB ajaxdb
XX
DE Delete a Mart Location object
XX
PN [1]
PA u thys AjPMartLoc*
PD MartLoc object
PX
RT void
RD 
RX
//


void ajMartLocDel(AjPMartLoc *thys)
{
    AjPMartLoc pthis;
    ajuint i;
    
    if(!thys)
        return;

    if(!*thys)
        return;

    pthis = *thys;
    
    for(i=0; i < pthis->Nregistries; ++i)
        ajTablestrFree(&pthis->Registries[i]);

    if(pthis->Nregistries)
        AJFREE(pthis->Registries);
    
    for(i=0; i < pthis->Ndatabases; ++i)
        ajTablestrFree(&pthis->Databases[i]);

    if(pthis->Ndatabases)
        AJFREE(pthis->Databases);
    
    for(i=0; i < pthis->Nurls; ++i)
        ajTablestrFree(&pthis->Urls[i]);

    if(pthis->Nurls)
        AJFREE(pthis->Urls);
    
    ajListFree(&pthis->Reg_read);
    ajListFree(&pthis->Data_read);    
    ajListFree(&pthis->Url_read);    
    
    AJFREE(pthis);
    *thys = NULL;

    return;
}
ID ajMartDatasetNew
TY public
MO ajmart
LB ajaxdb
XX
DE Create object for holding Mart Dataset read from XML (or tab-sep list)
XX
RT AjPMartDataset
RD MartDataset object
RX
//


AjPMartDataset ajMartDatasetNew(void)
{
    AjPMartDataset ret = NULL;

    AJNEW0(ret);

    ret->Set_read  = ajListNew();

    ret->Nsets = 0;
    ret->Depth = 0;

    return ret;
}
ID ajMartDatasetDel
TY public
MO ajmart
LB ajaxdb
XX
DE Delete a Mart Dataset object
XX
PN [1]
PA u thys AjPMartDataset*
PD MartDataset object
PX
RT void
RD 
RX
//


void ajMartDatasetDel(AjPMartDataset *thys)
{
    AjPMartDataset pthis;
    ajuint i;
    
    if(!thys)
        return;

    if(!*thys)
        return;

    pthis = *thys;
    
    for(i=0; i < pthis->Nsets; ++i)
        ajTablestrFree(&pthis->Sets[i]);

    if(pthis->Nsets)
        AJFREE(pthis->Sets);
    
    ajListFree(&pthis->Set_read);
    
    AJFREE(pthis);
    *thys = NULL;

    return;
}
ID ajMartAttributeNew
TY public
MO ajmart
LB ajaxdb
XX
DE Create object for holding Mart Attributes read from XML (or tab-sep list)
XX
RT AjPMartAttribute
RD MartAttribute object
RX
//


AjPMartAttribute ajMartAttributeNew(void)
{
    AjPMartAttribute ret = NULL;

    AJNEW0(ret);

    ret->Att_read  = ajListNew();

    ret->Natts = 0;
    ret->Depth = 0;

    return ret;
}
ID ajMartAttributeDel
TY public
MO ajmart
LB ajaxdb
XX
DE Delete a Mart Attribute object
XX
PN [1]
PA u thys AjPMartAttribute*
PD MartAttribute object
PX
RT void
RD 
RX
//


void ajMartAttributeDel(AjPMartAttribute *thys)
{
    AjPMartAttribute pthis;
    ajuint i;
    
    if(!thys)
        return;

    if(!*thys)
        return;

    pthis = *thys;

    if(pthis->Attributes)
        for(i=0; i < pthis->Natts; ++i)
            ajTablestrFree(&pthis->Attributes[i]);

    if(pthis->Natts)
        if(pthis->Attributes)
            AJFREE(pthis->Attributes);

    ajListFree(&pthis->Att_read);

    AJFREE(pthis);

    *thys = NULL;

    return;
}
ID ajMartFilterNew
TY public
MO ajmart
LB ajaxdb
XX
DE Create object for holding Mart Filters read from XML (or tab-sep list)
XX
RT AjPMartFilter
RD MartFilter object
RX
//


AjPMartFilter ajMartFilterNew(void)
{
    AjPMartFilter ret = NULL;

    AJNEW0(ret);

    ret->Filter_read  = ajListNew();

    ret->Nfilters = 0;
    ret->Depth = 0;

    return ret;
}
ID ajMartFilterDel
TY public
MO ajmart
LB ajaxdb
XX
DE Delete a Mart Filter object
XX
PN [1]
PA u thys AjPMartFilter*
PD MartFilter object
PX
RT void
RD 
RX
//


void ajMartFilterDel(AjPMartFilter *thys)
{
    AjPMartFilter pthis;
    ajuint i;
    
    if(!thys)
        return;

    if(!*thys)
        return;

    pthis = *thys;
    
    if(pthis->Filters)
        for(i=0; i < pthis->Nfilters; ++i)
            ajTablestrFree(&pthis->Filters[i]);

    if(pthis->Nfilters)
        if(pthis->Filters)
            AJFREE(pthis->Filters);
    
    ajListFree(&pthis->Filter_read);
    
    AJFREE(pthis);
    *thys = NULL;

    return;
}
ID ajMartDsinfoNew
TY public
MO ajmart
LB ajaxdb
XX
DE Create object for query dataset info
XX
RT AjPMartdsinfo
RD Martdsinfo object
RX
//


AjPMartdsinfo ajMartDsinfoNew(void)
{
    AjPMartdsinfo ret = NULL;

    AJNEW0(ret);

    ret->Attributes  = ajListNew();
    ret->Filters     = ajListNew();

    ret->Name      = ajStrNew();
    ret->Interface = ajStrNew();

    return ret;
}
ID ajMartDsinfoDel
TY public
MO ajmart
LB ajaxdb
XX
DE Delete a Mart Dsinfo object
XX
PN [1]
PA u thys AjPMartdsinfo*
PD Mart Dsinfo object
PX
RT void
RD 
RX
//


void ajMartDsinfoDel(AjPMartdsinfo *thys)
{
    AjPMartdsinfo pthis;
    AjPStr t = NULL;
    
    if(!thys)
        return;

    if(!*thys)
        return;

    pthis = *thys;

    while(ajListPop(pthis->Attributes,(void **)&t))
        ajStrDel(&t);
    
    while(ajListPop(pthis->Filters,(void **)&t))
        ajStrDel(&t);

    ajListFree(&pthis->Attributes);
    ajListFree(&pthis->Filters);

    ajStrDel(&pthis->Name);
    ajStrDel(&pthis->Interface);

    AJFREE(pthis);
    *thys = NULL;

    return;
}
ID ajMartQinfoNew
TY public
MO ajmart
LB ajaxdb
XX
DE Create object for query info
XX
PN [1]
PA r n ajuint
PD number of datasets
PX
RT AjPMartqinfo
RD Martqinfo object
RX
//


AjPMartqinfo ajMartQinfoNew(ajuint n)
{
    AjPMartqinfo ret = NULL;
    ajuint i;
    
    AJNEW0(ret);

    ret->Hvirtualschema = ajStrNew();
    ret->Hversion       = ajStrNew();
    ret->Hformatter     = ajStrNew();

    ret->Hcount         = ajFalse;
    ret->Hheader = ajFalse;
    ret->Hstamp  = ajTrue;
    ret->Hunique = ajFalse;
    ret->Verify  = ajFalse;

    ret->Dnsets = n;

    AJCNEW(ret->Dsets, n);
    
    for(i=0; i < n; ++i)
        ret->Dsets[i] = ajMartDsinfoNew();
    
    return ret;
}
ID ajMartQinfoDel
TY public
MO ajmart
LB ajaxdb
XX
DE Delete a Mart qinfo object
XX
PN [1]
PA u thys AjPMartqinfo*
PD Mart Qinfo object
PX
RT void
RD 
RX
//


void ajMartQinfoDel(AjPMartqinfo *thys)
{
    AjPMartqinfo pthis;
    ajuint i;
    
    if(!thys)
        return;

    if(!*thys)
        return;

    pthis = *thys;

    if(pthis->Dsets)
    {
        for(i = 0; i < pthis->Dnsets; ++i)
            ajMartDsinfoDel(&pthis->Dsets[i]);

        AJFREE(pthis->Dsets);
    }
    
    ajStrDel(&pthis->Hvirtualschema);
    ajStrDel(&pthis->Hversion);
    ajStrDel(&pthis->Hformatter);

    AJFREE(pthis);
    *thys = NULL;

    return;
}
ID martRegistryElementstart
TY static
MO ajmart
LB ajaxdb
XX
DE Handler for reading Mart registry XML elements
XX
PN [1]
PA u userData void*
PD XML structure being loaded
PX
PN [2]
PA r name const XML_Char*
PD Close element token name
PX
PN [3]
PA r atts const XML_Char**
PD Close element token name
PX
RT void
RD 
RX
//


static void martRegistryElementstart(void *userData, const XML_Char *name,
                                     const XML_Char **atts)
{
    AjPMartLoc locptr   = NULL;
    
    AjPTable t = NULL;

    AjPStr key   = NULL;
    AjPStr value = NULL;

    ajuint i;
    
    locptr = (AjPMartLoc) userData;
    
    if(locptr->Depth > 1)
        ajWarn("Nested element depth (%d) potentially excessive in Mart "
               "registry file", locptr->Depth + 1);


    if(ajCharMatchCaseC("MartRegistry",name))
    {
        ++locptr->Depth;

        return;
    }

    
    if(!(ajCharMatchC(name,"RegistryDBPointer")    ||
         ajCharMatchC(name,"RegistryURLPointer")   ||
         ajCharMatchC(name,"MartDBLocation")       ||
         ajCharMatchC(name,"MartURLLocation")))
        ajWarn("Registry config: expected RegistryDBPointer,"
               "RegistryURLPointer,MartDBLocation\n"
               "or MartURLLocation. Got %s instead",name);

    t = ajTablestrNew(REGTABGUESS);

    for(i = 0; atts[i]; i += 2)
    {
        key   = ajStrNewC(atts[i]);
        value = ajStrNewC(atts[i+1]);
        ajTablePut(t, (void *)key, (void *)value);
    }


    if(ajCharMatchCaseC(name,"RegistryDBPointer"))
    {
        ajListPushAppend(locptr->Reg_read,(void *)t);
        ++locptr->Nregistries;
    }
    else if(ajCharMatchCaseC(name,"RegistryURLPointer"))
    {
        ajListPushAppend(locptr->Reg_read,(void *)t);
        ++locptr->Nregistries;
    }
    else if(ajCharMatchCaseC(name,"MartDBLocation"))
    {
        ajListPushAppend(locptr->Data_read,(void *)t);
        ++locptr->Ndatabases;
    }
    else if(ajCharMatchCaseC(name,"MartURLLocation"))
    {
        ajListPushAppend(locptr->Url_read,(void *)t);
        ++locptr->Nurls;
    }
    else
    {
        ajWarn("Could not assign config entry to a registry, DB or URL");
        ajTablestrFree(&t);
    }

    ++locptr->Depth;
    
    return;
}
ID martRegistryElementend
TY static
MO ajmart
LB ajaxdb
XX
DE Handler for reading Mart registry XML elements
XX
PN [1]
PA u userData void*
PD XML structure being loaded
PX
PN [2]
PA r name const XML_Char*
PD Close element token name
PX
RT void
RD 
RX
//


static void martRegistryElementend(void *userData, const XML_Char *name)
{
    AjPMartLoc locptr = NULL;

    (void) name;                /* make it used */

    locptr = (AjPMartLoc) userData;

    --locptr->Depth;

    return;
}
ID martConvertLocToArrays
TY static
MO ajmart
LB ajaxdb
XX
DE Convert lists used for reading the XML into more usable arrays
DE within the same object.
XX
PN [1]
PA u loc AjPMartLoc
PD registry table
PX
RT void
RD 
RX
//


static void martConvertLocToArrays(AjPMartLoc loc)
{
    ajulong n;

    if(!loc)
        return;
    
    n = ajListToarray(loc->Reg_read, (void ***) &loc->Registries);
    if(n != loc->Nregistries)
    {
        ajWarn("martConvertLocToArrays: mismatching Registries count");
        return;
    }

    n = ajListToarray(loc->Data_read, (void ***) &loc->Databases);
    if(n != loc->Ndatabases)
    {
        ajWarn("martConvertLocToArrays: mismatching Databases count");
        return;
    }

    n = ajListToarray(loc->Url_read, (void ***) &loc->Urls);
    if(n != loc->Nurls)
    {
        ajWarn("martConvertLocToArrays: mismatching URLs count");
        return;
    }

    
    return;
}
ID ajMartregistryParse
TY public
MO ajmart
LB ajaxdb
XX
DE Read a mart registry file
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object holding mart info
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartregistryParse(AjPSeqin seqin)
{
    XML_Parser parser;
    AjPStr line = NULL;
    AjPFilebuff buff = NULL;
    AjPMartquery mq = NULL;
    AjPMartLoc loc;
    
    int done;
    size_t len;

    buff = seqin->Input->Filebuff;
    
    if(!buff)
        return ajFalse;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    loc = mq->Loc;
    
    if(!loc)
        return ajFalse;

    line = ajStrNew();
    
    parser = XML_ParserCreate(NULL);

    XML_SetElementHandler(parser, martRegistryElementstart,
                          martRegistryElementend);

    XML_SetUserData(parser, loc);

    done = 0;

    do
    {
        ajBuffreadLine(buff,&line);
        done = ajFilebuffIsEmpty(buff);
        len = ajStrGetLen(line);

        ajDebug("XML_Parse: '%S'", line);

        if(!XML_Parse(parser, line->Ptr, len, done))
        {
            ajWarn("ajMartregistryParse: %s at line %d\n",
                   XML_ErrorString(XML_GetErrorCode(parser)),
                   XML_GetCurrentLineNumber(parser));

            ajStrDel(&line);
            
            return ajFalse;
        }

    } while (!done);

    XML_ParserFree(parser);

    martConvertLocToArrays(loc);
    
    ajStrDel(&line);
    
    return ajTrue;
}
ID ajMartGetRegistry
TY public
MO ajmart
LB ajaxdb
XX
DE Return contents of a mart registry
XX
PN [1]
PA u seqin AjPSeqin
PD "Sequence" input object
PX
RT AjBool
RD true on success
RX
//


AjBool ajMartGetRegistry(AjPSeqin seqin)
{
    AjPQuery qry  = NULL;
    
    AjPStr get = NULL;
    
    AjPMartquery mq = NULL;
    
    AjBool force_write = ajFalse;
    ajuint httpcode = 0;

    qry = seqin->Input->Query;

    if(!qry)
        return ajFalse;
    
    mq  = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    /* First check whether registry is in the cache */

    if(ajStrGetLen(mq->Cachedir) && (mq->Cacheflag & MART_CACHE_RD))
    {
        /* Try a cache read */
        if(!martReadCacheEntry(mq, &seqin->Input->Filebuff, MART_REGENTRY))
        {
            if(mq->Cacheflag & MART_CACHE_WTIFEMPTY)
                force_write = ajTrue;
            else
                force_write = ajFalse;
        }
        else
            return ajTrue;
    }

    get       = ajStrNew();
    
    
    ajFmtPrintS(&get, "%S?type=registry\r\n", mq->Regpath);

    seqin->Input->Filebuff = ajHttpRead(qry->DbHttpVer, qry->DbName,
                                            qry->DbProxy,
                                            mq->Reghost, mq->Regport, get);

    if(!seqin->Input->Filebuff)
	return ajFalse;

    httpcode = ajFilebuffHtmlNoheader(seqin->Input->Filebuff);
    if(httpcode)
    {
        ajWarn("ajMartGetregistry HTTP code %u from '%S'",
               httpcode, get);
        return ajFalse;
    }

    if((ajStrGetLen(mq->Cachedir) && (mq->Cacheflag & MART_CACHE_WT)) ||
       force_write)
    {
        /* Write to cache */
        martWriteCacheEntry(mq, seqin->Input->Filebuff, MART_REGENTRY);
    }
    
    ajStrDel(&get);
    
    return ajTrue;
}
ID martGetMarttable
TY static
MO ajmart
LB ajaxdb
XX
DE returns a mart table given a mart name
XX
PN [1]
PA r seqin const AjPSeqin
PD seqin object
PX
PN [2]
PA r mart const AjPStr
PD mart name
PX
RT AjPTable
RD mart table or NULL
RX
//


static AjPTable martGetMarttable(const AjPSeqin seqin, const AjPStr mart)
{
    AjPTable t       = NULL;
    AjPMartquery pmq = NULL;
    AjPMartLoc loc   = NULL;
    ajuint nurls = 0;
    ajuint i = 0;
    AjPStr key   = NULL;
    const AjPStr value = NULL;
    
    if(!seqin)
        return NULL;

    pmq = ajMartGetMartqueryPtr(seqin);
    
    if(!pmq)
        return NULL;

    loc = pmq->Loc;
    nurls = loc->Nurls;

    key = ajStrNewC("name");
    
    for(i=0; i < nurls; ++i)
    {
        t = loc->Urls[i];
        value = ajTableFetchS(t, key);

        if(ajStrMatchCaseS(value,mart))
            break;
    }
    
    ajStrDel(&key);

    if(i == nurls)
        return NULL;

    return t;
}
ID ajMartGetDatasets
TY public
MO ajmart
LB ajaxdb
XX
DE Return datasets given a mart registry and a mart name within it
XX
PN [1]
PA u seqin AjPSeqin
PD "Sequence" input object
PX
PN [2]
PA r mart const AjPStr
PD "Sequence" mart name
PX
RT AjBool
RD true on success
RX
//


AjBool ajMartGetDatasets(AjPSeqin seqin, const AjPStr mart)
{
    AjPTable marttab = NULL;

    AjPStr spmart = NULL;
    const AjPStr host   = NULL;
    const AjPStr path   = NULL;
    const AjPStr port   = NULL;
    
    ajuint iport    = 0;
    
    AjPStr get       = NULL;

    AjPMartquery mq = NULL;
    AjPQuery qry = NULL;
    
    AjBool force_write = ajFalse;
    ajuint httpcode = 0;

    marttab = martGetMarttable(seqin, mart);
    
    if(!marttab)
    {
        ajWarn("martGetMarttable: No mart available called %S",mart);
        return ajFalse;
    }

    host = ajTableFetchC(marttab, "host");
    path = ajTableFetchC(marttab, "path");
    port = ajTableFetchC(marttab, "port");

    if(!host || !path || !port)
    {
        ajWarn("Invalid Mart location Host=%S Path=%S Port=%S",
               host,path,port);

        return ajFalse;
    }
    
    if(!ajStrToUint(port, &iport))
    {
        ajWarn("Invalid Port (%S)",port);

        return ajFalse;
    }
        

    /*
    ** Save the mart host locations at the top of the mart query object
    ** for future use by attribute/filter/query calls on the
    ** selected mart;
    ** This saves unnecessary searches through the tables each time
    ** such a mart is used.
    */

    qry = seqin->Input->Query;
    mq  = ajMartGetMartqueryPtr(seqin);

    ajStrAssignS(&mq->Marthost, host);
    ajStrAssignS(&mq->Martpath, path);
    mq->Martport = iport;


    ajStrAssignS(&mq->Mart, mart);
    
    /* First check whether dataset is in the cache */

    if(ajStrGetLen(mq->Cachedir) && (mq->Cacheflag & MART_CACHE_RD))
    {
        /* Try a cache read */
        if(!martReadCacheEntry(mq, &seqin->Input->Filebuff, MART_DSENTRY))
        {
            if(mq->Cacheflag & MART_CACHE_WTIFEMPTY)
                force_write = ajTrue;
            else
                force_write = ajFalse;
        }
        else
            return ajTrue;
    }
    
    /*
    ** Do the GET request
    */
    
    get       = ajStrNew();
    spmart    = ajStrNew();
    
    

    ajStrAssignS(&spmart,mart);
    martSpacesToHex(&spmart);

    ajFmtPrintS(&get, "%S?type=datasets&mart=%S", path, spmart);
    
    ajFilebuffDel(&seqin->Input->Filebuff);
    
    seqin->Input->Filebuff = ajHttpRead(qry->DbHttpVer, qry->DbName,
                                        qry->DbProxy,
                                        host, iport, get);

    if(!seqin->Input->Filebuff)
	return ajFalse;

    httpcode = ajFilebuffHtmlNoheader(seqin->Input->Filebuff);
    if(httpcode)
    {
        ajWarn("ajMartGetdatasets: HTTP code %u from '%S'",
               httpcode, get);
        return ajFalse;
    }

    if((ajStrGetLen(mq->Cachedir) && (mq->Cacheflag & MART_CACHE_WT)) ||
       force_write)
    {
        /* Write to cache */
        martWriteCacheEntry(mq, seqin->Input->Filebuff, MART_DSENTRY);
    }

    ajStrDel(&get);
    ajStrDel(&spmart);
    
    return ajTrue;
}
ID martTabToToken
TY static
MO ajmart
LB ajaxdb
XX
DE Parse tab-separated list. Can't use normal AJAX tokens as
DE there can be empty fields of the form \t\t
XX
PN [1]
PA w token AjPStr*
PD token
PX
PN [2]
PA r s const AjPStr
PD original string
PX
PN [3]
PA r loc ajint
PD current location within s
PX
RT ajint
RD updated value for loc or -1 if EOS
RX
//


static ajint martTabToToken(AjPStr *token, const AjPStr s, ajint loc)
{
    char *str = NULL;
    ajint p = 0;

    
    if(!token)
        return -1;

    if(!*token)
        return -1;

    ajStrAssignC(token,"");
    
    if(loc < 0)
        return -1;
    
    str = s->Ptr;

    p = loc;

    if(!str[p])
        return -1;

    while(str[p] != '\t' && str[p] != '\n')
    {
        if(!str[p])
            break;

        ++p;
    }

    if(p != loc)
        ajStrAssignSubC(token,str,loc,p-1);

    if(str[p])
        ++p;

    return p;
}
ID martBuffIsXML
TY static
MO ajmart
LB ajaxdb
XX
DE Tests whether a file buffer is XML
XX
PN [1]
PA u buff AjPFilebuff
PD File buffer
PX
RT AjBool
RD True if XML
RX
//


static AjBool martBuffIsXML(AjPFilebuff buff)
{
    ajuint i;
    AjBool isxml = ajFalse;
    AjPStr line = NULL;

    line = ajStrNew();
    
    for(i=0; i < MART_READ_AHEAD; ++i)
    {
        if(!ajBuffreadLine(buff,&line))
            break;

        if(strstr(line->Ptr, "/>"))
        {
            isxml = ajTrue;
            break;
        }
    }

    ajFilebuffReset(buff);
    ajStrDel(&line);
    
    return isxml;
}
ID martTablePush
TY static
MO ajmart
LB ajaxdb
XX
DE Push a key/value pair to a table
XX
PN [1]
PA u table AjPTable
PD Table
PX
PN [2]
PA r name const char*
PD Key
PX
PN [3]
PA r token const AjPStr
PD Value
PX
RT void
RD 
RX
//


static void martTablePush(AjPTable table, const char *name,
                          const AjPStr token)
{
    AjPStr key   = NULL;
    AjPStr value = NULL;
    
    key = ajStrNewC(name);
    value = ajStrNewS(token);
    ajTablePut(table, (void *)key, (void *)value);

    return;
}
ID martSpacesToHex
TY static
MO ajmart
LB ajaxdb
XX
DE Replace any spaces in a string with %20
XX
PN [1]
PA u s AjPStr*
PD String
PX
RT void
RD 
RX
//


static void martSpacesToHex(AjPStr *s)
{
    AjPStr stmp = NULL;
    const char *ptr = NULL;
    char c;
    
    if(!s)
        return;

    if(!*s)
        return;

    ptr = ajStrGetPtr(*s);
    
    stmp = ajStrNew();

    while((c = *ptr))
    {
        if(c == ' ')
            ajStrAppendC(&stmp,"%20");
        else
            ajStrAppendK(&stmp,c);

        ++ptr;
    }

    ajStrAssignS(s,stmp);

    ajStrDel(&stmp);
    
    return;
}
ID martParseTabbedDataset
TY static
MO ajmart
LB ajaxdb
XX
DE Parse dataset information in the original tab-delimited format
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object holding mart info
PX
RT AjBool
RD True on success
RX
//


static AjBool martParseTabbedDataset(AjPSeqin seqin)
{
    AjPFilebuff buff = NULL;
    AjPStr line  = NULL;
    AjPStr token = NULL;
    AjPTable table = NULL;
    AjPMartDataset ds = NULL;
    AjPMartquery mq   = NULL;
    AjBool error = ajFalse;
    
    ajulong n  = 0;
    ajint pos = 0;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    buff  = seqin->Input->Filebuff;
    line  = ajStrNew();
    token = ajStrNew();

    ds = mq->Dataset;
    
    while(ajBuffreadLine(buff,&line))
    {
        if(ajStrGetLen(line) < 10)
            continue;

        table = ajTablestrNew(REGTABGUESS);
        
        pos = 0;

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedDataset: missing tab field (1)1\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"type",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedDataset: missing tab field (2)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"name",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedDataset: missing tab field (3)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"displayName",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedDataset: missing tab field (4)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"visible",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedDataset: missing tab field (5)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"version",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedDataset: missing tab field (6)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"unk1",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedDataset: missing tab field (7)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"unk2",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
            ajStrAssignC(&token,"");
        
        martTablePush(table,"interface",token);

        pos = martTabToToken(&token,line,pos);

        if(pos < 0)
            ajStrAssignC(&token,"");
        
        martTablePush(table,"date",token);
        
        ajListPushAppend(ds->Set_read,(void *)table);
        ++ds->Nsets;
    }
    
    ajStrDel(&line);
    ajStrDel(&token);

    if(error)
        return ajFalse;
    
    n = ajListToarray(ds->Set_read, (void ***) &ds->Sets);
    if(n != ds->Nsets)
    {
        ajWarn("martParseTabbedDataset: mismatching Set count");
        return ajFalse;
    }


    
    return ajTrue;
}
ID ajMartdatasetParse
TY public
MO ajmart
LB ajaxdb
XX
DE Read a mart dataset buffer
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object holding mart info
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartdatasetParse(AjPSeqin seqin)
{
    AjBool ret = ajTrue;
    
    if(!martBuffIsXML(seqin->Input->Filebuff))
        ret = martParseTabbedDataset(seqin);
    else
        ajFatal("Looks like the new Biomart XML format for datasets "
                "has just been implemented. New function needed");

    return ret;
}
ID ajMartGetAttributes
TY public
MO ajmart
LB ajaxdb
XX
DE Return attributes given a mart dataset and a mart host/path/port
XX
PN [1]
PA u seqin AjPSeqin
PD "Sequence" input object
PX
PN [2]
PA r dataset const AjPStr
PD "Sequence" mart dataset name
PX
RT AjBool
RD true on success
RX
//


AjBool ajMartGetAttributes(AjPSeqin seqin, const AjPStr dataset)
{
    AjPStr get       = NULL;

    AjPMartquery mq = NULL;
    AjPQuery qry = NULL;

    const char *vschema = NULL;
    
    AjBool force_write = ajFalse;
    ajuint httpcode = 0;

    qry = seqin->Input->Query;
    mq  = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    if(!mq->Marthost || !mq->Martpath)
    {
        ajWarn("ajMartGetAttributes: Invalid Mart location Host=%S Path=%S",
               mq->Marthost,mq->Martpath);

        return ajFalse;
    }
    

    ajStrAssignS(&mq->Dsname, dataset);
    
    /* First check whether registry is in the cache */

    if(ajStrGetLen(mq->Cachedir) && (mq->Cacheflag & MART_CACHE_RD))
    {
        /* Try a cache read */
        if(!martReadCacheEntry(mq, &seqin->Input->Filebuff, MART_ATTENTRY))
        {
            if(mq->Cacheflag & MART_CACHE_WTIFEMPTY)
                force_write = ajTrue;
            else
                force_write = ajFalse;
        }
        else
            return ajTrue;
    }


    vschema = martGetVirtualSchema(dataset);
    
    ajFmtPrintS(&get, "%S?type=attributes&dataset=%S&virtualSchema=%s",
                        mq->Martpath, dataset, vschema);

    ajFilebuffDel(&seqin->Input->Filebuff);

    seqin->Input->Filebuff = ajHttpRead(qry->DbHttpVer, qry->DbName,
                                        qry->DbProxy,
                                        mq->Marthost, mq->Martport, get);

    if(!seqin->Input->Filebuff)
	return ajFalse;

    httpcode = ajFilebuffHtmlNoheader(seqin->Input->Filebuff);
    if(httpcode)
    {
        ajWarn("ajMartGetAttributes: HTTP code %u from '%S'",
               httpcode, get);
        return ajFalse;
    }

    if((ajStrGetLen(mq->Cachedir) && (mq->Cacheflag & MART_CACHE_WT)) ||
       force_write)
    {
        /* Write to cache */
        martWriteCacheEntry(mq, seqin->Input->Filebuff, MART_ATTENTRY);
    }

    ajStrDel(&get);
    
    return ajTrue;
}
ID ajMartGetAttributesRetry
TY public
MO ajmart
LB ajaxdb
XX
DE Retry an attribute fetch using the main biomart registry site
DE This routine should only be called after an ajMartGetAttribute
DE call followed by a ajMartattributesParse has returned an error.
DE That typically means that an external server has not provided all
DE the attributes fields, possibly due to outdated biomart software.
DE As the main biomart registry site contains all the metadata for
DE external marts (but not the marts themselves) it can be
DE queried using the same dataset. The true mart server should always
DE be tried first to minimise load on the main biomart site
XX
PN [1]
PA u seqin AjPSeqin
PD "Sequence" input object
PX
PN [2]
PA r dataset const AjPStr
PD "Sequence" mart dataset name
PX
RT AjBool
RD true on success
RX
//


AjBool ajMartGetAttributesRetry(AjPSeqin seqin, const AjPStr dataset)
{
    AjPMartquery mq = NULL;
    AjPStr orighost = NULL;
    AjPStr origpath = NULL;
    AjPStr schema   = NULL;
    
    ajuint origport = 0;
    AjBool ret = ajFalse;
    
    mq  = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    if(!mq->Marthost || !mq->Martpath)
    {
        ajWarn("ajMartGetAttributesRetry: No previous Mart queried.\n"
               "Invalid Mart location Host=%S Path=%S",
               mq->Marthost,mq->Martpath);

        return ajFalse;
    }

    ajMartAttributeDel(&mq->Atts);

    mq->Atts = ajMartAttributeNew();

    orighost = ajStrNew();
    origpath = ajStrNew();
 
    ajStrAssignS(&orighost, mq->Marthost);
    ajStrAssignS(&origpath, mq->Martpath);
    origport = mq->Martport;

    ajStrAssignC(&mq->Marthost, DEFAULT_BIOMART_MART_HOST);
    ajStrAssignC(&mq->Martpath, DEFAULT_BIOMART_MART_PATH);
    mq->Martport = DEFAULT_BIOMART_MART_PORT;

    schema = ajStrNewC("default");
    ret = ajMartGetAttributesSchema(seqin,dataset,schema);

    ajStrAssignS(&mq->Marthost, orighost);
    ajStrAssignS(&mq->Martpath, origpath);
    mq->Martport = origport;

    ajStrDel(&orighost);
    ajStrDel(&origpath);
    ajStrDel(&schema);
    
    return ret;
}
ID ajMartGetAttributesSchema
TY public
MO ajmart
LB ajaxdb
XX
DE Return attributes given a mart dataset, a mart host/path/port and a
DE schemaname.
DE
DE This function will be deleted once virtual schemas
DE are phased out. Use with caution.
XX
PN [1]
PA u seqin AjPSeqin
PD "Sequence" input object
PX
PN [2]
PA r dataset const AjPStr
PD "Sequence" mart dataset name
PX
PN [3]
PA r schema const AjPStr
PD Schema name
PX
RT AjBool
RD true on success
RX
//


AjBool ajMartGetAttributesSchema(AjPSeqin seqin, const AjPStr dataset,
                                 const AjPStr schema)
{
    AjPStr get       = NULL;

    AjPMartquery mq = NULL;
    AjPQuery qry = NULL;

    const char *vschema = NULL;
    
    ajuint httpcode = 0;

    qry = seqin->Input->Query;
    mq  = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    if(!mq->Marthost || !mq->Martpath)
    {
        ajWarn("ajMartGetAttributesSchema: Invalid Mart location "
               "Host=%S Path=%S",
               mq->Marthost,mq->Martpath);

        return ajFalse;
    }
    

    get       = ajStrNew();
    

    vschema = ajStrGetPtr(schema);
    
    ajFmtPrintS(&get, "%S?type=attributes&dataset=%S&virtualSchema=%s",
                        mq->Martpath, dataset, vschema);

    ajFilebuffDel(&seqin->Input->Filebuff);

    seqin->Input->Filebuff = ajHttpRead(qry->DbHttpVer, qry->DbName,
					qry->DbProxy,
					mq->Marthost, mq->Martport, get);

    if(!seqin->Input->Filebuff)
	return ajFalse;

    httpcode = ajFilebuffHtmlNoheader(seqin->Input->Filebuff);

    if(httpcode)
    {
        ajWarn("ajMartGetAttributesSchema: HTTP code %u from '%S'",
               httpcode, get);
        return ajFalse;
    }

    ajStrDel(&get);
    
    return ajTrue;
}
ID martAttcmp
TY static
MO ajmart
LB ajaxdb
XX
DE Finds the sort order of two mart tabbed strings  cast as void.
DE
DE For use with {ajListSort}, {ajListUnique} and other functions which
DE require a general function with void arguments.
XX
PN [1]
PA r str const void*
PD First string
PX
PN [2]
PA r str2 const void*
PD Second string
PX
RT int
RD -1 if first string should sort before second, +1 if the
RD second string should sort first. 0 if they are identical
RD in length and content.
RX
//


static int martAttcmp(const void* str, const void* str2)
{
    AjPStr a = NULL;
    AjPStr b = NULL;
    char *aptr = NULL;
    char *bptr = NULL;
    char *p = NULL;
    char *q = NULL;
    ajuint alen;
    ajuint blen;
    
    a = (*(AjPStr const *)str);
    b = (*(AjPStr const *)str2);
    
    aptr = a->Ptr;
    bptr = b->Ptr;

    p = strchr(aptr,(int)'\t');
    q = strchr(bptr,(int)'\t');
    
    alen = p - aptr;
    blen = q - bptr;

    if(alen > blen)
        return 1;

    if(blen > alen)
        return -1;

    return strncmp(aptr,bptr,alen);

}
ID martStrdel
TY static
MO ajmart
LB ajaxdb
XX
DE Deletes a string when called by ajListSortUnique
XX
PN [1]
PA r str void**
PD string to delete
PX
PN [2]
PA r cl void*
PD not used
PX
RT void
RD 
RX
//


static void martStrdel(void** str, void* cl) 
{
    (void) cl;				/* make it used */

    ajStrDel((AjPStr*)str);

    return;
}
ID martParseTabbedAttributes
TY static
MO ajmart
LB ajaxdb
XX
DE Parse attribute information in the original tab-delimited format
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object holding mart info
PX
RT AjBool
RD True on success
RX
//


static AjBool martParseTabbedAttributes(AjPSeqin seqin)
{
    AjPFilebuff buff = NULL;
    AjPStr line  = NULL;
    AjPStr token = NULL;
    AjPStr tline = NULL;
    
    AjPTable table = NULL;
    AjPMartAttribute att = NULL;
    AjPMartquery mq   = NULL;
    AjBool error = ajFalse;
    
    ajulong n  = 0;
    ajint pos = 0;
    
    AjPList ulist = NULL;
    
    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;
    
    buff  = seqin->Input->Filebuff;
    line  = ajStrNew();
    token = ajStrNew();

    ulist = ajListNew();
    
    
    att = mq->Atts;

    while(ajBuffreadLine(buff,&line))
    {
        ajDebug("martParseTabbedAttributes '%S'", line);
        if(ajStrGetLen(line) < 10)
            continue;

        tline = ajStrNewS(line);

        ajListPush(ulist,(void *) tline);
    }

    ajListSortUnique(ulist, &martAttcmp, &martStrdel);

    while(ajListPop(ulist, (void **)&tline))
    {
        table = ajTablestrNew(REGTABGUESS);

        pos = 0;
        
        pos = martTabToToken(&token,tline,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedAttributes: missing tab field (1)\n%S",
                   tline);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"name",token);

        pos = martTabToToken(&token,tline,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedAttributes: missing tab field (2)\n%S",
                   tline);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"displayName",token);

        /*
        ** Some mart servers only provide the first two fields so 
        ** need to return some indication of an incomplete read
        ** in case an attempt has to be made to get the metadata
        ** from the main biomart (assuming the initial query has been
        ** made to a satellite server
        */

        pos = martTabToToken(&token,tline,pos);
        if(pos < 0)
        {
            error = ajTrue;
            ajDebug("martParseTabbedAttributes: missing tab field (3)\n%S",
                    tline);
            ajStrAssignC(&token,"");
        }
        

        martTablePush(table,"description",token);

        pos = martTabToToken(&token,tline,pos);
        if(pos < 0)
        {
            error = ajTrue;
            ajDebug("martParseTabbedAttributes: missing tab field (4)\n%S",
                    tline);
            ajStrAssignC(&token,"");
        }
        
        martTablePush(table,"page",token);


        pos = martTabToToken(&token,tline,pos);
        if(pos < 0)
        {
            error = ajTrue;
            ajDebug("martParseTabbedAttributes: missing tab field (5)\n%S",
                    tline);
            ajStrAssignC(&token,"");
        }
        
        martTablePush(table,"format",token);

        pos = martTabToToken(&token,tline,pos);
        if(pos < 0)
        {
            error = ajTrue;
            ajDebug("martParseTabbedAttributes: missing tab field (6)\n%S",
                    tline);
            ajStrAssignC(&token,"");
        }

        martTablePush(table,"tableName",token);

        pos = martTabToToken(&token,tline,pos);
        if(pos < 0)
        {
            error = ajTrue;
            ajDebug("martParseTabbedAttributes: missing tab field (7)\n%S",
                    tline);
            ajStrAssignC(&token,"");
        }

        martTablePush(table,"columnName",token);

        ajListPushAppend(att->Att_read,(void *)table);

        ++att->Natts;

        ajStrDel(&tline);
    }

    ajStrDel(&line);
    ajStrDel(&token);

    if(error)
        return ajFalse;
    
    n = ajListToarray(att->Att_read, (void ***) &att->Attributes);

    if(n != att->Natts)
    {
        ajWarn("martParseTabbedAttributes: mismatching Attribute count");
        return ajFalse;
    }

    ajListFree(&ulist);

    return ajTrue;
}
ID ajMartattributesParse
TY public
MO ajmart
LB ajaxdb
XX
DE Read a mart attributes buffer
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object holding mart info
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartattributesParse(AjPSeqin seqin)
{
    AjBool ret = ajTrue;
    
    if(!martBuffIsXML(seqin->Input->Filebuff))
    {
        ret = martParseTabbedAttributes(seqin);
    }
    else
        ajFatal("Looks like the new Biomart XML format for attributes "
                "has just been implemented. New function needed");
    
    return ret;
}
ID ajMartGetFilters
TY public
MO ajmart
LB ajaxdb
XX
DE Return filters given a mart dataset and a mart host/path/port
XX
PN [1]
PA u seqin AjPSeqin
PD "Sequence" input object
PX
PN [2]
PA r dataset const AjPStr
PD "Sequence" mart dataset name
PX
RT AjBool
RD true on success
RX
//


AjBool ajMartGetFilters(AjPSeqin seqin, const AjPStr dataset)
{
    AjPStr get       = NULL;

    AjPMartquery mq = NULL;
    AjPQuery qry = NULL;

    const char *vschema = NULL;
    
    AjBool force_write = ajFalse;
    ajuint httpcode = 0;

    qry = seqin->Input->Query;
    mq  = ajMartGetMartqueryPtr(seqin);
    
    if(!mq)
        return ajFalse;

    if(!mq->Marthost || !mq->Martpath)
    {
        ajWarn("ajMartGetFilters: Invalid Mart location Host=%S Path=%S",
               mq->Marthost,mq->Martpath);

        return ajFalse;
    }
    
    ajStrAssignS(&mq->Dsname, dataset);
    
    /* First check whether registry is in the cache */

    if(ajStrGetLen(mq->Cachedir) && (mq->Cacheflag & MART_CACHE_RD))
    {
        /* Try a cache read */
        if(!martReadCacheEntry(mq, &seqin->Input->Filebuff, MART_FILENTRY))
        {
            if(mq->Cacheflag & MART_CACHE_WTIFEMPTY)
                force_write = ajTrue;
            else
                force_write = ajFalse;
        }
        else
            return ajTrue;
    }

    /*
    ** Do the GET request
    */
    
    get       = ajStrNew();

    vschema = martGetVirtualSchema(dataset);

    ajFmtPrintS(&get, "%S?type=filters&dataset=%S&virtualSchema=%s",
                    mq->Martpath, dataset, vschema);

    ajFilebuffDel(&seqin->Input->Filebuff);

    seqin->Input->Filebuff = ajHttpRead(qry->DbHttpVer, qry->DbName,
                                        qry->DbProxy,
                                        mq->Marthost, mq->Martport, get);
    
    if(!seqin->Input->Filebuff)
	return ajFalse;

    httpcode = ajFilebuffHtmlNoheader(seqin->Input->Filebuff);

    if(httpcode)
    {
        ajWarn("ajMartGetFilters: HTTP code %u from '%S'",
               httpcode, get);
        return ajFalse;
    }

    if((ajStrGetLen(mq->Cachedir) && (mq->Cacheflag & MART_CACHE_WT)) ||
       force_write)
    {
        /* Write to cache */
        martWriteCacheEntry(mq, seqin->Input->Filebuff, MART_FILENTRY);
    }

    ajStrDel(&get);
    
    return ajTrue;
}
ID ajMartGetFiltersSchema
TY public
MO ajmart
LB ajaxdb
XX
DE Return filters given a mart dataset, a mart host/path/port and a schema
XX
PN [1]
PA u seqin AjPSeqin
PD "Sequence" input object
PX
PN [2]
PA r dataset const AjPStr
PD "Sequence" mart dataset name
PX
PN [3]
PA r schema const AjPStr
PD "Sequence" mart schema name
PX
RT AjBool
RD true on success
RX
//


AjBool ajMartGetFiltersSchema(AjPSeqin seqin, const AjPStr dataset,
                              const AjPStr schema)
{
    AjPStr get       = NULL;
    AjPMartquery mq = NULL;
    AjPQuery qry = NULL;

    const char *vschema = NULL;
    
    ajuint httpcode = 0;

    qry = seqin->Input->Query;
    mq  = ajMartGetMartqueryPtr(seqin);
    
    if(!mq)
        return ajFalse;

    if(!mq->Marthost || !mq->Martpath)
    {
        ajWarn("ajMartGetFiltersSchema: Invalid Mart location "
               "Host=%S Path=%S",
               mq->Marthost,mq->Martpath);

        return ajFalse;
    }

    /*
    ** Do the GET request
    */
    
    get       = ajStrNew();

    vschema = ajStrGetPtr(schema);
    
    ajFmtPrintS(&get, "GET %S?type=filters&dataset=%S&virtualSchema=%s",
	        mq->Martpath, dataset, vschema);


    ajFilebuffDel(&seqin->Input->Filebuff);

    seqin->Input->Filebuff = ajHttpRead(qry->DbHttpVer, qry->DbName,
                                        qry->DbProxy,
                                        mq->Marthost, mq->Martport, get);
    
    if(!seqin->Input->Filebuff)
	return ajFalse;

    httpcode = ajFilebuffHtmlNoheader(seqin->Input->Filebuff);
    if(httpcode)
    {
        ajWarn("ajMartGetFiltersSchema: HTTP code %u from '%S'",
               httpcode, get);
        return ajFalse;
    }

    ajStrDel(&get);
    
    return ajTrue;
}
ID ajMartGetFiltersRetry
TY public
MO ajmart
LB ajaxdb
XX
DE Retry a filters fetch using the main biomart registry site
DE This routine should only be called after an ajMartGetFilters
DE call followed by a ajMartfiltersParse has returned an error.
DE That typically means that an external server has not provided all
DE the filters fields, possibly due to outdated biomart software.
DE As the main biomart registry site contains all the metadata for
DE external marts (but not the marts themselves) it can be
DE queried using the same dataset. The true mart server should always
DE be tried first to minimise load on the main biomart site
XX
PN [1]
PA u seqin AjPSeqin
PD "Sequence" input object
PX
PN [2]
PA r dataset const AjPStr
PD "Sequence" mart dataset name
PX
RT AjBool
RD true on success
RX
//


AjBool ajMartGetFiltersRetry(AjPSeqin seqin, const AjPStr dataset)
{
    AjPMartquery mq = NULL;
    AjPStr orighost = NULL;
    AjPStr origpath = NULL;
    AjPStr schema   = NULL;
    
    ajuint origport = 0;
    AjBool ret = ajFalse;
    
    mq  = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    if(!mq->Marthost || !mq->Martpath)
    {
        ajWarn("ajMartGetFiltersRetry: No previous Mart queried.\n"
               "Invalid Mart location Host=%S Path=%S",
               mq->Marthost,mq->Martpath);

        return ajFalse;
    }

    ajMartFilterDel(&mq->Filters);

    mq->Filters = ajMartFilterNew();

    orighost = ajStrNew();
    origpath = ajStrNew();
 
    ajStrAssignS(&orighost, mq->Marthost);
    ajStrAssignS(&origpath, mq->Martpath);
    origport = mq->Martport;

    ajStrAssignC(&mq->Marthost, DEFAULT_BIOMART_MART_HOST);
    ajStrAssignC(&mq->Martpath, DEFAULT_BIOMART_MART_PATH);
    mq->Martport = DEFAULT_BIOMART_MART_PORT;

    schema = ajStrNewC("default");
    ret = ajMartGetFiltersSchema(seqin,dataset,schema);

    ajStrAssignS(&mq->Marthost, orighost);
    ajStrAssignS(&mq->Martpath, origpath);
    mq->Martport = origport;

    ajStrDel(&orighost);
    ajStrDel(&origpath);
    ajStrDel(&schema);
    
    return ret;
}
ID martParseTabbedFilters
TY static
MO ajmart
LB ajaxdb
XX
DE Parse filter information in the original tab-delimited format
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object holding mart info
PX
RT AjBool
RD True on success
RX
//


static AjBool martParseTabbedFilters(AjPSeqin seqin)
{
    AjPFilebuff buff = NULL;
    AjPStr line  = NULL;
    AjPStr token = NULL;
    
    AjPTable table = NULL;
    AjPMartFilter filt = NULL;
    AjPMartquery mq   = NULL;
    AjBool error = ajFalse;
    
    ajint pos = 0;
    
    ajuint n = 0;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;
    
    buff  = seqin->Input->Filebuff;
    line  = ajStrNew();
    token = ajStrNew();
    
    filt = mq->Filters;

    while(ajBuffreadLine(buff,&line))
    {
        ajDebug("martParseTabbedFilters '%S'", line);
        if(ajStrGetLen(line) < 10)
            continue;

        table = ajTablestrNew(REGTABGUESS);

        pos = 0;
        
        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedFilters: missing tab field (1)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"name",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajWarn("martParseTabbedFilters: missing tab field (2)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"displayName",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajDebug("martParseTabbedFilters: missing tab field (3)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"allowedValues",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajDebug("martParseTabbedFilters: missing tab field (4)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"unk",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajDebug("martParseTabbedFilters: missing tab field (5)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"page",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajDebug("martParseTabbedFilters: missing tab field (6)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"type",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajDebug("martParseTabbedFilters: missing tab field (7)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"value",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajDebug("martParseTabbedFilters: missing tab field (8)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"tableName",token);

        pos = martTabToToken(&token,line,pos);
        if(pos < 0)
        {
            ajDebug("martParseTabbedFilters: missing tab field (9)\n%S",line);
            error = ajTrue;
            break;
        }
        
        martTablePush(table,"columnName",token);

        ajListPushAppend(filt->Filter_read,(void *)table);
        
        ++filt->Nfilters;
    }
    
    ajStrDel(&line);
    ajStrDel(&token);

    if(error)
        return ajFalse;
    
    n = (ajint) ajListToarray(filt->Filter_read, (void ***) &filt->Filters);
    if(n != filt->Nfilters)
    {
        ajWarn("martParseTabbedFilters: mismatching Filter count");
        return ajFalse;
    }

    
    return ajTrue;
}
ID ajMartfiltersParse
TY public
MO ajmart
LB ajaxdb
XX
DE Parse a mart filters buffer
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object holding mart info
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartfiltersParse(AjPSeqin seqin)
{
    AjBool ret = ajTrue;
    
    if(!martBuffIsXML(seqin->Input->Filebuff))
        ret = martParseTabbedFilters(seqin);
    else
        ajFatal("Looks like the new Biomart XML format for filters "
                "has just been implemented. New function needed");
    
    return ret;
}
ID ajMartFilterMatch
TY public
MO ajmart
LB ajaxdb
XX
DE Associate filters with a given attribute table
XX
PN [1]
PA u atab AjPTable
PD Attribute table
PX
PN [2]
PA r filt const AjPMartFilter
PD filters
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartFilterMatch(AjPTable atab, const AjPMartFilter filt)
{
    AjPStr filterkey = NULL;
    AjPStr filterval = NULL;
    
    const AjPStr atableval  = NULL;
    const AjPStr acolumnval = NULL;
    const AjPStr ftableval  = NULL;
    const AjPStr fcolumnval = NULL;
    const AjPStr nameval = NULL;

    AjPTable ftab = NULL;
    AjBool firstval = ajTrue;
    
    
    ajuint i;
    ajuint nf = 0;
    
    
    filterkey = ajStrNewC("filters");

    filterval = ajStrNew();
    
    firstval = ajTrue;

    atableval  = ajTableFetchC(atab, "tableName");
    acolumnval = ajTableFetchC(atab, "columnName");

    nf = filt->Nfilters;
    
    for(i=0; i < nf; ++i)
    {
        ftab = filt->Filters[i];
        
        ftableval  = ajTableFetchC(ftab, "tableName");
        fcolumnval = ajTableFetchC(ftab, "columnName");

        if(!ajStrMatchS(atableval,ftableval) ||
           !ajStrMatchS(acolumnval,fcolumnval))
            continue;

        nameval  = ajTableFetchC(ftab, "name");

        if(firstval)
        {
            firstval = ajFalse;
            ajFmtPrintS(&filterval,"%S",nameval);
        }
        else
            ajFmtPrintAppS(&filterval,",%S",nameval);

    }

    ajTablePut(atab, (void *)filterkey, (void *)filterval);

    return ajTrue;
}
ID ajMartAssociate
TY public
MO ajmart
LB ajaxdb
XX
DE Associate filters with attributes thgat can use them
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object holding mart info
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartAssociate(AjPSeqin seqin)
{
    AjPMartquery mq = NULL;

    AjPMartAttribute att = NULL;
    AjPMartFilter filt   = NULL;

    AjPTable t = NULL;
    
    ajuint na = 0;

    ajuint i = 0;
    
    mq   = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;
    
    att  = mq->Atts;
    filt = mq->Filters;
    
    na = att->Natts;

    for(i=0; i < na; ++i)
    {
        t = att->Attributes[i];
        ajMartFilterMatch(t, filt);
    }
    
    return ajTrue;
}
ID ajMartMakeQueryXml
TY public
MO ajmart
LB ajaxdb
XX
DE Produce XML from an AjPMartqinfo object
XX
PN [1]
PA r qinfo const AjPMartqinfo
PD Mart qinfo object
PX
PN [2]
PA u seqin AjPSeqin
PD Seqin object
PX
RT void
RD 
RX
//


void ajMartMakeQueryXml(const AjPMartqinfo qinfo, AjPSeqin seqin)
{
    AjPMartquery mq = NULL;
    
    AjPStr tstr = NULL;
    ajuint i;
    ajuint j;
    ajuint len = 0;
    AjPList lst = NULL;
    
    mq   = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return;
    
    /* Create the XML header */
    
    ajFmtPrintS(&mq->Query,""
                ""
                "Hvirtualschema))
        ajFmtPrintAppS(&mq->Query,"virtualSchemaName = \"%S\" ",
                       qinfo->Hvirtualschema);

    if(ajStrGetLen(qinfo->Hformatter))
        ajFmtPrintAppS(&mq->Query,"formatter = \"%S\" ",
                       qinfo->Hformatter);

    if(ajStrGetLen(qinfo->Hversion))
        ajFmtPrintAppS(&mq->Query,"datasetConfigVersion = \"%S\" ",
                       qinfo->Hversion);

    if(qinfo->Hcount)
        ajFmtPrintAppS(&mq->Query,"count = \"1\" ");

    if(qinfo->Hheader)
        ajFmtPrintAppS(&mq->Query,"header = \"1\" ");
        
    if(qinfo->Hunique)
        ajFmtPrintAppS(&mq->Query,"uniqueRows = \"1\" ");
        
    if(qinfo->Hstamp)
        ajFmtPrintAppS(&mq->Query,"completionStamp = \"1\" ");

    ajFmtPrintAppS(&mq->Query,">");
    
    /* Loop over datasets */

    for(i=0; i < qinfo->Dnsets; ++i)
    {
        ajFmtPrintAppS(&mq->Query,"Query,"name = \"%S\" ",qinfo->Dsets[i]->Name);
        ajFmtPrintAppS(&mq->Query,"interface = \"%S\" ",
                       qinfo->Dsets[i]->Interface);
        ajFmtPrintAppS(&mq->Query,">");

        lst = qinfo->Dsets[i]->Filters;
        len = (ajuint) ajListGetLength(lst);
        for(j = 0; j < len; ++j)
        {
            ajListPop(lst,(void **)&tstr);
            ajFmtPrintAppS(&mq->Query,"",tstr);
            ajListPushAppend(lst,(void *) tstr);
        }
    
        lst = qinfo->Dsets[i]->Attributes;
        len = (ajuint) ajListGetLength(lst);
        for(j = 0; j < len; ++j)
        {
            ajListPop(lst,(void **)&tstr);
            ajFmtPrintAppS(&mq->Query,"",tstr);
            ajListPushAppend(lst,(void *) tstr);
        }

        ajFmtPrintAppS(&mq->Query,"");
    }

    ajFmtPrintAppS(&mq->Query,"");

    return;
}
ID martHttpEncode
TY static
MO ajmart
LB ajaxdb
XX
DE Replaces unsafe characters with hex codes for http GET
XX
PN [1]
PA u str AjPStr*
PD string to encode
PX
RT AjBool
RD true if length changed
RX
//


static AjBool martHttpEncode(AjPStr *str)
{
    const char *p = NULL;
    AjPStr tstr = NULL;
    AjBool ret;
    
    char c;
    static const char *excl="$&+,/:;=?@_-.";
    
    if(!str)
        return ajFalse;

    ajDebug("martHttpEncode '%S'\n", *str);

    p = ajStrGetPtr(*str);

    tstr = ajStrNew();
    
    
    while((c = *p))
    {
        if(strchr(excl,(int)c))
            ajStrAppendK(&tstr,c);
        else if((c>='A' && c <='Z') || (c>='a' && c<='z') || (c>='0' && c<='9'))
            ajStrAppendK(&tstr,c);
        else
            ajFmtPrintAppS(&tstr,"%%%-02x",(int)c);

        ++p;
    }

    ret = (ajStrGetLen(*str) == ajStrGetLen(tstr)) ? ajFalse : ajTrue;

    ajStrAssignS(str,tstr);

    ajStrDel(&tstr);
    
    ajDebug("    result %3B '%S'\n", ret, *str);

    return ret;
}
ID ajMartSendQuery
TY public
MO ajmart
LB ajaxdb
XX
DE Send a query, given biomart query xml, to a mart server host/path/port
XX
PN [1]
PA u seqin AjPSeqin
PD "Sequence" input object
PX
RT AjBool
RD true on success
RX
//


AjBool ajMartSendQuery(AjPSeqin seqin)
{
    AjPStr get       = NULL;

    AjPMartquery mq = NULL;
    AjPQuery qry = NULL;

    ajuint httpcode = 0;

    qry = seqin->Input->Query;
    mq  = ajMartGetMartqueryPtr(seqin);
    
    if(!mq)
        return ajFalse;

    if(!mq->Marthost || !mq->Martpath)
    {
        ajWarn("ajMartSendQuery: Invalid Mart location Host=%S Path=%S",
               mq->Marthost,mq->Martpath);

        return ajFalse;
    }

    if(!ajStrGetLen(mq->Query))
    {
        ajWarn("ajMartSendQuery: No biomart query specified");

        return ajFalse;
    }
    
    martHttpEncode(&mq->Query);
    
    /*
    ** Do the GET request
    */
    
    get       = ajStrNew();

    ajFmtPrintS(&get, "%S?query=%S", mq->Martpath, mq->Query);
    
    ajFilebuffDel(&seqin->Input->Filebuff);

    seqin->Input->Filebuff = ajHttpRead(qry->DbHttpVer, qry->DbName,
                                        qry->DbProxy,
                                        mq->Marthost, mq->Martport, get);
    
    if(!seqin->Input->Filebuff)
	return ajFalse;
 
    httpcode = ajFilebuffHtmlNoheader(seqin->Input->Filebuff);
    if(httpcode)
    {
        ajWarn("ajMartSendQuery: HTTP code %u from '%S'",
               httpcode, get);
        return ajFalse;
    }

    ajStrDel(&get);
    
    return ajTrue;
}
ID ajMartGetReghost
TY public
MO ajmart
LB ajaxdb
XX
DE Return the Registry host name
XX
PN [1]
PA r seqin const AjPSeqin
PD Seqin object
PX
RT const AjPStr
RD Registry host name or NULL
RX
//


const AjPStr ajMartGetReghost(const AjPSeqin seqin)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return NULL;

    return mq->Reghost;
}
ID ajMartGetRegpath
TY public
MO ajmart
LB ajaxdb
XX
DE Return the Registry path name
XX
PN [1]
PA r seqin const AjPSeqin
PD Seqin object
PX
RT const AjPStr
RD Registry host name or NULL
RX
//


const AjPStr ajMartGetRegpath(const AjPSeqin seqin)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return NULL;

    return mq->Regpath;
}
ID ajMartGetRegport
TY public
MO ajmart
LB ajaxdb
XX
DE Return the Registry port
XX
PN [1]
PA r seqin const AjPSeqin
PD Seqin object
PX
RT ajuint
RD Registry port or 0
RX
//


ajuint ajMartGetRegport(const AjPSeqin seqin)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return 0;

    return mq->Regport;
}
ID ajMartGetMarthost
TY public
MO ajmart
LB ajaxdb
XX
DE Return the Mart host name
XX
PN [1]
PA r seqin const AjPSeqin
PD Seqin object
PX
RT const AjPStr
RD Mart host name or NULL
RX
//


const AjPStr ajMartGetMarthost(const AjPSeqin seqin)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return NULL;

    return mq->Marthost;
}
ID ajMartGetMartpath
TY public
MO ajmart
LB ajaxdb
XX
DE Return the Mart path name
XX
PN [1]
PA r seqin const AjPSeqin
PD Seqin object
PX
RT const AjPStr
RD Mart host name or NULL
RX
//


const AjPStr ajMartGetMartpath(const AjPSeqin seqin)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return NULL;

    return mq->Martpath;
}
ID ajMartGetMartport
TY public
MO ajmart
LB ajaxdb
XX
DE Return the Mart port
XX
PN [1]
PA r seqin const AjPSeqin
PD Seqin object
PX
RT ajuint
RD Mart port or 0
RX
//


ajuint ajMartGetMartport(const AjPSeqin seqin)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return 0;

    return mq->Martport;
}
ID ajMartSetReghostC
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Registry host name
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA r reghost const char*
PD Registry name
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartSetReghostC(AjPSeqin seqin, const char *reghost)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    ajStrAssignC(&mq->Reghost,reghost);
    
    return ajTrue;
}
ID ajMartSetRegpathC
TY public
MO ajmart
LB ajaxdb
XX
DE Return the Registry path name
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA r regpath const char*
PD Registry path
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartSetRegpathC(AjPSeqin seqin, const char *regpath)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    ajStrAssignC(&mq->Regpath,regpath);
    
    return ajTrue;
}
ID ajMartSetReghostS
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Registry host name
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA r reghost const AjPStr
PD Registry name
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartSetReghostS(AjPSeqin seqin, const AjPStr reghost)
{
    if(ajMartSetReghostC(seqin,reghost->Ptr))
        return ajTrue;

    return ajFalse;
}
ID ajMartSetRegpathS
TY public
MO ajmart
LB ajaxdb
XX
DE Return the Registry path name
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA r regpath const AjPStr
PD Registry path
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartSetRegpathS(AjPSeqin seqin, const AjPStr regpath)
{
    if(ajMartSetRegpathC(seqin,regpath->Ptr))
        return ajTrue;

    return ajFalse;
}
ID ajMartSetRegport
TY public
MO ajmart
LB ajaxdb
XX
DE Return the Registry port
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA r regport ajuint
PD Registry port
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartSetRegport(AjPSeqin seqin, ajuint regport)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    mq->Regport = regport;

    return ajTrue;
}
ID ajMartSetMarthostC
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Mart host name
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA r marthost const char*
PD Mart name
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartSetMarthostC(AjPSeqin seqin, const char *marthost)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    ajStrAssignC(&mq->Marthost,marthost);
    
    return ajTrue;
}
ID ajMartSetMartpathC
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Mart path name
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA r martpath const char*
PD Mart path
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartSetMartpathC(AjPSeqin seqin, const char *martpath)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    ajStrAssignC(&mq->Martpath,martpath);
    
    return ajTrue;
}
ID ajMartSetMarthostS
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Mart host name
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA r marthost const AjPStr
PD Mart name
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartSetMarthostS(AjPSeqin seqin, const AjPStr marthost)
{
    if(ajMartSetMarthostC(seqin,marthost->Ptr))
        return ajTrue;

    return ajFalse;
}
ID ajMartSetMartpathS
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Mart path name
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA r martpath const AjPStr
PD Mart path
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartSetMartpathS(AjPSeqin seqin, const AjPStr martpath)
{
    if(ajMartSetMartpathC(seqin,martpath->Ptr))
        return ajTrue;

    return ajFalse;
}
ID ajMartSetMartport
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Mart port
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA r martport ajuint
PD Mart port
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartSetMartport(AjPSeqin seqin, ajuint martport)
{
    AjPMartquery mq = NULL;

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    mq->Martport = martport;

    return ajTrue;
}
ID ajMartFixRegistry
TY public
MO ajmart
LB ajaxdb
XX
DE Try to fill in missing registry fields
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
RT void
RD 
RX
//


void ajMartFixRegistry(AjPSeqin seqin)
{
    if(!ajStrGetLen(ajMartGetReghost(seqin)))
        ajMartSetReghostC(seqin,DEFAULT_BIOMART_REG_HOST);

    if(!ajStrGetLen(ajMartGetRegpath(seqin)))
        ajMartSetRegpathC(seqin,DEFAULT_BIOMART_REG_PATH);

    if(!ajMartGetRegport(seqin))
        ajMartSetRegport(seqin,DEFAULT_BIOMART_REG_PORT);

    return;
}
ID ajMartFixMart
TY public
MO ajmart
LB ajaxdb
XX
DE Try to fill in missing mart fields
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
RT void
RD True if valid
RX
//


void ajMartFixMart(AjPSeqin seqin)
{
    if(!ajStrGetLen(ajMartGetMarthost(seqin)))
        ajMartSetMarthostC(seqin,DEFAULT_BIOMART_MART_HOST);

    if(!ajStrGetLen(ajMartGetMartpath(seqin)))
        ajMartSetMartpathC(seqin,DEFAULT_BIOMART_MART_PATH);

    if(!ajMartGetMartport(seqin))
        ajMartSetMartport(seqin,DEFAULT_BIOMART_MART_PORT);

    return;
}
ID martMatchAttribute
TY static
MO ajmart
LB ajaxdb
XX
DE Test whether a given name is found in the attributes tables
XX
PN [1]
PA r name const AjPStr
PD name
PX
PN [2]
PA r atts const AjPMartAttribute
PD Attribute tables
PX
RT AjBool
RD True if match found
RX
//


static AjBool martMatchAttribute(const AjPStr name, const AjPMartAttribute atts)
{
    ajuint i;
    ajuint n;
    AjBool ret = ajFalse;
    const AjPStr val = NULL;
    
    n = atts->Natts;

    for(i = 0; i < n; ++i)
    {
        val = ajTableFetchC(atts->Attributes[i], "name");

        if(ajStrMatchS(val, name))
        {
            ret = ajTrue;
            break;
        }
    }

    return ret;
}
ID martMatchFilter
TY static
MO ajmart
LB ajaxdb
XX
DE Test whether a given name is found in the filters tables
XX
PN [1]
PA r name const AjPStr
PD name
PX
PN [2]
PA r filts const AjPMartFilter
PD Filter tables
PX
RT AjBool
RD True if match found
RX
//


static AjBool martMatchFilter(const AjPStr name, const AjPMartFilter filts)
{
    ajuint i;
    ajuint n;
    AjBool ret = ajFalse;
    const AjPStr val = NULL;
    
    n = filts->Nfilters;

    for(i = 0; i < n; ++i)
    {
        val = ajTableFetchC(filts->Filters[i], "name");

        if(ajStrMatchS(val, name))
        {
            ret = ajTrue;
            break;
        }
    }

    return ret;
}
ID ajMartAttachMartquery
TY public
MO ajmart
LB ajaxdb
XX
DE Add a mart query object to a seqin object
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA u mq AjPMartquery
PD Martquery object
PX
RT void
RD 
RX
//


void ajMartAttachMartquery(AjPSeqin seqin, AjPMartquery mq)
{
    AjPQuery qry = NULL;

    if(!seqin)
        return;

    qry = seqin->Input->Query;

    if(!qry)
        return;

    qry->QryData = (void *) mq;

    return;
}
ID ajMartGetMartqueryPtr
TY public
MO ajmart
LB ajaxdb
XX
DE Return a seqin Query pointer
XX
PN [1]
PA r seqin const AjPSeqin
PD Seqin object
PX
RT AjPMartquery
RD Martquery
RX
//


AjPMartquery ajMartGetMartqueryPtr(const AjPSeqin seqin)
{
    AjPQuery qry = NULL;
    AjPMartquery mq = NULL;

    if(!seqin)
        return NULL;

    qry = seqin->Input->Query;

    if(!qry)
        return NULL;

    mq = (AjPMartquery) qry->QryData;

    return mq;
}
ID ajStrtokQuotR
TY public
MO ajmart
LB ajaxdb
XX
DE Reentrant strtok that disallows tokenisation at delimiter
DE characters if they are within quotation chracters
DE e.g. tokenisation of  -token1="a,b,c",-token2
DE will yield   -token1="a,b,c"   and   -token2
DE if the delimstr contains ',' and the quotstr contains '"'
DE
DE The source string is unchanged
XX
PN [1]
PA r srcstr const char*
PD Source string
PX
PN [2]
PA r delimstr const char*
PD Delimiter string
PX
PN [3]
PA r quotstr const char*
PD Allowable quotes(s)
PX
PN [4]
PA r ptrptr const char**
PD Saved position in srcstr
PX
PN [5]
PA w buf AjPStr*
PD Results buffer (the token)
PX
RT char*
RD Pointer to start of token (in buf) or NULL
RX
//


char* ajStrtokQuotR(const char *srcstr, const char *delimstr,
                    const char *quotstr, const char **ptrptr,
                    AjPStr *buf)
{
    const char *p = NULL;
    const char *q = NULL;
    AjBool quoted = ajFalse;
    ajuint len    = 0;
    char c = '\0';

    if(!*buf)
        *buf = ajStrNew();

    if(srcstr != NULL)
        p = srcstr;
    else
        p = *ptrptr;

    ajStrAssignC(buf,"");
    
    if(!*p)
        return NULL;

    len = strspn(p,delimstr);
    p += len;
    if(!*p)
        return NULL;

    q = p;
    quoted = ajFalse;

    while(*q)
    {
        if(strchr(quotstr,(int)*q))
        {
            c = *q;
            ++q;
            quoted = ajTrue;

            while(*q && *q != c)
                ++q;

            if(*q == c)
            {
                ++q;
                quoted = ajFalse;
            }

            continue;
        }

        if(strchr(delimstr,(int) *q))
            break;

        ++q;
    }

    if(!*q && quoted)
    {
        *ptrptr = q;
        return NULL;
    }

    ajStrAssignSubC(buf,p,0,q-p-1);
    *ptrptr = q;

    return ajStrGetuniquePtr(buf);
}
ID ajMartParseParameters
TY public
MO ajmart
LB ajaxdb
XX
DE Parses attribute and filter strings and load a given Qinfo object
DE with them
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Query object
PX
PN [2]
PA r atts const AjPStr
PD Attributes
PX
PN [3]
PA r filts const AjPStr
PD Filters
PX
PN [4]
PA r idx ajuint
PD dataset index
PX
RT AjBool
RD False on parsing or loading error
RX
//


AjBool ajMartParseParameters(AjPMartqinfo qinfo, const AjPStr atts,
                             const AjPStr filts, ajuint idx)
{
    AjPStr buf = NULL;
    AjPStr pushstr = NULL;
    AjPStr namstr = NULL;
    AjPStr valstr = NULL;
    
    const char *ptrsave = NULL;
    const char *p = NULL;
    const char *q = NULL;
    
    static const char *delimstr=",\t\n";
    static const char *quotstr="\"'";
    const char *tokstr = NULL;
    ajuint natts = 0;
    
    buf    = ajStrNew();
    namstr = ajStrNew();
    valstr = ajStrNew();
    
    tokstr = ajStrGetPtr(atts);
    
    while(ajStrtokQuotR(tokstr,delimstr,quotstr,&ptrsave,&buf))
    {
        tokstr = NULL;
        pushstr = ajStrNewS(buf);
        ajStrTrimWhite(&pushstr);
        
        ajListPushAppend(qinfo->Dsets[idx]->Attributes,(void *)pushstr);
        ++natts;
    }

    if(!natts)
    {
        ajStrDel(&buf);
        ajStrDel(&namstr);
        ajStrDel(&valstr);
        
        ajWarn("ajMartParseParameters: No parseable attributes given [%S]",
               atts);

        return ajFalse;
    }


    tokstr = ajStrGetPtr(filts);
    
    while(ajStrtokQuotR(tokstr,delimstr,quotstr,&ptrsave,&buf))
    {
        tokstr = NULL;
        ajStrTrimWhite(&buf);

        p = ajStrGetPtr(buf);
        
        if(ajStrPrefixC(buf,"!"))  /* Negated boolean */
        {
            if((q=strchr(p,(int)'=')))
            {                      /* Bad negated boolean */
                if(q == p+1)
                {
                    ajWarn("ajMartParseParameters: Illegal filter [%S]",buf);
                    ajStrDel(&buf);
                    ajStrDel(&namstr);
                    ajStrDel(&valstr);
                    
                    return ajFalse;
                }
                    
                ajWarn("ajMartParseParameters: Illegal assignment for "
                       "negated boolean filter [%S]\n"
                       "Stripping assignment and adding boolean",buf);
                ++p;
                ajStrAssignSubC(&namstr,p,0,q-p-1);

                pushstr = ajStrNew();
                ajFmtPrintS(&pushstr,"\"%S\" excluded = \"1\"",namstr);
                ajListPushAppend(qinfo->Dsets[idx]->Filters,(void *)pushstr);
            }
            else
            {                      /* Good negated boolean */
                if(ajStrGetLen(buf) < 2)
                {
                    ajWarn("ajMartParseParameters: Illegal filter [%S]",buf);
                    ajStrDel(&buf);
                    ajStrDel(&namstr);
                    ajStrDel(&valstr);
                    
                    return ajFalse;
                }

                ++p;
                ajStrAssignC(&namstr,p);

                pushstr = ajStrNew();
                ajFmtPrintS(&pushstr,"\"%S\" excluded = \"1\"",namstr);
                ajListPushAppend(qinfo->Dsets[idx]->Filters,(void *)pushstr);
            }

            continue;
        }


        /* Test for assignment type of filter '=' */
        
        if((q=strchr(p,(int)'=')))
        {
            if(q == p)
            {
                ajWarn("ajMartParseParameters: Illegal filter [%S]",buf);
                ajStrDel(&buf);
                ajStrDel(&namstr);
                ajStrDel(&valstr);
                
                return ajFalse;
            }

            ajStrAssignSubC(&namstr,p,0,q-p-1);
            p = q;
            while(*p == '=')
                ++p;

            ajStrAssignC(&valstr,p);
            ajStrTrimC(&valstr,"\"'");

            pushstr = ajStrNew();
            ajFmtPrintS(&pushstr,"\"%S\" value = \"%S\"",namstr,valstr);
            ajListPushAppend(qinfo->Dsets[idx]->Filters,(void *)pushstr);

            continue;
        }
        
        /* Must be a non-negated boolean */
        
        pushstr = ajStrNew();
        ajFmtPrintS(&pushstr,"\"%s\" excluded = \"0\"",p);
        ajListPushAppend(qinfo->Dsets[idx]->Filters,(void *)pushstr);
    }

    ajStrDel(&buf);
    ajStrDel(&namstr);
    ajStrDel(&valstr);
    
    return ajTrue;
}
ID ajMartSetQuerySchemaC
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Qinfo Virtual Schema field
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Seqin object
PX
PN [2]
PA r schema const char*
PD Virtual schema
PX
RT void
RD 
RX
//


void ajMartSetQuerySchemaC(AjPMartqinfo qinfo, const char *schema)
{
    if(!qinfo)
        return;

    ajStrAssignC(&qinfo->Hvirtualschema, schema);

    return;
}
ID ajMartSetQueryVersionC
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Qinfo Software Version field
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Seqin object
PX
PN [2]
PA r version const char*
PD Version
PX
RT void
RD 
RX
//


void ajMartSetQueryVersionC(AjPMartqinfo qinfo, const char *version)
{
    if(!qinfo)
        return;

    ajStrAssignC(&qinfo->Hversion, version);

    return;
}
ID ajMartSetQueryFormatC
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Qinfo Format field
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Seqin object
PX
PN [2]
PA r format const char*
PD Format
PX
RT void
RD 
RX
//


void ajMartSetQueryFormatC(AjPMartqinfo qinfo, const char *format)
{
    if(!qinfo)
        return;

    ajStrAssignC(&qinfo->Hformatter, format);

    return;
}
ID ajMartSetQueryCount
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Qinfo Count (unique ID count only) field
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Seqin object
PX
PN [2]
PA r count AjBool
PD count
PX
RT void
RD 
RX
//


void ajMartSetQueryCount(AjPMartqinfo qinfo, AjBool count)
{
    if(!qinfo)
        return;

    qinfo->Hcount = count;

    return;
}
ID ajMartSetQueryHeader
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Qinfo Header (column title) field
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Seqin object
PX
PN [2]
PA r header AjBool
PD Header
PX
RT void
RD 
RX
//


void ajMartSetQueryHeader(AjPMartqinfo qinfo, AjBool header)
{
    if(!qinfo)
        return;

    qinfo->Hheader = header;

    return;
}
ID ajMartSetQueryUnique
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Qinfo Unique rows field
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Seqin object
PX
PN [2]
PA r unique AjBool
PD Unique
PX
RT void
RD 
RX
//


void ajMartSetQueryUnique(AjPMartqinfo qinfo, AjBool unique)
{
    if(!qinfo)
        return;

    qinfo->Hunique = unique;

    return;
}
ID ajMartSetQueryStamp
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Qinfo Completion Stamp ([success]) field
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Seqin object
PX
PN [2]
PA r stamp AjBool
PD Stamp
PX
RT void
RD 
RX
//


void ajMartSetQueryStamp(AjPMartqinfo qinfo, AjBool stamp)
{
    if(!qinfo)
        return;

    qinfo->Hstamp = stamp;

    return;
}
ID ajMartSetQueryVerify
TY public
MO ajmart
LB ajaxdb
XX
DE Set the Qinfo verification field to test that given atts & filts
DE exist in the dataset
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Seqin object
PX
PN [2]
PA r verify AjBool
PD verify flag
PX
RT void
RD 
RX
//


void ajMartSetQueryVerify(AjPMartqinfo qinfo, AjBool verify)
{
    if(!qinfo)
        return;

    qinfo->Verify = verify;

    return;
}
ID ajMartSetQueryDatasetName
TY public
MO ajmart
LB ajaxdb
XX
DE Set a Qinfo dataset name field
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Seqin object
PX
PN [2]
PA r name const AjPStr
PD Name
PX
PN [3]
PA r idx ajuint
PD Dataset number (0->n-1)
PX
RT void
RD 
RX
//


void ajMartSetQueryDatasetName(AjPMartqinfo qinfo, const AjPStr name,
                               ajuint idx)
{
    if(!qinfo)
        return;

    if(!qinfo->Dsets)
        return;

    if(idx > qinfo->Dnsets - 1)
        return;
    
    ajStrAssignS(&qinfo->Dsets[idx]->Name, name);
    
    return;
}
ID ajMartSetQueryDatasetInterfaceC
TY public
MO ajmart
LB ajaxdb
XX
DE Set a Qinfo dataset interface field
XX
PN [1]
PA u qinfo AjPMartqinfo
PD Seqin object
PX
PN [2]
PA r iface const char*
PD Interface
PX
PN [3]
PA r idx ajuint
PD Dataset number (0->n-1)
PX
RT void
RD 
RX
//


void ajMartSetQueryDatasetInterfaceC(AjPMartqinfo qinfo, const char *iface,
                                     ajuint idx)
{
    if(!qinfo)
        return;

    if(!qinfo->Dsets)
        return;

    if(idx > qinfo->Dnsets - 1)
        return;
    
    ajStrAssignC(&qinfo->Dsets[idx]->Interface, iface);
    
    return;
}
ID ajMartCheckQinfo
TY public
MO ajmart
LB ajaxdb
XX
DE Try to fill in missing fields and check user attributes/filters
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA u qinfo AjPMartqinfo
PD Mart qinfo object
PX
RT AjBool
RD True if valid
RX
//


AjBool ajMartCheckQinfo(AjPSeqin seqin, AjPMartqinfo qinfo)
{
    AjPMartquery mq = NULL;
    AjPStr listval = NULL;
    AjPStr fname   = NULL;
    
    AjPMartAttribute att = NULL;
    AjPMartFilter filt    = NULL;
    AjPMartdsinfo dsinfo = NULL;
    

    ajuint i;
    ajuint j;
    ajuint n;
    

    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    if(!qinfo)
        return ajFalse;
    
    ajMartFixMart(seqin);
    

    if(!qinfo->Verify)
        return ajTrue;

    ajMartFixRegistry(seqin);

    for(i=0; i < qinfo->Dnsets; ++i)
    {
        /*
        ** Delete Existing atts. They may be from a previous query.
        ** Also, they could interfere with multi-dataset queries.
        */

        if(mq->Atts)
        {
            ajMartAttributeDel(&mq->Atts);
            mq->Atts = ajMartAttributeNew();
        }

        dsinfo = qinfo->Dsets[i];
        
        if(!ajMartGetAttributes(seqin,dsinfo->Name))
        {
            ajWarn("ajMartCheckQinfo: Cannot retrieve attributes for dataset "
                   "%S",dsinfo->Name);

            return ajFalse;
        }

        ajMartattributesParse(seqin);

        att = mq->Atts;

        n = (ajuint) ajListGetLength(dsinfo->Attributes);

        for(j=0; j < n; ++j)
        {
            ajListPop(dsinfo->Attributes,(void **)&listval);
            ajListPushAppend(dsinfo->Attributes,(void *)listval);

            if(!martMatchAttribute(listval, att))
            {
                ajWarn("ajMartCheckQinfo: Attribute %S not in dataset %S",
                       listval,dsinfo->Name);

                return ajFalse;
            }
        }

    }
    

    fname = ajStrNew();

    
    for(i=0; i < qinfo->Dnsets; ++i)
    {
        /*
        ** Delete Existing filts. They may be from a previous query.
        ** Also, they could interfere with multi-dataset queries.
        */

        if(mq->Filters)
        {
            ajMartFilterDel(&mq->Filters);
            mq->Filters = ajMartFilterNew();
        }
        
        dsinfo = qinfo->Dsets[i];

        if(!ajMartGetFilters(seqin,dsinfo->Name))
        {
            ajWarn("ajMartCheckQinfo: Cannot retrieve filters for dataset "
                   "%S",dsinfo->Name);


            ajStrDel(&fname);
            
            return ajFalse;
        }

        ajMartfiltersParse(seqin);        

        filt = mq->Filters;
        
        n = (ajuint) ajListGetLength(dsinfo->Filters);

        for(j=0; j < n; ++j)
        {
            ajListPop(dsinfo->Filters,(void **)&listval);
            ajListPushAppend(dsinfo->Filters,(void *)listval);
            ajFmtScanS(listval,"%S",&fname);
            ajStrTrimC(&fname,"\"\n ");
            
            if(!martMatchFilter(fname, filt))
            {
                ajWarn("ajMartCheckQinfo: Filter %S not in dataset %S",
                       fname,dsinfo->Name);

                ajStrDel(&fname);

                return ajFalse;
            }
        }
        

        
    }
    
    ajStrDel(&fname);

    return ajTrue;
}
ID ajMartGetConfiguration
TY public
MO ajmart
LB ajaxdb
XX
DE Return config info given a mart dataset and a mart host/path/port
XX
PN [1]
PA u seqin AjPSeqin
PD "Sequence" input object
PX
PN [2]
PA r dataset const AjPStr
PD "Sequence" mart dataset name
PX
RT AjBool
RD true on success
RX
//


AjBool ajMartGetConfiguration(AjPSeqin seqin, const AjPStr dataset)
{
    AjPStr get       = NULL;

    AjPMartquery mq = NULL;
    AjPQuery qry = NULL;

    const char *vschema = NULL;
    
    AjBool force_write = ajFalse;
    ajuint httpcode = 0;
   
    qry = seqin->Input->Query;
    mq  = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return ajFalse;

    if(!mq->Marthost || !mq->Martpath)
    {
        ajWarn("ajMartGetConfiguration: Invalid Mart location Host=%S Path=%S",
               mq->Marthost,mq->Martpath);

        return ajFalse;
    }
    
    ajStrAssignS(&mq->Dsname, dataset);
    
    /* First check whether registry is in the cache */

    if(ajStrGetLen(mq->Cachedir) && (mq->Cacheflag & MART_CACHE_RD))
    {
        /* Try a cache read */
        if(!martReadCacheEntry(mq, &seqin->Input->Filebuff, MART_CONENTRY))
        {
            if(mq->Cacheflag & MART_CACHE_WTIFEMPTY)
                force_write = ajTrue;
            else
                force_write = ajFalse;
        }
        else
            return ajTrue;
    }

    /*
    ** Do the GET request
    */
    
    get       = ajStrNew();


    vschema = martGetVirtualSchema(dataset);
    
    ajFmtPrintS(&get, "%S?type=configuration&dataset=%S"
		"&virtualSchema=%s", mq->Martpath, dataset, vschema);

    ajFilebuffDel(&seqin->Input->Filebuff);

    seqin->Input->Filebuff = ajHttpRead(qry->DbHttpVer, qry->DbName,
                                        qry->DbProxy,
                                        mq->Marthost, mq->Martport, get);
    
    if(!seqin->Input->Filebuff)
	return ajFalse;

    httpcode = ajFilebuffHtmlNoheader(seqin->Input->Filebuff);
    if(httpcode)
    {
        ajWarn("ajMartGetConfiguration: HTTP code %u from '%S'",
               httpcode, get);
        return ajFalse;
    }

    if((ajStrGetLen(mq->Cachedir) && (mq->Cacheflag & MART_CACHE_WT)) ||
       force_write)
    {
        /* Write to cache */
        martWriteCacheEntry(mq, seqin->Input->Filebuff, MART_CONENTRY);
    }

    ajStrDel(&get);
    
    return ajTrue;
}
ID ajMartconfigurationParse
TY public
MO ajmart
LB ajaxdb
XX
DE Parse a mart configuration buffer
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object holding mart info
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartconfigurationParse(AjPSeqin seqin)
{
    AjPMartquery pmq = NULL;
    AjBool ret = ajFalse;
    
    if(!seqin)
        return ajFalse;

    pmq = ajMartGetMartqueryPtr(seqin);
    
    if(!pmq)
        return ajFalse;

    pmq->Config = ajDomImplementationCreateDocument(NULL,NULL,NULL);
    if(!ajDomReadFilebuff(pmq->Config, seqin->Input->Filebuff))
        ret = ajTrue;
    
    return ret;
}
ID martAttributePageCompar
TY static
MO ajmart
LB ajaxdb
XX
DE Sort function based on attribute page names
XX
PN [1]
PA r a const void*
PD First table
PX
PN [2]
PA r b const void*
PD Second table
PX
RT int
RD comparison of page strings
RX
//


static int martAttributePageCompar(const void *a, const void *b)
{
    const AjPTable t1;
    const AjPTable t2;

    const AjPStr v1 = NULL;
    const AjPStr v2 = NULL;
    
    t1 = *((AjPTable const *) a);
    t2 = *((AjPTable const *) b);
    
    v1 = ajTableFetchC(t1, "page");
    v2 = ajTableFetchC(t2, "page");

    return strcmp(v1->Ptr,v2->Ptr);
}
ID ajMartattributesPageSort
TY public
MO ajmart
LB ajaxdb
XX
DE Sort attributes based on page name
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object holding attribute info
PX
RT AjBool
RD True on success
RX
//


AjBool ajMartattributesPageSort(AjPSeqin seqin)
{
    AjPMartAttribute att = NULL;
    AjPMartquery pmq = NULL;

    pmq = ajMartGetMartqueryPtr(seqin);
    
    if(!pmq)
        return ajFalse;

    att = pmq->Atts;

    qsort((void *)att->Attributes,att->Natts,sizeof(AjPTable),
          martAttributePageCompar);
    
    return ajTrue;
}
ID ajMartNameIsNucC
TY public
MO ajmart
LB ajaxdb
XX
DE Test whether name matches any of the set of known nucleic acid
DE biomart terms
XX
PN [1]
PA r name const char*
PD Name
PX
RT AjBool
RD True if nucleic acid name match
RX
//


AjBool ajMartNameIsNucC(const char *name)
{
    ajuint i;
    const char *p = NULL;
    
    i = 0;

    while((p = martNucTerms[i]))
    {
        if(ajCharMatchCaseC(p, name))
            return ajTrue;

        ++i;
    }

    return ajFalse;
}
ID ajMartNameIsProtC
TY public
MO ajmart
LB ajaxdb
XX
DE Test whether name matches any of the set of known protein
DE biomart terms
XX
PN [1]
PA r name const char*
PD Name
PX
RT AjBool
RD True if nucleic acid name match
RX
//


AjBool ajMartNameIsProtC(const char *name)
{
    ajuint i;
    const char *p = NULL;
    
    i = 0;

    while((p = martProtTerms[i]))
    {
        if(ajCharMatchCaseC(p, name))
            return ajTrue;

        ++i;
    }

    return ajFalse;
}
ID ajMartTableNameIsNuc
TY public
MO ajmart
LB ajaxdb
XX
DE Test whether table 'name' value  matches any of the set of known nucleic
DE acid biomart terms
XX
PN [1]
PA r t const AjPTable
PD Table
PX
RT AjBool
RD True if nucleic acid name match
RX
//


AjBool ajMartTableNameIsNuc(const AjPTable t)
{
    const AjPStr value  = NULL;
    const char *p = NULL;
    
    value = ajTableFetchC(t, "name");

    if(value)
    {
        p = ajStrGetPtr(value);

        if(ajMartNameIsNucC(p))
            return ajTrue;
    }
    
    return ajFalse;
}
ID ajMartTableNameIsProt
TY public
MO ajmart
LB ajaxdb
XX
DE Test whether table 'name' value  matches any of the set of known protein
DE biomart terms
XX
PN [1]
PA r t const AjPTable
PD Table
PX
RT AjBool
RD True if protein name match
RX
//


AjBool ajMartTableNameIsProt(const AjPTable t)
{
    const AjPStr value  = NULL;
    const char *p = NULL;
    
    value = ajTableFetchC(t, "name");

    if(value)
    {
        p = ajStrGetPtr(value);

        if(ajMartNameIsProtC(p))
            return ajTrue;
    }
    
    return ajFalse;
}
ID ajMartCheckHeader
TY public
MO ajmart
LB ajaxdb
XX
DE Return an array of attribute names in the order in which
DE they are returned by the biomart server.
DE Requires that the header flag was set and that an attribute
DE query has previously loaded to MartAttribute field.
XX
PN [1]
PA u seqin AjPSeqin
PD Seqin object
PX
PN [2]
PA u qinfo AjPMartqinfo
PD Mart qinfo object
PX
RT AjPStr*
RD Array of attribute names terminated by a NULL entry.
RD or NULL if mapping cannot be done
RX
//


AjPStr* ajMartCheckHeader(AjPSeqin seqin, AjPMartqinfo qinfo)
{
    AjPMartquery mq = NULL;
    
    AjPMartAttribute att = NULL;
    AjPFilebuff buff = NULL;
    AjPStr line = NULL;
    AjPStr *ret = NULL;

    AjPStr keyname  = NULL;
    AjPStr keydname = NULL;
    const AjPStr value    = NULL;

    AjPStrTok handle = NULL;
    AjPStr token     = NULL;
    
    
    ajulong nfields = 0;
    
    ajulong i;
    ajuint j;
    
    mq = ajMartGetMartqueryPtr(seqin);

    if(!mq)
        return NULL;

    if(!qinfo)
        return NULL;

    if(!qinfo->Hheader)
        return NULL;

    att = mq->Atts;
    if(!att)
        return NULL;
    
    buff = seqin->Input->Filebuff;
    
    if(!buff)
        return NULL;

    line = ajStrNew();

    if(!ajBuffreadLine(buff, &line))
    {
        ajStrDel(&line);
        return NULL;
    }

    nfields = ajStrCalcCountK(line,'\t');
    ++nfields;

    AJCNEW0(ret,nfields + 1);

    ret[nfields] = NULL;

    keyname  = ajStrNewC("name");
    keydname = ajStrNewC("displayName");

    ajStrTokenAssignC(&handle, line, "\t\n");
    token = ajStrNew();
    
    for(i = 0; i < nfields; ++i)
    {
        ret[i] = ajStrNew();

        ajStrTokenNextParseNoskip(&handle,&token);
        
        for(j = 0; j < att->Natts; ++j)
        {
            value = ajTableFetchS(att->Attributes[j], keydname);

            if(ajStrMatchS(value,token))
            {
                value = ajTableFetchS(att->Attributes[j], keyname);
                ajStrAssignS(&ret[i],value);
                break;
            }
        }

        if(j == att->Natts)
            ajErr("ajMartCheckHeader: Cannot match column %S",token);
    }


    ajStrDel(&keyname);
    ajStrDel(&keydname);
    ajStrDel(&line);
    ajStrDel(&token);
    ajStrTokenDel(&handle);
    
    return ret;
}
ID martVerifyOrCreateCacheDir
TY static
MO ajmart
LB ajaxdb
XX
DE Check whether cache directory exists, try to create it if not.
XX
PN [1]
PA u dir AjPStr
PD directory
PX
RT AjBool
RD true on success
RX
//


static AjBool martVerifyOrCreateCacheDir(AjPStr dir)
{
    if(!ajFilenameExistsDir(dir))
        if(!ajSysCommandMakedirS(dir))
            return ajFalse;

    return ajTrue;
}
ID martEncodeHname
TY static
MO ajmart
LB ajaxdb
XX
DE Encode host/path/port into a suitable name for a directory
XX
PN [1]
PA w str AjPStr*
PD Encoded directory name
PX
PN [2]
PA r host const AjPStr
PD Host
PX
PN [3]
PA r path const AjPStr
PD Path
PX
PN [4]
PA r port ajuint
PD Port
PX
RT void
RD 
RX
//


static void martEncodeHname(AjPStr *str, const AjPStr host, const AjPStr path,
                            ajuint port)
{
    AjPStr ho = NULL;
    AjPStr pa = NULL;

    ho = ajStrNew();
    pa = ajStrNew();


    ajStrAssignS(&ho, host);
    ajStrAssignS(&pa, path);

    ajStrExchangeKK(&ho,'.','%');
    ajStrExchangeKK(&pa,'/','^');

    ajFmtPrintS(str,"%S+%S+%u",ho,pa,port);
    
    ajStrDel(&ho);
    ajStrDel(&pa);

    return;
}
ID ajMartDecodeHname
TY public
MO ajmart
LB ajaxdb
XX
DE Decode directory name into host/path/port
XX
PN [1]
PA r dir const AjPStr
PD Encoded directory name
PX
PN [2]
PA w host AjPStr*
PD Host
PX
PN [3]
PA w path AjPStr*
PD Path
PX
PN [4]
PA w port ajuint*
PD Port
PX
RT void
RD 
RX
//


void ajMartDecodeHname(const AjPStr dir, AjPStr *host, AjPStr *path,
                       ajuint *port)
{
    AjPStr str = NULL;
    AjPStrTok handle = NULL;

    str = ajStrNew();

    handle = ajStrTokenNewC(dir, "+");    

    if(!ajStrTokenNextParse(&handle, &str))
    {
        ajErr("ajMartDecodeHname: Bad directory name [%S]",dir);
        return;
    }

    ajStrExchangeKK(&str,'%','.');
    ajStrAssignS(host,str);

    if(!ajStrTokenNextParse(&handle, &str))
    {
        ajErr("ajMartDecodeHname: Bad directory name [%S]",dir);
        return;
    }

    ajStrExchangeKK(&str,'^','/');
    ajStrAssignS(path,str);

    if(!ajStrTokenNextParse(&handle, &str))
    {
        ajErr("ajMartDecodeHname: Bad directory name [%S]",dir);
        return;
    }

    ajStrToUint(str,port);
    
    ajStrDel(&str);
    ajStrTokenDel(&handle);

    return;
}
ID martWriteCacheEntry
TY static
MO ajmart
LB ajaxdb
XX
DE Write buffer to cache file
XX
PN [1]
PA u mq AjPMartquery
PD Query object containing host & cache info
PX
PN [2]
PA u buff AjPFilebuff
PD File buffer
PX
PN [3]
PA r type ajint
PD Type of information held in the file buffer
PX
RT AjBool
RD true on success
RX
//


static AjBool martWriteCacheEntry(AjPMartquery mq, AjPFilebuff buff, ajint type)
{
    AjPStr dir   = NULL;
    AjPStr hname = NULL;
    AjPStr fname = NULL;
    AjPStr line  = NULL;
    
    AjPFile outf = NULL;
    
    if(!martVerifyOrCreateCacheDir(mq->Cachedir))
    {
        ajErr("Unable to create requested cache directory [%S]",
              mq->Cachedir);
        return ajFalse;
    }


    hname = ajStrNew();
    dir   = ajStrNew();
    
    switch(type)
    {
        case MART_REGENTRY:
            martEncodeHname(&hname,mq->Reghost,mq->Regpath,mq->Regport);
            
            ajFmtPrintS(&dir,"%S%c%S",mq->Cachedir,SLASH_CHAR,
                        hname);

            ajStrDel(&hname);
            
            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&dir);

                return ajFalse;
            }

            fname = ajStrNew();

            ajFmtPrintS(&fname,"%S%cRegistry",dir,SLASH_CHAR);
            ajStrDel(&dir);

            if(!(outf = ajFileNewOutNameS(fname)))
            {
                ajErr("Unable to open registry cache file for write [%S]",
                      fname);

                ajStrDel(&fname);

                return ajFalse;
            }

            ajStrDel(&fname);

            line = ajStrNew();

            while(ajBuffreadLine(buff,&line))
                ajFmtPrintF(outf,"%S",line);

            ajFileClose(&outf);
            ajStrDel(&line);

            ajFilebuffReset(buff);
            
            break;

        case MART_DSENTRY:
            martEncodeHname(&hname,mq->Marthost,mq->Martpath,mq->Martport);
            
            ajFmtPrintS(&dir,"%S%c%S",mq->Cachedir,SLASH_CHAR,
                        hname);

            /* Create server directory if necessary */
            
            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }

            /* Create mart directory if necessary */
            ajFmtPrintS(&dir,"%S%c%S%ceMART_%S",mq->Cachedir,SLASH_CHAR,
                        hname,SLASH_CHAR,mq->Mart);

            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }

            ajStrDel(&hname);
            fname = ajStrNew();

            ajFmtPrintS(&fname,"%S%cDatasets",dir,SLASH_CHAR);
            ajStrDel(&dir);

            if(!(outf = ajFileNewOutNameS(fname)))
            {
                ajErr("Unable to open registry cache file for write [%S]",
                      fname);

                ajStrDel(&fname);

                return ajFalse;
            }

            ajStrDel(&fname);

            line = ajStrNew();

            while(ajBuffreadLine(buff,&line))
                ajFmtPrintF(outf,"%S",line);

            ajFileClose(&outf);
            ajStrDel(&line);

            ajFilebuffReset(buff);
            
            break;

        case MART_ATTENTRY:
            martEncodeHname(&hname,mq->Marthost,mq->Martpath,mq->Martport);

            ajFmtPrintS(&dir,"%S%cDatasets",mq->Cachedir,SLASH_CHAR);

            /* Create Datasets directory if necessary */
            
            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }
            
            /* Create host directory if necessary */

            ajFmtPrintS(&dir,"%S%cDatasets%c%S",mq->Cachedir,SLASH_CHAR,
                        SLASH_CHAR,hname);
            
            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }

            /* Create dataset host directory if necessary */
            ajFmtPrintS(&dir,"%S%cDatasets%c%S%c%S",mq->Cachedir,
                        SLASH_CHAR, SLASH_CHAR,
                        hname, SLASH_CHAR, mq->Dsname);

            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }

            ajStrDel(&hname);
            fname = ajStrNew();

            ajFmtPrintS(&fname,"%S%cAttributes",dir,SLASH_CHAR);
            ajStrDel(&dir);

            if(!(outf = ajFileNewOutNameS(fname)))
            {
                ajErr("Unable to open registry cache file for write [%S]",
                      fname);

                ajStrDel(&fname);

                return ajFalse;
            }

            ajStrDel(&fname);

            line = ajStrNew();

            while(ajBuffreadLine(buff,&line))
                ajFmtPrintF(outf,"%S",line);

            ajFileClose(&outf);
            ajStrDel(&line);

            ajFilebuffReset(buff);
            
            break;

         case MART_FILENTRY:
            martEncodeHname(&hname,mq->Marthost,mq->Martpath,mq->Martport);

            ajFmtPrintS(&dir,"%S%cDatasets",mq->Cachedir,SLASH_CHAR);

            /* Create Datasets directory if necessary */
            
            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }
            
            /* Create host directory if necessary */

            ajFmtPrintS(&dir,"%S%cDatasets%c%S",mq->Cachedir,SLASH_CHAR,
                        SLASH_CHAR,hname);
            
            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }

            /* Create dataset host directory if necessary */
            ajFmtPrintS(&dir,"%S%cDatasets%c%S%c%S",mq->Cachedir,
                        SLASH_CHAR, SLASH_CHAR,
                        hname, SLASH_CHAR, mq->Dsname);

            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }

            ajStrDel(&hname);
            fname = ajStrNew();

            ajFmtPrintS(&fname,"%S%cFilters",dir,SLASH_CHAR);
            ajStrDel(&dir);

            if(!(outf = ajFileNewOutNameS(fname)))
            {
                ajErr("Unable to open registry cache file for write [%S]",
                      fname);

                ajStrDel(&fname);

                return ajFalse;
            }

            ajStrDel(&fname);

            line = ajStrNew();

            while(ajBuffreadLine(buff,&line))
                ajFmtPrintF(outf,"%S",line);

            ajFileClose(&outf);
            ajStrDel(&line);

            ajFilebuffReset(buff);
            
            break;

         case MART_CONENTRY:
            martEncodeHname(&hname,mq->Marthost,mq->Martpath,mq->Martport);

            ajFmtPrintS(&dir,"%S%cDatasets",mq->Cachedir,SLASH_CHAR);

            /* Create Datasets directory if necessary */
            
            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }
            
            /* Create host directory if necessary */

            ajFmtPrintS(&dir,"%S%cDatasets%c%S",mq->Cachedir,SLASH_CHAR,
                        SLASH_CHAR,hname);
            
            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }

            /* Create dataset host directory if necessary */
            ajFmtPrintS(&dir,"%S%cDatasets%c%S%c%S",mq->Cachedir,
                        SLASH_CHAR, SLASH_CHAR,
                        hname, SLASH_CHAR, mq->Dsname);

            if(!martVerifyOrCreateCacheDir(dir))
            {
                ajErr("Unable to create requested cache directory [%S]",
                      dir);

                ajStrDel(&hname);
                ajStrDel(&dir);

                return ajFalse;
            }

            ajStrDel(&hname);
            fname = ajStrNew();

            ajFmtPrintS(&fname,"%S%cConfiguration",dir,SLASH_CHAR);
            ajStrDel(&dir);

            if(!(outf = ajFileNewOutNameS(fname)))
            {
                ajErr("Unable to open registry cache file for write [%S]",
                      fname);

                ajStrDel(&fname);

                return ajFalse;
            }

            ajStrDel(&fname);

            line = ajStrNew();

            while(ajBuffreadLine(buff,&line))
                ajFmtPrintF(outf,"%S",line);

            ajFileClose(&outf);
            ajStrDel(&line);

            ajFilebuffReset(buff);
            
            break;

        default:
            ajStrDel(&hname);
            ajStrDel(&dir);
            break;
    }
    

    return ajTrue;
}
ID martReadCacheEntry
TY static
MO ajmart
LB ajaxdb
XX
DE Read cache file into file buffer
XX
PN [1]
PA u mq AjPMartquery
PD Query object containing host & cache info
PX
PN [2]
PA u buff AjPFilebuff*
PD File buffer
PX
PN [3]
PA r type ajint
PD Type of information held in the file buffer
PX
RT AjBool
RD true on success
RX
//


static AjBool martReadCacheEntry(AjPMartquery mq, AjPFilebuff *buff,
                                 ajint type)
{
    AjPStr hname = NULL;
    AjPStr fname = NULL;
    AjPStr line  = NULL;
    
    AjPFile inf = NULL;
    
    hname = ajStrNew();
    fname = ajStrNew();

    switch(type)
    {
        case MART_REGENTRY:
            martEncodeHname(&hname,mq->Reghost,mq->Regpath,mq->Regport);
            
            ajFmtPrintS(&fname,"%S%c%S%cRegistry",mq->Cachedir,SLASH_CHAR,
                        hname,SLASH_CHAR);

            ajStrDel(&hname);
            
            if(!(inf = ajFileNewInNameS(fname)))
            {
                ajStrDel(&fname);

                return ajFalse;
            }

            ajStrDel(&fname);

            line = ajStrNew();

            ajFilebuffDel(buff);
            *buff = ajFilebuffNewNofile();
            
            while(ajReadline(inf,&line))
                ajFilebuffLoadS(*buff,line);
            
            ajFileClose(&inf);
            ajStrDel(&line);

            ajFilebuffReset(*buff);

            break;

        case MART_DSENTRY:
            martEncodeHname(&hname,mq->Marthost,mq->Martpath,mq->Martport);
            
            ajFmtPrintS(&fname,"%S%c%S%ceMART_%S%cDatasets",mq->Cachedir,
                        SLASH_CHAR,hname,SLASH_CHAR,mq->Mart,SLASH_CHAR);

            ajStrDel(&hname);
            
            if(!(inf = ajFileNewInNameS(fname)))
            {
                ajStrDel(&fname);

                return ajFalse;
            }

            ajStrDel(&fname);

            line = ajStrNew();

            ajFilebuffDel(buff);
            *buff = ajFilebuffNewNofile();
            
            while(ajReadline(inf,&line))
                ajFilebuffLoadS(*buff,line);
            
            ajFileClose(&inf);
            ajStrDel(&line);

            ajFilebuffReset(*buff);

            break;

        case MART_ATTENTRY:
            martEncodeHname(&hname,mq->Marthost,mq->Martpath,mq->Martport);
            
            ajFmtPrintS(&fname,"%S%cDatasets%c%S%c%S%cAttributes",mq->Cachedir,
                        SLASH_CHAR, SLASH_CHAR, hname, SLASH_CHAR, mq->Dsname,
                        SLASH_CHAR);

            ajStrDel(&hname);
            
            if(!(inf = ajFileNewInNameS(fname)))
            {
                ajStrDel(&fname);

                return ajFalse;
            }

            ajStrDel(&fname);

            line = ajStrNew();

            ajFilebuffDel(buff);
            *buff = ajFilebuffNewNofile();
            
            while(ajReadline(inf,&line))
                ajFilebuffLoadS(*buff,line);
            
            ajFileClose(&inf);
            ajStrDel(&line);

            ajFilebuffReset(*buff);

            break;

        case MART_FILENTRY:
            martEncodeHname(&hname,mq->Marthost,mq->Martpath,mq->Martport);
            
            ajFmtPrintS(&fname,"%S%cDatasets%c%S%c%S%cFilters",mq->Cachedir,
                        SLASH_CHAR, SLASH_CHAR, hname, SLASH_CHAR, mq->Dsname,
                        SLASH_CHAR);

            ajStrDel(&hname);
            
            if(!(inf = ajFileNewInNameS(fname)))
            {
                ajStrDel(&fname);

                return ajFalse;
            }

            ajStrDel(&fname);

            line = ajStrNew();

            ajFilebuffDel(buff);
            *buff = ajFilebuffNewNofile();
            
            while(ajReadline(inf,&line))
                ajFilebuffLoadS(*buff,line);
            
            ajFileClose(&inf);
            ajStrDel(&line);

            ajFilebuffReset(*buff);

            break;

        case MART_CONENTRY:
            martEncodeHname(&hname,mq->Marthost,mq->Martpath,mq->Martport);
            
            ajFmtPrintS(&fname,"%S%cDatasets%c%S%c%S%cConfiguration",
                        mq->Cachedir, SLASH_CHAR, SLASH_CHAR, hname,
                        SLASH_CHAR, mq->Dsname, SLASH_CHAR);

            ajStrDel(&hname);
            
            if(!(inf = ajFileNewInNameS(fname)))
            {
                ajStrDel(&fname);

                return ajFalse;
            }

            ajStrDel(&fname);

            line = ajStrNew();

            ajFilebuffDel(buff);
            *buff = ajFilebuffNewNofile();
            
            while(ajReadline(inf,&line))
                ajFilebuffLoadS(*buff,line);
            
            ajFileClose(&inf);
            ajStrDel(&line);

            ajFilebuffReset(*buff);

            break;

        default:
            ajStrDel(&hname);
            ajStrDel(&fname);
            break;
    }
    

    return ajTrue;
}
ID ajRound
TY public
MO ajmath
LB core
XX
DE Rounds an integer to be a multiple of a given number.
XX
PN [1]
PA r i ajint
PD Integer to round.
PX
PN [2]
PA r vround ajint
PD Rounding multiple.
PX
RT ajint
RD Result.
RX
//


ajint ajRound(ajint i, ajint vround)
{
    return vround * ((ajint)(i+vround-1)/vround);
}
ID ajRoundFloat
TY public
MO ajmath
LB core
XX
DE Rounds a floating point number to have bits free for cumulative addition
XX
PN [1]
PA r a float
PD Float to round.
PX
PN [2]
PA r nbits ajint
PD Number of bits to free.
PX
RT float
RD Result.
RX
//


float ajRoundFloat(float a, ajint nbits)
{
    double w;
    double x;
    double y;
    double z;
    double b;
    double c;
    
    ajint i;
    ajint bitsused;

    /* save 16 bits for cumulative error */
    bitsused = FLT_MANT_DIG - nbits;
    /* usually leave 8 bits */
    if(bitsused < 8)
	bitsused = 8;

    /* a is between 0.5 and 1.0 */
    x = frexp(a, &i);
    /* i is the power of two */

    /* multiply by 2**n, convert to an integer, divide again */
    /* so we only keep n (or whatever) bits */

    y = ldexp(x, bitsused);		/* multiply by 2**n */
    z = modf(y, &w);		        /* change to an integer + remainder */

    if(z > 0.5)
        w += 1.0;		        /* round up ?*/

    if(z < -0.5)
        w -= 1.0;		        /* round down? */

    b = ldexp(w, -bitsused);		/* divide by 2**n */
    c = ldexp(b, i);	                /* divide by the orig. power of two */

    /*  ajDebug("\najRoundFloat(%.10e) c: %.10e bitsused: %d\n",
                a, c, bitsused);
	ajDebug("       x: %f i: %d y: %f w: %.1f\n", x, i, y, w);*/

    return (float) c;
}
ID ajCvtRecToPol
TY public
MO ajmath
LB core
XX
DE Converts Cartesian coordinates to polar
XX
PN [1]
PA r x float
PD X coordinate
PX
PN [2]
PA r y float
PD Y coordinate
PX
PN [3]
PA w radius float*
PD Radius
PX
PN [4]
PA w angle float*
PD Angle
PX
RT void
RD 
RX
//


void ajCvtRecToPol(float x, float y, float *radius, float *angle)
{
    *radius = (float) sqrt((double)(x*x+y*y));
    *angle  = (float) ajCvtRadToDeg((float)atan2((double)y,(double)x));

    return;
}
ID ajCvtPolToRec
TY public
MO ajmath
LB core
XX
DE Converts polar coordinates to Cartesian
XX
PN [1]
PA r radius float
PD Radius
PX
PN [2]
PA r angle float
PD Angle
PX
PN [3]
PA w x float*
PD X coordinate
PX
PN [4]
PA w y float*
PD Y coordinate
PX
RT void
RD 
RX
//


void ajCvtPolToRec(float radius, float angle, float *x, float *y)
{
    *x = radius*(float)cos((double)ajCvtDegToRad(angle));
    *y = radius*(float)sin((double)ajCvtDegToRad(angle));

    return;
}
ID ajCvtDegToRad
TY public
MO ajmath
LB core
XX
DE Converts degrees to radians
XX
PN [1]
PA r degrees float
PD Degrees
PX
RT float
RD Radians
RX
//


float ajCvtDegToRad(float degrees)
{
    return degrees*(float)(AJM_PI/180.0);
}
ID ajCvtRadToDeg
TY public
MO ajmath
LB core
XX
DE Converts radians to degrees
XX
PN [1]
PA r radians float
PD Radians
PX
RT float
RD Degrees
RX
//


float ajCvtRadToDeg(float radians)
{
    return radians*(float)(180.0/AJM_PI);
}
ID ajCvtGaussToProb
TY public
MO ajmath
LB core
XX
DE Returns a probability given a Gaussian distribution
XX
PN [1]
PA r mean float
PD mean
PX
PN [2]
PA r sd float
PD sd
PX
PN [3]
PA r score float
PD score
PX
RT double
RD probability
RX
//


double ajCvtGaussToProb(float mean, float sd, float score)
{
    return pow(AJM_E,(double)(-0.5*((score-mean)/sd)*((score-mean)/sd)))
	/ (sd * (float)2.0 * AJM_PI);
}
ID ajMathGmean
TY public
MO ajmath
LB core
XX
DE Calculate a geometric mean
XX
PN [1]
PA r s const float*
PD array of values
PX
PN [2]
PA r n ajint
PD number of values
PX
RT float
RD geometric mean
RX
//


float ajMathGmean(const float *s, ajint n)
{
    float x;
    ajint i;

    for(i=0,x=1.0;i= 0 ? seed : -seed) % 10000 + 1;
    iy = 2*ix+1;
    iz = 3*ix+1;

    for(i = -11; i < 101; ++i)
    {
        if(i >= 0)
            aj_rand_poly[i] = floor(AjRandomXmod*x);

        ix = (171*ix) % 30269;
        iy = (172*iy) % 30307;
        iz = (170*iz) % 30323;
        x = ((double)ix)/30269.0+((double)iy)/30307.0+((double)iz)/30323.0;
        x = x-floor(x);
    }

    aj_rand_other = floor(AjRandomYmod*x)/AjRandomYmod;
    aj_rand_index = 0;

    return;
}
ID ajRandomNumber
TY public
MO ajmath
LB core
XX
DE Generate a pseudo-random number between 0-32767
XX
RT ajint
RD Random number
RX
//


ajint ajRandomNumber(void)
{
    double td;
    ajint  rn;
    
    td = floor(ajRandomDouble()*32768.0);
    rn = (ajint) td;

    return rn;
}
ID ajRandomDouble
TY public
MO ajmath
LB core
XX
DE Generate a random number between 0-1.0
DE
DE Based on dprand and sdprand and used with the permission of the
DE author....
DE Copyright (C) 1992  N.M. Maclaren
DE Copyright (C) 1992  The University of Cambridge
DE
DE This software may be reproduced and used freely, provided that all
DE users of it agree that the copyright holders are not liable for any
DE damage or injury caused by use of this software and that this condition
DE is passed onto all subsequent recipients of the software, whether
DE modified or not.
XX
RT double
RD Random number
RX
//


double ajRandomDouble(void)
{
    static double offset = 1.0/AjRandomYmod;
    static double xmod2  = 2.0*AjRandomXmod;
    static double xmod4  = 4.0*AjRandomXmod;

    ajint n;
    double x, y;

    /*
     *  This returns a uniform (0,1) random number, with extremely good
     *  uniformity properties.  It assumes that double precision provides
     *  at least 33 bits of accuracy, and uses a power of two base.
     */

    if(!aj_rand_i)
	ajRandomSeed();

    /*
     *  See [Knuth] for why this implements the algorithm described in the
     *  paper.
     *  Note that this code is tuned for machines with fast double precision,
     *  but slow multiply and divide; many, many other options are possible.
     */

    if((n = aj_rand_index-64) < 0)
	n += 101;

    x = aj_rand_poly[aj_rand_index]+aj_rand_poly[aj_rand_index];
    x = xmod4-aj_rand_poly[n]-aj_rand_poly[n]-x-x-aj_rand_poly[aj_rand_index];

    if(x <= 0.0)
    {
        if(x < -AjRandomXmod)
	    x += xmod2;

        if(x < 0.0)
	    x += AjRandomXmod;
    }
    else
    {
        if(x >= xmod2)
	{
            x = x-xmod2;

            if(x >= AjRandomXmod)
		x -= AjRandomXmod;
        }

        if(x >= AjRandomXmod)
	    x -= AjRandomXmod;
    }

    aj_rand_poly[aj_rand_index] = x;

    if(++aj_rand_index >= 101)
	aj_rand_index = 0;

    /*
     *  Add in the second generator modulo 1, and force to be non-zero.
     *  The restricted ranges largely cancel themselves out.
     */

    do
    {
        y = 37.0*aj_rand_other+offset;
        aj_rand_other = y-floor(y);
    }
    while(E_FPZERO(aj_rand_other,U_DEPS));

    if((x = x/AjRandomXmod+aj_rand_other) >= 1.0)
	x -= 1.0;


    return x+AjRandomTiny;
}
ID spcrc64calctab
TY static
MO ajmath
LB core
XX
DE Initialise the crc table.
DE Polynomial x64+x4+x3+x1+1
XX
PN [1]
PA w crctab unsigned long long*
PD CRC lookup table
PX
RT void
RD 
RX
//


static void spcrc64calctab(unsigned long long *crctab)
{
    unsigned long long v;
    ajint i;
    ajint j;

    for(i=0;i>1);
#else
		v = 0xd800000000000000 ^ (v>>1);
#endif
	    else
		v >>= 1;
	crctab[i] = v;
    }
}
ID ajMathCrc32
TY public
MO ajmath
LB core
XX
DE Calculates the SwissProt style CRC32 checksum for a protein sequence.
DE This seems to be a bit reversal of a standard CRC32 checksum.
XX
PN [1]
PA r seq const AjPStr
PD Sequence as a string
PX
RT ajuint
RD CRC32 checksum.
RX
//


ajuint ajMathCrc32(const AjPStr seq)
{
    register ajulong crc;
    ajint c;
    const char* cp;
    static ajint calls = 0;

    if(!calls)
    {
	spcrc32gen();
	calls = 1;
    }

    cp = ajStrGetPtr(seq);

    crc = 0xFFFFFFFFL;

    while( *cp )
    {
	c = toupper((ajint) *cp);
	crc = ((crc >> 8) & 0x00FFFFFFL) ^ seqCrcTable[ (crc^c) & 0xFF ];
	cp++;
    }

    ajDebug("ajMathCrc32 calculated %08lX\n", crc);

    return (ajuint) crc;
}
ID spcrc32gen
TY static
MO ajmath
LB core
XX
DE Generates data for a CRC32 calculation in a static data structure.
XX
RT void
RD 
RX
//


static void spcrc32gen(void)
{
    ajulong crc;
    ajulong poly;
    ajint   i;
    ajint   j;

    poly = 0xEDB88320L;

    for(i=0; i<256; i++)
    {
	crc = i;

	for(j=8; j>0; j--)
	    if(crc&1)
		crc = (crc >> 1) ^ poly;
	    else
		crc >>= 1;

	seqCrcTable[i] = crc;
    }

    return;
}
ID ajMathCrc64
TY public
MO ajmath
LB core
XX
DE Calculate 64-bit crc
XX
PN [1]
PA r thys const AjPStr
PD sequence
PX
RT unsigned long long
RD 64-bit CRC
RX
//


unsigned long long ajMathCrc64(const AjPStr thys)
{
    static ajint initialised = 0;
    static unsigned long long crctab[AJCRC64LEN];
    unsigned long long crc;
    ajint i;
    ajint len;
    const char *p = NULL;
    

    if(!initialised)
    {
	spcrc64calctab(crctab);
	++initialised;
    }

#ifndef WIN32
    crc = 0ULL;
#else
    crc = 0U;
#endif
    p = ajStrGetPtr(thys);
    len = ajStrGetLen(thys);
    
    for(i=0;i>8);

    return crc;
}
ID ajCvtSposToPos
TY public
MO ajmath
LB core
XX
DE Converts a string position into a true position. If ipos is negative,
DE it is counted from the end of the string rather than the beginning.
XX
PN [1]
PA r len size_t
PD String length.
PX
PN [2]
PA r ipos ajlong
PD Position (0 start, negative from the end).
PX
RT size_t
RD string position between 0 and (length minus 1).
RX
//


size_t ajCvtSposToPos(size_t len, ajlong ipos)
{
    return ajCvtSposToPosStart(len, 0, ipos);
}
ID ajCvtSposToPosStart
TY public
MO ajmath
LB core
XX
DE Converts a position into a true position. If ipos is negative,
DE it is counted from the end of the string rather than the beginning.
DE
DE imin is a minimum relative position.
DE Usually this is the start position when the end of a range
DE is being tested.
XX
PN [1]
PA r len size_t
PD maximum length.
PX
PN [2]
PA r imin size_t
PD Start position (0 start, no negative values).
PX
PN [3]
PA r ipos ajlong
PD Position (0 start, negative from the end).
PX
RT size_t
RD string position between 0 and (length minus 1).
RX
//


size_t ajCvtSposToPosStart(size_t len, size_t imin, ajlong ipos)
{
    ajlong jpos;
    ajlong jmin = imin;
    ajlong jlen = len;

    if(ipos < 0)
	jpos = len + ipos;
    else
	jpos = ipos;

    if(jpos >= jlen)
	jpos = len - 1;

    if(jpos < jmin)
	jpos = imin;

    return (size_t) jpos;
}
ID ajNumLengthDouble
TY public
MO ajmath
LB core
XX
DE Returns the length of a number written as an integer
XX
PN [1]
PA r dnumber double
PD Double precision value
PX
RT ajuint
RD Number of digits
RX
//


ajuint ajNumLengthDouble(double dnumber)
{
    double dnum;
    ajuint ilen = 1;
    double td = 0.;

    dnum = fabs(dnumber);

    if(dnum >= 10.0)
    {
	td = log10(dnum);
	ilen += (ajuint) td;
    }

    if(dnumber < 0.0)
	ilen++;

    return ilen;
}
ID ajNumLengthFloat
TY public
MO ajmath
LB core
XX
DE Returns the length of a number written as an integer
XX
PN [1]
PA r fnumber float
PD Single precision value
PX
RT ajuint
RD Number of digits
RX
//


ajuint ajNumLengthFloat(float fnumber)
{
    double dnum;
    ajuint ilen = 1;
    double td = 0.;
    
    dnum = fabs((double)fnumber);


    if(dnum >= 10.0)
    {
	td = log10(dnum);
	ilen += (ajuint) td;
    }

    if(fnumber < 0.0)
	ilen++;

    return ilen;
}
ID ajNumLengthInt
TY public
MO ajmath
LB core
XX
DE Returns the length of a number written as an integer
XX
PN [1]
PA r inumber ajlong
PD Integer
PX
RT ajuint
RD Number of digits
RX
//


ajuint ajNumLengthInt(ajlong inumber)
{
    ajuint ilen = 1;
    ajulong maxnum = ULONG_MAX/10;
    ajulong i;
    ajulong iabs;

    if(inumber < 0)
	iabs = -inumber;
    else
	iabs = inumber;

    if(!iabs)
	return ilen;

    if(inumber < 0)
	ilen++;		/* space for the sign */

    for(i=10;i= i)
	    ilen++;
	else
	    break;
    }

    return ilen;
}
ID ajNumLengthUint
TY public
MO ajmath
LB core
XX
DE Returns the length of a number written as an integer
XX
PN [1]
PA r inumber ajulong
PD Unsigned integer
PX
RT ajuint
RD Number of digits
RX
//


ajuint ajNumLengthUint(ajulong inumber)
{
    ajuint ilen = 1;
    ajulong maxnum = ULONG_MAX/10;
    ajulong i;

    if(!inumber)
	return ilen;

    for(i=10;i= i)
	    ilen++;
	else
	    return ilen;
    }

    return ilen;
}
ID ajMatrixNew
TY public
MO ajmatrices
LB core
XX
DE Creates a new, zero matrix from an array of strings and a matrix name. If
DE the matrix is a residue substitution matrix then each string would be a
DE defined sequence character.
DE
DE The matrix comparison value table Matrix is created and initialised
DE with zeroes.
XX
PN [1]
PA r codes const AjPPStr
PD Matrix labels, e.g. valid sequence
PD character codes
PX
PN [2]
PA r n ajint
PD Number of labels
PX
PN [3]
PA r filename const AjPStr
PD Matrix filename
PX
RT AjPMatrix
RD New matrix, or NULL if codes, n or filename are 0.
RX
//


AjPMatrix ajMatrixNew(const AjPPStr codes, ajint n, const AjPStr filename)
{
    ajint     i   = 0;
    AjPMatrix ret = NULL;
    ajint nsize;

    if((!n) || (!codes) || (!filename))
	return NULL;

    nsize = n + 1;

    AJNEW0(ret);

    ajStrAssignS(&ret->Name, filename);

    AJCNEW0(ret->Codes, n);

    for(i=0; iCodes[i] = ajStrNew();
    
    for(i=0; iCodes[i], codes[i]);

    ret->Size = nsize;

    AJCNEW0(ret->Matrix, nsize);

    for(i=0; iMatrix[i], nsize);

    ret->Cvt = ajSeqcvtNewStr(codes, n);

    return ret;
}
ID ajMatrixNewAsym
TY public
MO ajmatrices
LB core
XX
DE Creates a new, zero asymmetrical matrix from two array of strings and a
DE matrix name. If the matrix is a substitution matrix then each string would
DE be a defined code, e.g. sequence character.
DE
DE The matrix comparison value table Matrix is created and initialised
DE with zeroes.
XX
PN [1]
PA r codes const AjPPStr
PD Matrix column labels, e.g. valid sequence
PD character codes
PX
PN [2]
PA r n ajint
PD Number of column labels
PX
PN [3]
PA r rcodes const AjPPStr
PD Matrix row labels, e.g. valid sequence
PD character codes
PX
PN [4]
PA r rn ajint
PD Number of row labels
PX
PN [5]
PA r filename const AjPStr
PD Matrix filename
PX
RT AjPMatrix
RD New matrix, or NULL if codes, n or filename are 0.
RX
//


AjPMatrix ajMatrixNewAsym(const AjPPStr codes, ajint n, 
			  const AjPPStr rcodes, ajint rn, 
			  const AjPStr filename)
{
    ajint     i   = 0;
    AjPMatrix ret = NULL;
    ajint nsize;
    ajint rnsize;

    if((!n) || (!codes) || (!filename))
	return NULL;

    nsize  = n + 1;
    rnsize = rn + 1;

    AJNEW0(ret);

    ajStrAssignS(&ret->Name, filename);

    AJCNEW0(ret->Codes, n);

    for(i=0; iCodes[i] = ajStrNew();
    
    for(i=0; iCodes[i], codes[i]);

    ret->Size  = nsize;
    AJCNEW0(ret->CodesRow, rn);

    for(i=0; iCodesRow[i] = ajStrNew();
    
    for(i=0; iCodesRow[i], rcodes[i]);

    ret->SizeRow = rnsize;
    AJCNEW0(ret->Matrix, rnsize);

    for(i=0; iMatrix[i], nsize);

    ret->Cvt = ajSeqcvtNewStrAsym(codes, n, rcodes, rn);

    return ret;
}
ID ajMatrixfNew
TY public
MO ajmatrices
LB core
XX
DE Creates a new, zero matrix from an array of strings and a matrix name. If
DE the matrix is a residue substitution matrix then each string would be a
DE defined sequence character.
DE
DE The matrix comparison value table Matrix is created and initialised
DE with zeroes.
XX
PN [1]
PA r codes const AjPPStr
PD Matrix labels,
PD e.g. valid sequence char codes
PX
PN [2]
PA r n ajint
PD Number of labels
PX
PN [3]
PA r filename const AjPStr
PD Matrix filename
PX
RT AjPMatrixf
RD New matrix, or NULL if codes, n or filename are 0.
RX
//


AjPMatrixf ajMatrixfNew(const AjPPStr codes, ajint n, const AjPStr filename)
{
    ajint i = 0;
    AjPMatrixf ret = 0;
    ajint nsize;
 
    if((!n) || (!codes) || (!filename))
	return NULL;

    nsize = n + 1;

    AJNEW0(ret);

    ajStrAssignS(&ret->Name, filename);

    AJCNEW0(ret->Codes, n);

    for(i=0; iCodes[i] = ajStrNew();

    for(i=0; iCodes[i], codes[i]);

    ret->Size = nsize;
    AJCNEW0(ret->Matrixf, nsize);

    for(i=0; iMatrixf[i], nsize);

    ret->Cvt = ajSeqcvtNewStr(codes, n);

    return ret;
}
ID ajMatrixfNewAsym
TY public
MO ajmatrices
LB core
XX
DE Creates a new, zero asymmetrical matrix from an array of strings and a
DE matrix name. If the matrix is a residue substitution matrix then each
DE string would be a defined sequence character.
DE
DE The matrix comparison value table Matrix is created and initialised
DE with zeroes.
XX
PN [1]
PA r codes const AjPPStr
PD Matrix labels,
PD e.g. valid sequence char codes
PX
PN [2]
PA r n ajint
PD Number of labels
PX
PN [3]
PA r rcodes const AjPPStr
PD Matrix row labels, e.g. valid sequence
PD character codes.
PX
PN [4]
PA r rn ajint
PD Number of row labels
PX
PN [5]
PA r filename const AjPStr
PD Matrix filename
PX
RT AjPMatrixf
RD New matrix, or NULL if codes, n or filename are 0.
RX
//


AjPMatrixf ajMatrixfNewAsym(const AjPPStr codes, ajint n, 
			    const AjPPStr rcodes, ajint rn, 
			    const AjPStr filename)
{
    ajint i = 0;
    AjPMatrixf ret = 0;
    ajint nsize;
    ajint rnsize;
 
    if((!n) || (!codes) || (!filename))
	return NULL;

    nsize = n + 1;
    rnsize = rn + 1;

    AJNEW0(ret);

    ajStrAssignS(&ret->Name, filename);

    AJCNEW0(ret->Codes, n);

    for(i=0; iCodes[i] = ajStrNew();

    for(i=0; iCodes[i], codes[i]);

    ret->Size = nsize;


    AJCNEW0(ret->CodesRow, rn);

    for(i=0; iCodesRow[i] = ajStrNew();

    for(i=0; iCodesRow[i], rcodes[i]);

    ret->SizeRow = rnsize;


    AJCNEW0(ret->Matrixf, rnsize);

    for(i=0; iMatrixf[i], nsize);

    ret->Cvt = ajSeqcvtNewStrAsym(codes, n, rcodes, rn);

    return ret;
}
ID ajMatrixfDel
TY public
MO ajmatrices
LB core
XX
DE Delete a float matrix
XX
PN [1]
PA w thys AjPMatrixf*
PD Matrix to delete
PX
RT void
RD 
RX
//


void ajMatrixfDel(AjPMatrixf *thys)
{
    ajint isize  = 0;
    ajint iisize = 0;
    ajint rsize  = 0;
    ajint ssize  = 0;
    ajint i      = 0;


    if(!*thys || !thys)
	return;
    
    isize = (*thys)->Size;
    iisize = isize - 1;
    rsize = (*thys)->SizeRow;
    ssize = rsize - 1;

    for(i=0; iCodes[i]);

    AJFREE((*thys)->Codes);

    for(i=0; iCodesRow[i]);

    AJFREE((*thys)->CodesRow);

    ajStrDel(&(*thys)->Name);

    for(i=0; iMatrixf[i]);

    AJFREE((*thys)->Matrixf);

    ajSeqcvtDel(&(*thys)->Cvt);
    AJFREE(*thys);

    return;
}
ID ajMatrixDel
TY public
MO ajmatrices
LB core
XX
DE Delete an integer matrix
XX
PN [1]
PA w thys AjPMatrix*
PD Matrix to delete
PX
RT void
RD 
RX
//


void ajMatrixDel(AjPMatrix *thys)
{
    ajint isize  = 0;
    ajint iisize = 0;
    ajint rsize  = 0;
    ajint ssize  = 0;
    ajint i      = 0;


    if(!*thys || !thys)
	return;

    isize = (*thys)->Size;
    iisize = isize - 1;
    rsize = (*thys)->SizeRow;
    ssize = rsize - 1;
    

    for(i=0; iCodes[i]);

    AJFREE((*thys)->Codes);

    for(i=0; iCodesRow[i]);

    AJFREE((*thys)->CodesRow);

    ajStrDel(&(*thys)->Name);

    for(i=0; iMatrix[i]);
    AJFREE((*thys)->Matrix);

    ajSeqcvtDel(&(*thys)->Cvt);
    AJFREE(*thys);

    return;
}
ID ajMatrixNewFile
TY public
MO ajmatrices
LB core
XX
DE Constructs a comparison matrix from a given local data file
XX
PN [1]
PA r filename const AjPStr
PD Input filename
PX
RT AjPMatrix
RD Matrix object
RX
//


AjPMatrix ajMatrixNewFile(const AjPStr filename)
{
    AjPMatrix ret = NULL;
    AjPStr buffer = NULL;
    const AjPStr tok    = NULL;

    AjPStr firststring  = NULL;
    AjPStr *orderstring = NULL;

    AjPFile file    = NULL;
    AjBool first    = ajTrue;
    const char *ptr = NULL;
    ajint **matrix  = NULL;

    ajint minval = -1;
    ajint i      = 0;
    ajint l      = 0;
    ajint k      = 0;
    ajint cols   = 0;
    ajint rows   = 0;

    ajint *templine = NULL;

    AjPList rlabel_list = NULL;
    AjPStr  *rlabel_arr  = NULL;

#ifndef WIN32
    static const char *delimstr = " :\t\n";
#else /* WIN32 */
    static const char *delimstr = " :\t\n\r";
#endif /* !WIN32 */

    rlabel_list = ajListNew();
    

    firststring = ajStrNew();
    
    file = ajDatafileNewInNameS(filename);
    
    if(!file)
    {
	ajStrDel(&firststring);
	ajListFree(&rlabel_list);

	return NULL;
    }
    
    /* Read row labels */
    while(ajReadline(file,&buffer))
    {
	ptr = ajStrGetPtr(buffer);
#ifndef WIN32
	if(*ptr != '#' && *ptr != '\n')
#else /* WIN32 */
	if(*ptr != '#' && *ptr != '\n' && *ptr != '\r')
#endif /* !WIN32 */
	{
	    if(first)
		first = ajFalse;
	    else
	    {	
		ajFmtScanC(ptr, "%S", &firststring);
		ajListPushAppend(rlabel_list, firststring);
		firststring = ajStrNew();	
	    }
	}
    }

    first = ajTrue;
    ajStrDel(&firststring);
    rows = (ajuint) ajListToarray(rlabel_list, (void ***) &rlabel_arr);
    ajFileSeek(file, 0, 0);


    while(ajReadline(file,&buffer))
    {
	ajStrRemoveWhiteExcess(&buffer);
	ptr = ajStrGetPtr(buffer);

	if(*ptr && *ptr != '#')
	{				
	    if(first)
	    {
		cols = ajStrParseCountC(buffer,delimstr);
		AJCNEW0(orderstring, cols);

		for(i=0; iMatrix;
	    }
	    else
	    {
		ajFmtScanC(ptr, "%S", &firststring);
		
		/* JISON 19/7/4
		   k = ajSeqcvtGetCodeK(ret->Cvt, ajStrGetCharFirst(firststring)); */
		k = ajSeqcvtGetCodeS(ret->Cvt, firststring);

		/* 
		 ** cols+1 is used below because 2nd and subsequent lines have 
		 ** one more string in them (the residue label) 
		 */
		templine = ajArrIntLine(buffer,delimstr,2,cols+1);
		
		for(i=0; iCvt,
					ajStrGetCharFirst(orderstring[i]))] 
					    = templine[i]; */
		    matrix[k][ajSeqcvtGetCodeAsymS(ret->Cvt,
					       orderstring[i])] 
						   = templine[i];
		}

		AJFREE(templine);
	    }
	}
    }

    ajDebug("fill rest with minimum value %d\n", minval);
    

    ajFileClose(&file);
    ajStrDel(&buffer);

    for(i=0; iMatrixf;
	    }
	    else
	    {
		ajFmtScanC(ptr, "%S", &firststring);
		/* JISON 19/7/4 
		   k = ajSeqcvtGetCodeK(ret->Cvt,
                   ajStrGetCharFirst(firststring)); */
		k = ajSeqcvtGetCodeS(ret->Cvt, firststring); 

		len = MAJSTRGETLEN(firststring);
		ajStrAssignSubC(&reststring, ptr, len, -1);

		/* 
		** Must discard the first string (label) and use 
		** reststring otherwise ajArrFloatLine would fail (it 
		** cannot convert a string to a float)
		**   
		** Use cols,1,cols in below because although 2nd and 
		** subsequent lines have one more string in them (the
		** residue label in the 1st column) we've discarded that
		** from the string that's passed
		*/
		templine = ajArrFloatLine(reststring,delimstr,1,cols);
		
		for(i=0; iCvt,
					ajStrGetCharFirst(orderstring[i]))] 
					    = templine[i]; */

		    matrix[k][ajSeqcvtGetCodeAsymS(ret->Cvt,
					       orderstring[i])] 
						   = templine[i];
		}
		AJFREE(templine);
	    }
	}
    }
    ajDebug("fill rest with minimum value %d\n", minval);
    

    ajFileClose(&file);
    ajStrDel(&buffer);

    for(i=0; iCvt, numseq);
}
ID ajMatrixfSeqIndex
TY public
MO ajmatrices
LB core
XX
DE Converts a sequence to index numbers using the matrix's
DE internal conversion table. Sequence characters not defined
DE in the matrix are converted to zero.
XX
PN [1]
PA r thys const AjPMatrixf
PD Float Matrix object
PX
PN [2]
PA r seq const AjPSeq
PD Sequence object
PX
PN [3]
PA w numseq AjPStr*
PD Index code version of the sequence
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajMatrixfSeqIndex(const AjPMatrixf thys, const AjPSeq seq,
                         AjPStr* numseq)
{
    return ajSeqConvertNum(seq, thys->Cvt, numseq);
}
ID ajMatrixGetCodes
TY public
MO ajmatrices
LB core
XX
DE Returns the character codes for each offset in the matrix
XX
PN [1]
PA r thys const AjPMatrix
PD Matrix object
PX
RT AjPStr
RD Matrix codes
RX
//


AjPStr ajMatrixGetCodes(const AjPMatrix thys)
{
    AjPStr ret = NULL;
    ajint i;
    ajint maxcode;

    ret = ajStrNewRes(thys->Size);
    maxcode = thys->Size - 1;

    for (i=0;iCodes[i]));

    return ret;
}
ID ajMatrixfGetCodes
TY public
MO ajmatrices
LB core
XX
DE Returns the character codes for each offset in the matrix
XX
PN [1]
PA r thys const AjPMatrixf
PD Matrix object
PX
RT AjPStr
RD Matrix codes
RX
//


AjPStr ajMatrixfGetCodes(const AjPMatrixf thys)
{
    AjPStr ret = NULL;
    ajint i;
    ajint maxcode;

    ret = ajStrNewRes(thys->Size + 1);
    maxcode = thys->Size - 1;

    for (i=0;iCodes[i]));

    return ret;
}
ID ajMatrixGetMatrix
TY public
MO ajmatrices
LB core
XX
DE Returns the matrix values array in the matrix
XX
PN [1]
PA r thys const AjPMatrix
PD Matrix object
PX
RT AjIntArray*
RD Matrix values array
RX
//


AjIntArray* ajMatrixGetMatrix(const AjPMatrix thys)
{
    if(!thys)
        return NULL;

    return thys->Matrix;
}
ID ajMatrixfGetMatrix
TY public
MO ajmatrices
LB core
XX
DE Returns the matrix values array in the matrix
XX
PN [1]
PA r thys const AjPMatrixf
PD Matrix object
PX
RT AjFloatArray*
RD Matrix values array
RX
//


AjFloatArray* ajMatrixfGetMatrix(const AjPMatrixf thys)
{
    if(!thys)
        return NULL;

    return thys->Matrixf;
}
ID ajMatrixGetRows
TY public
MO ajmatrices
LB core
XX
DE Returns the comparison matrix row size.
XX
PN [1]
PA r thys const AjPMatrix
PD Matrix object
PX
RT ajuint
RD .
RX
//


ajuint ajMatrixGetRows(const AjPMatrix thys)
{
    if(thys)
	return thys->SizeRow;

    return 0;
}
ID ajMatrixGetSize
TY public
MO ajmatrices
LB core
XX
DE Returns the comparison matrix size.
XX
PN [1]
PA r thys const AjPMatrix
PD Matrix object
PX
RT ajuint
RD .
RX
//


ajuint ajMatrixGetSize(const AjPMatrix thys)
{
    if(thys)
	return thys->Size;

    return 0;
}
ID ajMatrixfGetRows
TY public
MO ajmatrices
LB core
XX
DE Returns the comparison matrix row size.
XX
PN [1]
PA r thys const AjPMatrixf
PD Matrix object
PX
RT ajuint
RD .
RX
//


ajuint ajMatrixfGetRows(const AjPMatrixf thys)
{
    if(thys)
	return thys->SizeRow;

    return 0;
}
ID ajMatrixfGetSize
TY public
MO ajmatrices
LB core
XX
DE Returns the comparison matrix size.
XX
PN [1]
PA r thys const AjPMatrixf
PD Matrix object
PX
RT ajuint
RD .
RX
//


ajuint ajMatrixfGetSize(const AjPMatrixf thys)
{
    if(thys)
	return thys->Size;

    return 0;
}
ID ajMatrixGetCvt
TY public
MO ajmatrices
LB core
XX
DE Returns the sequence character conversion table for a matrix.
DE This table converts any character defined in the matrix to a
DE positive integer, and any other character is converted to zero.
XX
PN [1]
PA r thys const AjPMatrix
PD Matrix object
PX
RT AjPSeqCvt
RD sequence character conversion table
RX
//


AjPSeqCvt ajMatrixGetCvt(const AjPMatrix thys)
{
    if(thys)
	return thys->Cvt;

    return NULL;
}
ID ajMatrixfGetCvt
TY public
MO ajmatrices
LB core
XX
DE Returns the sequence character conversion table for a matrix.
DE This table converts any character defined in the matrix to a
DE positive integer, and any other character is converted to zero.
XX
PN [1]
PA r thys const AjPMatrixf
PD Float Matrix object
PX
RT AjPSeqCvt
RD sequence character conversion table
RX
//


AjPSeqCvt ajMatrixfGetCvt(const AjPMatrixf thys)
{
    if(thys)
	return thys->Cvt;

    return NULL;
}
ID ajMatrixGetLabelNum
TY public
MO ajmatrices
LB core
XX
DE Returns the sequence character (or asymmetric matrix label)
DE for a matrix column.
XX
PN [1]
PA r thys const AjPMatrix
PD Matrix object
PX
PN [2]
PA r i ajint
PD Character index
PX
RT const AjPStr
RD Matrix label, e.g. sequence character code
RD or '?' if not found
RX
//


const AjPStr ajMatrixGetLabelNum(const AjPMatrix thys, ajint i)
{
    if(!matrixStrQuery)
        ajStrAssignK(&matrixStrQuery, '?');

    if(!thys)
        return matrixStrQuery;

    if(i >= thys->Size) 
        return matrixStrQuery;
    
    if(i < 0)
        return matrixStrQuery;

    return thys->Codes[i];
}
ID ajMatrixfGetLabelNum
TY public
MO ajmatrices
LB core
XX
DE Returns the sequence character (or asymmetric matrix label)
DE for a matrix column.
XX
PN [1]
PA r thys const AjPMatrixf
PD Matrix object
PX
PN [2]
PA r i ajint
PD Character index
PX
RT const AjPStr
RD Matrix label, e.g. sequence character code
RD or '?' if not found
RX
//


const AjPStr ajMatrixfGetLabelNum(const AjPMatrixf thys, ajint i)
{
    if(!matrixStrQuery)
        ajStrAssignK(&matrixStrQuery, '?');

    if(!thys)
        return matrixStrQuery;

    if(i >= thys->Size) 
        return matrixStrQuery;
    
    if(i < 0)
        return matrixStrQuery;

    return thys->Codes[i];
}
ID ajMatrixGetName
TY public
MO ajmatrices
LB core
XX
DE Returns the name of a matrix object, usually the filename from
DE which it was read.
XX
PN [1]
PA r thys const AjPMatrix
PD Matrix object
PX
RT const AjPStr
RD The name, a pointer to the internal name.
RX
//


const AjPStr ajMatrixGetName(const AjPMatrix thys)
{
    static AjPStr emptystr = NULL;

    if(!thys)
    {
	if (!emptystr)
	    emptystr = ajStrNewC("");

	return emptystr;
    }

    return thys->Name;
}
ID ajMatrixfGetName
TY public
MO ajmatrices
LB core
XX
DE Returns the name of a matrix object, usually the filename from
DE which it was read.
XX
PN [1]
PA r thys const AjPMatrixf
PD Matrix object
PX
RT const AjPStr
RD The name, a pointer to the internal name.
RX
//


const AjPStr ajMatrixfGetName(const AjPMatrixf thys)
{
    static AjPStr emptystr = NULL;

    if(!thys)
    {
	emptystr = ajStrNewC("");

	return emptystr;
    }

    return thys->Name;
}
ID ajMatrixExit
TY public
MO ajmatrices
LB core
XX
DE Cleans matrix processing internals
XX
RT void
RD 
RX
//


void ajMatrixExit(void)
{
    ajStrDel(&matrixStrQuery);

    return;
}
ID ajMemAlloc
TY public
MO ajmem
LB core
XX
DE Allocates memory using malloc, and fails with an error message if
DE unsuccessful.
XX
PN [1]
PA r nbytes size_t
PD Number of bytes required
PX
PN [2]
PA r file const char*
PD Source file name, generated by a macro.
PX
PN [3]
PA r line ajint
PD Source line number, generated by a macro.
PX
PN [4]
PA r nofail AjBool
PD If true, return with a NULL pointer when
PD unable to allocate.
PX
RT void*
RD Successfully allocated memory, or NULL on failure.
RD Normal behaviour is to
RD raise an exception and fail, or if running
RD with Java, to print to standard error
RD and exit.
RX
//


void* ajMemAlloc(size_t nbytes, const char* file, ajint line, AjBool nofail)
{
    void *ptr;

#ifdef HAVE_JAVA
    (void) file;                /* make it used */
    (void) line;                /* make it used */
#endif /* HAVE_JAVA */

    if(nbytes <= 0)
    {
#ifdef HAVE_JAVA
	fprintf(stderr,"Attempt to allocate <=0 bytes");
	exit(-1);
#else /* !HAVE_JAVA */
	ajExceptRaise(&Mem_Badcount, file, line);
#endif /* !HAVE_JAVA */
    }

    ptr = malloc(nbytes);

    if(ptr == NULL)
    {
	if (nofail)
	    return NULL;
#ifdef HAVE_JAVA
	fprintf(stderr,"Memory allocation failed in ajMemAlloc");
	exit(-1);
#else /* !HAVE_JAVA */
	if(file == NULL)
	    AJRAISE(Mem_Failed);
	else
	    ajExceptRaise(&Mem_Failed, file, line);
#endif /* !HAVE_JAVA */
    }

#ifdef AJ_SAVESTATS
    memAlloc += nbytes;
    memCount++;
    memTotal++;
#endif /* AJ_SAVESTATS */

    return ptr;
}
ID ajMemCalloc
TY public
MO ajmem
LB core
XX
DE Allocates memory using calloc for an array of elements,
DE and fails with an error message if unsuccessful.
XX
PN [1]
PA r count size_t
PD Number of elements required
PX
PN [2]
PA r nbytes size_t
PD Number of bytes required per element
PX
PN [3]
PA r file const char*
PD Source file name, generated by a macro.
PX
PN [4]
PA r line ajint
PD Source line number, generated by a macro.
PX
PN [5]
PA r nofail AjBool
PD If true, return with a NULL pointer when
PD unable to allocate.
PX
RT void*
RD Successfully allocated memory, or NULL on failure.
RD Normal behaviour is to
RD raise an exception and fail, or if running
RD with Java, to print to standard error
RD and exit.
RX
//


void* ajMemCalloc(size_t count, size_t nbytes,
		  const char* file, ajint line, AjBool nofail)
{
    void *ptr;
    size_t ibytes = nbytes;
    size_t icount = count;

#ifdef HAVE_JAVA
    (void) file;                /* make it used */
    (void) line;                /* make it used */
#endif /* HAVE_JAVA */

    if(count <= 0)
        ajUtilCatch();

    if(!count)
       icount = 1;
    if(!nbytes)
        ibytes = 1;

    ptr = calloc(icount, ibytes);

    if(ptr == NULL)
    {
	if (nofail)
	    return NULL;
#ifdef HAVE_JAVA
	fprintf(stderr,"Memory allocation failed in ajMemCalloc");
	exit(-1);
#else /* !HAVE_JAVA */
	if(file == NULL)
	    AJRAISE(Mem_Failed);
	else
	    ajExceptRaise(&Mem_Failed, file, line);
#endif /* !HAVE_JAVA */
    }

#ifdef AJ_SAVESTATS
    memAlloc += (icount*ibytes);
    memCount++;
    memTotal++;
#endif /* AJ_SAVESTATS */

    return ptr;
}
ID ajMemCallocZero
TY public
MO ajmem
LB core
XX
DE Allocates memory using calloc for an array of elements,
DE and fails with an error message if unsuccessful.
DE
DE The memory is initialised to zero. This should be done by the standard
DE calloc function. It is explicitly done here to make sure.
XX
PN [1]
PA r count size_t
PD Number of elements required
PX
PN [2]
PA r nbytes size_t
PD Number of bytes required
PX
PN [3]
PA r file const char*
PD Source file name, generated by a macro.
PX
PN [4]
PA r line ajint
PD Source line number, generated by a macro.
PX
PN [5]
PA r nofail AjBool
PD If true, return with a NULL pointer when
PD unable to allocate.
PX
RT void*
RD Successfully allocated memory, or NULL on failure.
RD Normal behaviour is to
RD raise an exception and fail, or if running
RD with Java, to print to standard error
RD and exit.
RX
//


void* ajMemCallocZero(size_t count, size_t nbytes,
		      const char* file, ajint line, AjBool nofail)
{
    void *ptr;
    size_t ibytes = nbytes;
    size_t icount = count;

#ifdef HAVE_JAVA
    (void) file;                /* make it used */
    (void) line;                /* make it used */
#endif /* HAVE_JAVA */

    if(count <= 0)
        ajUtilCatch();

    if(!count)
       icount = 1;
    if(!nbytes)
        ibytes = 1;

    ptr = calloc(icount, ibytes);

    if(ptr == NULL)
    {
	if (nofail)
	    return NULL;
#ifdef HAVE_JAVA
	fprintf(stderr,"Memory allocation failed in ajMemCallocZero");
	exit(-1);
#else /* !HAVE_JAVA */
	if(file == NULL)
	    AJRAISE(Mem_Failed);
	else
	    ajExceptRaise(&Mem_Failed, file, line);
#endif /* !HAVE_JAVA */
    }

    memset(ptr, 0, icount*ibytes);

#ifdef AJ_SAVESTATS
    memAlloc += (icount*ibytes);
    memCount++;
    memTotal++;
    memZero += (icount*ibytes);
#endif /* AJ_SAVESTATS */

    return ptr;
}
ID ajMemSetZero
TY public
MO ajmem
LB core
XX
DE Zeroes memory for an array of elements,
XX
PN [1]
PA u ptr void*
PD Pointer to memory previously allocated with 'malloc'
PX
PN [2]
PA r count size_t
PD Number of elements required
PX
PN [3]
PA r nbytes size_t
PD Number of bytes required
PX
RT void
RD 
RX
//


void ajMemSetZero(void* ptr, size_t count, size_t nbytes)
{
    if (ptr == NULL)
	return;

    if(!nbytes)
        return;

    if(!count)
        return;

    memset(ptr, 0, count*nbytes);

#ifdef AJ_SAVESTATS
    memZero += (count*nbytes);
#endif /* AJ_SAVESTATS */

    return;
}
ID ajMemFree
TY public
MO ajmem
LB core
XX
DE Frees memory using 'free' and zeroes the pointer. Ignores NULL
DE (uninitialised) pointers.
XX
PN [1]
PA u ptr void**
PD Pointer to memory previously allocated with 'malloc'
PX
//


void ajMemFree(void** ptr)
{
    if(!ptr)
        return;
    if(!*ptr)
        return;
        
    free(*ptr);
    *ptr = NULL;

#ifdef AJ_SAVESTATS
    memCount--;
    memFree++;
#endif /* AJ_SAVESTATS */

    return;
}
ID ajMemResize
TY public
MO ajmem
LB core
XX
DE Resizes previously allocated memory, and ensures data is copied to
DE the new location if it is moved.
DE
DE If the pointer is new then new memory is allocated automatically.
DE
DE The C run-time library function realloc does preserve existing values
DE but does not initialise any memory after the old contents.
XX
PN [1]
PA u ptr void*
PD Pointer to memory previously allocated with 'malloc'
PX
PN [2]
PA r nbytes size_t
PD Number of bytes required
PX
PN [3]
PA r file const char*
PD Source file name, generated by a macro.
PX
PN [4]
PA r line ajint
PD Source line number, generated by a macro.
PX
PN [5]
PA r nofail AjBool
PD If true, return with a NULL pointer when
PD unable to allocate.
PX
RT void*
RD Successfully reallocated memory, or NULL on failure.
RD Normal behaviour is to
RD raise an exception and fail, or if running
RD with Java, to print to standard error
RD and exit.
RX
//


void* ajMemResize(void* ptr, size_t nbytes,
		  const char* file, ajint line, AjBool nofail)
{
    size_t ibytes = nbytes;

    if(!nbytes)
        ibytes = 1;

    if(ptr == NULL)
    {
	ptr = ajMemCallocZero(ibytes, 1, file, line, nofail);

	return ptr;
    }

    ptr = realloc(ptr, ibytes);

    if(ptr == NULL)
    {
	if (nofail)
	    return NULL;
#ifdef HAVE_JAVA
	fprintf(stderr,"Memory allocation failed in ajMemResize");
	exit(-1);
#else /* !HAVE_JAVA */
	if(file == NULL)
	    AJRAISE(Mem_Failed);
	else
	    ajExceptRaise(&Mem_Failed, file, line);
#endif /* !HAVE_JAVA */
    }
  
#ifdef AJ_SAVESTATS
    memResize += ibytes;
    memResizeCount++;
#endif /* AJ_SAVESTATS */
    
    return ptr;
}
ID ajMemResizeZero
TY public
MO ajmem
LB core
XX
DE Resizes previously allocated memory, and ensures data is copied to
DE the new location if it is moved.
DE
DE If the pointer is new then new memory is allocated automatically.
DE
DE Memory beyond the previous contents is initialised to zero
DE
DE The C run-time library function realloc does preserves existing values
DE but does not initialise any memory after the old contents. This is why
DE this function needs to be told what the old size was.
XX
PN [1]
PA u ptr void*
PD Pointer to memory previously allocated with 'malloc'
PX
PN [2]
PA r oldbytes size_t
PD Number of bytes required
PX
PN [3]
PA r nbytes size_t
PD Number of bytes required
PX
PN [4]
PA r file const char*
PD Source file name, generated by a macro.
PX
PN [5]
PA r line ajint
PD Source line number, generated by a macro.
PX
PN [6]
PA r nofail AjBool
PD If true, return with a NULL pointer when
PD unable to allocate.
PX
RT void*
RD Successfully reallocated memory, or NULL on failure.
RD Normal behaviour is to
RD raise an exception and fail, or if running
RD with Java, to print to standard error
RD and exit.
RX
//


void* ajMemResizeZero(void* ptr, size_t oldbytes, size_t nbytes,
		      const char* file, ajint line, AjBool nofail)
{
    size_t ibytes = nbytes;

    if(!nbytes)
        ibytes = 1;

    if(ptr == NULL)
    {
	ptr = ajMemCallocZero(ibytes, 1, file, line, nofail);

	return ptr;
    }

    ptr = realloc(ptr, ibytes);

    if(ptr == NULL)
    {
	if (nofail)
	    return NULL;
#ifdef HAVE_JAVA
	fprintf(stderr,"Memory allocation failed in ajMemResizeZero");
	exit(-1);
#else /* !HAVE_JAVA */
	if(file == NULL)
	    AJRAISE(Mem_Failed);
	else
	    ajExceptRaise(&Mem_Failed, file, line);
#endif /* !HAVE_JAVA */
    }
  
    if(ibytes > oldbytes)
	memset(((char*)ptr)+oldbytes, 0, (nbytes-oldbytes));

#ifdef AJ_SAVESTATS
    memResizeOld += oldbytes;
    memResize += ibytes;
    memResizeCount++;
#endif /* AJ_SAVESTATS */

    return ptr;
}
ID ajMemArrB
TY public
MO ajmem
LB core
XX
DE Creates an AjBool array.
DE Use AJFREE to free the memory when no longer needed.
XX
PN [1]
PA r size size_t
PD Number of array elements.
PX
RT ajint*
RD Newly allocated array.
RX
//


ajint* ajMemArrB(size_t size)
{
    return AJCALLOC(size, sizeof(AjBool));
}
ID ajMemArrI
TY public
MO ajmem
LB core
XX
DE Creates an integer array.
DE Use AJFREE to free the memory when no longer needed.
XX
PN [1]
PA r size size_t
PD Number of array elements.
PX
RT ajint*
RD Newly allocated array.
RX
//


ajint* ajMemArrI(size_t size)
{
    return AJCALLOC(size, sizeof(ajint));
}
ID ajMemArrF
TY public
MO ajmem
LB core
XX
DE Creates a float array.
DE Use AJFREE to free the memory when no longer needed.
XX
PN [1]
PA r size size_t
PD Number of array elements.
PX
RT float*
RD Newly allocated array.
RX
//


float* ajMemArrF(size_t size)
{
    return AJCALLOC(size, sizeof(float));
}
ID ajMemStat
TY public
MO ajmem
LB core
XX
DE Prints a summary of memory usage with debug calls
XX
PN [1]
PA r title const char*
PD Title for this summary
PX
RT void
RD 
RX
//


void ajMemStat(const char* title)
{
#ifdef AJ_SAVESTATS
    static ajlong statAlloc       = 0;
    static ajlong statCount       = 0;
    static ajlong statFree        = 0;
    static ajlong statResize      = 0;
    static ajlong statResizeOld   = 0;
    static ajlong statResizeCount = 0;
    static ajlong statTotal       = 0;
    static ajlong statZero        = 0;

    ajDebug("Memory usage since last call %s:\n", title);
    ajDebug("Memory usage (bytes): %Ld allocated, %Ld reallocated "
	    "%Ld returned %Ld zeroed\n",
	    memAlloc - statAlloc, memResize - statResize,
	    memResizeOld - statResizeOld, memZero - statZero);
    ajDebug("Memory usage (number): %Ld allocates, "
	    "%Ld frees, %Ld resizes, %Ld in use\n",
	    memTotal - statTotal, memFree - statFree,
	    memResizeCount - statResizeCount, memCount - statCount);

    statAlloc  = memAlloc;
    statCount  = memCount;
    statFree   = memFree;
    statResize = memResize;
    statTotal  = memTotal;
    statZero   = memZero;

    statResizeCount = memResizeCount;
#else /* !AJ_SAVESTATS */
    (void) title;               /* make it used */
#endif /* !AJ_SAVESTATS */

    return;
}
ID ajMemExit
TY public
MO ajmem
LB core
XX
DE Prints a summary of memory usage with debug calls
XX
RT void
RD 
RX
//


void ajMemExit(void)
{
#ifdef AJ_SAVESTATS
    ajDebug("Memory usage (bytes): %Ld allocated, %Ld reallocated "
	    "%Ld returned %Ld zeroed\n",
	    memAlloc, memResize,memResizeOld,  memZero);
    ajDebug("Memory usage (number): %Ld allocates, "
	    "%Ld frees, %Ld resizes, %Ld in use\n",
	    memTotal, memFree, memResizeCount, memCount);
#endif /* AJ_SAVESTATS */

    return;
}
ID ajMemCheck
TY public
MO ajmem
LB core
XX
DE Prints a message appropriate to the memcheck status
XX
PN [1]
PA r istat int
PD Enumerated value from mprobe
PX
RT void
RD 
RX
//

void ajMemCheck(int istat)
{
#ifdef HAVE_MCHECK
    if(probeTest)
    {
	if(istat == MCHECK_OK)
	    ajWarn("ajMemProbe called for %x in %s line %d",
		   probePtr, probeFile, probeLine);
	else
	    ajErr("ajMemProbe called for %x in %s line %d",
		  probePtr, probeFile, probeLine);
    }

    if(istat == MCHECK_DISABLED)
	ajUser("mcheck is disabled\n");
    else if(istat == MCHECK_OK)
	ajUser("mcheck OK\n");
    else if(istat == MCHECK_HEAD)
	ajUser("mcheck data before was modified\n");
    else if(istat == MCHECK_TAIL)
	ajUser("mcheck data after was modified\n");
    else if(istat == MCHECK_FREE)
	ajUser("mcheck data already freed\n");
    else
	ajUser("mcheck ... SOMETHING UNEXPECTED\n");


    if(probeMaxFail && (++probeFail >= probeMaxFail))
       ajDie("Maximum ajMemProbe failures %d", probeFail);
#else /* !HAVE_MCHECK */
    (void) istat;
#endif /* !HAVE_MCHECK */

    return;
}
ID ajMemCheckSetLimit
TY public
MO ajmem
LB core
XX
DE Prints a message appropriate to the memcheck status
XX
PN [1]
PA r maxfail ajint
PD Maximum failures allowed
PX
RT void
RD 
RX
//

void ajMemCheckSetLimit(ajint maxfail)
{
#ifdef HAVE_MCHECK
    if(probeFail >= maxfail)
      ajDie("Maximum ajMemProbe failures set to %d, already at %d",
	    maxfail, probeFail);

    probeMaxFail = maxfail;
#else /* !HAVE_MCHECK */
    (void) maxfail;
#endif /* !HAVE_MCHECK */

    return;
}
ID ajMemProbe
TY public
MO ajmem
LB core
XX
DE Probes a memory location for possible errors
XX
PN [1]
PA u ptr void*
PD Pointer to memory previously allocated with 'malloc'
PX
PN [2]
PA r file const char*
PD Source file name, generated by a macro.
PX
PN [3]
PA r line ajint
PD Source line number, generated by a macro.
PX
RT void
RD 
RX
//


void ajMemProbe(void* ptr,
		const char* file, ajint line)
{
#ifdef HAVE_MCHECK
    if(ptr == NULL)
    {
	ajWarn("ajMemProbe address %x NULL in %s at line %d", ptr, file, line);

	return;
    }

    probeLine = line;
    probeFile = file;
    probeTest = ajTrue;
    probePtr = ptr;

    mprobe(ptr);

    probeTest = ajFalse;
#else /* !HAVE_MCHECK */
    (void) ptr;
    (void) file;
    (void) line;
#endif /* !HAVE_MCHECK */

    return;
}
ID ajFatal
TY macro
MO ajmess
LB core
XX
DE Fatal error message to standard error.
DE Includes filename and line number in the source code that invokes it.
DE Newline is added automatically at the end of the format string.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v  ...
PD Variable length argument list
PX
RT void
RD 
RX
//
ID ajMessCrash
TY macro
MO ajmess
LB core
XX
DE Crash error message to standard error.
DE Includes filename and line number in the source code that invokes it.
DE Newline is added automatically at the end of the format string.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v  ...
PD Variable length argument list
PX
RT void
RD 
RX
//
ID ajMessInvokeDebugger
TY public
MO ajmess
LB core
XX
DE Used to trace in a debugger as a breakpoint
XX
RT void
RD 
RX
//


void ajMessInvokeDebugger(void)
{
    static AjBool reentrant = AJFALSE;

    if(!reentrant)
    {
	reentrant = AJTRUE;
	reentrant = AJFALSE;
    }

    return;
}
ID ajMessRegBeep
TY public
MO ajmess
LB core
XX
DE Sets a function to process beeps
XX
PN [1]
PA f func AjMessVoidRoutine
PD Function to be registered
PX
RT AjMessVoidRoutine
RD Previously defined function
RX
//


AjMessVoidRoutine ajMessRegBeep(AjMessVoidRoutine func)
{
    AjMessVoidRoutine old;

    old = beepRoutine;
    beepRoutine = func;

    return old;
}
ID ajMessRegOut
TY public
MO ajmess
LB core
XX
DE Sets a function to write messages
XX
PN [1]
PA f func AjMessOutRoutine
PD Function to be registered
PX
RT AjMessOutRoutine
RD Previously defined function
RX
//


AjMessOutRoutine ajMessRegOut(AjMessOutRoutine func)
{
    AjMessOutRoutine old;

    old = outRoutine;
    outRoutine = func;

    return old;
}
ID ajMessRegDump
TY public
MO ajmess
LB core
XX
DE Sets a function to dump data
XX
PN [1]
PA f func AjMessOutRoutine
PD Function to be registered
PX
RT AjMessOutRoutine
RD Previously defined function
RX
//


AjMessOutRoutine ajMessRegDump(AjMessOutRoutine func)
{
    AjMessOutRoutine old;

    old = dumpRoutine;
    dumpRoutine = func;

    return old;
}
ID ajMessRegErr
TY public
MO ajmess
LB core
XX
DE Sets a function to report errors
XX
PN [1]
PA f func AjMessOutRoutine
PD Function to be registered
PX
RT AjMessOutRoutine
RD Previously defined function
RX
//


AjMessOutRoutine ajMessRegErr(AjMessOutRoutine func)
{
    AjMessOutRoutine old;

    old = errorRoutine;
    errorRoutine = func;

    return old;
}
ID ajMessRegExit
TY public
MO ajmess
LB core
XX
DE Sets a function to exit
XX
PN [1]
PA f func AjMessOutRoutine
PD Function to be registered
PX
RT AjMessOutRoutine
RD Previously defined function
RX
//


AjMessOutRoutine ajMessRegExit(AjMessOutRoutine func)
{
    AjMessOutRoutine old;

    old = exitRoutine;
    exitRoutine = func;

    return old;
}
ID ajMessRegCrash
TY public
MO ajmess
LB core
XX
DE Sets a function to crash
XX
PN [1]
PA f func AjMessOutRoutine
PD Function to be registered
PX
RT AjMessOutRoutine
RD Previously defined function
RX
//


AjMessOutRoutine ajMessRegCrash(AjMessOutRoutine func)
{
    AjMessOutRoutine old;

    old = crashRoutine;
    crashRoutine = func;

    return old;
}
ID ajMessRegWarn
TY public
MO ajmess
LB core
XX
DE Sets a function to print warnings
XX
PN [1]
PA f func AjMessOutRoutine
PD Function to be registered
PX
RT AjMessOutRoutine
RD Previously defined function
RX
//


AjMessOutRoutine ajMessRegWarn(AjMessOutRoutine func)
{
    AjMessOutRoutine old;

    old = warningRoutine;
    warningRoutine = func;

    return old;
}
ID ajMessBeep
TY public
MO ajmess
LB core
XX
DE Calls the defined beep function, if any. Otherwise prints ASCII 7 to
DE standard error.
XX
RT void
RD 
RX
//


void ajMessBeep(void)
{
    if(beepRoutine)
	(*beepRoutine)();
    else
    {
	printf("%c",0x07);
	fflush(stdout);
    }

    return;
}
ID ajUser
TY public
MO ajmess
LB core
XX
DE Formats a message. Calls the defined output function (if any).
DE Otherwise prints the message to standard error with an extra newline.
XX
PN [1]
PA r format const char*
PD Format string
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajUser(const char *format,...)
{
    va_list args;
    const char *mesg_buf;

    AJAXFORMATSTRING(args, format, mesg_buf, NULL);

    if(outRoutine)
	(*outRoutine)(mesg_buf);
    else
	fprintf(stderr, "%s\n", mesg_buf);

    ajDebug("ajUser: %s\n", mesg_buf);

    return;
}
ID ajUserDumpC
TY public
MO ajmess
LB core
XX
DE Prints a string unchanged. Calls the defined output function (if any).
DE Otherwise prints the message to standard error with an extra newline.
XX
PN [1]
PA r txt const char*
PD String to print unchanged
PX
RT void
RD 
RX
//


void ajUserDumpC(const char* txt)
{
    if(outRoutine)
	(*outRoutine)(txt);
    else
	fprintf(stderr, "%s\n", txt);

    ajDebug("ajUserDumpC: %s\n", txt);

    return;
}
ID ajUserDumpS
TY public
MO ajmess
LB core
XX
DE Prints a string unchanged. Calls the defined output function (if any).
DE Otherwise prints the message to standard error with an extra newline.
XX
PN [1]
PA r str const AjPStr
PD String to print unchanged
PX
RT void
RD 
RX
//


void ajUserDumpS(const AjPStr str)
{
    const char *mesg_buf = ajStrGetPtr(str);

    if(outRoutine)
	(*outRoutine)(mesg_buf);
    else
	fprintf(stderr, "%s\n", mesg_buf);

    ajDebug("ajUserDumpS: %s\n", mesg_buf);

    return;
}
ID ajMessOut
TY public
MO ajmess
LB core
XX
DE Formats a message. Calls the defined output function (if any).
DE Otherwise prints the message to standard error with no newline.
XX
PN [1]
PA r format const char*
PD Format string
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajMessOut(const char *format,...)
{
    va_list args;
    char *mesg_buf;

    AJAXFORMATSTRING(args, format, mesg_buf, NULL);

    if(outRoutine)
	(*outRoutine)(mesg_buf);
    else
	fprintf(stderr, "%s", mesg_buf);

    ajDebug("ajMessOut: %s\n", mesg_buf);

    return;
}
ID ajVUser
TY public
MO ajmess
LB core
XX
DE Formats a message. Calls the defined output function (if any).
DE Otherwise prints the message to standard error.
XX
PN [1]
PA r format const char*
PD Format string
PX
PN [2]
PA v args va_list
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajVUser(const char *format, va_list args)
{
    char *mesg_buf;

    AJAXVFORMATSTRING(args, format, mesg_buf, NULL);

    if(outRoutine)
	(*outRoutine)(mesg_buf);
    else
	fprintf(stderr, "%s\n", mesg_buf);

    ajDebug("ajVUser: %s\n", mesg_buf);

    return;
}
ID ajMessDump
TY public
MO ajmess
LB core
XX
DE Formats a message. Calls the dump function (if any).
DE Otherwise no further action.
XX
PN [1]
PA r format const char*
PD format string.
PX
PN [2]
PA v vararg ...
PD Variable length argument list.
PX
RT void
RD 
RX
//


void ajMessDump(const char *format,...)
{
    static char dumpbuf[BUFSIZE];   /* BEWARE limited buffer size. */
    char *mesg_buf;
    va_list args;

    mesg_buf = &dumpbuf[0];

    AJAXFORMATSTRING(args, format, mesg_buf, NULL);

    strcat(mesg_buf, "\n"); /* assume we are writing to a file */

    if(dumpRoutine)
	(*dumpRoutine)(mesg_buf);

    return;
}
ID messDump
TY static
MO ajmess
LB core
XX
DE Calls the dump function (if any) to dump text followed by a newline.
XX
PN [1]
PA r message const char*
PD Message text
PX
RT void
RD 
RX
//


static void messDump(const char *message)
{
    if(dumpRoutine)
    {
	(*dumpRoutine)(message);
	(*dumpRoutine)("\n");
    }

    return;
}
ID ajMessGetCountError
TY public
MO ajmess
LB core
XX
DE Returns the number of times the error routines have been called.
XX
RT ajint
RD Error function call count.
RX
//


ajint ajMessGetCountError(void)
{
    return errorCount;
}
ID ajErr
TY public
MO ajmess
LB core
XX
DE Formats an error message. Calls the error function (if any).
DE Otherwise prints the message to standard error with a trailing newline.
DE
DE The error message count is incremented by 1 for each call.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajErr(const char *format, ...)
{
    const char *prefix   = ERROR_PREFIX;
    const char *mesg_buf = NULL;
    va_list args;

    ++errorCount;

    if(AjErrorLevel.error)
    {
	AJAXFORMATSTRING(args, format, mesg_buf, prefix);

	messDump(mesg_buf);

	if(errorRoutine)
	    (*errorRoutine)(mesg_buf);
	else
	    fprintf(stderr, "%s\n", mesg_buf);

        ajDebug("ajErr: %s\n", mesg_buf);

	ajMessInvokeDebugger();
        ajUtilCatch();
    }

    return;
}
ID ajVErr
TY public
MO ajmess
LB core
XX
DE Formats an error message. Calls the error function (if any).
DE Otherwise prints the message to standard error with a trailing newline.
DE
DE The error message count is incremented by 1 for each call.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v args va_list
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajVErr(const char *format, va_list args)
{
    const char *prefix   = ERROR_PREFIX;
    const char *mesg_buf = NULL;

    ++errorCount;

    AJAXVFORMATSTRING(args, format, mesg_buf, prefix);

    messDump(mesg_buf);

    if(errorRoutine)
	(*errorRoutine)(mesg_buf);
    else
    {
	if(AjErrorLevel.error)
        {
	    fprintf(stderr, "%s\n", mesg_buf);
            ajDebug("ajVErr: %s\n", mesg_buf);
        }
    }
    ajMessInvokeDebugger();

    return;
}
ID ajDie
TY public
MO ajmess
LB core
XX
DE Formats an error message. Calls the error function (if any).
DE Otherwise prints the message to standard error with a trailing newline.
DE Then kills the application.
DE
DE The error message count is incremented by 1 for each call.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


__noreturn void  ajDie(const char *format, ...)
{
    const char *prefix   = DIE_PREFIX;
    const char *mesg_buf = NULL;
    va_list args;

    ++errorCount;

    if(AjErrorLevel.die)
    {
	AJAXFORMATSTRING(args, format, mesg_buf, prefix);

	messDump(mesg_buf);

	if(errorRoutine)
	    (*errorRoutine)(mesg_buf);
	else
	    fprintf(stderr, "%s\n", mesg_buf);

        ajDebug("ajDie: %s\n", mesg_buf);

        ajMessInvokeDebugger();
    }


    exit(EXIT_FAILURE);
}
ID ajVDie
TY public
MO ajmess
LB core
XX
DE Formats an error message. Calls the error function (if any).
DE Otherwise prints the message to standard error with a trailing newline.
DE Then kills the application.
DE
DE The error message count is incremented by 1 for each call.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v args va_list
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajVDie(const char *format, va_list args)
{
    const char *prefix   = DIE_PREFIX;
    const char *mesg_buf = NULL;

    ++errorCount;

    AJAXVFORMATSTRING(args, format, mesg_buf, prefix);

    messDump(mesg_buf);

    if(errorRoutine)
	(*errorRoutine)(mesg_buf);
    else
	ajMessCrash(mesg_buf);

    ajMessInvokeDebugger();

    return;
}
ID ajWarn
TY public
MO ajmess
LB core
XX
DE Formats a warning message. Calls the warning function (if any).
DE Otherwise prints the message to standard error with a trailing newline.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajWarn(const char *format, ...)
{
    const char *prefix   = WARNING_PREFIX;
    const char *mesg_buf = NULL;
    va_list args;

    if(AjErrorLevel.warning)
    {
	AJAXFORMATSTRING(args, format, mesg_buf, prefix);

	messDump(mesg_buf);

	if(warningRoutine)
	    (*warningRoutine)(mesg_buf);
	else
	    fprintf(stderr, "%s\n", mesg_buf);

        ajDebug("ajWarn: %s\n", mesg_buf);

	ajMessInvokeDebugger();
        ajUtilCatch();
    }

    return;
}
ID ajVWarn
TY public
MO ajmess
LB core
XX
DE Formats a warning message. Calls the warning function (if any).
DE Otherwise prints the message to standard error with a trailing newline.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v args va_list
PD Variable length argument list
PX
RT void
RD 
RX
//


void ajVWarn(const char *format, va_list args)
{
    const char *prefix   = WARNING_PREFIX;
    const char *mesg_buf = NULL;

    AJAXVFORMATSTRING(args, format, mesg_buf, prefix);

    messDump(mesg_buf);

    if(warningRoutine)
	(*warningRoutine)(mesg_buf);
    else
	fprintf(stderr, "%s\n", mesg_buf);

    ajDebug("ajVWarn: %s\n", mesg_buf);

    ajMessInvokeDebugger();

    return;
}
ID ajMessExitmsg
TY public
MO ajmess
LB core
XX
DE Formats an exit message and calls the exit function (if any).
DE Otherwise prints the message to standard error with a trailing newline
DE and exist with code EXIT_FAILURE.
DE
DE Use this function for errors that while being unrecoverable are not a
DE problem with the AJAX code.
DE
DE Note that there errors are logged but that this routine will exit without
DE any chance to interrupt it (see the crash routine in ajMessCrashFL), this
DE could be changed to allow the application to register an exit handler.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


__noreturn void ajMessExitmsg(const char *format, ...)
{
    const char *prefix   = EXIT_PREFIX;
    const char *mesg_buf = NULL;
    va_list args;

    AJAXFORMATSTRING(args, format, mesg_buf, prefix);

    messDump(mesg_buf);

    if(exitRoutine)
	(*exitRoutine)(mesg_buf);
    else
	fprintf(stderr, "%s\n", mesg_buf);

    ajDebug("ajMessExitmsg: %s\n", mesg_buf);

    exit(EXIT_FAILURE);
}
ID ajMessCrashFL
TY public
MO ajmess
LB core
XX
DE This is the routine called by the ajFatal macro and others.
DE
DE This routine may encounter errors itself, in which case it will attempt
DE to call itself to report the error. To avoid infinite recursion we limit
DE this to just one reporting of an internal error and then we abort.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v vararg ...
PD Variable length argument list
PX
RT void
RD 
RX
//


__noreturn void ajMessCrashFL(const char *format, ...)
{
    enum {MAXERRORS = 1};
    static ajint internalErrors = 0;
    static char prefix[1024];
    ajint rc;
    const char *mesg_buf = NULL;
    va_list args;


    if(internalErrors > MAXERRORS)
	abort();
    else
	internalErrors++;

    /* Construct the message prefix, adding the program name if possible. */

    if(messGetErrorProgram() == NULL)
	rc = sprintf(prefix, CRASH_PREFIX_FORMAT, MESG_TITLE,
		     messGetErrorFile(), messGetErrorLine());
    else
	rc = sprintf(prefix, FULL_CRASH_PREFIX_FORMAT, MESG_TITLE,
		     messGetErrorProgram(), messGetErrorFile(),
		     messGetErrorLine());
    if(rc < 0)
	ajMessCrash("sprintf failed");

    if(AjErrorLevel.fatal)
    {
	AJAXFORMATSTRING(args, format, mesg_buf, prefix);

	messDump(mesg_buf);

	if(crashRoutine)
	    (*crashRoutine)(mesg_buf);
	else
	    fprintf(stderr, "%s\n", mesg_buf);

        ajDebug("ajMessCrashFL: %s\n", mesg_buf);

	ajMessInvokeDebugger();
    }


    exit(EXIT_FAILURE);
}
ID ajMessVCrashFL
TY public
MO ajmess
LB core
XX
DE This is the routine called by the ajVFatal macro and others.
DE
DE This routine may encounter errors itself, in which case it will attempt
DE to call itself to report the error. To avoid infinite recursion we limit
DE this to just one reporting of an internal error and then we abort.
XX
PN [1]
PA r format const char*
PD Format
PX
PN [2]
PA v args va_list
PD Variable length argument list
PX
RT void
RD 
RX
//


__noreturn void ajMessVCrashFL(const char *format, va_list args)
{
    enum {MAXERRORS = 1};
    static ajint internalErrors = 0;
    static char prefix[1024];
    ajint rc;
    char *mesg_buf = NULL;
    
    if(internalErrors > MAXERRORS)
	abort();
    else
	internalErrors++;
    
    /* Construct the message prefix, adding the program name if possible. */
    
    if(messGetErrorProgram() == NULL)
	rc = sprintf(prefix, CRASH_PREFIX_FORMAT, MESG_TITLE,
		     messGetErrorFile(), messGetErrorLine());
    else
	rc = sprintf(prefix, FULL_CRASH_PREFIX_FORMAT, MESG_TITLE,
		     messGetErrorProgram(), messGetErrorFile(),
		     messGetErrorLine());
    if(rc < 0)
	ajMessCrash("sprintf failed");
    
    
    AJAXVFORMATSTRING(args, format, mesg_buf, prefix);
    
    messDump(mesg_buf);
    
    if(crashRoutine)
	(*crashRoutine)(mesg_buf);
    else
	fprintf(stderr, "%s\n", mesg_buf);
    
    ajDebug("ajMessVCrashFL: %s\n", mesg_buf);

    ajMessInvokeDebugger();
    
    exit(EXIT_FAILURE);
}
ID ajMessGetMessageC
TY public
MO ajmess
LB core
XX
DE Returns the current message text.
XX
RT const char*
RD Message text
RX
//


const char* ajMessGetMessageC(void)
{
    return messbuf;
}
ID ajMessGetSysmessageC
TY public
MO ajmess
LB core
XX
DE Returns the system message text from 'strerror' from the standard C
DE library.
XX
RT const char*
RD System error message.
RX
//


const char* ajMessGetSysmessageC(void)
{
    char *mess;

    if(errno)
	mess = ajFmtString(SYSERR_FORMAT, errno, strerror(errno));
    else
	mess = ajFmtString(SYSERR_OK, errno, strerror(errno));
      
    /* must make copy - will be used when mess* calls itself */
    AJFREE(messErrMess);
    messErrMess = ajSysFuncStrdup(mess);

    AJFREE(mess);

    return messErrMess;
}
ID messFormat
TY static
MO ajmess
LB core
XX
DE Used by the AJAXFORMAT macros to format messages.
DE
DE This routine does the formatting of the message string using vsprintf,
DE it copes with the format string accidentally being our internal buffer.
DE
DE This routine does its best to check that the vsprintf is successful, if
DE not the routine bombs out with an error message. Note that num_bytes is
DE the return value from vsprintf.
DE
DE Failures trapped:
DE num_bytes less than 0  =  vsprintf failed, reason is reported.
DE num_bytes + 1 more than BUFSIZE  =  our internal buffer size was exceeded.
DE (vsprintf returns number of bytes written
DE _minus_ terminating NULL)
XX
PN [1]
PA v args va_list
PD Variable length argument list
PX
PN [2]
PA r format const char*
PD Format string
PX
PN [3]
PA r prefix const char*
PD Message prefix
PX
RT char*
RD Formatted message text
RX
//


static char* messFormat(va_list args, const char *format, const char *prefix)
{
    static char *new_buf = NULL;
    char *buf_ptr;
    ajint num_bytes;
    ajint prefix_len;


    if(format == NULL)
	ajMessCrash("invalid call, no format string.");

    if(prefix == NULL)
	prefix_len = 0;
    else
    {
	prefix_len = strlen(prefix);
	if((prefix_len + 1) > PREFIXSIZE)
	    ajMessCrash("prefix string is too long.");
    }


    /* If they supply the internal buffer as an argument, e.g. because they */
    /* used ajFmtString as an arg, then make a copy, otherwise use internal */
    /* buffer.                                                              */

    if(format == messbuf)
    {
	if(new_buf != NULL)
	    AJFREE(new_buf);
	buf_ptr = new_buf = ajSysFuncStrdup(format);
    }
    else
	buf_ptr = messbuf;

    /* Add the prefix if there is one. */
    if(prefix != NULL)
    {
	if(!strcpy(buf_ptr, prefix))
	    ajMessCrash("strcpy failed");
    }


    num_bytes = prefix_len + 1;
    num_bytes += ajFmtVPrintCL((buf_ptr + prefix_len),BUFSIZE, format, args);

    /*
    **  Check the result. This should never happen using the
    **  ajFmtVPrintCL routine instead of the vsprintf routine
    */

    if(num_bytes < 0)
	ajMessCrash("vsprintf failed: %s", ajMessGetSysmessageC());
    else if(num_bytes > BUFSIZE)
	ajMessCrash("messubs internal buffer size (%d) exceeded, "
		    "a total of %d bytes were written",
		    BUFSIZE, num_bytes);

    return(buf_ptr);
}
ID messGetFilename
TY static
MO ajmess
LB core
XX
DE Converts a filename into a base file name. Used for filenames passed
DE by macros from __FILE__ which could include part or all of the path
DE depending on how the source code was compiled.
XX
PN [1]
PA r path const char*
PD File name, possibly with full path.
PX
RT char*
RD Base file name
RX
//


static char* messGetFilename(const char *path)
{
    static char *path_copy = NULL;
    const char *path_delim = SUBDIR_DELIMITER_STR;
    char *result = NULL;
    char *tmp;

    if(path != NULL)
    {
	if(strcmp((path + strlen(path) - 1), path_delim) != 0)
	{				/* Last char = "/" ?? */
	    if(path_copy != NULL)
		AJFREE(path_copy);

	    path_copy = ajSysFuncStrdup(path);

	    tmp = ajSysFuncStrtok(path_copy, path_delim);

	    while(tmp != NULL)
	    {
		result = tmp;	 /* Keep results of previous strtok */

		tmp = ajSysFuncStrtok(NULL, path_delim);
	    }
	}
    }

    return(result);
}
ID ajMessErrorInit
TY public
MO ajmess
LB core
XX
DE Initialises the stored program name.
XX
PN [1]
PA r progname const char*
PD Program name.
PX
RT void
RD 
RX
//


void ajMessErrorInit(const char *progname)
{
    if(progname != NULL)
	messageG.progname = ajSysFuncStrdup(messGetFilename(progname));

    return;
}
ID ajMessSetErr
TY public
MO ajmess
LB core
XX
DE Stores the source file name (converted to a base name)
DE and the source line number to be
DE reported by the crash routines.
DE
DE Invoked automatically by a macro (e.g. ajFatal) where needed.
XX
PN [1]
PA r filename const char*
PD source filename, __FILE__
PX
PN [2]
PA r line_num ajint
PD source line number, __LINE__
PX
RT void
RD 
RX
//


void ajMessSetErr(const char *filename, ajint line_num)
{
    assert(filename != NULL && line_num != 0);

    /*
    ** We take the basename here because __FILE__ can be a path rather
    ** than just a filename, depending on how a module was compiled.
    */

    messageG.filename = ajSysFuncStrdup(messGetFilename(filename));

    messageG.line_num = line_num;

    ajUtilCatch();

    return;
}
ID messGetErrorProgram
TY static
MO ajmess
LB core
XX
DE Returns the stored program name.
XX
RT char*
RD Program name
RX
//


static char* messGetErrorProgram(void)
{
    return(messageG.progname);
}
ID messGetErrorFile
TY static
MO ajmess
LB core
XX
DE Returns the stored error file name.
XX
RT char*
RD Error file name
RX
//


static char* messGetErrorFile(void)
{
    return(messageG.filename);
}
ID messGetErrorLine
TY static
MO ajmess
LB core
XX
DE Returns the stored error source line number.
XX
RT ajint
RD Original source code line number
RX
//


static ajint messGetErrorLine(void)
{
    return(messageG.line_num);
}
ID ajMessErrorSetFile
TY public
MO ajmess
LB core
XX
DE Opens a file and sets this to be the error file.
XX
PN [1]
PA r errfile const char*
PD Error file name
PX
RT AjBool
RD ajTrue on success
RX
//


AjBool ajMessErrorSetFile(const char *errfile)
{
    FILE *fp = 0;

    if(errfile)
	if((fp = fopen(errfile,"r")))
	{
	    messErrorFile = ajSysFuncStrdup(errfile);
	    fclose(fp);

	    return ajTrue;
	}

    return ajFalse;
}
ID ajMessReadErrorFile
TY static
MO ajmess
LB core
XX
DE Reads the error message file (with a default of
DE $EMBOSS_ROOT/messages/messages.english)
DE and loads the results into an internal table.
XX
RT AjBool
RD ajTrue on success
RX
//


static AjBool ajMessReadErrorFile(void)
{
    char line[512];
    char name[12];
    char message[200];
    FILE *fp=0;
    char *mess;
    char *cp;
    char *namestore;
    char *messstore;

    if(messErrorFile)
	fp = fopen(messErrorFile,"r");

    if(!fp)
    {
	messErrorFile = ajFmtString("%s/messages/messages.english",
				    getenv("EMBOSS_ROOT"));
	fp = fopen(messErrorFile,"r");
    }


    if(!fp)
	return ajFalse;

    messErrorTable = ajTablecharNew(100);

    while(fgets(line, 512, fp))
    {
	if(sscanf(line,"%s %s",name,message)!=2)
	    ajFatal("Library sscanf1");

	cp = strchr(line,'"');
	cp++;
	mess = &message[0];

	while(*cp != '"')
	{
	    *mess = *cp;
	    cp++;
	    mess++;
	}

	*mess = '\0';
	namestore = ajCharNewC(name);
	messstore = ajCharNewC(message);

        if(ajTablePut(messErrorTable, namestore, messstore))
	    ajErr("%s is listed more than once in file %s",
                  name,messErrorFile);
    }

    return ajTrue;
}
ID ajMessOutCode
TY public
MO ajmess
LB core
XX
DE Writes an output message for a given message code.
XX
PN [1]
PA r code const char*
PD Message code
PX
RT void
RD 
RX
//


void ajMessOutCode(const char *code)
{
    const char *mess=0;

    if(messErrorTable)
    {
	mess = ajTableFetchC(messErrorTable, code);

	if(mess)
	    ajMessOut(mess);
	else
	    ajMessOut("could not find error code %s",code);
    }
    else
    {
	if(ajMessReadErrorFile())
	{
	    mess = ajTableFetchC(messErrorTable, code);

	    if(mess)
		ajMessOut(mess);
	    else
		ajMessOut("could not find error code %s",code);
	}
	else
	    ajMessOut("Could not read the error file hence no reference to %s",
		      code);
    }

    return;
}
ID ajMessErrorCode
TY public
MO ajmess
LB core
XX
DE Writes an error message for a given message code.
XX
PN [1]
PA r code const char*
PD Error code
PX
RT void
RD 
RX
//


void ajMessErrorCode(const char *code)
{
    const char *mess = 0;

    if(messErrorTable)
    {
	mess = ajTableFetchC(messErrorTable, code);

	if(mess)
	    ajErr(mess);
	else
	    ajErr("could not find error code %s",code);
    }
    else
    {
	if(ajMessReadErrorFile())
	{
	    mess = ajTableFetchC(messErrorTable, code);

	    if(mess)
		ajErr(mess);
	    else
		ajErr("could not find error code %s",code);
	}
	else
	    ajErr("Could not read the error file, "
		  "hence no reference to %s",
		  code);
    }

    return;
}
ID ajMessCrashCodeFL
TY public
MO ajmess
LB core
XX
DE Writes an error message for a given message code and crashes.
XX
PN [1]
PA r code const char*
PD Error code
PX
RT void
RD 
RX
//


__noreturn void  ajMessCrashCodeFL(const char *code)
{
    const char *mess = 0;

    if(messErrorTable)
    {
	mess = ajTableFetchC(messErrorTable, code);

	if(mess)
	    ajMessCrashFL(mess);
	else
	    ajMessCrashFL("could not find error code %s",code);
    }
    else
    {
	if(ajMessReadErrorFile())
	{
	    mess = ajTableFetchC(messErrorTable, code);

	    if(mess)
		ajMessCrashFL(mess);
	    else
		ajMessCrashFL("could not find error code %s",code);
	}
	else
	    ajMessCrashFL("Could not read the error file "
			  "hence no reference to %s",
			  code);
    }
}
ID ajMessCodesDelete
TY public
MO ajmess
LB core
XX
DE Deletes the message codes table.
XX
RT void
RD 
RX
//


void ajMessCodesDelete(void)
{
    messTableDelete(&messErrorTable);
}
ID messTableDelete
TY static
MO ajmess
LB core
XX
DE Delete a table, simply freeing the key and value
XX
PN [1]
PA d table AjPTable*
PD Table
PX
RT void
RD 
RX
//


static void messTableDelete(AjPTable* table) 
{
    void **keyarray = NULL;
    void **valarray = NULL;
    ajint i;

    if(!table)
	return;
    if(!*table)
	return;

    ajTableToarrayKeysValues(*table, &keyarray, &valarray);

    for(i = 0; keyarray[i]; i++)
    {
	AJFREE(keyarray[i]);
	AJFREE(valarray[i]);
    }

    AJFREE(keyarray);
    AJFREE(valarray);

    ajTableFree(table);
    *table = NULL;

    return;
}
ID ajDebug
TY public
MO ajmess
LB core
XX
DE Writes a debug message to the debug file if debugging is on.
DE Typically, debugging is turned on by adding '-debug' to the command line
DE or by defining a variable prefix_DEBUG
DE
DE Avoid using this call in any code which can be invoked before the command
DE line processing is complete as it can be a problem to find a reasonable
DE file name for debug output under these circumstances.
XX
PN [1]
PA r fmt const char*
PD Format.
PX
PN [2]
PA v vararg ...
PD Variable argument list.
PX
RT void
RD 
RX
//


void ajDebug(const char* fmt, ...)
{
    va_list args;
    static ajint debugset = 0;
    static ajint depth    = 0;
    AjPStr bufstr         = NULL;
    
    if(depth)
    {				   /* recursive call, get out quick */
	if(messDebugFile)
	{
	    va_start(args, fmt);
	    ajFmtVPrintF(messDebugFile, fmt, args);
	    va_end(args);
	}

	return;
    }

    depth++;

    if(!debugset && acdDebugSet)
    {
	messDebug = acdDebug;

	if(messDebug)
	{
	    ajFmtPrintS(&messDebugName, "%s.dbg", ajStrGetPtr(acdProgram));
	    messDebugFile = ajFileNewOutNameS(messDebugName);

	    if(!messDebugFile)
		ajFatal("Cannot open debug file %S",messDebugName);

	    if(ajNamGetValueC("debugbuffer", &bufstr))
	    {
		ajStrToBool(bufstr, &acdDebugBuffer);
	    }

	    if(!acdDebugBuffer)
		ajFileSetUnbuffer(messDebugFile);

	    ajFmtPrintF(messDebugFile, "Debug file %F buffered:%B\n",
			 messDebugFile, acdDebugBuffer);
	    ajStrDel(&bufstr);
	}

	debugset = 1;
    }

    if(messDebug)
    {
	va_start(args, fmt);
	ajFmtVPrintF(messDebugFile, fmt, args);
	va_end(args);
    }

    depth--;

    return;
}
ID ajDebugOn
TY public
MO ajmess
LB core
XX
DE Test whether debugging is on.
XX
RT AjBool
RD True if user has enabled debugging
RX
//


AjBool ajDebugOn(void)
{
    return acdDebug;
}
ID ajDebugTest
TY public
MO ajmess
LB core
XX
DE Tests a token string and returns true if the user has requested debug output
XX
PN [1]
PA r token const char*
PD Token name
PX
RT AjBool
RD True if token has debugging requested
RX
//


AjBool ajDebugTest(const char* token)
{
    AjPStr filename = NULL;
    const char* debugtestname = ".debugtest";
    char* ctoken = NULL;
    AjPStr line = NULL;
    AjPStr strtoken = NULL;
    AjPStr rest = NULL;
    static ajint depth    = 0;

    struct 
    {
        ajuint count;
        ajuint max;
    } *stats;
    
    if(depth)
        return ajFalse;

    depth++;

    if(!messDebugTestInit)
    {
        filename = ajStrNewC(debugtestname);

        if(ajFilenameExists(filename))
        {
            messDebugTestFile = ajFileNewInNameS(filename);
        }
        else
        {
            ajFmtPrintS(&filename, "%s%s%s",
                        getenv("HOME"), SLASH_STRING, debugtestname); 
            if(ajFilenameExists(filename))
                messDebugTestFile = ajFileNewInNameS(filename);
        }
        ajStrDel(&filename);

        if(messDebugTestFile) 
        {
            messDebugTestTable = ajTablecharNew(256);

            while(ajReadlineTrim(messDebugTestFile, &line))
            {
                if(!ajStrCutComments(&line))
                    continue;
                if(ajStrExtractFirst(line, &rest, &strtoken))
                {
                    AJNEW0(stats);
                    ctoken = ajCharNewS(strtoken);
                    if(ajStrIsInt(rest))
                        ajStrToUint(rest, &stats->max);
                    else
                        stats->max = UINT_MAX;

                    ajTablePut(messDebugTestTable, ctoken, stats);
                    ctoken = NULL;
                    stats = NULL;
                }
            }

            ajStrDel(&line);
            ajStrDel(&strtoken);
            ajStrDel(&rest);
            ajFileClose(&messDebugTestFile);
        }
        messDebugTestInit = ajTrue;
     }

    depth--;
    
    if(!messDebugTestTable)
        return ajFalse;

    depth++;
    stats = ajTableFetchmodC(messDebugTestTable, token);
    depth--;
    

    if(!stats)
        return ajFalse;

    if(!stats->max)
        return ajTrue;

    if(stats->count++ >= stats->max)
        return ajFalse;

    return ajTrue;
}
ID ajMessGetDebugfile
TY public
MO ajmess
LB core
XX
DE Returns the file used for debug output, or NULL if no debug file is open.
XX
RT FILE*
RD C runtime library file handle for debug output.
RX
//


FILE* ajMessGetDebugfile(void)
{
    if(!messDebugFile)
	return NULL;

    return ajFileGetFileptr(messDebugFile);
}
ID ajUserGet
TY public
MO ajmess
LB core
XX
DE Writes a prompt to the terminal and reads one line from the user.
XX
PN [1]
PA w pthis AjPStr*
PD Buffer for the user response.
PX
PN [2]
PA r fmt const char*
PD Format string
PX
PN [3]
PA v vararg ...
PD Variable argument list.
PX
RT ajint
RD Length of response string.
RX
//


ajint ajUserGet(AjPStr* pthis, const char* fmt, ...)
{
    AjPStr thys;
    const char *cp;
    char *buff;
    va_list args;
    ajint ipos;
    ajint isize;
    ajint ilen;
    ajint jlen;
    ajint fileBuffSize = ajFileValueBuffsize();

    va_start(args, fmt);
    ajFmtVError(fmt, args);
    va_end(args);

    if(ajFileValueRedirectStdin())
    {
	ajUser("(Standard input in use: using default)");
	ajStrAssignC(pthis, "");

	return ajStrGetLen(*pthis);
    }

    ajStrSetRes(pthis, fileBuffSize);
    buff  = ajStrGetuniquePtr(pthis);
    thys = *pthis;
    isize = ajStrGetRes(thys);
    ilen  = 0;
    ipos  = 0;
    

    /*ajDebug("ajUserGet buffer len: %d res: %d ptr: %x\n",
	     ajStrGetLen(thys), ajStrGetRes(thys), thys->Ptr);*/

    while(buff)
    {

#ifndef __ppc__
	cp = fgets(&buff[ipos], isize, stdin);
#else
	cp = ajSysFuncFgets(&buff[ipos], isize, stdin);
#endif

        if(!cp && !ipos)
	{
	    if(feof(stdin))
	    {
		ajErr("Unable to get reply from user - end of standard input");
		ajExitBad();
	    }
	    else
		ajFatal("Error reading from user: '%s'\n",
			strerror(errno));
	}

	jlen = strlen(&buff[ipos]);
	ilen += jlen;

	/*
	 ** We need to read again if:
	 ** We have read the entire buffer
	 ** and we don't have a newline at the end
	 ** (must be careful about that - we may just have read enough)
	 */
	ajStrSetValidLen(pthis, ilen);
	thys = *pthis;

	if((jlen == (isize-1)) &&
	   (ajStrGetCharLast(thys) != '\n'))
	{
	    ajStrSetRes(pthis, ajStrGetRes(thys)+fileBuffSize);
	    thys = *pthis;
	    /*ajDebug("more to do: jlen: %d ipos: %d isize: %d ilen: %d "
		    "Size: %d\n",
		    jlen, ipos, isize, ilen, ajStrGetRes(thys));*/
	    ipos += jlen;
	    buff = ajStrGetuniquePtr(pthis);
	    isize = ajStrGetRes(thys) - ipos;
	    /* ajDebug("expand to: ipos: %d isize: %d Size: %d\n",
		    ipos, isize, ajStrGetRes(thys)); */

	}
	else
	    buff = NULL;
    }
    
    ajStrSetValidLen(pthis, ilen);

    if(ajStrGetCharLast(*pthis) == '\n')
	ajStrCutEnd(pthis, 1);

    /* PC files have \r\n Macintosh files have just \r : this fixes both */

    if(ajStrGetCharLast(*pthis) == '\r')
    {
	/*ajDebug("Remove carriage-return characters from PC-style files\n");*/
	ajStrCutEnd(pthis, 1);
    }

    ajStrTrimWhite(pthis);

    return ajStrGetLen(*pthis);
}
ID ajMessExit
TY public
MO ajmess
LB core
XX
DE Delete any static initialised values
XX
RT void
RD 
RX
//


void ajMessExit(void)
{
    AJFREE(messErrMess);
    messTableDelete(&messErrorTable);
    messTableDelete(&messDebugTestTable);

    return;
}
ID ajMessExitDebug
TY public
MO ajmess
LB core
XX
DE Delete any static initialised values for ajDebug calls
XX
RT void
RD 
RX
//


void ajMessExitDebug(void)
{
    ajFileClose(&messDebugFile);
    ajStrDel(&messDebugName);
    ajFileClose(&messDebugTestFile);
    ajStrDel(&messDebugName);

    return;
}
ID namEntryDelete
TY static
MO ajnam
LB core
XX
DE Deletes a variable, database, or resource entry from the internal table.
XX
PN [1]
PA d Pentry NamPEntry*
PD The entry to be deleted.
PX
PN [2]
PA r which ajint
PD Internal table entry type
PX
RT void
RD 
RX
//


static void namEntryDelete(NamPEntry* Pentry, ajint which)
{

    AjPTable attrtable;
    NamPEntry entry;

    entry = *Pentry;

    ajStrDel(&entry->name);
    ajStrDel(&entry->value);
    ajStrDel(&entry->file);

    if(which == TYPE_DB)
    {
	attrtable = (AjPTable) entry->data;
        namDbAttrtableFree(&attrtable);
    }

    else if(which == TYPE_SVR)
    {
	attrtable = (AjPTable) entry->data;
        namSvrAttrtableFree(&attrtable);
    }

    else if(which == TYPE_RESOURCE)
    {
	attrtable = (AjPTable) entry->data;
        namRsAttrtableFree(&attrtable);
    }

    else if(which == TYPE_SVRDB)
    {
	attrtable = (AjPTable) entry->data;
        namListMasterDelete(attrtable, TYPE_DB);
        ajTableFree(&attrtable);
    }

    else if(which == TYPE_SVRALI)
    {
	attrtable = (AjPTable) entry->data;
        namListMasterDelete(attrtable, TYPE_ENV);
        ajTableFree(&attrtable);
    }

    else if(which == TYPE_ENV)
    {
    }

    else if(which == TYPE_ALIAS)
    {
    }

    AJFREE(entry);

    return;
}
ID namDbAttrtableFree
TY static
MO ajnam
LB core
XX
DE Free a database attribute table
XX
PN [1]
PA d Ptable AjPTable*
PD Attribute table
PX
//


static void namDbAttrtableFree(AjPTable* Ptable)
{
    AjPStr *keys = NULL;
    void** values = NULL;
    ajulong n;
    ajulong i;
    const NamPAttr attr = NULL;

    n = ajTableToarrayKeysValues(*Ptable, (void***)&keys, &values);

    for(i=0UL; i < n; i++)
    {
        attr = ajTableFetchS(namDbAttrTable, keys[i]);

        if(!attr)
            ajStrDel((AjPStr*) &values[i]);
        else
        {
            switch(attr->Type)
            {
                case ATTR_LIST:
                    ajListstrFreeData((AjPList*) &values[i]);
                    break;
                default:
                    ajStrDel((AjPStr*) &values[i]);
                    break;
            }
        }

        ajStrDel(&keys[i]);
    }
    
    ajTableFree(Ptable);

    AJFREE(keys);
    AJFREE(values);

    return;
}
ID namRsAttrtableFree
TY static
MO ajnam
LB core
XX
DE Free a resource attribute table
XX
PN [1]
PA d Ptable AjPTable*
PD Attribute table
PX
//


static void namRsAttrtableFree(AjPTable* Ptable)
{
    AjPStr *keys = NULL;
    void** values = NULL;
    ajulong n;
    ajulong i;
    const NamPAttr attr = NULL;

    n = ajTableToarrayKeysValues(*Ptable, (void***) &keys, &values);

    for(i=0UL; i < n; i++)
    {
        attr = ajTableFetchS(namResAttrTable, keys[i]);

        if(!attr)
            ajStrDel((AjPStr*) &values[i]);
        else
        {
            switch(attr->Type)
            {
                case ATTR_LIST:
                    ajListstrFreeData((AjPList*) &values[i]);
                    break;
                default:
                    ajStrDel((AjPStr*) &values[i]);
                    break;
            }
        }

        ajStrDel(&keys[i]);
    }

    ajTableFree(Ptable);

    AJFREE(keys);
    AJFREE(values);

    return;
}
ID namSvrAttrtableFree
TY static
MO ajnam
LB core
XX
DE Free a server attribute table
XX
PN [1]
PA d Ptable AjPTable*
PD Attribute table
PX
//


static void namSvrAttrtableFree(AjPTable* Ptable)
{
    AjPStr *keys = NULL;
    void** values = NULL;
    ajulong n;
    ajulong i;
    const NamPAttr attr = NULL;

    n = ajTableToarrayKeysValues(*Ptable, (void***) &keys, &values);

    for(i=0UL; i < n; i++)
    {
        attr = ajTableFetchS(namSvrAttrTable, keys[i]);

        if(!attr)
            ajStrDel((AjPStr*) &values[i]);
        else
        {
            switch(attr->Type)
            {
                case ATTR_LIST:
                    ajListstrFreeData((AjPList*) &values[i]);
                    break;
                default:
                    ajStrDel((AjPStr*) &values[i]);
                    break;
            }
        }

        ajStrDel(&keys[i]);
    }
    
    ajTableFree(Ptable);

    AJFREE(keys);
    AJFREE(values);

    return;
}
ID namListMasterDelete
TY static
MO ajnam
LB core
XX
DE Deletes all databases in the internal table. The table is converted to
DE an array, and each entry in turn is passed to namEntryDelete.
XX
PN [1]
PA u table AjPTable
PD Table object
PX
PN [2]
PA r which ajint
PD Internal table entry type
PX
RT void
RD 
RX
//


static void namListMasterDelete(AjPTable table, ajint which)
{
    ajint i;
    NamPEntry fnew = NULL;
    void **keyarray = NULL;
    void **valarray = NULL;

    if(!table)
        return;

    ajTableToarrayKeysValues(table, &keyarray, &valarray);

    for(i = 0; keyarray[i]; i++)
    {
	AJFREE(keyarray[i]);		/* the key */
	fnew = (NamPEntry) valarray[i];
	namEntryDelete(&fnew, which);
    }

    AJFREE(keyarray);
    AJFREE(valarray);

    return;
}
ID ajNamPrintSvrAttr
TY public
MO ajnam
LB core
XX
DE Prints a report of the server attributes available (for entrails)
XX
PN [1]
PA u outf AjPFile
PD Output file
PX
PN [2]
PA r full AjBool
PD Full output if AjTrue
PX
RT void
RD 
RX
//


void ajNamPrintSvrAttr(AjPFile outf, AjBool full)
{
    ajint i;
    AjPStr tmpstr = NULL;
    ajuint maxtmp = 0;

    (void) full;			/* no extra detail to report */

    ajFmtPrintF(outf, "# Server attributes\n");
    ajFmtPrintF(outf, "# %-15s %-12s %s\n", "Attribute", "Default", "Comment");
    ajFmtPrintF(outf, "namSvrAttrs {\n");

    for(i=0; namSvrAttrs[i].Name; i++)
    {
	ajFmtPrintF(outf, "  %-15s", namSvrAttrs[i].Name);
	ajFmtPrintS(&tmpstr, "\"%s\"", namSvrAttrs[i].Defval);

	if(ajStrGetLen(tmpstr) > maxtmp)
	    maxtmp = ajStrGetLen(tmpstr);

	ajFmtPrintF(outf, " %-12S", tmpstr);
	ajFmtPrintF(outf, " \"%s\"\n", namSvrAttrs[i].Comment);
    }

    if(maxtmp > 12)
        ajWarn("ajNamPrintSvrAttr max tmpstr len %u",
               maxtmp);	      

    ajFmtPrintF(outf, "}\n\n");
    ajStrDel(&tmpstr);

    return;
}
ID ajNamPrintDbAttr
TY public
MO ajnam
LB core
XX
DE Prints a report of the database attributes available (for entrails)
XX
PN [1]
PA u outf AjPFile
PD Output file
PX
PN [2]
PA r full AjBool
PD Full output if AjTrue
PX
RT void
RD 
RX
//


void ajNamPrintDbAttr(AjPFile outf, AjBool full)
{
    ajint i;
    AjPStr tmpstr = NULL;
    ajuint maxtmp = 0;

    (void) full;			/* no extra detail to report */

    ajFmtPrintF(outf, "# Database attributes\n");
    ajFmtPrintF(outf, "# %-15s %-12s %s\n", "Attribute", "Default", "Comment");
    ajFmtPrintF(outf, "namDbAttrs {\n");

    for(i=0; namDbAttrs[i].Name; i++)
    {
	ajFmtPrintF(outf, "  %-15s", namDbAttrs[i].Name);
	ajFmtPrintS(&tmpstr, "\"%s\"", namDbAttrs[i].Defval);

	if(ajStrGetLen(tmpstr) > maxtmp)
	    maxtmp = ajStrGetLen(tmpstr);

	ajFmtPrintF(outf, " %-12S", tmpstr);
	ajFmtPrintF(outf, " \"%s\"\n", namDbAttrs[i].Comment);
    }

    if(maxtmp > 12)
        ajWarn("ajNamPrintDbAttr max tmpstr len %u",
               maxtmp);	      

    ajFmtPrintF(outf, "}\n\n");
    ajStrDel(&tmpstr);

    return;
}
ID ajNamPrintRsAttr
TY public
MO ajnam
LB core
XX
DE Prints a report of the resource attributes available (for entrails)
XX
PN [1]
PA u outf AjPFile
PD Output file
PX
PN [2]
PA r full AjBool
PD Full output if AjTrue
PX
RT void
RD 
RX
//


void ajNamPrintRsAttr(AjPFile outf, AjBool full)
{
    ajint i;
    AjPStr tmpstr = NULL;
    ajuint maxtmp = 0;

    (void) full;			/* no extra detail to report */

    ajFmtPrintF(outf, "# Resource attributes\n");
    ajFmtPrintF(outf, "# %-15s %-26s %s\n", "Attribute", "Default", "Comment");
    ajFmtPrintF(outf, "namRsAttrs {\n");

    for(i=0; namRsAttrs[i].Name; i++)
    {
	ajFmtPrintF(outf, "  %-15s", namRsAttrs[i].Name);
	ajFmtPrintS(&tmpstr, "\"%s\"", namRsAttrs[i].Defval);

	if(ajStrGetLen(tmpstr) > maxtmp)
	    maxtmp = ajStrGetLen(tmpstr);

	ajFmtPrintF(outf, " %-36S", tmpstr);
	ajFmtPrintF(outf, " \"%s\"\n", namRsAttrs[i].Comment);
    }

    ajFmtPrintF(outf, "}\n\n");

    if(maxtmp > 36)
        ajWarn("ajNamPrintRsAttr max tmpstr len %u",
               maxtmp);	      

    ajStrDel(&tmpstr);

    return;
}
ID namDebugDatabase
TY static
MO ajnam
LB core
XX
DE Prints a report of defined attributes for a database definition.
XX
PN [1]
PA r dbtable const AjPTable
PD Attribute table from a database entry.
PX
RT void
RD 
RX
//


static void namDebugDatabase(const AjPTable dbtable)
{
    ajulong i;
    ajulong nkeys;
    AjPStr *names = NULL;
    AjPStr *values = NULL;

    nkeys = ajTableToarrayKeysValues(dbtable,
                                     (void***) &names, (void***) &values);
    
   for(i=0UL; i < nkeys; i++) 
	if(ajStrGetLen(values[i]))
	    namUser("\t%S: %S\n", names[i], values[i]);

    AJFREE(names);
    AJFREE(values);

    return;
}
ID namDebugServer
TY static
MO ajnam
LB core
XX
DE Prints a report of defined attributes for a server definition.
XX
PN [1]
PA r svrtable const AjPTable
PD Attribute table from a server entry.
PX
RT void
RD 
RX
//


static void namDebugServer(const AjPTable svrtable)
{
    ajulong i;
    ajulong nkeys;
    AjPStr *names = NULL;
    AjPStr *values = NULL;

    nkeys = ajTableToarrayKeysValues(svrtable,
                                     (void***) &names, (void***) &values);
    
   for(i=0UL; i < nkeys; i++) 
	if(ajStrGetLen(values[i]))
	    namUser("\t%S: %S\n", names[i], values[i]);

    AJFREE(names);
    AJFREE(values);

    return;
}
ID namDebugResource
TY static
MO ajnam
LB core
XX
DE Prints a report of defined attributes for a resource definition.
XX
PN [1]
PA r rstable const AjPTable
PD Attribute table from a database entry.
PX
RT void
RD 
RX
//


static void namDebugResource(const AjPTable rstable)
{
    ajulong i;
    ajulong nkeys;
    AjPStr *names = NULL;
    AjPStr *values = NULL;

    nkeys = ajTableToarrayKeysValues(rstable,
                                     (void***) &names, (void***) &values);
    
   for(i=0UL; i < nkeys; i++) 
	if(ajStrGetLen(values[i]))
	    namUser("\t%S: %S\n", names[i], values[i]);

    AJFREE(names);
    AJFREE(values);

    return;
}
ID namDebugMaster
TY static
MO ajnam
LB core
XX
DE Lists databases or variables defined in the internal table.
XX
PN [1]
PA r table const AjPTable
PD Table object
PX
PN [2]
PA r which ajint
PD Variable type, either TYPE_ENV for environment
PD variables or TYPE_DB for databases or
PD TYPE_RESOURCE for resources.
PX
RT void
RD 
RX
//


static void namDebugMaster(const AjPTable table, ajint which)
{
    ajint i;
    NamPEntry fnew;
    void **keyarray = NULL;
    void **valarray = NULL;
    char *key;

    ajTableToarrayKeysValues(table, &keyarray, &valarray);

    for(i = 0; keyarray[i]; i++)
    {
	key  = (char*) keyarray[i];
	fnew = (NamPEntry) valarray[i];

	if(TYPE_SVR == which)
	{
	    namUser("SVR %S\t *%s*\n", fnew->name, key);
	    namDebugServer(fnew->data);
	    namUser("\n");
	}

	else if(TYPE_DB == which)
	{
	    namUser("DB %S\t *%s*\n", fnew->name, key);
	    namDebugDatabase(fnew->data);
	    namUser("\n");
	}

	else if(TYPE_RESOURCE == which)
	{
	    namUser("RES %S\t *%s*\n", fnew->name, key);
	    namDebugResource(fnew->data);
	    namUser("\n");
	}

	else if(TYPE_ENV == which)
	    namUser("ENV %S\t%S\t *%s*\n",fnew->name,fnew->value,key);

	else if(TYPE_ALIAS == which)
	    namUser("ALIAS %S\t%S\t *%s*\n",fnew->name,fnew->value,key);
    }

    AJFREE(keyarray);
    AJFREE(valarray);

    return;
}
ID ajNamSvrGetAttrC
TY public
MO ajnam
LB core
XX
DE Return the value for a server attribute
XX
PN [1]
PA r name const AjPStr
PD server name
PX
PN [2]
PA r attribute const char*
PD server attribute name
PX
PN [3]
PA w value AjPStr*
PD attribute value
PX
RT AjBool
RD true if found
RX
//


AjBool ajNamSvrGetAttrC(const AjPStr name, const char *attribute,
                        AjPStr *value)
{
    ajint j;
    NamPEntry fnew = NULL;
    AjPTable svrtable;
    const AjPStr svrvalue;

    ajDebug("ajNamSvrGetAttrC '%S' '%s'\n", name, attribute);

    fnew = ajTableFetchmodS(namSvrMasterTable, name);

    if(!fnew)
    {
        ajWarn("unknown server '%S'",
               name);
	return ajFalse;
    }

    svrtable = (AjPTable) fnew->data;
    j = namSvrAttrC(attribute);

    if(j < 0)
    {
	ajWarn("unknown attribute '%s' requested for server '%S'",
               attribute, name);
        return ajFalse;
    }

    svrvalue = ajTableFetchmodC(svrtable, attribute);

    if(ajStrGetLen(svrvalue))
    {
	ajStrAssignS(value,svrvalue);

	return ajTrue;
    }

    return ajFalse;
}
ID ajNamSvrGetAttrS
TY public
MO ajnam
LB core
XX
DE Return the value for a server attribute
XX
PN [1]
PA r name const AjPStr
PD server name
PX
PN [2]
PA r attribute const AjPStr
PD server attribute name
PX
PN [3]
PA w value AjPStr*
PD attribute value
PX
RT AjBool
RD true if found
RX
//


AjBool ajNamSvrGetAttrS(const AjPStr name, const AjPStr attribute,
                       AjPStr *value)
{
    return ajNamSvrGetAttrC(name, MAJSTRGETPTR(attribute), value);
}
ID ajNamSvrGetAttrSpecialC
TY public
MO ajnam
LB core
XX
DE Return the value for the first occurrence of a named special attribute
XX
PN [1]
PA r name const AjPStr
PD server name
PX
PN [2]
PA r attribute const char*
PD special attribute name
PX
PN [3]
PA w value AjPStr*
PD value
PX
RT ajuint
RD Number of matching special values
RX
//


ajuint ajNamSvrGetAttrSpecialC(const AjPStr name, const char *attribute,
                               AjPStr* value)
{
    ajuint ret = 0;
    ajint j;
    NamPEntry fnew = NULL;
    AjPTable svrtable;
    AjPList svrvalue;
    AjIList iter;
    const AjPStr tmpstr = NULL;
    ajuint attrlen = 0;

    ajDebug("ajNamSvrGetAttrSpecial '%S' '%s'\n", name, attribute);

    attrlen = strlen(attribute);

    fnew = ajTableFetchmodS(namSvrMasterTable, name);

    if(!fnew)
    {
        ajWarn("unknown database '%S'",
               name);
	return ajFalse;
    }

    svrtable = (AjPTable) fnew->data;
    j = namSvrAttrC("special");

    if(j < 0)
    {
	ajWarn("unknown attribute '%s' requested for server '%S'",
               "special", name);
        return ajFalse;
    }

    svrvalue = ajTableFetchmodC(svrtable, "special");

    if(ajListGetLength(svrvalue))
    {
        iter = ajListIterNewread(svrvalue);

        while(!ajListIterDone(iter))
        {
            tmpstr = ajListIterGet(iter);
            if(ajStrPrefixC(tmpstr, attribute) &&
               ajStrGetCharPos(tmpstr, attrlen) == '=')
            {
                if(!ret++)
                {
                    ajStrAssignS(value, tmpstr);
                    ajStrCutStart(value, attrlen+1);
                }
            }
        }

        ajListIterDel(&iter);
    }

    return ret;
}
ID ajNamSvrGetAttrSpecialS
TY public
MO ajnam
LB core
XX
DE Return the value for the first occurrence of a named special attribute
XX
PN [1]
PA r name const AjPStr
PD server name
PX
PN [2]
PA r attribute const AjPStr
PD special attribute name
PX
PN [3]
PA w value AjPStr*
PD value
PX
RT ajuint
RD Number of matching special values
RX
//


ajuint ajNamSvrGetAttrSpecialS(const AjPStr name, const AjPStr attribute,
                               AjPStr* value)
{
    return ajNamSvrGetAttrSpecialC(name, MAJSTRGETPTR(attribute), value);
}
ID ajNamSvrGetAttrlist
TY public
MO ajnam
LB core
XX
DE Return a list of names and values for all of a server defined attributes
XX
PN [1]
PA r name const AjPStr
PD server name
PX
RT AjPList
RD Tag-value list
RX
//


AjPList ajNamSvrGetAttrlist(const AjPStr name)
{
    AjPList ret = NULL;
    AjPTagval tagval = NULL;
    NamPEntry fnew = NULL;
    AjPTable svrtable = NULL;
    const char* svrattr = NULL;
    NamOAttr attr;
    void* svrvalue;
    const AjPStr tmpstr;
    ajuint i;
    AjIList iter;

    fnew = ajTableFetchmodS(namSvrMasterTable, name);

    if(!fnew)
	return NULL;

    ret = ajListNew();
    svrtable = (AjPTable) fnew->data;

    for(i=0; namSvrAttrs[i].Name; i++)
    {
        attr = namSvrAttrs[i];
        svrattr = attr.Name;

        svrvalue = ajTableFetchmodC(svrtable, svrattr);
        if(svrvalue)
        {
            switch(attr.Type)
            {
                case ATTR_LIST:
                    iter = ajListIterNewread(svrvalue);

                    while(!ajListIterDone(iter))
                    {
                        tmpstr = ajListIterGet(iter);
                        tagval = ajTagvalNewC(svrattr,MAJSTRGETPTR(tmpstr));
                        ajListPushAppend(ret, tagval);
                    }

                    ajListIterDel(&iter);
                    break;
                default:
                    tagval = ajTagvalNewC(svrattr,ajStrGetPtr(svrvalue));
                    ajListPushAppend(ret, tagval);
                   break;
            }
        }
    }

    return ret;
  
}
ID ajNamSvrGetdbAttrC
TY public
MO ajnam
LB core
XX
DE Return the value for a server database attribute
XX
PN [1]
PA r name const AjPStr
PD server name
PX
PN [2]
PA r dbname const AjPStr
PD database name
PX
PN [3]
PA r attribute const char*
PD database attribute name
PX
PN [4]
PA w value AjPStr*
PD attribute value
PX
RT AjBool
RD true if found
RX
//


AjBool ajNamSvrGetdbAttrC(const AjPStr name, const AjPStr dbname,
                          const char *attribute,
                          AjPStr *value)
{
    ajint j;
    NamPEntry dbdata = NULL;
    NamPEntry fnew = NULL;
    AjPTable sdbtable;
    AjPTable dbtable;
    const AjPStr dbvalue;

    ajDebug("ajNamSvrGetdbAttrC '%S' '%S' '%s'\n",
            name, dbname, attribute);

    if(!ajNamDatabaseServer(dbname, name))
        return ajFalse;

    dbdata = ajTableFetchmodS(namSvrDatabaseTable, name);
    sdbtable = (AjPTable) dbdata->data;

    fnew = ajTableFetchmodS(sdbtable, dbname);

    dbtable = (AjPTable) fnew->data;
    j = namDbAttrC(attribute);

    if(j < 0)
    {
	ajWarn("unknown attribute '%s' requested for database '%S'",
               attribute, name);
        return ajFalse;
    }

    dbvalue = ajTableFetchmodC(dbtable, attribute);

    if(ajStrGetLen(dbvalue))
    {
	ajStrAssignS(value,dbvalue);

	return ajTrue;
    }

    return ajFalse;
}
ID ajNamSvrGetdbAttrS
TY public
MO ajnam
LB core
XX
DE Return the value for a database attribute
XX
PN [1]
PA r name const AjPStr
PD server name
PX
PN [2]
PA r dbname const AjPStr
PD database name
PX
PN [3]
PA r attribute const AjPStr
PD database attribute name
PX
PN [4]
PA w value AjPStr*
PD attribute value
PX
RT AjBool
RD true if found
RX
//


AjBool ajNamSvrGetdbAttrS(const AjPStr name, const AjPStr dbname,
                          const AjPStr attribute,
                          AjPStr *value)
{
    return ajNamSvrGetdbAttrC(name, dbname, MAJSTRGETPTR(attribute), value);
}
ID ajNamSvrGetdbAttrSpecialC
TY public
MO ajnam
LB core
XX
DE Return the value for a server database special attribute
XX
PN [1]
PA r name const AjPStr
PD server name
PX
PN [2]
PA r dbname const AjPStr
PD database name
PX
PN [3]
PA r attribute const char*
PD database special attribute name
PX
PN [4]
PA w value AjPStr*
PD attribute value
PX
RT ajuint
RD Number of matching special values
RX
//


ajuint ajNamSvrGetdbAttrSpecialC(const AjPStr name, const AjPStr dbname,
                                 const char *attribute,
                                 AjPStr *value)
{
    ajuint ret = 0;
    ajint j;
    NamPEntry fnew = NULL;
    NamPEntry dbdata = NULL;
    AjPTable sdbtable;
    AjPTable dbtable;
    AjPList dbvalue;
    AjIList iter;
    const AjPStr tmpstr = NULL;
    ajuint attrlen = 0;

    ajDebug("ajNamSvrGetdbAttrSpecialC '%S' '%S' '%s'\n",
            name, dbname, attribute);

    if(!ajNamDatabaseServer(dbname, name))
        return ajFalse;

    attrlen = strlen(attribute);
    
    dbdata = ajTableFetchmodS(namSvrDatabaseTable, name);
    sdbtable = (AjPTable) dbdata->data;

    fnew = ajTableFetchmodS(sdbtable, dbname);

    dbtable = (AjPTable) fnew->data;
    j = namDbAttrC("special");

    if(j < 0)
    {
	ajWarn("unknown attribute '%s' requested for database '%S'",
               "special", name);
        return ajFalse;
    }

    dbvalue = ajTableFetchmodC(dbtable, "special");

    if(ajListGetLength(dbvalue))
    {
        iter = ajListIterNewread(dbvalue);

        while(!ajListIterDone(iter))
        {
            tmpstr = ajListIterGet(iter);
            if(ajStrPrefixC(tmpstr, attribute) &&
               ajStrGetCharPos(tmpstr, attrlen) == '=')
            {
                if(!ret++)
                {
                    ajStrAssignS(value, tmpstr);
                    ajStrCutStart(value, attrlen+1);
                }
            }
        }

        ajListIterDel(&iter);
    }

    return ret;
}
ID ajNamSvrGetdbAttrSpecialS
TY public
MO ajnam
LB core
XX
DE Return the value for a database special attribute
XX
PN [1]
PA r name const AjPStr
PD server name
PX
PN [2]
PA r dbname const AjPStr
PD database name
PX
PN [3]
PA r attribute const AjPStr
PD database special attribute name
PX
PN [4]
PA w value AjPStr*
PD attribute value
PX
RT ajuint
RD Number of matching special values
RX
//


ajuint ajNamSvrGetdbAttrSpecialS(const AjPStr name, const AjPStr dbname,
                                 const AjPStr attribute,
                                 AjPStr *value)
{
    return ajNamSvrGetdbAttrSpecialC(name, dbname, MAJSTRGETPTR(attribute),
                                     value);
}
ID ajNamSvrDetails
TY public
MO ajnam
LB core
XX
DE Returns server access method information
XX
PN [1]
PA r name const AjPStr
PD Server name
PX
PN [2]
PA w type AjPStr*
PD datatype - 'Protein', 'Nucleic' etc.
PX
PN [3]
PA w scope AjPStr*
PD datatype scope Sequence Features etc.
PX
PN [4]
PA w id AjBool*
PD ajTrue = can access single entries
PX
PN [5]
PA w qry AjBool*
PD ajTrue = can access wild/query entries
PX
PN [6]
PA w all AjBool*
PD ajTrue = can access all entries
PX
PN [7]
PA w comment AjPStr*
PD comment about server
PX
PN [8]
PA w svrversion AjPStr*
PD Server version
PX
PN [9]
PA w methods AjPStr*
PD server access methods formatted
PX
PN [10]
PA w defined AjPStr*
PD server definition file short name
PX
PN [11]
PA w cachedirectory AjPStr*
PD cache directory name
PX
PN [12]
PA w cachefile AjPStr*
PD cache file name
PX
PN [13]
PA w url AjPStr*
PD server URL
PX
RT AjBool
RD ajTrue if server details were found
RX
//


AjBool ajNamSvrDetails(const AjPStr name, AjPStr* type, AjPStr *scope,
                       AjBool* id, AjBool* qry, AjBool* all,
                       AjPStr* comment, AjPStr* svrversion,
                       AjPStr* methods, AjPStr* defined,
                       AjPStr *cachedirectory, AjPStr* cachefile,
                       AjPStr *url)
{
    NamPType namtype = NULL;
    NamPEntry fnew = NULL;
    AjPTable svrtable = NULL;
    ajint i;
    ajint qryscope;
    AjPStr attrname  = NULL;
    const AjPStr attrval  = NULL;
    const AjPStr svrtype  = NULL;
    AjBool typetested = ajFalse;
    AjPStrTok handle = NULL;
    AjPStr nexttype = NULL;

    AjPFile cacheopen = NULL;

    AjBool *datatype;

    *id = *qry = *all = ajFalse;
    
    ajStrDelStatic(scope);
    ajStrDelStatic(type);
    ajStrDelStatic(comment);
    ajStrDelStatic(methods);
    ajStrDelStatic(defined);
    ajStrDelStatic(cachedirectory);
    ajStrDelStatic(cachefile);
    ajStrDelStatic(svrversion);
    ajStrDelStatic(url);
    
    fnew = ajTableFetchmodS(namSvrMasterTable, name);

    if(fnew)
    {
	ajDebug("ajNamSvrDetails '%S' found\n", name);

	ajStrAssignS(defined, fnew->file);

	svrtable = (AjPTable) fnew->data;
        svrtype = ajTableFetchmodC(svrtable, "type");

	for(i=0; namSvrAttrs[i].Name; i++)
	{
	    if(namSvrAttrs[i].Type != ATTR_STR)
                continue;

            ajStrAssignC(&attrname, namSvrAttrs[i].Name);
            attrval = ajTableFetchmodS(svrtable, attrname);
                ajDebug("Attribute name = %S, value = %S\n",
                        attrname, attrval);

	    if(ajStrGetLen(attrval))
	    {
                if(ajStrMatchC(attrname, "type"))
                {
                    ajStrTokenAssignC(&handle, attrval, " ,;");
                    ajStrAssignC(type, "");
                    ajStrAssignC(scope, "");
                    AJCNEW0(datatype, AJDATATYPE_MAX);

                    while(ajStrTokenNextParse(&handle, &nexttype))
                    {
                        if(ajStrGetLen(*type))
                            ajStrAppendK(type, ' ');

                        if(ajStrPrefixCaseC(attrval, "N"))
                            ajStrAppendC(type, "Nucleotide");
                        else if(ajStrPrefixCaseC(attrval, "P"))
                            ajStrAppendC(type, "Protein");
                        else 
                        {
                            ajStrFmtTitle(&nexttype);
                            ajStrAppendS(type, nexttype);
                        }

                        typetested = ajTrue;
                        namtype = ajTableFetchmodS(namDbTypeTable, nexttype);

                        if(namtype)
                        {
                            if(!datatype[namtype->DataType]++)
                            {
                                if(ajStrGetLen(*scope))
                                    ajStrAppendK(scope, ' ');

                                ajStrAppendC(scope, namtype->Scope);
                            }
                        }
                        else
                            ajWarn("Server '%S' type '%S' unknown scope",
                                   name, nexttype);
                    }
                    ajStrTokenDel(&handle);
                    AJFREE(datatype);
                    ajStrDel(&nexttype);
                }

		if(ajStrMatchC(attrname, "method"))
		{
		    qryscope = namMethod2Scope(attrval, svrtype);

		    if(qryscope & AJMETHOD_ENTRY)
                        *id = ajTrue;

		    if(qryscope & AJMETHOD_QUERY)
                        *qry = ajTrue;

		    if(qryscope & AJMETHOD_ALL)
                        *all = ajTrue;

		    ajStrAppendS(methods, attrval);
		}

		if(ajStrMatchC(attrname, "methodentry"))
		{
                    qryscope = namMethod2Scope(attrval, svrtype);

		    if(qryscope & AJMETHOD_ENTRY)
                        *id = ajTrue;

		    if(ajStrGetLen(*methods))
			ajStrAppendC(methods, ",");

		    ajStrAppendS(methods, attrval);
		    ajStrAppendC(methods, "(id)");
		}

		if(ajStrMatchC(attrname, "methodquery"))
		{
		    qryscope = namMethod2Scope(attrval, svrtype);

		    if(qryscope & AJMETHOD_ENTRY)
                        *id = ajTrue;

		    if(qryscope & AJMETHOD_QUERY)
                        *qry = ajTrue;

		    if(ajStrGetLen(*methods))
			ajStrAppendC(methods, ",");

		    ajStrAppendS(methods, attrval);
		    ajStrAppendC(methods, "(qry)");
		}

		if(ajStrMatchC(attrname, "methodall"))
		{
		    qryscope = namMethod2Scope(attrval, svrtype);

		    if(qryscope & AJMETHOD_ALL)
                        *all = ajTrue;

		    if(ajStrGetLen(*methods))
			ajStrAppendC(methods, ",");

		    ajStrAppendS(methods, attrval);
		    ajStrAppendC(methods, "(all)");
		}

                if(ajStrMatchC(attrname, "comment"))
		    ajStrAssignS(comment, attrval);

                if(ajStrMatchC(attrname, "serverversion"))
		    ajStrAssignS(svrversion, attrval);

                if(ajStrMatchC(attrname, "cachedirectory"))
		    ajStrAssignS(cachedirectory, attrval);

                if(ajStrMatchC(attrname, "cachefile"))
		    ajStrAssignS(cachefile, attrval);

                if(ajStrMatchC(attrname, "url"))
		    ajStrAssignS(url, attrval);
	    }
	}
	
	if(!typetested)
	{
	    ajDebug("Bad server definition for %S: No type. 'P' assumed\n",
		   name);
	    ajWarn("Bad server definition for %S: No type. 'P' assumed",
		   name);
	    ajStrAssignC(type, "P");
	}

	if(!*id && !*qry && !*all)
        {
	    ajDebug("Bad server definition for %S: No method(s) for access\n",
		   name);
	    ajWarn("Bad server definition for %S: No method(s) for access",
		   name);
	}

        ajStrDel(&attrname);

        if(ajStrGetLen(*cachefile))
        {
            cacheopen = namSvrCacheOpen(*cachefile);
            if(cacheopen)
            {
                ajFmtPrintS(cachefile, "%F", cacheopen);
                ajFileClose(&cacheopen);
            }
        }

	return ajTrue;
    }
    
    ajDebug("  '%S' not found\n", name);

    ajStrDel(&attrname);

    return ajFalse;
}
ID ajNamDbGetAttrC
TY public
MO ajnam
LB core
XX
DE Return the value for a database attribute
XX
PN [1]
PA r name const AjPStr
PD database name
PX
PN [2]
PA r attribute const char*
PD database attribute name
PX
PN [3]
PA w value AjPStr*
PD attribute value
PX
RT AjBool
RD true if found
RX
//


AjBool ajNamDbGetAttrC(const AjPStr name, const char *attribute,
                       AjPStr *value)
{
    ajint j;
    NamPEntry fnew = NULL;
    AjPTable dbtable;
    const AjPStr dbvalue;

    ajDebug("ajNamDbGetAttrC '%S' '%s'\n", name, attribute);

    fnew = ajTableFetchmodS(namDbMasterTable, name);

    if(!fnew)
    {
        ajWarn("unknown database '%S'",
               name);
	return ajFalse;
    }

    dbtable = (AjPTable) fnew->data;
    j = namDbAttrC(attribute);

    if(j < 0)
    {
	ajWarn("unknown attribute '%s' requested for database '%S'",
               attribute, name);
        return ajFalse;
    }

    dbvalue = ajTableFetchmodC(dbtable, attribute);

    if(ajStrGetLen(dbvalue))
    {
	ajStrAssignS(value,dbvalue);

	return ajTrue;
    }

    return ajFalse;
}
ID ajNamDbGetAttrS
TY public
MO ajnam
LB core
XX
DE Return the value for a database attribute
XX
PN [1]
PA r name const AjPStr
PD database name
PX
PN [2]
PA r attribute const AjPStr
PD database attribute name
PX
PN [3]
PA w value AjPStr*
PD attribute value
PX
RT AjBool
RD true if found
RX
//


AjBool ajNamDbGetAttrS(const AjPStr name, const AjPStr attribute,
                       AjPStr *value)
{
    return ajNamDbGetAttrC(name, MAJSTRGETPTR(attribute), value);
}
ID ajNamDbGetAttrSpecialC
TY public
MO ajnam
LB core
XX
DE Return the value for the first occurrence of a named special attribute
XX
PN [1]
PA r name const AjPStr
PD database name
PX
PN [2]
PA r attribute const char*
PD special attribute name
PX
PN [3]
PA w value AjPStr*
PD value
PX
RT ajuint
RD Number of matching special values
RX
//


ajuint ajNamDbGetAttrSpecialC(const AjPStr name, const char *attribute,
                              AjPStr* value)
{
    ajuint ret = 0;
    ajint j;
    NamPEntry fnew = NULL;
    AjPTable dbtable;
    AjPList dbvalue;
    AjIList iter;
    const AjPStr tmpstr = NULL;
    ajuint attrlen = 0;

    ajDebug("ajNamDbGetAttrSpecial '%S' '%s'\n", name, attribute);

    attrlen = strlen(attribute);

    fnew = ajTableFetchmodS(namDbMasterTable, name);

    if(!fnew)
    {
        ajWarn("unknown database '%S'",
               name);
	return ajFalse;
    }

    dbtable = (AjPTable) fnew->data;
    j = namDbAttrC("special");

    if(j < 0)
    {
	ajWarn("unknown attribute '%s' requested for database '%S'",
               "special", name);
        return ajFalse;
    }

    dbvalue = ajTableFetchmodC(dbtable, "special");

    if(ajListGetLength(dbvalue))
    {
        iter = ajListIterNewread(dbvalue);

        while(!ajListIterDone(iter))
        {
            tmpstr = ajListIterGet(iter);
            if(ajStrPrefixC(tmpstr, attribute) &&
               ajStrGetCharPos(tmpstr, attrlen) == '=')
            {
                if(!ret++)
                {
                    ajStrAssignS(value, tmpstr);
                    ajStrCutStart(value, attrlen+1);
                }
            }
        }

        ajListIterDel(&iter);
    }

    return ret;
}
ID ajNamDbGetAttrSpecialS
TY public
MO ajnam
LB core
XX
DE Return the value for the first occurrence of a named special attribute
XX
PN [1]
PA r name const AjPStr
PD database name
PX
PN [2]
PA r attribute const AjPStr
PD special attribute name
PX
PN [3]
PA w value AjPStr*
PD value
PX
RT ajuint
RD Number of matching special values
RX
//


ajuint ajNamDbGetAttrSpecialS(const AjPStr name, const AjPStr attribute,
                              AjPStr* value)
{
    return ajNamDbGetAttrSpecialC(name, MAJSTRGETPTR(attribute), value);
}
ID ajNamDbGetAttrlist
TY public
MO ajnam
LB core
XX
DE Return a list of names and values for all of a database defined attributes
XX
PN [1]
PA r name const AjPStr
PD database name
PX
RT AjPList
RD Tag-value list
RX
//


AjPList ajNamDbGetAttrlist(const AjPStr name)
{
    AjPList ret = NULL;
    AjPTagval tagval = NULL;
    NamPEntry fnew = NULL;
    AjPTable dbtable = NULL;
    const char* dbattr = NULL;
    NamOAttr attr;
    void* dbvalue;
    const AjPStr tmpstr;
    ajuint i;
    AjIList iter;

    fnew = ajTableFetchmodS(namDbMasterTable, name);

    if(!fnew)
    {
	return NULL;
    }

    ret = ajListNew();
    dbtable = (AjPTable) fnew->data;

    for(i=0; namDbAttrs[i].Name; i++)
    {
        attr = namDbAttrs[i];
        dbattr = attr.Name;

        dbvalue = ajTableFetchmodC(dbtable, dbattr);

        if(dbvalue)
        {
            switch(attr.Type)
            {
                case ATTR_LIST:
                    iter = ajListIterNewread(dbvalue);

                    while(!ajListIterDone(iter))
                    {
                        tmpstr = ajListIterGet(iter);
                        tagval = ajTagvalNewC(dbattr, MAJSTRGETPTR(tmpstr));
                        ajListPushAppend(ret, tagval);
                    }

                    ajListIterDel(&iter);
                    break;
                default:
                    tagval = ajTagvalNewC(dbattr, ajStrGetPtr(dbvalue));
                    ajListPushAppend(ret, tagval);
                   break;
            }
        }
    }

    return ret;
  
}
ID namDbtablePutAttrS
TY static
MO ajnam
LB core
XX
DE Store the value for a database attribute
XX
PN [1]
PA u dbtable AjPTable
PD Database attribute table
PX
PN [2]
PA u Pattribute AjPStr*
PD database attribute name
PX
PN [3]
PA u Pvalue AjPStr*
PD attribute value
PX
RT AjBool
RD true if found
RX
//


static AjBool namDbtablePutAttrS(AjPTable dbtable, AjPStr *Pattribute,
                                 AjPStr *Pvalue)
{
    void *oldval;
    NamPAttr attr;
    AjPList fdlist = NULL;

    attr  = ajTableFetchmodS(namDbAttrTable, *Pattribute);
    namUser("store db attr '%S' => '%S'\n", *Pattribute, *Pvalue);

    switch(attr->Type) 
    {
        case ATTR_LIST:
            fdlist = ajTableFetchmodS(dbtable, *Pattribute);

            if(!fdlist)
            {
                fdlist = ajListstrNew();
                ajTablePut(dbtable, (AjPStr) *Pattribute, (AjPList) fdlist);
                *Pattribute = NULL;
            }
            else
                ajStrDel(Pattribute);

            ajListstrPushAppend(fdlist, *Pvalue);
            break;

        default:
            oldval = ajTablePut(dbtable, (AjPStr) *Pattribute,
                                (AjPStr) *Pvalue);
            if(oldval)
                ajWarn("duplicate db keyword value for '%S' = '%S'",
                       *Pattribute, (AjPStr) oldval);

            *Pattribute = NULL;
            break;
    }
    
    *Pvalue = NULL;

    return ajTrue;
}
ID namRstablePutAttrS
TY static
MO ajnam
LB core
XX
DE Store the value for a resource attribute
XX
PN [1]
PA u rstable AjPTable
PD Resource attribute table
PX
PN [2]
PA u Pattribute AjPStr*
PD resource attribute name
PX
PN [3]
PA u Pvalue AjPStr*
PD attribute value
PX
RT AjBool
RD true if found
RX
//


static AjBool namRstablePutAttrS(AjPTable rstable, AjPStr *Pattribute,
                                 AjPStr *Pvalue)
{
    void *oldval;
    NamPAttr attr;
    AjPList fdlist = NULL;

    attr  = ajTableFetchmodS(namResAttrTable, *Pattribute);
    namUser("store resource attr '%S' => '%S\n", *Pattribute, *Pvalue);

    if(!attr)
        ajTablePut(rstable, (AjPStr) *Pattribute,
                   (AjPStr) *Pvalue);
    else
    {
        switch(attr->Type) 
        {
            case ATTR_LIST:
                fdlist = ajTableFetchmodS(rstable, *Pattribute);

                if(!fdlist)
                {
                    fdlist = ajListstrNew();
                    ajTablePut(rstable, (AjPStr) *Pattribute, (AjPList) fdlist);
                    *Pattribute = NULL;
                }
                else
                    ajStrDel(Pattribute);

                ajListstrPushAppend(fdlist, *Pvalue);
            break;

            default:
                oldval = ajTablePut(rstable, (AjPStr) *Pattribute,
                                    (AjPStr) *Pvalue);
                if(oldval)
                    ajWarn("duplicate resource keyword value for '%S' = '%S'",
                           *Pattribute, (AjPStr) oldval);

                *Pattribute = NULL;
                break;
        }
    }

    *Pvalue = NULL;

    return ajTrue;
}
ID namSvrtablePutAttrS
TY static
MO ajnam
LB core
XX
DE Store the value for a server attribute
XX
PN [1]
PA u svrtable AjPTable
PD Server attribute table
PX
PN [2]
PA u Pattribute AjPStr*
PD server attribute name
PX
PN [3]
PA u Pvalue AjPStr*
PD attribute value
PX
RT AjBool
RD true if found
RX
//


static AjBool namSvrtablePutAttrS(AjPTable svrtable, AjPStr *Pattribute,
                                  AjPStr *Pvalue)
{
    void *oldval;
    NamPAttr attr;
    AjPList fdlist = NULL;

    attr  = ajTableFetchmodS(namSvrAttrTable, *Pattribute);
    namUser("store server attr '%S' => '%S\n", *Pattribute, *Pvalue);

    switch(attr->Type) 
    {
        case ATTR_LIST:
            fdlist = ajTableFetchmodS(svrtable, *Pattribute);

            if(!fdlist)
            {
                fdlist = ajListstrNew();
                ajTablePut(svrtable, (AjPStr) *Pattribute, (AjPList) fdlist);
                *Pattribute = NULL;
            }
            else
                ajStrDel(Pattribute);

            ajListstrPushAppend(fdlist, *Pvalue);
            break;

        default:
            oldval = ajTablePut(svrtable, (AjPStr) *Pattribute,
                                (AjPStr) *Pvalue);
            if(oldval)
                ajWarn("duplicate server keyword value for '%S' = '%S'",
                       *Pattribute, (AjPStr) oldval);

            *Pattribute = NULL;
            break;
    }
    
    *Pvalue = NULL;

    return ajTrue;
}
ID ajNamDbDetails
TY public
MO ajnam
LB core
XX
DE Returns database access method information
XX
PN [1]
PA r name const AjPStr
PD Database name
PX
PN [2]
PA w type AjPStr*
PD sequence type - 'P' or 'N'
PX
PN [3]
PA w id AjBool*
PD ajTrue = can access single entries
PX
PN [4]
PA w qry AjBool*
PD ajTrue = can access wild/query entries
PX
PN [5]
PA w all AjBool*
PD ajTrue = can access all entries
PX
PN [6]
PA w comment AjPStr*
PD comment about database
PX
PN [7]
PA w release AjPStr*
PD database release date
PX
PN [8]
PA w methods AjPStr*
PD database access methods formatted
PX
PN [9]
PA w defined AjPStr*
PD database definition file short name
PX
RT AjBool
RD ajTrue if database details were found
RX
//


AjBool ajNamDbDetails(const AjPStr name, AjPStr* type, AjBool* id,
		      AjBool* qry, AjBool* all,
		      AjPStr* comment, AjPStr* release,
		      AjPStr* methods, AjPStr* defined)
{
    NamPEntry fnew = NULL;
    AjPTable dbtable = NULL;
    ajint i;
    ajint scope;
    AjPStr attrname  = NULL;
    const AjPStr attrval  = NULL;
    const AjPStr dbtype  = NULL;
    AjBool typetested = ajFalse;

    *id = *qry = *all = ajFalse;
    
    ajStrDelStatic(type);
    ajStrDelStatic(comment);
    ajStrDelStatic(release);
    ajStrDelStatic(methods);
    ajStrDelStatic(defined);
    
    fnew = ajTableFetchmodS(namDbMasterTable, name);

    if(fnew)
    {
	ajDebug("ajNamDbDetails: '%S' found\n", name);

	ajStrAssignS(defined, fnew->file);

	dbtable = (AjPTable) fnew->data;
        dbtype = ajTableFetchmodC(dbtable, "type");

	for(i=0; namDbAttrs[i].Name; i++)
	{
            if(namDbAttrs[i].Type != ATTR_STR)
                continue;

            ajStrAssignC(&attrname, namDbAttrs[i].Name);

            attrval = ajTableFetchmodS(dbtable, attrname);
	    ajDebug("Attribute name = %S, value = %S\n",
                    attrname, attrval);

	    if(ajStrGetLen(attrval))
	    {
		if(ajStrMatchC(attrname, "type"))
                {
                    if(ajStrPrefixCaseC(attrval, "N"))
                        ajStrAssignC(type, "Nucleotide");
                    else if(ajStrPrefixCaseC(attrval, "P"))
                        ajStrAssignC(type, "Protein");
                    else 
                    {
                        ajStrAssignS(type, attrval);
                        ajStrFmtTitle(type);
                    }

                    typetested = ajTrue;
                }

		if(ajStrMatchC(attrname, "method"))
		{
		    scope = namMethod2Scope(attrval, dbtype);

		    if(scope & AJMETHOD_ENTRY)
                        *id = ajTrue;

		    if(scope & AJMETHOD_QUERY)
                        *qry = ajTrue;

		    if(scope & AJMETHOD_ALL)
                        *all = ajTrue;

		    ajStrAppendS(methods, attrval);
		}

		if(ajStrMatchC(attrname, "methodentry"))
		{
                    scope = namMethod2Scope(attrval, dbtype);

		    if(scope & AJMETHOD_ENTRY)
                        *id = ajTrue;

		    if(ajStrGetLen(*methods))
			ajStrAppendC(methods, ",");

		    ajStrAppendS(methods, attrval);
		    ajStrAppendC(methods, "(id)");
		}

		if(ajStrMatchC(attrname, "methodquery"))
		{
		    scope = namMethod2Scope(attrval, dbtype);

		    if(scope & AJMETHOD_ENTRY)
                        *id = ajTrue;

		    if(scope & AJMETHOD_QUERY)
                        *qry = ajTrue;

		    if(ajStrGetLen(*methods))
			ajStrAppendC(methods, ",");

		    ajStrAppendS(methods, attrval);
		    ajStrAppendC(methods, "(qry)");
		}

		if(ajStrMatchC(attrname, "methodall"))
		{
		    scope = namMethod2Scope(attrval, dbtype);

		    if(scope & AJMETHOD_ALL)
                        *all = ajTrue;

		    if(ajStrGetLen(*methods))
			ajStrAppendC(methods, ",");

		    ajStrAppendS(methods, attrval);
		    ajStrAppendC(methods, "(all)");
		}

                if(ajStrMatchC(attrname, "comment"))
		    ajStrAssignS(comment, attrval);

		if(ajStrMatchC(attrname, "release"))
		    ajStrAssignS(release, attrval);
	    }
	}
	
	if(!typetested)
	{
	    ajDebug("Bad database definition for %S: No type. 'P' assumed\n",
		   name);
	    ajWarn("Bad database definition for %S: No type. 'P' assumed",
		   name);
	    ajStrAssignC(type, "P");
	}

	if(!*id && !*qry && !*all)
        {
	    ajDebug("Bad database definition for %S: No method(s) for access\n",
		   name);
	    ajWarn("Bad database definition for %S: No method(s) for access",
		   name);
	}

        ajStrDel(&attrname);

	return ajTrue;
    }
    
    ajDebug("ajNamDbDetails: FAILED '%S' not found\n", name);

    ajStrDel(&attrname);

    return ajFalse;
}
ID namAccessTest
TY static
MO ajnam
LB core
XX
DE Tests whether a named access method is known for a given data type
XX
PN [1]
PA r method const AjPStr
PD Access method name
PX
PN [2]
PA r dbtype const AjPStr
PD Database type
PX
RT AjBool
RD True if found
RX
//


static AjBool namAccessTest(const AjPStr method, const AjPStr dbtype)
{
    AjBool result;
    NamPType namtype;

    result = ajTextaccessMethodTest(method);

    if(!result)
    {
        namtype = ajTableFetchmodS(namDbTypeTable, dbtype);

        if(!namtype)
            return result;

        if(namtype->DataType == AJDATATYPE_ASSEMBLY)
            result = ajAssemaccessMethodTest(method);
        else if(namtype->DataType == AJDATATYPE_FEATURES)
            result = ajFeattabaccessMethodTest(method);
        else if(namtype->DataType == AJDATATYPE_OBO)
            result = ajOboaccessMethodTest(method);
        else if(namtype->DataType == AJDATATYPE_RESOURCE)
            result = ajResourceaccessMethodTest(method);
        else if(namtype->DataType == AJDATATYPE_SEQUENCE)
            result = ajSeqaccessMethodTest(method);
        else if(namtype->DataType == AJDATATYPE_TAXON)
            result = ajTaxaccessMethodTest(method);
        else if(namtype->DataType == AJDATATYPE_URL)
            result = ajUrlaccessMethodTest(method);
        else if(namtype->DataType == AJDATATYPE_VARIATION)
            result = ajVaraccessMethodTest(method);
        else if(namtype->DataType == AJDATATYPE_REFSEQ)
            result = ajRefseqaccessMethodTest(method);
    }

    return result;
}
ID namInformatTest
TY static
MO ajnam
LB core
XX
DE Tests whether a named data input format is known for a given data type
XX
PN [1]
PA r format const AjPStr
PD Format name
PX
PN [2]
PA r dbtype const AjPStr
PD Database type
PX
RT AjBool
RD True if found
RX
//


static AjBool namInformatTest(const AjPStr format, const AjPStr dbtype)
{
    AjBool result = ajFalse;
    NamPType namtype;

    namtype = ajTableFetchmodS(namDbTypeTable, dbtype);

    if(!namtype) 
        return ajFalse;

    ajDebug("namInformatTest format '%S' dbtype '%S' namtype '%s'\n",
            format, dbtype, namtype->Name);

    if(namtype->DataType == AJDATATYPE_ASSEMBLY)
        result = ajAsseminformatTest(format);
    else if(namtype->DataType == AJDATATYPE_FEATURES)
        result = ajFeattabinformatTest(format);
    else if(namtype->DataType == AJDATATYPE_OBO)
        result = ajOboinformatTest(format);
    else if(namtype->DataType == AJDATATYPE_RESOURCE)
        result = ajResourceinformatTest(format);
    else if(namtype->DataType == AJDATATYPE_SEQUENCE)
        result = ajSeqinformatTest(format);
    else if(namtype->DataType == AJDATATYPE_TAXON)
        result = ajTaxinformatTest(format);
    else if(namtype->DataType == AJDATATYPE_URL)
        result = ajUrlinformatTest(format);
    else if(namtype->DataType == AJDATATYPE_VARIATION)
        result = ajVarinformatTest(format);
    else if(namtype->DataType == AJDATATYPE_TEXT)
        result = ajTextinformatTest(format);
    else if(namtype->DataType == AJDATATYPE_REFSEQ)
        result = ajRefseqinformatTest(format);

    return result;
}
ID namMethod2Qlinks
TY static
MO ajnam
LB core
XX
DE Returns known query link operators
DE for the various types of access method for databases.
XX
PN [1]
PA r method const AjPStr
PD Access method string
PX
PN [2]
PA r datatype ajint
PD Enumerated database type
PX
RT const char*
RD Query link operators
RX
//


static const char* namMethod2Qlinks(const AjPStr method, ajint datatype)
{
    const char* result = NULL;

    result = ajTextaccessMethodGetQlinks(method);

    if(ajCharMatchC(result, "DATA"))
       result = namDatatype2Qlinks(datatype);

    ajDebug("namMethod2Qlinks method: '%S' datatype: %d text: '%s'\n",
           method, datatype, result);

    if(datatype == AJDATATYPE_TEXT)
        return result;

    if(!result)
    {
        if(datatype == AJDATATYPE_ASSEMBLY)
            result = ajAssemaccessMethodGetQlinks(method);
        else if(datatype == AJDATATYPE_FEATURES)
            result = ajFeattabaccessMethodGetQlinks(method);
        else if(datatype == AJDATATYPE_OBO)
            result = ajOboaccessMethodGetQlinks(method);
        else if(datatype == AJDATATYPE_RESOURCE)
            result = ajResourceaccessMethodGetQlinks(method);
        else if(datatype == AJDATATYPE_SEQUENCE)
            result = ajSeqaccessMethodGetQlinks(method);
        else if(datatype == AJDATATYPE_TAXON)
            result = ajTaxaccessMethodGetQlinks(method);
        else if(datatype == AJDATATYPE_URL)
            result = ajUrlaccessMethodGetQlinks(method);
        else if(datatype == AJDATATYPE_VARIATION)
            result = ajVaraccessMethodGetQlinks(method);
    }

    return result;
}
ID namMethod2Scope
TY static
MO ajnam
LB core
XX
DE Returns OR'ed values of AJMETHOD_ENTRY, AJMETHOD_QUERY and AJMETHOD_ALL
DE for the various types of access method for databases.
XX
PN [1]
PA r method const AjPStr
PD Access method string
PX
PN [2]
PA r dbtype const AjPStr
PD Database type
PX
RT ajuint
RD OR'ed values for the valid scope of the access method given
RX
//


static ajuint namMethod2Scope(const AjPStr method, const AjPStr dbtype)
{
    ajuint result = 0;
    NamPType namtype;
    AjPStrTok handle = NULL;
    AjPStr nexttype = NULL;

    result = ajTextaccessMethodGetScope(method);

    if(!result)
    {
        ajStrTokenAssignC(&handle, dbtype, " ,;");

        while(ajStrTokenNextParse(&handle, &nexttype))
        {
            namtype = ajTableFetchmodS(namDbTypeTable, nexttype);

            if(!namtype) 
                return result;

            if(namtype->DataType == AJDATATYPE_ASSEMBLY)
                result = ajAssemaccessMethodGetScope(method);
            else if(namtype->DataType == AJDATATYPE_FEATURES)
                result = ajFeattabaccessMethodGetScope(method);
            else if(namtype->DataType == AJDATATYPE_OBO)
                result = ajOboaccessMethodGetScope(method);
            else if(namtype->DataType == AJDATATYPE_RESOURCE)
                result = ajResourceaccessMethodGetScope(method);
            else if(namtype->DataType == AJDATATYPE_SEQUENCE)
                result = ajSeqaccessMethodGetScope(method);
            else if(namtype->DataType == AJDATATYPE_TAXON)
                result = ajTaxaccessMethodGetScope(method);
             else if(namtype->DataType == AJDATATYPE_URL)
                result = ajUrlaccessMethodGetScope(method);
             else if(namtype->DataType == AJDATATYPE_VARIATION)
                result = ajVaraccessMethodGetScope(method);
             else if(namtype->DataType == AJDATATYPE_REFSEQ)
                result = ajRefseqaccessMethodGetScope(method);
        }
    }

    ajStrDel(&nexttype);
    ajStrTokenDel(&handle);

    return result;
}
ID namDatatype2Fields
TY static
MO ajnam
LB core
XX
DE Returns the list of fields that can be processed by the "Read"
DE file parsing function for a specified datatype
XX
PN [1]
PA r datatype ajint
PD Enumerated data type
PX
RT const char*
RD Field name list
RX
//


static const char* namDatatype2Fields(ajint datatype)
{
    const char* result = NULL;

    if(datatype == AJDATATYPE_ASSEMBLY)
        result = ajAsseminTypeGetFields();
    else if(datatype == AJDATATYPE_FEATURES)
        result = ajFeattabinTypeGetFields();
    else if(datatype == AJDATATYPE_OBO)
        result = ajOboinTypeGetFields();
    else if(datatype == AJDATATYPE_RESOURCE)
        result = ajResourceinTypeGetFields();
    else if(datatype == AJDATATYPE_SEQUENCE)
        result = ajSeqinTypeGetFields();
    else if(datatype == AJDATATYPE_TAXON)
        result = ajTaxinTypeGetFields();
    else if(datatype == AJDATATYPE_URL)
        result = ajUrlinTypeGetFields();
    else if(datatype == AJDATATYPE_VARIATION)
        result = ajVarinTypeGetFields();

    if(!result)
        result = ajTextinTypeGetFields();

    return result;
}
ID namDatatype2Qlinks
TY static
MO ajnam
LB core
XX
DE Returns the list of query links that can be processed by the "Read"
DE file parsing function for a specified datatype
XX
PN [1]
PA r datatype ajint
PD Enumerated data type
PX
RT const char*
RD Known query link operators
RX
//


static const char* namDatatype2Qlinks(ajint datatype)
{
    const char* result = NULL;

    if(datatype == AJDATATYPE_ASSEMBLY)
        result = ajAsseminTypeGetQlinks();
    else if(datatype == AJDATATYPE_FEATURES)
        result = ajFeattabinTypeGetQlinks();
    else if(datatype == AJDATATYPE_OBO)
        result = ajOboinTypeGetQlinks();
    else if(datatype == AJDATATYPE_RESOURCE)
        result = ajResourceinTypeGetQlinks();
    else if(datatype == AJDATATYPE_SEQUENCE)
        result = ajSeqinTypeGetQlinks();
    else if(datatype == AJDATATYPE_TAXON)
        result = ajTaxinTypeGetQlinks();
    else if(datatype == AJDATATYPE_URL)
        result = ajUrlinTypeGetQlinks();
    else if(datatype == AJDATATYPE_VARIATION)
        result = ajVarinTypeGetQlinks();

    if(!result)
        result = ajTextinTypeGetQlinks();

    return result;
}
ID ajNamDebugOrigin
TY public
MO ajnam
LB core
XX
DE Writes a simple list of where the internal tables came from..
XX
RT void
RD 
RX
//


void ajNamDebugOrigin(void)
{
    ajDebug("Defaults and .rc files: %S\n", namFileOrig);

    return;
}
ID ajNamDebugServers
TY public
MO ajnam
LB core
XX
DE Writes a simple debug report of all servers in the internal table.
XX
RT void
RD 
RX
//


void ajNamDebugServers(void)
{
    ajDebug("SERVER servers\n");
    ajDebug("==============\n");
    namDebugMaster(namSvrMasterTable, TYPE_SVR);
    ajDebug("[SVR done]\n\n");

    return;
}
ID ajNamDebugDatabases
TY public
MO ajnam
LB core
XX
DE Writes a simple debug report of all databases in the internal table.
XX
RT void
RD 
RX
//


void ajNamDebugDatabases(void)
{
    ajDebug("DB databases\n");
    ajDebug("============\n");
    namDebugMaster(namDbMasterTable, TYPE_DB);
    ajDebug("[DB done]\n\n");

    return;
}
ID ajNamDebugResources
TY public
MO ajnam
LB core
XX
DE Writes a simple debug report of all databases in the internal table.
XX
RT void
RD 
RX
//


void ajNamDebugResources(void)
{
    ajDebug("RES resources\n");
    ajDebug("=============\n");
    namDebugMaster(namResMasterTable, TYPE_RESOURCE);
    ajDebug("[RES done]\n\n");

    return;
}
ID ajNamDebugVariables
TY public
MO ajnam
LB core
XX
DE Writes a simple debug report of all environment variables
DE in the internal table.
XX
RT void
RD 
RX
//


void ajNamDebugVariables(void)
{
    ajDebug("ENV variables\n");
    ajDebug("=============\n");
    namDebugMaster(namVarMasterTable, TYPE_ENV);
    ajDebug("[ENV done]\n\n");

    return;
}
ID ajNamDebugAliases
TY public
MO ajnam
LB core
XX
DE Writes a simple debug report of all aliases
DE in the internal table.
XX
RT void
RD 
RX
//


void ajNamDebugAliases(void)
{
    ajDebug("ALIAS names\n");
    ajDebug("=============\n");
    namDebugMaster(namAliasMasterTable, TYPE_ALIAS);
    ajDebug("[ALIAS done]\n\n");

    return;
}
ID ajNamListListServers
TY public
MO ajnam
LB core
XX
DE Creates a AjPList list of all servers in the internal table.
XX
PN [1]
PA w svrnames AjPList
PD Str List of names to be populated
PX
RT void
RD 
RX
//


void ajNamListListServers(AjPList svrnames)
{
    ajint i;
    NamPEntry fnew;
    void **valarray =  NULL;

    ajTableToarrayValues(namSvrMasterTable, &valarray);
    ajDebug("ajNamListListSevers\n");

    for(i = 0; valarray[i]; i++)
    {
	fnew = (NamPEntry) valarray[i];
	ajDebug("SVR: %S\n", fnew->name);
	ajListstrPushAppend(svrnames, fnew->name);
    }

    AJFREE(valarray);

    return;
}
ID ajNamListListDatabases
TY public
MO ajnam
LB core
XX
DE Creates a AjPList list of all databases in the internal table.
XX
PN [1]
PA w dbnames AjPList
PD Str List of names to be populated
PX
RT void
RD 
RX
//


void ajNamListListDatabases(AjPList dbnames)
{
    ajint i;
    NamPEntry fnew;
    void **valarray =  NULL;

    ajTableToarrayValues(namDbMasterTable, &valarray);
    ajDebug("ajNamListListDatabases\n");

    for(i = 0; valarray[i]; i++)
    {
	fnew = (NamPEntry) valarray[i];
	ajDebug("DB: %S\n", fnew->name);
	ajListstrPushAppend(dbnames, fnew->name);
    }

    AJFREE(valarray);

    return;
}
ID ajNamListListResources
TY public
MO ajnam
LB core
XX
DE Creates a AjPList list of all databases in the internal table.
XX
PN [1]
PA w rsnames AjPList
PD Str List of names to be populated
PX
RT void
RD 
RX
//


void ajNamListListResources(AjPList rsnames)
{
    ajint i;
    NamPEntry fnew;
    void **valarray = NULL;

    ajTableToarrayValues(namResMasterTable, &valarray);

    for(i = 0; valarray[i]; i++)
    {
	fnew = (NamPEntry) valarray[i];
	ajDebug("RES: %S\n", fnew->name);
	ajListstrPushAppend(rsnames, fnew->name);
    }

    AJFREE(valarray);

    return;
}
ID namDebugVariables
TY static
MO ajnam
LB core
XX
DE Writes a simple list of all variables in the internal table.
XX
RT void
RD 
RX
//


static void namDebugVariables(void)
{
    namUser("ENV---------->\n");
    namDebugMaster(namVarMasterTable, TYPE_ENV);
    namUser("ENV---------->\n");
    namUser("\n");

    return;
}
ID namDebugAliases
TY static
MO ajnam
LB core
XX
DE Writes a simple list of all aliases in the internal table.
XX
RT void
RD 
RX
//


static void namDebugAliases(void)
{
    namUser("ALIAS---------->\n");
    namDebugMaster(namAliasMasterTable, TYPE_ALIAS);
    namUser("ALIAS---------->\n");
    namUser("\n");

    return;
}
ID namListParse
TY static
MO ajnam
LB core
XX
DE Parse the text in a list of tokens read from the input file.
DE Derive environment variable and database definitions. Store
DE all these in the internal tables.
XX
PN [1]
PA u listwords AjPList
PD String list of word tokens to parse
PX
PN [2]
PA u listcount AjPList
PD List of word counts per line for
PD generating error messages
PX
PN [3]
PA u file AjPFile
PD Input file only for name in messages
PX
PN [4]
PA r shortname const AjPStr
PD Definition file short name
PX
RT void
RD 
RX
//


static void namListParse(AjPList listwords, AjPList listcount,
			 AjPFile file, const AjPStr shortname)
{
    static char* tabname   = NULL;
    static AjPStr name     = NULL;
    AjPStr value    = NULL;
    static NamPAttr attrvalue  = NULL;
    static char quoteopen  = '\0';
    static char quoteclose = '\0';
    static AjPTable svattr  = NULL;
    static AjPTable dbattr  = NULL;
    static AjPTable rsattr  = NULL;
    static ajint  svr_input = -1;
    static ajint  db_input = -1;
    static ajint  rs_input = -1;
    AjPStr rs_name = NULL;
    AjPTable saveTable = NULL;

    NamPEntry fnew  = NULL;
    NamPEntry entry = NULL;

    AjBool svrsave = ajFalse;
    AjBool dbsave = ajFalse;
    AjBool rssave = ajFalse;
    AjBool saveit = ajFalse;
    AjBool saveifblock = ajFalse;
    static ajint nsvrattr = 0;
    static ajint ndbattr = 0;
    static ajint ndbtype = 0;
    static ajint nrsattr = 0;

    AjPStr includefn = NULL;
    AjPFile iinf     = NULL;
    AjPStr key       = NULL;
    AjPStr val       = NULL;
    AjPStr oldval    = NULL;

    static AjPTable Ifiles = NULL;
    AjPStr curword = NULL;

    ajint wordcount = 0;
    ajint linecount = 0;
    ajint lineword  = 0;
    ajint *iword    = NULL;
    AjBool namstatus;
    AjPStr saveshortname = NULL;
    AjPStr saveword = NULL;
    AjPStr teststr = NULL;

    namUser("namListParse shortname: '%S'\n", shortname);

    /* ndbtype = count database types*/
    if(!ndbtype)
    {
        if(!namDbTypeTable)
            namDbTypeTable = ajTablestrNewCase(30);

	for(ndbtype=0; namDbTypes[ndbtype].Name; ndbtype++)
        {
            name = ajStrNewC(namDbTypes[ndbtype].Name);
            ajTablePut(namDbTypeTable, name, &namDbTypes[ndbtype]);
         }

        name = NULL;
    }

    /* nsvrattr = count server attributes */
    if(!nsvrattr)
    {
        if(!namSvrAttrTable)
            namSvrAttrTable = ajTablestrNew(30);

	for(nsvrattr=0; namSvrAttrs[nsvrattr].Name; nsvrattr++)
        {
            name = ajStrNewC(namSvrAttrs[nsvrattr].Name);
            attrvalue = &namSvrAttrs[nsvrattr];
            oldval = ajTablePut(namSvrAttrTable, name, attrvalue);
            namUser("store server default '%S' => '%s\n",
                    name, attrvalue->Defval);

            if(oldval)
            {
                ajWarn("duplicate server attr value for '%S' = '%S'",
                       name, oldval);
                ajStrDel(&oldval);
            }
        }

        name = NULL;
        value = NULL;
    }

    /* ndbattr = count database attributes */
    if(!ndbattr)
    {
        if(!namDbAttrTable)
            namDbAttrTable = ajTablestrNew(30);

	for(ndbattr=0; namDbAttrs[ndbattr].Name; ndbattr++)
        {
            name = ajStrNewC(namDbAttrs[ndbattr].Name);
            attrvalue = &namDbAttrs[ndbattr];
            oldval = ajTablePut(namDbAttrTable, name, attrvalue);
            namUser("store database default '%S' => '%s\n",
                    name, attrvalue->Defval);

            if(oldval)
            {
                ajWarn("duplicate db attr value for '%S' = '%S'",
                       name, oldval);
                ajStrDel(&oldval);
            }
        }

        name = NULL;
        value = NULL;
    }

    /* nrsattr = count resource attributes */
    if(!nrsattr)
    {
        if(!namResAttrTable)
            namResAttrTable = ajTablestrNew(30);

	for(nrsattr=0; namRsAttrs[nrsattr].Name; nrsattr++)
        {
            name = ajStrNewC(namRsAttrs[nrsattr].Name);
            attrvalue = &namRsAttrs[nrsattr];
            oldval = ajTablePut(namResAttrTable, name, attrvalue);
            namUser("store resource default '%S' => '%s\n",
                    name, attrvalue->Defval);

            if(oldval)
            {
                ajWarn("duplicate res attr value for '%S' = '%S'",
                       name, oldval);
                ajStrDel(&oldval);
            }
        }

        name = NULL;
        value = NULL;
    }

    ajStrAssignS(&saveshortname, shortname);
    ajStrDel(&name);
    ajStrDel(&value);
    quoteopen  = '\0';
    quoteclose = '\0';
    
    namLine = 1;
    namUser("namListParse of %F '%S' words: %Lu lines: %Lu\n", 
	    file, name, ajListGetLength(listwords), ajListGetLength(listcount));
    
    while(ajListstrPop(listwords, &curword))
    {
	while(ajListGetLength(listcount) && (lineword < wordcount))
	{
	    namUser("ajListPop %d < %d list %Lu\n",
		    lineword, wordcount, ajListGetLength(listcount));
	    ajListPop(listcount, (void**) &iword);
	    lineword = *iword;
	    linecount++;
	    namLine = linecount-1;
	    AJFREE(iword);
	}

	wordcount++;

	namUser("namListParse word: %d line: %d (%d) <%S>\n",
		wordcount, namLine, lineword, curword);
	
	if(!namParseType)
	{
	    namNoColon(&curword);
	    ajStrFmtLower(&curword);

	    if(ajCharPrefixS("env", curword))
		namParseType = TYPE_ENV;
	    else if(ajCharPrefixS("setenv", curword))
		namParseType = TYPE_ENV;
	    else if(ajCharPrefixS("server",curword))
		namParseType = TYPE_SVR;
	    else if(ajCharPrefixS("dbname",curword))
		namParseType = TYPE_DB;
	    else if(ajCharPrefixS("resource",curword))
		namParseType = TYPE_RESOURCE;
	    else if(ajCharPrefixS("alias",curword))
		namParseType = TYPE_ALIAS;
	    else if(ajCharPrefixS("include",curword))
		namParseType = TYPE_IFILE;
	    else if(ajCharPrefixS("if",curword))
            {
                namParseType = TYPE_IF;
                saveifblock = ajFalse;
            }
	    else if(ajCharPrefixS("ifdef",curword))
            {
                namParseType = TYPE_IFDEF;
                saveifblock = ajFalse;
            }
	    else if(ajCharPrefixS("else",curword))
		namParseType = TYPE_ELSE;
	    else if(ajCharPrefixS("endif",curword))
		namParseType = TYPE_ENDIF;

	    if(!namParseType)		/* test: badtype.rc */
		namError("Invalid definition type '%S'", curword);

	    namUser("type set to %s curword '%S'\n",
		    namTypes[namParseType], curword);
	}
	else if(quoteopen && ajStrMatchC(curword, "]"))
	{				/* test; dbnoquote.rc */
	    namError("']' found, unclosed quotes in '%S'", value);
	    quoteopen    = '\0';
	    namParseType = 0;
	}
	else if(quoteopen)
	{
	    /*
	    ** quote is open, so append word until close quote is found,
	    ** and set the appropriate save flag
	    */
	    namUser("<%c>..<%c> quote processing\n", quoteopen, quoteclose);
	    ajStrAppendC(&value," ");
	    ajStrAppendS(&value,curword);

	    /* close quote here ?? */
	    if(ajStrGetCharLast(curword) == quoteclose)
	    {
		namUser("close quotes\n");
		ajStrCutEnd(&value, 1);
		quoteopen = quoteclose = '\0';

		if(namParseType == TYPE_ENV) /* set save flag, value found */
		    saveit = ajTrue;
		else if(namParseType == TYPE_SVR)
		    svrsave = ajTrue;
		else if(namParseType == TYPE_DB)
		    dbsave = ajTrue;
		else if(namParseType == TYPE_RESOURCE)
		    rssave = ajTrue;
		else if(namParseType == TYPE_ALIAS)
		    saveit = ajTrue;
		else if(namParseType == TYPE_IFDEF)
		    saveifblock = ajTrue;
		else if(namParseType == TYPE_IF)
		    saveifblock = ajTrue;
	    }
	}
	else if(namParseType == TYPE_IF || namParseType == TYPE_IFDEF )
	{
            if(ajStrGetCharFirst(curword) == '\'')
                quoteopen = quoteclose = '\'';
            else if(ajStrGetCharFirst(curword) == '\"')
                quoteopen = quoteclose = '\"';

            ajStrAssignS(&value, curword);

            if(quoteopen)
            {
                /* trim the opening quote */
                ajStrCutStart(&value, 1);

                if(!ajStrGetLen(value))
                    ajErr("Bare quote %c found in namListParse",
                          quoteopen);
            }
            else
                saveifblock = ajTrue;
            
            if(ajStrGetCharLast(curword) == quoteclose)
            {
                /* end of quote on same word */
                quoteopen = quoteclose = '\0';
                /* remove quote at the end */
                ajStrCutEnd(&value, 1);
                saveifblock = ajTrue;
            }

            namUser("if: save value '%S' saveif: %B\n", value, saveifblock);
        }
	else if(namParseType == TYPE_ENV)
	{
	    if(name && value)
		saveit= ajTrue;
	    else if(name)
	    {
		/* if name already got then it must be the value */
		if(ajStrGetCharFirst(curword) == '\'')
		    quoteopen = quoteclose = '\'';
		else if(ajStrGetCharFirst(curword) == '\"')
		    quoteopen = quoteclose = '\"';

		ajStrAssignS(&value, curword);

		if(quoteopen)
		{
		    /* trim the opening quote */
		    ajStrCutStart(&value, 1);

		    if(!ajStrGetLen(value))
			ajErr("Bare quote %c found in namListParse",
			       quoteopen);
		}
		else
		    saveit = ajTrue;

		if(ajStrGetCharLast(curword) == quoteclose)
		{
		    /* end of quote on same word */
		    quoteopen = quoteclose = '\0';
		    saveit= ajTrue;
		    /* remove quote at the end */
		    ajStrCutEnd(&value, 1);
		}

                if(saveit)
                    ajNamResolve(&value);
		namUser("save value '%S' saveit: %B\n", value, saveit);
	    }
	    else
	    {
		ajStrAssignS(&name, curword);
		namUser("save name '%S'\n", name);
	    }
	}

        else if(namParseType == TYPE_ALIAS)
	{
            namUser("ALIAS saveit: %B name: '%S' value '%S' "
                    "quoteopen: '%c'\n",
                    saveit, name, value, quoteopen);
	    if(name && value)
		saveit= ajTrue;
	    else if(name)
	    {
		/* if name already got then it must be the value */
		if(ajStrGetCharFirst(curword) == '\'')
		    quoteopen = quoteclose = '\'';
		else if(ajStrGetCharFirst(curword) == '\"')
		    quoteopen = quoteclose = '\"';

		ajStrAssignS(&value, curword);

		if(quoteopen)
		{
		    /* trim the opening quote */
		    ajStrCutStart(&value, 1);

		    if(!ajStrGetLen(value))
			ajErr("Bare quote %c found in namListParse",
			       quoteopen);
		}
		else
		    saveit = ajTrue;

		if(ajStrGetCharLast(curword) == quoteclose)
		{
		    /* end of quote on same word */
		    quoteopen = quoteclose = '\0';
		    saveit= ajTrue;
		    /* remove quote at the end */
		    ajStrCutEnd(&value, 1);
		}

                if(saveit)
                    ajNamResolve(&value);
		namUser("save alias value '%S' saveit: %B\n", value, saveit);
	    }
	    else
	    {
		ajStrAssignS(&name, curword);
		namUser("save alias name '%S'\n", name);
	    }
	}
	
	
	else if(namParseType == TYPE_SVR)
	{
	    if(ajStrMatchC(curword, "[")) /* [ therefore new server */
		svattr = ajTablestrNew(100); /* new server obj */
	    else if(ajStrMatchC(curword, "]"))	/* ] therefore end of server */
		saveit = ajTrue;
	    else if(name)
	    {
		if(svr_input >= 0)
		{
		    /* So if keyword type has been set */
		    if(ajStrGetCharFirst(curword) == '\'')
		    {
			/* is there a quote? If so expect the */
			/* same at the end. No ()[] etc here*/
			quoteopen = quoteclose = '\'';
		    }
		    else if(ajStrGetCharFirst(curword) == '\"')
			quoteopen = quoteclose = '\"';

		    ajStrAssignS(&value, curword);

		    if(quoteopen)
		    {
			ajStrCutStart(&value, 1); /* trim opening quote */

			if(!ajStrGetLen(value))
			    ajErr("Bare quote %c found in namListParse",
				   quoteopen);
		    }
		    else
			svrsave = ajTrue; /* we are done - simple word */

		    if(ajStrGetCharLast(curword) == quoteclose)
		    {
			quoteopen = quoteclose = '\0';
			ajStrCutEnd(&value,1); /* trim closing quote */
			svrsave = ajTrue;
		    }

		    if(!quoteopen)     /* if we just reset it above */
			svrsave = ajTrue;

                    if(svrsave)
                        ajNamResolve(&value);
		}
		else if(ajStrGetCharLast(curword) == ':')
		{
		    /* if last character is : then its a keyword */
		    ajStrFmtLower(&curword); /* make it lower case */
		    namNoColon(&curword);
		    svr_input = namSvrAttrS(curword);

		    if(svr_input < 0)
			ajWarn("%F: bad attribute '%S' for server '%S'",
				file, curword, name);
		}
                else
                {
                    ajWarn("%F: unexpected token '%S' for server '%S'",
                           file, curword, name);
                }
	    }
	    else
	    {
		ajStrAssignS(&name, curword);

		if(!ajNamIsDbname(name))
		    ajErr("Invalid server name '%S'", name);

		namUser("saving svr name '%S'\n", name);
	    }
	}
	
	else if(namParseType == TYPE_DB)
	{
	    if(ajStrMatchC(curword, "[")) /* [ therefore new database */
		dbattr = ajTablestrNew(100); /* new db obj */
	    else if(ajStrMatchC(curword, "]"))	/* ] therefore end of db */
		saveit = ajTrue;
	    else if(name)
	    {
		if(db_input >= 0)
		{
		    /* So if keyword type has been set */
		    if(ajStrGetCharFirst(curword) == '\'')
		    {
			/* is there a quote? If so expect the */
			/* same at the end. No ()[] etc here*/
			quoteopen = quoteclose = '\'';
		    }
		    else if(ajStrGetCharFirst(curword) == '\"')
			quoteopen = quoteclose = '\"';

		    ajStrAssignS(&value, curword);

		    if(quoteopen)
		    {
			ajStrCutStart(&value, 1); /* trim opening quote */

			if(!ajStrGetLen(value))
			    ajErr("Bare quote %c found in namListParse",
				   quoteopen);
		    }
		    else
			dbsave = ajTrue; /* we are done - simple word */

		    if(ajStrGetCharLast(curword) == quoteclose)
		    {
			quoteopen = quoteclose = '\0';
			ajStrCutEnd(&value,1); /* trim closing quote */
			dbsave = ajTrue;
		    }

		    if(!quoteopen)     /* if we just reset it above */
			dbsave = ajTrue;

                    if(dbsave)
                        ajNamResolve(&value);
		}
		else if(ajStrGetCharLast(curword) == ':')
                {
                    /* if last character is : then its a keyword */
                    ajStrFmtLower(&curword); /* make it lower case */
                    namNoColon(&curword);
                    db_input = namDbAttrS(curword);
                        
                    if(db_input < 0)
                        ajWarn("%F: bad attribute '%S' for database '%S'",
                               file, curword, name);
                }
                else
                {
                    ajWarn("%F: unexpected token '%S' for database '%S'",
                           file, curword, name);
                }
            }
	    else
	    {
		ajStrAssignS(&name, curword);

		if(!ajNamIsDbname(name))
		    ajErr("Invalid database name '%S'", name);

		namUser("saving db name '%S'\n", name);
	    }
	}
	
	
	else if(namParseType == TYPE_RESOURCE)
	{
	    if(ajStrMatchC(curword, "[")) /* [ therefore new resource */
		rsattr = ajTablestrNew(100); /* new resource*/
	    else if(ajStrMatchC(curword, "]"))	/* ] end of resource */
		saveit = ajTrue;
	    else if(name)
	    {
                /* if last character is : */
		if(ajStrGetCharLast(curword) == ':')
		{		     	     /* then it is a keyword */
		    ajStrFmtLower(&curword); /* make it lower case */
		    namNoColon(&curword);
		    rs_input = namRsAttrS(curword);
                    ajStrDel(&rs_name);

		    if(rs_input < 0)	/* test: badresattr.rc */
                    {
                        if(namRsAttrFieldS(rsattr, curword))
                        {
                            rs_name = ajStrNewRef(curword);
                        }
                        else
                            namError("Bad attribute '%S' for resource '%S'",
                                     curword, name);
                    }
		}
		else if(rs_name || rs_input >= 0)
		{		 /* So if keyword type has been set */
		    if(ajStrGetCharFirst(curword) == '\'')
		    {	      /* is there a quote? If so expect the */
			/* same at the end. No ()[] etc here */
			quoteopen = quoteclose = '\'';
		    }
		    else if(ajStrGetCharFirst(curword) == '\"')
			quoteopen = quoteclose = '\"';

		    ajStrAssignS(&value, curword);

		    if(quoteopen)
		    {
			ajStrCutStart(&value, 1); /* trim opening quote */

			if(!ajStrGetLen(value))
			    ajErr("Bare quote %c found in namListParse",
				   quoteopen);
		    }
		    else
			rssave = ajTrue;

		    if(ajStrGetCharLast(curword) == quoteclose)
		    {
			quoteopen = quoteclose = '\0';
			ajStrCutEnd(&value,1); /* ignore quote if */
					             /* one at end */
			rssave = ajTrue;
		    }

		    if(!quoteopen)
			rssave = ajTrue;
		}

                if(rssave)
                    ajNamResolve(&value);
	    }
	    else
	    {
		ajStrAssignS(&name, curword);
		namUser("saving resource name '%S'\n", name);
	    }
	}
	
	
	else if(namParseType == TYPE_IFILE)
	{
	    if(!Ifiles)
		Ifiles = ajTablestrNew(NAM_INCLUDE_ESTIMATE);
	    namParseType = 0;

            ajStrAssignS(&saveword, curword);

            ajNamResolve(&curword);

	    if(ajTableFetchmodS(Ifiles,curword)) /* test: includeagain.rc */
            {
		if(ajStrMatchS(curword, saveword))
                    namError("%S already read ... skipping", curword);
                else
                    namError("%S (%S) already read ... skipping",
                             saveword, curword);
            }
	    else
	    {
		includefn = ajStrNew();
		ajStrAssignS(&includefn,curword);

		if(namFileOrig)
		    ajStrAppendC(&namFileOrig,", ");
		ajStrAppendS(&namFileOrig,includefn);

		key = ajStrNewS(includefn);
		val = ajStrNewS(includefn);
		oldval = ajTablePut(Ifiles,key,val);
                namUser("store file '%S' => '%S\n", key, val);

                if(oldval)
                {
                    ajWarn("duplicate include value for '%S' = '%S'",
                           key, oldval);
                    ajStrDel(&key);
                    ajStrDel(&oldval);
                }

                /* test: badinclude.rc */
		if(!(iinf = ajFileNewInNameS(includefn)))
		{
                    if(ajStrMatchS(includefn, saveword))
                        namError("Failed to open include file '%S'",
                                 includefn);
                    else
                        namError("Failed to open include file '%S' (%S)",
                                 saveword, includefn);
		    ajStrAppendC(&namFileOrig,"(Failed)");
		}
		else
		{
		    ajStrAppendC(&namFileOrig,"(OK)");
                    /* replaces namFile */
                    ajStrAssignS(&name, includefn);
                    ajFilenameTrimPath(&name);
		    namstatus = namProcessFile(iinf, name);
		    ajFmtPrintS(&namFileName, "%F",file);/* reset saved name */
		    namLine = linecount-1;

		    if(!namstatus)	/* test: badsummary.rc */
                    {
                        if(ajStrMatchS(includefn, saveword))
                            namError("Error(s) found in included file %F",
                                     iinf);
                        else
                            namError("Error(s) found in included file %S (%F)",
                                     saveword, iinf);
                    }
                    

		    ajFileClose(&iinf);
		}

		ajStrDel(&includefn);
	    }

	    namListParseOK = ajTrue;
	}
	
	
	if(svrsave)
	{
	    /* Save the keyword value */
            key = ajStrNewC(namSvrAttrs[svr_input].Name);
            namUser("store svr attr '%S' => '%S\n", key, value);
            namSvrtablePutAttrS(svattr, &key, &value);

	    svr_input =-1;
	    svrsave = ajFalse;
	}

	if(dbsave)
	{
	    /* Save the keyword value */
            key = ajStrNewC(namDbAttrs[db_input].Name);
            namDbtablePutAttrS(dbattr, &key, &value);

	    db_input =-1;
	    dbsave = ajFalse;
	}

	if(rssave)
	{
            if(rs_input >= 0)
                /* Save the keyword value */
                key = ajStrNewC(namRsAttrs[rs_input].Name);
            else
                key = ajStrNewS(rs_name);
            namUser("store resource attr '%S' => '%S\n", key, value);
            namRstablePutAttrS(rsattr, &key, &value);

	    rs_input =-1;
            ajStrDel(&rs_name);
	    rssave = ajFalse;
	}
	
	namListParseOK = saveit;
	
        if(namParseType == TYPE_ENDIF)
        {
            if(!namIfBlock)
            {
                namError("endif not in if-block");
            }
            else
            {
                --namIfBlock;
                ajListPop(namIfList, (void**) &namIfValue);
                namUser("endif restore %B (block %u)\n",
                       namIfValue, namIfBlock);

                if(*namIfValue)
                    namIfNow = ajTrue;
                else
                    namIfNow = ajFalse;
            }
            namUser("endif %B (block %u)\n", namIfNow, /* FIXME teststr, */ namIfBlock);
            ajStrDel(&teststr);
            namParseType = 0;
	    namListParseOK = ajTrue;
        }
        else if(namParseType == TYPE_ELSE)
        {
            namIfNow = !namIfNow;
            namUser("else %B (block %u)\n", namIfNow, namIfBlock);
            namParseType = 0;
	    namListParseOK = ajTrue;
        }

	if(saveifblock)
	{
            if(namParseType == TYPE_IF || namParseType == TYPE_IFDEF )
            {
                ajNamResolve(&value);

                namUser("if: '%S'\n", value);

                if(!namIfList)
                    namIfList = ajListNew();

                if(namIfNow)
                    ajListPush(namIfList, (void*) &namTrue);
                else
                    ajListPush(namIfList, (void*) &namFalse);

                ++namIfBlock;

                if(namParseType == TYPE_IF)
                {
                    ajNamGetValueS(value, &teststr);
                    namUser("if '%S' '%S'\n", value, teststr);
                    if(!ajStrToBool(teststr, &namIfNow))
                        namIfNow = ajFalse;
                }
                else 
                {
                    namUser("ifdef '%S'\n", value);
                    if(ajNamGetValueS(value, &teststr))
                        namIfNow = ajTrue;
                    else
                        namIfNow = ajFalse;
                }

                namUser("if value '%S' '%S' %B (block %u)\n",
                       value, teststr, namIfNow, namIfBlock);
                namParseType = 0;
                ajStrDel(&teststr);
                namListParseOK = ajTrue;
                ajStrDel(&value);
            }
            saveifblock = ajFalse;
        }

	else if(saveit)
	{
            if(!namIfNow)
            {
                namUser("skipping type %d name '%S' value '%S' line:%d\n",
                        namParseType, name, value, namLine);
                ajStrDel(&name);
                saveTable = NULL;

		namSvrAttrtableFree(&svattr);
                namDbAttrtableFree(&dbattr);
                namRsAttrtableFree(&rsattr);
            }
            else
            {
                namUser("saving type %d name '%S' value '%S' line:%d\n",
                        namParseType, name, value, namLine);
                AJNEW0(fnew);
                tabname = ajCharNewS(name);
                fnew->name = name;
                name = NULL;
                fnew->value = value;
                value = NULL;
                fnew->file = ajStrNewRef(saveshortname);

                if(namParseType == TYPE_SVR)
                {
                    fnew->data = (AjPTable) svattr;
                    saveTable = namSvrMasterTable;
                    svattr = NULL;
                }
                else if(namParseType == TYPE_DB)
                {
                    fnew->data = (AjPTable) dbattr;
                    saveTable = namDbMasterTable;
                    dbattr = NULL;
                }
                else if(namParseType == TYPE_RESOURCE)
                {
                    fnew->data = (AjPTable) rsattr;
                    saveTable = namResMasterTable;
                    rsattr = NULL;
                }
                else if(namParseType == TYPE_ENV)
                {
                    fnew->data = NULL;
                    saveTable = namVarMasterTable;
                }
                else if(namParseType == TYPE_ALIAS)
                {
                    fnew->data = NULL;
                    saveTable = namAliasMasterTable;
                }
                else
                {
                    fnew->data = NULL;
                }

                /* Validate the master table entry */

                if(namDoValid)
                    namValid(fnew, namParseType);

                /*
                ** Add new one to table 
                ** be very careful that everything in the table 
                ** is not about to be deallocated - so do not use "name" here
                */

                entry = ajTablePut(saveTable, tabname, fnew);
                namUser("store entry '%s' '%S' (%S)\n",
                        tabname, fnew->name, fnew->file);
                if(entry)
                {
                    /* it existed so over wrote previous table entry
                    ** Only a namUser message - redefining EMBOSSRC in
                    ** QA testing can give too many warnings
                    */
                    namUser("namTypes: [%d] '%s'\n",
                            namParseType, namTypes[namParseType]);
                    namUser("shortname: '%S'\n",
                            shortname);
                    namUser("entry: '%S' in file '%S'\n",
                            entry->name, entry->file);
                    namUser("%S: replaced %s %S definition from %S\n",
                            shortname,
                            namTypes[namParseType],
                            entry->name,
                            entry->file);
                    namEntryDelete(&entry, namParseType); /* previous entry */
                }
	    }

	    saveit = ajFalse;
	    namParseType = 0;
            saveTable = NULL;
	}

	ajStrDel(&curword);
    }
    
    if(namParseType)
    {
	/* test: badset.rc baddb.rc  */
	namError("Unexpected end of file in %s definition",
		 namTypes[namParseType]);
	namParseType = 0;
    }
    
    if(ajListGetLength(listcount))	/* cleanup the wordcount list */
    {
	namUser("** remaining wordcount items: %lu\n",
                ajListGetLength(listcount));

	while(ajListGetLength(listcount))
	{
	    ajListPop(listcount, (void**) &iword);
	    AJFREE(iword);
	}
    }

    if(value)
	namUser("++ namListParse value %x '%S'", value, value);

    ajStrDel(&saveword);
    ajStrDel(&saveshortname);
    ajTablestrFree(&Ifiles);

    return;
}
ID ajNamIsDbname
TY public
MO ajnam
LB core
XX
DE Returns true if the name is a valid database (or server) name.
DE
DE Database names must start with a letter, and have 1 or more letters,
DE numbers or underscores. No other characters are permitted.
XX
PN [1]
PA r name const AjPStr
PD character string to find in getenv list
PX
RT AjBool
RD True if name was defined.
RX
//


AjBool ajNamIsDbname(const AjPStr name)
{
    const char* cp = ajStrGetPtr(name);

    if (!*cp)
	return ajFalse;

    if (!isalpha((int)*cp++))
	return ajFalse;

    if (!*cp)
	return ajFalse;

    while (*cp)
    {
	if(!isalnum((int)*cp) && (*cp != '_'))
	    return ajFalse;

	cp++;
    }

    return ajTrue;
}
ID ajNamGetenvC
TY public
MO ajnam
LB core
XX
DE Looks for name as an environment variable.
DE the AjPStr for this in "value". If not found returns value as NULL;
XX
PN [1]
PA r name const char*
PD character string to find in getenv list
PX
PN [2]
PA w value AjPStr*
PD String for the value.
PX
RT AjBool
RD True if name was defined.
RX
//


AjBool ajNamGetenvC(const char* name,
		    AjPStr* value)
{
    char *envval;

    envval = getenv(name);

    if(envval)
    {
	ajStrAssignC(value, envval);

	return ajTrue;
    }

    ajStrDel(value);

    return ajFalse;
}
ID ajNamGetenvS
TY public
MO ajnam
LB core
XX
DE Looks for name as an environment variable.
DE the AjPStr for this in "value". If not found returns NULL;
XX
PN [1]
PA r name const AjPStr
PD character string to find in getenv list
PX
PN [2]
PA w value AjPStr*
PD String for the value.
PX
RT AjBool
RD True if name was defined.
RX
//


AjBool ajNamGetenvS(const AjPStr name,
		    AjPStr* value)
{
    return ajNamGetenvC(ajStrGetPtr(name), value);
}
ID ajNamGetAliasC
TY public
MO ajnam
LB core
XX
DE Looks for name as an alias in the hash table and if found returns
DE the AjPStr for this in "value". If not found returns NULL;
XX
PN [1]
PA r name const char*
PD character string find in hash table.
PX
PN [2]
PA w value AjPStr*
PD Str for the value.
PX
RT AjBool
RD True if name was defined.
RX
//


AjBool ajNamGetAliasC(const char* name, AjPStr* value)
{
    NamPEntry fnew       = NULL;
    
    fnew = ajTableFetchmodC(namAliasMasterTable, name);

    if(fnew)
    {
        ajDebug("Found alias '%s' value '%S' (%S)\n",
                name, fnew->value, fnew->file);
	ajStrAssignS(value, fnew->value);

	return ajTrue;
    }

    return ajFalse;
}
ID ajNamGetAliasS
TY public
MO ajnam
LB core
XX
DE Looks for name as an alias in the hash table and if found returns
DE the AjPStr for this in "value". If not found returns NULL;
XX
PN [1]
PA r namestr const AjPStr
PD character string find in hash table.
PX
PN [2]
PA w value AjPStr*
PD Str for the value.
PX
RT AjBool
RD True if name was defined.
RX
//


AjBool ajNamGetAliasS(const AjPStr namestr, AjPStr* value)
{
    NamPEntry fnew       = NULL;
    
    fnew = ajTableFetchmodS(namAliasMasterTable, namestr);

    if(fnew)
    {
        ajDebug("Found alias '%S' value '%S' (%S)\n",
                namestr, fnew->value, fnew->file);
	ajStrAssignS(value, fnew->value);

	return ajTrue;
    }

    return ajFalse;
}
ID ajNamGetValueC
TY public
MO ajnam
LB core
XX
DE Looks for name as an (upper case) environment variable,
DE and then as-is in the hash table and if found returns
DE the AjPStr for this in "value". If not found returns NULL;
XX
PN [1]
PA r name const char*
PD character string find in hash table.
PX
PN [2]
PA w value AjPStr*
PD Str for the value.
PX
RT AjBool
RD True if name was defined.
RX
//


AjBool ajNamGetValueC(const char* name, AjPStr* value)
{
    NamPEntry fnew       = NULL;
    AjBool hadPrefix     = ajFalse;
    AjBool ret           = ajFalse;

    ajDebug("ajNamGetValueC '%s'\n", name);

    if(ajCharPrefixCaseS(name, namPrefixStr)) /* may already have the prefix */
    {
	ajStrAssignC(&namValNameTmp, name);
	hadPrefix = ajTrue;
    }
    else
    {
	ajStrAssignS(&namValNameTmp, namPrefixStr);
	ajStrAppendC(&namValNameTmp, name);
    }

    /* upper case for ENV, otherwise don't care */
    ajStrFmtUpper(&namValNameTmp);
    
    ajDebug("ajNamGetValueC search for '%S': ", namValNameTmp);

    /* first test for an ENV variable */
    
    ret = ajNamGetenvS(namValNameTmp, value);

    if(ret)
    {
        ajDebug("Found environment variable value '%S'\n", *value);
	return ajTrue;
    }

    /* then test the table definitions - with the prefix */
    
    fnew = ajTableFetchmodS(namVarMasterTable, namValNameTmp);

    if(fnew)
    {
        ajDebug("Found %S prefix set variable '%S' value '%S' (%S)\n",
                namPrefixStr, namValNameTmp, fnew->value, fnew->file);
	ajStrAssignS(value, fnew->value);

	return ajTrue;
    }
    
    if(!hadPrefix)
    {

	/* then test the table definitions - as originally specified */

	fnew = ajTableFetchmodC(namVarMasterTable, name);

	if(fnew)
	{
            ajDebug("Found %S noprefix set variable '%s' value '%S' (%S)\n",
                    namPrefixStr, name, fnew->value, fnew->file);
	    ajStrAssignS(value, fnew->value);

	    return ajTrue;
	}
    }

    if(ajStrMatchC(namValNameTmp, "EMBOSS_INSTALLDIRECTORY"))
    {
        ajStrAssignS(value, ajNamValueInstalldir());
        ajDebug("Found builtin variable '%S' value '%S'\n",
                namValNameTmp, *value);
        return ajTrue;
    }
    
    if(ajStrMatchC(namValNameTmp, "EMBOSS_ROOTDIRECTORY"))
    {
        ajStrAssignS(value, ajNamValueRootdir());
        ajDebug("Found builtin variable '%S' value '%S'\n",
                namValNameTmp, *value);
        return ajTrue;
    }
    
    if(ajStrMatchC(namValNameTmp, "EMBOSS_BASEDIRECTORY"))
    {
        ajStrAssignS(value, ajNamValueBasedir());
        ajDebug("Found builtin variable '%S' value '%S'\n",
                namValNameTmp, *value);
        return ajTrue;
    }

    ajDebug("%S not found\n", namValNameTmp);

    return ajFalse;
}
ID ajNamGetValueS
TY public
MO ajnam
LB core
XX
DE Looks for name as an (upper case) environment variable,
DE and then as-is in the hash table and if found returns
DE the AjPStr for this in "value". If not found returns NULL;
XX
PN [1]
PA r name const AjPStr
PD character string find in hash table.
PX
PN [2]
PA w value AjPStr*
PD String for the value.
PX
RT AjBool
RD True if name was defined.
RX
//


AjBool ajNamGetValueS(const AjPStr name, AjPStr* value)
{
    return ajNamGetValueC(ajStrGetPtr(name), value);
}
ID ajNamServer
TY public
MO ajnam
LB core
XX
DE Looks for name in the server hash table and if found returns
DE the attribute for this. If not found returns  NULL;
XX
PN [1]
PA r name const AjPStr
PD character string find in hash table.
PX
RT AjBool
RD true if server name is valid.
RX
//


AjBool ajNamServer(const AjPStr name)
{
    const NamPEntry fnew = NULL;

    /* ajDebug("ajNamServer '%S'\n", name); */

    fnew = ajTableFetchS(namSvrMasterTable, name);

    if(fnew)
    {
	/* ajDebug("  '%S' found\n", name); */
	return ajTrue;
    }

    /* ajDebug("  '%S' not found\n", name); */
    return ajFalse;
}
ID ajNamDatabase
TY public
MO ajnam
LB core
XX
DE Looks for name in the database hash table and if found returns
DE true.
XX
PN [1]
PA r name const AjPStr
PD character string find in hash table.
PX
RT AjBool
RD true if database name is valid.
RX
//


AjBool ajNamDatabase(const AjPStr name)
{
    const NamPEntry fnew = NULL;

    /* ajDebug("ajNamDatabase '%S'\n", name); */

    fnew = ajTableFetchS(namDbMasterTable, name);

    if(fnew)
    {
	/* ajDebug("  '%S' found\n", name); */
	return ajTrue;
    }

    /* ajDebug("  '%S' not found\n", name); */
    return ajFalse;
}
ID ajNamAliasDatabase
TY public
MO ajnam
LB core
XX
DE Looks for name in the database hash table and the database hash
DE table and if found returns the true name for this. If not found
DE returns NULL.
XX
PN [1]
PA u Pname AjPStr*
PD character string to find in hash table.
PX
RT AjBool
RD true if database name is valid.
RX
//


AjBool ajNamAliasDatabase(AjPStr *Pname)
{
    const NamPEntry fnew = NULL;
    AjPStr alias = NULL;
    const AjPStr tryname = *Pname;

    /* ajDebug("ajNamAliasDatabase '%S'\n", *Pname); */

    fnew = ajTableFetchS(namDbMasterTable, *Pname);

    if(fnew)
    {
	/* ajDebug("  '%S' found\n", *Pname); */
	return ajTrue;
    }

    while(ajNamGetAliasS(tryname, &alias))
    {
        fnew = ajTableFetchS(namDbMasterTable, alias);
        if(fnew)
        {
            ajStrAssignS(Pname, alias);
            /*ajDebug("  '%S' found as '%S'\n", name, alias);*/
            ajStrDel(&alias);
            return ajTrue;
        }
        tryname = alias;
    }

    ajStrDel(&alias);
    /* ajDebug("  '%S' not found\n", name); */
    return ajFalse;
}
ID ajNamDatabaseServer
TY public
MO ajnam
LB core
XX
DE Looks for a database name for a specific server.
DE Reads the server cache file to find the database.
XX
PN [1]
PA r name const AjPStr
PD Database name to find for server
PX
PN [2]
PA r server const AjPStr
PD Server name
PX
RT AjBool
RD true if database name is valid.
RX
//


AjBool ajNamDatabaseServer(const AjPStr name, const AjPStr server)
{
    AjBool ret = ajTrue;
    const NamPEntry svdata = NULL;
    const NamPEntry dbdata = NULL;
    const NamPEntry sdbdata = NULL;
    AjPTable svrtable;
    AjPTable dbtable;
    const AjPStr svrval = NULL;
    AjPStr cachefile = NULL;

    ajDebug("ajNamDatabaseServer '%S' for server '%S'\n",
            name, server);

    svdata = ajTableFetchS(namSvrMasterTable, server);

    if(!svdata)
    {
	ajErr("Server '%S' not found, unable to search for database '%S'",
              server, name);
	return ajFalse;
    }

    dbdata = ajTableFetchS(namSvrDatabaseTable,server);

    if(!dbdata)
    {

        /* pick up the cache file name for this server */
        svrtable = (AjPTable) svdata->data;
        svrval = ajTableFetchmodC(svrtable, "cachefile");

        if(ajStrGetLen(svrval))
            cachefile = ajStrNewS(svrval);
        else
        {
            cachefile = ajStrNewC("server.");
            ajStrAppendS(&cachefile, server);
        }

        namSvrCacheRead(server, cachefile);
        dbdata = ajTableFetchS(namSvrDatabaseTable, server);

        ajStrDel(&cachefile);
    }

    if(!dbdata)
    {
        ajWarn("Server '%S' has no databases", server);
        return ajFalse;
    }

    dbtable = (AjPTable) dbdata->data;
    sdbdata = ajTableFetchS(dbtable, name);

    if(!sdbdata)
    {
        ajWarn("Server '%S' has no database '%S'", server, name);
        return ajFalse;
    }

    ajDebug("Found database '%S' on server '%S'\n", name, server);

    /* ajDebug("  '%S' not found\n", name); */
    return ret;
}
ID ajNamAliasServer
TY public
MO ajnam
LB core
XX
DE Looks for a database name for a specific server.
DE Reads the server cache file to find the database.
XX
PN [1]
PA u Pname AjPStr*
PD character string to find in hash table.
PX
PN [2]
PA r server const AjPStr
PD Server name
PX
RT AjBool
RD true if database name is valid.
RX
//


AjBool ajNamAliasServer(AjPStr *Pname, const AjPStr server)
{
    const NamPEntry svdata = NULL;
    const NamPEntry dbdata = NULL;
    const NamPEntry sdbdata = NULL;
    const NamPEntry alidata = NULL;
    const NamPEntry salidata = NULL;
    AjPTable svrtable;
    AjPTable dbtable;
    AjPTable alitable;
    const AjPStr svrval = NULL;
    AjPStr cachefile = NULL;
    AjPStr alias = NULL;
    AjBool hasAlias = ajFalse;

    ajDebug("ajNamAliasServer '%S' for server '%S'\n",
            *Pname, server);

    svdata = ajTableFetchS(namSvrMasterTable, server);

    if(!svdata)
    {
	ajErr("Server '%S' not found, unable to search for database '%S'",
              server, *Pname);
	return ajFalse;
    }

    dbdata = ajTableFetchS(namSvrDatabaseTable,server);
    alidata = ajTableFetchS(namSvrAliasTable,server);

    if(!dbdata)
    {

        /* pick up the cache file name for this server */
        svrtable = (AjPTable) svdata->data;
        svrval = ajTableFetchmodC(svrtable, "cachefile");

        if(ajStrGetLen(svrval))
        {
            cachefile = ajStrNewS(svrval);
        }
        else
        {
            cachefile = ajStrNewC("server.");
            ajStrAppendS(&cachefile, server);
        }

        namSvrCacheRead(server, cachefile);
        dbdata = ajTableFetchS(namSvrDatabaseTable, server);
        alidata = ajTableFetchS(namSvrAliasTable, server);

        ajStrDel(&cachefile);
    }

    if(!dbdata)
    {
        ajWarn("Server '%S' has no databases", server);
        return ajFalse;
    }

   ajStrAssignS(&alias, *Pname);

    alitable = (AjPTable) alidata->data;
    salidata = ajTableFetchS(alitable, alias);

    while(salidata)
    {
        hasAlias = ajTrue;
        ajStrAssignS(&alias,salidata->value);
        salidata = ajTableFetchS(alitable, alias);
    }

    dbtable = (AjPTable) dbdata->data;
    sdbdata = ajTableFetchS(dbtable, alias);
    if(!sdbdata)
    {
        if(hasAlias)
            ajWarn("Server '%S' has no database '%S' (alias '%S')",
                   server, *Pname, alias);
        else
            ajWarn("Server '%S' has no database '%S'", server, *Pname);

        ajStrDel(&alias);
        return ajFalse;
    }

    ajDebug("Found database '%S' (alias '%S') on server '%S'\n",
            *Pname, alias, server);

    ajStrAssignS(Pname, alias);

    ajStrDel(&alias);

    return ajTrue;
}
ID namSvrCacheParse
TY static
MO ajnam
LB core
XX
DE Parses a database cachefile for a server
XX
PN [1]
PA u cachefile AjPFile
PD Cache file
PX
PN [2]
PA u dbtable AjPTable
PD Database table for server
PX
PN [3]
PA u alitable AjPTable
PD Alias table for server
PX
RT AjBool
RD True on success
RX
//


static AjBool namSvrCacheParse(AjPFile cachefile, AjPTable dbtable,
                               AjPTable alitable)
{
    AjBool ret = ajTrue;
    AjPStr rdline = NULL;
    AjPStr word   = NULL;
    const char *ptr;
    ajint i = 0;
    ajint len;
    char quote = '\0';
    AjPList listwords;
    AjPList listcount;
    AjPStr wordptr;
    ajint iline = 0;
    ajint *k    = NULL;

    ajDebug("namSvrCacheParse '%F'\n", cachefile);

    ajFmtPrintS(&namFileName, "%F",cachefile);

    listwords = ajListstrNew();
    listcount = ajListNew();
    word      = ajStrNewRes(128);

    /* Read in the settings. */
    while(ajReadlineTrim(cachefile, &rdline))
    {
	iline++;
 	AJNEW0(k);
	*k = (ajuint) ajListGetLength(listwords);
	ajListPushAppend(listcount, k);

	if(!ajStrCutCommentsStart(&rdline))
	    continue;

	len = ajStrGetLen(rdline);

	/* now create a linked list of the "words" */
	if(len)
	{
	    ptr = ajStrGetPtr(rdline);
	    i = 0;

	    while(*ptr && i < len)
	    {
		if(*ptr == ' ' || *ptr == '\t')
		{
		    if(ajStrGetLen(word))
		    {
			wordptr = ajStrNewS(word);
			ajListstrPushAppend(listwords, wordptr);
			ajStrAssignClear(&word);
		    }

		    i++;
		    ptr++;
		    continue;
		}
		else if(*ptr == '\'' || *ptr == '\"')
		{
		    ajStrAppendK(&word,*ptr);

		    if(quote)
		    {
			if(quote == *ptr)
			    quote = '\0';
		    }
		    else
			quote = *ptr;
		}
		else if(!quote && ajStrGetLen(word) && *ptr == ']')
		{
		    wordptr = ajStrNewS(word);
		    ajListstrPushAppend(listwords, wordptr);
		    ajStrAssignClear(&word);
		    wordptr = ajStrNewC("]");
		    ajListstrPushAppend(listwords, wordptr);
		    ajStrAssignClear(&word);
		}
		else
		    ajStrAppendK(&word,*ptr);

		i++;
                ptr++;
	    }

	    if(ajStrGetLen(word))
	    {
		wordptr = ajStrNewS(word);
		ajListstrPushAppend(listwords, wordptr);
		ajStrAssignClear(&word);
	    }
	    
	}
    }

    ajStrDel(&rdline);
    
    AJNEW0(k);
    *k = (ajuint) ajListGetLength(listwords);
    ajListPushAppend(listcount, k);
    
    if(!namSvrCacheParseList(listwords, listcount, cachefile,
                             dbtable, alitable))
    {
	ajErr("%F: Unexpected end of file in %S at line %d\n",
              cachefile, iline); /* FIXME */
        ret = ajFalse;
    }

    ajListFree(&listwords);
    ajListFree(&listcount);
    ajStrDel(&word);

    return ret;
}
ID namSvrCacheParseList
TY static
MO ajnam
LB core
XX
DE Parse the text in a list of tokens read from the server cache file.
DE Derive server database definitions. Store all these in the server's
DE internal table.
XX
PN [1]
PA u listwords AjPList
PD String list of word tokens to parse
PX
PN [2]
PA u listcount AjPList
PD List of word counts per line for
PD generating error messages
PX
PN [3]
PA u cachefile AjPFile
PD Input file only for name in messages
PX
PN [4]
PA u dbtable AjPTable
PD Database table for server
PX
PN [5]
PA u alitable AjPTable
PD Alias table for server
PX
RT AjBool
RD True on success
RX
//


static AjBool namSvrCacheParseList(AjPList listwords, AjPList listcount,
                                   AjPFile cachefile,
                                   AjPTable dbtable, AjPTable alitable)
{
    static char* tabname   = NULL;
    static AjPStr name     = NULL;
    static AjPStr value    = NULL;
    static char quoteopen  = '\0';
    static char quoteclose = '\0';
    static AjPTable dbattr  = NULL;
    static ajint  db_input = -1;
    AjPTable saveTable = NULL;

    NamPEntry fnew  = NULL;
    NamPEntry entry = NULL;

    AjPStr includefn = NULL;
    AjPFile iinf     = NULL;
    AjPStr key       = NULL;
    AjPStr val       = NULL;
    AjPStr oldval    = NULL;
    static AjPTable Ifiles = NULL;
    AjPStr curword = NULL;

    ajint wordcount = 0;
    ajint linecount = 0;
    ajint lineword  = 0;
    ajint *iword    = NULL;
    AjBool namstatus;
    AjPStr saveshortname = NULL;
    AjPStr saveword = NULL;

    AjBool dbsave = ajFalse;
    AjBool saveit = ajFalse;

    ajFmtPrintS(&saveshortname, "%F", cachefile);
    quoteopen  = '\0';
    quoteclose = '\0';

    namLine = 1;

    while(ajListstrPop(listwords, &curword))
    {
	while(ajListGetLength(listcount) && (lineword < wordcount))
	{
	    namUser("ajListPop %d < %d list %Lu\n",
		    lineword, wordcount, ajListGetLength(listcount));
	    ajListPop(listcount, (void**) &iword);
	    lineword = *iword;
	    linecount++;
	    namLine = linecount-1;
	    AJFREE(iword);
	}

	wordcount++;

	if(!namParseType)
	{
	    namNoColon(&curword);
	    ajStrFmtLower(&curword);

	    if(ajCharPrefixS("dbname",curword))
		namParseType = TYPE_DB;
	    if(ajCharPrefixS("alias",curword))
		namParseType = TYPE_ALIAS;
	    else if(ajCharPrefixS("include",curword))
		namParseType = TYPE_IFILE;

	    if(!namParseType)		/* test: badtype.rc */
		namError("Invalid definition type '%S'", curword);
	}
	else if(quoteopen && ajStrMatchC(curword, "]"))
	{				/* test; dbnoquote.rc */
	    namError("']' found, unclosed quotes in '%S'", value);
	    quoteopen    = '\0';
	    namParseType = 0;
	}
	else if(quoteopen)
	{
	    /*
	    ** quote is open, so append word until close quote is found,
	    ** and set the appropriate save flag
	    */
	    namUser("<%c>..<%c> quote processing\n", quoteopen, quoteclose);
	    ajStrAppendC(&value," ");
	    ajStrAppendS(&value,curword);

	    /* close quote here ?? */
	    if(ajStrGetCharLast(curword) == quoteclose)
	    {
		namUser("close quotes\n");
		ajStrCutEnd(&value, 1);
		quoteopen = quoteclose = '\0';

		if(namParseType == TYPE_DB)
		    dbsave = ajTrue;
		if(namParseType == TYPE_ALIAS)
		    dbsave = ajTrue;
	    }
	}
	else if(namParseType == TYPE_ALIAS)
	{
	    namUser("CACHE ALIAS saveit: %B name: '%S' value '%S' "
                    "quoteopen: '%c'\n",
                    saveit, name, value, quoteopen);
	    if(name && value)
		saveit= ajTrue;
	    else if(name)
	    {
		/* if name already got then it must be the value */
		if(ajStrGetCharFirst(curword) == '\'')
		    quoteopen = quoteclose = '\'';
		else if(ajStrGetCharFirst(curword) == '\"')
		    quoteopen = quoteclose = '\"';

		ajStrAssignS(&value, curword);

		if(quoteopen)
		{
		    /* trim the opening quote */
		    ajStrCutStart(&value, 1);

		    if(!ajStrGetLen(value))
			ajErr("Bare quote %c found in namListParse",
			       quoteopen);
		}
		else
		    saveit = ajTrue;

		if(ajStrGetCharLast(curword) == quoteclose)
		{
		    /* end of quote on same word */
		    quoteopen = quoteclose = '\0';
		    saveit= ajTrue;
		    /* remove quote at the end */
		    ajStrCutEnd(&value, 1);
		}

                if(saveit)
                    ajNamResolve(&value);
		namUser("save alias value '%S'\n", value);
	    }
	    else
	    {
		ajStrAssignS(&name, curword);
		namUser("save alias name '%S'\n", name);
	    }
        }
	else if(namParseType == TYPE_DB)
	{
	    if(ajStrMatchC(curword, "[")) /* [ therefore new database */
		dbattr = ajTablestrNew(100); /* new db obj */
	    else if(ajStrMatchC(curword, "]"))	/* ] therefore end of db */
		saveit = ajTrue;
	    else if(name)
	    {
		if(db_input >= 0)
		{
		    /* So if keyword type has been set */
		    if(ajStrGetCharFirst(curword) == '\'')
		    {
			/* is there a quote? If so expect the */
			/* same at the end. No ()[] etc here*/
			quoteopen = quoteclose = '\'';
		    }
		    else if(ajStrGetCharFirst(curword) == '\"')
			quoteopen = quoteclose = '\"';

		    ajStrAssignS(&value, curword);

		    if(quoteopen)
		    {
			ajStrCutStart(&value, 1); /* trim opening quote */

			if(!ajStrGetLen(value))
			    ajErr("Bare quote %c found in namListParse",
				   quoteopen);
		    }
		    else
			dbsave = ajTrue; /* we are done - simple word */

		    if(ajStrGetCharLast(curword) == quoteclose)
		    {
			quoteopen = quoteclose = '\0';
			ajStrCutEnd(&value,1); /* trim closing quote */
			dbsave = ajTrue;
		    }

		    if(!quoteopen)     /* if we just reset it above */
			dbsave = ajTrue;

                    if(dbsave)
                        ajNamResolve(&value);
		}
		else if(ajStrGetCharLast(curword) == ':')
		{
		    /* if last character is : then its a keyword */
		    ajStrFmtLower(&curword); /* make it lower case */
		    namNoColon(&curword);
		    db_input = namDbAttrS(curword);

		    if(db_input < 0)
			ajWarn("%F: bad attribute '%S' for database '%S'",
				cachefile, curword, name);
		}
                else
                {
                    ajWarn("%F: unexpected token '%S' for database '%S'",
                           cachefile, curword, name);
                }
	    }
	    else
	    {
		ajStrAssignS(&name, curword);

		if(!ajNamIsDbname(name))
		    ajErr("Invalid database name '%S'", name);

		namUser("saving db name '%S'\n", name);
	    }
	}
	
	
	else if(namParseType == TYPE_IFILE)
	{
	    if(!Ifiles)
		Ifiles = ajTablestrNew(NAM_INCLUDE_ESTIMATE);

	    namParseType = 0;

            ajStrAssignS(&saveword, curword);

            ajNamResolve(&curword);

	    if(ajTableFetchS(Ifiles,curword)) /* test: includeagain.rc */
            {
		if(ajStrMatchS(curword, saveword))
                    namError("%S already read ... skipping", curword);
                else
                    namError("%S (%S) already read ... skipping",
                             saveword, curword);
            }
	    else
	    {
		includefn = ajStrNew();
		ajStrAssignS(&includefn,curword);

		if(namFileOrig)
		    ajStrAppendC(&namFileOrig,", ");

		ajStrAppendS(&namFileOrig,includefn);

		key = ajStrNewS(includefn);
		val = ajStrNewS(includefn);
		oldval = ajTablePut(Ifiles,key,val);
                namUser("store file '%S' => '%S\n", key, val);

                if(oldval)
                {
                    ajWarn("duplicate include value for '%S' = '%S'",
                           key, oldval);
                    ajStrDel(&key);
                    ajStrDel(&oldval);
                }

                /* test: badinclude.rc */
		if(!(iinf = ajFileNewInNameS(includefn)))
		{
                    if(ajStrMatchS(includefn, saveword))
                        namError("Failed to open include file '%S'",
                                 includefn);
                    else
                        namError("Failed to open include file '%S' (%S)",
                                 saveword, includefn);
		    ajStrAppendC(&namFileOrig,"(Failed)");
		}
		else
		{
		    ajStrAppendC(&namFileOrig,"(OK)");
                    /* replaces namFile */
                    ajStrAssignS(&name, includefn);
                    ajFilenameTrimPath(&name);
		    namstatus = namProcessFile(iinf, name);
		    ajFmtPrintS(&namFileName, "%F",
                                cachefile);/* reset saved name */
		    namLine = linecount-1;

		    if(!namstatus)	/* test: badsummary.rc */
                    {
                        if(ajStrMatchS(includefn, saveword))
                            namError("Error(s) found in included file %F",
                                     iinf);
                        else
                            namError("Error(s) found in included file %S (%F)",
                                     saveword, iinf);
                    }
                    

		    ajFileClose(&iinf);
		}

		ajStrDel(&includefn);
	    }

	    namListParseOK = ajTrue;
	}
	
	
	if(dbsave)
	{
	    /* Save the keyword value */
            key = ajStrNewC(namDbAttrs[db_input].Name);
            namDbtablePutAttrS(dbattr, &key, &value);

	    db_input =-1;
	    dbsave = ajFalse;
	}

	if(saveit)
	{
	    namUser("saving type %d name '%S' value '%S' line:%d\n",
		    namParseType, name, value, namLine);
	    AJNEW0(fnew);
	    tabname = ajCharNewS(name);
	    fnew->name = name;
	    name = NULL;
	    fnew->value = value;
	    value = NULL;
	    fnew->file = ajStrNewRef(saveshortname);

            if(namParseType == TYPE_DB)
	    {
		fnew->data = (AjPTable) dbattr;
                dbattr = NULL;
		saveTable = dbtable;
	    }
	    else if(namParseType == TYPE_ALIAS)
	    {
		fnew->data = NULL;
		saveTable = alitable;
	    }
	    else
	    {
		fnew->data = NULL;
	    }
	    entry = ajTablePut(saveTable, tabname, fnew);
            namUser("store entry '%s' '%S' (%S)\n",
                    tabname, fnew->name, fnew->file);

	    if(entry)
	    {
		/* it existed so over wrote previous table entry
		** Only a namUser message - redefining EMBOSSRC in
		** QA testing can give too many warnings
		*/
		ajErr("%F: replaced %s %S definition from %S\n",
                      cachefile,
                      namTypes[namParseType],
                      entry->name,
                      entry->file);
		namEntryDelete(&entry, namParseType); /* previous entry */
	    }

	    saveit = ajFalse;
	    namParseType = 0;
        }
	ajStrDel(&curword);
    }

    if(namParseType)
    {
	/* test: badset.rc baddb.rc  */
	namError("Unexpected end of file in %s definition",
		 namTypes[namParseType]);
	namParseType = 0;
    }
    
    if(ajListGetLength(listcount))	/* cleanup the wordcount list */
    {
	namUser("** remaining wordcount items: %Lu\n",
                ajListGetLength(listcount));

	while(ajListGetLength(listcount))
	{
	    ajListPop(listcount, (void**) &iword);
	    AJFREE(iword);
	}
    }

    if(value)
	namUser("++ namListParse value %x '%S'", value, value);

    ajStrDel(&saveword);
    ajStrDel(&saveshortname);
    ajTablestrFree(&Ifiles);

    return ajTrue;
}
ID ajNamSvrCount
TY public
MO ajnam
LB core
XX
DE Reads the database cachefile for a server and counts the number of databases
DE
DE Searches emboss_standard and the user directory ~/.embossdata/
DE testing the file data in each file found and parsing the most recent file.
DE
DE cache files should all start with a comment line
DE containing the file name and the creation date.
XX
PN [1]
PA r server const AjPStr
PD Server name
PX
RT ajuint
RD Number of databases
RX
//


ajuint ajNamSvrCount(const AjPStr server)
{
    const NamPEntry svdata = NULL;
    const NamPEntry dbdata = NULL;
    AjPTable svrtable;
    AjPTable dbtable;
    const AjPStr svrcache = NULL;
    AjPStr cachefile = NULL;

    dbdata = ajTableFetchS(namSvrDatabaseTable, server);

    if(!dbdata)
    {
        svdata = ajTableFetchS(namSvrMasterTable, server);

        if(!svdata)
        {
            ajErr("Server '%S' not found, unable to count databases",
                  server);
            return 0;
        }

        svrtable = (AjPTable) svdata->data;
        svrcache = ajTableFetchC(svrtable, "cachefile");

        if(ajStrGetLen(svrcache))
            cachefile = ajStrNewS(svrcache);
        else
        {
            cachefile = ajStrNewC("server.");
            ajStrAppendS(&cachefile, server);
        }

        namSvrCacheRead(server, cachefile);
        dbdata = ajTableFetchS(namSvrDatabaseTable, server);

        if(!dbdata)
        {
            ajDebug("ajNamSvrCount: server '%S' has no databases\n", server);
            ajStrDel(&cachefile);
            return 0;
        }
    }

    dbtable = (AjPTable) dbdata->data;

    ajStrDel(&cachefile);
    return (ajuint) ajTableGetLength(dbtable);
}
ID ajNamSvrListListDatabases
TY public
MO ajnam
LB core
XX
DE Reads the database cachefile for a server and lists the databases.
DE
DE Searches emboss_standard and the user directory ~/.embossdata/
DE testing the file data in each file found and parsing the most recent file.
DE
DE cache files should all start with a comment line
DE containing the file name and the creation date.
XX
PN [1]
PA r server const AjPStr
PD Server name
PX
PN [2]
PA w dbnames AjPList
PD Str List of names to be populated
PX
RT void
RD 
RX
//


void ajNamSvrListListDatabases(const AjPStr server, AjPList dbnames)
{
    void **valarray =  NULL;
    register ajint i = 0;

    const NamPEntry svdata = NULL;
    const NamPEntry dbdata = NULL;
    NamPEntry fnew   = NULL;
    AjPTable svrtable;
    AjPTable dbtable;
    const AjPStr svrcache = NULL;
    AjPStr cachefile = NULL;
    
    /*
    ** TODO: Should server files be read by a separate function?
    ** This function is largely a copy of ajNamSvrCount and
    ** ajNamListListDatabases so that server files are read by both functions.
    */
    
    dbdata = ajTableFetchS(namSvrDatabaseTable, server);
    
    if(!dbdata)
    {
        svdata = ajTableFetchS(namSvrMasterTable, server);
        
        if(!svdata)
        {
            ajErr("Server '%S' not found, unable to count databases",
                  server);
            return;
        }
        
        svrtable = (AjPTable) svdata->data;
        svrcache = ajTableFetchC(svrtable, "cachefile");
        
        if(ajStrGetLen(svrcache))
            cachefile = ajStrNewS(svrcache);
        else
        {
            cachefile = ajStrNewC("server.");
            ajStrAppendS(&cachefile, server);
        }
        
        namSvrCacheRead(server, cachefile);
        dbdata = ajTableFetchS(namSvrDatabaseTable, server);
        
        ajStrDel(&cachefile);
        
        if(!dbdata)
        {
            ajWarn("Server '%S' has no databases", server);
            return;
        }
    }
    
    dbtable = (AjPTable) dbdata->data;
    
    ajTableToarrayValues(dbtable, &valarray);
    ajDebug("ajNamSvrListListDatabases\n");
    
    for(i = 0; valarray[i]; i++)
    {
	fnew = (NamPEntry) valarray[i];
	ajDebug("DB: %S\n", fnew->name);
	ajListstrPushAppend(dbnames, fnew->name);
    }
    
    AJFREE(valarray);
    
    return;
}
ID namSvrCacheOpen
TY static
MO ajnam
LB core
XX
DE Opens the database cachefile for a server.
DE
DE Searches emboss_standard and the user directory ~/.embossdata/
DE testing the file data in each file found and opening the most recent file.
DE
DE cache files must all start with a comment line
DE containing the file name and the creation date.
XX
PN [1]
PA r cachename const AjPStr
PD Cache file name
PX
RT AjPFile
RD Filename of selected cache file
RX
//


static AjPFile namSvrCacheOpen(const AjPStr cachename)
{
    AjPFile ret = NULL;
    AjPFile userfile = NULL;
    AjPFile systemfile = NULL;

    AjPStr readline = NULL;
    AjPStrTok handle = NULL;
    AjPStr token = NULL;
    AjPTime usertime = NULL;
    AjPTime systemtime = NULL;

    systemfile = ajFileNewInNamePathS(cachename, namStandardDir);
    ajDebug("system file '%F' name: '%S' path: '%S'\n",
            systemfile, cachename, namStandardDir);

    if(systemfile)
    {
        ajReadlineTrim(systemfile, &readline);
        ajStrTokenAssignC(&handle, readline, "# \t");
        ajStrTokenNextParse(&handle, &token);

        if(!ajStrMatchS(token, cachename))
            ajWarn("%F: Cache file name '%S' expected, '%S' found",
                   systemfile, cachename, token);

        ajStrTokenNextParseC(&handle, "", &token); /* time */
        systemtime = ajTimeNew();
        ajTimeSetS(systemtime, token);
    }

    userfile = ajFileNewInNamePathS(cachename, namUserDir);
    ajDebug("user file '%F' name: '%S' path: '%S'\n",
            userfile, cachename, namStandardDir);

    if(userfile)
    {
        ajReadlineTrim(userfile, &readline);
        ajStrTokenAssignC(&handle, readline, "# \t");
        ajStrTokenNextParse(&handle, &token);

        if(!ajStrMatchS(token, cachename))
            ajWarn("%F: Cache file name '%S' expected, '%S' found",
                   userfile, cachename, token);

        ajStrTokenNextParseC(&handle, "", &token); /* time */
        usertime = ajTimeNew();
        ajTimeSetS(usertime, token);
    }

    if(userfile && (!systemfile || ajTimeDiff(systemtime, usertime) > 0.0))
    {
        ajFileClose(&systemfile);
        ret = userfile;
        ajDebug("user file '%F' %D selected\n", userfile, usertime);
    }
    else
    {
        ajFileClose(&userfile);
        ret = systemfile;
        ajDebug("system file '%F' %D selected\n", systemfile, systemtime);
    }

    ajStrTokenDel(&handle);
    ajStrDel(&readline);
    ajStrDel(&token);
    ajTimeDel(&systemtime);
    ajTimeDel(&usertime);

    return ret;
}
ID namSvrCacheRead
TY static
MO ajnam
LB core
XX
DE Reads the database cachefile for a server
DE
DE Searches emboss_standard and the user directory ~/.embossdata/
DE testing the file data in each file found and parsing the most recent file.
DE
DE cache files must all start with a comment line
DE containing the file name and the creation date.
XX
PN [1]
PA r server const AjPStr
PD Server name
PX
PN [2]
PA r cachename const AjPStr
PD Cache file name
PX
RT AjBool
RD True on success
RX
//


static AjBool namSvrCacheRead(const AjPStr server, const AjPStr cachename)
{
    AjPFile cachefile;
    AjPTable dbtable;
    AjPTable alitable;
    NamPEntry fnew = NULL;

    cachefile = namSvrCacheOpen(cachename);
    if(!cachefile)
        return ajFalse;

    dbtable = ajTablecharNewCase(30);
    alitable = ajTablecharNewCase(30);

    namSvrCacheParse(cachefile, dbtable, alitable);
    ajFileClose(&cachefile);

    AJNEW0(fnew);
    fnew->name = ajStrNewS(server);
    fnew->value = ajStrNewS(cachename);
    fnew->data = dbtable;
    ajTablePut(namSvrDatabaseTable, ajCharNewS(server), fnew);
    fnew = NULL;

    AJNEW0(fnew);
    fnew->name = ajStrNewS(server);
    fnew->value = ajStrNewS(cachename);
    fnew->data = alitable;
    ajTablePut(namSvrAliasTable, ajCharNewS(server), fnew);
    fnew = NULL;


    return ajTrue;
}
ID namProcessFile
TY static
MO ajnam
LB core
XX
DE Read the definitions file and append each token to the list.
XX
PN [1]
PA u file AjPFile
PD Input file object
PX
PN [2]
PA r shortname const AjPStr
PD Definitions file short name
PX
RT AjBool
RD ajTrue if no error were found
RX
//


static AjBool namProcessFile(AjPFile file, const AjPStr shortname)
{
    AjPStr rdline = NULL;
    AjPStr word   = NULL;
    const char *ptr;
    ajint i = 0;
    ajint len;
    char quote = '\0';
    AjPList listwords;
    AjPList listcount;
    AjPStr wordptr;
    ajint iline = 0;
    ajint *k    = NULL;
    
    ajint preverrorcount = namErrorCount;
    
    listwords = ajListstrNew();
    listcount = ajListNew();
    word      = ajStrNewRes(128);
    
    ajFmtPrintS(&namFileName, "%F", file);
    namUser("namProcessFile '%F'\n", file);
    
    /* Read in the settings. */
    while(ajReadlineTrim(file, &rdline))
    {
	iline++;
	AJNEW0(k);
	*k = (ajuint) ajListGetLength(listwords);
	ajListPushAppend(listcount, k);
	
        /* Ignore if the line is a comment */
	if(!ajStrCutCommentsStart(&rdline))
	    continue;
	
	/* namUser("%S\n",rdline); */
	len = ajStrGetLen(rdline);
	
	/* now create a linked list of the "words" */
	if(len)
	{
	    ptr = ajStrGetPtr(rdline);
	    i = 0;

	    while(*ptr && i < len)
	    {
		if(*ptr == ' ' || *ptr == '\t')
		{
		    if(ajStrGetLen(word))
		    {
			wordptr = ajStrNewS(word);
			ajListstrPushAppend(listwords, wordptr);
			ajStrAssignClear(&word);
		    }

		    i++;
		    ptr++;
		    continue;
		}
		else if(*ptr == '\'' || *ptr == '\"')
		{
		    ajStrAppendK(&word,*ptr);

		    if(quote)
		    {
			if(quote == *ptr)
			    quote = '\0';
		    }
		    else
			quote = *ptr;
		}
		else if(!quote && ajStrGetLen(word) && *ptr == ']')
		{
		    wordptr = ajStrNewS(word);
		    ajListstrPushAppend(listwords, wordptr);
		    ajStrAssignClear(&word);
		    wordptr = ajStrNewC("]");
		    ajListstrPushAppend(listwords, wordptr);
		    ajStrAssignClear(&word);
		}
		else
		    ajStrAppendK(&word,*ptr);
		i++;ptr++;
	    }

	    if(ajStrGetLen(word))
	    {
		wordptr = ajStrNewS(word);
		ajListstrPushAppend(listwords, wordptr);
		ajStrAssignClear(&word);
	    }
	    
	}
    }

    ajStrDel(&rdline);
    
    AJNEW0(k);
    *k = (ajuint) ajListGetLength(listwords);
    ajListPushAppend(listcount, k);
    
    namListParseOK = ajTrue;
    
    namUser("ready to parse\n");
    namListParse(listwords, listcount, file, shortname);
    
    if(!namListParseOK)
	namUser("Unexpected end of file in %S at line %d\n",
		namRootStr, iline);
    
    namUser("file read\n");
    ajListstrFreeData(&listwords); /* Delete the linked list structure */
    namUser("wordlist free\n");
    ajListFree(&listcount);	/* Delete the linked list structure */
    namUser("countlist free\n");
    
    namDebugVariables();
    namDebugAliases();
    ajStrDel(&word);
    
    namUser("namProcessFile done '%F'\n", file);

    if(namErrorCount > preverrorcount)
	return ajFalse;

    return ajTrue;
}
ID ajNamInit
TY public
MO ajnam
LB core
XX
DE Initialise the variable and database definitions. Find the definition
DE files and read them.
XX
PN [1]
PA r prefix const char*
PD Default prefix for all file
PD and variable names.
PX
RT void
RD 
RX
//


void ajNamInit(const char* prefix)
{
    const char *prefixRoot;
    AjPFile prefixRootFile;
    AjPStr prefixRootStr = NULL;
    AjPStr prefixStr     = NULL;
    AjPStr prefixCap     = NULL;
    AjPStr debugStr      = NULL;
    AjPStr debugVal      = NULL;
    AjPStr homercVal     = NULL;
    AjPStr basename      = NULL;
    AjBool root_defined;
    AjBool is_windows = ajFalse;
    NamPEntry entryAuto  = NULL;

    /*
    ** The Windows socket interface must be initialised before any
    ** socket calls are made elsewhere in the AJAX library.
    ** This must only be done once so here is as good a place
    ** as any to do it.
    ** Set the winsock version to 1.1
    **
    ** Also note that EMBOSS_ROOT must be set for ajNamInit to
    ** work with Windows. This will be done by the Windows
    ** installer but developers must set it manually e,g,
    **    set EMBOSS_ROOT C:\emboss\win32
    */
#ifdef WIN32    
    WSADATA wsaData;
    AjPStr tmpstr        = NULL;
#endif

    if(namVarMasterTable &&
       namAliasMasterTable &&
       namSvrMasterTable &&
       namDbMasterTable &&
       namResMasterTable)
	return;

#ifdef HAVE_MCHECK
    /*
    ** mcheck turns on checking of all malloc/calloc/realloc/free calls
    **
    ** it *must* be called before any other malloc by the main program
    **
    ** ajMemCheck reports on status. If called via ajMemProbe (AJMPROBE)
    ** it can also report the source file line it was invoked from
    **
    ** This is all specific to glibc and must be turned on with ./configure
    */
    if(mcheck(ajMemCheck))
	ajWarn("ajNamInit called after first malloc - ajMemCheck ignored");
#endif /* ! HAVE_MCHECK */

#ifdef WIN32
    WSAStartup(MAKEWORD(1, 1), &wsaData);
    is_windows = ajTrue;
#endif


    /*
    ** static namPrefixStr is the prefix for all variable names
    */
    
    ajStrAssignC(&namPrefixStr, prefix);
    
    ajStrAppendC(&namPrefixStr, "_");
    
    ajClockReset();
    ajTimeReset();

    /* create new tables to hold the values */

    namVarMasterTable = ajTablecharNewCase(100);
    namAliasMasterTable = ajTablecharNewCase(100);
    namSvrMasterTable = ajTablecharNewCase(100);
    namDbMasterTable = ajTablecharNewCase(100);
    namResMasterTable = ajTablecharNewCase(100);
    namSvrDatabaseTable = ajTablecharNewCase(100);
    namSvrAliasTable = ajTablecharNewCase(100);

#ifdef HAVE_AXIS2C
    AJNEW0(entryAuto);
    entryAuto->name = ajStrNewC("emboss_axis2c");
    entryAuto->value = ajStrNewC("1");
    entryAuto->file = ajStrNewC("auto");
    ajTablePut(namVarMasterTable, ajCharNewC("emboss_axis2c"),
               entryAuto);
    entryAuto = NULL;
#endif

#if defined(HAVE_MYSQL) || defined(HAVE_POSTGRESQL)
    AJNEW0(entryAuto);
    entryAuto->name = ajStrNewC("emboss_sql");
    entryAuto->value = ajStrNewC("1");
    entryAuto->file = ajStrNewC("auto");
    ajTablePut(namVarMasterTable, ajCharNewC("emboss_sql"),
               entryAuto);
    entryAuto = NULL;

#ifdef HAVE_MYSQL
    AJNEW0(entryAuto);
    entryAuto->name = ajStrNewC("emboss_mysql");
    entryAuto->value = ajStrNewC("1");
    entryAuto->file = ajStrNewC("auto");
    ajTablePut(namVarMasterTable, ajCharNewC("emboss_mysql"),
               entryAuto);
    entryAuto = NULL;
#endif
#ifdef HAVE_POSTGRESQL
    AJNEW0(entryAuto);
    entryAuto->name = ajStrNewC("emboss_postgresql");
    entryAuto->value = ajStrNewC("1");
    entryAuto->file = ajStrNewC("auto");
    ajTablePut(namVarMasterTable, ajCharNewC("emboss_postgresql"),
               entryAuto);
    entryAuto = NULL;
#endif
#endif

    /*
    ** for each type of file read it and save the values 
    ** Start at system level then go to user
    */

    /*
    ** local prefixRoot is the root directory 
    ** it is the value of (PREFIX)_ROOT (if set) or namFixedRoot
    */
    
    ajStrAssignC(&debugStr, prefix);
    
    ajStrAppendC(&debugStr, "_namdebug");
    ajStrFmtUpper(&debugStr);
    
    if(ajNamGetenvS(debugStr, &debugVal))
	ajStrToBool(debugVal, &namDoDebug);
    
    ajStrAssignC(&debugStr, prefix);
    
    ajStrAppendC(&debugStr, "_namvalid");
    ajStrFmtUpper(&debugStr);
    
    if(ajNamGetenvS(debugStr, &debugVal))
	ajStrToBool(debugVal, &namDoValid);
    
    ajStrDel(&debugStr);
    ajStrDel(&debugVal);
    
    ajStrAssignC(&prefixStr, prefix);
    
    ajStrAppendC(&prefixStr, "_ROOT");
    ajStrFmtUpper(&prefixStr);
    
    ajStrAppendC(&prefixCap, prefix);
    ajStrFmtUpper(&prefixCap);

    root_defined = ajNamGetenvS(prefixStr, &prefixRootStr);

    if(!root_defined && is_windows)
	ajDie("EMBOSS_ROOT must be defined for Windows");

    if(root_defined)
    {
	prefixRoot = ajStrGetPtr(prefixRootStr);

#ifdef WIN32
        /* namInstallRoot undefined for Windows ... use required EMBOSS_ROOT */

        ajStrAssignS(&tmpstr, prefixRootStr);
	ajStrAppendC(&tmpstr, "\\apps\\Release");

        if(ajFilenameExistsDir(tmpstr))
            strcpy(namInstallRoot, ajStrGetPtr(tmpstr));
        else
            strcpy(namInstallRoot,prefixRoot);

        ajStrDel(&tmpstr);
#endif
    }
    else
	prefixRoot = namFixedRoot;
    
    /* namFixedBaseStr is the directory above the source root */
    
    ajStrAssignC(&namFixedRootStr, prefixRoot);
    ajStrAssignS(&namFixedBaseStr, namFixedRootStr);
#ifndef WIN32
    ajDirnameUp(&namFixedBaseStr);
#endif

    ajStrAssignC(&namFixedPackageStr, namPackage);
    ajStrAssignC(&namFixedSystemStr, namSystem);
    ajStrAssignC(&namFixedVersionStr, namVersion);
    if(ajStrGetLen(namFixedVersionStr) < 7)
        ajStrAppendC(&namFixedVersionStr, ".0");

    ajStrAssignC(&namFixedInstallStr, namInstallRoot);

    if(is_windows)
    {
        ajFmtPrintS(&namStandardDir, "%s",
                    namInstallRoot);
    }
    else
    {
        ajFmtPrintS(&namStandardDir, "%s%sshare%s%S",
                    namInstallRoot, SLASH_STRING, SLASH_STRING, prefixCap);
    }
    ajFmtPrintS(&namStandardFilename, "%S%s%s.standard",
                namStandardDir, SLASH_STRING, prefix);
    prefixRootFile = ajFileNewInNameS(namStandardFilename);
    ajStrAssignC(&basename, "standard");

    if(!prefixRootFile)
    {
	/* try original directory */
        ajStrAssignC(&namStandardDir, prefixRoot);
	    ajFmtPrintS(&namStandardFilename, "%s%s%s.standard", prefixRoot,
			SLASH_STRING,prefix);

	prefixRootFile = ajFileNewInNameS(namStandardFilename);
    }

    if(prefixRootFile)
    {
        AJNEW0(entryAuto);
        entryAuto->name = ajStrNewC("emboss_standard");
        entryAuto->value = ajStrNewS(namStandardDir);
        entryAuto->file = ajStrNewRef(namStandardFilename);
        ajTablePut(namVarMasterTable, ajCharNewC("emboss_standard"),
                   entryAuto);
        entryAuto = NULL;

	ajStrAppendC(&namFileOrig, "(OK)");
	namProcessFile(prefixRootFile, basename);
	ajFileClose(&prefixRootFile);

        if(namFileOrig)
            ajStrAppendC(&namFileOrig, ", ");

        ajStrAppendS(&namFileOrig, namStandardFilename);
    }
    else
	ajStrAppendC(&namFileOrig, "(failed)");

    /*
    ** EMBOSS is distributed with an emboss.standard file
    ** Looks for this file and set EMBOSS_STANDARD variable to its directory
    */

    /*
    ** look for the site's default file in the install directory as
       /share/PREFIX/emboss.default
    **
    */
    
    ajFmtPrintS(&namRootStr, "%s%sshare%s%S%s%s.default",
		 namInstallRoot, SLASH_STRING, SLASH_STRING,
                prefixCap, SLASH_STRING, prefix);
    prefixRootFile = ajFileNewInNameS(namRootStr);
    ajStrAssignC(&basename, "global");
    
    /* look for $(PREFIX)_ROOT/../emboss.default */
    
    if(!prefixRootFile)
    {
	/* try original directory */
	    ajFmtPrintS(&namRootStr, "%s%s%s.default", prefixRoot,
			SLASH_STRING,prefix);

	prefixRootFile = ajFileNewInNameS(namRootStr);
	ajStrAssignC(&basename, "source");
    }
    
    if(namFileOrig)
	ajStrAppendC(&namFileOrig, ", ");

    ajStrAppendS(&namFileOrig, namRootStr);
    
    if(prefixRootFile)
    {
	ajStrAppendC(&namFileOrig, "(OK)");
	namProcessFile(prefixRootFile, basename);
	ajFileClose(&prefixRootFile);
    }
    else
	ajStrAppendC(&namFileOrig, "(failed)");
    
    
    
    /* look for .embossrc in an arbitrary directory */
    
    prefixRoot= getenv("EMBOSSRC");
    
    if(prefixRoot)
    {
	ajStrAssignC(&namRootStr, prefixRoot);
	ajStrAppendC(&namRootStr, SLASH_STRING);
	ajStrAppendC(&namRootStr, ".");
	ajStrAppendC(&namRootStr, prefix);
	ajStrAppendC(&namRootStr, "rc");

	if(namFileOrig)
	    ajStrAppendC(&namFileOrig, ", ");

	ajStrAppendS(&namFileOrig, namRootStr);

	prefixRootFile = ajFileNewInNameS(namRootStr);

	if(prefixRootFile)
	{
	    ajStrAssignC(&basename, "special");
	    ajStrAppendC(&namFileOrig, "(OK)");
	    namProcessFile(prefixRootFile, basename);
	    ajFileClose(&prefixRootFile);
	}
	else
	    ajStrAppendC(&namFileOrig, "(failed)");
    }
    

    /*
    ** look for $HOME/.embossrc
    **
    ** Note that this will not work with Windows as there is
    ** no concept of HOME
    */
    
    prefixRoot = ajSysGetHomedir();
    
    ajStrAssignC(&namUserDir, prefixRoot);
    ajStrAppendC(&namUserDir, SLASH_STRING);
    ajStrAppendC(&namUserDir, ".embossdata");
    ajStrAppendC(&namUserDir, SLASH_STRING);
    
    ajStrAssignC(&prefixStr, prefix);
    ajStrAppendC(&prefixStr, "_RCHOME");
    ajStrFmtUpper(&prefixStr);
    
    if(ajNamGetenvS(prefixStr, &homercVal))
	ajStrToBool(homercVal, &namDoHomeRc);

    ajStrDel(&homercVal);
    
    if(namDoHomeRc && prefixRoot)
    {
	ajStrAssignC(&namRootStr, prefixRoot);
	ajStrAppendC(&namRootStr, SLASH_STRING);
	ajStrAppendC(&namRootStr, ".");
	ajStrAppendC(&namRootStr, prefix);
	ajStrAppendC(&namRootStr, "rc");

	if(namFileOrig)
	    ajStrAppendC(&namFileOrig, ", ");

	ajStrAppendS(&namFileOrig, namRootStr);

	ajStrAssignC(&basename, "user");
	prefixRootFile = ajFileNewInNameS(namRootStr);

	if(prefixRootFile)
	{
	    ajStrAppendC(&namFileOrig, "(OK)");
	    namProcessFile(prefixRootFile, basename);
	    ajFileClose(&prefixRootFile);
	}
	else
	    ajStrAppendC(&namFileOrig, "(failed)");
    }
    
    namUser("Files processed: %S\n", namFileOrig);

    ajStrDel(&prefixRootStr);
    ajStrDel(&basename);
    ajStrDel(&prefixStr);
    ajStrDel(&prefixCap);

    if(!namFixedSystemStr)
        namFixedSystemStr = ajStrNewC(namSystem);
    
    if(!namFixedVersionStr)
    {
        namFixedVersionStr = ajStrNewC(namVersion);
        if(ajStrGetLen(namFixedVersionStr) < 7)
            ajStrAppendC(&namFixedVersionStr, ".0");
    }

    if(namErrorCount)		/* test: badsummary.rc */
	ajDie("Error(s) in configuration files");

    return;
}
ID namNoColon
TY static
MO ajnam
LB core
XX
DE Remove any trailing colon ':' in the input string.
XX
PN [1]
PA u thys AjPStr*
PD String.
PX
RT void
RD 
RX
//


static void namNoColon(AjPStr* thys)
{
    if(ajStrGetCharLast(*thys) == ':')
	ajStrCutEnd(thys, 1);

    return;
}
ID namDbAttrC
TY static
MO ajnam
LB core
XX
DE Return the index for a database attribute name.
XX
PN [1]
PA r str const char*
PD Attribute name.
PX
RT ajint
RD Index in namDbAttrs, or -1 on failure.
RX
//


static ajint namDbAttrC(const char* str)
{
    ajint i = 0;
    ajint j = 0;
    ajint ifound = 0;

    ajStrAssignC(&namCmpStr, str);
    ajStrFmtLower(&namCmpStr);

    for(i=0; namDbAttrs[i].Name; i++)
    {
	if(ajStrMatchC(namCmpStr, namDbAttrs[i].Name))
	    return i;

	if(ajCharPrefixS(namDbAttrs[i].Name, namCmpStr))
	{
	    ifound++;
	    j = i;
	}
    }

    if(ifound == 1)
	return j;

    return -1;
}
ID namDbAttrS
TY static
MO ajnam
LB core
XX
DE Return the index for a database attribute name.
XX
PN [1]
PA r thys const AjPStr
PD Attribute name.
PX
RT ajint
RD Index in namDbAttrs, or -1 on failure.
RX
//


static ajint namDbAttrS(const AjPStr thys)
{
    return namDbAttrC(ajStrGetPtr(thys));
}
ID namSvrAttrC
TY static
MO ajnam
LB core
XX
DE Return the index for a server attribute name.
XX
PN [1]
PA r str const char*
PD Attribute name.
PX
RT ajint
RD Index in namSvrAttrs, or -1 on failure.
RX
//


static ajint namSvrAttrC(const char* str)
{
    ajint i = 0;
    ajint j = 0;
    ajint ifound = 0;

    ajStrAssignC(&namCmpStr, str);
    ajStrFmtLower(&namCmpStr);

    for(i=0; namSvrAttrs[i].Name; i++)
    {
	if(ajStrMatchC(namCmpStr, namSvrAttrs[i].Name))
	    return i;

	if(ajCharPrefixS(namSvrAttrs[i].Name, namCmpStr))
	{
	    ifound++;
	    j = i;
	}
    }

    if(ifound == 1)
	return j;

    return -1;
}
ID namSvrAttrS
TY static
MO ajnam
LB core
XX
DE Return the index for a server attribute name.
XX
PN [1]
PA r thys const AjPStr
PD Attribute name.
PX
RT ajint
RD Index in namSvrAttrs, or -1 on failure.
RX
//


static ajint namSvrAttrS(const AjPStr thys)
{
    return namSvrAttrC(ajStrGetPtr(thys));
}
ID namRsAttrC
TY static
MO ajnam
LB core
XX
DE Return the index for a resource attribute name.
XX
PN [1]
PA r str const char*
PD Attribute name.
PX
RT ajint
RD Index in namRsAttrs, or -1 on failure.
RX
//


static ajint namRsAttrC(const char* str)
{
    ajint i = 0;
    ajint j = 0;
    ajint ifound = 0;

    ajStrAssignC(&namCmpStr, str);
    ajStrFmtLower(&namCmpStr);


    for(i=0; namRsAttrs[i].Name; i++)
    {
	if(ajStrMatchC(namCmpStr, namRsAttrs[i].Name))
	    return i;

	if(ajCharPrefixS(namRsAttrs[i].Name, namCmpStr))
	{
	    ifound++;
	    j = i;
	}
    }

    if(ifound == 1)
	return j;

    return -1;
}
ID namRsAttrS
TY static
MO ajnam
LB core
XX
DE Return the index for a resource attribute name.
XX
PN [1]
PA r thys const AjPStr
PD Attribute name.
PX
RT ajint
RD Index in namRsAttrs, or -1 on failure.
RX
//


static ajint namRsAttrS(const AjPStr thys)
{
    return namRsAttrC(ajStrGetPtr(thys));
}
ID namRsAttrFieldC
TY static
MO ajnam
LB core
XX
DE Return the validity of a resource attribute name using a known field prefix
XX
PN [1]
PA r rstable const AjPTable
PD Resource table
PX
PN [2]
PA r str const char*
PD Attribute name.
PX
RT AjBool
RD Attribute name matches a defined field and property
RX
//


static AjBool namRsAttrFieldC(const AjPTable rstable, const char* str)
{
    AjBool ret = ajFalse;
    AjPStr prefix = NULL;
    const AjPStr fields = NULL;
    AjPStrTok handle = NULL;
    AjPStr field = NULL;
    const AjPStr type = NULL;
    const AjPList fdlist = NULL;
    AjIList iter = NULL;
    const NamPAttr attr = NULL;

    type = ajTableFetchmodC(rstable, "type");

    if(!ajStrMatchC(type, "Index"))
        return ajFalse;

    if(ajCharSuffixC(str, "len"))
        ajStrAssignSubC(&prefix, str, 0, -4);
    else if(ajCharSuffixC(str, "seccachesize"))
        ajStrAssignSubC(&prefix, str, 0, -13);
    else if(ajCharSuffixC(str, "cachesize"))
        ajStrAssignSubC(&prefix, str, 0, -10);
    else if(ajCharSuffixC(str, "secpagesize"))
        ajStrAssignSubC(&prefix, str, 0, -12);
    else if(ajCharSuffixC(str, "pagesize"))
        ajStrAssignSubC(&prefix, str, 0, -9);
    else
        return ajFalse;

    if(ajStrGetLen(prefix))
    {
        fields = ajTableFetchmodC(rstable, "fields");

        if(fields)
        {
            handle = ajStrTokenNewC(fields, " ,;");

            while(ajStrTokenNextParse(&handle, &field))
                if(ajStrMatchS(prefix, field))
                    ret = ajTrue;
        }
        else
        {
            fdlist = ajTableFetchC(rstable, "field");

            if(fdlist)
            {
                iter = ajListIterNewread(fdlist);

                while(!ajListIterDone(iter))
                {
                    fields = ajListIterGet(iter);
                    ajStrTokenAssignC(&handle, fields, " ,;");

                    while(ajStrTokenNextParse(&handle, &field))
                    {
                        if(ajStrMatchC(field, "!"))
                            break;

                        if(ajStrMatchS(prefix, field))
                            ret = ajTrue;
                    }
                }

                ajListIterDel(&iter);
            }
            else 
            {
                attr = ajTableFetchC(namResAttrTable, "fields");
                handle = ajStrTokenNewcharC(attr->Defval, " ,;");

                while(ajStrTokenNextParse(&handle, &field))
                    if(ajStrMatchS(prefix, field))
                        ret = ajTrue;
            }
        }

        ajStrTokenDel(&handle);
        ajStrDel(&field);
    }

    ajStrDel(&prefix);

    return ret;
}
ID namRsAttrFieldS
TY static
MO ajnam
LB core
XX
DE Return the validity of a resource attribute name using a known field prefix
XX
PN [1]
PA r rstable const AjPTable
PD Resource table
PX
PN [2]
PA r str const AjPStr
PD Attribute name.
PX
RT AjBool
RD Attribute name matches a defined field and property
RX
//


static AjBool namRsAttrFieldS(const AjPTable rstable, const AjPStr str)
{
    AjBool ret = ajFalse;
    AjPStr prefix = NULL;
    const AjPStr fields = NULL;
    AjPStrTok handle = NULL;
    AjPStr field = NULL;
    const AjPStr type = NULL;
    AjPList fdlist = NULL;
    AjIList iter = NULL;
    const NamPAttr attr = NULL;

    type = ajTableFetchC(rstable, "type");

    if(!ajStrMatchC(type, "Index"))
        return ajFalse;

    if(ajStrSuffixC(str, "len"))
        ajStrAssignSubS(&prefix, str, 0, -4);
    else if(ajStrSuffixC(str, "seccachesize"))
        ajStrAssignSubS(&prefix, str, 0, -13);
    else if(ajStrSuffixC(str, "cachesize"))
        ajStrAssignSubS(&prefix, str, 0, -10);
    else if(ajStrSuffixC(str, "secpagesize"))
        ajStrAssignSubS(&prefix, str, 0, -12);
    else if(ajStrSuffixC(str, "pagesize"))
        ajStrAssignSubS(&prefix, str, 0, -9);
    else
        return ajFalse;

    if(ajStrGetLen(prefix))
    {
        fields = ajTableFetchC(rstable, "fields");

        if(fields)
        {
            handle = ajStrTokenNewC(fields, " ,;");

            while(ajStrTokenNextParse(&handle, &field))
                if(ajStrMatchS(prefix, field))
                    ret = ajTrue;
        }
        else
        {
            fdlist = ajTableFetchmodC(rstable, "field");

            if(fdlist)
            {
                iter = ajListIterNewread(fdlist);

                while(!ajListIterDone(iter))
                {
                    fields = ajListIterGet(iter);
                    ajStrTokenAssignC(&handle, fields, " ,;");

                    while(ajStrTokenNextParse(&handle, &field))
                    {
                        if(ajStrMatchC(field, "!"))
                            break;

                        if(ajStrMatchS(prefix, field))
                            ret = ajTrue;
                    }
                }

                ajListIterDel(&iter);
            }
            else
            {
                attr = ajTableFetchC(namResAttrTable, "fields");

                if(attr)
                {
                    handle = ajStrTokenNewcharC(attr->Defval, " ,;");

                    while(ajStrTokenNextParse(&handle, &field))
                        if(ajStrMatchS(prefix, field))
                            ret = ajTrue;
                }
                
            }
            
        }
        


        ajStrTokenDel(&handle);
        ajStrDel(&field);
    }

    ajStrDel(&prefix);

    return ret;
}
ID ajNamExit
TY public
MO ajnam
LB core
XX
DE Delete the initialisation values in the table.
XX
RT void
RD 
RX
//


void ajNamExit(void)
{
    namListMasterDelete(namVarMasterTable, TYPE_ENV); /* Delete elements */
    ajTableFree(&namVarMasterTable); /* free table and database structures */
    namListMasterDelete(namAliasMasterTable, TYPE_ALIAS); /* Delete elements */
    ajTableFree(&namAliasMasterTable); /* free table and database structures */
    namListMasterDelete(namSvrMasterTable, TYPE_SVR); /* Delete elements */
    ajTableFree(&namSvrMasterTable); /* free table and database structures */
    namListMasterDelete(namDbMasterTable, TYPE_DB); /* Delete elements */
    ajTableFree(&namDbMasterTable); /* free table and database structures */
    namListMasterDelete(namResMasterTable, TYPE_RESOURCE);/* Delete elements */
    ajTableFree(&namResMasterTable); /* free table and database structures */
    namListMasterDelete(namSvrDatabaseTable, TYPE_SVRDB);/* Delete elements */
    ajTableFree(&namSvrDatabaseTable); /* free table and database structures */
    namListMasterDelete(namSvrAliasTable, TYPE_SVRALI);/* Delete elements */
    ajTableFree(&namSvrAliasTable); /* free table and database structures */

    ajTablestrFreeKey(&namDbTypeTable); /* free table and strings */

    ajTablestrFree(&namSvrAttrTable); /* free table and strings */
    ajTablestrFree(&namDbAttrTable); /* free table and strings */
    ajTablestrFree(&namResAttrTable); /* free table and strings */

    ajStrDel(&namFixedBaseStr);    /* allocated in ajNamInit */
    ajStrDel(&namFixedRootStr);    /* allocated in ajNamInit */
    ajStrDel(&namFixedInstallStr); /* allocated in ajNamInit */
    ajStrDel(&namFixedPackageStr); /* allocated in ajNamInit */
    ajStrDel(&namFixedSystemStr);  /* allocated in ajNamInit */
    ajStrDel(&namFixedVersionStr); /* allocated in ajNamInit */
    ajStrDel(&namPrefixStr);       /* allocated in ajNamInit */
    ajStrDel(&namFileOrig);        /* allocated in ajNamInit */
    ajStrDel(&namRootStr);         /* allocated in ajNamInit */
    
    ajStrDel(&namFileName);		/* allocated in ajNamProcessFile */
    ajStrDel(&namValNameTmp);
    ajStrDel(&namCmpStr);
    ajStrDel(&namStandardDir);
    ajStrDel(&namStandardFilename);
    ajStrDel(&namUserDir);
    ajStrDel(&namUserFilename);

    ajRegFree(&namNameExp);
    ajRegFree(&namVarExp);

    ajListFree(&namIfList);

    ajDebug("ajNamExit done\n");

    return;
}
ID ajNamSvrTest
TY public
MO ajnam
LB core
XX
DE Looks for a server name in the known definitions.
XX
PN [1]
PA r svrname const AjPStr
PD Server name.
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajNamSvrTest(const AjPStr svrname)
{
    const NamPEntry data;

    data = ajTableFetchS(namSvrMasterTable, svrname);

    if(!data)
	return ajFalse;

    return ajTrue;
}
ID ajNamSvrGetUrl
TY public
MO ajnam
LB core
XX
DE Gets the URL definition for a server definition.
XX
PN [1]
PA r svrname const AjPStr
PD Server name.
PX
PN [2]
PA w url AjPStr*
PD URL returned.
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamSvrGetUrl(const AjPStr svrname, AjPStr* url)
{

    const NamPEntry data;
    AjPTable svrtable;
    const AjPStr svrval = NULL;

    data = ajTableFetchS(namSvrMasterTable, svrname);

    if(!data)
	ajFatal("%S is not a known server", svrname);

    svrtable = (AjPTable) data->data;

    svrval = ajTableFetchC(svrtable, "url");

    if(ajStrGetLen(svrval))
    {
	ajStrAssignS(url, svrval);

	return ajTrue;
    }

    return ajFalse;
}
ID ajNamQueryGetDatatypeC
TY public
MO ajnam
LB core
XX
DE Gets the datatype matching a query, and updates the datatype in the query
XX
PN [1]
PA r qry const AjPQuery
PD Database query
PX
RT const char*
RD Database type
RX
//


const char* ajNamQueryGetDatatypeC(const AjPQuery qry)
{
    ajuint i;
    const char* ret = NULL;

    if(ajStrGetLen(qry->DbType))
        return MAJSTRGETPTR(qry->DbType);

    for(i=0; namDbTypes[i].Name; i++)
        if(namDbTypes[i].DataType == qry->DataType)
            ret = namDbTypes[i].Name;

    return ret;
}
ID ajNamQueryGetUrl
TY public
MO ajnam
LB core
XX
DE Gets the URL definition for a server definition.
XX
PN [1]
PA r qry const AjPQuery
PD Database query
PX
RT const AjPStr
RD URL value
RX
//


const AjPStr ajNamQueryGetUrl(const AjPQuery qry)
{
    const AjPStr ret = NULL;
    const AjPStr svrval = NULL;
    const AjPTable dbtable;

    dbtable = namQuerySvrdata(qry);

    svrval = ajTableFetchC(dbtable, "url");

    if(svrval)
        ret = svrval;

    dbtable = namQueryDbdata(qry);

    svrval = ajTableFetchC(dbtable, "url");

    if(svrval)
        ret = svrval;

    return ret;
}
ID namQuerySvrdata
TY static
MO ajnam
LB core
XX
DE Gets the URL definition for a server definition.
XX
PN [1]
PA r qry const AjPQuery
PD Query.
PX
RT const AjPTable
RD DatabaseServer attributes table
RX
//


static const AjPTable namQuerySvrdata(const AjPQuery qry)
{
    const NamPEntry data;

    if(!ajStrGetLen(qry->SvrName))
        return NULL;

    data = ajTableFetchS(namSvrMasterTable, qry->SvrName);

    if(!data)
	ajFatal("%S is not a known server", qry->SvrName);

    return (const AjPTable) data->data;

}
ID namQueryDbdata
TY static
MO ajnam
LB core
XX
DE Gets the data table for a query database.
XX
PN [1]
PA r qry const AjPQuery
PD Database query
PX
RT const AjPTable
RD Database attributes table
RX
//


static const AjPTable namQueryDbdata(const AjPQuery qry)
{
    const NamPEntry data;
    const NamPEntry sdata;
    const AjPTable sdbtable;

    if(ajStrGetLen(qry->SvrName))
    {
        sdata = ajTableFetchS(namSvrDatabaseTable, qry->SvrName);
        sdbtable = (const AjPTable) sdata->data;
        data = ajTableFetchS(sdbtable, qry->DbName);

        if(!data)
            ajFatal("%S is not a known database for server %S",
                    qry->DbName, qry->SvrName);
    }
    else
    {
        data = ajTableFetchS(namDbMasterTable, qry->DbName);

        if(!data)
            ajFatal("%S is not a known database", qry->DbName);
    }

    return (const AjPTable) data->data;
}
ID ajNamSvrGetDbalias
TY public
MO ajnam
LB core
XX
DE Gets alias names for a server's database.
XX
PN [1]
PA r svrname const AjPStr
PD Server name.
PX
PN [2]
PA w dbalias AjPStr*
PD Alias returned.
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamSvrGetDbalias(const AjPStr svrname, AjPStr* dbalias)
{
    const NamPEntry data;
    const AjPTable svrtable;
    const AjPStr svrval = NULL;

    data = ajTableFetchS(namSvrMasterTable, svrname);

    if(!data)
	ajFatal("%S is not a known server", svrname);

    svrtable = (AjPTable) data->data;

    svrval = ajTableFetchC(svrtable, "dbalias");

    if(ajStrGetLen(svrval))
	ajStrAssignS(dbalias, svrval);
    else
    {
        ajStrAssignS(dbalias, svrname);
        ajStrFmtLower(dbalias);
    }

    return ajTrue;
}
ID ajNamSvrData
TY public
MO ajnam
LB core
XX
DE Given a query with server name, database name and search fields,
DE fill in the common fields. The query fields are set later.
DE
DE This part of the database definition is required (specifically
DE the "fields" definition) for setting the query details.
DE
DE See also ajNamSvrQuery, which calls this function if the common
DE query data is not yet set.
XX
PN [1]
PA u qry AjPQuery
PD Query structure with at least
PD dbname filled in
PX
PN [2]
PA r argc ajuint
PD Number of additional attribute name/value pairs
PX
PN [3]
PA v vararg ...
PD Variable length argument list
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamSvrData(AjPQuery qry, ajuint argc, ...)
{

    const NamPEntry data;
    const NamPEntry dbdata;

    const AjPTable svrtable;
    const AjPTable sdbtable;
    const AjPTable dbtable;
    AjPStrTok handle = NULL;
    AjPStr liststr = NULL;
    AjPStr token = NULL;
    ajuint i;
    const char* varAttrName;
    AjPStr* varAttrValue;
    va_list ap;
    const NamPType namtype;
    AjBool ok = ajTrue;
    AjEDataType testDataType = AJDATATYPE_UNKNOWN;
    AjPStr saveType = NULL;
    AjBool donecaseid = ajFalse;
    AjBool donehasacc = ajFalse;
    AjBool caseid = ajFalse;
    AjBool hasacc = ajFalse;

    if(qry->SetServer)
        return ajTrue;

    qry->SetServer = ajTrue;

    data = ajTableFetchS(namSvrMasterTable, qry->SvrName);

    if(!data)
	ajFatal("server %S unknown", qry->SvrName);

    svrtable = (const AjPTable) data->data;

    /* general defaults */

    namSvrSetAttrStrC(svrtable, "type", &liststr);
    ajStrTokenAssignC(&handle, liststr, " ,;");
    ajStrAssignC(&qry->DbType, "");

    ajDebug("Server type: '%S'\n", liststr);

    while(ajStrTokenNextParse(&handle, &token))
    {
        if(!ajStrSuffixCaseC(token,"features"))
        {
            if(ajStrPrefixCaseC(token, "n"))
        	ajStrAssignC(&token, "Nucleotide");
            else if(ajStrPrefixCaseC(token, "p"))
        	ajStrAssignC(&token, "Protein");
        }

        namtype = ajTableFetchS(namDbTypeTable, token);

        ajDebug("testing type '%S' namtype: '%s' \n",
		token, namtype->Name);

        if(!namtype)
        {
            ajErr("Server %S type '%S' unknown", qry->SvrName, token);
            continue;
        }

        ajDebug("test server type '%S' qry datatype %u nam "
                "datatype %u unk %u\n",
                token, qry->DataType, namtype->DataType, AJDATATYPE_UNKNOWN);

        if(qry->DataType == AJDATATYPE_UNKNOWN) /* take first available */
        {
            if(testDataType == AJDATATYPE_UNKNOWN ||
               namtype->DataType == AJDATATYPE_TEXT)
            {
                testDataType = namtype->DataType;
                ajStrAssignS(&saveType, token);
                ajDebug("Save type %d '%S'\n", testDataType, saveType);
            }
        }

        if(namtype->DataType == qry->DataType)
        {
            ajStrAssignS(&qry->DbType, token);
            break;
        }
    }
    
    if(ajStrGetLen(qry->Formatstr))
    {
        if(ajStrGetLen(qry->DbType) &&
           !ajStrMatchC(qry->DbType, "unknown") &&
           !namInformatTest(qry->Formatstr, qry->DbType))
        {
            ajErr("Query format '%S' does not support datatype '%S'",
                  qry->Formatstr, qry->DbType);
            ajStrDel(&saveType);
            return ajFalse;
        }
    }
    else
    {
        if(namSvrSetAttrStrC(svrtable, "format", &liststr))
        {
            ok = ajFalse;
            ajStrTokenAssignC(&handle, liststr, " ,;");

            while(ajStrTokenNextParse(&handle, &token))
            {
                if(namInformatTest(token, qry->DbType))
                {
                    ok = ajTrue;
                    ajStrAssignS(&qry->Formatstr, token);
                }
                if(saveType && namInformatTest(token, saveType))
                {
                    ok = ajTrue;
                    ajStrAssignS(&qry->Formatstr, token);
                }
            }

            if(!ok)
            {
                ajErr("Server %S format(s) '%S' do not support "
                      "datatype '%S'",
                      qry->SvrName, liststr, qry->DbType);
                ajStrDel(&saveType);
                return ajFalse;
            }
        }
    }
    
    namSvrSetAttrStrC(svrtable, "method", &qry->Method);
    namSvrSetAttrStrC(svrtable, "indexdirectory", &qry->IndexDir);
    namSvrSetAttrStrC(svrtable, "indexdirectory", &qry->Directory);
    namSvrSetAttrStrC(svrtable, "field", &qry->DbFields);
    namSvrSetAttrStrC(svrtable, "url", &qry->DbUrl);
    namSvrSetAttrStrC(svrtable, "proxy", &qry->DbProxy);
    namSvrSetAttrStrC(svrtable, "httpversion", &qry->DbHttpVer);
    namSvrSetAttrStrC(svrtable, "serverversion", &qry->ServerVer);
    if(namSvrSetAttrBoolC(svrtable, "caseidmatch", &caseid))
    {
        donecaseid = ajTrue;
        qry->CaseId = caseid;
    }
    if(namSvrSetAttrBoolC(svrtable, "hasaccession", &hasacc))
    {
        donehasacc = ajTrue;
        qry->HasAcc = hasacc;
    }

#ifdef WIN32
    ajStrExchangeKK(&qry->Directory, '/', '\\');
    if(ajStrPrefixC(qry->Directory, ".\\"))
        ajStrCutStart(&qry->Directory, 2);
    ajStrExchangeKK(&qry->IndexDir, '/', '\\');
    if(ajStrPrefixC(qry->IndexDir, ".\\"))
        ajStrCutStart(&qry->IndexDir, 2);
#endif

    if(argc)
    {
        va_start(ap, argc);

        for(i=0; i < argc; i++)
        {
            varAttrName = va_arg(ap, const char*);
            varAttrValue = va_arg(ap, AjPStr*);
            namSvrSetAttrStrC(svrtable, varAttrName, varAttrValue);
        }

        va_end(ap);
    }

    ajDebug("ajNamSvrQuery SvrName '%S'\n", qry->SvrName);
    ajDebug("    DbName      '%S'\n", qry->DbName);
    ajDebug("    DbType      '%S'\n", qry->DbType);
    ajDebug("    Method      '%S'\n", qry->Method);
    ajDebug("    Formatstr   '%S'\n", qry->Formatstr);
    ajDebug("    IndexDir    '%S'\n", qry->IndexDir);
    ajDebug("    Directory   '%S'\n", qry->Directory);

    if(ajStrGetLen(qry->DbName))
    {
        data = ajTableFetchS(namSvrDatabaseTable, qry->SvrName);

        if(!data)
        {
            ajWarn("server %S has no databases", qry->SvrName);
            ajStrDel(&saveType);
            return ajFalse;
        }
    
        sdbtable = (const AjPTable) data->data;
        dbdata = ajTableFetchS(sdbtable, qry->DbName);

        if(!dbdata)
            ajFatal("server %S has no database %S", qry->SvrName, qry->DbName);

        dbtable = (const AjPTable) dbdata->data;

        /* general defaults */

        if(namDbSetAttrStrC(dbtable, "type", &liststr))
        {
            ajDebug("db type '%S'\n", liststr);

            ajStrTokenAssignC(&handle, liststr, " ,;");
            ajStrAssignC(&qry->DbType, "");

            while(ajStrTokenNextParse(&handle, &token))
            {
                if(!ajStrSuffixCaseC(token,"features"))
                {
                    if(ajStrPrefixCaseC(token, "n"))
                        ajStrAssignC(&token, "Nucleotide");
                    else if(ajStrPrefixCaseC(token, "p"))
                        ajStrAssignC(&token, "Protein");
                }

                ajDebug("test token '%S' list '%S'\n", token, liststr);

                namtype = ajTableFetchS(namDbTypeTable, token);

                if(!namtype)
                {
                    ajErr("Database %S type '%S' unknown", qry->DbName, token);
                    continue;
                }

                ajDebug("qry->DataType %u namtype->DataType %u type '%s'\n",
                        qry->DataType, namtype->DataType, namtype->Name);

                if(qry->DataType == AJDATATYPE_UNKNOWN) /* take first type */
                {
                    if(testDataType == AJDATATYPE_UNKNOWN ||
                       namtype->DataType == AJDATATYPE_TEXT)
                    {
                        testDataType = namtype->DataType;
                        ajStrAssignS(&saveType, token);
                        ajDebug("Save type %d '%S'\n", testDataType, saveType);
                    }
                }

                if(namtype->DataType == qry->DataType)
                {
                    ajStrAssignS(&qry->DbType, token);
                    break;
                }
            }

        }

        if(qry->DataType == AJDATATYPE_UNKNOWN)
        {
            qry->DataType = testDataType;
            ajStrAssignS(&qry->DbType, saveType);
        }

        if(!ajStrGetLen(qry->DbType))
        {
            ajErr("Server %S database '%S' type(s) '%S' "
                  "no match to query type '%s'",
                  qry->SvrName, qry->DbName,
                  liststr, ajQueryGetDatatype(qry));
            ajStrDel(&saveType);
            return ajFalse;
        }

        if(ajStrGetLen(qry->Formatstr))
        {
            if(ajStrGetLen(qry->DbType) &&
               !ajStrMatchC(qry->DbType, "unknown") &&
               !namInformatTest(qry->Formatstr, qry->DbType))
            {
                ajErr("Query format '%S' does not support datatype '%S'",
                      qry->Formatstr, qry->DbType);
                ajStrDel(&saveType);
                return ajFalse;
            }
        }
        else if (ajStrGetLen(qry->DbType))
        {
            if(namDbSetAttrStrC(dbtable, "format", &liststr))
            {
                ok = ajFalse;
                ajStrTokenAssignC(&handle, liststr, " ,;");

                while(ajStrTokenNextParse(&handle, &token))
                {
                    if(namInformatTest(token, qry->DbType))
                    {
                        ok = ajTrue;
                        ajStrAssignS(&qry->Formatstr, token);
                        break;
                    }
                }

                if(!ok)
                {
                    ajErr("Server %S database %S format(s) '%S' "
			  "do not support datatype '%S'",
                          qry->SvrName, qry->DbName, liststr, qry->DbType);
                    ajStrDel(&saveType);
                    return ajFalse;
                }
            }
        }

        namDbSetAttrStrC(dbtable, "method", &qry->Method);
        namDbSetAttrStrC(dbtable, "app", &qry->Application);
        namDbSetAttrStrC(dbtable, "directory", &qry->IndexDir);
        namDbSetAttrStrC(dbtable, "indexdirectory", &qry->IndexDir);
        namDbSetAttrStrC(dbtable, "indexdirectory", &qry->Directory);
        namDbSetAttrStrC(dbtable, "directory", &qry->Directory);
        namDbSetAttrStrC(dbtable, "namespace", &qry->Namespace);
        namDbSetAttrStrC(dbtable, "organisms", &qry->Organisms);
        namDbSetAttrStrC(dbtable, "exclude", &qry->Exclude);
        namDbSetAttrStrC(dbtable, "filename", &qry->Filename);
        if(!namDbSetAttrStrC(dbtable, "field", &qry->DbFields))
            namDbSetAttrStrC(dbtable, "fields", &qry->DbFields);
        namDbSetAttrStrC(dbtable, "url", &qry->DbUrl);
        namDbSetAttrStrC(dbtable, "proxy", &qry->DbProxy);
        namDbSetAttrStrC(dbtable, "httpversion", &qry->DbHttpVer);
        namDbSetAttrStrC(dbtable, "serverversion", &qry->ServerVer);
        if(namDbSetAttrBoolC(dbtable, "caseidmatch", &caseid))
        {
            donecaseid = ajTrue;
            qry->CaseId = caseid;
        }
        if(namDbSetAttrBoolC(dbtable, "hasaccession", &hasacc))
        {
            donehasacc = ajTrue;
            qry->HasAcc = hasacc;
        }

        namDbSetAttrStrC(dbtable, "identifier", &qry->DbIdentifier);
        namDbSetAttrStrC(dbtable, "accession", &qry->DbAccession);
        namDbSetAttrStrC(dbtable, "filters", &qry->DbFilter);
        namDbSetAttrStrC(dbtable, "return", &qry->DbReturn);

        if(!donecaseid)
            qry->CaseId = caseid;
        if(!donehasacc)
            qry->HasAcc = hasacc;

#ifdef WIN32
	ajStrExchangeKK(&qry->Directory, '/', '\\');
	if(ajStrPrefixC(qry->Directory, ".\\"))
	  ajStrCutStart(&qry->Directory, 2);
	ajStrExchangeKK(&qry->IndexDir, '/', '\\');
	if(ajStrPrefixC(qry->IndexDir, ".\\"))
	  ajStrCutStart(&qry->IndexDir, 2);
#endif

        if(argc)
        {
            va_start(ap, argc);

            for(i=0; i < argc; i++)
            {
                varAttrName = va_arg(ap, const char*);
                varAttrValue = va_arg(ap, AjPStr*);
                namDbSetAttrStrC(dbtable, varAttrName, varAttrValue);
            }

            va_end(ap);
        }
    }

    ajDebug("ajNamSvrData '%S:%S' method:%S type:%S format:%S\n",
            qry->SvrName, qry->DbName, qry->Method, qry->DbType,
            qry->Formatstr);

    ajStrDel(&liststr);
    ajStrDel(&token);

    ajStrTokenDel(&handle);
    ajStrDel(&saveType);

    return ajTrue;
}
ID ajNamSvrQuery
TY public
MO ajnam
LB core
XX
DE Given a query with server name and search fields,
DE fill in the access method and some common fields according
DE to the query level.
XX
PN [1]
PA u qry AjPQuery
PD Query structure with at least
PD svrname filled in
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamSvrQuery(AjPQuery qry)
{
    const NamPEntry data;
    const AjPTable svrtable;

    data = ajTableFetchS(namSvrMasterTable, qry->SvrName);

    if(!data)
	ajFatal("server %S unknown", qry->SvrName);

    svrtable = (const AjPTable) data->data;

    if(!ajStrGetLen(qry->DbType))
	ajNamSvrData(qry, 0);

    if((qry->QueryType == AJQUERY_ALL) ||
       ((qry->QueryType == AJQUERY_UNKNOWN) && !ajQueryIsSet(qry)))
    {
        /* must have a method for all entries */
	namSvrSetAttrStrC(svrtable, "methodall", &qry->Method);
	namSvrSetAttrStrC(svrtable, "formatall", &qry->Formatstr);
	namSvrSetAttrStrC(svrtable, "appall", &qry->Application);
	qry->QueryType = AJQUERY_ALL;
    }
    else		      /* must be able to query the database */
    {
	namSvrSetAttrStrC(svrtable, "methodquery", &qry->Method);
	namSvrSetAttrStrC(svrtable, "formatquery", &qry->Formatstr);
	namSvrSetAttrStrC(svrtable, "appquery", &qry->Application);

	if((qry->QueryType != AJQUERY_QUERY) &&
           !ajQuerySetWild(qry)) /* ID - single entry may be available */
	{
	    namSvrSetAttrStrC(svrtable, "methodentry", &qry->Method);
	    namSvrSetAttrStrC(svrtable, "formatentry", &qry->Formatstr);
	    namSvrSetAttrStrC(svrtable, "appentry", &qry->Application);
	    qry->QueryType = AJQUERY_ENTRY;
	}
	else
	    qry->QueryType = AJQUERY_QUERY;
    }


    if(!ajStrGetLen(qry->Method))
    {
	ajErr("No access method for server '%S'",
              qry->SvrName);

	return ajFalse;
    }

    return ajTrue;
}
ID namSvrSetAttrStrC
TY static
MO ajnam
LB core
XX
DE Sets a named string attribute value from an attribute list.
XX
PN [1]
PA r svrtable const AjPTable
PD Attribute definitions table.
PX
PN [2]
PA r attrib const char*
PD Attribute name.
PX
PN [3]
PA w qrystr AjPStr*
PD Returned attribute value.
PX
RT AjBool
RD ajTrue on success.
RX
//


static AjBool namSvrSetAttrStrC(const AjPTable svrtable, const char* attrib,
                                AjPStr* qrystr)
{
    const void* svrval = NULL;
    const AjPStr strval = NULL;
    const NamPAttr attr = NULL;
    const AjPList fdlist = NULL;
    AjIList iter = NULL;
    AjPStrTok handle = NULL;
    AjPStr word = NULL;

    attr = ajTableFetchC(namSvrAttrTable, attrib);

    if(!attr)
        ajFatal("unknown attribute '%s' requested",  attrib);

    svrval = ajTableFetchC(svrtable, attrib);

    if(svrval)
    {
        switch(attr->Type)
        {
            case ATTR_LIST:
                ajStrAssignC(qrystr, "");
                fdlist = (const AjPList) svrval;
                iter = ajListIterNewread(fdlist);

                while(!ajListIterDone(iter))
                {
                    strval = ajListIterGet(iter);
                    ajStrTokenAssignC(&handle, strval, " ");

                    while(ajStrTokenNextParse(&handle, &word))
                    {
                        if(ajStrMatchC(word, "!"))
                            break;

                        if(ajStrGetLen(*qrystr))
                            ajStrAppendK(qrystr, ' ');

                        ajStrAppendS(qrystr, word);
                    }
                }

                ajStrTokenDel(&handle);
                ajStrDel(&word);
                ajListIterDel(&iter);
                break;

            default:
                if(!ajStrGetLen((const AjPStr) svrval))
                    return ajFalse;

                ajStrAssignS(qrystr, (const AjPStr) svrval);
                break;
        }
    }
    else 
    {
        if(!strlen(attr->Defval))
            return ajFalse;

        ajStrAssignC(qrystr, attr->Defval);
    }

    namVarResolve(qrystr);

    return ajTrue;
}
ID namSvrSetAttrBoolC
TY static
MO ajnam
LB core
XX
DE Sets a named boolean attribute value from an attribute list.
XX
PN [1]
PA r svrtable const AjPTable
PD Attribute definitions table
PX
PN [2]
PA r attrib const char*
PD Attribute name.
PX
PN [3]
PA w qrybool AjBool*
PD Returned attribute value.
PX
RT AjBool
RD ajTrue on success.
RX
//


static AjBool namSvrSetAttrBoolC(const AjPTable svrtable, const char* attrib,
                                 AjBool* qrybool)
{
    AjBool ret = ajTrue;
    AjPStr tmpstr = NULL;
    const AjPStr svrval = NULL;
    const char* txtval = NULL;
    const NamPAttr attrval = NULL;

    svrval = ajTableFetchC(svrtable, attrib);

    if(svrval)
        txtval = ajStrGetPtr(svrval);
    else 
    {
        attrval = ajTableFetchC(namSvrAttrTable, attrib);

        if(!attrval)
            ajFatal("unknown attribute '%s' requested",  attrib);

        txtval = attrval->Defval;
    }

    if(ajStrGetLen(svrval))
    {
	ajStrAssignS(&tmpstr, svrval);
	ret = ajTrue;
    }
    else
    {
	ajStrAssignC(&tmpstr, txtval);
	ret = ajFalse;
    }

    /* ajDebug("namSvrSetAttr('%S')\n", *qrystr); */

    namVarResolve(&tmpstr);
    ajStrToBool(tmpstr, qrybool);
    ajStrDel(&tmpstr);

    return ret;
}
ID ajNamDbTest
TY public
MO ajnam
LB core
XX
DE Looks for a database name in the known definitions.
XX
PN [1]
PA r dbname const AjPStr
PD Database name.
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajNamDbTest(const AjPStr dbname)
{
    const NamPEntry data;

    data = ajTableFetchS(namDbMasterTable, dbname);

    if(!data)
	return ajFalse;

    return ajTrue;
}
ID ajNamDbGetType
TY public
MO ajnam
LB core
XX
DE Gets the enumerated type for a database definition.
XX
PN [1]
PA r dbname const AjPStr
PD Database name.
PX
PN [2]
PA w itype ajuint*
PD Database enumerated type
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamDbGetType(const AjPStr dbname, ajuint *itype)
{
    AjBool ret = ajFalse;
    const NamPEntry data;
    const AjPTable dbtable;
    const AjPStr dbval = NULL;
    AjPStrTok handle = NULL;
    AjPStr token = NULL;
    const NamPType namtype;

    data = ajTableFetchS(namDbMasterTable, dbname);

    if(!data)
	ajFatal("%S is not a known database", dbname);

    dbtable = (const AjPTable) data->data;

    dbval = ajTableFetchC(dbtable, "type");
    ajStrTokenAssignC(&handle, dbval, " ,;");

    while(ajStrTokenNextParse(&handle, &token))
    {
        if(!ajStrSuffixCaseC(token,"features"))
        {
            if(ajStrPrefixCaseC(token, "n"))
        	ajStrAssignC(&token, "Nucleotide");
            else if(ajStrPrefixCaseC(token, "p"))
        	ajStrAssignC(&token, "Protein");
        }

        namtype = ajTableFetchS(namDbTypeTable, token);
        ajDebug("testing type '%S' namtype: '%s' \n",
		token, namtype->Name);

        *itype =  namtype->DataType;
        ret = ajTrue;
        break;
    }

    ajStrTokenDel(&handle);
    ajStrDel(&token);
    
    return ret;
}
ID ajNamDbGetUrl
TY public
MO ajnam
LB core
XX
DE Gets the URL definition for a database definition.
XX
PN [1]
PA r dbname const AjPStr
PD Database name.
PX
PN [2]
PA w url AjPStr*
PD URL returned.
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamDbGetUrl(const AjPStr dbname, AjPStr* url)
{

    const NamPEntry data;
    const AjPTable dbtable;
    const AjPStr dbval = NULL;

    data = ajTableFetchS(namDbMasterTable, dbname);

    if(!data)
	ajFatal("%S is not a known database", dbname);

    dbtable = (const AjPTable) data->data;

    dbval = ajTableFetchC(dbtable, "url");

    if(ajStrGetLen(dbval))
    {
	ajStrAssignS(url, dbval);

	return ajTrue;
    }

    if(ajStrGetLen(*url))
        return ajTrue;

    return ajFalse;
}
ID ajNamDbGetDbalias
TY public
MO ajnam
LB core
XX
DE Gets an alias name for a database.
XX
PN [1]
PA r dbname const AjPStr
PD Database name.
PX
PN [2]
PA w dbalias AjPStr*
PD Alias returned.
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamDbGetDbalias(const AjPStr dbname, AjPStr* dbalias)
{

    const NamPEntry data;
    const AjPTable dbtable;
    const AjPStr dbval = NULL;

    data = ajTableFetchS(namDbMasterTable, dbname);

    if(!data)
	ajFatal("%S is not a known database", dbname);

    dbtable = (const AjPTable) data->data;

    dbval = ajTableFetchC(dbtable, "dbalias");

    if(ajStrGetLen(dbval))
	ajStrAssignS(dbalias, dbval);
    else
    {
        ajStrAssignS(dbalias, dbname);
        ajStrFmtLower(dbalias);
    }

    return ajTrue;
}
ID ajNamDbGetIndexdir
TY public
MO ajnam
LB core
XX
DE Gets the index directory for a database.
XX
PN [1]
PA r dbname const AjPStr
PD Database name.
PX
PN [2]
PA w indexdir AjPStr*
PD Directory path returned.
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamDbGetIndexdir(const AjPStr dbname, AjPStr* indexdir)
{

   const  NamPEntry data;
    const AjPTable dbtable;
    const AjPStr dbval = NULL;

    data = ajTableFetchS(namDbMasterTable, dbname);

    if(!data)
	ajFatal("%S is not a known database", dbname);

    dbtable = (const AjPTable) data->data;

    dbval = ajTableFetchC(dbtable, "indexdirectory");

    if(ajStrGetLen(dbval))
    {
	ajStrAssignS(indexdir, dbval);
        return ajTrue;
    }

    dbval = ajTableFetchC(dbtable, "directory");

    if(ajStrGetLen(dbval))
    {
	ajStrAssignS(indexdir, dbval);
        return ajTrue;
    }

    return ajFalse;
}
ID ajNamQuerySetDbalias
TY public
MO ajnam
LB core
XX
DE Sets an alias name for a database query.
XX
PN [1]
PA u qry AjPQuery
PD Database query.
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamQuerySetDbalias(AjPQuery qry)
{
    const NamPEntry data;
    const NamPEntry sdata;
    const AjPTable dbtable;
    const AjPTable sdbtable;
    const AjPStr dbval = NULL;

    if(ajStrGetLen(qry->SvrName))
    {
	if(!ajStrGetLen(qry->DbType))
            ajNamSvrData(qry, 0);

        ajNamSvrQuery(qry);
        sdata = ajTableFetchS(namSvrDatabaseTable, qry->SvrName);
        sdbtable = (const AjPTable) sdata->data;
        data = ajTableFetchS(sdbtable, qry->DbName);

        if(!data)
            ajFatal("%S is not a known database for server %S",
                    qry->DbName, qry->SvrName);
    }
    else
    {
        data = ajTableFetchS(namDbMasterTable, qry->DbName);

        if(!data)
            ajFatal("%S is not a known database", qry->DbName);
    }

    dbtable = (const AjPTable) data->data;

    dbval = ajTableFetchC(dbtable, "dbalias");

    if(ajStrGetLen(dbval))
	ajStrAssignS(&qry->DbAlias, dbval);
    else
    {
        ajStrAssignS(&qry->DbAlias, qry->DbName);
        ajStrFmtLower(&qry->DbAlias);
    }

    return ajTrue;
}
ID ajNamDbData
TY public
MO ajnam
LB core
XX
DE Given a query with database name and search fields,
DE fill in the common fields. The query fields are set later.
DE
DE This part of the database definition is required (specifically
DE the "fields" definition) for setting the query details.
DE
DE See also ajNamDbQuery, which calls this function if the common
DE query data is not yet set.
XX
PN [1]
PA u qry AjPQuery
PD Query structure with at least
PD dbname filled in
PX
PN [2]
PA r argc ajuint
PD Number of additional attribute name/value pairs
PX
PN [3]
PA v vararg ...
PD Variable length argument list
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamDbData(AjPQuery qry, ajuint argc, ...)
{
    const NamPEntry data;
    const AjPTable dbtable;
    AjPStrTok handle = NULL;
    AjPStr liststr = NULL;
    AjPStr token = NULL;
    ajuint i;
    const char* varAttrName;
    AjPStr* varAttrValue;
    va_list ap;
    const NamPType namtype;
    AjBool ok = ajTrue;

    if(qry->SetDatabase)
        return ajTrue;

    qry->SetDatabase = ajTrue;

    data = ajTableFetchS(namDbMasterTable, qry->DbName);

    if(!data)
    {
	ajDebug("ajNamDbData: database %S unknown\n", qry->DbName);
        return ajFalse;
    }

    dbtable = (const AjPTable) data->data;

    /* general defaults */

    if(namDbSetAttrStrC(dbtable, "type", &liststr))
    {
        ajStrTokenAssignC(&handle, liststr, " ,;");
        ajStrAssignC(&qry->DbType, "");

        while(ajStrTokenNextParse(&handle, &token))
        {
            if(ajStrMatchCaseC(token, "n"))
                ajStrAssignC(&token, "Nucleotide");
            else if(ajStrMatchCaseC(token, "p"))
                ajStrAssignC(&token, "Protein");
            else if(ajStrMatchCaseC(token, "prot"))
                ajStrAssignC(&token, "Protein");

            namtype = ajTableFetchS(namDbTypeTable, token);

            if(!namtype)
            {
                ajErr("Database %S type '%S' unknown", qry->DbName, token);
                continue;
            }

            if(qry->DataType == AJDATATYPE_UNKNOWN) /* take first available */
                qry->DataType = namtype->DataType;

            if(namtype->DataType == qry->DataType)
            {
                ajStrAssignS(&qry->DbType, token);
                break;
            }
        }

        if(!ajStrGetLen(qry->DbType))
        {
            ajErr("Database %S type(s) '%S' do not match query type '%s'",
                  qry->DbName, liststr, ajQueryGetDatatype(qry));
            return ajFalse;
        }
    }

    if(ajStrGetLen(qry->Formatstr))
    {
        if(!namInformatTest(qry->Formatstr, qry->DbType))
        {
            ajErr("Query format '%S' does not support datatype '%S'",
                  qry->Formatstr, qry->DbType);
            return ajFalse;
        }
    }
    else
    {
        if(namDbSetAttrStrC(dbtable, "format", &liststr))
        {
            ok = ajFalse;
            ajStrTokenAssignC(&handle, liststr, " ,;");

            while(ajStrTokenNextParse(&handle, &token))
            {
                if(namInformatTest(token, qry->DbType))
                {
                    ok = ajTrue;
                    ajStrAssignS(&qry->Formatstr, token);
                }
            }

            if(!ok)
            {
                ajErr("Database %S format(s) '%S' do not support "
                      "datatype '%S'",
                      qry->DbName, liststr, qry->DbType);
                return ajFalse;
            }
        }
    }

    ajStrDel(&liststr);
    ajStrDel(&token);
    ajStrTokenDel(&handle);

    namDbSetAttrStrC(dbtable, "method", &qry->Method);
    namDbSetAttrStrC(dbtable, "app", &qry->Application);
    namDbSetAttrStrC(dbtable, "directory", &qry->IndexDir);
    namDbSetAttrStrC(dbtable, "indexdirectory", &qry->IndexDir);
    namDbSetAttrStrC(dbtable, "indexdirectory", &qry->Directory);
    namDbSetAttrStrC(dbtable, "directory", &qry->Directory);
    namDbSetAttrStrC(dbtable, "namespace", &qry->Namespace);
    namDbSetAttrStrC(dbtable, "organisms", &qry->Organisms);
    namDbSetAttrStrC(dbtable, "exclude", &qry->Exclude);
    namDbSetAttrStrC(dbtable, "filename", &qry->Filename);

    if(!namDbSetAttrStrC(dbtable, "field", &qry->DbFields))
        namDbSetAttrStrC(dbtable, "fields", &qry->DbFields);

    namDbSetAttrStrC(dbtable, "url", &qry->DbUrl);
    namDbSetAttrStrC(dbtable, "proxy", &qry->DbProxy);
    namDbSetAttrStrC(dbtable, "httpversion", &qry->DbHttpVer);
    namDbSetAttrStrC(dbtable, "serverversion", &qry->ServerVer);
    namDbSetAttrBoolC(dbtable, "caseidmatch", &qry->CaseId);
    namDbSetAttrBoolC(dbtable, "hasaccession", &qry->HasAcc);

    namDbSetAttrStrC(dbtable, "identifier", &qry->DbIdentifier);
    namDbSetAttrStrC(dbtable, "accession", &qry->DbAccession);
    namDbSetAttrStrC(dbtable, "filters", &qry->DbFilter);
    namDbSetAttrStrC(dbtable, "return", &qry->DbReturn);

#ifdef WIN32
    ajStrExchangeKK(&qry->Directory, '/', '\\');
    if(ajStrPrefixC(qry->Directory, ".\\"))
        ajStrCutStart(&qry->Directory, 2);
    ajStrExchangeKK(&qry->IndexDir, '/', '\\');
    if(ajStrPrefixC(qry->IndexDir, ".\\"))
        ajStrCutStart(&qry->IndexDir, 2);
#endif


    if(argc)
    {
        va_start(ap, argc);

        for(i=0; i < argc; i++)
        {
            varAttrName = va_arg(ap, const char*);
            varAttrValue = va_arg(ap, AjPStr*);
            namDbSetAttrStrC(dbtable, varAttrName, varAttrValue);
        }

        va_end(ap);
    }

    return ajTrue;
}
ID ajNamDbQuery
TY public
MO ajnam
LB core
XX
DE Given a query with database name and search fields,
DE fill in the access method and some common fields according
DE to the query level.
XX
PN [1]
PA u qry AjPQuery
PD Query structure with at least
PD dbname filled in
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamDbQuery(AjPQuery qry)
{

    const NamPEntry data;
    const NamPEntry sdata;

    const AjPTable sdbtable;

    const AjPTable dbtable;
    const char* qlinks;

    ajDebug("ajNamDbQuery SetQuery:%B QueryType:%d\n",
            qry->SetQuery, qry->QueryType);

    if(qry->SetQuery)
        return ajTrue;

    qry->SetQuery = ajTrue;

    if(qry->InDrcat)
        return ajTrue;

    if(ajStrGetLen(qry->SvrName))
    {
        if(ajStrGetLen(qry->DbType))
            ajNamSvrData(qry, 0);

        ajNamSvrQuery(qry);
        sdata = ajTableFetchS(namSvrDatabaseTable, qry->SvrName);
        sdbtable = (const AjPTable) sdata->data;
        data = ajTableFetchS(sdbtable, qry->DbName);
    }
    else
        data = ajTableFetchS(namDbMasterTable, qry->DbName);

    if(!data)
        ajFatal("database %S unknown", qry->DbName);

    dbtable = (const AjPTable) data->data;

    if(!ajStrGetLen(qry->DbType))
    {
	if(!ajNamDbData(qry, 0))
            return ajFalse;
    }

    if((qry->QueryType == AJQUERY_ALL) ||
       ((qry->QueryType == AJQUERY_UNKNOWN) && !ajQueryIsSet(qry))) 
    {
        /* must have a method for all entries */
	namDbSetAttrStrC(dbtable, "methodall", &qry->Method);
	namDbSetAttrStrC(dbtable, "formatall", &qry->Formatstr);
	namDbSetAttrStrC(dbtable, "appall", &qry->Application);
	qry->QueryType = AJQUERY_ALL;
    }
    else		      /* must be able to query the database */
    {
	namDbSetAttrStrC(dbtable, "methodquery", &qry->Method);
	namDbSetAttrStrC(dbtable, "formatquery", &qry->Formatstr);
	namDbSetAttrStrC(dbtable, "appquery", &qry->Application);

	if((qry->QueryType != AJQUERY_QUERY) &&
           !ajQuerySetWild(qry)) /* ID - single entry may be available */
	{
	    namDbSetAttrStrC(dbtable, "methodentry", &qry->Method);
	    namDbSetAttrStrC(dbtable, "formatentry", &qry->Formatstr);
	    namDbSetAttrStrC(dbtable, "appentry", &qry->Application);
	    qry->QueryType = AJQUERY_ENTRY;
	}
	else
	    qry->QueryType = AJQUERY_QUERY;
    }

    namDbSetAttrStrC(dbtable, "return", &qry->DbReturn);


    if(!ajStrGetLen(qry->Formatstr))
    {
	ajErr("No format defined for database '%S'", qry->DbName);

	return ajFalse;
    }

    if(!ajStrGetLen(qry->Method))
    {
	ajErr("No access method for database '%S'", qry->DbName);

	return ajFalse;
    }

    qlinks = namMethod2Qlinks(qry->Method, qry->DataType);

    if(!qlinks)
    {
        ajErr("Unknown access method '%S' for database '%S'",
              qry->Method, qry->DbName);
        return ajFalse;
    }

    ajStrAssignC(&qry->Qlinks, qlinks);

    return ajTrue;
}
ID ajNamFileQuery
TY public
MO ajnam
LB core
XX
DE Given a query with no database name and search fields, check
DE datatype specific fields according to the query level.
XX
PN [1]
PA u qry AjPQuery
PD Query structure with no database name
PX
RT AjBool
RD ajTrue if success.
RX
//


AjBool ajNamFileQuery(AjPQuery qry)
{
    const char* fields = NULL;
    const char* qlinks = NULL;

    if(qry->SetQuery)
        return ajTrue;

    qry->SetQuery = ajTrue;

    if(ajStrGetLen(qry->SvrName))
	ajFatal("ajNamFileQuery called with server '%S'", qry->SvrName);
    else if(ajStrGetLen(qry->DbName))
	ajFatal("ajNamFileQuery called with database '%S'", qry->DbName);

    switch(qry->QueryType)
    {
        case AJQUERY_ALL:
            break;

        case AJQUERY_QUERY:
            break;

        case AJQUERY_ENTRY:
            break;
            
        case AJQUERY_UNKNOWN:
        default:
            break;
    }

    /*
    if(!ajStrGetLen(qry->Formatstr))
    {
	ajErr("No format defined for file access query");

	return ajFalse;
    }
    */

    qlinks = namDatatype2Qlinks(qry->DataType);

    if(qlinks)
        ajStrAssignC(&qry->Qlinks, qlinks);
    else
    {
        ajWarn("no query links defined for type; '%s'",
               namDbTypes[qry->DataType].Name);
        ajStrAssignC(&qry->Qlinks, "|");
    }

    fields = namDatatype2Fields(qry->DataType);

    if(fields)
        ajStrAssignC(&qry->DbFields, fields);
    else
    {
        ajWarn("no fields defined for type; '%s'",
               namDbTypes[qry->DataType].Name);
        ajStrAssignC(&qry->DbFields, "");
    }

    return ajTrue;
}
ID namDbSetAttrStrC
TY static
MO ajnam
LB core
XX
DE Sets a named string attribute value from an attribute list.
XX
PN [1]
PA r dbtable const AjPTable
PD Attribute definitions table.
PX
PN [2]
PA r attrib const char*
PD Attribute name.
PX
PN [3]
PA w qrystr AjPStr*
PD Returned attribute value.
PX
RT AjBool
RD ajTrue on success.
RX
//


static AjBool namDbSetAttrStrC(const AjPTable dbtable, const char* attrib,
                               AjPStr* qrystr)
{
    const void* dbval = NULL;
    const AjPStr strval = NULL;
    const NamPAttr attr = NULL;
    const AjPList fdlist = NULL;
    AjIList iter = NULL;
    AjPStrTok handle = NULL;
    AjPStr word = NULL;

    attr = ajTableFetchC(namDbAttrTable, attrib);

    if(!attr)
        ajFatal("unknown attribute '%s' requested",  attrib);

    dbval = ajTableFetchC(dbtable, attrib);

    if(dbval)
    {
        switch(attr->Type)
        {
            case ATTR_LIST:
                ajStrAssignC(qrystr, "");
                fdlist = (const AjPList) dbval;
                iter = ajListIterNewread(fdlist);

                while(!ajListIterDone(iter))
                {
                    strval = ajListIterGet(iter);
                    ajStrTokenAssignC(&handle, strval, " ");

                    while(ajStrTokenNextParse(&handle, &word))
                    {
                        if(ajStrMatchC(word, "!"))
                            break;

                        if(ajStrGetLen(*qrystr))
                            ajStrAppendK(qrystr, ' ');

                        ajStrAppendS(qrystr, word);
                    }
                }

                ajStrTokenDel(&handle);
                ajStrDel(&word);
                ajListIterDel(&iter);
                break;

            default:
                if(!ajStrGetLen((const AjPStr) dbval))
                    return ajFalse;
                ajStrAssignS(qrystr, (const AjPStr) dbval);

                break;
        }
    }
    else 
    {
        if(!strlen(attr->Defval))
            return ajFalse;

        ajStrAssignC(qrystr, attr->Defval);
    }

    namVarResolve(qrystr);

    return ajTrue;
}
ID namDbSetAttrBoolC
TY static
MO ajnam
LB core
XX
DE Sets a named boolean attribute value from an attribute list.
XX
PN [1]
PA r dbtable const AjPTable
PD Attribute definitions table
PX
PN [2]
PA r attrib const char*
PD Attribute name.
PX
PN [3]
PA w qrybool AjBool*
PD Returned attribute value.
PX
RT AjBool
RD ajTrue on success.
RX
//


static AjBool namDbSetAttrBoolC(const AjPTable dbtable, const char* attrib,
                                AjBool* qrybool)
{
    AjBool ret = ajTrue;
    AjPStr tmpstr = NULL;
    const AjPStr dbval = NULL;
    const char* txtval = NULL;
    const NamPAttr attr = NULL;

    dbval = ajTableFetchC(dbtable, attrib);

    if(dbval)
        txtval = ajStrGetPtr(dbval);
    else 
    {
        attr = ajTableFetchC(namDbAttrTable, attrib);

        if(!attr)
            ajFatal("unknown attribute '%s' requested",  attrib);

        txtval = attr->Defval;
    }

    if(!ajStrGetLen(dbval))
    {
	ajStrAssignC(&tmpstr, txtval);
	ret = ajFalse;
    }
    else
    {
	ajStrAssignS(&tmpstr, dbval);
	ret = ajTrue;
    }

    /* ajDebug("namDbSetAttr('%S')\n", *qrystr); */

    namVarResolve(&tmpstr);
    ajStrToBool(tmpstr, qrybool);
    ajStrDel(&tmpstr);

    return ret;
}
ID namVarResolve
TY static
MO ajnam
LB core
XX
DE Resolves any variable or function references in a string.
XX
PN [1]
PA u var AjPStr*
PD String value
PX
RT AjBool
RD Always ajTrue so far
RX
//


static AjBool namVarResolve(AjPStr* var)
{

    AjPStr varname = NULL;
    AjPStr newvar  = NULL;
    AjPStr restvar = NULL;

    if(!namVarExp)
	namVarExp = ajRegCompC("^\\$([a-zA-Z0-9_.]+)");

    while(ajRegExec(namVarExp, *var))
    {
	ajRegSubI(namVarExp, 1, &varname); /* variable name */

	ajNamGetValueS(varname, &newvar);

	ajDebug("namVarResolve '%S' = '%S'\n", varname, newvar);

	if(ajRegPost(namVarExp, &restvar)) /* any more? */
	    ajStrAppendS(&newvar, restvar);

	ajStrAssignS(var, newvar);
    }

    ajStrDel(&varname);
    ajStrDel(&newvar);
    ajStrDel(&restvar);

    return ajFalse;
}
ID namUser
TY static
MO ajnam
LB core
XX
DE Formatted write as an error message.
XX
PN [1]
PA r fmt const char*
PD Format string
PX
PN [2]
PA v vararg ...
PD Format arguments.
PX
RT void
RD 
RX
//


static void namUser(const char* fmt, ...)
{
    va_list args;

    if(!namDoDebug)
	return;

    va_start(args, fmt);
    ajFmtVError(fmt, args);
    va_end(args);

    return;
}
ID namError
TY static
MO ajnam
LB core
XX
DE Formatted write as an error message.
XX
PN [1]
PA r fmt const char*
PD Format string
PX
PN [2]
PA v vararg ...
PD Format arguments.
PX
RT void
RD 
RX
//


static void namError(const char* fmt, ...)
{
    va_list args;
    AjPStr errstr = NULL;
  
    namErrorCount++;

    va_start(args, fmt);
    ajFmtVPrintS(&errstr, fmt, args);
    va_end(args);

    ajErr("File %S line %d: %S", namFileName, namLine, errstr);
    ajStrDel(&errstr);

    return;
}
ID ajNamValueInstalldir
TY public
MO ajnam
LB core
XX
DE Returns the install directory root for all file searches
DE (package level)
XX
RT const AjPStr
RD Install directory root
RX
//


const AjPStr ajNamValueInstalldir(void)
{
    return namFixedInstallStr;
}
ID ajNamValuePackage
TY public
MO ajnam
LB core
XX
DE Returns the package name for the library
XX
RT const AjPStr
RD Package name
RX
//


const AjPStr ajNamValuePackage(void)
{
    return namFixedPackageStr;
}
ID ajNamValueSystem
TY public
MO ajnam
LB core
XX
DE Returns the system information for the library
XX
RT const AjPStr
RD Version number
RX
//


const AjPStr ajNamValueSystem(void)
{
    return namFixedSystemStr;
}
ID ajNamValueVersion
TY public
MO ajnam
LB core
XX
DE Returns the version number for the library
XX
RT const AjPStr
RD Version number
RX
//


const AjPStr ajNamValueVersion(void)
{
    return namFixedVersionStr;
}
ID ajNamValueRootdir
TY public
MO ajnam
LB core
XX
DE Returns the directory for all file searches
DE (package level)
XX
RT const AjPStr
RD Package level root directory
RX
//


const AjPStr ajNamValueRootdir(void)
{
    return namFixedRootStr;
}
ID ajNamValueBasedir
TY public
MO ajnam
LB core
XX
DE Returns the base directory for all for all file searches
DE (above package level).
XX
RT const AjPStr
RD Base directory
RX
//


const AjPStr ajNamValueBasedir(void)
{
    return namFixedBaseStr;
}
ID ajNamResolve
TY public
MO ajnam
LB core
XX
DE Resolves a variable name if the input string starts with a dollar sign.
XX
PN [1]
PA w name AjPStr*
PD String
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajNamResolve(AjPStr* name)
{

    AjPStr varname  = NULL;
    AjPStr varvalue = NULL;
    AjPStr restname = NULL;
    AjBool ret;

    if(!namNameExp)
	namNameExp = ajRegCompC("^\\$([A-Za-z0-9_]+)");

    namUser("ajNamResolve of '%S'\n", *name);
    ret = ajRegExec(namNameExp, *name);

    if(ret)
    {
	ajRegSubI(namNameExp, 1, &varname);
	namUser("variable '%S' found\n", varname);
	ajRegPost(namNameExp, &restname);
	ret = ajNamGetValueS(varname, &varvalue);

	if(ret)
	{
	    ajStrAssignS(name, varvalue);
	    ajStrAppendS(name, restname);
	    namUser("converted to '%S'\n", *name);
	}
	else
	{
	    namUser("Variable unknown '$%S'\n", varname);
	    ajWarn("Variable unknown in '%S'", *name);
	    ajStrAssignS(name, restname);
	}

	ajStrDel(&varname);
	ajStrDel(&varvalue);
	ajStrDel(&restname);
    }

    return ret;
}
ID namValid
TY static
MO ajnam
LB core
XX
DE Validation of a master table entry
XX
PN [1]
PA r entry const NamPEntry
PD Internal table entry
PX
PN [2]
PA r entrytype ajint
PD Internal table entry type
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool namValid(const NamPEntry entry, ajint entrytype)
{
    if(entrytype == TYPE_ENV)
	return namValidVariable(entry);
    else if(entrytype == TYPE_SVR)
	return namValidServer(entry);
    else if(entrytype == TYPE_DB)
	return namValidDatabase(entry);
    else if(entrytype == TYPE_RESOURCE)
	return namValidResource(entry);
    else if(entrytype == TYPE_ALIAS)
	return namValidAlias(entry);

    /* fatal: cannot test - should not happen */
    namError("Unknown definition type number %d",
	      entrytype);

    return ajFalse;
}
ID namValidAlias
TY static
MO ajnam
LB core
XX
DE Validation of a master table alias entry
XX
PN [1]
PA r entry const NamPEntry
PD Internal table entry
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool namValidAlias(const NamPEntry entry)
{
    AjPTable attrtable;

    attrtable = (AjPTable) entry->data;

    if(attrtable)
    {			 /* strange - should be nothing for aliases */
	namError("Alias '%S' has a list of attributes",
		  entry->name);

	return ajFalse;
    }
    
    return ajTrue;
}
ID namValidServer
TY static
MO ajnam
LB core
XX
DE Validation of a master table server entry
XX
PN [1]
PA r entry const NamPEntry
PD Internal table entry
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool namValidServer(const NamPEntry entry)
{
    ajint iattr = 0;
    ajint j;
    ajint k;
    AjBool ok;
    AjBool oktype;
    AjPTable attrtable;
    AjBool hasmethod = ajFalse;
    AjBool hastype   = ajFalse;
    AjPStr name = NULL;
    AjPStrTok handle = NULL;
    AjPStr token = NULL;
    const AjPStr value = NULL;
    const AjPStr dbtype = NULL;

    attrtable = (AjPTable) entry->data;

    if(!attrtable)
    {			 /* fatal - should be set for all servers */
	namError("Server '%S' has no list of valid attributes",
		  entry->name);
	return ajFalse;
    }
    
    dbtype = ajTableFetchC(attrtable, "type");

    for(j=0; namSvrAttrs[j].Name; j++)
    {
        if(namSvrAttrs[j].Type != ATTR_STR)
            continue;

        ajStrAssignC(&name, namSvrAttrs[j].Name);
        value = ajTableFetchS(attrtable, name);

	if(value)
	{
	    iattr++;

	    if(ajStrPrefixC(name, "format"))
            {
                if(!namInformatTest(value, dbtype))
                        namError("Database '%S' %S: '%S' unknown",
                                 entry->name, name, value);
            }

            if(ajStrPrefixC(name, "method"))
            {
        	handle = NULL;
        	token = ajStrNew();
        	hasmethod=ajTrue;

        	ajStrTokenAssignC(&handle, dbtype, " ,;");

        	while(ajStrTokenNextParse(&handle, &token))
        	    if(!namAccessTest(value, token))
        		namError("Server '%S' %S: '%S' unknown",
        		         entry->name, name, value);

        	ajStrTokenDel(&handle);
        	ajStrDel(&token);
            }

	    if(ajStrPrefixC(name, "type"))
	    {
		hastype=ajTrue;
		oktype = ajFalse;

		for(k=0; namDbTypes[k].Name; k++)
		    if(ajStrFindAnyC(value, namDbTypes[k].Name) != -1)
			oktype = ajTrue;

		if(!oktype)
		    namError("Server '%S' %S: '%S' unknown",
			      entry->name, name, value);
	    }
	}
    }

    ok = ajTrue;

    if(!iattr)
    {
	namError("Server '%S' has no attributes", entry->name);
	ok = ajFalse;
    }

    if(!hastype)
    {
	namError("Database '%S' has no type definition", entry->name);
	ok = ajFalse;
    }

    if(!hasmethod)
    {
	namError("Database '%S' has no access method definition",
		  entry->name);
	ok = ajFalse;
    }

    ajStrDel(&name);

    return ok;
}
ID namValidDatabase
TY static
MO ajnam
LB core
XX
DE Validation of a master table database entry
XX
PN [1]
PA r entry const NamPEntry
PD Internal table entry
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool namValidDatabase(const NamPEntry entry)
{
    ajint iattr = 0;
    ajint j;
    ajint k;
    AjBool ok;
    AjBool oktype;
    AjPTable attrtable;
    AjBool hasformat = ajFalse;
    AjBool hasmethod = ajFalse;
    AjBool hastype   = ajFalse;
    AjPStr name = NULL;
    AjPStr token = NULL;
    AjPStr typetoken = NULL;
    AjPStrTok handle = NULL;
    AjPStrTok typehandle = NULL;
    const AjPStr value = NULL;
    const AjPStr dbtype = NULL;

    attrtable = (AjPTable) entry->data;

    if(!attrtable)
    {			 /* fatal - should be set for all databases */
	namError("Database '%S' has no list of valid attributes",
		  entry->name);
	return ajFalse;
    }
    
    dbtype = ajTableFetchC(attrtable, "type");

    for(j=0; namDbAttrs[j].Name; j++)
    {
        if(namDbAttrs[j].Type != ATTR_STR)
            continue;

        ajStrAssignC(&name, namDbAttrs[j].Name);
        value = ajTableFetchS(attrtable, name);

	if(value)
	{
	    iattr++;

	    if(ajStrPrefixC(name, "format"))
	    {
		hasformat=ajTrue;

                ajStrTokenAssignC(&typehandle, dbtype, " ,;");

                while(ajStrTokenNextParse(&typehandle, &typetoken))
                {
                    ok = ajFalse;
                    ajStrTokenAssignC(&handle, value, " ,;");
                    while(ajStrTokenNextParse(&handle, &token))
                    {
                        if(namInformatTest(token, typetoken))
                            ok = ajTrue;
                    }

                    if(!ok) /* test: dbunknowns.rc */
                        namError("Database '%S' %S: '%S' unknown for type '%S'",
                                 entry->name, name, value, typetoken);
                }

            }

            if(ajStrPrefixC(name, "method"))
	    {
		hasmethod=ajTrue;

                ajStrTokenAssignC(&typehandle, dbtype, " ,;");
 
                while(ajStrTokenNextParse(&typehandle, &typetoken))
                {
                    ok = ajFalse;
                    ajStrTokenAssignC(&handle, value, " ,;");
                    while(ajStrTokenNextParse(&handle, &token))
                    {
                        if(namAccessTest(value, typetoken))
                            ok = ajTrue;
                    }

                    if(!ok)  /* test: dbunknowns.rc */
                        namError("Database '%S' %S: '%S' unknown for type '%S'",
                                 entry->name, name, value, typetoken);
                }
            }

	    if(ajStrPrefixC(name, "type"))
	    {
		hastype=ajTrue;
		oktype = ajFalse;

                ajStrTokenAssignC(&typehandle, value, " ,;");
                while(ajStrTokenNextParse(&typehandle, &typetoken))
                {
                    for(k=0; namDbTypes[k].Name; k++)
                        if(ajStrMatchCaseC(typetoken, namDbTypes[k].Name)) 
                            oktype = ajTrue;
                }

		if(!oktype)		/* test: dbunknowns.rc */
		    namError("Database '%S' %S: '%S' unknown",
			      entry->name, name, value);
	    }
	}
    }

    ok = ajTrue;

    if(!iattr)
    {					/* test: dbempty.rc */
	namError("Database '%S' has no attributes", entry->name);
	ok = ajFalse;
    }

    if(!hasformat)		/* test: dbempty.rc */
    {
	namError("Database '%S' has no format definition", entry->name);
	ok = ajFalse;
    }

    if(!hastype)			/* test: dbempty.rc */
    {
	namError("Database '%S' has no type definition", entry->name);
	ok = ajFalse;
    }

    if(!hasmethod)		/* test: dbempty.rc */
    {
	namError("Database '%S' has no access method definition",
		  entry->name);
	ok = ajFalse;
    }

    ajStrDel(&name);
    ajStrTokenDel(&handle);
    ajStrTokenDel(&typehandle);
    ajStrDel(&token);
    ajStrDel(&typetoken);

    return ok;
}
ID namValidResource
TY static
MO ajnam
LB core
XX
DE Validation of a master table resource entry
XX
PN [1]
PA r entry const NamPEntry
PD Internal table entry
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool namValidResource(const NamPEntry entry)
{
    ajint iattr = 0;
    ajint j;
    AjPTable rstable;
    AjBool ok;

    rstable = (AjPTable) entry->data;

    if(!rstable)
    {			 /* fatal - should be set for all databases */
	namError("Resource '%S' has no list of valid attributes",
		  entry->name);
	return ajFalse;
    }

    for(j=0; namRsAttrs[j].Name; j++)
	if(ajTableFetchC(rstable,  namRsAttrs[j].Name))
	    iattr++;
    
    ok = ajTrue;

    if(!iattr)
    {					/* test: dbempty.rc */
	namError("Resource '%S' has no attributes", entry->name);
	ok =  ajFalse;
    }

    return ok;
}
ID namValidVariable
TY static
MO ajnam
LB core
XX
DE Validation of a master table variable entry
XX
PN [1]
PA r entry const NamPEntry
PD Internal table entry
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool namValidVariable(const NamPEntry entry)
{
    AjPTable attrtable;

    attrtable = (AjPTable) entry->data;

    if(attrtable)
    {			 /* strange - should be nothing for variables */
	namError("Variable '%S' has a list of attributes",
		  entry->name);

	return ajFalse;
    }
    
    return ajTrue;
}
ID ajNamSetControl
TY public
MO ajnam
LB core
XX
DE Sets special internal variables to reflect their presence.
DE
DE Currently these are "namdebug, namvalid"
XX
PN [1]
PA r optionName const char*
PD option name
PX
RT AjBool
RD ajTrue if option was recognised
RX
//


AjBool ajNamSetControl(const char* optionName)
{

    if(!ajCharCmpCase(optionName, "namdebug"))
    {
	namDoDebug = ajTrue;

	return ajTrue;
    }

    if(!ajCharCmpCase(optionName, "namvalid"))
    {
	namDoValid = ajTrue;

	return ajTrue;
    }

    ajDie("Unknown ajNamSetControl control option '%s'", optionName);

    return ajFalse;
}
ID ajNamRsAttrValueC
TY public
MO ajnam
LB core
XX
DE Return the value for a resource attribute
XX
PN [1]
PA r name const char*
PD resource name
PX
PN [2]
PA r attribute const char*
PD resource attribute
PX
PN [3]
PA w value AjPStr*
PD resource value
PX
RT AjBool
RD true if found
RX
//


AjBool ajNamRsAttrValueC(const char *name, const char *attribute,
			 AjPStr *value)
{
    ajint j;
    const NamPEntry fnew = NULL;
    const AjPTable rstable;
    const AjPStr rsvalue;

    ajDebug("ajNamRsAttrValueC '%s' '%s'\n", name, attribute);

    fnew = ajTableFetchC(namResMasterTable, name);

    if(!fnew)
	return ajFalse;
    
    rstable = (const AjPTable) fnew->data;
    j = namRsAttrC(attribute);

    if(j < 0)
    {
        if(namRsAttrFieldC(rstable, attribute))
            return ajFalse;
    
	ajFatal("unknown attribute '%s' requested for resource '%s'",
                attribute, name);
    }

    rsvalue = ajTableFetchC(rstable, attribute);

    if(ajStrGetLen(rsvalue))
    {
	ajStrAssignS(value,rsvalue);

	return ajTrue;
    }

    return ajFalse;
}
ID ajNamRsAttrValueS
TY public
MO ajnam
LB core
XX
DE Return the value for a resource attribute
XX
PN [1]
PA r name const AjPStr
PD resource name
PX
PN [2]
PA r attribute const AjPStr
PD resource attribute
PX
PN [3]
PA w value AjPStr*
PD resource value
PX
RT AjBool
RD true if found
RX
//


AjBool ajNamRsAttrValueS(const AjPStr name, const AjPStr attribute,
			 AjPStr *value)
{
    ajint j;
    const NamPEntry fnew = NULL;
    const AjPTable rstable;
    const AjPStr rsvalue;

    ajDebug("ajNamRsAttrValueS '%S' '%S'\n", name, attribute);

    fnew = ajTableFetchS(namResMasterTable, name);

    if(!fnew)
	ajFatal("unknown resource '%S'",
                name);

    j = namRsAttrS(attribute);

    rstable = (const AjPTable) fnew->data;
    rsvalue = ajTableFetchS(rstable, attribute);

    ajDebug("resource '%S' found file:%S attribute '%S' j:%d value: '%S'\n",
            name, fnew->file, attribute, j, rsvalue);

    if(ajStrGetLen(rsvalue))
    {
	ajStrAssignS(value,rsvalue);

	return ajTrue;
    }

    if(j < 0)
    {
        if(namRsAttrFieldS(rstable, attribute))
            return ajFalse;
    
	ajFatal("unknown attribute '%S' requested for resource '%S'",
                attribute, name);
    }

    return ajFalse;
}
ID ajNamRsListValue
TY public
MO ajnam
LB core
XX
DE Return the value for a resource attribute of type 'list'
XX
PN [1]
PA r name const AjPStr
PD resource name
PX
PN [2]
PA w value AjPStr*
PD resource value
PX
RT AjBool
RD true if found
RX
//


AjBool ajNamRsListValue(const AjPStr name, AjPStr *value)
{
    const NamPEntry fnew = NULL;
    const AjPTable rstable = NULL;
    const AjPStr rsvalue = NULL;

    fnew = ajTableFetchS(namResMasterTable, name);

    rstable = (const AjPTable) fnew->data;
    rsvalue = ajTableFetchC(rstable, "type");

    if(!ajStrMatchCaseC(rsvalue, "list"))
	return ajFalse;

    rsvalue = ajTableFetchC(rstable, "value");

    if(ajStrGetLen(rsvalue))
    {
	ajStrAssignS(value,rsvalue);

	return ajTrue;
    }

    return ajFalse;
}
ID ajNexusParse
TY public
MO ajnexus
LB core
XX
DE Parses a nexus buffered file
XX
PN [1]
PA u buff AjPFilebuff
PD Input buffered file
PX
RT AjPNexus
RD Nexus data object
RX
//


AjPNexus ajNexusParse(AjPFilebuff buff)
{
    AjPNexus ret;
    AjPStr rdline = NULL;
    AjPList blocklist = NULL;
    ajint ok;

    ajint commentlevel = 0;
    AjBool block;
    AjPStr prestr = NULL;
    AjPStr poststr = NULL;
    AjPStr blockname = NULL;

    if (!nexusCommExp)
	nexusCommExp = ajRegCompC("[\\[]([^&][^\\[\\]]+)[\\]]$");

    if (!nexusEndCommExp)
	nexusEndCommExp = ajRegCompC("^([^\\]]*)[\\]]");

    if (!nexusBegCommExp)
	nexusBegCommExp = ajRegCompC("[\\[]([^\\[\\]]*)$");

    if (!nexusBegExp)
	nexusBegExp = ajRegCompC("^\\s*[Bb][Ee][Gg][Ii][Nn]\\s+(\\S+)\\s*;\\s*$");

    if (!nexusEndExp)
	nexusEndExp = ajRegCompC("^\\s*[Ee][Nn][Dd]\\S*;\\s*$");

    ajDebug("ajNexusParse\n");

    ajBuffreadLine(buff, &rdline);

    if(!ajStrPrefixCaseC(rdline, "#NEXUS"))
    {				/* first line test */
	ajFilebuffReset(buff);
        ajStrDel(&rdline);

	return NULL;
    }

    ajDebug("ajNexusParse: First line ok '%S'\n", rdline);

    ok = ajBuffreadLine(buff, &rdline);
    if(!ok)
    {
        ajStrDel(&rdline);
	return NULL;
    }

    block = ajFalse;
    commentlevel = 0;
    blocklist = ajListstrNew();
    ret = ajNexusNew();

    while(ok)
    {
	ajStrTrimWhiteEnd(&rdline);
	/* first trim any comments from the input string */

	while (commentlevel && ajRegExec(nexusEndCommExp, rdline))
	{
	    ajRegPost(nexusEndCommExp, &poststr);
	    ajStrAssignS(&rdline, poststr);
	    --commentlevel;
	    ajDebug("end comment found: %S\n", rdline);
	}

	while (ajRegExec(nexusCommExp, rdline))
	{
	    ajRegPre(nexusCommExp, &prestr);
	    ajRegPost(nexusCommExp, &poststr);
	    ajStrAssignS(&rdline, prestr);
	    ajStrAppendS(&rdline, poststr);
	    ajDebug("comment found: %S\n", rdline);
	}

	while (ajRegExec(nexusBegCommExp, rdline))
	{
	    ajRegPre(nexusBegCommExp, &prestr);
	    ajStrAssignS(&rdline, prestr);
	    ++commentlevel;
	    ajDebug("begin comment found: %S\n", rdline);
	}

	if (commentlevel)
	{
	    ajDebug("comments only ... skipping\n");
	}
	else if (!block && ajRegExec(nexusBegExp, rdline)) /* begin line */
	{
	    block = ajTrue;
	    ajRegSubI(nexusBegExp, 1, &blockname);
	}
	else if (block && ajRegExec(nexusEndExp, rdline)) /* end line */
	{
	    if (!nexusBlockSave(ret, blocklist, blockname))
            {
                ajStrDel(&rdline);
                ajStrDel(&blockname);
		return NULL;
            }

	    ajStrAssignClear(&blockname);
	    block = ajFalse;
	}
	else if (block)				/* the rest */
	{
	    ajStrRemoveWhiteExcess(&rdline);

	    if (ajStrGetLen(rdline))
	    {
		ajListstrPushAppend(blocklist, rdline);
                rdline = NULL;
	    }
	}

	ok = ajBuffreadLine(buff, &rdline);
    }

    if (block)
    {
	ajDebug("ajNexusParse unclosed block '%S'\n", blockname);
        ajStrDel(&rdline);
        ajStrDel(&blockname);
	return NULL;
    }

    ajNexusTrace(ret);
    ajStrDel(&rdline);
    ajStrDel(&blockname);

    ajListFree(&blocklist);

    return ret;
}
ID ajNexusNew
TY public
MO ajnexus
LB core
XX
DE Constructor for AjPNexus
XX
RT AjPNexus
RD New AjPNexus object
RX
CA new
CT AjPNexus
CD Default constructor
CX
//


AjPNexus ajNexusNew(void)
{
    AjPNexus thys;

    AJNEW0(thys);

    return thys;
}
ID nexusTaxaNew
TY static
MO ajnexus
LB core
XX
DE Constructor for AjPNexusTaxa
XX
RT AjPNexusTaxa
RD New AjPNexusTaxa object
RX
CA new
CT AjPNexusTaxa
CD Default constructor
CX
//


static AjPNexusTaxa nexusTaxaNew(void)
{
    AjPNexusTaxa thys;

    AJNEW0(thys);

    return thys;
}
ID nexusCharactersNew
TY static
MO ajnexus
LB core
XX
DE Constructor for AjPNexusCharacters
XX
RT AjPNexusCharacters
RD New AjPNexusCharacters object
RX
CA new
CT AjPNexusCharacters
CD Default constructor
CX
//


static AjPNexusCharacters nexusCharactersNew(void)
{
    AjPNexusCharacters thys;

    AJNEW0(thys);

    ajStrAssignC(&thys->DataType, "standard");
    thys->NewTaxa = ajFalse;
    thys->Missing = '?';
    ajStrAssignC(&thys->Symbols, "0 1");
    thys->Labels = ajTrue;
    thys->Interleave = ajFalse;
    thys->Tokens = ajTrue;
    
    return thys;
}
ID nexusUnalignedNew
TY static
MO ajnexus
LB core
XX
DE Constructor for AjPNexusUnaligned
XX
RT AjPNexusUnaligned
RD New AjPNexusUnaligned object
RX
CA new
CT AjPNexusUnaligned
CD Default constructor
CX
//


static AjPNexusUnaligned nexusUnalignedNew(void)
{
    AjPNexusUnaligned thys;

    AJNEW0(thys);

    thys->NewTaxa = ajFalse;
    ajStrAssignC(&thys->DataType, "standard");
    thys->Missing = '?';
    thys->Labels = ajTrue;


    return thys;
}
ID nexusDistancesNew
TY static
MO ajnexus
LB core
XX
DE Constructor for AjPNexusDistances
XX
RT AjPNexusDistances
RD New AjPNexusDistances object
RX
CA new
CT AjPNexusDistances
CD Default constructor
CX
//


static AjPNexusDistances nexusDistancesNew(void)
{
    AjPNexusDistances thys;

    AJNEW0(thys);
    thys->NewTaxa = ajFalse;
    thys->Missing = '?';
    ajStrAssignC(&thys->Triangle, "lower");
    thys->Diagonal = ajTrue;
    thys->Labels = ajTrue;
    thys->Interleave = ajFalse;

    return thys;
}
ID nexusSetsNew
TY static
MO ajnexus
LB core
XX
DE Constructor for AjPNexusSets
XX
RT AjPNexusSets
RD New AjPNexusSets object
RX
CA new
CT AjPNexusSets
CD Default constructor
CX
//


static AjPNexusSets nexusSetsNew(void)
{
    AjPNexusSets thys;

    AJNEW0(thys);

    return thys;
}
ID nexusAssumptionsNew
TY static
MO ajnexus
LB core
XX
DE Constructor for AjPNexusAssumptions
XX
RT AjPNexusAssumptions
RD New AjPNexusAssumptions object
RX
CA new
CT AjPNexusAssumptions
CD Default constructor
CX
//


static AjPNexusAssumptions nexusAssumptionsNew(void)
{
    AjPNexusAssumptions thys;

    AJNEW0(thys);

    ajStrAssignC(&thys->DefType, "unord");
    ajStrAssignC(&thys->GapMode, "missing");

    return thys;
}
ID nexusCodonsNew
TY static
MO ajnexus
LB core
XX
DE Constructor for AjPNexusCodons
XX
RT AjPNexusCodons
RD New AjPNexusCodons object
RX
CA new
CT AjPNexusCodons
CD Default constructor
CX
//


static AjPNexusCodons nexusCodonsNew(void)
{
    AjPNexusCodons thys;

    AJNEW0(thys);

    return thys;
}
ID nexusTreesNew
TY static
MO ajnexus
LB core
XX
DE Constructor for AjPNexusTrees
XX
RT AjPNexusTrees
RD New AjPNexusTrees object
RX
CA new
CT AjPNexusTrees
CD Default constructor
CX
//


static AjPNexusTrees nexusTreesNew(void)
{
    AjPNexusTrees thys;

    AJNEW0(thys);

    return thys;
}
ID nexusNotesNew
TY static
MO ajnexus
LB core
XX
DE Constructor for AjPNexusNotes
XX
RT AjPNexusNotes
RD New AjPNexusNotes object
RX
CA new
CT AjPNexusNotes
CD Default constructor
CX
//


static AjPNexusNotes nexusNotesNew(void)
{
    AjPNexusNotes thys;

    AJNEW0(thys);

    return thys;
}
ID ajNexusDel
TY public
MO ajnexus
LB core
XX
DE Destructor for AjPNexus
XX
PN [1]
PA d pthys AjPNexus*
PD AjPNexus object
PX
RT void
RD 
RX
CA delete
CT AjPNexus
CD Default destructor
CX
//


void ajNexusDel(AjPNexus* pthys)
{
    AjPNexus thys;

    if (!*pthys)
        return;

    thys = *pthys;

    nexusTaxaDel(&thys->Taxa);
    nexusCharactersDel(&thys->Characters);
    nexusUnalignedDel(&thys->Unaligned);
    nexusDistancesDel(&thys->Distances);
    nexusSetsDel(&thys->Sets);
    nexusAssumptionsDel(&thys->Assumptions);
    nexusCodonsDel(&thys->Codons);
    nexusTreesDel(&thys->Trees);
    nexusNotesDel(&thys->Notes);

    AJFREE(*pthys);
}
ID nexusTaxaDel
TY static
MO ajnexus
LB core
XX
DE Destructor for AjPNexusTaxa
XX
PN [1]
PA d pthys AjPNexusTaxa*
PD AjPNexusTaxa object
PX
RT void
RD 
RX
CA delete
CT AjPNexusTaxa
CD Default destructor
CX
//


static void nexusTaxaDel(AjPNexusTaxa* pthys)
{
    AjPNexusTaxa thys;

    if (!*pthys)
        return;

    thys = *pthys;

    nexusArrayDel(&thys->TaxLabels);

    AJFREE(*pthys);
}
ID nexusCharactersDel
TY static
MO ajnexus
LB core
XX
DE Destructor for AjPNexusCharacters
XX
PN [1]
PA d pthys AjPNexusCharacters*
PD AjPNexusCharacters object
PX
RT void
RD 
RX
CA delete
CT AjPNexusCharacters
CD Default destructor
CX
//


static void nexusCharactersDel(AjPNexusCharacters* pthys)
{
    AjPNexusCharacters thys;

    if (!*pthys)
        return;

    thys = *pthys;

    ajStrDel(&thys->DataType);
    ajStrDel(&thys->Symbols);
    ajStrDel(&thys->Equate);
    ajStrDel(&thys->Items);
    ajStrDel(&thys->StatesFormat);
    ajStrDel(&thys->Eliminate);

    nexusArrayDel(&thys->CharStateLabels);
    nexusArrayDel(&thys->CharLabels);
    nexusArrayDel(&thys->StateLabels);
    nexusArrayDel(&thys->Matrix);
    nexusArrayDel(&thys->Sequences);

    AJFREE(*pthys);

    return;
}
ID nexusUnalignedDel
TY static
MO ajnexus
LB core
XX
DE Destructor for AjPNexusUnaligned
XX
PN [1]
PA d pthys AjPNexusUnaligned*
PD AjPNexusUnaligned object
PX
RT void
RD 
RX
CA delete
CT AjPNexusUnaligned
CD Default destructor
CX
//


static void nexusUnalignedDel(AjPNexusUnaligned* pthys)
{
    AjPNexusUnaligned thys;

    if (!*pthys)
        return;

    thys = *pthys;

    ajStrDel(&thys->DataType);
    ajStrDel(&thys->Symbols);
    ajStrDel(&thys->Equate);

    nexusArrayDel(&thys->Matrix);

    AJFREE(*pthys);

    return;
}
ID nexusDistancesDel
TY static
MO ajnexus
LB core
XX
DE Destructor for AjPNexusDistances
XX
PN [1]
PA d pthys AjPNexusDistances*
PD AjPNexusDistances object
PX
RT void
RD 
RX
CA delete
CT AjPNexusDistances
CD Default destructor
CX
//


static void nexusDistancesDel(AjPNexusDistances* pthys)
{
    AjPNexusDistances thys;

    if (!*pthys)
        return;

    thys = *pthys;

    ajStrDel(&thys->Triangle);

    nexusArrayDel(&thys->Matrix);

    AJFREE(*pthys);

    return;
}
ID nexusSetsDel
TY static
MO ajnexus
LB core
XX
DE Destructor for AjPNexusSets
XX
PN [1]
PA d pthys AjPNexusSets*
PD AjPNexusSets object
PX
RT void
RD 
RX
CA delete
CT AjPNexusSets
CD Default destructor
CX
//


static void nexusSetsDel(AjPNexusSets* pthys)
{
    AjPNexusSets thys;

    if (!*pthys)
        return;

    thys = *pthys;

    nexusArrayDel(&thys->CharSet);
    nexusArrayDel(&thys->StateSet);
    nexusArrayDel(&thys->ChangeSet);
    nexusArrayDel(&thys->TaxSet);
    nexusArrayDel(&thys->TreeSet);
    nexusArrayDel(&thys->CharPartition);
    nexusArrayDel(&thys->TaxPartition);
    nexusArrayDel(&thys->TreePartition);

    AJFREE(*pthys);

    return;
}
ID nexusAssumptionsDel
TY static
MO ajnexus
LB core
XX
DE Destructor for AjPNexusAssumptions
XX
PN [1]
PA d pthys AjPNexusAssumptions*
PD AjPNexuAssumptionss object
PX
RT void
RD 
RX
CA delete
CT AjPNexusAssumptions
CD Default destructor
CX
//


static void nexusAssumptionsDel(AjPNexusAssumptions* pthys)
{
    AjPNexusAssumptions thys;

    if (!*pthys)
        return;

    thys = *pthys;

    ajStrDel(&thys->DefType);
    ajStrDel(&thys->PolyTCount);
    ajStrDel(&thys->GapMode);
    nexusArrayDel(&thys->UserType);
    nexusArrayDel(&thys->TypeSet);
    nexusArrayDel(&thys->WtSet);
    nexusArrayDel(&thys->ExSet);
    nexusArrayDel(&thys->AncStates);

    AJFREE(*pthys);

    return;
}
ID nexusCodonsDel
TY static
MO ajnexus
LB core
XX
DE Destructor for AjPNexusCodons
XX
PN [1]
PA d pthys AjPNexusCodons*
PD AjPNexusCodons object
PX
RT void
RD 
RX
CA delete
CT AjPNexusCodons
CD Default destructor
CX
//


static void nexusCodonsDel(AjPNexusCodons* pthys)
{
    AjPNexusCodons thys;

    if(!*pthys)
        return;

    thys = *pthys;

    nexusArrayDel(&thys->CodonPosSet);
    nexusArrayDel(&thys->GeneticCode);
    nexusArrayDel(&thys->CodeSet);

    AJFREE(*pthys);

    return;
}
ID nexusTreesDel
TY static
MO ajnexus
LB core
XX
DE Destructor for AjPNexusTrees
XX
PN [1]
PA d pthys AjPNexusTrees*
PD AjPNexusTrees object
PX
RT void
RD 
RX
CA delete
CT AjPNexusTrees
CD Default destructor
CX
//


static void nexusTreesDel(AjPNexusTrees* pthys)
{
    AjPNexusTrees thys;

    if (!*pthys)
        return;

    thys = *pthys;

    nexusArrayDel(&thys->Translate);
    nexusArrayDel(&thys->Tree);

    AJFREE(*pthys);

    return;
}
ID nexusNotesDel
TY static
MO ajnexus
LB core
XX
DE Destructor for AjPNexusNotes
XX
PN [1]
PA d pthys AjPNexusNotes*
PD AjPNexusNotes object
PX
RT void
RD 
RX
CA delete
CT AjPNexusNotes
CD Default destructor
CX
//


static void nexusNotesDel(AjPNexusNotes* pthys)
{
    AjPNexusNotes thys;

    if (!*pthys)
        return;

    thys = *pthys;

    nexusArrayDel(&thys->Text);
    nexusArrayDel(&thys->Picture);

    AJFREE(*pthys);

    return;
}
ID nexusBlockSave
TY static
MO ajnexus
LB core
XX
DE Saves a nexus block in the appropriate block type
DE
DE Note: data is saved as a character block
XX
PN [1]
PA w thys AjPNexus
PD Nexus object
PX
PN [2]
PA w list AjPList
PD List of block records
PX
PN [3]
PA r blockname const AjPStr
PD Block name
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusBlockSave(AjPNexus thys, AjPList list,
			     const AjPStr blockname)
{
    if (ajStrMatchCaseC(blockname, "taxa"))
    {
	if (!nexusParseTaxa(thys, list))
	    return ajFalse;
    }
    else if (ajStrMatchCaseC(blockname, "characters"))
    {
	if (!nexusParseCharacters(thys, list, ajFalse))
	    return ajFalse;
    }
    else if (ajStrMatchCaseC(blockname, "unaligned"))
    {
	if (!nexusParseUnaligned(thys, list))
	    return ajFalse;
    }
    else if (ajStrMatchCaseC(blockname, "distances"))
    {
	if (!nexusParseDistances(thys, list))
	    return ajFalse;
    }
    else if (ajStrMatchCaseC(blockname, "sets"))
    {
	if (!nexusParseSets(thys, list))
	    return ajFalse;
    }
    else if (ajStrMatchCaseC(blockname, "assumptions"))
    {
	if (!nexusParseAssumptions(thys, list))
	    return ajFalse;
    }
    else if (ajStrMatchCaseC(blockname, "codons"))
    {
	if (!nexusParseCodons(thys, list))
	    return ajFalse;
    }
    else if (ajStrMatchCaseC(blockname, "trees"))
    {
	if (!nexusParseTrees(thys, list))
	    return ajFalse;
    }
    else if (ajStrMatchCaseC(blockname, "notes"))
    {
	if (!nexusParseNotes(thys, list))
	    return ajFalse;
    }
    else if (ajStrMatchCaseC(blockname, "data"))
    {
	if (!nexusParseCharacters(thys, list, ajTrue))
	    return ajFalse;
    }
    else
	ajDebug("nexusBlockSave: unknown block '%S'\n", blockname);

    return ajTrue;
}
ID nexusCommand
TY static
MO ajnexus
LB core
XX
DE Finds the next command and command string in a block
XX
PN [1]
PA u list AjPList
PD List of block records
PX
PN [2]
PA w command AjPStr*
PD Command name
PX
PN [3]
PA w cmdstr AjPStr*
PD Command string
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusCommand(AjPList list, AjPStr* command, AjPStr* cmdstr)
{
    AjPStr rdline = NULL;
    AjPStr tmpstr = NULL;

    AjBool incommand = ajFalse;

    if (!nexusBegCommandExp)
	nexusBegCommandExp = ajRegCompC("^\\s*(\\S*)\\s*");

    if (!nexusEndCommandExp)
	nexusEndCommandExp = ajRegCompC(";\\s*$");

    while (ajListstrPop(list, &rdline))
    {
	if (!incommand)
	{
	    if (ajRegExec(nexusBegCommandExp, rdline))
	    {
		ajRegSubI(nexusBegCommandExp, 1, command);
		ajRegPost(nexusBegCommandExp, cmdstr);

		if (ajRegExec(nexusEndCommandExp, *cmdstr))
		{
		    ajRegPre(nexusEndCommandExp, &tmpstr);
		    ajStrAssignS(cmdstr, tmpstr);
                    ajStrDel(&tmpstr);
                    ajStrDel(&rdline);

		    return ajTrue;
		}

		incommand = ajTrue;
	    }
	}
	else if (incommand)
	{
	    if (ajRegExec(nexusEndCommandExp, rdline))
	    {
		ajRegPre(nexusEndCommandExp, &tmpstr);
		ajStrAppendS(cmdstr, tmpstr);
                ajStrDel(&tmpstr);
                ajStrDel(&rdline);

		return ajTrue;
	    }
	    else
            {
		if (ajStrGetLen(*cmdstr))
		    ajStrAppendK(cmdstr, '\n');

		ajStrAppendS(cmdstr, rdline);
	    }
	}
    }

    ajStrDel(&rdline);

    return ajFalse;
}
ID nexusParseTaxa
TY static
MO ajnexus
LB core
XX
DE Parses Nexus commands for a Taxa block
XX
PN [1]
PA w thys AjPNexus
PD Nexus object
PX
PN [2]
PA u list AjPList
PD List of block records
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusParseTaxa(AjPNexus thys, AjPList list)
{
    AjPStr command = NULL;
    AjPStr cmdstr = NULL;
    ajuint i;

    if (!nexusDimExp)
	nexusDimExp = ajRegCompCaseC("\\bntax\\s*=\\s*(\\d+)");

    if (!nexusTaxLabExp)
	nexusTaxLabExp = ajRegCompCaseC("\\S+");

    if (!thys->Taxa)
	thys->Taxa = nexusTaxaNew();

    while (nexusCommand(list, &command, &cmdstr))
    {
	if (ajStrMatchCaseC(command, "dimensions"))
	{
	    if (!ajRegExec(nexusDimExp, cmdstr))
	    {
		ajDebug("Invalid Taxa '%S'\n", command);
		break;
	    }

	    if (nexusGetInt(cmdstr, nexusDimExp, 1,  &thys->Taxa->Ntax))
	    {
		if (!thys->Ntax)
		    thys->Ntax = thys->Taxa->Ntax;
	    }
	}
	else if (ajStrMatchCaseC(command, "taxlabels"))
	{
	    if (!thys->Taxa->Ntax)
	    {
		ajDebug("nexusParseTaxa failed: dimension Ntax not found\n");
                ajStrDel(&command);
                ajStrDel(&cmdstr);

		return ajFalse;
	    }

	    i = nexusGetArray(cmdstr, nexusTaxLabExp, &thys->Taxa->TaxLabels);

	    if (i != thys->Taxa->Ntax)
	    {
		ajDebug("nexusParseTaxa failed: Ntax %d read %d TaxLabels\n",
			thys->Taxa->Ntax, i);
                ajStrDel(&command);
                ajStrDel(&cmdstr);

		return ajFalse;
	    }
	}
    }

    ajStrDel(&command);
    ajStrDel(&cmdstr);

    if (!thys->Taxa->Ntax)
    {
	ajDebug("nexusParseTaxa failed: taxa data not found\n");

	return ajFalse;
    }

    return ajTrue;
}
ID nexusParseCharacters
TY static
MO ajnexus
LB core
XX
DE Parses Nexus commands for a Characters block
XX
PN [1]
PA w thys AjPNexus
PD Nexus object
PX
PN [2]
PA u list AjPList
PD List of block records
PX
PN [3]
PA r newtaxa AjBool
PD If true, set NewTaxa
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusParseCharacters(AjPNexus thys, AjPList list, AjBool newtaxa)
{
    AjPStr command = NULL;
    AjPStr cmdstr = NULL;
    ajuint i;

    if (!nexusNewlineExp)
	nexusNewlineExp = ajRegCompC("[^\n]+");
    if (!nexusDimExp1)
	nexusDimExp1 = ajRegCompCaseC("\\bnewtaxa\\b");
    if (!nexusDimExp2)
	nexusDimExp2 = ajRegCompCaseC("\\bntax\\s*=\\s*(\\d+)");
    if (!nexusDimExp3)
	nexusDimExp3 = ajRegCompCaseC("\\bnchar\\s*=\\s*(\\d+)");
    if (!nexusFormExp1)
	nexusFormExp1 = ajRegCompCaseC("\\bdatatype\\s*=\\s*(\\S+)");
    if (!nexusFormExp2)
	nexusFormExp2 = ajRegCompCaseC("\\brespectcase\\b");
    if (!nexusFormExp3)
	nexusFormExp3 = ajRegCompCaseC("\\bmissing\\s*=\\s*(\\S+)");
    if (!nexusFormExp4)
	nexusFormExp4 = ajRegCompCaseC("\\bgap\\s*=\\s*(\\S+)");
    if (!nexusFormExp5)
	nexusFormExp5 = ajRegCompCaseC("\\bsymbols\\s*=\\s*['\"]([^'\"]+)[\"']");
    if (!nexusFormExp6)
	nexusFormExp6 = ajRegCompCaseC("\\bequate\\s*=\\s*['\"]([^'\"]+)['\"]");
    if (!nexusFormExp7)
	nexusFormExp7 = ajRegCompCaseC("\\bmatchchar\\s*=\\s*(\\S+)");
    if (!nexusFormExp8)
	nexusFormExp8 = ajRegCompCaseC("\\b(no)?labels");
    if (!nexusFormExp9)
	nexusFormExp9 = ajRegCompCaseC("\\btranspose\\b");
    if (!nexusFormExp10)
	nexusFormExp10 = ajRegCompCaseC("\\binterleave\\b");
    if (!nexusFormExp11)
	nexusFormExp11 = ajRegCompCaseC("\\bitems\\s*=\\s*[(]([^)]+)[)]\\b");
    if (!nexusFormExp12)
	nexusFormExp12 = ajRegCompCaseC("\\bstatesformat\\s*=\\s*[{]([^}]+)[}]\\b");
    if (!nexusFormExp13)
	nexusFormExp13 = ajRegCompCaseC("\\b(no)?tokens");
    if(!nexusTaxLabExp)
        nexusTaxLabExp = ajRegCompCaseC("\\S+");
    if (!nexusChstLabExp)
	nexusChstLabExp = ajRegCompCaseC("\\d+\\s+[^,]+");
    if (!nexusChLabExp)
	nexusChLabExp = ajRegCompCaseC("\\S+");
    if (!nexusOptExp3)
	nexusOptExp3 = ajRegCompCaseC("\\bgapmode\\s*=\\s*(\\S+)");
 
    if (!thys->Characters)
	thys->Characters = nexusCharactersNew();

    if(newtaxa)
	thys->Characters->NewTaxa = ajTrue;

    while (nexusCommand(list, &command, &cmdstr))
    {
	if (ajStrMatchCaseC(command, "dimensions"))
	{
	    if(ajRegExec(nexusDimExp1, cmdstr))
		thys->Characters->NewTaxa = ajTrue;

	    if(nexusGetInt(cmdstr, nexusDimExp2, 1, &thys->Characters->Ntax))
	    {
		if (thys->Ntax)
		    ajDebug("nexus characters dimension ntax - "
			    "taxlabels already done\n");
		else
		{
		    if (!thys->Taxa)
			thys->Taxa = nexusTaxaNew();
		    thys->Ntax = thys->Taxa->Ntax = thys->Characters->Ntax;
		}
	    }

	    if(!nexusGetInt(cmdstr, nexusDimExp3, 1, &thys->Characters->Nchar))
	    {
		ajDebug("nexusParseCharacters failed: nchar undefined\n");
                ajStrDel(&command);
                ajStrDel(&cmdstr);
		return ajFalse;
	    }
	}
	else if (ajStrMatchCaseC(command, "format"))
	{
	    if(nexusGetStr(cmdstr, nexusFormExp1, 1, &thys->Characters->DataType))
		nexusVocab("characters format datatype",
			   thys->Characters->DataType,
			   nexusCharactersDataTypes);

	    if(ajRegExec(nexusFormExp2, cmdstr))
		thys->Characters->RespectCase = ajTrue;

	    nexusGetChar(cmdstr, nexusFormExp3, 1, &thys->Characters->Missing);
	    nexusGetChar(cmdstr, nexusFormExp4, 1, &thys->Characters->Gap);
	    nexusGetStr(cmdstr, nexusFormExp5, 1, &thys->Characters->Symbols);
	    nexusGetStr(cmdstr, nexusFormExp6, 1, &thys->Characters->Equate);
	    nexusGetChar(cmdstr, nexusFormExp7, 1, &thys->Characters->MatchChar);
	    nexusGetBool(cmdstr, nexusFormExp8, 1, &thys->Characters->Labels);

	    if(ajRegExec(nexusFormExp9, cmdstr))
		thys->Characters->Transpose = ajTrue;

	    if(ajRegExec(nexusFormExp10, cmdstr))
		thys->Characters->Interleave = ajTrue;

	    nexusGetStr(cmdstr, nexusFormExp11, 0, &thys->Characters->Items);

	    if(nexusGetStr(cmdstr, nexusFormExp12, 0,
			   &thys->Characters->StatesFormat))
		nexusVocab("characters format statesformat",
			   thys->Characters->StatesFormat,
			   nexusCharactersStatesFormat);
	    nexusGetBool(cmdstr, nexusFormExp13, 0, &thys->Characters->Tokens);
	}
	else if (ajStrMatchCaseC(command, "eliminate"))
	{
	    ajStrRemoveWhiteExcess(&cmdstr);
	    ajStrAssignS(&thys->Characters->Eliminate, cmdstr);
	}
	else if (ajStrMatchCaseC(command, "taxlabels"))
	{
	    if (!thys->Taxa)
		thys->Taxa = nexusTaxaNew();

	    i = nexusGetArray(cmdstr, nexusTaxLabExp, &thys->Taxa->TaxLabels);

	    if (i != thys->Taxa->Ntax)
	    {
		ajDebug("nexusParseCharacters "
			"failed: Ntax %d read %d TaxLabels\n",
			thys->Taxa->Ntax, i);
                ajStrDel(&command);
                ajStrDel(&cmdstr);

		return ajFalse;
	    }
	}
	else if (ajStrMatchCaseC(command, "charstatelabels"))
	{
	    i = nexusGetArray(cmdstr, nexusChstLabExp,
			    &thys->Characters->CharStateLabels);
	}
	else if (ajStrMatchCaseC(command, "charlabels"))
	{
	    i = nexusGetArray(cmdstr, nexusChLabExp,
			    &thys->Characters->CharLabels);
	}
	else if (ajStrMatchCaseC(command, "statelabels"))
	{
	    i = nexusGetArray(cmdstr, nexusChstLabExp,
			    &thys->Characters->StateLabels);
	}
	else if (ajStrMatchCaseC(command, "matrix"))
	{
	    i = nexusGetArray(cmdstr, nexusNewlineExp,
                              &thys->Characters->Matrix);
	}
	else if (ajStrMatchCaseC(command, "options")) /* not 1997 standard */
	{
	    if (!thys->Assumptions)
		thys->Assumptions = nexusAssumptionsNew();

	    if(nexusGetStr(cmdstr, nexusOptExp3, 1, /* should be in assumptions */
			&thys->Assumptions->GapMode))
		nexusVocab("characters options gapmode",
			   thys->Assumptions->GapMode,
			   nexusAssumptionsGapMode);
	}
	else
	{
	    ajDebug("nexusParseCharacters unknown command '%S' ignored\n",
		    command);
	}
    }

    ajStrDel(&command);
    ajStrDel(&cmdstr);

    if (!thys->Characters->Nchar)
    {
	ajDebug("nexusParseCharacters failed: dimension nchar undefined\n");

	return ajFalse;
    }

    return ajTrue;
}
ID nexusParseUnaligned
TY static
MO ajnexus
LB core
XX
DE Parses Nexus commands for a Unaligned block
XX
PN [1]
PA w thys AjPNexus
PD Nexus object
PX
PN [2]
PA u list AjPList
PD List of block records
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusParseUnaligned(AjPNexus thys, AjPList list)
{
    AjPStr command = NULL;
    AjPStr cmdstr = NULL;

    if (!nexusDimExp1)
	nexusDimExp1 = ajRegCompCaseC("\\bnewtaxa\\b");
    if (!nexusDimExp2)
	nexusDimExp2 = ajRegCompCaseC("\\bntax\\s*=\\s*(\\d+)");
    if (!nexusFormExp1)
	nexusFormExp1 = ajRegCompCaseC("\\bdatatype\\s*=\\s*(\\S+)");
    if (!nexusFormExp2)
	nexusFormExp2 = ajRegCompCaseC("\\brespectcase\\b");
    if (!nexusFormExp3)
	nexusFormExp3 = ajRegCompCaseC("\\bmissing\\s*=\\s*(\\S+)");
    if (!nexusFormExp5)
	nexusFormExp5 = ajRegCompCaseC("\\bsymbols\\s*=\\s*['\"]([^'\"]+)['\"]");
    if (!nexusFormExp6)
	nexusFormExp6 = ajRegCompCaseC("\\bequate\\s*=\\s*['\"]([^'\"]+)['\"]");
    if (!nexusFormExp8)
	nexusFormExp8 = ajRegCompCaseC("\\b(no)?labels");

    if (!thys->Unaligned)
	thys->Unaligned = nexusUnalignedNew();

    while (nexusCommand(list, &command, &cmdstr))
    {
	if (ajStrMatchCaseC(command, "dimensions"))
	{
	    if(ajRegExec(nexusDimExp1, cmdstr))
		thys->Unaligned->NewTaxa = ajTrue;

	    if(nexusGetInt(cmdstr, nexusDimExp2, 1, &thys->Unaligned->Ntax))
	    {
		if (thys->Ntax)
		    ajDebug("nexus unaligned dimension ntax - "
			    "taxlabels already done\n");
		else
		{
		    if (!thys->Taxa)
			thys->Taxa = nexusTaxaNew();
		    thys->Ntax = thys->Taxa->Ntax = thys->Unaligned->Ntax;
		}
	    }
	}
	else if (ajStrMatchCaseC(command, "format"))
	{
	    if(nexusGetStr(cmdstr, nexusFormExp1, 1, &thys->Unaligned->DataType))
		nexusVocab("unaligned format datatype",
			   thys->Unaligned->DataType,
			   nexusUnalignedDataTypes);

	    if(ajRegExec(nexusFormExp2, cmdstr))
		thys->Unaligned->RespectCase = ajTrue;

	    nexusGetChar(cmdstr, nexusFormExp3, 1, &thys->Unaligned->Missing);
	    nexusGetStr(cmdstr, nexusFormExp5, 1, &thys->Unaligned->Symbols);
	    nexusGetStr(cmdstr, nexusFormExp6, 1, &thys->Unaligned->Equate);
	    nexusGetBool(cmdstr, nexusFormExp8, 1, &thys->Unaligned->Labels);
	}
	else if (ajStrMatchCaseC(command, "matrix"))
	{
	    nexusGetArray(cmdstr, nexusNewlineExp, &thys->Unaligned->Matrix);
	}
	else
	{
	    ajDebug("nexusParseUnaligned unknown command '%S' ignored\n",
		    command);
	}
    }

    ajStrDel(&command);
    ajStrDel(&cmdstr);

    return ajTrue;
}
ID nexusParseDistances
TY static
MO ajnexus
LB core
XX
DE Parses Nexus commands for a Distances block
XX
PN [1]
PA w thys AjPNexus
PD Nexus object
PX
PN [2]
PA u list AjPList
PD List of block records
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusParseDistances(AjPNexus thys, AjPList list)
{
    AjPStr command = NULL;
    AjPStr cmdstr = NULL;
    ajuint i;

    if (!nexusDimExp1)
	nexusDimExp1 = ajRegCompCaseC("\\bnewtaxa\\b");
    if (!nexusDimExp2)
	nexusDimExp2 = ajRegCompCaseC("\\bntax\\s*=\\s*(\\d+)");
    if (!nexusDimExp3)
	nexusDimExp3 = ajRegCompCaseC("\\bnchar\\s*=\\s*(\\d+)");
    if (!nexusFormExp1)
	nexusFormExp1 = ajRegCompCaseC("\\btriangle\\s*=\\s*(\\S+)");
    if (!nexusFormExp2)
	nexusFormExp2 = ajRegCompCaseC("\\b(no)?diagonal\\b");
    if (!nexusFormExp3)
	nexusFormExp3 = ajRegCompCaseC("\\b(no)?labels\\b");
    if (!nexusFormExp4)
	nexusFormExp4 = ajRegCompCaseC("\\bmissing\\s*=\\s*(\\S+)");
    if (!nexusFormExp5)
	nexusFormExp5 = ajRegCompCaseC("\\binterleave\\b");
    if (!nexusTaxLabExp)
	nexusTaxLabExp = ajRegCompCaseC("\\S+");

    if (!thys->Distances)
	thys->Distances = nexusDistancesNew();

    while (nexusCommand(list, &command, &cmdstr))
    {
	if (ajStrMatchCaseC(command, "dimensions"))
	{
	    if(ajRegExec(nexusDimExp1,cmdstr))
	       thys->Distances->NewTaxa = ajTrue;

	    if(nexusGetInt(cmdstr, nexusDimExp2, 1, &thys->Distances->Ntax))
	    {
		if (thys->Ntax)
		    ajDebug("nexus distances dimension ntax - "
			    "taxlabels already done\n");
		else
		{
		    if (!thys->Taxa)
			thys->Taxa = nexusTaxaNew();

		    thys->Ntax = thys->Taxa->Ntax = thys->Distances->Ntax;
		}
	    }

	    /* nchar is optional for distances only */
	    nexusGetInt(cmdstr, nexusDimExp3, 1, &thys->Distances->Nchar);
	}
	else if (ajStrMatchCaseC(command, "format"))
	{
	    if(nexusGetStr(cmdstr, nexusFormExp1, 1, &thys->Distances->Triangle))
		nexusVocab("distances format triangle",
			   thys->Distances->Triangle,
			   nexusDistancesTriangle);

	    nexusGetBool(cmdstr, nexusFormExp2, 1, &thys->Distances->Diagonal);
	    nexusGetBool(cmdstr, nexusFormExp3, 1, &thys->Distances->Labels);
	    nexusGetChar(cmdstr, nexusFormExp4, 1, &thys->Distances->Missing);

	    if(ajRegExec(nexusFormExp5,cmdstr))
	       thys->Distances->Interleave = ajTrue;
	}
	else if (ajStrMatchCaseC(command, "taxlabels"))
	{
	    if (!thys->Taxa)
		thys->Taxa = nexusTaxaNew();

	    i = nexusGetArray(cmdstr, nexusTaxLabExp, &thys->Taxa->TaxLabels);

	    if (i != thys->Taxa->Ntax)
	    {
		ajDebug("nexusParseCharacters failed: "
			"Ntax %d read %d TaxLabels\n",
			thys->Taxa->Ntax, i);

		return ajFalse;
	    }
	}
	else if (ajStrMatchCaseC(command, "matrix"))
	    i = nexusGetArray(cmdstr, nexusNewlineExp,
                              &thys->Characters->Matrix);
	else
	{
	    ajDebug("nexusParseDistances unknown command '%S' ignored\n",
		    command);
	}
    }

    ajStrDel(&command);
    ajStrDel(&cmdstr);

    return ajTrue;
}
ID nexusParseSets
TY static
MO ajnexus
LB core
XX
DE Parses Nexus commands for a Sets block
XX
PN [1]
PA w thys AjPNexus
PD Nexus object
PX
PN [2]
PA u list AjPList
PD List of block records
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusParseSets(AjPNexus thys, AjPList list)
{
    AjPStr command = NULL;
    AjPStr cmdstr = NULL;

    if (!nexusNewlineExp)
	nexusNewlineExp = ajRegCompC("[^\n]+");

    if (!thys->Sets)
	thys->Sets = nexusSetsNew();

    while (nexusCommand(list, &command, &cmdstr))
    {
	ajDebug("nexusParseSets %S: '%S'\n", command, cmdstr);

	if (ajStrMatchCaseC(command, "charset"))
	    nexusGetArray(cmdstr, nexusNewlineExp, &thys->Sets->CharSet);
	else if (ajStrMatchCaseC(command, "stateset"))
	    nexusGetArray(cmdstr, nexusNewlineExp, &thys->Sets->StateSet);
	else if (ajStrMatchCaseC(command, "changeset"))
	    nexusGetArray(cmdstr, nexusNewlineExp, &thys->Sets->ChangeSet);
	else if (ajStrMatchCaseC(command, "taxset"))
	    nexusGetArray(cmdstr, nexusNewlineExp, &thys->Sets->TaxSet);
	else if (ajStrMatchCaseC(command, "treeset"))
	    nexusGetArray(cmdstr, nexusNewlineExp, &thys->Sets->TreeSet);
	else if (ajStrMatchCaseC(command, "charpartition"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Sets->CharPartition);
	else if (ajStrMatchCaseC(command, "taxpartition"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Sets->TaxPartition);
	else if (ajStrMatchCaseC(command, "treepartition"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Sets->TreePartition);
	else
	{
	    ajDebug("nexusParseSets unknown command '%S' ignored\n",
		    command);
	}
    }

    ajStrDel(&command);
    ajStrDel(&cmdstr);

    return ajTrue;
}
ID nexusParseAssumptions
TY static
MO ajnexus
LB core
XX
DE Parses Nexus commands for a Assumptions block
XX
PN [1]
PA w thys AjPNexus
PD Nexus object
PX
PN [2]
PA u list AjPList
PD List of block records
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusParseAssumptions(AjPNexus thys, AjPList list)
{
    AjPStr command = NULL;
    AjPStr cmdstr = NULL;

    if (!nexusNewlineExp)
	nexusNewlineExp = ajRegCompC("[^\n]+");
    if (!nexusOptExp1)
	nexusOptExp1 = ajRegCompCaseC("\\bdeftype\\s*=\\s*(\\S+)");
    if (!nexusOptExp2)
	nexusOptExp2 = ajRegCompCaseC("\\bpolycount\\s*=\\s*(\\S+)");
    if (!nexusOptExp3)
	nexusOptExp3 = ajRegCompCaseC("\\bgapmode\\s*=\\s*(\\S+)");

    if (!thys->Assumptions)
	thys->Assumptions = nexusAssumptionsNew();

    while (nexusCommand(list, &command, &cmdstr))
    {
	if (ajStrMatchCaseC(command, "options"))
	{
	    if(nexusGetStr(cmdstr, nexusOptExp1, 1,
                           &thys->Assumptions->DefType))
		nexusVocab("assumptions options deftype",
			   thys->Assumptions->DefType,
			   nexusAssumptionsDefType);
	    if(nexusGetStr(cmdstr, nexusOptExp2, 1,
                           &thys->Assumptions->PolyTCount))
		nexusVocab("assumptions options polytcount",
			   thys->Assumptions->PolyTCount,
			   nexusAssumptionsPolyTCount);
	    if(nexusGetStr(cmdstr, nexusOptExp3, 1,
                           &thys->Assumptions->GapMode))
		nexusVocab("assumptions options gapmode",
			   thys->Assumptions->GapMode,
			   nexusAssumptionsGapMode);
	}
	else if (ajStrMatchCaseC(command, "usertype"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Assumptions->UserType);
	else if (ajStrMatchCaseC(command, "typeset"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Assumptions->TypeSet);
	else if (ajStrMatchCaseC(command, "wtset"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Assumptions->WtSet);
	else if (ajStrMatchCaseC(command, "exset"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Assumptions->ExSet);
	else if (ajStrMatchCaseC(command, "ancstates"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Assumptions->AncStates);
	else if (ajStrMatchCaseC(command, "taxset")) /* not 1997 standard */
	{
	    if (!thys->Sets)
		thys->Sets = nexusSetsNew();
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Sets->TaxSet);
	}
	else if (ajStrMatchCaseC(command, "charset")) /* not 1997 standard */
	{
	    if (!thys->Sets)
		thys->Sets = nexusSetsNew();
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Sets->CharSet);
	}
	else
	{
	    ajDebug("nexusParseAssumptions unknown command '%S' ignored\n",
		    command);
	}
    }

    ajStrDel(&command);
    ajStrDel(&cmdstr);

    return ajTrue;
}
ID nexusParseCodons
TY static
MO ajnexus
LB core
XX
DE Parses Nexus commands for a Codons block
XX
PN [1]
PA w thys AjPNexus
PD Nexus object
PX
PN [2]
PA u list AjPList
PD List of block records
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusParseCodons(AjPNexus thys, AjPList list)
{
    AjPStr command = NULL;
    AjPStr cmdstr = NULL;

    if (!nexusNewlineExp)
	nexusNewlineExp = ajRegCompC("[^\n]+");

    if (!thys->Codons)
	thys->Codons = nexusCodonsNew();

    while (nexusCommand(list, &command, &cmdstr))
    {
	if (ajStrMatchCaseC(command, "codonposset"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Codons->CodonPosSet);
	else if (ajStrMatchCaseC(command, "geneticcode"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Codons->GeneticCode);
	else if (ajStrMatchCaseC(command, "codeset"))
	    nexusGetArray(cmdstr, nexusNewlineExp,
			  &thys->Codons->CodeSet);
	else
	{
	    ajDebug("nexusParseCodons unknown command '%S' ignored\n",
		    command);
	}
    }

    ajStrDel(&command);
    ajStrDel(&cmdstr);

    return ajTrue;
}
ID nexusParseTrees
TY static
MO ajnexus
LB core
XX
DE Parses Nexus commands for a Trees block
XX
PN [1]
PA w thys AjPNexus
PD Nexus object
PX
PN [2]
PA u list AjPList
PD List of block records
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusParseTrees(AjPNexus thys, AjPList list)
{
    AjPStr command = NULL;
    AjPStr cmdstr = NULL;

    if (!nexusNewlineExp)
	nexusNewlineExp = ajRegCompC("[^\n]+");

    if (!thys->Trees)
	thys->Trees = nexusTreesNew();

    while (nexusCommand(list, &command, &cmdstr))
    {
	if (ajStrMatchCaseC(command, "translate"))
	    nexusGetArray(cmdstr, nexusNewlineExp, &thys->Trees->Translate);
	else if (ajStrMatchCaseC(command, "tree"))
	    nexusGetArray(cmdstr, nexusNewlineExp, &thys->Trees->Tree);
	else
	{
	    ajDebug("nexusParseTrees unknown command '%S' ignored\n",
		    command);
	}
    }

    ajStrDel(&command);
    ajStrDel(&cmdstr);

    return ajTrue;
}
ID nexusParseNotes
TY static
MO ajnexus
LB core
XX
DE Parses Nexus commands for a Notes block
XX
PN [1]
PA w thys AjPNexus
PD Nexus object
PX
PN [2]
PA u list AjPList
PD List of block records
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusParseNotes(AjPNexus thys, AjPList list)
{
    AjPStr command = NULL;
    AjPStr cmdstr = NULL;

    if (!nexusNewlineExp)
	nexusNewlineExp = ajRegCompC("[^\n]+");

    if (!thys->Notes)
	thys->Notes = nexusNotesNew();

    while (nexusCommand(list, &command, &cmdstr))
    {
	if (ajStrMatchCaseC(command, "text"))
	    nexusGetArray(cmdstr, nexusNewlineExp, &thys->Notes->Text);
	else if (ajStrMatchCaseC(command, "picture"))
	    nexusGetArray(cmdstr, nexusNewlineExp, &thys->Notes->Picture);
	else
	{
	    ajDebug("nexusParseNotes unknown command '%S' ignored\n",
		    command);
	}
    }

    ajStrDel(&command);
    ajStrDel(&cmdstr);

    return ajTrue;
}
ID nexusGetArray
TY static
MO ajnexus
LB core
XX
DE Gets an array of strings from a parsed nexus command
DE Appends to an existing array to handle multiple commands
XX
PN [1]
PA w src AjPStr
PD Command string
PX
PN [2]
PA u exp AjPRegexp
PD Compiled regular expression for parsing
PX
PN [3]
PA w dest AjPStr**
PD Array generated
PX
RT ajint
RD Number of strings returned
RX
//


static ajint nexusGetArray(AjPStr src, AjPRegexp exp,
			    AjPStr** dest)
{
    AjPList strlist;
    AjPStr tmpstr = NULL;
    AjPStr tmpsrc = NULL;
    ajint i;

    strlist = ajListstrNew();
    ajStrAssignS(&tmpsrc, src);

    while (ajRegExec(exp, tmpsrc))
    {
	ajRegSubI(exp, 0, &tmpstr);
	ajListstrPushAppend(strlist, tmpstr);
	tmpstr = NULL;
	ajRegPost(exp, &tmpstr);
	ajStrAssignS(&tmpsrc, tmpstr);
    }

    i = (ajuint) ajListstrToarrayAppend(strlist, dest);
    ajListstrFree(&strlist);
    ajStrDel(&tmpstr);
    ajStrDel(&tmpsrc);

    return i;
}
ID nexusGetStr
TY static
MO ajnexus
LB core
XX
DE Gets a string from a parsed nexus command
XX
PN [1]
PA w src AjPStr
PD Command string
PX
PN [2]
PA u exp AjPRegexp
PD Compiled regular expression for parsing
PX
PN [3]
PA r isub ajint
PD Substring number to extract
PX
PN [4]
PA w dest AjPStr*
PD String generated
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusGetStr(AjPStr src, AjPRegexp exp, ajint isub, AjPStr* dest)
{
    if (ajRegExec(exp, src))
    {
	ajRegSubI(exp, isub, dest);

	return ajTrue;
    }

    return ajFalse;
}
ID nexusGetChar
TY static
MO ajnexus
LB core
XX
DE Gets a single character from a parsed nexus command
XX
PN [1]
PA w src AjPStr
PD Command string
PX
PN [2]
PA u exp AjPRegexp
PD Compiled regular expression for parsing
PX
PN [3]
PA r isub ajint
PD Substring number to extract
PX
PN [4]
PA w dest char*
PD String generated
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusGetChar(AjPStr src, AjPRegexp exp, ajint isub, char* dest)
{
    if (ajRegExec(exp, src))
    {
	ajRegSubI(exp, isub, &nexusTmpStr);

	if (ajStrGetLen(nexusTmpStr))
	{
	    *dest = ajStrGetCharFirst(nexusTmpStr);

	    return ajTrue;
	}

	*dest = '\0';

	return ajFalse;
    }

    return ajFalse;
}
ID nexusGetInt
TY static
MO ajnexus
LB core
XX
DE Gets an integer from a parsed nexus command
XX
PN [1]
PA w src AjPStr
PD Command string
PX
PN [2]
PA u exp AjPRegexp
PD Compiled regular expression for parsing
PX
PN [3]
PA r isub ajint
PD Substring number to extract
PX
PN [4]
PA w dest ajuint*
PD Integer generated
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusGetInt(AjPStr src, AjPRegexp exp, ajint isub, ajuint* dest)
{
    if (ajRegExec(exp, src))
    {
	ajRegSubI(exp, isub, &nexusTmpStr);

	return (ajStrToUint(nexusTmpStr, dest));
    }

    return ajFalse;
}
ID nexusGetBool
TY static
MO ajnexus
LB core
XX
DE Sets a nexus bool from a parsed nexus command in the form [no]name.
DE We know name is found, we test for the "no" part.
XX
PN [1]
PA w src AjPStr
PD Command string
PX
PN [2]
PA u exp AjPRegexp
PD Compiled regular expression for parsing
PX
PN [3]
PA r isub ajint
PD Substring number to extract
PX
PN [4]
PA w dest AjBool*
PD Boolean generated
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusGetBool(AjPStr src, AjPRegexp exp, ajint isub, AjBool* dest)
{
    if (ajRegExec(exp, src))
    {
	if (!ajRegSubI(exp, isub, &nexusTmpStr))
	    *dest = ajTrue;
	else
	    *dest = ajFalse;

	return ajTrue;
	    
    }

    return ajFalse;
}
ID nexusVocab
TY static
MO ajnexus
LB core
XX
DE Tests a string against a controlled vocabulary
XX
PN [1]
PA r title const char*
PD Vocabulary title
PX
PN [2]
PA r src const AjPStr
PD String to be tested
PX
PN [3]
PA r vocab const char* []
PD List of known values, ending in a NULL
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusVocab(const char* title, const AjPStr src,
			 const char* vocab[])
{
    ajint i;

    for (i=0; vocab[i]; i++)
	if (ajStrMatchCaseC(src, vocab[i]))
	    return ajTrue;

    ajDebug("nexus vocab failed: '%S' unknown for %s\n",
	    src, title);

    return ajFalse;
}
ID ajNexusTrace
TY public
MO ajnexus
LB core
XX
DE Reports the contents of a Nexus object to the debug file
XX
PN [1]
PA r thys const AjPNexus
PD nexus object
PX
RT void
RD 
RX
//


void ajNexusTrace(const AjPNexus thys)
{
    ajint i;

    ajDebug("Nexus trace\n");
    ajDebug("===========\n");

    ajDebug ("  Ntax: %d\n", thys->Ntax);

    if (!thys->Taxa)
	ajDebug("Taxa: \n");
    else
    {
	ajDebug("Taxa\n");
	ajDebug("----\n");
	ajDebug("  Ntax: %d\n", thys->Taxa->Ntax);

	if (thys->Taxa->TaxLabels)
	{
	    ajDebug("  TaxLabels:\n");

	    for (i=0; thys->Taxa->TaxLabels[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Taxa->TaxLabels[i]);
	}
	else
		ajDebug("  TaxLabels: \n");
    }

    if (!thys->Characters)
	ajDebug("Characters: \n");
    else
    {
	ajDebug("Characters\n");
	ajDebug("----------\n");
	ajDebug("  NewTaxa: %B\n", thys->Characters->NewTaxa);
	ajDebug("  Ntax: %d\n", thys->Characters->Ntax);
	ajDebug("  Nchar: %d\n", thys->Characters->Nchar);

	if (thys->Characters->DataType)
	    ajDebug("  DataType: '%S'\n", thys->Characters->DataType);
	else
	    ajDebug("  DataType: \n");

	ajDebug("  RespectCase: %B\n", thys->Characters->RespectCase);

	if (thys->Characters->Missing)
	    ajDebug("  Missing: '%c'\n", thys->Characters->Missing);
	else
	    ajDebug("  Missing: \n");

	if (thys->Characters->Gap)
	    ajDebug("  Gap: '%c'\n", thys->Characters->Gap);
	else
	    ajDebug("  Gap: \n");

	if (thys->Characters->Symbols)
	    ajDebug("  Symbols: '%S'\n", thys->Characters->Symbols);
	else
	    ajDebug("  Symbols: \n");

	if (thys->Characters->Equate)
	    ajDebug("  Equate: '%S'\n", thys->Characters->Equate);
	else
	    ajDebug("  Equate: \n");

	if (thys->Characters->MatchChar)
	    ajDebug("  MatchChar: '%c'\n", thys->Characters->MatchChar);
	else
	    ajDebug("  MatchChar: \n");

	ajDebug("  Labels: %B\n", thys->Characters->Labels);
	ajDebug("  Transpose: %B\n", thys->Characters->Transpose);
	ajDebug("  Interleave: %B\n", thys->Characters->Interleave);

	if (thys->Characters->Items)
	    ajDebug("  Items: '%S'\n", thys->Characters->Items);
	else
	    ajDebug("  Items: \n");

	if (thys->Characters->StatesFormat)
	    ajDebug("  StatesFormat: '%S'\n", thys->Characters->StatesFormat);
	else
	    ajDebug("  StatesFormat: \n");

	ajDebug("  Tokens: %B\n", thys->Characters->Tokens);

	if (thys->Characters->Eliminate)
	    ajDebug("  Eliminate: '%S'\n", thys->Characters->Eliminate);
	else
	    ajDebug("  Eliminate: \n");

	if (thys->Characters->CharStateLabels)
	{
	    ajDebug("  CharStateLabels:\n");

	    for (i=0; thys->Characters->CharStateLabels[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Characters->CharStateLabels[i]);
	}
	else
	    ajDebug("  CharStateLabels: \n");

	if (thys->Characters->CharLabels)
	{
	    ajDebug("  CharLabels:\n");
	    for (i=0; thys->Characters->CharLabels[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Characters->CharLabels[i]);
	}
	else
	    ajDebug("  CharLabels: \n");

	if (thys->Characters->StateLabels)
	{
	    ajDebug("  StateLabels:\n");

	    for (i=0; thys->Characters->StateLabels[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Characters->StateLabels[i]);
	}
	else
	    ajDebug("  StateLabels: \n");

	if (thys->Characters->Matrix)
	{
	    ajDebug("  Matrix:\n");

	    for (i=0; thys->Characters->Matrix[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Characters->Matrix[i]);
	}
	else
	    ajDebug("  Matrix: \n");
    }

    if (!thys->Unaligned)
	ajDebug("Unaligned: \n");
    else
    {
	ajDebug("Unaligned\n");
	ajDebug("---------\n");
	ajDebug("  NewTaxa: %B\n", thys->Unaligned->NewTaxa);
	ajDebug("  Ntax: %d\n", thys->Unaligned->Ntax);

	if (thys->Unaligned->DataType)
	    ajDebug("  DataType: '%S'\n", thys->Unaligned->DataType);
	else
	    ajDebug("  DataType: \n");

	ajDebug("  RespectCase: %B\n", thys->Unaligned->RespectCase);

	if (thys->Unaligned->Missing)
	    ajDebug("  Missing: '%c'\n", thys->Unaligned->Missing);
	else
	    ajDebug("  Missing: \n");

	if (thys->Unaligned->Symbols)
	    ajDebug("  Symbols: '%S'\n", thys->Unaligned->Symbols);
	else
	    ajDebug("  Symbols: \n");

	if (thys->Unaligned->Equate)
	    ajDebug("  Equate: '%S'\n", thys->Unaligned->Equate);
	else
	    ajDebug("  Equate: \n");

	ajDebug("  Labels: %B\n", thys->Unaligned->Labels);

	if (thys->Unaligned->Matrix)
	{
	    ajDebug("  Matrix:\n");

	    for (i=0; thys->Unaligned->Matrix[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Unaligned->Matrix[i]);
	}
	else
	    ajDebug("  Matrix: \n");
    }

    if (!thys->Distances)
	ajDebug("Distances: \n");
    else
    {
	ajDebug("Distances\n");
	ajDebug("---------\n");
	ajDebug("NewTaxa: %B\n", thys->Distances->NewTaxa);
	ajDebug("  Ntax: %d\n", thys->Distances->Ntax);
	ajDebug("  Nchar: %d\n", thys->Distances->Nchar);

	if (thys->Distances->Triangle)
	    ajDebug("  Triangle: '%S'\n", thys->Distances->Triangle);
	else
	    ajDebug("  Triangle: \n");

	ajDebug("  Diagonal: %B\n", thys->Distances->Diagonal);
	ajDebug("  Labels: %B\n", thys->Distances->Labels);

	if (thys->Distances->Missing)
	    ajDebug("  Missing: '%c'\n", thys->Distances->Missing);
	else
	    ajDebug("  Missing: \n");

	ajDebug("  Interleave: %B\n", thys->Distances->Interleave);

	if (thys->Distances->Matrix)
	{
	    ajDebug("  Matrix:\n");

	    for (i=0; thys->Distances->Matrix[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Distances->Matrix[i]);
	}
	else
	    ajDebug("  Matrix: \n");
    }

    if (!thys->Sets)
	ajDebug("Sets: \n");
    else
    {
	ajDebug("Sets\n");
	ajDebug("----\n");

	if (thys->Sets->CharSet)
	{
	    ajDebug("  CharSet:\n");

	    for (i=0; thys->Sets->CharSet[i]; i++)
		ajDebug("   [%d] '%S'\n",
			i, thys->Sets->CharSet[i]);
	}
	else
	    ajDebug("  CharSet: \n");

	if (thys->Sets->StateSet)
	{
	    ajDebug("  StateSet:\n");

	    for (i=0; thys->Sets->StateSet[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Sets->StateSet[i]);
	}
	else
	    ajDebug("  StateSet: \n");

	if (thys->Sets->ChangeSet)
	{
	    ajDebug("  ChangeSet:\n");

	    for (i=0; thys->Sets->ChangeSet[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Sets->ChangeSet[i]);
	}
	else
	    ajDebug("  ChangeSet: \n");

	if (thys->Sets->TaxSet)
	{
	    ajDebug("  TaxSet:\n");

	    for (i=0; thys->Sets->TaxSet[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Sets->TaxSet[i]);
	}
	else
	    ajDebug("  TaxSet: \n");

	if (thys->Sets->TreeSet)
	{
	    ajDebug("  TreeSet:\n");

	    for (i=0; thys->Sets->TreeSet[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Sets->TreeSet[i]);
	}
	else
	    ajDebug("  TreeSet: \n");

	if (thys->Sets->CharPartition)
	{
	    ajDebug("  CharPartition:\n");

	    for (i=0; thys->Sets->CharPartition[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Sets->CharPartition[i]);
	}
	else
	    ajDebug("  CharPartition: \n");

	if (thys->Sets->TaxPartition)
	{
	    ajDebug("  TaxPartition:\n");

	    for (i=0; thys->Sets->TaxPartition[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Sets->TaxPartition[i]);
	}
	else
	    ajDebug("  TaxPartition: \n");

	if (thys->Sets->TreePartition)
	{
	    ajDebug("  TreePartition:\n");

	    for (i=0; thys->Sets->TreePartition[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Sets->TreePartition[i]);
	}
	else
	    ajDebug("  TreePartition: \n");
    }

    if (!thys->Assumptions)
	ajDebug("Assumptions: \n");
    else
    {
	ajDebug("Assumptions\n");
	ajDebug("-----------\n");

	if (thys->Assumptions->DefType)
	    ajDebug("  DefType: '%S'\n", thys->Assumptions->DefType);
	else
	    ajDebug("  DefType: \n");

	if (thys->Assumptions->PolyTCount)
	    ajDebug("  PolyTCount: '%S'\n", thys->Assumptions->PolyTCount);
	else
	    ajDebug("  PolyTCount: \n");

	if (thys->Assumptions->GapMode)
	    ajDebug("  GapMode: '%S'\n", thys->Assumptions->GapMode);
	else
	    ajDebug("  GapMode: \n");

	if (thys->Assumptions->UserType)
	{
	    ajDebug("  UserType:\n");

	    for (i=0; thys->Assumptions->UserType[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Assumptions->UserType[i]);
	}
	else
	    ajDebug("  UserType: \n");

	if (thys->Assumptions->TypeSet)
	{
	    ajDebug("  TypeSet:\n");

	    for (i=0; thys->Assumptions->TypeSet[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Assumptions->TypeSet[i]);
	}
	else
	    ajDebug("  TypeSet: \n");

	if (thys->Assumptions->WtSet)
	{
	    ajDebug("  WtSet:\n");

	    for (i=0; thys->Assumptions->WtSet[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Assumptions->WtSet[i]);
	}
	else
	    ajDebug("  WtSet: \n");

	if (thys->Assumptions->ExSet)
	{
	    ajDebug("  ExSet:\n");
	    for (i=0; thys->Assumptions->ExSet[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Assumptions->ExSet[i]);
	}
	else
	    ajDebug("  ExSet: \n");

	if (thys->Assumptions->AncStates)
	{
	    ajDebug("  AncStates:\n");

	    for (i=0; thys->Assumptions->AncStates[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Assumptions->AncStates[i]);
	}
	else
	    ajDebug("  AncStates: \n");
    }

    if (!thys->Codons)
	ajDebug("Codons: \n");
    else
    {
	ajDebug("Codons\n");
	ajDebug("------\n");

	if (thys->Codons->CodonPosSet)
	{
	    ajDebug("  CodonPosSet:\n");

	    for (i=0; thys->Codons->CodonPosSet[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Codons->CodonPosSet[i]);
	}
	else
	    ajDebug("  CodonPosSet: \n");

	if (thys->Codons->GeneticCode)
	{
	    ajDebug("  GeneticCode:\n");

	    for (i=0; thys->Codons->GeneticCode[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Codons->GeneticCode[i]);
	}
	else
	    ajDebug("  GeneticCode: \n");

	if (thys->Codons->CodeSet)
	{
	    ajDebug("  CodeSet:\n");

	    for (i=0; thys->Codons->CodeSet[i]; i++)
		ajDebug("  CodeSet  [%d] '%S'\n", i, thys->Codons->CodeSet[i]);
	}
	else
	    ajDebug("  CodeSet: \n");
    }

    if (!thys->Trees)
	ajDebug("Trees: \n");
    else
    {
	ajDebug("Trees\n");
	ajDebug("-----\n");

	if (thys->Trees->Translate)
	{
	    ajDebug("  Translate:\n");

	    for (i=0; thys->Trees->Translate[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Trees->Translate[i]);
	}
	else
	    ajDebug("  Translate: \n");

	if (thys->Trees->Tree)
	{
	    ajDebug("  Tree:\n");

	    for (i=0; thys->Trees->Tree[i]; i++)
		ajDebug("    [%d] '%S'\n",
			i, thys->Trees->Tree[i]);
	}
	else
	    ajDebug("  Tree: \n");
    }

    if (!thys->Notes)
	ajDebug("Notes: \n");
    else
    {
	ajDebug("Notes\n");
	ajDebug("-----\n");

	if (thys->Notes->Text)
	{
	    ajDebug("  Text:\n");

	    for (i=0; thys->Notes->Text[i]; i++)
		ajDebug("    [%d] '%S'\n", i, thys->Notes->Text[i]);
	}
	else
	    ajDebug("  Text: \n");

	if (thys->Notes->Picture)
	{
	    ajDebug("  Picture:\n");

	    for (i=0; thys->Notes->Picture[i]; i++)
		ajDebug("    [%d] '%S'\n", i, thys->Notes->Picture[i]);
	}
	else
	    ajDebug("  Picture: \n");
    }

    return;
}
ID ajNexusGetTaxa
TY public
MO ajnexus
LB core
XX
DE Returns the taxa as a string array
XX
PN [1]
PA r thys const AjPNexus
PD Nexus object
PX
RT AjPStr*
RD taxa string array, NULL terminated, read only
RX
//


AjPStr* ajNexusGetTaxa(const AjPNexus thys)
{
    if(!thys)
	return NULL;

    if (thys->Taxa)
	return thys->Taxa->TaxLabels;

    return NULL;
}
ID ajNexusGetNtaxa
TY public
MO ajnexus
LB core
XX
DE Returns the number of taxa
XX
PN [1]
PA r thys const AjPNexus
PD Nexus object
PX
RT ajuint
RD Number of taxa
RX
//


ajuint ajNexusGetNtaxa(const AjPNexus thys)
{
    if(!thys)
	return 0;

    if (thys->Taxa)
	return thys->Taxa->Ntax;

    return 0;
}
ID ajNexusGetSequences
TY public
MO ajnexus
LB core
XX
DE Returns the sequences from the character matrix as a string array.
DE
DE Sequences are in the same order as the taxa returned by ajNexusGetTaxa.
DE Sequences may need to be set if not already done.
XX
PN [1]
PA u thys AjPNexus
PD Nexus object
PX
RT AjPStr*
RD taxa string array, NULL terminated, read only
RX
//


AjPStr* ajNexusGetSequences(AjPNexus thys)
{
    if(!thys)
	return NULL;

    nexusSetSequences(thys);

    if (thys->Characters)
	return thys->Characters->Sequences;

    return NULL;
}
ID nexusSetSequences
TY static
MO ajnexus
LB core
XX
DE Sets the sequences from the character matrix as a string array
XX
PN [1]
PA u thys AjPNexus
PD Nexus object
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool nexusSetSequences(AjPNexus thys)
{
    AjPTable seqtab;
    AjPStr firstseq = NULL;
    ajint i;
    AjPStr taxlabel = NULL;
    AjPStr tmpstr = NULL;
    AjPStr rdline = NULL;
    AjPStr seqstr = NULL;
    AjBool havetaxa = ajFalse;
    ajuint itax=0;
    AjPRegexp word;
    char gapch;

    ajDebug("nexusSetSequences\n");

    if(!thys)
	return ajFalse;


    if (!thys->Characters)		/* no characters defined */
    {
	ajDebug("Failed - No characters defined\n");

	return ajFalse;
    }

    if (thys->Characters->Sequences)	/* already done */
    {
	ajDebug("Success - Sequences already done\n");

	return ajTrue;
    }

    if (!thys->Characters->Nchar)	/* must have been defined */
    {
	ajDebug("Failed - must have been defined\n");

	return ajFalse;
    }

    if (!thys->Ntax)			/* taxa required */
    {
	ajDebug("Failed - number of taxa required\n");

	return ajFalse;
    }

    if (!thys->Taxa)			/* taxa required */
    {
	ajDebug("Failed - taxa names required\n");

	return ajFalse;
    }

    if (!thys->Characters->Matrix)	/* matrix required */
    {
	ajDebug("Failed - matrix required\n");

	return ajFalse;
    }

    if (ajStrMatchCaseC(thys->Characters->DataType, /* must be sequence data */
			"continuous") ||
	ajStrMatchCaseC(thys->Characters->DataType, /* must be sequence data */
			"standard"))
    {
	ajDebug("Failed - not sequence data\n");

	return ajFalse;
    }

    if(thys->Characters->Gap)
	gapch = thys->Characters->Gap;
    else
	gapch = '.';

    word = ajRegCompC("\\S+");

    seqtab = ajTablestrNew(thys->Taxa->Ntax);

    if (thys->Taxa->TaxLabels)
    {
	havetaxa = ajTrue;

	for (i=0; thys->Taxa->TaxLabels[i]; i++)
	{
            seqstr = ajStrNewRes(thys->Characters->Nchar+1);
	    ajTablePut(seqtab, thys->Taxa->TaxLabels[i], seqstr);
	    seqstr = NULL;
	}
    }
    else
    {
	AJCNEW0(thys->Taxa->TaxLabels, (thys->Ntax+1));
        ajDebug("thys->Taxa->TaxLabels 0..%u\n", (thys->Ntax+1));
    }

    taxlabel = NULL;
    itax=0;

    for (i=0; thys->Characters->Matrix[i]; i++)
    {
	ajStrAssignS(&rdline, thys->Characters->Matrix[i]);
	ajStrRemoveWhiteExcess(&rdline);

        ajDebug("rdline: '%S'\n", rdline);
	if (!taxlabel || thys->Characters->Interleave ||
	    (ajStrGetLen(seqstr) >= thys->Characters->Nchar))
	{				/* next tax label */
	    if (!ajRegExec(word, rdline))
		continue;

	    ajRegSubI(word, 0, &taxlabel);
	    ajRegPost(word, &tmpstr);
            ajDebug("taxlabel '%S' rest '%S'\n", taxlabel, tmpstr);
	    ajStrAssignS(&rdline, tmpstr);
	    ajStrQuoteStripAll(&taxlabel);
            ajDebug("unquoted taxlabel %p '%S'\n", taxlabel, taxlabel);

	    if (!havetaxa)
	    {
                ajDebug("ajTablePut itax.%u label %p '%S'\n",
                        itax,
                        thys->Taxa->TaxLabels[itax],
                        thys->Taxa->TaxLabels[itax]);
		ajStrAssignS(&thys->Taxa->TaxLabels[itax], taxlabel);
		seqstr = ajStrNewRes(thys->Characters->Nchar+1);
                ajDebug("ajTablePut itax %u label %p '%S'\n",
                        itax,
                        thys->Taxa->TaxLabels[itax],
                        thys->Taxa->TaxLabels[itax]);
		ajTablePut(seqtab, thys->Taxa->TaxLabels[itax], seqstr);
		seqstr = NULL;
		itax++;
		if (itax >= thys->Ntax)
		    havetaxa = ajTrue;
	    }

            ajDebug("havetaxa: %B taxlabel '%S'\n", havetaxa, taxlabel);
	    seqstr = ajTableFetchmodS(seqtab, taxlabel);

            ajDebug("seqstr '%S'\n", seqstr);
	    if (!seqstr)
	    {
		ajErr("Unknown taxon '%S' in nexus data", taxlabel);
		nexusArrayDel(&thys->Characters->Sequences);
		ajTableFree(&seqtab);

		return ajFalse;
	    }
	}

	if (!i)
	    ajStrAssignS(&firstseq, taxlabel);

	while (ajRegExec(word, rdline))
	{
	    ajRegSubI(word, 0, &tmpstr);
	    ajStrAppendS(&seqstr, tmpstr);
	    ajRegPost(word, &tmpstr);
	    ajStrAssignS(&rdline, tmpstr);
	}

	ajStrExchangeKK(&seqstr, gapch, '-');
        ajDebug("seqstr updated '%S'\n", seqstr);
    }

    ajRegFree(&word);
    ajStrDel(&tmpstr);
    ajStrDel(&rdline);

    AJCNEW0(thys->Characters->Sequences, (thys->Ntax+1));

    for (i=0; thys->Taxa->TaxLabels[i]; i++)
    {
	thys->Characters->Sequences[i] = ajTableFetchmodS(seqtab,
						    thys->Taxa->TaxLabels[i]);
	if (ajStrGetLen(thys->Characters->Sequences[i]) !=
	    thys->Characters->Nchar)
	{
	    ajErr("Nexus sequence length for '%S' is %d, expected %d\n",
		    thys->Taxa->TaxLabels[i],
		    ajStrGetLen(thys->Characters->Sequences[i]),
		    thys->Characters->Nchar);
	    nexusArrayDel(&thys->Characters->Sequences);
	    ajTableFree(&seqtab);

            return ajFalse;
	}
    }

    ajTableFree(&seqtab);
    ajStrDel(&firstseq);
    ajStrDel(&taxlabel);

    return ajTrue;
}
ID ajNexusExit
TY public
MO ajnexus
LB core
XX
DE Cleans up nexus parsing input internal memory
XX
RT void
RD 
RX
//


void ajNexusExit(void)
{
    ajStrDel(&nexusTmpStr);

    ajRegFree(&nexusCommExp);
    ajRegFree(&nexusBegCommExp);
    ajRegFree(&nexusEndCommExp);
    ajRegFree(&nexusBegExp);
    ajRegFree(&nexusEndExp);

    ajRegFree(&nexusBegCommandExp);
    ajRegFree(&nexusEndCommandExp);

    ajRegFree(&nexusNewlineExp);
    ajRegFree(&nexusDimExp);
    ajRegFree(&nexusDimExp1);
    ajRegFree(&nexusDimExp2);
    ajRegFree(&nexusDimExp3);
    ajRegFree(&nexusFormExp1);
    ajRegFree(&nexusFormExp2);
    ajRegFree(&nexusFormExp3);
    ajRegFree(&nexusFormExp4);
    ajRegFree(&nexusFormExp5);
    ajRegFree(&nexusFormExp6);
    ajRegFree(&nexusFormExp7);
    ajRegFree(&nexusFormExp8);
    ajRegFree(&nexusFormExp9);
    ajRegFree(&nexusFormExp10);
    ajRegFree(&nexusFormExp11);
    ajRegFree(&nexusFormExp12);
    ajRegFree(&nexusFormExp13);
    ajRegFree(&nexusTaxLabExp);
    ajRegFree(&nexusChstLabExp);
    ajRegFree(&nexusChLabExp);
    ajRegFree(&nexusOptExp1);
    ajRegFree(&nexusOptExp2);
    ajRegFree(&nexusOptExp3);

    return;
}
ID nexusArrayDel
TY static
MO ajnexus
LB core
XX
DE Default destructor for AJAX string arrays.
DE Called for each element in the array, which must end with a NULL.
DE
DE If the given string is NULL, or a NULL pointer, simply returns.
XX
PN [1]
PA d pthis AjPStr**
PD Pointer to the string array to be deleted.
PD The pointer is always deleted.
PX
RT void
RD 
RX
//


static void nexusArrayDel(AjPStr** pthis)
{
    AjPStr* thys;
    ajint i;

    thys = pthis ? *pthis : 0;

    if(!pthis)
	return;

    if(!*pthis)
	return;

    for (i=0; thys[i];i++)
	ajStrDel(&thys[i]);

    AJFREE(*pthis);

    return;
}
ID ajOboNew
TY public
MO ajobo
LB core
XX
DE Term constructor
XX
RT AjPObo
RD New object
RX
//


AjPObo ajOboNew(void)
{
    AjPObo ret;

    AJNEW0(ret);

    ret->Taglist = ajListNew();

    return ret;
}
ID ajOboNewObo
TY public
MO ajobo
LB core
XX
DE Term copy constructor
XX
PN [1]
PA r obo const AjPObo
PD Obo term
PX
RT AjPObo
RD New object
RX
//


AjPObo ajOboNewObo(const AjPObo obo)
{
    AjPObo ret;
    AjIList iter;
    AjPObotag tag = NULL;
    AjPOboxref xref = NULL;

    AJNEW0(ret);

    ret->Obsolete = obo->Obsolete;
    ret->Builtin = obo->Builtin;
    ret->Fpos = obo->Fpos;
    ret->Format = obo->Format;
    
    if(obo->Id)
        ret->Id = ajStrNewS(obo->Id);
    if(obo->Trueid)
        ret->Trueid = ajStrNewS(obo->Trueid);
    if(obo->Fullid)
        ret->Fullid = ajStrNewS(obo->Fullid);
    if(obo->Name)
        ret->Name = ajStrNewS(obo->Name);
    if(obo->Namespace)
        ret->Namespace = ajStrNewS(obo->Namespace);
    if(obo->Def)
        ret->Def = ajStrNewS(obo->Def);
    if(obo->Comment)
        ret->Comment = ajStrNewS(obo->Comment);
    if(obo->Replaced)
        ret->Replaced = ajStrNewS(obo->Replaced);
    if(obo->Db)
        ret->Db = ajStrNewS(obo->Db);
    if(obo->Setdb)
        ret->Setdb = ajStrNewS(obo->Setdb);
    if(obo->Full)
        ret->Full = ajStrNewS(obo->Full);
    if(obo->Qry)
        ret->Qry = ajStrNewS(obo->Qry);
    if(obo->Formatstr)
        ret->Formatstr = ajStrNewS(obo->Formatstr);
    if(obo->Filename)
        ret->Filename = ajStrNewS(obo->Filename);
    if(obo->TextPtr)
        ret->TextPtr = ajStrNewS(obo->TextPtr);

    if(obo->Taglist)
    {
        ret->Taglist = ajListNew();
        iter = ajListIterNewread(obo->Taglist);
        while(!ajListIterDone(iter))
        {
            tag = ajListIterGet(iter);
            ajListPushAppend(ret->Taglist, ajObotagNewTag(tag));
        }
        ajListIterDel(&iter);
    }

    if(obo->Dbxrefs)
    {
        ret->Dbxrefs = ajListNew();
        iter = ajListIterNewread(obo->Dbxrefs);
        while(!ajListIterDone(iter))
        {
            xref = ajListIterGet(iter);
            ajListPushAppend(ret->Dbxrefs, ajOboxrefNewXref(xref));
        }
        ajListIterDel(&iter);
    }

    return ret;
}
ID ajOboDel
TY public
MO ajobo
LB core
XX
DE Term destructor
XX
PN [1]
PA d Pobo AjPObo*
PD Obo term object to delete
PX
RT void
RD 
RX
//


void ajOboDel(AjPObo *Pobo)
{
    AjPObo     term = NULL;
    AjPObotag  tag  = NULL;
    AjPOboxref xref = NULL;

    if(!Pobo) return;
    if(!(*Pobo)) return;

    term = *Pobo;

    ajStrDel(&term->Id);
    ajStrDel(&term->Trueid);
    ajStrDel(&term->Fullid);
    ajStrDel(&term->Name);
    ajStrDel(&term->Namespace);
    ajStrDel(&term->Def);
    ajStrDel(&term->Comment);
    ajStrDel(&term->Replaced);

    while(ajListPop(term->Dbxrefs, (void**) &xref))
        ajOboxrefDel(&xref);

    while(ajListPop(term->Taglist, (void**) &tag))
        ajObotagDel(&tag);

    ajListFree(&term->Taglist);

    ajStrDel(&term->Db);
    ajStrDel(&term->Setdb);
    ajStrDel(&term->Full);
    ajStrDel(&term->Qry);
    ajStrDel(&term->Formatstr);
    ajStrDel(&term->Filename);
    ajStrDel(&term->TextPtr);

    AJFREE(*Pobo);
    *Pobo = NULL;

    return;
}
ID ajOboGetDb
TY public
MO ajobo
LB core
XX
DE Return the database name
XX
PN [1]
PA r obo const AjPObo
PD OBO term
PX
RT const AjPStr
RD Database name
RX
//


const AjPStr ajOboGetDb(const AjPObo obo)
{
    return obo->Db;
}
ID ajOboGetDef
TY public
MO ajobo
LB core
XX
DE Return the definition string and the number of dbxrefs
XX
PN [1]
PA r obo const AjPObo
PD OBO term
PX
PN [2]
PA w Pdefstr AjPStr*
PD Returned definition string
PX
PN [3]
PA w nrefs ajuint*
PD Number of dbxrefs after the definition
PX
RT AjBool
RD True on success.
RX
//


AjBool ajOboGetDef(const AjPObo obo, AjPStr *Pdefstr, ajuint *nrefs)
{
    const char* cp;
    ajuint i = 0;
    
    if(!obo) return ajFalse;

    cp = ajStrGetPtr(obo->Def);

    if(*cp == '"')
    {
        cp++;
        i = 1;
        while(*cp && *cp != '"')
        {
            i++;
            cp++;
        }
        cp++;
        ajStrAssignSubS(Pdefstr, obo->Def, 1, i);
    }
    else
    {
        i = 0;
        while(*cp && *cp != '[')
        {
            i++;
            cp++;
        }
        ajStrAssignSubS(Pdefstr, obo->Def, 0, i);
    }
    
    while(*cp && *cp != '[')
        cp++;

    *nrefs = 0;

    while (*cp) 
    {
        
    }
    

    return ajTrue;
    
}
ID ajOboGetEntry
TY public
MO ajobo
LB core
XX
DE Return the full text
XX
PN [1]
PA r obo const AjPObo
PD OBO term
PX
RT const AjPStr
RD Returned full text
RX
//


const AjPStr ajOboGetEntry(const AjPObo obo)
{
    if(!obo)
        return NULL;

    if(obo->TextPtr)
        return obo->TextPtr;

    return ajStrConstEmpty();
}
ID ajOboGetId
TY public
MO ajobo
LB core
XX
DE Return the identifier
XX
PN [1]
PA r obo const AjPObo
PD OBO term
PX
RT const AjPStr
RD Returned id
RX
//


const AjPStr ajOboGetId(const AjPObo obo)
{
    if(!obo)
        return NULL;

    return obo->Id;
}
ID ajOboGetName
TY public
MO ajobo
LB core
XX
DE Return the name
XX
PN [1]
PA r obo const AjPObo
PD OBO term
PX
RT const AjPStr
RD Returned name
RX
//


const AjPStr ajOboGetName(const AjPObo obo)
{
    if(!obo)
        return NULL;

    return obo->Name;
}
ID ajOboGetNamespace
TY public
MO ajobo
LB core
XX
DE Return the namespace name
XX
PN [1]
PA r obo const AjPObo
PD OBO term
PX
RT const AjPStr
RD Returned namespace
RX
//


const AjPStr ajOboGetNamespace(const AjPObo obo)
{
    if(!obo)
        return NULL;

    return obo->Namespace;
}
ID ajOboGetParents
TY public
MO ajobo
LB core
XX
DE Return a list of all parent terms derived from is_a tags in the current
DE obo term
XX
PN [1]
PA r obo const AjPObo
PD OBO term
PX
PN [2]
PA u uplist AjPList
PD List of parent terms
PX
RT ajuint
RD Number of new terms added to list
RX
//


ajuint ajOboGetParents(const AjPObo obo, AjPList uplist)
{
    AjIList iter;
    AjPObotag tag = NULL;
    AjPStr up = NULL;
    ajlong colpos = 0;

    ajulong oldlen;

    if(!obo)
        return 0;

    oldlen = ajListGetLength(uplist);

    iter = ajListIterNewread(obo->Taglist);

    while(!ajListIterDone(iter))
    {
        tag = (AjPObotag) ajListIterGet(iter);

        if(!ajStrMatchC(tag->Name, "is_a"))
            continue;

        colpos = 1 + ajStrFindAnyK(tag->Value, ':');
        ajStrAssignSubS(&up, tag->Value, colpos, -1);

        ajListPushAppend(uplist, up);
        up = NULL;
    }

    if(obo->Obsolete && ajStrGetLen(obo->Replaced))
    {
        colpos = 1 + ajStrFindAnyK(obo->Replaced, ':');
        ajStrAssignSubS(&up, obo->Replaced, colpos, -1);
        ajDebug("ajOboGetParents obo id '%S' obsolete, consider '%S'\n",
                obo->Id, up);
        ajListPushAppend(uplist, up);
        up = NULL;
    }
        
    ajListIterDel(&iter);

    return (ajuint) (ajListGetLength(uplist) - oldlen);
}
ID ajOboGetQryC
TY public
MO ajobo
LB core
XX
DE Returns the query string of an obo term.
DE Because this is a pointer to the real internal string
DE the caller must take care not to change the character string in any way.
DE If the string is to be changed (case for example) then it must first
DE be copied.
XX
PN [1]
PA r obo const AjPObo
PD Obo term object.
PX
RT const char*
RD Query as a character string.
RX
//


const char* ajOboGetQryC(const AjPObo obo)
{
    return MAJSTRGETPTR(ajOboGetQryS(obo));
}
ID ajOboGetQryS
TY public
MO ajobo
LB core
XX
DE Returns the query string of an obo term.
DE Because this is a pointer to the real internal string
DE the caller must take care not to change the character string in any way.
DE If the string is to be changed (case for example) then it must first
DE be copied.
XX
PN [1]
PA r obo const AjPObo
PD Obo term object.
PX
RT const AjPStr
RD Query as a string.
RX
//


const AjPStr ajOboGetQryS(const AjPObo obo)
{
    ajDebug("ajOboGetQryS '%S'\n", obo->Qry);

    if(ajStrGetLen(obo->Qry))
	return obo->Qry;

    oboMakeQry(obo, &oboTempQry);

    return oboTempQry;
}
ID ajOboGetReplaced
TY public
MO ajobo
LB core
XX
DE Return the recommended replacement for an obsolete term
XX
PN [1]
PA r obo const AjPObo
PD OBO term
PX
RT const AjPStr
RD Returned id
RX
//


const AjPStr ajOboGetReplaced(const AjPObo obo)
{
    return obo->Replaced;
}
ID ajOboGetTree
TY public
MO ajobo
LB core
XX
DE Return a list with all this term's descendants appended
XX
PN [1]
PA r obo const AjPObo
PD OBO term
PX
PN [2]
PA u obolist AjPList
PD List of obo terms
PX
RT ajuint
RD Number of terms returned
RX
//


ajuint ajOboGetTree(const AjPObo obo, AjPList obolist)
{
    AjPObo obonext = NULL;
    AjPOboin oboin = NULL;
    AjPStr oboqry = NULL;
    static ajuint depth = 0;

    depth++;
    oboin = ajOboinNew();
    ajFmtPrintS(&oboqry, "%S-isa:%S", obo->Db, obo->Id);
    ajOboinQryS(oboin, oboqry);

    if(ajStrGetLen(obo->TextPtr))
        oboin->Input->Text = ajTrue;

    ajDebug("ajOboGetTree %u '%S'\n", depth, oboqry);
    obonext = ajOboNew();
    while(ajOboinRead(oboin, obonext))
    {
        ajDebug("ajOboGetTree push '%S'\n", obonext->Id);
        ajListPushAppend(obolist, obonext);
        ajOboGetTree(obonext, obolist);
        obonext = ajOboNew();
    }

    ajOboDel(&obonext);
    ajOboinDel(&oboin);
    ajStrDel(&oboqry);

    depth--;

    return (ajuint) ajListGetLength(obolist);
}
ID ajOboIsObsolete
TY public
MO ajobo
LB core
XX
DE Test whether an obo term is obsolete
XX
PN [1]
PA r obo const AjPObo
PD OBO term
PX
RT AjBool
RD True if term is obsolete
RX
//


AjBool ajOboIsObsolete(const AjPObo obo)
{
    return obo->Obsolete;
}
ID oboMakeQry
TY static
MO ajobo
LB core
XX
DE Sets the query for an obo term
XX
PN [1]
PA r thys const AjPObo
PD Obo term object
PX
PN [2]
PA w qry AjPStr*
PD Query string in full
PX
RT void
RD 
RX
//


static void oboMakeQry(const AjPObo thys, AjPStr* qry)
{
    ajDebug("oboMakeQry (Name <%S> Formatstr <%S> Db <%S> "
	    "Filename <%S>)\n",
	    thys->Name, thys->Formatstr, thys->Db,
	    thys->Filename);

    /* ajOboTrace(thys); */

    if(ajStrGetLen(thys->Db))
	ajFmtPrintS(qry, "%S-id:%S", thys->Db, thys->Id);
    else
    {
	ajFmtPrintS(qry, "%S::%S:%S", thys->Formatstr,
                    thys->Filename,thys->Id);
    }

    ajDebug("      result: <%S>\n",
	    *qry);

    return;
}
ID ajOboClear
TY public
MO ajobo
LB core
XX
DE Resets all data for an obo term object so that it can be reused.
XX
PN [1]
PA u obo AjPObo
PD Obo term
PX
RT void
RD 
RX
//


void ajOboClear(AjPObo obo)
{
    AjPObotag  tag  = NULL;
    AjPOboxref xref = NULL;

    if(MAJSTRGETLEN(obo->Id))
       ajStrSetClear(&obo->Id);
    if(MAJSTRGETLEN(obo->Trueid))
       ajStrSetClear(&obo->Trueid);
    if(MAJSTRGETLEN(obo->Fullid))
        ajStrSetClear(&obo->Fullid);
    if(MAJSTRGETLEN(obo->Name))
       ajStrSetClear(&obo->Name);
    if(MAJSTRGETLEN(obo->Namespace))
       ajStrSetClear(&obo->Namespace);
    if(MAJSTRGETLEN(obo->Def))
       ajStrSetClear(&obo->Def);
    if(MAJSTRGETLEN(obo->Comment))
       ajStrSetClear(&obo->Comment);
    if(MAJSTRGETLEN(obo->Replaced))
       ajStrSetClear(&obo->Replaced);

    if(MAJSTRGETLEN(obo->Db))
       ajStrSetClear(&obo->Db);
    if(MAJSTRGETLEN(obo->Setdb))
       ajStrSetClear(&obo->Setdb);
    if(MAJSTRGETLEN(obo->Full))
       ajStrSetClear(&obo->Full);
    if(MAJSTRGETLEN(obo->Qry))
       ajStrSetClear(&obo->Qry);
    if(MAJSTRGETLEN(obo->Formatstr))
       ajStrSetClear(&obo->Formatstr);
    if(MAJSTRGETLEN(obo->Filename))
       ajStrSetClear(&obo->Filename);
    if(MAJSTRGETLEN(obo->TextPtr))
       ajStrSetClear(&obo->TextPtr);

    if(obo->Taglist)   
        while(ajListPop(obo->Taglist,(void**)&tag))
            ajObotagDel(&tag);

    if(obo->Dbxrefs)   
        while(ajListPop(obo->Dbxrefs,(void**)&xref))
            ajOboxrefDel(&xref);

    obo->Obsolete = ajFalse;
    obo->Builtin = ajFalse;
    obo->Fpos = 0L;
    obo->Format = 0;

    return;
}
ID ajObotagNewData
TY public
MO ajobo
LB core
XX
DE Tag constructor
XX
PN [1]
PA r name const AjPStr
PD Name
PX
PN [2]
PA r value const AjPStr
PD Value
PX
PN [3]
PA r modifier const AjPStr
PD Modifier
PX
PN [4]
PA r comment const AjPStr
PD Comment
PX
PN [5]
PA r linenum ajuint
PD OBO file line number
PX
RT AjPObotag
RD New object
RX
//


AjPObotag ajObotagNewData(const AjPStr name, const AjPStr value,
                          const AjPStr modifier, const AjPStr comment,
                          ajuint linenum)
{
    AjPObotag ret;

    AJNEW0(ret);
    ret->Name  = ajStrNewS(name);
    ret->Value    = ajStrNewS(value);
    ret->Modifier  = ajStrNewS(modifier);
    ret->Comment  = ajStrNewS(comment);
    ret->Linenumber = linenum;

    return ret;
}
ID ajObotagNewTag
TY public
MO ajobo
LB core
XX
DE Tag copy constructor
XX
PN [1]
PA r tag const AjPObotag
PD Obo tag
PX
RT AjPObotag
RD New object
RX
//


AjPObotag ajObotagNewTag(const AjPObotag tag)
{
    AjPObotag ret;

    AJNEW0(ret);

    if(tag->Name)
        ret->Name  = ajStrNewS(tag->Name);
    if(tag->Value)
        ret->Value    = ajStrNewS(tag->Value);
    if(tag->Modifier)
        ret->Modifier  = ajStrNewS(tag->Modifier);
    if(tag->Comment)
        ret->Comment  = ajStrNewS(tag->Comment);

    ret->Linenumber = tag->Linenumber;

    return ret;
}
ID ajObotagDel
TY public
MO ajobo
LB core
XX
DE Tag destructor
XX
PN [1]
PA d Ptag AjPObotag*
PD Tag object to delete
PX
RT void
RD 
RX
//


void ajObotagDel(AjPObotag *Ptag)
{
    if(!Ptag) return;
    if(!(*Ptag)) return;

    ajStrDel(&(*Ptag)->Name);
    ajStrDel(&(*Ptag)->Value);
    ajStrDel(&(*Ptag)->Modifier);
    ajStrDel(&(*Ptag)->Comment);

    AJFREE(*Ptag);
    *Ptag = NULL;

    return;
}
ID ajOboxrefNewData
TY public
MO ajobo
LB core
XX
DE Dbxref constructor
XX
PN [1]
PA r name const AjPStr
PD Name
PX
PN [2]
PA r desc const AjPStr
PD Description
PX
RT AjPOboxref
RD New object
RX
//


AjPOboxref ajOboxrefNewData(const AjPStr name, const AjPStr desc)
{
    AjPOboxref ret;

    AJNEW0(ret);
    ret->Name = ajStrNewS(name);
    ret->Desc = ajStrNewS(desc);

    return ret;
}
ID ajOboxrefNewXref
TY public
MO ajobo
LB core
XX
DE Dbxref copy constructor
XX
PN [1]
PA r xref const AjPOboxref
PD Dbxref
PX
RT AjPOboxref
RD New object
RX
//


AjPOboxref ajOboxrefNewXref(const AjPOboxref xref)
{
    AjPOboxref ret;

    AJNEW0(ret);

    ret->Name = ajStrNewS(xref->Name);
    ret->Desc = ajStrNewS(xref->Desc);

    return ret;
}
ID ajOboxrefDel
TY public
MO ajobo
LB core
XX
DE Dbxref destructor
XX
PN [1]
PA d Pxref AjPOboxref*
PD Dbxref object to delete
PX
RT void
RD 
RX
//


void ajOboxrefDel(AjPOboxref *Pxref)
{
    if(!Pxref) return;
    if(!(*Pxref)) return;

    ajStrDel(&(*Pxref)->Name);
    ajStrDel(&(*Pxref)->Desc);

    AJFREE(*Pxref);
    *Pxref = NULL;

    return;
}
ID ajOboExit
TY public
MO ajobo
LB core
XX
DE Cleans up obo term internal memory
XX
RT void
RD 
RX
//


void ajOboExit(void)
{
    /* Query processing regular expressions */

    ajOboinExit();
    ajObooutExit();

    return;
}
ID oboAccess
TY list
MO ajobodb
LB ajaxdb
XX
DE Functions to access each database or obo access method
XX
//


static AjOOboAccess oboAccess[] =
{
  /*  Name      AccessFunction   FreeFunction
      Qlink,   Description
      Alias    Entry    Query    All      Chunk    Padding */
    {
      NULL, NULL, NULL,
      NULL, NULL,
      AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE
    },
    {
      NULL, NULL, NULL,
      NULL, NULL,
      AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE
    }
};
ID ajObodbInit
TY public
MO ajobodb
LB ajaxdb
XX
DE Initialise obo database internals
XX
RT void
RD 
RX
//


void ajObodbInit(void)
{
    AjPTable table;
    ajuint i = 0;

    table = ajOboaccessGetDb();

    while(oboAccess[i].Name)
    {
        ajCallTableRegister(table, oboAccess[i].Name, (void*) &oboAccess[i]);
	i++;
    }

    return;
}
ID ajObodbPrintAccess
TY public
MO ajobodb
LB ajaxdb
XX
DE Reports the internal data structures
XX
PN [1]
PA u outf AjPFile
PD Output file
PX
PN [2]
PA r full AjBool
PD Full report (usually ajFalse)
PX
RT void
RD 
RX
//


void ajObodbPrintAccess(AjPFile outf, AjBool full)
{
    ajint i = 0;

    ajFmtPrintF(outf, "\n");
    ajFmtPrintF(outf, "# Obo term access methods\n");
    ajFmtPrintF(outf, "# Name       Alias Entry Query   All Description\n");
    ajFmtPrintF(outf, "\n");
    ajFmtPrintF(outf, "method {\n");

    for(i=0; oboAccess[i].Name; i++)
	if(full || !oboAccess[i].Alias)
	    ajFmtPrintF(outf, "  %-10s %5B %5B %5B %5B \"%s\"\n",
			oboAccess[i].Name,  oboAccess[i].Alias,
			oboAccess[i].Entry, oboAccess[i].Query,
			oboAccess[i].All,   oboAccess[i].Desc);

    ajFmtPrintF(outf, "}\n\n");

    return;
}
ID ajObodbExit
TY public
MO ajobodb
LB ajaxdb
XX
DE Cleans up obo database processing internal memory
XX
RT void
RD 
RX
//


void ajObodbExit(void)
{
    return;
}
ID ajOboinNew
TY public
MO ajoboread
LB core
XX
DE Creates a new obo input object.
XX
RT AjPOboin
RD New obo input object.
RX
CA new
CT AjPOboin
CD Default constructor
CX
//


AjPOboin ajOboinNew(void)
{
    AjPOboin pthis;

    AJNEW0(pthis);

    pthis->Input = ajTextinNewDatatype(AJDATATYPE_OBO);

    pthis->OboData      = NULL;

    return pthis;
}
ID ajOboinDel
TY public
MO ajoboread
LB core
XX
DE Deletes an obo input object.
XX
PN [1]
PA d pthis AjPOboin*
PD Obo input
PX
RT void
RD 
RX
CA delete
CT AjPOboin
CD Default destructor
CX
//


void ajOboinDel(AjPOboin* pthis)
{
    AjPOboin thys;

    if(!pthis)
        return;

    thys = *pthis;

    if(!thys)
        return;

    ajDebug("ajOboinDel called qry:'%S'\n", thys->Input->Qry);

    ajTextinDel(&thys->Input);

    AJFREE(*pthis);

    return;
}
ID ajOboinClear
TY public
MO ajoboread
LB core
XX
DE Clears an obo term input object back to "as new" condition, except
DE for the query list which must be preserved.
XX
PN [1]
PA w thys AjPOboin
PD obo term input
PX
RT void
RD 
RX
CA modify
CT AjPOboin
CD Resets ready for reuse.
CX
//


void ajOboinClear(AjPOboin thys)
{

    ajDebug("ajOboinClear called\n");

    if(!thys)
        return;

    ajTextinClear(thys->Input);

    thys->OboData = NULL;

    return;
}
ID ajOboinQryC
TY public
MO ajoboread
LB core
XX
DE Resets an obo term input object using a new Universal
DE obo term Address
XX
PN [1]
PA u thys AjPOboin
PD obo term input object.
PX
PN [2]
PA r txt const char*
PD Query
PX
RT void
RD 
RX
CA modify
CT AjPOboin
CD Resets using a new query
CX
//


void ajOboinQryC(AjPOboin thys, const char* txt)
{
    ajOboinClear(thys);
    ajTextinQryC(thys->Input, txt);

    return;
}
ID ajOboinQryS
TY public
MO ajoboread
LB core
XX
DE Resets an obo term input object using a new Universal
DE obo term Address
XX
PN [1]
PA u thys AjPOboin
PD obo term input object.
PX
PN [2]
PA r str const AjPStr
PD Query
PX
RT void
RD 
RX
CA modify
CT AjPOboin
CD Resets using a new query
CX
//


void ajOboinQryS(AjPOboin thys, const AjPStr str)
{
    ajOboinClear(thys);
    ajTextinQryS(thys->Input, str);

    return;
}
ID ajOboinGetQryS
TY public
MO ajoboread
LB core
XX
DE Returns the query of an obo term input object
XX
PN [1]
PA r thys const AjPOboin
PD obo term input object.
PX
RT const AjPStr
RD Query string
RX
//


const AjPStr ajOboinGetQryS(const AjPOboin thys)
{
    return ajTextinGetQryS(thys->Input);

}
ID ajOboinTrace
TY public
MO ajoboread
LB core
XX
DE Debug calls to trace the data in an obo term input object.
XX
PN [1]
PA r thys const AjPOboin
PD obo term input object.
PX
RT void
RD 
RX
//


void ajOboinTrace(const AjPOboin thys)
{
    ajDebug("obo term input trace\n");
    ajDebug("====================\n\n");

    ajTextinTrace(thys->Input);

    if(thys->OboData)
	ajDebug( "  OboData: exists\n");

    return;
}
ID ajOboinRead
TY public
MO ajoboread
LB core
XX
DE If the file is not yet open, calls oboinQryProcess to convert the query into
DE an open file stream.
DE
DE Uses oboinRead for the actual file reading.
DE
DE Returns the results in the AjPObo object.
XX
PN [1]
PA u oboin AjPOboin
PD obo term input definitions
PX
PN [2]
PA w obo AjPObo
PD obo term returned.
PX
RT AjBool
RD ajTrue on success.
RX
CA input
CT AjPObo
CD Master obo term input, calls specific functions for file access type and obo term format.
CX
//


AjBool ajOboinRead(AjPOboin oboin, AjPObo obo)
{
    AjBool ret       = ajFalse;
    AjPQueryList node = NULL;
    AjBool listdata  = ajFalse;

    if(oboin->Input->Filebuff)
    {
	/* (a) if file still open, keep reading */
	ajDebug("ajOboinRead: input file '%F' still there, try again at %Lu\n",
		oboin->Input->Filebuff->File, oboin->Input->Filebuff->Fpos);
	ret = oboinRead(oboin, obo);
	ajDebug("ajOboinRead: open buffer  qry: '%S' returns: %B\n",
		oboin->Input->Qry, ret);
    }
    else
    {
	/* (b) if we have a list, try the next query in the list */
	if(ajListGetLength(oboin->Input->List))
	{
	    listdata = ajTrue;
	    ajListPop(oboin->Input->List, (void**) &node);

	    ajDebug("++pop from list '%S'\n", node->Qry);
	    ajOboinQryS(oboin, node->Qry);
	    ajDebug("++SAVE OBOIN '%S' '%S' %d\n",
		    oboin->Input->Qry,
		    oboin->Input->Formatstr, oboin->Input->Format);

            oboinQryRestore(oboin, node);

	    ajStrDel(&node->Qry);
	    ajStrDel(&node->Formatstr);
	    AJFREE(node);

	    ajDebug("ajOboinRead: open list, try '%S'\n", oboin->Input->Qry);

	    if(!oboinQryProcess(oboin, obo) &&
               !ajListGetLength(oboin->Input->List))
		return ajFalse;

	    ret = oboinRead(oboin, obo);
	    ajDebug("ajOboinRead: list qry: '%S' returns: %B\n",
		    oboin->Input->Qry, ret);
	}
	else
	{
	    ajDebug("ajOboinRead: no file yet - test query '%S'\n",
                    oboin->Input->Qry);

	    /* (c) Must be a query - decode it */
	    if(!oboinQryProcess(oboin, obo) &&
               !ajListGetLength(oboin->Input->List))
		return ajFalse;

	    if(ajListGetLength(oboin->Input->List)) /* could be a new list */
		listdata = ajTrue;

	    ret = oboinRead(oboin, obo);
	    ajDebug("ajOboinRead: new qry: '%S' returns: %B\n",
		    oboin->Input->Qry, ret);
	}
    }

    /* Now read whatever we got */

    while(!ret && ajListGetLength(oboin->Input->List))
    {
	/* Failed, but we have a list still - keep trying it */
        if(listdata)
	    ajErr("Failed to read obo term '%S'", oboin->Input->Qry);

	listdata = ajTrue;
	ajListPop(oboin->Input->List,(void**) &node);
	ajDebug("++try again: pop from list '%S'\n", node->Qry);
	ajOboinQryS(oboin, node->Qry);
	ajDebug("++SAVE (AGAIN) OBOIN '%S' '%S' %d\n",
		oboin->Input->Qry,
		oboin->Input->Formatstr, oboin->Input->Format);

	oboinQryRestore(oboin, node);

	ajStrDel(&node->Qry);
	ajStrDel(&node->Formatstr);
	AJFREE(node);

	if(!oboinQryProcess(oboin, obo))
	    continue;

	ret = oboinRead(oboin, obo);
	ajDebug("ajOboinRead: list retry qry: '%S' returns: %B\n",
		oboin->Input->Qry, ret);
    }

    if(!ret)
    {
	if(listdata)
	    ajErr("Failed to read obo term '%S'", oboin->Input->Qry);

	return ajFalse;
    }


    oboDefine(obo, oboin);

    return ajTrue;
}
ID oboinReadFmt
TY static
MO ajoboread
LB core
XX
DE Tests whether an obo term can be read using the specified format.
DE Then tests whether the obo term matches obo term query criteria
DE and checks any specified type. Applies upper and lower case.
XX
PN [1]
PA u oboin AjPOboin
PD obo term input object
PX
PN [2]
PA w obo AjPObo
PD obo term object
PX
PN [3]
PA r format ajuint
PD input format code
PX
RT ajuint
RD 0 if successful.
RD 1 if the query match failed.
RD 2 if the obo term type failed
RD 3 if it failed to read an obo term
RX
//


static ajuint oboinReadFmt(AjPOboin oboin, AjPObo obo,
                           ajuint format)
{
    ajDebug("++oboinReadFmt format %d (%s) '%S'\n",
	    format, oboinFormatDef[format].Name,
	    oboin->Input->Qry);

    oboin->Input->Records = 0;

    /* Calling funclist oboinFormatDef() */
    if((*oboinFormatDef[format].Read)(oboin, obo))
    {
	ajDebug("oboinReadFmt success with format %d (%s) '%S'\n",
		format, oboinFormatDef[format].Name, obo->Name);
	oboin->Input->Format = format;
	ajStrAssignC(&oboin->Input->Formatstr, oboinFormatDef[format].Name);
	ajStrAssignC(&obo->Formatstr, oboinFormatDef[format].Name);
	ajStrAssignEmptyS(&obo->Db, oboin->Input->Db);
	ajStrAssignS(&obo->Filename, oboin->Input->Filename);

	if(oboQueryMatch(oboin->Input->Query, obo))
	{
            /* ajOboinTrace(oboin); */

            return FMT_OK;
        }

	ajDebug("query match failed, continuing ...\n");
	ajOboClear(obo);

	return FMT_NOMATCH;
    }
    else
    {
	ajDebug("Testing input buffer: IsBuff: %B Eof: %B\n",
		ajFilebuffIsBuffered(oboin->Input->Filebuff),
		ajFilebuffIsEof(oboin->Input->Filebuff));

	if (!ajFilebuffIsBuffered(oboin->Input->Filebuff) &&
	    ajFilebuffIsEof(oboin->Input->Filebuff))
	    return FMT_EOF;

	ajFilebuffResetStore(oboin->Input->Filebuff,
                             oboin->Input->Text, &obo->TextPtr);
	ajDebug("Format %d (%s) failed, file buffer reset by oboinReadFmt\n",
		format, oboinFormatDef[format].Name);
	/* ajFilebuffTraceFull(oboin->Input->Filebuff, 10, 10);*/
    }

    ajDebug("++oboinReadFmt failed - nothing read\n");

    return FMT_FAIL;
}
ID oboinRead
TY static
MO ajoboread
LB core
XX
DE Given data in a oboin structure, tries to read everything needed
DE using the specified format or by trial and error.
XX
PN [1]
PA u oboin AjPOboin
PD obo term input object
PX
PN [2]
PA w obo AjPObo
PD obo term object
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool oboinRead(AjPOboin oboin, AjPObo obo)
{
    ajuint i;
    ajuint istat = 0;
    ajuint jstat = 0;

    AjPFilebuff buff = oboin->Input->Filebuff;
/*    AjBool regfile = ajFalse; */
    AjBool ok;

    AjPTextAccess textaccess = oboin->Input->Query->TextAccess;
    AjPOboAccess  oboaccess  = oboin->Input->Query->Access;

    ajOboClear(obo);
    ajDebug("oboinRead: cleared\n");

    if(oboin->Input->Single && oboin->Input->Count)
    {
	/*
	** One obo term at a time is read.
	** The first obo term was read by ACD
	** for the following ones we need to reset the AjPOboin
	**
	** Single is set by the access method
	*/

	ajDebug("oboinRead: single access - count %d - call access"
		" routine again\n",
		oboin->Input->Count);
	/* Calling funclist oboinAccess() */
	if(textaccess)
        {
            if(!(*textaccess->Access)(oboin->Input))
            {
                ajDebug("oboinRead: (*textaccess->Access)(oboin->Input) "
                        "*failed*\n");

                return ajFalse;
            }
        }

	if(oboaccess)
        {
            if(!(*oboaccess->Access)(oboin))
            {
                ajDebug("oboinRead: (*oboaccess->Access)(oboin) "
                        "*failed*\n");

                return ajFalse;
            }
        }

        buff = oboin->Input->Filebuff;
    }

    ajDebug("oboinRead: oboin format %d '%S'\n", oboin->Input->Format,
	    oboin->Input->Formatstr);

    oboin->Input->Count++;

    if(!oboin->Input->Filebuff)
	return ajFalse;

    ok = ajFilebuffIsBuffered(oboin->Input->Filebuff);

    while(ok)
    {				/* skip blank lines */
        ok = ajBuffreadLine(oboin->Input->Filebuff, &oboinReadLine);

        if(!ajStrIsWhite(oboinReadLine))
        {
            ajFilebuffClear(oboin->Input->Filebuff,1);
            break;
        }
    }

    if(!oboin->Input->Format)
    {			   /* no format specified, try all defaults */

/*        regfile = ajFileIsFile(ajFilebuffGetFile(oboin->Input->Filebuff)); */

	for(i = 1; oboinFormatDef[i].Name; i++)
	{
	    if(!oboinFormatDef[i].Try)	/* skip if Try is ajFalse */
		continue;

	    ajDebug("oboinRead:try format %d (%s)\n",
		    i, oboinFormatDef[i].Name);

	    istat = oboinReadFmt(oboin, obo, i);

	    switch(istat)
	    {
	    case FMT_OK:
		ajDebug("++oboinRead OK, set format %d\n",
                        oboin->Input->Format);
		oboDefine(obo, oboin);

		return ajTrue;
	    case FMT_BADTYPE:
		ajDebug("oboinRead: (a1) oboinReadFmt stat == BADTYPE "
                        "*failed*\n");

		return ajFalse;
	    case FMT_FAIL:
		ajDebug("oboinRead: (b1) oboinReadFmt stat == FAIL "
                        "*failed*\n");
		break;			/* we can try next format */
	    case FMT_NOMATCH:
		ajDebug("oboinRead: (c1) oboinReadFmt stat==NOMATCH "
                        "try again\n");
		break;
	    case FMT_EOF:
		ajDebug("oboinRead: (d1) oboinReadFmt stat == EOF "
                        "*failed*\n");
		return ajFalse;			/* EOF and unbuffered */
	    case FMT_EMPTY:
		ajWarn("obo term '%S' has zero length, ignored",
		       ajOboGetQryS(obo));
		ajDebug("oboinRead: (e1) oboinReadFmt stat==EMPTY "
                        "try again\n");
		break;
	    default:
		ajDebug("unknown code %d from oboinReadFmt\n", stat);
	    }

	    ajOboClear(obo);

	    if(oboin->Input->Format)
		break;			/* we read something */

            /*ajFilebuffTrace(oboin->Input->Filebuff);*/
	}

	if(!oboin->Input->Format)
	{		     /* all default formats failed, give up */
	    ajDebug("oboinRead:all default formats failed, give up\n");

	    return ajFalse;
	}

	ajDebug("++oboinRead set format %d\n", oboin->Input->Format);
    }
    else
    {					/* one format specified */
	ajDebug("oboinRead: one format specified\n");
	ajFilebuffSetUnbuffered(oboin->Input->Filebuff);

	ajDebug("++oboinRead known format %d\n", oboin->Input->Format);
	istat = oboinReadFmt(oboin, obo, oboin->Input->Format);

	switch(istat)
	{
	case FMT_OK:
	    oboDefine(obo, oboin);

	    return ajTrue;
	case FMT_BADTYPE:
	    ajDebug("oboinRead: (a2) oboinReadFmt stat == BADTYPE "
                    "*failed*\n");

	    return ajFalse;

        case FMT_FAIL:
	    ajDebug("oboinRead: (b2) oboinReadFmt stat == FAIL "
                    "*failed*\n");

	    return ajFalse;

        case FMT_NOMATCH:
	    ajDebug("oboinRead: (c2) oboinReadFmt stat == NOMATCH "
                    "*try again*\n");
	    break;
	case FMT_EOF:
	    ajDebug("oboinRead: (d2) oboinReadFmt stat == EOF "
                    "*try again*\n");
            if(oboin->Input->Records)
                ajErr("Error reading file '%F' with format '%s': "
                      "end-of-file before end of data "
                      "(read %u records)",
                      ajFilebuffGetFile(oboin->Input->Filebuff),
                      oboinFormatDef[oboin->Input->Format].Name,
                      oboin->Input->Records);
	    break;		     /* simply end-of-file */
	case FMT_EMPTY:
	    ajWarn("obo term '%S' has zero length, ignored",
		   ajOboGetQryS(obo));
	    ajDebug("oboinRead: (e2) oboinReadFmt stat == EMPTY "
                    "*try again*\n");
	    break;
	default:
	    ajDebug("unknown code %d from oboinReadFmt\n", stat);
	}

	ajOboClear(obo); /* 1 : read, failed to match id/acc/query */
    }

    /* failed - probably entry/accession query failed. Can we try again? */

    ajDebug("oboinRead failed - try again with format %d '%s' code %d\n",
	    oboin->Input->Format,
            oboinFormatDef[oboin->Input->Format].Name, istat);

    ajDebug("Search:%B Chunk:%B Data:%x ajFileBuffEmpty:%B\n",
	    oboin->Input->Search, oboin->Input->ChunkEntries,
            oboin->Input->TextData, ajFilebuffIsEmpty(buff));

    if(ajFilebuffIsEmpty(buff) && oboin->Input->ChunkEntries)
    {
	if(textaccess && !(*textaccess->Access)(oboin->Input))
            return ajFalse;
	else if(oboaccess && !(*oboaccess->Access)(oboin))
            return ajFalse;
        buff = oboin->Input->Filebuff;
    }


    /* need to check end-of-file to avoid repeats */
    while(oboin->Input->Search &&
          (oboin->Input->TextData || !ajFilebuffIsEmpty(buff)))
    {
	jstat = oboinReadFmt(oboin, obo, oboin->Input->Format);

	switch(jstat)
	{
	case FMT_OK:
	    oboDefine(obo, oboin);

	    return ajTrue;

        case FMT_BADTYPE:
	    ajDebug("oboinRead: (a3) oboinReadFmt stat == BADTYPE "
                    "*failed*\n");

	    return ajFalse;

        case FMT_FAIL:
	    ajDebug("oboinRead: (b3) oboinReadFmt stat == FAIL "
                    "*failed*\n");

	    return ajFalse;
            
	case FMT_NOMATCH:
	    ajDebug("oboinRead: (c3) oboinReadFmt stat == NOMATCH "
                    "*try again*\n");
	    break;
	case FMT_EOF:
	    ajDebug("oboinRead: (d3) oboinReadFmt stat == EOF "
                    "*failed*\n");

	    return ajFalse;			/* we already tried again */

        case FMT_EMPTY:
	    if(istat != FMT_EMPTY)
                ajWarn("obo term '%S' has zero length, ignored",
                       ajOboGetQryS(obo));
	    ajDebug("oboinRead: (e3) oboinReadFmt stat == EMPTY "
                    "*try again*\n");
	    break;

        default:
	    ajDebug("unknown code %d from oboinReadFmt\n", stat);
	}

	ajOboClear(obo); /* 1 : read, failed to match id/acc/query */
    }

    if(oboin->Input->Format)
	ajDebug("oboinRead: *failed* to read obo term %S using format %s\n",
		oboin->Input->Qry, oboinFormatDef[oboin->Input->Format].Name);
    else
	ajDebug("oboinRead: *failed* to read obo term %S using any format\n",
		oboin->Input->Qry);

    return ajFalse;
}
ID oboinReadObo
TY static
MO ajoboread
LB core
XX
DE Given data in an obo structure, tries to read everything needed
DE using the OBO format.
XX
PN [1]
PA u oboin AjPOboin
PD Obo input object
PX
PN [2]
PA w obo AjPObo
PD Obo object
PX
RT AjBool
RD ajTrue on success
RX
//


static AjBool oboinReadObo(AjPOboin oboin, AjPObo obo)
{
    AjPFilebuff buff;

    AjPFile obofile;
    ajlong fpos     = 0;
    ajlong spos     = 0;
    ajuint linecnt  = 0;
    AjBool incomplete = ajFalse;
    AjBool isterm = ajFalse;
    AjPStr token = NULL;
    AjPStr saveline = NULL;
    AjPStr rest  = NULL;
    AjPStr name   = NULL;
    AjPStr modifier = NULL;
    AjPStr comment  = NULL;
    AjPObotag obotag   = NULL;
    AjBool ok = ajFalse;

    ajDebug("oboinReadObo\n");
    ajOboClear(obo);
    buff = oboin->Input->Filebuff;
    obofile = ajFilebuffGetFile(buff);

    /* ajFilebuffTrace(buff); */
    ok = ajBuffreadLine(buff, &oboinReadLine);
    while(ok && !ajStrPrefixC(oboinReadLine, "[Term]"))
    {
        ok = ajBuffreadLine(buff, &oboinReadLine);
    }

    if(!ok)
        return ajFalse;

    isterm = ajTrue;

    ajFilebuffClear(buff, 1);

    ok = ajBuffreadLinePosStore(buff, &oboinReadLine, &fpos,
                                oboin->Input->Text, &obo->TextPtr);
    if(ok)
        ok = ajBuffreadLinePosStore(buff, &oboinReadLine, &fpos,
                                    oboin->Input->Text, &obo->TextPtr);

    while (ok)
    {
        ajStrTrimWhite(&oboinReadLine);

        if(!ajStrGetLen(oboinReadLine))
            break;

        if(ajStrGetCharFirst(oboinReadLine) == '[') /* new stanza */
            break;

        linecnt++;

/*        ajDebug("line %u:%S\n", linecnt, oboinReadLine);*/

        ajObolineCutComment(&oboinReadLine, &comment);
        ajObolineEscape(&oboinReadLine);

        if((ajStrGetCharLast(oboinReadLine) == '\\') &&
           (ajStrGetCharPos(oboinReadLine, -2) != '\\'))
        {
            incomplete = ajTrue;
            ajStrAppendS(&saveline, oboinReadLine);
            ok = ajBuffreadLinePosStore(buff, &oboinReadLine, &fpos,
                                        oboin->Input->Text, &obo->TextPtr);
            continue;
        }
        
        if(incomplete)          /* continued line */
        {
            ajStrAppendS(&saveline, oboinReadLine);
            ajStrAssignS(&oboinReadLine, saveline);
            ajStrAssignC(&saveline, "");
            incomplete = ajFalse;
        }

        if (!ajStrGetLen(oboinReadLine)) /* empty line */
        {
            if(!ajStrGetLen(comment))
                break;

            ok = ajBuffreadLinePosStore(buff, &oboinReadLine, &fpos,
                                        oboin->Input->Text, &obo->TextPtr);
            continue;
        }

        /* normal name:value record */

       if(ajObolineCutModifier(&oboinReadLine, &modifier))
            oboinDebug(obofile, linecnt,
                       "modifier found '%S'",
                       modifier);

        /* parse name: value ! comment */
        ajStrExtractWord(oboinReadLine, &rest, &token);

        if(!ajStrGetLen(rest))
            oboinWarn(obofile, linecnt, obo->Id,
                      "missing value for tag '%S' in Term '%S'",
                      token, obo->Id);
        ajStrAssignS(&name, token);

        if(ajStrGetCharLast(name) != ':')
            oboinWarn(obofile, linecnt, obo->Id,
                      "bad name '%S'",
                      token);
        ajStrCutEnd(&name, 1);

        obotag = ajObotagNewData(name, rest, modifier, comment, linecnt);

        if(ajStrMatchC(name, "id"))
        {
            if(MAJSTRGETLEN(obo->Id))
                oboinWarn(obofile, linecnt, obo->Id,
                        "Multiple '%S' tags for ID '%S'",
                        name, obo->Id);
            ajStrAssignS(&obo->Fullid, obotag->Value);
            spos = ajStrFindlastK(obotag->Value, ':');
            if(spos < 0)
                ajStrAssignS(&obo->Id, obotag->Value);
            else
            {
                ajStrAssignSubS(&obo->Db, obotag->Value, 0, spos-1);
                ajStrAssignSubS(&obo->Id, obotag->Value, spos+1, -1);
            }
        }
        else if(ajStrMatchC(name, "name"))
        {
            if(MAJSTRGETLEN(obo->Name))
                oboinWarn(obofile, linecnt, obo->Id,
                        "Multiple '%S' tags for ID '%S'",
                        name, obo->Id);
            ajStrAssignS(&obo->Name, obotag->Value);
        }
        else if(ajStrMatchC(name, "synonym"))
        {
            ajListPushAppend(obo->Taglist, obotag);
            obotag = NULL;
        }
        else if(ajStrMatchC(name, "namespace"))
        {
            if(MAJSTRGETLEN(obo->Namespace))
                oboinWarn(obofile, linecnt, obo->Id,
                        "Multiple '%S' tags for ID '%S'",
                        name, obo->Id);
            ajStrAssignS(&obo->Namespace, obotag->Value);
        }
        else if(ajStrMatchC(name, "def"))
        {
            if(MAJSTRGETLEN(obo->Def))
                oboinWarn(obofile, linecnt, obo->Id,
                        "Multiple '%S' tags for ID '%S'",
                        name, obo->Id);
            ajStrAssignS(&obo->Def, obotag->Value);
            obolineParseDef(obo, obotag->Value);
        }
        else if(ajStrMatchC(name, "comment"))
        {
            if(MAJSTRGETLEN(obo->Comment))
                oboinWarn(obofile, linecnt, obo->Id,
                        "Multiple '%S' tags for ID '%S'",
                        name, obo->Id);
            ajStrAssignS(&obo->Comment, obotag->Value);
        }
        else if(ajStrMatchC(name, "is_obsolete"))
        {
            if(obo->Obsolete)
                oboinWarn(obofile, linecnt, obo->Id,
                        "Multiple '%S' tags for ID '%S'",
                        name, obo->Id);
            if(ajStrPrefixC(obotag->Value, "true"))
                obo->Obsolete = ajTrue;
            else if(ajStrPrefixC(obotag->Value, "false"))
                obo->Obsolete = ajFalse;
            else
                oboinWarn(obofile, linecnt, obo->Id,
                        "Bad value '%S' boolean tag for ID '%S'",
                        name, obo->Id);
            ajListPushAppend(obo->Taglist, obotag);
            obotag = NULL;
        }
        else if(ajStrMatchC(name, "builtin"))
        {
            if(obo->Obsolete)
                oboinWarn(obofile, linecnt, obo->Id,
                        "Multiple '%S' tags for ID '%S'",
                        name, obo->Id);
            if(ajStrPrefixC(obotag->Value, "true"))
                obo->Builtin = ajTrue;
            else if(ajStrPrefixC(obotag->Value, "false"))
                obo->Builtin = ajFalse;
            else
                oboinWarn(obofile, linecnt, obo->Id,
                        "Bad value '%S' boolean tag for ID '%S'",
                        name, obo->Id);
            ajListPushAppend(obo->Taglist, obotag);
            obotag = NULL;
        }
        else if(ajStrMatchC(name, "replaced_by"))
        {
            /*
            if(MAJSTRGETLEN(obo->Replaced))
                oboinWarn(obofile, linecnt, obo->Id,
                        "Multiple '%S' tags for ID '%S'",
                        name, obo->Id);
            */
            ajStrAssignS(&obo->Replaced, obotag->Value);
            ajListPushAppend(obo->Taglist, obotag);
            obotag = NULL;
        }
        else if(ajStrMatchC(name, "consider"))
        {
            ajStrAssignS(&obo->Replaced, obotag->Value);
            ajListPushAppend(obo->Taglist, obotag);
            obotag = NULL;
        }
        else 
        {
            ajListPushAppend(obo->Taglist, obotag);
            obotag = NULL;
        }
        ajObotagDel(&obotag);
        ok = ajBuffreadLinePosStore(buff, &oboinReadLine, &fpos,
                               oboin->Input->Text, &obo->TextPtr);
    }

    if(!isterm)
    {
        oboinWarn(obofile, linecnt, obo->Id,
                "Bad OBO term '%S' '%S'",
                name, obo->Id);
        return ajFalse;
    }

    /* ajDebug("First line: %S\n", oboinReadLine); */

    if(ok)
        ajFilebuffClearStore(buff, 1,
                             oboinReadLine,
                             oboin->Input->Text, &obo->TextPtr);
    else
        ajFilebuffClear(buff, 0);

    ajStrDel(&token);
    ajStrDel(&rest);
    ajStrDel(&saveline);
    ajStrDel(&name);
    ajStrDel(&modifier);
    ajStrDel(&comment);

    return ajTrue;
}
ID ajOboallNew
TY public
MO ajoboread
LB core
XX
DE Creates a new obo input stream object.
XX
RT AjPOboall
RD New obo input stream object.
RX
//


AjPOboall ajOboallNew(void)
{
    AjPOboall pthis;

    AJNEW0(pthis);

    pthis->Oboin = ajOboinNew();
    pthis->Obo   = ajOboNew();

    return pthis;
}
ID ajOboallDel
TY public
MO ajoboread
LB core
XX
DE Deletes an obo input stream object.
XX
PN [1]
PA d pthis AjPOboall*
PD Obo input stream
PX
RT void
RD 
RX
//


void ajOboallDel(AjPOboall* pthis)
{
    AjPOboall thys;

    if(!pthis)
        return;

    thys = *pthis;

    if(!thys)
        return;

    ajOboinDel(&thys->Oboin);
    if(!thys->Returned)
        ajOboDel(&thys->Obo);

    AJFREE(*pthis);

    return;
}
ID ajOboallClear
TY public
MO ajoboread
LB core
XX
DE Clears an obo term input stream object back to "as new" condition, except
DE for the query list which must be preserved.
XX
PN [1]
PA w thys AjPOboall
PD obo term input stream
PX
RT void
RD 
RX
//


void ajOboallClear(AjPOboall thys)
{

    ajDebug("ajOboallClear called\n");

    if(!thys)
        return;

    ajOboinClear(thys->Oboin);

    ajOboClear(thys->Obo);

    thys->Returned = ajFalse;

    return;
}
ID ajOboallGetQryS
TY public
MO ajoboread
LB core
XX
DE Returns the query of an input stream
XX
PN [1]
PA r thys const AjPOboall
PD obo term input stream
PX
RT const AjPStr
RD Identifier
RX
//


const AjPStr ajOboallGetQryS(const AjPOboall thys)
{
    if(!thys)
        return NULL;

    ajDebug("ajOboallGetQryS called\n");

    return ajOboinGetQryS(thys->Oboin);
}
ID ajOboallGetoboId
TY public
MO ajoboread
LB core
XX
DE Returns the identifier of the current obo term in an input stream
XX
PN [1]
PA r thys const AjPOboall
PD obo term input stream
PX
RT const AjPStr
RD Identifier
RX
//


const AjPStr ajOboallGetoboId(const AjPOboall thys)
{
    if(!thys)
        return NULL;

    ajDebug("ajOboallGetoboId called\n");

    return ajOboGetId(thys->Obo);
}
ID ajOboallNext
TY public
MO ajoboread
LB core
XX
DE Parse an OBO query into format, access, file and entry
DE
DE Split at delimiters. Check for the first part as a valid format
DE Check for the remaining first part as a database name or as a file
DE that can be opened.
DE Anything left is an entryname spec.
DE
DE Return the results in the AjPObo object but leave the file open for
DE future calls.
XX
PN [1]
PA w thys AjPOboall
PD Obo term input stream
PX
PN [2]
PA u Pobo AjPObo*
PD Obo term returned
PX
RT AjBool
RD ajTrue on success.
RX
//


AjBool ajOboallNext(AjPOboall thys, AjPObo *Pobo)
{
    if(!thys->Count)
    {
	thys->Count = 1;

	thys->Totterms++;

	*Pobo = thys->Obo;
	thys->Returned = ajTrue;

	return ajTrue;
    }


    if(ajOboinRead(thys->Oboin, thys->Obo))
    {
	thys->Count++;

	thys->Totterms++;

	*Pobo = thys->Obo;
	thys->Returned = ajTrue;

	ajDebug("ajOboallNext success\n");

	return ajTrue;
    }

    *Pobo = NULL;

    ajDebug("ajOboallNext failed\n");

    ajOboallClear(thys);

    return ajFalse;
}
ID oboinformatFind
TY static
MO ajoboread
LB core
XX
DE Looks for the specified format(s) in the internal definitions and
DE returns the index.
DE
DE Sets iformat as the recognised format, and returns ajTrue.
XX
PN [1]
PA r format const AjPStr
PD Format required.
PX
PN [2]
PA w iformat ajint*
PD Index
PX
RT AjBool
RD ajTrue on success.
RX
//


static AjBool oboinformatFind(const AjPStr format, ajint* iformat)
{
    AjPStr tmpformat = NULL;
    ajuint i = 0;

    /* ajDebug("oboinformatFind '%S'\n", format); */
    if(!ajStrGetLen(format))
	return ajFalse;

    ajStrAssignS(&tmpformat, format);
    ajStrFmtLower(&tmpformat);

    for(i=0; oboinFormatDef[i].Name; i++)
    {
	/* ajDebug("test %d '%s' \n", i, oboinFormatDef[i].Name); */
	if(ajStrMatchCaseC(tmpformat, oboinFormatDef[i].Name))
	{
	    *iformat = i;
	    ajStrDel(&tmpformat);
	    /* ajDebug("found '%s' at %d\n", oboinFormatDef[i].Name, i); */
	    return ajTrue;
	}
    }

    ajErr("Unknown input format '%S'", format);

    ajStrDel(&tmpformat);

    return ajFalse;
}
ID ajOboinformatTerm
TY public
MO ajoboread
LB core
XX
DE tests whether an OBO input format term is known
XX
PN [1]
PA r term const AjPStr
PD Format term EDAM ID
PX
RT AjBool
RD ajTrue if term was accepted
RX
//


AjBool ajOboinformatTerm(const AjPStr term)
{
    ajuint i;

    for(i=0; oboinFormatDef[i].Name; i++)
	if(ajStrMatchCaseC(term, oboinFormatDef[i].Obo))
	    return ajTrue;

    return ajFalse;
}
ID ajOboinformatTest
TY public
MO ajoboread
LB core
XX
DE tests whether a named OBO input format is known
XX
PN [1]
PA r format const AjPStr
PD Format
PX
RT AjBool
RD ajTrue if format was accepted
RX
//


AjBool ajOboinformatTest(const AjPStr format)
{
    ajuint i;

    for(i=0; oboinFormatDef[i].Name; i++)
	if(ajStrMatchCaseC(format, oboinFormatDef[i].Name))
	    return ajTrue;

    return ajFalse;
}
ID ajOboqryGetObo
TY public
MO ajoboread
LB core
XX
DE Returns an obo term given a query
XX
PN [1]
PA r qry const AjPStr
PD Query
PX
PN [2]
PA u obo AjPObo
PD obo term
PX
RT AjBool
RD ajTrue on success
RX
//


AjBool ajOboqryGetObo(const AjPStr qry, AjPObo obo)
{
    AjPOboin oboin;
    AjBool ok;

    oboin        = ajOboinNew();

    ajOboinQryS(oboin, qry);
    ok = ajOboinRead(oboin, obo);
    ajOboinDel(&oboin);

    if(!ok)
	return ajFalse;

    return ajTrue;
}
ID oboinQryProcess
TY static
MO ajoboread
LB core
XX
DE Converts an obo query into an open file.
DE
DE Tests for "format::" and sets this if it is found
DE
DE Then tests for "list:" or "
XX
PN [1]
PA u oboin AjPOboin
PD obo term input structure.
PX
PN [2]
PA u obo AjPObo
PD obo term to be read. The format will be replaced
PD if defined in the query string.
PX
RT AjBool
RD ajTrue on success.
RX
//


static AjBool oboinQryProcess(AjPOboin oboin, AjPObo obo)
{
    AjBool ret = ajTrue;
    AjPStr qrystr = NULL;
    AjBool obomethod = ajFalse;
    const AjPStr fmtstr = NULL;
    AjPTextin textin;
    AjPQuery qry;
    AjPOboAccess oboaccess = NULL;

    textin = oboin->Input;
    qry = textin->Query;

    /* pick up the original query string */
    qrystr = ajStrNewS(textin->Qry);

    ajDebug("oboinQryProcess '%S'\n", qrystr);

    /* look for a format:: prefix */
    fmtstr = ajQuerystrParseFormat(&qrystr, textin, oboinformatFind);
    ajDebug("oboinQryProcess ... fmtstr '%S' '%S'\n", fmtstr, qrystr);

    /* (seq/feat) DO NOT look for a [range] suffix */

    /* look for a list:: or @:: listfile of queries  - process and return */
    if(ajQuerystrParseListfile(&qrystr))
    {
        ajDebug("oboinQryProcess ... listfile '%S'\n", qrystr);
        ret = oboinListProcess(oboin, obo, qrystr);
        ajStrDel(&qrystr);
        return ret;
    }

    /* try general text access methods (file, asis, text database access */
    ajDebug("oboinQryProcess ... no listfile '%S'\n", qrystr);
    if(!ajQuerystrParseRead(&qrystr, textin, oboinformatFind, &obomethod))
    {
        ajStrDel(&qrystr);
        return ajFalse;
    }
    
    oboinFormatSet(oboin, obo);

    ajDebug("oboinQryProcess ... read nontext: %B '%S'\n",
            obomethod, qrystr);
    ajStrDel(&qrystr);

    /* we found a non-text method */
    if(obomethod)
    {
        ajDebug("oboinQryProcess ... call method '%S'\n", qry->Method);
        ajDebug("oboinQryProcess ... textin format %d '%S'\n",
                textin->Format, textin->Formatstr);
        ajDebug("oboinQryProcess ...  query format  '%S'\n",
                qry->Formatstr);
        qry->Access = ajCallTableGetS(oboDbMethods,qry->Method);
        oboaccess = qry->Access;
        return (*oboaccess->Access)(oboin);
    }

    ajDebug("oboinQryProcess text method '%S' success\n", qry->Method);

    return ajTrue;
}
ID oboinListProcess
TY static
MO ajoboread
LB core
XX
DE Processes a file of queries.
DE This function is called by, and calls, oboinQryProcess. There is
DE a depth check to avoid infinite loops, for example where a list file
DE refers to itself.
DE
DE This function produces a list (AjPList) of queries with all list references
DE expanded into lists of queries.
DE
DE Because queries in a list can have their own format
DE the prior settings are stored with each query in the list node so that they
DE can be restored after.
XX
PN [1]
PA u oboin AjPOboin
PD obo term input
PX
PN [2]
PA u obo AjPObo
PD obo term
PX
PN [3]
PA r listfile const AjPStr
PD Name of list file.,
PX
RT AjBool
RD ajTrue on success.
RX
//


static AjBool oboinListProcess(AjPOboin oboin, AjPObo obo,
                               const AjPStr listfile)
{
    AjPList list  = NULL;
    AjPFile file  = NULL;
    AjPStr token  = NULL;
    AjPStrTok handle = NULL;
    AjBool ret       = ajFalse;
    AjPQueryList node = NULL;

    static ajint depth    = 0;
    static ajint MAXDEPTH = 16;

    depth++;
    ajDebug("++oboListProcess %S depth %d\n",
	    listfile, depth);

    if(depth > MAXDEPTH)
	ajFatal("Query list too deep");

    if(!oboin->Input->List)
	oboin->Input->List = ajListNew();

    list = ajListNew();

    file = ajFileNewInNameS(listfile);

    if(!file)
    {
	ajErr("Failed to open list file '%S'", listfile);
	depth--;

	return ret;
    }

    while(ajReadlineTrim(file, &oboinReadLine))
    {
	oboinListNoComment(&oboinReadLine);

	if(ajStrGetLen(oboinReadLine))
	{
	    ajStrTokenAssignC(&handle, oboinReadLine, " \t\n\r");
	    ajStrTokenNextParse(&handle, &token);
	    /* ajDebug("Line  '%S'\n");*/
	    /* ajDebug("token '%S'\n", oboinReadLine, token); */

	    if(ajStrGetLen(token))
	    {
	        ajDebug("++Add to list: '%S'\n", token);
	        AJNEW0(node);
	        ajStrAssignS(&node->Qry, token);
	        oboinQrySave(node, oboin);
	        ajListPushAppend(list, node);
	    }

	    ajStrDel(&token);
	    token = NULL;
	}
    }

    ajFileClose(&file);
    ajStrDel(&token);

    /*
    ajDebug("Trace oboin->Input->List\n");
    ajQuerylistTrace(oboin->Input->List);
    ajDebug("Trace new list\n");
    ajQuerylistTrace(list);
    */

    ajListPushlist(oboin->Input->List, &list);

    ajDebug("Trace combined oboin->Input->List\n");
    ajQuerylistTrace(oboin->Input->List);

    /*
     ** now try the first item on the list
     ** this can descend recursively if it is also a list
     ** which is why we check the depth above
     */

    if(ajListPop(oboin->Input->List, (void**) &node))
    {
        ajDebug("++pop first item '%S'\n", node->Qry);
	ajOboinQryS(oboin, node->Qry);
	oboinQryRestore(oboin, node);
	ajStrDel(&node->Qry);
	ajStrDel(&node->Formatstr);
	AJFREE(node);
	ajDebug("descending with query '%S'\n", oboin->Input->Qry);
	ret = oboinQryProcess(oboin, obo);
    }

    ajStrTokenDel(&handle);
    depth--;
    ajDebug("++oboListProcess depth: %d returns: %B\n", depth, ret);

    return ret;
}
ID oboinListNoComment
TY static
MO ajoboread
LB core
XX
DE Strips comments from a character string (a line from an ACD file).
DE Comments are blank lines or any text following a "#" character.
XX
PN [1]
PA u text AjPStr*
PD Line of text from input file.
PX
RT void
RD 
RX
//


static void oboinListNoComment(AjPStr* text)
{
    ajuint i;
    char *cp;

    i = ajStrGetLen(*text);

    if(!i)				/* empty string */
	return;

    MAJSTRGETUNIQUESTR(text);
    
    cp = strchr(ajStrGetPtr(*text), '#');

    if(cp)
    {					/* comment found */
	*cp = '\0';
	ajStrSetValid(text);
    }

    return;
}
ID oboinFormatSet
TY static
MO ajoboread
LB core
XX
DE Sets the input format for an obo term using the obo term input object's
DE defined format, or a default from variable 'EMBOSS_FORMAT'.
XX
PN [1]
PA u oboin AjPOboin
PD obo term input.
PX
PN [2]
PA u obo AjPObo
PD obo term.
PX
RT AjBool
RD ajTrue on success.
RX
//


static AjBool oboinFormatSet(AjPOboin oboin, AjPObo obo)
{

    if(ajStrGetLen(oboin->Input->Formatstr))
    {
	ajDebug("... input format value '%S'\n", oboin->Input->Formatstr);

	if(oboinformatFind(oboin->Input->Formatstr, &oboin->Input->Format))
	{
	    ajStrAssignS(&obo->Formatstr, oboin->Input->Formatstr);
	    obo->Format = oboin->Input->Format;
	    ajDebug("...format OK '%S' = %d\n", oboin->Input->Formatstr,
		    oboin->Input->Format);
	}
	else
	    ajDebug("...format unknown '%S'\n", oboin->Input->Formatstr);

	return ajTrue;
    }
    else
	ajDebug("...input format not set\n");


    return ajFalse;
}
ID ajOboqueryGetallObofields
TY public
MO ajoboread
LB core
XX
DE Returns the standard obo query string from a query
XX
PN [1]
PA r query const AjPQuery
PD Query
PX
PN [2]
PA w id const AjPStr*
PD Id query
PX
PN [3]
PA w acc const AjPStr*
PD Accession query
PX
PN [4]
PA w nam const AjPStr*
PD Name query
PX
PN [5]
PA w des const AjPStr*
PD Description query
PX
PN [6]
PA w up const AjPStr*
PD Parent query
PX
PN [7]
PA w space const AjPStr*
PD Namespace query
PX
RT ajuint
RD Number of fields found
RX
//


ajuint ajOboqueryGetallObofields(const AjPQuery query,
                                 const AjPStr *id, const AjPStr *acc,
                                 const AjPStr *nam, const AjPStr *des,
                                 const AjPStr *up, const AjPStr *space)
{
    ajuint ret = 0;
    AjIList iter = NULL;
    AjPQueryField field = NULL;

    iter = ajListIterNewread(query->QueryFields);
    while(!ajListIterDone(iter))
    {
        field = ajListIterGet(iter);
        ret++;
        if(ajStrMatchC(field->Field, "id"))
            *id = field->Wildquery;
        else if(ajStrMatchC(field->Field, "acc"))
            *acc = field->Wildquery;
        else if(ajStrMatchC(field->Field, "nam"))
            *nam = field->Wildquery;
        else if(ajStrMatchC(field->Field, "des"))
            *des = field->Wildquery;
        else if(ajStrMatchC(field->Field, "up"))
            *up = field->Wildquery;
        else if(ajStrMatchC(field->Field, "div"))
            *space = field->Wildquery;
        else
            ret--;
    }
    ajListIterDel(&iter);

    return ret;
}
ID oboQueryMatch
TY static
MO ajoboread
LB core
XX
DE Compares an obo term to a query and returns true if they match.
XX
PN [1]
PA r thys const AjPQuery
PD query.
PX
PN [2]
PA r obo const AjPObo
PD obo term.
PX
RT AjBool
RD ajTrue if the obo term matches the query.
RX
//


static AjBool oboQueryMatch(const AjPQuery thys, const AjPObo obo)
{
    AjBool tested = ajFalse;
    AjIList iter  = NULL;
    AjIList iterfield  = NULL;
    AjPQueryField field = NULL;
    AjBool ok = ajFalse;

    ajDebug("oboQueryMatch '%S' fields: %Lu Case %B Done %B\n",
	    obo->Name, ajListGetLength(thys->QueryFields),
            thys->CaseId, thys->QryDone);

    if(!thys)			   /* no query to test, that's fine */
	return ajTrue;

    if(thys->QryDone)			/* do we need to test here? */
	return ajTrue;

    /* test the query field(s) */

    iterfield = ajListIterNewread(thys->QueryFields);
    while(!ajListIterDone(iterfield))
    {
        field = ajListIterGet(iterfield);

        ajDebug("  field: '%S' Query: '%S'\n",
                field->Field, field->Wildquery);
        if(ajStrMatchC(field->Field, "id"))
        {
            ajDebug("  id test: '%S'\n",
                    obo->Id);
            if(thys->CaseId)
            {
                if(ajStrMatchWildS(obo->Id, field->Wildquery))
                {
                    ajListIterDel(&iterfield);
                    return ajTrue;
                }
            }
            else
            {
                if(ajStrMatchWildCaseS(obo->Id, field->Wildquery))
                {
                    ajListIterDel(&iterfield);
                    return ajTrue;
                }
            }
            
            ajDebug("id test failed\n");
            tested = ajTrue;
            ok = ajFalse;
        }

        if(ajStrMatchC(field->Field, "nam")) /* test Sv and Gi */
        {
            ajDebug(" nam test: '%S'\n",
                    obo->Name);
            if(ajStrMatchWildCaseS(obo->Name, field->Wildquery))
            {
                ajListIterDel(&iterfield);
                return ajTrue;
            }

            ajDebug("sv test failed\n");
            tested = ajTrue;
            ok = ajFalse;
        }

        if(ajStrMatchC(field->Field, "acc")) /* test id, use trueid */
        {
            if(ajStrMatchWildCaseS(obo->Id, field->Wildquery))
            {
                ajListIterDel(&iterfield);
                return ajTrue;
            }
        }

        if(ajStrMatchC(field->Field, "des"))
        {
            ajDebug("  des test: '%S'\n",
                    obo->Def);
            if(ajStrGetLen(obo->Def))
            {            /* description test - check the string */
                ajDebug("... try description '%S' '%S'\n", obo->Def,
                        field->Wildquery);

                if(ajStrMatchWildWordCaseS(obo->Def, field->Wildquery))
                {
                    ajListIterDel(&iterfield);
                    return ajTrue;
                }

                tested = ajTrue;
                ajDebug("des test failed\n");
                ajListIterDel(&iter);
            }
            else
            {
                ajDebug("des test failed - nothing to test\n");
                ajListIterDel(&iterfield);
                return ajFalse;
            }
        }
    }
        
    ajListIterDel(&iterfield);

    if(!tested)		    /* nothing to test, so accept it anyway */
    {
        ajDebug("  no tests: assume OK\n");
	return ajTrue;
    }
    
    ajDebug("result: %B\n", ok);

    return ok;
}
ID oboDefine
TY static
MO ajoboread
LB core
XX
DE Make sure all obo term object attributes are defined
DE using values from the obo input object if needed
XX
PN [1]
PA w thys AjPObo
PD Obo term returned.
PX
PN [2]
PA u oboin AjPOboin
PD Obo term input definitions
PX
RT AjBool
RD ajTrue on success.
RX
//


static AjBool oboDefine(AjPObo thys, AjPOboin oboin)
{

    /* if values are missing in the OBO object, we can use defaults
       from oboin or calculate where possible */

    /*ajDebug("oboDefine: thys->Db '%S', oboin->Db '%S'\n",
      thys->Db, oboin->Db);*/
    /*ajDebug("oboDefine: thys->Name '%S' type: %S\n",
      thys->Name, thys->Type);*/
    /*ajDebug("oboDefine: thys->Entryname '%S', oboin->Entryname '%S'\n",
      thys->Entryname, oboin->Entryname);*/

    /* assign the dbname and entryname if defined in the oboin object */
    if(ajStrGetLen(oboin->Input->Db))
      ajStrAssignEmptyS(&thys->Db, oboin->Input->Db);

    /*ajDebug("oboDefine: returns thys->Name '%S' type: %S\n",
      thys->Name, thys->Type);*/

    return ajTrue;
}
ID oboinQryRestore
TY static
MO ajoboread
LB core
XX
DE Restores an obo input specification from an AjPQueryList node
XX
PN [1]
PA w oboin AjPOboin
PD Obo input object
PX
PN [2]
PA r node const AjPQueryList
PD Query list node
PX
RT void
RD 
RX
//


static void oboinQryRestore(AjPOboin oboin, const AjPQueryList node)
{
    oboin->Input->Format = node->Format;
    oboin->Input->Fpos   = node->Fpos;
    ajStrAssignS(&oboin->Input->Formatstr, node->Formatstr);
    ajStrAssignS(&oboin->Input->QryFields, node->QryFields);

    return;
}
ID oboinQrySave
TY static
MO ajoboread
LB core
XX
DE Saves an obo input specification in an OboPListQry node
XX
PN [1]
PA w node AjPQueryList
PD Query list node
PX
PN [2]
PA r oboin const AjPOboin
PD Obo input object
PX
RT void
RD 
RX
//


static void oboinQrySave(AjPQueryList node, const AjPOboin oboin)
{
    node->Format   = oboin->Input->Format;
    node->Fpos     = oboin->Input->Fpos;
    ajStrAssignS(&node->Formatstr, oboin->Input->Formatstr);
    ajStrAssignS(&node->QryFields, oboin->Input->QryFields);

    return;
}
ID ajObodataNew
TY public
MO ajoboread
LB core
XX
DE Obo data internals constructor
XX
RT AjPOboData
RD New object
RX
//


AjPOboData ajObodataNew(void)
{
    AjPOboData ret;

    AJNEW0(ret);

    return ret;
}
ID ajObodataParseObofile
TY public
MO ajoboread
LB core
XX
DE Parse an OBO format file
XX
PN [1]
PA u obofile AjPFile
PD OBO format input file
PX
PN [2]
PA r validations const char*
PD Validations to turn on or off
PX
RT AjPOboData
RD Ontology data object
RX
//


AjPOboData ajObodataParseObofile(AjPFile obofile, const char* validations)
{
    AjPOboData ret;
    AjPList idprefixlist = NULL;
    ajuint linecnt = 0;
    AjPStr line     = NULL;
    AjPStr saveline = NULL;
    AjBool isstanza   = ajFalse;
    AjBool incomplete = ajFalse;
    AjPStr token = NULL;
    AjPStr rest  = NULL;
    AjPStr stanzatype     = NULL;
    AjPStr laststanzatype = NULL;
    AjPStr id     = NULL;
    AjPStr altid  = NULL;
    AjPStr lastid = NULL;
    AjPStr name   = NULL;
    AjPStr modifier = NULL;
    AjPStr comment  = NULL;
    AjBool ok = ajFalse;
    ajulong i;
    ajulong j;
    ajint icmp;
    ajlong icolon;
    AjBool found;
    AjBool isterm = AJFALSE;
    AjPObo term    = NULL;
    AjPObo altterm = NULL;
    AjPObo oldterm = NULL;
    AjPList stanzalist = NULL;
    AjPList oldlist    = NULL;
    AjPObotag obotag   = NULL;
    ajuint ntypes = 0;
    AjPStr *typeids    = NULL;
    AjPList *typetags  = NULL;
    ajuint nterms = 0;
    AjPStr *termids    = NULL;
    AjPObo *terms  = NULL;
    AjIList iter       = NULL;
    AjIList iterpref   = NULL;
    ajulong s;
    ajlong k;
    AjPStr t;
    AjPList u;
    AjPObo uterm;
    ajulong jsave;
    ajuint jj;
    AjPStr idprefix       = NULL;
    AjPStr tmpstr         = NULL;
    AjPStr tmppref        = NULL;
    AjPStrTok idsplit     = NULL;
    AjPStrTok validsplit  = NULL;
    AjPStr validstr       = NULL;
    AjBool obovalid          = ajTrue;
    AjBool obovalididorder   = ajTrue;
    AjBool obovalididunk     = ajTrue;
    AjBool obovalidmultisyn  = ajTrue;
    AjPStr namekey = NULL;
    AjPStr namestr = NULL;
    AjPList namelist = NULL;
    const AjPObo isaterm = NULL;
    AjPStr tmpid = NULL;
    AjPStr tmpname = NULL;
    ajlong ipos;

    ajDebug("ajObodataParseObofile '%F', validations '%s'\n",
            obofile, validations);

    ret = ajObodataNew();
    namelist = ajListNew();

    if(validations)
    {
        validstr = ajStrNewC(validations);
        ajStrTokenAssignC(&validsplit, validstr, ",");

        while(ajStrTokenNextParse(&validsplit, &tmpstr))
        {
            if(ajStrMatchC(tmpstr, "none"))
            {
                obovalid = ajFalse;
                obovalididorder = ajFalse;
/*                obovalididunk = ajFalse;*/
                obovalidmultisyn = ajFalse;
            }
            else if(ajStrMatchC(tmpstr, "noidorder"))
                obovalididorder = ajFalse;
            else if(ajStrMatchC(tmpstr, "nounkid"))
                obovalididunk = ajFalse;
            else if(ajStrMatchC(tmpstr, "nomultisynonym"))
                obovalidmultisyn = ajFalse;
            else
                ajWarn("ajObodataParseObofile: unknown validation '%S'",
                       tmpstr);
        }
        
    }
    
    ret->Termtable = ajTablestrNew(35000);
    ret->Termnametable = ajTablestrNew(35000);
    ret->Typedeftable = ajTablestrNew(1000);
    ret->Instancetable = ajTablestrNew(1000);

    idprefixlist = ajListstrNew();

    while(ajReadlineTrim(obofile, &line))
    {
        linecnt++;

        if(ajStrGetLen(line) && isspace((int) ajStrGetCharLast(line)))
           oboinWarn(obofile, linecnt, term->Id,
                   "trailing whitespace on line");

        ajObolineCutComment(&line, &comment);
        ajObolineEscape(&line);

        if((ajStrGetCharLast(line) == '\\') &&
           (ajStrGetCharPos(line, -2) != '\\'))
        {
            incomplete = ajTrue;
            ajStrAppendS(&saveline, line);
            continue;
        }
        
        if(incomplete)          /* continued line */
        {
            ajStrAppendS(&saveline, line);
            ajStrAssignS(&line, saveline);
            ajStrAssignC(&saveline, "");
            incomplete = ajFalse;
        }

        if (!ajStrGetLen(line)) /* empty line */
        {
            continue;
        }
        else if(ajStrGetCharFirst(line) == '[')
        {
            if(!isstanza) 
            {
                /* validate header */
                isstanza = ajTrue;
            }
            else
            {
                if(stanzalist)
                {
                    if(isterm)
                    {
                        ajListPushlist(term->Taglist, &stanzalist);
                        oldterm = ajTablePut(ret->Termtable, id, term);

                        if(oldterm)
                        {
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Duplicate %S id '%S'",
                                    stanzatype, id);
                            oldterm = NULL;
                        }

                        while(ajListGetLength(namelist))
                        {
                            namekey = NULL;
                            ajListstrPop(namelist, &namestr);

                            if(ajStrGetLen(term->Namespace))
                                ajFmtPrintS(&namekey, "%S:%S",
                                            term->Namespace,
                                            namestr);
                            else
                                namekey = ajStrNewS(namestr);

                            if(term->Obsolete)
                                ajStrAppendC(&namekey, " (obsolete)");

                            oldterm = ajTablePut(ret->Termnametable,
                                                 namekey, term);
                            if(oldterm)
                            {
                                if(!ajStrMatchS(oldterm->Id, term->Id))
                                    oboinWarn(obofile, linecnt, term->Id,
                                              "%S Name/Synonym %S "
                                              "duplicates '%S'",
                                              term->Id, namekey, oldterm->Id);
                                else if(obovalidmultisyn)
                                    oboinWarn(obofile, linecnt, term->Id,
                                              "%S Name/Synonym %S "
                                              "multiply defined",
                                              term->Id, namekey);
                                oldterm = NULL;
                            }
                        }
                    }
                    else
                    {
                        if(ajStrMatchC(stanzatype, "Typedef"))
                            oldlist = ajTablePut(ret->Typedeftable,
                                                 id, stanzalist);
                        else if(ajStrMatchC(stanzatype, "Instance"))
                            oldlist = ajTablePut(ret->Instancetable,
                                                 id, stanzalist);
                        else if(ajStrMatchC(stanzatype, "Annotation"))
                            oldlist = ajTablePut(ret->Annotable,
                                                 id, stanzalist);
                        else if(ajStrMatchC(stanzatype, "Formula"))
                            oldlist = ajTablePut(ret->Formulatable,
                                                 id, stanzalist);
                        else 
                            oldlist = ajTablePut(ret->Misctable,
                                                 id, stanzalist);
                        if(oldlist)
                        {
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Duplicate %S id '%S'",
                                    stanzatype, id);
                            oldlist = NULL;
                        }
                    }
                }                
            }

            id = NULL;
            stanzalist = ajListNew();
            
            ajStrExtractWord(line, &rest, &token);
            if(ajStrGetLen(rest))
                oboinWarn(obofile, linecnt, term->Id,
                        "unexpected characters in stanza line: '%S'",
                        rest);

            if(ajStrGetCharLast(token) != ']')
                oboinWarn(obofile, linecnt, term->Id,
                        "unexpected characters in stanza type: '%S'",
                        token);

            ajStrAssignS(&laststanzatype, stanzatype);
            ajStrAssignS(&stanzatype, token);
            ajStrCutEnd(&stanzatype,1);
            ajStrCutStart(&stanzatype,1);

            if(ajStrMatchC(stanzatype, "Term"))
            {
                isterm = ajTrue;
                term = ajOboNew();
            }
            else
                isterm = ajFalse;

            if(ajStrGetLen(laststanzatype) &&
               !ajStrMatchS(laststanzatype, stanzatype))
            {
                ok = ajFalse;

                for(i=0; oboStanza[i]; i++)
                {
                    if(ajStrMatchC(laststanzatype, oboStanza[i]))
                    {
                        for(j = i + 1; oboStanza[j]; j++)
                        {
                            if(ajStrMatchC(stanzatype, oboStanza[j]))
                            {
                                ok = ajTrue;
                                break;
                            }
                        }
                        break;
                    }
                }

                if(!ok && obovalid)
                    oboinWarn(obofile, linecnt, term->Id,
                            "stanza '%S' after '%S'",
                            stanzatype, laststanzatype);
            }
            
            
            ok = ajFalse;

            for(i=0;oboStanza[i];i++)
            {
                if(ajStrMatchC(stanzatype, oboStanza[i]))
                    ok = ajTrue;
            }

            if(!ok)
                oboinWarn(obofile, linecnt, term->Id,
                        "unknown stanza type '%S'",
                        token);
        }
        else 
        {
            if(ajObolineCutModifier(&line, &modifier))
            {
                if(term)
                    ajDebug("%F %u: (%S) modifier found '%S'\n",
                            obofile, linecnt, term->Id,
                            modifier);
                else
                    ajDebug("%F %u: header modifier found '%S'\n",
                            obofile, linecnt,
                            modifier);
            }
            /* parse name: value ! comment */
            ajStrExtractWord(line, &rest, &token);

            if(!ajStrGetLen(rest))
            {
                if(isterm)
                    oboinWarn(obofile, linecnt, term->Id,
                              "missing value for tag '%S' in %S '%S'",
                              token, stanzatype, id);
                else
                    oboinWarn(obofile, linecnt, NULL,
                              "missing value for tag '%S' in %S '%S'",
                              token, stanzatype, id);
                    }
            
            ajStrAssignS(&name, token);

            if(ajStrGetCharLast(name) != ':')
                oboinWarn(obofile, linecnt, term->Id,
                        "bad name '%S'",
                        token);
            ajStrCutEnd(&name, 1);

            if(ajStrMatchC(name, "id"))
            {
                ajStrAssignS(&id, rest);
                icolon = ajStrFindAnyK(id, ':');

                if(icolon > 0)
                {
                    ajStrAssignSubS(&tmpstr, id, 0, icolon);

                    if(!ajStrMatchS(idprefix, tmpstr))
                    {
                        iterpref = ajListIterNew(idprefixlist);
                        found = ajFalse;

                        while(!ajListIterDone(iterpref))
                        {
                            tmppref = ajListstrIterGet(iterpref);

                            if(ajStrMatchS(tmppref, tmpstr))
                            {
                                found = ajTrue;
                                break;
                            }
                        }

                        ajListIterDel(&iterpref);

                        if(!found)
                        {
                            ajStrAssignS(&idprefix, tmpstr);
                            ajListstrPush(idprefixlist, tmpstr);
                            tmpstr = NULL;
                        }
                    }
                }
                
                if(ajStrGetLen(lastid))
                {
                    if(ajStrMatchS(stanzatype, laststanzatype))
                    {
                        icmp = MAJSTRCMPS(id, lastid);

                        if(!icmp)
                            oboinWarn(obofile, linecnt, term->Id,
                                    "duplicate of previous ID '%S'",
                                    id);
                        else if(obovalididorder && (icmp < 0))
                            oboinWarn(obofile, linecnt, term->Id,
                                    "ID out of order '%S' after '%S",
                                    id, lastid);
                    }
                }
                ajStrAssignS(&lastid, id);
            }

            if(isstanza)
            {
                obotag = ajObotagNewData(name, rest, modifier, comment,
                                         linecnt);
                ajListPushAppend(stanzalist, obotag);
                obotag = NULL;

                if(isterm)
                {
                    if(ajStrMatchC(name, "id"))
                    {
                        if(term->Id)
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Multiple '%S' tags for ID '%S'",
                                    name, id);
                        ajStrAssignS(&term->Id, rest);
                    }
                    else if(ajStrMatchC(name, "name"))
                    {
                        if(term->Name)
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Multiple '%S' tags for ID '%S'",
                                    name, id);

                        ajStrAssignS(&term->Name, rest);
                        namekey = ajStrNewS(rest);
                        ajListstrPushAppend(namelist, namekey);
                    }
                    else if(ajStrMatchC(name, "synonym"))
                    {
                        if(ajStrGetCharFirst(rest) == '"')
                        {
                            i = ajStrFindlastK(rest, '"');

                            if(i > 1)
                            {
                                namekey = ajStrNewRes((size_t) i);
                                ajStrAssignSubS(&namekey, rest,
                                                1, (ajlong) i-1);
                                ajListstrPushAppend(namelist, namekey);
                            }
                        }
                    }
                    else if(ajStrMatchC(name, "namespace"))
                    {
                        if(term->Namespace)
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Multiple '%S' tags for ID '%S'",
                                    name, id);

                        ajStrAssignS(&term->Namespace, rest);
                    }
                    else if(ajStrMatchC(name, "def"))
                    {
                        if(term->Def)
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Multiple '%S' tags for ID '%S'",
                                    name, id);

                        ajStrAssignS(&term->Def, rest);
                    }
                    else if(ajStrMatchC(name, "comment"))
                    {
                        if(term->Comment)
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Multiple '%S' tags for ID '%S'",
                                    name, id);

                        ajStrAssignS(&term->Comment, rest);
                    }
                    else if(ajStrMatchC(name, "is_obsolete"))
                    {
                        if(term->Obsolete)
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Multiple '%S' tags for ID '%S'",
                                    name, id);

                        if(ajStrPrefixC(rest, "true"))
                            term->Obsolete = ajTrue;
                        else if(ajStrPrefixC(rest, "false"))
                            term->Obsolete = ajFalse;
                        else
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Bad value '%S' boolean tag for ID '%S'",
                                    name, id);
                    }
                    else if(ajStrMatchC(name, "replaced_by"))
                    {
                        if(term->Replaced)
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Multiple '%S' tags for ID '%S'",
                                    name, id);

                        ajStrAssignS(&term->Replaced, rest);
                    }
                    else if(ajStrMatchC(name, "consider"))
                    {
                        ajStrAssignS(&term->Replaced, rest);
                    }
                    else if(ajStrMatchC(name, "builtin"))
                    {
                        if(term->Obsolete)
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Multiple '%S' tags for ID '%S'",
                                    name, id);

                        if(ajStrPrefixC(rest, "true"))
                            term->Builtin = ajTrue;
                        else if(ajStrPrefixC(rest, "false"))
                            term->Builtin = ajFalse;
                        else
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Bad value '%S' boolean tag for ID '%S'",
                                    name, id);
                    }
                    else if(ajStrMatchC(name, "alt_id"))
                    {
                        altterm = ajOboNew();
                        ajStrAssignS(&altterm->Id, rest);
                        ajStrAssignS(&altterm->Trueid, id);
                        altid = ajStrNewS(rest);
                        oldterm = ajTablePut(ret->Termtable, altid, altterm);
                        altid = NULL;

                        if(oldterm)
                        {
                            oboinWarn(obofile, linecnt, term->Id,
                                    "Duplicate %S alt_id '%S'",
                                    stanzatype, id);

                            oldterm = NULL;
                        }
                    }
                }
            }
            else                /* processing header tags */
            {
                ok =  ajFalse;

                for(i=0;oboHeaderTags[i];i++)
                {
                    if(ajStrMatchC(name, oboHeaderTags[i]))
                    {
                        ok = ajTrue;
                        break;
                    }
                }

                if(!ok)
                {
                    if(term)
                        oboinWarn(obofile, linecnt, term->Id,
                                  "unknown header name '%S'",
                                  token);
                    else
                        oboinWarn(obofile, linecnt, NULL,
                                  "unknown header name noid",
                                  token);
                }
            }

            ajStrTrimWhite(&rest);
        }
    }

    /*
    ** Apply the same tests to the last term
    ** if that was the final stanza in the input file
    */

    if(stanzalist)
    {
        if(isterm)
        {
            if(!term->Obsolete)
            {
                ajListPushlist(term->Taglist, &stanzalist);
                oldterm = ajTablePut(ret->Termtable, id, term);            

                if(oldterm)
                {
                    oboinWarn(obofile, linecnt, term->Id,
                            "Duplicate %S id '%S'",
                            stanzatype, id);
                    ajOboDel(&oldterm);
                }

                while(ajListGetLength(namelist))
                {
                    namekey = NULL;
                    ajListstrPop(namelist, &namestr);

                    if(ajStrGetLen(term->Namespace))
                        ajFmtPrintS(&namekey, "%S:%S",
                                    term->Namespace,
                                    namestr);
                    else
                        namekey = ajStrNewS(namestr);

                    if(term->Obsolete)
                        ajStrAppendC(&namekey, " (obsolete)");
                            
                    oldterm = ajTablePut(ret->Termnametable,
                                         namekey, term);
                    if(oldterm)
                    {
                        if(!ajStrMatchS(oldterm->Id, term->Id))
                            oboinWarn(obofile, linecnt, term->Id,
                                      "%S Name/Synonym %S "
                                      "duplicates '%S'",
                                      term->Id, namekey, oldterm->Id);
                        else if(obovalidmultisyn)
                            oboinWarn(obofile, linecnt, term->Id,
                                      "%S Name/Synonym %S "
                                      "multiply defined",
                                      term->Id, namekey);
                         oldterm = NULL;
                    }
                }

                term = NULL;
            }
        }
        else 
        {
            if(ajStrMatchC(stanzatype, "Typedef"))
                oldlist = ajTablePut(ret->Typedeftable, id, stanzalist);
            else if(ajStrMatchC(stanzatype, "Instance"))
                oldlist = ajTablePut(ret->Instancetable, id, stanzalist);
            else if(ajStrMatchC(stanzatype, "Annotation"))
                oldlist = ajTablePut(ret->Annotable, id, stanzalist);
            else if(ajStrMatchC(stanzatype, "Formula"))
                oldlist = ajTablePut(ret->Formulatable, id, stanzalist);
            else 
                oldlist = ajTablePut(ret->Misctable, id, stanzalist);

            if(oldlist)
            {
                oboinWarn(obofile, linecnt, term->Id,
                        "Duplicate %S id '%S'",
                        stanzatype, id);
                oldlist = NULL;
            }
        }
    }

    id = NULL;
    stanzalist = ajListNew();

    ajDebug("typedefs:%Lu terms:%Lu instances:%Lu annotations:%Lu "
            "formulas:%Lu other:%Lu\n",
            ajTableGetLength(ret->Typedeftable),
            ajTableGetLength(ret->Termtable),
            ajTableGetLength(ret->Instancetable),
            ajTableGetLength(ret->Annotable),
            ajTableGetLength(ret->Formulatable),
            ajTableGetLength(ret->Misctable));

    /*
    ** Typedefs first
    ** fetch and sort them
    */

    ntypes = (ajuint) ajTableToarrayKeysValues(ret->Typedeftable,
					       (void***) &typeids,
					       (void***) &typetags);
    for(s=ntypes/2; s>0; s /= 2)
	for(i=s; i=0 && MAJSTRCMPS(typeids[k],typeids[k+s])>0;
	      k -= (ajlong)s)
	    {
		t = typeids[k];
		typeids[k] = typeids[k+s];
		typeids[k+s] = t;

		u = typetags[k];
		typetags[k] = typetags[k+s];
		typetags[k+s] = u;
	    }

    for(i=0; iName, oboTypedefTags[j]))
                j++;

            if(oboTypedefTags[j])
                jsave = j;
            else
            {
                for(jj=0; jj < jsave; jj++)
                {
                    if(obovalididorder &&
                       ajStrMatchC(obotag->Name, oboTypedefTags[jj]))
                    {
                        ajWarn("type tag out of order '%S' after '%s' "
                               "for id '%S'",
                               obotag->Name, oboTypedefTags[jsave], id);
                        j = jj;
                        break;
                    }
                }
                
            }

            ajStrTokenAssignC(&idsplit, obotag->Value, " \t,;[]{}()'\"");

            while (ajStrTokenNextParse(&idsplit, &tmpstr))
            {
                if(ajStrPrefixS(tmpstr, idprefix))
                {
                    if(obovalididunk &&
                       !ajTableMatchS(ret->Termtable, tmpstr))
                        oboinWarn(obofile, obotag->Linenumber, NULL,
                                  "Unknown id '%S' for tag '%S' "
                                  "in typedef '%S'",
                                  tmpstr, obotag->Name, id);
                }
            }
        }
    }

    /*
    ** Validate terms
    */

    nterms = (ajuint) ajTableToarrayKeysValues(ret->Termtable,
					       (void***) &termids,
					       (void***) &terms);
    for(s=nterms/2; s>0; s /= 2)
	for(i=s; i=0 && MAJSTRCMPS(termids[k],termids[k+s])>0;
	      k -= (ajlong)s)
	    {
		t = termids[k];
		termids[k] = termids[k+s];
		termids[k+s] = t;

		uterm = terms[k];
		terms[k] = terms[k+s];
		terms[k+s] = uterm;
	    }

    for(i=0; iTrueid))
        {
            ajStrTokenAssignC(&idsplit, terms[i]->Trueid, " \t,;[]{}()'\"");

            while (ajStrTokenNextParse(&idsplit, &tmpstr))
            {
                ajStrTrimEndC(&tmpstr, ".");

                if(ajStrPrefixS(tmpstr, idprefix))
                    if(obovalididunk && !ajTableMatchS(ret->Termtable, tmpstr))
                        oboinWarn(obofile, 0, NULL,
                                "Unknown id '%S' for tag alt_id in term '%S'",
                                tmpstr, terms[i]->Trueid);
            }

            continue;
        }

        /* valid values for major tags*/

        if(!terms[i]->Name)
        {
            oboinWarn(obofile, 0, NULL,
                    "no 'name' for term '%S' ... OWL data only  in OBO 1.3",
                    id);
        }
        
        if(terms[i]->Def)
        {
            if(ajStrGetCharFirst(terms[i]->Def) != '"')
                oboinWarn(obofile, obotag->Linenumber, NULL,
                        "bad 'def' tag value for term '%S'",
                        id);
        }
        
        iter = ajListIterNewread(terms[i]->Taglist);
        j = 0;
        jj = 0;
        jsave = 0;

        while(!ajListIterDone(iter))
        {
            obotag = ajListIterGet(iter);

            j = 0;

            while(oboTermTags[j].Tag &&
                  !ajStrMatchC(obotag->Name, oboTermTags[j].Tag))
                j++;

            if(oboTermTags[j].Tag)
                jsave = j;
            else
            {
                jj = 0;

                while(typeids[jj] &&
                      !ajStrMatchS(obotag->Name, typeids[jj]))
                    jj++;

                if(!typeids[jj])
                    oboinWarn(obofile, obotag->Linenumber, NULL,
                            "term tag not found '%S' for id '%S'",
                            obotag->Name, id);
            }
            
            if(ajStrMatchC(obotag->Name, "is_a") ||
               ajStrMatchC(obotag->Name, "consider"))
            {
                ajStrAssignS(&tmpname, obotag->Comment);
                ipos = ajStrFindAnyK(tmpname, '!');
                if(ipos >= 0)
                {
                    ajStrKeepRange(&tmpname, 0, ipos-1);
                    ajStrTrimEndC(&tmpname, " ");
                }

                isaterm = ajTableFetchS(ret->Termtable, obotag->Value);
                if(!isaterm)
                    oboinWarn(obofile, obotag->Linenumber, NULL,
                              "Unknown id '%S' for tag '%S' "
                              "in term '%S'",
                              obotag->Value, obotag->Name, id);

                else if(!ajStrMatchS(isaterm->Name, tmpname))
                    oboinWarn(obofile, obotag->Linenumber, NULL,
                              "Bad name '%S' for id '%S' "
                              "for tag '%S' "
                              "in term '%S' expected '%S'",
                              tmpname, obotag->Value,
                              obotag->Name, id, isaterm->Name);
            }
            else if(ajStrMatchC(obotag->Name, "relationship"))
            {
                ajStrTokenAssignC(&idsplit, obotag->Value, " \t,;[]{}()'\"");
                ajStrTokenNextParse(&idsplit, &tmpstr);
                ajStrTokenNextParse(&idsplit, &tmpid);

                ajStrAssignS(&tmpname, obotag->Comment);
                ipos = ajStrFindAnyK(tmpname, '!');
                if(ipos >= 0)
                {
                    ajStrKeepRange(&tmpname, 0, ipos-1);
                    ajStrTrimEndC(&tmpname, " ");
                }

                isaterm = ajTableFetchS(ret->Termtable, tmpid);
                if(!isaterm)
                    oboinWarn(obofile, obotag->Linenumber, NULL,
                              "Unknown id '%S' for tag '%S' relation '%S'"
                              "in term '%S'",
                              tmpid, obotag->Name, tmpstr, id);

                else if(!ajStrMatchS(isaterm->Name, tmpname))
                    oboinWarn(obofile, obotag->Linenumber, NULL,
                              "Bad name '%S' for id '%S' "
                              "for tag '%S' "
                              "in term '%S' expected '%S'",
                              tmpname, tmpid,
                              obotag->Name, id, isaterm->Name);
            }
            else 
            {
                /* check for IDs */
                ajStrTokenAssignC(&idsplit, obotag->Value, " \t,;[]{}()'\"");

                while (ajStrTokenNextParse(&idsplit, &tmpstr))
                {
                    ajStrTrimEndC(&tmpstr, ".");

                    if(ajStrPrefixS(tmpstr, idprefix))
                        if(obovalididunk &&
                           !ajTableMatchS(ret->Termtable, tmpstr))
                            oboinWarn(obofile, obotag->Linenumber, NULL,
                                      "Unknown id '%S' for tag '%S' "
                                      "in term '%S'",
                                      tmpstr, obotag->Name, id);
                }
            }
        }
    }

    ajDebug("%Lu id prefixes current '%S'\n",
            ajListGetLength(idprefixlist), idprefix);

    iterpref = ajListIterNew(idprefixlist);
    found = ajFalse;

    while(!ajListIterDone(iterpref))
    {
        tmppref = ajListstrIterGet(iterpref);
        ajDebug("   '%S'\n", tmppref);
    }

    ajListIterDel(&iterpref);

    ajStrDel(&tmpid);
    ajStrDel(&tmpname);
    return ret; 
}
ID ajObodataFetchId
TY public
MO ajoboread
LB core
XX
DE Retrieves an OBO term by ID
XX
PN [1]
PA r thys const AjPOboData
PD Parsed ontology
PX
PN [2]
PA r query const AjPStr
PD OBO identifier
PX
RT AjPObo
RD OBO term
RX
//


AjPObo ajObodataFetchId(const AjPOboData thys, const AjPStr query)
{
    AjPObo ret;
    ajuint irecurs = 0;
    AjPStr tmpqry = NULL;
    AjBool isid = ajFalse;

    if(!thys)
        return NULL;

    if(ajStrPrefixC(query, "EDAM_identifier"))
    {
        isid = ajTrue;
        ajStrAssignS(&tmpqry, query);
        ajStrExchangeCC(&tmpqry, "EDAM_identifier:", "EDAM_data:");
        ret = ajTableFetchmodS(thys->Termtable, tmpqry);
        ajStrDel(&tmpqry);
    }
    else 
    {
        ret = ajTableFetchmodS(thys->Termtable, query);
    }

    while(ret && ajStrGetLen(ret->Trueid))
    {
        if(++irecurs > 256)
            return NULL;

        ret = ajTableFetchmodS(thys->Termtable, ret->Trueid);
    }

    if(isid)
    {
        ajStrExchangeCC(&ret->Id, "EDAM_data:", "EDAM_identifier:");
    }

    return ret;
}
ID ajObodataFetchName
TY public
MO ajoboread
LB core
XX
DE Retrieves an OBO term by name
DE
DE The name must be an exact match to a name or synonym for the term
XX
PN [1]
PA r thys const AjPOboData
PD Parsed ontology
PX
PN [2]
PA r query const AjPStr
PD OBO name
PX
RT AjPObo
RD OBO term
RX
//


AjPObo ajObodataFetchName(const AjPOboData thys, const AjPStr query)
{
    AjPObo ret;
    ajuint irecurs = 0;

    if(!thys)
        return NULL;

    ret = ajTableFetchmodS(thys->Termnametable, query);

    while(ret && ajStrGetLen(ret->Trueid))
    {
        if(++irecurs > 256)
            return NULL;

        ret = ajTableFetchmodS(thys->Termtable, ret->Trueid);
    }

    return ret;
}
ID ajObolineCutComment
TY public
MO ajoboread
LB core
XX
DE Remove comments starting with an unescaped '!'
XX
PN [1]
PA u Pline AjPStr*
PD Line with possible comment text
PX
PN [2]
PA u Pcomment AjPStr*
PD Comment text
PX
RT AjBool
RD True if a comment was found
RX
//


AjBool ajObolineCutComment(AjPStr *Pline, AjPStr *Pcomment)
{
    const char* cp;
    ajuint i      = 0;
    ajuint spaces = 0;
    ajuint ilen   = 0;
    AjBool doescape = ajFalse;

    ilen = ajStrGetLen(*Pline);
    cp = ajStrGetPtr(*Pline);

    while (*cp)
    {
        if(*cp == '!')
        {
            if(!doescape)
            {
                if((i+1) < ilen)
                {
                    ajStrAssignSubS(Pcomment, *Pline, i+1, -1);
                    ajStrTrimWhite(Pcomment);
                }
                else
                    ajStrAssignC(Pcomment, "");

                ajStrCutEnd(Pline, ilen-i+spaces);

                return ajTrue;
            }
        }
        
        if(*cp == '\\')
            doescape = ajTrue;
        else
        {
            doescape = ajFalse;

            if(isspace((int)*cp))
                spaces++;
            else
                spaces = 0;
        }
        i++;
        cp++;
    }
    
    ajStrAssignC(Pcomment, "");

    return ajFalse;
}
ID ajObolineCutDbxref
TY public
MO ajoboread
LB core
XX
DE Remove trailing dbxrefs in square braces
XX
PN [1]
PA u Pline AjPStr*
PD Line with possible trailing dbxref text
PX
PN [2]
PA u Pdbxref AjPStr*
PD Dbxref text
PX
RT AjBool
RD True if a trailing dbxref was found
RX
//


AjBool ajObolineCutDbxref(AjPStr *Pline, AjPStr *Pdbxref)
{
    ajlong i = 0;
    ajulong ilen;

    ilen = ajStrGetLen(*Pline);

    if(ajStrGetCharLast(*Pline) != ']')
        return ajFalse;

    i = ajStrFindlastK(*Pline, '[');

    if(i < 0)
    {
        ajWarn("Failed to complete trailing dbxref: '%S'",
               *Pline);
        return ajFalse;
    }

    ajStrAssignSubS(Pdbxref, *Pline, i, -1);
    ajStrTrimWhite(Pdbxref);

    if(i)
    {
      ajStrCutEnd(Pline, (size_t) (ilen-i));
        ajStrTrimWhite(Pline);
    }
    else
        ajStrAssignC(Pline, "");
 
    return ajTrue;
}
ID ajObolineCutModifier
TY public
MO ajoboread
LB core
XX
DE Remove trailing modifiers in curly braces
XX
PN [1]
PA u Pline AjPStr*
PD Line with possible trailing modifier text
PX
PN [2]
PA u Pmodifier AjPStr*
PD Modifier text
PX
RT AjBool
RD True if a trailing modifier was found
RX
//


AjBool ajObolineCutModifier(AjPStr *Pline, AjPStr *Pmodifier)
{
    ajlong i = 0;
    ajulong ilen = 0;

    ajStrAssignC(Pmodifier, "");

    ilen = ajStrGetLen(*Pline);

    if(ajStrGetCharLast(*Pline) != '}')
        return ajFalse;

    i = ajStrFindlastK(*Pline, '{');

    if(i < 0)
    {
        ajWarn("Failed to complete trailing modifier: '%S'",
               *Pline);
        return ajFalse;
    }

    ajStrAssignSubS(Pmodifier, *Pline, i, -1);
    ajStrTrimWhite(Pmodifier);

    if(i)
    {
      ajStrCutEnd(Pline, (size_t) (ilen-i));
        ajStrTrimWhite(Pline);
    }
    else
        ajStrAssignC(Pline, "");
 
    return ajTrue;
}
ID ajObolineEscape
TY public
MO ajoboread
LB core
XX
DE Clean up escaped characters
XX
PN [1]
PA u Pline AjPStr*
PD Line with possible comment text
PX
RT AjBool
RD True if an escaped character was found
RX
//


AjBool ajObolineEscape(AjPStr *Pline)
{
    const char* cp;
    ajuint ilen = 0;

    AjPStr tmpstr = NULL;
    char lastchar = '\0';

    AjBool ret = ajFalse;

    ilen = ajStrGetLen(*Pline);
    cp = ajStrGetPtr(*Pline);
    tmpstr = ajStrNewRes(ilen+1);

    while (*cp)
    {
        if(lastchar == '\\')
        {
            ret = ajTrue;

            switch (*cp)
            {
                case '\n':      /* ignore the newline */
                    break;
                 case 'n':
                    ajStrAppendK(&tmpstr, '\n');
                    break;
                case 't':
                    ajStrAppendK(&tmpstr, '\t');
                    break;
                case 'W':
                    ajStrAppendK(&tmpstr, ' ');
                    break;
                default:        /* usually just copy the character */
                    ajStrAppendK(&tmpstr, *cp);
                    break;
            }
            
        }
        else if(*cp != '\\')
            ajStrAppendK(&tmpstr, *cp);

        lastchar = *cp++;
    }

    if(lastchar == '\\')        /* \ at the end means ignore the newline */
        ret = ajTrue;

    if(ret)
        ajStrAssignS(Pline, tmpstr);

    ajStrDel(&tmpstr);

    return ret;
}
ID obolineParseDef
TY static
MO ajoboread
LB core
XX
DE Parse definition line and store results in Obo object. The raw text
DE may include backslash escaped characters.
DE
DE The expected format is
DE
DE "definition text" [db:xref "optional desc", db:xref "desc2"]
DE with name=value pairs possible after each dbxref or at the end
DE (these are to be ignored).
XX
PN [1]
PA u obo AjPObo
PD OBO term object
PX
PN [2]
PA r line const AjPStr
PD Original definition line
PX
RT AjBool
RD True if an escaped character was found
RX
//


static AjBool obolineParseDef(AjPObo obo, const AjPStr line)
{
    const char* cp;
    ajuint ilen = 0;

    AjPStr tmpstr = NULL;
    AjBool done;

    ilen = ajStrGetLen(line);
    cp = ajStrGetPtr(line);

    while (*cp && *cp != '"')
        cp++;

    if(!*cp)
        return ajFalse;

    cp++;

    tmpstr = ajStrNewRes(ilen+1);

    while (*cp && *cp != '"')
    {
        if(*cp == '\\')
        {
            cp++;
            switch (*cp)
            {
                case '\0':
                case '\n':      /* ignore the newline */
                    break;
                 case 'n':
                    ajStrAppendK(&tmpstr, '\n');
                    break;
                case 't':
                    ajStrAppendK(&tmpstr, '\t');
                    break;
                case 'W':
                    ajStrAppendK(&tmpstr, ' ');
                    break;
                default:        /* usually just copy the character */
                    ajStrAppendK(&tmpstr, *cp);
                    break;
            }
        }
        else
            ajStrAppendK(&tmpstr, *cp);

        cp++;
    }

    if(!*cp)
    {
        ajStrDel(&tmpstr);
        return ajFalse;
    }

    cp++;
    while (*cp && *cp == ' ')
        cp++;

    ajStrAssignS(&obo->Def, tmpstr);

    if(*cp == '[')
        cp++;

    done = ajFalse;
    while(!done)
    {
        ajStrAssignClear(&tmpstr);
        while (*cp && *cp != ' ')
        {
            if(*cp == '=')      /* name=value modifier */
            {
                done = ajTrue;
                break;
            }
            ajStrAssignK(&tmpstr, *cp++);
        }
        if(!*cp)
            done = ajTrue;
        else
            cp++;
    }
    
    ajStrDel(&tmpstr);

    return ajTrue;
}
ID ajOboinprintBook
TY public
MO ajoboread
LB core
XX
DE Reports the internal data structures as a Docbook table
XX
PN [1]
PA u outf AjPFile
PD Output file
PX
RT void
RD 
RX
//


void ajOboinprintBook(AjPFile outf)
{
    ajuint i = 0;
    ajuint j = 0;
    AjPStr namestr = NULL;
    AjPList fmtlist;
    AjPStr* names;

    fmtlist = ajListstrNew();

    ajFmtPrintF(outf, "The supported obo term formats are summarised "
                "in the table below. "
                "The columns are as follows: "
                "Input format (format name), "
                "Try (indicates whether the "
                "format can be detected automatically on input), and "
                "Description (short description of "
                "the format).\n\n");

    ajFmtPrintF(outf, "\n");
    ajFmtPrintF(outf, "  \n");
    ajFmtPrintF(outf, "  \n");
    ajFmtPrintF(outf, "    \n");
    ajFmtPrintF(outf, "      \n");
    ajFmtPrintF(outf, "      \n");
    ajFmtPrintF(outf, "      \n");
    ajFmtPrintF(outf, "    \n");
    ajFmtPrintF(outf, "  \n");
    ajFmtPrintF(outf, "  \n");

    for(i=1; oboinFormatDef[i].Name; i++)
    {
	if(!oboinFormatDef[i].Alias)
        {
            namestr = ajStrNewC(oboinFormatDef[i].Name);
            ajListPushAppend(fmtlist, namestr);
            namestr = NULL;
        }
    }

    ajListSort(fmtlist, &ajStrVcmp);
    ajListstrToarray(fmtlist, &names);

    for(i=0; names[i]; i++)
    {
        for(j=0; oboinFormatDef[j].Name; j++)
        {
            if(ajStrMatchC(names[i],oboinFormatDef[j].Name))
            {
                ajFmtPrintF(outf, "    \n");
                ajFmtPrintF(outf, "      \n",
                            oboinFormatDef[j].Name);
                ajFmtPrintF(outf, "      \n",
                            oboinFormatDef[j].Try);
                ajFmtPrintF(outf, "      \n",
                            oboinFormatDef[j].Desc);
                ajFmtPrintF(outf, "    \n");
            }
        }
    }
        

    ajFmtPrintF(outf, "  \n");
    ajFmtPrintF(outf, "
Input obo term formats
Input FormatTryDescription
%s%B%s
\n"); ajStrDel(&namestr); names = NULL; ajListstrFreeData(&fmtlist); return; } ID ajOboinprintHtml TY public MO ajoboread LB core XX DE Reports the internal data structures as an HTML table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajOboinprintHtml(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, ""); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); for(i=1; oboinFormatDef[i].Name; i++) { ajStrAssignC(&namestr, oboinFormatDef[i].Name); if(!oboinFormatDef[i].Alias) { for(j=i+1; oboinFormatDef[j].Name; j++) { if(oboinFormatDef[j].Read == oboinFormatDef[i].Read) { ajFmtPrintAppS(&namestr, " %s", oboinFormatDef[j].Name); if(!oboinFormatDef[j].Alias) { ajWarn("Input format '%s' same as '%s' but not alias", oboinFormatDef[j].Name, oboinFormatDef[i].Name); } } } ajFmtPrintF(outf, "\n", namestr, oboinFormatDef[i].Try); ajFmtPrintF(outf, "\n", oboinFormatDef[i].Desc); } } ajFmtPrintF(outf, "
Input FormatAutoMultiDescription
\n%S\n%B\n%s\n
\n"); ajStrDel(&namestr); return; } ID ajOboinprintText TY public MO ajoboread LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajOboinprintText(AjPFile outf, AjBool full) { ajuint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Obo term input formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Alias Alias name\n"); ajFmtPrintF(outf, "# Try Test for unknown input files\n"); ajFmtPrintF(outf, "# Name Alias Try " "Description"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "InFormat {\n"); for(i=0; oboinFormatDef[i].Name; i++) if(full || !oboinFormatDef[i].Alias) ajFmtPrintF(outf, " %-12s %5B %3B \"%s\"\n", oboinFormatDef[i].Name, oboinFormatDef[i].Alias, oboinFormatDef[i].Try, oboinFormatDef[i].Desc); ajFmtPrintF(outf, "}\n\n"); return; } ID ajOboinprintWiki TY public MO ajoboread LB core XX DE Reports the internal data structures as a wiki table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajOboinprintWiki(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!Try!!" "class=\"unsortable\"|Description\n"); for(i=1; oboinFormatDef[i].Name; i++) { ajStrAssignC(&namestr, oboinFormatDef[i].Name); if(!oboinFormatDef[i].Alias) { for(j=i+1; oboinFormatDef[j].Name; j++) { if(oboinFormatDef[j].Read == oboinFormatDef[i].Read) { ajFmtPrintAppS(&namestr, "
%s", oboinFormatDef[j].Name); if(!oboinFormatDef[j].Alias) { ajWarn("Input format '%s' same as '%s' but not alias", oboinFormatDef[j].Name, oboinFormatDef[i].Name); } } } ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "|%S||%B||%s\n", namestr, oboinFormatDef[i].Try, oboinFormatDef[i].Desc); } } ajFmtPrintF(outf, "|}\n\n"); ajStrDel(&namestr); return; } ID ajOboinExit TY public MO ajoboread LB core XX DE Cleans up obo term input internal memory XX RT void RD RX // void ajOboinExit(void) { /* Query processing regular expressions */ ajStrDel(&oboinReadLine); ajTableDel(&oboDbMethods); return; } ID oboinDebug TY static MO ajoboread LB core XX DE Formatted write as a debug message. XX PN [1] PA r obofile const AjPFile PD Input OBO file PX PN [2] PA r linecnt ajuint PD Current line number PX PN [3] PA r fmt const char* PD Format string PX PN [4] PA v vararg ... PD Format arguments. PX RT void RD RX // static void oboinDebug(const AjPFile obofile, ajuint linecnt, const char* fmt, ...) { va_list args; AjPStr errstr = NULL; oboErrorCount++; va_start(args, fmt) ; ajFmtVPrintS(&errstr, fmt, args); va_end(args) ; ajDebug("File %F line %u: %S\n", obofile, linecnt, errstr); ajStrDel(&errstr); return; } ID oboinWarn TY static MO ajoboread LB core XX DE Formatted write as an error message. XX PN [1] PA r obofile const AjPFile PD Input OBO file PX PN [2] PA r linecnt ajuint PD Current line number PX PN [3] PA r id const AjPStr PD Current term identifier PX PN [4] PA r fmt const char* PD Format string PX PN [5] PA v vararg ... PD Format arguments. PX RT void RD RX // static void oboinWarn(const AjPFile obofile, ajuint linecnt, const AjPStr id, const char* fmt, ...) { va_list args; AjPStr errstr = NULL; oboErrorCount++; va_start(args, fmt) ; ajFmtVPrintS(&errstr, fmt, args); va_end(args) ; if(!ajStrGetLen(id)) ajWarn("File %F line %u: %S", obofile, linecnt, errstr); else ajWarn("File %F term %S line %u: %S", obofile, id, linecnt, errstr); ajStrDel(&errstr); return; } ID ajOboinTypeGetFields TY public MO ajoboread LB core XX DE Returns the listof known field names for ajOboinRead XX RT const char* RD List of field names RX // const char* ajOboinTypeGetFields(void) { return "id acc nam des"; } ID ajOboinTypeGetQlinks TY public MO ajoboread LB core XX DE Returns the listof known query link operators for ajOboinRead XX RT const char* RD List of field names RX // const char* ajOboinTypeGetQlinks(void) { return "|"; } ID ajOboaccessGetDb TY public MO ajoboread LB core XX DE returns the table in which obo database access details are registered XX RT AjPTable RD Access functions hash table RX // AjPTable ajOboaccessGetDb(void) { if(!oboDbMethods) oboDbMethods = ajCallTableNew(); return oboDbMethods; } ID ajOboaccessMethodGetQlinks TY public MO ajoboread LB core XX DE Tests for a named method for obo term reading and returns the DE known query link operators XX PN [1] PA r method const AjPStr PD Method required. PX RT const char* RD Known link operators RX // const char* ajOboaccessMethodGetQlinks(const AjPStr method) { AjPOboAccess methoddata; methoddata = ajCallTableGetS(oboDbMethods, method); if(!methoddata) return NULL; return methoddata->Qlink; } ID ajOboaccessMethodGetScope TY public MO ajoboread LB core XX DE Tests for a named method for obo term reading and returns the scope DE (entry, query or all). XX PN [1] PA r method const AjPStr PD Method required. PX RT ajuint RD Scope flags RX // ajuint ajOboaccessMethodGetScope(const AjPStr method) { AjPOboAccess methoddata; ajuint ret = 0; methoddata = ajCallTableGetS(oboDbMethods, method); if(!methoddata) return 0; if(methoddata->Entry) ret |= AJMETHOD_ENTRY; if(methoddata->Query) ret |= AJMETHOD_QUERY; if(methoddata->All) ret |= AJMETHOD_ALL; return ret; } ID ajOboaccessMethodTest TY public MO ajoboread LB core XX DE Tests for a named method for obo reading. XX PN [1] PA r method const AjPStr PD Method required. PX RT AjBool RD ajTrue on success. RX // AjBool ajOboaccessMethodTest(const AjPStr method) { if(ajCallTableGetS(oboDbMethods, method)) return ajTrue; return ajFalse; } ID ajObooutWrite TY public MO ajobowrite LB core XX DE Write an OBO term in a named format XX PN [1] PA u outf AjPOutfile PD Output file PX PN [2] PA r obo const AjPObo PD Obo term object PX RT AjBool RD True on success RX // AjBool ajObooutWrite(AjPOutfile outf, const AjPObo obo) { ajuint i = ajOutfileGetFormatindex(outf); AjPFile outfile = ajOutfileGetFile(outf); return (*obooutFormatDef[i].Write)(outfile, obo); } ID oboWriteExcel TY static MO ajobowrite LB core XX DE Write an OBO term in an tab-delimited format XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r obo const AjPObo PD Obo term object PX RT AjBool RD True on success RX // static AjBool oboWriteExcel(AjPFile outf, const AjPObo obo) { if(!outf) return ajFalse; if(!obo) return ajFalse; /*ajFmtPrintF(outf, "id\tnamespace\tname\n");*/ if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, "%S\t", obo->Trueid); else ajFmtPrintF(outf, "%S\t", obo->Id); ajFmtPrintF(outf, "%S\t%S\n", obo->Namespace, obo->Name); return ajTrue; } ID oboWriteHtml TY static MO ajobowrite LB core XX DE Write an OBO term in an HTML format XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r obo const AjPObo PD Obo term object PX RT AjBool RD True on success RX // static AjBool oboWriteHtml(AjPFile outf, const AjPObo obo) { AjIList iter = NULL; AjPObotag obotag; if(!outf) return ajFalse; if(!obo) return ajFalse; ajFmtPrintF(outf, "[Term]\n"); if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, "id: %S\n", obo->Trueid); else ajFmtPrintF(outf, "id: %S\n", obo->Id); if(ajStrGetLen(obo->Name)) ajFmtPrintF(outf, "name: %S\n", obo->Name); if(ajStrGetLen(obo->Namespace)) ajFmtPrintF(outf, "namespace: %S\n", obo->Namespace); if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, "alt_id: %S\n", obo->Id); if(ajStrGetLen(obo->Def)) ajFmtPrintF(outf, "def: %S\n", obo->Def); if(ajStrGetLen(obo->Comment)) ajFmtPrintF(outf, "comment: %S\n", obo->Comment); iter = ajListIterNewread(obo->Taglist); while(!ajListIterDone(iter)) { obotag = (AjPObotag) ajListIterGet(iter); /* pick out ONTO:id for markup - based on each ontology */ /* EDAM: http://srs.ebi.ac.uk/srsbin/cgi-bin/wgetz?[edam-ID:0000346]+-e */ ajFmtPrintF(outf, "%S: %S", obotag->Name, obotag->Value); if(ajStrGetLen(obotag->Modifier)) ajFmtPrintF(outf, " %S", obotag->Modifier); if(ajStrGetLen(obotag->Comment)) ajFmtPrintF(outf, " ! %S", obotag->Comment); ajFmtPrintF(outf, "\n"); } ajListIterDel(&iter); return ajTrue; } ID oboWriteJson TY static MO ajobowrite LB core XX DE Write an OBO term in JSON format XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r obo const AjPObo PD Obo term object PX RT AjBool RD True on success RX // static AjBool oboWriteJson(AjPFile outf, const AjPObo obo) { AjIList iter = NULL; AjPObotag obotag; AjPStr defstr = NULL; AjPStr xrefacc = NULL; AjPStr xrefdb = NULL; AjPStr synscope = NULL; AjPStr synstr = NULL; AjPStr reltype = NULL; AjPStr relto = NULL; if(!outf) return ajFalse; if(!obo) return ajFalse; ajFmtPrintF(outf, "{\n"); if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, " \"id\": \"%S\",\n", obo->Trueid); else ajFmtPrintF(outf, " \"id\": \"%S\",\n", obo->Id); if(ajStrGetLen(obo->Name)) ajFmtPrintF(outf, " \"name\": \"%S\"\n", obo->Name); if(ajStrGetLen(obo->Namespace)) ajFmtPrintF(outf, " \"namespace\": \"%S\",\n", obo->Namespace); /* if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, "alt_id: %S\n", obo->Id); */ if(ajStrGetLen(obo->Def)) { ajFmtPrintF(outf, " \"def\":\n {\n"); ajFmtPrintF(outf, " \"defstr\": \"%S\",\n", defstr); ajFmtPrintF(outf, " \"dbxref\":\n"); ajFmtPrintF(outf, " [\n"); ajFmtPrintF(outf, " {\n"); ajFmtPrintF(outf, " \"acc\": \"%S\",\n", xrefacc); ajFmtPrintF(outf, " \"dbname\": \"%S\",\n", xrefdb); ajFmtPrintF(outf, " }\n"); /* comma if there are more */ ajFmtPrintF(outf, " ]\n"); ajFmtPrintF(outf, " },\n"); } if(ajStrGetLen(obo->Comment)) ajFmtPrintF(outf, " \"comment\": \"%S\",\n", obo->Comment); iter = ajListIterNewread(obo->Taglist); while(!ajListIterDone(iter)) { obotag = (AjPObotag) ajListIterGet(iter); if(ajStrMatchC(obotag->Name, "is_obsolete") || ajStrMatchC(obotag->Name, "built_in") || ajStrMatchC(obotag->Name, "is_anonymous")) { if(ajStrMatchC(obotag->Name, "true")) ajFmtPrintF(outf, " \"%S\": 1,\n", obotag->Name); } else if (ajStrMatchC(obotag->Name, "consider") || ajStrMatchC(obotag->Name, "is_a") || ajStrMatchC(obotag->Name, "intersection_of") || ajStrMatchC(obotag->Name, "union_of") || ajStrMatchC(obotag->Name, "disjoint_from") || ajStrMatchC(obotag->Name, "subset") || ajStrMatchC(obotag->Name, "replaced_by") || ajStrMatchC(obotag->Name, "alt_id")) { ajFmtPrintF(outf, " \"%S\": \"%S\",\n", obotag->Name, obotag->Value); } else if(ajStrMatchC(obotag->Name, "synonym")) { ajFmtPrintF(outf, " \"synonym\":\n"); ajFmtPrintF(outf, " {\n"); ajFmtPrintF(outf, " \"scope\": \"%S\",\n", synscope); ajFmtPrintF(outf, " \"synonym_text\": \"%S\",\n", synstr); ajFmtPrintF(outf, " },\n>"); } else if(ajStrMatchC(obotag->Name, "relationship")) { ajFmtPrintF(outf, " \"relationship\":\n"); ajFmtPrintF(outf, " [\n"); ajFmtPrintF(outf, " {\n"); ajFmtPrintF(outf, " \"type\": \"%S\",\n", reltype); ajFmtPrintF(outf, " \"to\": \"%S\",\n", relto); ajFmtPrintF(outf, " }\n"); /* comma if there are more */ ajFmtPrintF(outf, " ]\n"); } else { ajFmtPrintF(outf, " \"%S\": \"%S\",\n", obotag->Name, obotag->Value); ajWarn("ontology term '%S' has tag '%S:' unknown in JSON output", obo->Id, obotag->Name); } } ajFmtPrintF(outf, "}\n"); ajListIterDel(&iter); ajStrDel(&defstr); ajStrDel(&xrefacc); ajStrDel(&xrefdb); ajStrDel(&synscope); ajStrDel(&synstr); ajStrDel(&reltype); ajStrDel(&relto); return ajTrue; } ID oboWriteList TY static MO ajobowrite LB core XX DE Write an OBO term as the simple id with the ontology name prefix XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r obo const AjPObo PD Obo term object PX RT AjBool RD True on success RX // static AjBool oboWriteList(AjPFile outf, const AjPObo obo) { if(!outf) return ajFalse; if(!obo) return ajFalse; ajWritelineNewline(outf, obo->Fullid); return ajTrue; } ID oboWriteObo TY static MO ajobowrite LB core XX DE Write an OBO term in OBO format XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r obo const AjPObo PD Obo term object PX RT AjBool RD True on success RX // static AjBool oboWriteObo(AjPFile outf, const AjPObo obo) { AjIList iter = NULL; AjPObotag obotag; if(!outf) return ajFalse; if(!obo) return ajFalse; ajFmtPrintF(outf, "[Term]\n"); if(ajStrGetLen(obo->Db)) { if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, "id: %S:%S\n", obo->Db, obo->Trueid); else ajFmtPrintF(outf, "id: %S:%S\n", obo->Db, obo->Id); } else { if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, "id: %S\n", obo->Trueid); else ajFmtPrintF(outf, "id: %S\n", obo->Id); } if(ajStrGetLen(obo->Name)) ajFmtPrintF(outf, "name: %S\n", obo->Name); if(ajStrGetLen(obo->Namespace)) ajFmtPrintF(outf, "namespace: %S\n", obo->Namespace); if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, "alt_id: %S\n", obo->Id); if(ajStrGetLen(obo->Def)) ajFmtPrintF(outf, "def: %S\n", obo->Def); if(ajStrGetLen(obo->Comment)) ajFmtPrintF(outf, "comment: %S\n", obo->Comment); iter = ajListIterNewread(obo->Taglist); while(!ajListIterDone(iter)) { obotag = (AjPObotag) ajListIterGet(iter); ajFmtPrintF(outf, "%S: %S", obotag->Name, obotag->Value); if(ajStrGetLen(obotag->Modifier)) ajFmtPrintF(outf, " %S", obotag->Modifier); if(ajStrGetLen(obotag->Comment)) ajFmtPrintF(outf, " ! %S", obotag->Comment); ajFmtPrintF(outf, "\n"); } ajListIterDel(&iter); ajFmtPrintF(outf, "\n"); return ajTrue; } ID oboWriteObobrief TY static MO ajobowrite LB core XX DE Write an OBO term in brief OBO format (id, name and namespace only) XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r obo const AjPObo PD Obo term object PX RT AjBool RD True on success RX // static AjBool oboWriteObobrief(AjPFile outf, const AjPObo obo) { if(!outf) return ajFalse; if(!obo) return ajFalse; ajFmtPrintF(outf, "[Term]\n"); if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, "id: %S\n", obo->Trueid); else ajFmtPrintF(outf, "id: %S\n", obo->Id); if(ajStrGetLen(obo->Name)) ajFmtPrintF(outf, "name: %S\n", obo->Name); if(ajStrGetLen(obo->Namespace)) ajFmtPrintF(outf, "namespace: %S\n", obo->Namespace); return ajFalse; } ID oboWriteXml TY static MO ajobowrite LB core XX DE Write an OBO term in OBO-XML format XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r obo const AjPObo PD Obo term object PX RT AjBool RD True on success RX // static AjBool oboWriteXml(AjPFile outf, const AjPObo obo) { AjIList iter = NULL; AjPObotag obotag; AjPStr defstr = NULL; AjPStr xrefacc = NULL; AjPStr xrefdb = NULL; AjPStr synscope = NULL; AjPStr synstr = NULL; AjPStr reltype = NULL; AjPStr relto = NULL; ajuint nrefs = 0; ajuint i; if(!outf) return ajFalse; if(!obo) return ajFalse; ajFmtPrintF(outf, "\n"); if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, " %S\n", obo->Trueid); else ajFmtPrintF(outf, " %S\n", obo->Id); if(ajStrGetLen(obo->Name)) ajFmtPrintF(outf, " %S\n", obo->Name); if(ajStrGetLen(obo->Namespace)) ajFmtPrintF(outf, " %S\n", obo->Namespace); /* if(ajStrGetLen(obo->Trueid)) ajFmtPrintF(outf, "alt_id: %S\n", obo->Id); */ if(ajStrGetLen(obo->Def)) { ajOboGetDef(obo, &defstr, &nrefs); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " %S\n", defstr); for(i=1; i<= nrefs; i++) { /*ajOboGetDefXref(obo, i, &xrefdb, &xrefacc);*/ ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " %S\n", xrefacc); ajFmtPrintF(outf, " %S\n", xrefdb); ajFmtPrintF(outf, " \n"); } ajFmtPrintF(outf, " \n"); } if(ajStrGetLen(obo->Comment)) ajFmtPrintF(outf, " %S\n", obo->Comment); iter = ajListIterNewread(obo->Taglist); while(!ajListIterDone(iter)) { obotag = (AjPObotag) ajListIterGet(iter); if(ajStrMatchC(obotag->Name, "is_obsolete") || ajStrMatchC(obotag->Name, "built_in") || ajStrMatchC(obotag->Name, "is_anonymous")) { if(ajStrMatchC(obotag->Name, "true")) ajFmtPrintF(outf, " <%S>1\n", obotag->Name, obotag->Name); } else if (ajStrMatchC(obotag->Name, "consider") || ajStrMatchC(obotag->Name, "is_a") || ajStrMatchC(obotag->Name, "intersection_of") || ajStrMatchC(obotag->Name, "union_of") || ajStrMatchC(obotag->Name, "disjoint_from") || ajStrMatchC(obotag->Name, "subset") || ajStrMatchC(obotag->Name, "replaced_by") || ajStrMatchC(obotag->Name, "alt_id")) { ajFmtPrintF(outf, " <%S>%S\n", obotag->Name, obotag->Value, obotag->Name); } else if(ajStrMatchC(obotag->Name, "synonym")) { ajFmtPrintF(outf, " \n", synscope); ajFmtPrintF(outf, " %S\n", synstr); ajFmtPrintF(outf, " "); } else if(ajStrMatchC(obotag->Name, "relationship")) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " %S\n", reltype); ajFmtPrintF(outf, " %S\n", relto); ajFmtPrintF(outf, " \n"); } else { ajFmtPrintF(outf, " <%S>%S\n", obotag->Name, obotag->Value, obotag->Name); ajWarn("ontology term '%S' has tag '%S:' unknown in OBO-XML output", obo->Id, obotag->Name); } } ajListIterDel(&iter); ajStrDel(&defstr); ajStrDel(&xrefacc); ajStrDel(&xrefdb); ajStrDel(&synscope); ajStrDel(&synstr); ajStrDel(&reltype); ajStrDel(&relto); return ajTrue; } ID ajObooutprintBook TY public MO ajobowrite LB core XX DE Reports the obo term format internals as Docbook text XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajObooutprintBook(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, "The supported obo term output " "formats are summarised " "in the table below. The columns are as follows: " "Output format (format name), " "Description (short description of " "the format). \n\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=0; obooutFormatDef[i].Name; i++) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", obooutFormatDef[i].Name); ajFmtPrintF(outf, " \n", obooutFormatDef[i].Desc); } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Obo term output formats
Output FormatDescription
%s%s
\n"); return; } ID ajObooutprintHtml TY public MO ajobowrite LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajObooutprintHtml(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, ""); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Obo term output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "OboFormat {\n"); for(i=0; obooutFormatDef[i].Name; i++) { ajFmtPrintF(outf, "\n", obooutFormatDef[i].Name, obooutFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajObooutprintText TY public MO ajobowrite LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajObooutprintText(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Obo term output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "OboFormat {\n"); for(i=0; obooutFormatDef[i].Name; i++) { if(full) ajFmtPrintF(outf, " %-12s \"%s\"\n", obooutFormatDef[i].Name, obooutFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajObooutprintWiki TY public MO ajobowrite LB core XX DE Reports the obo term output format internals as wikitext XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajObooutprintWiki(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!" "class=\"unsortable\"|Description\n"); for(i=0; obooutFormatDef[i].Name; i++) { ajFmtPrintF(outf, "|-\n"); ajStrAssignC(&namestr, obooutFormatDef[i].Name); for(j=i+1; obooutFormatDef[j].Name; j++) { if(obooutFormatDef[j].Write == obooutFormatDef[i].Write) { ajFmtPrintAppS(&namestr, "
%s", obooutFormatDef[j].Name); ajWarn("Obo term output format '%s' same as '%s' " "but not alias", obooutFormatDef[j].Name, obooutFormatDef[i].Name); } } ajFmtPrintF(outf, "|%S||%s\n", namestr, obooutFormatDef[i].Desc); } ajFmtPrintF(outf, "|}\n"); ajStrDel(&namestr); return; } ID ajObooutformatFind TY public MO ajobowrite LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Sets iformat as the recognised format, and returns ajTrue. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // AjBool ajObooutformatFind(const AjPStr format, ajint* iformat) { AjPStr tmpformat = NULL; ajuint i = 0; /* ajDebug("obooutformatFind '%S'\n", format); */ if(!ajStrGetLen(format)) return ajFalse; ajStrAssignS(&tmpformat, format); ajStrFmtLower(&tmpformat); for(i=0; obooutFormatDef[i].Name; i++) { /* ajDebug("test %d '%s' \n", i, obooutFormatDef[i].Name); */ if(ajStrMatchCaseC(tmpformat, obooutFormatDef[i].Name)) { *iformat = i; ajStrDel(&tmpformat); /* ajDebug("found '%s' at %d\n", obooutFormatDef[i].Name, i); */ return ajTrue; } } ajErr("Unknown output format '%S'", format); ajStrDel(&tmpformat); *iformat = 0; return ajFalse; } ID ajObooutformatTest TY public MO ajobowrite LB core XX DE Tests whether a named OBO output format is known XX PN [1] PA r format const AjPStr PD Format PX RT AjBool RD ajTrue if formats was accepted RX // AjBool ajObooutformatTest(const AjPStr format) { ajint i; for (i=0; obooutFormatDef[i].Name; i++) if(ajStrMatchCaseC(format, obooutFormatDef[i].Name)) return ajTrue; return ajFalse; } ID ajObooutExit TY public MO ajobowrite LB core XX DE Cleans up obo term outputinternal memory XX RT void RD RX // void ajObooutExit(void) { return; } ID ajPatternSeqNewList TY public MO ajpat LB core XX DE Constructor for a sequence pattern object. Sets all but compiled object. DE That is set with search function. Adds the pattern to a pattern list. XX PN [1] PA u plist AjPPatlistSeq PD Pattern list PX PN [2] PA r name const AjPStr PD Name of the pattern PX PN [3] PA r pat const AjPStr PD Pattern as string PX PN [4] PA r mismatch ajuint PD mismatch value PX RT AjPPatternSeq RD New pattern object RX // AjPPatternSeq ajPatternSeqNewList (AjPPatlistSeq plist, const AjPStr name, const AjPStr pat, ajuint mismatch) { AjPPatternSeq pthis; if (!ajStrGetLen(pat)) return NULL; AJNEW0(pthis); if(ajStrGetLen(name)) ajStrAssignS (&pthis->Name,name); else ajFmtPrintS(&pthis->Name, "pattern%Lu", 1UL + ajListGetLength(plist->Patlist)); ajStrAssignS(&pthis->Pattern,pat); pthis->Protein = plist->Protein; pthis->Mismatch = mismatch; ajPatlistAddSeq(plist,pthis); return pthis; } ID ajPatternRegexNewList TY public MO ajpat LB core XX DE Constructor for a pattern object. Sets all but compiled object. That is set DE with search function. Adds the pattern to a pattern list. XX PN [1] PA u plist AjPPatlistRegex PD Regular expression pattern list PX PN [2] PA r name const AjPStr PD Name of the pattern PX PN [3] PA r pat const AjPStr PD Pattern as string PX RT AjPPatternRegex RD New regular expression pattern object RX // AjPPatternRegex ajPatternRegexNewList(AjPPatlistRegex plist, const AjPStr name, const AjPStr pat) { AjPPatternRegex pthis; if (!ajStrGetLen(pat)) return NULL; AJNEW0(pthis); if(ajStrGetLen(name)) ajStrAssignS (&pthis->Name,name); else ajFmtPrintS(&pthis->Name, "regex%Lu", 1UL + ajListGetLength(plist->Patlist)); ajStrAssignS (&pthis->Pattern,pat); pthis->Type = plist->Type; pthis->Compiled = ajRegComp(pthis->Pattern); ajPatlistAddRegex (plist,pthis); return pthis; } ID ajPatternRegexDel TY public MO ajpat LB core XX DE Destructor for a regular expression pattern object XX PN [1] PA d pthys AjPPatternRegex* PD Pattern object reference PX RT void RD RX // void ajPatternRegexDel (AjPPatternRegex* pthys) { AjPPatternRegex thys = *pthys; ajStrDel(&thys->Name); ajStrDel(&thys->Pattern); ajRegFree(&thys->Compiled); AJFREE (*pthys); return; } ID ajPatternSeqDel TY public MO ajpat LB core XX DE Destructor for a pattern object XX PN [1] PA d pthys AjPPatternSeq* PD Pattern object reference PX RT void RD RX // void ajPatternSeqDel (AjPPatternSeq* pthys) { AjPPatternSeq thys = *pthys; ajStrDel(&thys->Name); ajStrDel(&thys->Pattern); ajPatCompDel((AjPPatComp*) &thys->Compiled); AJFREE (*pthys); return; } ID ajPatternSeqGetName TY public MO ajpat LB core XX DE Returns the name of the pattern. XX PN [1] PA r thys const AjPPatternSeq PD Pattern PX RT const AjPStr RD Name of the pattern. Real pointer in structure. RX // const AjPStr ajPatternSeqGetName (const AjPPatternSeq thys) { return thys->Name; } ID ajPatternRegexGetName TY public MO ajpat LB core XX DE Returns the name of the pattern. XX PN [1] PA r thys const AjPPatternRegex PD Pattern PX RT const AjPStr RD Name of the pattern. Real pointer in structure. RX // const AjPStr ajPatternRegexGetName (const AjPPatternRegex thys) { return thys->Name; } ID ajPatternSeqGetPattern TY public MO ajpat LB core XX DE Returns pattern in string format. XX PN [1] PA r thys const AjPPatternSeq PD Pattern PX RT const AjPStr RD Pattern. Real pointer in structure. RX // const AjPStr ajPatternSeqGetPattern (const AjPPatternSeq thys) { return thys->Pattern; } ID ajPatternRegexGetPattern TY public MO ajpat LB core XX DE Returns pattern in string format. XX PN [1] PA r thys const AjPPatternRegex PD Pattern PX RT const AjPStr RD Pattern. Real pointer in structure. RX // const AjPStr ajPatternRegexGetPattern (const AjPPatternRegex thys) { return thys->Pattern; } ID ajPatternSeqGetCompiled TY public MO ajpat LB core XX DE Returns void pointer to compiled pattern. XX PN [1] PA r thys const AjPPatternSeq PD Pattern PX RT AjPPatComp RD Reference for compiled pattern RX // AjPPatComp ajPatternSeqGetCompiled (const AjPPatternSeq thys) { return thys->Compiled; } ID ajPatternRegexGetCompiled TY public MO ajpat LB core XX DE Returns void pointer to compiled pattern. Compiles expression if not DE yet done. XX PN [1] PA r thys const AjPPatternRegex PD Pattern PX RT AjPRegexp RD Reference for compiled pattern RX // AjPRegexp ajPatternRegexGetCompiled (const AjPPatternRegex thys) { return thys->Compiled; } ID ajPatternSeqGetProtein TY public MO ajpat LB core XX DE Returns true if the pattern is for a protein sequence. XX PN [1] PA r thys const AjPPatternSeq PD Pattern PX RT AjBool RD ajTrue for a protein pattern RX // AjBool ajPatternSeqGetProtein (const AjPPatternSeq thys) { return thys->Protein; } ID ajPatternRegexGetType TY public MO ajpat LB core XX DE Returns the type of the pattern. XX PN [1] PA r thys const AjPPatternRegex PD Pattern PX RT ajuint RD Type of the pattern. RX // ajuint ajPatternRegexGetType (const AjPPatternRegex thys) { return thys->Type; } ID ajPatternSeqGetMismatch TY public MO ajpat LB core XX DE Returns the mismatch of the pattern. XX PN [1] PA r thys const AjPPatternSeq PD Pattern PX RT ajuint RD Mismatch value of the pattern. RX // ajuint ajPatternSeqGetMismatch (const AjPPatternSeq thys) { return thys->Mismatch; } ID ajPatternSeqSetCompiled TY public MO ajpat LB core XX DE Sets the compiled pattern XX PN [1] PA u thys AjPPatternSeq PD Pattern PX PN [2] PA u pat void* PD Compiled pattern PX RT void RD RX // void ajPatternSeqSetCompiled (AjPPatternSeq thys, void* pat) { thys->Compiled = pat; return; } ID ajPatternRegexSetCompiled TY public MO ajpat LB core XX DE Sets the compiled pattern XX PN [1] PA u thys AjPPatternRegex PD Pattern PX PN [2] PA u pat AjPRegexp PD Compiled pattern PX RT void RD RX // void ajPatternRegexSetCompiled (AjPPatternRegex thys, AjPRegexp pat) { thys->Compiled = pat; return; } ID ajPatternSeqDebug TY public MO ajpat LB core XX DE Constructor for a pattern list object XX PN [1] PA r pat const AjPPatternSeq PD Pattern object PX RT void RD RX // void ajPatternSeqDebug (const AjPPatternSeq pat) { ajDebug ("patPatternSeqDebug:\n name: %S\n pattern: %S\n protein: %B," " mismatch: %d\n", pat->Name, pat->Pattern, pat->Protein, pat->Mismatch); return; } ID ajPatternRegexDebug TY public MO ajpat LB core XX DE Constructor for a pattern list object XX PN [1] PA r pat const AjPPatternRegex PD Pattern object PX RT void RD RX // void ajPatternRegexDebug (const AjPPatternRegex pat) { ajDebug ("patPatternRegexDebug:\n name: %S\n pattern: %S\n type: %d", pat->Name, pat->Pattern, pat->Type); return; } ID ajPatlistRegexNew TY public MO ajpat LB core XX DE Constructor for a pattern list object XX RT AjPPatlistRegex RD New pattern list object RX // AjPPatlistRegex ajPatlistRegexNew (void) { AjPPatlistRegex pthis; AJNEW0(pthis); pthis->Patlist = ajListNew(); pthis->Iter = NULL; /* ajDebug ("ajPatlistRegexNew size '%Lu'\n",ajListGetLength(pthis->Patlist)); */ return pthis; } ID ajPatlistRegexNewType TY public MO ajpat LB core XX DE Constructor for a pattern list object with a specified type XX PN [1] PA r type ajuint PD type value PX RT AjPPatlistRegex RD New pattern list object RX // AjPPatlistRegex ajPatlistRegexNewType (ajuint type) { AjPPatlistRegex pthis; AJNEW0(pthis); pthis->Patlist = ajListNew(); pthis->Iter = NULL; pthis->Type = type; /* ajDebug ("ajPatlistRegexNew size '%Lu'\n",ajListGetLength(pthis->Patlist)); */ return pthis; } ID ajPatlistSeqNew TY public MO ajpat LB core XX DE Constructor for a pattern list object. Defaults to protein. XX RT AjPPatlistSeq RD New pattern list object RX // AjPPatlistSeq ajPatlistSeqNew (void) { AjPPatlistSeq pthis; AJNEW0(pthis); pthis->Patlist = ajListNew(); pthis->Iter = NULL; pthis->Protein = ajTrue; ajDebug ("ajPatlistSeqNew\n"); return pthis; } ID ajPatlistSeqNewType TY public MO ajpat LB core XX DE Constructor for a pattern list object XX PN [1] PA r type AjBool PD True for a protein pattern PX RT AjPPatlistSeq RD New pattern list object RX // AjPPatlistSeq ajPatlistSeqNewType(AjBool type) { AjPPatlistSeq pthis; AJNEW0(pthis); pthis->Patlist = ajListNew(); pthis->Iter = NULL; pthis->Protein = type; ajDebug ("ajPatlistSeqNewType type '%d'\n", pthis->Protein); return pthis; } ID ajPatlistRegexDel TY public MO ajpat LB core XX DE Destructor for a pattern list object XX PN [1] PA d pthys AjPPatlistRegex* PD Pattern list object reference PX RT void RD RX // void ajPatlistRegexDel (AjPPatlistRegex* pthys) { AjPPatlistRegex thys = NULL; AjPPatternRegex patternregex = NULL; thys = *pthys; while (ajListPop(thys->Patlist, (void **)&patternregex)) { ajDebug("ajPatlistRegexDel list size: %Lu\n", ajListGetLength(thys->Patlist)); ajPatternRegexDel(&patternregex); } if (thys->Iter) ajListIterDel(&thys->Iter); ajListFree(&thys->Patlist); AJFREE(*pthys); return; } ID ajPatlistSeqDel TY public MO ajpat LB core XX DE Destructor for a pattern list object XX PN [1] PA d pthys AjPPatlistSeq* PD Pattern list object reference PX RT void RD RX // void ajPatlistSeqDel (AjPPatlistSeq* pthys) { AjPPatlistSeq thys = NULL; AjPPatternSeq patternseq = NULL; thys = *pthys; while (ajListPop(thys->Patlist, (void **)&patternseq)) ajPatternSeqDel(&patternseq); if (thys->Iter) ajListIterDel(&thys->Iter); ajListFree(&thys->Patlist); AJFREE(*pthys); return; } ID ajPatlistSeqRead TY public MO ajpat LB core XX DE Parses a file into pattern list object. If there is no mismatch value on DE the pattern in the file, it is assumed to be 0. XX PN [1] PA r patspec const AjPStr PD Pattern specification PX PN [2] PA r patname const AjPStr PD Default pattern name prefix PX PN [3] PA r fmt const AjPStr PD Pattern file format PX PN [4] PA r protein AjBool PD ajTrue for protein patterns PX PN [5] PA r mismatches ajuint PD default number of mismatches PX RT AjPPatlistSeq RD Pattern list RX // AjPPatlistSeq ajPatlistSeqRead (const AjPStr patspec, const AjPStr patname, const AjPStr fmt, AjBool protein, ajuint mismatches) { AjPPatlistSeq patlist = NULL; AjPStr line = NULL; AjPStr name = NULL; AjPFilebuff infile = NULL; AjPRegexp mismreg = NULL; AjPStr patstr = NULL; AjPStr pat = NULL; ajuint mismatch = 0; ajint ifmt = 0; ajuint npat = 0; AjPStr namestr = NULL; ajStrAssignS(&namestr, patname); ajStrAssignEmptyC(&namestr, "pattern"); ajStrAssignS(&patstr, patspec); patlist = ajPatlistSeqNewType(protein); ifmt = patternSeqFormat(fmt); ajDebug("ajPatlistSeqRead patspec: '%S' patname: '%S' " "protein: %B mismatches: %d\n", patspec, patname, protein, mismatches); if(ajStrGetCharFirst(patstr) == '@') { ajStrCutStart(&patstr, 1); infile = ajFilebuffNewNameS(patstr); if(!infile) { ajErr("Unable to open pattern file '%S'", patstr); return NULL; } line = ajStrNew(); name = ajStrNew(); if(!ifmt) { ajBuffreadLineTrim(infile,&line); if(ajStrPrefixC(line, ">")) ifmt = 2; else ifmt = 1; ajFilebuffReset(infile); } switch(ifmt) { case 1: while (ajBuffreadLineTrim(infile,&line)) { npat++; ajStrAppendS (&pat,line); ajFmtPrintS(&name, "%S%u", namestr, npat); ajPatternSeqNewList(patlist,name,pat,mismatches); ajStrSetClear(&pat); } break; default: mismreg = ajRegCompC(""); mismatch=mismatches; /* set for first pattern */ while (ajBuffreadLineTrim(infile,&line)) { if (ajStrGetCharFirst(line) == '>') { if (ajStrGetLen(name)) { ajPatternSeqNewList(patlist,name,pat, mismatch); ajStrSetClear(&name); ajStrSetClear(&pat); mismatch=mismatches; /* reset for next pattern */ } ajStrCutStart(&line,1); if (ajRegExec(mismreg,line)) { ajRegSubI(mismreg,1,&name); ajStrToUint(name,&mismatch); ajStrTruncateLen(&line,ajRegOffset(mismreg)); ajStrTrimWhiteEnd(&line); } ajStrAssignS (&name,line); ajStrAssignEmptyS(&name, patname); } else ajStrAppendS (&pat,line); } ajStrAssignEmptyS(&name, patname); ajPatternSeqNewList(patlist,name,pat,mismatch); ajRegFree(&mismreg); break; } ajFilebuffDel(&infile); } else { ajStrAssignS(&name, namestr); ajPatternSeqNewList(patlist,name,patstr,mismatches); } ajStrDel(&name); ajStrDel(&line); ajStrDel(&pat); ajStrDel(&namestr); ajStrDel(&patstr); return patlist; } ID ajPatlistRegexRead TY public MO ajpat LB core XX DE Parses a file of regular expressions into a pattern list object. XX PN [1] PA r patspec const AjPStr PD Name of the file with patterns PX PN [2] PA r patname const AjPStr PD Default pattern name prefix PX PN [3] PA r fmt const AjPStr PD Pattern file format PX PN [4] PA r type ajuint PD Type of the patterns PX PN [5] PA r upper AjBool PD Convert to upper case PX PN [6] PA r lower AjBool PD Convert to lower case PX RT AjPPatlistRegex RD Pattern list RX // AjPPatlistRegex ajPatlistRegexRead (const AjPStr patspec, const AjPStr patname, const AjPStr fmt, ajuint type, AjBool upper, AjBool lower) { AjPPatlistRegex patlist = NULL; AjPStr line = NULL; AjPStr pat = NULL; AjPStr name = NULL; AjPFilebuff infile = NULL; AjPStr patstr = NULL; ajuint ifmt; ajuint npat = 0; AjPStr namestr = NULL; ajStrAssignS(&namestr, patname); ajStrAssignEmptyC(&namestr, "regex"); ajStrAssignS(&patstr, patspec); patlist = ajPatlistRegexNewType(type); ifmt = patternRegexFormat(fmt); if(ajStrGetCharFirst(patspec) == '@') { ajStrCutStart(&patstr, 1); infile = ajFilebuffNewNameS(patstr); if(!infile) { ajErr("Unable to open regular expression file '%S'", patstr); return NULL; } line = ajStrNew(); pat = ajStrNew(); name = ajStrNew(); if(!ifmt) { ajBuffreadLineTrim(infile,&line); if(ajStrPrefixC(line, ">")) ifmt = 2; else ifmt = 1; ajFilebuffReset(infile); } switch(ifmt) { case 1: while (ajBuffreadLineTrim(infile,&line)) { npat++; ajStrAppendS (&pat,line); if(lower) ajStrFmtLower(&pat); if(upper) ajStrFmtUpper(&pat); ajFmtPrintS(&name, "%S%u", namestr, npat); ajPatternRegexNewList(patlist,name,pat); ajStrSetClear(&pat); } break; default: while (ajBuffreadLineTrim(infile,&line)) { if (ajStrFindC(line,">")>-1) { npat++; if (ajStrGetLen(name)) { if(lower) ajStrFmtLower(&pat); if(upper) ajStrFmtUpper(&pat); ajPatternRegexNewList(patlist,name,pat); ajStrSetClear(&name); ajStrSetClear(&pat); } ajStrCutStart(&line,1); ajStrAssignS (&name,line); if(!ajStrGetLen(name)) ajFmtPrintS(&name, "%S%u", namestr, npat); } else ajStrAppendS (&pat,line); } ajStrAssignEmptyS(&name, patname); ajPatternRegexNewList(patlist,name,pat); ajStrSetClear(&pat); break; } ajFilebuffDel(&infile); } else { ajStrAssignS(&pat, patspec); if(lower) ajStrFmtLower(&pat); if(upper) ajStrFmtUpper(&pat); ajStrAssignS(&name, namestr); ajPatternRegexNewList(patlist,name,pat); } ajStrDel(&name); ajStrDel(&namestr); ajStrDel(&patstr); ajStrDel(&line); ajStrDel(&pat); return patlist; } ID ajPatlistSeqGetSize TY public MO ajpat LB core XX DE Gets number of patterns from list. XX PN [1] PA r thys const AjPPatlistSeq PD Pattern list object PX RT ajuint RD Number of patterns RX // ajuint ajPatlistSeqGetSize (const AjPPatlistSeq thys) { return (ajuint) ajListGetLength(thys->Patlist); } ID ajPatlistRegexGetSize TY public MO ajpat LB core XX DE Gets number of patterns from list. XX PN [1] PA r thys const AjPPatlistRegex PD Pattern list object PX RT ajuint RD Number of patterns RX // ajuint ajPatlistRegexGetSize (const AjPPatlistRegex thys) { return (ajuint) ajListGetLength(thys->Patlist); } ID ajPatlistSeqGetNext TY public MO ajpat LB core XX DE Gets next available pattern from list. XX PN [1] PA u thys AjPPatlistSeq PD Pattern list object PX PN [2] PA w pattern AjPPatternSeq* PD Pattern object reference PX RT AjBool RD ajTrue if there was next object RX // AjBool ajPatlistSeqGetNext (AjPPatlistSeq thys, AjPPatternSeq* pattern) { if (!thys->Iter) thys->Iter = ajListIterNew(thys->Patlist); if (!ajListIterDone(thys->Iter)) *pattern = ajListIterGet(thys->Iter); else { ajPatlistSeqRewind(thys); return ajFalse; } return ajTrue; } ID ajPatlistRegexGetNext TY public MO ajpat LB core XX DE Gets next available pattern from list. XX PN [1] PA u thys AjPPatlistRegex PD Pattern list object PX PN [2] PA w pattern AjPPatternRegex* PD Pattern object reference PX RT AjBool RD ajTrue if there was next object RX // AjBool ajPatlistRegexGetNext (AjPPatlistRegex thys, AjPPatternRegex* pattern) { if (!thys->Iter) thys->Iter = ajListIterNew(thys->Patlist); if (!ajListIterDone(thys->Iter)) *pattern = ajListIterGet(thys->Iter); else { ajPatlistRegexRewind(thys); return ajFalse; } return ajTrue; } ID ajPatlistRegexRewind TY public MO ajpat LB core XX DE Resets the pattern list iteration. XX PN [1] PA u thys AjPPatlistRegex PD Pattern list object reference PX RT void RD RX // void ajPatlistRegexRewind (AjPPatlistRegex thys) { if (thys->Iter) ajListIterDel(&thys->Iter); thys->Iter=NULL; return; } ID ajPatlistSeqRewind TY public MO ajpat LB core XX DE Resets the pattern list iteration. XX PN [1] PA u thys AjPPatlistSeq PD Pattern list object reference PX RT void RD RX // void ajPatlistSeqRewind (AjPPatlistSeq thys) { if (thys->Iter) ajListIterDel(&thys->Iter); thys->Iter=NULL; return; } ID ajPatlistRegexRemoveCurrent TY public MO ajpat LB core XX DE Removes current pattern from pattern list. If looping has not started or DE pattern list has just been rewound then nothing is removed. XX PN [1] PA u thys AjPPatlistRegex PD Pattern list from which to remove PX RT void RD RX // void ajPatlistRegexRemoveCurrent (AjPPatlistRegex thys) { if (!thys->Iter) return; ajListIterRemove(thys->Iter); ajListIterGetBack(thys->Iter); return; } ID ajPatlistSeqRemoveCurrent TY public MO ajpat LB core XX DE Removes current pattern from pattern list. If looping has not started or DE pattern list has just been rewound then nothing is removed. XX PN [1] PA u thys AjPPatlistSeq PD Pattern list from which to remove PX RT void RD RX // void ajPatlistSeqRemoveCurrent (AjPPatlistSeq thys) { if (!thys->Iter) return; ajListIterRemove(thys->Iter); ajListIterGetBack(thys->Iter); return; } ID ajPatlistAddSeq TY public MO ajpat LB core XX DE Adds pattern into patternlist XX PN [1] PA u thys AjPPatlistSeq PD Pattern list object reference PX PN [2] PA u pat AjPPatternSeq PD Pattern to be added PX RT void RD RX // void ajPatlistAddSeq (AjPPatlistSeq thys, AjPPatternSeq pat) { ajDebug ("ajPatlistAddSeq list size %Lu '%S' '%S' '%B' '%d'\n", ajListGetLength (thys->Patlist), pat->Name, pat->Pattern, pat->Protein, pat->Mismatch); ajListPushAppend(thys->Patlist, pat); return; } ID ajPatlistAddRegex TY public MO ajpat LB core XX DE Adds pattern into patternlist XX PN [1] PA u thys AjPPatlistRegex PD Pattern list object reference PX PN [2] PA u pat AjPPatternRegex PD Pattern to be added PX RT void RD RX // void ajPatlistAddRegex (AjPPatlistRegex thys, AjPPatternRegex pat) { ajDebug ("ajPatlistAddRegex list size %Lu '%S' '%S' '%d'\n", ajListGetLength (thys->Patlist), pat->Name, pat->Pattern, pat->Type); ajListPushAppend(thys->Patlist, pat); return; } ID ajPatCompNew TY public MO ajpat LB core XX DE Create prosite pattern structure. XX RT AjPPatComp RD pattern structure RX // AjPPatComp ajPatCompNew (void) { AjPPatComp pthis; AJNEW0(pthis); pthis->pattern=ajStrNew(); pthis->regex=ajStrNew(); return pthis; } ID ajPatCompDel TY public MO ajpat LB core XX DE Delete prosite pattern structure. XX PN [1] PA d pthys AjPPatComp* PD Prosite pattern structure PX RT void RD RX // void ajPatCompDel (AjPPatComp *pthys) { ajuint i; AjPPatComp thys; if(!pthys) return; if(!*pthys) return; thys = *pthys; ajStrDel(&thys->pattern); ajStrDel(&thys->regex); if(thys->buf) AJFREE(thys->buf); if(thys->sotable) AJFREE(thys->sotable); if(thys->type==6) { for(i=0;im;++i) AJFREE(thys->skipm[i]); AJFREE(thys->skipm); } AJFREE(*pthys); return; } ID patternRegexFormat TY static MO ajpat LB core XX DE Returns format associated with a named format of regular expression XX PN [1] PA r fmt const AjPStr PD Regular expression format PX RT ajuint RD Format number, defaults to 0 (string) RX // static ajuint patternRegexFormat(const AjPStr fmt) { ajuint i = 0; if(!ajStrGetLen(fmt)) return 0; while (patRegInformat[i].Name) { if(ajStrMatchCaseC(fmt, patRegInformat[i].Name)) return i; i++; } ajErr("Unrecognized regular expression file format '%S'",fmt); return 0; } ID patternSeqFormat TY static MO ajpat LB core XX DE Returns format associated with a named format of sequence pattern XX PN [1] PA r fmt const AjPStr PD Regular expression format PX RT ajuint RD Format number, defaults to 0 RX // static ajuint patternSeqFormat(const AjPStr fmt) { ajuint i = 0; if(!ajStrGetLen(fmt)) return 0; while (patSeqInformat[i].Name) { if(ajStrMatchCaseC(fmt, patSeqInformat[i].Name)) return i; i++; } ajErr("Unrecognized pattern file format '%S'",fmt); return 0; } ID ajPatternRegexType TY public MO ajpat LB core XX DE Returns type associated with a named type of regular expression XX PN [1] PA r type const AjPStr PD Regular expression type PX RT ajuint RD Type number, defaults to 0 (string) RX // ajuint ajPatternRegexType(const AjPStr type) { ajuint i = 0; while (patRegTypes[i].Name) { if(ajStrMatchCaseC(type, patRegTypes[i].Name)) return i; i++; } return 0; } ID ajPatlistRegexDoc TY public MO ajpat LB core XX DE Documents patterns to a formatted string XX PN [1] PA u plist AjPPatlistRegex PD Pattern list object PX PN [2] PA w Pdoc AjPStr* PD Formatted string PX RT ajuint RD Number of patterns RX // ajuint ajPatlistRegexDoc (AjPPatlistRegex plist, AjPStr* Pdoc) { AjPPatternRegex pat = NULL; ajFmtPrintS(Pdoc, "%-12S %S\n", "Pattern_name", "Pattern"); while (ajPatlistRegexGetNext(plist, &pat)) ajFmtPrintAppS(Pdoc, "%-12S %S\n", ajPatternRegexGetName(pat), ajPatternRegexGetPattern(pat)); return (ajuint) ajListGetLength(plist->Patlist); } ID ajPatlistSeqDoc TY public MO ajpat LB core XX DE Documents patterns to a formatted string XX PN [1] PA u plist AjPPatlistSeq PD Pattern list object PX PN [2] PA w Pdoc AjPStr* PD Formatted string PX RT ajuint RD Number of patterns RX // ajuint ajPatlistSeqDoc (AjPPatlistSeq plist, AjPStr* Pdoc) { AjPPatternSeq pat = NULL; ajFmtPrintS(Pdoc, "%-12s %8s %s\n", "Pattern_name", "Mismatch", "Pattern"); while (ajPatlistSeqGetNext(plist, &pat)) { ajFmtPrintAppS(Pdoc, "%-12S %8d %S\n", ajPatternSeqGetName(pat), ajPatternSeqGetMismatch(pat), ajPatternSeqGetPattern(pat)); } return (ajuint) ajListGetLength(plist->Patlist); } ID pdbSortPdbtospPdb TY static MO ajpdb LB core XX DE Function to compare AJAX PDB to Swiss-Prot objects by Pdb member. DE Usually called by ajPdbtospReadAllNew. XX PN [1] PA r item1 const void* PD AJAX PDB to Swiss-Prot address 1 PX PN [2] PA r item2 const void* PD AJAX PDB to Swiss-Prot address 2 PX RT ajint RD -1 if Pdb1 should sort before Pdb2, RD +1 if the Pdb2 should sort first. RD 0 if they are identical in length and content. RX // static ajint pdbSortPdbtospPdb(const void *item1, const void *item2) { AjPPdbtosp pdbtosp1 = *(AjOPdbtosp *const *) item1; AjPPdbtosp pdbtosp2 = *(AjOPdbtosp *const *) item2; return ajStrCmpS(pdbtosp1->Pdb, pdbtosp2->Pdb); } ID ajPdbtospReadAllRawNew TY public MO ajpdb LB core XX DE Reads the swissprot:pdb equivalence table available at URL (1) DE (1) http://www.expasy.ch/cgi-bin/lists?pdbtosp.txt DE and returns the data as a list of Pdbtosp objects. XX PN [1] PA u inf AjPFile PD Input file PX RT AjPList RD AJAX List of AJAX PDB to Swiss-Prot (AjPPdbtosp) objects RX // AjPList ajPdbtospReadAllRawNew(AjPFile inf) { AjPList list = NULL; /* List of Pdbtosp objects to return */ AjPPdbtosp pdbtosp = NULL; /* Temp. pointer to Pdbtosp object */ AjPStr pdb = NULL; /* PDB identifier */ AjPStr spr = NULL; /* Swissprot identifier */ AjPStr acc = NULL; /* Accession number */ AjPStr line = NULL; /* Line from file */ AjPStr token = NULL; /* Token from line */ const AjPStr subtoken = NULL; /* Token from line */ AjPList acclist = NULL; /* List of accession numbers */ AjPList sprlist = NULL; /* List of swissprot identifiers */ ajint n = 0; /* No. of accession numbers for current pdb * code */ AjBool ok = ajFalse; /* True if "____ _" has been found and we * can start parsing */ AjBool done_1st = ajFalse; /* True if the first line of data has been * parsed */ /* Memory allocation */ line = ajStrNew(); token = ajStrNew(); subtoken = ajStrNew(); pdb = ajStrNew(); acclist = ajListstrNew(); sprlist = ajListstrNew(); list = ajListNew(); /* Read lines from file */ while (ajReadlineTrim(inf, &line)) { if (ajStrPrefixC(line, "____ _")) { ok = ajTrue; continue; } if (!ok) continue; if (ajStrMatchC(line, "")) break; /* Read in pdb code first. Then tokenise by ':', discard the first * token, then tokenise the second token by ',', parsing out the * swissprot codes and accession numbers from the subtokens */ /* Make sure this is a line containing the pdb code */ if ((ajStrFindC(line, ":") != -1)) { if (done_1st) { pdbtosp = ajPdbtospNew(0); ajStrAssignS(&pdbtosp->Pdb, pdb); pdbtosp->Number = n; ajListToarray(acclist, (void ***) &pdbtosp->Acc); ajListToarray(sprlist, (void ***) &pdbtosp->Spr); ajListPushAppend(list, (void *) pdbtosp); ajListstrFree(&acclist); ajListstrFree(&sprlist); acclist = ajListstrNew(); sprlist = ajListstrNew(); n = 0; } ajFmtScanS(line, "%S", &pdb); ajStrParseC(line, ":"); ajStrAssignS(&token, ajStrParseC(NULL, ":")); done_1st = ajTrue; } else { ajStrAssignS(&token, line); } spr = ajStrNew(); acc = ajStrNew(); ajFmtScanS(token, "%S (%S", &spr, &acc); if (ajStrSuffixC(acc, "),")) ajStrCutEnd(&acc, 2); else ajStrCutEnd(&acc, 1); ajListstrPushAppend(acclist, acc); ajListstrPushAppend(sprlist, spr); n++; ajStrParseC(token, ","); while ((subtoken = ajStrParseC(NULL, ","))) { spr = ajStrNew(); acc = ajStrNew(); ajFmtScanS(subtoken, "%S (%S", &spr, &acc); if (ajStrSuffixC(acc, "),")) ajStrCutEnd(&acc, 2); else ajStrCutEnd(&acc, 1); ajListstrPushAppend(acclist, acc); ajListstrPushAppend(sprlist, spr); n++; } } /* Data for last pdb code ! */ pdbtosp = ajPdbtospNew(0); ajStrAssignS(&pdbtosp->Pdb, pdb); pdbtosp->Number = n; ajListToarray(acclist, (void ***) &pdbtosp->Acc); ajListToarray(sprlist, (void ***) &pdbtosp->Spr); ajListPushAppend(list, (void *) pdbtosp); ajListstrFree(&acclist); ajListstrFree(&sprlist); ajStrDel(&line); ajStrDel(&token); ajStrDel(&pdb); ajListstrFree(&acclist); ajListstrFree(&sprlist); return list; } ID ajPdbtospReadNew TY public MO ajpdb LB core XX DE Read a Pdbtosp object from a file in embl-like format (see documentation DE for DOMAINATRIX "pdbtosp" application). XX PN [1] PA u inf AjPFile PD Input file stream PX PN [2] PA r entry const AjPStr PD Pdb id PX RT AjPPdbtosp RD True on success RX // AjPPdbtosp ajPdbtospReadNew(AjPFile inf, const AjPStr entry) { return ajPdbtospReadCNew(inf, ajStrGetPtr(entry)); } ID ajPdbtospReadCNew TY public MO ajpdb LB core XX DE Read a Pdbtosp object from a file in embl-like format. Memory for the DE object is allocated. XX PN [1] PA u inf AjPFile PD Input file stream PX PN [2] PA r entry const char* PD Pdb id PX RT AjPPdbtosp RD True on success RX // AjPPdbtosp ajPdbtospReadCNew(AjPFile inf, const char *entry) { AjPPdbtosp pdbtosp = NULL; AjPStr line = NULL; AjPStr tentry = NULL; AjPStr pdb = NULL; AjBool ok = ajFalse; ajint n = 0; ajint i = 0; line = ajStrNew(); tentry = ajStrNew(); pdb = ajStrNew(); ajStrAssignC(&tentry, entry); ajStrFmtUpper(&tentry); while ((ok = ajReadlineTrim(inf, &line))) { if (!ajStrPrefixC(line, "EN ")) continue; ajFmtScanS(line, "%*S %S", &pdb); if (ajStrMatchWildS(pdb, tentry)) break; } if (!ok) { ajStrDel(&line); ajStrDel(&tentry); ajStrDel(&pdb); return NULL; } while (ok && !ajStrPrefixC(line, "//")) { if (ajStrPrefixC(line, "XX")) { ok = ajReadlineTrim(inf, &line); continue; } else if (ajStrPrefixC(line, "NE")) { ajFmtScanS(line, "%*S %d", &n); pdbtosp = ajPdbtospNew(n); ajStrAssignS(&pdbtosp->Pdb, pdb); } else if (ajStrPrefixC(line, "IN")) { ajFmtScanS(line, "%*S %S %*S %S", &pdbtosp->Spr[i], &pdbtosp->Acc[i]); i++; } ok = ajReadlineTrim(inf, &line); } ajStrDel(&line); ajStrDel(&tentry); ajStrDel(&pdb); return pdbtosp; } ID ajPdbtospReadAllNew TY public MO ajpdb LB core XX DE Read all the Pdbtosp objects in a file in embl-like format (see DE documentation for DOMAINATRIX "pdbtosp" application) and writes a list of DE these objects. It then sorts the list by PDB id. XX PN [1] PA u inf AjPFile PD Input file stream PX RT AjPList RD List of Pdbtosp objects. RX // AjPList ajPdbtospReadAllNew(AjPFile inf) { AjPList list = NULL; AjPPdbtosp pdbtosp = NULL; /* Check args and allocate list if necessary */ if (!inf) return NULL; list = ajListNew(); while ((pdbtosp = ajPdbtospReadCNew(inf, "*"))) ajListPush(list, (void *) pdbtosp); ajListSort(list, &pdbSortPdbtospPdb); return list; } ID ajCmapReadINew TY public MO ajpdb LB core XX DE Read a Cmap object from a file in CON format (see DE documentation for DOMAINATRIX "contacts" application). Takes the chain DE identifier as an integer. If the arguments mod and chn are both 0, the DE function will read the next Cmap in the file. XX PN [1] PA u inf AjPFile PD Input file stream PX PN [2] PA r chn ajint PD Chain number PX PN [3] PA r mod ajint PD Model number PX RT AjPCmap RD Pointer to new Cmap object. RX CA new CT AjPCmap CD Cmap constructor from reading file in CON format (see documentation for the EMBASSY DOMAINATRIX package). CX // AjPCmap ajCmapReadINew(AjPFile inf, ajint chn, ajint mod) { AjPCmap cmap = NULL; if (!(cmap = ajCmapReadNew(inf, CMAP_MODE_I, chn, mod))) return NULL; return cmap; } ID ajCmapReadCNew TY public MO ajpdb LB core XX DE Read a Cmap object from a file in CON format (see DE documentation for DOMAINATRIX "contacts" application). Takes the chain DE identifier as a character. XX PN [1] PA u inf AjPFile PD Input file stream PX PN [2] PA r chn char PD Chain number PX PN [3] PA r mod ajint PD Model number PX RT AjPCmap RD Pointer to new Cmap object. RX CA new CT AjPCmap CD Cmap constructor from reading file in CON format (see documentation for the EMBASSY DOMAINATRIX package). CX // AjPCmap ajCmapReadCNew(AjPFile inf, char chn, ajint mod) { AjPCmap cmap = NULL; if (!(cmap = ajCmapReadNew(inf, CMAP_MODE_C, (ajint) chn, mod))) return NULL; return cmap; } ID ajCmapReadAllNew TY public MO ajpdb LB core XX DE Read every Cmap object from a file in CON format (see DE documentation for DOMAINATRIX "contacts" application) and returns a list DE of these objects. XX PN [1] PA u inf AjPFile PD Input file stream PX RT AjPList RD List of Cmap objects. RX // AjPList ajCmapReadAllNew(AjPFile inf) { AjPList list = NULL; AjPCmap cmap = NULL; list = ajListNew(); while ((cmap = ajCmapReadNew(inf, CMAP_MODE_I, 0, 0))) ajListPushAppend(list, cmap); return list; } ID ajCmapReadNew TY public MO ajpdb LB core XX DE Read a Cmap object from a file in CON format (see DE documentation for DOMAINATRIX "contacts" application). This is not DE usually called by the user, who uses ajCmapReadINew or ajCmapReadCNew DE instead. If mode==CMAP_MODE_I, chn==0 and mod==0, the function will DE read the next Cmap in the file. XX PN [1] PA u inf AjPFile PD Input file stream. PX PN [2] PA r mode ajint PD Mode, either CMAP_MODE_I (treat chn arg as PD an integer) or CMAP_MODE_C (treat chn arg as PD a character). PX PN [3] PA r chn ajint PD Chain identifier / number. PX PN [4] PA r mod ajint PD Model number. PX RT AjPCmap RD True on success (an object read) RX CA new CT AjPCmap CD Cmap constructor from reading file in CON format (see documentation for the EMBASSY DOMAINATRIX package). CX // AjPCmap ajCmapReadNew(AjPFile inf, ajint mode, ajint chn, ajint mod) { AjPCmap cmap = NULL; const AjPStr token = NULL; /* For parsing */ ajint smcon = 0; /* No. of SM contacts */ ajint licon = 0; /* No. of LI contacts */ ajint x = 0; /* No. of first residue making contact */ ajint y = 0; /* No. of second residue making contact */ ajint md = -1; /* Model number */ ajint cn1 = -1; /* Chain number 1 */ ajint cn2 = -1; /* Chain number 2 */ char id1 = -1; /* Chain id 1 */ char id2 = -1; /* Chain id 2 */ ajint nres1 = 0; /* No. of residues in domain / chain 1 */ ajint nres2 = 0; /* No. of residues in domain / chain 2 */ AjPStr seq1 = NULL; /* Sequence 1 */ AjPStr seq2 = NULL; /* Sequence 2 */ AjBool idok = ajFalse; /* If the required chain has been found */ ajint en; /* Entry number. */ ajint ns; /* No. of sites, SITES output only */ ajint sn; /* Site number, SITES output only */ /* Check args */ if (!inf) { ajWarn("Invalid args to ajCmapReadNew"); return NULL; } /* Convert '_' chain identifiers to '.' if necessary */ if (mode == CMAP_MODE_C) if (chn == '_') chn = '.'; /* Initialise strings */ if (!pdbGStrline) { pdbGStrline = ajStrNew(); pdbGStrtemp_id = ajStrNew(); pdbGStrtemp_domid = ajStrNew(); pdbGStrtemp_ligid = ajStrNew(); pdbGStrdesc = ajStrNew(); pdbGStrtmpstr = ajStrNew(); } /* Start of main loop */ while ((ajReadlineTrim(inf, &pdbGStrline))) { /* // */ if (ajStrPrefixC(pdbGStrline, "//")) { /* If the delimiter between entries is found and cmap is * non-NULL, i.e. has been allocated, the function should return. */ ajStrDel(&seq1); ajStrDel(&seq2); return cmap; } /* SI */ else if (ajStrPrefixC(pdbGStrline, "SI")) { token = ajStrParseC(pdbGStrline, ";"); ajFmtScanS(token, "%*s %*s %d", &sn); token = ajStrParseC(NULL, ";"); ajFmtScanS(token, "%*s %d", &ns); } /* EN */ else if (ajStrPrefixC(pdbGStrline, "EN")) { ajFmtScanS(pdbGStrline, "%*s %*c%d", &en); } /* TY */ else if (ajStrPrefixC(pdbGStrline, "TY")) { ajFmtScanS(pdbGStrline, "%*s %S", &pdbGStrtype); ajStrSetClear(&seq1); ajStrSetClear(&seq2); id1 = '.'; id2 = '.'; cn1 = 0; cn2 = 0; nres1 = 0; nres2 = 0; } /* EX, NE records are not parsed */ /* ID */ else if (ajStrPrefixC(pdbGStrline, "ID")) { token = ajStrParseC(pdbGStrline, ";"); ajFmtScanS(token, "%*s %*s %S", &pdbGStrtemp_id); token = ajStrParseC(NULL, ";"); ajFmtScanS(token, "%*s %S", &pdbGStrtemp_domid); token = ajStrParseC(NULL, ";"); ajFmtScanS(token, "%*s %S", &pdbGStrtemp_ligid); } /* DE records are not parsed (SITES output) */ else if (ajStrPrefixC(pdbGStrline, "DE")) { ajStrAssignSubS(&pdbGStrdesc, pdbGStrline, 4, -1); } /* CN */ else if (ajStrPrefixC(pdbGStrline, "CN")) { token = ajStrParseC(pdbGStrline, ";"); #if AJFALSE ajFmtScanS(token, "%*s %*s %d", &md); if (md == '.') md = 0; #endif ajFmtScanS(token, "%*s %*s %S", &pdbGStrtmpstr); if (ajStrMatchC(pdbGStrtmpstr, ".")) md = 0; else ajFmtScanS(pdbGStrtmpstr, "%d", &md); token = ajStrParseC(NULL, ";"); ajFmtScanS(token, "%*s %d", &cn1); token = ajStrParseC(NULL, ";"); ajFmtScanS(token, "%*s %d", &cn2); token = ajStrParseC(NULL, ";"); ajFmtScanS(token, "%*s %c", &id1); token = ajStrParseC(NULL, ";"); ajFmtScanS(token, "%*s %c", &id2); token = ajStrParseC(NULL, ";"); #if AJFALSE ajFmtScanS(token, "%*s %d", &nres1); if (nres1 == '.') nres1 = 0; #endif ajFmtScanS(token, "%*s %S", &pdbGStrtmpstr); if (ajStrMatchC(pdbGStrtmpstr, ".")) nres1 = 0; else ajFmtScanS(pdbGStrtmpstr, "%d", &nres1); token = ajStrParseC(NULL, ";"); #if AJFALSE ajFmtScanS(token, "%*s %d", &nres2); if ((char) nres2 == '.') nres2 = 0; #endif ajFmtScanS(token, "%*s %S", &pdbGStrtmpstr); if (ajStrMatchC(pdbGStrtmpstr, ".")) nres2 = 0; else ajFmtScanS(pdbGStrtmpstr, "%d", &nres2); } /* S1 */ else if (ajStrPrefixC(pdbGStrline, "S1")) { while (ajReadlineTrim(inf, &pdbGStrline) && !ajStrPrefixC(pdbGStrline, "XX")) ajStrAppendC(&seq1, ajStrGetPtr(pdbGStrline)); ajStrRemoveWhite(&seq1); } /* S2 */ else if (ajStrPrefixC(pdbGStrline, "S2")) { while (ajReadlineTrim(inf, &pdbGStrline) && !ajStrPrefixC(pdbGStrline, "XX")) ajStrAppendC(&seq2, ajStrGetPtr(pdbGStrline)); ajStrRemoveWhite(&seq2); } /* NC */ else if ((ajStrPrefixC(pdbGStrline, "NC")) && ((md == mod) || ((chn == 0) && (mod == 0) && (mode == CMAP_MODE_I)))) { token = ajStrParseC(pdbGStrline, ";"); ajFmtScanS(token, "%*s %*s %d", &smcon); token = ajStrParseC(NULL, ";"); ajFmtScanS(token, "%*s %d", &licon); /* ** The fourth conditional is to capture those few domains which ** are made up from more than one chain. For these, the chain ** character passed in might be an A or a B (e.g. the character ** extracted from the scop domain code) whereas the chain id given ** in the contact map file will be a '.' - because of how ** scopparse copes with these cases. (A '.' is also in the contact ** maps for where a chain id was not specified in the original ** pdb file). */ if (((cn1 == chn) && (mode == CMAP_MODE_I)) || ((chn == 0) && (mod == 0) && (mode == CMAP_MODE_I)) || ((toupper((int) id1) == toupper(chn)) && (mode == CMAP_MODE_C)) || ((toupper((int) id1) == '.') && (toupper(chn) != '.') && (mode == CMAP_MODE_C)) ) { idok = ajTrue; /* Allocate contact map and write values */ if (ajStrMatchC(pdbGStrtype, "INTER")) { if (nres1 > nres2) cmap = ajCmapNew(nres1); else cmap = ajCmapNew(nres2); } else cmap = ajCmapNew(nres1); ajStrAssignS(&cmap->Id, pdbGStrtemp_id); ajStrAssignS(&cmap->Domid, pdbGStrtemp_domid); ajStrAssignS(&cmap->Ligid, pdbGStrtemp_ligid); if (ajStrMatchC(pdbGStrtype, "INTRA")) { cmap->Type = ajECmapTypeIntra; cmap->Ncon = smcon; } else if (ajStrMatchC(pdbGStrtype, "INTER")) { cmap->Type = ajECmapTypeInter; cmap->Ncon = smcon; } else if (ajStrMatchC(pdbGStrtype, "LIGAND")) { cmap->Type = ajECmapTypeLigand; cmap->Ncon = licon; cmap->ns = ns; cmap->sn = sn; ajStrAssignS(&cmap->Desc, pdbGStrdesc); } else ajFatal("Unrecognised contact type"); cmap->Chn1 = cn1; cmap->Chn2 = cn2; cmap->Chid1 = id1; cmap->Chid2 = id2; cmap->Nres1 = nres1; cmap->Nres2 = nres2; cmap->en = en; ajStrAssignS(&cmap->Seq1, seq1); ajStrAssignS(&cmap->Seq2, seq2); } } /* SM */ else if ((ajStrPrefixC(pdbGStrline, "SM")) && ((md == mod) || ((chn == 0) && (mod == 0) && (mode == CMAP_MODE_I))) && (idok)) { ajFmtScanS(pdbGStrline, "%*s %*s %d %*c %*s %d", &x, &y); /* Check residue number is in range */ if ((x > cmap->Dim) || (y > cmap->Dim)) ajFatal("Fatal attempt to write bad data in " "ajCmapReadNew\nFile: %S (%S)\nx: %d y:%d\n", ajFileGetPrintnameS(inf), pdbGStrtemp_id, x, y); /* Enter '1' in matrix to indicate contact */ ajUint2dPut(&cmap->Mat, x - 1, y - 1, 1); ajUint2dPut(&cmap->Mat, y - 1, x - 1, 1); } /* LI */ else if ((ajStrPrefixC(pdbGStrline, "LI")) && ((md == mod) || ((chn == 0) && (mod == 0) && (mode == CMAP_MODE_I))) && (idok)) { ajFmtScanS(pdbGStrline, "%*s %*s %d", &x); /* Check residue number is in range */ if ((x > cmap->Dim)) ajFatal("Fatal attempt to write bad data in " "ajCmapReadNew\nFile: %S (%S)\nx: %d\n", ajFileGetPrintnameS(inf), pdbGStrtemp_id, x); /* Enter '1' in matrix to indicate contact. For ligand contacts, * the first row / column only is used. */ ajUint2dPut(&cmap->Mat, x - 1, 0, 1); ajUint2dPut(&cmap->Mat, 0, x - 1, 1); } } ajStrDel(&seq1); ajStrDel(&seq2); return cmap; } ID ajVdwallReadNew TY public MO ajpdb LB core XX DE Read a Vdwall object from a file in embl-like format (see documentation DE for the EMBASSY DOMAINATRIX package). XX PN [1] PA u inf AjPFile PD Input file stream PX RT AjPVdwall RD Pointer to Vdwall object. RX CA new CT AjPVdwall CD Vdwall constructor from reading file in embl-like format (see documentation for the EMBASSY DOMAINATRIX package). CX // AjPVdwall ajVdwallReadNew(AjPFile inf) { AjPVdwall vdwall = NULL; AjPStr line = NULL; /* Line of text */ ajint nres = 0; /* No. residues */ ajint natm = 0; /* No. atoms */ ajint rcnt = 0; /* Residue count */ ajint acnt = 0; /* Atom count */ char id1 = '\0'; /* Residue 1 char id code */ AjPStr id3 = NULL; /* Residue 3 char id code */ /* Allocate strings */ line = ajStrNew(); id3 = ajStrNew(); /* Start of main loop */ while ((ajReadlineTrim(inf, &line))) { /* Parse NR line */ if (ajStrPrefixC(line, "NR")) { ajFmtScanS(line, "%*s %d", &nres); /* Allocate Vdwall object */ vdwall = ajVdwallNew(nres); } /* Parse residue id 3 char */ else if (ajStrPrefixC(line, "AA")) { rcnt++; acnt = 0; ajFmtScanS(line, "%*s %S", &id3); } /* Parse residue id 1 char */ else if (ajStrPrefixC(line, "ID")) ajFmtScanS(line, "%*s %c", &id1); /* Parse number of atoms */ else if (ajStrPrefixC(line, "NN")) { ajFmtScanS(line, "%*s %d", &natm); /* Allocate next Vdwres object */ vdwall->Res[rcnt - 1] = ajVdwresNew(natm); /* Write members of Vdwres object */ vdwall->Res[rcnt - 1]->Id1 = id1; ajStrAssignS(&vdwall->Res[rcnt - 1]->Id3, id3); } /* Parse atom line */ else if (ajStrPrefixC(line, "AT")) { acnt++; ajFmtScanS(line, "%*s %S %*c %f", &vdwall->Res[rcnt - 1]->Atm[acnt - 1], &vdwall->Res[rcnt - 1]->Rad[acnt - 1]); } } ajStrDel(&line); ajStrDel(&id3); return vdwall; } ID ajHetReadNew TY public MO ajpdb LB core XX DE Read heterogen dictionary, the Het object is allocated. XX PN [1] PA u inf AjPFile PD Pointer to Het file PX RT AjPHet RD Het object. RX CA new CT AjPHet CD Het constructor from reading dictionary of heterogen groups in clean format (see documentation for the EMBASSY DOMAINATRIX package). CX // AjPHet ajHetReadNew(AjPFile inf) { AjPHet het = NULL; AjPHetent hetent = NULL; /* current entry */ AjPList list = NULL; /* AJAX List of AJAX Heterogen Entry objects */ AjPStr line = NULL; /* current line */ AjPStr temp = NULL; /* Temporary string */ /* Check args */ if (!inf) { ajWarn("Bad args passed to ajHetReadNew\n"); return NULL; } /* Create Het object if necessary */ if (!het) het = ajHetNew(0); /* Create string and list objects */ line = ajStrNew(); temp = ajStrNew(); list = ajListNew(); /* Read lines from file */ while (ajReadlineTrim(inf, &line)) { if (ajStrPrefixC(line, "ID ")) { hetent = ajHetentNew(); ajFmtScanS(line, "%*s %S", &hetent->abv); } else if (ajStrPrefixC(line, "DE ")) { /* NEED TO ACCOUNT FOR MULTIPLE LINES */ ajStrAssignSubS(&temp, line, 5, -1); if (ajStrGetLen(hetent->ful)) ajStrAppendS(&hetent->ful, temp); else ajStrAssignS(&hetent->ful, temp); } else if (ajStrPrefixC(line, "SY ")) { #if AJFALSE ajFmtScanS(line, "%*s %S", &hetent->syn); #endif ajStrAssignSubS(&temp, line, 5, -1); if (ajStrGetLen(hetent->syn)) ajStrAppendS(&hetent->syn, temp); else ajStrAssignS(&hetent->syn, temp); } else if (ajStrPrefixC(line, "NN ")) ajFmtScanS(line, "%*s %S", &hetent->cnt); else if (ajStrPrefixC(line, "//")) ajListPush(list, (AjPHetent) hetent); } het->Number = (ajuint) ajListToarray(list, (void ***) &het->Entries); ajStrDel(&line); ajStrDel(&temp); ajListFree(&list); return het; } ID ajHetReadRawNew TY public MO ajpdb LB core XX DE Reads a dictionary of heterogen groups available at DE http://pdb.rutgers.edu/het_dictionary.txt and writes a Het object. XX PN [1] PA u inf AjPFile PD Pointer to dictionary PX RT AjPHet RD True on success RX CA new CT AjPHet CD Het constructor from reading dictionary of heterogen groups in raw format. CX // AjPHet ajHetReadRawNew(AjPFile inf) { AjPHet het = NULL; AjPStr line = NULL; /* A line from the file */ AjPHetent entry = NULL; /* The current entry */ AjPHetent tmp = NULL; /* Temp. pointer */ AjPList list = NULL; /* List of entries */ ajint het_cnt = 0; /* Count of number of HET records in file */ ajint formul_cnt = 0; /* Count of number of FORMUL records in file */ /* Check arg's */ if (!inf) { ajWarn("Bad args passed to ajHetReadRawNew\n"); return NULL; } /* Create strings etc */ line = ajStrNew(); list = ajListNew(); /* Read lines from file */ while (ajReadlineTrim(inf, &line)) { if (ajStrPrefixC(line, "HET ")) { het_cnt++; entry = ajHetentNew(); ajFmtScanS(line, "%*s %S", &entry->abv); } else if (ajStrPrefixC(line, "HETNAM")) { ajStrAppendC(&entry->ful, &line->Ptr[15]); } else if (ajStrPrefixC(line, "HETSYN")) { ajStrAppendC(&entry->syn, &line->Ptr[15]); } else if (ajStrPrefixC(line, "FORMUL")) { formul_cnt++; /* In cases where HETSYN or FORMUL were not specified, assign a * value of '.' */ if (MAJSTRGETLEN(entry->ful) == 0) ajStrAssignC(&entry->ful, "."); if (MAJSTRGETLEN(entry->syn) == 0) ajStrAssignC(&entry->syn, "."); /* Push entry onto list */ ajListPush(list, (AjPHetent) entry); } } if (het_cnt != formul_cnt) { while (ajListPop(list, (void **) &tmp)) ajHetentDel(&tmp); ajListFree(&list); ajStrDel(&line); ajFatal("Fatal discrepancy in count of HET and FORMUL records\n"); } het = ajHetNew(0); het->Number = (ajuint) ajListToarray(list, (void ***) &het->Entries); ajStrDel(&line); ajListFree(&list); return het; } ID ajPdbReadFirstModelNew TY public MO ajpdb LB core XX DE Reads a clean coordinate file file (see documentation for DOMAINATRIX DE "pdbparse" application) and writes a filled Pdb object. Data for the first DE model only is read in. XX PN [1] PA u inf AjPFile PD Pointer to clean coordinate file PX RT AjPPdb RD Pointer to Pdb object. RX CA new CT AjPPdb CD Pdb constructor from reading ccf format file (retrieve data for 1st model only). CX // AjPPdb ajPdbReadFirstModelNew(AjPFile inf) { return (AjPPdb) ajPdbReadNew(inf, 0); } ID ajPdbReadAllModelsNew TY public MO ajpdb LB core XX DE Reads a clean coordinate file (see documentation for DOMAINATRIX "pdbparse" DE application) and writes a filled Pdb object. Data for all models is read. XX PN [1] PA u inf AjPFile PD Pointer to clean coordinate file PX RT AjPPdb RD Pointer to Pdb object. RX CA new CT AjPPdb CD Pdb constructor from reading ccf format file. CX // AjPPdb ajPdbReadAllModelsNew(AjPFile inf) { return (AjPPdb) ajPdbReadNew(inf, 1); } ID ajPdbReadNew TY public MO ajpdb LB core XX DE Reads a clean coordinate file file (see documentation for DOMAINATRIX DE "pdbparse" application) and writes a filled Pdb object. Data for the first DE model only is read in. XX PN [1] PA u inf AjPFile PD Pointer to clean coordinate file PX PN [2] PA r mode ajint PD Mode. 0==Read first model only. 1==Read all models. PX RT AjPPdb RD Pointer to Pdb object. RX CA new CT AjPPdb CD Pdb constructor from reading ccf format file (retrieve data for 1st model only). CX // AjPPdb ajPdbReadNew(AjPFile inf, ajint mode) { AjPPdb pdb = NULL; ajint nmod = 0; ajint ncha = 0; ajint ngrp = 0; ajint nc = 0; ajint mod = 0; ajint chn = 0; ajint gpn = 0; float reso = 0.0F; AjPStr line = NULL; AjPStr token = NULL; AjPStr idstr = NULL; AjPStr destr = NULL; AjPStr osstr = NULL; AjPStr xstr = NULL; AjPStrTok handle = NULL; AjPAtom atom = NULL; AjPResidue residue = NULL; ajuint rn_last = UINT_MAX; ajuint mn_last = UINT_MAX; AjBool fixReadAtoms = ajTrue; /* Initialise strings */ line = ajStrNew(); token = ajStrNew(); idstr = ajStrNew(); destr = ajStrNew(); osstr = ajStrNew(); xstr = ajStrNew(); /* Start of main loop */ while (ajReadlineTrim(inf, &line)) { if (ajStrPrefixC(line, "XX")) continue; /* Parse ID */ else if (ajStrPrefixC(line, "ID")) { ajStrTokenAssignC(&handle, line, " \n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &idstr); continue; } /* Parse number of chains */ else if (ajStrPrefixC(line, "CN")) { ajStrTokenAssignC(&handle, line, " []\n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &nc); continue; } /* Parse description text */ else if (ajStrPrefixC(line, "DE")) { ajStrTokenAssignC(&handle, line, " "); ajStrTokenNextParse(&handle, &token); /* 'DE' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* desc */ if (ajStrGetLen(destr)) { ajStrAppendC(&destr, " "); ajStrAppendS(&destr, token); } else ajStrAssignS(&destr, token); continue; } /* Parse source text */ else if (ajStrPrefixC(line, "OS")) { ajStrTokenAssignC(&handle, line, " "); ajStrTokenNextParse(&handle, &token); /* 'OS' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* source */ if (ajStrGetLen(osstr)) { ajStrAppendC(&osstr, " "); ajStrAppendS(&osstr, token); } else ajStrAssignS(&osstr, token); continue; } /* Parse experimental line */ else if (ajStrPrefixC(line, "EX")) { ajStrTokenAssignC(&handle, line, " ;\n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &xstr); /* method */ ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* reso */ ajStrToFloat(token, &reso); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* nmod */ ajStrToInt(token, &nmod); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* ncha */ ajStrToInt(token, &ncha); ajStrTokenNextParse(&handle, &token); /* nlig */ ajStrToInt(token, &ngrp); pdb = ajPdbNew(ncha); ajStrAssignS(&pdb->Pdb, idstr); ajStrAssignS(&pdb->Compnd, destr); ajStrAssignS(&pdb->Source, osstr); if (ajStrMatchC(xstr, "xray")) pdb->Method = ajEPdbMethodXray; else pdb->Method = ajEPdbMethodNmr; pdb->Reso = reso; /* ** 0 == Read first model only. Number of models is hard-coded to 1 ** as only the data for the first model is read in. ** 1 == Read all models. */ if (mode == 0) pdb->Nmod = 1; else if (mode == 1) pdb->Nmod = nmod; else ajFatal("Unrecognised mode in ajPdbReadNew"); pdb->Nchn = ncha; pdb->Ngp = ngrp; continue; } /* Parse information line */ else if (ajStrPrefixC(line, "IN")) { ajStrTokenAssignC(&handle, line, " ;\n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* id value */ ajStrTokenNextParse(&handle, &token); pdb->Chains[nc - 1]->Id = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); /* residues */ ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &pdb->Chains[nc - 1]->Nres); ajStrTokenNextParse(&handle, &token); /* hetatm */ ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &pdb->Chains[nc - 1]->Nlig); /* helices */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &pdb->Chains[nc - 1]->numHelices); /* strands */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &pdb->Chains[nc - 1]->numStrands); /* sheets */ #if AJFALSE ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &pdb->Chains[nc - 1]->numSheets); #endif /* turns */ #if AJFALSE ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &pdb->Chains[nc - 1]->numTurns); #endif continue; } /* Parse sequence line */ else if (ajStrPrefixC(line, "SQ")) { while (ajReadlineTrim(inf, &line) && !ajStrPrefixC(line, "XX")) ajStrAppendC(&pdb->Chains[nc - 1]->Seq, ajStrGetPtr(line)); ajStrRemoveWhite(&pdb->Chains[nc - 1]->Seq); continue; } /* Parse atom line */ else if (fixReadAtoms && ajStrPrefixC(line, "AT")) { mod = chn = gpn = 0; /* Skip AT record */ ajStrTokenAssignC(&handle, line, " \t\n\r"); ajStrTokenNextParse(&handle, &token); /* Model number. 0 == Read first model only */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &mod); if ((mode == 0) && (mod != 1)) { /* break; */ /* Discard remaining AT lines */ while (ajReadlineTrim(inf, &line) && ajStrPrefixC(line, "AT")); } /* Chain number */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &chn); /* Group number */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &gpn); /* Allocate object */ atom = ajAtomNew(); atom->Mod = mod; atom->Chn = chn; atom->Gpn = gpn; /* Residue number */ ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &atom->Idx); /* Residue number string */ ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Pdb, token); /* Residue id, 1 char */ ajStrTokenNextParse(&handle, &token); atom->Id1 = *ajStrGetPtr(token); /* Residue id, 3 char */ ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Id3, token); /* Atom type */ ajStrTokenNextParse(&handle, &token); atom->Type = *ajStrGetPtr(token); /* Atom identifier */ ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Atm, token); /* X coordinate */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->X); /* Y coordinate */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->Y); /* Z coordinate */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->Z); /* Occupancy */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->O); /* B value thermal factor. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->B); /* ** Push atom onto appropriate list. ** Check for coordinates for water or groups that could not ** be uniquely assigned to a chain */ if (chn == 0) { /* Heterogen */ if (atom->Type == 'H') ajListPushAppend(pdb->Groups, (void *) atom); else if (atom->Type == 'W') ajListPushAppend(pdb->Water, (void *) atom); else ajFatal("Unexpected parse error in " "ajPdbReadFirstModelNew"); } else { ajListPushAppend(pdb->Chains[chn - 1]->Atoms, (void *) atom); } continue; } /* Parse residue line */ else if (ajStrPrefixC(line, "RE")) { mod = chn = 0; /* Skip RE record */ ajStrTokenAssignC(&handle, line, " \t\n\r"); ajStrTokenNextParse(&handle, &token); /* Model number. 0 == Read first model only */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &mod); if ((mode == 0) && (mod != 1)) { /* break; */ /* Discard remaining RE lines */ while (ajReadlineTrim(inf, &line) && ajStrPrefixC(line, "RE")); } /* Chain number */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &chn); /* Allocate object */ residue = ajResidueNew(); residue->Mod = mod; residue->Chn = chn; /* Residue number */ ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &residue->Idx); /* Residue number (original string) */ ajStrTokenNextParse(&handle, &token); ajStrAssignS(&residue->Pdb, token); /* Residue id, 1 char */ ajStrTokenNextParse(&handle, &token); residue->Id1 = *ajStrGetPtr(token); /* Residue id, 3 char */ ajStrTokenNextParse(&handle, &token); ajStrAssignS(&residue->Id3, token); /* Element serial number (PDB elements) */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &residue->eNum); /* Element identifier (PDB elements) */ ajStrTokenNextParse(&handle, &token); ajStrAssignS(&residue->eId, token); /* Element type (PDB elements) */ ajStrTokenNextParse(&handle, &token); residue->eType = *ajStrGetPtr(token); /* Class of helix (PDB elements) */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &residue->eClass); /* Number of the element (stride) */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &residue->eStrideNum); /* Element type (stride) */ ajStrTokenNextParse(&handle, &token); residue->eStrideType = *ajStrGetPtr(token); /* Phi angle */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->Phi); /* Psi angle */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->Psi); /* Residue solvent accessible area. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->Area); /* Absolute accessibility, all atoms. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->all_abs); /* Relative accessibility, all atoms. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->all_rel); /* Absolute accessibility, atoms in side chain. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->side_abs); /* Relative accessibility, atoms in side chain. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->side_rel); /* Absolute accessibility, atoms in main chain. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->main_abs); /* Relative accessibility, atoms in main chain. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->main_rel); /* Absolute accessibility, non-polar atoms. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->npol_abs); /* Relative accessibility, non-polar atoms. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->npol_rel); /* Absolute accessibility, polar atoms. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->pol_abs); /* Relative accessibility, polar atoms. */ ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->pol_rel); ajListPushAppend(pdb->Chains[chn - 1]->Residues, (void *) residue); continue; } /* Parse coordinate line */ else if (ajStrPrefixC(line, "CO")) { mod = chn = gpn = 0; ajStrTokenAssignC(&handle, line, " \t\n\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &mod); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &chn); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &gpn); atom = ajAtomNew(); atom->Mod = mod; atom->Chn = chn; atom->Gpn = gpn; ajStrTokenNextParse(&handle, &token); atom->Type = ajStrGetCharFirst(token); ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &atom->Idx); ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Pdb, token); /* Residue object */ if (atom->Type == 'P') { /* New model */ if (atom->Mod != mn_last) { rn_last = UINT_MAX; mn_last = atom->Mod; } /* New residue */ if (atom->Idx != rn_last) { residue = ajResidueNew(); residue->Mod = atom->Mod; residue->Chn = atom->Chn; residue->Idx = atom->Idx; ajStrAssignS(&residue->Pdb, atom->Pdb); } } ajStrTokenNextParse(&handle, &token); residue->eType = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &residue->eNum); ajStrTokenNextParse(&handle, &token); ajStrAssignS(&residue->eId, token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &residue->eClass); ajStrTokenNextParse(&handle, &token); residue->eStrideType = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &residue->eStrideNum); ajStrTokenNextParse(&handle, &token); atom->Id1 = *ajStrGetPtr(token); residue->Id1 = atom->Id1; ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Id3, token); ajStrAssignS(&residue->Id3, atom->Id3); ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Atm, token); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->X); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->Y); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->Z); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->O); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->B); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->Phi); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->Psi); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->Area); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->all_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->all_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->side_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->side_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->main_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->main_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->npol_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->npol_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->pol_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->pol_rel); /* Check for coordinates for water or groups that could not be * uniquely assigned to a chain */ if (chn == 0) { /* Heterogen */ if (atom->Type == 'H') ajListPushAppend(pdb->Groups, (void *) atom); else if (atom->Type == 'W') ajListPushAppend(pdb->Water, (void *) atom); else ajFatal("Unexpected parse error in ajPdbRead"); } else ajListPushAppend(pdb->Chains[chn - 1]->Atoms, (void *) atom); ajListPushAppend(pdb->Chains[chn - 1]->Residues, (void *) residue); } } /* End of main application loop */ ajStrTokenDel(&handle); ajStrDel(&line); ajStrDel(&token); ajStrDel(&idstr); ajStrDel(&destr); ajStrDel(&osstr); ajStrDel(&xstr); return pdb; } ID ajPdbReadoldNew TY public MO ajpdb LB core XX DE Reads a clean coordinate file (see documentation for DOMAINATRIX "pdbparse" DE application) lacking residue-level description in RE records and writes a DE filled Pdb object. XX PN [1] PA u inf AjPFile PD Pointer to clean coordinate file PX RT AjPPdb RD Pointer to Pdb object. RX CA new CT AjPPdb CD Pdb constructor from reading ccf format file. CX // AjPPdb ajPdbReadoldNew(AjPFile inf) { AjPPdb pdb = NULL; ajint nmod = 0; ajint ncha = 0; ajint ngrp = 0; ajint nc = 0; ajint mod = 0; ajint chn = 0; ajint gpn = 0; float reso = 0.0F; AjPStr line = NULL; AjPStr token = NULL; AjPStr idstr = NULL; AjPStr destr = NULL; AjPStr osstr = NULL; AjPStr xstr = NULL; AjPStrTok handle = NULL; AjPAtom atom = NULL; AjPResidue residue = NULL; ajuint rn_last = UINT_MAX; ajuint mn_last = UINT_MAX; /* Initialise strings */ line = ajStrNew(); token = ajStrNew(); idstr = ajStrNew(); destr = ajStrNew(); osstr = ajStrNew(); xstr = ajStrNew(); /* Start of main application loop */ while (ajReadlineTrim(inf, &line)) { if (ajStrPrefixC(line, "XX")) continue; /* Parse ID */ if (ajStrPrefixC(line, "ID")) { ajStrTokenAssignC(&handle, line, " \n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &idstr); continue; } /* Parse number of chains */ if (ajStrPrefixC(line, "CN")) { ajStrTokenAssignC(&handle, line, " []\n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &nc); continue; } /* Parse description text */ if (ajStrPrefixC(line, "DE")) { ajStrTokenAssignC(&handle, line, " "); ajStrTokenNextParse(&handle, &token); /* 'DE' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* desc */ if (ajStrGetLen(destr)) { ajStrAppendC(&destr, " "); ajStrAppendS(&destr, token); } else ajStrAssignS(&destr, token); continue; } /* Parse source text */ if (ajStrPrefixC(line, "OS")) { ajStrTokenAssignC(&handle, line, " "); ajStrTokenNextParse(&handle, &token); /* 'OS' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* source */ if (ajStrGetLen(osstr)) { ajStrAppendC(&osstr, " "); ajStrAppendS(&osstr, token); } else ajStrAssignS(&osstr, token); continue; } /* Parse experimental line */ if (ajStrPrefixC(line, "EX")) { ajStrTokenAssignC(&handle, line, " ;\n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &xstr); /* method */ ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* reso */ ajStrToFloat(token, &reso); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* nmod */ ajStrToInt(token, &nmod); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* nchn */ ajStrToInt(token, &ncha); ajStrTokenNextParse(&handle, &token); /* nlig */ ajStrToInt(token, &ngrp); pdb = ajPdbNew(ncha); ajStrAssignS(&pdb->Pdb, idstr); ajStrAssignS(&pdb->Compnd, destr); ajStrAssignS(&pdb->Source, osstr); if (ajStrMatchC(xstr, "xray")) pdb->Method = ajEPdbMethodXray; else pdb->Method = ajEPdbMethodNmr; pdb->Reso = reso; pdb->Nmod = nmod; pdb->Nchn = ncha; pdb->Ngp = ngrp; } /* Parse information line */ if (ajStrPrefixC(line, "IN")) { ajStrTokenAssignC(&handle, line, " ;\n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* id value */ pdb->Chains[nc - 1]->Id = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* residues */ ajStrToUint(token, &pdb->Chains[nc - 1]->Nres); ajStrTokenNextParse(&handle, &token); /* hetatm */ ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &pdb->Chains[nc - 1]->Nlig); /* helices */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &pdb->Chains[nc - 1]->numHelices); /* strands */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &pdb->Chains[nc - 1]->numStrands); /* sheets */ #if AJFALSE ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &pdb->Chains[nc - 1]->numSheets); #endif /* turns */ #if AJFALSE ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &pdb->Chains[nc - 1]->numTurns); #endif continue; } /* Parse sequence line */ if (ajStrPrefixC(line, "SQ")) { while (ajReadlineTrim(inf, &line) && !ajStrPrefixC(line, "XX")) ajStrAppendC(&pdb->Chains[nc - 1]->Seq, ajStrGetPtr(line)); ajStrRemoveWhite(&pdb->Chains[nc - 1]->Seq); continue; } /* Parse coordinate line */ if (ajStrPrefixC(line, "CO")) { mod = chn = gpn = 0; ajStrTokenAssignC(&handle, line, " \t\n\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &mod); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &chn); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &gpn); atom = ajAtomNew(); atom->Mod = mod; atom->Chn = chn; atom->Gpn = gpn; ajStrTokenNextParse(&handle, &token); atom->Type = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &atom->Idx); ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Pdb, token); /* Residue object */ if (atom->Type == 'P') { /* New model */ if (atom->Mod != mn_last) { rn_last = UINT_MAX; mn_last = atom->Mod; } /* New residue */ if (atom->Idx != rn_last) { residue = ajResidueNew(); residue->Mod = atom->Mod; residue->Chn = atom->Chn; residue->Idx = atom->Idx; ajStrAssignS(&residue->Pdb, atom->Pdb); } } ajStrTokenNextParse(&handle, &token); residue->eType = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &residue->eNum); ajStrTokenNextParse(&handle, &token); ajStrAssignS(&residue->eId, token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &residue->eClass); ajStrTokenNextParse(&handle, &token); residue->eStrideType = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &residue->eStrideNum); ajStrTokenNextParse(&handle, &token); atom->Id1 = *ajStrGetPtr(token); residue->Id1 = atom->Id1; ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Id3, token); ajStrAssignS(&residue->Id3, atom->Id3); ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Atm, token); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->X); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->Y); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->Z); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->O); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->B); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->Phi); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->Psi); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->Area); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->all_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->all_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->side_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->side_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->main_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->main_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->npol_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->npol_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->pol_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &residue->pol_rel); /* Check for coordinates for water or groups that could not be * uniquely assigned to a chain */ if (chn == 0) { /* Heterogen */ if (atom->Type == 'H') ajListPushAppend(pdb->Groups, (void *) atom); else if (atom->Type == 'W') ajListPushAppend(pdb->Water, (void *) atom); else ajFatal("Unexpected parse error in ajPdbRead"); } else ajListPushAppend(pdb->Chains[chn - 1]->Atoms, (void *) atom); ajListPushAppend(pdb->Chains[chn - 1]->Residues, (void *) residue); } } /* End of main application loop */ ajStrTokenDel(&handle); ajStrDel(&line); ajStrDel(&token); ajStrDel(&idstr); ajStrDel(&destr); ajStrDel(&osstr); ajStrDel(&xstr); return pdb; } ID ajPdbReadoldFirstModelNew TY public MO ajpdb LB core XX DE Reads a clean coordinate file file (see documentation for DOMAINATRIX DE "pdbparse" application) lacking residue-level description in RE records and DE writes a filled Pdb object. Data for the first model only is read in. XX PN [1] PA u inf AjPFile PD Pointer to clean coordinate file PX RT AjPPdb RD Pointer to Pdb object. RX CA new CT AjPPdb CD Pdb constructor from reading ccf format file (retrieve data for 1st model only). CX // AjPPdb ajPdbReadoldFirstModelNew(AjPFile inf) { AjPPdb ret = NULL; ajint nmod = 0; ajint ncha = 0; ajint ngrp = 0; ajint nc = 0; ajint mod = 0; ajint chn = 0; ajint gpn = 0; float reso = 0.0F; AjPStr line = NULL; AjPStr token = NULL; AjPStr idstr = NULL; AjPStr destr = NULL; AjPStr osstr = NULL; AjPStr xstr = NULL; AjPStrTok handle = NULL; AjPAtom atom = NULL; AjPResidue res = NULL; ajuint rn_last = UINT_MAX; ajuint mn_last = UINT_MAX; /* Initialise strings */ line = ajStrNew(); token = ajStrNew(); idstr = ajStrNew(); destr = ajStrNew(); osstr = ajStrNew(); xstr = ajStrNew(); /* Start of main application loop */ while (ajReadlineTrim(inf, &line)) { if (ajStrPrefixC(line, "XX")) continue; /* Parse ID */ if (ajStrPrefixC(line, "ID")) { ajStrTokenAssignC(&handle, line, " \n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &idstr); continue; } /* Parse number of chains */ if (ajStrPrefixC(line, "CN")) { ajStrTokenAssignC(&handle, line, " []\n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &nc); continue; } /* Parse description text */ if (ajStrPrefixC(line, "DE")) { ajStrTokenAssignC(&handle, line, " "); ajStrTokenNextParse(&handle, &token); /* 'DE' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* desc */ if (ajStrGetLen(destr)) { ajStrAppendC(&destr, " "); ajStrAppendS(&destr, token); } else ajStrAssignS(&destr, token); continue; } /* Parse source text */ if (ajStrPrefixC(line, "OS")) { ajStrTokenAssignC(&handle, line, " "); ajStrTokenNextParse(&handle, &token); /* 'OS' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* source */ if (ajStrGetLen(osstr)) { ajStrAppendC(&osstr, " "); ajStrAppendS(&osstr, token); } else ajStrAssignS(&osstr, token); continue; } /* Parse experimental line */ if (ajStrPrefixC(line, "EX")) { ajStrTokenAssignC(&handle, line, " ;\n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &xstr); /* method */ ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* reso */ ajStrToFloat(token, &reso); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* nmod */ ajStrToInt(token, &nmod); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* ncha */ ajStrToInt(token, &ncha); ajStrTokenNextParse(&handle, &token); /* nlig */ ajStrToInt(token, &ngrp); ret = ajPdbNew(ncha); ajStrAssignS(&(ret)->Pdb, idstr); ajStrAssignS(&(ret)->Compnd, destr); ajStrAssignS(&(ret)->Source, osstr); if (ajStrMatchC(xstr, "xray")) (ret)->Method = ajEPdbMethodXray; else (ret)->Method = ajEPdbMethodNmr; (ret)->Reso = reso; /* (ret)->Nmod = nmod; */ /* ** Number of models is hard-coded to 1 as only the ** data for the first model is read in */ (ret)->Nmod = 1; (ret)->Nchn = ncha; (ret)->Ngp = ngrp; } /* Parse information line */ if (ajStrPrefixC(line, "IN")) { ajStrTokenAssignC(&handle, line, " ;\n\t\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* id value */ (ret)->Chains[nc - 1]->Id = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); /* residues */ ajStrToUint(token, &(ret)->Chains[nc - 1]->Nres); ajStrTokenNextParse(&handle, &token); /* hetatm */ ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &(ret)->Chains[nc - 1]->Nlig); /* helices */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &(ret)->Chains[nc - 1]->numHelices); /* strands */ ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &(ret)->Chains[nc - 1]->numStrands); /* sheets */ #if AJFALSE ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &(ret)->Chains[nc - 1]->numSheets); #endif /* turns */ #if AJFALSE ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &(ret)->Chains[nc - 1]->numTurns); #endif continue; } /* Parse sequence line */ if (ajStrPrefixC(line, "SQ")) { while (ajReadlineTrim(inf, &line) && !ajStrPrefixC(line, "XX")) ajStrAppendC(&(ret)->Chains[nc - 1]->Seq, ajStrGetPtr(line)); ajStrRemoveWhite(&(ret)->Chains[nc - 1]->Seq); continue; } /* Parse coordinate line */ if (ajStrPrefixC(line, "CO")) { mod = chn = gpn = 0; ajStrTokenAssignC(&handle, line, " \t\n\r"); ajStrTokenNextParse(&handle, &token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &mod); if (mod != 1) break; ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &chn); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &gpn); atom = ajAtomNew(); atom->Mod = mod; atom->Chn = chn; atom->Gpn = gpn; ajStrTokenNextParse(&handle, &token); atom->Type = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrToUint(token, &atom->Idx); ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Pdb, token); /* Residue object */ if (atom->Type == 'P') { /* New model */ if (atom->Mod != mn_last) { rn_last = UINT_MAX; mn_last = atom->Mod; } /* New residue */ if (atom->Idx != rn_last) { res = ajResidueNew(); res->Mod = atom->Mod; res->Chn = atom->Chn; res->Idx = atom->Idx; ajStrAssignS(&res->Pdb, atom->Pdb); } } ajStrTokenNextParse(&handle, &token); res->eType = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &res->eNum); ajStrTokenNextParse(&handle, &token); ajStrAssignS(&res->eId, token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &res->eClass); ajStrTokenNextParse(&handle, &token); res->eStrideType = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrToInt(token, &res->eStrideNum); ajStrTokenNextParse(&handle, &token); atom->Id1 = *ajStrGetPtr(token); ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Id3, token); ajStrAssignS(&res->Id3, atom->Id3); ajStrTokenNextParse(&handle, &token); ajStrAssignS(&atom->Atm, token); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->X); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->Y); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->Z); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->O); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &atom->B); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->Phi); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->Psi); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->Area); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->all_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->all_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->side_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->side_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->main_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->main_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->npol_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->npol_rel); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->pol_abs); ajStrTokenNextParse(&handle, &token); ajStrToFloat(token, &res->pol_rel); /* Check for coordinates for water or groups that could not * be * uniquely assigned to a chain */ if (chn == 0) { /* Heterogen */ if (atom->Type == 'H') ajListPushAppend((ret)->Groups, (void *) atom); else if (atom->Type == 'W') ajListPushAppend((ret)->Water, (void *) atom); else ajFatal("Unexpected parse error in " "ajPdbReadFirstModelNew"); } else ajListPushAppend((ret)->Chains[chn - 1]->Atoms, (void *) atom); ajListPushAppend((ret)->Chains[chn - 1]->Residues, (void *) res); } } /* End of main application loop */ ajStrTokenDel(&handle); ajStrDel(&line); ajStrDel(&token); ajStrDel(&idstr); ajStrDel(&destr); ajStrDel(&osstr); ajStrDel(&xstr); return ret; } ID ajAtomNew TY public MO ajpdb LB core XX DE Atom object constructor. DE This is normally called by the ajChainNew function. XX RT AjPAtom RD Pointer to an atom object RX CA new CT AjPAtom CD Default Atom constructor. CX // AjPAtom ajAtomNew(void) { AjPAtom atom = NULL; AJNEW0(atom); atom->Id3 = ajStrNew(); atom->Atm = ajStrNew(); atom->Pdb = ajStrNew(); #if AJFALSE atom->eId = ajStrNew(); #endif atom->Id1 = '.'; #if AJFALSE atom->eType = '.'; ajStrAssignC(&atom->eId, "."); atom->eStrideType = '.'; #endif return atom; } ID ajResidueNew TY public MO ajpdb LB core XX DE Residue object constructor. DE This is normally called by the ajChainNew function. XX RT AjPResidue RD Pointer to a Residue object RX CA new CT AjPResidue CD Default Residue constructor. CX // AjPResidue ajResidueNew(void) { AjPResidue residue = NULL; AJNEW0(residue); residue->Pdb = ajStrNew(); residue->Id3 = ajStrNew(); residue->eId = ajStrNew(); residue->Id1 = '.'; residue->eType = '.'; ajStrAssignC(&residue->eId, "."); residue->eStrideType = '.'; return residue; } ID ajChainNew TY public MO ajpdb LB core XX DE Chain object constructor. DE This is normally called by the ajPdbNew function XX RT AjPChain RD Pointer to a chain object RX CA new CT AjPChain CD Default Chain constructor. CX // AjPChain ajChainNew(void) { AjPChain chain = NULL; AJNEW0(chain); chain->Seq = ajStrNewC(""); chain->Atoms = ajListNew(); chain->Residues = ajListNew(); return chain; } ID ajPdbNew TY public MO ajpdb LB core XX DE Pdb object constructor. Fore-knowledge of the number of chains DE is required. This is normally called by the functions that read PDB DE files or clean coordinate files (see embpdb.c & embpdbraw.c). XX PN [1] PA r n ajuint PD Number of chains in this pdb file PX RT AjPPdb RD Pointer to a pdb object RX CA new CT AjPPdb CD Default Pdb constructor. CX // AjPPdb ajPdbNew(ajuint n) { ajuint i = 0U; AjPPdb pdb = NULL; AJNEW0(pdb); pdb->Pdb = ajStrNew(); pdb->Compnd = ajStrNew(); pdb->Source = ajStrNew(); pdb->Groups = ajListNew(); pdb->Water = ajListNew(); pdb->gpid = ajChararrNew(); if (n) { AJCNEW0(pdb->Chains, n); for (i = 0U; i < n; ++i) pdb->Chains[i] = ajChainNew(); } return pdb; } ID ajHetentNew TY public MO ajpdb LB core XX DE Hetent object constructor. XX RT AjPHetent RD Pointer to a Hetent object RX CA new CT AjPHetent CD Default Hetent constructor. CX // AjPHetent ajHetentNew(void) { AjPHetent hetent = NULL; AJNEW0(hetent); hetent->abv = ajStrNew(); hetent->syn = ajStrNew(); hetent->ful = ajStrNew(); return hetent; } ID ajHetNew TY public MO ajpdb LB core XX DE Het object constructor. XX PN [1] PA r n ajuint PD Number of entries in dictionary. PX RT AjPHet RD Pointer to a Het object RX CA new CT AjPHet CD Default Het constructor. CX // AjPHet ajHetNew(ajuint n) { ajuint i = 0U; AjPHet het = NULL; AJNEW0(het); if (n) { AJCNEW0(het->Entries, n); het->Number = n; for (i = 0U; i < n; i++) het->Entries[i] = ajHetentNew(); } else { het->Number = 0U; het->Entries = NULL; } return het; } ID ajVdwallNew TY public MO ajpdb LB core XX DE Vdwall object constructor. This is normally called by the ajVdwallReadNew DE function. Fore-knowledge of the number of residues is required. XX PN [1] PA r n ajuint PD Number of residues PX RT AjPVdwall RD Pointer to a Vdwall object RX CA new CT AjPVdwall CD Default Vdwall constructor. CX // AjPVdwall ajVdwallNew(ajuint n) { AjPVdwall vdwall = NULL; AJNEW0(vdwall); vdwall->N = n; if (n) AJCNEW0(vdwall->Res, n); return vdwall; } ID ajVdwresNew TY public MO ajpdb LB core XX DE Vdwres object constructor. This is normally called by the ajVdwallReadNew DE function. Fore-knowledge of the number of atoms is required. XX PN [1] PA r n ajuint PD Number of atoms PX RT AjPVdwres RD Pointer to a Vdwres object RX CA new CT AjPVdwres CD Default Vdwres constructor. CX // AjPVdwres ajVdwresNew(ajuint n) { ajuint i = 0U; AjPVdwres vdwres = NULL; AJNEW0(vdwres); vdwres->Id3 = ajStrNew(); vdwres->N = n; if (n) { AJCNEW0(vdwres->Atm, n); for (i = 0U; i < n; i++) vdwres->Atm[i] = ajStrNew(); AJCNEW0(vdwres->Rad, n); } return vdwres; } ID ajCmapNew TY public MO ajpdb LB core XX DE Cmap object constructor. This is normally called by the ajCmapReadNew DE function. Fore-knowledge of the dimension (number of residues) for the DE contact map is required. XX PN [1] PA r n ajuint PD Dimension of contact map PX RT AjPCmap RD Pointer to a Cmap object RX CA new CT AjPCmap CD Default Cmap constructor. CX // AjPCmap ajCmapNew(ajuint n) { ajuint i = 0; AjPCmap cmap = NULL; AJNEW0(cmap); cmap->Id = ajStrNew(); cmap->Domid = ajStrNew(); cmap->Ligid = ajStrNew(); cmap->Seq1 = ajStrNew(); cmap->Seq2 = ajStrNew(); cmap->Desc = ajStrNew(); cmap->Chid1 = '.'; cmap->Chid2 = '.'; if (n) { /* Create the SQUARE contact map */ cmap->Mat = ajUint2dNewRes((ajint) n); for (i = 0U; i < n; i++) ajUint2dPut(&cmap->Mat, i, n - 1, 0U); } cmap->Dim = n; cmap->Ncon = 0; return cmap; } ID ajPdbtospNew TY public MO ajpdb LB core XX DE Pdbtosp object constructor. Fore-knowledge of the number of entries is DE required. This is normally called by the ajPdbtospReadCNew / DE ajPdbtospReadNew functions. XX PN [1] PA r n ajuint PD Number of entries PX RT AjPPdbtosp RD Pointer to a Pdbtosp object RX CA new CT AjPPdbtosp CD Default Pdbtosp constructor. CX // AjPPdbtosp ajPdbtospNew(ajuint n) { ajuint i = 0U; AjPPdbtosp pdbtosp = NULL; AJNEW0(pdbtosp); pdbtosp->Pdb = ajStrNew(); if (n) { AJCNEW0(pdbtosp->Acc, n); AJCNEW0(pdbtosp->Spr, n); for (i = 0U; i < n; i++) { pdbtosp->Acc[i] = ajStrNew(); pdbtosp->Spr[i] = ajStrNew(); } } pdbtosp->Number = n; return pdbtosp; } ID ajAtomDel TY public MO ajpdb LB core XX DE Destructor for an AJAX Atom object. XX PN [1] PA d Patom AjPAtom* PD AJAX Atom address PX RT void RD RX CA delete CT AjPAtom CD Default Atom destructor CX // void ajAtomDel(AjPAtom *Patom) { AjPAtom atom = NULL; if (!Patom || !*Patom) return; atom = *Patom; ajStrDel(&atom->Id3); ajStrDel(&atom->Atm); ajStrDel(&atom->Pdb); #if AJFALSE ajStrDel(&atom->eId); #endif AJFREE(atom); *Patom = NULL; return; } ID ajResidueDel TY public MO ajpdb LB core XX DE Destructor for an AJAX Residue object. XX PN [1] PA d Presidue AjPResidue* PD AJAX Residue address PX RT void RD RX CA delete CT AjPResidue CD Default AJAX Residue destructor CX // void ajResidueDel(AjPResidue *Presidue) { AjPResidue residue = NULL; if (!Presidue || !*Presidue) return; residue = *Presidue; ajStrDel(&residue->Pdb); ajStrDel(&residue->Id3); ajStrDel(&residue->eId); AJFREE(residue); *Presidue = NULL; return; } ID ajChainDel TY public MO ajpdb LB core XX DE Destructor for an AJAX Chain object. XX PN [1] PA d Pchain AjPChain* PD AJAX Chain address PX RT void RD RX CA delete CT AjPChain CD Default AJAX Chain destructor. CX // void ajChainDel(AjPChain *Pchain) { AjPAtom atom = NULL; AjPChain chain = NULL; AjPResidue residue = NULL; if (!Pchain || !*Pchain) return; chain = *Pchain; while (ajListPop(chain->Atoms, (void **) &atom)) ajAtomDel(&atom); ajListFree(&chain->Atoms); while (ajListPop(chain->Residues, (void **) &residue)) ajResidueDel(&residue); ajListFree(&chain->Residues); ajStrDel(&chain->Seq); AJFREE(chain); *Pchain = NULL; return; } ID ajPdbDel TY public MO ajpdb LB core XX DE Destructor for an AJAX PDB object. XX PN [1] PA d Ppdb AjPPdb* PD AJAX PDB address PX RT void RD RX CA delete CT AjPPdb CD Default AJAX PDB destructor. CX // void ajPdbDel(AjPPdb *Ppdb) { ajuint i = 0U; AjPAtom atom = NULL; AjPPdb pdb = NULL; if (!Ppdb || !*Ppdb) return; pdb = *Ppdb; ajStrDel(&pdb->Pdb); ajStrDel(&pdb->Compnd); ajStrDel(&pdb->Source); ajChararrDel(&pdb->gpid); while (ajListPop(pdb->Water, (void **) &atom)) ajAtomDel(&atom); ajListFree(&pdb->Water); while (ajListPop(pdb->Groups, (void **) &atom)) ajAtomDel(&atom); ajListFree(&pdb->Groups); for (i = 0U; i < pdb->Nchn; i++) ajChainDel(&pdb->Chains[i]); AJFREE(pdb->Chains); AJFREE(pdb); *Ppdb = NULL; return; } ID ajHetentDel TY public MO ajpdb LB core XX DE Destructor for an AJAX Hetent object. XX PN [1] PA d Phetent AjPHetent* PD AJAX Hetent address PX RT void RD RX CA delete CT AjPHetent CD Default AJAX Hetent destructor. CX // void ajHetentDel(AjPHetent *Phetent) { AjPHetent hetent = NULL; if (!Phetent || !*Phetent) return; hetent = *Phetent; ajStrDel(&hetent->abv); ajStrDel(&hetent->syn); ajStrDel(&hetent->ful); AJFREE(hetent); *Phetent = NULL; return; } ID ajHetDel TY public MO ajpdb LB core XX DE Destructor for an AJAX Het object. XX PN [1] PA d Phet AjPHet* PD AJAX Het address PX RT void RD RX CA delete CT AjPHet CD Default AJAX Het destructor. CX // void ajHetDel(AjPHet *Phet) { ajuint i = 0U; AjPHet het = NULL; if (!Phet || !*Phet) return; het = *Phet; if (het->Entries) { for (i = 0U; i < het->Number; i++) ajHetentDel(&het->Entries[i]); AJFREE(het->Entries); } AJFREE(het); *Phet = NULL; return; } ID ajVdwallDel TY public MO ajpdb LB core XX DE Destructor for an AJAX Vdwall object. XX PN [1] PA d Pvdwall AjPVdwall* PD AJAX Vdwall address PX RT void RD RX CA delete CT AjPVdwall CD Default AJAX Vdwall destructor. CX // void ajVdwallDel(AjPVdwall *Pvdwall) { ajuint i = 0U; AjPVdwall vdwall = NULL; if(!Pvdwall || !*Pvdwall) return; vdwall = *Pvdwall; for (i = 0U; i < vdwall->N; i++) ajVdwresDel(&vdwall->Res[i]); AJFREE(vdwall->Res); AJFREE(vdwall); *Pvdwall = NULL; return; } ID ajVdwresDel TY public MO ajpdb LB core XX DE Destructor for an AJAX Vdwres object. XX PN [1] PA d Pvdwres AjPVdwres* PD AJAX Vdwres address PX RT void RD RX CA delete CT AjPVdwres CD Default AJAX Vdwres destructor. CX // void ajVdwresDel(AjPVdwres *Pvdwres) { ajuint i = 0U; AjPVdwres vdwres = NULL; if(!Pvdwres || !*Pvdwres) return; vdwres = *Pvdwres; ajStrDel(&vdwres->Id3); for (i = 0U; i < vdwres->N; i++) ajStrDel(&vdwres->Atm[i]); AJFREE(vdwres->Atm); AJFREE(vdwres->Rad); AJFREE(vdwres); *Pvdwres = NULL; return; } ID ajCmapDel TY public MO ajpdb LB core XX DE Destructor for an AJAX Cmap object. XX PN [1] PA d Pcmap AjPCmap* PD AJAX Cmap address PX RT void RD RX CA delete CT AjPCmap CD Default AJAX Cmap destructor. CX // void ajCmapDel(AjPCmap *Pcmap) { AjPCmap cmap = NULL; if (!Pcmap || !*Pcmap) return; cmap = *Pcmap; ajStrDel(&cmap->Id); ajStrDel(&cmap->Domid); ajStrDel(&cmap->Ligid); ajStrDel(&cmap->Seq1); ajStrDel(&cmap->Seq2); ajStrDel(&cmap->Desc); ajUint2dDel(&cmap->Mat); AJFREE(cmap); *Pcmap = NULL; return; } ID ajPdbtospDel TY public MO ajpdb LB core XX DE Destructor for an AJAX Pdbtosp object. XX PN [1] PA d Ppdbtosp AjPPdbtosp* PD AJAX Pdbtosp address PX RT void RD RX CA delete CT AjPPdbtosp CD Default AJAX Pdbtosp destructor. CX // void ajPdbtospDel(AjPPdbtosp *Ppdbtosp) { ajuint i = 0U; AjPPdbtosp pdbtosp = NULL; if (!Ppdbtosp || !*Ppdbtosp) return; pdbtosp = *Ppdbtosp; ajStrDel(&pdbtosp->Pdb); if (pdbtosp->Number) { for (i = 0U; i < pdbtosp->Number; i++) { ajStrDel(&pdbtosp->Acc[i]); ajStrDel(&pdbtosp->Spr[i]); } AJFREE(pdbtosp->Acc); AJFREE(pdbtosp->Spr); } AJFREE(pdbtosp); *Ppdbtosp = NULL; return; } ID ajAtomCopy TY public MO ajpdb LB core XX DE Copies the data from an AJAX Atom object to an AJAX Atom object; DE the new object is created if needed. DE DE IMPORTANT - THIS DOES NOT COPY THE eNum & eType ELEMENTS, WHICH ARE SET DE TO ZERO and '.' INSTEAD. XX PN [1] PA w Pto AjPAtom* PD AJAX Atom address PX PN [2] PA r from const AjPAtom PD AJAX Atom PX RT AjBool RD True on success RX // AjBool ajAtomCopy(AjPAtom *Pto, const AjPAtom from) { if (!Pto) { ajWarn("Bad arg (NULL) passed to ajAtomCopy"); return ajFalse; } if (!*Pto) *Pto = ajAtomNew(); (*Pto)->Mod = from->Mod; (*Pto)->Chn = from->Chn; (*Pto)->Gpn = from->Gpn; (*Pto)->Idx = from->Idx; ajStrAssignS(&((*Pto)->Pdb), from->Pdb); (*Pto)->Id1 = from->Id1; ajStrAssignS(&((*Pto)->Id3), from->Id3); (*Pto)->Type = from->Type; ajStrAssignS(&((*Pto)->Atm), from->Atm); (*Pto)->X = from->X; (*Pto)->Y = from->Y; (*Pto)->Z = from->Z; (*Pto)->O = from->O; (*Pto)->B = from->B; return ajTrue; } ID ajResidueCopy TY public MO ajpdb LB core XX DE Copies the data from a Residue object to an Residue object; the new object DE is created if needed. DE DE IMPORTANT - THIS DOES NOT COPY THE eNum & eType ELEMENTS, WHICH ARE SET DE TO ZERO and '.' INSTEAD. XX PN [1] PA w Pto AjPResidue* PD AJAX Residue address PX PN [2] PA r from const AjPResidue PD AJAX Residue PX RT AjBool RD True on success RX // AjBool ajResidueCopy(AjPResidue *Pto, const AjPResidue from) { if (!Pto) { ajWarn("Bad arg (NULL) passed to ajResidueCopy"); return ajFalse; } if (!*Pto) *Pto = ajResidueNew(); (*Pto)->Mod = from->Mod; (*Pto)->Chn = from->Chn; (*Pto)->Idx = from->Idx; ajStrAssignS(&((*Pto)->Pdb), from->Pdb); (*Pto)->Id1 = from->Id1; ajStrAssignS(&((*Pto)->Id3), from->Id3); (*Pto)->Phi = from->Phi; (*Pto)->Psi = from->Psi; (*Pto)->Area = from->Area; (*Pto)->eNum = from->eNum; ajStrAssignS(&((*Pto)->eId), from->eId); (*Pto)->eType = from->eType; (*Pto)->eClass = from->eClass; (*Pto)->eStrideNum = from->eStrideNum; (*Pto)->eStrideType = from->eStrideType; (*Pto)->all_abs = from->all_abs; (*Pto)->all_rel = from->all_rel; (*Pto)->side_abs = from->side_abs; (*Pto)->side_rel = from->side_rel; (*Pto)->main_abs = from->main_abs; (*Pto)->main_rel = from->main_rel; (*Pto)->npol_abs = from->npol_abs; (*Pto)->npol_rel = from->npol_rel; (*Pto)->pol_abs = from->pol_abs; (*Pto)->pol_rel = from->pol_rel; return ajTrue; } ID ajAtomListCopy TY public MO ajpdb LB core XX DE Read a list of Atom structures and writes a copy of the list. The DE data are copied and the list is created if necessary. XX PN [1] PA w Pto AjPList* PD AJAX List of AJAX Atom objects to write PX PN [2] PA r from const AjPList PD List of Atom objects to read PX RT AjBool RD True if list was copied ok. RX // AjBool ajAtomListCopy(AjPList *Pto, const AjPList from) { AjIList iter = NULL; AjPAtom hit = NULL; AjPAtom new = NULL; /* Check arg's */ if (!from || !Pto) { ajWarn("Bad arg's passed to ajAtomListCopy\n"); return ajFalse; } /* Allocate the new list, if necessary */ if (!*Pto) *Pto = ajListNew(); /* Initialise the iterator */ iter = ajListIterNewread(from); /* Iterate through the list of Atom objects */ while ((hit = (AjPAtom) ajListIterGet(iter))) { new = ajAtomNew(); ajAtomCopy(&new, hit); /* Push scophit onto list */ ajListPushAppend(*Pto, new); } ajListIterDel(&iter); return ajTrue; } ID ajResidueListCopy TY public MO ajpdb LB core XX DE Read a list of Residue structures and writes a copy of the list. The DE data are copied and the list is created if necessary. XX PN [1] PA w to AjPList* PD List of Residue objects to write PX PN [2] PA r from const AjPList PD List of Residue objects to read PX RT AjBool RD True if list was copied ok. RX // AjBool ajResidueListCopy(AjPList *to, const AjPList from) { AjIList iter = NULL; AjPResidue hit = NULL; AjPResidue new = NULL; /* Check arg's */ if (!from || !to) { ajWarn("Bad arg's passed to ajResidueListCopy\n"); return ajFalse; } /* Allocate the new list, if necessary */ if (!(*to)) *to = ajListNew(); /* Initialise the iterator */ iter = ajListIterNewread(from); /* Iterate through the list of Atom objects */ while ((hit = (AjPResidue) ajListIterGet(iter))) { new = ajResidueNew(); ajResidueCopy(&new, hit); /* Push scophit onto list */ ajListPushAppend(*to, new); } ajListIterDel(&iter); return ajTrue; } ID ajPdbCopy TY public MO ajpdb LB core XX DE Copies data from one AHAX PDB object to another; the new object is DE always created. XX PN [1] PA w Pto AjPPdb* PD AJAX PDB address PX PN [2] PA r from const AjPPdb PD AJAX PDB PX RT AjBool RD True on success RX // AjBool ajPdbCopy(AjPPdb *Pto, const AjPPdb from) { ajuint i = 0U; if (!from) { ajWarn("NULL arg passed to ajPdbCopy"); return ajFalse; } if ((*Pto)) { ajWarn("Pointer passed to ajPdbCopy should be NULL but isn't !"); return ajFalse; } /* Copy data in Pdb object */ (*Pto) = ajPdbNew(from->Nchn); ajStrAssignS(&((*Pto)->Pdb), from->Pdb); ajStrAssignS(&((*Pto)->Compnd), from->Compnd); ajStrAssignS(&((*Pto)->Source), from->Source); (*Pto)->Method = from->Method; (*Pto)->Reso = from->Reso; (*Pto)->Nmod = from->Nmod; (*Pto)->Nchn = from->Nchn; (*Pto)->Ngp = from->Ngp; for (i = 0U; i < from->Ngp; i++) ajChararrPut(&((*Pto)->gpid), i, ajChararrGet(from->gpid, i)); ajListFree(&((*Pto)->Groups)); ajListFree(&((*Pto)->Water)); #if AJFALSE (*Pto)->Groups = ajAtomListCopy(from->Groups); (*Pto)->Water = ajAtomListCopy(from->Water); #endif if (!ajAtomListCopy(&(*Pto)->Groups, from->Groups)) ajFatal("Error copying Groups list"); if (!ajAtomListCopy(&(*Pto)->Water, from->Water)) ajFatal("Error copying Water list"); /* Copy data in Chain objects */ for (i = 0; i < from->Nchn; i++) { ajListFree(&((*Pto)->Chains[i]->Atoms)); (*Pto)->Chains[i]->Id = from->Chains[i]->Id; (*Pto)->Chains[i]->Nres = from->Chains[i]->Nres; (*Pto)->Chains[i]->Nlig = from->Chains[i]->Nlig; (*Pto)->Chains[i]->numHelices = from->Chains[i]->numHelices; (*Pto)->Chains[i]->numStrands = from->Chains[i]->numStrands; ajStrAssignS(&((*Pto)->Chains[i]->Seq), from->Chains[i]->Seq); #if AJFALSE (*Pto)->Chains[i]->Atoms = ajAtomListCopy(from->Chains[i]->Atoms); #endif if (!ajAtomListCopy(&(*Pto)->Chains[i]->Atoms, from->Chains[i]->Atoms)) ajFatal("Error copying Atoms list"); if (!ajResidueListCopy(&(*Pto)->Chains[i]->Residues, from->Chains[i]->Residues)) ajFatal("Error copying Residues list"); } return ajTrue; } ID ajResidueSSEnv TY public MO ajpdb LB core XX DE Assigns secondary structure environment of an AJAX Residue XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA w SEnv char* PD Character for the Secondary structure environment PX PN [3] PA w flog AjPFile PD Log file PX RT AjBool RD ajTrue on success RX // AjBool ajResidueSSEnv(const AjPResidue residue, char *SEnv, AjPFile flog) { *SEnv = '\0'; ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f\n", residue->Id1, residue->Idx, residue->eStrideType, residue->side_rel); if (residue->eStrideType == 'H' || residue->eStrideType == 'G') *SEnv = 'H'; else if (residue->eStrideType == 'E' || residue->eStrideType == 'B' || residue->eStrideType == 'b') *SEnv = 'S'; else if (residue->eStrideType == 'T' || residue->eStrideType == 'C' || residue->eStrideType == 'I') *SEnv = 'C'; /* If no stride assignment, get pdb assignment */ else if (residue->eStrideType == '.') { if (residue->eType == 'H') *SEnv = 'H'; else if (residue->eType == 'E') *SEnv = 'S'; else if (residue->eType == 'C' || residue->eType == 'T') *SEnv = 'C'; else if (residue->eType == '.') { ajFmtPrintF(flog, "SEnv unknown for residue %d\n", residue->Idx); /* *SEnv='C'; */ *SEnv = '\0'; return ajFalse; } } return ajTrue; } ID ajResidueEnv1 TY public MO ajpdb LB core XX DE Assigns environment based only of side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv1(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { if (SEnv == '\0') { ajStrSetClear(OEnv); return 0; } if ((residue->side_rel <= 15) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((residue->side_rel <= 15) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((residue->side_rel <= 15) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((residue->side_rel > 15) && (residue->side_rel <= 30) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((residue->side_rel > 15) && (residue->side_rel <= 30) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((residue->side_rel > 15) && (residue->side_rel <= 30) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((residue->side_rel > 30) && (residue->side_rel <= 45) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((residue->side_rel > 30) && (residue->side_rel <= 45) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((residue->side_rel > 30) && (residue->side_rel <= 45) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((residue->side_rel > 45) && (residue->side_rel <= 60) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((residue->side_rel > 45) && (residue->side_rel <= 60) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((residue->side_rel > 45) && (residue->side_rel <= 60) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((residue->side_rel > 60) && (residue->side_rel <= 75) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((residue->side_rel > 60) && (residue->side_rel <= 75) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((residue->side_rel > 60) && (residue->side_rel <= 75) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((residue->side_rel > 75) && (residue->side_rel <= 90) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((residue->side_rel > 75) && (residue->side_rel <= 90) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((residue->side_rel > 75) && (residue->side_rel <= 90) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((residue->side_rel > 90) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((residue->side_rel > 90) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((residue->side_rel > 90) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); return 0; } /* The total number of environments */ return 21; } ID ajResidueEnv2 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv2(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float BLimit = 40.0F; /* Upper limit for the relative solvent * accessible area for a buried residue */ float PBLimit = 114.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float HLimit = 45.0F; /* Upper limit for the fraction polar measure * of a Hydrophobic residue */ float MPLimit = 67.0F; /* Upper limit for the fraction polar measure * of a Moderately polar residue */ AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); /* Assign the basic classes */ /* Buried, Hydrophobic */ if ((residue->side_rel <= BLimit) && (residue->pol_rel <= HLimit)) ajStrAssignC(&BEnv, "B1"); /* buried, moderately polar */ else if ((residue->side_rel <= BLimit) && (residue->pol_rel > HLimit) && (residue->pol_rel <= MPLimit)) ajStrAssignC(&BEnv, "B2"); /* buried, polar */ else if ((residue->side_rel <= BLimit) && (residue->pol_rel > MPLimit)) ajStrAssignC(&BEnv, "B3"); /* Partially buried, moderately Polar */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel <= MPLimit)) ajStrAssignC(&BEnv, "P1"); /* Partially buried, Polar */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > MPLimit)) ajStrAssignC(&BEnv, "P2"); /* Exposed */ else if (residue->side_rel > PBLimit) ajStrAssignC(&BEnv, "E"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 18; } ID ajResidueEnv3 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv3(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float BLimit = 5.0F; /* Upper limit for the relative solvent * accessible area for a buried residue */ float PBLimit = 25.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float PolLimit1 = 10.0F; float PolLimit2 = 30.0F; float PolLimit3 = 50.0F; float PolLimit4 = 80.0F; AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); /* Assign the basic classes */ /* Buried, Hydrophobic */ if ((residue->side_rel <= BLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "B1"); /* buried, moderately polar */ else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit2)) ajStrAssignC(&BEnv, "B2"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit2) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "B3"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit3) && (residue->pol_rel <= PolLimit4)) ajStrAssignC(&BEnv, "B4"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit4)) ajStrAssignC(&BEnv, "B5"); /* Partially buried, moderately Polar */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "P1"); /* Partially buried, Polar */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit3)) ajStrAssignC(&BEnv, "P2"); /* Exposed */ else if (residue->side_rel > PBLimit) ajStrAssignC(&BEnv, "E"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajResidueEnv4 TY public MO ajpdb LB core XX DE Assigns environment based only of side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv4(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { ajStrSetClear(OEnv); if (SEnv == '\0') { ajStrSetClear(OEnv); return 0; } if ((residue->side_rel <= 5) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((residue->side_rel <= 5) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((residue->side_rel <= 5) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((residue->side_rel > 5) && (residue->side_rel <= 25) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((residue->side_rel > 5) && (residue->side_rel <= 25) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((residue->side_rel > 5) && (residue->side_rel <= 25) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((residue->side_rel > 25) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((residue->side_rel > 25) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((residue->side_rel > 25) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); return 0; } return 9; } ID ajResidueEnv5 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv5(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float BLimit = 5.0F; /* Upper limit for the relative solvent * accessible area for a buried residue */ float PBLimit = 25.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float PolLimit1 = 10.0F; float PolLimit2 = 30.0F; float PolLimit3 = 50.0F; float PolLimit4 = 80.0F; AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); /* Assign the basic classes */ /* Buried */ if (residue->side_rel <= BLimit) ajStrAssignC(&BEnv, "B"); /* partially buried, hydrophobic */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "P1"); /* partially buried, moderately polar */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit2)) ajStrAssignC(&BEnv, "P2"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit2) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "P3"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit3) && (residue->pol_rel <= PolLimit4)) ajStrAssignC(&BEnv, "P4"); /* partially buried, polar */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit4)) ajStrAssignC(&BEnv, "P5"); /* Exposed, moderately Polar */ else if ((residue->side_rel > PBLimit) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "E1"); /* Exposed, Polar */ else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit3)) ajStrAssignC(&BEnv, "E2"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "P3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "P3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "P3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "P4")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "P4")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "P4")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "P5")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "P5")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "P5")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajResidueEnv6 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv6(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float BLimit = 5.0F; /* Upper limit for the relative solvent * accessible area for a buried residue */ float PBLimit = 25.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float PolLimit1 = 10.0F; float PolLimit2 = 30.0F; float PolLimit3 = 50.0F; float PolLimit4 = 80.0F; AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); /* Assign the basic classes */ /* Buried */ if (residue->side_rel <= BLimit) ajStrAssignC(&BEnv, "B"); /* Partially buried, hydrophobic */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "P1"); /* Partially buried, Polar */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit3)) ajStrAssignC(&BEnv, "P2"); /* partially buried, hydrophobic */ else if ((residue->side_rel > PBLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "E1"); /* partially buried, moderately polar */ else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit2)) ajStrAssignC(&BEnv, "E2"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit2) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "E3"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit3) && (residue->pol_rel <= PolLimit4)) ajStrAssignC(&BEnv, "E4"); /* partially buried, polar */ else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit4)) ajStrAssignC(&BEnv, "E5"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "E3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "E3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "E3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "E4")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "E4")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "E4")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E5")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E5")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E5")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajResidueEnv7 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv7(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float BLimit = 5.0F; /* Upper limit for the relative solvent * accessible area for a buried residue */ float PBLimit = 25.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float PolLimit1 = 10.0F; float PolLimit2 = 50.0F; float PolLimit3 = 90.0F; AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); ajStrDel(&BEnv); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); /* Assign the basic classes */ /* Buried */ if ((residue->side_rel <= BLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "B1"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "B2"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit3)) ajStrAssignC(&BEnv, "B3"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit)) ajStrAssignC(&BEnv, "P"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "E1"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit2)) ajStrAssignC(&BEnv, "E2"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit2) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "E3"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit3)) ajStrAssignC(&BEnv, "E4"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "P")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "P")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "P")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "E3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "E3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "E3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E4")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E4")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E4")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajResidueEnv8 TY public MO ajpdb LB core XX DE Assigns environment based only of side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv8(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { ajStrSetClear(OEnv); if (SEnv == '\0') { ajStrSetClear(OEnv); return 0; } if ((residue->pol_rel <= 15) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((residue->pol_rel <= 15) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((residue->pol_rel <= 15) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((residue->pol_rel > 15) && (residue->pol_rel <= 30) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((residue->pol_rel > 15) && (residue->pol_rel <= 30) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((residue->pol_rel > 15) && (residue->pol_rel <= 30) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((residue->pol_rel > 30) && (residue->pol_rel <= 45) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((residue->pol_rel > 30) && (residue->pol_rel <= 45) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((residue->pol_rel > 30) && (residue->pol_rel <= 45) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((residue->pol_rel > 45) && (residue->pol_rel <= 60) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((residue->pol_rel > 45) && (residue->pol_rel <= 60) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((residue->pol_rel > 45) && (residue->pol_rel <= 60) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((residue->pol_rel > 60) && (residue->pol_rel <= 75) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((residue->pol_rel > 60) && (residue->pol_rel <= 75) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((residue->pol_rel > 60) && (residue->pol_rel <= 75) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((residue->pol_rel > 75) && (residue->pol_rel <= 90) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((residue->pol_rel > 75) && (residue->pol_rel <= 90) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((residue->pol_rel > 75) && (residue->pol_rel <= 90) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((residue->pol_rel > 90) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((residue->pol_rel > 90) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((residue->pol_rel > 90) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); return 0; } return 21; } ID ajResidueEnv9 TY public MO ajpdb LB core XX DE Assigns environment based only of side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv9(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { ajStrSetClear(OEnv); if (SEnv == '\0') { ajStrSetClear(OEnv); return 0; } if ((residue->pol_rel <= 5) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((residue->pol_rel <= 5) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((residue->pol_rel <= 5) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((residue->pol_rel > 5) && (residue->pol_rel <= 25) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((residue->pol_rel > 5) && (residue->pol_rel <= 25) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((residue->pol_rel > 5) && (residue->pol_rel <= 25) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((residue->pol_rel > 25) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((residue->pol_rel > 25) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((residue->pol_rel > 25) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); return 0; } return 9; } ID ajResidueEnv10 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv10(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float BLimit = 5.0F; /* Upper limit for the relative solvent * accessible area for a buried residue */ float PBLimit = 25.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float HLimit = 5.0F; /* Upper limit for the fraction polar measure * of a Hydrophobic residue */ float MPLimit = 25.0F; /* Upper limit for the fraction polar measure * of a Moderately polar residue */ AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); /* Assign the basic classes */ /* Buried, Hydrophobic */ if ((residue->side_rel <= BLimit) && (residue->pol_rel <= HLimit)) ajStrAssignC(&BEnv, "B1"); /* buried, moderately polar */ else if ((residue->side_rel <= BLimit) && (residue->pol_rel > HLimit) && (residue->pol_rel <= MPLimit)) ajStrAssignC(&BEnv, "B2"); /* buried, polar */ else if ((residue->side_rel <= BLimit) && (residue->pol_rel > MPLimit)) ajStrAssignC(&BEnv, "B3"); /* Partially buried, moderately hydrophobic */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel <= HLimit)) ajStrAssignC(&BEnv, "P1"); /* Partially buried, moderately polar */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > HLimit) && (residue->pol_rel <= MPLimit)) ajStrAssignC(&BEnv, "P2"); /* Partially buried, polar */ else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > MPLimit)) ajStrAssignC(&BEnv, "P3"); /* Exposed, moderately polar */ else if ((residue->side_rel > PBLimit) && (residue->pol_rel <= MPLimit)) ajStrAssignC(&BEnv, "E1"); /* Exposed, polar */ else if ((residue->side_rel > PBLimit) && (residue->pol_rel > MPLimit)) ajStrAssignC(&BEnv, "E2"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "P3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "P3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "P3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajResidueEnv11 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv11(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float BLimit = 5.0F; /* Upper limit for the relative solvent * accessible area for a buried residue */ float PBLimit = 25.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float PolLimit1 = 10.0F; float PolLimit2 = 30.0F; float PolLimit3 = 50.0F; float PolLimit4 = 70.0F; float PolLimit5 = 90.0F; AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); /* Assign the basic classes */ if ((residue->side_rel <= BLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "B1"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit2)) ajStrAssignC(&BEnv, "B2"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit2) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "B3"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit3) && (residue->pol_rel <= PolLimit4)) ajStrAssignC(&BEnv, "B4"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit4) && (residue->pol_rel <= PolLimit5)) ajStrAssignC(&BEnv, "B5"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit5)) ajStrAssignC(&BEnv, "B6"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit)) ajStrAssignC(&BEnv, "P"); else if (residue->side_rel > PBLimit) ajStrAssignC(&BEnv, "E"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "P")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "P")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "P")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajResidueEnv12 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv12(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float BLimit = 5.0F; /* Upper limit for the relative solvent * accessible area for a buried residue */ float PBLimit = 25.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float PolLimit1 = 10.0F; float PolLimit2 = 30.0F; float PolLimit3 = 50.0F; float PolLimit4 = 70.0F; float PolLimit5 = 90.0F; AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); /* Assign the basic classes */ if ((residue->side_rel <= BLimit)) ajStrAssignC(&BEnv, "B"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "P1"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit2)) ajStrAssignC(&BEnv, "P2"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit2) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "P3"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit3) && (residue->pol_rel <= PolLimit4)) ajStrAssignC(&BEnv, "P4"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit4) && (residue->pol_rel <= PolLimit5)) ajStrAssignC(&BEnv, "P5"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit5)) ajStrAssignC(&BEnv, "P6"); else if (residue->side_rel > PBLimit) ajStrAssignC(&BEnv, "E"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "P1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "P2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "P3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "P3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "P3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "P4")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "P4")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "P4")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "P5")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "P5")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "P5")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "P6")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "P6")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "P6")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajResidueEnv13 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv13(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float BLimit = 5.0F; /* Upper limit for the relative solvent * accessible area for a buried residue */ float PBLimit = 25.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float PolLimit1 = 10.0F; float PolLimit2 = 30.0F; float PolLimit3 = 50.0F; float PolLimit4 = 70.0F; float PolLimit5 = 90.0F; AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); /* Assign the basic classes */ if ((residue->side_rel <= BLimit)) ajStrAssignC(&BEnv, "B"); else if ((residue->side_rel > BLimit) && (residue->side_rel <= PBLimit)) ajStrAssignC(&BEnv, "P"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "E1"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit2)) ajStrAssignC(&BEnv, "E2"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit2) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "E3"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit3) && (residue->pol_rel <= PolLimit4)) ajStrAssignC(&BEnv, "E4"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit4) && (residue->pol_rel <= PolLimit5)) ajStrAssignC(&BEnv, "E5"); else if ((residue->side_rel > PBLimit) && (residue->pol_rel > PolLimit5)) ajStrAssignC(&BEnv, "E6"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "P")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "P")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "P")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "E1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "E2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "E3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "E3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "E3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "E4")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "E4")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "E4")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "E5")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "E5")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "E5")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E6")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E6")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E6")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajResidueEnv14 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv14(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float PBLimit = 25.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float PolLimit1 = 5.0F; float PolLimit2 = 15.0F; float PolLimit3 = 25.0F; float PolLimit4 = 40.0F; float PolLimit5 = 60.0F; float PolLimit6 = 80.0F; AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); if ((residue->side_rel <= PBLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "B1"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit2)) ajStrAssignC(&BEnv, "B2"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit2) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "B3"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit3) && (residue->pol_rel <= PolLimit4)) ajStrAssignC(&BEnv, "B4"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit4) && (residue->pol_rel <= PolLimit5)) ajStrAssignC(&BEnv, "B5"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit5) && (residue->pol_rel <= PolLimit6)) ajStrAssignC(&BEnv, "B6"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit6)) ajStrAssignC(&BEnv, "B7"); else if ((residue->side_rel > PBLimit)) ajStrAssignC(&BEnv, "E"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "B7")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "B7")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "B7")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajResidueEnv15 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv15(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float PBLimit = 25.0F; /* Upper limit for the relative solvent * accessible area for a Partially buried * residue */ float PolLimit1 = 5.0F; float PolLimit2 = 15.0F; float PolLimit3 = 25.0F; float PolLimit4 = 35.0F; float PolLimit5 = 45.0F; float PolLimit6 = 75.0F; AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); if ((residue->side_rel <= PBLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "B1"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit2)) ajStrAssignC(&BEnv, "B2"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit2) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "B3"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit3) && (residue->pol_rel <= PolLimit4)) ajStrAssignC(&BEnv, "B4"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit4) && (residue->pol_rel <= PolLimit5)) ajStrAssignC(&BEnv, "B5"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit5) && (residue->pol_rel <= PolLimit6)) ajStrAssignC(&BEnv, "B6"); else if ((residue->side_rel <= PBLimit) && (residue->pol_rel > PolLimit6)) ajStrAssignC(&BEnv, "B7"); else if ((residue->side_rel > PBLimit)) ajStrAssignC(&BEnv, "E"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "B7")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "B7")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "B7")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajResidueEnv16 TY public MO ajpdb LB core XX DE Assigns environment based on side chain accessibility and secondary DE structure. Assigns environment of "*" as default. XX PN [1] PA r residue const AjPResidue PD AJAX Residue PX PN [2] PA r SEnv char PD Secondary structure environment code PX PN [3] PA w OEnv AjPStr* PD Character for the overall environment class PX PN [4] PA w flog AjPFile PD Log file PX RT ajint RD Number of environments RX // ajint ajResidueEnv16(const AjPResidue residue, char SEnv, AjPStr *OEnv, AjPFile flog) { float BLimit = 5.0F; /* Upper limit for the relative solvent * accessible area for a buried residue */ float PolLimit1 = 5.0F; float PolLimit2 = 15.0F; float PolLimit3 = 25.0F; float PolLimit4 = 35.0F; float PolLimit5 = 45.0F; float PolLimit6 = 75.0F; AjPStr BEnv = NULL; if (!residue) { ajWarn("No residue to ajResidueEnv"); return 0; } ajStrSetClear(OEnv); BEnv = ajStrNew(); ajFmtPrintF(flog, "R:%c-%d S:%c A:%.2f f:%.2f\n", residue->Id1, residue->Idx, residue->eType, residue->side_rel, residue->pol_rel); if ((residue->side_rel <= BLimit) && (residue->pol_rel <= PolLimit1)) ajStrAssignC(&BEnv, "B1"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit1) && (residue->pol_rel <= PolLimit2)) ajStrAssignC(&BEnv, "B2"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit2) && (residue->pol_rel <= PolLimit3)) ajStrAssignC(&BEnv, "B3"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit3) && (residue->pol_rel <= PolLimit4)) ajStrAssignC(&BEnv, "B4"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit4) && (residue->pol_rel <= PolLimit5)) ajStrAssignC(&BEnv, "B5"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit5) && (residue->pol_rel <= PolLimit6)) ajStrAssignC(&BEnv, "B6"); else if ((residue->side_rel <= BLimit) && (residue->pol_rel > PolLimit6)) ajStrAssignC(&BEnv, "B7"); else if ((residue->side_rel > BLimit)) ajStrAssignC(&BEnv, "E"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "BEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } /* Assign overall environment class */ if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AA"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AB"); else if ((ajStrMatchC(BEnv, "B1")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AC"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AD"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AE"); else if ((ajStrMatchC(BEnv, "B2")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AF"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AG"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AH"); else if ((ajStrMatchC(BEnv, "B3")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AI"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AJ"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AK"); else if ((ajStrMatchC(BEnv, "B4")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AL"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AM"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AN"); else if ((ajStrMatchC(BEnv, "B5")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AO"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AP"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AQ"); else if ((ajStrMatchC(BEnv, "B6")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AR"); else if ((ajStrMatchC(BEnv, "B7")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AS"); else if ((ajStrMatchC(BEnv, "B7")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AT"); else if ((ajStrMatchC(BEnv, "B7")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AU"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'H')) ajStrAssignC(OEnv, "AV"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'S')) ajStrAssignC(OEnv, "AW"); else if ((ajStrMatchC(BEnv, "E")) && (SEnv == 'C')) ajStrAssignC(OEnv, "AX"); else { ajStrSetClear(OEnv); ajFmtPrintF(flog, "OEnv unassigned for residue %d\n", residue->Idx); ajStrDel(&BEnv); return 0; } ajStrDel(&BEnv); return 24; } ID ajPdbGetEStrideType TY public MO ajpdb LB core XX DE Reads a Pdb object and writes a string with the secondary structure. The DE string that is written is the same length as the full-length chain DE (regardless of whether coordinates for a residue were available or not) DE therefore it can be indexed into using residue numbers. The string is DE allocated if necessary. If secondary structure assignment was not available DE for a residue a '.' is given in the string. XX PN [1] PA r pdb const AjPPdb PD AJAX PDB object PX PN [2] PA r chn ajuint PD Chain number PX PN [3] PA w EStrideType AjPStr* PD String to hold secondary structure PX RT ajint RD Length (residues) of array that was written or -1 (for RD an error) RX // ajint ajPdbGetEStrideType(const AjPPdb pdb, ajuint chn, AjPStr *EStrideType) { AjPResidue residue = NULL; AjIList iter = NULL; ajuint idx = 0U; if (!pdb || !EStrideType || (chn < 1)) { ajWarn("Bad args passed to ajPdbGetEStrideType"); return -1; } if (chn > pdb->Nchn) { ajWarn("chn arg in ajPdbGetEStrideType exceeds no. chains"); return -1; } else idx = chn - 1U; /* +1 is for the NULL */ if (!*EStrideType) *EStrideType = ajStrNewRes(pdb->Chains[idx]->Nres + 1U); else { ajStrDel(EStrideType); *EStrideType = ajStrNewRes(pdb->Chains[idx]->Nres + 1U); } /* Set all positions to . */ ajStrAppendCountK(EStrideType, '.', pdb->Chains[idx]->Nres); iter = ajListIterNewread(pdb->Chains[idx]->Residues); while ((residue = (AjPResidue) ajListIterGet(iter))) (*EStrideType)->Ptr[residue->Idx - 1] = residue->eStrideType; ajListIterDel(&iter); return pdb->Chains[idx]->Nres; } ID ajPdbChnidToNum TY public MO ajpdb LB core XX DE Finds the chain number for a given chain identifier in a pdb structure XX PN [1] PA r identifier char PD Chain identifier PX PN [2] PA r pdb const AjPPdb PD Pdb object PX PN [3] PA w chn ajuint* PD Chain number PX RT AjBool RD True on success RX // AjBool ajPdbChnidToNum(char identifier, const AjPPdb pdb, ajuint *chn) { ajuint i = 0U; for (i = 0U; i < pdb->Nchn; i++) { if (toupper((int) pdb->Chains[i]->Id) == toupper((int) identifier)) { *chn = i + 1; return ajTrue; } /* ** Cope with chain id's of ' ' (which might be given as '.' in ** the Pdb object) */ if ((identifier == ' ') && (pdb->Chains[i]->Id == '.')) { *chn = i + 1; return ajTrue; } } /* ** A '.' may be given as the id for domains comprising more than one ** chain */ if (identifier == '.') { *chn = 1; return ajTrue; } return ajFalse; } ID ajPdbtospArrFindPdbid TY public MO ajpdb LB core XX DE Performs a binary search for a PDB code over an array of Pdbtosp DE structures (which of course must first have been sorted). This is a DE case-insensitive search. XX PN [1] PA r array AjPPdbtosp const* PD Array of AjOPdbtosp objects PX PN [2] PA r size ajint PD Size of array PX PN [3] PA r identifier const AjPStr PD Search term PX RT ajint RD Index of first Pdbtosp object found with an PDB code RD matching id, or -1 if id is not found. RX // ajint ajPdbtospArrFindPdbid(AjPPdbtosp const *array, ajint size, const AjPStr identifier) { ajint l = 0; ajint m = 0; ajint h = size - 1; ajint c = 0; while (l <= h) { m = (l + h) >> 1; if ((c = ajStrCmpCaseS(identifier, array[m]->Pdb)) < 0) h = m - 1; else if (c > 0) l = m + 1; else return m; } return -1; } ID ajPdbWriteAll TY public MO ajpdb LB core XX DE Writes a clean coordinate file for a protein. XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD AJAX PDB object PX RT AjBool RD True on success RX CA output CT AjPPdb CD Writes a ccf-format file for a protein. CX // AjBool ajPdbWriteAll(AjPFile outf, const AjPPdb pdb) { ajuint i = 0U; ajuint j = 0U; AjIList iter = NULL; AjPAtom atom = NULL; AjPResidue residue = NULL; AjPSeqout outseq = NULL; /* Write the header information */ ajFmtPrintF(outf, "%-5s%S\n", "ID", pdb->Pdb); ajFmtPrintF(outf, "XX\n"); ajFmtPrintSplit(outf, pdb->Compnd, "DE ", 75, " \t\r\n"); ajFmtPrintF(outf, "XX\n"); ajFmtPrintSplit(outf, pdb->Source, "OS ", 75, " \t\r\n"); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5sMETHOD ", "EX"); if (pdb->Method == ajEPdbMethodXray) ajFmtPrintF(outf, "xray; "); else ajFmtPrintF(outf, "nmr_or_model; "); ajFmtPrintF(outf, "RESO %.2f; NMOD %d; NCHN %d; NGRP %d;\n", pdb->Reso, pdb->Nmod, pdb->Nchn, pdb->Ngp); /* Write chain-specific information */ for (i = 0U; i < pdb->Nchn; i++) { ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5s[%d]\n", "CN", i + 1); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5s", "IN"); if (pdb->Chains[i]->Id == ' ') ajFmtPrintF(outf, "ID %c; ", '.'); else ajFmtPrintF(outf, "ID %c; ", pdb->Chains[i]->Id); #if AJFALSE ajFmtPrintF(outf, "NR %d; NL %d; NH %d; NE %d; NS %d; NT %d;\n", pdb->Chains[i]->Nres, pdb->Chains[i]->Nlig, pdb->Chains[i]->numHelices, pdb->Chains[i]->numStrands, pdb->Chains[i]->numSheets, pdb->Chains[i]->numTurns); #endif ajFmtPrintF(outf, "NR %d; NL %d; NH %d; NE %d;\n", pdb->Chains[i]->Nres, pdb->Chains[i]->Nlig, pdb->Chains[i]->numHelices, pdb->Chains[i]->numStrands); #if AJFALSE ajFmtPrintF(outf, "%-5sID %c; NR %d; NH %d; NW %d;\n", "IN", pdb->Chains[i]->Id, pdb->Chains[i]->Nres, pdb->Chains[i]->Nhet, pdb->Chains[i]->Nwat); #endif ajFmtPrintF(outf, "XX\n"); outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, pdb->Chains[i]->Seq, "SQ"); ajSeqoutDel(&outseq); } ajFmtPrintF(outf, "XX\n"); /* printf("NCHN: %d NMOD: %d\n", pdb->Nchn, pdb->Nmod); */ /* Write RESIDUES list */ for (i = 1U; i <= pdb->Nmod; i++) { for (j = 0U; j < pdb->Nchn; j++) { iter = ajListIterNewread(pdb->Chains[j]->Residues); while (!ajListIterDone(iter)) { residue = (AjPResidue) ajListIterGet(iter); if (residue->Mod > i) break; else if (residue->Mod != i) continue; else { ajFmtPrintF(outf, "%-5s%-5d%-5d%-5d%-6S%-2c%-6S", "RE", residue->Mod, residue->Chn, residue->Idx, residue->Pdb, residue->Id1, residue->Id3); if (residue->eNum != 0) ajFmtPrintF(outf, "%-5d", residue->eNum); else ajFmtPrintF(outf, "%-5c", '.'); ajFmtPrintF(outf, "%-5S%-5c", residue->eId, residue->eType); if (residue->eType == 'H') ajFmtPrintF(outf, "%-5d", residue->eClass); else ajFmtPrintF(outf, "%-5c", '.'); if (residue->eStrideNum != 0) ajFmtPrintF(outf, "%-5d", residue->eStrideNum); else ajFmtPrintF(outf, "%-5c", '.'); ajFmtPrintF(outf, "%-5c", residue->eStrideType); ajFmtPrintF(outf, "%8.2f%8.2f%8.2f%8.2f%8.2f" "%8.2f%8.2f%8.2f%8.2f%8.2f%8.2f%8.2f" "%8.2f\n", residue->Phi, residue->Psi, residue->Area, residue->all_abs, residue->all_rel, residue->side_abs, residue->side_rel, residue->main_abs, residue->main_rel, residue->npol_abs, residue->npol_rel, residue->pol_abs, residue->pol_rel); } } ajListIterDel(&iter); } } /* Write ATOMS list */ for (i = 1U; i <= pdb->Nmod; i++) { for (j = 0U; j < pdb->Nchn; j++) { /* Print out chain-specific coordinates */ iter = ajListIterNewread(pdb->Chains[j]->Atoms); while (!ajListIterDone(iter)) { atom = (AjPAtom) ajListIterGet(iter); if (atom->Mod > i) break; else if (atom->Mod != i) continue; else { if (atom->Type == 'H') ajFmtPrintF(outf, "%-5s%-5d%-5d%-5d%-5c%-6S%-2c%-6S" "%-2c%-6S" "%9.3f%9.3f%9.3f%8.2f%8.2f\n", "AT", atom->Mod, atom->Chn, atom->Gpn, '.', atom->Pdb, atom->Id1, atom->Id3, atom->Type, atom->Atm, atom->X, atom->Y, atom->Z, atom->O, atom->B); else { ajFmtPrintF(outf, "%-5s%-5d%-5d%-5c%-5d%-6S%-2c%-6S" "%-2c%-6S" "%9.3f%9.3f%9.3f%8.2f%8.2f\n", "AT", atom->Mod, atom->Chn, '.', atom->Idx, atom->Pdb, atom->Id1, atom->Id3, atom->Type, atom->Atm, atom->X, atom->Y, atom->Z, atom->O, atom->B); } } } ajListIterDel(&iter); } /* Print out group-specific coordinates for this model */ iter = ajListIterNewread(pdb->Groups); while (!ajListIterDone(iter)) { atom = (AjPAtom) ajListIterGet(iter); if (atom->Mod > i) break; else if (atom->Mod != i) continue; else { ajFmtPrintF(outf, "%-5s%-5d%-5c%-5d%-5c%-6S%-2c%-6S%-2c%-6S" "%9.3f%9.3f%9.3f%8.2f%8.2f\n", "AT", atom->Mod, '.', atom->Gpn, '.', atom->Pdb, atom->Id1, atom->Id3, atom->Type, atom->Atm, atom->X, atom->Y, atom->Z, atom->O, atom->B); } } ajListIterDel(&iter); /* Print out water-specific coordinates for this model */ iter = ajListIterNewread(pdb->Water); while (!ajListIterDone(iter)) { atom = (AjPAtom) ajListIterGet(iter); if (atom->Mod > i) break; else if (atom->Mod != i) continue; else { ajFmtPrintF(outf, "%-5s%-5d%-5c%-5c%-5c%-6S%-2c%-6S%-2c%-6S" "%9.3f%9.3f%9.3f%8.2f%8.2f\n", "AT", atom->Mod, '.', '.', '.', atom->Pdb, atom->Id1, atom->Id3, atom->Type, atom->Atm, atom->X, atom->Y, atom->Z, atom->O, atom->B); } } ajListIterDel(&iter); } ajFmtPrintF(outf, "//\n"); return ajTrue; } ID ajPdbWriteSegment TY public MO ajpdb LB core XX DE Writes a clean coordinate file for a segment, e.g. a domain. The segment DE corresponds to a sequence that is passed to the function. DE In the clean coordinate file, the coordinates are presented as belonging DE to a single chain. Coordinates for heterogens are NOT written to file. XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD Pdb object PX PN [3] PA r segment const AjPStr PD Sequence of segment to print out. PX PN [4] PA r chnid char PD Chain id of segment PX PN [5] PA r domain const AjPStr PD Domain code for segment PX PN [6] PA w errf AjPFile PD Output file stream for error messages PX RT AjBool RD True on success RX // AjBool ajPdbWriteSegment(AjPFile outf, const AjPPdb pdb, const AjPStr segment, char chnid, const AjPStr domain, AjPFile errf) { ajuint chn = 0U; ajlong start = 0L; ajlong end = 0L; char id; AjIList iter = NULL; AjPAtom atm = NULL; AjPResidue residue = NULL; AjBool found_start = ajFalse; AjBool found_end = ajFalse; AjPSeqout outseq = NULL; /* Check for unknown or zero-length chain */ if (!ajPdbChnidToNum(chnid, pdb, &chn)) { ajWarn("Chain incompatibility error in " "ajPbdWriteSegment"); ajFmtPrintF(errf, "//\n%S\nERROR Chain incompatibility " "error in ajPbdWriteDomain\n", domain); return ajFalse; } else if (pdb->Chains[chn - 1]->Nres == 0) { ajWarn("Chain length zero"); ajFmtPrintF(errf, "//\n%S\nERROR Chain length zero\n", domain); return ajFalse; } /* Check if segment exists in this chain */ if ((start = ajStrFindS(pdb->Chains[chn - 1]->Seq, segment)) == -1) { ajWarn("Domain not found in ajPbdWriteSegment"); ajFmtPrintF(errf, "//\n%S\nERROR Domain not found " "in ajPbdWriteSegment\n", domain); return ajFalse; } else { /* Residue numbers start at 1 ! */ start++; end = start + MAJSTRGETLEN(segment) - 1; } /* Write header info. to domain coordinate file */ ajFmtPrintF(outf, "%-5s%S\n", "ID", domain); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5sCo-ordinates for domain %S\n", "DE", domain); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5sDomain defined from sequence segment\n", "OS"); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5sMETHOD ", "EX"); if (pdb->Method == ajEPdbMethodXray) ajFmtPrintF(outf, "xray; "); else ajFmtPrintF(outf, "nmr_or_model; "); /* The NCHN and NMOD are hard-coded to 1 for domain files */ ajFmtPrintF(outf, "RESO %.2f; NMOD 1; NCHN 1; NGRP 0;\n", pdb->Reso); id = pdb->Chains[chn - 1]->Id; if (id == ' ') id = '.'; /* Write sequence to domain coordinate file */ ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5s[1]\n", "CN"); ajFmtPrintF(outf, "XX\n"); ajFmtPrintF(outf, "%-5sID %c; NR %d; NL 0; NH 0; NE 0;\n", "IN", id, MAJSTRGETLEN(segment)); ajFmtPrintF(outf, "XX\n"); outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, segment, "SQ"); ajSeqoutDel(&outseq); ajFmtPrintF(outf, "XX\n"); /* Write coordinates list to domain coordinate file */ ajPdbChnidToNum(chnid, pdb, &chn); /* Initialise the iterator */ iter = ajListIterNewread(pdb->Chains[chn - 1]->Atoms); /* Iterate through the list of residues */ while ((residue = (AjPResidue) ajListIterGet(iter))) { if (residue->Mod != 1) break; if (!found_start) { if (residue->Idx == start) found_start = ajTrue; else continue; } if (!found_end) { if (residue->Idx == end) found_end = ajTrue; } /* ** The end position has been found, and the current residue is ** not the final residue. */ else if (residue->Idx != end && found_end) break; /* Print out coordinate line */ ajFmtPrintF(outf, "%-5s%-5d%-5d%-5d%-6S%-2c%-6S", "RE", residue->Mod, 1, /* chn number is always given as 1 */ residue->Idx - start + 1, residue->Pdb, residue->Id1, residue->Id3); if (residue->eNum != 0) ajFmtPrintF(outf, "%-5d", residue->eNum); else ajFmtPrintF(outf, "%-5c", '.'); ajFmtPrintF(outf, "%-5S%-5c", residue->eId, residue->eType); if (residue->eType == 'H') ajFmtPrintF(outf, "%-5d", residue->eClass); else ajFmtPrintF(outf, "%-5c", '.'); if (residue->eStrideNum != 0) ajFmtPrintF(outf, "%-5d", residue->eStrideNum); else ajFmtPrintF(outf, "%-5c", '.'); ajFmtPrintF(outf, "%-5c", residue->eStrideType); ajFmtPrintF(outf, "%8.2f%8.2f%8.2f%8.2f%8.2f" "%8.2f%8.2f%8.2f%8.2f%8.2f%8.2f%8.2f" "%8.2f\n", residue->Phi, residue->Psi, residue->Area, residue->all_abs, residue->all_rel, residue->side_abs, residue->side_rel, residue->main_abs, residue->main_rel, residue->npol_abs, residue->npol_rel, residue->pol_abs, residue->pol_rel); /* Assign pointer for this chain */ /* residue2 = residue; but it's never used */ } ajListIterDel(&iter); /* Iterate through the list of atoms */ while ((atm = (AjPAtom) ajListIterGet(iter))) { if (atm->Mod != 1) break; if (atm->Type != 'P') continue; if (!found_start) { if (atm->Idx == start) found_start = ajTrue; else continue; } if (!found_end) { if (atm->Idx == end) found_end = ajTrue; } /* ** The end position has been found, and the current atom no longer ** belongs to this final residue. */ else if (atm->Idx != end && found_end) break; /* Print out coordinate line */ ajFmtPrintF(outf, "%-5s%-5d%-5d%-5c%-5d%-6S%-2c%-6S%-2c%-6S" "%9.3f%9.3f%9.3f%8.2f%8.2f\n" "AT", atm->Mod, /* It will always be 1 */ 1, /* chn number is always given as 1 */ '.', atm->Idx - start + 1, atm->Pdb, atm->Id1, atm->Id3, atm->Type, atm->Atm, atm->X, atm->Y, atm->Z, atm->O, atm->B); /* Assign pointer for this chain */ /* atm2 = atm; but it's never used */ } ajListIterDel(&iter); /* Write last line in file */ ajFmtPrintF(outf, "//\n"); return ajTrue; } ID ajHetWrite TY public MO ajpdb LB core XX DE Writes the contents of a Het object to file. XX PN [1] PA w outf AjPFile PD Output file PX PN [2] PA r het const AjPHet PD AJAX Het PX PN [3] PA r dogrep AjBool PD Flag (True if we are to write 'cnt' PD element of the Het object to file). PX RT AjBool RD True on success RX CA output CT AjPHet CD Write Het object to file in clean CX // AjBool ajHetWrite(AjPFile outf, const AjPHet het, AjBool dogrep) { ajuint i = 0U; /* Check arg's */ if (!outf || !het) return ajFalse; for (i = 0U; i < het->Number; i++) { ajFmtPrintF(outf, "ID %S\n", het->Entries[i]->abv); ajFmtPrintSplit(outf, het->Entries[i]->ful, "DE ", 70, " \t\n\r"); ajFmtPrintSplit(outf, het->Entries[i]->syn, "SY ", 70, " \t\n\r"); if (dogrep) ajFmtPrintF(outf, "NN %d\n", het->Entries[i]->cnt); ajFmtPrintF(outf, "//\n"); } return ajTrue; } ID ajPdbtospWrite TY public MO ajpdb LB core XX DE Reads a list of Pdbtosp objects, e.g. taken from the swissprot:pdb DE equivalence table available at URL: DE (1) http://www.expasy.ch/cgi-bin/lists?pdbtosp.txt) DE and writes the list out to file in embl-like format (see DE documentation for DOMAINATRIX "pdbtosp" application). XX PN [1] PA w outf AjPFile PD Output file PX PN [2] PA r list const AjPList PD List of Pdbtosp objects PX RT AjBool RD True of file was written ok. RX // AjBool ajPdbtospWrite(AjPFile outf, const AjPList list) { ajuint i = 0; AjIList iter = NULL; AjPPdbtosp pdbtosp = NULL; if (!outf || !list) { ajWarn("Bad args passed to ajPdbtospWrte"); return ajFalse; } iter = ajListIterNewread(list); while ((pdbtosp = (AjPPdbtosp) ajListIterGet(iter))) { ajFmtPrintF(outf, "%-5s%S\nXX\n%-5s%u\nXX\n", "EN", pdbtosp->Pdb, "NE", pdbtosp->Number); for (i = 0U; i < pdbtosp->Number; i++) ajFmtPrintF(outf, "%-5s%S ID; %S ACC;\n", "IN", pdbtosp->Spr[i], pdbtosp->Acc[i]); ajFmtPrintF(outf, "XX\n//\n"); } ajListIterDel(&iter); return ajTrue; } ID ajCmapWrite TY public MO ajpdb LB core XX DE Write a Cmap object to file in CON format (see documentation for DE DOMAINATRIX "contacts" application). XX PN [1] PA u outf AjPFile PD Output file stream. PX PN [2] PA r cmap const AjPCmap PD Cmap object pointer. PX RT AjBool RD True on success (object was written successfully) RX CA output CT AjPCmap CD Write Cmap object to file in CON format. CX // AjBool ajCmapWrite(AjPFile outf, const AjPCmap cmap) { ajint x = 0; ajint y = 0; AjPStr Id = NULL; AjPStr Domid = NULL; AjPStr Ligid = NULL; AjPStr res1 = NULL; AjPStr res2 = NULL; AjPSeqout outseq = NULL; if (!cmap || !outf) return ajFalse; Id = ajStrNew(); Domid = ajStrNew(); Ligid = ajStrNew(); res1 = ajStrNew(); res2 = ajStrNew(); /* EN */ ajFmtPrintF(outf, "%-5s[%d]\n", "EN", cmap->en); ajFmtPrintF(outf, "XX\n"); /* ID */ if (MAJSTRGETLEN(cmap->Id)) ajStrAssignS(&Id, cmap->Id); else ajStrAssignC(&Id, "."); if (MAJSTRGETLEN(cmap->Domid)) ajStrAssignS(&Domid, cmap->Domid); else ajStrAssignC(&Domid, "."); if (MAJSTRGETLEN(cmap->Ligid)) ajStrAssignS(&Ligid, cmap->Ligid); else ajStrAssignC(&Ligid, "."); ajFmtPrintF(outf, "%-5sPDB %S; DOM %S; LIG %S;\n", "ID", Id, Domid, Ligid); ajFmtPrintF(outf, "XX\n"); /* DE */ ajFmtPrintF(outf, "DE %S\n", cmap->Desc); ajFmtPrintF(outf, "XX\n"); /* SI */ ajFmtPrintF(outf, "%-5sSN %d; NS %d\n", "SI", cmap->sn, cmap->ns); ajFmtPrintF(outf, "XX\n"); /* CN */ if ((cmap->Type == ajECmapTypeIntra) || (cmap->Type == ajECmapTypeLigand)) ajFmtPrintF(outf, "%-5sMO .; CN1 %d; CN2 .; ID1 %c; ID2 .; " "NRES1 %d; NRES2 .\n", "CN", cmap->Chn1, cmap->Chid1, cmap->Nres1); else if (cmap->Type == ajECmapTypeInter) ajFmtPrintF(outf, "%-5sMO .; CN1 %d; CN2 %d; ID1 %c; ID2 %c; " "NRES1 %d; NRES2 %d\n", "CN", cmap->Chn1, cmap->Chn2, cmap->Chid1, cmap->Chid2, cmap->Nres1, cmap->Nres2); else ajFatal("cmap type not known in ajCmapWrite"); /* S1 */ if (MAJSTRGETLEN(cmap->Seq1)) { outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, cmap->Seq1, "S1"); ajSeqoutDel(&outseq); ajFmtPrintF(outf, "XX\n"); } /* S2 */ if (cmap->Type == ajECmapTypeInter) { if (MAJSTRGETLEN(cmap->Seq2)) { outseq = ajSeqoutNewFile(outf); ajSeqoutDumpSwisslike(outseq, cmap->Seq2, "S2"); ajSeqoutDel(&outseq); ajFmtPrintF(outf, "XX\n"); } } /* NC */ if ((cmap->Type == ajECmapTypeIntra) || (cmap->Type == ajECmapTypeInter)) { ajFmtPrintF(outf, "%-5sSM %d; LI .\n", "NC", cmap->Ncon); ajFmtPrintF(outf, "XX\n"); } else { ajFmtPrintF(outf, "%-5sSM .; LI %d\n", "NC", cmap->Ncon); ajFmtPrintF(outf, "XX\n"); } /* SM or LI */ if (cmap->Type == ajECmapTypeIntra) { for (x = 0; x < cmap->Nres1; x++) for (y = x + 1; y < cmap->Nres1; y++) { if ((ajUint2dGet(cmap->Mat, x, y) == 1)) { /* Assign residue id. */ if (!ajResidueToTriplet(ajStrGetCharPos(cmap->Seq1, x), &res1)) ajFatal("Index out of range in ajCmapWrite"); if (!ajResidueToTriplet(ajStrGetCharPos(cmap->Seq1, y), &res2)) ajFatal("Index out of range in ajCmapWrite"); /* Print out the contact. */ ajFmtPrintF(outf, "%-5s%S %d ; %S %d\n", "SM", res1, x + 1, res2, y + 1); } } } else if (cmap->Type == ajECmapTypeInter) { for (x = 0; x < cmap->Nres1; x++) for (y = x + 1; y < cmap->Nres2; y++) { if ((ajUint2dGet(cmap->Mat, x, y) == 1)) { /* Assign residue id. */ if (!ajResidueToTriplet(ajStrGetCharPos(cmap->Seq1, x), &res1)) ajFatal("Index out of range in ajCmapWrite"); if (!ajResidueToTriplet(ajStrGetCharPos(cmap->Seq2, y), &res2)) ajFatal("Index out of range in ajCmapWrite"); /* Print out the contact. */ ajFmtPrintF(outf, "%-5s%S %d ; %S %d\n", "SM", res1, x + 1, res2, y + 1); } } } else if (cmap->Type == ajECmapTypeLigand) { for (x = 0; x < cmap->Nres1; x++) if ((ajUint2dGet(cmap->Mat, 0, x) == 1)) { /* Assign residue id. */ if (!ajResidueToTriplet(ajStrGetCharPos(cmap->Seq1, x), &res1)) ajFatal("Index out of range in ajCmapWrite"); /* Print out the contact. */ ajFmtPrintF(outf, "%-5s%S %d\n", "LI", res1, x + 1); } } ajFmtPrintF(outf, "//\n"); ajStrDel(&Id); ajStrDel(&Domid); ajStrDel(&Ligid); ajStrDel(&res1); ajStrDel(&res2); return ajTrue; } ID ajPdbExit TY public MO ajpdb LB core XX DE Cleanup of Pdb function internals. XX RT void RD RX // void ajPdbExit(void) { ajStrDel(&pdbGStrline); ajStrDel(&pdbGStrtemp_id); ajStrDel(&pdbGStrtemp_domid); ajStrDel(&pdbGStrtemp_ligid); ajStrDel(&pdbGStrtype); ajStrDel(&pdbGStrdesc); ajStrDel(&pdbGStrtmpstr); return; } ID pdbioWriteSeqresChain TY static MO ajpdbio LB core XX DE Writes sequence for a protein chain to an output file in pdb format DE (SEQRES records). Sequence is taken from a Pdb structure. XX PN [1] PA w errf AjPFile PD Output file stream for error messages PX PN [2] PA w outf AjPFile PD Output file stream PX PN [3] PA r pdb const AjPPdb PD Pdb object PX PN [4] PA r chn ajint PD chain number, beginning at 1 PX RT AjBool RD True on success RX // static AjBool pdbioWriteSeqresChain(AjPFile errf, AjPFile outf, const AjPPdb pdb, ajint chn) { ajuint last_rn = 0U; ajuint this_rn = 0U; ajuint i = 0U; ajuint j = 0U; ajuint len = 0U; const char *p; AjPStr tmp1 = NULL; AjPStr tmp2 = NULL; AjIList iter = NULL; AjPAtom atom = NULL; tmp1 = ajStrNew(); tmp2 = ajStrNew(); iter = ajListIterNewread(pdb->Chains[chn - 1]->Atoms); /* Iterate through list of atoms */ while ((atom = (AjPAtom) ajListIterGet(iter))) { /* ** Hard-coded to work on model 1 ** Break if a non-protein atom is found or model no. !=1 ** Continue / break if a non-protein atom is found or model no. !=1 */ if (atom->Mod != 1) break; if (atom->Type != 'P') continue; /* If we are onto a new residue */ this_rn = atom->Idx; if (this_rn != last_rn) { /* Assign sequence for residues missing from the linked list */ for (i = last_rn; i < this_rn - 1; i++) { /* Check that position i is in range for the sequence */ if (!ajResidueToTriplet( ajStrGetCharPos(pdb->Chains[chn - 1]->Seq, i), &tmp2)) { ajWarn("Index out of range in pdbioWriteSeqresChain"); ajFmtPrintF(errf, "//\n%S\nERROR Index out " "of range in pdbioWriteSeqresChain\n", pdb->Pdb); ajStrDel(&tmp1); ajStrDel(&tmp2); ajListIterDel(&iter); return ajFalse; } else { ajStrAppendS(&tmp1, tmp2); ajStrAppendC(&tmp1, " "); } } ajStrAppendS(&tmp1, atom->Id3); ajStrAppendC(&tmp1, " "); last_rn = this_rn; } } /* Assign sequence for residues missing from end of linked list */ for (i = last_rn; i < pdb->Chains[chn - 1]->Nres; i++) if (!ajResidueToTriplet( ajStrGetCharPos(pdb->Chains[chn - 1]->Seq, i), &tmp2)) { ajStrDel(&tmp1); ajStrDel(&tmp2); ajListIterDel(&iter); ajWarn("Index out of range in pdbioWriteSeqresChain"); ajFmtPrintF(errf, "//\n%S\nERROR Index out of range " "in pdbioWriteSeqresChain\n", pdb->Pdb); return ajFalse; } else { ajStrAppendS(&tmp1, tmp2); ajStrAppendC(&tmp1, " "); } /* Print out SEQRES records */ for (p = ajStrGetPtr(tmp1), len = ajStrGetLen(tmp1), i = 0U, j = 1U; i < len; i += 52, j++, p += 52) ajFmtPrintF(outf, "SEQRES%4u %c%5u %-61.52s\n", j, pdb->Chains[chn - 1]->Id, pdb->Chains[chn - 1]->Nres, p); /* Tidy up */ ajStrDel(&tmp1); ajStrDel(&tmp2); ajListIterDel(&iter); return ajTrue; } ID pdbioWriteSeqresDomain TY static MO ajpdbio LB core XX DE Writes sequence for a SCOP domain to an output file in pdb format (SEQRES DE records). Sequence is taken from a Pdb structure, domain definition is DE taken from a Scop structure. Where coordinates for multiple models (e.g. DE NMR structures) are given, data for model 1 are written. XX PN [1] PA w errf AjPFile PD Output file stream for error messages PX PN [2] PA w outf AjPFile PD Output file stream PX PN [3] PA r pdb const AjPPdb PD Pdb object PX PN [4] PA r scop const AjPScop PD Scop object PX RT AjBool RD True on success RX // static AjBool pdbioWriteSeqresDomain(AjPFile errf, AjPFile outf, const AjPPdb pdb, const AjPScop scop) { ajuint last_rn = 0U; ajuint this_rn = 0U; ajuint i = 0U; ajuint j = 0U; ajint rcnt = 0; ajuint len = 0U; ajuint chn = 0U; const char *p = NULL; char id = '\0'; AjPStr tmp1 = NULL; AjPStr tmp2 = NULL; AjBool found_start = ajFalse; AjBool found_end = ajFalse; AjBool nostart = ajFalse; AjBool noend = ajFalse; AjIList iter = NULL; AjPAtom atom = NULL; AjPStr tmpstr = NULL; /* AjPResidue *resarr = NULL; */ /* Allocate strings etc */ tmp1 = ajStrNew(); tmp2 = ajStrNew(); tmpstr = ajStrNew(); /* Loop for each chain in the domain */ for (i = 0U; i < scop->Number; i++, found_start = ajFalse, found_end = ajFalse, last_rn = 0) { /* Check for error in chain id */ if (!ajPdbChnidToNum(scop->Chain[i], pdb, &chn)) { ajListIterDel(&iter); ajStrDel(&tmp1); ajStrDel(&tmp2); ajStrDel(&tmpstr); ajWarn("Chain incompatibility error in " "pdbioWriteSeqresDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Chain incompatibility " "error in pdbioWriteSeqresDomain\n", scop->Entry); return ajFalse; } #if AJFALSE if (resarr) AJFREE(resarr); ajListToarray(pdb->Chains[chn - 1]->Residues, (void ***) &resarr); #endif /* AJFALSE */ /* Initialise iterator for list of atoms */ iter = ajListIterNewread(pdb->Chains[chn - 1]->Atoms); /* Start of chain not specified */ if (!ajStrCmpC(scop->Start[i], ".")) nostart = ajTrue; else nostart = ajFalse; /* End of chain not specified */ if (!ajStrCmpC(scop->End[i], ".")) noend = ajTrue; else noend = ajFalse; /* Iterate through list of atoms */ while ((atom = (AjPAtom) ajListIterGet(iter))) { /* Hard-coded to work on model 1 */ /* ** Continue / break if a non-protein atom is found or ** model no. != 1 */ if (atom->Mod != 1) break; if (atom->Type != 'P') continue; /* If there is a new residue */ this_rn = atom->Idx; if (this_rn != last_rn) { /* ** The start position was specified, but has not ** been found yet */ if (!found_start && !nostart) { ajStrAssignS(&tmpstr, scop->Start[i]); ajStrAppendK(&tmpstr, '*'); /* Start position found */ /* if(!ajStrCmpCaseS(atom->Pdb, scop->Start[i])) */ if (ajStrMatchWildS(atom->Pdb, tmpstr)) /* ** if(ajStrMatchWildS(resarr[atom->Idx-1]->Pdb, ** tmpstr)) */ { if (!ajStrMatchS(atom->Pdb, scop->Start[i])) /* if(!ajStrMatchS(resarr[atom->Idx-1]->Pdb, * * scop->Start[i])) */ { ajWarn("Domain start found by wildcard match " "only in pdbioWriteSeqresDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain start " "found " "by wildcard match only in " "pdbioWriteSeqresDomain\n", scop->Entry); } last_rn = this_rn; found_start = ajTrue; } else { last_rn = this_rn; continue; } } /* ** Assign sequence for residues missing from the linked list ** of atoms of known structure */ for (j = last_rn; j < this_rn - 1; j++) { /* Check that position j is in range for the sequence */ if (!ajResidueToTriplet( ajStrGetCharPos(pdb->Chains[chn - 1]->Seq, j), &tmp2)) { ajListIterDel(&iter); ajStrDel(&tmp1); ajStrDel(&tmp2); ajStrDel(&tmpstr); ajWarn("Index out of range in " "pdbioWriteSeqresDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Index out of range " "in pdbioWriteSeqresDomain\n", scop->Entry); return ajFalse; } else { ajStrAppendS(&tmp1, tmp2); ajStrAppendC(&tmp1, " "); rcnt++; } } last_rn = this_rn; /* Append the residue to the sequence */ ajStrAppendS(&tmp1, atom->Id3); ajStrAppendC(&tmp1, " "); rcnt++; /* The end position was specified, but has not been found yet */ if (!found_end && !noend) { ajStrAssignS(&tmpstr, scop->End[i]); ajStrAppendK(&tmpstr, '*'); /* End found */ /* ** if(!ajStrCmpCaseS(atom->Pdb, scop->End[i])) */ if (ajStrMatchWildS(atom->Pdb, tmpstr)) /* ** if(ajStrMatchWildS(resarr[atom->Idx-1]->Pdb, ** tmpstr)) */ { if (!ajStrMatchS(atom->Pdb, scop->End[i])) /* ** if(!ajStrMatchS(resarr[atom->Idx-1]->Pdb, ** scop->End[i])) */ { ajWarn("Domain end found by wildcard match only " "in pdbioWriteSeqresDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain end found " "by wildcard match only in " "pdbioWriteSeqresDomain\n", scop->Entry); } found_end = ajTrue; break; } } } } /* Domain start specified but not found */ if (!found_start && !nostart) { ajListIterDel(&iter); ajStrDel(&tmp1); ajStrDel(&tmp2); ajStrDel(&tmpstr); ajWarn("Domain start not found in pdbioWriteSeqresDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain start not found " "in pdbioWriteSeqresDomain\n", scop->Entry); return ajFalse; } /* Domain end specified but not found */ if (!found_end && !noend) { ajListIterDel(&iter); ajStrDel(&tmp1); ajStrDel(&tmp2); ajStrDel(&tmpstr); ajWarn("Domain end not found in pdbioWriteSeqresDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain end not found " "in pdbioWriteSeqresDomain\n", scop->Entry); return ajFalse; } /* ** Assign sequence for residues missing from end of linked list ** Only needs to be done where the end of the domain is not specified */ if (noend) { for (j = last_rn; j < pdb->Chains[chn - 1]->Nres; j++) if (!ajResidueToTriplet( ajStrGetCharPos(pdb->Chains[chn - 1]->Seq, j), &tmp2)) { ajStrDel(&tmp1); ajStrDel(&tmp2); ajStrDel(&tmpstr); ajListIterDel(&iter); ajWarn("Index out of range in pdbioWriteSeqresDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Index out of " "range in pdbioWriteSeqresDomain\n", scop->Entry); return ajFalse; } else { ajStrAppendS(&tmp1, tmp2); ajStrAppendC(&tmp1, " "); rcnt++; } } ajListIterDel(&iter); } /* ** If the domain was composed of more than once chain then a '.' is ** given as the chain identifier */ if (scop->Number > 1) id = '.'; else id = pdb->Chains[chn - 1]->Id; /* Print out SEQRES records */ for (p = ajStrGetPtr(tmp1), len = ajStrGetLen(tmp1), i = 0U, j = 1; i < len; i += 52, j++, p += 52) ajFmtPrintF(outf, "SEQRES%4u %c%5d %-61.52s\n", j, id, rcnt, p); #if AJFALSE if (resarr) AJFREE(resarr); #endif /* AJFALSE */ ajStrDel(&tmp1); ajStrDel(&tmp2); ajStrDel(&tmpstr); return ajTrue; } ID pdbioWriteAtomChain TY static MO ajpdbio LB core XX DE Writes coordinates for a protein chain to an output file in pdb format DE (ATOM records). Coordinates are taken from a Pdb structure. The model DE number argument should have a value of 1 for x-ray structures. XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD Pdb object PX PN [3] PA r mod ajuint PD Model number, beginning at 1 PX PN [4] PA r chn ajuint PD Chain number, beginning at 1 PX PN [5] PA u mode AjEPdbMode PD AJAX PDB Mode enumeration. Either ajEPdbModePdb PD or ajEPdbModeIdx if the original or PD corrected residue number is to be used. PX RT AjBool RD True on success RX // static AjBool pdbioWriteAtomChain(AjPFile outf, const AjPPdb pdb, ajuint mod, ajuint chn, AjEPdbMode mode) { AjBool doneter = ajFalse; AjIList iter = NULL; AjPAtom atom1 = NULL; AjPAtom atom2 = NULL; ajint acnt = 0; #if AJFALSE AjPResidue *resarr = NULL; #endif /* AJFALSE */ /* Check args are not NULL */ if (!outf || !pdb || mod < 1 || chn < 1) return ajFalse; #if AJFALSE ajListToarray(pdb->Chains[chn - 1]->Residues, (void ***) &resarr); #endif /* AJFALSE */ doneter = ajFalse; iter = ajListIterNewread(pdb->Chains[chn - 1]->Atoms); while ((atom1 = (AjPAtom) ajListIterGet(iter))) if (atom1->Mod == mod) break; for (acnt = 1; atom1; atom1 = (AjPAtom) ajListIterGet(iter)) { /* Break if on a new model */ if (atom1->Mod != mod) break; /* End of protein atoms - so write a TER record */ if (atom1->Type != 'P' && (!doneter)) { switch (mode) { case ajEPdbModeIdx: ajFmtPrintF(outf, "%-6s%5d %-4S%c%4d%54s\n", "TER", acnt++, atom2->Id3, pdb->Chains[chn - 1]->Id, atom2->Idx, " "); break; case ajEPdbModePdb: ajFmtPrintF(outf, "%-6s%5d %-4S%c%4S%54s\n", "TER", acnt++, atom2->Id3, pdb->Chains[chn - 1]->Id, atom2->Pdb, /* resarr[atom2->Idx-1]->Pdb, */ " "); break; default: ajFatal("Invalid mode in pdbioWriteAtomChain"); } doneter = ajTrue; } /* Write out ATOM or HETATM line */ if (atom1->Type == 'P') ajFmtPrintF(outf, "%-6s", "ATOM"); else ajFmtPrintF(outf, "%-6s", "HETATM"); switch (mode) { case ajEPdbModeIdx: ajFmtPrintF(outf, "%5d %-4S%-4S%c%4d%12.3f%8.3f%8.3f" "%6.2f%6.2f%11s%-3c\n", acnt++, atom1->Atm, atom1->Id3, pdb->Chains[chn - 1]->Id, atom1->Idx, atom1->X, atom1->Y, atom1->Z, atom1->O, atom1->B, " ", *ajStrGetPtr(atom1->Atm)); break; case ajEPdbModePdb: ajFmtPrintF(outf, "%5d %-4S%-4S%c%4S%12.3f%8.3f%8.3f" "%6.2f%6.2f%11s%-3c\n", acnt++, atom1->Atm, atom1->Id3, pdb->Chains[chn - 1]->Id, atom1->Pdb, /* resarr[atom1->Idx-1]->Pdb, */ atom1->X, atom1->Y, atom1->Z, atom1->O, atom1->B, " ", *ajStrGetPtr(atom1->Atm)); break; default: ajFatal("Invalid mode in pdbioWriteAtomChain"); } atom2 = atom1; } /* Write TER record if its not already done */ if (!doneter) { ajFmtPrintF(outf, "%-6s%5d %-4S%c%4d%54s\n", "TER", acnt++, atom2->Id3, pdb->Chains[chn - 1]->Id, atom2->Idx, " "); doneter = ajTrue; } ajListIterDel(&iter); #if AJFALSE if (resarr) AJFREE(resarr); #endif /* AJFALSE */ return ajTrue; } ID pdbioWriteAtomDomain TY static MO ajpdbio LB core XX DE Writes coordinates for a SCOP domain to an output file in pdb format (ATOM DE records). Coordinates are taken from a Pdb structure, domain definition is DE taken from a Scop structure. The model number argument should have a value DE of 1 for x-ray structures. Coordinates for heterogens are NOT written to DE file. The corrected residue numbers are given (these give an index into DE the SEQRES sequence. XX PN [1] PA w errf AjPFile PD Output file stream for error messages PX PN [2] PA w outf AjPFile PD Output file stream PX PN [3] PA r pdb const AjPPdb PD Pdb object PX PN [4] PA r scop const AjPScop PD Scop object PX PN [5] PA r mod ajuint PD Model number, beginning at 1 PX PN [6] PA u mode AjEPdbMode PD AJAX PDB Mode enumeration, either ajEPdbModePdb PD or ajEPdbModeIdx if the original or corrected PD residue number is to be used. PX RT AjBool RD True on success RX // static AjBool pdbioWriteAtomDomain(AjPFile errf, AjPFile outf, const AjPPdb pdb, const AjPScop scop, ajuint mod, AjEPdbMode mode) { /* ** rn_mod is a modifier to the residue number to give correct residue ** numbering for the domain */ ajint acnt = 1; ajint rn_mod = 0; ajuint z = 0U; ajuint finalrn = 0U; ajuint chn = 0U; char id = '\0'; AjBool found_start = ajFalse; AjBool found_end = ajFalse; AjBool nostart = ajFalse; AjBool noend = ajFalse; AjIList iter = NULL; AjPAtom atom1 = NULL; AjPAtom atom2 = NULL; AjPStr tmpstr = NULL; #if AJFALSE AjPResidue *resarr = NULL; #endif /* AJFALSE */ if (!errf || !outf || !pdb || !scop) ajFatal("Bad args. passed to pdbioWriteAtomDomain"); /* Allocate strings etc */ tmpstr = ajStrNew(); /* Loop for each chain in the domain */ for (z = 0; z < scop->Number; z++, found_start = ajFalse, found_end = ajFalse) { /* Check for chain error */ if (!ajPdbChnidToNum(scop->Chain[z], pdb, &chn)) { ajListIterDel(&iter); ajWarn("Chain incompatibility error in " "pdbioWriteAtomDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Chain incompatibility " "error in pdbioWriteAtomDomain\n", scop->Entry); ajStrDel(&tmpstr); return ajFalse; } #if AJFALSE ajListToarray(pdb->Chains[chn - 1]->Residues, (void ***) &resarr); #endif /* AJFALSE */ /* Iterate up to the correct model */ iter = ajListIterNewread(pdb->Chains[chn - 1]->Atoms); while ((atom1 = (AjPAtom) ajListIterGet(iter))) if (atom1->Mod == mod) break; if (!atom1) ajFatal("Unexpected error (atom1 == NULL) in " "pdbioWriteAtomDomain\n"); /* Increment res. counter from last chain if appropriate */ if (noend) rn_mod += atom2->Idx; else rn_mod += finalrn; /* Start of chain was not specified */ if (!ajStrCmpC(scop->Start[z], ".")) nostart = ajTrue; else nostart = ajFalse; /* End of chain was not specified */ if (!ajStrCmpC(scop->End[z], ".")) noend = ajTrue; else noend = ajFalse; /* If the domain was composed of more than once chain then a '.' is * given as the chain identifier */ if (scop->Number > 1) id = '.'; else id = pdb->Chains[chn - 1]->Id; for (; atom1; atom1 = (AjPAtom) ajListIterGet(iter)) { /* ** Continue / break if a non-protein atom is found or ** model no. is incorrect */ if (atom1->Mod != mod) break; if (atom1->Type != 'P') continue; /* ** The start position was specified, but has not ** been found yet */ if (!found_start && !nostart) { ajStrAssignS(&tmpstr, scop->Start[z]); ajStrAppendK(&tmpstr, '*'); /* Start position found */ /* ** if(!ajStrCmpCaseS(atom1->Pdb, scop->Start[z])) */ if (ajStrMatchWildS(atom1->Pdb, tmpstr)) /* ** if(ajStrMatchWildS(resarr[atom1->Idx-1]->Pdb, tmpstr)) */ { if (!ajStrMatchS(atom1->Pdb, scop->Start[z])) /* ** if(!ajStrMatchS(resarr[atom1->Idx-1]->Pdb, ** scop->Start[z])) */ { ajWarn("Domain start found by wildcard match only " "in pdbioWriteAtomDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain start found " "by wildcard match only in " "pdbioWriteAtomDomain\n", scop->Entry); } rn_mod -= atom1->Idx - 1; found_start = ajTrue; } else continue; } /* The end position was specified, but has not been found yet */ if (!found_end && !noend) { ajStrAssignS(&tmpstr, scop->End[z]); ajStrAppendK(&tmpstr, '*'); /* End position found */ /* ** if(!ajStrCmpCaseS(atom1->Pdb, scop->End[z])) */ if (ajStrMatchWildS(atom1->Pdb, tmpstr)) /* ** if(ajStrMatchWildS(resarr[atom1->Idx-1]->Pdb, tmpstr)) */ { if (!ajStrMatchS(atom1->Pdb, scop->End[z])) /* ** if(!ajStrMatchS(resarr[atom1->Idx-1]->Pdb, ** scop->End[z])) */ { ajWarn("Domain end found by wildcard match only " "in pdbioWriteAtomDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain end found " "by wildcard match only in " "pdbioWriteAtomDomain\n", scop->Entry); } found_end = ajTrue; finalrn = atom1->Idx; } } else if (atom1->Idx != finalrn && !noend) break; /* Write out ATOM line to pdb file */ switch (mode) { case ajEPdbModeIdx: ajFmtPrintF(outf, "%-6s%5d %-4S%-4S%c%4d%12.3f%8.3f" "%8.3f%6.2f%6.2f%11s%-3c\n", "ATOM", acnt++, atom1->Atm, atom1->Id3, id, atom1->Idx + rn_mod, atom1->X, atom1->Y, atom1->Z, atom1->O, atom1->B, " ", *ajStrGetPtr(atom1->Atm)); break; case ajEPdbModePdb: ajFmtPrintF(outf, "%-6s%5d %-4S%-4S%c%4S%12.3f%8.3f" "%8.3f%6.2f%6.2f%11s%-3c\n", "ATOM", acnt++, atom1->Atm, atom1->Id3, id, atom1->Pdb, /* resarr[atom1->Idx-1]->Pdb, */ atom1->X, atom1->Y, atom1->Z, atom1->O, atom1->B, " ", *ajStrGetPtr(atom1->Atm)); break; default: ajFatal("Invalid mode in pdbioWriteAtomDomain"); } /* Assign pointer for this chain */ atom2 = atom1; } /* Diagnostic if start was specified but not found */ if (!found_start && !nostart) { ajListIterDel(&iter); ajWarn("Domain start not found in pdbioWriteAtomDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain start not " "found in pdbioWriteAtomDomain\n", scop->Entry); ajStrDel(&tmpstr); return ajFalse; } /* Diagnostic if end was specified but not found */ if (!found_end && !noend) { ajListIterDel(&iter); ajWarn("Domain end not found in pdbioWriteAtomDomain"); ajFmtPrintF(errf, "//\n%S\nERROR Domain end not " "found in pdbioWriteAtomDomain\n", scop->Entry); ajStrDel(&tmpstr); return ajFalse; } ajListIterDel(&iter); } if (!atom2) ajFatal("Unexpected error (atom2 == NULL) in pdbioWriteAtomDomain\n"); /* Write the TER record to the pdb file */ ajFmtPrintF(outf, "%-6s%5d %-4S%c%4d%54s\n", "TER", acnt++, atom2->Id3, id, atom2->Idx + rn_mod, " "); ajStrDel(&tmpstr); return ajTrue; } ID pdbioWriteAtomDomainPdb TY static MO ajpdbio LB core XX DE Writes coordinates for a SCOP domain to an output file in pdb format (ATOM DE records). Coordinates are taken from a Pdb structure, domain definition DE is taken from a Scop structure. DE Coordinates for heterogens are NOT written to DE file. The original (pdb) residue numbers are given (these do NOT give an DE index into the SEQRES sequence. Use pdbioWriteAtomDomainIdx if you DE need an index into the SEQRES sequence. XX PN [1] PA w errf AjPFile PD Output file stream for error messages PX PN [2] PA w outf AjPFile PD Output file stream PX PN [3] PA r pdb const AjPPdb PD Pdb object PX PN [4] PA r scop const AjPScop PD Scop object PX PN [5] PA r mod ajint PD Model number, beginning at 1 PX RT AjBool RD True on success RX // static AjBool pdbioWriteAtomDomainPdb(AjPFile errf, AjPFile outf, const AjPPdb pdb, const AjPScop scop, ajint mod) { if (pdbioWriteAtomDomain(errf, outf, pdb, scop, mod, ajEPdbModePdb)) return ajTrue; return ajFalse; } ID pdbioWriteAtomDomainIdx TY static MO ajpdbio LB core XX DE Writes coordinates for a SCOP domain to an output file in pdb format (ATOM DE records). Coordinates are taken from a Pdb structure, domain definition DE is taken from a Scop structure. The model number argument should have a DE value of 1 for x-ray structures. Coordinates for heterogens are NOT DE written to file. The corrected residue numbers are given (these give an DE index into the SEQRES sequence). Use pdbioWriteAtomDomainPdb if you DE wish to maintain the original residue number. XX PN [1] PA w errf AjPFile PD Output file stream for error messages PX PN [2] PA w outf AjPFile PD Output file stream PX PN [3] PA r pdb const AjPPdb PD Pdb object PX PN [4] PA r scop const AjPScop PD Scop object PX PN [5] PA r mod ajint PD Model number, beginning at 1 PX RT AjBool RD True on success RX // static AjBool pdbioWriteAtomDomainIdx(AjPFile errf, AjPFile outf, const AjPPdb pdb, const AjPScop scop, ajint mod) { if (pdbioWriteAtomDomain(errf, outf, pdb, scop, mod, ajEPdbModeIdx)) return ajTrue; return ajFalse; } ID pdbioWriteHeterogen TY static MO ajpdbio LB core XX DE Writes coordinates for heterogens that could not be uniquely associated DE with a chain to an output file in pdb format (HETATM records). Coordinates DE are taken from a Pdb structure. The model number argument should have a DE value of 1 for x-ray structures. XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD Pdb object PX PN [3] PA r mod ajuint PD Model number, beginning at 1 PX RT AjBool RD True on success RX // static AjBool pdbioWriteHeterogen(AjPFile outf, const AjPPdb pdb, ajuint mod) { AjIList iter = NULL; AjPAtom atom = NULL; ajint acnt; /* Check args are not NULL */ if (!outf || !pdb || mod < 1) return ajFalse; iter = ajListIterNewread(pdb->Groups); while ((atom = (AjPAtom) ajListIterGet(iter))) if (atom->Mod == mod) break; for (acnt = 1; atom; atom = (AjPAtom) ajListIterGet(iter)) { /* Break if on t0 a new model */ if (atom->Mod != mod) break; /* Write out HETATM line */ if (atom->Type == 'H') ajFmtPrintF(outf, "%-6s%5d %-4S%-4S%c%4d%12.3f%8.3f%8.3f" "%6.2f%6.2f%11s%-3c\n", "HETATM", acnt++, atom->Atm, atom->Id3, ajChararrGet(pdb->gpid, atom->Gpn - 1), atom->Idx, atom->X, atom->Y, atom->Z, atom->O, atom->B, " ", *ajStrGetPtr(atom->Atm)); else ajFmtPrintF(outf, "%-6s%5d %-4S%-4S%c%4d%12.3f%8.3f%8.3f" "%6.2f%6.2f%11s%-3c\n", "HETATM", acnt++, atom->Atm, atom->Id3, ' ', atom->Idx, atom->X, atom->Y, atom->Z, atom->O, atom->B, " ", *ajStrGetPtr(atom->Atm)); /* atom2 = atom; Unused variable */ } ajListIterDel(&iter); return ajTrue; } ID pdbioWriteText TY static MO ajpdbio LB core XX DE Writes text to file in the format of pdb records XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r str const AjPStr PD Text to print out PX PN [3] PA r prefix const char* PD pdb record (e.g. "HEADER") PX RT AjBool RD True on success RX // static AjBool pdbioWriteText(AjPFile outf, const AjPStr str, const char *prefix) { ajint n = 0; ajint l = 0; ajint c = 0; AjPStrTok handle = NULL; AjPStr token = NULL; AjPStr tmp = NULL; if (!outf) return ajFalse; /* Initialise strings */ token = ajStrNew(); tmp = ajStrNewC(""); handle = ajStrTokenNewC(str, " \t\r\n"); while (ajStrTokenNextParse(&handle, &token)) { if (!c) ajFmtPrintF(outf, "%-11s", prefix); if ((l = n + ajStrGetLen(token)) < 68) { if (c++) ajStrAppendC(&tmp, " "); ajStrAppendS(&tmp, token); n = ++l; } else { ajFmtPrintF(outf, "%-*S\n", 69, tmp); ajStrAssignS(&tmp, token); ajStrAppendC(&tmp, " "); n = ajStrGetLen(token); c = 0; } } if (c) ajFmtPrintF(outf, "%-*S\n", 69, tmp); ajStrTokenDel(&handle); ajStrDel(&token); ajStrDel(&tmp); return ajTrue; } ID pdbioWriteHeader TY static MO ajpdbio LB core XX DE Writes the Pdb element of a Pdb structure to an output file in pdb format XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD Pdb object PX RT AjBool RD True on success RX // static AjBool pdbioWriteHeader(AjPFile outf, const AjPPdb pdb) { if (pdb && outf) { ajFmtPrintF(outf, "%-11sCLEANED-UP PDB FILE FOR %-45S\n", "HEADER", pdb->Pdb); return ajTrue; } return ajFalse; } ID pdbioWriteHeaderScop TY static MO ajpdbio LB core XX DE Writes the Entry element of a Scop structure to an output file in pdb DE format XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r scop const AjPScop PD Scop object PX RT AjBool RD True on success RX // static AjBool pdbioWriteHeaderScop(AjPFile outf, const AjPScop scop) { if (scop && outf) { ajFmtPrintF(outf, "%-11sCLEANED-UP PDB FILE FOR SCOP DOMAIN %-33S\n", "HEADER", scop->Entry); return ajTrue; } return ajFalse; } ID pdbioWriteTitle TY static MO ajpdbio LB core XX DE Writes a TITLE record to an output file in pdb format DE The text is hard-coded. XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD Pdb object PX RT AjBool RD True on success RX // static AjBool pdbioWriteTitle(AjPFile outf, const AjPPdb pdb) { if (pdb && outf) { ajFmtPrintF(outf, "%-11sTHIS FILE IS MISSING MOST RECORDS FROM THE " "ORIGINAL PDB FILE%9s\n", "TITLE", " "); return ajTrue; } return ajFalse; } ID pdbioWriteCompnd TY static MO ajpdbio LB core XX DE Writes the Compnd element of a Pdb structure to an output file in pdb DE format XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD Pdb object PX RT AjBool RD True on success RX // static AjBool pdbioWriteCompnd(AjPFile outf, const AjPPdb pdb) { if (pdb && outf) { pdbioWriteText(outf, pdb->Compnd, "COMPND"); return ajTrue; } return ajFalse; } ID pdbioWriteSource TY static MO ajpdbio LB core XX DE Writes the Source element of a Pdb structure to an output file in pdb DE format XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD Pdb object PX RT AjBool RD True on success RX // static AjBool pdbioWriteSource(AjPFile outf, const AjPPdb pdb) { if (pdb && outf) { pdbioWriteText(outf, pdb->Source, "SOURCE"); return ajTrue; } return ajFalse; } ID pdbioWriteEmptyRemark TY static MO ajpdbio LB core XX DE Writes an empty REMARK record to an output file in pdb format XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD Pdb object PX RT AjBool RD True on success RX // static AjBool pdbioWriteEmptyRemark(AjPFile outf, const AjPPdb pdb) { if (pdb && outf) { ajFmtPrintF(outf, "%-11s%-69s\n", "REMARK", " "); return ajTrue; } return ajFalse; } ID pdbioWriteResolution TY static MO ajpdbio LB core XX DE Writes the Reso element of a Pdb structure to an output file in pdb DE format XX PN [1] PA w outf AjPFile PD Output file stream PX PN [2] PA r pdb const AjPPdb PD Pdb object PX RT AjBool RD True on success RX // static AjBool pdbioWriteResolution(AjPFile outf, const AjPPdb pdb) { if (pdb && outf) { ajFmtPrintF(outf, "%-11sRESOLUTION. %-6.2f%-51s\n", "REMARK", pdb->Reso, "ANGSTROMS."); return ajTrue; } return ajFalse; } ID pdbioReadLines TY static MO ajpdbio LB core XX DE Reads a pdb file and returns a pointer to a partially filled Pdbfile object. DE All of the lines from the pdb file are written to the <lines> array of the DE object and the <nlines> element is written. DE DE Memory for the object itself and any arrays whose size is equal to the DE number of lines is allocated: DE lines, linetype, chnn, gpn, modn, resni, resn1, resn2, pdbn, oddnum, atype, DE rtype, x,y,z,o,b, elementNum, elementId, elementType & helixClass. DE DE The following elements are written: DE nlines, lines. DE DE The linetype array is set to default value of pdbfileELinetypeIgnore XX PN [1] PA u inf AjPFile PD Pointer to pdb file PX RT AjPPdbfile RD Pdbfile object pointer, or NULL on failure. RX // static AjPPdbfile pdbioReadLines(AjPFile inf) { AjPPdbfile pdbfile = NULL; /* pdbfile object to be returned */ AjPList list = NULL; /* List of lines in pdb file */ AjPStr line = NULL; /* A line from a pdb file */ ajuint i = 0U; /* Check args */ if (!inf) return NULL; /* Allocate list and pdbfile object */ list = ajListstrNew(); /* Don't know number of lines or chains yet */ pdbfile = pdbioPdbfileNew(0, 0); /* Read pdb file and append lines to list */ line = ajStrNew(); while (ajReadlineTrim(inf, &line)) { ajListstrPushAppend(list, line); line = ajStrNew(); } /* Convert list to array in pdbfile object */ pdbfile->nlines = (ajuint) ajListstrToarray(list, &pdbfile->lines); if (pdbfile->nlines == 0) { ajStrDel(&line); ajListstrFree(&list); pdbioPdbfileDel(&pdbfile); return NULL; } /* Allocate memory for x,y,z,o,b, modn, chnn, linetype, ok, coord and * pdbn arrays in pdbfile object */ AJCNEW0(pdbfile->x, pdbfile->nlines); AJCNEW0(pdbfile->y, pdbfile->nlines); AJCNEW0(pdbfile->z, pdbfile->nlines); AJCNEW0(pdbfile->o, pdbfile->nlines); AJCNEW0(pdbfile->b, pdbfile->nlines); AJCNEW0(pdbfile->elementNum, pdbfile->nlines); AJCNEW0(pdbfile->elementType, pdbfile->nlines); AJCNEW0(pdbfile->helixClass, pdbfile->nlines); AJCNEW0(pdbfile->resni, pdbfile->nlines); AJCNEW0(pdbfile->resn1, pdbfile->nlines); AJCNEW0(pdbfile->resn2, pdbfile->nlines); AJCNEW0(pdbfile->modn, pdbfile->nlines); AJCNEW0(pdbfile->chnn, pdbfile->nlines); AJCNEW0(pdbfile->gpn, pdbfile->nlines); AJCNEW0(pdbfile->linetype, pdbfile->nlines); for (i = 0U; i < pdbfile->nlines; i++) pdbfile->linetype[i] = pdbfileELinetypeIgnore; AJCNEW0(pdbfile->pdbn, pdbfile->nlines); for (i = 0U; i < pdbfile->nlines; i++) pdbfile->pdbn[i] = ajStrNew(); AJCNEW0(pdbfile->elementId, pdbfile->nlines); for (i = 0U; i < pdbfile->nlines; i++) pdbfile->elementId[i] = ajStrNew(); AJCNEW0(pdbfile->atype, pdbfile->nlines); for (i = 0U; i < pdbfile->nlines; i++) pdbfile->atype[i] = ajStrNewRes(4); AJCNEW0(pdbfile->rtype, pdbfile->nlines); for (i = 0U; i < pdbfile->nlines; i++) pdbfile->rtype[i] = ajStrNewRes(4); AJCNEW0(pdbfile->oddnum, pdbfile->nlines); for (i = 0U; i < pdbfile->nlines; i++) pdbfile->oddnum[i] = ajFalse; #if AJFALSE /* DIAGNOSTIC */ for (i = 0U; i < pdbfile->nlines; i++) { ajFmtPrintF(tempfile, "%S\n", pdbfile->lines[i]); fflush(tempfile->fp); } #endif /* AJFALSE */ /* Tidy up and return */ ajStrDel(&line); ajListstrFree(&list); return pdbfile; } ID pdbioPdbfileDel TY static MO ajpdbio LB core XX DE Destructor for a PDB File object. XX PN [1] PA d Ppdbfile AjPPdbfile* PD PDB File address PX RT void RD RX // static void pdbioPdbfileDel(AjPPdbfile *Ppdbfile) { ajuint i = 0U; /* Check args */ if (!*Ppdbfile) { ajWarn("NULL arg passed to pdbioPdbfileDel.\n"); return; } if ((*Ppdbfile)->resn1ok) AJFREE((*Ppdbfile)->resn1ok); if ((*Ppdbfile)->nres) AJFREE((*Ppdbfile)->nres); if ((*Ppdbfile)->nligands) AJFREE((*Ppdbfile)->nligands); if ((*Ppdbfile)->chainok) AJFREE((*Ppdbfile)->chainok); if ((*Ppdbfile)->pdbid) ajStrDel(&(*Ppdbfile)->pdbid); if ((*Ppdbfile)->compnd) ajStrDel(&(*Ppdbfile)->compnd); if ((*Ppdbfile)->source) ajStrDel(&(*Ppdbfile)->source); if ((*Ppdbfile)->modn) AJFREE((*Ppdbfile)->modn); if ((*Ppdbfile)->x) AJFREE((*Ppdbfile)->x); if ((*Ppdbfile)->y) AJFREE((*Ppdbfile)->y); if ((*Ppdbfile)->z) AJFREE((*Ppdbfile)->z); if ((*Ppdbfile)->o) AJFREE((*Ppdbfile)->o); if ((*Ppdbfile)->b) AJFREE((*Ppdbfile)->b); if ((*Ppdbfile)->numHelices) AJFREE((*Ppdbfile)->numHelices); if ((*Ppdbfile)->numStrands) AJFREE((*Ppdbfile)->numStrands); if ((*Ppdbfile)->numSheets) AJFREE((*Ppdbfile)->numSheets); if ((*Ppdbfile)->numTurns) AJFREE((*Ppdbfile)->numTurns); if ((*Ppdbfile)->elementNum) AJFREE((*Ppdbfile)->elementNum); if ((*Ppdbfile)->elementType) AJFREE((*Ppdbfile)->elementType); if ((*Ppdbfile)->helixClass) AJFREE((*Ppdbfile)->helixClass); if ((*Ppdbfile)->resni) AJFREE((*Ppdbfile)->resni); if ((*Ppdbfile)->resn1) AJFREE((*Ppdbfile)->resn1); if ((*Ppdbfile)->resn2) AJFREE((*Ppdbfile)->resn2); if ((*Ppdbfile)->chnn) AJFREE((*Ppdbfile)->chnn); if ((*Ppdbfile)->gpn) AJFREE((*Ppdbfile)->gpn); if ((*Ppdbfile)->linetype) AJFREE((*Ppdbfile)->linetype); if ((*Ppdbfile)->oddnum) AJFREE((*Ppdbfile)->oddnum); if ((*Ppdbfile)->lines) { for (i = 0U; i < (*Ppdbfile)->nlines; i++) ajStrDel(&(*Ppdbfile)->lines[i]); AJFREE((*Ppdbfile)->lines); } if ((*Ppdbfile)->pdbn) { for (i = 0U; i < (*Ppdbfile)->nlines; i++) ajStrDel(&(*Ppdbfile)->pdbn[i]); AJFREE((*Ppdbfile)->pdbn); } if ((*Ppdbfile)->elementId) { for (i = 0U; i < (*Ppdbfile)->nlines; i++) ajStrDel(&(*Ppdbfile)->elementId[i]); AJFREE((*Ppdbfile)->elementId); } if ((*Ppdbfile)->atype) { for (i = 0U; i < (*Ppdbfile)->nlines; i++) ajStrDel(&(*Ppdbfile)->atype[i]); AJFREE((*Ppdbfile)->atype); } if ((*Ppdbfile)->rtype) { for (i = 0U; i < (*Ppdbfile)->nlines; i++) ajStrDel(&(*Ppdbfile)->rtype[i]); AJFREE((*Ppdbfile)->rtype); } if ((*Ppdbfile)->seqres) { for (i = 0U; i < (*Ppdbfile)->nchains; i++) ajStrDel(&(*Ppdbfile)->seqres[i]); AJFREE((*Ppdbfile)->seqres); } if ((*Ppdbfile)->seqresful) { for (i = 0U; i < (*Ppdbfile)->nchains; i++) ajStrDel(&(*Ppdbfile)->seqresful[i]); AJFREE((*Ppdbfile)->seqresful); } if ((*Ppdbfile)->chid) ajChararrDel(&(*Ppdbfile)->chid); if ((*Ppdbfile)->gpid) ajChararrDel(&(*Ppdbfile)->gpid); AJFREE(*Ppdbfile); *Ppdbfile = NULL; return; } ID pdbioElementsNew TY static MO ajpdbio LB core XX DE Constructor for Elements object. XX PN [1] PA r nelms ajuint PD Number of elements PX RT AjPElements RD Pointer to Elements object, or NULL on failure. RX // static AjPElements pdbioElementsNew(ajuint nelms) { ajuint i = 0U; AjPElements elements = NULL; AJNEW0(elements); elements->n = nelms; if (nelms) { AJCNEW0(elements->elms, nelms); for (i = 0U; i < nelms; i++) elements->elms[i] = pdbioElementNew(); } #if AJFALSE else ajWarn("Value of zero passed to pdbioElementsNew"); #endif /* AJFALSE */ return elements; } ID pdbioElementsDel TY static MO ajpdbio LB core XX DE Destructor for Elements object. XX PN [1] PA d Pelements AjPElements* PD Elements address PX RT void RD RX // static void pdbioElementsDel(AjPElements *Pelements) { ajuint i = 0U; if (!*Pelements) { ajWarn("NULL arg passed to pdbioElementsDel"); return; } if ((*Pelements)->elms) { for (i = 0U; i < (*Pelements)->n; i++) pdbioElementDel(&(*Pelements)->elms[i]); AJFREE((*Pelements)->elms); } AJFREE(*Pelements); *Pelements = NULL; return; } ID pdbioElementNew TY static MO ajpdbio LB core XX DE Constructor for Element object. XX RT AjPElement RD Element object or NULL RX // static AjPElement pdbioElementNew(void) { AjPElement element = NULL; AJNEW0(element); element->elementId = ajStrNew(); element->initResName = ajStrNew(); element->initSeqNum = ajStrNew(); element->endResName = ajStrNew(); element->endSeqNum = ajStrNew(); element->elementNum = 0; element->elementType = ' '; element->helixClass = 0; element->chainId = ' '; return element; } ID pdbioElementDel TY static MO ajpdbio LB core XX DE Destructor for Element object. XX PN [1] PA d Pelement AjPElement* PD Element object pointer PX RT void RD RX // static void pdbioElementDel(AjPElement *Pelement) { ajStrDel(&((*Pelement)->elementId)); ajStrDel(&((*Pelement)->initResName)); ajStrDel(&((*Pelement)->initSeqNum)); ajStrDel(&((*Pelement)->endResName)); ajStrDel(&((*Pelement)->endSeqNum)); AJFREE(*Pelement); *Pelement = NULL; return; } ID pdbioPdbfileNew TY static MO ajpdbio LB core XX DE Constructor for Pdbfile object. XX PN [1] PA r nlines ajuint PD No. of lines in pdb file PX PN [2] PA r nchains ajuint PD No. of chains in pdb file PX RT AjPPdbfile RD Pointer to pdbfile object, or NULL on failure. RX // static AjPPdbfile pdbioPdbfileNew(ajuint nlines, ajuint nchains) { ajuint i = 0U; AjPPdbfile pdbfile = NULL; AJNEW0(pdbfile); pdbfile->pdbid = ajStrNew(); pdbfile->compnd = ajStrNew(); pdbfile->source = ajStrNew(); pdbfile->nomod = ajFalse; pdbfile->toofewter = ajFalse; pdbfile->nchains = nchains; if (nchains) { AJCNEW0(pdbfile->resn1ok, nchains); for (i = 0U; i < nchains; i++) pdbfile->resn1ok[i] = ajTrue; AJCNEW0(pdbfile->numHelices, nchains); for (i = 0U; i < nchains; i++) pdbfile->numHelices[i] = ajTrue; AJCNEW0(pdbfile->numStrands, nchains); for (i = 0U; i < nchains; i++) pdbfile->numStrands[i] = ajTrue; AJCNEW0(pdbfile->numSheets, nchains); for (i = 0U; i < nchains; i++) pdbfile->numSheets[i] = ajTrue; AJCNEW0(pdbfile->numTurns, nchains); for (i = 0U; i < nchains; i++) pdbfile->numTurns[i] = ajTrue; AJCNEW0(pdbfile->chainok, nchains); for (i = 0U; i < nchains; i++) pdbfile->chainok[i] = ajTrue; AJCNEW0(pdbfile->nres, nchains); AJCNEW0(pdbfile->nligands, nchains); AJCNEW0(pdbfile->seqres, nchains); for (i = 0U; i < nchains; i++) pdbfile->seqres[i] = ajStrNew(); AJCNEW0(pdbfile->seqresful, nchains); for (i = 0U; i < nchains; i++) pdbfile->seqresful[i] = ajStrNew(); pdbfile->chid = ajChararrNewRes(nchains); } else pdbfile->chid = ajChararrNew(); pdbfile->gpid = ajChararrNew(); pdbfile->nlines = nlines; if (nlines) { AJCNEW0(pdbfile->x, nlines); AJCNEW0(pdbfile->y, nlines); AJCNEW0(pdbfile->z, nlines); AJCNEW0(pdbfile->o, nlines); AJCNEW0(pdbfile->b, nlines); AJCNEW0(pdbfile->elementNum, nlines); AJCNEW0(pdbfile->elementType, nlines); AJCNEW0(pdbfile->helixClass, nlines); AJCNEW0(pdbfile->resni, nlines); AJCNEW0(pdbfile->resn1, nlines); AJCNEW0(pdbfile->resn2, nlines); AJCNEW0(pdbfile->modn, nlines); AJCNEW0(pdbfile->chnn, nlines); AJCNEW0(pdbfile->gpn, nlines); AJCNEW0(pdbfile->linetype, nlines); AJCNEW0(pdbfile->oddnum, nlines); AJCNEW0(pdbfile->lines, nlines); AJCNEW0(pdbfile->pdbn, nlines); AJCNEW0(pdbfile->elementId, nlines); AJCNEW0(pdbfile->atype, nlines); AJCNEW0(pdbfile->rtype, nlines); for (i = 0U; i < nlines; i++) { pdbfile->linetype[i] = pdbfileELinetypeIgnore; pdbfile->oddnum[i] = ajFalse; pdbfile->lines[i] = ajStrNew(); pdbfile->pdbn[i] = ajStrNew(); pdbfile->elementId[i] = ajStrNew(); pdbfile->atype[i] = ajStrNewRes(4); pdbfile->rtype[i] = ajStrNewRes(4); } } #if AJFALSE else ajWarn("Zero sized arg passed to pdbioPdbfileNew.\n"); #endif /* AJFALSE */ return pdbfile; } ID pdbioFirstPass TY static MO ajpdbio LB core XX DE The initial read of the pdb file as held in the <lines> array of a Pdbfile DE object. Bibliographic information is parsed, the number of chains DE determined and the sequences and chain ids from the SEQRES records are DE parsed. The line type (see below) of each line is determined, and for DE lines with coordinates, the residue type is parsed. A count of the number DE of TER records and the location of the first coordinate line is also DE determined. DE Secondary structure information is also parsed and an AjPElements DE object is written. DE DE Memory for any arrays whose size is equal to the number of chains is DE allocated: DE seqres, seqresful, nres, chainok, resn1ok, nligands, numHelices, DE numStrands, numSheets, numTurns DE DE The following arrays are written: DE seqres, seqresful, chid, pdbn, resn1 and resn2, rtype, linetype DE DE The following elements are written: DE nchains, tercnt, modcnt, nomod, source, compnd, method, reso, idxfirst DE DE The chainok & resn1ok arrays are set to default values of ajTrue DE DE Writing resn1/resn2 & pdbn arrays DE The pdbn array is the raw residue number (as a string) and is filled for DE lines for which <linetype> == pdbfileELinetypeCoordinate. The resn1/resn2 arrays are DE given initial values which at this stage are simply the integer component DE of pdbn. The values for resn1/resn2 are changed later in the program. DE DE Writing modcnt and nomod elements DE modcnt is a count of the number of MODEL records (excluding duplicate DE records). However, if no MODEL records are found, modcnt is set to the DE minimum value of 1, and nomod is set to ajTrue. DE DE Writing linetype array DE The linetype array is set as follows: DE DE pdbfileELinetypeCoordinate for ATOM or HETATM records which contain both atom and DE residue identifier codes and which are not duplicate positions. DE Duplicate positions for (i) whole residues or (ii) individual atoms DE are presumed where a ATOM or HETATM record uses a value other than '1' or DE 'A' in the (i) residue alternate location indicator (column 17) or (ii) DE the first column of the atom name (column 13) respectively. DE DE pdbfileELinetypeTER for TER records DE DE pdbfileELinetypeMODEL for MODEL records DE DE pdbfileELinetypeWater for HOH (should be HETATM records) DE DE For all other lines, it is left as the default of pdbfileELinetypeIgnore (the value DE might change later in the program). XX PN [1] PA w pdbfile AjPPdbfile PD Pdbfile object PX PN [2] PA u flog AjPFile PD Log file (build diagnostics) PX PN [3] PA w elms AjPElements* PD Elements object pointer PX PN [4] PA r camask AjBool PD Whether to mask non-amino acid residues PD within protein chains which do not PD have a C-alpha atom. PX RT AjBool RD True if file was parsed, False otherwise RX // static AjBool pdbioFirstPass(AjPPdbfile pdbfile, AjPFile flog, AjPElements *elms, AjBool camask) { ajuint i = 0U; /* Loop counter */ ajuint j = 0U; /* Loop counter */ ajuint k = 0U; /* Loop counter */ AjBool donefirstatom = ajFalse; /* Flag for finding first ATOM or * HETATM line */ char pdbn[6]; /* Residue number */ AjBool resolfound = ajFalse;/* Flag for finding RESOLUTION record */ AjBool seqresfound = ajFalse; /* Flag for finding SEQRES record */ AjPStr tmpstr = NULL; /* A temp. string */ AjPStr seqres = NULL; /* Sequence from SEQRES records */ ajuint seqreslen = 0U; /* Indicated length of sequence from SEQRES * records */ ajuint lenful = 0U; /* Length of SEQRES sequence including ACE, * FOR & NH2 groups that might be discarded * by the call to pdbioSeqresToSequence */ char last_id = ' '; /* CHain id of last SEQRES line read */ AjPStr tmpseq = NULL; /* A temp. string for a sequence */ AjPStr tmpseqful = NULL; /* A temp. string for a sequence */ AjPList listseqs = NULL; /* For list of sequences from SEQRES records */ AjPList listseqsful = NULL; /* For list of sequences (using 3-letter * codes) from SEQRES records */ AjBool done_msg = ajFalse; /* Flag for error messaging */ AjPList listelms = NULL; /* Temp. list of secondary structure elements * (from HELIX, SHEET and TURN records) */ AjPElement elm = NULL; /* Temp. Element object pointer */ AjPElement FirstStrand = NULL; /* Temp. pointer to first strand of * each sheet */ AjBool doneFirstStrand = ajFalse; /* Flag for parsing first strand of * each sheet */ AjPStr LastSheetId = NULL; /* Sheet identifier of the last sheet read in */ /* Check args */ if (!pdbfile || !flog || !(*elms)) { ajWarn("Bad args passed to pdbioFirstPass\n"); return ajFalse; } /* Allocate memory etc */ tmpstr = ajStrNew(); seqres = ajStrNew(); LastSheetId = ajStrNew(); listseqs = ajListstrNew(); listseqsful = ajListstrNew(); listelms = ajListNew(); /* Start of main loop */ for (i = 0U; i < pdbfile->nlines; i++) { if ((ajStrPrefixC(pdbfile->lines[i], "ATOM")) || (ajStrPrefixC(pdbfile->lines[i], "HETATM"))) { /* In instances where >1 residue positions are given, ignore all * but position 'A' & '1' In instances where >1 atom positions * are given, ignore all but position '1' */ if (((ajStrGetCharPos(pdbfile->lines[i], 16) != ' ') && ((ajStrGetCharPos(pdbfile->lines[i], 16) != 'A') && (ajStrGetCharPos(pdbfile->lines[i], 16) != '1'))) || ((ajStrGetCharPos(pdbfile->lines[i], 12) != ' ') && ((ajStrGetCharPos(pdbfile->lines[i], 12) != '1') && (isdigit((int) ajStrGetCharPos(pdbfile->lines[i], 12)))))) { if (!done_msg) { ajFmtPrintF(flog, "%-15s%d\n", "DUPATOMRES", i + 1); done_msg = ajTrue; } } /* In instances where no atom or residue identity is given, * ignore line */ else if ((ajStrGetCharPos(pdbfile->lines[i], 12) == ' ' && ajStrGetCharPos(pdbfile->lines[i], 13) == ' ' && ajStrGetCharPos(pdbfile->lines[i], 14) == ' ') || (ajStrGetCharPos(pdbfile->lines[i], 17) == ' ' && ajStrGetCharPos(pdbfile->lines[i], 19) == ' ')) { ajFmtPrintF(flog, "%-15s%d\n", "NOATOMRESID", i + 1); } else { pdbfile->linetype[i] = pdbfileELinetypeCoordinate; if (!donefirstatom) { donefirstatom = ajTrue; pdbfile->idxfirst = i; } /* Write residue number for the line */ for (k = 22, j = 0; k <= 26; k++) if ((isalnum((int) ajStrGetCharPos(pdbfile->lines[i], k))) || ajStrGetCharPos(pdbfile->lines[i], k) == '-') pdbn[j++] = ajStrGetCharPos(pdbfile->lines[i], k); pdbn[j] = '\0'; ajStrAssignC(&(pdbfile->pdbn[i]), pdbn); if (!ajFmtScanS(pdbfile->pdbn[i], "%d", &(pdbfile->resn1[i]))) { ajFmtPrintF(flog, "%-15s%d\n", "ATOMNONUM", i + 1); pdbfile->linetype[i] = pdbfileELinetypeIgnore; } else { pdbfile->resn2[i] = pdbfile->resn1[i]; /* Assign residue type */ ajStrAssignSubS(&pdbfile->rtype[i], pdbfile->lines[i], 17, 19); ajStrRemoveWhite(&pdbfile->rtype[i]); /* JONNEW */ if (ajStrMatchC(pdbfile->rtype[i], "HOH")) pdbfile->linetype[i] = pdbfileELinetypeWater; } } } else if (ajStrPrefixC(pdbfile->lines[i], "SEQRES")) { seqresfound = ajTrue; /* Read first SEQRES line */ ajStrAssignSubS(&seqres, pdbfile->lines[i], 14, 18); if (!ajFmtScanS(seqres, "%u", &seqreslen)) ajFmtPrintF(flog, "%-15s%d (%c)\n", "SEQRESLEN", pdbfile->nchains, ajStrGetCharPos(pdbfile->lines[i], 11)); ajStrAssignSubS(&seqres, pdbfile->lines[i], 19, 70); /* Append a ' ' in case this is missing from the PDB file, e.g. * pdb1iie.ent */ ajStrAppendK(&seqres, ' '); ajChararrPut(&(pdbfile->chid), 0, (last_id = ajStrGetCharPos(pdbfile->lines[i], 11))); pdbfile->nchains++; /* Read subsequent SEQRES lines */ for (i++; i < pdbfile->nlines; i++) if (ajStrPrefixC(pdbfile->lines[i], "SEQRES")) { /* Still on same chain */ if (ajStrGetCharPos(pdbfile->lines[i], 11) == last_id) { ajStrAppendSubS(&seqres, pdbfile->lines[i], 19, 70); /* Append a ' ' in case this is missing from the PDB * file, e.g. pdb1iie.ent */ ajStrAppendK(&seqres, ' '); } /* On new chain */ else { tmpseq = ajStrNew(); /* Process last chain */ if (!pdbioSeqresToSequence(seqres, &tmpseq, camask, &lenful)) { ajWarn("Sequence conversion error in" " pdbioFirstPass\nEmail jison@hgmp.mrc.ac.uk\n"); ajStrDel(&tmpseq); continue; } tmpseqful = ajStrNew(); ajStrAssignS(&tmpseqful, seqres); /* Check length of sequence vs indicated length */ if (lenful != seqreslen) ajFmtPrintF(flog, "%-15s%d (%c)\n", "SEQRESLENDIF", pdbfile->nchains, last_id); /* Push sequences onto lists */ ajListstrPushAppend(listseqs, tmpseq); ajListstrPushAppend(listseqsful, tmpseqful); /* Read first SEQRES line of new chain */ ajStrAssignSubS(&seqres, pdbfile->lines[i], 14, 18); if (!ajFmtScanS(seqres, "%u", &seqreslen)) ajFmtPrintF(flog, "%-15s%d (%c)\n", "SEQRESLEN", pdbfile->nchains, ajStrGetCharPos(pdbfile->lines[i], 11)); ajStrAssignSubS(&seqres, pdbfile->lines[i], 19, 70); /* Append a ' ' in case this is missing from the PDB * file, e.g. pdb1iie.ent */ ajStrAppendK(&seqres, ' '); ajChararrPut(&(pdbfile->chid), pdbfile->nchains, (last_id = ajStrGetCharPos(pdbfile->lines[i], 11))); #if AJFALSE if ((last_id = ajStrGetCharPos(pdbfile->lines[i], 11)) == ' ') ajChararrPut(&(pdbfile->chid), pdbfile->nchains, '.'); else ajChararrPut(&(pdbfile->chid), pdbfile->nchains, last_id); #endif /* AJFALSE */ pdbfile->nchains++; } } else { tmpseq = ajStrNew(); /* Process last chain */ if (!pdbioSeqresToSequence(seqres, &tmpseq, camask, &lenful)) { ajWarn("Sequence conversion error in " "pdbioFirstPass\nEmail jison@hgmp.mrc.ac.uk\n"); ajStrDel(&tmpseq); continue; } tmpseqful = ajStrNew(); ajStrAssignS(&tmpseqful, seqres); /* Check length of sequence vs indicated length */ if (lenful != seqreslen) { ajFmtPrintF(flog, "%-15s%d (%c)\n", "SEQRESLENDIF", pdbfile->nchains, last_id); } /* Push sequences onto lists */ ajListstrPushAppend(listseqs, tmpseq); ajListstrPushAppend(listseqsful, tmpseqful); /* Convert lists to arrays in pdbfile object and delete * list */ ajListstrToarray(listseqs, &(pdbfile->seqres)); ajListFree(&listseqs); ajListstrToarray(listseqsful, &(pdbfile->seqresful)); ajListFree(&listseqsful); /* i will get incremented in main loop */ i--; break; } } else if (ajStrPrefixC(pdbfile->lines[i], "COMPND")) { /* Read first COMPND line */ ajStrAssignSubS(&(pdbfile->compnd), pdbfile->lines[i], 10, 71); /* Read subsequent COMPND lines */ for (i++; i < pdbfile->nlines; i++) if (ajStrPrefixC(pdbfile->lines[i], "COMPND")) { ajStrAppendSubS(&(pdbfile->compnd), pdbfile->lines[i], 10, 71); } else { ajStrRemoveWhiteExcess(&(pdbfile->compnd)); /* i will get incremented in main loop */ i--; break; } } else if (ajStrPrefixC(pdbfile->lines[i], "SOURCE")) { /* Read first SOURCE line */ ajStrAssignSubS(&(pdbfile->source), pdbfile->lines[i], 10, 71); /* Read subsequent SOURCE lines */ for (i++; i < pdbfile->nlines; i++) if (ajStrPrefixC(pdbfile->lines[i], "SOURCE")) { ajStrAppendSubS(&(pdbfile->source), pdbfile->lines[i], 10, 71); } else { ajStrRemoveWhiteExcess(&(pdbfile->source)); /* i will get incremented in main loop */ i--; break; } } else if (ajStrPrefixC(pdbfile->lines[i], "TER")) { /* By default ok == ajTrue */ /* pdbfile->ok[i] = ajTrue; */ pdbfile->tercnt++; pdbfile->linetype[i] = pdbfileELinetypeTER; } else if (ajStrPrefixC(pdbfile->lines[i], "MODEL")) { pdbfile->modcnt++; pdbfile->linetype[i] = pdbfileELinetypeMODEL; if (!donefirstatom) { donefirstatom = ajTrue; pdbfile->idxfirst = i; } } else if (ajStrPrefixC(pdbfile->lines[i], "ENDMDL")) { pdbfile->linetype[i] = pdbfileELinetypeENDMDL; } else if ((!resolfound) && (ajStrPrefixC(pdbfile->lines[i], "REMARK"))) { /* Assign method and resolution */ ajFmtScanS(pdbfile->lines[i], "%*s %*d %S", &tmpstr); if (!ajStrCmpLenC(tmpstr, "RESOLUTION", 10)) { resolfound = ajTrue; if (isdigit((int) ajStrGetCharPos(pdbfile->lines[i], 23))) { if ((ajFmtScanS(pdbfile->lines[i], "%*s %*d %*s %f", &(pdbfile->reso))) != 1) ajFmtPrintF(flog, "%-15s\n", "RESOLUNK"); pdbfile->method = ajEPdbMethodXray; } else { pdbfile->reso = 0; pdbfile->method = ajEPdbMethodNmr; } } } else if (ajStrPrefixC(pdbfile->lines[i], "HELIX")) { doneFirstStrand = ajFalse; elm = pdbioElementNew(); ajStrAssignSubS(&tmpstr, pdbfile->lines[i], 7, 9); ajStrRemoveWhite(&tmpstr); ajFmtScanS(tmpstr, "%d", &elm->elementNum); ajStrAssignSubS(&elm->elementId, pdbfile->lines[i], 11, 13); ajStrRemoveWhite(&elm->elementId); elm->elementType = 'H'; ajStrAssignSubS(&elm->initResName, pdbfile->lines[i], 15, 17); ajStrRemoveWhite(&elm->initResName); ajStrAssignSubS(&elm->initSeqNum, pdbfile->lines[i], 21, 25); ajStrRemoveWhite(&elm->initSeqNum); ajStrAssignSubS(&elm->endResName, pdbfile->lines[i], 27, 29); ajStrRemoveWhite(&elm->endResName); ajStrAssignSubS(&elm->endSeqNum, pdbfile->lines[i], 33, 37); ajStrRemoveWhite(&elm->endSeqNum); elm->chainId = ajStrGetCharPos(pdbfile->lines[i], 19); if (elm->chainId != ajStrGetCharPos(pdbfile->lines[i], 31)) { ajFmtPrintF(flog, "%-15s%c %c\n", "SECTWOCHN", elm->chainId, ajStrGetCharPos(pdbfile->lines[i], 31)); pdbioElementDel(&elm); continue; } ajStrAssignSubS(&tmpstr, pdbfile->lines[i], 38, 39); ajStrRemoveWhite(&tmpstr); ajFmtScanS(tmpstr, "%d", &elm->helixClass); /* Check that all records are present and flag an error if * they're not */ if (MAJSTRGETLEN(elm->initResName) && MAJSTRGETLEN(elm->initSeqNum) && MAJSTRGETLEN(elm->endResName) && MAJSTRGETLEN(elm->endSeqNum) && MAJSTRGETLEN(elm->elementId)) { ajListPushAppend(listelms, elm); } else { ajFmtPrintF(flog, "%-15s%d\n", "SECMISS", i + 1); pdbioElementDel(&elm); continue; } } else if (ajStrPrefixC(pdbfile->lines[i], "SHEET")) { elm = pdbioElementNew(); ajStrAssignSubS(&tmpstr, pdbfile->lines[i], 7, 9); ajStrRemoveWhite(&tmpstr); ajFmtScanS(tmpstr, "%d", &elm->elementNum); ajStrAssignSubS(&elm->elementId, pdbfile->lines[i], 11, 13); ajStrRemoveWhite(&elm->elementId); if (!ajStrMatchS(elm->elementId, LastSheetId)) doneFirstStrand = ajFalse; elm->elementType = 'E'; ajStrAssignSubS(&elm->initResName, pdbfile->lines[i], 17, 19); ajStrRemoveWhite(&elm->initResName); ajStrAssignSubS(&elm->initSeqNum, pdbfile->lines[i], 22, 26); ajStrRemoveWhite(&elm->initSeqNum); ajStrAssignSubS(&elm->endResName, pdbfile->lines[i], 28, 30); ajStrRemoveWhite(&elm->endResName); ajStrAssignSubS(&elm->endSeqNum, pdbfile->lines[i], 33, 37); ajStrRemoveWhite(&elm->endSeqNum); elm->chainId = ajStrGetCharPos(pdbfile->lines[i], 21); if (elm->chainId != ajStrGetCharPos(pdbfile->lines[i], 32)) { ajFmtPrintF(flog, "%-15s%c %c\n", "SECTWOCHN", elm->chainId, ajStrGetCharPos(pdbfile->lines[i], 32)); pdbioElementDel(&elm); continue; } /* Check for beta-barrels - where the first and last strands are * identical requiring us to ignore the last strand */ if (doneFirstStrand) { if (ajStrMatchS(elm->initResName, FirstStrand->initResName) && ajStrMatchS(elm->endResName, FirstStrand->endResName) && ajStrMatchS(elm->initSeqNum, FirstStrand->initSeqNum) && ajStrMatchS(elm->endSeqNum, FirstStrand->endSeqNum)) { pdbioElementDel(&elm); continue; } } /* Check that all records are present and flag an error if * they're not */ if (MAJSTRGETLEN(elm->initResName) && MAJSTRGETLEN(elm->initSeqNum) && MAJSTRGETLEN(elm->endResName) && MAJSTRGETLEN(elm->endSeqNum) && MAJSTRGETLEN(elm->elementId)) { ajListPushAppend(listelms, elm); } else { ajFmtPrintF(flog, "%-15s%d\n", "SECMISS", i + 1); pdbioElementDel(&elm); continue; } ajStrAssignS(&LastSheetId, elm->elementId); FirstStrand = elm; doneFirstStrand = ajTrue; } else if (ajStrPrefixC(pdbfile->lines[i], "TURN")) { doneFirstStrand = ajFalse; elm = pdbioElementNew(); ajStrAssignSubS(&tmpstr, pdbfile->lines[i], 7, 9); ajStrRemoveWhite(&tmpstr); ajFmtScanS(tmpstr, "%d", &elm->elementNum); ajStrAssignSubS(&elm->elementId, pdbfile->lines[i], 11, 13); ajStrRemoveWhite(&elm->elementId); elm->elementType = 'T'; ajStrAssignSubS(&elm->initResName, pdbfile->lines[i], 15, 17); ajStrRemoveWhite(&elm->initResName); ajStrAssignSubS(&elm->initSeqNum, pdbfile->lines[i], 20, 24); ajStrRemoveWhite(&elm->initSeqNum); ajStrAssignSubS(&elm->endResName, pdbfile->lines[i], 26, 28); ajStrRemoveWhite(&elm->endResName); ajStrAssignSubS(&elm->endSeqNum, pdbfile->lines[i], 31, 35); ajStrRemoveWhite(&elm->endSeqNum); elm->chainId = ajStrGetCharPos(pdbfile->lines[i], 19); if (elm->chainId != ajStrGetCharPos(pdbfile->lines[i], 30)) { ajFmtPrintF(flog, "%-15s%c %c\n", "SECTWOCHN", elm->chainId, ajStrGetCharPos(pdbfile->lines[i], 30)); pdbioElementDel(&elm); continue; } /* Check that all records are present and flag an error if * they're not */ if (MAJSTRGETLEN(elm->initResName) && MAJSTRGETLEN(elm->initSeqNum) && MAJSTRGETLEN(elm->endResName) && MAJSTRGETLEN(elm->endSeqNum) && MAJSTRGETLEN(elm->elementId)) { ajListPushAppend(listelms, elm); } else { ajFmtPrintF(flog, "%-15s%d\n", "SECMISS", i + 1); pdbioElementDel(&elm); continue; } } } /* Write array in Elements structure */ (*elms)->n = (ajuint) ajListToarray(listelms, (void ***) &(*elms)->elms); /* Generate diagnostics and set defaults */ if ((ajStrGetLen(pdbfile->compnd) == 0)) { ajStrAssignC(&pdbfile->compnd, "."); ajFmtPrintF(flog, "%-15s\n", "NOCOMPND"); } if ((ajStrGetLen(pdbfile->source) == 0)) { ajStrAssignC(&pdbfile->source, "."); ajFmtPrintF(flog, "%-15s\n", "NOSOURCE"); } if ((pdbfile->method == ajEPdbMethodNmr) && (pdbfile->modcnt == 0)) ajFmtPrintF(flog, "%-15s\n", "NOMODEL"); if (!E_FPZERO(pdbfile->reso, U_FEPS) && pdbfile->modcnt) { ajFmtPrintF(flog, "%-15s\n", "RESOLMOD"); pdbfile->method = ajEPdbMethodNmr; } /* Every pdb file is considered to have at least one model */ if (pdbfile->modcnt == 0) { pdbfile->modcnt = 1; pdbfile->nomod = ajTrue; } if (!resolfound) { pdbfile->reso = 0; pdbfile->method = ajEPdbMethodNmr; ajFmtPrintF(flog, "%-15s\n", "NORESOLUTION"); } if (!seqresfound) { ajWarn("No SEQRES record found in raw pdb file"); ajFmtPrintF(flog, "%-15s\n", "NOSEQRES"); /* Free memory and return */ ajListFree(&listelms); ajStrDel(&LastSheetId); ajStrDel(&tmpstr); ajStrDel(&seqres); return ajFalse; } if (!donefirstatom) { ajWarn("No ATOM record found in raw pdb file"); ajFmtPrintF(flog, "%-15s\n", "NOATOM"); /* Free memory and return */ ajListFree(&listelms); ajStrDel(&LastSheetId); ajStrDel(&tmpstr); ajStrDel(&seqres); return ajFalse; } AJCNEW0(pdbfile->nligands, pdbfile->nchains); AJCNEW0(pdbfile->nres, pdbfile->nchains); AJCNEW0(pdbfile->numHelices, pdbfile->nchains); AJCNEW0(pdbfile->numStrands, pdbfile->nchains); AJCNEW0(pdbfile->numSheets, pdbfile->nchains); AJCNEW0(pdbfile->numTurns, pdbfile->nchains); AJCNEW0(pdbfile->chainok, pdbfile->nchains); AJCNEW0(pdbfile->resn1ok, pdbfile->nchains); for (i = 0U; i < pdbfile->nchains; i++) { pdbfile->chainok[i] = ajTrue; pdbfile->resn1ok[i] = ajTrue; } /* Free memory and return */ ajStrDel(&LastSheetId); ajListFree(&listelms); ajStrDel(&tmpstr); ajStrDel(&seqres); return ajTrue; } ID pdbioSeqresToSequence TY static MO ajpdbio LB core XX DE Reads a string containing a SEQRES sequence (e.g. "ALA ALA LEU" ) and DE writes a string containing a normal sequence (e.g. "AAL"). XX PN [1] PA r seqres const AjPStr PD SEQRES sequence PX PN [2] PA w seq AjPStr* PD Output sequence PX PN [3] PA r camask AjBool PD Whether to ignore residues which do not PD have a C-alpha atom, these are defined as ACE, FOR and NH2 groups. PX PN [4] PA w len ajuint* PD Length of sequence INCLUDING ACE, FOR and PD NH2 groups. PX RT AjBool RD ajTrue on success, ajFalse otherwise. RX // static AjBool pdbioSeqresToSequence(const AjPStr seqres, AjPStr *seq, AjBool camask, ajuint *len) { const AjPStr aa3 = NULL; char aa1 = '\0'; ajuint nrem = 0; /* No. 'residues' that were removed */ /* Check args */ if (!seqres || !seq) { ajWarn("Bad args passed to pdbioSeqresToSequence\n"); return ajFalse; } /* Allocate memory */ if ((aa3 = ajStrParseC(seqres, " \n"))) { /* Parse seqres string */ do { if (ajStrMatchC(aa3, "FOR") || ajStrMatchC(aa3, "ACE") || ajStrMatchC(aa3, "NH2")) { if (camask) { nrem++; continue; } } ajResidueFromTriplet(aa3, &aa1); ajStrAppendK(seq, aa1); } while ((aa3 = ajStrParseC(NULL, " \n"))); } else return ajFalse; *len = ajStrGetLen(*seq) + nrem; return ajTrue; } ID pdbioCheckChains TY static MO ajpdbio LB core XX DE Reads a Pdbfile object and checks whether chains from the SEQRES records DE (i) use unique chain ids, (ii) do not use an id of a space (' ') alongside DE non-space chain ids and (iii) contain at least the user-defined threshold DE number of amino acid residues. If any of these conditions are not met then DE the chain is discarded (chainok array is set to ajFalse). DE DE The chainok array is written. XX PN [1] PA w pdbfile AjPPdbfile PD Pdbfile object PX PN [2] PA u flog AjPFile PD Pointer to log file (build PD diagnostics). PX PN [3] PA r min_chain_size ajint PD Minimum number of amino acids in PD a chain. PX RT AjBool RD True if SEQRES records contained at least one protein RD chain, False otherwise. RX // static AjBool pdbioCheckChains(AjPPdbfile pdbfile, AjPFile flog, ajint min_chain_size) { ajuint i = 0U; /* Loop counter */ ajuint j = 0U; /* Loop counter */ AjIStr iter = NULL; /* Iterator for sequence strings */ ajint aacnt = 0; /* Counter for number of amino acids in * sequence strings */ AjBool ok = ajFalse; /* Flag which is True if amino acid chains * are found in the SEQRES records */ char id1 = ' '; /* Chain id */ char id2 = ' '; /* Chain id */ AjBool iderr = ajFalse; /* ajTrue if both a space and a character * are used as chain id's in the same file */ /* Check args */ if (!pdbfile || !flog) { ajWarn("Bad args passed to pdbioCheckChains\n"); return ajFalse; } /* Report problems with chain id's */ for (i = 0U; i < pdbfile->nchains; i++) { id1 = ajChararrGet(pdbfile->chid, i); for (j = i + 1U; j < pdbfile->nchains; j++) { if (id1 == (id2 = ajChararrGet(pdbfile->chid, j))) { ajFmtPrintF(flog, "%-15s%d (%c) %d (%c)\n", "CHAINIDS", i + 1, ajChararrGet(pdbfile->chid, i), j + 1, ajChararrGet(pdbfile->chid, j)); pdbfile->chainok[i] = ajFalse; pdbfile->chainok[j] = ajFalse; } if ((((id1 == ' ') && (id2 != ' ')) || ((id2 == ' ') && (id1 != ' '))) && (!(iderr))) { ajFmtPrintF(flog, "%-15s\n", "CHAINIDSPC"); iderr = ajTrue; } } } /* Report problems with non-protein chains */ for (i = 0U; i < pdbfile->nchains; i++) { if (!pdbfile->chainok[i]) continue; aacnt = 0; iter = ajStrIterNew(pdbfile->seqres[i]); if (toupper((int) ajStrIterGetK(iter)) != 'X') ++aacnt; while (ajStrIterNext(iter)) if (toupper((int) ajStrIterGetK(iter)) != 'X') if (++aacnt >= min_chain_size) break; ajStrIterDel(&iter); if (aacnt == 0) { ajFmtPrintF(flog, "%-15s%d (%c)\n", "SEQRESNOAA", i + 1, ajChararrGet(pdbfile->chid, i)); pdbfile->chainok[i] = ajFalse; } else if (aacnt < min_chain_size) { ajFmtPrintF(flog, "%-15s%d (%c)\n", "SEQRESFEWAA", i + 1, ajChararrGet(pdbfile->chid, i)); pdbfile->chainok[i] = ajFalse; } else { ok = ajTrue; } } /* Return now if no protein chains are found */ if (!ok) { ajWarn("No protein chains found in raw pdb file"); ajFmtPrintF(flog, "%-15s\n", "NOPROTEINS"); return ajFalse; } return ajTrue; } ID pdbioCheckTer TY static MO ajpdbio LB core XX DE Reads a Pdbfile object and checks whether the expected number of TER DE and MODEL records are present. Any unwanted records (e.g. TER records that DE delimit fragments of chain digests and duplicate MODEL records) are DE discarded (the linetype array for the lines are set to pdbfileELinetypeIgnore). DE DE The linetype array and modcnt variable may be modified. The toofewter DE element is written. DE The value of modcnt is reduced by 1 for each MODEL record that was masked DE but this is not done for tercnt. XX PN [1] PA w pdbfile AjPPdbfile PD Pdbfile object PX PN [2] PA u flog AjPFile PD Pointer to log file (build diagnostics) PX RT AjBool RD True on success, False otherwise. RX // static AjBool pdbioCheckTer(AjPPdbfile pdbfile, AjPFile flog) { char aa = ' '; ajuint i = 0U; /* Loop counter */ AjBool toomany = ajFalse; AjBool toofew = ajFalse; AjPStr aa1 = NULL; AjPStr aa2 = NULL; /* Check args */ if (!pdbfile || !flog) { ajWarn("Bad args passed to pdbioCheckTer\n"); return ajFalse; } /* Allocate memory */ aa1 = ajStrNew(); aa2 = ajStrNew(); /* Report problems with TER records */ if (!pdbfile->tercnt) ajFmtPrintF(flog, "%-15s\n", "TERNONE"); else { if (pdbfile->method == ajEPdbMethodNmr) { if (pdbfile->tercnt > (pdbfile->nchains * pdbfile->modcnt)) toomany = ajTrue; else if (pdbfile->tercnt < (pdbfile->nchains * pdbfile->modcnt)) toofew = ajTrue; } else { if (pdbfile->tercnt > pdbfile->nchains) toomany = ajTrue; else if (pdbfile->tercnt < pdbfile->nchains) toofew = ajTrue; } } /* Report diagnostics */ if (toomany) ajFmtPrintF(flog, "%-15s\n", "TERTOOMANY"); else if (toofew) { ajFmtPrintF(flog, "%-15s\n", "TERTOOFEW"); pdbfile->toofewter = ajTrue; } /* Mask out the extra TER records */ if (toomany) { for (i = pdbfile->idxfirst; i < pdbfile->nlines; i++) { /* This is a TER record. Mask it out if it is flanked by ATOM or * HETATM records for AMINO ACIDS and with identical chain ids. */ if (pdbfile->linetype[i] == pdbfileELinetypeTER) { if ((pdbfile->linetype[i - 1] == pdbfileELinetypeCoordinate) && (pdbfile->linetype[i + 1] == pdbfileELinetypeCoordinate) && (ajStrGetCharPos(pdbfile->lines[i - 1], POS_CHID) == ajStrGetCharPos(pdbfile->lines[i + 1], POS_CHID))) { ajStrAssignSubS(&aa1, pdbfile->lines[i - 1], 17, 19); ajStrAssignSubS(&aa2, pdbfile->lines[i + 1], 17, 19); if ((ajResidueFromTriplet(aa1, &aa)) && (ajResidueFromTriplet(aa2, &aa))) { pdbfile->linetype[i] = pdbfileELinetypeIgnore; } } } } } /* Check for duplicate MODEL records */ for (i = pdbfile->idxfirst + 1U; i < pdbfile->nlines; i++) { if ((pdbfile->linetype[i - 1] == pdbfileELinetypeMODEL) && (pdbfile->linetype[i] == pdbfileELinetypeMODEL)) { pdbfile->linetype[i - 1] = pdbfileELinetypeIgnore; pdbfile->modcnt--; ajFmtPrintF(flog, "%-15s%d\n", "MODELDUP", i + 1); } } /* Tidy up and return */ ajStrDel(&aa1); ajStrDel(&aa2); return ajTrue; } ID pdbioNumberChains TY static MO ajpdbio LB core XX DE Reads a Pdbfile object and assigns each ATOM or HETATM record to a specific DE chain and model. Lines containing coordinates for water molecules and DE other non-protein groups ("heterogens") are identified. Water molecules are DE uniquely associated with a whole model whereas other non-protein groups are DE associated with a unique chain if possible. If this is not possible they DE are assigned a unique group number. DE DE The modn, chnn and gpn arrays are written. The linetype array is modified. DE The nligands array (count of groups associated with a chain), ngroups DE element (count of groups not associated with a chain) and gpid (identifiers DE of these later groups) are written. DE DE linetype array DE The linetype array is written with a value of pdbfileELinetypeWater for DE lines containing coordinates for water, and to pdbfileELinetypeHeterogen or DE pdbfileELinetypeGroups for non-protein groups that, respectively, could or DE could not be uniquely associated with a chain. XX PN [1] PA w pdbfile AjPPdbfile PD Pdbfile object PX PN [2] PA u flog AjPFile PD Log file (build diagnostics) PX RT AjBool RD True on success, False otherwise RX // static AjBool pdbioNumberChains(AjPPdbfile pdbfile, AjPFile flog) { char id = ' '; /* Chain id */ ajuint i = 0U; /* Loop counter */ ajuint j = 0U; /* Loop counter */ ajint mod = 0; /* Model number */ AjBool done = ajFalse; /* True if we have assigned a chain id for * this line */ ajuint this = 0U; /* Chain number of last line read in */ ajuint chn = 0U; /* Chain number as index (starting from 0) */ AjPInt gpns = NULL; /* Gives the correct group number for groups * that could not be identified as belonging * to a chain, in cases where a single chain * only is present in the file */ ajint gpn = 0; /* Current group number */ ajint offset = 0; /* Offset for finding correct value for gpns * (for use with files with a single chain * only */ AjBool *chndone = NULL; /* Array whose elements are TRUE if we have * already read a line in belonging to the * appropriate chain for this model */ AjPStr *htype = NULL; /* Array holding the residue type of the last * heterogen read in for the appropriate * chain */ /* Check args */ if (!pdbfile || !flog) { ajWarn("Bad args passed to pdbioNumberChains\n"); return ajFalse; } /* Allocate memory */ gpns = ajIntNew(); AJCNEW0(htype, pdbfile->nchains); for (i = 0U; i < pdbfile->nchains; i++) htype[i] = ajStrNew(); AJCNEW0(chndone, pdbfile->nchains); for (i = 0U; i < pdbfile->nchains; i++) chndone[i] = ajFalse; if (((pdbfile->method == ajEPdbMethodNmr) && pdbfile->nomod) || (pdbfile->method == ajEPdbMethodXray)) mod = 1; for (i = pdbfile->idxfirst; i < pdbfile->nlines; i++) { /* JONNEW Lines may already have been assigned to pdbfileELinetypeWater * in pdbioFirstPass function, so we need to check here to ensure model * number gets assigned */ if ((pdbfile->linetype[i] == pdbfileELinetypeCoordinate) || (pdbfile->linetype[i] == pdbfileELinetypeWater)) { pdbfile->modn[i] = mod; /* Assign appropriate chain number to any ATOM or HETATM line * which has an id exhibited in the SEQRES records. */ done = ajFalse; id = ajStrGetCharPos(pdbfile->lines[i], POS_CHID); for (j = 0U; j < pdbfile->nchains; j++) { if (ajChararrGet(pdbfile->chid, j) == id) { pdbfile->chnn[i] = j + 1; chn = pdbfile->chnn[i] - 1; this = j + 1; if (chndone[this - 1]) { /* Mark up water coordinates */ if (ajStrMatchC(pdbfile->rtype[i], "HOH")) pdbfile->linetype[i] = pdbfileELinetypeWater; else { /* Mark up ligand coordinates */ pdbfile->linetype[i] = pdbfileELinetypeHeterogen; /* New heterogen */ if (!ajStrMatchS(htype[chn], pdbfile->rtype[i])) { offset++; pdbfile->nligands[chn]++; pdbfile->gpn[i] = pdbfile->nligands[chn]; ajStrAssignS(&htype[chn], pdbfile->rtype[i]); } /* More atoms of the same heterogen */ else pdbfile->gpn[i] = pdbfile->nligands[chn]; } } #if AJFALSE /* Unused */ else doneter = ajFalse; #endif /* AJFALSE */ done = ajTrue; break; } } if (!done) { /* Any ATOM or HETATM record with a whitespace as chain id * and which has not already been assigned belongs to the * chain immediately preceding it. Assign these lines as * NON_PROTEIN_CHAIN lines. */ if (id == ' ') { /* This won't be set until we've read in at least one * coordinate line for protein chain, so if this==0, set * it to 1 (first chain). This prevents problems for * 1qjh.pxyz */ if (this == 0) this = 1; pdbfile->chnn[i] = this; chn = pdbfile->chnn[i] - 1; /* Mark up water coordinates */ if (ajStrMatchC(pdbfile->rtype[i], "HOH")) pdbfile->linetype[i] = pdbfileELinetypeWater; else { /* Mark up ligand coordinates */ pdbfile->linetype[i] = pdbfileELinetypeHeterogen; /* New ligand */ if (!ajStrMatchS(htype[chn], pdbfile->rtype[i])) { offset++; pdbfile->nligands[chn]++; pdbfile->gpn[i] = pdbfile->nligands[chn]; ajStrAssignS(&htype[chn], pdbfile->rtype[i]); } /* More atoms of the same heterogen */ else { pdbfile->gpn[i] = pdbfile->nligands[chn]; } } } else /* Assign any ATOM or HETATM records with a non- * whitespace chain id that does not appear in the SEQRES * records as a NON_PROTEIN_CHAIN. Record the chain id's * used and assign a GROUP NUMBER as appropriate. */ { /* Mark up water coordinates */ if (ajStrMatchC(pdbfile->rtype[i], "HOH")) pdbfile->linetype[i] = pdbfileELinetypeWater; else { /* If there is a single chain only then the group is * of course associated with that chain */ if (pdbfile->nchains == 1) { /* Mark up ligand coordinates */ pdbfile->linetype[i] = pdbfileELinetypeHeterogen; pdbfile->chnn[i] = 1; for (done = ajFalse, j = 0U; j < pdbfile->ngroups; j++) if (ajChararrGet(pdbfile->gpid, j) == id) { pdbfile->gpn[i] = ajIntGet(gpns, j); done = ajTrue; break; } if (!done) { ajIntPut(&gpns, j, (gpn = j + 1 + offset)); pdbfile->gpn[i] = gpn; /* NO - this code would be used only if the * group could not be associated with a * chain. ajChararrPut(&(pdbfile->gpid), * pdbfile->ngroups, id); pdbfile->ngroups++; */ /* Increment number of ligands and reset last * ligand type read in */ pdbfile->nligands[0]++; ajStrAssignC(&htype[0], " "); } } else { /* Mark up ligand coordinates */ pdbfile->linetype[i] = pdbfileELinetypeGroups; for (done = ajFalse, j = 0U; j < pdbfile->ngroups; j++) if (ajChararrGet(pdbfile->gpid, j) == id) { pdbfile->gpn[i] = j + 1; done = ajTrue; break; } if (!done) { ajChararrPut(&(pdbfile->gpid), pdbfile->ngroups, id); pdbfile->ngroups++; pdbfile->gpn[i] = j + 1; } } } } } } else if (pdbfile->linetype[i] == pdbfileELinetypeMODEL) { mod++; /* doneter=ajFalse; Unused variable */ for (j = 0U; j < pdbfile->nchains; j++) chndone[j] = ajFalse; /* doneoneter=ajFalse; Unused variable */ } else if (pdbfile->linetype[i] == pdbfileELinetypeTER) { chndone[this - 1] = ajTrue; /* prev = this; Unused variable */ /* doneoneter=ajTrue; Unused variable */ /* doneter=ajTrue; Unused variable */ } /* ** Check for missing TER records. ** Where chains are not separated by TER records ** (the chain id changes from line to line without an intervening TER ** record and both chain id's are not whitespace). ** Where ATOM and HETATM groups are not separated ** by TER records (a chain id is given on one line, a whitespace chain ** id is given on the next line, and there is no intervening TER ** record). ** ** This code is identical to a fragment from pdbioCheckTer. ** ** Note that chndone only is modified. If the code in the function ** was made to use prev, doneoneter, doneter then the code below would ** also have to be modified ** ** Only do this now where there aren't enough TER records in the file. ** Without this check, it was failing for cases where the order of ** chains is inconsistent (see around line 4095 of pdb1cm4.ent) */ if (pdbfile->toofewter) { if (i > pdbfile->idxfirst) if (pdbfile->linetype[i - 1] == pdbfileELinetypeCoordinate && ((pdbfile->linetype[i] == pdbfileELinetypeCoordinate) || (pdbfile->linetype[i] == pdbfileELinetypeHeterogen)) && ((ajStrGetCharPos(pdbfile->lines[i - 1], POS_CHID)) != ajStrGetCharPos(pdbfile->lines[i], POS_CHID))) { for (j = 0; j < pdbfile->nchains; j++) if (ajChararrGet(pdbfile->chid, j) == ajStrGetCharPos(pdbfile->lines[i - 1], POS_CHID)) { chndone[j] = ajTrue; break; } } } } /* ** The above code cannot cope for cases where the ATOM and HETATM records ** use the same (or no) chain identifier and are not separated by a TER ** record (e.g. 1rbp) ** ** For files with less than the expected number of TER records, ** check again for COORDHET lines, which are identified as ** (i) a line beginning with a HETATM record with the same chain ** identifier but lower residue number than the preceding line, or ** JONNEW ** (ii) a line beginning with a HETATM record which is not followed ** anywhere in the file by an ATOM record with the same chain identifier ** (from the PDB record) or number (assigned by parser) */ for (i = 0; i < pdbfile->nchains; i++) ajStrAssignC(&htype[i], "\0"); if (pdbfile->tercnt < (pdbfile->nchains * pdbfile->modcnt)) for (i = pdbfile->idxfirst + 1; i < pdbfile->nlines; i++) { chn = pdbfile->chnn[i] - 1; if (pdbfile->linetype[i - 1] == pdbfileELinetypeCoordinate && pdbfile->linetype[i] == pdbfileELinetypeCoordinate && ((ajStrGetCharPos(pdbfile->lines[i - 1], POS_CHID)) == ajStrGetCharPos(pdbfile->lines[i], POS_CHID))) if (ajStrPrefixC(pdbfile->lines[i], "HETATM")) if ((pdbfile->resn1[i] < pdbfile->resn1[i - 1]) || pdbioNoMoreAtoms(pdbfile, i)) /* if(pdbfile->resn1[i]resn1[i-1]) */ while ((ajStrPrefixC(pdbfile->lines[i], "HETATM"))) { if (ajStrMatchC(pdbfile->rtype[i], "HOH")) pdbfile->linetype[i] = pdbfileELinetypeWater; else { pdbfile->linetype[i] = pdbfileELinetypeHeterogen; /* New heterogen */ if (!ajStrMatchS(htype[chn], pdbfile->rtype[i])) { offset++; pdbfile->nligands[chn]++; pdbfile->gpn[i] = pdbfile->nligands[chn]; ajStrAssignS(&htype[chn], pdbfile->rtype[i]); } /* More atoms of the same heterogen */ else { pdbfile->gpn[i] = pdbfile->nligands[chn]; } } i++; } } /* For files with a single chain only, set the number of groups that * could not be associated with a chain to zero */ if (pdbfile->nchains == 1) { /* We might need to add ngroups to nligands[0] */ if (pdbfile->ngroups != 0) ajFatal("Must check ngroups versus nligands in the file"); pdbfile->ngroups = 0; } /* Tidy up and return */ for (i = 0; i < pdbfile->nchains; i++) ajStrDel(&htype[i]); AJFREE(htype); ajIntDel(&gpns); AJFREE(chndone); return ajTrue; } ID pdbioNoMoreAtoms TY static MO ajpdbio LB core XX DE This function is called by function <pdbioNumberChains> to identify ligands DE (COORDHET lines) in files with less than the expected number of TER DE records. DE These are identified here by a line beginning with a HETATM record which DE is not followed by an ATOM record with the same chain identifier (from DE the PDB record) or number (assigned by parser). The function returns if DE a line of a different chain is found or at the first non-ATOM/HETAM line. DE Additional processing is done in <pdbioNumberChains> itself. XX PN [1] PA w pdbfile AjPPdbfile PD Pdbfile object PX PN [2] PA r linen ajuint PD Line number PX RT AjBool RD True (no more atoms), False otherwise RX // static AjBool pdbioNoMoreAtoms(AjPPdbfile pdbfile, ajuint linen) { ajuint i = 0U; for (i = linen + 1; i < pdbfile->nlines; i++) { if ((ajStrPrefixC(pdbfile->lines[i], "ATOM"))) { /* Same chain */ if ((ajStrGetCharPos(pdbfile->lines[linen], POS_CHID) == ajStrGetCharPos(pdbfile->lines[i], POS_CHID)) || (pdbfile->chnn[linen] == pdbfile->chnn[i])) return ajFalse; else /* Different chain */ return ajTrue; } else if ((ajStrPrefixC(pdbfile->lines[i], "HETATM"))) { /* Different chain */ if ((ajStrGetCharPos(pdbfile->lines[linen], POS_CHID) != ajStrGetCharPos(pdbfile->lines[i], POS_CHID)) || (pdbfile->chnn[linen] != pdbfile->chnn[i])) return ajTrue; } else /* Different chain or near EOF */ return ajTrue; } return ajTrue; } ID pdbioMaskChains TY static MO ajpdbio LB core XX DE Reads a Pdbfile object and checks to see whether the ATOM records for DE each chain contain sufficient amino acids. Any chains with insufficient DE amino acids either in the SEQRES or ATOM records, or with ambiguous chain DE id's are discarded. Optionally, amino acid residues and non-amino DE acid groups (e.g. ACE, NH2 etc) in protein chains with no CA atom are also DE discarded (the linetype array for the lines are set to pdbfileELinetypeIgnore). DE For non-amino acid groups, the corresponding characters are removed from DE the sequence derived from the SEQRES records. Coordinate data and atom DE type are parsed for each atom. Optionally, amino acids or groups in DE protein chains with a single atom only are also discarded. DE DE Checks whether chains from the ATOM records contain at least the DE user-defined threshold number of amino acid residues. If not then the chain DE is discarded (chainok array is set to ajFalse). If NO chains with DE sufficient residues are found, a "NOPROTEINS" error is generated and DE ajFalse is returned. DE DE Writes the x,y,z,o,b and atype elements of a Pdbfile object. The linetype, DE and possibly seqres, seqresful and nres arrays are modified. DE DE linetype array DE Coordinate data are extracted for lines of linetype pdbfileELinetypeCoordinate, DE pdbfileELinetypeHeterogen and pdbfileELinetypeGroups. DE DE seqres & seqresful arrays DE Three-letter codes of any groups that are (i) not standard amino acids and DE (ii) which do not contain a CA atom are removed from the seqres sequence DE if the <camask> is set. The seqresful array is an intermediate array to DE achieve this. XX PN [1] PA w pdbfile AjPPdbfile PD Pdbfile object PX PN [2] PA u flog AjPFile PD Log file (build diagnostics) PX PN [3] PA r min_chain_size ajint PD Min. no. of amino acids in a chain PX PN [4] PA r camask AjBool PD Whether to mask non-amino acid PD residues within protein chains which PD do not have a C-alpha atom (remove them PD from the seqres sequence and set the PD linetype array for the lines PD to pdbfileELinetypeIgnore). PX PN [5] PA r camask1 AjBool PD Whether to mask amino acid residues PD within protein chains which do not have PD a C-alpha atom (set the linetype PD array for the lines to pdbfileELinetypeIgnore). PX PN [6] PA r atommask AjBool PD Whether to mask residues or groups PD with a single atom only. PX RT AjBool RD True on success, False otherwise RX // static AjBool pdbioMaskChains(AjPPdbfile pdbfile, AjPFile flog, ajint min_chain_size, AjBool camask, AjBool camask1, AjBool atommask) { ajuint i = 0U; /* Loop counter */ ajuint j = 0U; /* Loop counter */ AjPStr aa3 = NULL; /* Amino acid */ ajint rcnt = 0; /* Residue count */ ajint acnt = 0; /* Atom count */ ajint modcnt = 0; /* Count of MODEL records */ ajuint lastatom = 0U; /* Line number of last coordinate line read * in */ ajuint firstatom = 0U; /* Line number of coordinate line for first * atom of residue */ AjBool noca = ajFalse; /* True if this residue does not contain a CA * atom */ ajint lastchn = 0; /* Chain number of last line read in */ AjBool *chainok; /* Array of flags which are True if a chain * in the SEQRES records is found in the ATOM * records */ char aa1 = ' '; /* Amino acid id */ AjPStr lastrn = NULL; /* Number of last residue read in */ AjBool msgdone = ajFalse; /* Flag for message reporting */ AjPStr sub = NULL; AjPStr tmpseq = NULL; ajuint lenful = 0U; /* Length of SEQRES sequence including ACE, * FOR & NH2 groups that might be discarded * by the call to pdbioSeqresToSequence */ ajuint ipos = 0U; char tmp = ' '; AjBool odd = ajFalse; /* Whether the current residue / group is of * unknown type */ AjBool ok = ajFalse; /* True if the file, after processing by this * function, is found to contain at least one * chain for which chainok == ajTrue */ /* Check args */ if (!pdbfile || !flog) { ajWarn("Bad args passed to pdbioMaskChains\n"); return ajFalse; } /* Allocate memory */ AJCNEW0(chainok, pdbfile->nchains); for (i = 0U; i < pdbfile->nchains; i++) chainok[i] = ajFalse; aa3 = ajStrNew(); lastrn = ajStrNew(); sub = ajStrNew(); ajStrAssignClear(&sub); firstatom = lastatom = pdbfile->idxfirst; for (i = pdbfile->idxfirst; i < pdbfile->nlines; i++) { if ((pdbfile->linetype[i] == pdbfileELinetypeCoordinate) || (pdbfile->linetype[i] == pdbfileELinetypeHeterogen) || (pdbfile->linetype[i] == pdbfileELinetypeGroups) || (pdbfile->linetype[i] == pdbfileELinetypeWater)) { /* Assign x, y, z, o, b data */ /* Replace this with code so that internals of structure are not * probed (when code becomes available) */ if ((sscanf(&(pdbfile->lines[i]->Ptr[28]), "%f %f %f %f", &(pdbfile->x[i]), &(pdbfile->y[i]), &(pdbfile->z[i]), &(pdbfile->o[i]))) != 4) ajFatal("Scan error in pdbioMaskChains\n" "Email jison@hgmp.mrc.ac.uk"); if (!sscanf(&(pdbfile->lines[i]->Ptr[60]), "%f", &(pdbfile->b[i]))) ajFatal("Scan error in pdbioMaskChains\n" "Email jison@hgmp.mrc.ac.uk"); /* Usually position 12 is used for the alternative position * indicator (taken in the code below to be indicated by a * number) for atoms, but occasionally can be incorrectly used * for the atom type itself (indicated in the code below by a * character in pos 12). This code copes for both cases */ /* Assign atom type */ if (isalpha((int) pdbfile->lines[i]->Ptr[12])) { ajStrAssignSubS(&pdbfile->atype[i], pdbfile->lines[i], 12, 15); ajStrRemoveWhite(&pdbfile->atype[i]); if (!msgdone) { ajFmtPrintF(flog, "%-15s%d\n", "ATOMCOL12", i + 1); msgdone = ajTrue; } } else { ajStrAssignSubS(&pdbfile->atype[i], pdbfile->lines[i], 13, 15); ajStrRemoveWhite(&pdbfile->atype[i]); } } if (pdbfile->linetype[i] == pdbfileELinetypeCoordinate) { /* Check whether CA atom is present */ if (!(ajStrCmpC(pdbfile->atype[i], "CA"))) noca = ajFalse; if (pdbfile->chnn[i] < lastchn) ajFmtPrintF(flog, "%-15s%d\n", "CHAINORDER", i + 1); if (pdbfile->chnn[i] != lastchn) { rcnt = 0; lastchn = pdbfile->chnn[i]; } /* If this is a new residue */ if (!ajStrMatchS(pdbfile->pdbn[i], lastrn)) { /* Mask coordinate lines where there are only a single atom */ if (acnt == 1) { ajFmtPrintF(flog, "%-15s%d\n", "ATOMONEONLY", lastatom + 1); if (atommask) pdbfile->linetype[lastatom] = pdbfileELinetypeIgnore; } /* Mask coordinate lines for residues lacking a CA atom */ if (noca) { odd = (!(ajResidueFromTriplet(pdbfile->rtype[lastatom - 1], &tmp))); if ((camask && odd) || (camask1 && !odd)) for (j = firstatom; j <= lastatom; j++) pdbfile->linetype[j] = pdbfileELinetypeIgnore; /* Remove residues from SEQRES records */ if ((camask && odd)) { ipos = pdbfile->chnn[firstatom] - 1; ajStrExchangeSS(&pdbfile->seqresful[ipos], pdbfile->rtype[firstatom], sub); } if (firstatom == lastatom) ajFmtPrintF(flog, "%-15s%d\n", "ATOMNOCA", firstatom + 1); else ajFmtPrintF(flog, "%-15s%d %d\n", "ATOMNOCA", firstatom + 1, lastatom + 1); } /* Increment the residue counter if the code is recognised */ if (ajResidueFromTriplet(pdbfile->rtype[i], &aa1)) rcnt++; if (rcnt >= min_chain_size) chainok[pdbfile->chnn[i] - 1] = ajTrue; ajStrAssignS(&lastrn, pdbfile->pdbn[i]); /* Set count of atoms to zero, set the position of the first * atom and set flag for recognising CA atom */ acnt = 1; firstatom = i; if (!(ajStrCmpC(pdbfile->atype[i], "CA"))) noca = ajFalse; else noca = ajTrue; } /* Set the position for the last atom read in */ lastatom = i; /* Increment the atom counter */ acnt++; } else if (pdbfile->linetype[i] == pdbfileELinetypeMODEL) { rcnt = 0; lastchn = 0; modcnt++; if (modcnt != 1) { for (j = 0; j < pdbfile->nchains; j++) { /* Only bother reporting error messages if a message * about the SEQRES records not containing enough aa's * has not already been reported */ /* If pdbfile->chainok is False, leave it so */ if (!pdbfile->chainok[j]) continue; else { if (!chainok[j]) { pdbfile->chainok[j] = chainok[j]; ajFmtPrintF(flog, "%-15s%d (%c) %d\n", "ATOMFEWAA", j + 1, (ajChararrGet(pdbfile->chid, j)), modcnt); } } } } } } /* Ensure that C-terminal residues are masked if necessary */ /* ** else if((pdbfile->linetype[i] == pdbfileELinetypeTER) || ** (pdbfile->linetype[i] == pdbfileELinetypeENDMDL)) ** { */ if (noca) { odd = (!(ajResidueFromTriplet(pdbfile->rtype[lastatom - 1], &tmp))); if ((camask && odd) || (camask1 && !odd)) for (j = firstatom; j <= lastatom; j++) pdbfile->linetype[j] = pdbfileELinetypeIgnore; /* Remove residues from SEQRES records */ if ((camask && odd)) { ipos = pdbfile->chnn[firstatom] - 1; ajStrExchangeSS(&pdbfile->seqresful[ipos], pdbfile->rtype[firstatom], sub); } if (firstatom == lastatom) { ajFmtPrintF(flog, "%-15s%d\n", "ATOMNOCA", firstatom + 1); } else ajFmtPrintF(flog, "%-15s%d %d\n", "ATOMNOCA", firstatom + 1, lastatom + 1); } /* ** } */ /* Write the new (masked) seqres sequences if necessary */ if (camask) { for (i = 0; i < pdbfile->nchains; i++) { tmpseq = ajStrNew(); if (!pdbioSeqresToSequence(pdbfile->seqresful[i], &tmpseq, camask, &lenful)) ajFatal("Sequence conversion error in " "pdbioFirstPass\nEmail jison@hgmp.mrc.ac.uk\n"); ajStrAssignS(&pdbfile->seqres[i], tmpseq); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); ajStrDel(&tmpseq); } } /* Set modcnt to 1 for xray structures */ if (!modcnt) modcnt = 1; /* Check for xray structures or last model of nmr structures */ for (i = 0; i < pdbfile->nchains; i++) { /* Only bother reporting error messages if a message about the SEQRES * records not containing enough aa's has not already been reported */ /* If pdbfile->chainok is False, leave it so */ if (!pdbfile->chainok[i]) continue; else { if (!chainok[i]) { pdbfile->chainok[i] = chainok[i]; ajFmtPrintF(flog, "%-15s%d (%c) %d\n", "ATOMFEWAA", i + 1, (ajChararrGet(pdbfile->chid, i)), modcnt); } } } /* Mask out any chains with insufficient amino acids either in the SEQRES * or ATOM records */ for (i = pdbfile->idxfirst; i < pdbfile->nlines; i++) if (((pdbfile->linetype[i] == pdbfileELinetypeCoordinate) || (pdbfile->linetype[i] == pdbfileELinetypeHeterogen)) && (!pdbfile->chainok[pdbfile->chnn[i] - 1])) { pdbfile->linetype[i] = pdbfileELinetypeIgnore; } /* Check for missing TER records. Where chains are not separated by TER * records (the chain id changes from line to line without an intervening * TER record and both chain id's are not whitespace). Where ATOM and * HETATM groups are not separated by TER records (a chain id is given on * one line, a whitespace chain id is given on the next line, and there * is no intervening TER record) */ for (i = pdbfile->idxfirst + 1; i < pdbfile->nlines; i++) { if ((pdbfile->linetype[i - 1] == pdbfileELinetypeCoordinate) && (pdbfile->linetype[i] == pdbfileELinetypeCoordinate) && pdbfile->chnn[i - 1] != pdbfile->chnn[i]) ajFmtPrintF(flog, "%-15s%d %d\n", "TERMISSCHN", i, i + 1); else if ((pdbfile->linetype[i - 1] == pdbfileELinetypeCoordinate) && ((pdbfile->linetype[i] == pdbfileELinetypeHeterogen) || (pdbfile->linetype[i] == pdbfileELinetypeWater)) && pdbfile->chnn[i - 1] == pdbfile->chnn[i]) ajFmtPrintF(flog, "%-15s%d %d\n", "TERMISSHET", i, i + 1); } /* Tidy up */ AJFREE(chainok); ajStrDel(&aa3); ajStrDel(&lastrn); ajStrDel(&sub); /* Report problems with non-protein chains */ for (i = 0; i < pdbfile->nchains; i++) if (pdbfile->chainok[i]) { ok = ajTrue; break; } /* Return now if no protein chains are found */ if (!ok) { ajWarn("No protein chains found in raw pdb file"); ajFmtPrintF(flog, "%-15s\n", "NOPROTEINS"); return ajFalse; } return ajTrue; } ID pdbioStandardiseNumbering TY static MO ajpdbio LB core XX DE Reads a Pdbfile object and standardises the two sets of residue numbers DE (resn1 & resn2 arrays) derived from the raw residue numbers. The residue DE numbering is corrected for zero or negative residue numbers, non-standard DE numbering schemes and any other cases of non-sequentiality (e.g. where the DE next residue number is lower than the previous one, see 1pca). DE resn1 gives the sequence presuming an alternative numbering scheme, resn2 DE gives the sequence presuming heterogeneity. Heterogeneity is indicated by DE a character in position lines[26] (the same position used to indicate DE alternative residue numbering schemes). DE DE The resn1 & resn2 arrays of a Pdbfile object are modified. The oddnum DE array is written. DE DE oddnum array DE This is an array of Bool's which are TRUE for duplicate residues of DE heterogenous positions (e.g. if 2 different residues are both numbered '8' DE or one is '8' and the other '8A' for example then <oddnum> would be set DE True for the second residue. XX PN [1] PA w pdbfile AjPPdbfile PD Pdbfile object PX PN [2] PA u flog AjPFile PD Pointer to log file (build diagnostics) PX RT AjBool RD True on success, False otherwise RX // static AjBool pdbioStandardiseNumbering(AjPPdbfile pdbfile, AjPFile flog) { ajuint i = 0U; ajint lastchn = -1; /* Chain number of last line read in */ AjBool first = ajFalse; /* True if we have processed the first * residue in a chain */ ajint first_num = 0; /* Number of first residue in chain */ ajint modrn = 0; /* Corrected first residue number */ AjBool neg = ajFalse; /* True if first residue number is negative */ AjBool zer = ajFalse; /* True if first residue number is zero */ AjBool report_neg = ajFalse;/* True if we have reported an error that a * residue number is negative for this chain */ AjBool report_zer = ajFalse;/* True if we have reported an error that a * residue number is zero for this chain */ ajint add = 0; /* An amount to add to the residue numbers to * correct them */ AjBool ignore = ajFalse; AjBool odd = ajFalse; ajint rn = 0; /* Current residue number */ ajint last_rn = 0; /* Last residue number read in */ ajint this_rn = 0; /* Current residue number read in */ char last = ' '; /* Chain id of last chain */ char curr = ' '; /* Chain id of current chain */ AjPStr last_rt = NULL; /* Type of previous residue */ AjPStr this_rt = NULL; /* Type of current residue */ AjBool report_nonstd = ajFalse; /* True if we have reported an error * that a non-standard residue * numbering scheme is used for this * chain */ AjBool report_nonsqt = ajFalse; /* True if we have reported an error * that any other cases of * non-sequential numbering are found * for this this chain */ char aa1 = ' '; /* Amino acid single character code */ ajuint ipos = 0; last_rt = ajStrNew(); this_rt = ajStrNew(); /* Check args */ if (!pdbfile || !flog) { ajWarn("Bad args passed to pdbioStandardiseNumbering\n"); return ajFalse; } /* Check whether the integer part of the original pdb numbering (at this * point in code held in resn1 and resn2) gives the correct index into * the SEQRES sequence */ for (i = pdbfile->idxfirst; i < pdbfile->nlines; i++) { /* If model number has gone past 1 then we must have checked all the * chains, so break */ if (pdbfile->modn[i] > 1) break; if (pdbfile->linetype[i] != pdbfileELinetypeCoordinate) continue; /* If residue number is not negative, zero, or greater then the * length of the SEQRES sequence and if the residue matches then * continue */ if ((pdbfile->resn1[i] <= pdbfile->nres[pdbfile->chnn[i] - 1]) || (pdbfile->resn1[i] >= 1)) { ajResidueFromTriplet(pdbfile->rtype[i], &aa1); ipos = pdbfile->chnn[i] - 1; if (aa1 == ajStrGetCharPos(pdbfile->seqres[ipos], pdbfile->resn1[i] - 1)) continue; } /* Otherwise flag an error for this chain and move to the end of the * chain */ ajFmtPrintF(flog, "%-15s%d (%c)\n", "BADINDEX", pdbfile->chnn[i], ajChararrGet(pdbfile->chid, pdbfile->chnn[i] - 1)); for (lastchn = pdbfile->chnn[i]; i < pdbfile->nlines; i++) { if (pdbfile->linetype[i] != pdbfileELinetypeCoordinate) continue; if (pdbfile->modn[i] > 1) break; if (pdbfile->chnn[i] != lastchn) { i--; break; } } } /* Fix for zero or negative residue numbers. This is done for both resn1 * and resn2 arrays of a Pdbfile object */ for (first = ajFalse, i = pdbfile->idxfirst; i < pdbfile->nlines; i++) { /* Coordinate line */ if (pdbfile->linetype[i] == pdbfileELinetypeCoordinate) { /* New chain */ if (pdbfile->chnn[i] != lastchn) { neg = ajFalse; zer = ajFalse; report_neg = ajFalse; report_zer = ajFalse; first = ajFalse; lastchn = pdbfile->chnn[i]; } rn = pdbfile->resn1[i]; if (!first) { first_num = rn; if (first_num > 0) { /* Advance counter to next chain */ for (; i < pdbfile->nlines; i++) if (((pdbfile->linetype[i] == pdbfileELinetypeCoordinate) && (pdbfile->chnn[i] != lastchn)) || pdbfile->linetype[i] == pdbfileELinetypeMODEL) { neg = ajFalse; zer = ajFalse; report_neg = ajFalse; report_zer = ajFalse; first = ajFalse; lastchn = -1; break; } /* i will get incremented in main loop above */ i--; continue; } first = ajTrue; } if (rn < 0) { neg = ajTrue; if (zer) modrn = rn - (first_num - 1); else /* if(neg && !zer) */ modrn = rn - (first_num - 1); if (!report_neg) { ajFmtPrintF(flog, "%-15s%d (%c) %d\n", "NEGNUM", pdbfile->chnn[i], ajChararrGet(pdbfile->chid, (pdbfile->chnn[i] - 1)), i + 1); report_neg = ajTrue; } } else if (rn == 0) { zer = ajTrue; if (neg) modrn = rn - (first_num - 1); else /* if(!neg) */ modrn = rn + 1; if (!report_zer) { ajFmtPrintF(flog, "%-15s%d (%c) %d\n", "ZERNUM", pdbfile->chnn[i], ajChararrGet(pdbfile->chid, (pdbfile->chnn[i] - 1)), i + 1); report_zer = ajTrue; } } else /* rn is (+ve) */ { if (!neg && zer) modrn = rn + 1; else if (neg && zer) modrn = rn - (first_num - 1); else /* if(neg && !zer) */ modrn = rn - (first_num); } pdbfile->resn1[i] = modrn; pdbfile->resn2[i] = modrn; } /* New model */ else if (pdbfile->linetype[i] == pdbfileELinetypeMODEL) { neg = ajFalse; zer = ajFalse; report_neg = ajFalse; report_zer = ajFalse; first = ajFalse; lastchn = -1; } } /* Fix non-standard residue numbering scheme. This is done for resn1 * array of a Pdbfile object only. */ for (lastchn = -1, i = pdbfile->idxfirst; i < pdbfile->nlines; i++) { /* Coordinate line */ if (pdbfile->linetype[i] == pdbfileELinetypeCoordinate) { /* New chain */ if (pdbfile->chnn[i] != lastchn) { add = 0; first = ajFalse; ignore = ajFalse; lastchn = pdbfile->chnn[i]; report_nonstd = ajFalse; } if (!first) { /* Remove the chmyotrypsin numbering code */ last_rn = pdbfile->resn1[i]; last = ajStrGetCharPos(pdbfile->lines[i], 26); first = ajTrue; continue; } rn = pdbfile->resn1[i]; curr = ajStrGetCharPos(pdbfile->lines[i], 26); if (curr != last) { if (rn == last_rn) { add++; ignore = ajTrue; } } if (rn != last_rn) { ignore = ajFalse; } last = curr; last_rn = rn; pdbfile->resn1[i] = rn + add; if (ignore) { pdbfile->oddnum[i] = ajTrue; if (!report_nonstd) { ajFmtPrintF(flog, "%-15s%d (%c) %d\n", "ODDNUM", pdbfile->chnn[i], ajChararrGet(pdbfile->chid, (pdbfile->chnn[i] - 1)), i + 1); report_nonstd = ajTrue; } } } else if (pdbfile->linetype[i] == pdbfileELinetypeMODEL) { add = 0; first = ajFalse; ignore = ajFalse; lastchn = -1; report_nonstd = ajFalse; } } /* Fix remaining non-sequential residue numbering in resn1 array of * Pdbfile object */ for (lastchn = -1, i = pdbfile->idxfirst; i < pdbfile->nlines; i++) { /* Coordinate line */ if (pdbfile->linetype[i] == pdbfileELinetypeCoordinate) { /* New chain */ if (pdbfile->chnn[i] != lastchn) { add = 0; first = ajFalse; ignore = ajFalse; lastchn = pdbfile->chnn[i]; report_nonsqt = ajFalse; } if (!first) { last_rn = pdbfile->resn1[i]; ajStrAssignS(&last_rt, pdbfile->rtype[i]); first = ajTrue; continue; } this_rn = pdbfile->resn1[i]; ajStrAssignS(&this_rt, pdbfile->rtype[i]); /* A new residue is indicated if this ATOM is 'N' or if this is a * different residue type */ if (!(ajStrCmpC(pdbfile->atype[i], "N")) || !(ajStrMatchS(this_rt, last_rt))) { /* Check for duplicate residue numbers */ if (this_rn == last_rn) { add++; ignore = ajTrue; odd = ajTrue; } /* Check for drops in residue numbers, see 1pca */ if (this_rn < last_rn) { add += (last_rn - this_rn + 1); ignore = ajTrue; odd = ajFalse; } } if (this_rn > last_rn) ignore = ajFalse; pdbfile->resn1[i] = this_rn + add; ajStrAssignS(&last_rt, this_rt); last_rn = this_rn; if (ignore) { if (odd) pdbfile->oddnum[i] = ajTrue; if (!report_nonsqt) { ajFmtPrintF(flog, "%-15s%d (%c) %d\n", "NONSQNTL", pdbfile->chnn[i], ajChararrGet(pdbfile->chid, (pdbfile->chnn[i] - 1)), i + 1); report_nonsqt = ajTrue; } } } else if (pdbfile->linetype[i] == pdbfileELinetypeMODEL) { add = 0; first = ajFalse; ignore = ajFalse; lastchn = -1; report_nonsqt = ajFalse; } } /* Fix remaining non-sequential residue numbering in resn2 array of * Pdbfile object (duplicate lines for presumed heterogenous residues * positions are ignored) */ for (lastchn = -1, i = pdbfile->idxfirst; i < pdbfile->nlines; i++) { /* Coordinate line */ if ((pdbfile->linetype[i] == pdbfileELinetypeCoordinate) && (!pdbfile->oddnum[i])) { /* New chain */ if (pdbfile->chnn[i] != lastchn) { add = 0; first = ajFalse; lastchn = pdbfile->chnn[i]; } if (!first) { last_rn = pdbfile->resn2[i]; ajStrAssignS(&last_rt, pdbfile->rtype[i]); first = ajTrue; continue; } this_rn = pdbfile->resn2[i]; ajStrAssignS(&this_rt, pdbfile->rtype[i]); /* A new residue is indicated if this ATOM is 'N' or if this is a * different residue type */ if (!(ajStrCmpC(pdbfile->atype[i], "N")) || !(ajStrMatchS(this_rt, last_rt))) { /* Check for duplicate residue numbers */ if (this_rn == last_rn) add++; /* Check for drops in residue numbers, see 1pca */ if (this_rn < last_rn) add += (last_rn - this_rn + 1); } pdbfile->resn2[i] = this_rn + add; ajStrAssignS(&last_rt, this_rt); last_rn = this_rn; } else if (pdbfile->linetype[i] == pdbfileELinetypeMODEL) { add = 0; first = ajFalse; lastchn = -1; } } /* Tidy up and return */ ajStrDel(&last_rt); ajStrDel(&this_rt); return ajTrue; } ID pdbioAlignNumbering TY static MO ajpdbio LB core XX DE Reads a Pdbfile object and determines for each chain a set of residue DE numbers (the resni array) that give the correct index into the full length DE (SEQRES) sequence for residues listed in the ATOM records. DE DE The resni and resn1ok arrays of a Pdbfile object are written. DE The seqres and nres elements may be modified for any missing N-terminal DE residues. DE DE resn1ok array DE This array contains Bool's for each chain which are TRUE if resn1 was DE used to derive resni, i.e. gave correct alignment to the full-length DE (SEQRES) sequence. If False then resn2 was used ( resn1 gives the DE sequence presuming an alternative numbering scheme, resn2 gives the DE sequence presuming heterogeneity). XX PN [1] PA w pdbfile AjPPdbfile PD Pdbfile object PX PN [2] PA u flog AjPFile PD Pointer to log file (build diagnostics) PX PN [3] PA r lim ajuint PD Max. no. permissible mismatches between PD the ATOM & SEQRES sequences. PX PN [4] PA r lim2 ajuint PD Max. no. residues to trim when checking PD for missing C-terminal SEQRES residues. PX RT AjBool RD True on success, False otherwise RX // static AjBool pdbioAlignNumbering(AjPPdbfile pdbfile, AjPFile flog, ajuint lim, ajuint lim2) { /* Sequence and residue number data are given for each unique chain (data * for the first model only is considered when assigning residue numbers) */ /* Rather than use, e.g. seq1, seq2 & seq, we could use a single 2 * dimensional array, but we would need new code for 2d arrays of * characters */ ajuint a = 0U; /* Loop counter */ ajuint b = 0U; /* Loop counter */ ajuint maxb = 0U; /* Max value of b */ ajuint i = 0U; /* Loop counter */ ajint j = 0; /* Loop counter */ ajint k = 0; /* Loop counter */ ajint x = 0; /* Loop counter */ ajint y = 0; /* Loop counter */ ajint z = 0; /* Loop counter */ AjPStr *seq1 = NULL; /* Sequences of residues from ATOM records * (all residues) */ AjPStr *seq2 = NULL; /* Sequences of residues from ATOM records * (excluding residues for which oddnum array * in Pdbfile object is True) */ AjPStr *seq = NULL; /* Pointer to seq1 or seq2 */ AjPStr seqbit = NULL; /* Subsequence of seq (real copy) */ ajlong lenseqbit = 0; /* Length of seqbit */ ajint *nres1 = NULL; /* No. residues for seq1/arr1 */ ajint *nres2 = NULL; /* No. residues for seq2/arr2 */ ajint *nres = NULL; /* Pointer to nres1 or nres2 */ AjPInt *num1 = NULL; /* Residue numbers for seq1 (from resn1 * element of the Pdbfile object) */ AjPInt *num2 = NULL; /* Residue numbers for seq2 (from resn2 * element of the Pdbfile object) */ AjPInt *num = NULL; /* Pointer to num1 or num2 */ AjPInt *idx = NULL; /* Gives correct index into seqres sequence * (from Pdbfile object) for the current * sequence. These are residue numbers and * therefore idx would have a value of 1 for * the first seqres residue. */ AjPInt *idx_full = NULL; /* As idx but with empty array elements * replacing missing residues so that we can * index into idx_full using residue numbers * from num */ ajint last1 = -1000; /* Number of last residue for seq1/arr1 */ ajint last2 = -1000; /* Number of last residue for seq2/arr2 */ char aa1 = ' '; /* Amino acid single character code */ ajint c = 0; /* No. of current chain */ AjBool done = ajFalse; /* True if we have found the correct residue * numbering */ char *insert = NULL; /* String from N-terminus of ATOM sequence to * insert at N-terminus of SEQRES sequence in * case of the later missing residues */ AjPStr tmpseqres = NULL; /* Temp. string for seqres sequence from * Pdbfile object */ AjPStr bit = NULL; /* Temp. string for a bit of sequence */ ajuint nmismatches = 0; /* No. of mismatches between ATOM and SEQRES * sequence */ ajlong loc = 0L; /* Location of ATOM sequence in SEQRES * sequence (if applicable) */ ajint len = 0; /* Length of seqres sequence from Pdbfile * object */ AjBool err = ajFalse; /* True if a residue number from the ATOM * records would cause an array boundary * error in the seqres sequence */ ajint siz_substr = 0; /* Size of substring for alignment of ATOM * and SEQRES sequences */ const char *atom_ptr = NULL;/* Pointer to ATOM sequence */ const char *seqres_ptr = NULL; /* Pointer to SEQRES sequence */ const char *loc_ptr = NULL; /* Pointer for location of match of substring * to SEQRES sequence */ AjPStr substr = NULL; /* Substring of ATOM sequence */ AjPStr substr2 = NULL; /* Substring of ATOM sequence */ ajint atom_idx = 0; /* Index into ATOM sequence */ ajint seqres_idx = 0; /* Index into SEQRES sequence */ ajint seqres_idx_last = 0; /* Index into SEQRES sequence for C-terminal * residue of substring */ char aa_last = ' '; /* Amino acid residue code of C-terminal * residue of substring */ AjBool fixed = ajFalse; /* Whether the mismatch residue of the * substring was later aligned correctly */ AjBool done_end = ajFalse; /* True if we have aligned the terminus of * the ATOM sequence */ AjBool founderr = ajFalse; /* Match of substring of ATOM sequence to * SEQRES found with potential mismatched * residue */ AjPStr msgstr = NULL; /* A string to hold a message */ AjPStr msgbit = NULL; /* A temp. string to hold part of a message */ ajint idx_misfit_atom = 0; /* Index into ATOM sequence (seq) for first * residue that does not match SEQRES * sequence */ ajint idx_misfit_seqres = 0;/* Index into SEQRES sequence for first * residue that does not match ATOM sequence */ AjPStr aa_misfit = NULL; /* Original (PDB) residue number for first * residue mismatch between ATOM and SEQRES * sequences */ ajint this_num = 0; /* Current residue number */ /*DIAGNOSTIC*/ #if AJFALSE ajint max = 0; /* Used in diagnostics code */ #endif /* AJFALSE */ /* Check args */ if (!pdbfile || !flog) { ajWarn("Bad args passed to pdbioAlignNumbering\n"); return ajFalse; } /* Allocate memory for arrays etc */ aa_misfit = ajStrNew(); msgstr = ajStrNew(); msgbit = ajStrNew(); seqbit = ajStrNew(); insert = ajCharNewRes(MAXMISSNTERM); tmpseqres = ajStrNew(); bit = ajStrNew(); substr = ajStrNew(); substr2 = ajStrNew(); AJCNEW0(seq1, pdbfile->nchains); AJCNEW0(seq2, pdbfile->nchains); AJCNEW0(num1, pdbfile->nchains); AJCNEW0(num2, pdbfile->nchains); AJCNEW0(idx, pdbfile->nchains); AJCNEW0(idx_full, pdbfile->nchains); AJCNEW0(nres1, pdbfile->nchains); AJCNEW0(nres2, pdbfile->nchains); for (i = 0U; i < pdbfile->nchains; i++) { if (!pdbfile->chainok[i]) continue; seq1[i] = ajStrNew(); seq2[i] = ajStrNew(); num1[i] = ajIntNew(); num2[i] = ajIntNew(); } /* Assign arrays */ for (i = pdbfile->idxfirst; i < pdbfile->nlines; i++) { /* Coordinate line */ if (pdbfile->linetype[i] == pdbfileELinetypeCoordinate) { /* Break if we are no longer in the first model */ if (pdbfile->modn[i] != 1) break; else c = pdbfile->chnn[i] - 1; #if AJFALSE ajFmtPrint("%S\n", pdbfile->lines[i]); #endif /* AJFALSE */ if (pdbfile->resn1[i] != last1) { ajResidueFromTriplet(pdbfile->rtype[i], &aa1); ajStrAppendK(&seq1[c], aa1); ajIntPut(&num1[c], nres1[c], pdbfile->resn1[i]); last1 = pdbfile->resn1[i]; nres1[c]++; } if ((pdbfile->resn2[i] != last2) && (!pdbfile->oddnum[i])) { ajResidueFromTriplet(pdbfile->rtype[i], &aa1); ajStrAppendK(&seq2[c], aa1); ajIntPut(&num2[c], nres2[c], pdbfile->resn2[i]); last2 = pdbfile->resn2[i]; nres2[c]++; } } } /* Allocate memory for arrays of residue numbers */ for (i = 0; i < pdbfile->nchains; i++) { if (!pdbfile->chainok[i]) continue; /* Array must be big enough to cope with either sequence */ if (nres1[i] > nres2[i]) idx[i] = ajIntNewRes(nres1[i]); else idx[i] = ajIntNewRes(nres2[i]); /* Array must be big enough to cope with highest the residue number * from either array */ if (ajIntGet(num1[i], nres1[i] - 1) > ajIntGet(num2[i], nres2[i] - 1)) idx_full[i] = ajIntNewRes(ajIntGet(num1[i], nres1[i] - 1) + 1); else idx_full[i] = ajIntNewRes(ajIntGet(num2[i], nres2[i] - 1) + 1); } /* Loop for each chain */ for (i = 0; i < pdbfile->nchains; i++) { /* Skip this chain if necessary */ if (!(pdbfile->chainok[i])) continue; else ajStrAssignS(&tmpseqres, pdbfile->seqres[i]); /* Loop for checking for missing residues from N-term of SEQRES * sequence */ for (done = ajFalse, j = 0; j < MAXMISSNTERM + 1; j++) { /* Loop for the 2 sequences derived from the ATOM records */ for (x = 0; x < 2; x++) { if (x == 0) { seq = seq1; nres = nres1; num = num1; } else { seq = seq2; nres = nres2; num = num2; } /* Restore the original seqres sequence */ ajStrAssignS(&(pdbfile->seqres[i]), tmpseqres); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); /* Modify the seqres sequence in the Pdbfile object by adding * the first j residues from the N-terminus of the ATOM * sequence to the N-terminus of . */ for (k = 0; (k < j) && (k < nres[i]); k++) insert[k] = ajStrGetCharPos(seq[i], k); insert[k] = '\0'; ajStrInsertC(&(pdbfile->seqres[i]), 0, insert); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); #if AJFALSE /* DIAGNOSTIC CODE */ ajFmtPrintF(flog, "\nChainMod %d" " (seq %d)\n%S\n%S\n\n\n", i + 1, x + 1, seq[i], pdbfile->seqres[i]); #endif /* AJFALSE */ /***********************************************/ /******************* STEP 1 ********************/ /***********************************************/ #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP1 tmpseqres: %S\n", tmpseqres); ajFmtPrintF(flog, "chnn : %d\n" "seq1 : %S\n" "seq2 : %S\n" "seqr : %S\n", i + 1, seq1[i], seq2[i], pdbfile->seqres[i]); ajFmtPrintF(flog, "\n"); if (ajStrMatchS(seq1[i], seq2[i])) ajFmtPrintF(flog, "seq1 and seq2 match\n"); else ajFmtPrintF(flog, "seq1 and seq2 DO NOT match\n"); if (ajStrMatchS(seq1[i], pdbfile->seqres[i])) ajFmtPrintF(flog, "seq1 and seqres match\n"); else ajFmtPrintF(flog, "seq1 and seqres DO NOT match\n"); ajFmtPrintF(flog, "\n"); #endif /* AJFALSE */ /* Check whether sequences are identical length */ if (nres[i] == pdbfile->nres[i]) { /* Sequences are identical - assign residue numbers 'by * hand' */ if (ajStrMatchS(seq[i], pdbfile->seqres[i])) { for (k = 0; k < nres[i]; k++) ajIntPut(&idx[i], k, k + 1); if (x == 0) pdbfile->resn1ok[i] = ajTrue; else pdbfile->resn1ok[i] = ajFalse; done = ajTrue; #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP1 OK\n"); #endif /* AJFALSE */ break; } /* Sequence are same length but contain mismatches */ else { for (ajStrAssignClear(&msgstr), nmismatches = 0, k = 0; k < nres[i]; k++) if (ajStrGetCharPos(seq[i], k) != ajStrGetCharPos(pdbfile->seqres[i], k)) { nmismatches++; /* Correct the seqres sequence. Replace this * with appropriate library call once * available so we don't have to probe the * internals of the structure */ /* a will give the number of the first * coordinate line for the mismatch residue * from the ATOM records */ a = pdbioPdbfileFindLine(pdbfile, i + 1, x, ajIntGet(num[i], k)); /* Get the id of the mismatch residue in the * SEQRES sequence. */ ajResidueToTriplet(pdbfile->seqres[i]->Ptr[k], &aa_misfit); /* To give correct index into SEQRES records * in original PDB file, subtract j to * account for modifications to the * N-terminus that were made for missing * residues relative to ATOM sequence. A * further 1 is added to give a number * starting from 1 (rather than 0) */ ajFmtPrintS(&msgbit, "%S%S:%S%d. ", pdbfile->rtype[a], pdbfile->pdbn[a], aa_misfit, k - j + 1); ajStrAppendS(&msgstr, msgbit); pdbfile->seqres[i]->Ptr[k] = ajStrGetCharPos(seq[i], k); } /* Sequences are same length (acceptable number of * mismatches) */ if (nmismatches <= lim) { if (nmismatches) ajFmtPrintF(flog, "%-15s%d (%c) %d %S\n", "MISMATCH", i + 1, ajChararrGet(pdbfile->chid, i), nmismatches, msgstr); for (k = 0; k < nres[i]; k++) ajIntPut(&idx[i], k, k + 1); if (x == 0) pdbfile->resn1ok[i] = ajTrue; else pdbfile->resn1ok[i] = ajFalse; done = ajTrue; #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP1 OK %d mismatches\n", nmismatches); #endif /* AJFALSE */ break; } else { /* Otherwise, sequences are same length * (unacceptable number of mismatches) Restore * the original seqres sequence */ ajStrAssignS(&(pdbfile->seqres[i]), tmpseqres); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP1 **NOT** OK %d mismatches\n", nmismatches); #endif /* AJFALSE */ } } } /***********************************************/ /******************* STEP 2 ********************/ /***********************************************/ #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP2 tmpseqres: %S\n", tmpseqres); ajFmtPrintF(flog, "chnn : %d\n" "seq1 : %S\n" "seq2 : %S\n" "seqr : %S\n", i + 1, seq1[i], seq2[i], pdbfile->seqres[i]); ajFmtPrintF(flog, "\n"); if (ajStrMatchS(seq1[i], seq2[i])) ajFmtPrintF(flog, "seq1 and seq2 match\n"); else ajFmtPrintF(flog, "seq1 and seq2 DO NOT match\n"); if (ajStrMatchS(seq1[i], pdbfile->seqres[i])) ajFmtPrintF(flog, "seq1 and seqres match\n"); else ajFmtPrintF(flog, "seq1 and seqres DO NOT match\n"); ajFmtPrintF(flog, "\n"); #endif /* AJFALSE */ /* JONNEW The code below replaces 'ORIGINAL' code block */ maxb = lim2; if (maxb >= MAJSTRGETLEN(seq[i])) maxb = MAJSTRGETLEN(seq[i]) - 1; #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "maxb = %d\nlim2 = %d\n"); ajDebug("strlen: %d\n", maxb, lim2, MAJSTRGETLEN(seq[i])); #endif /* AJFALSE */ /* First pass through loop is full-length sequence */ for (b = 0; b < maxb + 1; b++) { lenseqbit = MAJSTRGETLEN(seq[i]) - b; ajStrAssignSubS(&seqbit, seq[i], 0, (lenseqbit - 1)); ajStrAssignSubS(&bit, seq[i], (lenseqbit), -1); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "Trying ATOM substring %S\n" "versus SEQRES %S\n", seqbit, pdbfile->seqres[i]); #endif /* AJFALSE */ /* Check whether ATOM is substring of SEQRES sequence */ if ((loc = ajStrFindS(pdbfile->seqres[i], seqbit)) != -1) { /* Check to ensure that the last substring residue is * aligned to the last residue of the SEQRES residue, * otherwise, problems would arise in cases where * SEQRES sequence had C-terminal residues that were * absent from the ATOM (& therefore also substring) * sequence. */ if ((loc + lenseqbit) != pdbfile->nres[i]) break; /* ATOM is substring of SEQRES sequence - assign * residue numbers 'by hand' */ for (k = 0; k < nres[i]; k++) ajIntPut(&idx[i], k, k + (ajint) loc + 1); if (x == 0) pdbfile->resn1ok[i] = ajTrue; else pdbfile->resn1ok[i] = ajFalse; /* SEQRES sequence is missing C-terminal ATOM * residues */ if (b) { ajFmtPrintF(flog, "%-15s%d (%c) %d\n", "MISSCTERM", i + 1, ajChararrGet(pdbfile->chid, i), b); ajStrAppendS(&(pdbfile->seqres[i]), bit); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); } done = ajTrue; #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP2 OK\n"); #endif /* AJFALSE */ break; } } if (done) break; else { /* Otherwise, ATOM is NOT a substring of SEQRES sequence */ #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP2 **NOT** OK\n"); #endif /* AJFALSE */ } #if AJFALSE /* ORIGINAL */ if ((loc = ajStrFindS(pdbfile->seqres[i], seq[i])) != -1) { for (k = 0; k < nres[i]; k++) ajIntPut(&idx[i], k, k + loc + 1); if (x == 0) pdbfile->resn1ok[i] = ajTrue; else pdbfile->resn1ok[i] = ajFalse; done = ajTrue; ajFmtPrintF(flog, "STEP2 OK\n"); break; } #endif /* AJFALSE */ /***********************************************/ /******************* STEP 3 ********************/ /***********************************************/ #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP3 tmpseqres: %S\n", tmpseqres); ajFmtPrintF(flog, "chnn : %d\n" "seq1 : %S\n" "seq2 : %S\n" "seqr : %S\n", i + 1, seq1[i], seq2[i], pdbfile->seqres[i]); ajFmtPrintF(flog, "\n"); if (ajStrMatchS(seq1[i], seq2[i])) ajFmtPrintF(flog, "seq1 and seq2 match\n"); else ajFmtPrintF(flog, "seq1 and seq2 DO NOT match\n"); if (ajStrMatchS(seq1[i], pdbfile->seqres[i])) ajFmtPrintF(flog, "seq1 and seqres match\n"); else ajFmtPrintF(flog, "seq1 and seqres DO NOT match\n"); ajFmtPrintF(flog, "\n"); #endif /* AJFALSE */ /* Check whether SEQRES is substring of ATOM sequence */ /* This will only find omissions from the SEQRES sequence * where the ATOM sequence would align to it without gaps, * and where the SEQRES sequence does not have extra * N-terminal residues relative to ATOM (such cases are * caught in STEP 2) */ if ((loc = ajStrFindS(seq[i], pdbfile->seqres[i])) != -1) { /* SEQRES is substring of ATOM sequence - correct for * residues missing from SEQRES sequence and assign * residue numbers 'by hand' */ /* N-terminal insertion needed */ if (loc != 0) { ajFmtPrintF(flog, "%-15s%d (%c) %d\n", "MISSNTERM", i + 1, ajChararrGet(pdbfile->chid, i), loc); ajStrAssignSubS(&bit, seq[i], 0, loc - 1); ajStrInsertS(&(pdbfile->seqres[i]), 0, bit); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); } /* C-terminal insertion needed */ if (pdbfile->nres[i] != nres[i]) { ajFmtPrintF(flog, "%-15s%d (%c) %d\n", "MISSCTERM", i + 1, ajChararrGet(pdbfile->chid, i), (nres[i] - pdbfile->nres[i])); ajStrAssignSubS(&bit, seq[i], pdbfile->nres[i], nres[i] - 1); ajStrAppendS(&(pdbfile->seqres[i]), bit); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); } for (k = 0; k < nres[i]; k++) ajIntPut(&idx[i], k, k + 1); if (x == 0) pdbfile->resn1ok[i] = ajTrue; else pdbfile->resn1ok[i] = ajFalse; #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP3 OK\n"); */ #endif /* AJFALSE */ done = ajTrue; break; } /* Otherwise, SEQRES is NOT a substring of the ATOM sequence */ #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP3 **NOT** OK\n"); */ #endif /* AJFALSE */ /***********************************************/ /******************* STEP 4 ********************/ /***********************************************/ #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP4.1 tmpseqres: %S\n", tmpseqres); ajFmtPrintF(flog, "chnn : %d\n" "seq1 : %S\n" "seq2 : %S\n" "seqr : %S\n", i + 1, seq1[i], seq2[i], pdbfile->seqres[i]); ajFmtPrintF(flog, "\n"); if (ajStrMatchS(seq1[i], seq2[i])) ajFmtPrintF(flog, "seq1 and seq2 match\n"); else ajFmtPrintF(flog, "seq1 and seq2 DO NOT match\n"); if (ajStrMatchS(seq1[i], pdbfile->seqres[i])) ajFmtPrintF(flog, "seq1 and seqres match\n"); else ajFmtPrintF(flog, "seq1 and seqres DO NOT match\n"); ajFmtPrintF(flog, "\n"); ajFmtPrintF(flog, "STEP4.2 tmpseqres: %S\n", tmpseqres); #endif /* AJFALSE */ /* Check whether residue numbering is correct (and count the * number of mismatches) */ for (err = ajFalse, ajStrAssignClear(&msgstr), nmismatches = 0, k = 0; k < nres[i]; k++) { this_num = ajIntGet(num[i], k); /* Check to prevent array boundary error */ if (this_num <= pdbfile->nres[i]) { if (ajStrGetCharPos(seq[i], k) != ajStrGetCharPos(pdbfile->seqres[i], this_num - 1)) { nmismatches++; /* Correct the seqres sequence. Replace this with * appropriate library call once available so we * don't have to probe the internals of the * structure */ /* a will give the number of the first coordinate * line for the mismatch residue from the ATOM * records */ a = pdbioPdbfileFindLine(pdbfile, i + 1, x, ajIntGet(num[i], k)); /* Get the id of the mismatch residue in the * SEQRES sequence. */ ajResidueToTriplet( pdbfile->seqres[i]->Ptr[this_num - 1], &aa_misfit); ajFmtPrintS(&msgbit, "%S%S:%S%d. ", pdbfile->rtype[a], pdbfile->pdbn[a], aa_misfit, this_num - j); ajStrAppendS(&msgstr, msgbit); pdbfile->seqres[i]->Ptr[this_num - 1] = ajStrGetCharPos(seq[i], k); } } else { err = ajTrue; break; } } #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP4.3 tmpseqres: %S\n", tmpseqres); #endif /* AJFALSE */ if (!err) { /* Residue numbering is correct (no or acceptable number * of mismatches) */ if (nmismatches <= lim) { if (nmismatches) ajFmtPrintF(flog, "%-15s%d (%c) %d %S\n", "MISMATCH", i + 1, ajChararrGet(pdbfile->chid, i), nmismatches, msgstr); for (k = 0; k < nres[i]; k++) ajIntPut(&idx[i], k, ajIntGet(num[i], k)); if (x == 0) pdbfile->resn1ok[i] = ajTrue; else pdbfile->resn1ok[i] = ajFalse; done = ajTrue; #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP4 OK %d mismatches\n", nmismatches); #endif /* AJFALSE */ break; } else { /* Otherwise, residue numbering is incorrect * (unacceptable number of mismatches) Restore the * original seqres sequence */ ajStrAssignS(&(pdbfile->seqres[i]), tmpseqres); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP4 **NOT** OK %d mismatches\n", nmismatches); #endif /* AJFALSE */ } } else { /* Otherwise, residue numbering is incorrect (residue * number is out of range) Restore the original seqres * sequence */ ajStrAssignS(&(pdbfile->seqres[i]), tmpseqres); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP4 **NOT** OK out_of_range\n"); #endif /* AJFALSE */ } /***********************************************/ /******************* STEP 5 ********************/ /***********************************************/ #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP5 tmpseqres: %S\n", tmpseqres); ajFmtPrintF(flog, "chnn : %d\n" "seq1 : %S\n" "seq2 : %S\n" "seqr : %S\n", i + 1, seq1[i], seq2[i], pdbfile->seqres[i]); ajFmtPrintF(flog, "\n"); if (ajStrMatchS(seq1[i], seq2[i])) ajFmtPrintF(flog, "seq1 and seq2 match\n"); else ajFmtPrintF(flog, "seq1 and seq2 DO NOT match\n"); if (ajStrMatchS(seq1[i], pdbfile->seqres[i])) ajFmtPrintF(flog, "seq1 and seqres match\n"); else ajFmtPrintF(flog, "seq1 and seqres DO NOT match\n"); ajFmtPrintF(flog, "\n"); #endif /* AJFALSE */ /* None of the measures above could find the correct residue * numbering so try by alignment. * * Align the SEQRES sequence to the ATOM sequence by taking * progressively decreasing sized substrings from the ATOM * sequence. It returns 0 if any of the alignments would not * leave enough space to be able to align the rest of the * ATOM sequence to the SEQRES sequence - i.e. alignments * giving an overspill of the ATOM sequence past the * C-terminus of the SEQRES sequence are NOT allowed. * * NO Mismatches are allowed at this stage */ for (done_end = ajFalse, len = pdbfile->nres[i], siz_substr = nres[i], atom_ptr = ajStrGetPtr(seq[i]), seqres_ptr = ajStrGetPtr(pdbfile->seqres[i]); siz_substr > 0;) { ajStrAssignSubC(&substr, atom_ptr, 0, siz_substr - 1); if ((loc_ptr = strstr(seqres_ptr, ajStrGetPtr(substr))) == NULL) { siz_substr--; continue; } else { atom_idx = (int) ((atom_ptr - ajStrGetPtr(seq[i])) / sizeof (char)); seqres_idx = (int) ((loc_ptr - ajStrGetPtr(pdbfile->seqres[i])) / sizeof (char)); /* CHECK TO SEE IF THERE IS SPACE TO FIT THE REMAINER * OF THE ATOM SEQUENCE IN THE SEQRES SEQUENCE GIVEN * THIS ALIGNMENT */ if ((nres[i] - atom_idx) > (len - seqres_idx)) break; for (k = 0, y = atom_idx, z = seqres_idx; k < siz_substr; k++, y++, z++) ajIntPut(&idx[i], y, z + 1); /* Mark up last SEQRES residue as having been done */ if (y == nres[i]) done_end = ajTrue; } atom_ptr += siz_substr; seqres_ptr = loc_ptr + siz_substr; siz_substr = nres[i] - (atom_idx + siz_substr); } /* Check to ensure that position for last residue has been * worked out */ if (done_end) { #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "chnn : %d\n" "seq1 : %S\n" "seq2 : %S\n" "seqr : %S\n", i + 1, seq1[i], seq2[i], pdbfile->seqres[i]); ajFmtPrintF(flog, "\n"); if (ajStrMatchS(seq1[i], seq2[i])) ajFmtPrintF(flog, "seq1 and seq2 match\n"); else ajFmtPrintF(flog, "seq1 and seq2 DO NOT match\n"); if (ajStrMatchS(seq1[i], pdbfile->seqres[i])) ajFmtPrintF(flog, "seq1 and seqres match\n"); else ajFmtPrintF(flog, "seq1 and seqres DO NOT match\n"); ajFmtPrintF(flog, "\n"); #endif /* AJFALSE */ /* Residue numbering is correct after alignment (no * mismatches) */ if (x == 0) pdbfile->resn1ok[i] = ajTrue; else pdbfile->resn1ok[i] = ajFalse; ajFmtPrintF(flog, "%-15s%d (%c)\n", "GAPPEDOK", i + 1, ajChararrGet(pdbfile->chid, i)); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP5 OK\n"); #endif /* AJFALSE */ done = ajTrue; break; } #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP5 **NOT** OK\n"); #endif /* AJFALSE */ /* Otherwise, agreement could not be found */ /* array might contain junk values now but this should not * matter as the array should be overwritten */ /***********************************************/ /******************* STEP 6 ********************/ /***********************************************/ #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP6 tmpseqres: %S\n", tmpseqres); ajFmtPrintF(flog, "chnn : %d\n" "seq1 : %S\n" "seq2 : %S\n" "seqr : %S\n", i + 1, seq1[i], seq2[i], pdbfile->seqres[i]); ajFmtPrintF(flog, "\n"); if (ajStrMatchS(seq1[i], seq2[i])) ajFmtPrintF(flog, "seq1 and seq2 match\n"); else ajFmtPrintF(flog, "seq1 and seq2 DO NOT match\n"); if (ajStrMatchS(seq1[i], pdbfile->seqres[i])) ajFmtPrintF(flog, "seq1 and seqres match\n"); else ajFmtPrintF(flog, "seq1 and seqres DO NOT match\n"); ajFmtPrintF(flog, "\n"); #endif /* AJFALSE */ /* Try again by alignment. Mismatches ARE allowed at this * stage Must change: */ for (ajStrAssignClear(&msgstr), nmismatches = 0, done_end = ajFalse, len = pdbfile->nres[i], siz_substr = nres[i], atom_ptr = ajStrGetPtr(seq[i]), seqres_idx_last = -1, seqres_ptr = ajStrGetPtr(pdbfile->seqres[i]); siz_substr > 0;) { /* On the first pass, atom_ptr and seqres_ptr point to * the start of the ATOM and SEQRES sequences * respectively. */ founderr = ajFalse; /* Copy block (of progressively decreasing size) from * ATOM sequence to substring1 */ ajStrAssignSubC(&substr, atom_ptr, 0, siz_substr - 1); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "\n***\n%12s\n%s\n%12s\n%s\n", "Aligning ", ajStrGetPtr(substr), "To ", seqres_ptr); #endif /* AJFALSE */ /* Set loc_ptr to point to the first occurrence of * substring1 in SEQRES sequence */ /* If substring1 does not occur in SEQRES sequence */ if ((loc_ptr = strstr(seqres_ptr, ajStrGetPtr(substr))) == NULL) { /* See if there is an error in the residue id at the * start of Create a new substring from the * ATOM records but omit the first character. There * is a special case if substr is only 1 character * long. */ if (siz_substr == 1) { loc_ptr = seqres_ptr; nmismatches++; founderr = ajTrue; } else { /* Copy substring1 to substring2 but omit the * first character */ ajStrAssignSubS(&substr2, substr, 1, -1); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "\n!!!\n%12s\n%s\n%12s\n%s\n", "Aligning ", ajStrGetPtr(substr2), "To ", seqres_ptr); #endif /* AJFALSE */ /* Set loc_ptr to point to the first occurrence * of substring1 in SEQRES sequence */ /* If substring2 does not occur in the SEQRES * sequence, continue with a smaller substring */ loc_ptr = strstr(seqres_ptr, ajStrGetPtr(substr2)); if (!loc_ptr) { siz_substr--; continue; } /* substring2 is found in the SEQRES sequence */ else { /* If there is not enough space to * accommodate the 'missing' residue continue * (with a smaller substring) */ if (loc_ptr == seqres_ptr) { siz_substr--; continue; } #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "\n\n\n"); ajFmtPrintF(flog, "nmismatches = " "%d\n%12s%s\n%12s%s\n%12s%s\n%12s%s\n", nmismatches, "atom_ptr:", atom_ptr, "seqres_ptr:", seqres_ptr, "substr:", substr, "substr2", substr2); /* DIAGNOSTIC */ ajFmtPrintF(flog, "MISMATCH FOUND OK\n"); #endif /* AJFALSE */ /* There is enough space to accommodate * substring2 and the 'missing' (mismatch) * residue */ nmismatches++; founderr = ajTrue; } } } #if AJFALSE /* DIAGNOSTIC */ else ajFmtPrintF(flog, "ALIGNMENT FOUND OK\n"); #endif /* AJFALSE */ /* atom_idx and seqres_idx are set to give the index into * ATOM and SEQRES sequences respectively for the * position of match of N-terminal residue of substring * (if founderr is True this will be the position of the * N-terminal mismatch residue) */ atom_idx = (int) ((atom_ptr - ajStrGetPtr(seq[i])) / sizeof (char)); if (founderr) seqres_idx = (int) (((loc_ptr - 1) - ajStrGetPtr(pdbfile->seqres[i])) / sizeof (char)); else seqres_idx = (int) ((loc_ptr - ajStrGetPtr(pdbfile->seqres[i])) / sizeof (char)); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "seqres_idx : %d\n", seqres_idx); #endif /* AJFALSE */ /* If there was a mismatch residue, idx_misfit_atom and * idx_misfit_seqres will give the index into the ATOM * and SEQRES sequences respectively for its position */ if (founderr) { idx_misfit_atom = atom_idx; idx_misfit_seqres = seqres_idx; } /* CHECK TO SEE IF THERE IS SPACE TO FIT THE REMAINDER OF * THE ATOM SEQUENCE IN THE SEQRES SEQUENCE GIVEN THIS * ALIGNMENT */ if ((nres[i] - atom_idx) > (len - seqres_idx)) break; /**************************************************/ /* This will have to change for 1st residue */ /**************************************************/ /* Try and find an exact match within the gap for the * mismatch residue */ fixed = ajFalse; if (founderr) { #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "About to try (seqres_idx_last: %d, " "seqres_idx: %d) ...\n", seqres_idx_last, seqres_idx); #endif /* AJFALSE */ aa_last = ajStrGetCharFirst(substr); for (z = seqres_idx_last + 1; z < seqres_idx; z++) { #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "Trying ATOM:SEQRES %c:%c\n", aa_last, pdbfile->seqres[i]->Ptr[z]); #endif /* AJFALSE */ if (pdbfile->seqres[i]->Ptr[z] == aa_last) { nmismatches--; founderr = ajFalse; fixed = ajTrue; /* Assign residue number */ ajIntPut(&idx[i], atom_idx, z + 1); for (k = 0, y = atom_idx + 1, z = seqres_idx + 1; k < siz_substr - 1; k++, y++, z++) ajIntPut(&idx[i], y, z + 1); break; } } } if (!fixed) { #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "FAILED TO FIX\n"); #endif /* AJFALSE */ /* Assign residue number */ for (k = 0, y = atom_idx, z = seqres_idx; k < siz_substr; k++, y++, z++) ajIntPut(&idx[i], y, z + 1); } /* Mark up last SEQRES residue as having been done */ if (y == nres[i]) done_end = ajTrue; /* If the substring matched but with a residue mismatch * for the 1st residue */ /**************************************************/ /* This block should only be called if we */ /* can't fit the mismatch residue in somewhere. */ /**************************************************/ if (founderr) { /* a will give the number of the first coordinate * line for the mismatch residue from the ATOM * records */ a = pdbioPdbfileFindLine(pdbfile, i + 1, x, ajIntGet(num[i], idx_misfit_atom)); /* Get the id of the mismatch residue in the SEQRES * sequence. */ ajResidueToTriplet( pdbfile->seqres[i]->Ptr[idx_misfit_seqres], &aa_misfit); /* To give correct index into SEQRES records in * original PDB file, subtract j to account for * modifications to the N-terminus that were made for * missing residues relative to ATOM sequence. A * further 1 is added to give a number starting from * 1 (rather than 0) */ #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "a : %d\n".a); ajFmtPrintF(flog, "pdbfile->rtype[a] : %S\n", pdbfile->rtype[a]); ajFmtPrintF(flog, "pdbfile->pdbn[a] : %S\n", pdbfile->pdbn[a]); #endif /* AJFALSE */ ajFmtPrintS(&msgbit, "%S %S %S %d; ", pdbfile->rtype[a], pdbfile->pdbn[a], aa_misfit, idx_misfit_seqres - j + 1); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintS(&msgbit, "ATOM residue %d (%c) vs " "SEQRES residue %d (%c). ", ajIntGet(num[i], atom_idx), ajStrGetCharFirst(substr), seqres_idx + 1, pdbfile->seqres[i]->Ptr[seqres_idx]); #endif /* AJFALSE */ ajStrAppendS(&msgstr, msgbit); pdbfile->seqres[i]->Ptr[seqres_idx] = ajStrGetCharFirst(substr); } /* atom_ptr and seqres_ptr now point to 1 residue past * the end of the match of the substring in the ATOM and * SEQRES sequences respectively. */ atom_ptr += siz_substr; if (founderr) seqres_ptr = (loc_ptr - 1) + siz_substr; else seqres_ptr = loc_ptr + siz_substr; siz_substr = nres[i] - (atom_idx + siz_substr); /**************************************************/ /* Must assign index into SEQRES for */ /* C-terminal residue of substring */ /**************************************************/ /* seqres_idx_last is set to give the index into SEQRES * sequence for the position of match of C-terminal * residue of substring + 1 */ seqres_idx_last = (int) z - 1; } /* Check to ensure that position for last residue has been * worked out */ if ((done_end) && (nmismatches <= lim)) { if (nmismatches) ajFmtPrintF(flog, "%-15s%d (%c) %d %S\n", "GAPPED", i + 1, ajChararrGet(pdbfile->chid, i), nmismatches, msgstr); else ajFmtPrintF(flog, "%-15s%d (%c)\n", "GAPPEDOK", i + 1, ajChararrGet(pdbfile->chid, i)); /* Residue numbering is correct after alignment * (acceptable number of mismatches) */ if (x == 0) pdbfile->resn1ok[i] = ajTrue; else pdbfile->resn1ok[i] = ajFalse; #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP6 OK %d mismatches\n", nmismatches); #endif /* AJFALSE */ done = ajTrue; break; } #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "STEP6 **NOT** OK %d mismatches\n", nmismatches); #endif /* AJFALSE */ /* Otherwise, agreement could not be found - unacceptable * number of mismatches. Restore the original seqres sequence */ ajStrAssignS(&(pdbfile->seqres[i]), tmpseqres); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); } /* for(x = 0; x < 2; x++) */ if (done) break; } /* After trying 6 different alignment steps with (potentially * modified) seqres sequences and 2 (possibly different) sequences * derived from the ATOM records an alignment with agreement in * residue numbering still cannot be found. Use data from ATOM * records only - use seq1 (all residues) and presume there are no * missing residues. */ if (!done) { ajFmtPrintF(flog, "%-15s%d (%c)\n", "NOMATCH", i + 1, ajChararrGet(pdbfile->chid, i)); ajStrAssignS(&(pdbfile->seqres[i]), seq1[i]); pdbfile->nres[i] = ajStrGetLen(pdbfile->seqres[i]); for (k = 0; k < nres1[i]; k++) ajIntPut(&idx[i], k, k + 1); pdbfile->resn1ok[i] = ajTrue; } else { if (j) ajFmtPrintF(flog, "%-15s%d (%c) %d\n", "MISSNTERM", i + 1, ajChararrGet(pdbfile->chid, i), j); if (!ajStrMatchS(seq1[i], seq2[i])) { if (x == 0) ajFmtPrintF(flog, "%-15s%d\n", "ALTERNOK", i + 1); else ajFmtPrintF(flog, "%-15s%d\n", "HETEROK", i + 1); } } } /* Write the index arrays */ for (i = 0; i < pdbfile->nchains; i++) { if (!pdbfile->chainok[i]) continue; if (pdbfile->resn1ok[i]) for (j = 0; j < nres1[i]; j++) ajIntPut(&idx_full[i], ajIntGet(num1[i], j), ajIntGet(idx[i], j)); else for (j = 0; j < nres2[i]; j++) ajIntPut(&idx_full[i], ajIntGet(num2[i], j), ajIntGet(idx[i], j)); } /* Write the resni element of the Pdbfile object. These are the residue * numbers that give the correct index into the finalised seqres sequence */ for (i = pdbfile->idxfirst; i < pdbfile->nlines; i++) { if (pdbfile->linetype[i] == pdbfileELinetypeCoordinate) { if (pdbfile->resn1ok[pdbfile->chnn[i] - 1]) { pdbfile->resni[i] = ajIntGet(idx_full[pdbfile->chnn[i] - 1], pdbfile->resn1[i]); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "Got position %d (%d) ok\n", pdbfile->resn1[i], ajIntGet(idx_full[pdbfile->chnn[i] - 1], pdbfile->resn1[i])); #endif /* AJFALSE */ } else { pdbfile->resni[i] = ajIntGet(idx_full[pdbfile->chnn[i] - 1], pdbfile->resn2[i]); #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "Got position %d (%d) ok\n", pdbfile->resn2[i], ajIntGet(idx_full[pdbfile->chnn[i] - 1], pdbfile->resn2[i])); #endif /* AJFALSE */ } } } #if AJFALSE /* DIAGNOSTIC */ ajFmtPrintF(flog, "\n\n\n"); seq = seq1; for (i = 0; i < pdbfile->nchains; i++) { if (!pdbfile->chainok[i]) { ajFmtPrintF(flog, "Chain %d\nSEQRES %S\nCHAIN NOT OK\n\n\n", i + 1, pdbfile->seqres[i]); continue; } ajFmtPrintF(flog, "Chain %d\nSEQRES %S\nSEQ__1 %S\nSEQ__2 %S\n\n\n", i + 1, pdbfile->seqres[i], seq1[i], seq2[i]); } for (i = 0; i < pdbfile->nchains; i++) { if (!pdbfile->chainok[i]) continue; if (nres1[i] > nres2[i]) max = nres1[i]; else max = nres2[i]; ajFmtPrintF(flog, "CHAIN %d\n", i + 1); ajFmtPrintF(flog, "seqres %S\n", pdbfile->seqres[i]); ajFmtPrintF(flog, "%-6s%-6s%-6s%-6s%-6s%-6s%-6s\n", "RES", "NUM", "SEQ1", "RESN1", "SEQ2", "RESN2", "IDX"); for (j = 0; j < max; j++) { if (j < nres1[i] && j < nres2[i]) ajFmtPrintF(flog, "%-6s%-6d%-6c%-6d%-6c%-6d%-6d\n", "RES", j + 1, ajStrGetCharPos(seq1[i], j), ajIntGet(num1[i], j), ajStrGetCharPos(seq2[i], j), ajIntGet(num2[i], j), ajIntGet(idx[i], j)); else if (j < nres1[i]) { ajFmtPrintF(flog, "%-6s%-6d%-6c%-6d%-6c%-6c", "RES", j + 1, ajStrGetCharPos(seq1[i], j), ajIntGet(num1[i], j), '.', '.'); if (pdbfile->resn1ok[i]) ajFmtPrintF(flog, "%-6d\n", ajIntGet(idx[i], j)); else ajFmtPrintF(flog, "%-6c\n", '.'); } else { ajFmtPrintF(flog, "%-6s%-6d%-6c%-6c%-6c%-6d", "RES", j + 1, '.', '.', ajStrGetCharPos(seq2[i], j), ajIntGet(num2[i], j)); if (!pdbfile->resn1ok[i]) ajFmtPrintF(flog, "%-6d\n", ajIntGet(idx[i], j)); else ajFmtPrintF(flog, "%-6c\n", '.'); } } } #endif /* AJFALSE */ /* Tidy up and return */ ajStrDel(&aa_misfit); ajStrDel(&seqbit); ajStrDel(&msgstr); ajStrDel(&msgbit); AJFREE(insert); ajStrDel(&tmpseqres); ajStrDel(&bit); ajStrDel(&substr); ajStrDel(&substr2); for (i = 0U; i < pdbfile->nchains; i++) { if (!pdbfile->chainok[i]) continue; ajStrDel(&seq1[i]); ajStrDel(&seq2[i]); ajIntDel(&num1[i]); ajIntDel(&num2[i]); ajIntDel(&idx[i]); ajIntDel(&idx_full[i]); } AJFREE(seq1); AJFREE(seq2); AJFREE(num1); AJFREE(num2); AJFREE(idx); AJFREE(idx_full); AJFREE(nres1); AJFREE(nres2); return ajTrue; } ID pdbioPdbfileToPdb TY static MO ajpdbio LB core XX DE Reads data from a Pdbfile object and writes a Pdb object. DE Chains that did not contain at least the user-defined threshold number of DE amino acid residues are discarded, i.e. are NOT copied and will NOT appear DE in the output file that is eventually generated. XX PN [1] PA w Ppdb AjPPdb* PD Pdb object pointer PX PN [2] PA u pdbfile AjPPdbfile PD Pdbfile object PX RT AjBool RD True on success, False otherwise RX // static AjBool pdbioPdbfileToPdb(AjPPdb *Ppdb, AjPPdbfile pdbfile) { ajuint i = 0U; /* Loop counter */ ajint idx = 0; /* Index into chain array */ ajuint j = 0U; /* Loop counter */ AjPAtom atom = NULL; /* Atom object */ AjPResidue residue = NULL; /* Residue object */ ajuint nchn = 0U; /* No. chains that have min. no. of aa's */ AjPInt lookup; /* Array of chain numbers for chains in ret * for all chains in pdb.A '0' is given for * chains with < threshold no. of aa's */ ajint chn = 0; ajuint rn_last = UINT_MAX; ajuint mn_last = UINT_MAX; ajint eNum; AjPStr eId = NULL; char eType; ajint eClass; if (!Ppdb || !pdbfile) { ajWarn("Bad args passed to pdbioPdbfileToPdb"); return ajFalse; } if (*Ppdb) { ajWarn("Bad args passed to pdbioPdbfileToPdb - PDB object exists"); return ajFalse; } eId = ajStrNew(); lookup = ajIntNewRes(pdbfile->nchains); ajIntPut(&lookup, pdbfile->nchains - 1, 0); for (nchn = 0U, i = 0U; i < pdbfile->nchains; i++) if (pdbfile->chainok[i]) { nchn++; ajIntPut(&lookup, i, nchn); } *Ppdb = ajPdbNew(nchn); (*Ppdb)->Nchn = nchn; ajStrAssignS(&((*Ppdb)->Pdb), pdbfile->pdbid); ajStrAssignS(&((*Ppdb)->Compnd), pdbfile->compnd); ajStrAssignS(&((*Ppdb)->Source), pdbfile->source); (*Ppdb)->Method = pdbfile->method; (*Ppdb)->Reso = pdbfile->reso; (*Ppdb)->Nmod = pdbfile->modcnt; (*Ppdb)->Ngp = pdbfile->ngroups; /* (*Ppdb)->Nchn = pdbfile->nchains; */ for (i = 0U; i < pdbfile->ngroups; i++) ajChararrPut(&((*Ppdb)->gpid), i, ajChararrGet(pdbfile->gpid, i)); for (idx = -1, i = 0U; i < pdbfile->nchains; i++) { if (pdbfile->chainok[i]) idx++; else continue; (*Ppdb)->Chains[idx]->Id = ajChararrGet(pdbfile->chid, i); /* These counts are no longer made from the PDB records. They are * only made if the file is annotated with stride secondary structure * info by using pdbstride */ #if AJFALSE (*Ppdb)->Chains[idx]->numHelices = pdbfile->numHelices[i]; (*Ppdb)->Chains[idx]->numStrands = pdbfile->numStrands[i]; (*Ppdb)->Chains[idx]->numSheets = pdbfile->numSheets[i]; (*Ppdb)->Chains[idx]->numTurns = pdbfile->numTurns[i]; #endif /* AJFALSE */ (*Ppdb)->Chains[idx]->Nres = pdbfile->nres[i]; (*Ppdb)->Chains[idx]->Nlig = pdbfile->nligands[i]; ajStrAssignS(&((*Ppdb)->Chains[idx]->Seq), pdbfile->seqres[i]); } for (j = pdbfile->idxfirst; j < pdbfile->nlines; j++) { if ((pdbfile->linetype[j] == pdbfileELinetypeCoordinate) || (pdbfile->linetype[j] == pdbfileELinetypeHeterogen) || (pdbfile->linetype[j] == pdbfileELinetypeGroups) || (pdbfile->linetype[j] == pdbfileELinetypeWater)) { /* Skip this line if it for a heterogenous (duplicate) position */ if ((!pdbfile->resn1ok[pdbfile->chnn[j] - 1]) && pdbfile->oddnum[j]) continue; atom = ajAtomNew(); atom->Mod = pdbfile->modn[j]; /* atom->Chn = pdbfile->chnn[j]; */ atom->Chn = ajIntGet(lookup, pdbfile->chnn[j] - 1); atom->Gpn = pdbfile->gpn[j]; switch (pdbfile->linetype[j]) { case pdbfileELinetypeHeterogen: atom->Type = 'H'; break; case pdbfileELinetypeGroups: atom->Type = 'H'; break; case pdbfileELinetypeCoordinate: atom->Type = 'P'; break; case pdbfileELinetypeWater: atom->Type = 'W'; break; default: break; } atom->Idx = pdbfile->resni[j]; ajStrAssignS(&atom->Pdb, pdbfile->pdbn[j]); if ((pdbfile->linetype[j] == pdbfileELinetypeHeterogen) || (pdbfile->linetype[j] == pdbfileELinetypeGroups) || (pdbfile->linetype[j] == pdbfileELinetypeWater)) atom->Id1 = '.'; else ajResidueFromTriplet(pdbfile->rtype[j], &atom->Id1); ajStrAssignS(&atom->Id3, pdbfile->rtype[j]); ajStrAssignS(&atom->Atm, pdbfile->atype[j]); atom->X = pdbfile->x[j]; atom->Y = pdbfile->y[j]; atom->Z = pdbfile->z[j]; atom->O = pdbfile->o[j]; atom->B = pdbfile->b[j]; ajStrAssignS(&eId, pdbfile->elementId[j]); eNum = pdbfile->elementNum[j]; eType = pdbfile->elementType[j]; eClass = pdbfile->helixClass[j]; if (pdbfile->linetype[j] == pdbfileELinetypeGroups) ajListPushAppend((*Ppdb)->Groups, atom); else if (pdbfile->linetype[j] == pdbfileELinetypeWater) ajListPushAppend((*Ppdb)->Water, atom); else { if (pdbfile->chainok[pdbfile->chnn[j] - 1]) { #if AJFALSE ajListPushAppend((*Ppdb)->Chains[pdbfile->chnn[j] - 1]->Atoms, atom); ajListPushAppend((*Ppdb)->Chains[ajIntGet(lookup, pdbfile->chnn[j] - 1) - 1]->Atoms, atom); #endif /* AJFALSE */ chn = ajIntGet(lookup, pdbfile->chnn[j] - 1) - 1; ajListPushAppend((*Ppdb)->Chains[chn]->Atoms, atom); /* Write residue object */ if (atom->Type == 'P') { /* New model */ if (atom->Mod != mn_last) { rn_last = UINT_MAX; mn_last = atom->Mod; } /* New residue */ if (atom->Idx != rn_last) { residue = ajResidueNew(); residue->Mod = atom->Mod; residue->Chn = atom->Chn; residue->Idx = atom->Idx; ajStrAssignS(&residue->Pdb, atom->Pdb); residue->Id1 = atom->Id1; ajStrAssignS(&residue->Id3, atom->Id3); residue->eNum = eNum; ajStrAssignS(&residue->eId, eId); residue->eType = eType; residue->eClass = eClass; ajListPushAppend((*Ppdb)->Chains[chn]->Residues, (void *) residue); rn_last = atom->Idx; } } } else ajAtomDel(&atom); } } else continue; } ajIntDel(&lookup); ajStrDel(&eId); return ajTrue; } ID pdbioPdbfileFindLine TY static MO ajpdbio LB core XX DE Returns the line number of the first instance of a line with a specified DE residue and chain number. XX PN [1] PA r pdbfile const AjPPdbfile PD Pdbfile object pointer PX PN [2] PA r chn ajint PD Chain number PX PN [3] PA r which ajint PD 0 or 1, refer to resn1 or resn2 residue PX PN [4] PA r pos ajint PD Residue number PX RT ajint RD Line number (index, i.e. starts from 0). RX // static ajint pdbioPdbfileFindLine(const AjPPdbfile pdbfile, ajint chn, ajint which, ajint pos) { ajuint a = 0U; /* a will give the number of the first coordinate line for the mismatch * residue from the ATOM records */ for (a = pdbfile->idxfirst; a < pdbfile->nlines; a++) if (pdbfile->linetype[a] == pdbfileELinetypeCoordinate && pdbfile->chnn[a] == chn) /* First sequence (all residues) derived for atom records */ /* OR Second sequence (excluding certain residues) derived for * atom records */ if (((which == 0) && (pos == pdbfile->resn1[a])) || ((which == 1) && (pos == pdbfile->resn2[a]))) break; if (a == pdbfile->nlines) ajFatal("Unexpected loop failure in pdbioPdbfileFindLine. " "Email jison@hgmp.mrc.ac.uk\n"); return a; } ID pdbioPdbfileChain TY static MO ajpdbio LB core XX DE Finds the chain number for a given chain identifier in a pdbfile structure XX PN [1] PA r id char PD Chain identifier PX PN [2] PA r pdbfile const AjPPdbfile PD Pdbfile object PX PN [3] PA w chn ajint* PD Chain number PX RT AjBool RD True on success RX // static AjBool pdbioPdbfileChain(char id, const AjPPdbfile pdbfile, ajint *chn) { ajuint a = 0U; for (a = 0U; a < pdbfile->nchains; a++) { if (toupper((int) ajChararrGet(pdbfile->chid, a)) == toupper((int) id)) { *chn = a + 1; return ajTrue; } /* Cope with chain id's of ' ' (which might be given as '.' in the * Pdbfile object) */ if ((id == ' ') && (ajChararrGet(pdbfile->chid, a) == '.')) { *chn = a + 1; return ajTrue; } } /* A '.' may be given as the id for domains comprising more than one * chain */ if (id == '.') { *chn = 1; return ajTrue; } return ajFalse; } ID pdbioWriteElementData TY static MO ajpdbio LB core XX DE Reads the secondary structure information from an Elements object DE and writes equivalent variables in an Pdbfile object. XX PN [1] PA w pdbfile AjPPdbfile PD Pdbfile object PX PN [2] PA u flog AjPFile PD Pointer to log file (build diagnostics) PX PN [3] PA r elms const AjPElements PD Elements object pointer PX RT AjBool RD True on success, False otherwise RX // static AjBool pdbioWriteElementData(AjPPdbfile pdbfile, AjPFile flog, const AjPElements elms) { ajuint x = 0U; ajint y = 0; ajint z = 0; ajuint modn = 0U; /* Model number */ ajuint idx = 0U; /* idx into lines in Pdbfile object */ ajuint idx_start = 0u; /* Line index of start of element */ ajuint idx_end = 0U; /* Line index of end of element */ ajuint idx_last = 0U; /* Line index of last line to try */ ajuint idx_tmp = 0U; /* Temp. line index */ ajint chn = 0; /* Chain id of current element as integer */ AjPInt nsheets = NULL; /* Number of sheets in each chain */ AjPStr *lastids = NULL; /* Last sheet identifier read in for each * chain */ AjBool found_start = ajFalse; /* Whether start residue of the * current element has been found yet */ AjBool found_end = ajFalse; /* Whether the end residue of the current * element has been found yet */ ajint n_unknown = 0; /* No. of unknown chain ids */ AjPChar unknowns = NULL; /* Unknown chain ids */ AjBool found = ajFalse; /* True if we have already reported an error * message for the unknown chain id */ /* Check args */ if (!pdbfile || !(elms) || !(flog)) { ajWarn("NULL arg passed to pdbioWriteElementData"); return ajFalse; } /* Assign default values for secondary structure fields in Pdbfile object */ for (x = 0U; x < pdbfile->nlines; x++) { pdbfile->elementType[x] = '.'; ajStrAssignC(&pdbfile->elementId[x], "."); } /* Allocate memory */ nsheets = ajIntNewRes(pdbfile->nchains); ajIntPut(&nsheets, pdbfile->nchains, 0); unknowns = ajChararrNew(); AJCNEW0(lastids, pdbfile->nchains); for (x = 0U; x < pdbfile->nchains; x++) { lastids[x] = ajStrNew(); /* Assign a silly value for starters */ ajStrAssignC(&lastids[x], "?????"); } #if AJFALSE ajFmtPrint("LOOK HERE x:%d elms->n:%d\n", x, elms->n); #endif /* AJFALSE */ for (modn = 1U; modn <= pdbfile->modcnt; modn++) { /* Loop for each element. Set the current line to the first line in * the file */ for (idx = pdbfile->idxfirst, x = 0U; x < elms->n; x++) { /* Find the chain number of the current element */ if (!pdbioPdbfileChain(elms->elms[x]->chainId, pdbfile, &chn)) { /* Only report errors once for each unknown id */ if (modn == 1) { for (found = ajFalse, y = 0; y < n_unknown; y++) if (ajChararrGet(unknowns, y) == elms->elms[x]->chainId) { found = ajTrue; break; } if (!found) { ajFmtPrintF(flog, "%-15s%c %u\n", "SECCHAIN", elms->elms[x]->chainId, idx); ajChararrPut(&unknowns, n_unknown, elms->elms[x]->chainId); n_unknown++; } } continue; } /* Only want to do this once (for the first model ) */ if (modn == 1) { /* Make a count of the number of beta sheets */ if (elms->elms[x]->elementType == 'E') if (!ajStrMatchS(lastids[chn - 1], elms->elms[x]->elementId)) { ajIntInc(&nsheets, chn - 1); ajStrAssignS(&lastids[chn - 1], elms->elms[x]->elementId); } } /* Loop for two passes. z is for efficiency, if z == 0 it will * check from the current position up to the last coordinate * line, if z == 1 it will check from the first coordinate line up * to the last position checked */ for (found_start = ajFalse, found_end = ajFalse, z = 0; z < 2; z++) { if (z == 0) idx_last = pdbfile->nlines; else { idx = pdbfile->idxfirst; idx_last = idx_tmp; } /* Find the start and end of the current element (as an index * into the line array) */ for (; idx < idx_last; idx++) { /* Find the correct chain and skip lines that are not for * amino acids */ if ((pdbfile->chnn[idx] != chn) || (pdbfile->linetype[idx] != pdbfileELinetypeCoordinate) || (pdbfile->modn[idx] != modn)) continue; /* We have not found the start residue yet */ if (!found_start) if (ajStrMatchS(elms->elms[x]->initSeqNum, pdbfile->pdbn[idx])) if (ajStrMatchS(elms->elms[x]->initResName, pdbfile->rtype[idx])) { /* Residue number for start found and residue * type matches */ idx_start = idx; /* printf("found_start !\n"); */ found_start = ajTrue; } if (ajStrMatchS(elms->elms[x]->endSeqNum, pdbfile->pdbn[idx])) if (ajStrMatchS(elms->elms[x]->endResName, pdbfile->rtype[idx])) { /* Residue number for end found and residue type * matches */ idx_end = idx; /* printf("idx_end: %d ...", idx_end); */ /* Set the index to the LAST atom of the residue */ for (; idx_end < pdbfile->nlines; idx_end++) { if (pdbfile->linetype[idx_end] != pdbfileELinetypeCoordinate) continue; if (!ajStrMatchS(elms->elms[x]->endSeqNum, pdbfile->pdbn[idx_end]) || !ajStrMatchS(elms->elms[x]->endResName, pdbfile->rtype[idx_end]) || pdbfile->chnn[idx_end] != chn || pdbfile->modn[idx_end] != modn) break; } idx_end--; #if AJFALSE printf(" %d\n", idx_end); ajFmtPrint("found_end !\n" "elms->elms[x]->endSeqNum " ": pdbfile->pdbn[idx_end] === %S : %S\n" "elms->elms[x]->endResName " ": pdbfile->rtype[idx_end] === %S : %S\n" "pdbfile->chnn[idx_end] " ": chn === %d : %d\n" "pdbfile->modn[idx_end] " ": modn === %d : %d\n", elms->elms[x]->endSeqNum, pdbfile->pdbn[idx_end + 1], elms->elms[x]->endResName, pdbfile->rtype[idx_end + 1], pdbfile->chnn[idx_end + 1], chn, pdbfile->modn[idx_end + 1], modn); #endif /* AJFALSE */ found_end = ajTrue; idx_tmp = idx; break; } } if (found_start && found_end) break; } if (!found_start || !found_end) { if (!found_start && !found_end) ajFmtPrintF(flog, "%-15s%d %d %S %S %S %S\n", "SECBOTH", chn, modn, elms->elms[x]->initResName, elms->elms[x]->initSeqNum, elms->elms[x]->endResName, elms->elms[x]->endSeqNum); else if (!found_start) ajFmtPrintF(flog, "%-15s%d %d %S %S\n", "SECSTART", chn, modn, elms->elms[x]->initResName, elms->elms[x]->initSeqNum); else if (!found_end) ajFmtPrintF(flog, "%-15s%d %d %S %S\n", "SECEND", chn, modn, elms->elms[x]->endResName, elms->elms[x]->endSeqNum); } /* Assign secondary structure fields in Pdbfile object */ for (idx = idx_start; idx <= idx_end; idx++) { pdbfile->elementNum[idx] = elms->elms[x]->elementNum; pdbfile->elementType[idx] = elms->elms[x]->elementType; if (elms->elms[x]->elementType == 'H') pdbfile->helixClass[idx] = elms->elms[x]->helixClass; ajStrAssignS(&pdbfile->elementId[idx], elms->elms[x]->elementId); } /* Only want to do this once */ if (modn == 1) { if (elms->elms[x]->elementType == 'H') pdbfile->numHelices[chn - 1]++; else if (elms->elms[x]->elementType == 'E') pdbfile->numStrands[chn - 1]++; else if (elms->elms[x]->elementType == 'T') pdbfile->numTurns[chn - 1]++; } } } /* Assign number of sheets */ if (modn == 1) for (x = 0; x < pdbfile->nchains; x++) pdbfile->numSheets[x] = ajIntGet(nsheets, x); /* Tidy up and return */ ajIntDel(&nsheets); for (x = 0; x < pdbfile->nchains; x++) ajStrDel(&lastids[x]); AJFREE(lastids); ajChararrDel(&unknowns); return ajTrue; } ID ajPdbReadRawNew TY public MO ajpdbio LB core XX DE Reads a pdb file and returns a pointer to a filled Pdb object. DE DE The pdb id is derived from the file name and extension of the pdb file DE (these are passed in by argument). XX PN [1] PA u inf AjPFile PD Pointer to pdb file PX PN [2] PA r pdbid const AjPStr PD PDB id code of pdb file PX PN [3] PA r min_chain_size ajint PD Minimum number of amino acids in a chain PX PN [4] PA r max_mismatch ajint PD Maximum number of permissible mismatches PD between the ATOM and SEQRES sequences PX PN [5] PA r max_trim ajint PD Max. no. residues to trim when checking PD for missing N- or C-terminal ATOM or PD SEQRES sequences. PX PN [6] PA r camask AjBool PD Whether to mask non-amino acid groups PD within protein chains which do not have a C-alpha atom. PX PN [7] PA r camask1 AjBool PD Whether to mask amino acid residues PD within protein chains which do not have a C-alpha atom. PX PN [8] PA r atommask AjBool PD Whether to mask residues or groups PD in protein chains with a single atom only. PX PN [9] PA u flog AjPFile PD Pointer to log file (build diagnostics) PX RT AjPPdb RD pdb object pointer, or NULL on failure. RX // AjPPdb ajPdbReadRawNew(AjPFile inf, const AjPStr pdbid, ajint min_chain_size, ajint max_mismatch, ajint max_trim, AjBool camask, AjBool camask1, AjBool atommask, AjPFile flog) { AjPPdbfile pdbfile = NULL; /* Pdbfile structure (for raw data) */ AjPPdb ret = NULL; /* Pdb structure (for parsed data) */ AjPElements elms = NULL; /* Elements structure (for parsed data) */ if (!inf || !flog) { ajWarn("Null arg passed to ajPdbReadRawNew"); return NULL; } /* Write pdbfile structure */ if (!(pdbfile = pdbioReadLines(inf))) return NULL; /* Allocate Elements object */ elms = pdbioElementsNew(0); ajStrAssignS(&(pdbfile->pdbid), pdbid); ajStrFmtLower(&(pdbfile->pdbid)); /* Initial read of pdb file, read sequences for chains from SEQRES * records, mark lines up to ignore or as coordinate lines, assigning * initial residue numbers, read bibliographic information etc. */ if (!pdbioFirstPass(pdbfile, flog, &elms, camask)) { pdbioPdbfileDel(&pdbfile); pdbioElementsDel(&elms); return NULL; } #if AJFALSE /* DIAGNOSTIC */ pdbioDiagnostic(&pdbfile, 0); pdbioDiagnostic(&pdbfile, 1); #endif /* AJFALSE */ /* Check that SEQRES records contain protein chains. Check that chain * id's are unique */ if (!pdbioCheckChains(pdbfile, flog, min_chain_size)) { pdbioPdbfileDel(&pdbfile); pdbioElementsDel(&elms); return NULL; } /* Check for correct number of TER records. Mask unwanted TER records */ if (!pdbioCheckTer(pdbfile, flog)) { pdbioPdbfileDel(&pdbfile); pdbioElementsDel(&elms); return NULL; } /* Assign model and chain number to each coordinate line. Mark up * non-protein coordinates */ if (!pdbioNumberChains(pdbfile, flog)) { pdbioPdbfileDel(&pdbfile); pdbioElementsDel(&elms); return NULL; } /* Mask any ATOM or HETATM records with chain id's of chains of * non-proteins or chains that have non-unique id's (chainok==ajFalse). * Check that ATOM records contain protein chains. */ if (!pdbioMaskChains(pdbfile, flog, min_chain_size, camask, camask1, atommask)) { pdbioElementsDel(&elms); pdbioPdbfileDel(&pdbfile); return NULL; } #if AJFALSE /* DIAGNOSTIC */ pdbioDiagnostic(pdbfile, 0); pdbioDiagnostic(pdbfile, 1); #endif /* AJFALSE */ /* Standardise residue numbering */ if (!pdbioStandardiseNumbering(pdbfile, flog)) { pdbioPdbfileDel(&pdbfile); pdbioElementsDel(&elms); return NULL; } /* Find correct residue numbering */ if (!pdbioAlignNumbering(pdbfile, flog, max_mismatch, max_trim)) { pdbioPdbfileDel(&pdbfile); pdbioElementsDel(&elms); return NULL; } if (!pdbioWriteElementData(pdbfile, flog, elms)) { pdbioPdbfileDel(&pdbfile); pdbioElementsDel(&elms); return NULL; } pdbioElementsDel(&elms); /* Copy data from Pdbfile object to Pdb object. pdbioPdbfileToPdb creates the * Pdb object (ret) */ if (!pdbioPdbfileToPdb(&ret, pdbfile)) { pdbioPdbfileDel(&pdbfile); ajPdbDel(&ret); return NULL; } /* Tidy up and return */ pdbioPdbfileDel(&pdbfile); return ret; } ID ajPdbWriteDomainRecordRaw TY public MO ajpdbio LB core XX DE Writes lines to a PDB file. What is written depends upon the mode: DE ajEPdbioModeHeaderDomain Header line for domain PDB file. DE ajEPdbioModeSeqresDomain SEQRES records for domain. DE ajEPdbioModeAtomPdbDomain ATOM records for domain using original residue DE numbers. DE ajEPdbioModeAtomIdxDomain ATOM records for domain using residues numbers DE that give correct index into SEQRES sequence. XX PN [1] PA u mode AjEPdbioMode PD Mode that controls what is printed: one of PD ajEPdbioModeHeaderDomain, ajEPdbioModeSeqresDomain, PD ajEPdbioModeAtomPdbDomain, ajEPdbioModeAtomIdxDomain PX PN [2] PA r pdb const AjPPdb PD Pdb object PX PN [3] PA r mod ajint PD Model number PX PN [4] PA r scop const AjPScop PD Scop object for domain PX PN [5] PA w outf AjPFile PD Output file stream PX PN [6] PA w errf AjPFile PD Output file stream for error messages PX RT AjBool RD True on success RX // AjBool ajPdbWriteDomainRecordRaw(AjEPdbioMode mode, const AjPPdb pdb, ajint mod, const AjPScop scop, AjPFile outf, AjPFile errf) { /* Check args */ if (!outf || !scop) ajFatal("Invalid args passed to ajPdbWriteDomainRecordRaw"); switch (mode) { case ajEPdbioModeHeaderDomain: if (!pdbioWriteHeaderScop(outf, scop)) return ajFalse; break; case ajEPdbioModeSeqresDomain: if (!errf || !pdb) ajFatal("Invalid args passed to ajPdbWriteDomainRecordRaw"); if (!pdbioWriteSeqresDomain(errf, outf, pdb, scop)) return ajFalse; break; case ajEPdbioModeAtomPdbDomain: if (!errf || !pdb) ajFatal("Invalid args passed to ajPdbWriteDomainRecordRaw"); if (!pdbioWriteAtomDomainPdb(errf, outf, pdb, scop, mod)) return ajFalse; break; case ajEPdbioModeAtomIdxDomain: if (!errf || !pdb) ajFatal("Invalid args passed to ajPdbWriteDomainRecordRaw"); if (!pdbioWriteAtomDomainIdx(errf, outf, pdb, scop, mod)) return ajFalse; break; default: ajFatal("Invalid mode in ajPdbWriteDomainRecordRaw"); } return ajTrue; } ID ajPdbWriteRecordRaw TY public MO ajpdbio LB core XX DE Writes lines in pdb format to a PDB file. What is written depends upon DE the mode: DE ajEPdbioModeSeqResChain SEQRES records for a chain. DE ajEPdbioModeAtomPdbChain ATOM records for chain using original residue DE numbers. DE ajEPdbioModeAtomIdxChain ATOM records for domain using residues numbers that DE give correct index into SEQRES sequence. DE ajEPdbioModeHeterogen ATOM line for a heterogen (small ligand). DE ajEPdbioModeHeader Header line. DE ajEPdbioModeTitle Title line. DE ajEPdbioModeCompnd COMPND records (info. on compound) DE ajEPdbioModeSource SOURCE records (info. on protein source) DE ajEPdbioModeEmptyRemark An empty REMARK record. DE ajEPdbioModeResolution Record with resolution of the structure. XX PN [1] PA u mode AjEPdbioMode PD Mode that controls what is printed: one of PD ajEPdbioModeSeqResChain, ajEPdbioModeAtomPdbChain, PD ajEPdbioModeAtomIdxChain, ajEPdbioModeHeterogen, PD ajEPdbioModeHeader, ajEPdbioModeTitle, PD ajEPdbioModeCompnd, ajEPdbioModeSource, PD ajEPdbioModeEmptyRemark, ajEPdbioModeResolution. PX PN [2] PA r pdb const AjPPdb PD Pdb object PX PN [3] PA r mod ajint PD Model number. PX PN [4] PA r chn ajint PD Chain number. PX PN [5] PA w outf AjPFile PD Output file stream PX PN [6] PA w errf AjPFile PD Output file stream for error messages PX RT AjBool RD True on success RX // AjBool ajPdbWriteRecordRaw(AjEPdbioMode mode, const AjPPdb pdb, ajint mod, ajint chn, AjPFile outf, AjPFile errf) { /* Check args */ if (!outf || !pdb) ajFatal("Invalid args passed to ajPdbWriteRecordRaw"); switch (mode) { case ajEPdbioModeSeqResChain: if (!errf) ajFatal("Invalid args passed to ajPdbWriteRecordRaw"); if (!pdbioWriteSeqresChain(errf, outf, pdb, chn)) return ajFalse; break; case ajEPdbioModeAtomPdbChain: if (!pdbioWriteAtomChain(outf, pdb, mod, chn, ajEPdbModePdb)) return ajFalse; break; case ajEPdbioModeAtomIdxChain: if (!pdbioWriteAtomChain(outf, pdb, mod, chn, ajEPdbModeIdx)) return ajFalse; break; case ajEPdbioModeHeterogen: if (!pdbioWriteHeterogen(outf, pdb, mod)) return ajFalse; break; case ajEPdbioModeHeader: if (!pdbioWriteHeader(outf, pdb)) return ajFalse; break; case ajEPdbioModeTitle: if (!pdbioWriteTitle(outf, pdb)) return ajFalse; break; case ajEPdbioModeCompnd: if (!pdbioWriteCompnd(outf, pdb)) return ajFalse; break; case ajEPdbioModeSource: if (!pdbioWriteSource(outf, pdb)) return ajFalse; break; case ajEPdbioModeEmptyRemark: if (!pdbioWriteEmptyRemark(outf, pdb)) return ajFalse; break; case ajEPdbioModeResolution: if (!pdbioWriteResolution(outf, pdb)) return ajFalse; break; default: ajFatal("Invalid mode in ajPdbWriteRecordRaw"); } return ajTrue; } ID ajPdbWriteAllRaw TY public MO ajpdbio LB core XX DE Writes a pdb file for a protein. XX PN [1] PA u mode AjEPdbMode PD AJAX PDB Mode enumeration, either ajEPdbModePdb PD or ajEPdbModeIdx if the original or PD corrected residue number is to be used. PX PN [2] PA r pdb const AjPPdb PD Pdb object PX PN [3] PA w outf AjPFile PD Output file stream PX PN [4] PA w errf AjPFile PD Output file stream for error messages PX RT AjBool RD True on success RX // AjBool ajPdbWriteAllRaw(AjEPdbMode mode, const AjPPdb pdb, AjPFile outf, AjPFile errf) { ajuint i = 0U; ajuint j = 0U; /* Write bibliographic info. */ ajPdbWriteRecordRaw(ajEPdbioModeHeader, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeTitle, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeCompnd, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeSource, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeEmptyRemark, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeResolution, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeEmptyRemark, pdb, 0, 0, outf, NULL); /* Write SEQRES records */ for (i = 0U; i < pdb->Nchn; i++) if (!ajPdbWriteRecordRaw( ajEPdbioModeSeqResChain, pdb, 0, i + 1, outf, errf)) { ajWarn("Error writing file in ajPdbWriteAllRaw"); return ajFalse; } /* Loop for each model */ for (j = 0U; j < pdb->Nmod; j++) { /* Write the MODEL record */ if (pdb->Method == ajEPdbMethodNmr) ajFmtPrintF(outf, "MODEL%9d%66s\n", j + 1, " "); /* Write ATOM/HETATM records */ for (i = 0U; i < pdb->Nchn; i++) { switch (mode) { case ajEPdbModePdb: if (!ajPdbWriteRecordRaw( ajEPdbioModeAtomPdbChain, pdb, j + 1, i + 1, outf, NULL)) { ajWarn("Error writing file in ajPdbWriteAllRaw"); return ajFalse; } break; case ajEPdbModeIdx: if (!ajPdbWriteRecordRaw( ajEPdbioModeAtomIdxChain, pdb, j + 1, i + 1, outf, NULL)) { ajWarn("Error writing file in ajPdbWriteAllRaw"); return ajFalse; } break; default: ajFatal("Invalid mode in ajPdbWriteAllRaw"); } if (!ajPdbWriteRecordRaw( ajEPdbioModeHeterogen, pdb, j + 1, 0, outf, NULL)) { ajWarn("Error writing file in ajPdbWriteAllRaw"); return ajFalse; } /* Write ENDMDL record */ if (pdb->Method == ajEPdbMethodNmr) ajFmtPrintF(outf, "%-80s\n", "ENDMDL"); } } /* Write END record */ ajFmtPrintF(outf, "%-80s\n", "END"); return ajTrue; } ID ajPdbWriteDomainRaw TY public MO ajpdbio LB core XX DE Writes a pdb file for a SCOP domain. Where coordinates for multiple DE models (e.g. NMR structures) are given, data for model 1 are written. DE Coordinates are taken from a Pdb structure, domain definition is taken DE from a Scop structure. DE In the pdb file, the coordinates are presented as belonging to a single DE chain regardless of how many chains the domain comprised. DE Coordinates for heterogens are NOT written to file. XX PN [1] PA u mode AjEPdbMode PD AJAX PDB Mode enumeration, either ajEPdbModePdb PD or ajEPdbModeIdx if the original or PD corrected residue number is to be used. PX PN [2] PA r pdb const AjPPdb PD Pdb object PX PN [3] PA r scop const AjPScop PD Scop object PX PN [4] PA w outf AjPFile PD Output file stream PX PN [5] PA w errf AjPFile PD Output file stream for error messages PX RT AjBool RD True on success RX // AjBool ajPdbWriteDomainRaw(AjEPdbMode mode, const AjPPdb pdb, const AjPScop scop, AjPFile outf, AjPFile errf) { ajuint i = 0U; /* A counter */ ajuint chn = 0U; /* No. of the chain in the pdb structure */ if (!pdb || !scop || !outf || !errf) ajFatal("Bad args passed to ajPdbWriteDomainRaw"); /* Check for errors in chain identifier and length */ for (i = 0U; i < scop->Number; i++) if (!ajPdbChnidToNum(scop->Chain[i], pdb, &chn)) { ajWarn("Chain incompatibility error in " "ajPdbWriteDomainRaw"); ajFmtPrintF(errf, "//\n%S\nERROR Chain incompatibility error " "in ajPdbWriteDomainRaw\n", scop->Entry); return ajFalse; } else if (pdb->Chains[chn - 1]->Nres == 0) { ajWarn("Chain length zero"); ajFmtPrintF(errf, "//\n%S\nERROR Chain length zero\n", scop->Entry); return ajFalse; } /* Write bibliographic info. */ ajPdbWriteDomainRecordRaw( ajEPdbioModeHeaderDomain, NULL, 0, scop, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeTitle, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeCompnd, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeSource, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeEmptyRemark, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeResolution, pdb, 0, 0, outf, NULL); ajPdbWriteRecordRaw(ajEPdbioModeEmptyRemark, pdb, 0, 0, outf, NULL); /* Write SEQRES records */ if (!ajPdbWriteDomainRecordRaw(ajEPdbioModeSeqresDomain, pdb, 0, scop, outf, errf)) { ajWarn("Error writing file in ajPdbWriteDomainRaw"); return ajFalse; } /* Write MODEL record, if appropriate */ if (pdb->Method == ajEPdbMethodNmr) ajFmtPrintF(outf, "MODEL%9d%66s\n", 1, " "); /* Write ATOM/HETATM records */ if (!pdbioWriteAtomDomain(errf, outf, pdb, scop, 1, mode)) { ajWarn("Error writing file in ajPdbWriteDomainRaw"); return ajFalse; } /* Write END/ENDMDL records */ if (pdb->Method == ajEPdbMethodNmr) ajFmtPrintF(outf, "%-80s\n", "ENDMDL"); ajFmtPrintF(outf, "%-80s\n", "END"); return ajTrue; } ID ajPhyloDistNew TY public MO ajphylo LB core XX DE Constructor for AjPPhyloDist XX RT AjPPhyloDist RD AjPPhyloDist object RX // AjPPhyloDist ajPhyloDistNew(void) { AjPPhyloDist ret = NULL; AJNEW0(ret); return ret; } ID ajPhyloFreqNew TY public MO ajphylo LB core XX DE Constructor for AjPPhyloFreq XX RT AjPPhyloFreq RD AjPPhyloFreq object RX // AjPPhyloFreq ajPhyloFreqNew(void) { AjPPhyloFreq ret = NULL; AJNEW0(ret); return ret; } ID ajPhyloPropNew TY public MO ajphylo LB core XX DE Constructor for AjPPhyloProp XX RT AjPPhyloProp RD AjPPhyloProp object RX // AjPPhyloProp ajPhyloPropNew(void) { AjPPhyloProp ret = NULL; AJNEW0(ret); return ret; } ID ajPhyloStateNew TY public MO ajphylo LB core XX DE Constructor for AjPPhyloState XX RT AjPPhyloState RD AjPPhyloState object RX // AjPPhyloState ajPhyloStateNew(void) { AjPPhyloState ret = NULL; AJNEW0(ret); return ret; } ID ajPhyloTreeNew TY public MO ajphylo LB core XX DE Constructor for AjPPhyloTree XX RT AjPPhyloTree RD AjPPhyloTree object RX // AjPPhyloTree ajPhyloTreeNew(void) { AjPPhyloTree ret = NULL; AJNEW0(ret); return ret; } ID ajPhyloDistDel TY public MO ajphylo LB core XX DE Destructor for AjPPhyloDist XX PN [1] PA d pthis AjPPhyloDist* PD Distance object PX RT void RD RX // void ajPhyloDistDel(AjPPhyloDist* pthis) { AjPPhyloDist thys; ajint i; if(!pthis) return; thys = *pthis; if(!thys) return; if(thys->Names) { for(i=0; i < thys->Size; i++) ajStrDel(&thys->Names[i]); AJFREE(thys->Names); } AJFREE(thys->Data); AJFREE(thys->Replicates); AJFREE(*pthis); return; } ID ajPhyloFreqDel TY public MO ajphylo LB core XX DE Destructor for AjPPhyloFreq XX PN [1] PA d pthis AjPPhyloFreq* PD frequency object PX RT void RD RX // void ajPhyloFreqDel(AjPPhyloFreq* pthis) { AjPPhyloFreq thys; ajint i; if(!pthis) return; thys = *pthis; if(!thys) return; if(thys->Names) { for(i=0; i < thys->Size; i++) ajStrDel(&thys->Names[i]); AJFREE(thys->Names); } AJFREE(thys->Locus); AJFREE(thys->Allele); AJFREE(thys->Species); AJFREE(thys->Individuals); AJFREE(thys->Data); AJFREE(*pthis); return; } ID ajPhyloPropDel TY public MO ajphylo LB core XX DE Destructor for AjPPhyloProp XX PN [1] PA d pthis AjPPhyloProp* PD Properties object PX RT void RD RX // void ajPhyloPropDel(AjPPhyloProp* pthis) { AjPPhyloProp thys; ajint i; if(!pthis) return; thys = *pthis; if(!thys) return; if(thys->Str) for(i=0; i < thys->Size; i++) ajStrDel(&thys->Str[i]); AJFREE(*pthis); return; } ID ajPhyloStateDel TY public MO ajphylo LB core XX DE Destructor for AjPPhyloState XX PN [1] PA d pthis AjPPhyloState* PD Discrete states object PX RT void RD RX // void ajPhyloStateDel(AjPPhyloState* pthis) { AjPPhyloState thys; ajint i; if(!pthis) return; thys = *pthis; if(!thys) return; if(thys->Names) { for(i=0; i < thys->Size; i++) ajStrDel(&thys->Names[i]); AJFREE(thys->Names); } if(thys->Str) { for(i=0; i < thys->Size; i++) ajStrDel(&thys->Str[i]); AJFREE(thys->Str); } ajStrDel(&thys->Characters); AJFREE(*pthis); return; } ID ajPhyloStateDelarray TY public MO ajphylo LB core XX DE Destructor for AjPPhyloState array XX PN [1] PA d pthis AjPPhyloState** PD States object array PX RT void RD RX // void ajPhyloStateDelarray(AjPPhyloState** pthis) { ajuint i = 0; if(!pthis) return; if(!*pthis) return; while((*pthis)[i]) { ajPhyloStateDel(&(*pthis)[i]); i++; } AJFREE(*pthis); return; } ID ajPhyloTreeDel TY public MO ajphylo LB core XX DE Destructor for AjPPhyloTree XX PN [1] PA d pthis AjPPhyloTree* PD Trees object PX RT void RD RX // void ajPhyloTreeDel(AjPPhyloTree* pthis) { AjPPhyloTree thys; if(!pthis) return; thys = *pthis; if(!thys) return; ajStrDel(&thys->Tree); AJFREE(*pthis); return; } ID ajPhyloTreeDelarray TY public MO ajphylo LB core XX DE Destructor for AjPPhyloTree array XX PN [1] PA d pthis AjPPhyloTree** PD Trees object array PX RT void RD RX // void ajPhyloTreeDelarray(AjPPhyloTree** pthis) { ajuint i = 0; if(!pthis) return; if(!*pthis) return; while((*pthis)[i]) { ajPhyloTreeDel(&(*pthis)[i]); i++; } AJFREE(*pthis); return; } ID ajPhyloDistRead TY public MO ajphylo LB core XX DE Reads phylogenetic distance matrix from a file XX PN [1] PA r filename const AjPStr PD input filename PX PN [2] PA r size ajint PD Number of rows in distance matrix PX PN [3] PA r missing AjBool PD Missing values (replicates zero) allowed PX RT AjPPhyloDist* RD Phylogenetic distances object on success RD NULL on failure RX // AjPPhyloDist* ajPhyloDistRead(const AjPStr filename, ajint size, AjBool missing) { AjPPhyloDist* ret = NULL; AjPPhyloDist dist = NULL; AjPFile distfile = NULL; AjPList distlist = NULL; AjPStr rdline = NULL; ajint count; ajint irow = 0 ; ajint icol = 0; ajint i = 0; ajint j = 0; ajint k = 0; ajint ncells; float dval; ajint ival; ajint ipos = 0; ajint iend; ajint idiag; ajint jpos; AjBool done = ajFalse; AjPStr tmpstr = NULL; AjPStr tmpval = NULL; AjBool lowertri = ajFalse; AjBool uppertri = ajFalse; AjPStr name = NULL; AjPStr rest = NULL; ajuint imaxlen; ajuint linenum = 0; if(!phyloRegDistFloat) phyloRegDistFloat = ajRegCompC("^\\s*(-?[0-9]+[.]?[0-9]*)\\s+(([0-9]+)[^0-9.])?"); distlist = ajListNew(); distfile = ajFileNewInNameS(filename); if(!distfile) return NULL; done = ajTrue; while(ajReadline(distfile, &rdline)) { linenum++; if(ajStrIsWhite(rdline)) continue; if(done) { ajStrTrimWhite(&rdline); if(!ajStrToInt(rdline, &count)) { ajErr("Distance file '%S' line %u bad header record '%S'", filename, linenum, rdline); return NULL; } ajDebug("DistRead: row count: %d\n", count); if(size && (count != size)) { ajErr("Distance file '%S' line %u " "found %d expected %d rows '%S'", filename, linenum, count, size); return NULL; } irow = -1; icol = 0; i = 0; done = ajFalse; dist = ajPhyloDistNew(); dist->Size = count; AJCNEW0(dist->Names, count); ncells = count*count; AJCNEW0(dist->Data,ncells); AJCNEW0(dist->Replicates,ncells); continue; } /*ajDebug("DistRead line: %S", rdline); */ if(!i || !ajRegExec(phyloRegDistFloat, rdline)) { if(!irow) /* how long was that first row */ { if(!icol) /* empty - lower triangular */ { lowertri = ajTrue; ajDebug("DistRead: ++Lower-Triangular form++\n"); } else if(icol == (count - 1)) /* one short - upper triangular */ { uppertri = ajTrue; ajDebug("DistRead: ++Upper-Triangular form++\n"); /* now we need to move along one */ for(j=count-1; j; j--) { dist->Data[j] = dist->Data[j-1]; dist->Replicates[j] = dist->Replicates[j-1]; } } else if(icol != count) /* something wrong */ { ajErr("Distance file %S: line %u Row '%S' has %d values", filename, linenum, dist->Names[irow], icol); } } else if(irow > 0) /* later rows - we know what to expect */ { if(lowertri) { /*ajDebug("DistRead Lower irow: %d i: %d\n", irow, i);*/ if(icol != irow) ajErr("Distance file %S: line %u Row '%S' has %d values" " in lower-triangular format", filename, linenum, dist->Names[irow], icol); } else if(uppertri) { /*ajDebug("DistRead Upper irow: %d i: %d\n", irow, i);*/ if(i) ajErr("Distance file %S: line %u Row '%S' has %d values" " in upper-triangular format", filename, linenum, dist->Names[irow], icol - irow - 1); } else { /*ajDebug("DistRead Square irow: %d i: %d\n", irow, i);*/ if(icol != count) ajErr("Distance file %S: line %u Row '%S' has %d values" " in square format", filename, linenum, dist->Names[irow], icol); } } irow++; if(irow > count) { ajErr("Distances file line %u read beyond %d rows", linenum, count); return NULL; } ajStrExtractWord(rdline, &rest, &name); if(ajStrGetLen(name) >= 10) imaxlen = 10; else imaxlen = ajStrGetLen(name); ajStrAssignSubS(&dist->Names[irow], rdline, 0, imaxlen-1); ajStrCutStart(&rdline, imaxlen); ajStrTrimWhiteEnd(&dist->Names[irow]); ajDebug("DistRead name [%d] '%S' i: %d\n", irow, dist->Names[irow], i); if(uppertri) { icol = irow + 1; i = irow + 1; } else { i = 0; icol = 0; } } while(ajRegExec(phyloRegDistFloat, rdline)) { ajRegSubI(phyloRegDistFloat, 1, &tmpval); ajStrToFloat(tmpval, &dval); ajRegSubI(phyloRegDistFloat, 3, &tmpval); if(ajStrGetLen(tmpval)) { ajStrToInt(tmpval, &ival); if(ival) dist->HasReplicates = ajTrue; else { if(!missing) { ajErr("Distances file %S: line %u " "Zero (%S) replicates for %S", filename, linenum, tmpval, dist->Names[irow]); return NULL; } dist->HasMissing = ajTrue; } /*ajDebug("DistRead row %2d [%d] %.3f %d\n", irow, i, dval, ival);*/ } else { ival = 1; /*ajDebug("DistRead row %2d [%d] %.3f .\n", irow, i, dval);*/ } if(dval < 0.0) { if(!missing) { ajErr("Distances file %S: line %u " "negative (%f) value for %S", filename, linenum, dval, dist->Names[irow]); return NULL; } dist->HasMissing = ajTrue; ival = 0; dval = 0.0; } ipos = irow*count + i; dist->Data[ipos] = dval; dist->Replicates[ipos] = ival; i++; icol++; if(i == count) i = 0; ajRegPost(phyloRegDistFloat, &tmpstr); ajStrAssignS(&rdline, tmpstr); /*ajDebug("irow: %d icol: %d\n", irow, icol);*/ } if(irow == (count-1)) { if(lowertri) /* fill in the rest */ { for(k=0; k < count; k++) { idiag = k*count + k; /* diagonal */ dist->Data[idiag] = 0.0; dist->Replicates[idiag] = 1; iend = k*count+count; /* end of row */ ipos = idiag + 1; jpos = ipos + count - 1; while(ipos < iend) { dist->Data[ipos] = dist->Data[jpos]; dist->Replicates[ipos] = dist->Replicates[jpos]; ipos++; jpos += count; } } done = ajTrue; } else if(uppertri) /* fill in the rest */ { for(k=0; k < count; k++) { idiag = k*count + k; /* diagonal */ dist->Data[idiag] = 0.0; dist->Replicates[idiag] = 1; ipos = k*count; /* start of row*/ jpos = k; while(ipos < idiag) { dist->Data[ipos] = dist->Data[jpos]; dist->Replicates[ipos] = dist->Replicates[jpos]; ipos++; jpos += count; } } done = ajTrue; } else if(icol == count) { done = ajTrue; } if(done) { ajListPushAppend(distlist, dist); dist = NULL; } } } if(irow != (count-1)) { ajErr("Distances file found %d rows, expected %d", irow, count); return NULL; } ajDebug("Distances file '%S' has %Lu distance matrices\n", filename, ajListGetLength(distlist)); ajFileClose(&distfile); ajListToarray(distlist, (void***) &ret); /*ret = (AjPPhyloTree*) trees;*/ ajListFree(&distlist); for(i=0; ret[i]; i++) ajPhyloDistTrace(ret[i]); ajStrDel(&name); ajStrDel(&rest); return ret; } ID ajPhyloDistTrace TY public MO ajphylo LB core XX DE Reports phylogenetic distance data to the debug file XX PN [1] PA r thys const AjPPhyloDist PD Phylogenetic frequencies object PX RT void RD RX // void ajPhyloDistTrace(const AjPPhyloDist thys) { ajint i; ajint j; ajint jend; ajDebug("ajPhyloDistTrace\n"); ajDebug("================\n"); ajDebug(" Count: %d HasReplicates: %B HasMissing: %B\n", thys->Size, thys->HasReplicates, thys->HasMissing); jend = 0; j = 0; ajDebug("%-10.10s", "Name"); for(i=0; i < thys->Size; i++) ajDebug(" %6d ++", (i+1)); ajDebug("\n"); ajDebug("=========="); for(i=0; i < thys->Size; i++) ajDebug(" ====== =="); ajDebug("\n"); for(i=0; i < thys->Size; i++) { jend += thys->Size; ajDebug("%-10.10S", thys->Names[i]); while(j < jend) { ajDebug(" %6.3f %2d", thys->Data[j], thys->Replicates[j]); j++; } ajDebug("\n"); } return; } ID ajPhyloFreqRead TY public MO ajphylo LB core XX DE Reads phylogenetic frequencies from a file XX PN [1] PA r filename const AjPStr PD input filename PX PN [2] PA r contchar AjBool PD Continuous character data expected PX PN [3] PA r genedata AjBool PD Gene frequency data expected PX PN [4] PA r indiv AjBool PD Multiple individuals for one name PD only for continuous character data. PX RT AjPPhyloFreq RD Phylogenetic frequencies object on success RD NULL on failure RX // AjPPhyloFreq ajPhyloFreqRead(const AjPStr filename, AjBool contchar, AjBool genedata, AjBool indiv) { AjPPhyloFreq ret = NULL; AjPFile freqfile = NULL; AjPStr rdline = NULL; AjPStr tmpline = NULL; AjPStr tmpstr = NULL; AjPStr tmpval = NULL; ajint count; ajint len; ajint ncells; ajint nfreq = 0; ajint i; ajint j; ajint k; ajint jnew; ajint jold; ajint misspos; ajint irow; ajint icol; ajint ipos = 0; ajint jpos = 0; float jtot; float jrest; AjBool done = ajFalse; AjBool alldata = ajFalse; ajint totfreq; ajint totsp; ajint isp; float dval; float** pvals = NULL; (void) indiv; /* make it used */ if(!phyloRegFreqFloat) phyloRegFreqFloat = ajRegCompC("^\\s*([-]?[0-9]+[.]?[0-9]*)\\s+"); if(!phyloRegFreqInt) phyloRegFreqInt = ajRegCompC("^\\s*([1-9][0-9]*)(\\s+[0-9\\s]*)$"); if(!phyloRegFreqIndiv) phyloRegFreqIndiv = ajRegCompC("^(\\S.........)\\s+([1-9][0-9]*)\\s*$"); freqfile = ajFileNewInNameS(filename); if(!freqfile) return NULL; ajReadline(freqfile, &rdline); /* process header */ if(!ajRegExec(phyloRegFreqInt, rdline)) { ajErr("Frequencies file %S: Bad header line '%S'", filename, rdline); return NULL; } ajRegSubI(phyloRegFreqInt, 1, &tmpval); ajStrToInt(tmpval, &count); ajRegSubI(phyloRegFreqInt, 2, &tmpstr); if(!ajRegExec(phyloRegFreqInt, tmpstr)) { ajErr("Frequencies file %S: Bad header line '%S'", filename, rdline); return NULL; } ajRegSubI(phyloRegFreqInt, 1, &tmpval); ajStrToInt(tmpval, &len); ret = ajPhyloFreqNew(); ret->Size = count; ret->Loci = len; AJCNEW0(ret->Names,count); AJCNEW0(ret->Allele,len); /* process alleles counts, if any */ ajReadline(freqfile, &rdline); if(ajRegExec(phyloRegFreqInt, rdline)) /* allele counts */ { if(contchar) { ajErr("Frequencies file %S: Has gene frequency data", filename); return NULL; } ajStrAssignS(&tmpline, rdline); for(i=0; i < len; i++) { if(!ajRegExec(phyloRegFreqInt, rdline)) { ajErr("Frequencies file %S: Bad allele header," " expected %d allele counts, found %d", filename, len, i); return NULL; } ajRegSubI(phyloRegFreqInt, 1, &tmpval); ajStrToInt(tmpval, &ret->Allele[i]); ajRegSubI(phyloRegFreqInt, 2, &tmpstr); ajStrAssignS(&rdline, tmpstr); nfreq += ret->Allele[i]; /* assume all values */ } if(ajRegExec(phyloRegFreqInt, rdline)) { ajErr("Frequencies file %S: Bad allele header," " more than expected %d allele counts", filename, len); return NULL; } ret->Len = nfreq; AJCNEW0(ret->Locus,nfreq); jpos = 0; for(i=0; i < len; i++) for(j=0; j < ret->Allele[i]; j++) ret->Locus[jpos++] = i; ncells = count*nfreq; AJCNEW0(ret->Data,ncells); ajReadline(freqfile, &rdline); } else { if(genedata) { ajErr("Frequencies file %S: Has continuous character data", filename); return NULL; } ret->ContChar = ajTrue; alldata = ajTrue; nfreq = len; ncells = count*nfreq; ret->Len = nfreq; AJCNEW0(ret->Data,ncells); AJCNEW0(ret->Locus,nfreq); for(i=0; i < len; i++) { ret->Locus[i] = i; ret->Allele[i] = 2; } if(ajRegExec(phyloRegFreqIndiv, rdline)) { ret->Within = ajTrue; AJCNEW0(ret->Individuals, count); AJCNEW0(pvals, count); } } irow = -1; icol = 0; i = 0; done = ajFalse; if(ret->Within) { totfreq = 0; totsp = 0; while(!done) { if(ajRegExec(phyloRegFreqIndiv, rdline)) { irow++; if(irow && icol != nfreq) ajErr("Frequencies file %S: Read %d values" " for '%S', expected %d", filename, icol, ret->Names[irow], nfreq); ajRegSubI(phyloRegFreqIndiv, 1, &ret->Names[irow]); ajStrTrimWhiteEnd(&ret->Names[irow]); ajRegSubI(phyloRegFreqIndiv, 2, &tmpval); ajStrToInt(tmpval, &ret->Individuals[irow]); nfreq = ret->Individuals[irow]*len; totfreq += nfreq; totsp += ret->Individuals[irow]; AJCNEW0(pvals[irow],ret->Individuals[irow]); icol = 0; } while(ajRegExec(phyloRegFreqFloat, rdline)) { ajRegSubI(phyloRegFreqFloat, 1, &tmpval); ajStrToFloat(tmpval, &dval); pvals[irow][icol] = dval; ajRegPost(phyloRegFreqFloat, &tmpstr); ajStrAssignS(&rdline, tmpstr); icol++; ipos++; } if(!ajReadline(freqfile, &rdline)) break; } if(irow != (count-1)) { ajErr("Frequencies file %S: found %d rows, expected %d", filename, irow, count); return NULL; } AJCNEW0(ret->Data, totfreq); AJCNEW0(ret->Species, totsp); isp = 0; ipos = 0; for(i=0; i < count; i++) { for(j=0; j < ret->Individuals[i]; j++) ret->Species[isp++] = i; nfreq = ret->Individuals[i]*len; for(j=0; j < nfreq; j++) ret->Data[ipos++] = pvals[i][j]; AJFREE(pvals[i]); } ajDebug("FreqRead: wrote %d values\n", ipos); AJFREE(pvals); ajPhyloFreqTrace(ret); return ret; } /* We read, for each 'species', a 10-character name and 'nfreq' values */ while(!done) { /*ajDebug("FreqRead line: %S", rdline);*/ if(!ajRegExec(phyloRegFreqFloat, rdline)) { irow++; if(irow == 1) { if(icol == nfreq) alldata = ajTrue; else if(!ret->ContChar && icol == (nfreq - len)) alldata = ajFalse; else { if(ret->ContChar) ajErr("Frequencies file %S: Read %d values," " expected %d", filename, icol, nfreq); else ajErr("Frequencies file %S: Read %d values," " expected %d or %d", filename, icol, nfreq, (nfreq - len)); return NULL; } } if(irow > count) { ajErr("Frequencies file read beyond %d rows", count); return NULL; } if(irow) { if(alldata) { if(icol != nfreq) { ajErr("Frequencies file %S:" " read only %d rows for %S", filename, icol, ret->Names[irow-1]); return NULL; } } else { if(icol != (nfreq - len)) { ajErr("Frequencies file %S:" " read only %d rows for %S", filename, icol, ret->Names[irow-1]); return NULL; } } } ajStrAssignSubS(&ret->Names[irow], rdline, 0, 9); ajStrCutStart(&rdline, 10); ajStrTrimWhiteEnd(&ret->Names[irow]); ajDebug("FreqRead name [%d] '%S' i: %d\n", irow, ret->Names[irow], i); icol = 0; ipos = irow * nfreq; } while(ajRegExec(phyloRegFreqFloat, rdline)) { ajRegSubI(phyloRegFreqFloat, 1, &tmpval); ajStrToFloat(tmpval, &dval); ret->Data[ipos] = dval; ajRegPost(phyloRegFreqFloat, &tmpstr); ajStrAssignS(&rdline, tmpstr); icol++; ipos++; } if(!ajReadline(freqfile, &rdline)) break; } if(irow != (count-1)) { ajErr("Frequencies file %S: found %d rows, expected %d", filename, irow, count); return NULL; } if(!alldata) /* fill in the missing frequencies */ { for(i=0; i < count; i++) /* for each row (Name) */ { misspos = i*nfreq + nfreq - 1; jnew = misspos - 1; jold = misspos - ret->Loci; for(j=ret->Loci; j; j--) { jtot = 0.0; for(k=1; k < ret->Allele[j-1]; k++) { jtot += ret->Data[jold]; ret->Data[jnew--] = ret->Data[jold--]; } jrest = (float) 1.0 - jtot; ret->Data[misspos] = jrest; misspos -= ret->Allele[j-1]; jnew--; } } } ajPhyloFreqTrace(ret); ajStrDel(&tmpline); ajStrDel(&tmpval); ajStrDel(&tmpstr); ajStrDel(&rdline); ajFileClose(&freqfile); return ret; } ID ajPhyloFreqTrace TY public MO ajphylo LB core XX DE Reports phylogenetic frequencies data to the debug file XX PN [1] PA r thys const AjPPhyloFreq PD Phylogenetic frequencies object PX RT void RD RX // void ajPhyloFreqTrace(const AjPPhyloFreq thys) { ajint i; ajint j; ajint k; ajint ipos; ajint isp; ajDebug("ajPhyloFreqTrace\n"); ajDebug("================\n"); ajDebug(" Count: %d Len: %d Loci: %d ContChar: %B Within: %B\n", thys->Size, thys->Len, thys->Loci, thys->ContChar, thys->Within); if(thys->ContChar) /* no alleles */ { ajDebug(" Continuous frequency data\n"); ajDebug(" -------------------------\n"); ajDebug("%-10.10s", "Name"); for(j=0; j < thys->Len; j++) ajDebug(" %7d", 1+thys->Locus[j]); ajDebug("\n"); ajDebug("=========="); for(j=0; j < thys->Len; j++) ajDebug(" ======="); ajDebug("\n"); if(thys->Species) { isp = 0; ipos = 0; for(i=0; i < thys->Size; i++) { for(k=0; k < thys->Individuals[i]; k++) { if(k) ajDebug("sp.%3d %3d", thys->Species[isp]+1, k+1); else ajDebug("%-10.10S", thys->Names[i]); for(j=0; j < thys->Len; j++) ajDebug(" %7.3f", thys->Data[ipos++]); ajDebug("\n"); isp++; } } } else { for(i=0; i < thys->Size; i++) { ajDebug("%-10.10S", thys->Names[i]); ipos = i*thys->Len; for(j=0; j < thys->Len; j++) ajDebug(" %7.3f", thys->Data[ipos++]); ajDebug("\n"); } } } else { ajDebug(" Allele frequency data\n"); ajDebug(" ---------------------\n"); for(j=0; j < thys->Loci; j++) ajDebug("Locus %d Alleles %d\n", j+1, thys->Allele[j]); ajDebug("\n"); ajDebug("%-10.10s", "Name"); for(j=0; j < thys->Len; j++) ajDebug(" Loc. %2d", 1+thys->Locus[j]); ajDebug("\n"); ajDebug("=========="); for(j=0; j < thys->Len; j++) ajDebug(" ======="); ajDebug("\n"); for(i=0; i < thys->Size; i++) { ajDebug("%-10.10S", thys->Names[i]); ipos = i*thys->Len; for(j=0; j < thys->Len; j++) ajDebug(" %7.3f", thys->Data[ipos++]); ajDebug("\n"); } } return; } ID ajPhyloPropRead TY public MO ajphylo LB core XX DE Reads phylogenetic properties (weights, factors, ancestral states) DE from a file XX PN [1] PA r filename const AjPStr PD input filename PX PN [2] PA r propchars const AjPStr PD Valid property characters PX PN [3] PA r len ajint PD Length of properties string PX PN [4] PA r size ajint PD Number of property sets expected PD If zero, read first only or whatever the file says PX RT AjPPhyloProp RD Phylogenetic properties object on success RD NULL on failure RX // AjPPhyloProp ajPhyloPropRead(const AjPStr filename, const AjPStr propchars, ajint len, ajint size) { AjPPhyloProp ret = NULL; AjPFile propfile = NULL; AjPList proplist = NULL; AjBool propok = ajFalse; AjPStr token = NULL; void ** props = NULL; ajint i; ajint ilen; const char* cp; AjPStr rdline = NULL; AjPStr proppat = NULL; AjPStr propstr = NULL; ajint count; ajint dosize; ret = ajPhyloPropNew(); if(ajStrMatchC(propchars, "")) ajFmtPrintS(&proppat, "\\S+"); else ajFmtPrintS(&proppat, "[%S]+", propchars); ajRegFree(&phyloRegPropProp); phyloRegPropProp = ajRegComp(proppat); ajDebug("ajPhyloPropRead '%S' propchars: '%S' len: %d size: %d\n", filename, propchars, len, size); proplist = ajListstrNew(); dosize = size; if (size) count = size; else count = 1; propfile = ajFileNewInNameS(filename); if(!propfile) /* read the filename string as data */ { if (size > 1) { ajErr("Bad properties string '%S':" " valid filename required to read %d sets", filename, size); return NULL; } ajStrAssignS(&rdline, filename); /*ajStrFmtUpper(&rdline);*/ /* keep to catch names without '.' */ if(!ajRegExec(phyloRegPropProp, rdline)) return NULL; ajRegSubI(phyloRegPropProp, 0, &token); if(!ajStrMatchS(rdline, token)) return NULL; dosize = 1; ilen = ajStrGetLen(token); if(ilen != len) { ajErr("Bad properties string (not valid filename) '%S':" " read %d properties, expected %d", filename, ilen, len); return NULL; } AJCNEW0(ret->Str, 2); ajStrAssignS(&ret->Str[0], token); ret->Size = 1; ret->Len = ilen; } else /* read data from the file */ { i = 0; while (!dosize || i < count) { propstr = ajStrNewRes(len+1); propok = ajFalse; ilen = 0; while(!propok && ajReadlineTrim(propfile, &rdline)) { ajStrFmtUpper(&rdline); cp = ajStrGetPtr(rdline); while(cp && ajRegExecC(phyloRegPropProp, cp)) { ajRegSubI(phyloRegPropProp, 0, &token); ajStrAppendS(&propstr, token); ilen += ajStrGetLen(token); ajRegPostC(phyloRegPropProp, &cp); } if(ilen == len) propok = ajTrue; else if(ilen > len) ajErr("Bad properties file '%S':" " read %d properties, expected %d", filename, ilen, len); } if(!propok) { if (ilen) ajErr("End of properties file '%S':" " after %d properties, expected %d", filename, ilen, len); else if (size) ajErr("End of properties file '%S':" " after %d sets, expected %d", filename, i, size); else if (ajFileIsEof(propfile)) break; } ajListstrPushAppend(proplist, propstr); i++; } ajFileClose(&propfile); ajListToarray(proplist, (void***) &props); ret->Str = (AjPStr*) props; ret->Size = (ajuint) ajListGetLength(proplist);; ret->Len = len; } ajListFree(&proplist); ajStrDel(&proppat); ajStrDel(&token); ajStrDel(&rdline); ajPhyloPropTrace(ret); return ret; } ID ajPhyloPropGetSize TY public MO ajphylo LB core XX DE Returns size (number of property strings) XX PN [1] PA r thys const AjPPhyloProp PD Properties object PX RT ajint RD Number of property strings RX // ajint ajPhyloPropGetSize(const AjPPhyloProp thys) { if(!thys) return 0; return thys->Size; } ID ajPhyloPropTrace TY public MO ajphylo LB core XX DE Reports phylogenetic property data to the debug file XX PN [1] PA r thys const AjPPhyloProp PD Phylogenetic frequencies object PX RT void RD RX // void ajPhyloPropTrace(const AjPPhyloProp thys) { ajint i; ajDebug("ajPhyloPropTrace\n"); ajDebug("================\n"); ajDebug(" Len: %d Size: %d IsWeight: %B IsFactor: %B\n", thys->Len, thys->Size, thys->IsWeight, thys->IsFactor); for(i=0; i < thys->Size; i++) { ajDebug("%3d: '%S'",i, thys->Str[i]); ajDebug("\n"); } return; } ID ajPhyloStateRead TY public MO ajphylo LB core XX DE Reads phylogenetic discrete states from a file XX PN [1] PA r filename const AjPStr PD input filename PX PN [2] PA r statechars const AjPStr PD Valid state characters PX RT AjPPhyloState* RD Phylogenetic discrete states object on success RD NULL on failure RX // AjPPhyloState* ajPhyloStateRead(const AjPStr filename, const AjPStr statechars) { AjPPhyloState* ret = NULL; AjPPhyloState state = NULL; AjPFile statefile = NULL; AjPList statelist = NULL; AjPStr rdline = NULL; AjPStr tmpstr = NULL; AjPStr tmpval = NULL; AjPStr charpat = NULL; AjPStr token = NULL; ajint size; ajint len; ajint count; ajint i; ajint j; ajint ilen; const char* cp; void **states = NULL; if(!phyloRegStateInt) phyloRegStateInt = ajRegCompC("^\\s+([1-9][0-9]*)\\s+([1-9][0-9]*)" "\\s+([1-9][0-9]*)?\\s*$"); if(!phyloRegStateState) phyloRegStateState = ajRegCompC("^(\\S.........)\\s*(\\S.*)$"); if(ajStrMatchC(statechars, "")) ajFmtPrintS(&charpat, "\\S+"); else ajFmtPrintS(&charpat, "[%S]+", statechars); ajRegFree(&phyloRegStateChar); phyloRegStateChar = ajRegComp(charpat); statelist = ajListNew(); statefile = ajFileNewInNameS(filename); if(!statefile) return NULL; while(ajReadline(statefile, &rdline)) { if(!ajRegExec(phyloRegStateInt, rdline)) { ajErr("Discrete states file %S: Bad header line '%S'", filename, rdline); return NULL; } ajRegSubI(phyloRegStateInt, 1, &tmpval); ajStrToInt(tmpval, &size); ajRegSubI(phyloRegStateInt, 2, &tmpval); ajStrToInt(tmpval, &len); ajRegSubI(phyloRegStateInt, 3, &tmpval); if(ajStrGetLen(tmpval)) ajStrToInt(tmpval, &count); else count = 1; state = ajPhyloStateNew(); state->Size = size; state->Len = len; state->Count = count; AJCNEW0(state->Names,size); AJCNEW0(state->Str,size); /* process alleles counts, if any */ ilen = 0; i = 0; while(ajReadline(statefile, &rdline)) { if(ilen == 0 && ajRegExec(phyloRegStateState, rdline)) { ajRegSubI(phyloRegStateState, 1, &state->Names[i]); ajStrTrimWhiteEnd(&state->Names[i]); ajRegSubI(phyloRegStateState, 2, &tmpstr); ajStrAssignS(&rdline, tmpstr); } ajStrFmtUpper(&rdline); cp = ajStrGetPtr(rdline); j = 0; while(cp && ajRegExecC(phyloRegStateChar, cp)) { ajRegSubI(phyloRegStateChar, 0, &token); ajRegPre(phyloRegStateChar, &tmpstr); ajStrAppendS(&state->Str[i], token); ilen += ajStrGetLen(token); ajRegPostC(phyloRegStateChar, &cp); if(!j++ && !ajStrIsWhite(tmpstr)) { ajStrRemoveWhite(&tmpstr); ajErr("found bad state character(s) '%S' for '%S', " "expected [%S]", tmpstr, state->Names[i], statechars); } if(*cp && !isspace((int)*cp)) ajErr("found bad state character '%c' for '%S', " "expected [%S]", *cp, state->Names[i], statechars); } if(ilen == len) { ilen = 0; i++; } else if(ilen > len) { ajErr("Bad discrete states file b '%S':" " read %d states for '%S', expected %d", filename, ilen, state->Names[i], len); return NULL; } } if(i != size) { ajErr("Bad discrete states file '%S':" " read %d species, expected %d", filename, i, len); return NULL; } ajListPushAppend(statelist, state); } ajFileClose(&statefile); ajListToarray(statelist, (void***) &states); ret = (AjPPhyloState*) states; ajListFree(&statelist); ajStrDel(&tmpval); ajStrDel(&tmpstr); ajStrDel(&rdline); ajStrDel(&charpat); ajStrDel(&token); return ret; } ID ajPhyloStateTrace TY public MO ajphylo LB core XX DE Reports phylogenetic discrete state data to the debug file XX PN [1] PA r thys const AjPPhyloState PD Phylogenetic discrete states object PX RT void RD RX // void ajPhyloStateTrace(const AjPPhyloState thys) { ajint i; ajDebug("ajPhyloStateTrace\n"); ajDebug("=================\n"); ajDebug(" Len: %d Size: %d Count: %d Characters: '%S'\n", thys->Len, thys->Size, thys->Count, thys->Characters); for(i=0; i < thys->Size; i++) ajDebug("%S: %S\n", thys->Names[i], thys->Str[i]); return; } ID ajPhyloTreeRead TY public MO ajphylo LB core XX DE Reads phylogenetic trees from a file. DE DE For background see the NESCent Hackathon Wiki DE https://www.nescent.org/wg_phyloinformatics/Main_Page DE DE Programs using or producing trees include: DE DE Editors in http://bioinfo.unice.fr/biodiv/Tree_editors.html DE DE Packages PHYLIP, GARLI, MrBayes, PROTML, TREE-PUZZLE DE PAUP DE DE Tree formats include: DE DE Newick (New Hampshire) is a simple string with parentheses DE see http://evolution.genetics.washington.edu/phylip/newicktree.html DE and http://evolution.genetics.washington.edu/phylip/newick_doc.html DE Newick format is used by PHYLIP, GARLI, MrBayes, PROTML, TREE-PUZZLE DE In PAUP the format is called "phylip" DE Phylip includes weights for multiple input trees as a final comment DE containing only a floating point number e.g. [0.5]; DE DE Nexus files use Translate to define numbers, and then have a DE Newick string using the numbers, or simply define the Newick string DE with names. The branch lengths appear as in Newick. DE http://workshop.molecularevolution.org/resources/fileformats/tree_formats.php DE Test files (including malformed ones) are available from DE https://www.nescent.org/wg_phyloinformatics/Supporting_NEXUS DE DE nhx (New Hampshire Extended) includes annotation using specially formatted DE comments after the name:length in the form [&&NHX:code=value;code=value] DE where codes are documented in DE http://www.phylosoft.org/forester/NHX.html DE according to some documentation, ATV and FORESTER can read NHX with the [] DE removed but this is deprecated (naturally, as this would be a valid Newick DE tree with proper comments). DE NHX also bans the use of empty nodes so names are required. DE Apparently PHYLIP can read trees with no names. DE DE svggraph is an output format only DE (or at least, no sign of anything reading it) DE DE tabtree is an ASCII text representation used for output only by DE BioPerl::TreeIO DE DE lintree is a program output format. The program is available from DE http://www.bio.psu.edu/People/Faculty/Nei/Lab/lintre/lintre.unix.tar.Z DE DE phyloXML is an effort to define an XML format, still in progress DE http://www.phyloxml.org/ DE DE TGF treegraph format DE http://www.math.uni-bonn.de/people/jmueller/extra/treegraph/docu.pdf DE DE TGF TreeDyn format ... not the same as treegraph DE http://www.treedyn.org/ XX PN [1] PA r filename const AjPStr PD input filename PX PN [2] PA r size ajint PD Number of trees expected PD If zero, read all trees PX RT AjPPhyloTree* RD Phylogenetic tree object array on success RD NULL on failure RX // AjPPhyloTree* ajPhyloTreeRead(const AjPStr filename, ajint size) { AjPPhyloTree* ret = NULL; AjPPhyloTree tree = NULL; /*AjPTree mytree = NULL;*/ /* only for tracing the tree */ AjPFile treefile = NULL; AjPList treelist = NULL; AjBool treeok; ajint i; ajint count; ajint headcount = 0; const char* cp; AjPStr rdline = NULL; AjPStr token = NULL; AjPStr treecopy = NULL; AjPStr pretoken = NULL; AjPStr posttoken = NULL; if(!phyloRegTreeTree) /* tree definition */ phyloRegTreeTree = ajRegCompC("\\S+"); if(!phyloRegTreeRoot) /* (a,b) node to be trimmed */ phyloRegTreeRoot = ajRegCompC("^(.+)(\\([^\\)]+\\))"); if(!phyloRegTreeUnroot) /* remaining unrooted (a,b,c); */ phyloRegTreeUnroot = ajRegCompC("^\\([^,]*,[^,]*,.*\\);$"); if(!phyloRegTreeMulti) /* multifurcating (a,b,c) anywhere */ phyloRegTreeMulti = ajRegCompC("^\\([^,]*,[^,]*,.*\\)$"); if(!phyloRegTreeQuart) /* unrooted quartet ((a,b),(c,d)); */ phyloRegTreeQuart = ajRegCompC("^\\(\\.*\\)\\);$"); if(!phyloRegTreeLength) /* unrooted quartet ((a,b),(c,d)); */ phyloRegTreeLength = ajRegCompC(":[0-9][0-9.]*"); treelist = ajListNew(); count = size; if(count < 1) count = 1; treefile = ajFileNewInNameS(filename); if(!treefile) return NULL; if(treefile) { treeok = ajFalse; i = 0; while(!treeok) { ajDebug("ajPhyloTreeRead i: %d count: %d size: %d\n", i, count, size); tree = ajPhyloTreeNew(); while(ajReadlineTrim(treefile, &rdline)) { if(!i && !ajStrGetLen(tree->Tree)) { if(ajStrToInt(rdline, &headcount)) { ajDebug("ajPhyloTreeRead count: %d\n", headcount); if(size) { if(size < headcount) ajWarn("Tree file '%S' has %d trees," " expected %d", filename, headcount, size); if(size > headcount) ajErr("Tree file '%S' has %d trees," " required %d", filename, headcount, size); } ajReadlineTrim(treefile, &rdline); count = headcount; } } cp = ajStrGetPtr(rdline); /*ajDebug("ajPhyloTreeRead rdline '%S'\n", rdline);*/ while(cp && ajRegExecC(phyloRegTreeTree, cp)) { if(!size && !headcount && !ajStrGetLen(tree->Tree)) { count++; ajDebug("ajPhyloTreeRead count++ %d\n", count); } ajRegSubI(phyloRegTreeTree, 0, &token); ajStrAppendS(&tree->Tree, token); ajDebug("ajPhyloTreeRead token '%S'\n", token); ajRegPostC(phyloRegTreeTree, &cp); } if(ajStrGetCharLast(tree->Tree) == ';') break; ajDebug("ajPhyloTreeRead processing tree->Tree '%S'\n", tree->Tree); } if(ajStrGetLen(tree->Tree) && ajStrGetCharLast(tree->Tree) == ';') { ajDebug("ajPhyloTreeRead tree done tree->Tree '%S'\n", tree->Tree); tree->Size = 1 + (ajuint) ajStrCalcCountK(tree->Tree, ','); tree->BaseBifurcated = ajTrue; if(ajRegExec(phyloRegTreeQuart, tree->Tree)) { tree->BaseBifurcated = ajFalse; /* but rooted for phylip */ tree->BaseQuartet = ajTrue; } if(ajRegExec(phyloRegTreeLength, tree->Tree)) { tree->HasLengths = ajTrue; } tree->Multifurcated = ajFalse; ajStrAssignS(&treecopy, tree->Tree); while(ajRegExec(phyloRegTreeRoot, treecopy)) { ajRegSubI(phyloRegTreeRoot, 1, &pretoken); ajRegSubI(phyloRegTreeRoot, 2, &token); ajRegPost(phyloRegTreeRoot, &posttoken); ajDebug("ajPhyloTreeRead root match '%S': '%S'\n", treecopy, token); if(ajRegExec(phyloRegTreeMulti, token)) { tree->Multifurcated = ajTrue; ajDebug("ajPhyloTree multifurcated node '%S'\n", token); } ajStrAssignS(&treecopy, pretoken); ajStrAppendS(&treecopy, posttoken); } if(ajRegExec(phyloRegTreeUnroot, treecopy)) tree->BaseBifurcated = ajFalse; ajDebug("ajPhyloTreeRead tree '%S' rooted: %B " "basetrifurcated: %B" " treecopy: '%S'\n", tree->Tree, tree->BaseBifurcated, tree->BaseTrifurcated, treecopy); ajListPushAppend(treelist, tree); tree = NULL; i++; if(i == headcount) treeok = ajTrue; else if(size && size == i) treeok = ajTrue; } else { if(ajFileIsEof(treefile)) treeok = ajTrue; else { ajErr("Tree file %S: Unexpected end of file", filename); return NULL; } } } if(size && size != i) { ajErr("Tree file '%S' has %d trees," " required %d", filename, i, size); return NULL; } ajDebug("Tree file '%S' has %d (%Lu) trees," " required %d\n", filename, i, ajListGetLength(treelist), size); ajFileClose(&treefile); ajListToarray(treelist, (void***) &ret); /*ret = (AjPPhyloTree*) trees;*/ ajListFree(&treelist); } /* this code block fails to clean up the temporary tree data */ /* for(i=0;ret[i];i++) { mytree = ajTreeNewNewick(ret[i]->Tree); ajTreeTrace(mytree); ajTreeFree(&mytree); } */ ajStrDel(&rdline); ajStrDel(&token); ajStrDel(&treecopy); ajStrDel(&pretoken); ajStrDel(&posttoken); ajPhyloTreeDel(&tree); return ret; } ID ajPhyloTreeTrace TY public MO ajphylo LB core XX DE Reports phylogenetic discrete state data to the debug file XX PN [1] PA r thys const AjPPhyloTree PD Phylogenetic discrete states object PX RT void RD RX // void ajPhyloTreeTrace(const AjPPhyloTree thys) { ajDebug("ajPhyloTreeTrace\n"); ajDebug("================\n"); ajDebug(" Multifurcated: %B BaseTrifurcated: %B BaseBifurcated: %B" " BaseQuartet: %B Tree: '%S'\n", thys->Multifurcated, thys->BaseTrifurcated, thys->BaseBifurcated, thys->BaseQuartet, thys->Tree); return; } ID ajPhyloExit TY public MO ajphylo LB core XX DE Cleans up phylogenetic data internal memory XX RT void RD RX // void ajPhyloExit(void) { ajRegFree(&phyloRegDistFloat); ajRegFree(&phyloRegFreqFloat); ajRegFree(&phyloRegFreqInt); ajRegFree(&phyloRegFreqIndiv); ajRegFree(&phyloRegPropProp); ajRegFree(&phyloRegStateState); ajRegFree(&phyloRegStateInt); ajRegFree(&phyloRegStateChar); ajRegFree(&phyloRegTreeTree); ajRegFree(&phyloRegTreeRoot); ajRegFree(&phyloRegTreeUnroot); ajRegFree(&phyloRegTreeMulti); ajRegFree(&phyloRegTreeQuart); ajRegFree(&phyloRegTreeLength); return; } ID ajQueryNew TY public MO ajquery LB core XX DE Creates a new query object for a specific datatype from the DE AJDATATYPE enumerated types XX PN [1] PA r datatype const AjEDataType PD Enumerated datatype PX RT AjPQuery RD New query object. RX // AjPQuery ajQueryNew(const AjEDataType datatype) { AjPQuery pthis; AJNEW0(pthis); pthis->SvrName = ajStrNew(); pthis->DbName = ajStrNew(); pthis->DbAlias= ajStrNew(); pthis->QueryFields = ajListNew(); pthis->ResultsList = ajListNew(); pthis->ResultsTable = ajTablestrNewConst(100); pthis->DbIdentifier = ajStrNew(); pthis->DbAccession = ajStrNew(); pthis->Wild = ajFalse; pthis->Method = ajStrNew(); pthis->Formatstr = ajStrNew(); pthis->IndexDir = ajStrNew(); pthis->Directory = ajStrNew(); pthis->Filename = ajStrNew(); pthis->Application = ajStrNew(); pthis->SingleField = ajStrNew(); pthis->DbFilter = ajStrNew(); pthis->DbReturn = ajStrNew(); pthis->QueryType = AJQUERY_UNKNOWN; pthis->Access = NULL; pthis->DataType = datatype; pthis->QryData = NULL; pthis->Fpos = NULLFPOS; pthis->QryDone = ajFalse; pthis->HasAcc = ajTrue; return pthis; } ID ajQueryDel TY public MO ajquery LB core XX DE Deletes a query object XX PN [1] PA d pthis AjPQuery* PD Address of query object PX RT void RD RX // void ajQueryDel(AjPQuery* pthis) { AjPQuery thys; AjPQueryField field = NULL; AjPTextAccess textaccess; if(!pthis) return; if(!*pthis) return; ajDebug("ajQueryDel db:'%S' svr: '%S' fields:%Lu\n", (*pthis)->DbName, (*pthis)->SvrName, ajListGetLength((*pthis)->QueryFields)); thys = *pthis; ajStrDel(&thys->SvrName); ajStrDel(&thys->DbName); ajStrDel(&thys->DbAlias); ajStrDel(&thys->DbType); ajStrDel(&thys->DbIdentifier); ajStrDel(&thys->DbAccession); ajStrDel(&thys->DbFilter); ajStrDel(&thys->DbReturn); while(ajListPop(thys->QueryFields, (void **) &field)) ajQueryfieldDel(&field); ajListFree(&thys->QueryFields); ajListFreeData(&thys->ResultsList); ajTableFree(&thys->ResultsTable); ajStrDel(&thys->Method); ajStrDel(&thys->Qlinks); ajStrDel(&thys->Formatstr); ajStrDel(&thys->IndexDir); ajStrDel(&thys->Directory); ajStrDel(&thys->Filename); ajStrDel(&thys->Exclude); ajStrDel(&thys->Namespace); ajStrDel(&thys->Organisms); ajStrDel(&thys->DbFields); ajStrDel(&thys->DbUrl); ajStrDel(&thys->DbProxy); ajStrDel(&thys->DbHttpVer); ajStrDel(&thys->ServerVer); ajStrDel(&thys->SingleField); ajStrDel(&thys->QryString); ajStrDel(&thys->QryFields); ajStrDel(&thys->Application); if(thys->QryData) { textaccess = thys->TextAccess; if(textaccess) { if(textaccess->AccessFree) (*textaccess->AccessFree)(thys); } AJFREE(thys->QryData); } AJFREE(*pthis); return; } ID ajQueryGetDatatype TY public MO ajquery LB core XX DE Returns the query datatype from a query XX PN [1] PA r query const AjPQuery PD Query PX RT const char* RD Standard name for query datatype RX // const char* ajQueryGetDatatype(const AjPQuery query) { if(query->DataType > AJDATATYPE_MAX) return "invalid"; return queryDatatypeName[query->DataType]; } ID ajQueryGetFormat TY public MO ajquery LB core XX DE Returns the format name from a query XX PN [1] PA r query const AjPQuery PD Query PX RT const AjPStr RD Format name RX // const AjPStr ajQueryGetFormat(const AjPQuery query) { return query->Formatstr; } ID ajQueryGetId TY public MO ajquery LB core XX DE Returns the ID query string from a query XX PN [1] PA r query const AjPQuery PD Query PX RT const AjPStr RD Wildcard ID query string RX // const AjPStr ajQueryGetId(const AjPQuery query) { return queryGetFieldC(query, "id"); } ID ajQueryGetQuery TY public MO ajquery LB core XX DE Returns a report of the query string from a query XX PN [1] PA r query const AjPQuery PD Query PX PN [2] PA w Pdest AjPStr* PD Query string PX RT AjBool RD True on success RX // AjBool ajQueryGetQuery(const AjPQuery query, AjPStr *Pdest) { AjIList iter = NULL; AjPQueryField field = NULL; const char* queryLinkNames[] = {"", "OR", "AND", "EOR", "NOT", "ELSE"}; if(!Pdest) return ajFalse; ajStrAssignClear(Pdest); iter = ajListIterNewread(query->QueryFields); while(!ajListIterDone(iter)) { field = ajListIterGet(iter); if(field->Link != AJQLINK_INIT) ajFmtPrintAppS(Pdest, " %s ", queryLinkNames[field->Link]); ajFmtPrintAppS(Pdest, "%S:%S", field->Field, field->Wildquery); } ajListIterDel(&iter); return ajTrue; } ID ajQueryGetallFields TY public MO ajquery LB core XX DE Returns the standard sequence query string from a query XX PN [1] PA r query const AjPQuery PD Query PX RT const AjPList RD List of field objects RX // const AjPList ajQueryGetallFields(const AjPQuery query) { return query->QueryFields; } ID queryGetFieldC TY static MO ajquery LB core XX DE Returns the query string from a query for a named field XX PN [1] PA r query const AjPQuery PD Query PX PN [2] PA r txt const char* PD Field name PX RT const AjPStr RD Wildcard ID query string RX // static const AjPStr queryGetFieldC(const AjPQuery query, const char* txt) { AjPStr ret = NULL; AjIList iter = NULL; AjPQueryField field = NULL; iter = ajListIterNewread(query->QueryFields); while(!ajListIterDone(iter)) { field = ajListIterGet(iter); if(ajStrMatchC(field->Field, txt)) { ret = field->Wildquery; break; } } ajListIterDel(&iter); return ret; } ID ajQueryIsSet TY public MO ajquery LB core XX DE Tests whether any element of a query has been set. XX PN [1] PA r thys const AjPQuery PD Query object. PX RT AjBool RD ajTrue if query should be made. ajFalse if the query RD includes all entries. RX // AjBool ajQueryIsSet(const AjPQuery thys) { ajDebug("ajQueryIsSet list:%Lu\n", ajListGetLength(thys->QueryFields)); if(ajListGetLength(thys->QueryFields)) return ajTrue; return ajFalse; } ID ajQueryKnownFieldC TY public MO ajquery LB core XX DE Checks whether a query field is defined for a database as a "fields:" DE string in the database definition. XX PN [1] PA r thys const AjPQuery PD Query object PX PN [2] PA r fieldtxt const char* PD field name PX RT AjBool RD ajTrue if the field is defined RX // AjBool ajQueryKnownFieldC(const AjPQuery thys, const char* fieldtxt) { AjPStrTok handle = NULL; AjPStr token = NULL; ajDebug("ajQueryKnownFieldC qry '%s' fields '%S'\n", fieldtxt, thys->DbFields); ajStrTokenAssignC(&handle, thys->DbFields, "\t ,;\n\r"); while(ajStrTokenNextParse(&handle, &token)) { if(ajStrMatchCaseC(token, fieldtxt)) { ajDebug("ajQueryKnownFieldC match '%S'\n", token); ajStrTokenDel(&handle); ajStrDel(&token); return ajTrue; } } ajStrTokenDel(&handle); ajStrDel(&token); if(ajCharMatchCaseC(fieldtxt, "id")) return ajTrue; if(thys->HasAcc && ajCharMatchCaseC(fieldtxt, "acc")) return ajTrue; return ajFalse; } ID ajQueryKnownFieldS TY public MO ajquery LB core XX DE Checks whether a query field is defined for a database as a "fields:" DE string in the database definition. XX PN [1] PA r thys const AjPQuery PD Query object PX PN [2] PA r field const AjPStr PD field name PX RT AjBool RD ajTrue if the field is defined RX // AjBool ajQueryKnownFieldS(const AjPQuery thys, const AjPStr field) { return ajQueryKnownFieldC(thys, ajStrGetPtr(field)); } ID ajQueryAddFieldAndC TY public MO ajquery LB core XX DE Adds a query with an 'AND' operator XX PN [1] PA u thys AjPQuery PD Query object PX PN [2] PA r fieldtxt const char* PD field name PX PN [3] PA r wildquerytxt const char* PD wildcard query string PX RT AjBool RD ajTrue on success RX // AjBool ajQueryAddFieldAndC(AjPQuery thys, const char* fieldtxt, const char* wildquerytxt) { AjPQueryField qryfield; qryfield = ajQueryfieldNewC(fieldtxt, wildquerytxt, AJQLINK_AND); if(!thys->QueryFields) thys->QueryFields = ajListNew(); ajListPushAppend(thys->QueryFields, qryfield); qryfield = NULL; return ajTrue; } ID ajQueryAddFieldAndS TY public MO ajquery LB core XX DE Adds a query with an 'AND' operator XX PN [1] PA u thys AjPQuery PD Query object PX PN [2] PA r field const AjPStr PD field name PX PN [3] PA r wildquery const AjPStr PD wildcard query string PX RT AjBool RD ajTrue on success RX // AjBool ajQueryAddFieldAndS(AjPQuery thys, const AjPStr field, const AjPStr wildquery) { AjPQueryField qryfield; qryfield = ajQueryfieldNewS(field, wildquery, AJQLINK_AND); if(!thys->QueryFields) thys->QueryFields = ajListNew(); ajListPushAppend(thys->QueryFields, qryfield); qryfield = NULL; return ajTrue; } ID ajQueryAddFieldElseC TY public MO ajquery LB core XX DE Adds a query with an 'ELSE' operator XX PN [1] PA u thys AjPQuery PD Query object PX PN [2] PA r fieldtxt const char* PD field name PX PN [3] PA r wildquerytxt const char* PD wildcard query string PX RT AjBool RD ajTrue on success RX // AjBool ajQueryAddFieldElseC(AjPQuery thys, const char* fieldtxt, const char* wildquerytxt) { AjPQueryField qryfield; qryfield = ajQueryfieldNewC(fieldtxt, wildquerytxt, AJQLINK_ELSE); if(!thys->QueryFields) thys->QueryFields = ajListNew(); ajListPushAppend(thys->QueryFields, qryfield); qryfield = NULL; return ajTrue; } ID ajQueryAddFieldElseS TY public MO ajquery LB core XX DE Adds a query with an 'ELSE' operator XX PN [1] PA u thys AjPQuery PD Query object PX PN [2] PA r field const AjPStr PD field name PX PN [3] PA r wildquery const AjPStr PD wildcard query string PX RT AjBool RD ajTrue on success RX // AjBool ajQueryAddFieldElseS(AjPQuery thys, const AjPStr field, const AjPStr wildquery) { AjPQueryField qryfield; qryfield = ajQueryfieldNewS(field, wildquery, AJQLINK_ELSE); if(!thys->QueryFields) thys->QueryFields = ajListNew(); ajListPushAppend(thys->QueryFields, qryfield); qryfield = NULL; return ajTrue; } ID ajQueryAddFieldEorC TY public MO ajquery LB core XX DE Adds a query with an 'EOR' operator XX PN [1] PA u thys AjPQuery PD Query object PX PN [2] PA r fieldtxt const char* PD field name PX PN [3] PA r wildquerytxt const char* PD wildcard query string PX RT AjBool RD ajTrue on success RX // AjBool ajQueryAddFieldEorC(AjPQuery thys, const char* fieldtxt, const char* wildquerytxt) { AjPQueryField qryfield; qryfield = ajQueryfieldNewC(fieldtxt, wildquerytxt, AJQLINK_EOR); if(!thys->QueryFields) thys->QueryFields = ajListNew(); ajListPushAppend(thys->QueryFields, qryfield); qryfield = NULL; return ajTrue; } ID ajQueryAddFieldEorS TY public MO ajquery LB core XX DE Adds a query with an 'EOR' operator XX PN [1] PA u thys AjPQuery PD Query object PX PN [2] PA r field const AjPStr PD field name PX PN [3] PA r wildquery const AjPStr PD wildcard query string PX RT AjBool RD ajTrue on success RX // AjBool ajQueryAddFieldEorS(AjPQuery thys, const AjPStr field, const AjPStr wildquery) { AjPQueryField qryfield; qryfield = ajQueryfieldNewS(field, wildquery, AJQLINK_EOR); if(!thys->QueryFields) thys->QueryFields = ajListNew(); ajListPushAppend(thys->QueryFields, qryfield); qryfield = NULL; return ajTrue; } ID ajQueryAddFieldNotC TY public MO ajquery LB core XX DE Adds a query with a 'NOT' operator XX PN [1] PA u thys AjPQuery PD Query object PX PN [2] PA r fieldtxt const char* PD field name PX PN [3] PA r wildquerytxt const char* PD wildcard query string PX RT AjBool RD ajTrue on success RX // AjBool ajQueryAddFieldNotC(AjPQuery thys, const char* fieldtxt, const char* wildquerytxt) { AjPQueryField qryfield; qryfield = ajQueryfieldNewC(fieldtxt, wildquerytxt, AJQLINK_NOT); if(!thys->QueryFields) thys->QueryFields = ajListNew(); ajListPushAppend(thys->QueryFields, qryfield); qryfield = NULL; return ajTrue; } ID ajQueryAddFieldNotS TY public MO ajquery LB core XX DE Adds a query with a 'NOT' operator XX PN [1] PA u thys AjPQuery PD Query object PX PN [2] PA r field const AjPStr PD field name PX PN [3] PA r wildquery const AjPStr PD wildcard query string PX RT AjBool RD ajTrue on success RX // AjBool ajQueryAddFieldNotS(AjPQuery thys, const AjPStr field, const AjPStr wildquery) { AjPQueryField qryfield; qryfield = ajQueryfieldNewS(field, wildquery, AJQLINK_NOT); if(!thys->QueryFields) thys->QueryFields = ajListNew(); ajListPushAppend(thys->QueryFields, qryfield); qryfield = NULL; return ajTrue; } ID ajQueryAddFieldOrC TY public MO ajquery LB core XX DE Adds a query with an 'OR' operator XX PN [1] PA u thys AjPQuery PD Query object PX PN [2] PA r fieldtxt const char* PD field name PX PN [3] PA r wildquerytxt const char* PD wildcard query string PX RT AjBool RD ajTrue on success RX // AjBool ajQueryAddFieldOrC(AjPQuery thys, const char* fieldtxt, const char* wildquerytxt) { AjPQueryField qryfield; if(!thys->QueryFields) thys->QueryFields = ajListNew(); if(!ajListGetLength(thys->QueryFields)) qryfield = ajQueryfieldNewC(fieldtxt, wildquerytxt, AJQLINK_INIT); else qryfield = ajQueryfieldNewC(fieldtxt, wildquerytxt, AJQLINK_OR); ajListPushAppend(thys->QueryFields, qryfield); qryfield = NULL; return ajTrue; } ID ajQueryAddFieldOrS TY public MO ajquery LB core XX DE Adds a query with an 'OR' operator XX PN [1] PA u thys AjPQuery PD Query object PX PN [2] PA r field const AjPStr PD field name PX PN [3] PA r wildquery const AjPStr PD wildcard query string PX RT AjBool RD ajTrue on success RX // AjBool ajQueryAddFieldOrS(AjPQuery thys, const AjPStr field, const AjPStr wildquery) { AjPQueryField qryfield; if(!thys->QueryFields) thys->QueryFields = ajListNew(); if(!ajListGetLength(thys->QueryFields)) qryfield = ajQueryfieldNewS(field, wildquery, AJQLINK_INIT); else qryfield = ajQueryfieldNewS(field, wildquery, AJQLINK_OR); ajListPushAppend(thys->QueryFields, qryfield); qryfield = NULL; return ajTrue; } ID ajQueryClear TY public MO ajquery LB core XX DE Resets a query object to a clean state for reuse. DE Keep the begin, end and reverse values. XX PN [1] PA u thys AjPQuery PD query object PX RT void RD RX // void ajQueryClear(AjPQuery thys) { AjPQueryField field = NULL; void *result; ajStrSetClear(&thys->SvrName); ajStrSetClear(&thys->DbName); ajStrSetClear(&thys->DbAlias); ajStrSetClear(&thys->DbType); while(ajListPop(thys->QueryFields, (void **) &field)) ajQueryfieldDel(&field); while(ajListPop(thys->ResultsList, (void **) &result)) continue; ajTableClear(thys->ResultsTable); ajStrSetClear(&thys->Method); ajStrSetClear(&thys->Qlinks); ajStrSetClear(&thys->Formatstr); ajStrSetClear(&thys->IndexDir); ajStrSetClear(&thys->Directory); ajStrSetClear(&thys->Filename); ajStrSetClear(&thys->Exclude); ajStrSetClear(&thys->Namespace); ajStrSetClear(&thys->Organisms); ajStrSetClear(&thys->DbFields); ajStrSetClear(&thys->DbUrl); ajStrSetClear(&thys->DbProxy); ajStrSetClear(&thys->DbHttpVer); ajStrSetClear(&thys->ServerVer); ajStrSetClear(&thys->SingleField); ajStrSetClear(&thys->QryString); ajStrSetClear(&thys->QryFields); ajStrSetClear(&thys->Application); ajStrSetClear(&thys->DbIdentifier); ajStrSetClear(&thys->DbAccession); ajStrSetClear(&thys->DbFilter); ajStrSetClear(&thys->DbReturn); thys->Fpos = 0L; thys->TextAccess = NULL; thys->Access = NULL; if(thys->QryData) AJFREE(thys->QryData); thys->QueryType = AJQUERY_UNKNOWN; /*thys->DataType = AJDATATYPE_UNKNOWN;*/ thys->QryDone = ajFalse; thys->SetServer = ajFalse; thys->SetDatabase = ajFalse; thys->SetQuery = ajFalse; thys->InDrcat = ajFalse; thys->Wild = ajFalse; thys->CaseId = ajFalse; thys->HasAcc = ajTrue; /* default true in ajQueryNew */ return; } ID ajQuerySetWild TY public MO ajquery LB core XX DE Tests whether a query includes wild cards in any element, DE or can return more than one entry (keyword and some other search terms DE will find multiple entries) XX PN [1] PA u thys AjPQuery PD Query object. PX RT AjBool RD ajTrue if query had wild cards. RX // AjBool ajQuerySetWild(AjPQuery thys) { AjIList iter = NULL; AjPQueryField field = NULL; AjBool ret = ajFalse; ajuint ifield = 0; if(!queryRegWild) queryWildComp(); ajDebug("ajQuerySetWild fields: %Lu\n", ajListGetLength(thys->QueryFields)); /* first test for ID query */ iter = ajListIterNewread(thys->QueryFields); while(!ajListIterDone(iter)) { field = ajListIterGet(iter); ajDebug("test1 query %S '%S' %u\n", field->Field, field->Wildquery, field->Link); if(ajStrMatchC(field->Field, "id")) { if(ajRegExec(queryRegWild, field->Wildquery)) { thys->Wild = ajTrue; ajDebug("wild id query %S '%S'\n", field->Field, field->Wildquery); ajListIterDel(&iter); return ajTrue; } break; } } ajListIterRewind(iter); /* now test the other fields */ while(!ajListIterDone(iter)) { field = ajListIterGet(iter); ajDebug("test2 query %S '%S'\n", field->Field, field->Wildquery); if(ifield++ && field->Link != AJQLINK_ELSE) /* field [|&!^] field */ { ajListIterDel(&iter); return ajTrue; } if(ajStrMatchC(field->Field, "id")) continue; if(ajRegExec(queryRegWild, field->Wildquery)) { thys->Wild = ajTrue; ajDebug("wild query %S '%S'\n", field->Field, field->Wildquery); ajListIterDel(&iter); return ajTrue; } if(!ajStrMatchC(field->Field, "acc")) ret = ajTrue; } ajListIterDel(&iter); ajDebug("wildcard in stored query: %B\n", ret); return ret; } ID ajQueryStarclear TY public MO ajquery LB core XX DE Clears elements of a query object if they are simply "*" because this DE is equivalent to a null string. XX PN [1] PA u thys AjPQuery PD Query object. PX RT void RD RX // void ajQueryStarclear(AjPQuery thys) { AjIList iter = NULL; AjPQueryField field = NULL; iter = ajListIterNew(thys->QueryFields); while(!ajListIterDone(iter)) { field = ajListIterGet(iter); if(ajStrMatchC(field->Wildquery, "*")) { ajDebug("ajQueryStarclear keep field %S '%S'\n", field->Field, field->Wildquery); /* ajQueryfieldDel(&field); ajListIterRemove(iter); */ } } ajListIterDel(&iter); return; } ID ajQueryTrace TY public MO ajquery LB core XX DE Debug calls to trace the data in a query object. XX PN [1] PA r thys const AjPQuery PD query object. PX RT void RD RX // void ajQueryTrace(const AjPQuery thys) { AjIList iter = NULL; AjPQueryField field = NULL; const char* operators[] = { "init", "OR", "AND", "EOR", "NOT", "ELSE" }; ajDebug( " Query Trace\n"); if(ajStrGetLen(thys->SvrName)) ajDebug( " SvrName: '%S'\n", thys->SvrName); if(ajStrGetLen(thys->DbName)) ajDebug( " DbName: '%S'\n", thys->DbName); if(ajStrGetLen(thys->DbAlias)) ajDebug( " DbAlias: '%S'\n", thys->DbAlias); if(ajStrGetLen(thys->DbType)) ajDebug( " DbType: '%S' (%d)\n", thys->DbType, thys->QueryType); iter = ajListIterNewread(thys->QueryFields); while(!ajListIterDone(iter)) { field = ajListIterGet(iter); if(field->Link < AJQLINK_MAX) ajDebug( " %S: '%S' %d (%s)\n", field->Field, field->Wildquery, field->Link, operators[field->Link]); else ajDebug( " %S: '%S' %d\n", field->Field, field->Wildquery, field->Link); } ajListIterDel(&iter); ajDebug( " Case-sensitive Id: '%B'\n", thys->CaseId); ajDebug( " Has accession: %B\n", thys->HasAcc); if(ajStrGetLen(thys->Method)) ajDebug( " Method: '%S'\n", thys->Method); if(ajStrGetLen(thys->Formatstr)) ajDebug( " Formatstr: '%S'\n", thys->Formatstr); if(ajStrGetLen(thys->IndexDir)) ajDebug( " IndexDir: '%S'\n", thys->IndexDir); if(ajStrGetLen(thys->Directory)) ajDebug( " Directory: '%S'\n", thys->Directory); if(ajStrGetLen(thys->Filename)) ajDebug( " Filename: '%S'\n", thys->Filename); if(ajStrGetLen(thys->Exclude)) ajDebug( " Exclude: '%S'\n", thys->Exclude); if(ajStrGetLen(thys->Namespace)) ajDebug( " Namespace: '%S'\n", thys->Namespace); if(ajStrGetLen(thys->Organisms)) ajDebug( " Organisms: '%S'\n", thys->Organisms); if(ajStrGetLen(thys->DbFields)) ajDebug( " DbFields: '%S'\n", thys->DbFields); if(ajStrGetLen(thys->DbIdentifier)) ajDebug( " DbIdentifier: '%S'\n", thys->DbIdentifier); if(ajStrGetLen(thys->DbAccession)) ajDebug( " DbAccession: '%S'\n", thys->DbAccession); if(ajStrGetLen(thys->DbFilter)) ajDebug( " DbFilter: '%S'\n", thys->DbFilter); if(ajStrGetLen(thys->DbReturn)) ajDebug( " DbReturn: '%S'\n", thys->DbReturn); if(ajStrGetLen(thys->DbUrl)) ajDebug( " DbUrl: '%S'\n", thys->DbUrl); if(ajStrGetLen(thys->DbProxy)) ajDebug( " DbProxy: '%S'\n", thys->DbProxy); if(ajStrGetLen(thys->DbHttpVer)) ajDebug( " DbHttpVer: '%S'\n", thys->DbHttpVer); if(ajStrGetLen(thys->SingleField)) ajDebug( " SingleField: '%S'\n", thys->SingleField); if(ajStrGetLen(thys->QryString)) ajDebug( " QryString: '%S'\n", thys->QryString); if(ajStrGetLen(thys->QryFields)) ajDebug( " QryFields: '%S'\n", thys->QryFields); if(ajStrGetLen(thys->Application)) ajDebug( " Application: '%S'\n", thys->Application); ajDebug( " Fpos: %Ld\n", thys->Fpos); ajDebug( " QryDone: %B\n", thys->QryDone); ajDebug( " Wildcard in query: %B\n", thys->Wild); if(thys->Access) ajDebug( " Access: exists\n"); if(thys->QryData) ajDebug( " QryData: exists\n"); return; } ID ajQueryExit TY public MO ajquery LB core XX DE Cleans up query processing internal memory XX RT void RD RX // void ajQueryExit(void) { ajStrDel(&queryFormat); ajStrDel(&queryList); ajStrDel(&querySvr); ajStrDel(&queryDb); ajStrDel(&queryChr); ajStrDel(&queryTest); ajRegFree(&queryRegAsis); ajRegFree(&queryRegHttp); ajRegFree(&queryRegFtp); ajRegFree(&queryRegSvr); ajRegFree(&queryRegDb); ajRegFree(&queryRegFmt); ajRegFree(&queryRegFieldId); ajRegFree(&queryRegId); ajRegFree(&queryRegList); ajRegFree(&queryRegQryId); ajRegFree(&queryRegQryField); ajRegFree(&queryRegRange); ajRegFree(&queryRegRefrange); ajRegFree(&queryRegWild); return; } ID ajQueryfieldNewC TY public MO ajquery LB core XX DE Constructor for a query field XX PN [1] PA r fieldtxt const char* PD Field name PX PN [2] PA r wildtxt const char* PD Wildcard query string PX PN [3] PA r oper AjEQryLink PD Operator PX RT AjPQueryField RD Query field RX // AjPQueryField ajQueryfieldNewC(const char* fieldtxt, const char* wildtxt, AjEQryLink oper) { AjPQueryField ret; AJNEW0(ret); ajDebug("ajQueryfieldNewC '%s' '%s'\n", fieldtxt, wildtxt); ret->Field = ajStrNewC(fieldtxt); ret->Wildquery = ajStrNewC(wildtxt); ret->Link = oper; ajStrFmtLower(&ret->Field); return ret; } ID ajQueryfieldNewS TY public MO ajquery LB core XX DE Constructor for a query field XX PN [1] PA r field const AjPStr PD Field name PX PN [2] PA r wild const AjPStr PD Wildcard query string PX PN [3] PA r oper AjEQryLink PD Operator PX RT AjPQueryField RD Query field RX // AjPQueryField ajQueryfieldNewS(const AjPStr field, const AjPStr wild, AjEQryLink oper) { AjPQueryField ret; AJNEW0(ret); ajDebug("ajQueryfieldNewS '%S' '%S'\n", field, wild); ret->Field = ajStrNewS(field); ret->Wildquery = ajStrNewS(wild); ret->Link = oper; ajStrFmtLower(&ret->Field); return ret; } ID ajQueryfieldDel TY public MO ajquery LB core XX DE Destructor for a query field XX PN [1] PA d Pthis AjPQueryField* PD Query field object to be deleted PX RT void RD RX // void ajQueryfieldDel(AjPQueryField *Pthis) { AjPQueryField thys; if(!Pthis) return; if(!*Pthis) return; thys = *Pthis; ajStrDel(&thys->Field); ajStrDel(&thys->Wildquery); AJFREE(*Pthis); return; } ID ajQuerylistTrace TY public MO ajquery LB core XX DE Traces the nodes in a query list XX PN [1] PA r list const AjPList PD The query list PX RT void RD RX // void ajQuerylistTrace(const AjPList list) { AjIList iter; AjPQueryList node; ajuint i = 0; iter = ajListIterNewread(list); ajDebug("ajQuerylistTrace %Lu nodes\n", ajListGetLength(list)); while(!ajListIterDone(iter)) { node = (AjPQueryList) ajListIterGet(iter); ajDebug("%3d: '%S' '%S' %d\n", ++i, node->Qry, node->Formatstr, node->Format); } ajListIterDel(&iter); ajDebug("...Done...\n"); return; } ID queryWildComp TY static MO ajquery LB core XX DE Compiles the regular expressions for testing wild cards in queries. DE These are held in static storage and built once only if needed. XX RT void RD RX // static void queryWildComp(void) { if(!queryRegWild) queryRegWild = ajRegCompC("[*?]"); return; } ID ajQuerystrParseFormat TY public MO ajquery LB core XX DE Parses a query (USA, UFO or general query). DE DE Then tests for "format::" and returns this if it is found, DE removing the format part of the original query. XX PN [1] PA u Pqry AjPStr* PD Query string PX PN [2] PA u textin AjPTextin PD Text input object PX PN [3] PA f findformat AjBool function PD Function to validate format name PX RT const AjPStr RD Format name if found RX // const AjPStr ajQuerystrParseFormat(AjPStr *Pqry, AjPTextin textin, AjBool findformat(const AjPStr format, ajint *iformat)) { AjBool fmtstat = ajFalse; /* status returns from regex tests */ AjBool asisstat = ajFalse; AjBool liststat = ajFalse; AjPQuery qry = textin->Query; ajDebug("++ajQuerystrParseFormat '%S'\n", *Pqry); if(!queryRegInitDone) queryRegInit(); /* Strip any leading spaces */ ajStrTrimC(Pqry," \t\n"); asisstat = ajRegExec(queryRegAsis, *Pqry); liststat = ajRegExec(queryRegList, *Pqry); if(liststat || asisstat) return NULL; fmtstat = ajRegExec(queryRegFmt, *Pqry); if(!fmtstat) { if(ajStrGetLen(textin->Formatstr)) { if(findformat(textin->Formatstr, &textin->Format)) ajStrAssignS(&qry->Formatstr, textin->Formatstr); } return NULL; } ajRegSubI(queryRegFmt, 1, &queryFormat); ajRegSubI(queryRegFmt, 2, Pqry); ajDebug("found format %S rest '%S'\n", queryFormat, *Pqry); ajStrAssignEmptyC(&queryFormat, "unknown"); if(findformat(queryFormat, &textin->Format)) { ajStrAssignS(&qry->Formatstr, queryFormat); ajStrAssignS(&textin->Formatstr, queryFormat); } else ajErr("Unknown input format '%S'\n", queryFormat); return queryFormat; } ID ajQuerystrParseListfile TY public MO ajquery LB core XX DE Parses a query (USA, UFO or general query). DE DE Then tests for "list::" or " XX PN [1] PA u Pqry AjPStr* PD Query string PX RT AjBool RD True if found RX // AjBool ajQuerystrParseListfile(AjPStr *Pqry) { AjBool liststat = ajFalse; /* status returns from regex tests */ ajDebug("++ajQuerystrParseListfile '%S'\n", *Pqry); if(!queryRegInitDone) queryRegInit(); /* Strip any leading spaces */ ajStrTrimC(Pqry," \t\n"); ajStrAssignS(&queryTest, *Pqry); liststat = ajRegExec(queryRegList, queryTest); if(liststat) { ajRegSubI(queryRegList, 2, Pqry); ajDebug("list found @%S\n", *Pqry); } return liststat; } ID ajQuerystrParseRange TY public MO ajquery LB core XX DE Parses a query (USA, UFO or general query). DE DE Tests for "[n:n:r]" range and sets this if it is found, DE removing the range part of the original query. XX PN [1] PA u Pqry AjPStr* PD Query string PX PN [2] PA u Pbegin ajint* PD Begin position PX PN [3] PA u Pend ajint* PD End position PX PN [4] PA u Prev AjBool* PD Reverse orientation PX RT AjBool RD True if range was found. RX // AjBool ajQuerystrParseRange(AjPStr *Pqry, ajint *Pbegin, ajint *Pend, AjBool *Prev) { AjPStr tmpstr = NULL; AjBool rangestat = ajFalse; if(!queryRegInitDone) queryRegInit(); ajDebug("ajQuerystrParseRange: '%S'\n", *Pqry); rangestat = ajRegExec(queryRegRange, *Pqry); if(rangestat) { ajRegSubI(queryRegRange, 2, &tmpstr); if(ajStrGetLen(tmpstr)) ajStrToInt(tmpstr, Pbegin); ajRegSubI(queryRegRange, 3, &tmpstr); if(ajStrGetLen(tmpstr)) ajStrToInt(tmpstr, Pend); ajRegSubI(queryRegRange, 5, &tmpstr); if(ajStrGetLen(tmpstr)) *Prev = ajTrue; ajStrDel(&tmpstr); ajRegSubI(queryRegRange, 1, Pqry); ajDebug("range found [%d:%d:%b]\n", *Pbegin, *Pend, *Prev); return ajTrue; } rangestat = ajRegExec(queryRegRefrange, *Pqry); if(rangestat) { ajRegSubI(queryRegRefrange, 2, &tmpstr); if(ajStrGetLen(tmpstr)) ajStrToInt(tmpstr, Pbegin); ajRegSubI(queryRegRefrange, 4, &tmpstr); if(ajStrGetLen(tmpstr)) ajStrToInt(tmpstr, Pend); ajRegSubI(queryRegRefrange, 6, &tmpstr); if(ajStrGetLen(tmpstr)) *Prev = ajTrue; ajStrDel(&tmpstr); ajRegSubI(queryRegRefrange, 1, Pqry); ajDebug("refrange found [%d:%d:%b]\n", *Pbegin, *Pend, *Prev); return ajTrue; } return ajFalse; } ID ajQuerystrParseRead TY public MO ajquery LB core XX DE Parses a query (USA, UFO or general query). DE DE First tests for "asis" input and sets the rest of the query as input data. DE DE Then tests for server:dbname:query or dbname:query DE and tests whether the access method is a text method. DE DE For text access, calls the access method to opens the file DE (and set the file position). DE DE If there is no database, looks for file:query and opens the file. DE DE If an offset is provided by %offset this sets the file position DE DE If the file does not exist, tests again for a database of that name DE from any known server. DE DE If no text data was found, returns the filled in datatype-specific details XX PN [1] PA u Pqry AjPStr* PD Query string PX PN [2] PA u textin AjPTextin PD Text input structure. PX PN [3] PA f findformat AjBool function PD Function to validate format name PX PN [4] PA w Pnontext AjBool* PD True if access is a non-text method PX RT AjBool RD ajTrue on success. RX // AjBool ajQuerystrParseRead(AjPStr *Pqry, AjPTextin textin, AjBool findformat(const AjPStr format, ajint *iformat), AjBool *Pnontext) { AjPQuery qry; AjBool svrstat = ajFalse; AjBool dbstat = ajFalse; AjBool regstat = ajFalse; AjBool accstat = ajFalse; /* return status from reading something */ AjBool asisstat = ajFalse; AjBool ftpstat = ajFalse; AjBool httpstat = ajFalse; AjBool drcatstat = ajFalse; AjBool isspecialfile = ajFalse; AjBool inbraces = ajFalse; AjPStr braceopen = NULL; AjPStr braceclose = NULL; AjPStr idstr = NULL; AjPStr lastoper= NULL; AjPStr operstr = NULL; AjPStr qrystr = NULL; ajlong i; ajuint iquery = 0; AjPStrTok handle = NULL; AjPTextAccess textaccess = NULL; ajint iformat = 0; #ifdef __CYGWIN__ char cygd; #endif *Pnontext = ajFalse; qry = textin->Query; if(qry->QryDone) return ajFalse; ajStrAssignS(&qry->QryString, *Pqry); ajStrDel(&qry->SingleField); /* clear it. we test this for regstat */ if(!queryRegInitDone) queryRegInit(); ajDebug("ajQuerystrParseRead qry '%S' iquery '%S' ioffset '%Ld' " "format: '%S' (%d)\n", qry->QryString, textin->QryFields, textin->Fpos, textin->Formatstr, textin->Format); ajStrAssignS(&qry->QryFields, textin->QryFields); qry->Fpos = textin->Fpos; /* Strip any leading spaces */ ajStrTrimC(Pqry," \t\n"); /* ** First we test some basic access methods */ /* ** ASIS ** asis::datastream */ asisstat = ajRegExec(queryRegAsis, *Pqry); if(asisstat) { ajStrAssignC(&queryFormat, "asis"); if(!findformat(queryFormat, &textin->Format)) { ajErr("asis:: not supported by datatype for '%S'", *Pqry); return ajFalse; } ajStrAssignS(&qry->Formatstr, queryFormat); ajStrAssignS(&textin->Formatstr, queryFormat); ajRegSubI(queryRegAsis, 1, &qry->Filename); ajDebug("asis data '%S'\n", qry->Filename); isspecialfile = ajTrue; } /* ** FTP URL ** ftp://host.address/path/to/file ** supports -ioffset -iformat */ ftpstat = ajRegExec(queryRegFtp, *Pqry); if(ftpstat) { ajRegSubI(queryRegFtp, 1, &qry->Filename); ajDebug("ftp data '%S'\n", qry->Filename); isspecialfile = ajTrue; } /* ** HTTP URL ** http://host.address/path/to/file ** supports -ioffset -iformat */ httpstat = ajRegExec(queryRegHttp, *Pqry); if(httpstat) { ajRegSubI(queryRegHttp, 1, &qry->Filename); ajDebug("http data '%S'\n", qry->Filename); isspecialfile = ajTrue; } #ifdef __CYGWIN__ /* ** replace C: drive with /cygdrive/c/ */ if(ajStrGetCharPos(*Pqry,1) == ':') { cygd = ajStrGetCharFirst(*Pqry); if(isupper((int) cygd)) ajStrPasteCountK(Pqry, 0, (char) tolower((int)cygd), 1); ajStrPasteCountK(Pqry, 1, '/', 1); ajStrInsertC(Pqry, 0, "/cygdrive/"); } #endif ajDebug("query to test: '%S'\n\n", *Pqry); /* ** test a name: prefix for a possible server name */ svrstat = ajRegExec(queryRegSvr, *Pqry); ajDebug("server dbexp: %B '%S'\n", svrstat, *Pqry); if(svrstat) { ajRegSubI(queryRegSvr, 1, &querySvr); /* clear it if this was really a file */ if(ajNamServer(querySvr)) { ajRegPost(queryRegSvr, Pqry); ajDebug("found server %S, rest '%S'\n", querySvr, *Pqry); } else { ajDebug("unknown server %S, try dbname\n", querySvr); ajStrDel(&querySvr); svrstat = ajFalse; } } regstat = ajFalse; if(!isspecialfile) { regstat = ajRegExec(queryRegFieldId, *Pqry); ajDebug("entry-id regexp: %B\n", regstat); ajDebug("filetest regexp queryRegFieldId: %B '%S'\n", regstat, *Pqry); if(regstat) { ajRegSubI(queryRegFieldId, 1, &queryDb); ajDebug("queryRegFieldId '%S'\n", queryDb); if(queryDbfind(&queryDb, querySvr, findformat, qry, &dbstat, &drcatstat)) { ajRegSubI(queryRegFieldId, 3, &qry->SingleField); ajRegSubI(queryRegFieldId, 4, &braceopen); ajRegSubI(queryRegFieldId, 5, &qry->QryFields); ajRegSubI(queryRegFieldId, 6, &braceclose); ajStrTrimStartC(&braceopen, ":"); } else { ajDebug("failed: not a database or file '%S'\n", queryDb); regstat = ajFalse; } } if(!regstat) { regstat = ajRegExec(queryRegId, *Pqry); ajDebug("entry-id regexp: %B\n", regstat); ajDebug("filetest regexp queryRegId: %B '%S'\n", regstat, *Pqry); if(regstat) { ajRegSubI(queryRegId, 1, &queryDb); ajDebug("queryRegId '%S'\n", queryDb); ajRegSubI(queryRegId, 3, &queryChr); ajRegSubI(queryRegId, 5, &braceopen); ajRegSubI(queryRegId, 6, &qry->QryFields); ajRegSubI(queryRegId, 7, &braceclose); regstat = queryDbfind(&queryDb, querySvr, findformat, qry, &dbstat, &drcatstat); if(!regstat) ajDebug("failed: not a database or file '%S'\n", queryDb); } } if(!regstat) { ajStrAssignS(&queryDb, *Pqry); } } if(svrstat && !dbstat) { ajErr("Server '%S:' but no database name in '%S'", querySvr, *Pqry); return ajFalse; } if(regstat && !dbstat && ajStrMatchC(queryChr, "%")) { ajStrToLong(qry->QryFields, &qry->Fpos); regstat = ajFalse; } /* ** no query found, but test -iquery input */ if(!regstat && ajStrGetLen(textin->QryFields)) { regstat = ajRegExec(queryRegQryField, textin->QryFields); if(regstat) { ajDebug("queryRegQryField\n"); ajRegSubI(queryRegQryField, 1, &qry->SingleField); ajRegSubI(queryRegQryField, 3, &queryChr); ajRegSubI(queryRegQryField, 5, &braceopen); ajRegSubI(queryRegQryField, 6, &qry->QryFields); ajRegSubI(queryRegQryField, 7, &braceclose); } } if(!regstat && ajStrGetLen(textin->QryFields)) { regstat = ajRegExec(queryRegQryId, textin->QryFields); ajDebug("entry-id regexp: %B\n", regstat); ajDebug("filetest regexp queryRegQryId: %B '%S'\n", regstat, textin->QryFields); if(regstat) { ajDebug("queryRegQryId\n"); ajRegSubI(queryRegQryId, 1, &braceopen); ajRegSubI(queryRegQryId, 2, &qry->QryFields); ajRegSubI(queryRegQryId, 3, &braceclose); } } ajDebug("regstat: %B qry '%S' iquery '%S' queryDB: '%S' dbstat: %B " "open: '%S' close: '%S' field: '%S' qryfields: '%S'\n", regstat, *Pqry, textin->QryFields, queryDb, dbstat, braceopen, braceclose, qry->SingleField, qry->QryFields); ajQueryStarclear(qry); if(dbstat) /* we have a database */ { ajStrAssignS(&qry->DbName, queryDb); if(ajStrGetLen(querySvr)) { ajStrAssignS(&qry->SvrName, querySvr); ajDebug("found server '%S' dbname '%S' level: '%S' " "qryfields: '%S'\n", qry->SvrName, qry->DbName, qry->SingleField, qry->QryFields); dbstat = ajNamSvrData(qry, 0); if(!dbstat) { ajStrDel(&qry->QryFields); return ajFalse; } } else { ajDebug("found dbname '%S' level: '%S' qryfields: '%S'\n", qry->DbName, qry->SingleField, qry->QryFields); if(drcatstat) dbstat = ajTrue; else { dbstat = ajNamDbData(qry, 0); if(!dbstat) { ajStrDel(&qry->QryFields); return ajFalse; } } } if(!dbstat) { ajErr("no access method available for '%S'", *Pqry); return ajFalse; } if(findformat(qry->Formatstr, &iformat)) ajStrAssignS(&textin->Formatstr, qry->Formatstr); else { ajErr("unknown format '%S' for '%S' type '%S'\n", qry->Formatstr, *Pqry, qry->DbType); return ajFalse; } } /* ** process the query fields */ ajStrTrimWhite(&qry->QryFields); if(*MAJSTRGETPTR(braceopen)) { if(*MAJSTRGETPTR(braceclose)) inbraces = ajTrue; else { ajErr("Bad query syntax: unclosed braces '%S'", *Pqry); ajStrDel(&qry->QryFields); return ajFalse; } } else { if(*MAJSTRGETPTR(braceclose)) { ajErr("Bad query syntax: unopened braces '%S'", *Pqry); ajStrDel(&qry->QryFields); return ajFalse; } } ajDebug("Query qryfields: '%S' open: '%S' close: '%S' " "inbraces: %B\n", qry->QryFields, braceopen, braceclose, inbraces); ajStrDel(&braceopen); ajStrDel(&braceclose); if(iformat >= 0) { textin->Format = iformat; textin->TextFormat = 0; } else { textin->TextFormat = - iformat; textin->Format = 0; } if(dbstat) ajDebug("database type: '%S' format '%S' %u textformat %u \n", qry->DbType, qry->Formatstr, textin->Format, textin->TextFormat); else ajDebug("file: '%S' format '%S' %u textformat %u \n", queryDb, qry->Formatstr, textin->Format, textin->TextFormat); if(regstat) { if(ajStrGetLen(qry->QryFields)) { ajDebug("file Qry '%S' Field '%S' hasAcc:%B queryChr '%S'\n", qry->QryFields, qry->SingleField, qry->HasAcc, queryChr); if(ajStrGetCharFirst(qry->QryFields) == '!') ajStrInsertK(&qry->QryFields, 0, '*'); ajStrAssignC(&lastoper, "|"); if(ajStrGetLen(qry->SingleField)) /* one specified field */ { ajDebug("query processing: single field '%S' query '%S'\n", qry->SingleField, qry->QryFields); qry->QueryType = AJQUERY_QUERY; if(dbstat) ajNamDbQuery(qry); else ajNamFileQuery(qry); /* ajDebug(" qry->SingleField %S\n", qry->SingleField); */ if(!ajQueryKnownFieldS(qry, qry->SingleField)) { if(dbstat) ajErr("Query '%S' query field '%S' not defined" " for database '%S'", *Pqry, qry->SingleField, qry->DbName); else ajErr("Query '%S' query field '%S' not defined" " for datatype '%s'", *Pqry, qry->SingleField, ajQueryGetDatatype(qry)); return ajFalse; } /* ** in braces we treat spaces as delimiters ** in simple queries they are real */ if(inbraces) ajStrTokenAssignC(&handle, qry->QryFields, "|&!^ \t\n\r"); else ajStrTokenAssignC(&handle, qry->QryFields, "|&!^\t\n\r"); while(ajStrTokenNextParseDelimiters(&handle, &idstr, &operstr)) { if(iquery && (!ajStrGetLen(qry->Qlinks) || (ajStrFindAnyS(qry->Qlinks, lastoper) == -1))) { if(dbstat) ajErr("Query link operator '%S' not supported " "by access method '%S' for database '%S'" " allowed links are '%S'", lastoper, qry->Method, qry->DbName, qry->Qlinks); else ajErr("Query link operator '%S' not supported " "by access method '%S' for datatype '%s'" " allowed links are '%S'", lastoper, qry->Method, ajQueryGetDatatype(qry), qry->Qlinks); return ajFalse; } iquery++; switch(ajStrGetCharFirst(lastoper)) { case '|': ajQueryAddFieldOrS(qry, qry->SingleField, idstr); ajDebug("ajQueryAddFieldOrS.a '%S' '%S'\n", qry->SingleField, idstr); break; case '&': ajQueryAddFieldAndS(qry, qry->SingleField, idstr); ajDebug("ajQueryAddFieldAndS.a '%S' '%S'\n", qry->SingleField, idstr); break; case '!': ajQueryAddFieldNotS(qry, qry->SingleField, idstr); ajDebug("ajQueryAddFieldNotS.a '%S' '%S'\n", qry->SingleField, idstr); break; case '^': ajQueryAddFieldEorS(qry, qry->SingleField, idstr); ajDebug("ajQueryAddFieldEorS.a '%S' '%S'\n", qry->SingleField, idstr); break; default: ajErr("bad query syntax: " "unknown operator '%S'", lastoper); break; } ajStrAssignS(&lastoper, operstr); ajStrTrimWhite(&lastoper); } } else if(ajStrFindAnyK(qry->QryFields, ':') > 0) /* 1+ fields */ { ajDebug("query processing: full query '%S'\n", qry->QryFields); qry->QueryType = AJQUERY_QUERY; if(dbstat) ajNamDbQuery(qry); else ajNamFileQuery(qry); ajStrTokenAssignC(&handle, qry->QryFields, "|&^!"); while(ajStrTokenNextParseDelimiters(&handle, &qrystr, &operstr)) { if(ajStrGetLen(operstr)) { if(!ajStrGetLen(qry->Qlinks) || (ajStrFindAnyS(qry->Qlinks, operstr) == -1)) { if(dbstat) ajErr("Query link operator '%S' not supported " "by access method '%S' for database '%S'" " allowed links are '%S'", operstr, qry->Method, qry->DbName, qry->Qlinks); else ajErr("Query link operator '%S' not supported " "by access method '%S' for datatype '%s'" " allowed links are '%S'", operstr, qry->Method, ajQueryGetDatatype(qry), qry->Qlinks); return ajFalse; } } ajStrTrimWhite(&qrystr); i = ajStrFindAnyK(qrystr, ':'); if(i>0) { ajStrAssignSubS(&qry->SingleField, qrystr, 0, i-1); ajStrAssignSubS(&idstr, qrystr, i+1, -1); ajDebug("qrystr: '%S' field: '%S' qry: '%S'\n", qrystr, qry->SingleField, idstr); } if(!ajQueryKnownFieldS(qry, qry->SingleField)) { if(dbstat) ajErr("Query '%S' query field '%S' not defined" " for database '%S'", *Pqry, qry->SingleField, qry->DbName); else ajErr("Query '%S' query field '%S' not defined" " for datatype '%s'", *Pqry, qry->SingleField, ajQueryGetDatatype(qry)); ajStrDel(&qry->QryFields); return ajFalse; } if(iquery && (!ajStrGetLen(qry->Qlinks) || (ajStrFindAnyS(qry->Qlinks, lastoper) == -1))) { if(dbstat) ajErr("Query link operator '%S' not supported " "by access method '%S' for database '%S'" " allowed links are '%S'", lastoper, qry->Method, qry->DbName, qry->Qlinks); else ajErr("Query link operator '%S' not supported " "by access method '%S' for datatype '%s'" " allowed links are '%S'", lastoper, qry->Method, ajQueryGetDatatype(qry), qry->Qlinks); return ajFalse; } iquery++; switch(ajStrGetCharFirst(lastoper)) { case '|': ajQueryAddFieldOrS(qry, qry->SingleField, idstr); ajDebug("ajQueryAddFieldOrS.b '%S' '%S'\n", qry->SingleField, idstr); break; case '&': ajQueryAddFieldAndS(qry, qry->SingleField, idstr); ajDebug("ajQueryAddFieldAndS.b '%S' '%S'\n", qry->SingleField, idstr); break; case '!': ajQueryAddFieldNotS(qry, qry->SingleField, idstr); ajDebug("ajQueryAddFieldNotS.b '%S' '%S'\n", qry->SingleField, idstr); break; case '^': ajQueryAddFieldEorS(qry, qry->SingleField, idstr); ajDebug("ajQueryAddFieldEorS.b '%S' '%S'\n", qry->SingleField, idstr); break; default: ajErr("bad query syntax: " "unknown operator '%S'", lastoper); break; } ajStrAssignS(&lastoper, operstr); ajStrTrimWhite(&lastoper); } ajStrDel(&qrystr); } else if(ajStrFindAnyC(qry->QryFields, " \t\n\r,;|") >= 0) { ajDebug("query processing: idlist '%S'\n", qry->QryFields); qry->QueryType = AJQUERY_QUERY; if(dbstat) ajNamDbQuery(qry); else ajNamFileQuery(qry); ajStrTokenAssignC(&handle, qry->QryFields, " \t\n\r,;|"); while(ajStrTokenNextParse(&handle, &idstr)) { ajQueryAddFieldOrC(qry, "id", MAJSTRGETPTR(idstr)); ajDebug("ajQueryAddFieldOrC.c '%s' '%S'\n", "id", idstr); if(qry->HasAcc && ajQueryKnownFieldC(qry, "acc")) { ajQueryAddFieldElseC(qry, "acc", MAJSTRGETPTR(idstr)); ajDebug("ajQueryAddFieldElseC.c '%s' '%S'\n", "acc", idstr); } if(ajQueryKnownFieldC(qry, "sv")) { ajQueryAddFieldElseC(qry, "sv", MAJSTRGETPTR(idstr)); ajDebug("ajQueryAddFieldElseC.c '%s' '%S'\n", "sv", idstr); } } } else /* just a simple id */ { ajDebug("query processing: simple id '%S'\n", qry->QryFields); qry->QueryType = AJQUERY_ENTRY; if(dbstat) ajNamDbQuery(qry); else ajNamFileQuery(qry); ajQueryAddFieldOrC(qry, "id", MAJSTRGETPTR(qry->QryFields)); ajDebug("ajQueryAddFieldOrC.d '%s' '%S'\n", "id", qry->QryFields); if(qry->HasAcc && ajQueryKnownFieldC(qry, "acc")) { ajQueryAddFieldElseC(qry, "acc", MAJSTRGETPTR(qry->QryFields)); ajDebug("ajQueryAddFieldElseC.d '%s' '%S'\n", "acc", qry->QryFields); } if(ajQueryKnownFieldC(qry, "sv")) { ajQueryAddFieldElseC(qry, "sv", MAJSTRGETPTR(qry->QryFields)); ajDebug("ajQueryAddFieldElseC.d '%s' '%S'\n", "sv", qry->QryFields); } } ajStrAssignS(&lastoper, operstr); ajStrTrimWhite(&lastoper); } else { qry->QueryType = AJQUERY_ALL; } ajStrTokenDel(&handle); ajStrDel(&idstr); ajStrDel(&qrystr); ajStrDel(&operstr); ajStrDel(&lastoper); } else { qry->QueryType = AJQUERY_ALL; } if(ajQuerySetWild(qry)) { if(qry->QueryType == AJQUERY_ENTRY) qry->QueryType = AJQUERY_QUERY; } if(qry->QueryType == AJQUERY_ALL && ajStrGetLen(qry->Organisms)) { qry->QueryType = AJQUERY_QUERY; if(ajQueryKnownFieldC(qry, "org")) { ajQueryAddFieldOrC(qry, "org", MAJSTRGETPTR(qry->Organisms)); } else if(ajQueryKnownFieldC(qry, "tax")) { ajQueryAddFieldOrC(qry, "tax", MAJSTRGETPTR(qry->Organisms)); } else { if(dbstat) ajErr("Query '%S' query field 'org' or 'tax' not defined" " for database '%S'", *Pqry, qry->DbName); else ajErr("Query '%S' query field 'org' or 'tax' not defined" " for datatype '%s'", *Pqry, ajQueryGetDatatype(qry)); } } if(qry->QueryType == AJQUERY_ALL && ajStrGetLen(qry->Namespace)) { qry->QueryType = AJQUERY_QUERY; if(ajQueryKnownFieldC(qry, "namespace")) { ajQueryAddFieldOrC(qry, "namespace", MAJSTRGETPTR(qry->Namespace)); } else if(ajQueryKnownFieldC(qry, "ns")) { ajQueryAddFieldOrC(qry, "ns", MAJSTRGETPTR(qry->Namespace)); } else { if(dbstat) ajErr("Query '%S' query field 'ns' or 'namespace' not defined" " for database '%S'", *Pqry, qry->DbName); else ajErr("Query '%S' query field 'ns' or 'namespace' not defined" " for datatype '%s'", *Pqry, ajQueryGetDatatype(qry)); } } ajQueryTrace(qry); if(isspecialfile) { if(asisstat) return ajTextinAccessAsis(textin); if(ftpstat) return ajTextinAccessFtp(textin); if(httpstat) return ajTextinAccessHttp(textin); } else if(dbstat) { ajDebug("try TEXT access by method '%S'\n", qry->Method); qry->TextAccess = ajCallTableGetS(textDbMethods,qry->Method); if(!qry->TextAccess) { *Pnontext = ajTrue; return ajTrue; } textaccess = qry->TextAccess; accstat = (*textaccess->Access)(textin); if(accstat) return ajTrue; ajDebug("Database '%S' : access method '%S' failed\n", qry->DbName, qry->Method); return ajFalse; } else if(ajFilenameExists(queryDb)) { ajDebug("found filename %S\n", queryDb); ajStrAssignS(&qry->Filename, queryDb); if(qry->Fpos) { accstat = ajTextinAccessOffset(textin); ajQueryTrace(qry); if(accstat) { ajStrDel(&qry->QryFields); return ajTrue; } } accstat = ajTextinAccessFile(textin); if(accstat) return ajTrue; ajErr("Failed to open filename '%S'", qry->Filename); return ajFalse; } else { ajErr("Failed to open filename '%S'", queryDb); } ajDebug("no valid filename specified\n"); ajDebug("\n"); return ajFalse; } ID queryDbfind TY static MO ajquery LB core XX DE Test a possible database name XX PN [1] PA u Pdb AjPStr* PD Database name PX PN [2] PA r svr const AjPStr PD Server name PX PN [3] PA f findformat AjBool function PD Function to validate format name PX PN [4] PA u qry AjPQuery PD Query object PX PN [5] PA w dbstat AjBool* PD True if database is valid PX PN [6] PA w drcatstat AjBool* PD True if database is found via DRCAT PX RT AjBool RD ajTrue if string is a known file or database name RX // static AjBool queryDbfind(AjPStr *Pdb, const AjPStr svr, AjBool findformat(const AjPStr format, ajint *iformat), AjPQuery qry, AjBool* dbstat, AjBool* drcatstat) { AjBool ret = ajFalse; AjPResource drcat = NULL; *dbstat = ajFalse; *drcatstat = ajFalse; ajDebug("queryDbfind db: '%S' svr: '%S'\n", *Pdb, svr); if(ajRegExec(queryRegDb, *Pdb)) { *dbstat = ajTrue; if(ajStrGetLen(svr)) /* is it a server:dbname */ { if(!ajNamAliasServer(Pdb, svr)) { ajDebug("unknown dbname %S for server %S\n", *Pdb, svr); *dbstat = ajFalse; } } else if(!ajNamAliasDatabase(Pdb)) /* is it a defined dbname */ { ajDebug("not a db '%S'\n", *Pdb); if(ajFilenameExists(*Pdb)) /* existing filename */ { ajDebug("is a file '%S'\n", *Pdb); ret = ajTrue; *dbstat = ajFalse; } else /* search DRCAT */ { drcat = ajResourceNewDrcat(*Pdb); if(drcat) { ajDebug("database '%S' found via DRCAT datatype %u\n", *Pdb, qry->DataType); if(!ajResourceGetDbdata(drcat, qry, findformat)) *dbstat = ajFalse; else { if(qry->DataType == AJDATATYPE_URL) { qry->QryData = drcat; drcat = NULL; } *drcatstat = ajTrue; } ajResourceDel(&drcat); } else { ajDebug("unknown dbname %S, try filename\n", *Pdb); *dbstat = ajFalse; } ajDebug("drcat search: %B '%S'\n", *drcatstat, *Pdb); } } } if(*dbstat || ret) return ajTrue; if(ajFilenameExists(*Pdb)) /* existing filename */ { ajDebug("not a dbname but is a file '%S'\n", *Pdb); ret = ajTrue; *dbstat = ajFalse; } return ret; } ID queryRegInit TY static MO ajquery LB core XX DE Initialised regular expressions for parsing queries XX RT void RD RX // static void queryRegInit(void) { if(queryRegInitDone) return; if(!queryRegFmt) queryRegFmt = ajRegCompC("^([A-Za-z0-9-]*)::(.*)$"); /* \1 format letters and numbers only */ /* \2 remainder (filename, etc.)*/ if(!queryRegSvr) queryRegSvr = ajRegCompC("^([A-Za-z][A-Za-z0-9_.]+):"); /* \1 svrname (start with a letter, then alphanumeric) */ if(!queryRegDb) queryRegDb = ajRegCompC("^[A-Za-z][A-Za-z0-9_.]*[A-Za-z0-9]$"); if(!queryRegQryField) queryRegQryField = ajRegCompC("^([A-Za-z]+)?" "(([:]?)(([{]?)([^}]*)(}?)))?$"); if(!queryRegQryId) queryRegQryId = ajRegCompC("^([{]?)([^}]*)(}?)$"); /* \1 dbname (start with a letter, then alphanumeric) */ /* \2 -id or -acc etc. */ /* \3 qry->SingleField (id or acc etc.) */ /* \4 :qrystring or {qrystring}*/ /* \5 qrystring */ if(!queryRegFieldId) #ifndef WIN32 /* \1 is filename \3 is the qry->SingleField \6 is the qrystring */ queryRegFieldId = ajRegCompC("^(([^{|]+[|])|[^{:]+)" "[:-]([^{:-]+)" "([:{]+)([^}]+)(}?)$"); #else /* Windows file names can start with e.g.: 'C:\' */ /* But allow e.g. 'C:/...', for Staden spin */ /* \1 is filename \3 is the qry->SingleField \6 is the qrystring */ queryRegFieldId = ajRegCompC ("^(([a-zA-Z]:[\\\\/])?[^{:]+)" "[:-]([^{:-]+)" "([:{]+)([^}]+)(}?)$"); #endif if(!queryRegId) #ifndef WIN32 /* \1 is filename \3 is the qry->SingleField \6 is the qry->QryString */ queryRegId = ajRegCompC("^(([^|{]+[|])|[^:{%]*[^:{%-])" "([:%-]?)(({?)([^}]*)(}?))?$"); #else /* Windows file names can start with e.g.: 'C:\' */ /* But allow e.g. 'C:/...', for Staden spin */ /* \1 is filename \6 is the qry->SingleField \7 is the qry->QryString */ queryRegId = ajRegCompC ("^(([a-zA-Z]:[\\\\/])?[^:{%]*[^:{%-])" "([:%-]?)(({?)([^}]*)(}?))?$"); #endif if(!queryRegList) /* \1 is filename \3 is the qry->QryString */ queryRegList = ajRegCompC("^(@|[Ll][Ii][Ss][Tt]:+)(.+)$"); if(!queryRegAsis) /* \1 is filename \3 is the qry->QryString */ queryRegAsis = ajRegCompC("^[Aa][Ss][Ii][Ss]:+(.+)$"); if(!queryRegFtp) /* \1 is filename \3 is the qry->QryString */ queryRegFtp = ajRegCompC("^([Ff][Tt][Pp]:/*.+)$"); if(!queryRegHttp) /* \1 is filename \3 is the qry->QryString */ queryRegHttp = ajRegCompC("^([Hh][Tt][Tt][Pp]:/*.+)$"); if(!queryRegWild) queryRegWild = ajRegCompC("(.*[*].*)"); /* \1 wildcard query */ if(!queryRegRange) /* \1 is rest of USA \2 start \3 end \5 reverse */ queryRegRange = ajRegCompC("(.*)[[](-?[0-9]*):(-?[0-9]*)" "(:([Rr])?)?[]]$"); if(!queryRegRefrange) /* \1 is rest of USA \2 start \4 end \6 rev */ queryRegRefrange = ajRegCompC("(.*):([0-9]*)(:|\\.\\.)([0-9]*)" "(:([Rr]))?$"); queryRegInitDone = ajTrue; return; } ID ajRangeNewI TY public MO ajrange LB core XX DE Default constructor for AJAX range objects. XX PN [1] PA r n ajuint PD number of ranges PX RT AjPRange RD Pointer to a range object RX CA new CT AjPRange CD Default constructor for range objects CX // AjPRange ajRangeNewI(ajuint n) { AjPRange thys; AJNEW0(thys); thys->n = n; if(n > 0) { thys->start = AJALLOC0(n * sizeof (ajuint)); thys->end = AJALLOC0(n * sizeof (ajuint)); thys->text = AJALLOC0(n * sizeof (AjPStr *)); } return thys; } ID ajRangeNewFilename TY public MO ajrange LB core XX DE Load a range object from a file DE DE The format of the range file is: DE Comment lines start with '#' in the first column. DE Comment lines and blank lines are ignored. DE The line may start with white-space. DE There are two positive numbers per line separated by white-space. DE The second number must be greater or equal to the first number. DE There is optional text after the two numbers. DE White-space before or after the text is removed. DE DE e.g.: DE DE # this is my set of ranges DE 12 23 DE 4 5 this is like 12-23, but smaller DE 67 10348 interesting region XX PN [1] PA r name const AjPStr PD range file name PX RT AjPRange RD range object RX CA new CT AjPRange CD Create a range object from a file CX // AjPRange ajRangeNewFilename(const AjPStr name) { return ajRangeNewFilenameLimits(name, 1, UINT_MAX, 0, 0); } ID ajRangeNewFilenameLimits TY public MO ajrange LB core XX DE Load a range object from a file DE DE The format of the range file is: DE Comment lines start with '#' in the first column. DE Comment lines and blank lines are ignored. DE The line may start with white-space. DE There are two positive numbers per line separated by white-space. DE The second number must be greater or equal to the first number. DE There is optional text after the two numbers. DE White-space before or after the text is removed. DE DE e.g.: DE DE # this is my set of ranges DE 12 23 DE 4 5 this is like 12-23, but smaller DE 67 10348 interesting region XX PN [1] PA r name const AjPStr PD range file name PX PN [2] PA r imin ajuint PD Minimum value PX PN [3] PA r imax ajuint PD Maximum value PX PN [4] PA r minsize ajuint PD Minimum number of values PX PN [5] PA r size ajuint PD Required number of values, zero for unlimited PX RT AjPRange RD range object RX CA new CT AjPRange CD Create a range object from a file CX // AjPRange ajRangeNewFilenameLimits(const AjPStr name, ajuint imin, ajuint imax, ajuint minsize, ajuint size) { AjPRange ret = NULL; AjPFile infile = NULL; AjPStr line = NULL; char whiteSpace[] = " \t\n\r"; char notSpace[] = "\n\r"; AjPStrTok tokens; ajuint n = 0U; /* ranges found so far */ ajuint k; ajuint numone; ajuint numtwo; AjPStr one = NULL; AjPStr two = NULL; AjPStr text = NULL; AjPList onelist; AjPList twolist; AjPList textlist; onelist = ajListstrNew(); twolist = ajListstrNew(); textlist = ajListstrNew(); if((infile = ajFileNewInNameS(name)) == NULL) return NULL; while(ajReadlineTrim(infile, &line)) { ajStrTrimWhite(&line); if(!ajStrFindC(line, "#")) /* starts with # (comment line) */ continue; if(!ajStrGetLen(line)) /* empty line */ continue; /* ** parse the numbers out of the line and store in temporary ** list (we may be reading data from stdin, so we can't read ** in once to count the number of ajRange elements, close ** file, open it again and read the data again to populate ** ajRange) */ tokens = ajStrTokenNewC(line, whiteSpace); one = ajStrNew(); ajStrTokenNextParse(&tokens, &one); ajListstrPushAppend(onelist, one); one = NULL; two = ajStrNew(); ajStrTokenNextParse(&tokens, &two); if(ajStrGetLen(two)) { ajListstrPushAppend(twolist, two); two = NULL; } else { ajWarn("Odd integer(s) in range specification:\n%S\n", line); return NULL; } /* get any remaining text and store in temporary list */ text = ajStrNew(); ajStrTokenNextParseC(&tokens, notSpace, &text); ajStrTrimWhite(&text); ajListstrPushAppend(textlist, text); text = NULL; ajStrTokenDel( &tokens); } ajFileClose(&infile); ajStrDel(&line); /* now we know how many pairs of numbers to store, create ajRange object */ n = (ajuint) ajListstrGetLength(onelist); if(size) { if(n != size) { ajWarn("Range specification requires exactly %d pairs", size); return NULL; } } else if (n < minsize) { ajWarn("Range specification requires at least %d pairs", minsize); return NULL; } ret = ajRangeNewI(n); /* populate ajRange object from lists and check numbers are valid */ for(k = 0; k < n; k++) { ajListstrPop(onelist, &one); if(!ajStrToUint(one, &numone)) { ajWarn("Bad range value [%S]",one); ajRangeDel(&ret); return NULL; } ajListstrPop(twolist, &two); if(!ajStrToUint(two, &numtwo)) { ajWarn("Bad range value [%S]",two); ajRangeDel(&ret); return NULL; } ajStrDel(&one); ajStrDel(&two); if(numone > numtwo) { ajWarn("From range [%d] greater than To range [%d]", numone, numtwo); ajRangeDel(&ret); return NULL; } if (numone < imin) { ajWarn("From range [%d] less than minimum [%d]", numone,imin); ajRangeDel(&ret); return NULL; } if (numtwo > imax) { ajWarn("To range [%d] greater than maximum [%d]", numtwo,imax); ajRangeDel(&ret); return NULL; } ret->start[k] = numone; ret->end[k] = numtwo; /* do the text */ ajListstrPop(textlist, &text); ret->text[k] = text; text = NULL; } ajListstrFreeData(&onelist); ajListstrFreeData(&twolist); ajListstrFreeData(&textlist); return ret; } ID ajRangeNewRange TY public MO ajrange LB core XX DE Copy constructor for AJAX range objects. XX PN [1] PA r src const AjPRange PD Source range PX RT AjPRange RD Pointer to a range object RX CA new CT AjPRange CD Copy constructor for range objects CX // AjPRange ajRangeNewRange(const AjPRange src) { AjPRange thys; ajuint i; ajuint n; AJNEW0(thys); n = src->n; thys->n = n; if(src->n > 0) { thys->start = AJALLOC0(n * sizeof (ajuint)); thys->end = AJALLOC0(n * sizeof (ajuint)); thys->text = AJALLOC0(n * sizeof (AjPStr *)); for(i = 0; i < n; i++) { thys->start[i] = src->start[i]; thys->end[i] = src->end[i]; ajStrAssignS(&thys->text[i], src->text[i]); } } return thys; } ID ajRangeNewString TY public MO ajrange LB core XX DE Create a range object from a string XX PN [1] PA r str const AjPStr PD range string PX RT AjPRange RD range object RX CA new CT AjPRange CD Create a range object from a string CX // AjPRange ajRangeNewString(const AjPStr str) { return ajRangeNewStringLimits(str, 1, UINT_MAX, 0, 0); } ID ajRangeNewStringLimits TY public MO ajrange LB core XX DE Create a range object from a string XX PN [1] PA r str const AjPStr PD range string PX PN [2] PA r imin ajuint PD Minimum value PX PN [3] PA r imax ajuint PD Maximum value PX PN [4] PA r minsize ajuint PD Minimum number of values PX PN [5] PA r size ajuint PD Required number of values, zero for unlimited PX RT AjPRange RD range object RX CA new CT AjPRange CD Create a range object from a string CX // AjPRange ajRangeNewStringLimits(const AjPStr str, ajuint imin, ajuint imax, ajuint minsize, ajuint size) { AjPRange ret = NULL; AjPStr c1 = NULL; AjPStr c2 = NULL; AjPStr c3 = NULL; static AjPStr s =NULL; const char *cp; char *p; ajuint n; ajuint e; ajuint f; ajuint t; ajuint i; AjBool doneone = ajFalse; const char *nondigit="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" " \t\n\r!@#$%^&*()_-+=|\\~`{[}]:;\"'<,>.?/"; const char *digit="0123456789"; ajStrAssignS(&s, str); /* clean up the ranges string */ ajStrTrimWhite(&s); /* is this a file of ranges? (does it start with a '@' ?) */ if(*(ajStrGetPtr(s)) == '@') { /* knock off the '@' */ ajStrKeepRange(&s, 1, ajStrGetLen(s)); ret = ajRangeNewFilenameLimits(s, imin, imax, minsize, size); } else { /* get some copies of the string for parsing with strtok */ ajStrAssignS(&c1, s); ajStrAssignS(&c2, s); ajStrAssignS(&c3, s); cp = ajStrGetPtr(c1); p = ajSysFuncStrtok(cp, nondigit); n = 0; if(p) { /* * count the pairs of numbers so that we know the size of * arrays to create */ ++n; while((p = ajSysFuncStrtok(NULL, nondigit))) ++n; if(n % 2) { ajWarn("Odd integer(s) in range specification [%d]",n); return NULL; } if(size) { if(n != size) { ajWarn("Range specification requires exactly %d pairs", size); return NULL; } } else if (n < minsize) { ajWarn("Range specification requires at least %d pairs", minsize); return NULL; } ret = ajRangeNewI((e = n >> 1)); /* get the pairs of numbers and put them in the AjPRange object */ cp = ajStrGetPtr(c2); p = ajSysFuncStrtok(cp, nondigit); if(!sscanf(p,"%u",&f)) { ajWarn("Bad range value [%s]",p); ajRangeDel(&ret); return NULL; } p = ajSysFuncStrtok(NULL, nondigit); if(!sscanf(p, "%u", &t)) { ajWarn("Bad range value [%s]",p); ajRangeDel(&ret); return NULL; } if(f>t) { ajWarn("From range [%d] greater than To range [%d]", f, t); ajRangeDel(&ret); return NULL; } if (f < imin) { ajWarn("From range [%d] less than minimum [%d]", f, imin); ajRangeDel(&ret); return NULL; } if (t > imax) { ajWarn("To range [%d] greater than maximum [%d]", t, imax); ajRangeDel(&ret); return NULL; } ret->start[0] = f; ret->end[0] = t; for(i = 1; i < e; ++i) { p = ajSysFuncStrtok(NULL, nondigit); if(!sscanf(p, "%u", &f)) { ajWarn("Bad range value [%s]", p); ajRangeDel(&ret); return NULL; } p = ajSysFuncStrtok(NULL, nondigit); if(!sscanf(p, "%u", &t)) { ajWarn("Bad range value [%s]", p); ajRangeDel(&ret); return NULL; } if(f > t) { ajWarn("From range [%d] greater than To range [%d]", f, t); ajRangeDel(&ret); return NULL; } ret->start[i] = f; ret->end[i] = t; } /* now get any strings after the pairs of ranges */ cp = ajStrGetPtr(c3); if(!isdigit((ajint) *cp)) { doneone = ajTrue; p = ajSysFuncStrtok(cp, digit); } for(i = 0; i < e; ++i) { /* ignore anything between the two numbers */ if(!doneone) { p = ajSysFuncStrtok(cp, digit); doneone = ajTrue; } else p = ajSysFuncStrtok(NULL, digit); /* this must be the text after the pair of numbers */ /* get the string after the two numbers */ p = ajSysFuncStrtok(NULL, digit); if(p) { ajStrAssignC(&(ret->text[i]), p); ajStrTrimWhite(&(ret->text[i])); } } } else { if(size) { ajWarn("Range specification requires exactly %d pairs", size); return NULL; } else if (0 < minsize) { ajWarn("Range specification requires at least %d pairs", minsize); return NULL; } ret = ajRangeNewI(0); } ajStrDel(&c1); ajStrDel(&c2); ajStrDel(&c3); } ajStrDel(&s); return ret; } ID ajRangeDel TY public MO ajrange LB core XX DE Default destructor for AJAX range objects. XX PN [1] PA d thys AjPRange* PD range structure PX RT void RD RX CA delete CT AjPRange CD Default destructor for range objects CX // void ajRangeDel(AjPRange *thys) { ajuint i; if(!*thys) return; if((*thys)->n > 0) { AJFREE((*thys)->start); AJFREE((*thys)->end); for(i = 0; i < (*thys)->n; i++) ajStrDel(&(*thys)->text[i]); } AJFREE((*thys)->text); AJFREE(*thys); return; } ID ajRangeGetSize TY public MO ajrange LB core XX DE Return the number of ranges in a range object XX PN [1] PA r thys const AjPRange PD range object PX RT ajuint RD number of ranges RX CA use CT AjPRange CD Return the number of ranges in a range object CX // ajuint ajRangeGetSize(const AjPRange thys) { return thys->n; } ID ajRangeElementGetText TY public MO ajrange LB core XX DE Return (as parameters) text value of a range element DE DE The text value of a range is any non-digit after the pair of range numbers DE e.g. in a pair of range '10-20 potential exon 50-60 repeat' DE the text values of the two ranges are: 'potential exon' and 'repeat' XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r element ajuint PD range element (0 to n-1) PX PN [3] PA w text AjPStr* PD text value PX RT AjBool RD true if range exists RX CA use CT AjPRange CD Return (as parameters) text value of a range CX // AjBool ajRangeElementGetText(const AjPRange thys, ajuint element, AjPStr * text) { if(element >= thys->n) return ajFalse; if(thys->text[element]) ajStrAssignS(text, thys->text[element]); else ajStrAssignClear(text); return ajTrue; } ID ajRangeElementGetValues TY public MO ajrange LB core XX DE Return (as parameters) start and end values in a range XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r element ajuint PD range element (0 to n-1) PX PN [3] PA w start ajuint* PD start value PX PN [4] PA w end ajuint* PD end value PX RT AjBool RD true if range exists RX CA use CT AjPRange CD Return (as parameters) start and end values in a range CX // AjBool ajRangeElementGetValues(const AjPRange thys, ajuint element, ajuint *start, ajuint *end) { if(element >= thys->n) return ajFalse; if(thys->start[element] < 1) return ajFalse; if(thys->end[element] < 1) return ajFalse; if(thys->start[element] > thys->end[element]) return ajFalse; *start = thys->start[element]; *end = thys->end[element]; return ajTrue; } ID ajRangeElementSet TY public MO ajrange LB core XX DE Set the values of a start and end in a (pre-existing) range element XX PN [1] PA w thys AjPRange PD range object PX PN [2] PA r element ajuint PD range element (0 to n-1) PX PN [3] PA r start ajuint PD start value PX PN [4] PA r end ajuint PD end value PX RT AjBool RD true if range exists RX CA modify CT AjPRange CD Set the values of a start and end in a range element CX // AjBool ajRangeElementSet(AjPRange thys, ajuint element, ajuint start, ajuint end) { if(element >= thys->n) return ajFalse; thys->start[element] = start; thys->end[element] = end; return ajTrue; } ID ajRangeSetOffset TY public MO ajrange LB core XX DE Sets the range values offset relative to the Begin value. DE DE Used when a sequence has -sbegin= and -send= parameters set DE and we have extracted the specified subsequence. DE So if -sbeg 11 has been set and the range is 11-12 DE the resulting range is changed to 1-2 XX PN [1] PA u thys AjPRange PD range object PX PN [2] PA r begin ajuint PD begin parameter obtained from ajSeqGetBegin(seq) PX RT AjBool RD true if region values modified RX CA modify CT AjPRange CD Sets the range values relative to the Begin value CX // AjBool ajRangeSetOffset(AjPRange thys, ajuint begin) { ajuint nr; ajuint i; ajuint st; ajuint en; AjBool result = ajFalse; nr = thys->n; for(i = 0; i < nr; i++) { if(begin > 1) result = ajTrue; ajRangeElementGetValues(thys, i, &st, &en); st -= begin - 1; en -= begin - 1; ajRangeElementSet(thys, i, st, en); } return result; } ID ajRangeSeqExtractList TY public MO ajrange LB core XX DE Extract the range from a sequence and place the resulting text in a DE list of strings. DE DE N.B. the resulting list will be regions of the input sequence listed DE in the order specified in the set of ranges. If these are not in ascending DE order, the resulting list of strings will not be in ascending order either. XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r seq const AjPSeq PD sequence to extract from PX PN [3] PA w outliststr AjPList PD resulting list of strings PX RT AjBool RD true if result is not the whole sequence RX CA use CT AjPRange CD PushApp substrings defined by range onto list CX // AjBool ajRangeSeqExtractList(const AjPRange thys, const AjPSeq seq, AjPList outliststr) { ajuint nr; ajuint i; ajuint st; ajuint en; AjBool result = ajFalse; AjPStr str; nr = thys->n; if(nr) { for(i = 0; i < nr; i++) { result = ajTrue; if(!ajRangeElementGetValues(thys, i, &st, &en)) continue; str = ajStrNew(); ajStrAppendSubS(&str, ajSeqGetSeqS(seq), st - 1, en - 1); ajListstrPushAppend(outliststr, str); } } else { str = ajSeqGetSeqCopyS(seq); ajListstrPushAppend(outliststr, str); } return result; } ID ajRangeSeqExtract TY public MO ajrange LB core XX DE Extract the range from a sequence (Remove regions not in the range(s)) DE N.B. the resulting sequence will be regions of the original concatenated DE in the order specified in the set of ranges. If these are not in ascending DE order, the resulting sequence will not be in position order either. XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA u seq AjPSeq PD Sequence PX RT AjBool RD true if sequence was modified RX CA use CT AjPRange CD Extract substrings defined by range CX // AjBool ajRangeSeqExtract(const AjPRange thys, AjPSeq seq) { ajuint nr; ajuint i; ajuint st; ajuint en; AjBool result = ajFalse; AjPStr outstr = NULL; nr = thys->n; ajDebug("ajRangeSeqExtract Number:%d\n", nr); if (nr) { for(i = 0; i < nr; i++) { result = ajTrue; if(!ajRangeElementGetValues(thys, i, &st, &en)) continue; ajStrAppendSubS(&outstr, ajSeqGetSeqS(seq), st - 1, en - 1); ajDebug("Range [%d] %d..%d '%S'\n", i, st, en, outstr); } ajSeqAssignSeqS(seq, outstr); ajStrDel(&outstr); } return result; } ID ajRangeSeqExtractPep TY public MO ajrange LB core XX DE Extract the range from a sequence (Remove regions not in the range(s)) DE and translate to protein. DE DE N.B. the resulting sequence will be regions of the original concatenated DE in the order specified in the set of ranges. If these are not in ascending DE order, the resulting sequence will not be in position order either. XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA u seq AjPSeq PD Sequence PX PN [3] PA r trntable const AjPTrn PD Translation table PX PN [4] PA r frame ajint PD Reading frame 1..3 or -1..-3 PX RT AjPSeq RD Translated protein sequence RX CA use CT AjPRange CD Extract substrings defined by range CX // AjPSeq ajRangeSeqExtractPep(const AjPRange thys, AjPSeq seq, const AjPTrn trntable, ajint frame) { ajuint nr; ajuint i; ajuint st; ajuint en; AjPStr outstr = NULL; AjPSeq pepseq = NULL; AjPStr pepstr = NULL; ajuint pos = 0; ajuint lastpos = 0; ajuint npos = 0; ajuint rlen = 0; ajuint shift = 0; nr = thys->n; if (!nr) return NULL; if(frame > 0) { for(i = 0; i < nr; i++) { if(!ajRangeElementGetValues(thys, i, &st, &en)) continue; ajStrAppendSubS(&outstr, ajSeqGetSeqS(seq), st - 1, en - 1); } ajSeqAssignSeqS(seq, outstr); ajStrAssignClear(&outstr); pepseq = ajTrnSeqOrig(trntable, seq, 1); ajStrAssignS(&pepstr, ajSeqGetSeqS(pepseq)); for(i = 0; i < nr; i++) { if(!ajRangeElementGetValues(thys, i, &st, &en)) continue; shift = npos % 3; rlen = (en - st) + 1; npos += rlen; pos = (npos - 1) / 3; if(frame % 3 == (((ajint) st - (ajint) shift) % 3)) { if(pos >= lastpos) ajStrAppendSubS(&outstr, pepstr, lastpos, pos); } if(pos >= lastpos) lastpos = pos + 1; else lastpos = pos; } ajSeqAssignSeqS(pepseq, outstr); ajStrDel(&pepstr); ajStrDel(&outstr); } else if (frame < 0) { for(i = nr; i > 0; i--) { if(!ajRangeElementGetValues(thys, i - 1, &st, &en)) continue; ajStrAppendSubS(&outstr, ajSeqGetSeqS(seq), st - 1, en - 1); } ajSeqAssignSeqS(seq, outstr); ajStrAssignClear(&outstr); pepseq = ajTrnSeqOrig(trntable, seq, -1); ajStrAssignS(&pepstr, ajSeqGetSeqS(pepseq)); for(i = 0; i < nr; i++) { if(!ajRangeElementGetValues(thys, i, &st, &en)) continue; rlen = (en - st) + 1; npos += rlen; pos = (npos - 1) / 3; if((-frame) % 3 == (((ajint) st) % 3)) { if(pos >= lastpos) ajStrAppendSubS(&outstr, pepstr, lastpos, pos); } if(pos >= lastpos) lastpos = pos + 1; else lastpos = pos; } ajSeqAssignSeqS(pepseq, outstr); ajStrDel(&outstr); } return pepseq; } ID ajRangeSeqStuff TY public MO ajrange LB core XX DE The opposite of ajRangeSeqExtract() DE Stuff space characters into a string to pad out to the range. DE DE It takes a string and an ordered, non-overlapping set of ranges and puts DE spaces into the string between the ranges. DE So starting with the string 'abcde' and the ranges 3-5,7-8 it will produce: DE ' abc de' XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA u seq AjPSeq PD Sequence to be modified PX RT AjBool RD true if sequence was modified RX CA use CT AjPRange CD The opposite of ajRangeSeqExtract CX // AjBool ajRangeSeqStuff(const AjPRange thys, AjPSeq seq) { ajuint nr; ajuint i; ajuint j; ajuint lasten = 0; ajuint lastst = 0; ajuint len; ajuint st; ajuint en; AjBool result = ajFalse; AjPStr outstr = NULL; nr = thys->n; if(nr) { for(i = 0; i < nr; i++) { result = ajTrue; if(!ajRangeElementGetValues(thys, i, &st, &en)) continue; /* change range positions to string positions */ --st; --en; len = en - st; for(j = lasten; j < st; j++) ajStrAppendC(&outstr, " "); ajStrAppendSubS(&outstr, ajSeqGetSeqS(seq), lastst, lastst + len); lastst = lastst + len + 1; lasten = en + 1; } ajSeqAssignSeqS(seq, outstr); ajStrDel(&outstr); } return result; } ID ajRangeSeqStuffPep TY public MO ajrange LB core XX DE The opposite of ajRangeSeqExtractPep() DE Stuff space characters into a translated string to pad out to the range. DE DE It takes a string and an ordered, non-overlapping set of ranges and puts DE spaces into the string between the ranges. DE So starting with the string 'abcde' and the ranges 3-5,7-8 it will produce: DE ' abc de' XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA u seq AjPSeq PD Sequence to be modified PX PN [3] PA r frame ajint PD Reading frame 1..3 or -1..-3 PX RT AjBool RD true if sequence was modified RX CA use CT AjPRange CD The opposite of ajRangeSeqExtract CX // AjBool ajRangeSeqStuffPep(const AjPRange thys, AjPSeq seq, ajint frame) { ajuint nr; ajuint i; ajuint j; ajuint lasten = 0; ajuint lastst = 0; ajuint len; ajuint st; ajuint en; ajuint nbases = 0; ajuint shift = 0; AjBool result = ajFalse; AjPStr outstr = NULL; ajint lastframe = 0; nr = thys->n; if(!nr) return ajFalse; if(frame > 0) { for(i = 0; i < nr; i++) { result = ajTrue; if(!ajRangeElementGetValues(thys, i, &st, &en)) continue; shift = nbases%3; nbases += en - st + 1; if((frame) % 3 != (((ajint) st - (ajint) shift) % 3)) continue; /* change range positions to string positions */ --st; --en; len = en - st; ajDebug("lasten:%u st:%u shift:%u frame:%d lastframe:%d\n", lasten, st, shift, frame, lastframe); for(j = lasten; j < st; j++) ajStrAppendC(&outstr, " "); if(shift && (frame != lastframe)) for(j = 3; j > shift; j--) ajStrAppendC(&outstr, " "); ajStrAppendSubS(&outstr, ajSeqGetSeqS(seq), lastst, lastst + len); lastst = lastst + len + 1; lasten = en + 1; lastframe = frame; } ajSeqAssignSeqS(seq, outstr); ajStrDel(&outstr); } else if (frame < 0) { ajSeqReverseOnly(seq); for(i = nr; i > 0; i--) { result = ajTrue; if(!ajRangeElementGetValues(thys, i - 1, &st, &en)) continue; if((-frame) % 3 != (((ajint) st) % 3)) continue; /* change range positions to string positions */ --st; --en; len = en - st; for(j = lasten; j < st; j++) ajStrAppendC(&outstr, " "); ajStrAppendSubS(&outstr, ajSeqGetSeqS(seq), lastst, lastst + len); lastst = lastst + len + 1; lasten = en + 1; } ajSeqAssignSeqS(seq, outstr); ajStrDel(&outstr); } return result; } ID ajRangeSeqMask TY public MO ajrange LB core XX DE Mask the range in a String XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r maskchar const AjPStr PD character to mask with PX PN [3] PA u seq AjPSeq PD sequence to be masked PX RT AjBool RD true if string modified RX CA use CT AjPRange CD Mask the range in a String CX // AjBool ajRangeSeqMask(const AjPRange thys, const AjPStr maskchar, AjPSeq seq) { ajuint nr; ajuint i; ajuint j; ajuint jj; ajuint st; ajuint en; AjBool result = ajFalse; AjPStr str = NULL; nr = thys->n; if (nr) { for(i = 0; i < nr; ++i) { result = ajTrue; if(!ajRangeElementGetValues(thys, i, &st, &en)) continue; /* change range positions to string positions */ --st; --en; /* cut out the region */ ajStrCutRange(&str, st, en); /* replace the region with the mask character */ for(j = st; j <= en; ++j) ajStrInsertS(&str, st, maskchar); } ajSeqAssignSeqS(seq, str); ajStrDel(&str); } else { str = ajStrNew(); for(jj = 0; jj <= ajStrGetLen(str); ++jj) ajStrInsertS(&str, jj, maskchar); ajSeqAssignSeqS(seq, str); ajStrDel(&str); } return result; } ID ajRangeSeqToLower TY public MO ajrange LB core XX DE Change the range in a String to lower-case XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA u seq AjPSeq PD sequence to be lower-cased PX RT AjBool RD true if sequence was modified RX CA use CT AjPRange CD Change to lower-case the range in a sequence CX // AjBool ajRangeSeqToLower(const AjPRange thys, AjPSeq seq) { ajuint nr; ajuint i; ajuint st; ajuint en; AjBool result = ajFalse; AjPStr substr = NULL; AjPStr str = NULL; const AjPStr seqstr; nr = thys->n; if (nr) { substr = ajStrNew(); str = ajStrNew(); seqstr = ajSeqGetSeqS(seq); for(i = 0; i < nr; ++i) { if(!ajRangeElementGetValues(thys, i, &st, &en)) continue; /* change range positions to string positions */ --st; --en; /* extract the region and lowercase */ ajStrAppendSubS(&substr, seqstr, st, en); ajStrFmtLower(&substr); /* remove and replace the lowercased region */ ajStrCutRange(&str, st, en); ajStrInsertS(&str, st, substr); ajStrSetClear(&substr); } ajStrDel(&substr); } else ajSeqFmtLower(seq); return result; } ID ajRangeStrExtractList TY public MO ajrange LB core XX DE Extract the range from a String and place the resulting text in a DE list of strings. DE DE N.B. the resulting list will be regions of the input string listed DE in the order specified in the set of ranges. If these are not in ascending DE order, the resulting list of strings will not be in ascending order either. XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r instr const AjPStr PD string to extract from PX PN [3] PA w outliststr AjPList PD resulting list of strings PX RT AjBool RD true if string modified RX CA use CT AjPRange CD PushApp substrings defined by range onto list CX // AjBool ajRangeStrExtractList(const AjPRange thys, const AjPStr instr, AjPList outliststr) { ajuint nr; ajuint i; ajuint st; ajuint en; AjBool result = ajFalse; AjPStr str; nr = thys->n; for(i = 0; i < nr; i++) { result = ajTrue; ajRangeElementGetValues(thys, i, &st, &en); str = ajStrNew(); ajStrAppendSubS(&str, instr, st - 1, en - 1); ajListstrPushAppend(outliststr, str); } return result; } ID ajRangeStrExtract TY public MO ajrange LB core XX DE Extract the range from a String (Remove regions not in the range(s)) DE DE N.B. the resulting string will be regions of the input string appended DE in the order specified in the set of ranges. If these are not in ascending DE order, the resulting string will not be in ascending order either. DE DE Intended for use in extracting exon ranges from the sequence of DE a transcript. XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r instr const AjPStr PD string to extract from PX PN [3] PA w outstr AjPStr* PD resulting extracted string PX RT AjBool RD true if string modified RX CA use CT AjPRange CD Extract substrings defined by range CX // AjBool ajRangeStrExtract(const AjPRange thys, const AjPStr instr, AjPStr *outstr) { ajuint nr; ajuint i; ajuint st; ajuint en; AjBool result = ajFalse; nr = thys->n; ajDebug("ajRangeStrExtract Number:%d\n", nr); if (nr) for(i = 0; i < nr; i++) { result = ajTrue; ajRangeElementGetValues(thys, i, &st, &en); ajStrAppendSubS(outstr, instr, st - 1, en - 1); ajDebug("Range [%d] %d..%d '%S'\n", i, st, en, *outstr); } else ajStrAssignS(outstr, instr); return result; } ID ajRangeStrStuff TY public MO ajrange LB core XX DE The opposite of ajRangeStrExtract() DE Stuff space characters into a string to pad out to the range. DE DE It takes a string and an ordered, non-overlapping set of ranges and puts DE spaces into the string between the ranges. DE So starting with the string 'abcde' and the ranges 3-5,7-8 it will produce: DE ' abc de' XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r instr const AjPStr PD string to stuff PX PN [3] PA w outstr AjPStr* PD resulting stuffed string PX RT AjBool RD true if string modified RX CA use CT AjPRange CD The opposite of ajRangeStrExtract CX // AjBool ajRangeStrStuff(const AjPRange thys, const AjPStr instr, AjPStr *outstr) { ajuint nr; ajuint i; ajuint j; ajuint lasten = 0; ajuint lastst = 0; ajuint len; ajuint st; ajuint en; AjBool result = ajFalse; nr = thys->n; for(i = 0; i < nr; i++) { result = ajTrue; ajRangeElementGetValues(thys, i, &st, &en); /* change range positions to string positions */ --st; --en; len = en - st; for(j = lasten; j < st; j++) ajStrAppendC(outstr, " "); ajStrAppendSubS(outstr, instr, lastst, lastst + len); lastst = lastst + len + 1; lasten = en + 1; } return result; } ID ajRangeStrMask TY public MO ajrange LB core XX DE Mask the positions defined by a range in a string XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r maskchar const AjPStr PD character to mask with PX PN [3] PA w str AjPStr* PD string to be masked PX RT AjBool RD true if string modified RX CA use CT AjPRange CD Mask the range in a String CX // AjBool ajRangeStrMask(const AjPRange thys, const AjPStr maskchar, AjPStr *str) { ajuint nr; ajuint i; ajuint j; ajuint st; ajuint en; AjBool result = ajFalse; nr = thys->n; for(i = 0; i < nr; ++i) { result = ajTrue; ajRangeElementGetValues(thys, i, &st, &en); /* change range positions to string positions */ --st; --en; /* cut out the region */ ajStrCutRange(str, st, en); /* replace the region with the mask character */ for(j = st; j <= en; ++j) ajStrInsertS(str, st, maskchar); } return result; } ID ajRangeStrToLower TY public MO ajrange LB core XX DE Change positions defined by the range in a string to lower-case XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA w str AjPStr* PD string to be lower-cased PX RT AjBool RD true if string modified RX CA use CT AjPRange CD Change to lower-case the range in a String CX // AjBool ajRangeStrToLower(const AjPRange thys, AjPStr *str) { ajuint nr; ajuint i; ajuint st; ajuint en; AjBool result = ajFalse; AjPStr substr; substr = ajStrNew(); nr = thys->n; for(i = 0; i < nr; ++i) { result = ajTrue; ajRangeElementGetValues(thys, i, &st, &en); /* change range positions to string positions */ --st; --en; /* extract the region and lowercase */ ajStrAppendSubS(&substr, *str, st, en); ajStrFmtLower(&substr); /* remove and replace the lowercased region */ ajStrCutRange(str, st, en); ajStrInsertS(str, st, substr); ajStrSetClear(&substr); } ajStrDel(&substr); return result; } ID ajRangeElementTypeOverlap TY public MO ajrange LB core XX DE Detect an overlap of a single range element to a region of a sequence XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r element ajuint PD range element (0 to n-1) PX PN [3] PA r pos ajuint PD position in sequence of start of region of sequence PX PN [4] PA r length ajuint PD length of region of sequence PX RT ajuint RD 0=no overlap 1=internal 2=complete 3=at left 4=at right RX // ajuint ajRangeElementTypeOverlap(const AjPRange thys, ajuint element, ajuint pos, ajuint length) { ajuint start; ajuint end; ajuint posend; if(element >= thys->n) return 0; if(thys->start[element] < 1) return 0; if(thys->end[element] < 1) return 0; if(thys->start[element] > thys->end[element]) return 0; start = thys->start[element]; end = thys->end[element]; /* end position of region in sequence */ posend = pos + length - 1; /* convert range positions to sequence positions */ start--; end--; if(end < pos || start > posend) return 0; /* no overlap ~~~~ |--------| */ if(start >= pos && end <= posend) return 1; /* internal overlap |-~~~~~--| */ if(start < pos && end > posend) return 2; /* complete overlap ~~~~~|~~~~~~~~|~~ */ if(start < pos && end >= pos ) return 3; /* overlap at left ~~~~~|~~~-----| */ if(start >= pos && end > posend ) return 4; /* overlap at right |----~~~~|~~~ */ ajFatal("ajRangeElementTypeOverlap error"); return -1; } ID ajRangeCountOverlaps TY public MO ajrange LB core XX DE Detect overlaps of a set of ranges to a region of a sequence XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r pos ajuint PD position in sequence of start of region of sequence PX PN [3] PA r length ajuint PD length of region of sequence PX RT ajuint RD Number of ranges in range object with overlaps RD to the region RX CA use CT AjPRange CD Detect overlaps of a set of ranges to a seq region CX // ajuint ajRangeCountOverlaps(const AjPRange thys, ajuint pos, ajuint length) { ajuint nr; ajuint i; ajuint result = 0; nr = thys->n; for(i = 0; i < nr; i++) { if(ajRangeElementTypeOverlap(thys, i, pos, length)) result++; } return result; } ID ajRangeIsOrdered TY public MO ajrange LB core XX DE Tests to see if the set of ranges are in ascending non-overlapping order XX PN [1] PA r thys const AjPRange PD range object PX RT AjBool RD ajTrue if in ascending non-overlapping order RX CA use CT AjPRange CD Test if ranges are in ascending non-overlapping order CX // AjBool ajRangeIsOrdered(const AjPRange thys) { ajuint nr; ajuint i; ajuint st; ajuint en; ajuint last = 0; nr = thys->n; for(i = 0; i < nr; i++) { ajRangeElementGetValues(thys, i, &st, &en); ajDebug("ajRangeOrdered [%u] st:%u en:%u (last:%u)\n", i, st, en, last); if(st <= last || en <= st) return ajFalse; last = en; } return ajTrue; } ID ajRangeIsWhole TY public MO ajrange LB core XX DE Test whether the default range is used for a sequence DE DE The test is whether the given range is a single range from the start to DE the end of a sequence string. XX PN [1] PA r thys const AjPRange PD range object PX PN [2] PA r s const AjPSeq PD sequence PX RT AjBool RD true if default range RX CA use CT AjPRange CD Test if the default range has been set CX // AjBool ajRangeIsWhole(const AjPRange thys, const AjPSeq s) { /* test the range - 1..end or empty means whole sequence */ if(thys->n == 0) { ajDebug("ajRangeDefault n:%d begin:%u end:%u\n", thys->n, ajSeqGetBegin(s), ajSeqGetEnd(s)); return ajTrue; } ajDebug("ajRangeDefault n:%d start:%d end:%d begin:%u end:%u\n", thys->n, thys->start[0], thys->end[0], ajSeqGetBegin(s), ajSeqGetEnd(s)); if(thys->n == 1 && thys->start[0] == ajSeqGetBegin(s) && thys->end[0] == ajSeqGetEnd(s)) return ajTrue; return ajFalse; } ID ajRefseqNew TY public MO ajrefseq LB core XX DE Refseq data constructor XX RT AjPRefseq RD New object RX // AjPRefseq ajRefseqNew(void) { AjPRefseq ret; AJNEW0(ret); return ret; } ID ajRefseqDel TY public MO ajrefseq LB core XX DE Reference sequence data destructor XX PN [1] PA d Prefseq AjPRefseq* PD Reference sequence data object PD to delete PX RT void RD RX // void ajRefseqDel(AjPRefseq *Prefseq) { AjPRefseq refseq; AjPSeqRange tmprange; if(!Prefseq) return; if(!(*Prefseq)) return; refseq = *Prefseq; ajStrDel(&refseq->Id); ajStrDel(&refseq->Db); ajStrDel(&refseq->Setdb); ajStrDel(&refseq->Full); ajStrDel(&refseq->Qry); ajStrDel(&refseq->Formatstr); ajStrDel(&refseq->Filename); ajStrDel(&refseq->TextPtr); ajStrDel(&refseq->Desc); ajStrDel(&refseq->Seq); while(ajListPop(refseq->Seqlist,(void **)&tmprange)) ajSeqrangeDel(&tmprange); ajListFree(&refseq->Seqlist); AJFREE(*Prefseq); *Prefseq = NULL; return; } ID ajRefseqGetDb TY public MO ajrefseq LB core XX DE Return the database name XX PN [1] PA r refseq const AjPRefseq PD Refseq PX RT const AjPStr RD Database name RX // const AjPStr ajRefseqGetDb(const AjPRefseq refseq) { return refseq->Db; } ID ajRefseqGetId TY public MO ajrefseq LB core XX DE Return the identifier XX PN [1] PA r refseq const AjPRefseq PD Refseq PX RT const AjPStr RD Returned id RX // const AjPStr ajRefseqGetId(const AjPRefseq refseq) { return refseq->Id; } ID ajRefseqGetQryC TY public MO ajrefseq LB core XX DE Returns the query string of a reference sequence data object. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r refseq const AjPRefseq PD Reference sequence data object. PX RT const char* RD Query as a character string. RX // const char* ajRefseqGetQryC(const AjPRefseq refseq) { return MAJSTRGETPTR(ajRefseqGetQryS(refseq)); } ID ajRefseqGetQryS TY public MO ajrefseq LB core XX DE Returns the query string of a reference sequence data object. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r refseq const AjPRefseq PD Reference sequence data object. PX RT const AjPStr RD Query as a string. RX // const AjPStr ajRefseqGetQryS(const AjPRefseq refseq) { ajDebug("ajRefseqGetQryS '%S'\n", refseq->Qry); if(ajStrGetLen(refseq->Qry)) return refseq->Qry; refseqMakeQry(refseq, &refseqTempQry); return refseqTempQry; } ID refseqMakeQry TY static MO ajrefseq LB core XX DE Sets the query for a reference sequence data object. XX PN [1] PA r thys const AjPRefseq PD Reference sequence data object PX PN [2] PA w qry AjPStr* PD Query string in full PX RT void RD RX // static void refseqMakeQry(const AjPRefseq thys, AjPStr* qry) { ajDebug("refseqMakeQry (Id <%S> Formatstr <%S> Db <%S> " "Filename <%S>)\n", thys->Id, thys->Formatstr, thys->Db, thys->Filename); /* ajRefseqTrace(thys); */ if(ajStrGetLen(thys->Db)) ajFmtPrintS(qry, "%S-id:%S", thys->Db, thys->Id); else { ajFmtPrintS(qry, "%S::%S:%S", thys->Formatstr, thys->Filename,thys->Id); } ajDebug(" result: <%S>\n", *qry); return; } ID ajRefseqClear TY public MO ajrefseq LB core XX DE Resets all data for a reference sequence data object so that it can DE be reused. XX PN [1] PA u refseq AjPRefseq PD Reference sequence data object PX RT void RD RX // void ajRefseqClear(AjPRefseq refseq) { AjPSeqRange tmprange = NULL; if(MAJSTRGETLEN(refseq->Id)) ajStrSetClear(&refseq->Id); if(MAJSTRGETLEN(refseq->Db)) ajStrSetClear(&refseq->Db); if(MAJSTRGETLEN(refseq->Setdb)) ajStrSetClear(&refseq->Setdb); if(MAJSTRGETLEN(refseq->Full)) ajStrSetClear(&refseq->Full); if(MAJSTRGETLEN(refseq->Qry)) ajStrSetClear(&refseq->Qry); if(MAJSTRGETLEN(refseq->Formatstr)) ajStrSetClear(&refseq->Formatstr); if(MAJSTRGETLEN(refseq->Filename)) ajStrSetClear(&refseq->Filename); ajStrDel(&refseq->TextPtr); ajStrDel(&refseq->Desc); ajStrDel(&refseq->Seq); while(ajListPop(refseq->Seqlist,(void **)&tmprange)) ajSeqrangeDel(&tmprange); ajListFree(&refseq->Seqlist); refseq->Count = 0; refseq->Fpos = 0L; refseq->Format = 0; return; } ID ajRefseqExit TY public MO ajrefseq LB core XX DE Cleans up reference sequence processing internal memory XX RT void RD RX // void ajRefseqExit(void) { ajRefseqinExit(); ajRefseqoutExit(); return; } ID refseqAccess TY list MO ajrefseqdb LB ajaxdb XX DE Functions to access each database or reference sequence access method XX // static AjORefseqAccess refseqAccess[] = { /* Name AccessFunction FreeFunction Qlink Description Alias Entry Query All Chunk Padding */ { NULL, NULL, NULL, NULL, NULL, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE }, { NULL, NULL, NULL, NULL, NULL, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE }, }; ID ajRefseqdbInit TY public MO ajrefseqdb LB ajaxdb XX DE Initialise reference sequence database internals XX RT void RD RX // void ajRefseqdbInit(void) { AjPTable table; ajuint i = 0; table = ajRefseqaccessGetDb(); while(refseqAccess[i].Name) { ajCallTableRegister(table, refseqAccess[i].Name, (void*) &refseqAccess[i]); i++; } return; } ID ajRefseqdbPrintAccess TY public MO ajrefseqdb LB ajaxdb XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajRefseqdbPrintAccess(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Reference sequence access methods\n"); ajFmtPrintF(outf, "# Name Alias Entry Query All Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "method {\n"); for(i=0; refseqAccess[i].Name; i++) if(full || !refseqAccess[i].Alias) ajFmtPrintF(outf, " %-10s %5B %5B %5B %5B \"%s\"\n", refseqAccess[i].Name, refseqAccess[i].Alias, refseqAccess[i].Entry, refseqAccess[i].Query, refseqAccess[i].All, refseqAccess[i].Desc); ajFmtPrintF(outf, "}\n\n"); return; } ID ajRefseqdbExit TY public MO ajrefseqdb LB ajaxdb XX DE Cleans up reference sequence database processing internal memory XX RT void RD RX // void ajRefseqdbExit(void) { return; } ID ajRefseqinNew TY public MO ajrefseqread LB core XX DE Creates a new reference sequence input object. XX RT AjPRefseqin RD New reference sequence input object. RX CA new CT AjPRefseqin CD Default constructor CX // AjPRefseqin ajRefseqinNew(void) { AjPRefseqin pthis; AJNEW0(pthis); pthis->Input = ajTextinNewDatatype(AJDATATYPE_REFSEQ); pthis->RefseqData = NULL; return pthis; } ID ajRefseqinDel TY public MO ajrefseqread LB core XX DE Deletes a reference sequence input object. XX PN [1] PA d pthis AjPRefseqin* PD Reference sequence input PX RT void RD RX CA delete CT AjPRefseqin CD Default destructor CX // void ajRefseqinDel(AjPRefseqin* pthis) { AjPRefseqin thys; if(!pthis) return; thys = *pthis; if(!thys) return; ajDebug("ajRefseqinDel called qry:'%S'\n", thys->Input->Qry); ajTextinDel(&thys->Input); AJFREE(*pthis); return; } ID ajRefseqinClear TY public MO ajrefseqread LB core XX DE Clears a reference sequence input object back to "as new" condition, except DE for the query list which must be preserved. XX PN [1] PA w thys AjPRefseqin PD Reference sequence input PX RT void RD RX CA modify CT AjPRefseqin CD Resets ready for reuse. CX // void ajRefseqinClear(AjPRefseqin thys) { ajDebug("ajRefseqinClear called\n"); ajTextinClear(thys->Input); thys->RefseqData = NULL; return; } ID ajRefseqinQryC TY public MO ajrefseqread LB core XX DE Resets a reference sequence input object using a new Universal DE Query Address XX PN [1] PA u thys AjPRefseqin PD Reference sequence input object. PX PN [2] PA r txt const char* PD Query PX RT void RD RX // void ajRefseqinQryC(AjPRefseqin thys, const char* txt) { ajRefseqinClear(thys); ajStrAssignC(&thys->Input->Qry, txt); return; } ID ajRefseqinQryS TY public MO ajrefseqread LB core XX DE Resets a reference sequence input object using a new Universal DE Query Address XX PN [1] PA u thys AjPRefseqin PD Reference sequence input object. PX PN [2] PA r str const AjPStr PD Query PX RT void RD RX // void ajRefseqinQryS(AjPRefseqin thys, const AjPStr str) { ajRefseqinClear(thys); ajStrAssignS(&thys->Input->Qry, str); return; } ID ajRefseqinTrace TY public MO ajrefseqread LB core XX DE Debug calls to trace the data in a reference sequence input object. XX PN [1] PA r thys const AjPRefseqin PD Reference sequence input object. PX RT void RD RX // void ajRefseqinTrace(const AjPRefseqin thys) { ajDebug("reference sequence input trace\n"); ajDebug("====================\n\n"); ajTextinTrace(thys->Input); if(thys->RefseqData) ajDebug( " RefseqData: exists\n"); return; } ID ajRefseqinRead TY public MO ajrefseqread LB core XX DE If the file is not yet open, calls refseqinQryProcess to convert the query DE into an open file stream. DE DE Uses refseqinRead for the actual file reading. DE DE Returns the results in the AjPRefseq object. XX PN [1] PA u refseqin AjPRefseqin PD Reference sequence data input definitions PX PN [2] PA w refseq AjPRefseq PD Reference sequence data returned. PX RT AjBool RD ajTrue on success. RX CA input CT AjPRefseq CD Master reference sequence data input, calls specific functions for file access type and reference sequence data format. CX // AjBool ajRefseqinRead(AjPRefseqin refseqin, AjPRefseq refseq) { AjBool ret = ajFalse; AjPQueryList node = NULL; AjBool listdata = ajFalse; if(refseqin->Input->Filebuff) { /* (a) if file still open, keep reading */ ajDebug("ajRefseqinRead: input file '%F' still there, try again\n", refseqin->Input->Filebuff->File); ret = refseqinRead(refseqin, refseq); ajDebug("ajRefseqinRead: open buffer qry: '%S' returns: %B\n", refseqin->Input->Qry, ret); } else { /* (b) if we have a list, try the next query in the list */ if(ajListGetLength(refseqin->Input->List)) { listdata = ajTrue; ajListPop(refseqin->Input->List, (void**) &node); ajDebug("++pop from list '%S'\n", node->Qry); ajRefseqinQryS(refseqin, node->Qry); ajDebug("++SAVE WXYZIN '%S' '%S' %d\n", refseqin->Input->Qry, refseqin->Input->Formatstr, refseqin->Input->Format); refseqinQryRestore(refseqin, node); ajStrDel(&node->Qry); ajStrDel(&node->Formatstr); AJFREE(node); ajDebug("ajRefseqinRead: open list, try '%S'\n", refseqin->Input->Qry); if(!refseqinQryProcess(refseqin, refseq) && !ajListGetLength(refseqin->Input->List)) return ajFalse; ret = refseqinRead(refseqin, refseq); ajDebug("ajRefseqinRead: list qry: '%S' returns: %B\n", refseqin->Input->Qry, ret); } else { ajDebug("ajRefseqinRead: no file yet - test query '%S'\n", refseqin->Input->Qry); /* (c) Must be a query - decode it */ if(!refseqinQryProcess(refseqin, refseq) && !ajListGetLength(refseqin->Input->List)) return ajFalse; if(ajListGetLength(refseqin->Input->List)) /* could be a new list */ listdata = ajTrue; ret = refseqinRead(refseqin, refseq); ajDebug("ajRefseqinRead: new qry: '%S' returns: %B\n", refseqin->Input->Qry, ret); } } /* Now read whatever we got */ while(!ret && ajListGetLength(refseqin->Input->List)) { /* Failed, but we have a list still - keep trying it */ if(listdata) ajErr("Failed to read reference sequence data '%S'", refseqin->Input->Qry); listdata = ajTrue; ajListPop(refseqin->Input->List,(void**) &node); ajDebug("++try again: pop from list '%S'\n", node->Qry); ajRefseqinQryS(refseqin, node->Qry); ajDebug("++SAVE (AGAIN) WXYZIN '%S' '%S' %d\n", refseqin->Input->Qry, refseqin->Input->Formatstr, refseqin->Input->Format); refseqinQryRestore(refseqin, node); ajStrDel(&node->Qry); ajStrDel(&node->Formatstr); AJFREE(node); if(!refseqinQryProcess(refseqin, refseq)) continue; ret = refseqinRead(refseqin, refseq); ajDebug("ajRefseqinRead: list retry qry: '%S' returns: %B\n", refseqin->Input->Qry, ret); } if(!ret) { if(listdata) ajErr("Failed to read reference sequence data '%S'", refseqin->Input->Qry); return ajFalse; } refseqDefine(refseq, refseqin); return ajTrue; } ID refseqinQueryMatch TY static MO ajrefseqread LB core XX DE Compares a reference sequence data item to a query and returns true DE if they match. XX PN [1] PA r thys const AjPQuery PD query. PX PN [2] PA r refseq const AjPRefseq PD Reference sequence data. PX RT AjBool RD ajTrue if the reference sequence data matches the query. RX // static AjBool refseqinQueryMatch(const AjPQuery thys, const AjPRefseq refseq) { AjBool tested = ajFalse; AjIList iterfield = NULL; AjPQueryField field = NULL; AjBool ok = ajFalse; ajDebug("refseqinQueryMatch '%S' fields: %Lu Case %B Done %B\n", refseq->Id, ajListGetLength(thys->QueryFields), thys->CaseId, thys->QryDone); if(!thys) /* no query to test, that's fine */ return ajTrue; if(thys->QryDone) /* do we need to test here? */ return ajTrue; /* test the query field(s) */ iterfield = ajListIterNewread(thys->QueryFields); while(!ajListIterDone(iterfield)) { field = ajListIterGet(iterfield); ajDebug(" field: '%S' Query: '%S'\n", field->Field, field->Wildquery); if(ajStrMatchC(field->Field, "id")) { ajDebug(" id test: '%S'\n", refseq->Id); if(thys->CaseId) { if(ajStrMatchWildS(refseq->Id, field->Wildquery)) { ajListIterDel(&iterfield); return ajTrue; } } else { if(ajStrMatchWildCaseS(refseq->Id, field->Wildquery)) { ajListIterDel(&iterfield); return ajTrue; } } ajDebug("id test failed\n"); tested = ajTrue; ok = ajFalse; } if(ajStrMatchC(field->Field, "acc")) /* test id, use trueid */ { if(ajStrMatchWildCaseS(refseq->Id, field->Wildquery)) { ajListIterDel(&iterfield); return ajTrue; } } } ajListIterDel(&iterfield); if(!tested) /* nothing to test, so accept it anyway */ { ajDebug(" no tests: assume OK\n"); return ajTrue; } ajDebug("result: %B\n", ok); return ok; } ID refseqDefine TY static MO ajrefseqread LB core XX DE Make sure all reference sequence data object attributes are defined DE using values from the reference sequence input object if needed XX PN [1] PA w thys AjPRefseq PD Reference sequence data returned. PX PN [2] PA u refseqin AjPRefseqin PD Reference sequence data input definitions PX RT AjBool RD ajTrue on success. RX // static AjBool refseqDefine(AjPRefseq thys, AjPRefseqin refseqin) { /* if values are missing in the reference sequence object, we can use defaults from refseqin or calculate where possible */ /* assign the dbname if defined in the refseqin object */ if(ajStrGetLen(refseqin->Input->Db)) ajStrAssignS(&thys->Db, refseqin->Input->Db); return ajTrue; } ID refseqinReadFmt TY static MO ajrefseqread LB core XX DE Tests whether reference sequence data can be read using the DE specified format. Then tests whether the reference sequence data DE matches reference sequence data query criteria and checks any DE specified type. Applies upper and lower case. XX PN [1] PA u refseqin AjPRefseqin PD Reference sequence data input object PX PN [2] PA w refseq AjPRefseq PD Reference sequence data object PX PN [3] PA r format ajuint PD input format code PX RT ajuint RD 0 if successful. RD 1 if the query match failed. RD 2 if the reference sequence data type failed RD 3 if it failed to read any reference sequence data RX // static ajuint refseqinReadFmt(AjPRefseqin refseqin, AjPRefseq refseq, ajuint format) { ajDebug("++refseqinReadFmt format %d (%s) '%S'\n", format, refseqinFormatDef[format].Name, refseqin->Input->Qry); refseqin->Input->Records = 0; /* Calling funclist refseqinFormatDef() */ if((*refseqinFormatDef[format].Read)(refseqin, refseq)) { ajDebug("refseqinReadFmt success with format %d (%s)\n", format, refseqinFormatDef[format].Name); ajDebug("id: '%S'\n", refseq->Id); refseqin->Input->Format = format; ajStrAssignC(&refseqin->Input->Formatstr, refseqinFormatDef[format].Name); ajStrAssignC(&refseq->Formatstr, refseqinFormatDef[format].Name); ajStrAssignEmptyS(&refseq->Db, refseqin->Input->Db); ajStrAssignS(&refseq->Filename, refseqin->Input->Filename); if(refseqinQueryMatch(refseqin->Input->Query, refseq)) { /* ajRefseqinTrace(refseqin); */ return FMT_OK; } ajDebug("query match failed, continuing ...\n"); ajRefseqClear(refseq); return FMT_NOMATCH; } else { ajDebug("Testing input buffer: IsBuff: %B Eof: %B\n", ajFilebuffIsBuffered(refseqin->Input->Filebuff), ajFilebuffIsEof(refseqin->Input->Filebuff)); if (!ajFilebuffIsBuffered(refseqin->Input->Filebuff) && ajFilebuffIsEof(refseqin->Input->Filebuff)) return FMT_EOF; ajFilebuffReset(refseqin->Input->Filebuff); ajDebug("Format %d (%s) failed, file buffer reset by refseqinReadFmt\n", format, refseqinFormatDef[format].Name); /* ajFilebuffTraceFull(refseqin->Filebuff, 10, 10);*/ } ajDebug("++refseqinReadFmt failed - nothing read\n"); return FMT_FAIL; } ID refseqinRead TY static MO ajrefseqread LB core XX DE Given data in a refseqin structure, tries to read everything needed DE using the specified format or by trial and error. XX PN [1] PA u refseqin AjPRefseqin PD Reference sequence data input object PX PN [2] PA w refseq AjPRefseq PD Reference sequence data object PX RT AjBool RD ajTrue on success RX // static AjBool refseqinRead(AjPRefseqin refseqin, AjPRefseq refseq) { ajuint i; ajuint istat = 0; ajuint jstat = 0; AjPFilebuff buff = refseqin->Input->Filebuff; /*AjBool regfile = ajFalse;*/ AjBool ok; AjPTextAccess textaccess = refseqin->Input->Query->TextAccess; AjPRefseqAccess refseqaccess = refseqin->Input->Query->Access; ajRefseqClear(refseq); ajDebug("refseqinRead: cleared\n"); if(refseqin->Input->Single && refseqin->Input->Count) { /* ** One reference sequence data item at a time is read. ** The first reference sequence data item was read by ACD ** for the following ones we need to reset the AjPRefseqin ** ** Single is set by the access method */ ajDebug("refseqinRead: single access - count %d - call access" " routine again\n", refseqin->Input->Count); /* Calling funclist refseqinAccess() */ if(textaccess) { if(!(*textaccess->Access)(refseqin->Input)) { ajDebug("refseqinRead: (*textaccess->Access)(refseqin->Input) " "*failed*\n"); return ajFalse; } } if(refseqaccess) { if(!(*refseqaccess->Access)(refseqin)) { ajDebug("refseqinRead: (*refseqaccess->Access)(refseqin) " "*failed*\n"); return ajFalse; } } buff = refseqin->Input->Filebuff; } ajDebug("refseqinRead: refseqin format %d '%S'\n", refseqin->Input->Format, refseqin->Input->Formatstr); refseqin->Input->Count++; if(!refseqin->Input->Filebuff) return ajFalse; ok = ajFilebuffIsBuffered(refseqin->Input->Filebuff); while(ok) { /* skip blank lines */ ok = ajBuffreadLine(refseqin->Input->Filebuff, &refseqinReadLine); if(!ajStrIsWhite(refseqinReadLine)) { ajFilebuffClear(refseqin->Input->Filebuff,1); break; } } if(!refseqin->Input->Format) { /* no format specified, try all defaults */ /*regfile = ajFileIsFile(ajFilebuffGetFile(refseqin->Input->Filebuff));*/ for(i = 1; refseqinFormatDef[i].Name; i++) { if(!refseqinFormatDef[i].Try) /* skip if Try is ajFalse */ continue; ajDebug("refseqinRead:try format %d (%s)\n", i, refseqinFormatDef[i].Name); istat = refseqinReadFmt(refseqin, refseq, i); switch(istat) { case FMT_OK: ajDebug("++refseqinRead OK, set format %d\n", refseqin->Input->Format); refseqDefine(refseq, refseqin); return ajTrue; case FMT_BADTYPE: ajDebug("refseqinRead: (a1) " "refseqinReadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("refseqinRead: (b1) " "refseqinReadFmt stat == FAIL *failed*\n"); break; /* we can try next format */ case FMT_NOMATCH: ajDebug("refseqinRead: (c1) " "refseqinReadFmt stat==NOMATCH try again\n"); break; case FMT_EOF: ajDebug("refseqinRead: (d1) " "refseqinReadFmt stat == EOF *failed*\n"); return ajFalse; /* EOF and unbuffered */ case FMT_EMPTY: ajWarn("reference sequence data '%S' has zero length, ignored", ajRefseqGetQryS(refseq)); ajDebug("refseqinRead: (e1) " "refseqinReadFmt stat==EMPTY try again\n"); break; default: ajDebug("unknown code %d from refseqinReadFmt\n", stat); } ajRefseqClear(refseq); if(refseqin->Input->Format) break; /* we read something */ ajFilebuffTrace(refseqin->Input->Filebuff); } if(!refseqin->Input->Format) { /* all default formats failed, give up */ ajDebug("refseqinRead:all default formats failed, give up\n"); return ajFalse; } ajDebug("++refseqinRead set format %d\n", refseqin->Input->Format); } else { /* one format specified */ ajDebug("refseqinRead: one format specified\n"); ajFilebuffSetUnbuffered(refseqin->Input->Filebuff); ajDebug("++refseqinRead known format %d\n", refseqin->Input->Format); istat = refseqinReadFmt(refseqin, refseq, refseqin->Input->Format); switch(istat) { case FMT_OK: refseqDefine(refseq, refseqin); return ajTrue; case FMT_BADTYPE: ajDebug("refseqinRead: (a2) " "refseqinReadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("refseqinRead: (b2) " "refseqinReadFmt stat == FAIL *failed*\n"); return ajFalse; case FMT_NOMATCH: ajDebug("refseqinRead: (c2) " "refseqinReadFmt stat == NOMATCH *try again*\n"); break; case FMT_EOF: ajDebug("refseqinRead: (d2) " "refseqinReadFmt stat == EOF *try again*\n"); if(refseqin->Input->Records) ajErr("Error reading file '%F' with format '%s': " "end-of-file before end of data " "(read %u records)", ajFilebuffGetFile(refseqin->Input->Filebuff), refseqinFormatDef[refseqin->Input->Format].Name, refseqin->Input->Records); break; /* simply end-of-file */ case FMT_EMPTY: ajWarn("assmebly data '%S' has zero length, ignored", ajRefseqGetQryS(refseq)); ajDebug("refseqinRead: (e2) " "refseqinReadFmt stat == EMPTY *try again*\n"); break; default: ajDebug("unknown code %d from refseqinReadFmt\n", stat); } ajRefseqClear(refseq); /* 1 : read, failed to match id/acc/query */ } /* failed - probably entry/accession query failed. Can we try again? */ ajDebug("refseqinRead failed - try again with format %d '%s' code %d\n", refseqin->Input->Format, refseqinFormatDef[refseqin->Input->Format].Name, istat); ajDebug("Search:%B Chunk:%B Data:%x ajFileBuffEmpty:%B\n", refseqin->Input->Search, refseqin->Input->ChunkEntries, refseqin->Input->TextData, ajFilebuffIsEmpty(buff)); if(ajFilebuffIsEmpty(buff) && refseqin->Input->ChunkEntries) { if(textaccess && !(*textaccess->Access)(refseqin->Input)) return ajFalse; else if(refseqaccess && !(*refseqaccess->Access)(refseqin)) return ajFalse; buff = refseqin->Input->Filebuff; } /* need to check end-of-file to avoid repeats */ while(refseqin->Input->Search && (refseqin->Input->TextData || !ajFilebuffIsEmpty(buff))) { jstat = refseqinReadFmt(refseqin, refseq, refseqin->Input->Format); switch(jstat) { case FMT_OK: refseqDefine(refseq, refseqin); return ajTrue; case FMT_BADTYPE: ajDebug("refseqinRead: (a3) " "refseqinReadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("refseqinRead: (b3) " "refseqinReadFmt stat == FAIL *failed*\n"); return ajFalse; case FMT_NOMATCH: ajDebug("refseqinRead: (c3) " "refseqinReadFmt stat == NOMATCH *try again*\n"); break; case FMT_EOF: ajDebug("refseqinRead: (d3) " "refseqinReadFmt stat == EOF *failed*\n"); return ajFalse; /* we already tried again */ case FMT_EMPTY: if(istat != FMT_EMPTY) ajWarn("assmebly data '%S' has zero length, ignored", ajRefseqGetQryS(refseq)); ajDebug("refseqinRead: (e3) " "refseqinReadFmt stat == EMPTY *try again*\n"); break; default: ajDebug("unknown code %d from refseqinReadFmt\n", stat); } ajRefseqClear(refseq); /* 1 : read, failed to match id/acc/query */ } if(refseqin->Input->Format) ajDebug("refseqinRead: *failed* to read reference sequence data %S " "using format %s\n", refseqin->Input->Qry, refseqinFormatDef[refseqin->Input->Format].Name); else ajDebug("refseqinRead: *failed* to read reference sequence data %S " "using any format\n", refseqin->Input->Qry); return ajFalse; } ID refseqinReadEmblcon TY static MO ajrefseqread LB core XX DE Given data in a reference sequence structure, tries to read DE everything needed using EMBL CON format. XX PN [1] PA u refseqin AjPRefseqin PD Refseq input object PX PN [2] PA w refseq AjPRefseq PD refseq object PX RT AjBool RD ajTrue on success RX // static AjBool refseqinReadEmblcon(AjPRefseqin refseqin, AjPRefseq refseq) { AjPFilebuff buff; AjPTextin input; AjPStrTok handle = NULL; AjPStr token = NULL; AjBool ok; AjIList iter = NULL; AjPSeqRange conrange = NULL; ajuint fraglen = 0; ajuint gaplen = 0; ajuint conlen = 0; AjBool conrev = ajFalse; AjPSeqin conseqin = NULL; AjPSeq conseq = NULL; AjPStr conqry = NULL; AjPStr constr = NULL; AjPStr condb = NULL; AjPStr confield = NULL; AjPStr tmpstr = NULL; AjPStr seqFtFmtEmbl = NULL; AjBool dofeat = ajFalse; AjPStr liststr; /* for lists, do not delete */ AjPStr datestr = NULL; AjPStr relstr = NULL; AjPStr cmtstr = NULL; /* stored in AjPSeq - do not delete */ ajuint icount; AjPSeqRef seqref = NULL; AjPSeqXref xref = NULL; ajuint refnum; ajuint seqlen=1024; ajuint tmplen; ajuint itmp; ajuint i; ajint dotpos; ajint colonpos; AjPStr numstr = NULL; ajuint start = 0; ajuint end = 0; ajint istat = 0; /*ajuint taxid = 0;*/ ajDebug("refseqinReadEmblcon\n"); ajRefseqClear(refseq); input = refseqin->Input; buff = input->Filebuff; /*textfile = ajFilebuffGetFile(buff);*/ if(!seqFtFmtEmbl) ajStrAssignC(&seqFtFmtEmbl, "embl"); if(!ajBuffreadLineStore(buff, &refseqinReadLine, input->Text, &refseq->TextPtr)) return ajFalse; /* for GCG formatted databases */ while(ajStrPrefixC(refseqinReadLine, "WP ")) { if(!ajBuffreadLineStore(buff, &refseqinReadLine, input->Text, &refseq->TextPtr)) return ajFalse; input->Records++; } /* extra blank lines */ while(ajStrIsWhite(refseqinReadLine)) { if(!ajBuffreadLineStore(buff, &refseqinReadLine, input->Text, &refseq->TextPtr)) return ajFalse; } ajDebug("seqReadEmbl first line '%S'\n", refseqinReadLine); if(!ajStrPrefixC(refseqinReadLine, "ID ")) { ajFilebuffResetStore(buff, input->Text, &refseq->TextPtr); return ajFalse; } input->Records++; if(input->Text) ajStrAssignC(&refseq->TextPtr,ajStrGetPtr(refseqinReadLine)); ajDebug("seqReadEmbl ID line found\n"); ajStrTokenAssignC(&handle, refseqinReadLine, " ;\t\n\r"); ajStrTokenNextParse(&handle, &token); /* 'ID' */ ajStrTokenNextParse(&handle, &refseq->Id); /* entry name */ /* seqSetName(thys, token); */ ajStrTokenNextParse(&handle, &token); /* SV for new syntax */ if(ajStrMatchC(token, "SV")) /* new post-2006 EMBL line */ { ajStrTokenNextParse(&handle, &token); /* SV */ ajStrInsertK(&token, 0, '.'); /*ajStrInsertS(&token, 0, refseq->Name);*/ /* seqSvSave(thys, token); */ ajStrTokenNextParse(&handle, &token); /* linear or circular */ ajStrTokenNextParseC(&handle, ";\t\n\r", &token); ajStrTrimWhite(&token); /*ajSeqmolSetEmbl(&thys->Molecule, token);*/ ajStrTokenNextParse(&handle, &token); ajStrTrimWhite(&token); /*ajStrAssignS(&thys->Class, token);*/ ajStrTokenNextParse(&handle, &token); ajStrTrimWhite(&token); /*ajStrAssignS(&thys->Division, token);*/ ajStrTokenNextParse(&handle, &token); ajStrTrimEndC(&token, "BP."); ajStrTrimWhite(&token); ajStrToUint(token, &seqlen); } else /* test for a SwissProt/SpTrEMBL entry */ { if(ajStrFindC(refseqinReadLine, " PRT; ")>= 0 || ajStrFindC(refseqinReadLine, " Unreviewed; ") >= 0 || ajStrFindC(refseqinReadLine, " Reviewed; ") >= 0 || ajStrFindC(refseqinReadLine, " Preliminary; ") >= 0 ) { ajFilebuffResetStore(buff, input->Text, &refseq->TextPtr); ajStrTokenDel(&handle); ajStrDel(&token); return ajFalse; } } ok = ajBuffreadLineStore(buff, &refseqinReadLine, input->Text, &refseq->TextPtr); while(ok && !ajStrPrefixC(refseqinReadLine, "CO") && !ajStrPrefixC(refseqinReadLine, "SQ")) { input->Records++; /* check for Staden Experiment format instead */ if(ajStrPrefixC(refseqinReadLine, "EN ") || ajStrPrefixC(refseqinReadLine, "TN ") || ajStrPrefixC(refseqinReadLine, "EX ") ) { ajFilebuffResetStore(buff, input->Text, &refseq->TextPtr); ajStrDel(&token); return ajFalse;; } else if(ajStrPrefixC(refseqinReadLine, "FH ") || ajStrPrefixC(refseqinReadLine, "AH ")) ok = ajTrue; /* ignore these lines */ else if(ajStrPrefixC(refseqinReadLine, "AC ") || ajStrPrefixC(refseqinReadLine, "PA ") ) /* emblcds database format */ { ajStrTokenAssignC(&handle, refseqinReadLine, " ;\n\r"); ajStrTokenNextParse(&handle, &token); /* 'AC' */ /* while(ajStrTokenNextParse(&handle, &token)) seqAccSave(thys, token); */ } else if(ajStrPrefixC(refseqinReadLine, "SV ") || ajStrPrefixC(refseqinReadLine, "IV ") ) /* emblcds database format */ { ajStrTokenAssignC(&handle, refseqinReadLine, " \n\r"); ajStrTokenNextParse(&handle, &token); /* 'SV' */ ajStrTokenNextParse(&handle, &token); /* version */ /* seqSvSave(thys, token); */ } else if(ajStrPrefixC(refseqinReadLine, "DE ")) { ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'DE' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* desc */ if(ajStrGetLen(refseq->Desc)) { ajStrAppendC(&refseq->Desc, " "); ajStrAppendS(&refseq->Desc, token); } else ajStrAssignS(&refseq->Desc, token); } if(ajStrPrefixC(refseqinReadLine, "KW ")) { ajStrTokenAssignC(&handle, refseqinReadLine, " \n\r"); ajStrTokenNextParse(&handle, &token); /* 'KW' */ while(ajStrTokenNextParseC(&handle, ".;\n\r", &token)) { liststr = ajStrNewS(token); ajStrTrimWhite(&liststr); /*ajSeqAddKey(thys, liststr);*/ } } else if(ajStrPrefixC(refseqinReadLine, "OS ")) { ajStrTokenAssignC(&handle, refseqinReadLine, " \n\r"); ajStrTokenNextParse(&handle, &token); /* 'OS' */ /* maybe better remove . from this, and trim from end */ while(ajStrTokenNextParseC(&handle, ".;\n\r", &token)) { ajStrAssignS(&tmpstr, token); ajStrTrimWhite(&tmpstr); /*seqTaxSave(thys, tmpstr, 1);*/ ajStrDel(&tmpstr); } } else if(ajStrPrefixC(refseqinReadLine, "OC ")) { ajStrTokenAssignC(&handle, refseqinReadLine, " \n\r"); ajStrTokenNextParse(&handle, &token); /* 'OC' */ /* maybe better remove . from this, and trim from end */ while(ajStrTokenNextParseC(&handle, ".;\n\r", &token)) { ajStrAssignS(&tmpstr, token); ajStrTrimWhite(&tmpstr); /*seqTaxSave(thys, tmpstr, 0);*/ ajStrDel(&tmpstr); } } else if(ajStrPrefixC(refseqinReadLine, "OG ")) { ajStrTokenAssignC(&handle, refseqinReadLine, " \n\r"); ajStrTokenNextParse(&handle, &token); /* 'OG' */ /* maybe better remove . from this, and trim from end */ while(ajStrTokenNextParseC(&handle, ".;\n\r", &token)) { ajStrAssignS(&tmpstr, token); ajStrTrimWhite(&tmpstr); /*seqTaxSave(thys, tmpstr, 2);*/ ajStrDel(&tmpstr); } } else if(ajStrPrefixC(refseqinReadLine, "CC ")) { ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'CC' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* comment */ if(ajStrGetLen(cmtstr)) ajStrAppendC(&cmtstr, "\n"); ajStrAppendS(&cmtstr, token); /* trying to keep comments in one long string with embedded returns ** probably fails for long comments - and also fails for contact details ** which have very short comment lines ** switch to just keeping original lines */ /* if(ajStrGetLen(cmtstr)) { if(ajStrGetLen(token)) { if(ajStrGetCharLast(cmtstr) != '\n') ajStrAppendK(&cmtstr, ' '); ajStrAppendS(&cmtstr, token); } else { if(ajStrGetCharLast(cmtstr) != '\n') ajStrAppendK(&cmtstr, '\n'); ajStrAppendC(&cmtstr, " \n"); } } else ajStrAssignS(&cmtstr, token); if(ajStrGetCharLast(token) == '.') ajStrAppendK(&cmtstr, '\n'); */ } else if(ajStrPrefixC(refseqinReadLine, "DR ")) { AJNEW0(xref); ajStrTokenAssignC(&handle, refseqinReadLine, " ;\n\r"); ajStrTokenNextParse(&handle, &token); /* 'DR' */ ajStrTokenNextParseC(&handle, ";\n\r", &token); /* dbname */ ajStrAssignS(&xref->Db, token); ajStrTrimWhite(&token); ajStrTokenNextParse(&handle, &token); /* primary */ ajStrTrimWhite(&token); ajStrAssignS(&xref->Id, token); ajStrTokenNextParse(&handle, &token); /* secondary*/ if(!ajStrGetLen(token)) { if(ajStrGetCharLast(xref->Id) == '.') ajStrCutEnd(&xref->Id, 1); } else { if(ajStrGetCharLast(token) == '.') ajStrCutEnd(&token, 1); ajStrTrimWhite(&token); ajStrAssignS(&xref->Secid, token); ajStrTokenNextParse(&handle, &token); /* secondary*/ if(!ajStrGetLen(token)) { if(ajStrGetCharLast(xref->Secid) == '.') ajStrCutEnd(&xref->Secid, 1); } else { if(ajStrGetCharLast(token) == '.') ajStrCutEnd(&token, 1); ajStrTrimWhite(&token); ajStrAssignS(&xref->Terid, token); ajStrTokenNextParse(&handle, &token); /* secondary*/ if(!ajStrGetLen(token)) { if(ajStrGetCharLast(xref->Terid) == '.') ajStrCutEnd(&xref->Terid, 1); } else { if(ajStrGetCharLast(token) == '.') ajStrCutEnd(&token, 1); ajStrTrimWhite(&token); ajStrAssignS(&xref->Quatid, token); } } } xref->Type = XREF_DR; /*ajSeqAddXref(thys, xref);*/ } else if(ajStrPrefixC(refseqinReadLine, "RN ")) { if(!seqref) seqref = ajSeqrefNew(); ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'RN' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* [num] */ ajStrAssignSubS(&tmpstr, token, 1, -2); ajStrToUint(tmpstr, &refnum); ajSeqrefSetnumNumber(seqref, refnum); } else if(ajStrPrefixC(refseqinReadLine, "RG ")) { if(!seqref) seqref = ajSeqrefNew(); ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'RG' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* groupname */ ajSeqrefAppendGroupname(seqref, token); } else if(ajStrPrefixC(refseqinReadLine, "RX ")) { if(!seqref) seqref = ajSeqrefNew(); ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'RX' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* xref */ ajSeqrefAppendXref(seqref, token); } else if(ajStrPrefixC(refseqinReadLine, "RP ")) { if(!seqref) seqref = ajSeqrefNew(); ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'RP' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* position */ ajSeqrefAppendPosition(seqref, token); } else if(ajStrPrefixC(refseqinReadLine, "RA ")) { if(!seqref) seqref = ajSeqrefNew(); ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'RA' */ ajStrTokenNextParseC(&handle, "\n\r;", &token); /* authors */ ajSeqrefAppendAuthors(seqref, token); } else if(ajStrPrefixC(refseqinReadLine, "RT ")) { if(!seqref) seqref = ajSeqrefNew(); ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'RT' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* title */ if(!ajStrMatchC(token, ";")) ajSeqrefAppendTitle(seqref, token); } else if(ajStrPrefixC(refseqinReadLine, "RL ")) { if(!seqref) seqref = ajSeqrefNew(); ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'RL' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* authors */ ajSeqrefAppendLocation(seqref, token); } else if(ajStrPrefixC(refseqinReadLine, "RC ")) { if(!seqref) seqref = ajSeqrefNew(); ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'RC' */ ajStrTokenNextParseC(&handle, "\n\r", &token); /* comment */ ajSeqrefAppendComment(seqref, token); } else if(ajStrPrefixC(refseqinReadLine, "FT ")) { if(!dofeat) { dofeat = ajTrue; /*seqin->Ftquery = ajFeattabinNewSS(seqFtFmtEmbl, thys->Name, "N"); */ } /* ajFilebuffLoadS(seqin->Ftquery->Input->Filebuff, refseqinReadLine);*/ } else if(ajStrPrefixC(refseqinReadLine, "DT ")) { /*if(!thys->Date) thys->Date = ajSeqdateNew();*/ ajStrTokenAssignC(&handle, refseqinReadLine, " (),"); icount = 0; while(ajStrTokenNextParse(&handle, &token)) { icount++; if(icount==2) ajStrAssignS(&datestr, token); else if(icount==4) ajStrAssignS(&relstr, token); else if(icount==5) { if(ajStrMatchC(token, "Created")) { /* ajSeqdateSetCreateS(thys->Date, datestr); ajStrAssignS(&thys->Date->CreRel, relstr); */ } } else if(icount==8) { /* ajSeqdateSetModifyS(thys->Date, datestr); ajStrAssignS(&thys->Date->ModRel, relstr); ajStrAssignS(&thys->Date->ModVer, token); */ } } } else if(ajStrPrefixC(refseqinReadLine, "XX")) { if(seqref) { ajSeqrefStandard(seqref); /*ajSeqAddRef(thys, seqref);*/ /*seqref = NULL;*/ } if(ajStrGetLen(cmtstr)) { /*ajSeqAddCmt(thys, cmtstr);*/ /*cmtstr = NULL;*/ } } /* ignored line types */ /* other line types */ /* if(ajStrPrefixC(refseqinReadLine, "RN ")) if(ajStrPrefixC(refseqinReadLine, "RC ")) if(ajStrPrefixC(refseqinReadLine, "RP ")) if(ajStrPrefixC(refseqinReadLine, "RX ")) if(ajStrPrefixC(refseqinReadLine, "RG ")) if(ajStrPrefixC(refseqinReadLine, "RA ")) if(ajStrPrefixC(refseqinReadLine, "RT ")) if(ajStrPrefixC(refseqinReadLine, "RL ")) if(ajStrPrefixC(refseqinReadLine, "AS ")) if(ajStrPrefixC(refseqinReadLine, "CO ")) if(ajStrPrefixC(refseqinReadLine, "CC ")) */ ok = ajBuffreadLineStore(buff, &refseqinReadLine, input->Text, &refseq->TextPtr); } if(dofeat) { /* ajDebug("EMBL FEAT TabIn %x\n", seqin->Ftquery); */ /* ajFeattableDel(&thys->Fttable); thys->Fttable = ajFeattableNewRead(seqin->Ftquery); ajFeattabinClear(seqin->Ftquery);*/ } if(ajStrPrefixC(refseqinReadLine, "SQ")) { ajStrTokenAssignC(&handle, refseqinReadLine, " "); ajStrTokenNextParse(&handle, &token); /* 'SQ' */ ajStrTokenNextParse(&handle, &token); /* 'Sequence' */ ajStrTokenNextParse(&handle, &token); /* len */ ajStrToUint(token, &tmplen); if(tmplen > seqlen) seqlen = tmplen; ajStrTokenNextParse(&handle, &token); /* BP; */ tmplen = 0; for(i=0;i<4;i++) { ajStrTokenNextParse(&handle, &token); /* count */ ajStrToUint(token, &itmp); ajStrTokenNextParse(&handle, &token); /* 'A' 'C' 'G' 'T' 'other' */ tmplen += itmp; } if(tmplen > seqlen) seqlen = tmplen; /* read the sequence and terminator */ ok = ajBuffreadLineStore(buff, &refseqinReadLine, input->Text, &refseq->TextPtr); /*ajStrSetRes(&thys->Seq, seqlen+1);*/ while(ok && !ajStrPrefixC(refseqinReadLine, "//")) { /*seqAppend(&thys->Seq, refseqinReadLine);*/ input->Records++; ok = ajBuffreadLineStore(buff, &refseqinReadLine, input->Text, &refseq->TextPtr); } } else if (ajStrPrefixC(refseqinReadLine, "CO")) { if(!constr) constr = ajStrNewRes(4096); while(ok && ajStrPrefixC(refseqinReadLine, "CO")) { ajStrTrimWhiteEnd(&refseqinReadLine); ajStrAppendSubS(&constr, refseqinReadLine, 5, -1); ok = ajBuffreadLineStore(buff, &refseqinReadLine, input->Text, &refseq->TextPtr); } if(ajStrPrefixC(constr, "join(") && ajStrSuffixC(constr, ")")) { ajStrCutEnd(&constr, 1); ajStrCutStart(&constr, 5); } if(!refseq->Seqlist) refseq->Seqlist = ajListstrNew(); ajStrTokenAssignC(&handle, constr, ","); conlen = 0; while(ajStrTokenNextParse(&handle, &token)) { if(ajStrPrefixC(token, "gap(")) { ajDebug("%Lu: '%S'\n", ajListGetLength(refseq->Seqlist), token); ajStrCutEnd(&token, 1); ajStrCutStart(&token, 4); if(ajStrToUint(token, &gaplen)) { ajDebug("gap %u bases total %u\n", gaplen, ajStrGetLen(refseq->Seq)); } else ajWarn("Unknown gap length in '%S'", constr); conrange = ajSeqrangeNewValues(conlen, gaplen, NULL); ajListPushAppend(refseq->Seqlist, conrange); conrange = NULL; conlen += gaplen; } else { if(ajStrPrefixC(token, "complement(")) { ajStrCutEnd(&token, 1); ajStrCutStart(&token, 11); conrev = ajTrue; } if(!condb) { if(!ajNamDbGetAttrSpecialC(input->Db, "ConDatabase", &condb)) ajStrAssignS(&condb, input->Db); if(!ajNamDbGetAttrSpecialC(input->Db, "ConField", &confield)) ajStrAssignC(&confield, "sv"); } dotpos = (ajint) ajStrFindAnyK(token, '.'); colonpos = (ajint) ajStrFindAnyK(token, ':'); ajStrAssignSubS(&numstr, token, colonpos+1, -1); istat = ajFmtScanS(numstr, "%u..%u", &start, &end); if(istat != 2) { ajWarn("EMBLCON badly formed fragment '%S'", token); start = 1; end = 0; } fraglen = 1 + end - start; if(ajStrMatchC(confield, "sv")) { ajFmtPrintS(&conqry, "%S-sv:%S", input->Db, token); if(conrev) ajStrAppendC(&conqry, ":r"); } else { if((dotpos > 0) && (dotpos < colonpos)) { ajStrCutRange(&token, dotpos, colonpos-1); ajFmtPrintS(&conqry, "%S-%S:%S", condb, confield, token); if(conrev) ajStrAppendC(&conqry, ":r"); } } ajDebug("%Lu: '%S' '%S' rev:%B\n", ajListGetLength(refseq->Seqlist), token, conqry, conrev); conrange = ajSeqrangeNewValues(conlen, fraglen, conqry); ajListPushAppend(refseq->Seqlist, conrange); conrange = NULL; conlen += fraglen; } } ajDebug("conlen: %u seqlen: %u\n", conlen, seqlen); ajStrTokenDel(&handle); } if(ok &&!ajStrPrefixC(refseqinReadLine, "//")) ajWarn("Unexpected line after sequence data: %S", refseqinReadLine); if(!ok) ajWarn("Unexpected end of file after sequence data"); conseqin = ajSeqinNew(); conseq = ajSeqNew(); iter = ajListIterNewread(refseq->Seqlist); while(!ajListIterDone(iter)) { conrange = ajListIterGet(iter); if(conrange->Start != ajStrGetLen(refseq->Seq)) ajWarn("EMBLCON entry '%S' reading at %u expected %Lu", refseq->Id, ajStrGetLen(refseq->Seq), conrange->Start); if(conrange->Query) { ajSeqinUsa(&conseqin, conrange->Query); if(!ajSeqRead(conseq, conseqin)) ajErr("EMBLCON entry '%S' failed to read '%S'", refseq->Id, conrange->Query); else { ajSeqTrim(conseq); if(conrev) ajSeqReverseDo(conseq); ajStrAppendS(&refseq->Seq,ajSeqGetSeqS(conseq)); ajDebug("Read %u bases total %u\n", ajSeqGetLen(conseq), ajStrGetLen(refseq->Seq)); } } else { ajStrAppendCountK(&refseq->Seq, 'N', conrange->Length); } } ajListIterDel(&iter); ajSeqDel(&conseq); ajSeqinDel(&conseqin); conrange = NULL; ajFilebuffClear(buff, 0); ajSeqrefDel(&seqref); ajSeqxrefDel(&xref); ajStrDel(&tmpstr); ajStrDel(&cmtstr); ajStrDel(&liststr); ajStrDel(&constr); ajStrDel(&conqry); ajStrDel(&condb); ajStrDel(&confield); ajStrDel(&token); ajStrDel(&datestr); ajStrDel(&relstr); ajStrDel(&numstr); ajStrDel(&seqFtFmtEmbl); ajStrTokenDel(&handle); return ajTrue; } ID ajRefseqinprintBook TY public MO ajrefseqread LB core XX DE Reports the internal data structures as a Docbook table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajRefseqinprintBook(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; AjPList fmtlist; AjPStr* names; fmtlist = ajListstrNew(); ajFmtPrintF(outf, "The supported reference sequence formats are " "summarised in the table below. " "The columns are as follows: " "Input format (format name), " "Try (indicates whether the " "format can be detected automatically on input), and " "Description (short description of " "the format).\n\n"); ajFmtPrintF(outf, "
Obo term FormatDescription
\n%-12s\n" "\"%s\"
\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=1; refseqinFormatDef[i].Name; i++) { if(!refseqinFormatDef[i].Alias) { namestr = ajStrNewC(refseqinFormatDef[i].Name); ajListPushAppend(fmtlist, namestr); namestr = NULL; } } ajListSort(fmtlist, ajStrVcmp); ajListstrToarray(fmtlist, &names); for(i=0; names[i]; i++) { for(j=0; refseqinFormatDef[j].Name; j++) { if(ajStrMatchC(names[i],refseqinFormatDef[j].Name)) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", refseqinFormatDef[j].Name); ajFmtPrintF(outf, " \n", refseqinFormatDef[j].Try); ajFmtPrintF(outf, " \n", refseqinFormatDef[j].Desc); ajFmtPrintF(outf, " \n"); } } } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Input reference sequence formats
Input FormatTryDescription
%s%B%s
\n"); ajStrDel(&namestr); names = NULL; ajListstrFreeData(&fmtlist); return; } ID ajRefseqinprintHtml TY public MO ajrefseqread LB core XX DE Reports the internal data structures as an HTML table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajRefseqinprintHtml(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, ""); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); for(i=1; refseqinFormatDef[i].Name; i++) { ajStrAssignC(&namestr, refseqinFormatDef[i].Name); if(!refseqinFormatDef[i].Alias) { for(j=i+1; refseqinFormatDef[j].Name; j++) { if(refseqinFormatDef[j].Read == refseqinFormatDef[i].Read) { ajFmtPrintAppS(&namestr, " %s", refseqinFormatDef[j].Name); if(!refseqinFormatDef[j].Alias) { ajWarn("Input format '%s' same as '%s' but not alias", refseqinFormatDef[j].Name, refseqinFormatDef[i].Name); } } } ajFmtPrintF(outf, "\n", namestr, refseqinFormatDef[i].Try); ajFmtPrintF(outf, "\n", refseqinFormatDef[i].Desc); } } ajFmtPrintF(outf, "
Input FormatAutoMultiDescription
\n%S\n%B\n%s\n
\n"); ajStrDel(&namestr); return; } ID ajRefseqinprintText TY public MO ajrefseqread LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajRefseqinprintText(AjPFile outf, AjBool full) { ajuint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Reference sequence input formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Alias Alias name\n"); ajFmtPrintF(outf, "# Try Test for unknown input files\n"); ajFmtPrintF(outf, "# Name Alias Try " "Description"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "InFormat {\n"); for(i=0; refseqinFormatDef[i].Name; i++) if(full || !refseqinFormatDef[i].Alias) ajFmtPrintF(outf, " %-12s %5B %3B \"%s\"\n", refseqinFormatDef[i].Name, refseqinFormatDef[i].Alias, refseqinFormatDef[i].Try, refseqinFormatDef[i].Desc); ajFmtPrintF(outf, "}\n\n"); return; } ID ajRefseqinprintWiki TY public MO ajrefseqread LB core XX DE Reports the internal data structures as a wiki table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajRefseqinprintWiki(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!Try!!" "class=\"unsortable\"|Description\n"); for(i=1; refseqinFormatDef[i].Name; i++) { ajStrAssignC(&namestr, refseqinFormatDef[i].Name); if(!refseqinFormatDef[i].Alias) { for(j=i+1; refseqinFormatDef[j].Name; j++) { if(refseqinFormatDef[j].Read == refseqinFormatDef[i].Read) { ajFmtPrintAppS(&namestr, "
%s", refseqinFormatDef[j].Name); if(!refseqinFormatDef[j].Alias) { ajWarn("Input format '%s' same as '%s' but not alias", refseqinFormatDef[j].Name, refseqinFormatDef[i].Name); } } } ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "|%S||%B||%s\n", namestr, refseqinFormatDef[i].Try, refseqinFormatDef[i].Desc); } } ajFmtPrintF(outf, "|}\n\n"); ajStrDel(&namestr); return; } ID ajRefseqinExit TY public MO ajrefseqread LB core XX DE Cleans up reference sequence input internal memory XX RT void RD RX // void ajRefseqinExit(void) { ajStrDel(&refseqinReadLine); ajTableDel(&refseqDbMethods); return; } ID ajRefseqinTypeGetFields TY public MO ajrefseqread LB core XX DE Returns the list of known field names for ajRefseqinRead XX RT const char* RD List of field names RX // const char* ajRefseqinTypeGetFields(void) { return "id acc"; } ID ajRefseqinTypeGetQlinks TY public MO ajrefseqread LB core XX DE Returns the listof known query link operators for ajRefseqinRead XX RT const char* RD List of field names RX // const char* ajRefseqinTypeGetQlinks(void) { return "|"; } ID ajRefseqaccessGetDb TY public MO ajrefseqread LB core XX DE Returns the table in which reference sequence database access DE details are registered XX RT AjPTable RD Access functions hash table RX // AjPTable ajRefseqaccessGetDb(void) { if(!refseqDbMethods) refseqDbMethods = ajCallTableNew(); return refseqDbMethods; } ID ajRefseqaccessMethodGetQlinks TY public MO ajrefseqread LB core XX DE Tests for a named method for refseq data reading returns the DE known query link operators XX PN [1] PA r method const AjPStr PD Method required. PX RT const char* RD Known link operators RX // const char* ajRefseqaccessMethodGetQlinks(const AjPStr method) { AjPRefseqAccess methoddata; methoddata = ajCallTableGetS(refseqDbMethods, method); if(!methoddata) return NULL; return methoddata->Qlink; } ID ajRefseqaccessMethodGetScope TY public MO ajrefseqread LB core XX DE Tests for a named method for reference sequence data reading and DE returns the scope (entry, query or all). XX PN [1] PA r method const AjPStr PD Method required. PX RT ajuint RD Scope flags RX // ajuint ajRefseqaccessMethodGetScope(const AjPStr method) { AjPRefseqAccess methoddata; ajuint ret = 0; methoddata = ajCallTableGetS(refseqDbMethods, method); if(!methoddata) return 0; if(methoddata->Entry) ret |= AJMETHOD_ENTRY; if(methoddata->Query) ret |= AJMETHOD_QUERY; if(methoddata->All) ret |= AJMETHOD_ALL; return ret; } ID ajRefseqaccessMethodTest TY public MO ajrefseqread LB core XX DE Tests for a named method for reference sequence data reading. XX PN [1] PA r method const AjPStr PD Method required. PX RT AjBool RD ajTrue on success. RX // AjBool ajRefseqaccessMethodTest(const AjPStr method) { if(ajCallTableGetS(refseqDbMethods, method)) return ajTrue; return ajFalse; } ID refseqinQryRestore TY static MO ajrefseqread LB core XX DE Restores a reference sequence input specification from an AjPQueryList node XX PN [1] PA w refseqin AjPRefseqin PD Reference sequence input object PX PN [2] PA r node const AjPQueryList PD Query list node PX RT void RD RX // static void refseqinQryRestore(AjPRefseqin refseqin, const AjPQueryList node) { refseqin->Input->Format = node->Format; refseqin->Input->Fpos = node->Fpos; ajStrAssignS(&refseqin->Input->Formatstr, node->Formatstr); ajStrAssignS(&refseqin->Input->QryFields, node->QryFields); return; } ID refseqinQrySave TY static MO ajrefseqread LB core XX DE Saves a reference sequence input specification in an AjPQueryList node XX PN [1] PA w node AjPQueryList PD Query list node PX PN [2] PA r refseqin const AjPRefseqin PD Reference sequence input object PX RT void RD RX // static void refseqinQrySave(AjPQueryList node, const AjPRefseqin refseqin) { node->Format = refseqin->Input->Format; node->Fpos = refseqin->Input->Fpos; ajStrAssignS(&node->Formatstr, refseqin->Input->Formatstr); ajStrAssignS(&node->QryFields, refseqin->Input->QryFields); return; } ID refseqinQryProcess TY static MO ajrefseqread LB core XX DE Converts a reference sequence data query into an open file. DE DE Tests for "format::" and sets this if it is found DE DE Then tests for "list:" or " XX PN [1] PA u refseqin AjPRefseqin PD Reference sequence data input structure. PX PN [2] PA u refseq AjPRefseq PD Reference sequence data to be read. PD The format will be replaced PD if defined in the query string. PX RT AjBool RD ajTrue on success. RX // static AjBool refseqinQryProcess(AjPRefseqin refseqin, AjPRefseq refseq) { AjBool ret = ajTrue; AjPStr qrystr = NULL; AjBool refseqmethod = ajFalse; const AjPStr fmtstr = NULL; AjPTextin textin; AjPQuery qry; AjPRefseqAccess refseqaccess = NULL; textin = refseqin->Input; qry = textin->Query; /* pick up the original query string */ qrystr = ajStrNewS(textin->Qry); ajDebug("refseqinQryProcess '%S'\n", qrystr); /* look for a format:: prefix */ fmtstr = ajQuerystrParseFormat(&qrystr, textin, refseqinformatFind); ajDebug("refseqinQryProcess ... fmtstr '%S' '%S'\n", fmtstr, qrystr); /* (seq/feat) DO NOT look for a [range] suffix */ /* look for a list:: or @:: listfile of queries - process and return */ if(ajQuerystrParseListfile(&qrystr)) { ajDebug("refseqinQryProcess ... listfile '%S'\n", qrystr); ret = refseqinListProcess(refseqin, refseq, qrystr); ajStrDel(&qrystr); return ret; } /* try general text access methods (file, asis, text database access */ ajDebug("refseqinQryProcess ... no listfile '%S'\n", qrystr); if(!ajQuerystrParseRead(&qrystr, textin, refseqinformatFind, &refseqmethod)) { ajStrDel(&qrystr); return ajFalse; } refseqinFormatSet(refseqin, refseq); ajDebug("refseqinQryProcess ... read nontext: %B '%S'\n", refseqmethod, qrystr); ajStrDel(&qrystr); /* we found a non-text method */ if(refseqmethod) { ajDebug("refseqinQryProcess ... call method '%S'\n", qry->Method); ajDebug("refseqinQryProcess ... textin format %d '%S'\n", textin->Format, textin->Formatstr); ajDebug("refseqinQryProcess ... query format '%S'\n", qry->Formatstr); qry->Access = ajCallTableGetS(refseqDbMethods,qry->Method); refseqaccess = qry->Access; return (*refseqaccess->Access)(refseqin); } ajDebug("refseqinQryProcess text method '%S' success\n", qry->Method); return ajTrue; } ID refseqinListProcess TY static MO ajrefseqread LB core XX DE Processes a file of queries. DE This function is called by, and calls, refseqinQryProcess. There is DE a depth check to avoid infinite loops, for example where a list file DE refers to itself. DE DE This function produces a list (AjPList) of queries with all list references DE expanded into lists of queries. DE DE Because queries in a list can have their own format DE the prior settings are stored with each query in the list node so that they DE can be restored after. XX PN [1] PA u refseqin AjPRefseqin PD Reference sequence data input PX PN [2] PA u refseq AjPRefseq PD Reference sequence data PX PN [3] PA r listfile const AjPStr PD Name of list file., PX RT AjBool RD ajTrue on success. RX // static AjBool refseqinListProcess(AjPRefseqin refseqin, AjPRefseq refseq, const AjPStr listfile) { AjPList list = NULL; AjPFile file = NULL; AjPStr token = NULL; AjPStrTok handle = NULL; AjBool ret = ajFalse; AjPQueryList node = NULL; static ajint depth = 0; static ajint MAXDEPTH = 16; depth++; ajDebug("++refseqinListProcess %S depth %d\n", listfile, depth); if(depth > MAXDEPTH) ajFatal("Query list too deep"); if(!refseqin->Input->List) refseqin->Input->List = ajListNew(); list = ajListNew(); file = ajFileNewInNameS(listfile); if(!file) { ajErr("Failed to open list file '%S'", listfile); depth--; return ret; } while(ajReadlineTrim(file, &refseqinReadLine)) { refseqinListNoComment(&refseqinReadLine); if(ajStrGetLen(refseqinReadLine)) { ajStrTokenAssignC(&handle, refseqinReadLine, " \t\n\r"); ajStrTokenNextParse(&handle, &token); /* ajDebug("Line '%S'\n");*/ /* ajDebug("token '%S'\n", refseqinReadLine, token); */ if(ajStrGetLen(token)) { ajDebug("++Add to list: '%S'\n", token); AJNEW0(node); ajStrAssignS(&node->Qry, token); refseqinQrySave(node, refseqin); ajListPushAppend(list, node); } ajStrDel(&token); token = NULL; } } ajFileClose(&file); ajStrDel(&token); ajDebug("Trace refseqin->Input->List\n"); ajQuerylistTrace(refseqin->Input->List); ajDebug("Trace new list\n"); ajQuerylistTrace(list); ajListPushlist(refseqin->Input->List, &list); ajDebug("Trace combined refseqin->Input->List\n"); ajQuerylistTrace(refseqin->Input->List); /* ** now try the first item on the list ** this can descend recursively if it is also a list ** which is why we check the depth above */ if(ajListPop(refseqin->Input->List, (void**) &node)) { ajDebug("++pop first item '%S'\n", node->Qry); ajRefseqinQryS(refseqin, node->Qry); refseqinQryRestore(refseqin, node); ajStrDel(&node->Qry); ajStrDel(&node->Formatstr); AJFREE(node); ajDebug("descending with query '%S'\n", refseqin->Input->Qry); ret = refseqinQryProcess(refseqin, refseq); } ajStrTokenDel(&handle); depth--; ajDebug("++refseqinListProcess depth: %d returns: %B\n", depth, ret); return ret; } ID refseqinListNoComment TY static MO ajrefseqread LB core XX DE Strips comments from a character string (a line from an ACD file). DE Comments are blank lines or any text following a "#" character. XX PN [1] PA u text AjPStr* PD Line of text from input file. PX RT void RD RX // static void refseqinListNoComment(AjPStr* text) { ajuint i; char *cp; i = ajStrGetLen(*text); if(!i) /* empty string */ return; MAJSTRGETUNIQUESTR(text); cp = strchr(ajStrGetPtr(*text), '#'); if(cp) { /* comment found */ *cp = '\0'; ajStrSetValid(text); } return; } ID refseqinFormatSet TY static MO ajrefseqread LB core XX DE Sets the input format for reference sequence data using the DE reference sequence data input object's defined format XX PN [1] PA u refseqin AjPRefseqin PD Reference sequence data input. PX PN [2] PA u refseq AjPRefseq PD Reference sequence data PX RT AjBool RD ajTrue on success. RX // static AjBool refseqinFormatSet(AjPRefseqin refseqin, AjPRefseq refseq) { if(ajStrGetLen(refseqin->Input->Formatstr)) { ajDebug("... input format value '%S'\n", refseqin->Input->Formatstr); if(refseqinformatFind(refseqin->Input->Formatstr, &refseqin->Input->Format)) { ajStrAssignS(&refseq->Formatstr, refseqin->Input->Formatstr); refseq->Format = refseqin->Input->Format; ajDebug("...format OK '%S' = %d\n", refseqin->Input->Formatstr, refseqin->Input->Format); } else ajDebug("...format unknown '%S'\n", refseqin->Input->Formatstr); return ajTrue; } else ajDebug("...input format not set\n"); return ajFalse; } ID ajRefseqallNew TY public MO ajrefseqread LB core XX DE Creates a new refseq input stream object. XX RT AjPRefseqall RD New refseq input stream object. RX // AjPRefseqall ajRefseqallNew(void) { AjPRefseqall pthis; AJNEW0(pthis); pthis->Refseqin = ajRefseqinNew(); pthis->Refseq = ajRefseqNew(); return pthis; } ID ajRefseqallDel TY public MO ajrefseqread LB core XX DE Deletes a refseq input stream object. XX PN [1] PA d pthis AjPRefseqall* PD Refseq input stream PX RT void RD RX // void ajRefseqallDel(AjPRefseqall* pthis) { AjPRefseqall thys; if(!pthis) return; thys = *pthis; if(!thys) return; ajRefseqinDel(&thys->Refseqin); if(!thys->Returned) ajRefseqDel(&thys->Refseq); AJFREE(*pthis); return; } ID ajRefseqallClear TY public MO ajrefseqread LB core XX DE Clears a refseq input stream object back to "as new" condition, except DE for the query list which must be preserved. XX PN [1] PA w thys AjPRefseqall PD Refseq input stream PX RT void RD RX // void ajRefseqallClear(AjPRefseqall thys) { ajDebug("ajRefseqallClear called\n"); ajRefseqinClear(thys->Refseqin); ajRefseqClear(thys->Refseq); thys->Returned = ajFalse; return; } ID ajRefseqallGetrefseqId TY public MO ajrefseqread LB core XX DE Returns the identifier of the current refseq in an input stream XX PN [1] PA r thys const AjPRefseqall PD Refseq input stream PX RT const AjPStr RD Identifier RX // const AjPStr ajRefseqallGetrefseqId(const AjPRefseqall thys) { if(!thys) return NULL; ajDebug("ajRefseqallGetrefseqId called\n"); return ajRefseqGetId(thys->Refseq); } ID ajRefseqallNext TY public MO ajrefseqread LB core XX DE Parse a refseq query into format, access, file and entry DE DE Split at delimiters. Check for the first part as a valid format DE Check for the remaining first part as a database name or as a file DE that can be opened. DE Anything left is an entryname spec. DE DE Return the results in the AjPRefseq object but leave the file open for DE future calls. XX PN [1] PA w thys AjPRefseqall PD Refseq input stream PX PN [2] PA u Prefseq AjPRefseq* PD Refseq returned PX RT AjBool RD ajTrue on success. RX // AjBool ajRefseqallNext(AjPRefseqall thys, AjPRefseq *Prefseq) { ajDebug("ajRefseqallNext count:%u\n", thys->Count); if(!thys->Count) { thys->Count = 1; thys->Totterms++; *Prefseq = thys->Refseq; thys->Returned = ajTrue; return ajTrue; } if(ajRefseqinRead(thys->Refseqin, thys->Refseq)) { thys->Count++; thys->Totterms++; *Prefseq = thys->Refseq; thys->Returned = ajTrue; ajDebug("ajRefseqallNext success\n"); return ajTrue; } *Prefseq = NULL; ajDebug("ajRefseqallNext failed\n"); ajRefseqallClear(thys); return ajFalse; } ID refseqinformatFind TY static MO ajrefseqread LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Sets iformat as the recognised format, and returns ajTrue. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool refseqinformatFind(const AjPStr format, ajint* iformat) { AjPStr tmpformat = NULL; ajuint i = 0; /* ajDebug("refseqinformatFind '%S'\n", format); */ if(!ajStrGetLen(format)) return ajFalse; ajStrAssignS(&tmpformat, format); ajStrFmtLower(&tmpformat); for(i=0; refseqinFormatDef[i].Name; i++) { /* ajDebug("test %d '%s' \n", i, refseqinFormatDef[i].Name); */ if(ajStrMatchCaseC(tmpformat, refseqinFormatDef[i].Name)) { *iformat = i; ajStrDel(&tmpformat); /* ajDebug("found '%s' at %d\n", refseqinFormatDef[i].Name, i); */ return ajTrue; } } ajErr("Unknown input format '%S'", format); ajStrDel(&tmpformat); return ajFalse; } ID ajRefseqinformatTerm TY public MO ajrefseqread LB core XX DE Tests whether a reference sequence data input format term is known XX PN [1] PA r term const AjPStr PD Format term EDAM ID PX RT AjBool RD ajTrue if term was accepted RX // AjBool ajRefseqinformatTerm(const AjPStr term) { ajuint i; for(i=0; refseqinFormatDef[i].Name; i++) if(ajStrMatchCaseC(term, refseqinFormatDef[i].Obo)) return ajTrue; return ajFalse; } ID ajRefseqinformatTest TY public MO ajrefseqread LB core XX DE Tests whether a named reference sequence data input format is known XX PN [1] PA r format const AjPStr PD Format PX RT AjBool RD ajTrue if term was accepted RX // AjBool ajRefseqinformatTest(const AjPStr format) { ajuint i; for(i=0; refseqinFormatDef[i].Name; i++) if(ajStrMatchCaseC(format, refseqinFormatDef[i].Name)) return ajTrue; return ajFalse; } ID ajRefseqoutWrite TY public MO ajrefseqwrite LB core XX DE Write refseq data in a named format XX PN [1] PA u outf AjPOutfile PD Output file PX PN [2] PA r refseq const AjPRefseq PD Refseq data object PX RT AjBool RD True on success RX // AjBool ajRefseqoutWrite(AjPOutfile outf, const AjPRefseq refseq) { ajuint i = ajOutfileGetFormatindex(outf); AjPFile outfile = ajOutfileGetFile(outf); return (*refseqoutFormatDef[i].Write)(outfile, refseq); } ID refseqoutWriteEmbl TY static MO ajrefseqwrite LB core XX DE Write reference sequence data in embl format XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r refseq const AjPRefseq PD Reference sequence data object PX RT AjBool RD True on success RX // static AjBool refseqoutWriteEmbl(AjPFile outf, const AjPRefseq refseq) { AjPSeq seq = NULL; AjPSeqout seqout = NULL; if(!outf) return ajFalse; if(!refseq) return ajFalse; seq = ajSeqNewRefseq(refseq); seqout = ajSeqoutNewFile(outf); ajSeqoutSetFormatC(seqout, "embl"); ajSeqoutWriteSeq(seqout, seq); ajSeqDel(&seq); ajSeqoutDel(&seqout); return ajTrue; } ID refseqoutWriteList TY static MO ajrefseqwrite LB core XX DE Write reference sequence object id to file XX PN [1] PA u outf AjPFile PD Output reference sequence file PX PN [2] PA r refseq const AjPRefseq PD Reference sequence object PX RT AjBool RD True on success RX // static AjBool refseqoutWriteList(AjPFile outf, const AjPRefseq refseq) { if(!outf) return ajFalse; if(!refseq) return ajFalse; if(ajStrGetLen(refseq->Db)) ajFmtPrintF(outf, "%S:%S\n", refseq->Db, refseq->Id); else ajFmtPrintF(outf, "%S\n", refseq->Id); return ajTrue; } ID ajRefseqoutprintBook TY public MO ajrefseqwrite LB core XX DE Reports the reference sequence format internals as Docbook text XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajRefseqoutprintBook(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, "The supported reference sequence output " "formats are summarised " "in the table below. The columns are as follows: " "Output format (format name), " "Description (short description of " "the format). \n\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=0; refseqoutFormatDef[i].Name; i++) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", refseqoutFormatDef[i].Name); ajFmtPrintF(outf, " \n", refseqoutFormatDef[i].Desc); } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Reference sequence output formats
Output FormatDescription
%s%s
\n"); return; } ID ajRefseqoutprintHtml TY public MO ajrefseqwrite LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajRefseqoutprintHtml(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, ""); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Reference sequence output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "RefseqFormat {\n"); for(i=0; refseqoutFormatDef[i].Name; i++) { ajFmtPrintF(outf, "\n", refseqoutFormatDef[i].Name, refseqoutFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajRefseqoutprintText TY public MO ajrefseqwrite LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajRefseqoutprintText(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Reference sequence output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "RefseqFormat {\n"); for(i=0; refseqoutFormatDef[i].Name; i++) { if(full) ajFmtPrintF(outf, " %-12s \"%s\"\n", refseqoutFormatDef[i].Name, refseqoutFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajRefseqoutprintWiki TY public MO ajrefseqwrite LB core XX DE Reports the reference sequence output format internals as wikitext XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajRefseqoutprintWiki(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!" "class=\"unsortable\"|Description\n"); for(i=0; refseqoutFormatDef[i].Name; i++) { ajFmtPrintF(outf, "|-\n"); ajStrAssignC(&namestr, refseqoutFormatDef[i].Name); for(j=i+1; refseqoutFormatDef[j].Name; j++) { if(refseqoutFormatDef[j].Write == refseqoutFormatDef[i].Write) { ajFmtPrintAppS(&namestr, "
%s", refseqoutFormatDef[j].Name); ajWarn("Reference sequence output format '%s' same as '%s' " "but not alias", refseqoutFormatDef[j].Name, refseqoutFormatDef[i].Name); } } ajFmtPrintF(outf, "|%S||%s\n", namestr, refseqoutFormatDef[i].Desc); } ajFmtPrintF(outf, "|}\n"); ajStrDel(&namestr); return; } ID ajRefseqoutformatFind TY public MO ajrefseqwrite LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Sets iformat as the recognised format, and returns ajTrue. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // AjBool ajRefseqoutformatFind(const AjPStr format, ajint* iformat) { AjPStr tmpformat = NULL; ajuint i = 0; /* ajDebug("ajRefseqoutformatFind '%S'\n", format); */ if(!ajStrGetLen(format)) return ajFalse; ajStrAssignS(&tmpformat, format); ajStrFmtLower(&tmpformat); for(i=0; refseqoutFormatDef[i].Name; i++) { /* ajDebug("test %d '%s' \n", i, refseqoutFormatDef[i].Name); */ if(ajStrMatchCaseC(tmpformat, refseqoutFormatDef[i].Name)) { *iformat = i; ajStrDel(&tmpformat); /* ajDebug("found '%s' at %d\n", refseqoutFormatDef[i].Name, i); */ return ajTrue; } } ajErr("Unknown output format '%S'", format); ajStrDel(&tmpformat); *iformat = 0; return ajFalse; } ID ajRefseqoutformatTest TY public MO ajrefseqwrite LB core XX DE Tests whether a named refseq output format is known XX PN [1] PA r format const AjPStr PD Format PX RT AjBool RD ajTrue if formats was accepted RX // AjBool ajRefseqoutformatTest(const AjPStr format) { ajint i; for (i=0; refseqoutFormatDef[i].Name; i++) if(ajStrMatchCaseC(format, refseqoutFormatDef[i].Name)) return ajTrue; return ajFalse; } ID ajRefseqoutExit TY public MO ajrefseqwrite LB core XX DE Cleans up reference sequence output internal memory XX RT void RD RX // void ajRefseqoutExit(void) { return; } ID ajRegComp TY public MO ajreg LB core XX DE Compiles a regular expression. XX PN [1] PA r rexp const AjPStr PD Regular expression string. PX RT AjPRegexp RD Compiled regular expression. RX // AjPRegexp ajRegComp(const AjPStr rexp) { return ajRegCompC(ajStrGetPtr(rexp)); } ID ajRegCompC TY public MO ajreg LB core XX DE Compiles a regular expression. XX PN [1] PA r rexp const char* PD Regular expression character string. PX RT AjPRegexp RD Compiled regular expression. RX // AjPRegexp ajRegCompC(const char* rexp) { AjPRegexp ret; int options = 0; int errpos = 0; const char *errptr = NULL; const unsigned char *tableptr = NULL; AJNEW0(ret); AJCNEW0(ret->ovector, AJREG_OVECSIZE); ret->ovecsize = AJREG_OVECSIZE/3; ret->pcre = pcre_compile(rexp, options, &errptr, &errpos, tableptr); if(!ret->pcre) { ajErr("Failed to compile regular expression '%s' at position %d: %s", rexp, errpos, errptr); AJFREE(ret); return NULL; } regAlloc += sizeof(ret); regCount ++; regTotal ++; /*ajDebug("ajRegCompC %x size %d regexp '%s'\n", ret, (int) sizeof(ret), rexp);*/ return ret; } ID ajRegCompCase TY public MO ajreg LB core XX DE Compiles a case-insensitive regular expression. XX PN [1] PA r rexp const AjPStr PD Regular expression string. PX RT AjPRegexp RD Compiled regular expression. RX // AjPRegexp ajRegCompCase(const AjPStr rexp) { return ajRegCompCaseC(ajStrGetPtr(rexp)); } ID ajRegCompCaseC TY public MO ajreg LB core XX DE Compiles a case-insensitive regular expression. XX PN [1] PA r rexp const char* PD Regular expression character string. PX RT AjPRegexp RD Compiled regular expression. RX // AjPRegexp ajRegCompCaseC(const char* rexp) { AjPRegexp ret; int options = PCRE_CASELESS; int errpos = 0; const char *errptr = NULL; const unsigned char *tableptr = NULL; AJNEW0(ret); AJCNEW0(ret->ovector, AJREG_OVECSIZE); ret->ovecsize = AJREG_OVECSIZE/3; ret->pcre = pcre_compile(rexp, options, &errptr, &errpos, tableptr); if(!ret->pcre) { ajErr("Failed to compile regular expression '%s' at position %d: %s", rexp, errpos, errptr); AJFREE(ret); return NULL; } regAlloc += sizeof(ret); regCount ++; regTotal ++; /*ajDebug("ajRegCompCaseC %x size %d regexp '%s'\n", ret, (int) sizeof(ret), rexp);*/ return ret; } ID ajRegExec TY public MO ajreg LB core XX DE Execute a regular expression search. DE The expression must first have been compiled with ajRegComp or ajRegCompC. DE DE Internal data structures in the expression will be set to substrings DE which other functions can retrieve. XX PN [1] PA u prog AjPRegexp PD Compiled regular expression. PX PN [2] PA r str const AjPStr PD String to be compared. PX RT AjBool RD ajTrue if a match was found. RX // AjBool ajRegExec(AjPRegexp prog, const AjPStr str) { int startoffset = 0; int options = 0; int status = 0; status = pcre_exec(prog->pcre, prog->extra, ajStrGetPtr(str), ajStrGetLen(str), startoffset, options, prog->ovector, 3*prog->ovecsize); if(status >= 0) { prog->orig = ajStrGetPtr(str); if(status == 0) ajWarn("ajRegExec too many substrings"); return ajTrue; } if(status < -1) /* -1 is a simple fail to match */ { /* others are recursion limits etc. */ ajDebug("ajRegExec returned unexpected status '%d'\n", status); prog->orig = ajStrGetPtr(str); /* needed for the trace */ ajRegTrace(prog); } prog->orig = NULL; return ajFalse; } ID ajRegExecC TY public MO ajreg LB core XX DE Execute a regular expression search. DE The expression must first have been compiled with ajRegComp or ajRegCompC. DE DE Internal data structures in the expression will be set to substrings DE which other functions can retrieve. XX PN [1] PA u prog AjPRegexp PD Compiled regular expression. PX PN [2] PA r str const char* PD String to be compared. PX RT AjBool RD ajTrue if a match was found. RX // AjBool ajRegExecC(AjPRegexp prog, const char* str) { int startoffset = 0; int options = 0; int status = 0; status = pcre_exec(prog->pcre, prog->extra, str, strlen(str), startoffset, options, prog->ovector, 3*prog->ovecsize); if(status >= 0) { prog->orig = str; if(status == 0) ajWarn("ajRegExecC too many substrings"); return ajTrue; } if(status < -1) /* -1 is a simple fail to match */ { /* others are recursion limits etc. */ ajDebug("ajRegExecC returned unexpected status '%d'\n", status); prog->orig = str; /* needed for the trace */ ajRegTrace(prog); } prog->orig = NULL; return ajFalse; } ID ajRegExecall TY public MO ajreg LB core XX DE Execute a regular expression search using the alternative 'DFA' algorithm DE which generates all matches instead of the perl-compatible maximum match. DE DE The expression must first have been compiled with ajRegComp or ajRegCompC. DE DE Internal data structures in the expression will be set to substrings DE which other functions can retrieve. XX PN [1] PA u prog AjPRegexp PD Compiled regular expression. PX PN [2] PA r str const AjPStr PD String to be compared. PX RT AjBool RD ajTrue if a match was found. RX // AjBool ajRegExecall(AjPRegexp prog, const AjPStr str) { int startoffset = 0; int options = 0; if(!regDfaWorkspace) AJCNEW(regDfaWorkspace, regDfaWsCount); prog->matches = pcre_dfa_exec(prog->pcre, prog->extra, ajStrGetPtr(str), ajStrGetLen(str), startoffset, options, prog->ovector, 3*prog->ovecsize, regDfaWorkspace, regDfaWsCount); if(prog->matches >= 0) { prog->orig = ajStrGetPtr(str); if(prog->matches == 0) ajWarn("ajRegExecall too many substrings"); return ajTrue; } if(prog->matches < -1) /* -1 is a simple fail to match */ { /* others are recursion limits etc. */ ajDebug("ajRegExecall returned unexpected status '%d'\n", prog->matches); prog->orig = ajStrGetPtr(str); /* needed for the trace */ ajRegTrace(prog); } prog->orig = NULL; return ajFalse; } ID ajRegExecallC TY public MO ajreg LB core XX DE Execute a regular expression search using the alternative 'DFA' algorithm DE which generates all matches instead of the perl-compatible maximum match. DE DE The expression must first have been compiled with ajRegComp or ajRegCompC. DE DE Internal data structures in the expression will be set to substrings DE which other functions can retrieve. XX PN [1] PA u prog AjPRegexp PD Compiled regular expression. PX PN [2] PA r str const char* PD String to be compared. PX RT AjBool RD ajTrue if a match was found. RX // AjBool ajRegExecallC(AjPRegexp prog, const char* str) { int startoffset = 0; int options = 0; if(!regDfaWorkspace) AJCNEW(regDfaWorkspace, regDfaWsCount); prog->matches = pcre_dfa_exec(prog->pcre, prog->extra, str, strlen(str), startoffset, options, prog->ovector, 3*prog->ovecsize, regDfaWorkspace, regDfaWsCount); if(prog->matches >= 0) { prog->orig = str; if(prog->matches == 0) ajWarn("ajRegExecallC too many substrings"); return ajTrue; } if(prog->matches < -1) /* -1 is a simple fail to match */ { /* others are recursion limits etc. */ ajDebug("ajRegExecallC returned unexpected status '%d'\n", prog->matches); prog->orig = str; /* needed for the trace */ ajRegTrace(prog); } prog->orig = NULL; return ajFalse; } ID ajRegGetMatches TY public MO ajreg LB core XX DE After a successful regular expression match, uses the regular DE expression and the original string to calculate the offset DE of the match from the start of the string. XX PN [1] PA r rp const AjPRegexp PD Compiled regular expression. PX RT ajint RD Number of matches found RX // ajint ajRegGetMatches(const AjPRegexp rp) { return (rp->matches); } ID ajRegOffset TY public MO ajreg LB core XX DE After a successful regular expression match, uses the regular DE expression and the original string to calculate the offset DE of the match from the start of the string. DE DE This information is normally lost during processing. XX PN [1] PA r rp const AjPRegexp PD Compiled regular expression. PX RT ajint RD Offset of match from start of string. RD -1 if the string and the expression do not match. RX // ajint ajRegOffset(const AjPRegexp rp) { return (rp->ovector[0]); } ID ajRegOffsetI TY public MO ajreg LB core XX DE After a successful regular expression match, uses the regular DE expression and the original string to calculate the offset DE of a substring from the start of the string. DE DE This information is normally lost during processing. XX PN [1] PA r rp const AjPRegexp PD Compiled regular expression. PX PN [2] PA r isub ajint PD Substring number. PX RT ajint RD Offset of match from start of string. RD -1 if the string and the expression do not match. RX // ajint ajRegOffsetI(const AjPRegexp rp, ajint isub) { if(isub < 0) ajErr("Invalid substring number %d", isub); if(isub >= (rp->ovecsize)) ajErr("Invalid substring number %d", isub); return (rp->ovector[isub*2]); } ID ajRegLenI TY public MO ajreg LB core XX DE After a successful comparison, returns the length of a substring. XX PN [1] PA r rp const AjPRegexp PD Compiled regular expression. PX PN [2] PA r isub ajint PD Substring number. PX RT ajint RD Substring length, or 0 if not found. RX // ajint ajRegLenI(const AjPRegexp rp, ajint isub) { ajint istart; ajint iend; istart = 2*isub; iend = istart+1; if(isub < 0) return 0; if(isub >= rp->ovecsize) return 0; if(rp->ovector[istart] < 0) return 0; return (rp->ovector[iend] - rp->ovector[istart]); } ID ajRegPost TY public MO ajreg LB core XX DE After a successful match, returns the remainder of the string. XX PN [1] PA r rp const AjPRegexp PD Compiled regular expression. PX PN [2] PA w post AjPStr* PD String to hold the result. PX RT AjBool RD ajTrue on success. RX // AjBool ajRegPost(const AjPRegexp rp, AjPStr* post) { if(rp->ovector[1]) { ajStrAssignC(post, &rp->orig[rp->ovector[1]]); return ajTrue; } ajStrDelStatic(post); return ajFalse; } ID ajRegPostC TY public MO ajreg LB core XX DE After a successful match, returns the remainder of the string. DE Result is a character string, which is set to point to the internal DE string data. This in turn is part of the original string. If this DE changes then the results are undefined. XX PN [1] PA r rp const AjPRegexp PD Compiled regular expression. PX PN [2] PA w post const char** PD Character string to hold the result. PX RT AjBool RD ajTrue on success. RX // AjBool ajRegPostC(const AjPRegexp rp, const char** post) { if(rp->ovector[1]) { *post = &rp->orig[rp->ovector[1]]; return ajTrue; } *post = 0; return ajFalse; } ID ajRegPre TY public MO ajreg LB core XX DE After a successful match, returns the string before the match. XX PN [1] PA r rp const AjPRegexp PD Compiled regular expression. PX PN [2] PA w dest AjPStr* PD String to hold the result. PX RT AjBool RD ajTrue on success. RX // AjBool ajRegPre(const AjPRegexp rp, AjPStr* dest) { ajint ilen; ilen = rp->ovector[0]; ajStrSetRes(dest, ilen+1); if(ilen) { memmove((*dest)->Ptr, rp->orig, ilen); (*dest)->Len = ilen; (*dest)->Ptr[ilen] = '\0'; return ajTrue; } ajStrDelStatic(dest); return ajFalse; } ID ajRegSubI TY public MO ajreg LB core XX DE After a successful match, returns a substring. XX PN [1] PA r rp const AjPRegexp PD Compiled regular expression. PX PN [2] PA r isub ajint PD Substring number. PX PN [3] PA w dest AjPStr* PD String to hold the result. PX RT AjBool RD ajTrue if a substring was defined RD ajFalse if the substring is not matched RD ajFalse if isub is out of range RX // AjBool ajRegSubI(const AjPRegexp rp, ajint isub, AjPStr* dest) { ajint ilen; ajint istart; ajint iend; istart = 2*isub; iend = istart+1; if(isub < 0) { ajStrDelStatic(dest); return ajFalse; } if(isub >= rp->ovecsize) { ajStrDelStatic(dest); return ajFalse; } if(rp->ovector[istart] < 0) { ajStrDelStatic(dest); return ajFalse; } ilen = rp->ovector[iend] - rp->ovector[istart]; ajStrSetRes(dest, ilen+1); if(ilen) memmove((*dest)->Ptr, &rp->orig[rp->ovector[istart]], ilen); (*dest)->Len = ilen; (*dest)->Ptr[ilen] = '\0'; return ajTrue; } ID ajRegFree TY public MO ajreg LB core XX DE Clears and frees a compiled regular expression. XX PN [1] PA d pexp AjPRegexp* PD Compiled regular expression. PX RT void RD RX // void ajRegFree(AjPRegexp* pexp) { AjPRegexp exp; if(!pexp) return; if(!*pexp) return; exp = *pexp; /* ajDebug("ajRegFree %x size regexp %d\n", exp, (ajint) sizeof(exp)); */ regFreeCount += 1; regFree += sizeof(*exp); if(exp->pcre) regFree += sizeof(exp->pcre); if(exp->extra) regFree += sizeof(exp->extra); regTotal --; AJFREE(exp->pcre); AJFREE(exp->extra); AJFREE(exp->ovector); AJFREE(*pexp); return; } ID ajRegTrace TY public MO ajreg LB core XX DE Traces a compiled regular expression with debug calls. XX PN [1] PA r rexp const AjPRegexp PD Compiled regular expression. PX RT void RD RX // void ajRegTrace(const AjPRegexp rexp) { ajint isub; ajint ilen; ajint ipos; ajint istart; ajint iend; static AjPStr str = NULL; ajDebug(" REGEXP trace\n"); if (!rexp->orig) ajDebug("original string not saved - unable to trace string values\n"); for(isub=0; isub < rexp->ovecsize; isub++) { istart = 2*isub; iend = istart+1; if (!rexp->orig) { if(!isub) ajDebug("original string from %d .. %d\n", rexp->ovector[istart], rexp->ovector[iend]); else ajDebug("substring %2d from %d .. %d\n", isub, rexp->ovector[istart], rexp->ovector[iend]); continue; } if(rexp->ovector[iend] >= rexp->ovector[istart]) { ilen = rexp->ovector[iend] - rexp->ovector[istart]; ajStrSetRes(&str, ilen+1); memmove(str->Ptr, &rexp->orig[rexp->ovector[istart]], ilen); str->Len = ilen; str->Ptr[ilen] = '\0'; if(!isub) { ajDebug(" original string '%s'\n", rexp->orig); ajDebug(" string match '%S'\n", str); } else { ipos = rexp->ovector[istart]; ajDebug(" substring %2d '%S' at %d\n", isub, str, ipos); } } } ajDebug("\n"); ajStrDel(&str); return; } ID ajRegExit TY public MO ajreg LB core XX DE Prints a summary of regular expression (AjPRegexp) usage with debug calls XX RT void RD RX // void ajRegExit(void) { if(regDfaWorkspace) AJFREE(regDfaWorkspace); ajDebug("Regexp usage (bytes): %Ld allocated, %Ld freed, %Ld in use " "(sizes change)\n", regAlloc, regFree, (regAlloc - regFree)); ajDebug("Regexp usage (number): %Ld allocated, %Ld freed %Ld in use\n", regCount, regFreeCount, regTotal); return; } ID reportFormat TY list MO ajreport LB core XX DE Functions to write feature reports XX // static ReportOFormat reportFormat[] = { /* Name Description */ /* Alias MinTags Showseq Nuc Prot Header Function */ /* standard feature formats */ /* {"unknown", "Unknown feature format", AJFALSE, 0, AJFALSE, AJTRUE, AJFALSE, AJFALSE, reportWriteSimple},*/ {"simple", "Simple report", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJTRUE, &reportWriteSimple}, {"embl", "EMBL feature format", AJFALSE, 0, AJFALSE, AJTRUE, AJFALSE, AJFALSE, &reportWriteEmbl}, {"genbank", "Genbank feature format", AJFALSE, 0, AJFALSE, AJTRUE, AJFALSE, AJFALSE, &reportWriteGenbank}, {"gff", "GFF3 feature format", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJFALSE, &reportWriteGff}, {"gff3", "GFF3 feature format (alias)", AJTRUE, 0, AJFALSE, AJTRUE, AJTRUE, AJFALSE, &reportWriteGff}, {"pir", "PIR feature format", AJFALSE, 0, AJFALSE, AJFALSE, AJTRUE, AJFALSE, &reportWritePir}, {"swiss", "Swissprot feature format", AJFALSE, 0, AJFALSE, AJFALSE, AJTRUE, AJFALSE, &reportWriteSwiss}, {"dasgff", "DAS GFF feature format", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJFALSE, &reportWriteDasgff}, /* trace for debug */ {"debug", "Debugging trace of full internal data content", AJFALSE, 0, AJTRUE, AJTRUE, AJTRUE, AJTRUE, &reportWriteTrace}, /* list file for input to other programs */ {"listfile", "EMBOSS list file of sequence USAs with ranges", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJTRUE, &reportWriteListFile}, /* feature reports */ {"dbmotif", "Motif database hits", AJFALSE, 0, AJTRUE, AJTRUE, AJTRUE, AJTRUE, &reportWriteDbMotif}, {"diffseq", "Differences between a pair of sequences", AJFALSE, 7, AJTRUE, AJTRUE, AJTRUE, AJTRUE, &reportWriteDiffseq}, /* cirdna/lindna input format - may need work */ {"draw", "lindna/cirdna input format", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJTRUE, &reportWriteDraw}, {"restrict", "lindna/cirdna input format for restriction sites", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJTRUE, &reportWriteRestrict}, {"excel", "Tab-delimited file for import to Microsoft Excel", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJFALSE, &reportWriteExcel}, {"feattable", "EMBL format feature table with internal tags", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJFALSE, &reportWriteFeatTable}, {"motif", "Motif report", AJFALSE, 0, AJTRUE, AJTRUE, AJTRUE, AJTRUE, &reportWriteMotif}, {"nametable", "Simple table with sequence name", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJTRUE, &reportWriteNameTable}, {"regions", "Annotated sequence regions", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJTRUE, &reportWriteRegions}, {"seqtable", "Simple table with sequence on each line", AJFALSE, 0, AJTRUE, AJTRUE, AJTRUE, AJTRUE, &reportWriteSeqTable}, {"srs", "Simple report format for SRS", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJTRUE, &reportWriteSrs}, {"table", "Simple table", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJTRUE, &reportWriteTable}, {"tagseq", "Sequence with features marked below", AJFALSE, 0, AJFALSE, AJTRUE, AJTRUE, AJTRUE, &reportWriteTagseq}, {NULL, NULL, AJFALSE, 0, AJFALSE, AJFALSE, AJFALSE, AJFALSE, NULL} }; ID reportWriteTrace TY static MO ajreport LB core XX DE Writes a report in Trace format XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteTrace(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { ajReportWriteHeader(thys, ftable, seq); ajFmtPrintF(thys->File, "Trace output\n"); ajFmtPrintF(thys->File, "Name: '%S'\n", thys->Name); ajFmtPrintF(thys->File, "Type: '%S'\n", thys->Type); ajFmtPrintF(thys->File, "Formatstr: '%S'\n", thys->Formatstr); ajFmtPrintF(thys->File, "Feattable (internal): %u features\n", ajFeattableGetSize(thys->Fttable)); if (thys->Fttable) ajFeattablePrint(thys->Fttable, thys->File); ajFmtPrintF(thys->File, "FtQuery: '%S'\n", ajFeattabOutFilename(thys->Ftquery)); ajFmtPrintF(thys->File, "Extension: '%S'\n", thys->Extension); ajFmtPrintF(thys->File, "File: '%F'\n", thys->File); ajFmtPrintF(thys->File, "Tagnames: %Lu\n", ajListGetLength(thys->Tagnames)); ajFmtPrintF(thys->File, "Tagprints: %Lu\n", ajListGetLength(thys->Tagprints)); ajFmtPrintF(thys->File, "Tagtypes: %Lu\n", ajListGetLength(thys->Tagtypes)); /* ajFmtPrintF(thys->File, "Header: '%S'\n", thys->Header);*/ /* ajFmtPrintF(thys->File, "SubHeader: '%S'\n", thys->SubHeader);*/ /* ajFmtPrintF(thys->File, "Tail: '%S'\n", thys->Tail);*/ /* ajFmtPrintF(thys->File, "SubTail: '%S'\n", thys->SubTail);*/ ajFmtPrintF(thys->File, "FileNames: %Lu\n", ajListGetLength(thys->FileNames)); ajFmtPrintF(thys->File, "FileTypes: %Lu\n", ajListGetLength(thys->FileTypes)); ajFmtPrintF(thys->File, "Precision: %d\n", thys->Precision); ajFmtPrintF(thys->File, "Showacc: %B\n", thys->Showacc); ajFmtPrintF(thys->File, "Showdes: %B\n", thys->Showdes); ajFmtPrintF(thys->File, "Showusa: %B\n", thys->Showusa); ajFmtPrintF(thys->File, "Showscore: %B\n", thys->Showscore); ajFmtPrintF(thys->File, "Showstrand: %B\n", thys->Showstrand); ajFmtPrintF(thys->File, "Multi: %B\n", thys->Multi); ajFmtPrintF(thys->File, "Mintags: %d\n", thys->Mintags); ajFmtPrintF(thys->File, "CountSeq: %d\n", thys->CountSeq); ajFmtPrintF(thys->File, "CountHit: %d\n", thys->CountHit); ajFmtPrintF(thys->File, "TotHits: %d\n", thys->TotHits); ajFmtPrintF(thys->File, "MaxHitAll: %d\n", thys->MaxHitAll); ajFmtPrintF(thys->File, "MaxHitSeq: %d\n", thys->MaxHitSeq); ajFmtPrintF(thys->File, "Format: %d\n", thys->Format); ajFmtPrintF(thys->File, "\nFeattable (external): %u features\n", ajFeattableGetSize(ftable)); ajFeattablePrint(ftable, thys->File); ajReportWriteTail(thys, ftable, seq); return; } ID reportWriteEmbl TY static MO ajreport LB core XX DE Writes a report in EMBL format XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteEmbl(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { /* ajFmtPrintF(thys->File, "#EMBL output\n"); */ ajFeattabOutDel(&thys->Ftquery); thys->Ftquery = ajFeattabOutNewCSF("embl", ajSeqGetNameS(seq), ajStrGetPtr(thys->Type), thys->File); if(!ajFeattableWrite(thys->Ftquery, ftable)) ajWarn("ajReportWriteEmbl features output failed format: 'embl'"); return; } ID reportWriteGenbank TY static MO ajreport LB core XX DE Writes a report in Genbank format XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD const RX // static void reportWriteGenbank(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { /* ajFmtPrintF(thys->File, "#Genbank output\n"); */ ajFeattabOutDel(&thys->Ftquery); thys->Ftquery = ajFeattabOutNewCSF("genbank", ajSeqGetNameS(seq), ajStrGetPtr(thys->Type), thys->File); if(!ajFeattableWrite(thys->Ftquery, ftable)) ajWarn("ajReportWriteGenbank features output failed format: 'genbank'"); return; } ID reportWriteGff TY static MO ajreport LB core XX DE Writes a report in GFF format XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteGff(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { ajFeattabOutDel(&thys->Ftquery); thys->Ftquery = ajFeattabOutNewCSF("gff", ajSeqGetNameS(seq), ajStrGetPtr(thys->Type), thys->File); if(!ajFeattableWrite(thys->Ftquery, ftable)) ajWarn("ajReportWriteGff features output failed format: 'gff'"); return; } ID reportWritePir TY static MO ajreport LB core XX DE Writes a report in PIR format XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWritePir(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { ajFeattabOutDel(&thys->Ftquery); thys->Ftquery = ajFeattabOutNewCSF("pir", ajSeqGetNameS(seq), ajStrGetPtr(thys->Type), thys->File); if(!ajFeattableWrite(thys->Ftquery, ftable)) ajWarn("ajReportWritePir features output failed format: 'pir'"); return; } ID reportWriteSwiss TY static MO ajreport LB core XX DE Writes a report in SwissProt format XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteSwiss(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { ajFeattabOutDel(&thys->Ftquery); thys->Ftquery = ajFeattabOutNewCSF("swissprot", ajSeqGetNameS(seq), ajStrGetPtr(thys->Type), thys->File); if(!ajFeattableWrite(thys->Ftquery, ftable)) ajWarn("ajReportWriteSwiss features output failed format: 'swissprot'"); return; } ID reportWriteDasgff TY static MO ajreport LB core XX DE Writes a report in DAS GFF format XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteDasgff(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { if(!thys->CountSeq) { ajFeattabOutDel(&thys->Ftquery); thys->Ftquery = ajFeattabOutNewCSF("dasgff", ajSeqGetNameS(seq), ajStrGetPtr(thys->Type), thys->File); } else ajFeattabOutSetSeqname(thys->Ftquery, ajSeqGetNameS(seq)); if(!ajFeattableWrite(thys->Ftquery, ftable)) ajWarn("ajReportWriteDasgff features output failed format: 'dasgff'"); return; } ID reportWriteDbMotif TY static MO ajreport LB core XX DE Writes a report in DbMotif format DE DE Format:<br> DE Length = [length] <br> DE Start = position [start] of sequence <br> DE End = position [start] of sequence <br> DE ... other tags ... <br> DE [sequence] <br> DE [start and end numbered below sequence] <br> DE DE Data reported: Length, Start, End, Sequence (5 bases around feature) DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: <br> DE all tags reported as name = value XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteDbMotif(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; ajuint istart = 0; ajuint iend = 0; ajuint ilen = 0; AjPStr subseq = NULL; AjPStr seqstr = NULL; AjPStr tmpstr = NULL; ajuint ntags; static AjPStr* tagtypes = NULL; static AjPStr* tagnames = NULL; static AjPStr* tagprints = NULL; static ajuint* tagsizes = NULL; ajuint j = 0; AjPStr tagval = NULL; ajuint jstart; ajuint jend; char strand; const AjPStr seqalias; outf = thys->File; if(ajSeqIsReversedTrue(seq)) { ajStrAssignS(&seqstr, ajSeqGetSeqS(seq)); ajSeqstrReverse(&seqstr); seqalias = seqstr; } else seqalias = ajSeqGetSeqS(seq); ajReportWriteHeader(thys, ftable, seq); ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); strand = ajFeatGetStrand(feature); istart = feature->Start; iend = feature->End; ilen = iend - istart + 1; jstart = AJMAX(6, istart); jstart -= 6; jend = AJMIN(ajSeqGetLen(seq), iend+4); ajStrAssignResC(&tmpstr, ilen+10, ""); j=istart+1; while(j++File; if(ajSeqIsReversedTrue(seq)) { ajStrAssignS(&seqstr, ajSeqGetSeqS(seq)); ajSeqstrReverse(&seqstr); seqalias = seqstr; } else seqalias = ajSeqGetSeqS(seq); ajReportWriteHeader(thys, ftable, seq); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); strand = ajFeatGetStrand(feature); istart = feature->Start; iend = feature->End; ilen = iend - istart + 1; if(ilen == 2 && (feature->Flags & AJFEATFLAG_BETWEEN_SEQ)) ilen = 0; ajStrAssignSubS(&subseq, seqalias, istart-1, iend-1); if(strand == '-') ajSeqstrReverse(&subseq); /* ajStrFmtUpper(&subseq); */ i++; if(!ajFeatGetNoteC(feature, "start", &tagval)) jstart = 0; else ajStrToUint(tagval, &jstart); if(!ajFeatGetNoteC(feature, "end", &tagval)) jend = 0; else ajStrToUint(tagval, &jend); if(!ajFeatGetNoteC(feature, "length", &tagval)) jlen = 0; else ajStrToUint(tagval, &jlen); if(!ajFeatGetNoteC(feature, "strand", &tagval)) jstrand = '+'; else jstrand = ajStrGetCharFirst(tagval); if(!ajFeatGetNoteC(feature, "name", &jname)) ajStrAssignClear(&jname); if(ilen > 0) { if(strand == '-') ajFmtPrintF(outf, "\n%S %d-%d Length: %d (Reversed)\n", ajReportGetSeqnameSeq(thys, seq), istart, iend, ilen); else ajFmtPrintF(outf, "\n%S %d-%d Length: %d\n", ajReportGetSeqnameSeq(thys, seq), istart, iend, ilen); ifeat = 1; while(ajFeatGetNoteCI(feature, "first_feature", ifeat++, &firstfeat)) ajFmtPrintF(outf, "Feature: %S\n", firstfeat); ajFmtPrintF(outf, "Sequence: %S\n", subseq); } else { ajFmtPrintF(outf, "\n%S %d Length: %d\n", ajReportGetSeqnameSeq(thys, seq), istart, ilen); ifeat = 1; while(ajFeatGetNoteCI(feature, "first_feature", ifeat++, &firstfeat)) ajFmtPrintF(outf, "Feature: %S\n", firstfeat); ajFmtPrintF(outf, "Sequence: \n"); } if(!ajFeatGetNoteC(feature, "sequence", &subseq)) ajStrAssignClear(&subseq); if(jlen > 0) { ajFmtPrintF(outf, "Sequence: %S\n", subseq); ifeat = 1; while(ajFeatGetNoteCI(feature, "second_feature", ifeat++, &secondfeat)) ajFmtPrintF(outf, "Feature: %S\n", secondfeat); if(jstrand == '-') ajFmtPrintF(outf, "%S %d-%d Length: %d (Reversed)\n", jname, jstart, jend, jlen); else ajFmtPrintF(outf, "%S %d-%d Length: %d\n", jname, jstart, jend, jlen); } else { ajFmtPrintF(outf, "Sequence: \n"); ifeat = 1; while(ajFeatGetNoteCI(feature, "second_feature", ifeat++, &secondfeat)) ajFmtPrintF(outf, "Feature: %S\n", secondfeat); if(jstrand == '-') ajFmtPrintF(outf, "%S %d Length: %d (Reversed)\n", jname, jstart, jlen); else ajFmtPrintF(outf, "%S %d Length: %d\n", jname, jstart, jlen); } ajStrDelStatic(&tmpstr); } ajReportWriteTail(thys, ftable, seq); ajStrDel(&subseq); ajStrDel(&seqstr); ajStrDel(&tmpstr); ajStrDel(&tagval); ajStrDel(&jname); ajListIterDel(&iterft); return; } ID reportWriteDraw TY static MO ajreport LB core XX DE Writes a report in Draw format, for use as input to cirdna or lindna DE DE Format:<br> DE group<br> DE <br> DE label<br> DE tick [tagvalue] [start] 8<br> DE endlabel<br> DE label<br> DE tick [tagvalue] [end] 3<br> DE endlabel<br> DE <br> DE endgroup<br> DE DE Data reported: DE DE Tags required: DE DE Tags used: DE DE Tags reported: XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteDraw(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; AjPStr subseq = NULL; AjPStr tagval = NULL; ajint istart = 0; ajint iend = 0; outf = thys->File; ajReportWriteHeader(thys, ftable, seq); ajFmtPrintF(outf, "Start %d\n", ajSeqGetBegin(seq) + ajSeqGetOffset(seq)); ajFmtPrintF(outf, "End %d\n", ajSeqGetEnd(seq) + ajSeqGetOffset(seq)); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "group\n"); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); /*strand = ajFeatGetStrand(feature);*/ istart = feature->Start; iend = feature->End; ajFmtPrintF(outf, "label\n"); ajFmtPrintF(outf, "Range %d %d | | 9 H\n", istart, iend); ajFmtPrintF(outf, "%S\n", ajFeatGetType(feature)); ajFmtPrintF(outf, "endlabel\n"); ajFmtPrintF(outf, "\n"); } ajFmtPrintF(outf, "endgroup\n"); ajReportWriteTail(thys, ftable, seq); ajStrDel(&subseq); ajStrDel(&tagval); ajListIterDel(&iterft); return; } ID reportWriteRestrict TY static MO ajreport LB core XX DE Writes a report in Draw format, for use as input to cirdna or lindna DE specialised to select data from restriction sites by selecting feature tags DE DE Format:<br> DE group<br> DE <br> DE label<br> DE tick [tagvalue] [start] 8<br> DE endlabel<br> DE label<br> DE tick [tagvalue] [end] 3<br> DE endlabel<br> DE <br> DE endgroup<br> DE DE Data reported: DE DE Tags required: Enzyme_name, 5prime, 3primem 5primerev, 3primerev DE DE Tags used: DE DE Tags reported: XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteRestrict(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; AjPStr subseq = NULL; ajuint ntags; static AjPStr* tagtypes = NULL; static AjPStr* tagnames = NULL; static AjPStr* tagprints = NULL; static ajuint* tagsizes = NULL; ajuint j = 0; AjPStr tagval = NULL; ajint jenz = -1; ajint j5 = -1; ajint j3 = -1; ajuint jstart; ajuint jend; outf = thys->File; ajReportWriteHeader(thys, ftable, seq); ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); for(j=0; j < ntags; j++) { if(ajStrMatchCaseC(tagnames[j], "enzyme")) jenz = j; if(ajStrMatchCaseC(tagnames[j], "5prime")) j5 = j; if(ajStrMatchCaseC(tagnames[j], "3prime")) j3 = j; } ajFmtPrintF(outf, "Start %d\n", ajSeqGetBegin(seq) + ajSeqGetOffset(seq)); ajFmtPrintF(outf, "End %d\n", ajSeqGetEnd(seq) + ajSeqGetOffset(seq)); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "group\n"); ajFmtPrintF(outf, "Forward\n"); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); if (j5 >= 0) { ajFmtPrintF(outf, "label\n"); ajFeatGetNoteS(feature, tagnames[j5], &tagval); ajStrToUint(tagval, &jstart); ajFmtPrintF(outf, "Tick %d 8\n", jstart); if (jenz >= 0) { ajFeatGetNoteS(feature, tagnames[jenz], &tagval); ajFmtPrintF(outf, "%S\n", tagval); } else ajFmtPrintF(outf, "Enz\n"); ajFmtPrintF(outf, "endlabel\n"); } ajFmtPrintF(outf, "\n"); } ajListIterDel(&iterft); ajFmtPrintF(outf, "endgroup\n"); ajFmtPrintF(outf, "group\n"); ajFmtPrintF(outf, "Reverse\n"); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); if (j3 >= 0) { ajFmtPrintF(outf, "label\n"); ajFeatGetNoteS(feature, tagnames[j3], &tagval); ajStrToUint(tagval, &jend); ajFmtPrintF(outf, "Tick %d 3\n", jend); if (jenz >= 0) { ajFeatGetNoteS(feature, tagnames[jenz], &tagval); ajFmtPrintF(outf, "%S\n", tagval); } else ajFmtPrintF(outf, "Enz\n"); ajFmtPrintF(outf, "endlabel\n"); } ajFmtPrintF(outf, "\n"); } ajListIterDel(&iterft); ajFmtPrintF(outf, "endgroup\n"); ajReportWriteTail(thys, ftable, seq); ajStrDel(&subseq); ajStrDel(&tagval); AJFREE(tagtypes); AJFREE(tagnames); AJFREE(tagprints); AJFREE(tagsizes); return; } ID reportWriteExcel TY static MO ajreport LB core XX DE Writes a report in Excel (tab delimited) format. Name, start, end DE score and strand are always reported. Other tags in the report definition DE are added as extra columns. DE DE All values are (for now) unquoted. Missing values are reported as '.' DE DE Format:<br> DE "SeqName Start End Score" (tab delimited) <br> DE [extra tag names added to first line] <br> DE Name Start End Score [extra tag values] (tab delimited) <br> DE DE Data reported: Name Start End Score Strand DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: All defined tags XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteExcel(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; ajuint istart = 0; ajuint iend = 0; float score = 0.0; AjPStr subseq = NULL; AjPStr tmpstr = NULL; ajuint i = 0; ajuint j = 0; char strand; ajuint ntags; static AjPStr* tagtypes = NULL; static AjPStr* tagnames = NULL; static AjPStr* tagprints = NULL; static ajuint* tagsizes = NULL; AjPStr tagval = NULL; outf = thys->File; ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); if(thys->Showscore) ajFmtPrintF(outf, "SeqName\tStart\tEnd\tScore\tStrand"); else ajFmtPrintF(outf, "SeqName\tStart\tEnd\tStrand"); /* then extra tags */ for(j=0; j < ntags; j++) ajFmtPrintF(outf, "\t%S", tagprints[j]); ajFmtPrintF(outf, "\n"); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); istart = feature->Start; iend = feature->End; score = feature->Score; strand = feature->Strand; if(strand != '-') strand = '+'; ajStrAssignSubS(&subseq, ajSeqGetSeqS(seq), istart-1, iend-1); /* ajStrFmtUpper(&subseq); */ i++; if(thys->Showscore) ajFmtPrintF(outf, "%S\t%d\t%d\t%.*f\t%c", ajReportGetSeqnameSeq(thys, seq), istart, iend, thys->Precision, score, strand); else ajFmtPrintF(outf, "%S\t%d\t%d\t%c", ajReportGetSeqnameSeq(thys, seq), istart, iend, strand); for(j=0; j < ntags; j++) { /* then extra tags */ if(ajFeatGetNoteS(feature, tagnames[j], &tagval)) ajFmtPrintF(outf, "\t%S", tagval); else ajFmtPrintF(outf, "\t."); /* missing value '.' for now */ } ajFmtPrintF(outf, "\n"); ajStrDelStatic(&tmpstr); } ajStrDel(&subseq); ajStrDel(&tmpstr); ajStrDel(&tagval); ajListIterDel(&iterft); AJFREE(tagtypes); AJFREE(tagnames); AJFREE(tagprints); AJFREE(tagsizes); return; } ID reportWriteFeatTable TY static MO ajreport LB core XX DE Writes a report in FeatTable format. The report is an EMBL feature DE table using only the tags in the report definition. There is no DE requirement for tag names to match standards for the EMBL feature DE table. DE DE The original EMBOSS application for this format was cpgreport. DE DE Format:<br> DE FT [type] [start]..[end] <br> DE /[tagname]=[tagvalue] <br> DE DE Data reported: Type, Start, End DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: All XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteFeatTable(AjPReport thys,const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; ajuint istart = 0; ajuint iend = 0; AjPStr subseq = NULL; ajuint ntags; static AjPStr* tagtypes = NULL; static AjPStr* tagnames = NULL; static AjPStr* tagprints = NULL; static ajuint* tagsizes = NULL; ajuint j = 0; AjPStr tagval = NULL; AjBool isbetween; outf = thys->File; ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); istart = feature->Start; iend = feature->End; isbetween = (feature->Flags & AJFEATFLAG_BETWEEN_SEQ); ajStrAssignSubS(&subseq, ajSeqGetSeqS(seq), istart-1, iend-1); /* ajStrFmtUpper(&subseq); */ if(feature->Strand == '-') ajFmtPrintF(outf, "FT %-15.15S complement(%d..%d)\n", ajFeatGetType(feature), istart, iend); else if(isbetween) ajFmtPrintF(outf, "FT %-15.15S %d^%d\n", ajFeatGetType(feature), istart, iend); else ajFmtPrintF(outf, "FT %-15.15S %d..%d\n", ajFeatGetType(feature), istart, iend); for(j=0; j < ntags; j++) { if(ajFeatGetNoteS(feature, tagnames[j], &tagval)) ajFmtPrintF(outf, "FT /%S=\"%S\"\n", tagprints[j], tagval); else { /* skip the missing ones for now */ /*ajFmtPrintF(outf, "FT /%S=\"\"\n", tagprints[j]);*/ } } } ajStrDel(&subseq); ajStrDel(&tagval); ajListIterDel(&iterft); AJFREE(tagtypes); AJFREE(tagnames); AJFREE(tagprints); AJFREE(tagsizes); return; } ID reportWriteListFile TY static MO ajreport LB core XX DE Writes a report in ListFile format for use as input to another application. DE DE Format:<br> DE Name[start:end] <br> DE DE Data reported: Name, Start, End, Strand DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: None XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteListFile(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; ajuint istart = 0; ajuint iend = 0; ajuint i = 0; ajlong ipos; ajint jpos; AjPStr tmpstr = NULL; AjPStr tmpname = NULL; outf = thys->File; thys->Showusa = ajTrue; /* so we get a usable USA */ ajReportWriteHeader(thys, ftable, seq); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); istart = feature->Start; iend = feature->End; i++; ajFmtPrintS(&tmpstr, "["); if(istart) ajFmtPrintAppS(&tmpstr, "%d", istart); ajFmtPrintAppS(&tmpstr, ":"); if(iend) ajFmtPrintAppS(&tmpstr, "%d", iend); if(feature->Strand == '-') ajFmtPrintAppS(&tmpstr, ":r"); ajFmtPrintAppS(&tmpstr, "]"); ajStrAssignS(&tmpname, ajSeqGetUsaS(seq)); if(ajStrGetCharLast(tmpname) == ']') { jpos = ajStrGetLen(tmpname); ipos = ajStrFindlastC(tmpname, "["); ajStrCutRange(&tmpname, ipos, jpos); } if(ajStrGetLen(tmpstr) > 3) ajFmtPrintF(outf, "%S%S\n", tmpname, tmpstr); else ajFmtPrintF(outf, "%S\n", tmpname); ajStrDelStatic(&tmpstr); } ajReportWriteTail(thys, ftable, seq); ajListIterDel(&iterft); ajStrDel(&tmpstr); ajStrDel(&tmpname); return; } ID reportWriteMotif TY static MO ajreport LB core XX DE Writes a report in Motif format. Based on the original output DE format of antigenic, helixturnhelix and sigcleave. DE DE Format:<br> DE (1) Score [score] length [length] at [name] [start->[end] <br> DE (marked at position pos) <br> DE [sequence] <br> DE | | <br> DE [start] [end] <br> DE [tagname]: tagvalue DE DE Data reported: Name, Start, End, Length, Score, Sequence DE DE Tags required: None DE DE Tags used: <br> DE pos (integer, maximum score position) DE DE Tags reported: All XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteMotif(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; ajuint istart = 0; ajuint iend = 0; float score = 0.0; ajuint ilen = 0; AjPStr subseq = NULL; AjPStr seqstr = NULL; ajuint i = 0; AjPStr tmpstr = NULL; ajuint ntags; static AjPStr* tagtypes = NULL; static AjPStr* tagnames = NULL; static AjPStr* tagprints = NULL; static ajuint* tagsizes = NULL; ajuint j = 0; ajuint jstar = UINT_MAX; ajuint jplus = UINT_MAX; AjPStr tagval = NULL; ajuint jpos = 0; ajuint jpos2 = 0; ajuint jposmin = 0; ajuint jposmax = 0; ajuint jmax = UINT_MAX; char strand = '+'; const AjPStr seqalias; outf = thys->File; if(ajSeqIsReversedTrue(seq)) { ajStrAssignS(&seqstr, ajSeqGetSeqS(seq)); ajSeqstrReverse(&seqstr); seqalias = seqstr; } else seqalias = ajSeqGetSeqS(seq); ajReportWriteHeader(thys, ftable, seq); ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); for(j=0; j < ntags; j++) { if(ajStrMatchCaseC(tagnames[j], "pos")) { jstar = j; ajFmtPrintF(outf, "%S at \"*\"\n", tagprints[jstar]); } if(ajStrMatchCaseC(tagnames[j], "pos2")) { jplus = j; ajFmtPrintF(outf, "%S at \"+\"\n", tagprints[jplus]); } } if(jstar != UINT_MAX || jplus != UINT_MAX) ajFmtPrintF(outf, "\n"); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); istart = feature->Start; iend = feature->End; score = feature->Score; ilen = iend - istart + 1; ajStrAssignSubS(&subseq, seqalias, istart-1, iend-1); /* ajStrFmtUpper(&subseq); */ i++; ajFmtPrintF(outf, "(%d)", i); if(thys->Showscore) ajFmtPrintF(outf, " Score %.*f", thys->Precision, score); ajFmtPrintF(outf, " length %d at %s %d->%d", ilen, reportCharname(thys), istart, iend); if(strand == '-') ajFmtPrintF(outf, " (Reversed)"); ajFmtPrintF(outf, "\n"); jposmin = 0; jposmax = 0; jpos=0; jpos2=0; if(jstar != UINT_MAX) { if(ajFeatGetNoteS(feature, tagnames[jstar], &tagval)) { ajStrToUint(tagval, &jpos); jposmin = jposmax = jpos; } } if(jplus != UINT_MAX) { if(ajFeatGetNoteS(feature, tagnames[jplus], &tagval)) { ajStrToUint(tagval, &jpos2); if(jpos2 < jpos) jposmin = jpos2; else jposmax = jpos2; } } if(jposmin > iend) jposmin=iend; if(jposmax > iend) jposmax=jposmin; ajStrAssignResC(&tmpstr, ilen,""); for(j=istart; j jposmin) { for(j++; jFile; if(thys->Showstrand && ajSeqIsNuc(seq)) dostrand = ajTrue; if(!tmpstr) ajStrAssignC(&tmpstr, "str"); ajReportWriteHeader(thys, ftable, seq); ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); for(j=0; j < ntags; j++) if(ajFeatGetNoteS(feature, tagnames[j], &tagval)) tagsizes[j] = AJMAX(tagsizes[j], ajStrGetLen(tagval)); } ajListIterDel(&iterft); ajFmtPrintF(outf, "%-20s %7s %7s", "USA", "Start", "End"); if(dostrand) ajFmtPrintF(outf, " %7s", "Strand"); if(thys->Showscore) ajFmtPrintF(outf, " %7s", "Score"); for(j=0; j < ntags; j++) { if(ajStrMatchS(tagtypes[j], tmpstr)) ajFmtPrintF(outf, " %-*S", tagsizes[j], tagprints[j]); else ajFmtPrintF(outf, " %*S", tagsizes[j], tagprints[j]); } ajFmtPrintF(outf, "\n"); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature =(AjPFeature)ajListIterGet(iterft); strand = ajFeatGetStrand(feature); istart = feature->Start; iend = feature->End; score = feature->Score; isbetween = (feature->Flags & AJFEATFLAG_BETWEEN_SEQ); if(isbetween) { istart = feature->End; iend = feature->Start; } ajStrAssignSubS(&subseq, ajSeqGetSeqS(seq), istart-1, iend-1); /* ajStrFmtUpper(&subseq); */ ajFmtPrintF(outf, "%-20S %7d %7d", ajReportGetSeqnameSeq(thys, seq), istart, iend); if(dostrand) ajFmtPrintF(outf, " %c", strand); if(thys->Showscore) ajFmtPrintF(outf, " %7.*f", thys->Precision, score); for(j=0; j < ntags; j++) { if(!ajFeatGetNoteS(feature, tagnames[j], &tagval)) ajStrAssignC(&tagval, "."); if(ajStrMatchS(tagtypes[j], tmpstr)) ajFmtPrintF(outf, " %-*S", tagsizes[j], tagval); else ajFmtPrintF(outf, " %*S", tagsizes[j], tagval); } ajFmtPrintF(outf, "\n"); } ajReportWriteTail(thys, ftable, seq); ajStrDel(&subseq); ajStrDel(&tagval); ajListIterDel(&iterft); AJFREE(tagtypes); AJFREE(tagnames); AJFREE(tagprints); AJFREE(tagsizes); return; } ID reportWriteRegions TY static MO ajreport LB core XX DE Writes a report in Regions format. The report (unusually for the current DE report formats) includes the feature type. DE DE Format: <br> DE [type] from [start] to [end] ([length] [name]) <br> DE ([tagname]: [tagvalue], [tagname]: [tagvalue] ...) <br> DE DE Data reported: Type, Start, End, Length, Name DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: All XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteRegions(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; ajuint istart = 0; ajuint iend = 0; float score = 0.0; ajuint ilen = 0; AjPStr subseq = NULL; AjPStr tagstr = NULL; ajuint ntags; static AjPStr* tagtypes = NULL; static AjPStr* tagnames = NULL; static AjPStr* tagprints = NULL; static ajuint* tagsizes = NULL; ajuint j = 0; AjPStr tagval = NULL; outf = thys->File; ajReportWriteHeader(thys, ftable, seq); ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); istart = feature->Start; iend = feature->End; score = feature->Score; ilen = iend - istart + 1; ajStrAssignSubS(&subseq, ajSeqGetSeqS(seq), istart-1, iend-1); /* ajStrFmtUpper(&subseq); */ ajFmtPrintF(outf, "%S from %d to %d (%d %s)\n", ajFeatGetType(feature), istart, iend, ilen, reportCharname(thys)); if(thys->Showscore) ajFmtPrintF(outf, " Max score: %.*f", thys->Precision, score); if(ntags) { ajFmtPrintF(outf, " ("); for(j=0; j < ntags; j++) if(ajFeatGetNoteS(feature, tagnames[j], &tagval)) { if(j) ajFmtPrintF(outf, ", "); ajFmtPrintF(outf, "%S: %S", tagprints[j], tagval); } ajFmtPrintF(outf, ")"); } ajFmtPrintF(outf, "\n\n"); } ajReportWriteTail(thys, ftable, seq); ajStrDel(&subseq); ajStrDel(&tagstr); ajStrDel(&tagval); ajListIterDel(&iterft); AJFREE(tagtypes); AJFREE(tagnames); AJFREE(tagprints); AJFREE(tagsizes); return; } ID reportWriteSeqTable TY static MO ajreport LB core XX DE Writes a report in SeqTable format. DE DE This is a simple table format that DE includes the feature sequence. See reportWriteTable for a version DE without the sequence. Missing tag values are reported as '.' DE The column width is 6, or longer if the name is longer. DE DE Format:<br> DE Start End [tagnames] Sequence DE [start] [end] [tagvalues] [sequence] DE DE Data reported: DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: All XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteSeqTable(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; ajuint istart = 0; ajuint iend = 0; AjPStr subseq = NULL; AjPStr seqstr = NULL; ajuint ntags; static AjPStr* tagtypes = NULL; static AjPStr* tagnames = NULL; static AjPStr* tagprints = NULL; static ajuint* tagsizes = NULL; ajuint j=0; AjPStr tagval = NULL; AjPStr tmpstr = NULL; AjBool dostrand = ajFalse; char strand = '+'; /* ajint seqlen; */ const AjPStr seqalias; AjBool isbetween; outf = thys->File; /* seqlen = ajSeqGetLen(seq); */ if(ajSeqIsReversedTrue(seq)) { ajStrAssignS(&seqstr, ajSeqGetSeqS(seq)); ajSeqstrReverse(&seqstr); seqalias = seqstr; } else seqalias = ajSeqGetSeqS(seq); if(!tmpstr) ajStrAssignC(&tmpstr, "str"); if(thys->Showstrand && ajSeqIsNuc(seq)) dostrand = ajTrue; ajReportWriteHeader(thys, ftable, seq); ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); /*ajDebug("dostrand: %B " "ftable->Offset: %d ftable->Len: %d seqlen: %dan", dostrand, ftable->Offset, ftable->Len, ajSeqGetLen(seq));*/ /*ajDebug("'%S'an", ajSeqGetSeqS(seq));*/ iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); for(j=0; j < ntags; j++) if(ajFeatGetNoteS(feature, tagnames[j], &tagval)) tagsizes[j] = AJMAX(tagsizes[j], ajStrGetLen(tagval)); } ajListIterDel(&iterft); ajFmtPrintF(outf, "%7s %7s", "Start", "End"); if(dostrand) ajFmtPrintF(outf, " %7s", "Strand"); for(j=0; j < ntags; j++) { if(ajStrMatchS(tagtypes[j], tmpstr)) ajFmtPrintF(outf, " %-*S", tagsizes[j], tagprints[j]); else ajFmtPrintF(outf, " %*S", tagsizes[j], tagprints[j]); } ajFmtPrintF(outf, " Sequence\n"); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); strand = ajFeatGetStrand(feature); istart = feature->Start; iend = feature->End; isbetween = (feature->Flags & AJFEATFLAG_BETWEEN_SEQ); if(isbetween) { istart = feature->End; iend = feature->Start; ajStrAssignC(&subseq, ""); } else { ajStrAssignSubS(&subseq, seqalias, istart-1, iend-1); if(strand == '-') ajSeqstrReverse(&subseq); } /*ajDebug("%d..%d start: %d end: %d ajFeatGetStrand: '%c'\n", feature->Start, feature->End, istart, iend, ajFeatGetStrand(feature));*/ /* ajStrFmtUpper(&subseq); */ ajDebug("reportWriteSeqTable subseq %d seq %d %d..%d\n", ajStrGetLen(subseq), ajSeqGetLen(seq), istart, iend); ajFmtPrintF(outf, "%7d %7d", istart, iend); if(dostrand) ajFmtPrintF(outf, " %c", strand); for(j=0; j < ntags; j++) { if(!ajFeatGetNoteS(feature, tagnames[j], &tagval)) ajStrAssignC(&tagval, "."); ajDebug("reportWriteSeqTable tagsizes[%d] %d tagval '%S'\n", j, tagsizes[j], tagval); if(ajStrMatchS(tagtypes[j], tmpstr)) ajFmtPrintF(outf, " %-*S", tagsizes[j], tagval); else ajFmtPrintF(outf, " %*S", tagsizes[j], tagval); } ajFmtPrintF(outf, " %S\n", subseq); } ajReportWriteTail(thys, ftable, seq); ajStrDel(&tmpstr); ajStrDel(&subseq); ajStrDel(&seqstr); ajStrDel(&tagval); ajListIterDel(&iterft); AJFREE(tagtypes); AJFREE(tagnames); AJFREE(tagprints); AJFREE(tagsizes); return; } ID reportWriteSimple TY static MO ajreport LB core XX DE Writes a report in SRS simple format This is a simple parsable format that DE does not include the feature sequence (see also SRS format) DE for applications where features can be large. DE Missing tag values are reported as '.' DE DE Format:<br> DE Start End [tagnames] Sequence DE [start] [end] [tagvalues] [sequence] DE DE Data reported: DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: All XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteSimple(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { static AjBool withSeq = AJFALSE; reportWriteSrsFlags(thys, ftable, seq, withSeq); return; } ID reportWriteSrs TY static MO ajreport LB core XX DE Writes a report in SRS format This is a simple parsable format that DE includes the feature sequence. DE Missing tag values are reported as '.' DE DE Format:<br> DE Start End [tagnames] Sequence DE [start] [end] [tagvalues] [sequence] DE DE Data reported: DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: All XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteSrs(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { static AjBool withSeq = AJTRUE; reportWriteSrsFlags(thys, ftable, seq, withSeq); return; } ID reportWriteSrsFlags TY static MO ajreport LB core XX DE Writes a report in SRS format. DE A flag controls whether to include the sequence. DE Missing tag values are reported as '.' DE DE Format:<br> DE Start End [tagnames] Sequence DE [start] [end] [tagvalues] [sequence] DE DE Data reported: DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: All XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX PN [4] PA r withSeq AjBool PD If ajTrue, includes the sequence in the output. PX RT void RD RX // static void reportWriteSrsFlags(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq, AjBool withSeq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; ajuint istart = 0; ajuint iend = 0; ajuint ilen = 0; float score = 0.0; AjPStr subseq = NULL; ajuint ift = 0; ajuint ntags; static AjPStr* tagtypes = NULL; static AjPStr* tagnames = NULL; static AjPStr* tagprints = NULL; static ajuint* tagsizes = NULL; ajuint j = 0; AjPStr tagval = NULL; outf = thys->File; ajReportWriteHeader(thys, ftable, seq); ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); istart = feature->Start; iend = feature->End; ilen = iend - istart + 1; score = feature->Score; ajStrAssignSubS(&subseq, ajSeqGetSeqS(seq), istart-1, iend-1); /* ajStrFmtUpper(&subseq); */ /* blank line before each feature */ /* don't write at the end, because tail always starts with a blank line */ if(ift) ajFmtPrintF(outf, "\n"); ift++; ajFmtPrintF(outf, "Feature: %d\n", ift); ajFmtPrintF(outf, "Name: %S\n", ajReportGetSeqnameSeq(thys, seq)); ajFmtPrintF(outf, "Start: %d\n", istart); ajFmtPrintF(outf, "End: %d\n", iend); ajFmtPrintF(outf, "Length: %d\n", ilen); if(withSeq) ajFmtPrintF(outf, "Sequence: %S\n", subseq); /* We always write the score - ignore thys->Showscore */ ajFmtPrintF(outf, "Score: %.*f\n",thys->Precision, score); /* We always write the strand - ignore thys->Showstrand */ ajFmtPrintF(outf, "Strand: %c\n", ajFeatGetStrand(feature)); if(feature->Frame) ajFmtPrintF(outf, "Frame: %d\n", feature->Frame); for(j=0; j < ntags; j++) { if(ajFeatGetNoteS(feature, tagnames[j], &tagval)) ajFmtPrintF(outf, "%S: %S\n", tagprints[j], tagval); else ajFmtPrintF(outf, "%S: .\n", tagprints[j]); /* missing value '.' for now */ } } ajReportWriteTail(thys, ftable, seq); ajStrDel(&subseq); ajStrDel(&tagval); ajListIterDel(&iterft); AJFREE(tagtypes); AJFREE(tagnames); AJFREE(tagprints); AJFREE(tagsizes); return; } ID reportWriteTable TY static MO ajreport LB core XX DE Writes a report in Table format. See reportWriteSeqTable for a version DE with the sequence. See reportWriteNameTable for a version with DE the name, which was the earlier format for reportWriteTable. DE The name already appears in the sequence header DE DE Missing tag values are reported as '.' DE The column width is 6, or longer if the name is longer. DE DE Format:<br> DE USA Start End Score [tagnames] DE [name] [start] [end] [score] [tagvalues] DE DE Data reported: DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteTable(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjPFeature feature = NULL; ajint istart = 0; ajint iend = 0; float score = 0.0; AjPStr subseq = NULL; ajuint ntags; static AjPStr* tagtypes = NULL; static AjPStr* tagnames = NULL; static AjPStr* tagprints = NULL; static ajuint* tagsizes = NULL; ajuint j = 0; AjPStr tagval = NULL; AjPStr tmpstr = NULL; char strand; AjBool dostrand = ajFalse; AjBool isbetween; outf = thys->File; if(thys->Showstrand && ajSeqIsNuc(seq)) dostrand = ajTrue; if(!tmpstr) ajStrAssignC(&tmpstr, "str"); ajReportWriteHeader(thys, ftable, seq); ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); for(j=0; j < ntags; j++) if(ajFeatGetNoteS(feature, tagnames[j], &tagval)) tagsizes[j] = AJMAX(tagsizes[j], ajStrGetLen(tagval)); } ajListIterDel(&iterft); ajFmtPrintF(outf, "%7s %7s", "Start", "End"); if(dostrand) ajFmtPrintF(outf, " %7s", "Strand"); if(thys->Showscore) ajFmtPrintF(outf, " %7s", "Score"); for(j=0; j < ntags; j++) { if(ajStrMatchS(tagtypes[j], tmpstr)) ajFmtPrintF(outf, " %-*S", tagsizes[j], tagprints[j]); else ajFmtPrintF(outf, " %*S", tagsizes[j], tagprints[j]); } ajFmtPrintF(outf, "\n"); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); strand = ajFeatGetStrand(feature); istart = feature->Start; iend = feature->End; score = feature->Score; isbetween = (feature->Flags & AJFEATFLAG_BETWEEN_SEQ); if(isbetween) { istart = feature->End; iend = feature->Start; } ajStrAssignSubS(&subseq, ajSeqGetSeqS(seq), istart-1, iend-1); /* ajStrFmtUpper(&subseq); */ ajFmtPrintF(outf, "%7d %7d", istart, iend); if(dostrand) ajFmtPrintF(outf, " %c", strand); if(thys->Showscore) ajFmtPrintF(outf, " %7.*f", thys->Precision, score); for(j=0; j < ntags; j++) { if(!ajFeatGetNoteS(feature, tagnames[j], &tagval)) ajStrAssignC(&tagval, "."); if(ajStrMatchS(tagtypes[j], tmpstr)) ajFmtPrintF(outf, " %-*S", tagsizes[j], tagval); else ajFmtPrintF(outf, " %*S", tagsizes[j], tagval); } ajFmtPrintF(outf, "\n"); } ajReportWriteTail(thys, ftable, seq); ajStrDel(&subseq); ajStrDel(&tagval); ajStrDel(&tmpstr); ajListIterDel(&iterft); AJFREE(tagtypes); AJFREE(tagnames); AJFREE(tagprints); AJFREE(tagsizes); return; } ID reportWriteTagseq TY static MO ajreport LB core XX DE Writes a report in Tagseq format. Features are marked up below the sequence. DE Originally developed for the garnier application, but has general uses. DE For garnier, it checks for a '*garnier' feature tag and hard codes the DE standard garnier row names as the tagname. DE DE Format:<br> DE Sequence (50 residues)<br> DE tagname ++++++++++++ +++++++++<br> DE DE If the tag value is a 1 letter code, use this instead of '+' DE DE Data reported: DE DE Tags required: None DE DE Tags used: None DE DE Tags reported: XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // static void reportWriteTagseq(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjIList iterft = NULL; AjIList iterkey = NULL; AjPFeature feature = NULL; ajint istart = 0; ajint iend = 0; ajuint ilen = 0; AjPStr subseq = NULL; AjPStr seqstr = NULL; AjPStr substr = NULL; ajuint ntags; static AjPStr* tagtypes = NULL; static AjPStr* tagnames = NULL; static AjPStr* tagprints = NULL; static ajuint* tagsizes = NULL; ajuint j = 0; ajuint i = 0; AjPStr tagval = NULL; AjPStr seqmarkup; AjPStr seqnumber = NULL; ajuint seqbeg; ajuint seqend; ajuint seqlen; ajuint ilast; ajuint jlast; ajuint iinit; ajuint istop; ajuint margin = 5; AjPStr featname; AjPTable featkeys = NULL; AjPList listkeys = NULL; AjBool isgarnier = ajFalse; char strand; char ctagval; const char* garnierkeys[] = {"helix", "sheet", "turns", "coil"}; AjBool isnuc = ajFalse; const AjPStr seqalias; outf = thys->File; if(ajSeqIsReversedTrue(seq)) { ajStrAssignS(&seqstr, ajSeqGetSeqS(seq)); ajSeqstrReverse(&seqstr); seqalias = seqstr; } else seqalias = ajSeqGetSeqS(seq); seqlen = ajSeqGetLen(seq); seqbeg = ajSeqGetBegin(seq) + ajSeqGetOffset(seq); seqend = ajSeqGetEnd(seq) + ajSeqGetOffset(seq); ajReportWriteHeader(thys, ftable, seq); featkeys = ajTablestrNew(100); listkeys = ajListstrNew(); if(ajFeattableIsNuc(ftable)) isnuc = ajTrue; if(ajSeqIsNuc(seq)) isnuc = ajTrue; ntags = reportGetTags(thys, &tagtypes, &tagnames, &tagprints, &tagsizes); for(j=0; j < ntags; j++) if(ajStrMatchC(tagnames[j], "garnier")) isgarnier = ajTrue; if(isgarnier) for(i=0; i < 4; i++) { featname = ajStrNewC(garnierkeys[i]); seqmarkup = ajStrNewRes(seqlen+1); ajStrAppendCountK(&seqmarkup, ' ', seqlen); ajTablePut(featkeys, featname, seqmarkup); ajListstrPushAppend(listkeys, featname); margin = AJMAX(margin, ajStrGetLen(featname)); } else { iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); if(!ajTableMatchS(featkeys, feature->Type)) { featname = ajStrNewS(feature->Type); seqmarkup = ajStrNewRes(seqlen+1); ajStrAppendCountK(&seqmarkup, ' ', seqlen); ajTablePut(featkeys, featname,seqmarkup); ajListstrPushAppend(listkeys, featname); if(isnuc) margin = AJMAX(margin, ajStrGetLen(ajFeatTypeNuc(featname))); else margin = AJMAX(margin, ajStrGetLen(ajFeatTypeProt(featname))); } } ajListIterDel(&iterft); } ajFmtPrintAppS(&seqnumber, " .%5d", 10); if(seqend > 9) istop = seqend-9; else istop = 0; for(i=10; i < istop; i+=10) ajFmtPrintAppS(&seqnumber, " .%5d", i+10); iterft = ajListIterNewread(ftable->Features); while(!ajListIterDone(iterft)) { feature = (AjPFeature)ajListIterGet(iterft); strand = ajFeatGetStrand(feature); istart = feature->Start; iend = feature->End; ilen = iend - istart + 1; if(isgarnier && ajFeatGetNoteC(feature, "garnier", &tagval)) { ctagval = ajStrGetCharFirst(tagval); switch(ctagval) { case 'H': seqmarkup = ajTableFetchmodC(featkeys, garnierkeys[0]); break; case 'E': seqmarkup = ajTableFetchmodC(featkeys, garnierkeys[1]); break; case 'T': seqmarkup = ajTableFetchmodC(featkeys, garnierkeys[2]); break; default: seqmarkup = ajTableFetchmodC(featkeys, garnierkeys[3]); break; } ajStrPasteCountK(&seqmarkup, istart-1, ctagval, ilen); } else { seqmarkup = ajTableFetchmodS(featkeys, feature->Type); ajStrPasteCountK(&seqmarkup, istart-1, strand, ilen); } } iinit = seqbeg-1; for(i=iinit; i < seqend; i+=50) { ilast = AJMIN(i+50-1, seqend-1); jlast = AJMIN(i+50-1, ajStrGetLen(seqnumber)-1); ajStrAssignSubS(&substr, seqnumber, i, jlast); ajFmtPrintF(outf, "%*s %S\n", margin, "", substr); ajStrAssignSubS(&subseq, seqalias, i, ilast); /* ajStrFmtUpper(&subseq); */ ajFmtPrintF(outf, "%*s %S\n", margin, "", subseq); iterkey = ajListIterNewread(listkeys); while(!ajListIterDone(iterkey)) { featname = ajListstrIterGet(iterkey); seqmarkup = ajTableFetchmodS(featkeys, featname); ajStrAssignSubS(&substr, seqmarkup, i, ilast); if(isgarnier) ajFmtPrintF(outf, "%*S %S\n", margin, featname, substr); else if (isnuc) ajFmtPrintF(outf, "%*S %S\n", margin, ajFeatTypeNuc(featname), substr); else ajFmtPrintF(outf, "%*S %S\n", margin, ajFeatTypeProt(featname), substr); } ajListIterDel(&iterkey); } ajReportWriteTail(thys, ftable, seq); ajStrDel(&seqstr); ajStrDel(&subseq); ajStrDel(&tagval); ajStrDel(&substr); ajStrDel(&seqnumber); ajListIterDel(&iterft); AJFREE(tagtypes); AJFREE(tagnames); AJFREE(tagprints); AJFREE(tagsizes); ajTablestrFree(&featkeys); ajListFree(&listkeys); return; } ID ajReportDel TY public MO ajreport LB core XX DE Destructor for report objects XX PN [1] PA d pthys AjPReport* PD Report object reference PX RT void RD RX CA delete CT AjPReport CD Default destructor CX // void ajReportDel(AjPReport* pthys) { AjPReport thys; AjPStr str = NULL; thys = *pthys; if(!thys) return; ajStrDel(&thys->Name); ajStrDel(&thys->Type); ajStrDel(&thys->Formatstr); ajStrDel(&thys->Extension); while(ajListPop(thys->FileTypes,(void **)&str)) ajStrDel(&str); ajListFree(&thys->FileTypes); while(ajListPop(thys->FileNames,(void **)&str)) ajStrDel(&str); ajListFree(&thys->FileNames); while(ajListPop(thys->Tagnames,(void **)&str)) ajStrDel(&str); ajListFree(&thys->Tagnames); while(ajListPop(thys->Tagprints,(void **)&str)) ajStrDel(&str); ajListFree(&thys->Tagprints); while(ajListPop(thys->Tagtypes,(void **)&str)) ajStrDel(&str); ajListFree(&thys->Tagtypes); ajStrDel(&thys->Header); ajStrDel(&thys->SubHeader); ajStrDel(&thys->Tail); ajStrDel(&thys->SubTail); ajFeattableDel(&thys->Fttable); ajFeattabOutDel(&thys->Ftquery); ajFileClose(&thys->File); AJFREE(*pthys); return; } ID ajReportOpen TY public MO ajreport LB core XX DE Opens a new report file XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r name const AjPStr PD File name PX RT AjBool RD ajTrue on success RX // AjBool ajReportOpen(AjPReport thys, const AjPStr name) { if(!ajReportValid(thys)) return ajFalse; thys->File = ajFileNewOutNameS(name); if(thys->File) return ajTrue; return ajFalse; } ID ajReportFormatDefault TY public MO ajreport LB core XX DE Sets the default format for a feature report XX PN [1] PA w pformat AjPStr* PD Default format returned PX RT AjBool RD ajTrue is format was returned RX // AjBool ajReportFormatDefault(AjPStr* pformat) { if(ajStrGetLen(*pformat)) ajDebug("... output format '%S'\n", *pformat); else { /* ajStrAssignEmptyC(pformat, reportFormat[0].Name);*/ ajStrAssignEmptyC(pformat, "gff"); /* use the real name */ ajDebug("... output format not set, default to '%S'\n", *pformat); } return ajTrue; } ID ajReportFindFormat TY public MO ajreport LB core XX DE Looks for the specified report format in the internal definitions and DE returns the index. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // AjBool ajReportFindFormat(const AjPStr format, ajint* iformat) { AjPStr tmpformat = NULL; ajint i = 0; if(!ajStrGetLen(format)) return ajFalse; ajStrAssignS(&tmpformat, format); ajStrFmtLower(&tmpformat); while(reportFormat[i].Name) { if(ajStrMatchCaseC(tmpformat, reportFormat[i].Name)) { *iformat = i; ajStrDel(&tmpformat); return ajTrue; } i++; } ajStrDel(&tmpformat); return ajFalse; } ID ajReportSetTagsS TY public MO ajreport LB core XX DE Sets the tag list for a report XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r taglist const AjPStr PD Tag names list PX RT AjBool RD ajTrue on success RX // AjBool ajReportSetTagsS(AjPReport thys, const AjPStr taglist) { AjPStr tmplist = NULL; AjPStr tmpstr = NULL; AjPStr tagtype = NULL; AjPStr tagname = NULL; AjPStr tagprint = NULL; /* ** assume the tags are a simple list in this format: ** type: name[=printname] ** ** spaces are not allowed in names (for ease of parsing the results) */ if(!reportTagExp) reportTagExp = ajRegCompC("^ *([^:]+):([^= ]+)(=([^ ]+))?"); ajStrAssignS(&tmplist, taglist); while(ajRegExec(reportTagExp, tmplist)) { tagtype = NULL; tagname = NULL; tagprint = NULL; ajRegSubI(reportTagExp, 1, &tagtype); ajRegSubI(reportTagExp, 2, &tagname); ajRegSubI(reportTagExp, 4, &tagprint); if(!ajStrGetLen(tagprint)) ajStrAssignS(&tagprint, tagname); ajDebug("Tag '%S' : '%S' print '%S'\n", tagtype, tagname, tagprint); ajRegPost(reportTagExp, &tmpstr); ajStrAssignS(&tmplist, tmpstr); if(!ajListGetLength(thys->Tagtypes)) { thys->Tagtypes = ajListNew(); thys->Tagnames = ajListNew(); thys->Tagprints = ajListNew(); } ajListPushAppend(thys->Tagtypes, tagtype); ajListPushAppend(thys->Tagnames, tagname); ajListPushAppend(thys->Tagprints, tagprint); } if(ajStrGetLen(tmplist)) { /* test acdc-reportbadtaglist */ ajErr("Bad report taglist at '%S'", tmplist); return ajFalse; } ajStrDel(&tmpstr); ajStrDel(&tmplist); return ajTrue; } ID ajReportValid TY public MO ajreport LB core XX DE Test for a report object. DE DE Checks the format works with the number of tags. DE Checks the format works with the type (protein or nucleotide). DE Sets the format if not already defined. XX PN [1] PA u thys AjPReport PD Report object PX RT AjBool RD ajTrue on success RX // AjBool ajReportValid(AjPReport thys) { if(!thys->Format) /* test acdc-reportbadformat */ if(!ajReportFindFormat(thys->Formatstr, &thys->Format)) { ajErr("Unknown report format '%S'", thys->Formatstr); return ajFalse; } /* so far, only diffseq format has mintags non-zero */ /* it needs 7 special tags to compare features */ if( reportFormat[thys->Format].Mintags > ajListGetLength(thys->Tagnames)) { ajErr("Report format '%s' needs %d tags, has only %Lu", reportFormat[thys->Format].Name, reportFormat[thys->Format].Mintags, ajListGetLength(thys->Tagnames)); return ajFalse; } return ajTrue; } ID ajReportNew TY public MO ajreport LB core XX DE Constructor for a report object XX RT AjPReport RD New report object RX CA new CT AjPReport CD Default constructor CX // AjPReport ajReportNew(void) { AjPReport pthis; AJNEW0(pthis); pthis->CountSeq = 0; pthis->CountHit = 0; pthis->TotHits = 0; pthis->Name = ajStrNew(); pthis->Formatstr = ajStrNew(); pthis->Format = 0; pthis->Fttable = NULL; pthis->Ftquery = ajFeattabOutNew(); pthis->Extension = ajStrNew(); pthis->Precision = 3; pthis->File = NULL; pthis->Showscore = ajTrue; return pthis; } ID ajReportWrite TY public MO ajreport LB core XX DE Writes a feature report XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT AjBool RD True if data was written RD False if maximum output has already been reached. RX CA output CT AjPReport CD Master sequence output routine CX // AjBool ajReportWrite(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { ajint maxreport = 0; AjPFeattable ftcopy = NULL; AjPStr tmpstr = NULL; AjBool isnuc = ajFalse; AjBool isprot = ajFalse; AjBool ok; ajDebug("ajReportWrite\n"); /* add ftable name and size */ if(!thys->Format) if(!ajReportFindFormat(thys->Formatstr, &thys->Format)) ajDie("unknown report format '%S'", thys->Formatstr); if(ajSeqIsNuc(seq) && ajFeattableIsNuc(ftable)) isnuc = ajTrue; if(ajSeqIsProt(seq) && ajFeattableIsProt(ftable)) isprot = ajTrue; ok = ajFalse; if(isnuc && reportFormat[thys->Format].Nucleotide) ok = ajTrue; else if(isprot && reportFormat[thys->Format].Protein) ok = ajTrue; if(!ok) { if(isnuc) ajErr("Report format '%S' not supported for nucleotide sequences", thys->Formatstr); else if(isprot) ajErr("Report format '%S' not supported for protein sequences", thys->Formatstr); else ajErr("Report format '%S' failed: unknown sequence type", thys->Formatstr); return ajFalse; } ajDebug("ajReportWrite %d '%s' %d\n", thys->Format, reportFormat[thys->Format].Name, ajFeattableGetSize(ftable)); if(thys->MaxHitAll) { maxreport = thys->MaxHitAll - thys->CountHit; if(maxreport <= 0) { thys->MaxLimit = ajTrue; return ajFalse; } } if(thys->MaxHitSeq) { if(thys->MaxHitAll) maxreport = AJMIN(maxreport, thys->MaxHitSeq); else maxreport = thys->MaxHitSeq; } ajDebug("ajReportWrite MaxHitSeq: %d MaxHitAll: %d " "CountHit: %d FeattableGetSize: %u maxreport: %d\n", thys->MaxHitSeq, thys->MaxHitAll, thys->CountHit, ajFeattableGetSize(ftable), maxreport); ajReportSetType(thys, ftable, seq); /* Calling funclist reportFormat() */ if(maxreport && maxreport < (ajint)ajFeattableGetSize(ftable)) { ajFmtPrintS(&tmpstr, "HitLimit: %d/%u", maxreport, ajFeattableGetSize(ftable)); ajReportAppendSubtailS(thys, tmpstr); ajDebug("new subtail '%S'\n", tmpstr); ajStrDel(&tmpstr); ftcopy = ajFeattableNewFtableLimit(ftable, maxreport); (*reportFormat[thys->Format].Write)(thys, ftcopy, seq); ajFeattableDel(&ftcopy); thys->CountHit += maxreport; thys->TotHits += ajFeattableGetSize(ftable); } else { (*reportFormat[thys->Format].Write)(thys, ftable, seq); thys->CountHit += ajFeattableGetSize(ftable); thys->TotHits += ajFeattableGetSize(ftable); } ++thys->CountSeq; return ajTrue; } ID ajReportClose TY public MO ajreport LB core XX DE Closes a feature report XX PN [1] PA u thys AjPReport PD Report object PX RT void RD RX // void ajReportClose(AjPReport thys) { if(!thys) return; ajDebug("ajReportClose '%F' CountSeq %d CountHit %d\n", thys->File, thys->CountSeq, thys->CountHit); if(!thys->File) /* already closed, nothing to write to */ return; if(!thys->Format) if(!ajReportFindFormat(thys->Formatstr, &thys->Format)) ajDie("unknown report format '%S'", thys->Formatstr); if(reportFormat[thys->Format].Showheader) { if(!thys->CountSeq) ajReportWriteHeader(thys, NULL, NULL); ajReportWriteTail(thys, NULL, NULL); } if(thys->File && !thys->CountHit) ajWarn("No hits in output report file '%F'", thys->File); ajFeattabOutClear(&thys->Ftquery); ajFileClose(&thys->File); return; } ID reportGetTags TY static MO ajreport LB core XX DE Converts a report tagtypes definition (ACD taglist attribute) DE into arrays of tag types, names and printnames. XX PN [1] PA r thys const AjPReport PD Report object PX PN [2] PA w types AjPStr** PD Address of array of types generated PX PN [3] PA w names AjPStr** PD Address of array of names generated PX PN [4] PA w prints AjPStr** PD Address of array of print names generated PX PN [5] PA w sizes ajuint** PD Width needed to print heading PX RT ajint RD Number of tagtypes (size of arrays created) RX // static ajint reportGetTags(const AjPReport thys, AjPStr** types, AjPStr** names, AjPStr** prints, ajuint** sizes) { ajulong ntags; static ajuint jmin = 6; ajulong i; if(!ajListGetLength(thys->Tagtypes)) return 0; ntags = ajListToarray(thys->Tagnames, (void***) names); ntags = ajListToarray(thys->Tagprints, (void***) prints); ntags = ajListToarray(thys->Tagtypes, (void***) types); if(ntags) { AJCRESIZE(*sizes, (size_t) ntags); for(i=0UL; i < ntags; i++) (*sizes)[i] = AJMAX(jmin, ajStrGetLen((*prints)[i])); } else AJFREE(sizes); return (ajint) ntags; } ID ajReportWriteHeader TY public MO ajreport LB core XX DE Writes a feature report header and updates internal counters. XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // void ajReportWriteHeader(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjPStr tmpstr = NULL; AjPStr tmpname = NULL; AjPStr tmptype = NULL; AjIList itername = NULL; AjIList itertype = NULL; AjBool doSingle =ajFalse; /* turned off for now - always multi format */ int i; AjPTime today = NULL; outf = thys->File; today = ajTimeNewTodayFmt("report"); /* Header for the top of the file (first call for report only) */ if(!thys->CountSeq) { ajFmtPrintF(outf, "########################################\n"); ajFmtPrintF(outf, "# Program: %S\n", ajUtilGetProgram()); ajFmtPrintF(outf, "# Rundate: %D\n", today); ajFmtPrintF(outf, "# Commandline: %S\n", ajUtilGetProgram()); ajStrAssignS(&tmpstr, ajUtilGetCmdline()); if(ajStrGetLen(tmpstr)) { ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajFmtPrintF(outf, "# %S\n", tmpstr); } ajStrAssignS(&tmpstr, ajUtilGetInputs()); if(ajStrGetLen(tmpstr)) { ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajFmtPrintF(outf, "# %S\n", tmpstr); } if(thys->MaxHitAll) ajFmtPrintF(outf, "# Max_hits_total: %d\n", thys->MaxHitAll); if(thys->MaxHitSeq) ajFmtPrintF(outf, "# Max_hits_sequence: %d\n", thys->MaxHitSeq); ajFmtPrintF(outf, "# Report_format: %S\n", thys->Formatstr); ajFmtPrintF(outf, "# Report_file: %F\n", outf); if(ajListGetLength(thys->FileNames)) { i = 0; itername = ajListIterNewread(thys->FileNames); itertype = ajListIterNewread(thys->FileTypes); ajFmtPrintF(outf, "# Additional_files: %Lu\n", ajListGetLength(thys->FileNames)); while(!ajListIterDone(itername) && !ajListIterDone(itertype)) { tmpname = (AjPStr)ajListIterGet(itername); tmptype = (AjPStr)ajListIterGet(itertype); ajFmtPrintF(outf, "# %d: %S (%S)\n", ++i, tmpname, tmptype); } ajListIterDel(&itername); ajListIterDel(&itertype); } if(!doSingle || thys->Multi) ajFmtPrintF(outf, "########################################\n\n"); else ajFmtPrintF(outf, "#\n"); } /* Sequence header (can be part of top header) */ if(!doSingle || thys->Multi) ajFmtPrintF(outf, "#=======================================\n#\n"); if(seq) { if(ajSeqIsReversedTrue(seq)) ajFmtPrintF(outf, "# Sequence: %S from: %d to: %d " "(Reversed)\n", ajReportGetSeqnameSeq(thys, seq), ajSeqGetBegin(seq) + ajSeqGetOffset(seq), ajSeqGetEnd(seq) + ajSeqGetOffset(seq)); else ajFmtPrintF(outf, "# Sequence: %S from: %d to: %d\n", ajReportGetSeqnameSeq(thys, seq), ajSeqGetBegin(seq) + ajSeqGetOffset(seq), ajSeqGetEnd(seq) + ajSeqGetOffset(seq)); if(thys->Showacc) ajFmtPrintF(outf, "# Accession: %S\n", ajSeqGetAccS(seq)); if(thys->Showdes) ajFmtPrintF(outf, "# Description: %S\n", ajSeqGetDescS(seq)); } if(ftable) { ajFmtPrintF(outf, "# HitCount: %d\n", ajFeattableGetSize(ftable)); } if(ajStrGetLen(thys->Header)) { ajStrAssignS(&tmpstr, thys->Header); ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajStrTrimEndC(&tmpstr, " "); ajFmtPrintF(outf, "#\n"); ajFmtPrintF(outf, "# %S", tmpstr); if(!ajStrSuffixC(tmpstr, "\n#")) ajFmtPrintF(outf, "\n#"); ajFmtPrintF(outf, "\n"); } if(ajStrGetLen(thys->SubHeader)) { ajStrAssignS(&tmpstr, thys->SubHeader); ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajStrTrimEndC(&tmpstr, " "); ajFmtPrintF(outf, "#\n"); ajFmtPrintF(outf, "# %S", tmpstr); if(!ajStrSuffixC(tmpstr, "\n#")) ajFmtPrintF(outf, "\n#"); ajFmtPrintF(outf, "\n"); ajStrDel(&thys->SubHeader); } if(!doSingle || thys->Multi) ajFmtPrintF(outf, "#=======================================\n\n"); else ajFmtPrintF(outf, "########################################\n\n"); ajStrDel(&tmpstr); AJFREE(today); return; } ID ajReportWriteTail TY public MO ajreport LB core XX DE Writes (and clears) a feature report tail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // void ajReportWriteTail(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { AjPFile outf; AjPStr tmpstr = NULL; AjBool doSingle = ajFalse; /* turned off for now - always multi format */ (void) seq; /* make it used - we may need it later */ outf = thys->File; if(!doSingle || thys->Multi) ajFmtPrintF(outf, "\n#---------------------------------------\n"); else ajFmtPrintF(outf, "\n########################################\n"); if(ftable) { if(ajStrGetLen(thys->SubTail)) { ajStrAssignS(&tmpstr, thys->SubTail); ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajStrTrimEndC(&tmpstr, " "); ajFmtPrintF(outf, "#\n"); ajFmtPrintF(outf, "# %S", tmpstr); if(!ajStrSuffixC(tmpstr, "\n#")) ajFmtPrintF(outf, "\n#"); ajFmtPrintF(outf, "\n"); ajStrDel(&thys->SubTail); } if(ajStrGetLen(thys->Tail)) { ajStrAssignS(&tmpstr, thys->Tail); ajStrExchangeCC(&tmpstr, "\n", "\1# "); ajStrExchangeCC(&tmpstr, "\1", "\n"); ajStrTrimEndC(&tmpstr, " "); ajFmtPrintF(outf, "#\n"); ajFmtPrintF(outf, "# %S", tmpstr); if(!ajStrSuffixC(tmpstr, "\n#")) ajFmtPrintF(outf, "\n#"); ajFmtPrintF(outf, "\n"); } } if(!ftable) { if(thys->Totseqs) ajFmtPrintF(outf, "# Total_sequences: %Ld\n", thys->Totseqs); if(thys->Totlength) ajFmtPrintF(outf, "# Total_length: %Ld\n", thys->Totlength); ajFmtPrintF(outf, "# Reported_sequences: %d\n", thys->CountSeq); ajFmtPrintF(outf, "# Reported_hitcount: %d\n", thys->CountHit); if(thys->CountHit < thys->TotHits) ajFmtPrintF(outf, "# Unreported_hitcount: %d\n", thys->TotHits - thys->CountHit); if(thys->MaxHitAll) ajFmtPrintF(outf, "# Max_hitcount: %d\n", thys->MaxHitAll); if(thys->MaxLimit) ajFmtPrintF(outf, "# Maxhits_stop: %B\n", thys->MaxLimit); } if(!doSingle || thys->Multi) ajFmtPrintF(outf, "#---------------------------------------\n"); else ajFmtPrintF(outf, "########################################\n"); ajStrDel(&tmpstr); return; } ID ajReportSetHeaderS TY public MO ajreport LB core XX DE Defines a feature report header XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r header const AjPStr PD Report header with embedded newlines PX RT void RD RX // void ajReportSetHeaderS(AjPReport thys, const AjPStr header) { if(!thys) return; ajStrAssignS(&thys->Header, header); return; } ID ajReportSetHeaderC TY public MO ajreport LB core XX DE Defines a feature report header XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r header const char* PD Report header with embedded newlines PX RT void RD RX // void ajReportSetHeaderC(AjPReport thys, const char* header) { if(!thys) return; ajStrAssignC(&thys->Header, header); return; } ID ajReportAppendHeaderC TY public MO ajreport LB core XX DE Appends to a feature report header XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r header const char* PD Report header with embedded newlines PX RT void RD RX // void ajReportAppendHeaderC(AjPReport thys, const char* header) { if(!thys) return; if(ajStrGetLen(thys->Header)) if(ajStrGetCharLast(thys->Header) != '\n') ajStrAppendK(&thys->Header, '\n'); ajStrAppendC(&thys->Header, header); return; } ID ajReportAppendHeaderS TY public MO ajreport LB core XX DE Defines a feature report header XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r header const AjPStr PD Report header with embedded newlines PX RT void RD RX // void ajReportAppendHeaderS(AjPReport thys, const AjPStr header) { if(!thys) return; if(ajStrGetLen(thys->Header)) if(ajStrGetCharLast(thys->Header) != '\n') ajStrAppendK(&thys->Header, '\n'); ajStrAppendS(&thys->Header, header); return; } ID ajReportSetSubheaderC TY public MO ajreport LB core XX DE Appends to a feature report subheader XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r header const char* PD Report header with embedded newlines PX RT void RD RX // void ajReportSetSubheaderC(AjPReport thys, const char* header) { if(!thys) return; ajStrAssignC(&thys->SubHeader, header); return; } ID ajReportSetSubheaderS TY public MO ajreport LB core XX DE Defines a feature report subheader XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r header const AjPStr PD Report header with embedded newlines PX RT void RD RX // void ajReportSetSubheaderS(AjPReport thys, const AjPStr header) { if(!thys) return; ajStrAssignS(&thys->SubHeader, header); return; } ID ajReportAppendSubheaderC TY public MO ajreport LB core XX DE Appends to a feature report subheader XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r header const char* PD Report header with embedded newlines PX RT void RD RX // void ajReportAppendSubheaderC(AjPReport thys, const char* header) { if(!thys) return; if(ajStrGetLen(thys->SubHeader)) if(ajStrGetCharLast(thys->SubHeader) != '\n') ajStrAppendK(&thys->SubHeader, '\n'); ajStrAppendC(&thys->SubHeader, header); return; } ID ajReportAppendSubheaderS TY public MO ajreport LB core XX DE Defines a feature report subheader XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r header const AjPStr PD Report header with embedded newlines PX RT void RD RX // void ajReportAppendSubheaderS(AjPReport thys, const AjPStr header) { if(!thys) return; if(ajStrGetLen(thys->SubHeader)) if(ajStrGetCharLast(thys->SubHeader) != '\n') ajStrAppendK(&thys->SubHeader, '\n'); ajStrAppendS(&thys->SubHeader, header); return; } ID ajReportSetSeqstats TY public MO ajreport LB core XX DE Defines a feature report tail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r seqall const AjPSeqall PD Sequence stream object PX RT void RD RX // void ajReportSetSeqstats(AjPReport thys, const AjPSeqall seqall) { if(!thys) return; thys->Totseqs = ajSeqallGetCount(seqall); thys->Totlength = ajSeqallGetTotlength(seqall); return; } ID ajReportSetSeqsetstats TY public MO ajreport LB core XX DE Defines a feature report tail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r seqset const AjPSeqset PD Sequence set object PX RT void RD RX // void ajReportSetSeqsetstats(AjPReport thys, const AjPSeqset seqset) { if(!thys) return; thys->Totseqs = ajSeqsetGetSize(seqset); thys->Totlength = ajSeqsetGetTotlength(seqset); return; } ID ajReportSetStatistics TY public MO ajreport LB core XX DE Defines a feature report tail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r totseqs ajlong PD Total number of sequences processed PX PN [3] PA r totlength ajlong PD Total sequence length processed PX RT void RD RX // void ajReportSetStatistics(AjPReport thys, ajlong totseqs, ajlong totlength) { if(!thys) return; thys->Totseqs = totseqs; thys->Totlength = totlength; return; } ID ajReportSetTailC TY public MO ajreport LB core XX DE Defines a feature report tail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r tail const char* PD Report tail with embedded newlines PX RT void RD RX // void ajReportSetTailC(AjPReport thys, const char* tail) { if(!thys) return; ajStrAssignC(&thys->Tail, tail); return; } ID ajReportSetTailS TY public MO ajreport LB core XX DE Defines a feature report tail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r tail const AjPStr PD Report tail with embedded newlines PX RT void RD RX // void ajReportSetTailS(AjPReport thys, const AjPStr tail) { if(!thys) return; ajStrAssignS(&thys->Tail, tail); return; } ID ajReportAppendTailC TY public MO ajreport LB core XX DE Appends to a feature report tail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r tail const char* PD Report tail with embedded newlines PX RT void RD RX // void ajReportAppendTailC(AjPReport thys, const char* tail) { if(!thys) return; if(ajStrGetLen(thys->Tail)) if(ajStrGetCharLast(thys->Tail) != '\n') ajStrAppendK(&thys->Tail, '\n'); ajStrAppendC(&thys->Tail, tail); return; } ID ajReportAppendTailS TY public MO ajreport LB core XX DE Appends to a feature report tail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r tail const AjPStr PD Report tail with embedded newlines PX RT void RD RX // void ajReportAppendTailS(AjPReport thys, const AjPStr tail) { if(!thys) return; if(ajStrGetLen(thys->Tail)) if(ajStrGetCharLast(thys->Tail) != '\n') ajStrAppendK(&thys->Tail, '\n'); ajStrAppendS(&thys->Tail, tail); return; } ID ajReportSetSubtailC TY public MO ajreport LB core XX DE Defines a feature report subtail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r tail const char* PD Report tail with embedded newlines PX RT void RD RX // void ajReportSetSubtailC(AjPReport thys, const char* tail) { if(!thys) return; ajStrAssignC(&thys->SubTail, tail); return; } ID ajReportSetSubtailS TY public MO ajreport LB core XX DE Defines a feature report subtail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r tail const AjPStr PD Report tail with embedded newlines PX RT void RD RX // void ajReportSetSubtailS(AjPReport thys, const AjPStr tail) { if(!thys) return; ajStrAssignS(&thys->SubTail, tail); return; } ID ajReportAppendSubtailC TY public MO ajreport LB core XX DE Appends to a feature report subtail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r tail const char* PD Report tail with embedded newlines PX RT void RD RX // void ajReportAppendSubtailC(AjPReport thys, const char* tail) { if(!thys) return; if(ajStrGetLen(thys->SubTail)) if(ajStrGetCharLast(thys->SubTail) != '\n') ajStrAppendK(&thys->SubTail, '\n'); ajStrAppendC(&thys->SubTail, tail); return; } ID ajReportAppendSubtailS TY public MO ajreport LB core XX DE Appends to a feature report subtail XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r tail const AjPStr PD Report tail with embedded newlines PX RT void RD RX // void ajReportAppendSubtailS(AjPReport thys, const AjPStr tail) { if(!thys) return; if(ajStrGetLen(thys->SubTail)) if(ajStrGetCharLast(thys->SubTail) != '\n') ajStrAppendK(&thys->SubTail, '\n'); ajStrAppendS(&thys->SubTail, tail); return; } ID ajReportSetType TY public MO ajreport LB core XX DE Sets the report type (if it is not set already) XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA r ftable const AjPFeattable PD Feature table object PX PN [3] PA r seq const AjPSeq PD Sequence object PX RT void RD RX // void ajReportSetType(AjPReport thys, const AjPFeattable ftable, const AjPSeq seq) { ajDebug("ajReportSetType '%S' ft: '%S' sq: '%S'\n", thys->Type, ftable->Type, seq->Type); if(ajStrGetLen(thys->Type)) return; if(ajStrGetLen(ftable->Type)) { ajStrAssignS(&thys->Type, ftable->Type); return; } if(seq && ajStrGetLen(seq->Type)) { ajStrAssignS(&thys->Type, seq->Type); return; } return; } ID reportCharname TY static MO ajreport LB core XX DE Returns 'residues' for a protein report, 'bases' for a nucleotide report. XX PN [1] PA r thys const AjPReport PD Report object PX RT const char* RD String to print the sequence character type RX // static const char* reportCharname(const AjPReport thys) { static const char* protstr = "residues"; static const char* nucstr = "bases"; if(!ajStrGetLen(thys->Type)) return protstr; switch(ajStrGetCharFirst(thys->Type)) { case 'n': case 'N': return nucstr; default: break; } return protstr; } ID ajReportGetSeqnameSeq TY public MO ajreport LB core XX DE Returns the sequence name or USA depending on the setting in the DE report object (derived from the ACD and command line -rusa option) XX PN [1] PA r thys const AjPReport PD Report object PX PN [2] PA r seq const AjPSeq PD Sequence object PX RT const AjPStr RD Sequence name for this report RX // const AjPStr ajReportGetSeqnameSeq(const AjPReport thys, const AjPSeq seq) { if(thys->Showusa) return ajSeqGetUsaS(seq); return ajSeqGetNameS(seq); } ID ajReportAddFileF TY public MO ajreport LB core XX DE Adds an additional file name and description to the report. DE DE These will appear in the header as "Additional files" DE to let the user know that there are associated output files available. XX PN [1] PA u thys AjPReport PD Report object PX PN [2] PA u file AjPFile PD File PX PN [3] PA r type const AjPStr PD Type (simple text description) PX RT void RD RX // void ajReportAddFileF(AjPReport thys, AjPFile file, const AjPStr type) { AjPStr tmpname = NULL; AjPStr tmptype = NULL; if(!thys->FileTypes) thys->FileTypes = ajListstrNew(); if(!thys->FileNames) thys->FileNames = ajListstrNew(); ajStrAssignS(&tmptype, type); ajListstrPushAppend(thys->FileTypes, tmptype); ajFmtPrintS(&tmpname, "%F", file); ajListstrPushAppend(thys->FileNames, tmpname); return; } ID ajReportPrintFormat TY public MO ajreport LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajReportPrintFormat(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Report output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Alias Alias name\n"); ajFmtPrintF(outf, "# Nuc Valid for nucleotide sequences\n"); ajFmtPrintF(outf, "# Pro Valid for protein sequences\n"); ajFmtPrintF(outf, "# Head Include standard header/footer blocks\n"); ajFmtPrintF(outf, "# Mintags Minimum number of tags to be specified " "(0 for all)\n"); ajFmtPrintF(outf, "# Showseq Includes sequence\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Alias Nuc Pro Head Mintags Showseq " "Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "RFormat {\n"); for(i=0; reportFormat[i].Name; i++) if(full || !reportFormat[i].Alias) ajFmtPrintF(outf, " %-12s %5B %3B %3B %3B %7d %7B \"%s\"\n", reportFormat[i].Name, reportFormat[i].Alias, reportFormat[i].Nucleotide, reportFormat[i].Protein, reportFormat[i].Showheader, reportFormat[i].Mintags, reportFormat[i].Showseq, reportFormat[i].Desc); ajFmtPrintF(outf, "}\n\n"); return; } ID ajReportPrinthtmlFormat TY public MO ajreport LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajReportPrinthtmlFormat(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, "
Reference sequence FormatDescription
\n%-12s\n" "\"%s\"
"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); for(i=0; reportFormat[i].Name; i++) if(!reportFormat[i].Alias) ajFmtPrintF(outf, "" "" "\n", reportFormat[i].Name, reportFormat[i].Alias, reportFormat[i].Nucleotide, reportFormat[i].Protein, reportFormat[i].Showheader, reportFormat[i].Mintags, reportFormat[i].Showseq, reportFormat[i].Desc); ajFmtPrintF(outf, "
Report FormatAliasNucProShowheaderMintagsShowseqDescription
\n%-12s\n%5B%3B%3B%3B%7d%7B\"%s\"
\n"); return; } ID ajReportPrintbookFormat TY public MO ajreport LB core XX DE Reports the report format internals in DocBook format XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajReportPrintbookFormat(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; AjPList fmtlist; AjPStr* names; fmtlist = ajListstrNew(); ajFmtPrintF(outf, "The supported report formats are summarised " "in the table below. The columns are as follows: " "Output format (format name), " "Nuc " "(\"true\" indicates nucleotide sequence data may be " "represented), " "Pro (\"true\" indicates protein " "sequence data may be represented, " "Header (whether the standard EMBOSS " "report header is included), " "Seq (whether the sequence corresponding " "to the features is included), " "Tags (number of specific tag-values " "reported. A non-zero value suggests a format is not " "suitable for application output that does not generate " "these specific tags.) " "Description (short description of " "the format).\n\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=1; reportFormat[i].Name; i++) { if(!reportFormat[i].Alias) { namestr = ajStrNewC(reportFormat[i].Name); ajListPush(fmtlist, namestr); namestr = NULL; } } ajListSort(fmtlist, &ajStrVcmp); ajListstrToarray(fmtlist, &names); for(i=0; names[i]; i++) { for(j=0; reportFormat[j].Name; j++) { if(ajStrMatchC(names[i],reportFormat[j].Name)) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", reportFormat[j].Name); ajFmtPrintF(outf, " \n", reportFormat[j].Nucleotide); ajFmtPrintF(outf, " \n", reportFormat[j].Protein); ajFmtPrintF(outf, " \n", reportFormat[j].Showheader); ajFmtPrintF(outf, " \n", reportFormat[j].Showseq); ajFmtPrintF(outf, " \n", reportFormat[j].Mintags); ajFmtPrintF(outf, " \n", reportFormat[j].Desc); ajFmtPrintF(outf, " \n"); } } } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Report formats
Output FormatNucProHeaderSeqTagsDescription
%s%B%B%B%B%d%s
\n"); ajStrDel(&namestr); names = NULL; ajListstrFreeData(&fmtlist); return; } ID ajReportPrintwikiFormat TY public MO ajreport LB core XX DE Reports the report format internals as wikitext XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajReportPrintwikiFormat(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!Nuc!!Pro!!Header!!Seq||Tags!!" "class=\"unsortable\"|Description\n"); for(i=1; reportFormat[i].Name; i++) { if(!reportFormat[i].Alias) { ajFmtPrintF(outf, "|-\n"); ajStrAssignC(&namestr, reportFormat[i].Name); for(j=i+1; reportFormat[j].Name; j++) { if(reportFormat[j].Write == reportFormat[i].Write) { ajFmtPrintAppS(&namestr, "
%s", reportFormat[j].Name); if(!reportFormat[j].Alias) { ajWarn("Report output format '%s' same as '%s' " "but not alias", reportFormat[j].Name, reportFormat[i].Name); } } } ajFmtPrintF(outf, "|%S||%B||%B||%B||%B||%d||%s\n", namestr, reportFormat[i].Nucleotide, reportFormat[i].Protein, reportFormat[i].Showheader, reportFormat[i].Showseq, reportFormat[i].Mintags, reportFormat[i].Desc); } } ajFmtPrintF(outf, "|}\n"); ajStrDel(&namestr); return; } ID ajReportDummyFunction TY public MO ajreport LB core XX DE Dummy function to catch all unused functions defined in the ajreport DE source file. XX RT void RD RX // void ajReportDummyFunction(void) { return; } ID ajReportExit TY public MO ajreport LB core XX DE Cleans up report processing internal memory XX RT void RD RX // void ajReportExit(void) { ajRegFree(&reportTagExp); return; } ID ajResourceNew TY public MO ajresource LB core XX DE Resource data constructor XX RT AjPResource RD New object RX // AjPResource ajResourceNew(void) { AjPResource ret; AJNEW0(ret); ret->Id = ajStrNew(); ret->Idalt = ajListstrNew(); ret->Acc = ajStrNew(); ret->Name = ajStrNew(); ret->Desc = ajStrNew(); ret->Url = ajStrNew(); ret->Urllink = ajStrNew(); ret->Urlrest = ajStrNew(); ret->Urlsoap = ajStrNew(); ret->Cat = ajListstrNew(); ret->Taxon = ajListNew(); ret->Edamdat = ajListNew(); ret->Edamfmt = ajListNew(); ret->Edamid = ajListNew(); ret->Edamtpc = ajListNew(); ret->Xref = ajListNew(); ret->Query = ajListNew(); ret->Example = ajListstrNew(); return ret; } ID ajResourceNewDrcat TY public MO ajresource LB core XX DE Resource data constructor for an entry in DRCAT XX PN [1] PA r dbname const AjPStr PD DRCAT identifier PX RT AjPResource RD New object RX // AjPResource ajResourceNewDrcat(const AjPStr dbname) { AjPResource drcat = NULL; AjPResourcein drcatin = NULL; drcat = ajResourceNew(); drcatin = ajResourceinNewDrcat(dbname); if(!ajResourceinRead(drcatin, drcat)) ajResourceDel(&drcat); ajResourceinDel(&drcatin); return drcat; } ID ajResourceNewResource TY public MO ajresource LB core XX DE Copy constructor for a public resource object XX PN [1] PA r res const AjPResource PD Resource object PX RT AjPResource RD Resource object RX // AjPResource ajResourceNewResource(const AjPResource res) { AjPResource ret; AJNEW0(ret); ret->Id = ajStrNewS(res->Id); ret->Acc = ajStrNewS(res->Acc); ret->Name = ajStrNewS(res->Name); ret->Desc = ajStrNewS(res->Desc); ret->Url = ajStrNewS(res->Url); ret->Cat = ajListstrNewList(res->Cat); ret->Idalt = ajListstrNewList(res->Idalt); ret->Taxon = ajListNew(); ret->Edamdat = ajListNew(); ret->Edamfmt = ajListNew(); ret->Edamid = ajListNew(); ret->Edamtpc = ajListNew(); ajRestermlistClone(res->Taxon, ret->Taxon); ajRestermlistClone(res->Edamdat, ret->Edamdat); ajRestermlistClone(res->Edamfmt, ret->Edamfmt); ajRestermlistClone(res->Edamid, ret->Edamid); ajRestermlistClone(res->Edamtpc, ret->Edamtpc); ret->Xref = ajListNew(); ajReslinklistClone(res->Xref, ret->Xref); ret->Query = ajListNew(); ajResquerylistClone(res->Query, ret->Query); ret->Example = ajListstrNewList(res->Example); return ret; } ID ajResourceDel TY public MO ajresource LB core XX DE Data resource data destructor XX PN [1] PA d Presource AjPResource* PD Data resource data object to delete PX RT void RD RX // void ajResourceDel(AjPResource *Presource) { AjPResource thys; AjPReslink reslink; AjPResquery resqry; AjPResterm resterm; AjIList iter; if(!Presource) return; if(!(*Presource)) return; thys = *Presource; ajStrDel(&thys->Id); ajListstrFreeData(&thys->Idalt); ajStrDel(&thys->Acc); ajStrDel(&thys->Name); ajStrDel(&thys->Desc); ajStrDel(&thys->Url); ajStrDel(&thys->Urllink); ajStrDel(&thys->Urlrest); ajStrDel(&thys->Urlsoap); ajListstrFreeData(&thys->Cat); iter = ajListIterNewread(thys->Taxon); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); ajRestermDel(&resterm); } ajListIterDel(&iter); ajListFree(&thys->Taxon); iter = ajListIterNewread(thys->Edamdat); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); ajRestermDel(&resterm); } ajListIterDel(&iter); ajListFree(&thys->Edamdat); iter = ajListIterNewread(thys->Edamfmt); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); ajRestermDel(&resterm); } ajListIterDel(&iter); ajListFree(&thys->Edamfmt); iter = ajListIterNewread(thys->Edamid); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); ajRestermDel(&resterm); } ajListIterDel(&iter); ajListFree(&thys->Edamid); iter = ajListIterNewread(thys->Edamtpc); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); ajRestermDel(&resterm); } ajListIterDel(&iter); ajListFree(&thys->Edamtpc); iter = ajListIterNewread(thys->Xref); while(!ajListIterDone(iter)) { reslink = ajListIterGet(iter); ajReslinkDel(&reslink); } ajListIterDel(&iter); ajListFree(&thys->Xref); iter = ajListIterNewread(thys->Query); while(!ajListIterDone(iter)) { resqry = ajListIterGet(iter); ajResqueryDel(&resqry); } ajListIterDel(&iter); ajListFree(&thys->Query); ajListstrFreeData(&thys->Example); ajStrDel(&thys->Db); ajStrDel(&thys->Setdb); ajStrDel(&thys->Full); ajStrDel(&thys->Qry); ajStrDel(&thys->Formatstr); ajStrDel(&thys->Filename); ajStrDel(&thys->TextPtr); AJFREE(*Presource); *Presource = NULL; return; } ID ajResourceGetDbdata TY public MO ajresource LB core XX DE Returns a query database definition using a data resource data object. XX PN [1] PA r resource const AjPResource PD Data resource data object. PX PN [2] PA w qry AjPQuery PD Query object. PX PN [3] PA f findformat AjBool function PD Find format function for datatype PX RT AjBool RD True on success RX // AjBool ajResourceGetDbdata(const AjPResource resource, AjPQuery qry, AjBool findformat(const AjPStr format, ajint *iformat)) { AjIList iter; AjPResquery resqry = NULL; ajint format; AjBool ret = ajFalse; if(!resource) return ajFalse; qry->InDrcat = ajTrue; ajStrAssignC(&qry->DbType, ajNamQueryGetDatatypeC(qry)); ajDebug("ajResourceGetDbdata dbtype %S %d\n", qry->DbType, qry->DataType); if(qry->DataType == AJDATATYPE_URL) { ajStrAssignC(&qry->Method, "urlonly"); qry->QueryType = AJQUERY_ENTRY; qry->HasAcc = ajFalse; iter = ajListIterNewread(resource->Query); while(!ajListIterDone(iter)) { resqry = ajListIterGet(iter); ajDebug("ajResourceGetDbdata test fmt: '%S'\n", resqry->Format); if(ajStrMatchC(resqry->Format, "HTML")) { ajDebug(" OK fmt: '%S' url '%S'\n", resqry->Format, resqry->Url); ajStrAssignS(&qry->Formatstr, resqry->Format); ajStrAssignS(&qry->DbUrl, resqry->Url); ret = ajTrue; } } ajListIterDel(&iter); } else { ajStrAssignC(&qry->Method, "url"); qry->QueryType = AJQUERY_ENTRY; qry->HasAcc = ajFalse; iter = ajListIterNewread(resource->Query); while(!ajListIterDone(iter)) { resqry = ajListIterGet(iter); ajDebug("ajResourceGetDbdata test fmt: '%S'\n", resqry->Format); if(findformat(resqry->Format, &format)) { ajDebug(" OK fmt: '%S' url '%S'\n", resqry->Format, resqry->Url); ajStrAssignS(&qry->Formatstr, resqry->Format); ajStrAssignS(&qry->DbUrl, resqry->Url); ret = ajTrue; } } ajListIterDel(&iter); } return ret; } ID ajResourceGetEntry TY public MO ajresource LB core XX DE Return the complete text XX PN [1] PA r resource const AjPResource PD Data resource PX RT const AjPStr RD Returned entry text RX // const AjPStr ajResourceGetEntry(const AjPResource resource) { if(resource->TextPtr) return resource->TextPtr; return ajStrConstEmpty(); } ID ajResourceGetId TY public MO ajresource LB core XX DE Return the identifier XX PN [1] PA r resource const AjPResource PD Data resource PX RT const AjPStr RD Returned id RX // const AjPStr ajResourceGetId(const AjPResource resource) { return resource->Id; } ID ajResourceGetQryC TY public MO ajresource LB core XX DE Returns the query string of a data resource data object. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r resource const AjPResource PD Data resource data object. PX RT const char* RD Query as a character string. RX // const char* ajResourceGetQryC(const AjPResource resource) { return MAJSTRGETPTR(ajResourceGetQryS(resource)); } ID ajResourceGetQryS TY public MO ajresource LB core XX DE Returns the query string of a data resource data object. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r resource const AjPResource PD Data resource data object. PX RT const AjPStr RD Query as a string. RX // const AjPStr ajResourceGetQryS(const AjPResource resource) { ajDebug("ajResourceGetQryS '%S'\n", resource->Qry); if(ajStrGetLen(resource->Qry)) return resource->Qry; resourceMakeQry(resource, &resourceTempQry); return resourceTempQry; } ID resourceMakeQry TY static MO ajresource LB core XX DE Sets the query for a data resource data object. XX PN [1] PA r thys const AjPResource PD Data resource data object PX PN [2] PA w qry AjPStr* PD Query string in full PX RT void RD RX // static void resourceMakeQry(const AjPResource thys, AjPStr* qry) { ajDebug("resourceMakeQry (Id <%S> Formatstr <%S> Db <%S> " "Filename <%S>)\n", thys->Id, thys->Formatstr, thys->Db, thys->Filename); /* ajResourceTrace(thys); */ if(ajStrGetLen(thys->Db)) ajFmtPrintS(qry, "%S-id:%S", thys->Db, thys->Id); else { ajFmtPrintS(qry, "%S::%S:%S", thys->Formatstr, thys->Filename,thys->Id); } ajDebug(" result: <%S>\n", *qry); return; } ID ajResourceClear TY public MO ajresource LB core XX DE Resets all data for a data resource data object so that it can be reused. XX PN [1] PA u resource AjPResource PD Data resource data object PX RT void RD RX // void ajResourceClear(AjPResource resource) { AjPReslink lnk = NULL; AjPResquery qry = NULL; AjPResterm resterm = NULL; AjPStr ptr = NULL; if(MAJSTRGETLEN(resource->Id)) ajStrSetClear(&resource->Id); if(ajListGetLength(resource->Idalt)) while(ajListstrPop(resource->Idalt,&ptr)) ajStrDel(&ptr); if(MAJSTRGETLEN(resource->Acc)) ajStrSetClear(&resource->Acc); if(MAJSTRGETLEN(resource->Name)) ajStrSetClear(&resource->Name); if(MAJSTRGETLEN(resource->Desc)) ajStrSetClear(&resource->Desc); if(MAJSTRGETLEN(resource->Url)) ajStrSetClear(&resource->Url); if(MAJSTRGETLEN(resource->Urllink)) ajStrSetClear(&resource->Urllink); if(MAJSTRGETLEN(resource->Urlrest)) ajStrSetClear(&resource->Urlrest); if(MAJSTRGETLEN(resource->Urlsoap)) ajStrSetClear(&resource->Urlsoap); if(ajListGetLength(resource->Cat)) while(ajListstrPop(resource->Cat,&ptr)) ajStrDel(&ptr); if(ajListGetLength(resource->Taxon)) while(ajListPop(resource->Taxon,(void**)&resterm)) ajRestermDel(&resterm); if(ajListGetLength(resource->Edamdat)) while(ajListPop(resource->Edamdat,(void**)&resterm)) ajRestermDel(&resterm); if(ajListGetLength(resource->Edamfmt)) while(ajListPop(resource->Edamfmt,(void**)&resterm)) ajRestermDel(&resterm); if(ajListGetLength(resource->Edamid)) while(ajListPop(resource->Edamid,(void**)&resterm)) ajRestermDel(&resterm); if(ajListGetLength(resource->Edamtpc)) while(ajListPop(resource->Edamtpc,(void**)&resterm)) ajRestermDel(&resterm); if(ajListGetLength(resource->Xref)) while(ajListPop(resource->Xref,(void**)&lnk)) ajReslinkDel(&lnk); if(ajListGetLength(resource->Query)) while(ajListPop(resource->Query,(void**)&qry)) ajResqueryDel(&qry); if(ajListGetLength(resource->Example)) while(ajListstrPop(resource->Example,&ptr)) ajStrDel(&ptr); if(MAJSTRGETLEN(resource->Db)) ajStrSetClear(&resource->Db); if(MAJSTRGETLEN(resource->Setdb)) ajStrSetClear(&resource->Setdb); if(MAJSTRGETLEN(resource->Full)) ajStrSetClear(&resource->Full); if(MAJSTRGETLEN(resource->Qry)) ajStrSetClear(&resource->Qry); if(MAJSTRGETLEN(resource->Formatstr)) ajStrSetClear(&resource->Formatstr); if(MAJSTRGETLEN(resource->Filename)) ajStrSetClear(&resource->Filename); ajStrDel(&resource->TextPtr); resource->Count = 0; resource->Fpos = 0L; resource->Format = 0; return; } ID ajResourceTrace TY public MO ajresource LB core XX DE Write debug report of a resource object XX PN [1] PA r thys const AjPResource PD Resource object PX RT void RD RX // void ajResourceTrace(const AjPResource thys) { AjIList iter; AjPStr tmpstr; ajuint i; AjPReslink reslink = NULL; AjPResquery resqry = NULL; AjPResterm resterm = NULL; if(!thys) { ajDebug("ajResourceTrace NULL\n"); return; } ajDebug("\najResourceTrace\n"); ajDebug(" Id: %S\n", thys->Id); ajDebug(" Acc: %S\n", thys->Acc); ajDebug(" Name: %S\n", thys->Name); ajDebug(" Desc: %S\n", thys->Desc); ajDebug(" Url: %S\n", thys->Url); ajDebug(" Idalt: %Lu\n", ajListGetLength(thys->Idalt)); if(ajListGetLength(thys->Idalt)) { i=0; iter = ajListIterNewread(thys->Idalt); while(!ajListIterDone(iter)) { tmpstr = ajListIterGet(iter); ajDebug("%15d: %S\n", ++i, tmpstr); } ajListIterDel(&iter); } ajDebug(" Cat: %Lu\n", ajListGetLength(thys->Cat)); if(ajListGetLength(thys->Cat)) { i = 0; iter = ajListIterNewread(thys->Cat); while(!ajListIterDone(iter)) { tmpstr = ajListIterGet(iter); ajDebug("%15d: %S\n", ++i, tmpstr); } ajListIterDel(&iter); } ajDebug(" Taxon: %Lu\n", ajListGetLength(thys->Taxon)); if(ajListGetLength(thys->Taxon)) { i = 0; iter = ajListIterNewread(thys->Taxon); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); ajDebug("%15d: %S | %S\n", ++i, resterm->Id, resterm->Name); } ajListIterDel(&iter); } ajDebug(" Edamtpc: %Lu\n", ajListGetLength(thys->Edamtpc)); if(ajListGetLength(thys->Edamtpc)) { i = 0; iter = ajListIterNewread(thys->Edamtpc); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); ajDebug("%15d: %S | %S\n", ++i, resterm->Id, resterm->Name); } ajListIterDel(&iter); } ajDebug(" Edamdat: %Lu\n", ajListGetLength(thys->Edamdat)); if(ajListGetLength(thys->Edamdat)) { i = 0; iter = ajListIterNewread(thys->Edamdat); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); ajDebug("%15d: %S | %S\n", ++i, resterm->Id, resterm->Name); } ajListIterDel(&iter); } ajDebug(" Edamid: %Lu\n", ajListGetLength(thys->Edamid)); if(ajListGetLength(thys->Edamid)) { i = 0; iter = ajListIterNewread(thys->Edamid); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); ajDebug("%15d: %S | %S\n", ++i, resterm->Id, resterm->Name); } ajListIterDel(&iter); } ajDebug(" Edamfmt: %Lu\n", ajListGetLength(thys->Edamfmt)); if(ajListGetLength(thys->Edamfmt)) { i = 0; iter = ajListIterNewread(thys->Edamfmt); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); ajDebug("%15d: %S | %S\n", ++i, resterm->Id, resterm->Name); } ajListIterDel(&iter); } ajDebug(" Xref: %Lu\n", ajListGetLength(thys->Xref)); if(ajListGetLength(thys->Xref)) { i = 0; iter = ajListIterNewread(thys->Xref); while(!ajListIterDone(iter)) { reslink = ajListIterGet(iter); ajDebug("%15d: %S | %S\n", ++i, reslink->Source, reslink->Term); } ajListIterDel(&iter); } ajDebug(" Query: %Lu\n", ajListGetLength(thys->Query)); if(ajListGetLength(thys->Query)) { i = 0; iter = ajListIterNewread(thys->Query); while(!ajListIterDone(iter)) { resqry = ajListIterGet(iter); ajDebug("%15d: %S | %S | %S | %S\n", ++i, resqry->Datatype, resqry->Format, resqry->Term, resqry->Url); } ajListIterDel(&iter); } ajDebug(" Example: %Lu\n", ajListGetLength(thys->Example)); if(ajListGetLength(thys->Example)) { i = 0; iter = ajListIterNewread(thys->Example); while(!ajListIterDone(iter)) { tmpstr = ajListIterGet(iter); ajDebug("%15d: %S\n", ++i, tmpstr); } ajListIterDel(&iter); } return; } ID ajReslinkNew TY public MO ajresource LB core XX DE Constructor for a public resource link object XX RT AjPReslink RD Resource link object RX // AjPReslink ajReslinkNew(void) { AjPReslink ret; AJNEW0(ret); return ret; } ID ajReslinkNewReslink TY public MO ajresource LB core XX DE Constructor for a public resource link object XX PN [1] PA r reslink const AjPReslink PD Source resource link object PX RT AjPReslink RD Resource link object RX // AjPReslink ajReslinkNewReslink(const AjPReslink reslink) { AjPReslink ret; AJNEW0(ret); if(!reslink) return ret; ajStrAssignS(&ret->Source, reslink->Source); ajStrAssignS(&ret->Term, reslink->Term); ret->Nterms = reslink->Nterms; return ret; } ID ajReslinkDel TY public MO ajresource LB core XX DE Destructor for a public resource link object XX PN [1] PA d Preslink AjPReslink* PD Resource link object PX RT void RD RX // void ajReslinkDel(AjPReslink *Preslink) { AjPReslink thys; if(!Preslink) return; if(!*Preslink) return; thys = *Preslink; ajStrDel(&thys->Source); ajStrDel(&thys->Term); AJFREE(*Preslink); return; } ID ajReslinklistClone TY public MO ajresource LB core XX DE Copy a list of resource links to another list XX PN [1] PA r src const AjPList PD Source list of resource links PX PN [2] PA w dest AjPList PD Destination list of resource links PX RT AjBool RD True on success RX // AjBool ajReslinklistClone(const AjPList src, AjPList dest) { AjIList iter; AjPReslink linkout = NULL; AjPReslink linkin = NULL; if(ajListGetLength(dest)) return ajFalse; iter = ajListIterNewread(src); while ((linkin = (AjPReslink) ajListIterGet(iter))) { linkout = ajReslinkNewReslink(linkin); ajListPushAppend(dest, linkout); } ajListIterDel(&iter); return ajTrue; } ID ajResqueryNew TY public MO ajresource LB core XX DE Constructor for a public resource query object XX RT AjPResquery RD Resource query object RX // AjPResquery ajResqueryNew(void) { AjPResquery ret; AJNEW0(ret); return ret; } ID ajResqueryNewResquery TY public MO ajresource LB core XX DE Constructor for a public resource query object XX PN [1] PA r qry const AjPResquery PD Source resource query object PX RT AjPResquery RD Resource query object RX // AjPResquery ajResqueryNewResquery(const AjPResquery qry) { AjPResquery ret; AJNEW0(ret); if(!qry) return ret; ajStrAssignS(&ret->Datatype, qry->Datatype); ajStrAssignS(&ret->Format, qry->Format); ajStrAssignS(&ret->FormatTerm, qry->FormatTerm); ajStrAssignS(&ret->Term, qry->Term); ajStrAssignS(&ret->Url, qry->Url); ret->Nterms = qry->Nterms; return ret; } ID ajResqueryDel TY public MO ajresource LB core XX DE Destructor for a public resource query object XX PN [1] PA d Presquery AjPResquery* PD Resource query object PX RT void RD RX // void ajResqueryDel(AjPResquery *Presquery) { AjPResquery thys; if(!Presquery) return; if(!*Presquery) return; thys = *Presquery; ajStrDel(&thys->Datatype); ajStrDel(&thys->Format); ajStrDel(&thys->FormatTerm); ajStrDel(&thys->Term); ajStrDel(&thys->Url); AJFREE(*Presquery); return; } ID ajResquerylistClone TY public MO ajresource LB core XX DE Copy a list of resource queries to another list XX PN [1] PA r src const AjPList PD Source list of resource queries PX PN [2] PA w dest AjPList PD Destination list of resource queries PX RT AjBool RD True on success RX // AjBool ajResquerylistClone(const AjPList src, AjPList dest) { AjIList iter; AjPResquery qryout = NULL; AjPResquery qryin = NULL; if(ajListGetLength(dest)) return ajFalse; iter = ajListIterNewread(src); while ((qryin = (AjPResquery) ajListIterGet(iter))) { qryout = ajResqueryNewResquery(qryin); ajListPushAppend(dest, qryout); } ajListIterDel(&iter); return ajTrue; } ID ajRestermNew TY public MO ajresource LB core XX DE Constructor for a public resource term object XX RT AjPResterm RD Resource term object RX // AjPResterm ajRestermNew(void) { AjPResterm ret; AJNEW0(ret); return ret; } ID ajRestermNewResterm TY public MO ajresource LB core XX DE Constructor for a public resource term object XX PN [1] PA r term const AjPResterm PD Source resource term object PX RT AjPResterm RD Resource term object RX // AjPResterm ajRestermNewResterm(const AjPResterm term) { AjPResterm ret; AJNEW0(ret); if(!term) return ret; ajStrAssignS(&ret->Id, term->Id); ajStrAssignS(&ret->Name, term->Name); return ret; } ID ajRestermDel TY public MO ajresource LB core XX DE Destructor for a public resource term object XX PN [1] PA d Presterm AjPResterm* PD Resource term object PX RT void RD RX // void ajRestermDel(AjPResterm *Presterm) { AjPResterm thys; if(!Presterm) return; if(!*Presterm) return; thys = *Presterm; ajStrDel(&thys->Id); ajStrDel(&thys->Name); AJFREE(*Presterm); return; } ID ajRestermlistClone TY public MO ajresource LB core XX DE Copy a list of resource terms to another list XX PN [1] PA r src const AjPList PD Source list of resource terms PX PN [2] PA w dest AjPList PD Destination list of resource terms PX RT AjBool RD True on success RX // AjBool ajRestermlistClone(const AjPList src, AjPList dest) { AjIList iter; AjPResterm termout = NULL; AjPResterm termin = NULL; if(ajListGetLength(dest)) return ajFalse; iter = ajListIterNewread(src); while ((termin = (AjPResterm) ajListIterGet(iter))) { termout = ajRestermNewResterm(termin); ajListPushAppend(dest, termout); } ajListIterDel(&iter); return ajTrue; } ID ajResourceExit TY public MO ajresource LB core XX DE Cleans up data processing internal memory XX RT void RD RX // void ajResourceExit(void) { ajResourceinExit(); ajResourceoutExit(); return; } ID resourceAccess TY list MO ajresourcedb LB ajaxdb XX DE Functions to access each database or data access method XX // static AjOResourceAccess resourceAccess[] = { /* Name AccessFunction FreeFunction Qlink Description Alias Entry Query All Chunk Padding */ { NULL, NULL, NULL, NULL, NULL, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE }, { NULL, NULL, NULL, NULL, NULL, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE, AJFALSE } }; ID ajResourcedbInit TY public MO ajresourcedb LB ajaxdb XX DE Initialise data database internals XX RT void RD RX // void ajResourcedbInit(void) { AjPTable table; ajuint i = 0; table = ajResourceaccessGetDb(); while(resourceAccess[i].Name) { ajCallTableRegister(table, resourceAccess[i].Name, (void*) &resourceAccess[i]); i++; } return; } ID ajResourcedbPrintAccess TY public MO ajresourcedb LB ajaxdb XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajResourcedbPrintAccess(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Data resource access methods\n"); ajFmtPrintF(outf, "# Name Alias Entry Query All Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "method {\n"); for(i=0; resourceAccess[i].Name; i++) if(full || !resourceAccess[i].Alias) ajFmtPrintF(outf, " %-10s %5B %5B %5B %5B \"%s\"\n", resourceAccess[i].Name, resourceAccess[i].Alias, resourceAccess[i].Entry, resourceAccess[i].Query, resourceAccess[i].All, resourceAccess[i].Desc); ajFmtPrintF(outf, "}\n\n"); return; } ID ajResourcedbExit TY public MO ajresourcedb LB ajaxdb XX DE Cleans up data database processing internal memory XX RT void RD RX // void ajResourcedbExit(void) { return; } ID ajResourceinNew TY public MO ajresourceread LB core XX DE Creates a new data input object. XX RT AjPResourcein RD New data input object. RX CA new CT AjPResourcein CD Default constructor CX // AjPResourcein ajResourceinNew(void) { AjPResourcein pthis; AJNEW0(pthis); pthis->Input = ajTextinNewDatatype(AJDATATYPE_RESOURCE); pthis->ResourceData = NULL; return pthis; } ID ajResourceinNewDrcat TY public MO ajresourceread LB core XX DE Creates a new data input object with a resolved DRCAT query XX PN [1] PA r dbname const AjPStr PD DRCAT identifier PX RT AjPResourcein RD New data input object. RX CA new CT AjPResourcein CD Default constructor CX // AjPResourcein ajResourceinNewDrcat(const AjPStr dbname) { AjPResourcein pthis; AjPQuery qry; AjPTextin textin; AjPTextAccess textaccess; AjBool dbstat; AJNEW0(pthis); pthis->Input = ajTextinNewDatatype(AJDATATYPE_RESOURCE); pthis->ResourceData = NULL; textin = pthis->Input; qry = textin->Query; ajStrAssignC(&qry->DbName, "drcat"); dbstat = ajNamDbData(qry, 0); if(!dbstat) ajDie("ajResourceinNewDrcat '%S' failed to find drcat", dbname); ajFmtPrintS(&textin->Qry, "drcat:%S", dbname); ajStrAssignC(&qry->SingleField, "id"); ajStrAssignS(&qry->QryString, dbname); ajQueryAddFieldOrS(qry, qry->SingleField, qry->QryString); ajStrAssignC(&qry->SingleField, "acc"); ajQueryAddFieldOrS(qry, qry->SingleField, qry->QryString); ajStrAssignC(&qry->Formatstr, "drcat"); ajStrAssignC(&textin->Formatstr, "drcat"); resourceinformatFind(textin->Formatstr, &textin->Format); ajStrAssignC(&qry->Method, "emboss"); dbstat = ajNamDbQuery(qry); if(!dbstat) ajDie("ajResourceinNewDrcat '%S' failed to set querytype", dbname); qry->TextAccess = ajCallTableGetS(textDbMethods,qry->Method); textaccess = qry->TextAccess; (*textaccess->Access)(textin); return pthis; } ID ajResourceinDel TY public MO ajresourceread LB core XX DE Deletes a data input object. XX PN [1] PA d pthis AjPResourcein* PD Data resource input PX RT void RD RX CA delete CT AjPResourcein CD Default destructor CX // void ajResourceinDel(AjPResourcein* pthis) { AjPResourcein thys; if(!pthis) return; thys = *pthis; if(!thys) return; ajDebug("ajResourceinDel called qry:'%S'\n", thys->Input->Qry); ajTextinDel(&thys->Input); AJFREE(*pthis); return; } ID ajResourceinClear TY public MO ajresourceread LB core XX DE Clears a data input object back to "as new" condition, except DE for the query list which must be preserved. XX PN [1] PA w thys AjPResourcein PD Data resource input PX RT void RD RX CA modify CT AjPResourcein CD Resets ready for reuse. CX // void ajResourceinClear(AjPResourcein thys) { ajDebug("ajResourceinClear called\n"); if(!thys) return; ajTextinClear(thys->Input); thys->ResourceData = NULL; return; } ID ajResourceinQryC TY public MO ajresourceread LB core XX DE Resets a data input object using a new Universal DE Query Address XX PN [1] PA u thys AjPResourcein PD Data resource input object. PX PN [2] PA r txt const char* PD Query PX RT void RD RX // void ajResourceinQryC(AjPResourcein thys, const char* txt) { ajResourceinClear(thys); ajStrAssignC(&thys->Input->Qry, txt); return; } ID ajResourceinQryS TY public MO ajresourceread LB core XX DE Resets a data input object using a new Universal DE Query Address XX PN [1] PA u thys AjPResourcein PD Data resource input object. PX PN [2] PA r str const AjPStr PD Query PX RT void RD RX // void ajResourceinQryS(AjPResourcein thys, const AjPStr str) { if(!thys) return; ajResourceinClear(thys); ajStrAssignS(&thys->Input->Qry, str); return; } ID ajResourceinTrace TY public MO ajresourceread LB core XX DE Debug calls to trace the data in a data input object. XX PN [1] PA r thys const AjPResourcein PD Data resource input object. PX RT void RD RX // void ajResourceinTrace(const AjPResourcein thys) { ajDebug("data input trace\n"); ajDebug("====================\n\n"); ajTextinTrace(thys->Input); if(thys->ResourceData) ajDebug( " ResourceData: exists\n"); return; } ID ajResourceinRead TY public MO ajresourceread LB core XX DE If the file is not yet open, calls resourceinQryProcess to convert the query DE into an open file stream. DE DE Uses resourceinRead for the actual file reading. DE DE Returns the results in the AjPResource object. XX PN [1] PA u resourcein AjPResourcein PD Data resource data input definitions PX PN [2] PA w resource AjPResource PD Data resource data returned. PX RT AjBool RD ajTrue on success. RX CA input CT AjPResource CD Master data data input, calls specific functions for file access type and data data format. CX // AjBool ajResourceinRead(AjPResourcein resourcein, AjPResource resource) { AjBool ret = ajFalse; AjPQueryList node = NULL; AjBool listdata = ajFalse; if(resourcein->Input->Filebuff) { /* (a) if file still open, keep reading */ ret = resourceinRead(resourcein, resource); } else { /* (b) if we have a list, try the next query in the list */ if(ajListGetLength(resourcein->Input->List)) { listdata = ajTrue; ajListPop(resourcein->Input->List, (void**) &node); ajResourceinQryS(resourcein, node->Qry); resourceinQryRestore(resourcein, node); ajStrDel(&node->Qry); ajStrDel(&node->Formatstr); AJFREE(node); if(!resourceinQryProcess(resourcein, resource) && !ajListGetLength(resourcein->Input->List)) return ajFalse; ret = resourceinRead(resourcein, resource); } else { /* (c) Must be a query - decode it */ if(!resourceinQryProcess(resourcein, resource) && !ajListGetLength(resourcein->Input->List)) return ajFalse; if(ajListGetLength(resourcein->Input->List)) /* maybe a new list */ listdata = ajTrue; ret = resourceinRead(resourcein, resource); } } /* Now read whatever we got */ while(!ret && ajListGetLength(resourcein->Input->List)) { /* Failed, but we have a list still - keep trying it */ if(listdata) ajErr("Failed to read data data '%S'", resourcein->Input->Qry); listdata = ajTrue; ajListPop(resourcein->Input->List,(void**) &node); ajResourceinQryS(resourcein, node->Qry); resourceinQryRestore(resourcein, node); ajStrDel(&node->Qry); ajStrDel(&node->Formatstr); AJFREE(node); if(!resourceinQryProcess(resourcein, resource)) continue; ret = resourceinRead(resourcein, resource); } if(!ret) { if(listdata) ajErr("Failed to read data data '%S'", resourcein->Input->Qry); return ajFalse; } resourceDefine(resource, resourcein); return ajTrue; } ID resourceinQueryMatch TY static MO ajresourceread LB core XX DE Compares a data data item to a query and returns true if they match. XX PN [1] PA r thys const AjPQuery PD query. PX PN [2] PA r resource const AjPResource PD Data resource data. PX RT AjBool RD ajTrue if the data data matches the query. RX // static AjBool resourceinQueryMatch(const AjPQuery thys, const AjPResource resource) { AjBool tested = ajFalse; AjIList iterfield = NULL; AjPQueryField field = NULL; AjBool ok = ajFalse; ajDebug("resourceinQueryMatch '%S' fields: %Lu Case %B Done %B\n", resource->Id, ajListGetLength(thys->QueryFields), thys->CaseId, thys->QryDone); if(!thys) /* no query to test, that's fine */ return ajTrue; if(thys->QryDone) /* do we need to test here? */ return ajTrue; /* test the query field(s) */ iterfield = ajListIterNewread(thys->QueryFields); while(!ajListIterDone(iterfield)) { field = ajListIterGet(iterfield); ajDebug(" field: '%S' Query: '%S'\n", field->Field, field->Wildquery); if(ajStrMatchC(field->Field, "id")) { ajDebug(" id test: '%S'\n", resource->Id); if(thys->CaseId) { if(ajStrMatchWildS(resource->Id, field->Wildquery)) { ajListIterDel(&iterfield); return ajTrue; } } else { if(ajStrMatchWildCaseS(resource->Id, field->Wildquery)) { ajListIterDel(&iterfield); return ajTrue; } } ajDebug("id test failed\n"); tested = ajTrue; ok = ajFalse; } if(ajStrMatchC(field->Field, "acc")) /* test id, use trueid */ { if(ajStrMatchWildCaseS(resource->Id, field->Wildquery)) { ajListIterDel(&iterfield); return ajTrue; } } } ajListIterDel(&iterfield); if(!tested) /* nothing to test, so accept it anyway */ { ajDebug(" no tests: assume OK\n"); return ajTrue; } ajDebug("result: %B\n", ok); return ok; } ID resourceDefine TY static MO ajresourceread LB core XX DE Make sure all data data object attributes are defined DE using values from the data input object if needed XX PN [1] PA w thys AjPResource PD Data resource data returned. PX PN [2] PA u resourcein AjPResourcein PD Data resource data input definitions PX RT AjBool RD ajTrue on success. RX // static AjBool resourceDefine(AjPResource thys, AjPResourcein resourcein) { /* if values are missing in the data object, we can use defaults from resourcein or calculate where possible */ /* assign the dbname if defined in the resourcein object */ if(ajStrGetLen(resourcein->Input->Db)) ajStrAssignS(&thys->Db, resourcein->Input->Db); return ajTrue; } ID resourceinReadFmt TY static MO ajresourceread LB core XX DE Tests whether data data can be read using the specified format. DE Then tests whether the data data matches data data query criteria DE and checks any specified type. Applies upper and lower case. XX PN [1] PA u resourcein AjPResourcein PD Data resource data input object PX PN [2] PA w resource AjPResource PD Data resource data object PX PN [3] PA r format ajuint PD input format code PX RT ajuint RD 0 if successful. RD 1 if the query match failed. RD 2 if the data data type failed RD 3 if it failed to read any data data RX // static ajuint resourceinReadFmt(AjPResourcein resourcein, AjPResource resource, ajuint format) { ajDebug("++resourceinReadFmt format %d (%s) '%S'\n", format, resourceinFormatDef[format].Name, resourcein->Input->Qry); resourcein->Input->Records = 0; /* Calling funclist resourceinFormatDef() */ if((*resourceinFormatDef[format].Read)(resourcein, resource)) { ajDebug("resourceinReadFmt success with format %d (%s)\n", format, resourceinFormatDef[format].Name); ajDebug("id: '%S'\n", resource->Id); resourcein->Input->Format = format; ajStrAssignC(&resourcein->Input->Formatstr, resourceinFormatDef[format].Name); ajStrAssignC(&resource->Formatstr, resourceinFormatDef[format].Name); ajStrAssignEmptyS(&resource->Db, resourcein->Input->Db); ajStrAssignS(&resource->Filename, resourcein->Input->Filename); if(resourceinQueryMatch(resourcein->Input->Query, resource)) { /* ajResourceinTrace(resourcein); */ return FMT_OK; } ajDebug("query match failed, continuing ...\n"); ajResourceClear(resource); return FMT_NOMATCH; } else { ajDebug("Testing input buffer: IsBuff: %B Eof: %B\n", ajFilebuffIsBuffered(resourcein->Input->Filebuff), ajFilebuffIsEof(resourcein->Input->Filebuff)); if (!ajFilebuffIsBuffered(resourcein->Input->Filebuff) && ajFilebuffIsEof(resourcein->Input->Filebuff)) return FMT_EOF; ajFilebuffReset(resourcein->Input->Filebuff); ajDebug("Format %d (%s) failed, " "file buffer reset by resourceinReadFmt\n", format, resourceinFormatDef[format].Name); /* ajFilebuffTraceFull(resourcein->Filebuff, 10, 10);*/ } ajDebug("++resourceinReadFmt failed - nothing read\n"); return FMT_FAIL; } ID resourceinRead TY static MO ajresourceread LB core XX DE Given data in a resourcein structure, tries to read everything needed DE using the specified format or by trial and error. XX PN [1] PA u resourcein AjPResourcein PD Data resource data input object PX PN [2] PA w resource AjPResource PD Data resource data object PX RT AjBool RD ajTrue on success RX // static AjBool resourceinRead(AjPResourcein resourcein, AjPResource resource) { ajuint i; ajuint istat = 0; ajuint jstat = 0; AjPFilebuff buff = resourcein->Input->Filebuff; AjBool ok; AjPTextAccess textaccess = resourcein->Input->Query->TextAccess; AjPResourceAccess resourceaccess = resourcein->Input->Query->Access; ajResourceClear(resource); ajDebug("resourceinRead: cleared\n"); if(resourcein->Input->Single && resourcein->Input->Count) { /* ** One data data item at a time is read. ** The first data data item was read by ACD ** for the following ones we need to reset the AjPResourcein ** ** Single is set by the access method */ ajDebug("resourceinRead: single access - count %d - call access" " routine again\n", resourcein->Input->Count); /* Calling funclist resourceinAccess() */ if(textaccess) { if(!(*textaccess->Access)(resourcein->Input)) { ajDebug("resourceinRead: (*textaccess->Access)" "(resourcein->Input) *failed*\n"); return ajFalse; } } if(resourceaccess) { if(!(*resourceaccess->Access)(resourcein)) { ajDebug("resourceinRead: (*resourceaccess->Access)(resourcein) " "*failed*\n"); return ajFalse; } } buff = resourcein->Input->Filebuff; } ajDebug("resourceinRead: resourcein format %d '%S'\n", resourcein->Input->Format, resourcein->Input->Formatstr); resourcein->Input->Count++; if(!resourcein->Input->Filebuff) return ajFalse; ok = ajFilebuffIsBuffered(resourcein->Input->Filebuff); while(ok) { /* skip blank lines */ ok = ajBuffreadLine(resourcein->Input->Filebuff, &resourceinReadLine); if(!ajStrIsWhite(resourceinReadLine)) { ajFilebuffClear(resourcein->Input->Filebuff,1); break; } } if(!resourcein->Input->Format) { /* no format specified, try all defaults */ for(i = 1; resourceinFormatDef[i].Name; i++) { if(!resourceinFormatDef[i].Try) /* skip if Try is ajFalse */ continue; ajDebug("resourceinRead:try format %d (%s)\n", i, resourceinFormatDef[i].Name); istat = resourceinReadFmt(resourcein, resource, i); switch(istat) { case FMT_OK: ajDebug("++resourceinRead OK, set format %d\n", resourcein->Input->Format); resourceDefine(resource, resourcein); return ajTrue; case FMT_BADTYPE: ajDebug("resourceinRead: (a1) " "resourceinReadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("resourceinRead: (b1) " "resourceinReadFmt stat == FAIL *failed*\n"); break; /* we can try next format */ case FMT_NOMATCH: ajDebug("resourceinRead: (c1) " "resourceinReadFmt stat==NOMATCH try again\n"); break; case FMT_EOF: ajDebug("resourceinRead: (d1) " "resourceinReadFmt stat == EOF *failed*\n"); return ajFalse; /* EOF and unbuffered */ case FMT_EMPTY: ajWarn("data data '%S' has zero length, ignored", ajResourceGetQryS(resource)); ajDebug("resourceinRead: (e1) " "resourceinReadFmt stat==EMPTY try again\n"); break; default: ajDebug("unknown code %d from resourceinReadFmt\n", stat); } ajResourceClear(resource); if(resourcein->Input->Format) break; /* we read something */ ajFilebuffTrace(resourcein->Input->Filebuff); } if(!resourcein->Input->Format) { /* all default formats failed, give up */ ajDebug("resourceinRead:all default formats failed, give up\n"); return ajFalse; } ajDebug("++resourceinRead set format %d\n", resourcein->Input->Format); } else { /* one format specified */ ajDebug("resourceinRead: one format specified\n"); ajFilebuffSetUnbuffered(resourcein->Input->Filebuff); ajDebug("++resourceinRead known format %d\n", resourcein->Input->Format); istat = resourceinReadFmt(resourcein, resource, resourcein->Input->Format); switch(istat) { case FMT_OK: resourceDefine(resource, resourcein); return ajTrue; case FMT_BADTYPE: ajDebug("resourceinRead: (a2) " "resourceinReadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("resourceinRead: (b2) " "resourceinReadFmt stat == FAIL *failed*\n"); return ajFalse; case FMT_NOMATCH: ajDebug("resourceinRead: (c2) " "resourceinReadFmt stat == NOMATCH *try again*\n"); break; case FMT_EOF: ajDebug("resourceinRead: (d2) " "resourceinReadFmt stat == EOF *try again*\n"); if(resourcein->Input->Records) ajErr("Error reading file '%F' with format '%s': " "end-of-file before end of data " "(read %u records)", ajFilebuffGetFile(resourcein->Input->Filebuff), resourceinFormatDef[resourcein->Input->Format].Name, resourcein->Input->Records); break; /* simply end-of-file */ case FMT_EMPTY: ajWarn("resource data '%S' has zero length, ignored", ajResourceGetQryS(resource)); ajDebug("resourceinRead: (e2) " "resourceinReadFmt stat == EMPTY *try again*\n"); break; default: ajDebug("unknown code %d from resourceinReadFmt\n", stat); } ajResourceClear(resource); /* 1 : read, failed to match id/acc/query */ } /* failed - probably entry/accession query failed. Can we try again? */ ajDebug("resourceinRead failed - try again with format %d '%s' code %d\n", resourcein->Input->Format, resourceinFormatDef[resourcein->Input->Format].Name, istat); ajDebug("Search:%B Chunk:%B Data:%x ajFileBuffEmpty:%B\n", resourcein->Input->Search, resourcein->Input->ChunkEntries, resourcein->Input->TextData, ajFilebuffIsEmpty(buff)); if(ajFilebuffIsEmpty(buff) && resourcein->Input->ChunkEntries) { if(textaccess && !(*textaccess->Access)(resourcein->Input)) return ajFalse; else if(resourceaccess && !(*resourceaccess->Access)(resourcein)) return ajFalse; buff = resourcein->Input->Filebuff; } /* need to check end-of-file to avoid repeats */ while(resourcein->Input->Search && (resourcein->Input->TextData || !ajFilebuffIsEmpty(buff))) { jstat = resourceinReadFmt(resourcein, resource, resourcein->Input->Format); switch(jstat) { case FMT_OK: resourceDefine(resource, resourcein); return ajTrue; case FMT_BADTYPE: ajDebug("resourceinRead: (a3) " "resourceinReadFmt stat == BADTYPE *failed*\n"); return ajFalse; case FMT_FAIL: ajDebug("resourceinRead: (b3) " "resourceinReadFmt stat == FAIL *failed*\n"); return ajFalse; case FMT_NOMATCH: ajDebug("resourceinRead: (c3) " "resourceinReadFmt stat == NOMATCH *try again*\n"); break; case FMT_EOF: ajDebug("resourceinRead: (d3) " "resourceinReadFmt stat == EOF *failed*\n"); return ajFalse; /* we already tried again */ case FMT_EMPTY: if(istat != FMT_EMPTY) ajWarn("assmebly data '%S' has zero length, ignored", ajResourceGetQryS(resource)); ajDebug("resourceinRead: (e3) " "resourceinReadFmt stat == EMPTY *try again*\n"); break; default: ajDebug("unknown code %d from resourceinReadFmt\n", stat); } ajResourceClear(resource); /* 1 : read, failed to match id/acc/query */ } if(resourcein->Input->Format) ajDebug("resourceinRead: *failed* to read data data %S " "using format %s\n", resourcein->Input->Qry, resourceinFormatDef[resourcein->Input->Format].Name); else ajDebug("resourceinRead: *failed* to read data data %S " "using any format\n", resourcein->Input->Qry); return ajFalse; } ID resourceinReadDrcat TY static MO ajresourceread LB core XX DE Given data in a data structure, tries to read everything needed DE using drcat format. XX PN [1] PA u resourcein AjPResourcein PD Resource input object PX PN [2] PA w resource AjPResource PD resource object PX RT AjBool RD ajTrue on success RX // static AjBool resourceinReadDrcat(AjPResourcein resourcein, AjPResource resource) { AjPFilebuff buff; AjPStr name = NULL; AjPStr rest = NULL; ajlong fpos = 0; ajuint linecnt = 0; AjBool ok = ajTrue; AjPStrTok handle = NULL; AjPResquery resqry = NULL; AjPReslink reslink = NULL; AjPResterm resterm = NULL; AjPStr token = NULL; AjIList iter = NULL; ajDebug("resourceinReadDrcat\n"); ajResourceClear(resource); buff = resourcein->Input->Filebuff; /* ajFilebuffTrace(buff); */ ok = ajBuffreadLine(buff, &resourceinReadLine); while (ok && !ajStrPrefixC(resourceinReadLine, "ID ")) { ok = ajBuffreadLine(buff, &resourceinReadLine); } if(!ok) return ajFalse; ajFilebuffClear(buff, 1); ok = ajBuffreadLinePosStore(buff, &resourceinReadLine, &fpos, resourcein->Input->Text, &resource->TextPtr); ajStrExtractFirst(resourceinReadLine, &rest, &name); ajStrTrimWhite(&rest); ajStrAssignS(&resource->Id, rest); if(ok) ok = ajBuffreadLinePosStore(buff, &resourceinReadLine, &fpos, resourcein->Input->Text, &resource->TextPtr); while(ok && ajStrGetLen(resourceinReadLine) > 1) { linecnt++; ajStrExtractFirst(resourceinReadLine, &rest, &name); ajStrTrimWhite(&rest); if(linecnt > 1 && ajStrMatchC(name, "ID")) { break; } else if(ajStrMatchC(name, "IDalt")) { if(!ajStrMatchC(rest, "None")) ajListstrPushAppend(resource->Idalt, ajStrNewS(rest)); } else if(ajStrMatchC(name, "Acc")) { if(!ajStrMatchC(rest, "None")) ajStrAssignS(&resource->Acc, rest); } else if(ajStrMatchC(name, "Name")) { if(!ajStrMatchC(rest, "None")) ajStrAssignS(&resource->Name, rest); } else if(ajStrMatchC(name, "Desc")) { if(!ajStrMatchC(rest, "None")) ajStrAssignS(&resource->Desc, rest); } else if(ajStrMatchC(name, "URL")) { if(!ajStrMatchC(rest, "None")) ajStrAssignS(&resource->Url, rest); } else if(ajStrMatchC(name, "URLlink")) { if(!ajStrMatchC(rest, "None")) ajStrAssignS(&resource->Urllink, rest); } else if(ajStrMatchC(name, "URLrest")) { if(!ajStrMatchC(rest, "None")) ajStrAssignS(&resource->Urlrest, rest); } else if(ajStrMatchC(name, "URLsoap")) { if(!ajStrMatchC(rest, "None")) ajStrAssignS(&resource->Urlsoap, rest); } else if(ajStrMatchC(name, "Cat")) { if(!ajStrMatchC(rest, "None")) { ajStrTokenAssignC(&handle, rest, ";"); while(ajStrTokenNextParse(&handle, &token)) { ajStrRemoveWhiteExcess(&token); ajListstrPushAppend(resource->Cat, ajStrNewS(token)); } } } else if(ajStrMatchC(name, "EDAMtpc")) { if(!ajStrMatchC(rest, "None")) { resterm = ajRestermNew(); ajStrTokenAssignC(&handle, rest, "|"); ajStrTokenNextParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resterm->Id, token); ajStrTokenRestParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resterm->Name, token); ajListPushAppend(resource->Edamtpc, resterm); resterm = NULL; } } else if(ajStrMatchC(name, "EDAMdat")) { if(!ajStrMatchC(rest, "None")) { resterm = ajRestermNew(); ajStrTokenAssignC(&handle, rest, "|"); ajStrTokenNextParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resterm->Id, token); ajStrTokenRestParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resterm->Name, token); ajListPushAppend(resource->Edamdat, resterm); resterm = NULL; } } else if(ajStrMatchC(name, "EDAMid")) { if(!ajStrMatchC(rest, "None")) { resterm = ajRestermNew(); ajStrTokenAssignC(&handle, rest, "|"); ajStrTokenNextParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resterm->Id, token); ajStrTokenRestParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resterm->Name, token); ajListPushAppend(resource->Edamid, resterm); resterm = NULL; } } else if(ajStrMatchC(name, "EDAMfmt")) { if(!ajStrMatchC(rest, "None")) { resterm = ajRestermNew(); ajStrTokenAssignC(&handle, rest, "|"); ajStrTokenNextParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resterm->Id, token); ajStrTokenRestParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resterm->Name, token); ajListPushAppend(resource->Edamfmt, resterm); resterm = NULL; } } else if(ajStrMatchC(name, "Xref")) { if(!ajStrMatchC(rest, "None")) { reslink = ajReslinkNew(); ajStrTokenAssignC(&handle, rest, "|"); ajStrTokenNextParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&reslink->Source, token); ajStrTokenRestParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&reslink->Term, token); reslink->Nterms = (ajint) ajStrCalcCountK(token, ';'); ajListPushAppend(resource->Xref, reslink); reslink = NULL; } } else if(ajStrMatchC(name, "Query")) { if(!ajStrMatchC(rest, "None")) { resqry = ajResqueryNew(); ajStrTokenAssignC(&handle, rest, "|"); ajStrTokenNextParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resqry->Datatype, token); ajStrTokenNextParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resqry->Format, token); iter = ajListIterNew(resource->Edamfmt); while(!ajListIterDone(iter)) { resterm = ajListIterGet(iter); if(ajStrMatchS(resqry->Format, resterm->Name)) ajStrAssignS(&resqry->FormatTerm, resterm->Id); } ajListIterDel(&iter); ajStrTokenNextParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resqry->Term, token); resqry->Nterms = (ajint) ajStrCalcCountK(token, ';'); ajStrTokenRestParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resqry->Url, token); ajListPushAppend(resource->Query, resqry); resqry = NULL; } } else if(ajStrMatchC(name, "Example")) { if(!ajStrMatchC(rest, "None")) ajListstrPushAppend(resource->Example, ajStrNewS(rest)); } else if(ajStrMatchC(name, "Taxon")) { if(!ajStrMatchC(rest, "None")) { resterm = ajRestermNew(); ajStrTokenAssignC(&handle, rest, "|"); ajStrTokenNextParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resterm->Id, token); ajStrTokenRestParse(&handle, &token); ajStrRemoveWhiteExcess(&token); ajStrAssignS(&resterm->Name, token); ajListPushAppend(resource->Taxon, resterm); resterm = NULL; } } ok = ajBuffreadLinePosStore(buff, &resourceinReadLine, &fpos, resourcein->Input->Text, &resource->TextPtr); } if(ok) ajFilebuffClearStore(buff, 1, resourceinReadLine, resourcein->Input->Text, &resource->TextPtr); else ajFilebuffClear(buff, 0); ajStrDel(&name); ajStrDel(&rest); ajStrDel(&token); ajStrTokenDel(&handle); return ajTrue; } ID ajResourceinprintBook TY public MO ajresourceread LB core XX DE Reports the internal data structures as a Docbook table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajResourceinprintBook(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; AjPList fmtlist; AjPStr* names; fmtlist = ajListstrNew(); ajFmtPrintF(outf, "The supported data resource formats" "are summarised in the table below. " "The columns are as follows: " "Input format (format name), " "Try (indicates whether the " "format can be detected automatically on input), and " "Description (short description of " "the format).\n\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=1; resourceinFormatDef[i].Name; i++) { if(!resourceinFormatDef[i].Alias) { namestr = ajStrNewC(resourceinFormatDef[i].Name); ajListPushAppend(fmtlist, namestr); namestr = NULL; } } ajListSort(fmtlist, &ajStrVcmp); ajListstrToarray(fmtlist, &names); for(i=0; names[i]; i++) { for(j=0; resourceinFormatDef[j].Name; j++) { if(ajStrMatchC(names[i],resourceinFormatDef[j].Name)) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", resourceinFormatDef[j].Name); ajFmtPrintF(outf, " \n", resourceinFormatDef[j].Try); ajFmtPrintF(outf, " \n", resourceinFormatDef[j].Desc); ajFmtPrintF(outf, " \n"); } } } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Input data resource formats
Input FormatTryDescription
%s%B%s
\n"); ajStrDel(&namestr); names = NULL; ajListstrFreeData(&fmtlist); return; } ID ajResourceinprintHtml TY public MO ajresourceread LB core XX DE Reports the internal data structures as an HTML table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajResourceinprintHtml(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, ""); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); for(i=1; resourceinFormatDef[i].Name; i++) { ajStrAssignC(&namestr, resourceinFormatDef[i].Name); if(!resourceinFormatDef[i].Alias) { for(j=i+1; resourceinFormatDef[j].Name; j++) { if(resourceinFormatDef[j].Read == resourceinFormatDef[i].Read) { ajFmtPrintAppS(&namestr, " %s", resourceinFormatDef[j].Name); if(!resourceinFormatDef[j].Alias) { ajWarn("Input format '%s' same as '%s' but not alias", resourceinFormatDef[j].Name, resourceinFormatDef[i].Name); } } } ajFmtPrintF(outf, "\n", namestr, resourceinFormatDef[i].Try); ajFmtPrintF(outf, "\n", resourceinFormatDef[i].Desc); } } ajFmtPrintF(outf, "
Input FormatAutoMultiDescription
\n%S\n%B\n%s\n
\n"); ajStrDel(&namestr); return; } ID ajResourceinprintText TY public MO ajresourceread LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajResourceinprintText(AjPFile outf, AjBool full) { ajuint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Data resource input formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Alias Alias name\n"); ajFmtPrintF(outf, "# Try Test for unknown input files\n"); ajFmtPrintF(outf, "# Name Alias Try " "Description"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "InFormat {\n"); for(i=0; resourceinFormatDef[i].Name; i++) if(full || !resourceinFormatDef[i].Alias) ajFmtPrintF(outf, " %-12s %5B %3B \"%s\"\n", resourceinFormatDef[i].Name, resourceinFormatDef[i].Alias, resourceinFormatDef[i].Try, resourceinFormatDef[i].Desc); ajFmtPrintF(outf, "}\n\n"); return; } ID ajResourceinprintWiki TY public MO ajresourceread LB core XX DE Reports the internal data structures as a wiki table XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajResourceinprintWiki(AjPFile outf) { ajuint i = 0; ajuint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!Try!!" "class=\"unsortable\"|Description\n"); for(i=1; resourceinFormatDef[i].Name; i++) { ajStrAssignC(&namestr, resourceinFormatDef[i].Name); if(!resourceinFormatDef[i].Alias) { for(j=i+1; resourceinFormatDef[j].Name; j++) { if(resourceinFormatDef[j].Read == resourceinFormatDef[i].Read) { ajFmtPrintAppS(&namestr, "
%s", resourceinFormatDef[j].Name); if(!resourceinFormatDef[j].Alias) { ajWarn("Input format '%s' same as '%s' but not alias", resourceinFormatDef[j].Name, resourceinFormatDef[i].Name); } } } ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "|%S||%B||%s\n", namestr, resourceinFormatDef[i].Try, resourceinFormatDef[i].Desc); } } ajFmtPrintF(outf, "|}\n\n"); ajStrDel(&namestr); return; } ID ajResourceinExit TY public MO ajresourceread LB core XX DE Cleans up data input internal memory XX RT void RD RX // void ajResourceinExit(void) { ajStrDel(&resourceinReadLine); ajTableDel(&resourceDbMethods); return; } ID ajResourceinTypeGetFields TY public MO ajresourceread LB core XX DE Returns the listof known field names for ajResourceinRead XX RT const char* RD List of field names RX // const char* ajResourceinTypeGetFields(void) { return "id acc"; } ID ajResourceinTypeGetQlinks TY public MO ajresourceread LB core XX DE Returns the listof known query link operators for ajResourceinRead XX RT const char* RD List of field names RX // const char* ajResourceinTypeGetQlinks(void) { return "|"; } ID ajResourceaccessGetDb TY public MO ajresourceread LB core XX DE Returns the table in which data database access details are registered XX RT AjPTable RD Access functions hash table RX // AjPTable ajResourceaccessGetDb(void) { if(!resourceDbMethods) resourceDbMethods = ajCallTableNew(); return resourceDbMethods; } ID ajResourceaccessMethodGetQlinks TY public MO ajresourceread LB core XX DE Tests for a named method for data data reading and returns the DE known query link operators XX PN [1] PA r method const AjPStr PD Method required. PX RT const char* RD Known link operators RX // const char* ajResourceaccessMethodGetQlinks(const AjPStr method) { AjPResourceAccess methoddata; methoddata = ajCallTableGetS(resourceDbMethods, method); if(!methoddata) return NULL; return methoddata->Qlink; } ID ajResourceaccessMethodGetScope TY public MO ajresourceread LB core XX DE Tests for a named method for data data reading and returns the scope DE (entry, query or all). XX PN [1] PA r method const AjPStr PD Method required. PX RT ajuint RD Scope flags RX // ajuint ajResourceaccessMethodGetScope(const AjPStr method) { AjPResourceAccess methoddata; ajuint ret = 0; methoddata = ajCallTableGetS(resourceDbMethods, method); if(!methoddata) return 0; if(methoddata->Entry) ret |= AJMETHOD_ENTRY; if(methoddata->Query) ret |= AJMETHOD_QUERY; if(methoddata->All) ret |= AJMETHOD_ALL; return ret; } ID ajResourceaccessMethodTest TY public MO ajresourceread LB core XX DE Tests for a named method for data data reading. XX PN [1] PA r method const AjPStr PD Method required. PX RT AjBool RD ajTrue on success. RX // AjBool ajResourceaccessMethodTest(const AjPStr method) { if(ajCallTableGetS(resourceDbMethods, method)) return ajTrue; return ajFalse; } ID resourceinQryRestore TY static MO ajresourceread LB core XX DE Restores a data input specification from an AjPQueryList node XX PN [1] PA w resourcein AjPResourcein PD Data resource input object PX PN [2] PA r node const AjPQueryList PD Query list node PX RT void RD RX // static void resourceinQryRestore(AjPResourcein resourcein, const AjPQueryList node) { resourcein->Input->Format = node->Format; ajStrAssignS(&resourcein->Input->Formatstr, node->Formatstr); return; } ID resourceinQrySave TY static MO ajresourceread LB core XX DE Saves a data input specification in an AjPQueryList node XX PN [1] PA w node AjPQueryList PD Query list node PX PN [2] PA r resourcein const AjPResourcein PD Data resource input object PX RT void RD RX // static void resourceinQrySave(AjPQueryList node, const AjPResourcein resourcein) { node->Format = resourcein->Input->Format; ajStrAssignS(&node->Formatstr, resourcein->Input->Formatstr); return; } ID resourceinQryProcess TY static MO ajresourceread LB core XX DE Converts a data data query into an open file. DE DE Tests for "format::" and sets this if it is found DE DE Then tests for "list:" or " XX PN [1] PA u resourcein AjPResourcein PD Data resource data input structure. PX PN [2] PA u resource AjPResource PD Data resource data to be read. PD The format will be replaced PD if defined in the query string. PX RT AjBool RD ajTrue on success. RX // static AjBool resourceinQryProcess(AjPResourcein resourcein, AjPResource resource) { AjBool ret = ajTrue; AjPStr qrystr = NULL; AjBool resourcemethod = ajFalse; const AjPStr fmtstr = NULL; AjPTextin textin; AjPQuery qry; AjPResourceAccess resourceaccess = NULL; textin = resourcein->Input; qry = textin->Query; /* pick up the original query string */ qrystr = ajStrNewS(textin->Qry); ajDebug("resourceinQryProcess '%S'\n", qrystr); /* look for a format:: prefix */ fmtstr = ajQuerystrParseFormat(&qrystr, textin, resourceinformatFind); ajDebug("resourceinQryProcess ... fmtstr '%S' '%S'\n", fmtstr, qrystr); /* (seq/feat) DO NOT look for a [range] suffix */ /* look for a list:: or @:: listfile of queries - process and return */ if(ajQuerystrParseListfile(&qrystr)) { ajDebug("resourceinQryProcess ... listfile '%S'\n", qrystr); ret = resourceinListProcess(resourcein, resource, qrystr); ajStrDel(&qrystr); return ret; } /* try general text access methods (file, asis, text database access */ ajDebug("resourceinQryProcess ... no listfile '%S'\n", qrystr); if(!ajQuerystrParseRead(&qrystr, textin, resourceinformatFind, &resourcemethod)) { ajStrDel(&qrystr); return ajFalse; } resourceinFormatSet(resourcein, resource); ajDebug("resourceinQryProcess ... read nontext: %B '%S'\n", resourcemethod, qrystr); ajStrDel(&qrystr); /* we found a non-text method */ if(resourcemethod) { ajDebug("resourceinQryProcess ... call method '%S'\n", qry->Method); ajDebug("resourceinQryProcess ... textin format %d '%S'\n", textin->Format, textin->Formatstr); ajDebug("resourceinQryProcess ... query format '%S'\n", qry->Formatstr); qry->Access = ajCallTableGetS(resourceDbMethods,qry->Method); resourceaccess = qry->Access; return (*resourceaccess->Access)(resourcein); } ajDebug("resourceinQryProcess text method '%S' success\n", qry->Method); return ajTrue; } ID resourceinListProcess TY static MO ajresourceread LB core XX DE Processes a file of queries. DE This function is called by, and calls, resourceinQryProcess. There is DE a depth check to avoid infinite loops, for example where a list file DE refers to itself. DE DE This function produces a list (AjPList) of queries with all list references DE expanded into lists of queries. DE DE Because queries in a list can have their own format DE the prior settings are stored with each query in the list node so that they DE can be restored after. XX PN [1] PA u resourcein AjPResourcein PD Data resource data input PX PN [2] PA u resource AjPResource PD Data resource data PX PN [3] PA r listfile const AjPStr PD Name of list file., PX RT AjBool RD ajTrue on success. RX // static AjBool resourceinListProcess(AjPResourcein resourcein, AjPResource resource, const AjPStr listfile) { AjPList list = NULL; AjPFile file = NULL; AjPStr token = NULL; AjPStrTok handle = NULL; AjBool ret = ajFalse; AjPQueryList node = NULL; static ajint depth = 0; static ajint MAXDEPTH = 16; depth++; ajDebug("++resourceinListProcess %S depth %d\n", listfile, depth); if(depth > MAXDEPTH) ajFatal("Query list too deep"); if(!resourcein->Input->List) resourcein->Input->List = ajListNew(); list = ajListNew(); file = ajFileNewInNameS(listfile); if(!file) { ajErr("Failed to open list file '%S'", listfile); depth--; return ret; } while(ajReadlineTrim(file, &resourceinReadLine)) { resourceinListNoComment(&resourceinReadLine); if(ajStrGetLen(resourceinReadLine)) { ajStrTokenAssignC(&handle, resourceinReadLine, " \t\n\r"); ajStrTokenNextParse(&handle, &token); /* ajDebug("Line '%S'\n");*/ /* ajDebug("token '%S'\n", resourceinReadLine, token); */ if(ajStrGetLen(token)) { ajDebug("++Add to list: '%S'\n", token); AJNEW0(node); ajStrAssignS(&node->Qry, token); resourceinQrySave(node, resourcein); ajListPushAppend(list, node); } ajStrDel(&token); token = NULL; } } ajFileClose(&file); ajStrDel(&token); ajDebug("Trace resourcein->Input->List\n"); ajQuerylistTrace(resourcein->Input->List); ajDebug("Trace new list\n"); ajQuerylistTrace(list); ajListPushlist(resourcein->Input->List, &list); ajDebug("Trace combined resourcein->Input->List\n"); ajQuerylistTrace(resourcein->Input->List); /* ** now try the first item on the list ** this can descend recursively if it is also a list ** which is why we check the depth above */ if(ajListPop(resourcein->Input->List, (void**) &node)) { ajDebug("++pop first item '%S'\n", node->Qry); ajResourceinQryS(resourcein, node->Qry); resourceinQryRestore(resourcein, node); ajStrDel(&node->Qry); ajStrDel(&node->Formatstr); AJFREE(node); ajDebug("descending with query '%S'\n", resourcein->Input->Qry); ret = resourceinQryProcess(resourcein, resource); } ajStrTokenDel(&handle); depth--; ajDebug("++resourceinListProcess depth: %d returns: %B\n", depth, ret); return ret; } ID resourceinListNoComment TY static MO ajresourceread LB core XX DE Strips comments from a character string (a line from an ACD file). DE Comments are blank lines or any text following a "#" character. XX PN [1] PA u text AjPStr* PD Line of text from input file. PX RT void RD RX // static void resourceinListNoComment(AjPStr* text) { ajuint i; char *cp; i = ajStrGetLen(*text); if(!i) /* empty string */ return; MAJSTRGETUNIQUESTR(text); cp = strchr(ajStrGetPtr(*text), '#'); if(cp) { /* comment found */ *cp = '\0'; ajStrSetValid(text); } return; } ID resourceinFormatSet TY static MO ajresourceread LB core XX DE Sets the input format for data data using the data data DE input object's defined format XX PN [1] PA u resourcein AjPResourcein PD Data resource data input. PX PN [2] PA u resource AjPResource PD Data resource data PX RT AjBool RD ajTrue on success. RX // static AjBool resourceinFormatSet(AjPResourcein resourcein, AjPResource resource) { if(ajStrGetLen(resourcein->Input->Formatstr)) { ajDebug("... input format value '%S'\n", resourcein->Input->Formatstr); if(resourceinformatFind(resourcein->Input->Formatstr, &resourcein->Input->Format)) { ajStrAssignS(&resource->Formatstr, resourcein->Input->Formatstr); resource->Format = resourcein->Input->Format; ajDebug("...format OK '%S' = %d\n", resourcein->Input->Formatstr, resourcein->Input->Format); } else ajDebug("...format unknown '%S'\n", resourcein->Input->Formatstr); return ajTrue; } else ajDebug("...input format not set\n"); return ajFalse; } ID ajResourceallNew TY public MO ajresourceread LB core XX DE Creates a new data resource input stream object. XX RT AjPResourceall RD New data resource input stream object. RX // AjPResourceall ajResourceallNew(void) { AjPResourceall pthis; AJNEW0(pthis); pthis->Resourcein = ajResourceinNew(); pthis->Resource = ajResourceNew(); return pthis; } ID ajResourceallDel TY public MO ajresourceread LB core XX DE Deletes a data resource input stream object. XX PN [1] PA d pthis AjPResourceall* PD Data resource input stream PX RT void RD RX // void ajResourceallDel(AjPResourceall* pthis) { AjPResourceall thys; if(!pthis) return; thys = *pthis; if(!thys) return; ajResourceinDel(&thys->Resourcein); if(!thys->Returned) ajResourceDel(&thys->Resource); AJFREE(*pthis); return; } ID ajResourceallClear TY public MO ajresourceread LB core XX DE Clears a data resource term input stream object back to "as new" condition, DE except for the query list which must be preserved. XX PN [1] PA w thys AjPResourceall PD Data resource input stream PX RT void RD RX // void ajResourceallClear(AjPResourceall thys) { ajDebug("ajResourceallClear called\n"); if(!thys) return; ajResourceinClear(thys->Resourcein); ajResourceClear(thys->Resource); thys->Returned = ajFalse; return; } ID ajResourceallGetresourceId TY public MO ajresourceread LB core XX DE Returns the identifier of the current data resource in an input stream XX PN [1] PA r thys const AjPResourceall PD Data resource term input stream PX RT const AjPStr RD Identifier RX // const AjPStr ajResourceallGetresourceId(const AjPResourceall thys) { if(!thys) return NULL; ajDebug("ajResourceallGetresourceId called\n"); return ajResourceGetId(thys->Resource); } ID ajResourceallNext TY public MO ajresourceread LB core XX DE Parse a data resource query into format, access, file and entry DE DE Split at delimiters. Check for the first part as a valid format DE Check for the remaining first part as a database name or as a file DE that can be opened. DE Anything left is an entryname spec. DE DE Return the results in the AjPResource object but leave the file open for DE future calls. XX PN [1] PA w thys AjPResourceall PD Data resource input stream PX PN [2] PA u Presource AjPResource* PD Data resource returned PX RT AjBool RD ajTrue on success. RX // AjBool ajResourceallNext(AjPResourceall thys, AjPResource *Presource) { if(!thys->Count) { thys->Count = 1; thys->Totresources++; *Presource = thys->Resource; thys->Returned = ajTrue; return ajTrue; } if(ajResourceinRead(thys->Resourcein, thys->Resource)) { thys->Count++; thys->Totresources++; *Presource = thys->Resource; thys->Returned = ajTrue; ajDebug("ajResourceallNext success\n"); return ajTrue; } *Presource = NULL; ajDebug("ajResourceallNext failed\n"); ajResourceallClear(thys); return ajFalse; } ID resourceinformatFind TY static MO ajresourceread LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Sets iformat as the recognised format, and returns ajTrue. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // static AjBool resourceinformatFind(const AjPStr format, ajint* iformat) { AjPStr tmpformat = NULL; ajuint i = 0; /* ajDebug("resourceinformatFind '%S'\n", format); */ if(!ajStrGetLen(format)) return ajFalse; ajStrAssignS(&tmpformat, format); ajStrFmtLower(&tmpformat); for(i=0; resourceinFormatDef[i].Name; i++) { /* ajDebug("test %d '%s' \n", i, resourceinFormatDef[i].Name); */ if(ajStrMatchCaseC(tmpformat, resourceinFormatDef[i].Name)) { *iformat = i; ajStrDel(&tmpformat); /* ajDebug("found '%s' at %d\n", resourceinFormatDef[i].Name, i); */ return ajTrue; } } ajErr("Unknown input format '%S'", format); ajStrDel(&tmpformat); return ajFalse; } ID ajResourceinformatTerm TY public MO ajresourceread LB core XX DE Tests whether a data resource data input format term is known XX PN [1] PA r term const AjPStr PD Format term EDAM ID PX RT AjBool RD ajTrue if term was accepted RX // AjBool ajResourceinformatTerm(const AjPStr term) { ajuint i; for(i=0; resourceinFormatDef[i].Name; i++) if(ajStrMatchCaseC(term, resourceinFormatDef[i].Name)) return ajTrue; return ajFalse; } ID ajResourceinformatTest TY public MO ajresourceread LB core XX DE Tests whether a named data data input format is known XX PN [1] PA r format const AjPStr PD Format term EDAM ID PX RT AjBool RD ajTrue if format was accepted RX // AjBool ajResourceinformatTest(const AjPStr format) { ajuint i; for(i=0; resourceinFormatDef[i].Name; i++) if(ajStrMatchCaseC(format, resourceinFormatDef[i].Name)) return ajTrue; return ajFalse; } ID ajResourceoutWrite TY public MO ajresourcewrite LB core XX DE Write a data resource in a named format XX PN [1] PA u outf AjPOutfile PD Output file PX PN [2] PA r resource const AjPResource PD Data resource object PX RT AjBool RD True on success RX // AjBool ajResourceoutWrite(AjPOutfile outf, const AjPResource resource) { ajuint i = ajOutfileGetFormatindex(outf); AjPFile outfile = ajOutfileGetFile(outf); return (*resourceoutFormatDef[i].Write)(outfile, resource); } ID resourceoutWriteDrcat TY static MO ajresourcewrite LB core XX DE Write resource object to file in EMBOSS drcat.dat format. DE DE All elements / fields are written. XX PN [1] PA u outf AjPFile PD Output resource file PX PN [2] PA r resource const AjPResource PD Resource object PX RT AjBool RD True on success RX // static AjBool resourceoutWriteDrcat(AjPFile outf, const AjPResource resource) { AjPReslink tmplink = NULL; AjPResquery tmpqry = NULL; AjPResterm resterm = NULL; AjPStr tmpstr = NULL; AjIList iter = NULL; if(!outf) return ajFalse; ajFmtPrintF(outf, "%-8s%S\n", "ID", resource->Id); iter = ajListIterNew(resource->Idalt); while((tmpstr = ajListstrIterGet(iter))) ajFmtPrintF(outf, "%-8s%S\n", "IDalt", tmpstr); ajListIterDel(&iter); if(ajStrGetLen(resource->Acc)) ajFmtPrintF(outf, "%-8s%S\n", "Acc", resource->Acc); ajFmtPrintF(outf, "%-8s%S\n", "Name", resource->Name); ajFmtPrintF(outf, "%-8s%S\n", "Desc", resource->Desc); if(ajStrGetLen(resource->Url)) ajFmtPrintF(outf, "%-8s%S\n", "URL", resource->Url); if(ajStrGetLen(resource->Urllink)) ajFmtPrintF(outf, "%-8s%S\n", "URLlink", resource->Urllink); if(ajStrGetLen(resource->Urlrest)) ajFmtPrintF(outf, "%-8s%S\n", "URLrest", resource->Urlrest); if(ajStrGetLen(resource->Urlsoap)) ajFmtPrintF(outf, "%-8s%S\n", "URLsoap", resource->Urlsoap); iter = ajListIterNew(resource->Cat); while((tmpstr = ajListstrIterGet(iter))) ajFmtPrintF(outf, "%-8s%S\n", "Cat", tmpstr); ajListIterDel(&iter); iter = ajListIterNew(resource->Taxon); while((resterm = ajListIterGet(iter))) ajFmtPrintF(outf, "%-8s%S | %S\n", "Taxon", resterm->Id, resterm->Name); ajListIterDel(&iter); iter = ajListIterNew(resource->Edamtpc); while((resterm = ajListIterGet(iter))) ajFmtPrintF(outf, "%-8s%S | %S\n", "EDAMtpc", resterm->Id, resterm->Name); ajListIterDel(&iter); iter = ajListIterNew(resource->Edamdat); while((resterm = ajListIterGet(iter))) ajFmtPrintF(outf, "%-8s%S | %S\n", "EDAMdat", resterm->Id, resterm->Name); ajListIterDel(&iter); iter = ajListIterNew(resource->Edamid); while((resterm = ajListIterGet(iter))) ajFmtPrintF(outf, "%-8s%S | %S\n", "EDAMid", resterm->Id, resterm->Name); ajListIterDel(&iter); iter = ajListIterNew(resource->Edamfmt); while((resterm = ajListIterGet(iter))) ajFmtPrintF(outf, "%-8s%S | %S\n", "EDAMfmt", resterm->Id, resterm->Name); ajListIterDel(&iter); iter = ajListIterNew(resource->Xref); while((tmplink = ajListIterGet(iter))) ajFmtPrintF(outf, "%-8s%S | %S\n", "Xref", tmplink->Source, tmplink->Term); ajListIterDel(&iter); iter = ajListIterNew(resource->Query); while((tmpqry = ajListIterGet(iter))) ajFmtPrintF(outf, "%-8s %S | %S | %S | %S\n", "Query", tmpqry->Datatype, tmpqry->Format, tmpqry->Term, tmpqry->Url); ajListIterDel(&iter); iter = ajListIterNew(resource->Example); while((tmpstr = ajListstrIterGet(iter))) ajFmtPrintF(outf, "%-8s%S\n", "Example", tmpstr); ajListIterDel(&iter); ajFmtPrintF(outf, "\n"); return ajTrue; } ID resourceoutWriteBasic TY static MO ajresourcewrite LB core XX DE Write resource object to file in EMBOSS drcat.dat format. DE DE Only the most important elements / fields are written: DE Recommended or official unique identifier ('ID') DE The full (verbose english) name ('Name') DE URL of the database server ('URL') XX PN [1] PA u outf AjPFile PD Output resource file PX PN [2] PA r resource const AjPResource PD Resource object PX RT AjBool RD True on success RX // static AjBool resourceoutWriteBasic(AjPFile outf, const AjPResource resource) { if(!outf) return ajFalse; ajFmtPrintF(outf, "%-8s%S\n", "ID", resource->Id); ajFmtPrintF(outf, "%-8s%S\n", "Name", resource->Name); if(ajStrGetLen(resource->Url)) ajFmtPrintF(outf, "%-8s%S\n", "URL", resource->Url); ajFmtPrintF(outf, "\n"); return ajTrue; } ID resourceoutWriteList TY static MO ajresourcewrite LB core XX DE Write resource object id to file XX PN [1] PA u outf AjPFile PD Output resource file PX PN [2] PA r resource const AjPResource PD Resource object PX RT AjBool RD True on success RX // static AjBool resourceoutWriteList(AjPFile outf, const AjPResource resource) { if(!outf) return ajFalse; if(!resource) return ajFalse; if(ajStrGetLen(resource->Db)) ajFmtPrintF(outf, "%S:%S\n", resource->Db, resource->Id); else ajFmtPrintF(outf, "%S\n", resource->Id); return ajTrue; } ID resourceoutWriteWebpage TY static MO ajresourcewrite LB core XX DE Write resource object to file in DRCAT web pageformat. DE DE All elements / fields are written. XX PN [1] PA u outf AjPFile PD Output resource file PX PN [2] PA r resource const AjPResource PD Resource object PX RT AjBool RD True on success RX // static AjBool resourceoutWriteWebpage(AjPFile outf, const AjPResource resource) { AjPResquery tmpqry = NULL; AjPResterm resterm = NULL; AjPStr snstr = NULL; AjPStr tmpstr = NULL; AjPStr tmpurl = NULL; AjPStr tmpid = NULL; AjPStr tmpname = NULL; AjPStr tmpqid = NULL; AjPStr tmpqname = NULL; AjPStr tmpqterm = NULL; AjIList iter = NULL; AjIList iterq = NULL; ajuint i = 0; AjPStrTok handle = NULL; AjPStrTok namehandle = NULL; AjPStrTok termhandle = NULL; const char* biourl = "http://bioportal.bioontology.org/ontologies/45846"; if(!outf) return ajFalse; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "Bioinformatics Data Resource Catalogue\n"); ajFmtPrintF(outf, "\n\n\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); if(ajStrGetLen(resource->Url)) ajFmtPrintF(outf, "

%S

\n", resource->Url, resource->Id); else ajFmtPrintF(outf, "

%S

\n", resource->Id); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "

%S

\n", resource->Name); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "

%S

\n", resource->Desc); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n", resource->Id); ajFmtPrintF(outf, "\n"); if(ajListGetLength(resource->Idalt)) { ajFmtPrintF(outf, " \n"); } if(ajStrGetLen(resource->Acc)) { ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n", resource->Acc); } if(ajStrGetLen(resource->Url)) ajFmtPrintF(outf, " \n", resource->Url, resource->Url); if(ajListGetLength(resource->Edamtpc)) { ajFmtPrintF(outf, "\n"); iter = ajListIterNew(resource->Edamtpc); while((resterm = ajListIterGet(iter))) { ajStrAssignS(&tmpid, resterm->Id); ajStrCutBraces(&tmpid); ajStrFmtPercentEncodeC(&tmpid, " /()"); ajFmtPrintF(outf, " \n", biourl, tmpid, resterm->Name); } ajListIterDel(&iter); } ajFmtPrintF(outf, "\n"); if(ajListGetLength(resource->Cat)) { iter = ajListIterNew(resource->Cat); while((tmpstr = ajListstrIterGet(iter))) ajFmtPrintF(outf, " " "\n", tmpstr); ajListIterDel(&iter); } ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "\n"); if(ajStrGetLen(resource->Urllink)) ajFmtPrintF(outf, "\n", resource->Urllink, resource->Urllink); ajFmtPrintF(outf, "\n"); if(ajStrGetLen(resource->Urlrest)) ajFmtPrintF(outf, "\n", resource->Urlrest, resource->Urlrest); ajFmtPrintF(outf, "\n"); if(ajStrGetLen(resource->Urlsoap)) ajFmtPrintF(outf, "\n", resource->Urlsoap, resource->Urlsoap); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "
ID%S
IDalt"); i = 0; iter = ajListIterNew(resource->Idalt); while((tmpstr = ajListstrIterGet(iter))) { if(i++) ajFmtPrintF(outf, ", "); ajFmtPrintF(outf, "%S", tmpstr); } ajListIterDel(&iter); ajFmtPrintF(outf, "
Acc%S
Home" "%S
EDAM topic" "%S" "
Category%S
Taxon"); i = 0; iter = ajListIterNew(resource->Taxon); while((resterm = ajListIterGet(iter))) { if(i++) ajFmtPrintF(outf, ", "); ajFmtPrintF(outf, "%S", resterm->Id, resterm->Name); } ajListIterDel(&iter); ajFmtPrintF(outf, "
Linking" "%S
REST" "%S
SOAP" "%S
\n"); ajFmtPrintF(outf, " \n"); if(ajListGetLength(resource->Query)) { ajFmtPrintF(outf, "

Available data

\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); iter = ajListIterNew(resource->Query); while((tmpqry = ajListIterGet(iter))) { ajStrAssignS(&tmpname, tmpqry->Datatype); ajStrCutBraces(&tmpname); ajStrFmtPercentEncodeC(&tmpname, " /()"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", biourl, tmpname, tmpqry->Datatype); ajStrAssignS(&tmpname, tmpqry->Format); ajStrCutBraces(&tmpname); ajStrFmtPercentEncodeC(&tmpname, " /()"); ajFmtPrintF(outf, " \n", biourl, tmpname, tmpqry->Format); ajStrAssignS(&tmpname, tmpqry->Term); ajStrCutBraces(&tmpname); ajStrFmtPercentEncodeC(&tmpname, " /()"); ajFmtPrintF(outf, " \n", biourl, tmpname, tmpqry->Term); ajFmtPrintF(outf, " \n", tmpqry->Url); ajFmtPrintF(outf, " \n\n"); } ajListIterDel(&iter); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
DataFormatQueryLink
" "%S" "%S" "%S%S
\n"); } ajFmtPrintF(outf, "\n"); if(ajListGetLength(resource->Example)) { ajFmtPrintF(outf, "

Example queries

\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); iter = ajListIterNew(resource->Example); while((tmpstr = ajListstrIterGet(iter))) { ajStrTokenAssignC(&handle, tmpstr, "|"); ajStrTokenNextParse(&handle, &tmpid); ajStrRemoveWhiteExcess(&tmpid); ajStrTokenNextParse(&handle, &tmpname); ajStrRemoveWhiteExcess(&tmpname); iterq = ajListIterNew(resource->Query); while((tmpqry = ajListIterGet(iterq))) { if(!ajStrMatchS(tmpid, tmpqry->Term)) continue; ajStrAssignS(&tmpqid, tmpqry->Datatype); ajStrCutBraces(&tmpqid); ajStrFmtPercentEncodeC(&tmpqid, " /()"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", biourl, tmpqid, tmpqry->Datatype); /* datatype */ ajStrAssignS(&tmpqid, tmpqry->Format); ajStrCutBraces(&tmpqid); ajStrFmtPercentEncodeC(&tmpqid, " /()"); ajFmtPrintF(outf, " \n", biourl, tmpqid, tmpqry->Format); /* format */ ajStrAssignS(&tmpurl, tmpqry->Url); ajStrAssignS(&tmpqid, tmpqry->Term); ajStrCutBraces(&tmpqid); ajStrFmtPercentEncodeC(&tmpqid, " /()"); i = 0; ajStrTokenAssignC(&handle, tmpid, ";"); ajStrTokenAssignC(&namehandle, tmpname, ";"); ajStrTokenAssignC(&termhandle, tmpqry->Term, ";"); ajFmtPrintF(outf, " \n"); ajFmtPrintS(&snstr, "%%s"); ajStrExchangeSS(&tmpurl, snstr, tmpname); ajFmtPrintF(outf, " \n", tmpurl, tmpname); /* url, exampleid */ ajFmtPrintF(outf, " \n"); } ajListIterDel(&iterq); } ajListIterDel(&iter); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
DataFormatQueryExample
" "%S" "%S"); while(ajStrTokenNextParse(&handle, &tmpqid)) { ajStrTokenNextParse(&namehandle, &tmpqname); ajStrTokenNextParse(&termhandle, &tmpqterm); if(i) ajFmtPrintF(outf, "; "); ajStrRemoveWhiteExcess(&tmpqid); ajStrCutBraces(&tmpqid); ajFmtPrintF(outf, "%S", biourl, tmpqid, tmpqterm); /* id term */ ajFmtPrintS(&snstr, "%%s%u", (i+1)); ajStrExchangeSS(&tmpurl, snstr, tmpqname); i++; } ajFmtPrintF(outf, "%S
\n"); } ajFmtPrintF(outf, "\n\n\n"); ajStrDel(&tmpid); ajStrDel(&tmpname); ajStrDel(&tmpurl); ajStrDel(&tmpqid); ajStrDel(&tmpqname); ajStrDel(&tmpqterm); ajStrDel(&snstr); ajStrTokenDel(&handle); ajStrTokenDel(&namehandle); ajStrTokenDel(&termhandle); return ajTrue; } ID resourceoutWriteWsbasic TY static MO ajresourcewrite LB core XX DE Write resource object to file in EMBOSS db.dat format. DE DE An extension of ajResourceoutWriteBasic, the following fields / elements DE are written: DE DE Recommended or official unique identifier ('ID') DE The full (verbose english) name ('Name') DE URL of the database server ('URL') DE DE and also: DE URL of documentation on SOAP-based interfaces ('URLsoap') DE URL of documentation on REST-based interfaces ('URLrest') XX PN [1] PA u outf AjPFile PD Output resource file PX PN [2] PA r resource const AjPResource PD Resource object PX RT AjBool RD True on success RX // static AjBool resourceoutWriteWsbasic(AjPFile outf, const AjPResource resource) { if(!outf) return ajFalse; (void) resource; (void) outf; return ajTrue; } ID ajResourceoutprintBook TY public MO ajresourcewrite LB core XX DE Reports the resource format internals as Docbook text XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajResourceoutprintBook(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, "The supported data resource output " "formats are summarised " "in the table below. The columns are as follows: " "Output format (format name), " "Description (short description of " "the format). \n\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n"); for(i=0; resourceoutFormatDef[i].Name; i++) { ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, " \n", resourceoutFormatDef[i].Name); ajFmtPrintF(outf, " \n", resourceoutFormatDef[i].Desc); } ajFmtPrintF(outf, " \n"); ajFmtPrintF(outf, "
Data resource output formats
Output FormatDescription
%s%s
\n"); return; } ID ajResourceoutprintHtml TY public MO ajresourcewrite LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajResourceoutprintHtml(AjPFile outf) { ajint i = 0; ajFmtPrintF(outf, ""); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Data resource output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "ResourceFormat {\n"); for(i=0; resourceoutFormatDef[i].Name; i++) { ajFmtPrintF(outf, "\n", resourceoutFormatDef[i].Name, resourceoutFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajResourceoutprintText TY public MO ajresourcewrite LB core XX DE Reports the internal data structures XX PN [1] PA u outf AjPFile PD Output file PX PN [2] PA r full AjBool PD Full report (usually ajFalse) PX RT void RD RX // void ajResourceoutprintText(AjPFile outf, AjBool full) { ajint i = 0; ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "# Data resource output formats\n"); ajFmtPrintF(outf, "# Name Format name (or alias)\n"); ajFmtPrintF(outf, "# Desc Format description\n"); ajFmtPrintF(outf, "# Name Description\n"); ajFmtPrintF(outf, "\n"); ajFmtPrintF(outf, "ResourceFormat {\n"); for(i=0; resourceoutFormatDef[i].Name; i++) { if(full) ajFmtPrintF(outf, " %-12s \"%s\"\n", resourceoutFormatDef[i].Name, resourceoutFormatDef[i].Desc); } ajFmtPrintF(outf, "}\n\n"); return; } ID ajResourceoutprintWiki TY public MO ajresourcewrite LB core XX DE Reports the data resource output format internals as wikitext XX PN [1] PA u outf AjPFile PD Output file PX RT void RD RX // void ajResourceoutprintWiki(AjPFile outf) { ajint i = 0; ajint j = 0; AjPStr namestr = NULL; ajFmtPrintF(outf, "{| class=\"wikitable sortable\" border=\"2\"\n"); ajFmtPrintF(outf, "|-\n"); ajFmtPrintF(outf, "!Format!!" "class=\"unsortable\"|Description\n"); for(i=0; resourceoutFormatDef[i].Name; i++) { ajFmtPrintF(outf, "|-\n"); ajStrAssignC(&namestr, resourceoutFormatDef[i].Name); for(j=i+1; resourceoutFormatDef[j].Name; j++) { if(resourceoutFormatDef[j].Write == resourceoutFormatDef[i].Write) { ajFmtPrintAppS(&namestr, "
%s", resourceoutFormatDef[j].Name); ajWarn("Data resource output format '%s' same as '%s' " "but not alias", resourceoutFormatDef[j].Name, resourceoutFormatDef[i].Name); } } ajFmtPrintF(outf, "|%S||%s\n", namestr, resourceoutFormatDef[i].Desc); } ajFmtPrintF(outf, "|}\n"); ajStrDel(&namestr); return; } ID ajResourceoutExit TY public MO ajresourcewrite LB core XX DE Cleans up data output internal memory XX RT void RD RX // void ajResourceoutExit(void) { return; } ID ajResourceoutformatFind TY public MO ajresourcewrite LB core XX DE Looks for the specified format(s) in the internal definitions and DE returns the index. DE DE Sets iformat as the recognised format, and returns ajTrue. XX PN [1] PA r format const AjPStr PD Format required. PX PN [2] PA w iformat ajint* PD Index PX RT AjBool RD ajTrue on success. RX // AjBool ajResourceoutformatFind(const AjPStr format, ajint* iformat) { AjPStr tmpformat = NULL; ajuint i = 0; /* ajDebug("ajResourceoutformatFind '%S'\n", format); */ if(!ajStrGetLen(format)) return ajFalse; ajStrAssignS(&tmpformat, format); ajStrFmtLower(&tmpformat); for(i=0; resourceoutFormatDef[i].Name; i++) { /* ajDebug("test %d '%s' \n", i, resourceoutFormatDef[i].Name); */ if(ajStrMatchCaseC(tmpformat, resourceoutFormatDef[i].Name)) { *iformat = i; ajStrDel(&tmpformat); /* ajDebug("found '%s' at %d\n", resourceoutFormatDef[i].Name, i); */ return ajTrue; } } ajErr("Unknown output format '%S'", format); ajStrDel(&tmpformat); *iformat = 0; return ajFalse; } ID ajResourceoutformatTest TY public MO ajresourcewrite LB core XX DE Tests whether a named data resource output format is known XX PN [1] PA r format const AjPStr PD Format PX RT AjBool RD ajTrue if formats was accepted RX // AjBool ajResourceoutformatTest(const AjPStr format) { ajint i; for (i=0; resourceoutFormatDef[i].Name; i++) if(ajStrMatchCaseC(format, resourceoutFormatDef[i].Name)) return ajTrue; return ajFalse; } ID ajSeqNew TY public MO ajseq LB core XX DE Creates and initialises a sequence object. XX RT AjPSeq RD New sequence object. RX // AjPSeq ajSeqNew(void) { return ajSeqNewRes(0); } ID ajSeqNewNameC TY public MO ajseq LB core XX DE Creates and initialises a sequence object with a specified existing DE sequence as a char XX PN [1] PA r txt const char* PD Sequence string PX PN [2] PA r name const char* PD Sequence name PX RT AjPSeq RD New sequence object. RX // AjPSeq ajSeqNewNameC(const char* txt, const char* name) { AjPSeq pthis; pthis = ajSeqNew(); ajStrAssignC(&pthis->Name, name); ajStrAssignC(&pthis->Seq, txt); return pthis; } ID ajSeqNewNameS TY public MO ajseq LB core XX DE Creates and initialises a sequence object with a specified existing DE sequence as a string XX PN [1] PA r str const AjPStr PD Sequence string PX PN [2] PA r name const AjPStr PD Sequence name PX RT AjPSeq RD New sequence object. RX // AjPSeq ajSeqNewNameS(const AjPStr str, const AjPStr name) { AjPSeq pthis; pthis = ajSeqNew(); pthis->Name = ajStrNewS(name); ajStrAssignS(&pthis->Seq, str); return pthis; } ID ajSeqNewRangeC TY public MO ajseq LB core XX DE Creates and initialises a sequence object with a specified existing DE sequence as a string,and provides offsets, and direction. DE DE The sequence is set to be already trimmed and if necessary reversed. DE DE Start and end positions are 0 (full sequence), as it is trimmed. DE Any start and end are represented by the offsets. XX PN [1] PA r txt const char* PD Sequence string PX PN [2] PA r offset ajint PD Offset at start PX PN [3] PA r offend ajint PD Offset at end PX PN [4] PA r rev AjBool PD Reversed if true (reverses offsets) PX RT AjPSeq RD New sequence object. RX // AjPSeq ajSeqNewRangeC(const char* txt, ajint offset, ajint offend, AjBool rev) { AjPSeq pthis; pthis = ajSeqNew(); ajDebug("ajSeqNewRangeC %d %d %B\n", offset, offend, rev); ajStrAssignC(&pthis->Seq, txt); pthis->Rev = ajFalse; pthis->Reversed = rev; /* we are setting everything here */ pthis->Trimmed = ajTrue; /* we are setting everything here */ pthis->EType = 0; pthis->Format = 0; pthis->Begin = 0; pthis->End = 0; if (rev) { pthis->Offset = offend; pthis->Offend = offset; } else { pthis->Offset = offset; pthis->Offend = offend; } pthis->Weight = 1.0; ajDebug("ajSeqNewRangeC rev:%B offset:%d/%d offend:%d/%d\n", rev, offset, pthis->Offset, offend, pthis->Offend); return pthis; } ID ajSeqNewRangeS TY public MO ajseq LB core XX DE Creates and initialises a sequence object with a specified existing DE sequence as a string,and provides offsets, and direction. DE DE The sequence is set to be already trimmed and if necessary reversed. DE DE Start and end positions are 0 (full sequence), as it is trimmed. DE Any start and end are represented by the offsets. XX PN [1] PA r str const AjPStr PD Sequence string PX PN [2] PA r offset ajint PD Offset at start PX PN [3] PA r offend ajint PD Offset at end PX PN [4] PA r rev AjBool PD Reversed if true (reverses offsets) PX RT AjPSeq RD New sequence object. RX // AjPSeq ajSeqNewRangeS(const AjPStr str, ajint offset, ajint offend, AjBool rev) { return ajSeqNewRangeC(ajStrGetPtr(str), offset, offend, rev); } ID ajSeqNewRefseq TY public MO ajseq LB core XX DE Creates and initialises a sequence object with a specified existing DE reference sequence. XX PN [1] PA r refseq const AjPRefseq PD Reference sequence object PX RT AjPSeq RD New sequence object. RX // AjPSeq ajSeqNewRefseq(const AjPRefseq refseq) { AjPSeq pthis; AJNEW0(pthis); if(refseq->Id) ajStrAssignS(&pthis->Name, refseq->Id); if(refseq->Db) ajStrAssignS(&pthis->Db, refseq->Db); if(refseq->Desc) ajStrAssignS(&pthis->Desc, refseq->Desc); if(refseq->Qry) ajStrAssignS(&pthis->Usa, refseq->Qry); if(refseq->Filename) ajStrAssignS(&pthis->Filename, refseq->Filename); ajStrAssignS(&pthis->Seq, refseq->Seq); return pthis; } ID ajSeqNewRes TY public MO ajseq LB core XX DE Creates and initialises a sequence object with a specified sequence length. XX PN [1] PA r size size_t PD Reserved space for the sequence, including PD a trailing null character. PX RT AjPSeq RD New sequence object. RX // AjPSeq ajSeqNewRes(size_t size) { AjPSeq pthis; AJNEW0(pthis); pthis->Name = ajStrNew(); pthis->Acc = ajStrNew(); /* // pthis->Sv = ajStrNew(); // pthis->Gi = ajStrNew(); // pthis->Tax = ajStrNew(); // pthis->Taxid = ajStrNew(); // pthis->Organelle = ajStrNew(); // pthis->Type = ajStrNew(); // pthis->Db = ajStrNew(); // pthis->Full = ajStrNew(); // pthis->Desc = ajStrNew(); // pthis->Doc = ajStrNew(); // pthis->Usa = ajStrNew(); // pthis->Ufo = ajStrNew(); // // pthis->Formatstr = ajStrNew(); // pthis->Filename = ajStrNew(); // pthis->Entryname = ajStrNew(); // pthis->TextPtr = ajStrNew(); */ if(size) pthis->Seq = ajStrNewRes(size); else pthis->Seq = ajStrNew(); pthis->Rev = ajFalse; pthis->Reversed = ajFalse; pthis->Trimmed = ajFalse; pthis->EType = 0; pthis->Format = 0; pthis->Begin = 0; pthis->End = 0; pthis->Offset = 0; pthis->Offend = 0; pthis->Weight = 1.0; /* // pthis->Acclist = ajListstrNew(); // pthis->Keylist = ajListstrNew(); // pthis->Taxlist = ajListstrNew(); // pthis->Genelist = ajListNew(); // pthis->Cmtlist = ajListstrNew(); // pthis->Xreflist = ajListNew(); // pthis->Reflist = ajListNew(); // // pthis->Fulldesc = ajSeqdescNew(); */ return pthis; } ID ajSeqNewSeq TY public MO ajseq LB core XX DE Creates and initialises a sequence object with a specified existing DE sequence. XX PN [1] PA r seq const AjPSeq PD Old sequence object PX RT AjPSeq RD New sequence object. RX // AjPSeq ajSeqNewSeq(const AjPSeq seq) { AjPSeq pthis; AJNEW0(pthis); if(seq->Name) ajStrAssignS(&pthis->Name, seq->Name); if(seq->Acc) ajStrAssignS(&pthis->Acc, seq->Acc); if(seq->Sv) ajStrAssignS(&pthis->Sv, seq->Sv); if(seq->Gi) ajStrAssignS(&pthis->Gi, seq->Gi); if(seq->Tax) ajStrAssignS(&pthis->Tax, seq->Tax); if(seq->Taxid) ajStrAssignS(&pthis->Taxid, seq->Taxid); if(seq->Organelle) ajStrAssignS(&pthis->Organelle, seq->Organelle); if(seq->Type) ajStrAssignS(&pthis->Type, seq->Type); pthis->EType = seq->EType; if(seq->Db) ajStrAssignS(&pthis->Db, seq->Db); if(seq->Setdb) ajStrAssignS(&pthis->Setdb, seq->Setdb); if(seq->Full) ajStrAssignS(&pthis->Full, seq->Full); if(seq->Desc) ajStrAssignS(&pthis->Desc, seq->Desc); if(seq->Doc) ajStrAssignS(&pthis->Doc, seq->Doc); pthis->Rev = seq->Rev; pthis->Reversed = seq->Reversed; pthis->Trimmed = seq->Trimmed; pthis->Begin = seq->Begin; pthis->End = seq->End; pthis->Offset = seq->Offset; pthis->Offend = seq->Offend; pthis->Weight = seq->Weight; pthis->Fpos = seq->Fpos; if(seq->Usa) ajStrAssignS(&pthis->Usa, seq->Usa); if(seq->Ufo) ajStrAssignS(&pthis->Ufo, seq->Ufo); if(seq->Formatstr) ajStrAssignS(&pthis->Formatstr, seq->Formatstr); pthis->Format = seq->Format; if(seq->Filename) ajStrAssignS(&pthis->Filename, seq->Filename); if(seq->Entryname) ajStrAssignS(&pthis->Entryname, seq->Entryname); if(seq->TextPtr) ajStrAssignS(&pthis->TextPtr, seq->TextPtr); if(seq->Acclist) pthis->Acclist = ajListstrNewList(seq->Acclist); if(seq->Keylist) pthis->Keylist = ajListstrNewList(seq->Keylist); if(seq->Taxlist) pthis->Taxlist = ajListstrNewList(seq->Taxlist); if(seq->Cmtlist) pthis->Cmtlist = ajListstrNewList(seq->Cmtlist); if(seq->Xreflist) { pthis->Xreflist = ajListNew(); ajSeqxreflistClone(seq->Xreflist, pthis->Xreflist); } if(seq->Genelist) { pthis->Genelist = ajListNew(); ajSeqgenelistClone(seq->Genelist, pthis->Genelist); } if(seq->Reflist) { pthis->Reflist = ajListNew(); ajSeqreflistClone(seq->Reflist, pthis->Reflist); } if(seq->Fulldesc) pthis->Fulldesc = ajSeqdescNewDesc(seq->Fulldesc); ajStrAssignS(&pthis->Seq, seq->Seq); if (seq->Fttable) pthis->Fttable = ajFeattableNewFtable(seq->Fttable); if (seq->Accuracy && seq->Qualsize) { pthis->Qualsize = seq->Qualsize; AJCNEW0(pthis->Accuracy,pthis->Qualsize); memmove(pthis->Accuracy,seq->Accuracy,pthis->Qualsize*sizeof(float)); } return pthis; } ID ajSeqDel TY public MO ajseq LB core XX DE Deletes a sequence object. XX PN [1] PA d Pseq AjPSeq* PD Sequence object PX RT void RD RX // void ajSeqDel(AjPSeq* Pseq) { AjPSeq seq; AjPSeqRef tmpref = NULL; AjPSeqXref tmpxref = NULL; AjPSeqGene tmpgene = NULL; seq = Pseq ? *Pseq : 0; if(!Pseq) return; if(!*Pseq) return; ajStrDel(&seq->Name); ajStrDel(&seq->Acc); ajStrDel(&seq->Sv); ajStrDel(&seq->Gi); ajStrDel(&seq->Tax); ajStrDel(&seq->Taxid); ajStrDel(&seq->Organelle); ajStrDel(&seq->Type); ajStrDel(&seq->Molecule); ajStrDel(&seq->Class); ajStrDel(&seq->Division); ajStrDel(&seq->Evidence); ajStrDel(&seq->Db); ajStrDel(&seq->Setdb); ajStrDel(&seq->Full); ajStrDel(&seq->Desc); ajStrDel(&seq->Doc); ajStrDel(&seq->Usa); ajStrDel(&seq->Ufo); ajStrDel(&seq->Formatstr); ajStrDel(&seq->Filename); ajStrDel(&seq->Entryname); ajStrDel(&seq->TextPtr); ajStrDel(&seq->Seq); AJFREE(seq->Accuracy); if(seq->Fttable) ajFeattableDel(&seq->Fttable); ajListstrFreeData(&seq->Acclist); ajListstrFreeData(&seq->Keylist); ajListstrFreeData(&seq->Taxlist); while(ajListPop(seq->Genelist,(void **)&tmpgene)) ajSeqgeneDel(&tmpgene); ajListFree(&seq->Genelist); while(ajListPop(seq->Reflist,(void **)&tmpref)) ajSeqrefDel(&tmpref); ajListFree(&seq->Reflist); ajListstrFreeData(&seq->Cmtlist); while(ajListPop(seq->Xreflist,(void **)&tmpxref)) ajSeqxrefDel(&tmpxref); ajListFree(&seq->Xreflist); ajSeqdateDel(&seq->Date); ajSeqdescDel(&seq->Fulldesc); AJFREE(*Pseq); return; } ID ajSeqDelarray TY public MO ajseq LB core XX DE Destructor for array of sequence objects XX PN [1] PA d PPseq AjPSeq** PD Sequence object array PX RT void RD RX // void ajSeqDelarray(AjPSeq **PPseq) { ajuint i = 0; if(!PPseq || !*PPseq) return; while((*PPseq)[i]) { ajSeqDel(&(*PPseq)[i]); i++; } ajDebug("ajSeqDelarray size: %d\n", i); AJFREE(*PPseq); return; } ID ajSeqAddCmt TY public MO ajseq LB core XX DE Adds a comment to a sequence object XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA u str AjPStr PD Comment. PX RT void RD RX // void ajSeqAddCmt(AjPSeq seq, AjPStr str) { if(!seq->Cmtlist) seq->Cmtlist = ajListstrNew(); ajListstrPushAppend(seq->Cmtlist, str); return; } ID ajSeqAddGene TY public MO ajseq LB core XX DE Adds a gene to a sequence object XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA u gene AjPSeqGene PD Gene object PX RT void RD RX // void ajSeqAddGene(AjPSeq seq, AjPSeqGene gene) { if(!seq->Genelist) seq->Genelist = ajListNew(); ajListPushAppend(seq->Genelist, gene); return; } ID ajSeqAddKey TY public MO ajseq LB core XX DE Adds a keyword to a sequence object XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA u str AjPStr PD Comment. PX RT void RD RX // void ajSeqAddKey(AjPSeq seq, AjPStr str) { if(!seq->Keylist) seq->Keylist = ajListstrNew(); ajListstrPushAppend(seq->Keylist, str); return; } ID ajSeqAddRef TY public MO ajseq LB core XX DE Adds a literature reference to a sequence object XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA u ref AjPSeqRef PD Literature reference. PX RT void RD RX // void ajSeqAddRef(AjPSeq seq, AjPSeqRef ref) { if(!seq->Reflist) seq->Reflist = ajListNew(); ajListPushAppend(seq->Reflist, ref); return; } ID ajSeqAddXref TY public MO ajseq LB core XX DE Adds a cross-reference to a sequence object XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA u xref AjPSeqXref PD Comment. PX RT void RD RX // void ajSeqAddXref(AjPSeq seq, AjPSeqXref xref) { if(!seq->Xreflist) seq->Xreflist = ajListNew(); ajListPushAppend(seq->Xreflist, xref); return; } ID ajSeqAssignAccC TY public MO ajseq LB core XX DE Assigns the sequence accession number. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD Accession number as a C character string. PX RT void RD RX // void ajSeqAssignAccC(AjPSeq seq, const char* txt) { ajStrAssignC(&seq->Acc, txt); return; } ID ajSeqAssignAccS TY public MO ajseq LB core XX DE Assigns the sequence accession number. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD Accession number as a string. PX RT void RD RX // void ajSeqAssignAccS(AjPSeq seq, const AjPStr str) { ajStrAssignS(&seq->Acc, str); return; } ID ajSeqAssignDescC TY public MO ajseq LB core XX DE Assigns a modified description to an existing AjPSeq sequence. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD New description as a C character string. PX RT void RD RX // void ajSeqAssignDescC(AjPSeq seq, const char* txt) { ajStrAssignC(&seq->Desc, txt); return; } ID ajSeqAssignDescS TY public MO ajseq LB core XX DE Assigns a modified description to an existing AjPSeq sequence. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD New description as a string. PX RT void RD RX // void ajSeqAssignDescS(AjPSeq seq, const AjPStr str) { ajStrAssignS(&seq->Desc, str); return; } ID ajSeqAssignEntryC TY public MO ajseq LB core XX DE Assigns the sequence entryname. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD Entry name as a C character string. PX RT void RD RX // void ajSeqAssignEntryC(AjPSeq seq, const char* txt) { ajStrAssignC(&seq->Entryname, txt); return; } ID ajSeqAssignEntryS TY public MO ajseq LB core XX DE Assigns the sequence entryname. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD Entry name as a string. PX RT void RD RX // void ajSeqAssignEntryS(AjPSeq seq, const AjPStr str) { ajStrAssignS(&seq->Entryname, str); return; } ID ajSeqAssignFileC TY public MO ajseq LB core XX DE Assigns the sequence filename. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD File name as a C character string. PX RT void RD RX // void ajSeqAssignFileC(AjPSeq seq, const char* txt) { ajStrAssignC(&seq->Filename, txt); return; } ID ajSeqAssignFileS TY public MO ajseq LB core XX DE Assigns the sequence file name. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD File name as a string. PX RT void RD RX // void ajSeqAssignFileS(AjPSeq seq, const AjPStr str) { ajStrAssignS(&seq->Filename, str); return; } ID ajSeqAssignFullC TY public MO ajseq LB core XX DE Assigns the sequence name. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD Full name as a C character string. PX RT void RD RX // void ajSeqAssignFullC(AjPSeq seq, const char* txt) { ajStrAssignC(&seq->Full, txt); return; } ID ajSeqAssignFullS TY public MO ajseq LB core XX DE Assigns the sequence full name. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD Full name as a string. PX RT void RD RX // void ajSeqAssignFullS(AjPSeq seq, const AjPStr str) { ajStrAssignS(&seq->Full, str); return; } ID ajSeqAssignGiC TY public MO ajseq LB core XX DE Assigns the GI version number. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD GI number as a C character string. PX RT void RD RX // void ajSeqAssignGiC(AjPSeq seq, const char* txt) { ajStrAssignC(&seq->Gi, txt); return; } ID ajSeqAssignGiS TY public MO ajseq LB core XX DE Assigns the GI version number. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD GI number as a string. PX RT void RD RX // void ajSeqAssignGiS(AjPSeq seq, const AjPStr str) { ajStrAssignS(&seq->Gi, str); return; } ID ajSeqAssignNameC TY public MO ajseq LB core XX DE Assigns the sequence name. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD Name as a string. PX RT void RD RX // void ajSeqAssignNameC(AjPSeq seq, const char* txt) { ajStrAssignC(&seq->Name, txt); return; } ID ajSeqAssignNameS TY public MO ajseq LB core XX DE Assigns the sequence name. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD Name as a string. PX RT void RD RX // void ajSeqAssignNameS(AjPSeq seq, const AjPStr str) { ajStrAssignS(&seq->Name, str); return; } ID ajSeqAssignQualLen TY public MO ajseq LB core XX DE Assigns base quality scores to an existing sequence. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r qual const float* PD Base quality scores. PX PN [3] PA r len ajuint PD Number of quality scores to use PX RT void RD RX // void ajSeqAssignQualLen(AjPSeq seq, const float* qual, ajuint len) { seq->Qualsize = len; AJCRESIZE(seq->Accuracy,seq->Qualsize); memmove(seq->Accuracy, qual, seq->Qualsize*sizeof(float)); return; } ID ajSeqAssignSeqC TY public MO ajseq LB core XX DE Assigns a modified sequence to an existing AjPSeq sequence. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD New sequence as a C character string. PX RT void RD RX // void ajSeqAssignSeqC(AjPSeq seq, const char* txt) { ajint i = strlen(txt); ajSeqAssignSeqLenC(seq, txt, i); return; } ID ajSeqAssignSeqLenC TY public MO ajseq LB core XX DE Assigns a modified sequence to an existing AjPSeq sequence. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD New sequence as a C character string. PX PN [3] PA r len ajuint PD Number of characters to use PX RT void RD RX // void ajSeqAssignSeqLenC(AjPSeq seq, const char* txt, ajuint len) { ajStrAssignLenC(&seq->Seq, txt, len); seq->Begin = 0; seq->End = 0; seq->Offset = 0; seq->Offend = 0; seq->Rev = ajFalse; seq->Reversed = ajFalse; seq->Trimmed = ajFalse; return; } ID ajSeqAssignSeqS TY public MO ajseq LB core XX DE Assigns a modified sequence to an existing AjPSeq sequence. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD New sequence as a string. PX RT void RD RX // void ajSeqAssignSeqS(AjPSeq seq, const AjPStr str) { ajSeqAssignSeqLenC(seq, ajStrGetPtr(str), ajStrGetLen(str)); return; } ID ajSeqAssignSvC TY public MO ajseq LB core XX DE Assigns the sequence version number. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD SeqVersion number as a C character string. PX RT void RD RX // void ajSeqAssignSvC(AjPSeq seq, const char* txt) { ajStrAssignC(&seq->Sv, txt); return; } ID ajSeqAssignSvS TY public MO ajseq LB core XX DE Assigns the sequence version number. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD SeqVersion number as a string. PX RT void RD RX // void ajSeqAssignSvS(AjPSeq seq, const AjPStr str) { ajStrAssignS(&seq->Sv, str); return; } ID ajSeqAssignUfoC TY public MO ajseq LB core XX DE Assigns the sequence feature file name. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD UFO as a C character string. PX RT void RD RX // void ajSeqAssignUfoC(AjPSeq seq, const char* txt) { ajStrAssignC(&seq->Ufo, txt); return; } ID ajSeqAssignUfoS TY public MO ajseq LB core XX DE Assigns the sequence feature file name. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD UFO as a string. PX RT void RD RX // void ajSeqAssignUfoS(AjPSeq seq, const AjPStr str) { ajStrAssignS(&seq->Ufo, str); return; } ID ajSeqAssignUsaC TY public MO ajseq LB core XX DE Assigns the sequence full name. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r txt const char* PD USA as a C character string. PX RT void RD RX // void ajSeqAssignUsaC(AjPSeq seq, const char* txt) { ajStrAssignC(&seq->Usa, txt); return; } ID ajSeqAssignUsaS TY public MO ajseq LB core XX DE Assigns the sequence full name. XX PN [1] PA u seq AjPSeq PD Sequence object. PX PN [2] PA r str const AjPStr PD USA as a string. PX RT void RD RX // void ajSeqAssignUsaS(AjPSeq seq, const AjPStr str) { ajStrAssignS(&seq->Usa, str); return; } ID ajSeqClear TY public MO ajseq LB core XX DE Resets all data for a sequence object so that it can be reused. XX PN [1] PA u seq AjPSeq PD Sequence PX RT void RD RX // void ajSeqClear(AjPSeq seq) { AjPStr ptr = NULL; AjPSeqRef tmpref = NULL; AjPSeqXref tmpxref = NULL; AjPSeqGene tmpgene = NULL; if(!seq) return; if(MAJSTRGETLEN(seq->Name)) ajStrSetClear(&seq->Name); if(MAJSTRGETLEN(seq->Acc)) ajStrSetClear(&seq->Acc); if(MAJSTRGETLEN(seq->Sv)) ajStrSetClear(&seq->Sv); if(MAJSTRGETLEN(seq->Gi)) ajStrSetClear(&seq->Gi); if(MAJSTRGETLEN(seq->Tax)) ajStrSetClear(&seq->Tax); if(MAJSTRGETLEN(seq->Taxid)) ajStrSetClear(&seq->Taxid); if(MAJSTRGETLEN(seq->Organelle)) ajStrSetClear(&seq->Organelle); if(MAJSTRGETLEN(seq->Type)) ajStrSetClear(&seq->Type); if(MAJSTRGETLEN(seq->Molecule)) ajStrSetClear(&seq->Molecule); if(MAJSTRGETLEN(seq->Class)) ajStrSetClear(&seq->Class); if(MAJSTRGETLEN(seq->Division)) ajStrSetClear(&seq->Division); if(MAJSTRGETLEN(seq->Evidence)) ajStrSetClear(&seq->Evidence); if(MAJSTRGETLEN(seq->Db)) ajStrSetClear(&seq->Db); if(MAJSTRGETLEN(seq->Full)) ajStrSetClear(&seq->Full); if(MAJSTRGETLEN(seq->Desc)) ajStrSetClear(&seq->Desc); if(MAJSTRGETLEN(seq->Doc)) ajStrSetClear(&seq->Doc); if(MAJSTRGETLEN(seq->Usa)) ajStrSetClear(&seq->Usa); if(MAJSTRGETLEN(seq->Ufo)) ajStrSetClear(&seq->Ufo); if(MAJSTRGETLEN(seq->Formatstr)) ajStrSetClear(&seq->Formatstr); if(MAJSTRGETLEN(seq->Filename)) ajStrSetClear(&seq->Filename); if(MAJSTRGETLEN(seq->Entryname)) ajStrSetClear(&seq->Entryname); if(MAJSTRGETLEN(seq->TextPtr)) ajStrSetClear(&seq->TextPtr); if(MAJSTRGETLEN(seq->Seq)) ajStrSetClear(&seq->Seq); if(seq->Accuracy && seq->Qualsize) AJCSET0(seq->Accuracy, seq->Qualsize); seq->Begin = 0; seq->End = 0; seq->Rev = ajFalse; seq->Reversed = ajFalse; seq->Trimmed = ajFalse; seq->Circular = ajFalse; if(seq->Acclist) while(ajListstrPop(seq->Acclist,&ptr)) ajStrDel(&ptr); if(seq->Keylist) while(ajListstrPop(seq->Keylist,&ptr)) ajStrDel(&ptr); if(seq->Taxlist) while(ajListstrPop(seq->Taxlist,&ptr)) ajStrDel(&ptr); if(seq->Genelist) while(ajListPop(seq->Genelist,(void **)&tmpgene)) ajSeqgeneDel(&tmpgene); if(seq->Reflist) while(ajListPop(seq->Reflist,(void **)&tmpref)) ajSeqrefDel(&tmpref); if(seq->Cmtlist) while(ajListPop(seq->Cmtlist,(void **)&ptr)) ajStrDel(&ptr); if(seq->Xreflist) while(ajListPop(seq->Xreflist,(void **)&tmpxref)) ajSeqxrefDel(&tmpxref); if(seq->Date) ajSeqdateClear(seq->Date); if(seq->Fulldesc) ajSeqdescClear(seq->Fulldesc); if(seq->Fttable) ajFeattableDel(&seq->Fttable); return; } ID ajSeqSetCircular TY public MO ajseq LB core XX DE Sets a sequence to be circular XX PN [1] PA u seq AjPSeq PD Sequence PX RT void RD RX // void ajSeqSetCircular(AjPSeq seq) { seq->Circular = ajTrue; return; } ID ajSeqSetName TY public MO ajseq LB core XX DE Provides a unique (for this program run) name for a sequence. XX PN [1] PA w seq AjPSeq PD Sequence object PX PN [2] PA r setname const AjPStr PD Name set by caller PX RT void RD RX // void ajSeqSetName(AjPSeq seq, const AjPStr setname) { if(ajStrGetLen(seq->Name)) { ajDebug("ajSeqSetName already has a name '%S'\n", seq->Name); return; } if (ajStrGetLen(setname)) { ajStrAssignS(&seq->Name, setname); } else { ajStrAssignC(&seq->Name, "EMBOSS"); } ajDebug("ajSeqSetName set to '%S'\n", seq->Name); return; } ID ajSeqSetNameMulti TY public MO ajseq LB core XX DE Provides a unique (for this program run) name for a sequence. DE If a name is generated, append a count XX PN [1] PA w seq AjPSeq PD Sequence object PX PN [2] PA r setname const AjPStr PD Name set by caller PX RT void RD RX // void ajSeqSetNameMulti(AjPSeq seq, const AjPStr setname) { static ajint count = 0; if(ajStrGetLen(seq->Name)) { ajDebug("ajSeqSetNameMulti already has a name '%S'\n", seq->Name); return; } if (ajStrGetLen(setname)) { if(count) ajFmtPrintS(&seq->Name, "%S_%3.3d", setname, ++count); else { ajStrAssignS(&seq->Name, setname); ++count; } } else { ajFmtPrintS(&seq->Name, "EMBOSS_%3.3d", ++count); } ajDebug("ajSeqSetNameMulti set to '%S'\n", seq->Name); return; } ID ajSeqSetOffsets TY public MO ajseq LB core XX DE Sets the offsets for each end of a subsequence. DE DE Needed mainly for local alignments so the original sequence numbering DE can be preserved. XX PN [1] PA u seq AjPSeq PD Sequence object to be set. PX PN [2] PA r offset ajint PD Offset from start of original sequence PX PN [3] PA r origlen ajint PD Original length, used to calculate the offset PD from the end. PX RT void RD RX // void ajSeqSetOffsets(AjPSeq seq, ajint offset, ajint origlen) { ajDebug("ajSeqSetOffsets(len:%d gap:%d off:%d origlen:%d) " "Offset:%d Offend:%d\n", ajSeqGetLen(seq), ajSeqCountGaps(seq), offset, origlen, seq->Offset, seq->Offend); if(seq->Trimmed) { ajWarn("Sequence '%S' already trimmed in ajSeqSetOffsets", ajSeqGetNameS(seq)); } if(seq->Reversed) { if(offset && !seq->Offend) seq->Offend = offset; if(origlen && !seq->Offset) { seq->Offset = origlen - offset - ajSeqGetLen(seq) + ajSeqCountGaps(seq); } } else { if(offset && !seq->Offset) seq->Offset = offset; if(origlen && !seq->Offend) { seq->Offend = origlen - offset - ajSeqGetLen(seq) + ajSeqCountGaps(seq); } } ajDebug(" result: (len: %d truelen:%d Offset:%d Offend:%d)\n", ajSeqGetLen(seq), ajSeqGetLen(seq)-ajSeqCountGaps(seq), seq->Offset, seq->Offend); return; } ID ajSeqSetRange TY public MO ajseq LB core XX DE Sets the start and end positions for a sequence (not for a sequence set). DE Trim the sequence to convert to the subsequence. XX PN [1] PA u seq AjPSeq PD Sequence object to be set. PX PN [2] PA r pos1 ajint PD Start position. Negative values are from the end. PX PN [3] PA r pos2 ajint PD End position. Negative values are from the end. PX RT void RD RX // void ajSeqSetRange(AjPSeq seq, ajint pos1, ajint pos2) { ajDebug("ajSeqSetRange (len: %d %d..%d old %d..%d) rev:%B reversed:%B\n", ajSeqGetLen(seq), pos1, pos2, seq->Begin, seq->End, seq->Rev, seq->Reversed); if(seq->Trimmed) { ajWarn("Sequence '%S' already trimmed in ajSeqSetRange", ajSeqGetNameS(seq)); } if(pos1 && !seq->Begin) seq->Begin = pos1; if(pos2 && !seq->End) seq->End = pos2; ajDebug(" result: (len: %d %d..%d)\n", ajSeqGetLen(seq), seq->Begin, seq->End); if(seq->Rev && !seq->Reversed) ajSeqReverseDo(seq); return; } ID ajSeqSetRangeRev TY public MO ajseq LB core XX DE Sets the start and end positions for a sequence (not for a sequence set), DE and set the sequence to be reversed. XX PN [1] PA u seq AjPSeq PD Sequence object to be set. PX PN [2] PA r pos1 ajint PD Start position. Negative values are from the end. PX PN [3] PA r pos2 ajint PD End position. Negative values are from the end. PX RT void RD RX // void ajSeqSetRangeRev(AjPSeq seq, ajint pos1, ajint pos2) { ajDebug("ajSeqSetRange (len: %d %d..%d old %d..%d) rev:%B reversed:%B\n", ajSeqGetLen(seq), pos1, pos2, seq->Begin, seq->End, seq->Rev, seq->Reversed); if(seq->Trimmed) ajWarn("Sequence '%S' already trimmed in ajSeqSetRange", ajSeqGetNameS(seq)); if(pos1 && !seq->Begin) seq->Begin = pos1; if(pos2 && !seq->End) seq->End = pos2; ajDebug(" result: (len: %d %d..%d)\n", ajSeqGetLen(seq), seq->Begin, seq->End); if(!seq->Rev) { seq->Rev = ajTrue; seq->Reversed = ajFalse; } ajSeqReverseDo(seq); return; } ID ajSeqSetUnique TY public MO ajseq LB core XX DE Makes a sequence modifiable by making sure there is no duplicate DE copy of the sequence. XX PN [1] PA u seq AjPSeq PD Sequence PX RT void RD RX // void ajSeqSetUnique(AjPSeq seq) { ajStrGetuniqueStr(&seq->Seq); return; } ID ajSeqTagRev TY public MO ajseq LB core XX DE Adds a tag to the sequence description to note it has been reversed XX PN [1] PA u seq AjPSeq PD Sequence object to be set. PX RT void RD RX // void ajSeqTagRev(AjPSeq seq) { if(ajStrGetLen(seq->Desc)) ajStrInsertC(&seq->Desc, 0, "Reversed: "); else ajStrAssignC(&seq->Desc, "Reversed:"); return; } ID seqMakeUsa TY static MO ajseq LB core XX DE Sets the USA for a sequence. XX PN [1] PA r thys const AjPSeq PD Sequence object PX PN [2] PA w usa AjPStr* PD USA in full PX RT void RD RX // static void seqMakeUsa(const AjPSeq thys, AjPStr* usa) { AjPStr tmpstr = NULL; ajDebug("seqMakeUsa (Name <%S> Formatstr <%S> Db <%S> " "Entryname <%S> Filename <%S>)\n", thys->Name, thys->Formatstr, thys->Db, thys->Entryname, thys->Filename); ajDebug("Begin:%d End:%d Rev:%B Reversed:%B\n", thys->Begin, thys->End, thys->Rev, thys->Reversed); /* ajSeqTrace(thys); */ if(ajStrGetLen(thys->Db)) ajFmtPrintS(usa, "%S-id:%S", thys->Db, thys->Entryname); else { /*ajFmtPrintS(&thys->Usa, "%S::%S (%S)", thys->Formatstr, thys->Filename, thys->Entryname);*/ if(ajStrMatchC(thys->Formatstr, "asis")) { if(thys->Reversed) { ajStrAssignS(&tmpstr, thys->Seq); ajSeqstrReverse(&tmpstr); ajFmtPrintS(usa, "asis::%S", tmpstr); ajStrDel(&tmpstr); } else ajFmtPrintS(usa, "asis::%S", thys->Seq); } else if(ajStrGetLen(thys->Entryname)) ajFmtPrintS(usa, "%S::%S:%S", thys->Formatstr, thys->Filename,thys->Entryname); else ajFmtPrintS(usa, "%S::%S", thys->Formatstr, thys->Filename); } ajFmtPrintS(&tmpstr, "["); if(thys->Reversed) { if(thys->End) ajFmtPrintAppS(&tmpstr, "%d", -thys->End); ajFmtPrintAppS(&tmpstr, ":"); if(thys->Begin) ajFmtPrintAppS(&tmpstr, "%d", -thys->Begin); ajFmtPrintAppS(&tmpstr, ":r"); } else { if(thys->Begin) ajFmtPrintAppS(&tmpstr, "%d", thys->Begin); ajFmtPrintAppS(&tmpstr, ":"); if(thys->End) ajFmtPrintAppS(&tmpstr, "%d", thys->End); } ajFmtPrintAppS(&tmpstr, "]"); if(ajStrGetLen(tmpstr) > 3) ajStrAppendS(usa, tmpstr); ajStrDel(&tmpstr); ajDebug(" result: <%S>\n", *usa); return; } ID ajSeqComplement TY public MO ajseq LB core XX DE Complements but does not reverse a nucleotide sequence. XX PN [1] PA u seq AjPSeq PD Sequence PX RT void RD RX // void ajSeqComplement(AjPSeq seq) { ajSeqstrComplement(&seq->Seq); return; } ID ajSeqFmtLower TY public MO ajseq LB core XX DE Converts a sequence to lower case. XX PN [1] PA u seq AjPSeq PD Sequence PX RT void RD RX // void ajSeqFmtLower(AjPSeq seq) { ajStrFmtLower(&seq->Seq); return; } ID ajSeqFmtUpper TY public MO ajseq LB core XX DE Converts a sequence to upper case. XX PN [1] PA u seq AjPSeq PD Sequence PX RT void RD RX // void ajSeqFmtUpper(AjPSeq seq) { ajStrFmtUpper(&seq->Seq); return; } ID ajSeqGapFill TY public MO ajseq LB core XX DE Fills a single sequence with gaps up to a specified length. XX PN [1] PA u seq AjPSeq PD Sequence object to be set. PX PN [2] PA r len ajuint PD Length to pad fill to. PX RT void RD RX // void ajSeqGapFill(AjPSeq seq, ajuint len) { ajuint ilen = 0; ajDebug("ajSeqGapFill(len: %d -> ilen:%d)\n", ajSeqGetLen(seq), ilen); if(ajSeqGetLen(seq) < len) { ilen = len - ajSeqGetLen(seq); ajStrAppendCountK(&seq->Seq, '-', ilen); } ajDebug(" result: (len: %d added: %d\n", ajSeqGetLen(seq), ilen); return; } ID ajSeqGapStandard TY public MO ajseq LB core XX DE Makes all gaps in a string use a standard gap character XX PN [1] PA w seq AjPSeq PD Sequence object PX PN [2] PA r gapchar char PD Gap character (or '-' if zero) PX RT void RD RX // void ajSeqGapStandard(AjPSeq seq, char gapchar) { char newgap = '-'; static char testchars[] = "-~.? "; /* all known gap characters */ char *testgap; testgap = testchars; if(gapchar) newgap = gapchar; /*ajDebug("ajSeqGapStandardS '%c'=>'%c' '%S'\n", gapch, newgap, seq->Seq);*/ while(*testgap) { if(newgap != *testgap) { ajStrExchangeKK(&seq->Seq, *testgap, newgap); /*ajDebug(" ajSeqGapStandardS replaced '%c'=>'%c' '%S'\n", *testgap, newgap, thys);*/ } testgap++; } return; } ID ajSeqReverseDo TY public MO ajseq LB core XX DE Reverses and complements a nucleotide sequence, unless it is already done. DE DE If the sequence is not flagged for reversal, use ajSeqReverseForce instead. XX PN [1] PA u seq AjPSeq PD Sequence PX RT void RD RX // void ajSeqReverseDo(AjPSeq seq) { ajint ibegin; ajint iend; ajint itemp; ajuint i; ajuint j; float atemp; ajDebug("ajSeqReverseDo len: %d Begin: %d End: %d Rev: %B Reversed: %B\n", ajSeqGetLen(seq), seq->Begin, seq->End, seq->Rev, seq->Reversed); if(!seq->Rev) /* Not flagged for reversal */ return; ibegin = seq->Begin; iend = seq->End; seq->End = -(ibegin); seq->Begin = -(iend); itemp = seq->Offend; seq->Offend = seq->Offset; seq->Offset = itemp; seq->Rev = ajFalse; if(seq->Reversed) seq->Reversed = ajFalse; else seq->Reversed = ajTrue; ajSeqstrReverse(&seq->Seq); ajDebug(" result len: %d Begin: %d End: %d\n", ajSeqGetLen(seq), seq->Begin, seq->End); if(seq->Fttable) ajFeattableReverse(seq->Fttable); if(seq->Accuracy) { j = seq->Qualsize; for(i=0;iQualsize;i++) { if(--j <= i) break; atemp = seq->Accuracy[j]; seq->Accuracy[j] = seq->Accuracy[i]; seq->Accuracy[i] = atemp; } } return; } ID ajSeqReverseForce TY public MO ajseq LB core XX DE Reverses and complements a nucleotide sequence. DE Forces reversal to be done even if the sequence is flagged DE as already reversed. DE DE This happens, for example, where an input sequence has been reversed DE with -sreverse on the command line, but the application needs to reverse it DE in processing both directions. XX PN [1] PA u seq AjPSeq PD Sequence PX RT void RD RX // void ajSeqReverseForce(AjPSeq seq) { ajDebug("ajSeqReverseForce len: %d Begin: %d End: %d Rev: %B " "Reversed: %B\n", ajSeqGetLen(seq), seq->Begin, seq->End, seq->Rev, seq->Reversed); seq->Rev = ajTrue; ajSeqReverseDo(seq); return; } ID ajSeqReverseOnly TY public MO ajseq LB core XX DE Reverses but does not complement a nucleotide sequence. DE DE Intended for sequence display, but can be used to fix incorrect DE sequence entries. XX PN [1] PA u seq AjPSeq PD Sequence PX RT void RD RX // void ajSeqReverseOnly(AjPSeq seq) { ajint ibegin; ajint iend; ajDebug("ajSeqRevOnly len: %d Begin: %d End: %d\n", ajSeqGetLen(seq), seq->Begin, seq->End); ibegin = seq->Begin; iend = seq->End; if(ibegin) seq->End = -(ibegin); if(iend) seq->Begin = -(iend); ajStrReverse(&seq->Seq); ajDebug(" only result len: %d Begin: %d End: %d\n", ajSeqGetLen(seq), seq->Begin, seq->End); return; } ID ajSeqTrim TY public MO ajseq LB core XX DE Trim a sequence using the Begin and Ends. DE Also reverse complements a nucleotide sequence if required. XX PN [1] PA u seq AjPSeq PD Sequence to be trimmed. PX RT void RD RX // void ajSeqTrim(AjPSeq seq) { AjBool okay = ajTrue; ajint begin; ajint end; ajint jbegin; ajint jend; /*ajDebug("ajSeqTrim '%S'\n", seq->Seq);*/ ajDebug("ajSeqTrim '%S' Rev:%B Reversed:%B Begin:%d End:%d " "Offset:%d Offend:%d Len:%d\n", ajSeqGetNameS(seq), seq->Rev, seq->Reversed, seq->Begin, seq->End, seq->Offset, seq->Offend, seq->Seq->Len); if(seq->Trimmed) { ajWarn("Sequence '%S' already trimmed", ajSeqGetNameS(seq)); return; } if(seq->Rev) ajSeqReverseDo(seq); /*ajDebug("ajSeqTrim '%S'\n", seq->Seq);*/ ajDebug("ajSeqTrim Rev:%B Reversed:%B Begin:%d End:%d " "Offset:%d Offend:%d Len:%d okay:%B\n", seq->Rev, seq->Reversed, seq->Begin, seq->End, seq->Offset, seq->Offend, seq->Seq->Len, okay); jbegin = seq->Begin; if(jbegin > 0) jbegin--; jend = seq->End; if(jend > 0) jend--; /* if(jbegin > (ajint) ajSeqGetLen(seq)) ajWarn("sequence begin %d more than length %u", jbegin, ajSeqGetLen(seq)); if(jend > (ajint) ajSeqGetLen(seq)) ajWarn("sequence end %d more than length %u", jbegin, ajSeqGetLen(seq)); */ begin = 1 + ajCvtSposToPosStart(ajSeqGetLen(seq), 0, jbegin); end = 1 + ajCvtSposToPosStart(ajSeqGetLen(seq), begin-1, jend); ajDebug("Trimming %d from %d (%d) to %d (%d) " "Rev: %B Reversed: %B Trimmed: %B\n", seq->Seq->Len,seq->Begin,begin, seq->End, end, seq->Rev, seq->Reversed, seq->Trimmed); if(seq->End) { if(end < begin) return; seq->Offend = seq->Seq->Len-(end); okay = ajStrCutEnd(&(seq->Seq),seq->Seq->Len-(end)); seq->End = 0; } if(seq->Begin) { okay = ajStrCutStart(&seq->Seq,begin-1); seq->Offset = begin-1; seq->Begin = 0; } ajDebug("After Trimming len = %d off = %d offend = %d\n", seq->Seq->Len, seq->Offset, seq->Offend); /*ajDebug("After Trimming len = %d '%S'\n",thys->Seq->Len, thys->Seq);*/ if(okay && seq->Fttable) okay = ajFeattableTrimOff(seq->Fttable, seq->Offset, seq->Seq->Len); /*ajDebug("ajSeqTrim '%S'\n", seq->Seq);*/ ajDebug("ajSeqTrim 'Rev:%B Reversed:%B Begin:%d End:%d " "Offset:%d Offend:%d Len:%d okay:%B\n", seq->Rev, seq->Reversed, seq->Begin, seq->End, seq->Offset, seq->Offend, seq->Seq->Len, okay); return; } ID ajSeqGetAccC TY public MO ajseq LB core XX DE Returns the sequence accession number. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const char* RD Accession number as a character string. RX // const char* ajSeqGetAccC(const AjPSeq seq) { return MAJSTRGETPTR(seq->Acc); } ID ajSeqGetAccS TY public MO ajseq LB core XX DE Returns the sequence accession number. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const AjPStr RD Accession number as a string. RX // const AjPStr ajSeqGetAccS(const AjPSeq seq) { if(seq->Acc) return seq->Acc; return ajStrConstEmpty(); } ID ajSeqGetBegin TY public MO ajseq LB core XX DE Returns the sequence start position within the current stored sequence, DE or 1 if no start has been set. DE DE To return the position within the original sequence, which may be different DE if the sequence has been trimmed, use ajSeqGetBeginTrue XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT ajuint RD Start position. RX // ajuint ajSeqGetBegin(const AjPSeq seq) { ajint i; ajint j; if(!seq->Begin) return 1; i = seq->Begin; if(seq->Begin > 0) i = seq->Begin - 1; j = 1 + ajCvtSposToPosStart(ajSeqGetLen(seq), 0, i); return j; } ID ajSeqGetBeginTrue TY public MO ajseq LB core XX DE Returns the sequence start position in the original sequence, DE which may have been trimmed. DE DE To return the position within the current stored sequence, DE which may be different if the sequence has been trimmed, use ajSeqBegin XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT ajuint RD Start position. RX // ajuint ajSeqGetBeginTrue(const AjPSeq seq) { if(!seq->Begin) return ajSeqCalcTruepos(seq, 1); return ajSeqCalcTruepos(seq, seq->Begin); } ID ajSeqGetDbC TY public MO ajseq LB core XX DE Returns the sequence database name. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const char* RD Database name as a character string. RX // const char* ajSeqGetDbC(const AjPSeq seq) { return MAJSTRGETPTR(seq->Db); } ID ajSeqGetDbS TY public MO ajseq LB core XX DE Returns the sequence database name. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const AjPStr RD Database name as a string. RX // const AjPStr ajSeqGetDbS(const AjPSeq seq) { if(seq->Db) return seq->Db; return ajStrConstEmpty(); } ID ajSeqGetDescC TY public MO ajseq LB core XX DE Returns the sequence description. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const char* RD Description as a character string. RX // const char* ajSeqGetDescC(const AjPSeq seq) { return MAJSTRGETPTR(seq->Desc); } ID ajSeqGetDescS TY public MO ajseq LB core XX DE Returns the sequence description. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const AjPStr RD Description as a string. RX // const AjPStr ajSeqGetDescS(const AjPSeq seq) { if(seq->Desc) return seq->Desc; return ajStrConstEmpty(); } ID ajSeqGetEnd TY public MO ajseq LB core XX DE Returns the sequence end position, or the sequence length if no end DE has been set. XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT ajuint RD End position. RX // ajuint ajSeqGetEnd(const AjPSeq seq) { ajint i; if(!seq->End) return (ajSeqGetLen(seq)); i = seq->End; if(seq->End > 0) i--; i = 1 +ajCvtSposToPosStart(ajSeqGetLen(seq), ajSeqGetBegin(seq)-1, i); return i; } ID ajSeqGetEndTrue TY public MO ajseq LB core XX DE Returns the sequence end position, or the sequence length if no end DE has been set. XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT ajuint RD End position. RX // ajuint ajSeqGetEndTrue(const AjPSeq seq) { if(!seq->End) { if(ajSeqIsReversed(seq)) return seq->Offend + ajSeqGetLen(seq); else return seq->Offset + ajSeqGetLen(seq); } return ajSeqCalcTrueposMin(seq, ajSeqGetBeginTrue(seq), seq->End); } ID ajSeqGetEntryC TY public MO ajseq LB core XX DE Returns the sequence full text entry. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const char* RD Entry as a character string. RX // const char* ajSeqGetEntryC(const AjPSeq seq) { return MAJSTRGETPTR(seq->TextPtr); } ID ajSeqGetEntryS TY public MO ajseq LB core XX DE Returns the sequence full text entry. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const AjPStr RD Entry as a string. RX // const AjPStr ajSeqGetEntryS(const AjPSeq seq) { if(seq->TextPtr) return seq->TextPtr; return ajStrConstEmpty(); } ID ajSeqGetFeat TY public MO ajseq LB core XX DE Returns the sequence feature table. DE Because this is a pointer to the real internal table DE the caller must take care not to change it in any way, DE or to delete it. DE DE If the table is to be changed or deleted then it must first DE be copied with ajSeqCopyFeat XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const AjPFeattable RD feature table (if any) RX // const AjPFeattable ajSeqGetFeat(const AjPSeq seq) { return seq->Fttable; } ID ajSeqGetFeatCopy TY public MO ajseq LB core XX DE Returns a copy of the sequence feature table. DE Because this is a copy of all the data, the caller is responsible DE for deleting it after use. DE DE If the table is not to be changed or deleted then ajSeqGetFeat DE can return a copy of the internal pointer. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT AjPFeattable RD feature table (if any) RX // AjPFeattable ajSeqGetFeatCopy(const AjPSeq seq) { return ajFeattableNewFtable(seq->Fttable); } ID ajSeqGetGiC TY public MO ajseq LB core XX DE Returns the GI version number. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const char* RD SeqVersion number as a character string. RX // const char* ajSeqGetGiC(const AjPSeq seq) { return MAJSTRGETPTR(seq->Gi); } ID ajSeqGetGiS TY public MO ajseq LB core XX DE Returns the GI version number. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const AjPStr RD SeqVersion number as a string. RX // const AjPStr ajSeqGetGiS(const AjPSeq seq) { if(seq->Gi) return seq->Gi; return ajStrConstEmpty(); } ID ajSeqGetLen TY public MO ajseq LB core XX DE Returns the sequence length. XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT ajuint RD Sequence length. RX // ajuint ajSeqGetLen(const AjPSeq seq) { return ajStrGetLen(seq->Seq); } ID ajSeqGetLenTrimmed TY public MO ajseq LB core XX DE Returns the sequence length after trimming begin and end positions XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT ajuint RD Sequence length. RX // ajuint ajSeqGetLenTrimmed(const AjPSeq seq) { return ajSeqGetEnd(seq) - ajSeqGetBegin(seq) + 1 ; } ID ajSeqGetLenTrue TY public MO ajseq LB core XX DE Returns the length of the original sequence, including any gap characters. XX PN [1] PA r seq const AjPSeq PD Target sequence. PX RT ajuint RD string position between 1 and length. RX // ajuint ajSeqGetLenTrue(const AjPSeq seq) { return (ajStrGetLen(seq->Seq) + seq->Offset + seq->Offend); } ID ajSeqGetLenUngapped TY public MO ajseq LB core XX DE Returns the sequence length excluding all gap characters. XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT ajuint RD Sequence length. RX // ajuint ajSeqGetLenUngapped(const AjPSeq seq) { ajint ret = 0; const char *gapchars = "-.~"; const char* cp; size_t i; cp = ajStrGetPtr(seq->Seq); i = strspn(cp, gapchars); cp += i; while (*cp) { i = strcspn(cp, gapchars); ret += i; cp += i; cp += strspn(cp, gapchars); } return ret; } ID ajSeqGetNameC TY public MO ajseq LB core XX DE Returns the sequence name. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const char* RD Name as a character string. RX // const char* ajSeqGetNameC(const AjPSeq seq) { if(!seq) return ""; return MAJSTRGETPTR(seq->Name); } ID ajSeqGetNameS TY public MO ajseq LB core XX DE Returns the sequence name. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const AjPStr RD Name as a string. RX // const AjPStr ajSeqGetNameS(const AjPSeq seq) { if(!seq) return ajStrConstEmpty(); if(seq->Name) return seq->Name; return ajStrConstEmpty(); } ID ajSeqGetOffend TY public MO ajseq LB core XX DE Returns the sequence offend value. DE This is the number of positions removed from the original end. XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT ajuint RD Sequence offend. RX // ajuint ajSeqGetOffend(const AjPSeq seq) { return seq->Offend; } ID ajSeqGetOffset TY public MO ajseq LB core XX DE Returns the sequence offset from -sbegin originally. XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT ajuint RD Sequence offset. RX // ajuint ajSeqGetOffset(const AjPSeq seq) { return seq->Offset; } ID ajSeqGetQual TY public MO ajseq LB core XX DE Returns the base quality scores as an array. DE Because this is a pointer to the real internal array DE the caller must take care not to change the array in any way. XX PN [1] PA r seq const AjPSeq PD Sequence. PX RT const float* RD Base quality scores. RX // const float* ajSeqGetQual(const AjPSeq seq) { if(!seq) return NULL; return seq->Accuracy; } ID ajSeqGetRange TY public MO ajseq LB core XX DE Returns the sequence range for a sequence. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX PN [2] PA w begin ajint* PD Sequence range begin PX PN [3] PA w end ajint* PD Sequence range end PX RT ajuint RD Sequence range length RX // ajuint ajSeqGetRange(const AjPSeq seq, ajint* begin, ajint* end) { ajint jbegin; ajint jend; jbegin = seq->Begin; if(seq->Begin > 0) jbegin--; jend = seq->End; if(seq->End > 0) jend--; ajDebug("ajSeqGetRange '%S'\n", seq->Name); *begin = ajCvtSposToPos(ajSeqGetLen(seq), jbegin); /* string position */ if(seq->End) *end = 1 + ajCvtSposToPosStart(ajSeqGetLen(seq), *begin, jend); else *end = 1 + ajCvtSposToPosStart(ajSeqGetLen(seq), *begin, ajSeqGetLen(seq)); (*begin)++; /* sequence positions are 1..end */ return (*end - *begin + 1); } ID ajSeqGetRev TY public MO ajseq LB core XX DE Returns the sequence direction. DE DE See ajSeqReversed for whether it has already been reverse-complemented XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT AjBool RD Sequence Direction. RX // AjBool ajSeqGetRev(const AjPSeq seq) { return seq->Rev; } ID ajSeqGetSeqC TY public MO ajseq LB core XX DE Returns the sequence in a sequence object as a character string. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence. PX RT const char* RD Sequence as a character string. RX // const char* ajSeqGetSeqC(const AjPSeq seq) { if(!seq) return ""; return MAJSTRGETPTR(seq->Seq); } ID ajSeqGetSeqS TY public MO ajseq LB core XX DE Returns the sequence in a sequence object as a string. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence. PX RT const AjPStr RD Sequence as a string. RX // const AjPStr ajSeqGetSeqS(const AjPSeq seq) { if(!seq) return NULL; if(seq->Seq) return seq->Seq; return ajStrConstEmpty(); } ID ajSeqGetSeqCopyC TY public MO ajseq LB core XX DE Returns a sequence as a C character string. This is a copy of the string DE so the caller can do anything with it. DE It must be copied back DE to a sequence (e.g. with ajSeqReplace) before output. XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT char* RD Sequence as a null terminated character string. RX // char* ajSeqGetSeqCopyC(const AjPSeq seq) { return ajCharNewS(seq->Seq); } ID ajSeqGetSeqCopyS TY public MO ajseq LB core XX DE Returns the sequence in a sequence object as a string. DE Because this is a copy of the internal string DE the caller may change the string. It must be copied back DE to a sequence (e.g. with ajSeqReplace) before output. XX PN [1] PA r seq const AjPSeq PD Sequence. PX RT AjPStr RD Sequence as a string. RX // AjPStr ajSeqGetSeqCopyS(const AjPSeq seq) { static AjPStr str; str = ajStrNewS(seq->Seq); return str; } ID ajSeqGetSvC TY public MO ajseq LB core XX DE Returns the sequence version number. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const char* RD SeqVersion number as a character string. RX // const char* ajSeqGetSvC(const AjPSeq seq) { return MAJSTRGETPTR(seq->Sv); } ID ajSeqGetSvS TY public MO ajseq LB core XX DE Returns the sequence version number. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const AjPStr RD SeqVersion number as a string. RX // const AjPStr ajSeqGetSvS(const AjPSeq seq) { if(seq->Sv) return seq->Sv; return ajStrConstEmpty(); } ID ajSeqGetTaxC TY public MO ajseq LB core XX DE Returns the sequence primary taxon (species). DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const char* RD Description as a character string. RX // const char* ajSeqGetTaxC(const AjPSeq seq) { return MAJSTRGETPTR(seq->Tax); } ID ajSeqGetTaxS TY public MO ajseq LB core XX DE Returns the sequence primary taxon (species). DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const AjPStr RD Description as a string. RX // const AjPStr ajSeqGetTaxS(const AjPSeq seq) { if(seq->Tax) return seq->Tax; return ajStrConstEmpty(); } ID ajSeqGetTaxid TY public MO ajseq LB core XX DE Returns the numeric identifier of the sequence primary taxon (species). DE This is an identifier from the NCBI taxonomy XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT ajuint RD Taxon ID. RX // ajuint ajSeqGetTaxid(const AjPSeq seq) { ajuint ret = 0; ajuint i = 0; if(ajStrGetLen(seq->Taxid)) { ajStrToUint(seq->Taxid, &ret); return ret; } while(seqTaxon[i].Name) { if(ajStrPrefixCaseC(seq->Tax, seqTaxon[i].Name)) { return seqTaxon[i].Taxid; } i++; } return 0; } ID ajSeqGetUsaC TY public MO ajseq LB core XX DE Returns the original USA of a sequence. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const char* RD USA as a character string. RX // const char* ajSeqGetUsaC(const AjPSeq seq) { return MAJSTRGETPTR(ajSeqGetUsaS(seq)); } ID ajSeqGetUsaS TY public MO ajseq LB core XX DE Returns the original USA of a sequence. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeq PD Sequence object. PX RT const AjPStr RD USA as a string. RX // const AjPStr ajSeqGetUsaS(const AjPSeq seq) { ajDebug("ajSeqGetUsa '%S'\n", seq->Usa); if(ajStrGetLen(seq->Usa)) return seq->Usa; seqMakeUsa(seq, &seqTempUsa); return seqTempUsa; } ID ajSeqGetXrefs TY public MO ajseq LB core XX DE Returns the list of cross-references derived from the sequence XX PN [1] PA r seq const AjPSeq PD Sequence object. PX PN [2] PA u list AjPList PD List of cross-reference objects PX RT ajuint RD NUmber of cross-references returned RX // ajuint ajSeqGetXrefs(const AjPSeq seq, AjPList list) { ajDebug("ajSeqGetXrefs '%S'\n", seq->Usa); ajSeqxreflistClone(seq->Xreflist, list); return (ajuint) ajListGetLength(list); } ID ajSeqIsCircular TY public MO ajseq LB core XX DE Returns true if a sequence is circular XX PN [1] PA r seq const AjPSeq PD Sequence PX RT AjBool RD ajTrue for a circular sequence RX // AjBool ajSeqIsCircular(const AjPSeq seq) { return seq->Circular; } ID ajSeqIsNuc TY public MO ajseq LB core XX DE Tests whether a sequence is nucleotide. XX PN [1] PA r seq const AjPSeq PD Sequence PX RT AjBool RD ajTrue for a nucleotide sequence. RX // AjBool ajSeqIsNuc(const AjPSeq seq) { ajDebug("ajSeqIsNuc Type '%S'\n", seq->Type); if(ajStrMatchC(seq->Type, "N")) return ajTrue; if(ajStrMatchC(seq->Type, "P")) return ajFalse; if(ajSeqTypeGapnucS(seq->Seq)) /* returns char 0 on success */ { ajDebug ("ajSeqIsNuc failed\n", seq->Type); return ajFalse; } return ajTrue; } ID ajSeqIsProt TY public MO ajseq LB core XX DE Tests whether a sequence is protein. XX PN [1] PA r seq const AjPSeq PD Sequence PX RT AjBool RD ajTrue for a protein sequence. RX // AjBool ajSeqIsProt(const AjPSeq seq) { ajDebug("ajSeqIsProt Type '%S'\n", seq->Type); if(ajStrMatchC(seq->Type, "P")) return ajTrue; if(ajStrMatchC(seq->Type, "N")) return ajFalse; if(ajSeqTypeAnyprotS(seq->Seq)) /* returns char 0 on success */ { ajDebug ("ajSeqIsProt failed\n", seq->Type); return ajFalse; } return ajTrue; } ID ajSeqIsReversed TY public MO ajseq LB core XX DE Returns whether the sequence has been reversed XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT AjBool RD Sequence Direction. RX // AjBool ajSeqIsReversed(const AjPSeq seq) { return seq->Reversed; } ID ajSeqIsReversedTrue TY public MO ajseq LB core XX DE Returns ajTrue if the sequence is reversed relative to the original sequence DE DE If the sequence has already been reversed, or is set to be reversed, DE the result will be true. XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT AjBool RD ajTrue if sequence is set to be reversed RX // AjBool ajSeqIsReversedTrue(const AjPSeq seq) { if (seq->Reversed) { if (seq->Rev) return ajFalse; else return ajTrue; } return seq->Rev; } ID ajSeqIsSpeciesBsubtilis TY public MO ajseq LB core XX DE Tests whether a sequence is annotated as B. subtilis XX PN [1] PA r seq const AjPSeq PD Sequence PX RT AjBool RD ajTrue for a nucleotide sequence. RX // AjBool ajSeqIsSpeciesBsubtilis(const AjPSeq seq) { return ajSeqIsTaxon(seq, 1432); } ID ajSeqIsSpeciesHuman TY public MO ajseq LB core XX DE Tests whether a sequence is annotated as human XX PN [1] PA r seq const AjPSeq PD Sequence PX RT AjBool RD ajTrue for a nucleotide sequence. RX // AjBool ajSeqIsSpeciesHuman(const AjPSeq seq) { return ajSeqIsTaxon(seq, 9606); } ID ajSeqIsSpeciesMouse TY public MO ajseq LB core XX DE Tests whether a sequence is annotated as mouse XX PN [1] PA r seq const AjPSeq PD Sequence PX RT AjBool RD ajTrue for a nucleotide sequence. RX // AjBool ajSeqIsSpeciesMouse(const AjPSeq seq) { return ajSeqIsTaxon(seq, 10090); } ID ajSeqIsSpeciesYeast TY public MO ajseq LB core XX DE Tests whether a sequence is annotated as yeast (S. cerevisiae) XX PN [1] PA r seq const AjPSeq PD Sequence PX RT AjBool RD ajTrue for a nucleotide sequence. RX // AjBool ajSeqIsSpeciesYeast(const AjPSeq seq) { return ajSeqIsTaxon(seq, 4932); } ID ajSeqIsTaxon TY public MO ajseq LB core XX DE Tests whether a sequence is annotated as from a specific taxon ID. XX PN [1] PA r seq const AjPSeq PD Sequence PX PN [2] PA r taxid ajuint PD NCBI Taxonomy ID PX RT AjBool RD ajTrue for a nucleotide sequence. RX // AjBool ajSeqIsTaxon(const AjPSeq seq, ajuint taxid) { ajuint itax; itax = ajSeqGetTaxid(seq); if(itax != taxid) return ajFalse; return ajTrue; } ID ajSeqIsTrimmed TY public MO ajseq LB core XX DE Returns ajTrue if the sequence is already trimmed XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT AjBool RD ajTrue if sequence is set to be reversed RX // AjBool ajSeqIsTrimmed(const AjPSeq seq) { return seq->Trimmed; } ID ajSeqConvertNum TY public MO ajseq LB core XX DE Converts a string of sequence characters to numbers using DE a conversion table. XX PN [1] PA r seq const AjPSeq PD Sequence as a string PX PN [2] PA r cvt const AjPSeqCvt PD Conversion table. PX PN [3] PA w Pnumseq AjPStr* PD Output numeric version of the sequence. PX RT AjBool RD ajTrue on success. RX // AjBool ajSeqConvertNum(const AjPSeq seq, const AjPSeqCvt cvt, AjPStr* Pnumseq) { const char *cp; char *ncp; cp = ajStrGetPtr(seq->Seq); ajStrAssignS(Pnumseq, seq->Seq); ncp = ajStrGetuniquePtr(Pnumseq); while(*cp) { *ncp = cvt->table[(ajint)*cp]; cp++; ncp++; } return ajTrue; } ID ajSeqCalcCheckgcg TY public MO ajseq LB core XX DE Calculates a GCG checksum for a sequence. XX PN [1] PA r seq const AjPSeq PD Sequence. PX RT ajint RD GCG checksum. RX // ajint ajSeqCalcCheckgcg(const AjPSeq seq) { register ajlong i; register ajlong check = 0; register ajlong count = 0; const char *cp; ajint ilen; cp = ajStrGetPtr(seq->Seq); ilen = ajStrGetLen(seq->Seq); for(i = 0; i < ilen; i++) { count++; check += count * toupper((ajint) cp[i]); if(count == 57) count = 0; } check %= 10000; return (ajint) check; } ID ajSeqCalcCount TY public MO ajseq LB core XX DE Counts the numbers of A, C, G and T in a nucleotide sequence. XX PN [1] PA r seq const AjPSeq PD Sequence object PX PN [2] PA w b ajint* PD integer array, minimum size 5, to hold the results. PX RT void RD RX // void ajSeqCalcCount(const AjPSeq seq, ajint* b) { const char* cp; ajDebug("ajSeqCount %d bases\n", ajSeqGetLen(seq)); b[0] = b[1] = b[2] = b[3] = b[4] = 0; cp = ajStrGetPtr(seq->Seq); while(*cp) { switch (*cp) { case 'A': case 'a': b[0]++; break; case 'C': case 'c': b[1]++; break; case 'G': case 'g': b[2]++; break; case 'T': case 't': case 'U': case 'u': b[3]++; break; default: break; } cp++; } b[4] = ajSeqGetLen(seq) - b[0] - b[1] - b[2] - b[3]; return; } ID ajSeqCalcMolwt TY public MO ajseq LB core XX DE Calculates the molecular weight of a protein sequence. XX PN [1] PA r seq const AjPSeq PD Sequence PX RT float RD Molecular weight. RX // float ajSeqCalcMolwt(const AjPSeq seq) { return ajSeqstrCalcMolwt(seq->Seq); } ID ajSeqCalcTruepos TY public MO ajseq LB core XX DE Converts a string position into a true position. If ipos is negative, DE it is counted from the end of the string rather than the beginning. DE DE For strings, the result can go off the end to the terminating NULL. DE For sequences the maximum is the last base XX PN [1] PA r seq const AjPSeq PD Target sequence. PX PN [2] PA r ipos ajint PD Position. PX RT ajint RD string position between 1 and length. RX // ajint ajSeqCalcTruepos(const AjPSeq seq, ajint ipos) { ajint jpos; jpos = ipos; if (ipos > 0) jpos = ipos - 1; if(ajSeqGetRev(seq)) return 1 + seq->Offend + ajCvtSposToPosStart(ajSeqGetLen(seq), 0, jpos); return 1 + seq->Offset + ajCvtSposToPosStart(ajSeqGetLen(seq), 0, jpos); } ID ajSeqCalcTrueposMin TY public MO ajseq LB core XX DE Converts a string position into a true position. If ipos is negative, DE it is counted from the end of the string rather than the beginning. DE DE imin is a minimum relative position, also counted from the end DE if negative. Usually this is the start position when the end of a range DE is being tested. XX PN [1] PA r seq const AjPSeq PD Target sequence. PX PN [2] PA r imin ajint PD Start position. PX PN [3] PA r ipos ajint PD Position. PX RT ajint RD string position between 1 and length. RX // ajint ajSeqCalcTrueposMin(const AjPSeq seq, ajint imin, ajint ipos) { ajint jmin; ajint jpos; jmin = imin; if (imin > 0) jmin--; jpos = ipos; if(ipos > 0) jpos--; if(ajSeqGetRev(seq)) return 1 + seq->Offend + ajCvtSposToPosStart(ajSeqGetLen(seq), jmin, jpos); else return 1 + seq->Offset + ajCvtSposToPosStart(ajSeqGetLen(seq), jmin, jpos); } ID ajSeqCountGaps TY public MO ajseq LB core XX DE Returns the number of gaps in a sequence (counting any possible DE gap character XX PN [1] PA r seq const AjPSeq PD Sequence object PX RT ajuint RD Number of gaps RX // ajuint ajSeqCountGaps(const AjPSeq seq) { ajuint ret = 0; static char testchars[] = "-~.? "; /* all known gap characters */ const char *testgap; testgap = testchars; while(*testgap) { ret += (ajuint) ajStrCalcCountK(seq->Seq, *testgap); testgap++; } return ret; } ID ajSeqExit TY public MO ajseq LB core XX DE Cleans up sequence processing internal memory XX RT void RD RX // void ajSeqExit(void) { ajSeqReadExit(); ajSeqoutExit(); ajSeqTypeExit(); ajStrDel(&seqVersionAccnum); ajStrDel(&seqTempUsa); ajStrDel(&seqMoleculeDef); ajStrDel(&seqDivisionDef); ajStrDel(&seqClassDef); ajTableDel(&seqTableMol); ajTableDel(&seqTableMolEmbl); ajTableDel(&seqTableMolDdbj); ajTableDel(&seqTableMolGb); ajTableDel(&seqTableDiv); ajTableDel(&seqTableDivEmbl); ajTableDel(&seqTableDivDdbj); ajTableDel(&seqTableDivGb); ajTableDel(&seqTableCls); ajTableDel(&seqTableClsEmbl); ajTableDel(&seqTableClsDdbj); ajTableDel(&seqTableClsGb); return; } ID ajSeqTrace TY public MO ajseq LB core XX DE Debug calls to trace the data in a sequence object. XX PN [1] PA r seq const AjPSeq PD Sequence. PX RT void RD RX // void ajSeqTrace(const AjPSeq seq) { AjIList it; AjPStr cur; ajint i; ajDebug("Sequence trace\n"); ajDebug( "==============\n\n"); ajDebug( " Name: '%S'\n", seq->Name); if(ajStrGetLen(seq->Acc)) ajDebug( " Accession: '%S'\n", seq->Acc); if(ajListGetLength(seq->Acclist)) { ajDebug( " Acclist: (%Lu) ", ajListGetLength(seq->Acclist)); it = ajListIterNewread(seq->Acclist); while((cur = (AjPStr) ajListIterGet(it))) ajDebug(" %S", cur); ajListIterDel(&it); ajDebug(" \n"); } if(ajStrGetLen(seq->Sv)) ajDebug( " SeqVersion: '%S'\n", seq->Sv); if(ajStrGetLen(seq->Gi)) ajDebug( " GenInfo Id: '%S'\n", seq->Gi); if(ajStrGetLen(seq->Type)) ajDebug( " Type: '%S' (%d)\n", seq->Type, seq->EType); if(ajStrGetLen(seq->Desc)) ajDebug( " Description: '%S'\n", seq->Desc); if(ajStrGetLen(seq->Tax)) ajDebug( " Taxonomy: '%S'\n", seq->Tax); if(ajStrGetLen(seq->Taxid)) ajDebug( " Taxid: '%S'\n", seq->Taxid); if(ajStrGetLen(seq->Organelle)) ajDebug( " Organelle: '%S'\n", seq->Organelle); if(ajListGetLength(seq->Taxlist)) { ajDebug( " Taxlist: (%Lu)", ajListGetLength(seq->Taxlist)); it = ajListIterNewread(seq->Taxlist); while((cur = (AjPStr) ajListIterGet(it))) ajDebug(" '%S'", cur); ajListIterDel(&it); ajDebug("\n"); } if(ajListGetLength(seq->Keylist)) { ajDebug( " Keywordlist: (%Lu)", ajListGetLength(seq->Keylist)); it = ajListIterNewread(seq->Keylist); while((cur = (AjPStr) ajListIterGet(it))) ajDebug(" '%S'", cur); ajListIterDel(&it); ajDebug("\n"); } if(ajSeqGetLen(seq)) ajDebug( " Length: %d\n", ajSeqGetLen(seq)); i = ajSeqCountGaps(seq); if(i) ajDebug( " Gap count: %d\n", i); if(seq->Rev) ajDebug( " Rev: %B\n", seq->Rev); if(seq->Reversed) ajDebug( "Reversed: %B\n", seq->Reversed); if(seq->Begin) ajDebug( " Begin: %d\n", ajSeqGetBegin(seq)); if(seq->End) ajDebug( " End: %d\n", ajSeqGetEnd(seq)); if(seq->Offset) ajDebug( " Offset: %d\n", seq->Offset); if(seq->Offend) ajDebug( " Offend: %d\n", seq->Offend); if(ajStrGetRes(seq->Seq)) ajDebug( " Reserved: %d\n", ajStrGetRes(seq->Seq)); if(ajStrGetLen(seq->Db)) ajDebug( " Database: '%S'\n", seq->Db); if(ajStrGetLen(seq->Full)) ajDebug( " Full name: '%S'\n", seq->Full); if(ajStrGetLen(seq->Usa)) ajDebug( " Usa: '%S'\n", seq->Usa); if(ajStrGetLen(seq->Ufo)) ajDebug( " Ufo: '%S'\n", seq->Ufo); if(seq->Fttable) ajDebug( " Fttable: exists\n"); if(ajStrGetLen(seq->Formatstr)) ajDebug( " Input format: '%S' (%d)\n", seq->Formatstr, seq->Format); if(ajStrGetLen(seq->Filename)) ajDebug( " Filename: '%S'\n", seq->Filename); if(ajStrGetLen(seq->Entryname)) ajDebug( " Entryname: '%S'\n", seq->Entryname); if(!E_FPZERO(seq->Weight,U_FEPS)) ajDebug( " Weight: %.3f\n", seq->Weight); if(ajStrGetLen(seq->Doc)) ajDebug( " Documentation:...\n%S\n", seq->Doc); ajDebug( "Sequence:...\n%S\n", seq->Seq); ajDebug( "\n"); return; } ID ajSeqTraceTitle TY public MO ajseq LB core XX DE Reports an AjPSeq object to debug output XX PN [1] PA r seq const AjPSeq PD alignment object PX PN [2] PA r title const char* PD Trace report title PX RT void RD RX // void ajSeqTraceTitle(const AjPSeq seq, const char* title) { ajDebug("\n%s\n",title); ajSeqTrace(seq); return; } ID ajSeqallNew TY public MO ajseq LB core XX DE Creates a new sequence stream object to hold one sequence at a time. XX RT AjPSeqall RD New sequence stream object. RX // AjPSeqall ajSeqallNew(void) { AjPSeqall pthis; AJNEW0(pthis); pthis->Seq = ajSeqNew(); pthis->Seqin = ajSeqinNew(); pthis->Count = 0; pthis->Totseqs = 0; pthis->Totlength = 0; return pthis; } ID ajSeqallDel TY public MO ajseq LB core XX DE Destructor for sequence stream objects XX PN [1] PA d Pseq AjPSeqall* PD Sequence stream object reference PX RT void RD RX // void ajSeqallDel(AjPSeqall *Pseq) { if(!*Pseq) return; if(!(*Pseq)->Returned) ajSeqDel(&(*Pseq)->Seq); ajDebug("ajSeqallDel\n"); ajSeqinDel(&(*Pseq)->Seqin); AJFREE(*Pseq); return; } ID ajSeqallClear TY public MO ajseq LB core XX DE Resets all data internals for a sequence stream object when DE input has been read, but leaves statistics for reporting. XX PN [1] PA u seq AjPSeqall PD Sequence stream PX RT void RD RX // void ajSeqallClear(AjPSeqall seq) { if(!seq) return; ajSeqClear(seq->Seq); ajSeqinClear(seq->Seqin); seq->Count = 0; seq->Begin = 0; seq->End = 0; seq->Rev = ajFalse; seq->Returned = ajFalse; return; } ID ajSeqallClearStats TY public MO ajseq LB core XX DE Resets all data for a sequence stream object so that it can be reused, DE including statistics. XX PN [1] PA u seq AjPSeqall PD Sequence stream PX RT void RD RX // void ajSeqallClearStats(AjPSeqall seq) { ajSeqallClear(seq); seq->Totlength = 0; seq->Totseqs = 0; return; } ID ajSeqallSetRange TY public MO ajseq LB core XX DE Sets the start and end positions for a sequence stream. XX PN [1] PA u seq AjPSeqall PD Sequence stream object to be set. PX PN [2] PA r pos1 ajint PD Start position. Negative values are from the end. PX PN [3] PA r pos2 ajint PD End position. Negative values are from the end. PX RT void RD RX // void ajSeqallSetRange(AjPSeqall seq, ajint pos1, ajint pos2) { ajDebug("ajSeqallSetRange (len: %d %d, %d)\n", ajSeqGetLen(seq->Seq), pos1, pos2); if(pos1) seq->Begin = seq->Seq->Begin = pos1; if(pos2) seq->End = seq->Seq->End = pos2; ajDebug(" result: (len: %d %d, %d)\n", ajSeqGetLen(seq->Seq), seq->Begin, seq->End); return; } ID ajSeqallSetRangeRev TY public MO ajseq LB core XX DE Sets the start and end positions for a sequence stream, DE and set the sequences to be reversed. XX PN [1] PA u seq AjPSeqall PD Sequence stream object to be set. PX PN [2] PA r pos1 ajint PD Start position. Negative values are from the end. PX PN [3] PA r pos2 ajint PD End position. Negative values are from the end. PX RT void RD RX // void ajSeqallSetRangeRev(AjPSeqall seq, ajint pos1, ajint pos2) { ajDebug("ajSeqallSetRange (len: %d %d, %d)\n", ajSeqGetLen(seq->Seq), pos1, pos2); if(pos1) seq->Begin = seq->Seq->Begin = pos1; if(pos2) seq->End = seq->Seq->End = pos2; ajDebug(" result: (len: %d %d, %d)\n", ajSeqGetLen(seq->Seq), seq->Begin, seq->End); if(!seq->Rev) { seq->Rev = ajTrue; } return; } ID ajSeqallGetCount TY public MO ajseq LB core XX DE Returns the number of sequences read by a seqall object XX PN [1] PA r seq const AjPSeqall PD Seqall object pointer. PX RT ajlong RD Number of sequences read. RX // ajlong ajSeqallGetCount(const AjPSeqall seq) { if(!seq) return 0; if(!seq->Seqin) return 0; return seq->Totseqs; } ID ajSeqallGetFilename TY public MO ajseq LB core XX DE Returns the filename of a seqall object. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqall PD Seqall object pointer. PX RT const AjPStr RD Name as a string. RX // const AjPStr ajSeqallGetFilename(const AjPSeqall seq) { if(!seq) return NULL; if(!seq->Seqin) return NULL; ajDebug("ajSeqallGetFilename '%S' usa: '%S'\n", seq->Seqin->Name, seq->Seqin->Input->Qry); if(ajStrGetLen(seq->Seqin->Input->Filename)) return seq->Seqin->Input->Filename; return NULL; } ID ajSeqallGetName TY public MO ajseq LB core XX DE Returns the sequence name of a sequence stream. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqall PD Sequence stream object. PX RT const AjPStr RD Name as a string. RX // const AjPStr ajSeqallGetName(const AjPSeqall seq) { ajDebug("ajSeqallGetName '%S'\n", seq->Seqin->Name); return seq->Seqin->Name; } ID ajSeqallGetTotlength TY public MO ajseq LB core XX DE Returns the total length of sequences read by a seqall object XX PN [1] PA r seq const AjPSeqall PD Seqall object pointer. PX RT ajlong RD Total length of sequences read. RX // ajlong ajSeqallGetTotlength(const AjPSeqall seq) { if(!seq) return 0; return seq->Totlength; } ID ajSeqallGetUsa TY public MO ajseq LB core XX DE Returns the sequence name of a sequence stream. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqall PD Sequence object. PX RT const AjPStr RD Name as a string. RX // const AjPStr ajSeqallGetUsa(const AjPSeqall seq) { ajDebug("ajSeqallGetUsa '%S'\n", seq->Seqin->Input->Qry); return seq->Seqin->Input->Qry; } ID ajSeqallGetseqBegin TY public MO ajseq LB core XX DE Returns the sequence stream start position, or 1 if no start has been set. XX PN [1] PA r seq const AjPSeqall PD Sequence stream object PX RT ajint RD Start position. RX // ajint ajSeqallGetseqBegin(const AjPSeqall seq) { ajint jbegin; if (seq->Begin) { jbegin = seq->Begin; if(jbegin > 0) jbegin--; return 1 + ajCvtSposToPosStart(ajSeqGetLen(seq->Seq), 0, jbegin); } if(seq->Seq->Begin) { jbegin = seq->Seq->Begin; if(jbegin > 0) jbegin--; return 1 + ajCvtSposToPosStart(ajSeqGetLen(seq->Seq), 0, jbegin); } return 1; } ID ajSeqallGetseqEnd TY public MO ajseq LB core XX DE Returns the sequence stream end position, or the sequence length if no end DE has been set. XX PN [1] PA r seq const AjPSeqall PD Sequence stream object PX RT ajint RD Start position. RX // ajint ajSeqallGetseqEnd(const AjPSeqall seq) { ajint jend; if (seq->End) { jend = seq->End; if(jend > 0) jend--; return 1 + ajCvtSposToPosStart(ajSeqGetLen(seq->Seq), ajSeqallGetseqBegin(seq)-1, jend); } if(seq->Seq->End) { jend = seq->Seq->End; if(jend > 0) jend--; return 1 + ajCvtSposToPosStart(ajSeqGetLen(seq->Seq), ajSeqallGetseqBegin(seq)-1, jend); } return ajSeqGetLen(seq->Seq); } ID ajSeqallGetseqLen TY public MO ajseq LB core XX DE Returns the length of a sequence stream, which is the length of the DE latest sequence read. XX PN [1] PA r seq const AjPSeqall PD Sequence stream object PX RT ajint RD sequence length. RX // ajint ajSeqallGetseqLen(const AjPSeqall seq) { return ajSeqGetLen(seq->Seq); } ID ajSeqallGetseqName TY public MO ajseq LB core XX DE Returns the sequence name of a sequence stream. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqall PD Sequence stream object. PX RT const AjPStr RD Name as a string. RX // const AjPStr ajSeqallGetseqName(const AjPSeqall seq) { if(!seq) return ajStrConstEmpty(); ajDebug("ajSeqallGetseqName '%S'\n", seq->Seq->Name); return ajSeqGetNameS(seq->Seq); } ID ajSeqallGetseqRange TY public MO ajseq LB core XX DE Returns the sequence range for a sequence stream XX PN [1] PA r seq const AjPSeqall PD Sequence stream object. PX PN [2] PA w begin ajint* PD Sequence range begin PX PN [3] PA w end ajint* PD Sequence range end PX RT ajint RD Sequence range length RX // ajint ajSeqallGetseqRange(const AjPSeqall seq, ajint* begin, ajint* end) { ajDebug("ajSeqallGetRange '%S'\n", seq->Seq->Name); return ajSeqGetRange(seq->Seq, begin, end); } ID ajSeqsetNew TY public MO ajseq LB core XX DE Creates a new sequence set object to hold all sequences in memory. XX RT AjPSeqset RD New sequence set object. RX // AjPSeqset ajSeqsetNew(void) { AjPSeqset pthis; AJNEW0(pthis); pthis->Size = 0; pthis->Len = 0; pthis->Begin = 0; pthis->End = 0; pthis->Totweight = 0.0; pthis->Name = ajStrNew(); pthis->Type = ajStrNew(); pthis->Full = ajStrNew(); pthis->Usa = ajStrNew(); pthis->Ufo = ajStrNew(); pthis->Formatstr = ajStrNew(); pthis->Filename = ajStrNew(); pthis->Seq = NULL; pthis->Seqweight = NULL; pthis->EType = 0; pthis->Format = 0; return pthis; } ID ajSeqsetDel TY public MO ajseq LB core XX DE Destructor for sequence set objects XX PN [1] PA d Pseq AjPSeqset* PD Sequence set object reference PX RT void RD RX // void ajSeqsetDel(AjPSeqset *Pseq) { ajuint i; AjPSeqset seq = NULL; if(!Pseq || !*Pseq) return; seq = *Pseq; ajDebug("ajSeqsetDel size: %d\n", seq->Size); ajStrDel(&seq->Type); ajStrDel(&seq->Formatstr); ajStrDel(&seq->Filename); ajStrDel(&seq->Full); ajStrDel(&seq->Name); ajStrDel(&seq->Usa); ajStrDel(&seq->Ufo); for(i=0; iSize; ++i) ajSeqDel(&seq->Seq[i]); AJFREE(seq->Seq); AJFREE(seq->Seqweight); AJFREE(*Pseq); return; } ID ajSeqsetDelarray TY public MO ajseq LB core XX DE Destructor for array of sequence set objects XX PN [1] PA d PPseq AjPSeqset** PD Sequence set object array PX RT void RD RX // void ajSeqsetDelarray(AjPSeqset **PPseq) { ajuint i = 0; if(!PPseq || !*PPseq) return; while((*PPseq)[i]) { ajSeqsetDel(&(*PPseq)[i]); i++; } ajDebug("ajSeqsetDelarray size: %d\n", i); AJFREE(*PPseq); return; } ID ajSeqsetGetFilename TY public MO ajseq LB core XX DE Returns the filename of a sequence set. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX RT const AjPStr RD Name as a string. RX // const AjPStr ajSeqsetGetFilename(const AjPSeqset seq) { ajDebug("ajSeqsetGetFilename '%S' usa: '%S'\n", seq->Name, seq->Usa); if(!seq) return NULL; if(ajStrGetLen(seq->Filename)) return seq->Filename; return NULL; } ID ajSeqsetGetFormat TY public MO ajseq LB core XX DE Returns the sequence format for a sequence set XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX RT const AjPStr RD Sequence format RX // const AjPStr ajSeqsetGetFormat(const AjPSeqset seq) { return seq->Formatstr; } ID ajSeqsetGetOffend TY public MO ajseq LB core XX DE Returns the sequence set offend value. DE This is the number of positions removed from the original end. XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX RT ajint RD Sequence offend. RX // ajint ajSeqsetGetOffend(const AjPSeqset seq) { return seq->Offend; } ID ajSeqsetGetOffset TY public MO ajseq LB core XX DE Returns the sequence set offset value. DE This is the number of positions removed from the original end. XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX RT ajint RD Sequence offset. RX // ajint ajSeqsetGetOffset(const AjPSeqset seq) { return seq->Offset; } ID ajSeqsetGetRange TY public MO ajseq LB core XX DE Returns the sequence range for a sequence set XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX PN [2] PA w begin ajint* PD Sequence range begin PX PN [3] PA w end ajint* PD Sequence range end PX RT ajint RD Sequence range length RX // ajint ajSeqsetGetRange(const AjPSeqset seq, ajint* begin, ajint* end) { ajint jbegin; ajint jend; jbegin = seq->Begin; if(jbegin > 0) jbegin--; jend = seq->End; if(jend > 0) jend--; ajDebug("ajSeqsetGetRange '%S' begin %d end %d len: %d\n", seq->Name, seq->Begin, seq->End, seq->Len); *begin = ajCvtSposToPosStart(seq->Len, 0, jbegin); if(seq->End) *end = 1 + ajCvtSposToPosStart(seq->Len, *begin, jend); else *end = 1 + ajCvtSposToPosStart(seq->Len, *begin, seq->Len); (*begin)++; return (*end - *begin + 1); } ID ajSeqsetGetTotlength TY public MO ajseq LB core XX DE Returns the total number of bases or residues in a sequence set, DE ignoring any gap characters in aligned sequences. XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX RT ajint RD Total ungapped length RX // ajint ajSeqsetGetTotlength(const AjPSeqset seq) { ajuint n; ajuint i; ajint ret = 0; n = seq->Size; for(i=0; i < n; i++) ret += ajSeqGetLenUngapped(seq->Seq[i]); return ret; } ID ajSeqsetGetUsa TY public MO ajseq LB core XX DE Returns the sequence name of a sequence set. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX RT const AjPStr RD Name as a string. RX // const AjPStr ajSeqsetGetUsa(const AjPSeqset seq) { ajDebug("ajSeqetGetUsa '%S'\n", seq->Usa); return seq->Usa; } ID ajSeqsetClear TY public MO ajseq LB core XX DE Reset a sequence set object XX PN [1] PA u seq AjPSeqset PD Sequence set object reference PX RT void RD RX // void ajSeqsetClear(AjPSeqset seq) { ajuint i; if(!seq) return; ajDebug("ajSeqsetDel size: %d\n", seq->Size); if(MAJSTRGETLEN(seq->Type)) ajStrSetClear(&seq->Type); if(MAJSTRGETLEN(seq->Formatstr)) ajStrSetClear(&seq->Formatstr); if(MAJSTRGETLEN(seq->Filename)) ajStrSetClear(&seq->Filename); if(MAJSTRGETLEN(seq->Full)) ajStrSetClear(&seq->Full); if(MAJSTRGETLEN(seq->Name)) ajStrSetClear(&seq->Name); if(MAJSTRGETLEN(seq->Usa)) ajStrSetClear(&seq->Usa); if(MAJSTRGETLEN(seq->Ufo)) ajStrSetClear(&seq->Ufo); for(i=0; iSize; ++i) ajSeqDel(&seq->Seq[i]); AJFREE(seq->Seq); AJFREE(seq->Seqweight); seq->Size = 0; seq->Begin = 0; seq->End = 0; seq->Len = 0; seq->Offset = 0; seq->Offend = 0; seq->Rev = ajTrue; seq->Trimmed = ajTrue; return; } ID ajSeqsetFill TY public MO ajseq LB core XX DE Fills a sequence set with gaps at the ends of any shorter sequences. XX PN [1] PA u seq AjPSeqset PD Sequence set object to be set. PX RT ajint RD Number of gaps inserted RX // ajint ajSeqsetFill(AjPSeqset seq) { ajuint i; ajuint ifix = 0; ajuint nfix = 0; ajuint ilen; ajDebug("ajSeqsetFill(len: %d)\n", seq->Len); for(i=0; i< seq->Size; i++) if(ajSeqGetLen(seq->Seq[i]) < seq->Len) { nfix++; ilen = seq->Len - ajSeqGetLen(seq->Seq[i]); if(ilen > ifix) ifix = ilen; ajStrAppendCountK(&seq->Seq[i]->Seq, '-', ilen); } ajDebug(" result: (len: %d added: %u number of seqs fixed: %u\n", seq->Len, ifix, nfix); return ifix; } ID ajSeqsetFmtLower TY public MO ajseq LB core XX DE Converts all sequences in a set to lower case. XX PN [1] PA u seq AjPSeqset PD Sequence set object PX RT void RD RX // void ajSeqsetFmtLower(AjPSeqset seq) { ajuint i; for(i=0; i < seq->Size; i++) ajSeqFmtLower(seq->Seq[i]); return; } ID ajSeqsetFmtUpper TY public MO ajseq LB core XX DE Converts all sequences in a set to upper case. XX PN [1] PA u seq AjPSeqset PD Sequence set object PX RT void RD RX // void ajSeqsetFmtUpper(AjPSeqset seq) { ajuint i; for(i=0; i < seq->Size; i++) ajSeqFmtUpper(seq->Seq[i]); return; } ID ajSeqsetReverse TY public MO ajseq LB core XX DE Reverse complements all sequences in a sequence set. XX PN [1] PA u seq AjPSeqset PD Sequence set object PX RT void RD RX // void ajSeqsetReverse(AjPSeqset seq) { ajuint i; ajint ibegin; ajint iend; ajDebug("ajSeqsetReverse len: %d Begin: %d End: %d\n", ajSeqsetGetLen(seq), seq->Begin, seq->End); ibegin = seq->Begin; iend = seq->End; if(ibegin) seq->End = -(ibegin); if(iend) seq->Begin = -(iend); for(i=0; i < seq->Size; i++) { seq->Seq[i]->Rev = ajTrue; ajSeqReverseDo(seq->Seq[i]); } ajDebug(" set result len: %d Begin: %d End: %d\n", ajSeqsetGetLen(seq), seq->Begin, seq->End); return; } ID ajSeqsetSortLen TY public MO ajseq LB core XX DE Sorts a sequence set by sequence length XX PN [1] PA u seq AjPSeqset PD Sequence set object PX RT void RD RX // void ajSeqsetSortLen(AjPSeqset seq) { ajuint i; ajuint j; ajuint k; ajuint * lengths; ajuint * seqnum; AjPSeq tmp; AJCNEW0(lengths,seq->Size); AJCNEW0(seqnum,seq->Size); for(i=0;iSize;i++) { lengths[i] = ajSeqGetLenUngapped(seq->Seq[i]); seqnum[i] = i; } ajSortUintIncI(lengths,seqnum,seq->Size); j = 0; for(i=0;iSize;i++) { tmp = seq->Seq[i]; for(k=i; seqnum[k] != i; k=seqnum[j], seqnum[j]=j) { j = k; seq->Seq[k] = seq->Seq[seqnum[k]]; } seq->Seq[k] = tmp; seqnum[k] = k; } AJFREE(lengths); AJFREE(seqnum); return; } ID ajSeqsetTrim TY public MO ajseq LB core XX DE Trims a sequence set to start and end positions XX PN [1] PA u seq AjPSeqset PD Sequence set object PX RT void RD RX // void ajSeqsetTrim(AjPSeqset seq) { ajuint i; ajint begin; ajint end; ajint jbegin; ajint jend; if(seq->Trimmed) { ajWarn("Sequence set '%S' already trimmed", ajSeqsetGetNameS(seq)); return; } ajDebug("ajSeqsetTrim len: %d begin: %d end: %d\n", seq->Len, seq->Begin, seq->End); for(i=0; i < seq->Size; i++) ajSeqTrim(seq->Seq[i]); jbegin = seq->Begin; if(jbegin > 0) jbegin--; jend = seq->End; if(jend > 0) jend--; begin = 1 + ajCvtSposToPosStart(seq->Len, 0, jbegin); end = 1 + ajCvtSposToPosStart(seq->Len, begin-1, jend); if(seq->End) { if(end < begin) return; seq->Offend = seq->Len - end; seq->End = 0; seq->Len = end; } if(seq->Begin) { seq->Offset = begin-1; seq->Begin = 0; seq->Len -= begin; } ajDebug("ajSeqsetTrim result len: %d begin: %d end: %d\n", seq->Len, seq->Begin, seq->End); return; } ID ajSeqsetSetRange TY public MO ajseq LB core XX DE Sets the start and end positions for a sequence set. XX PN [1] PA u seq AjPSeqset PD Sequence set object to be set. PX PN [2] PA r pos1 ajint PD Start position. Negative values are from the end. PX PN [3] PA r pos2 ajint PD End position. Negative values are from the end. PX RT void RD RX // void ajSeqsetSetRange(AjPSeqset seq, ajint pos1, ajint pos2) { ajuint i; ajDebug("ajSeqsetSetRange(len: %d %d, %d)\n", seq->Len, pos1, pos2); if(pos1) seq->Begin = pos1; if(pos2) seq->End = pos2; for(i=0; i< seq->Size; i++) { if(pos1) seq->Seq[i]->Begin = pos1; if(pos2) seq->Seq[i]->End = pos2; } ajDebug(" result: (len: %u %d, %d)\n", seq->Len, seq->Begin, seq->End); return; } ID ajSeqsetGetBegin TY public MO ajseq LB core XX DE Returns the sequence set start position, or 1 if no start has been set. XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX RT ajuint RD Start position. RX // ajuint ajSeqsetGetBegin(const AjPSeqset seq) { ajint jbegin; if(!seq->Begin) return 1; jbegin = seq->Begin; if(jbegin > 0) jbegin--; return 1 + ajCvtSposToPosStart(seq->Len, 0, jbegin); } ID ajSeqsetGetEnd TY public MO ajseq LB core XX DE Returns the sequence set end position, or the sequence length if no end DE has been set. XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX RT ajuint RD Start position. RX // ajuint ajSeqsetGetEnd(const AjPSeqset seq) { ajint jend; if(!seq->End) return (seq->Len); jend = seq->End; if(jend > 0) jend--; return 1 + ajCvtSposToPosStart(seq->Len, ajSeqsetGetBegin(seq)-1, jend); } ID ajSeqsetGetLen TY public MO ajseq LB core XX DE Returns the length of a sequence set, which is the maximum sequence DE length in the set. XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX RT ajuint RD sequence set length. RX // ajuint ajSeqsetGetLen(const AjPSeqset seq) { return seq->Len; } ID ajSeqsetGetNameC TY public MO ajseq LB core XX DE Returns the sequence name of a sequence set. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX RT const char* RD Name as a C character string. RX // const char* ajSeqsetGetNameC(const AjPSeqset seq) { ajDebug("ajSeqsetGetName '%S' usa: '%S'\n", seq->Name, seq->Usa); if(ajStrGetLen(seq->Name)) return MAJSTRGETPTR(seq->Name); return MAJSTRGETPTR(seq->Usa); } ID ajSeqsetGetNameS TY public MO ajseq LB core XX DE Returns the sequence name of a sequence set. DE Because this is a pointer to the real internal string DE the caller must take care not to change the character string in any way. DE If the string is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX RT const AjPStr RD Name as a string. RX // const AjPStr ajSeqsetGetNameS(const AjPSeqset seq) { if(!seq) return ajStrConstEmpty(); ajDebug("ajSeqsetGetNameS '%S' usa: '%S'\n", seq->Name, seq->Usa); if(ajStrGetLen(seq->Name)) return seq->Name; return seq->Usa; } ID ajSeqsetGetSeqarray TY public MO ajseq LB core XX DE Returns an array of sequences. DE Because this is a pointer to the real internal sequence DE the caller must take care not to change the data in any way. DE If the sequence is to be changed (case for example) then it must first DE be copied. DE DE The array is 1 larger than the sequence set, DE with the last element set to NULL. XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX RT AjPSeq* RD Sequence object. RX // AjPSeq* ajSeqsetGetSeqarray(const AjPSeqset seq) { AjPSeq* ret; ajuint i; ajDebug("ajSeqsetGetSeqArray '%S' %d\n", seq->Name, seq->Size); AJCNEW0(ret, (seq->Size+1)); for (i=0; iSize;i++) { ret[i] = ajSeqNewSeq(seq->Seq[i]); } return ret; } ID ajSeqsetGetSize TY public MO ajseq LB core XX DE Returns the number of sequences in a sequence set XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX RT ajuint RD sequence set size. RX // ajuint ajSeqsetGetSize(const AjPSeqset seq) { return seq->Size; } ID ajSeqsetGetTotweight TY public MO ajseq LB core XX DE Returns the weight of all sequences in a sequence set XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX RT float RD sequence weight as a float. RX // float ajSeqsetGetTotweight(const AjPSeqset seq) { ajuint i; float ret = 0.0; for(i=0; i < seq->Size; i++) ret += seq->Seq[i]->Weight; return ret; } ID ajSeqsetGetseqAccC TY public MO ajseq LB core XX DE Returns the accession number of a sequence in a sequence set XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX PN [2] PA r i ajuint PD Sequence index PX RT const char* RD accession number as a string. RX // const char* ajSeqsetGetseqAccC(const AjPSeqset seq, ajuint i) { if(i >= seq->Size) return NULL; return MAJSTRGETPTR(seq->Seq[i]->Acc); } ID ajSeqsetGetseqAccS TY public MO ajseq LB core XX DE Returns the accession number of a sequence in a sequence set XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX PN [2] PA r i ajuint PD Sequence index PX RT const AjPStr RD accession number as a string. RX // const AjPStr ajSeqsetGetseqAccS(const AjPSeqset seq, ajuint i) { if(i >= seq->Size) return NULL; if(seq->Seq[i]->Acc) return seq->Seq[i]->Acc; return ajStrConstEmpty(); } ID ajSeqsetGetseqNameC TY public MO ajseq LB core XX DE Returns the name of a sequence in a sequence set XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX PN [2] PA r i ajuint PD Sequence index PX RT const char* RD sequence name as a string. RX // const char* ajSeqsetGetseqNameC(const AjPSeqset seq, ajuint i) { if(!seq) return ""; if(i >= seq->Size) return ""; if(seq->Seq[i]->Name) return MAJSTRGETPTR(seq->Seq[i]->Name); return ""; } ID ajSeqsetGetseqNameS TY public MO ajseq LB core XX DE Returns the name of a sequence in a sequence set XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX PN [2] PA r i ajuint PD Sequence index PX RT const AjPStr RD sequence name as a string. RX // const AjPStr ajSeqsetGetseqNameS(const AjPSeqset seq, ajuint i) { if(!seq) return ajStrConstEmpty(); if(i >= seq->Size) return ajStrConstEmpty(); if(seq->Seq[i]->Name) return seq->Seq[i]->Name; return ajStrConstEmpty(); } ID ajSeqsetGetseqSeq TY public MO ajseq LB core XX DE Returns one sequence from a sequence set. DE Because this is a pointer to the real internal sequence DE the caller must take care not to change the data in any way. DE If the sequence is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX PN [2] PA r i ajuint PD Sequence index number in set PX RT const AjPSeq RD Sequence object. RX // const AjPSeq ajSeqsetGetseqSeq(const AjPSeqset seq, ajuint i) { if(i >= seq->Size) return NULL; return seq->Seq[i]; } ID ajSeqsetGetseqSeqC TY public MO ajseq LB core XX DE Returns one sequence from a sequence set. DE Because this is a pointer to the real internal sequence DE the caller must take care not to change the data in any way. DE If the sequence is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX PN [2] PA r i ajuint PD Sequence index number in set PX RT const char* RD Sequence as a C string. RX // const char* ajSeqsetGetseqSeqC(const AjPSeqset seq, ajuint i) { if(i >= seq->Size) return NULL; return MAJSTRGETPTR(seq->Seq[i]->Seq); } ID ajSeqsetGetseqSeqS TY public MO ajseq LB core XX DE Returns one sequence from a sequence set. DE Because this is a pointer to the real internal sequence DE the caller must take care not to change the data in any way. DE If the sequence is to be changed (case for example) then it must first DE be copied. XX PN [1] PA r seq const AjPSeqset PD Sequence set object. PX PN [2] PA r i ajuint PD Sequence index number in set PX RT const AjPStr RD Sequence object. RX // const AjPStr ajSeqsetGetseqSeqS(const AjPSeqset seq, ajuint i) { ajDebug("ajSeqsetGetseqSeq '%S' %d/%d\n", seq->Name, i, seq->Size); if(i >= seq->Size) return NULL; if(seq->Seq[i]->Seq) return seq->Seq[i]->Seq; return ajStrConstEmpty(); } ID ajSeqsetGetseqWeight TY public MO ajseq LB core XX DE Returns the weight of a sequence in a sequence set XX PN [1] PA r seq const AjPSeqset PD Sequence set object PX PN [2] PA r i ajuint PD Sequence index PX RT float RD sequence weight as a float. RX // float ajSeqsetGetseqWeight(const AjPSeqset seq, ajuint i) { if(i >= seq->Size) return 0.0; return seq->Seq[i]->Weight; } ID ajSeqsetIsDna TY public MO ajseq LB core XX DE Tests whether a sequence set is DNA. XX PN [1] PA r seq const AjPSeqset PD Sequence set PX RT AjBool RD ajTrue for a nucleotide sequence set. RX // AjBool ajSeqsetIsDna(const AjPSeqset seq) { AjPSeq myseq; if(ajStrMatchC(seq->Type, "P")) return ajFalse; myseq = seq->Seq[0]; if(ajSeqTypeGapdnaS(myseq->Seq)) return ajFalse; return ajTrue; } ID ajSeqsetIsNuc TY public MO ajseq LB core XX DE Tests whether a sequence set is nucleotide. XX PN [1] PA r seq const AjPSeqset PD Sequence set PX RT AjBool RD ajTrue for a nucleotide sequence set. RX // AjBool ajSeqsetIsNuc(const AjPSeqset seq) { AjPSeq myseq; if(ajStrMatchC(seq->Type, "N")) return ajTrue; myseq = seq->Seq[0]; if(ajSeqTypeGapnucS(myseq->Seq)) return ajFalse; return ajTrue; } ID ajSeqsetIsProt TY public MO ajseq LB core XX DE Tests whether a sequence set is protein. XX PN [1] PA r seq const AjPSeqset PD Sequence set PX RT AjBool RD ajTrue for a protein sequence set. RX // AjBool ajSeqsetIsProt(const AjPSeqset seq) { AjPSeq myseq; if(ajStrMatchC(seq->Type, "P")) return ajTrue; if(ajSeqsetIsNuc(seq)) return ajFalse; myseq = seq->Seq[0]; return ajSeqIsProt(myseq); } ID ajSeqsetIsRna TY public MO ajseq LB core XX DE Tests whether a sequence set is RNA. XX PN [1] PA r seq const AjPSeqset PD Sequence set PX RT AjBool RD ajTrue for a nucleotide sequence set. RX // AjBool ajSeqsetIsRna(const AjPSeqset seq) { AjPSeq myseq; if(ajStrMatchC(seq->Type, "P")) return ajFalse; myseq = seq->Seq[0]; if(ajSeqTypeGaprnaS(myseq->Seq)) return ajFalse; return ajTrue; } ID ajSeqstrConvertNum TY public MO ajseq LB core XX DE Converts a string of sequence characters to numbers using DE a conversion table. XX PN [1] PA r seq const AjPStr PD Sequence as a string PX PN [2] PA r cvt const AjPSeqCvt PD Conversion table. PX PN [3] PA w Pnumseq AjPStr* PD Output numeric version of the sequence. PX RT AjBool RD ajTrue on success. RX // AjBool ajSeqstrConvertNum(const AjPStr seq, const AjPSeqCvt cvt, AjPStr* Pnumseq) { const char *cp; char *ncp; cp = ajStrGetPtr(seq); ajStrAssignS(Pnumseq, seq); ncp = ajStrGetuniquePtr(Pnumseq); while(*cp) { *ncp = cvt->table[(ajint)*cp]; cp++; ncp++; } return ajTrue; } ID ajSeqcvtTrace TY public MO ajseq LB core XX DE Traces a conversion table with debug calls. XX PN [1] PA r cvt const AjPSeqCvt PD Conversion table. PX RT void RD RX // void ajSeqcvtTrace(const AjPSeqCvt cvt) { ajint i; ajDebug("Cvt table for '%S'\n\n", cvt->bases); ajDebug("index num ch\n"); ajDebug("----- --- --\n"); for(i=0; i < cvt->size; i++) if(cvt->table[i]) ajDebug("%5d %3d <%c>\n", i, cvt->table[i], ajSysCastItoc(i)); ajDebug("... all others are zero ...\n", cvt->bases); return; } ID ajSeqcvtNewC TY public MO ajseq LB core XX DE Generates a new conversion table in which the first character in the DE string provided is converted to 1, the second to 2, and so on. DE Upper and lower case characters are converted to the same numbers. DE All other characters are set to zero. XX PN [1] PA r bases const char* PD Allowed sequence characters. PX RT AjPSeqCvt RD Conversion table. RX // AjPSeqCvt ajSeqcvtNewC(const char* bases) { static AjPSeqCvt ret; ajint i; const char *cp; cp = bases; AJNEW0(ret); ret->len = strlen(bases); ret->size = CHAR_MAX - CHAR_MIN + 1; ret->table = AJCALLOC0(ret->size, sizeof(char)); ret->bases = ajStrNewC(bases); ret->missing = 0; i = 0; while(*cp) { i++; ret->table[toupper((ajint) *cp)] = ajSysCastItoc(i); ret->table[tolower((ajint) *cp)] = ajSysCastItoc(i); cp++; } return ret; } ID ajSeqcvtNewEndC TY public MO ajseq LB core XX DE Generates a new conversion table in which the first character in the DE string provided is converted to 0, the second to 1, and so on. DE Upper and lower case characters are converted to the same numbers. DE All other characters are converted to one more than the highest char. XX PN [1] PA r bases const char* PD Allowed sequence characters. PX RT AjPSeqCvt RD Conversion table. RX // AjPSeqCvt ajSeqcvtNewEndC(const char* bases) { static AjPSeqCvt ret; ajint i; ajint j; ajint imax; const char *cp; cp = bases; imax = strlen(bases); AJNEW0(ret); ret->len = imax; ret->size = CHAR_MAX - CHAR_MIN + 1; ret->table = AJCALLOC0(ret->size, sizeof(char)); ret->bases = ajStrNewC(bases); ret->missing = imax; for(j=0; j < ret->size; j++) ret->table[j] = ajSysCastItoc(imax); i = 0; while(*cp) { ret->table[toupper((ajint) *cp)] = ajSysCastItoc(i); ret->table[tolower((ajint) *cp)] = ajSysCastItoc(i); cp++; i++; } return ret; } ID ajSeqcvtNewNumberC TY public MO ajseq LB core XX DE Generates a new conversion table in which the characters are retained DE as upper case, numbers are set to -1 and all other characters DE are set to -2. XX PN [1] PA r bases const char* PD Allowed sequence characters. PX RT AjPSeqCvt RD Conversion table. RX // AjPSeqCvt ajSeqcvtNewNumberC(const char* bases) { static AjPSeqCvt ret; ajint i; ajint j; const char *cp; char c; cp = bases; AJNEW0(ret); ret->len = strlen(bases); ret->size = CHAR_MAX - CHAR_MIN + 1; ret->table = AJCALLOC0(ret->size, sizeof(char)); ret->bases = ajStrNewC(bases); ret->missing = -1; for(j=0; j < ret->size; j++) if(isdigit(j)) ret->table[j] = -1; else ret->table[j] = -2; i = 0; while(*cp) { c = ajSysCastItoc(toupper((ajint)*cp)); ret->table[toupper((ajint) *cp)] = c; ret->table[tolower((ajint) *cp)] = c; cp++; i++; } return ret; } ID ajSeqcvtNewStr TY public MO ajseq LB core XX DE Generates a new conversion table in which the first character of the first DE string in the array provided is converted to 1, the first character of the DE second string is converted to 2, the first character of the third string is DE converted to 3 and so on. DE Upper and lower case characters are converted to the same numbers. DE All other characters are set to zero. XX PN [1] PA r basearray const AjPPStr PD Allowed sequence character strings PD (size specified by parameter n) PX PN [2] PA r numbases ajint PD Number of strings PX RT AjPSeqCvt RD Conversion table. RX // AjPSeqCvt ajSeqcvtNewStr (const AjPPStr basearray, ajint numbases) { static AjPSeqCvt ret; ajint i; AJNEW0(ret); ret->len = numbases; ret->size = CHAR_MAX - CHAR_MIN + 1; ret->table = AJCALLOC0(ret->size, sizeof(char)); ret->bases = ajStrNew(); ret->missing = 0; AJCNEW0(ret->rlabels, numbases); for(i=0; irlabels[i] = ajStrNew(); for(i=0; irlabels[i], basearray[i]); AJCNEW0(ret->clabels, numbases); for(i=0; iclabels[i] = ajStrNew(); for(i=0; iclabels[i], basearray[i]); for(i=0; ibases, ajStrGetCharFirst(basearray[i])); ret->table[toupper((ajint) ajStrGetCharFirst(basearray[i]))] = ajSysCastItoc(i+1); ret->table[tolower((ajint) ajStrGetCharFirst(basearray[i]))] = ajSysCastItoc(i+1); } return ret; } ID ajSeqcvtNewStrAsym TY public MO ajseq LB core XX DE Generates a new conversion table in which the first character of the first DE string in the array provided is converted to 1, the first character of the DE second string is converted to 2, the first character of the third string is DE converted to 3 and so on. DE Upper and lower case characters are converted to the same numbers. DE All other characters are set to zero. DE For use with asymmetrical matrices. XX PN [1] PA r basearray const AjPPStr PD Allowed sequence character strings PD (size specified by parameter n) PX PN [2] PA r numbases ajint PD Number of strings PX PN [3] PA r matchbases const AjPPStr PD Allowed sequence character strings for PD rows (size specified by parameter rn) PX PN [4] PA r nummatch ajint PD Number of strings (rows) PX RT AjPSeqCvt RD Conversion table. RX // AjPSeqCvt ajSeqcvtNewStrAsym (const AjPPStr basearray, ajint numbases, const AjPPStr matchbases, ajint nummatch) { static AjPSeqCvt ret; ajint i; AJNEW0(ret); ret->len = numbases; ret->nclabels = numbases; ret->nrlabels = nummatch; ret->size = CHAR_MAX - CHAR_MIN + 1; ret->table = AJCALLOC0(ret->size, sizeof(char)); ret->bases = ajStrNew(); ret->missing = 0; AJCNEW0(ret->rlabels, nummatch); for(i=0; irlabels[i] = ajStrNew(); for(i=0; irlabels[i], matchbases[i]); AJCNEW0(ret->clabels, numbases); for(i=0; iclabels[i] = ajStrNew(); for(i=0; iclabels[i], basearray[i]); for(i=0; ilabels[i], bases[i]); */ ajStrAppendK(&ret->bases, ajStrGetCharFirst(basearray[i])); ret->table[toupper((ajint) ajStrGetCharFirst(basearray[i]))] = ajSysCastItoc(i+1); ret->table[tolower((ajint) ajStrGetCharFirst(basearray[i]))] = ajSysCastItoc(i+1); } return ret; } ID ajSeqcvtDel TY public MO ajseq LB core XX DE Delete a conversion table XX PN [1] PA w Pcvt AjPSeqCvt* PD Conversion table reference PX RT void RD RX // void ajSeqcvtDel (AjPSeqCvt* Pcvt) { ajint i=0; if(!*Pcvt|| !Pcvt) return; AJFREE((*Pcvt)->table); ajStrDel(&(*Pcvt)->bases); if((*Pcvt)->rlabels) { for(i=0;i<(*Pcvt)->nrlabels;i++) ajStrDel(&(*Pcvt)->rlabels[i]); AJFREE((*Pcvt)->rlabels); } if((*Pcvt)->clabels) { for(i=0;i<(*Pcvt)->nclabels;i++) ajStrDel(&(*Pcvt)->clabels[i]); AJFREE((*Pcvt)->clabels); } AJFREE(*Pcvt); return; } ID ajSeqcvtGetCodeK TY public MO ajseq LB core XX DE Returns the integer code corresponding to a sequence character DE in a conversion table XX PN [1] PA r cvt const AjPSeqCvt PD Conversion table PX PN [2] PA r ch char PD Sequence character PX RT ajint RD Conversion code RX // ajint ajSeqcvtGetCodeK(const AjPSeqCvt cvt, char ch) { return cvt->table[(ajint)ch]; } ID ajSeqcvtGetCodeS TY public MO ajseq LB core XX DE Returns the integer code corresponding to a sequence character string DE in a conversion table (for rows in asymmetrical matrices). XX PN [1] PA r cvt const AjPSeqCvt PD Conversion table PX PN [2] PA r str const AjPStr PD Sequence character string PX RT ajint RD Conversion code RX // ajint ajSeqcvtGetCodeS (const AjPSeqCvt cvt, const AjPStr str) { ajint i=0; for(i=0;inrlabels;i++) if(ajStrMatchS(str, cvt->rlabels[i])) return i+1; /* i+1 is returned because the size of a matrix is always 1 bigger than the number of labels. This is the "padding" first row/column which has all values of 0. */ ajWarn("Sequence character string not found in ajSeqcvtGetCodeS"); return 0; } ID ajSeqcvtGetCodeAsymS TY public MO ajseq LB core XX DE Returns the integer code corresponding to a sequence character string DE in a conversion table (for columns in asymmetrical matrices). XX PN [1] PA r cvt const AjPSeqCvt PD Conversion table PX PN [2] PA r str const AjPStr PD Sequence character string PX RT ajint RD Conversion code RX // ajint ajSeqcvtGetCodeAsymS (const AjPSeqCvt cvt, const AjPStr str) { ajint i=0; for(i=0;inclabels;i++) if(ajStrMatchS(str, cvt->clabels[i])) return i+1; /* i+1 is returned because the size of a matrix is always 1 bigger than the number of labels. This is the "padding" first row/column which has all values of 0. */ ajWarn("Sequence character string not found in ajSeqcvtGetCodeAsymS"); return 0; } ID ajSeqcvtGetCodeAsymrowS TY public MO ajseq LB core XX DE Returns the integer code corresponding to a sequence character string DE in a conversion table (for rows in asymmetrical matrices). XX PN [1] PA r cvt const AjPSeqCvt PD Conversion table PX PN [2] PA r str const AjPStr PD Sequence character string PX RT ajint RD Conversion code RX // ajint ajSeqcvtGetCodeAsymrowS (const AjPSeqCvt cvt, const AjPStr str) { ajint i=0; for(i=0;inrlabels;i++) if(ajStrMatchS(str, cvt->rlabels[i])) return i+1; /* i+1 is returned because the size of a matrix is always 1 bigger than the number of labels. This is the "padding" first row/column which has all values of 0. */ ajWarn("Sequence character string not found in ajSeqcvtGetCodeAsymrowS"); return 0; } ID ajSeqcvtGetLen TY public MO ajseq LB core XX DE Returns the length of a conversion table string (number of sequence DE characters explicitly included) XX PN [1] PA r cvt const AjPSeqCvt PD Conversion table PX RT ajuint RD Length RX // ajuint ajSeqcvtGetLen(const AjPSeqCvt cvt) { return cvt->len; } ID ajSeqdateNew TY public MO ajseq LB core XX DE Constructor for empty sequence date object XX RT AjPSeqDate RD Empty sequence date object RX // AjPSeqDate ajSeqdateNew(void) { AjPSeqDate ret; AJNEW0(ret); return ret; } ID ajSeqdateNewDate TY public MO ajseq LB core XX DE Constructor for copy of a sequence date object XX PN [1] PA r date const AjPSeqDate PD Sequence date object PX RT AjPSeqDate RD Empty sequence date object RX // AjPSeqDate ajSeqdateNewDate(const AjPSeqDate date) { AjPSeqDate ret; AJNEW0(ret); if(!date) return ret; if(date->CreDate) ret->CreDate = ajTimeNewTime(date->CreDate); if(date->ModDate) ret->ModDate = ajTimeNewTime(date->ModDate); if(date->SeqDate) ret->SeqDate = ajTimeNewTime(date->SeqDate); if(date->CreRel) ajStrAssignS(&ret->CreRel, date->CreRel); if(date->ModRel) ajStrAssignS(&ret->ModRel, date->ModRel); if(date->SeqRel) ajStrAssignS(&ret->SeqRel, date->SeqRel); if(date->CreVer) ajStrAssignS(&ret->CreVer, date->CreVer); if(date->ModVer) ajStrAssignS(&ret->ModVer, date->ModVer); if(date->SeqVer) ajStrAssignS(&ret->SeqVer, date->SeqVer); return ret; } ID ajSeqdateDel TY public MO ajseq LB core XX DE Deletes a sequence date object. XX PN [1] PA d Pdate AjPSeqDate* PD Sequence date object PX RT void RD RX // void ajSeqdateDel(AjPSeqDate* Pdate) { AjPSeqDate date; if(!Pdate) return; if(!*Pdate) return; date = *Pdate; ajTimeDel(&date->CreDate); ajTimeDel(&date->ModDate); ajTimeDel(&date->SeqDate); ajStrDel(&date->CreRel); ajStrDel(&date->ModRel); ajStrDel(&date->SeqRel); ajStrDel(&date->CreVer); ajStrDel(&date->ModVer); ajStrDel(&date->SeqVer); AJFREE(*Pdate); return; } ID ajSeqdateClear TY public MO ajseq LB core XX DE Resets all data for a sequence date object. XX PN [1] PA u date AjPSeqDate PD Sequence date object PX RT void RD RX // void ajSeqdateClear(AjPSeqDate date) { if(!date) return; ajTimeDel(&date->CreDate); ajTimeDel(&date->ModDate); ajTimeDel(&date->SeqDate); ajStrSetClear(&date->CreRel); ajStrSetClear(&date->ModRel); ajStrSetClear(&date->SeqRel); ajStrSetClear(&date->CreVer); ajStrSetClear(&date->ModVer); ajStrSetClear(&date->SeqVer); return; } ID ajSeqdateSetCreateS TY public MO ajseq LB core XX DE Set the sequence creation date XX PN [1] PA w date AjPSeqDate PD Sequence date PX PN [2] PA r datestr const AjPStr PD Date string PX RT AjBool RD ajTrue on success RX // AjBool ajSeqdateSetCreateS(AjPSeqDate date, const AjPStr datestr) { if(!date) return ajFalse; if(!seqDateSet(&date->CreDate, datestr)) return ajFalse; if(!date->CreVer) date->CreVer = ajStrNewC("1"); return ajTrue; } ID ajSeqdateSetModifyS TY public MO ajseq LB core XX DE Set the entry modification date XX PN [1] PA w date AjPSeqDate PD Sequence date PX PN [2] PA r datestr const AjPStr PD Date string PX RT AjBool RD ajTrue on success RX // AjBool ajSeqdateSetModifyS(AjPSeqDate date, const AjPStr datestr) { if(!date) return ajFalse; if(!seqDateSet(&date->ModDate, datestr)) return ajFalse; if(!date->ModVer) date->ModVer = ajStrNewC("1"); return ajTrue; } ID ajSeqdateSetModseqS TY public MO ajseq LB core XX DE Set the sequence modification date XX PN [1] PA w date AjPSeqDate PD Sequence date PX PN [2] PA r datestr const AjPStr PD Date string PX RT AjBool RD ajTrue on success RX // AjBool ajSeqdateSetModseqS(AjPSeqDate date, const AjPStr datestr) { if(!date) return ajFalse; if(!seqDateSet(&date->SeqDate, datestr)) return ajFalse; if(!date->SeqVer) date->SeqVer = ajStrNewC("1"); return ajTrue; } ID seqDateSet TY static MO ajseq LB core XX DE Sets a sequence date object XX PN [1] PA w date AjPTime* PD Sequence date object PX PN [2] PA r datestr const AjPStr PD Date as a string in EMBL/UniProt style PX RT AjBool RD ajTrue on success RX // static AjBool seqDateSet(AjPTime* date, const AjPStr datestr) { AjBool ok = ajTrue; AjPStrTok handle = NULL; AjPStr tmpstr = NULL; const char* months[] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC", NULL }; ajint day; ajint month; ajint year; ajStrTokenAssignC(&handle, datestr, "-"); ajStrTokenNextParse(&handle, &tmpstr); ajStrToInt(tmpstr, &day); ajStrTokenNextParse(&handle, &tmpstr); month = 0; while(months[month] && !ajStrMatchC(tmpstr, months[month])) month++; if(!months[month]) { ok = ajFalse; month = 1; } else month++; ajStrTokenNextParse(&handle, &tmpstr); ajStrToInt(tmpstr, &year); if(year > 1900) year -= 1900; if(!day) { ok = ajFalse; day = 1; } if(ok) *date = ajTimeNewDayFmt("dtline", day, month, year); ajStrTokenDel(&handle); ajStrDel(&tmpstr); return ok; } ID ajSeqdescNew TY public MO ajseq LB core XX DE Constructor for empty description object XX RT AjPSeqDesc RD Empty sequence description object RX // AjPSeqDesc ajSeqdescNew(void) { AjPSeqDesc ret; AJNEW0(ret); ret->Name = ajStrNew(); ret->Short = ajListstrNew(); ret->EC = ajListstrNew(); ret->AltNames = ajListNew(); ret->SubNames = ajListNew(); ret->Includes = ajListNew(); ret->Contains = ajListNew(); return ret; } ID ajSeqdescNewDesc TY public MO ajseq LB core XX DE Constructor for copy of a description object XX PN [1] PA r desc const AjPSeqDesc PD Description object PX RT AjPSeqDesc RD Copied description object RX // AjPSeqDesc ajSeqdescNewDesc(const AjPSeqDesc desc) { AjPSeqDesc ret; AJNEW0(ret); if(!desc) return ret; ret->AltNames = ajListNew(); ret->SubNames = ajListNew(); ret->Includes = ajListNew(); ret->Contains = ajListNew(); ajStrAssignS(&ret->Name, desc->Name); ret->Short = ajListstrNewList(desc->Short); ret->EC = ajListstrNewList(desc->EC); ajSeqsubdesclistClone(desc->AltNames, ret->AltNames); ajSeqsubdesclistClone(desc->SubNames, ret->SubNames); ajSeqdesclistClone(desc->Includes, ret->Includes); ajSeqdesclistClone(desc->Contains, ret->Contains); ret->Precursor = desc->Precursor; ret->Fragments = desc->Fragments; return ret; } ID ajSeqdescDel TY public MO ajseq LB core XX DE Deletes a description object. XX PN [1] PA d Pdesc AjPSeqDesc* PD Description object PX RT void RD RX // void ajSeqdescDel(AjPSeqDesc* Pdesc) { AjPSeqDesc sdesc; AjPSeqDesc desc; AjPSeqSubdesc sub; if(!Pdesc) return; if(!*Pdesc) return; sdesc = *Pdesc; ajStrDel(&sdesc->Name); ajListstrFreeData(&sdesc->Short); ajListstrFreeData(&sdesc->EC); while(ajListPop(sdesc->AltNames,(void **)&sub)) ajSeqsubdescDel(&sub); ajListFree(&sdesc->AltNames); while(ajListPop(sdesc->SubNames,(void **)&sub)) ajSeqsubdescDel(&sub); ajListFree(&sdesc->SubNames); while(ajListPop(sdesc->Includes,(void **)&desc)) ajSeqdescDel(&desc); ajListFree(&sdesc->Includes); while(ajListPop(sdesc->Contains,(void **)&desc)) ajSeqdescDel(&desc); ajListFree(&sdesc->Contains); AJFREE(*Pdesc); return; } ID ajSeqdescAppendName TY public MO ajseq LB core XX DE Append to the name of a description XX PN [1] PA u desc AjPSeqDesc PD Description object PX PN [2] PA r str const AjPStr PD Name string PX RT AjBool RD True on success RX // AjBool ajSeqdescAppendName(AjPSeqDesc desc, const AjPStr str) { if(ajStrGetLen(desc->Name)) ajStrAppendK(&desc->Name, ' '); ajStrAppendS(&desc->Name, str); return ajTrue; } ID ajSeqdescClear TY public MO ajseq LB core XX DE Resets data for a description object. XX PN [1] PA u desc AjPSeqDesc PD Description object PX RT void RD RX // void ajSeqdescClear(AjPSeqDesc desc) { AjPSeqDesc sdesc; AjPSeqSubdesc sub; AjPStr ptr = NULL; if(!desc) return; ajStrSetClear(&desc->Name); while(ajListstrPop(desc->Short,&ptr)) ajStrDel(&ptr); while(ajListstrPop(desc->EC,&ptr)) ajStrDel(&ptr); while(ajListPop(desc->AltNames,(void **)&sub)) ajSeqsubdescDel(&sub); while(ajListPop(desc->SubNames,(void **)&sub)) ajSeqsubdescDel(&sub); while(ajListPop(desc->Includes,(void **)&sdesc)) ajSeqdescDel(&sdesc); while(ajListPop(desc->Contains,(void **)&sdesc)) ajSeqdescDel(&sdesc); desc->Precursor = ajFalse; desc->Fragments = 0; return; } ID ajSeqdescSetName TY public MO ajseq LB core XX DE Set the name of a description object XX PN [1] PA u desc AjPSeqDesc PD Description object PX PN [2] PA r str const AjPStr PD Synonyms string PX RT AjBool RD True on success RX // AjBool ajSeqdescSetName(AjPSeqDesc desc, const AjPStr str) { ajStrAssignS(&desc->Name, str); return ajTrue; } ID ajSeqdesclistClone TY public MO ajseq LB core XX DE Copy a list of genes to another list XX PN [1] PA r src const AjPList PD Source list of descriptions PX PN [2] PA w dest AjPList PD Destination list of descriptions PX RT AjBool RD True on success RX // AjBool ajSeqdesclistClone(const AjPList src, AjPList dest) { AjIList iter; AjPSeqDesc descout = NULL; AjPSeqDesc descin = NULL; if(ajListGetLength(dest)) return ajFalse; iter = ajListIterNewread(src); while ((descin = (AjPSeqDesc) ajListIterGet(iter))) { descout = ajSeqdescNewDesc(descin); ajListPushAppend(dest, descout); } ajListIterDel(&iter); return ajTrue; } ID ajSeqsubdescNew TY public MO ajseq LB core XX DE Constructor for empty sub-description object XX RT AjPSeqSubdesc RD Empty sequence sub-description object RX // AjPSeqSubdesc ajSeqsubdescNew(void) { AjPSeqSubdesc ret; AJNEW0(ret); ret->Short = ajListstrNew(); ret->EC = ajListstrNew(); ret->Allergen = ajListstrNew(); ret->Biotech = ajListstrNew(); ret->Cdantigen = ajListstrNew(); ret->Inn = ajListstrNew(); return ret; } ID ajSeqsubdescNewSubdesc TY public MO ajseq LB core XX DE Constructor for copy of a sub-description object XX PN [1] PA r desc const AjPSeqSubdesc PD Sub-description object PX RT AjPSeqSubdesc RD Copied sub-description object RX // AjPSeqSubdesc ajSeqsubdescNewSubdesc(const AjPSeqSubdesc desc) { AjPSeqSubdesc ret; AJNEW0(ret); if(!desc) return ret; ajStrAssignS(&ret->Name, desc->Name); ret->Short = ajListstrNewList(desc->Short); ret->EC = ajListstrNewList(desc->EC); ret->Allergen = ajListstrNewList(desc->Allergen); ret->Biotech = ajListstrNewList(desc->Biotech); ret->Cdantigen = ajListstrNewList(desc->Cdantigen); ret->Inn = ajListstrNewList(desc->Inn); return ret; } ID ajSeqsubdescDel TY public MO ajseq LB core XX DE Deletes a sub-description object. XX PN [1] PA d Pdesc AjPSeqSubdesc* PD Sub-description object PX RT void RD RX // void ajSeqsubdescDel(AjPSeqSubdesc* Pdesc) { AjPSeqSubdesc sdesc; if(!Pdesc) return; if(!*Pdesc) return; sdesc = *Pdesc; ajStrDel(&sdesc->Name); ajListstrFreeData(&sdesc->Short); ajListstrFreeData(&sdesc->EC); ajListstrFreeData(&sdesc->Allergen); ajListstrFreeData(&sdesc->Biotech); ajListstrFreeData(&sdesc->Cdantigen); ajListstrFreeData(&sdesc->Inn); AJFREE(*Pdesc); return; } ID ajSeqsubdescAppendName TY public MO ajseq LB core XX DE Append to the name of a description XX PN [1] PA u desc AjPSeqSubdesc PD Description object PX PN [2] PA r str const AjPStr PD Name string PX RT AjBool RD True on success RX // AjBool ajSeqsubdescAppendName(AjPSeqSubdesc desc, const AjPStr str) { if(ajStrGetLen(desc->Name)) ajStrAppendK(&desc->Name, ' '); ajStrAppendS(&desc->Name, str); return ajTrue; } ID ajSeqsubdescClear TY public MO ajseq LB core XX DE Resets data for a sub-description object. XX PN [1] PA u desc AjPSeqSubdesc PD Sub-description object PX RT void RD RX // void ajSeqsubdescClear(AjPSeqSubdesc desc) { AjPStr ptr = NULL; if(!desc) return; ajStrSetClear(&desc->Name); while(ajListstrPop(desc->Short,&ptr)) ajStrDel(&ptr); while(ajListstrPop(desc->EC,&ptr)) ajStrDel(&ptr); while(ajListstrPop(desc->Allergen,&ptr)) ajStrDel(&ptr); while(ajListstrPop(desc->Biotech,&ptr)) ajStrDel(&ptr); while(ajListstrPop(desc->Cdantigen,&ptr)) ajStrDel(&ptr); while(ajListstrPop(desc->Inn,&ptr)) ajStrDel(&ptr); return; } ID ajSeqsubdescSetName TY public MO ajseq LB core XX DE Set the name of a sub-description object XX PN [1] PA u desc AjPSeqSubdesc PD Sub-description object PX PN [2] PA r str const AjPStr PD Synonyms string PX RT AjBool RD True on success RX // AjBool ajSeqsubdescSetName(AjPSeqSubdesc desc, const AjPStr str) { ajStrAssignS(&desc->Name, str); return ajTrue; } ID ajSeqsubdesclistClone TY public MO ajseq LB core XX DE Copy a list of sub-descriptions to another list XX PN [1] PA r src const AjPList PD Source list of sub-descriptions PX PN [2] PA w dest AjPList PD Destination list of sub-descriptions PX RT AjBool RD True on success RX // AjBool ajSeqsubdesclistClone(const AjPList src, AjPList dest) { AjIList iter; AjPSeqSubdesc descout = NULL; AjPSeqSubdesc descin = NULL; if(ajListGetLength(dest)) return ajFalse; iter = ajListIterNewread(src); while ((descin = (AjPSeqSubdesc) ajListIterGet(iter))) { descout = ajSeqsubdescNewSubdesc(descin); ajListPushAppend(dest, descout); } ajListIterDel(&iter); return ajTrue; } ID ajSeqgeneNew TY public MO ajseq LB core XX DE Constructor for empty gene object XX RT AjPSeqGene RD Empty sequence gene object RX // AjPSeqGene ajSeqgeneNew(void) { AjPSeqGene ret; AJNEW0(ret); return ret; } ID ajSeqgeneNewGene TY public MO ajseq LB core XX DE Constructor for copy of a gene object XX PN [1] PA r gene const AjPSeqGene PD Gene object PX RT AjPSeqGene RD Copied gene object RX // AjPSeqGene ajSeqgeneNewGene(const AjPSeqGene gene) { AjPSeqGene ret; AJNEW0(ret); if(!gene) return ret; ajStrAssignS(&ret->Name, gene->Name); ajStrAssignS(&ret->Synonyms, gene->Synonyms); ajStrAssignS(&ret->Orf, gene->Orf); ajStrAssignS(&ret->Oln, gene->Oln); return ret; } ID ajSeqgeneNewName TY public MO ajseq LB core XX DE Constructor for gene with given name XX PN [1] PA r str const AjPStr PD Gene name PX RT AjPSeqGene RD New gene object RX // AjPSeqGene ajSeqgeneNewName(const AjPStr str) { AjPSeqGene ret; AJNEW0(ret); if(!str) return ret; ajStrAssignS(&ret->Name, str); return ret; } ID ajSeqgeneDel TY public MO ajseq LB core XX DE Deletes a gene object. XX PN [1] PA d Pgene AjPSeqGene* PD Gene object PX RT void RD RX // void ajSeqgeneDel(AjPSeqGene* Pgene) { AjPSeqGene sgene; if(!Pgene) return; if(!*Pgene) return; sgene = *Pgene; ajStrDel(&sgene->Name); ajStrDel(&sgene->Synonyms); ajStrDel(&sgene->Orf); ajStrDel(&sgene->Oln); AJFREE(*Pgene); return; } ID ajSeqgeneAppendName TY public MO ajseq LB core XX DE Append to the name of a gene XX PN [1] PA u gene AjPSeqGene PD Gene object PX PN [2] PA r str const AjPStr PD Name string PX RT AjBool RD True on success RX // AjBool ajSeqgeneAppendName(AjPSeqGene gene, const AjPStr str) { if(ajStrGetLen(gene->Name)) ajStrAppendK(&gene->Name, ' '); ajStrAppendS(&gene->Name, str); return ajTrue; } ID ajSeqgeneAppendOln TY public MO ajseq LB core XX DE Append to ordered locus names of a gene XX PN [1] PA u gene AjPSeqGene PD Gene object PX PN [2] PA r str const AjPStr PD Ordered locus names string PX RT AjBool RD True on success RX // AjBool ajSeqgeneAppendOln(AjPSeqGene gene, const AjPStr str) { if(ajStrGetLen(gene->Oln)) ajStrAppendK(&gene->Oln, ' '); ajStrAppendS(&gene->Oln, str); return ajTrue; } ID ajSeqgeneAppendOrf TY public MO ajseq LB core XX DE Append to the ORF name of a gene XX PN [1] PA u gene AjPSeqGene PD Gene object PX PN [2] PA r str const AjPStr PD ORF name string PX RT AjBool RD True on success RX // AjBool ajSeqgeneAppendOrf(AjPSeqGene gene, const AjPStr str) { if(ajStrGetLen(gene->Orf)) ajStrAppendK(&gene->Orf, ' '); ajStrAppendS(&gene->Orf, str); return ajTrue; } ID ajSeqgeneAppendSynonyms TY public MO ajseq LB core XX DE Append to the name of a gene XX PN [1] PA u gene AjPSeqGene PD Gene object PX PN [2] PA r str const AjPStr PD Synonyms string PX RT AjBool RD True on success RX // AjBool ajSeqgeneAppendSynonyms(AjPSeqGene gene, const AjPStr str) { if(ajStrGetLen(gene->Synonyms)) ajStrAppendK(&gene->Synonyms, ' '); ajStrAppendS(&gene->Synonyms, str); return ajTrue; } ID ajSeqgeneSetName TY public MO ajseq LB core XX DE Set the name of a gene XX PN [1] PA u gene AjPSeqGene PD Gene object PX PN [2] PA r str const AjPStr PD Name string PX RT AjBool RD True on success RX // AjBool ajSeqgeneSetName(AjPSeqGene gene, const AjPStr str) { ajStrAssignS(&gene->Name, str); return ajTrue; } ID ajSeqgeneSetOln TY public MO ajseq LB core XX DE Set the ordered locus names of a gene XX PN [1] PA u gene AjPSeqGene PD Gene object PX PN [2] PA r str const AjPStr PD Ordered locus names string PX RT AjBool RD True on success RX // AjBool ajSeqgeneSetOln(AjPSeqGene gene, const AjPStr str) { ajStrAssignS(&gene->Oln, str); return ajTrue; } ID ajSeqgeneSetOrf TY public MO ajseq LB core XX DE Set the ORF name of a gene XX PN [1] PA u gene AjPSeqGene PD Gene object PX PN [2] PA r str const AjPStr PD ORF name string PX RT AjBool RD True on success RX // AjBool ajSeqgeneSetOrf(AjPSeqGene gene, const AjPStr str) { ajStrAssignS(&gene->Orf, str); return ajTrue; } ID ajSeqgeneSetSynonyms TY public MO ajseq LB core XX DE Set the name of a gene XX PN [1] PA u gene AjPSeqGene PD Gene object PX PN [2] PA r str const AjPStr PD Synonyms string PX RT AjBool RD True on success RX // AjBool ajSeqgeneSetSynonyms(AjPSeqGene gene, const AjPStr str) { ajStrAssignS(&gene->Synonyms, str); return ajTrue; } ID ajSeqgenelistClone TY public MO ajseq LB core XX DE Copy a list of genes to another list XX PN [1] PA r src const AjPList PD Source list of genes PX PN [2] PA w dest AjPList PD Destination list of genes PX RT AjBool RD True on success RX // AjBool ajSeqgenelistClone(const AjPList src, AjPList dest) { AjIList iter; AjPSeqGene geneout = NULL; AjPSeqGene genein = NULL; if(ajListGetLength(dest)) return ajFalse; iter = ajListIterNewread(src); while ((genein = (AjPSeqGene) ajListIterGet(iter))) { geneout = ajSeqgeneNewGene(genein); ajListPushAppend(dest, geneout); } ajListIterDel(&iter); return ajTrue; } ID ajSeqxrefNew TY public MO ajseq LB core XX DE Constructor for empty sequence citation object XX RT AjPSeqXref RD Empty sequence cross-reference object RX // AjPSeqXref ajSeqxrefNew(void) { AjPSeqXref ret; AJNEW0(ret); return ret; } ID ajSeqxrefNewDbC TY public MO ajseq LB core XX DE Constructor for copy of a sequence citation object XX PN [1] PA r id const AjPStr PD Primary identifier PX PN [2] PA r db const char* PD Database name PX PN [3] PA r reftype ajuint PD Reference type PX RT AjPSeqXref RD New sequence cross-reference object RX // AjPSeqXref ajSeqxrefNewDbC(const AjPStr id, const char* db, ajuint reftype) { AjPSeqXref ret; AJNEW0(ret); ajStrAssignC(&ret->Db, db); ajStrAssignS(&ret->Id, id); if(ajStrPrefixCaseS(ret->Id, ret->Db) && ajStrGetCharPos(ret->Id, ajStrGetLen(ret->Db)) == ':') ajStrCutStart(&ret->Id, 1+ajStrGetLen(ret->Db)); ret->Type = reftype; return ret; } ID ajSeqxrefNewDbS TY public MO ajseq LB core XX DE Constructor for copy of a sequence citation object XX PN [1] PA r id const AjPStr PD Primary identifier PX PN [2] PA r db const AjPStr PD Database name PX PN [3] PA r reftype ajuint PD Reference type PX RT AjPSeqXref RD New sequence cross-reference object RX // AjPSeqXref ajSeqxrefNewDbS(const AjPStr id, const AjPStr db, ajuint reftype) { AjPSeqXref ret; AJNEW0(ret); ajStrAssignS(&ret->Db, db); ajStrAssignS(&ret->Id, id); if(ajStrPrefixCaseS(ret->Id, ret->Db) && ajStrGetCharPos(ret->Id, ajStrGetLen(ret->Db)) == ':') ajStrCutStart(&ret->Id, 1+ajStrGetLen(ret->Db)); ret->Type = reftype; return ret; } ID ajSeqxrefNewRef TY public MO ajseq LB core XX DE Constructor for copy of a sequence citation object XX PN [1] PA r xref const AjPSeqXref PD Sequence cross-reference object PX RT AjPSeqXref RD Copied sequence cross-reference object RX // AjPSeqXref ajSeqxrefNewRef(const AjPSeqXref xref) { AjPSeqXref ret; AJNEW0(r
Data resource FormatDescription
\n%-12s\n" "\"%s\"