7d6d14047849f1ddcf0aadf5491f2830996df7b5
braney
  Mon Jul 28 15:37:04 2014 -0700
allow underbars in gff3.  Grok Is_circular.  #13673
diff --git src/lib/gff3.c src/lib/gff3.c
index 4fe91c9..fd6187b 100644
--- src/lib/gff3.c
+++ src/lib/gff3.c
@@ -29,30 +29,31 @@
  */
 
 static const int gffNumCols = 9;
 
 /* standard attribute names */
 char *gff3AttrID = "ID";
 char *gff3AttrName = "Name";
 char *gff3AttrAlias = "Alias";
 char *gff3AttrParent = "Parent";
 char *gff3AttrTarget = "Target";
 char *gff3AttrGap = "Gap";
 char *gff3AttrDerivesFrom = "Derives_from";
 char *gff3AttrNote = "Note";
 char *gff3AttrDbxref = "Dbxref";
 char *gff3AttrOntologyTerm = "Ontology_term";
+char *gff3AttrIsCircular = "Is_circular";
 
 /* commonly used features names */
 char *gff3FeatGene = "gene";
 char *gff3FeatMRna = "mRNA";
 char *gff3FeatExon = "exon";
 char *gff3FeatCDS = "CDS";
 char *gff3FeatThreePrimeUTR = "three_prime_UTR";
 char *gff3FeatFivePrimeUTR = "five_prime_UTR";
 char *gff3FeatStartCodon = "start_codon";
 char *gff3FeatStopCodon = "stop_codon";
 char *gff3FeatTranscript = "transcript";
 
 static bool gff3FileStopDueToErrors(struct gff3File *g3f)
 /* determine if we should stop due to the number of errors */
 {
@@ -335,31 +336,31 @@
     // spec currently doesn't restrict phase, unclear if it's allowed on start/stop codon features
     if (g3a->phase >= 0)
         gff3AnnErr(g3a, "phase only allowed on CDS features");
 #endif
     }
 }
 
 /* check that an attribute tag name is valid. */
 static boolean checkAttrTag(struct gff3Ann *g3a, char *tag)
 {
 // FIXME: spec is not clear on what is a valid tag.
 char *tc = tag;
 boolean isOk = isalpha(*tc);
 for (tc++; isOk && (*tc != '\0'); tc++)
     {
-    if (!((*tc == '_') || isalnum(*tc)))
+    if (!((*tc == '-') || (*tc == '_') || isalnum(*tc)))
         isOk = FALSE;
     }
 if (!isOk)
     gff3AnnErr(g3a, "invalid attribute tag, must start with an alphabetic character and be composed of alphanumeric or underscore characters: %s", tag);
 return isOk;
 }
 
 static struct slName *parseAttrVals(struct gff3Ann *g3a, char *tag, char *valsStr)
 /* parse an attribute into its values */
 {
 int i, numVals = chopString(valsStr, ",", NULL, 0);
 char **vals = needMem((numVals+1)*sizeof(char**)); // +1 allows for no values
 chopString(valsStr, ",", vals, numVals);
 struct slName *unescVals = NULL;
 for (i = 0; i < numVals; i++)
@@ -480,30 +481,36 @@
 
 static void parseGapAttr(struct gff3Ann *g3a, struct gff3Attr *attr)
 /* parse the Gap attribute */
 {
 checkSingleValAttr(g3a, attr);
 g3a->gap = attr->vals->name;
 }
 
 static void parseDerivesFromAttr(struct gff3Ann *g3a, struct gff3Attr *attr)
 /* parse the Derives_from attribute */
 {
 checkSingleValAttr(g3a, attr);
 g3a->derivesFromId = attr->vals->name;
 }
 
+static void parseIsCircular(struct gff3Ann *g3a, struct gff3Attr *attr)
+/* parse the Note attribute */
+{
+g3a->isCircular = TRUE;
+}
+
 static void parseNoteAttr(struct gff3Ann *g3a, struct gff3Attr *attr)
 /* parse the Note attribute */
 {
 g3a->notes = attr->vals;
 }
 
 static void parseDbxrefAttr(struct gff3Ann *g3a, struct gff3Attr *attr)
 /* parse the Dbxref attribute */
 {
 g3a->dbxrefs = attr->vals;
 }
 
 static void parseOntologyTermAttr(struct gff3Ann *g3a, struct gff3Attr *attr)
 /* parse the Ontology_term attribute */
 {
@@ -517,30 +524,32 @@
 {
 if (sameString(attr->tag, gff3AttrID))
     parseIDAttr(g3a, attr);
 else if (sameString(attr->tag, gff3AttrName))
     parseNameAttr(g3a, attr);
 else if (sameString(attr->tag, gff3AttrAlias))
     parseAliasAttr(g3a, attr);
 else if (sameString(attr->tag, gff3AttrParent))
     parseParentAttr(g3a, attr);
 else if (sameString(attr->tag, gff3AttrTarget))
     parseTargetAttr(g3a, attr);
 else if (sameString(attr->tag, gff3AttrGap))
     parseGapAttr(g3a, attr);
 else if (sameString(attr->tag, gff3AttrDerivesFrom))
     parseDerivesFromAttr(g3a, attr);
+else if (sameString(attr->tag, gff3AttrIsCircular))
+    parseIsCircular(g3a, attr);
 else if (sameString(attr->tag, gff3AttrNote))
     parseNoteAttr(g3a, attr);
 else if (sameString(attr->tag, gff3AttrDbxref))
     parseDbxrefAttr(g3a, attr);
 else if (sameString(attr->tag, gff3AttrOntologyTerm))
     parseOntologyTermAttr(g3a, attr);
 else
     gff3AnnErr(g3a, "unknown standard attribute, user defined attributes must start with a lower-case letter: %s", attr->tag);
 }
 
 static void parseStdAttrs(struct gff3Ann *g3a)
 /* parse standard attributes (starting with upper case) into attributes
  * have been parsed into attribute list, which would have  merged multiply
  * specified attributes. */
 {