alnread.c
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:173k
源码类别:

生物技术

开发平台:

C/C++

  1.             return offset;
  2.         }
  3.     }
  4.     return -1;
  5. }
  6. /* This function returns a pointer to the memory in which the ID for the
  7.  * Nth sequence is stored, unless there aren't that many sequences, in which
  8.  * case NULL is returned.
  9.  */
  10. static char * 
  11. s_GetAlignRawSeqIDByOffset 
  12. (TAlignRawSeqPtr list,
  13.  int  offset)
  14. {
  15.     TAlignRawSeqPtr arsp;
  16.     int            index;
  17.     arsp = list;
  18.     index = 0;
  19.     while ( arsp != NULL  &&  index != offset ) {
  20.         arsp = arsp->next;
  21.         index++;
  22.     }
  23.     if (index == offset  &&  arsp != NULL) {
  24.         return arsp->id;
  25.     } else {
  26.         return NULL;
  27.     }
  28. }
  29. /* This function adds data to a sequence by looking for the specified ID in
  30.  * the list.  If the id is not found, a new sequence with that ID is added to
  31.  * the end of the list.
  32.  * The function returns a pointer to the first item in the list.
  33.  */
  34. static TAlignRawSeqPtr
  35. s_AddAlignRawSeqById
  36. (TAlignRawSeqPtr list,
  37.  char *  id,
  38.  char *  data,
  39.  int     id_line_num,
  40.  int     data_line_num,
  41.  int     data_line_offset)
  42. {
  43.     TAlignRawSeqPtr arsp;
  44.     TIntLinkPtr     ilp;
  45.     arsp = s_FindAlignRawSeqById (list, id);
  46.     if (arsp == NULL) {
  47.         arsp = s_AlignRawSeqNew (list);
  48.         if (arsp == NULL) {
  49.             return NULL;
  50.         }
  51.         if (list == NULL) list = arsp;
  52.         arsp->id = strdup (id);
  53.     }
  54.     arsp->sequence_data = s_AddLineInfo (arsp->sequence_data,
  55.                                        data,
  56.                                        data_line_num,
  57.                                        data_line_offset);
  58.     ilp = s_IntLinkNew (id_line_num, arsp->id_lines);
  59.     if (arsp->id_lines == NULL) arsp->id_lines = ilp;
  60.     return list;
  61. }
  62. /* This function adds data to the Nth sequence in the sequence list and
  63.  * returns eTrue, unless there aren't that many sequences in the list, in
  64.  * which case the function returns eFalse.
  65.  */
  66. static EBool 
  67. s_AddAlignRawSeqByIndex 
  68. (TAlignRawSeqPtr list,
  69.  int     index,
  70.  char *  data,
  71.  int     data_line_num,
  72.  int     data_line_offset)
  73. {
  74.     TAlignRawSeqPtr arsp;
  75.     int            curr;
  76.     curr = 0;
  77.     for (arsp = list; arsp != NULL  &&  curr < index; arsp = arsp->next) {
  78.         curr++;
  79.     }
  80.     if (arsp == NULL) {
  81.         return eFalse;
  82.     } else {
  83.         arsp->sequence_data = s_AddLineInfo (arsp->sequence_data,
  84.                                            data,
  85.                                            data_line_num,
  86.                                            data_line_offset);
  87.         return eTrue;
  88.     }
  89. }
  90. /* This function frees memory associated with the SAlignRawFileData structure.
  91.  */
  92. static void s_AlignFileRawFree (SAlignRawFilePtr afrp)
  93. {
  94.     if (afrp == NULL) {
  95.         return;
  96.     }
  97.     s_LineInfoFree (afrp->organisms);
  98.     s_LineInfoFree (afrp->deflines);
  99.     s_LineInfoFree (afrp->line_list);
  100.     s_AlignRawSeqFree (afrp->sequences);
  101.     s_IntLinkFree (afrp->offset_list);
  102.     free (afrp);
  103. }
  104. /* This function allocates memory for an SAlignRawFileData structure and
  105.  * initializes its member variables.  The function returns a pointer to
  106.  * the newly allocated structure.
  107.  */
  108. static SAlignRawFilePtr s_AlignFileRawNew (void)
  109. {
  110.     SAlignRawFilePtr afrp;
  111.     afrp = (SAlignRawFilePtr)malloc (sizeof (SAlignRawFileData));
  112.     if (afrp == NULL) {
  113.         return NULL;
  114.     }
  115.     afrp->marked_ids            = eFalse;
  116.     afrp->line_list             = NULL;
  117.     afrp->organisms             = NULL;
  118.     afrp->num_organisms         = 0;
  119.     afrp->deflines              = NULL;
  120.     afrp->num_deflines          = 0;
  121.     afrp->block_size            = 0;
  122.     afrp->offset_list           = NULL;
  123.     afrp->sequences             = NULL;
  124.     afrp->report_error          = NULL;
  125.     afrp->report_error_userdata = NULL;
  126.     afrp->alphabet              = NULL;
  127.     afrp->expected_num_sequence = 0;
  128.     afrp->expected_sequence_len = 0;
  129.     afrp->num_segments          = 1;
  130.     return afrp;
  131. }
  132. /* The following functions are used to analyze the structure of a file and
  133.  * assemble the sequences listed in the file.
  134.  * Sequence data in a file is organized in one of two general formats - 
  135.  * interleaved or contiguous.  Interleaved data can be recognized by looking
  136.  * for repeated blocks of the same number of lines within a file separated
  137.  * by blank or skippable lines from other lines in the file.  The first of
  138.  * these blocks must have at least two elements separated by whitespace
  139.  * in each line, the first of these elements is the ID for the sequence in
  140.  * that row and for the sequences in that position within the block for the
  141.  * remainder of the file.
  142.  * Contiguous data can be recognized by either looking for "marked" sequence
  143.  * IDs, which begin with a '>' character, or by looking for repeated patterns
  144.  * of lines with the same numbers of characters.
  145.  */
  146. /* The following functions are used to analyze interleaved data. */
  147. /* This function creates a SLengthListData structure that describes the pattern
  148.  * of character lengths in the string pointed to by cp.
  149.  */
  150. static SLengthListPtr s_GetBlockPattern (char * cp)
  151. {
  152.     SLengthListPtr this_pattern;
  153.     int           len;
  154.     this_pattern = s_LengthListNew (NULL);
  155.     if (this_pattern == NULL) {
  156.         return NULL;
  157.     }
  158.     this_pattern->num_appearances = 1;
  159.     while (*cp != 0) {
  160.         len = strcspn (cp, " tr");
  161.         s_AddLengthRepeat (this_pattern, len);
  162.         cp += len;
  163.         cp += strspn (cp, " tr");
  164.     }
  165.     return this_pattern;
  166. }
  167. /* This function attempts to predict whether the following lines will be
  168.  * an interleaved block.  If so, the function returns the location of the
  169.  * beginning of the block, otherwise the function returns -1.
  170.  */
  171. static int 
  172. s_ForecastBlockPattern 
  173. (SLengthListPtr pattern_list,
  174.  TIntLinkPtr    next_offset,
  175.  int            line_start,
  176.  int            block_size)
  177. {
  178.     int  line_counter;
  179.     SLengthListPtr llp;
  180.     line_counter = line_start;
  181.     if (next_offset != NULL
  182.         &&  next_offset->ival - line_counter < block_size) {
  183.         return -1;
  184.     }
  185.     
  186.     for (llp = pattern_list;
  187.          llp != NULL
  188.            &&  (next_offset == NULL  ||  line_counter < next_offset->ival - 1)
  189.            &&  line_counter - line_start < block_size;
  190.          llp = llp->next)
  191.     {
  192.         if (llp->lengthrepeats == NULL) {
  193.             return -1;
  194.         }
  195.         line_counter += llp->num_appearances;
  196.     }
  197.     if (line_counter - line_start == block_size) {
  198.         if (llp->next == NULL) {
  199.             return line_start;
  200.         }
  201.         llp = llp->next;
  202.         if (llp->lengthrepeats == NULL) {
  203.             return line_start;
  204.         }
  205.     }
  206.     return -1;
  207. }
  208. /* This function looks for malformed blocks between the identified blocks
  209.  * indicated by the offset_list.  It returns a pointer to the list with the
  210.  * new locations inserted at the appropriate locations.
  211.  */
  212. static TIntLinkPtr
  213. s_AugmentBlockPatternOffsetList
  214. (SLengthListPtr pattern_list,
  215.  TIntLinkPtr    offset_list,
  216.  int            block_size)
  217. {
  218.     int            line_counter;
  219.     SLengthListPtr llp;
  220.     TIntLinkPtr    next_offset, prev_offset, new_offset;
  221.     int            forecast_pos;
  222.     prev_offset = NULL;
  223.     next_offset = offset_list;
  224.     line_counter = 0;
  225.     llp = pattern_list;
  226.     while (llp != NULL) {
  227.         if (next_offset != NULL  &&  line_counter == next_offset->ival) {
  228.             prev_offset = next_offset;
  229.             next_offset = next_offset->next;
  230.             /* skip past the lines for this block */
  231.             while (line_counter - prev_offset->ival < block_size
  232.                    &&  llp != NULL)
  233.             {
  234.                 line_counter += llp->num_appearances;
  235.                 llp = llp->next;
  236.             }
  237.         } else {
  238.             forecast_pos = s_ForecastBlockPattern (llp, next_offset,
  239.                                                  line_counter,
  240.                                                  block_size);
  241.             if (forecast_pos > 0) {
  242.                 new_offset = s_IntLinkNew (forecast_pos, NULL);
  243.                 if (new_offset == NULL) {
  244.                     return NULL;
  245.                 }
  246.                 if (prev_offset == NULL) {
  247.                     new_offset->next = offset_list;
  248.                     offset_list = new_offset;
  249.                 } else {
  250.                     new_offset->next = next_offset;
  251.                     prev_offset->next = new_offset;
  252.                 }
  253.                 prev_offset = new_offset;
  254.                 /* skip past the lines for this block */
  255.                 while (line_counter - prev_offset->ival < block_size
  256.                        &&  llp != NULL)
  257.                 {
  258.                     line_counter += llp->num_appearances;
  259.                     llp = llp->next;
  260.                 }
  261.             } else {
  262.                 line_counter += llp->num_appearances;
  263.                 llp = llp->next;
  264.             }
  265.         }
  266.     }
  267.     return offset_list;
  268. }
  269. /* This function looks for lines that could not be assigned to an interleaved
  270.  * block.  It returns eTrue if it finds any such lines after the first offset,
  271.  * eFalse otherwise, and reports all instances of unused lines as errors.
  272.  */
  273. static EBool
  274. s_FindUnusedLines 
  275. (SLengthListPtr pattern_list,
  276.  SAlignRawFilePtr afrp)
  277. {
  278.     TIntLinkPtr    offset;
  279.     SLengthListPtr llp;
  280.     int            line_counter;
  281.     int            block_line_counter;
  282.     EBool          rval = eFalse;
  283.     TLineInfoPtr   line_val;
  284.     int            skip;
  285.     if (pattern_list == NULL  ||  afrp == NULL
  286.         ||  afrp->offset_list == NULL  ||  afrp->block_size < 2) {
  287.         return eFalse;
  288.     }
  289.     
  290.     offset = afrp->offset_list;
  291.     llp = pattern_list;
  292.     line_counter = 0;
  293.     line_val = afrp->line_list;
  294.  
  295.     while (llp != NULL  &&  line_val != NULL) {
  296.         while (llp != NULL  &&  line_val != NULL
  297.                &&  (offset == NULL  ||  line_counter < offset->ival)) {
  298.             if (llp->lengthrepeats != NULL) {
  299.                 s_ReportUnusedLine (line_counter,
  300.                                     line_counter + llp->num_appearances - 1,
  301.                                     line_val,
  302.                                     afrp->report_error,
  303.                                     afrp->report_error_userdata);
  304.                 if (offset != afrp->offset_list) {
  305.                     rval = eTrue;
  306.                 }
  307.             }
  308.             line_counter += llp->num_appearances;
  309.             for (skip = 0;
  310.                  skip < llp->num_appearances  &&  line_val != NULL;
  311.                  skip++) {
  312.                 line_val = line_val->next;
  313.             }
  314.             llp = llp->next;
  315.         }
  316.         block_line_counter = 0;
  317.         while (block_line_counter < afrp->block_size  &&  llp != NULL) {
  318.             block_line_counter += llp->num_appearances;
  319.             line_counter += llp->num_appearances;
  320.             for (skip = 0;
  321.                  skip < llp->num_appearances  &&  line_val != NULL;
  322.                  skip++) {
  323.                 line_val = line_val->next;
  324.             }
  325.             llp = llp->next;
  326.         }
  327.         if (offset != NULL) {
  328.             offset = offset->next;
  329.         }
  330.     }
  331.     return rval;
  332. }
  333. /* This function examines a list of line lengths, looking for interleaved
  334.  * blocks.  If it finds them, it will set the SAlignRawFileData offset_list
  335.  * member variable to point to a list of locations for the blocks.
  336.  */
  337. static void
  338. s_FindInterleavedBlocks 
  339. (SLengthListPtr pattern_list,
  340.  SAlignRawFilePtr afrp)
  341. {
  342.     SLengthListPtr llp, llp_next;
  343.     TSizeInfoPtr   size_list, best_ptr;
  344.     TIntLinkPtr    new_offset;
  345.     int            line_counter;
  346.     afrp->block_size = 0;
  347.     size_list = NULL;
  348.     afrp->offset_list = NULL;
  349.     for (llp = pattern_list; llp != NULL; llp = llp->next) {
  350.         llp_next = llp->next;
  351.         if (llp->num_appearances > 1 
  352.             &&  (llp_next == NULL  ||  llp_next->lengthrepeats == NULL)) {
  353.             size_list = s_AddSizeInfo (size_list, llp->num_appearances);
  354.         }
  355.     }
  356.     best_ptr = s_GetMostPopularSizeInfo (size_list);
  357.     if (best_ptr != NULL  &&  best_ptr->num_appearances > 1) {
  358.         afrp->block_size = best_ptr->size_value;
  359.         line_counter = 0;
  360.         for (llp = pattern_list; llp != NULL; llp = llp->next) {
  361.             llp_next = llp->next;
  362.             if (llp->num_appearances == afrp->block_size
  363.                 &&  (llp_next == NULL  ||  llp_next->lengthrepeats == NULL))
  364.             {
  365.                 new_offset = s_IntLinkNew (line_counter, afrp->offset_list);
  366.                 if (new_offset == NULL) {
  367.                     return;
  368.                 }
  369.                 if (afrp->offset_list == NULL) afrp->offset_list = new_offset;
  370.             }
  371.             line_counter += llp->num_appearances;
  372.         }
  373.         afrp->offset_list = s_AugmentBlockPatternOffsetList (pattern_list,
  374.                                                            afrp->offset_list, 
  375.                                                            afrp->block_size);
  376.     }
  377.     if (s_FindUnusedLines (pattern_list, afrp)) {
  378.         s_IntLinkFree (afrp->offset_list);
  379.         afrp->offset_list = NULL;
  380.         afrp->block_size = 0;
  381.     }
  382.     s_SizeInfoFree (size_list);
  383.     
  384. }
  385. static void s_TrimEndSpace (char *linestring)
  386. {
  387.     int len;
  388.     char *cp;
  389.   
  390.     if (linestring == NULL) return;
  391.     len = strlen (linestring);
  392.     cp = linestring + len - 1;
  393.     while (cp > linestring && (*cp == ' ' || *cp == 't' || *cp == 'r' || *cp == 'n'))
  394.     {
  395.        *cp = 0;
  396.        cp--;
  397.     }
  398. }
  399. static SAlignRawFilePtr
  400. s_ReadAlignFileRaw
  401. (FReadLineFunction    readfunc,
  402.  void *             userdata,
  403.  TSequenceInfoPtr     sequence_info,
  404.  FReportErrorFunction errfunc,
  405.  void *             errdata)
  406. {
  407.     char *                   linestring;
  408.     SAlignRawFilePtr         afrp;
  409.     char *                   tmp;
  410.     EBool                    found_stop;
  411.     int                      overall_line_count;
  412.     EBool                    found_expected_ntax = eFalse;
  413.     EBool                    found_expected_nchar = eFalse;
  414.     EBool                    found_char_comment = eFalse;
  415.     SLengthListPtr           pattern_list = NULL;
  416.     SLengthListPtr           this_pattern;
  417.     char *                   cp;
  418.     int                      len;
  419.     TIntLinkPtr              new_offset;
  420.     EBool                    in_taxa_comment;
  421.     EBool                    in_bracketed_comment = eFalse;
  422.     TBracketedCommentListPtr comment_list = NULL, last_comment = NULL;
  423.     
  424.     if (readfunc == NULL  ||  sequence_info == NULL) {
  425.         return NULL;
  426.     }
  427.     afrp = s_AlignFileRawNew ();
  428.     if (afrp == NULL) {
  429.         return NULL;
  430.     }
  431.   
  432.     afrp->alphabet = strdup (sequence_info->alphabet);
  433.     afrp->report_error = errfunc;
  434.     afrp->report_error_userdata = errdata;
  435.     overall_line_count = 0;
  436.     found_stop = eFalse;
  437.     in_taxa_comment = eFalse;
  438.     linestring = readfunc (userdata);
  439.     if (s_IsASN1 (linestring)) {
  440.         s_ReportASN1Error (afrp->report_error, afrp->report_error_userdata);
  441.         s_AlignFileRawFree (afrp);
  442.         return NULL;
  443.     }
  444.     while (linestring != NULL  &&  linestring [0] != EOF) {
  445.         s_TrimEndSpace (linestring);
  446.         s_ReadOrgNamesFromText (linestring, overall_line_count, afrp);
  447.         /* we want to remove the comment from the line for the purpose 
  448.          * of looking for blank lines and skipping,
  449.          * but save comments for storing in array if line is not skippable or
  450.          * blank
  451.          */ 
  452.         len = strspn (linestring, " trn");
  453.         tmp = strdup (linestring + len);
  454.         if (tmp == NULL) {
  455.             return NULL;
  456.         }
  457.  
  458.         if (! found_stop && ! in_taxa_comment) {
  459.             found_stop = s_FoundStopLine (tmp);
  460.         }
  461.         if (! found_stop) {
  462.             if (! found_expected_ntax  ||  ! found_expected_nchar) {
  463.                 if (s_IsTwoNumbersSeparatedBySpace (tmp)) {
  464.                     s_GetFASTAExpectedNumbers (tmp, afrp);
  465.                     found_expected_ntax = eTrue;
  466.                     found_expected_nchar = eTrue;
  467.                } else {
  468.                     s_GetNexusSizeComments (tmp, &found_expected_ntax,
  469.                                             &found_expected_nchar, afrp);
  470.                 }
  471.             }
  472.             if (! found_char_comment) {
  473.                 found_char_comment = s_CheckNexusCharInfo (tmp, sequence_info, 
  474.                                                    afrp->report_error,
  475.                                                    afrp->report_error_userdata);
  476.             }
  477.             
  478.             if (in_taxa_comment) {
  479.                 if (strncmp (tmp, "end;", 4) == 0) {
  480.                     in_taxa_comment = eFalse;
  481.                 } 
  482.                 tmp [0] = 0;
  483.             } else if (strncmp (tmp, "begin taxa;", 11) == 0) {
  484.                 tmp [0] = 0;
  485.                 in_taxa_comment = eTrue;
  486.             }
  487.             /* remove complete single-line bracketed comments from line 
  488.              *before checking for multiline bracketed comments */
  489.             s_RemoveCommentFromLine (tmp);
  490.             if (in_bracketed_comment) {
  491.                 len = strspn (linestring, " trn");
  492.                 if (last_comment != NULL) 
  493.                 {
  494.                  s_BracketedCommentListAddLine (last_comment, linestring + len,
  495.                                                 overall_line_count, len);
  496.                 }
  497.                 if (strchr (tmp, ']') != NULL) {
  498.                     in_bracketed_comment = eFalse;
  499.                 }
  500.                 tmp [0] = 0;
  501.             } else if (tmp [0] == '[' && strchr (tmp, ']') == NULL) {
  502.                 in_bracketed_comment = eTrue;
  503.                 len = strspn (linestring, " trn");
  504.                 last_comment = s_BracketedCommentListNew (comment_list,
  505.                                                           linestring + len,
  506.                                                           overall_line_count, len);
  507.                 if (comment_list == NULL) 
  508.                 {
  509.                  comment_list = last_comment;
  510.                 }
  511.                 tmp [0] = 0;
  512.             }
  513.             if (s_SkippableString (tmp)) {
  514.                 tmp [0] = 0;
  515.             }
  516.   
  517.             if (tmp [0] == '>'  &&  ! found_stop) {
  518.                 afrp->marked_ids = eTrue;
  519.                 new_offset = s_IntLinkNew (overall_line_count + 1,
  520.                                           afrp->offset_list);
  521.                 if (afrp->offset_list == NULL) afrp->offset_list = new_offset;
  522.             }
  523.             if (! afrp->marked_ids) {
  524.                 /* add to length list for interleaved block search */
  525.                 len = strcspn (tmp, " tr");
  526.                 if (len > 0) {
  527.                     cp = tmp + len;
  528.                     len = strspn (cp, " tr");
  529.                     if (len > 0) {
  530.                         cp = cp + len;
  531.                     }
  532.                     if (*cp == 0) {
  533.                         this_pattern = s_GetBlockPattern (tmp);
  534.                     } else {
  535.                         this_pattern = s_GetBlockPattern (cp);
  536.                     }
  537.                     pattern_list = s_AddPatternRepeat (pattern_list,
  538.                                                      this_pattern);
  539.                 } else {
  540.                     this_pattern = s_GetBlockPattern (tmp);
  541.                     pattern_list = s_AddPatternRepeat (pattern_list,
  542.                                                      this_pattern);
  543.                 }
  544.             }
  545.             len = strspn (linestring, " trn");
  546.             afrp->line_list = s_AddLineInfo (afrp->line_list,
  547.                                            linestring + len,
  548.                                            overall_line_count, len);
  549.         }
  550.         free (linestring);
  551.         free (tmp);
  552.         linestring = readfunc (userdata);
  553.         overall_line_count ++;
  554.     }
  555.     afrp->num_segments = s_GetNumSegmentsInAlignment (comment_list, errfunc, errdata);
  556.     if (afrp->num_segments > 1) 
  557.     {
  558.         if (afrp->offset_list != NULL)
  559.         {
  560.          s_ReportSegmentedAlignmentError (afrp->offset_list,
  561.                                           errfunc, errdata);
  562.             s_AlignFileRawFree (afrp);
  563.             s_LengthListFree (pattern_list);
  564.             s_BracketedCommentListFree (comment_list);
  565.             return NULL;        
  566.         }
  567.         else
  568.         {
  569.          afrp->offset_list = GetSegmentOffsetList (comment_list);
  570.          afrp->marked_ids = eTrue;
  571.         }
  572.     }
  573.     if (! afrp->marked_ids) {
  574.         s_FindInterleavedBlocks (pattern_list, afrp);
  575.     }
  576.     s_LengthListFree (pattern_list);
  577.     s_BracketedCommentListFree (comment_list);
  578.     return afrp;
  579. }
  580. /* This function analyzes a block to see if it contains, as the first element
  581.  * of any of its lines, one of the sequence IDs already identified.  If the
  582.  * one of the lines does begin with a sequence ID, all of the lines are
  583.  * assumed to begin with sequence IDs and the function returns eTrue, otherwise
  584.  * the function returns eFalse.
  585.  */
  586. static EBool 
  587. s_DoesBlockHaveIds 
  588. (SAlignRawFilePtr afrp, 
  589.  TLineInfoPtr     first_line,
  590.  int             num_lines_in_block)
  591. {
  592.     TLineInfoPtr    lip;
  593.     char *          linestring;
  594.     char *          this_id;
  595.     TAlignRawSeqPtr arsp;
  596.     size_t          len;
  597.     int             block_offset;
  598.     if (afrp->sequences == NULL) {
  599.          return eTrue;
  600.     }
  601.     for (lip = first_line, block_offset = 0;
  602.          lip != NULL  &&  block_offset < num_lines_in_block;
  603.          lip = lip->next, block_offset++)
  604.     {
  605.         linestring = lip->data;
  606.         if (linestring != NULL) {
  607.             len = strcspn (linestring, " tr");
  608.             if (len > 0  &&  len < strlen (linestring)) {
  609.                 this_id = (char *) malloc (len + 1);
  610.                 if (this_id == NULL) {
  611.                     return eFalse;
  612.                 }
  613.                 strncpy (this_id, linestring, len);
  614.                 this_id [len] = 0;
  615.                 arsp = s_FindAlignRawSeqById (afrp->sequences, this_id);
  616.                 free (this_id);
  617.                 if (arsp != NULL) {
  618.                     return eTrue;
  619.                 }
  620.             }
  621.         }
  622.     }
  623.     return eFalse;
  624. }
  625. /* This function analyzes the lines of the block to see if the pattern of
  626.  * the lengths of the whitespace-separated pieces of sequence data matches
  627.  * for all lines within the block.  The function returns eTrue if this is so,
  628.  * otherwise the function returns eFalse.
  629.  */
  630. static EBool 
  631. s_BlockIsConsistent
  632. (SAlignRawFilePtr afrp,
  633.  TLineInfoPtr     first_line,
  634.  int              num_lines_in_block,
  635.  EBool            has_ids,
  636.  EBool            first_block)
  637. {
  638.     TLineInfoPtr   lip;
  639.     SLengthListPtr list, this_pattern, best;
  640.     int            len, block_offset, id_offset;
  641.     char *         tmp_id;
  642.     EBool          rval;
  643.     char *         cp;
  644.     rval = eTrue;
  645.     list = NULL;
  646.     for (lip = first_line, block_offset = 0;
  647.          lip != NULL  &&  block_offset < num_lines_in_block;
  648.          lip = lip->next, block_offset ++)
  649.     {
  650.         cp = lip->data;
  651.         if (has_ids) {
  652.             len = strcspn (cp, " tr");
  653.             tmp_id = (char *) malloc ( (len + 1) * sizeof (char));
  654.             if (tmp_id == NULL) {
  655.                 return eFalse;
  656.             }
  657.             strncpy (tmp_id, cp, len);
  658.             tmp_id [len] = 0;
  659.             id_offset = s_FindAlignRawSeqOffsetById (afrp->sequences, tmp_id);
  660.             if (id_offset != block_offset  &&  ! first_block) {
  661.                 rval = eFalse;
  662.                 s_ReportInconsistentID (tmp_id, lip->line_num,
  663.                                       afrp->report_error,
  664.                                       afrp->report_error_userdata);
  665.             }
  666.             free (tmp_id);
  667.             cp += len;
  668.             cp += strspn (cp, " tr");
  669.         }
  670.         this_pattern = s_GetBlockPattern (cp);
  671.         list = s_AddLengthList (list, this_pattern);
  672.     }
  673.     /* Now find the pattern with the most appearances */
  674.     best = NULL;
  675.     for (this_pattern = list;
  676.          this_pattern != NULL;
  677.          this_pattern = this_pattern->next)
  678.     {
  679.         if (this_pattern->num_appearances == 0) continue;
  680.         if (best == NULL 
  681.           ||  this_pattern->num_appearances > best->num_appearances)
  682.         {
  683.             best = this_pattern;
  684.         }
  685.     }
  686.     /* now identify and report inconsistent lines */
  687.     for (lip = first_line, block_offset = 0;
  688.          lip != NULL  &&  block_offset < num_lines_in_block;
  689.          lip = lip->next, block_offset ++)
  690.     {
  691.         cp = lip->data;
  692.         if (has_ids) {
  693.             len = strcspn (cp, " tr");
  694.             tmp_id = (char *) malloc ( (len + 1) * sizeof (char));
  695.             if (tmp_id == NULL) {
  696.                 return eFalse;
  697.             }
  698.             strncpy (tmp_id, cp, len);
  699.             tmp_id [len] = 0;
  700.             cp += len;
  701.             cp += strspn (cp, " tr");
  702.         } else {
  703.             tmp_id = s_GetAlignRawSeqIDByOffset (afrp->sequences, block_offset);
  704.         }
  705.         this_pattern = s_GetBlockPattern (cp);
  706.         if ( ! s_DoLengthPatternsMatch (this_pattern, best)) {
  707.             rval = eFalse;
  708.             s_ReportInconsistentBlockLine (tmp_id, lip->line_num,
  709.                                          afrp->report_error,
  710.                                          afrp->report_error_userdata);
  711.         }
  712.         s_LengthListFree (this_pattern);
  713.         if (has_ids) {
  714.             free (tmp_id);
  715.         }
  716.     }
  717.     s_LengthListFree (list);
  718.     return rval;
  719. }
  720. /* This function processes a block of lines and adds the sequence data from
  721.  * each line in the block to the appropriate sequence in the list.
  722.  */
  723. static void 
  724. s_ProcessBlockLines 
  725. (SAlignRawFilePtr afrp,
  726.  TLineInfoPtr     lines,
  727.  int              num_lines_in_block,
  728.  EBool            first_block)
  729. {
  730.     TLineInfoPtr  lip;
  731.     char *        linestring;
  732.     char *        cp;
  733.     char *        this_id;
  734.     int           len;
  735.     int           line_number;
  736.     EBool         this_block_has_ids;
  737.     int           pos;
  738.     this_block_has_ids = s_DoesBlockHaveIds (afrp, lines, num_lines_in_block);
  739.     s_BlockIsConsistent (afrp, lines, num_lines_in_block, this_block_has_ids,
  740.                        first_block);
  741.     for (lip = lines, line_number = 0;
  742.          lip != NULL  &&  line_number < num_lines_in_block;
  743.          lip = lip->next, line_number ++)
  744.     {
  745.         linestring = lip->data;
  746.         if (linestring != NULL) {
  747.             pos = 0;
  748.             if (this_block_has_ids) {
  749.                 len = strcspn (linestring, " tr");
  750.                 this_id = (char *) malloc (len + 1);
  751.                 if (this_id == NULL) {
  752.                     return;
  753.                 }
  754.                 strncpy (this_id, linestring, len);
  755.                 this_id [len] = 0;
  756.                 cp = linestring + len;
  757.                 pos += len;
  758.                 len = strspn (linestring, " tr");
  759.                 cp += len;
  760.                 pos += len;
  761.                 afrp->sequences = s_AddAlignRawSeqById (afrp->sequences,
  762.                                                       this_id, cp,
  763.                                                       lip->line_num,
  764.                                                       lip->line_num,
  765.                                            lip->line_offset + cp - linestring);
  766.                 free (this_id);
  767.             } else {
  768.                 if (! s_AddAlignRawSeqByIndex (afrp->sequences, line_number,
  769.                                              linestring, 
  770.                                              lip->line_num, lip->line_offset))
  771.                 {
  772.                     s_ReportBlockLengthError ("", lip->line_num,
  773.                                             afrp->block_size,
  774.                                             line_number,
  775.                                             afrp->report_error,
  776.                                             afrp->report_error_userdata);
  777.                 }
  778.             }
  779.         }
  780.     }
  781. }
  782. /* This function removes comments from the lines of an interleaved block of
  783.  * data.
  784.  */
  785. static void
  786. s_RemoveCommentsFromBlock
  787. (TLineInfoPtr first_line,
  788.  int         num_lines_in_block)
  789. {
  790.     TLineInfoPtr lip;
  791.     int         block_offset;
  792.     for (lip = first_line, block_offset = 0;
  793.          lip != NULL  &&  block_offset < num_lines_in_block;
  794.          lip = lip->next)
  795.     {                   
  796.         s_RemoveCommentFromLine (lip->data);
  797.     }
  798. }
  799. /* This function processes the interleaved block of data found at each
  800.  * location listed in afrp->offset_list.
  801.  */
  802. static void s_ProcessAlignRawFileByBlockOffsets (SAlignRawFilePtr afrp)
  803. {
  804.     int           line_counter;
  805.     TIntLinkPtr   offset_ptr;
  806.     TLineInfoPtr  lip;
  807.     EBool         first_block = eTrue;
  808.     EBool         in_taxa_comment = eFalse;
  809.  
  810.     if (afrp == NULL) {
  811.         return;
  812.     }
  813.  
  814.     line_counter = 0;
  815.     offset_ptr = afrp->offset_list;
  816.     lip = afrp->line_list;
  817.     while (lip != NULL  &&  offset_ptr != NULL
  818.            &&  (in_taxa_comment  ||  ! s_FoundStopLine (lip->data))) {
  819.         if (in_taxa_comment) {
  820.             if (strncmp (lip->data, "end;", 4) == 0) {
  821.                 in_taxa_comment = eFalse;
  822.             } 
  823.         } else if (lip->data != NULL
  824.             &&  strncmp (lip->data, "begin taxa;", 11) == 0) {
  825.             in_taxa_comment = eTrue;
  826.         }
  827.         if (line_counter == offset_ptr->ival) {
  828.             s_RemoveCommentsFromBlock (lip, afrp->block_size);
  829.             s_ProcessBlockLines (afrp, lip, afrp->block_size, first_block);
  830.             first_block = eFalse;
  831.             offset_ptr = offset_ptr->next;
  832.         }
  833.         lip = lip->next;
  834.         line_counter ++;
  835.     }
  836. }
  837. /* The following functions are used to analyze contiguous data. */
  838. static void 
  839. s_CreateSequencesBasedOnTokenPatterns 
  840. (TLineInfoPtr     token_list,
  841.  TIntLinkPtr      offset_list,
  842.  SLengthListPtr * anchorpattern,
  843.  SAlignRawFilePtr afrp)
  844. {
  845.     TLineInfoPtr lip;
  846.     int          line_counter;
  847.     TIntLinkPtr  offset_ptr, next_offset_ptr;
  848.     char *       curr_id;
  849.     TSizeInfoPtr sip;
  850.     int          pattern_line_counter;
  851.     int          curr_seg;
  852.     if (token_list == NULL  ||  offset_list == NULL
  853.         ||  anchorpattern == NULL 
  854.         ||  afrp == NULL)
  855.     {
  856.         return;
  857.     }
  858.     for (curr_seg = 0; curr_seg < afrp->num_segments; curr_seg ++)
  859.     {
  860.      if (anchorpattern [curr_seg] == NULL || anchorpattern [curr_seg]->lengthrepeats == NULL)
  861.      {
  862.      return;
  863.      }
  864.     }
  865.     line_counter = 0;
  866.     lip = token_list;
  867.     offset_ptr = offset_list;
  868.     curr_seg = 0;
  869.   
  870.     for (offset_ptr = offset_list;
  871.          offset_ptr != NULL  &&  lip != NULL;
  872.          offset_ptr = offset_ptr->next)
  873.     {
  874.         next_offset_ptr = offset_ptr->next;
  875.         while (line_counter < offset_ptr->ival - 1  &&  lip != NULL) {
  876.             lip = lip->next;
  877.             line_counter ++;
  878.         }
  879.         if (lip != NULL) {
  880.             curr_id = lip->data;
  881.             lip = lip->next;
  882.             line_counter ++;
  883.             for (sip = anchorpattern[curr_seg]->lengthrepeats;
  884.                  sip != NULL
  885.                    &&  lip != NULL
  886.                    &&  (next_offset_ptr == NULL 
  887.                      ||  line_counter < next_offset_ptr->ival - 1);
  888.                  sip = sip->next)
  889.             {
  890.                 for (pattern_line_counter = 0;
  891.                      pattern_line_counter < sip->num_appearances
  892.                          &&  lip != NULL
  893.                          &&  (next_offset_ptr == NULL
  894.                              ||  line_counter < next_offset_ptr->ival - 1);
  895.                      pattern_line_counter ++)
  896.                 {
  897.                     if ((int) strlen (lip->data) != sip->size_value) {
  898.                         s_ReportLineLengthError (curr_id, lip, sip->size_value,
  899.                                                afrp->report_error,
  900.                                                afrp->report_error_userdata);
  901.                     }
  902.                     afrp->sequences = s_AddAlignRawSeqById (afrp->sequences, 
  903.                                                           curr_id, 
  904.                                                           lip->data,
  905.                                                           lip->line_num,
  906.                                                           lip->line_num,
  907.                                                           lip->line_offset);
  908.                     lip = lip->next;
  909.                     line_counter ++;
  910.                 }
  911.             }
  912.             if (sip != NULL  &&  lip != NULL) {
  913.                 s_ReportBlockLengthError (curr_id, lip->line_num,
  914.                                         afrp->block_size,
  915.                                         line_counter - offset_ptr->ival,
  916.                                         afrp->report_error,
  917.                                         afrp->report_error_userdata);
  918.             }
  919.         }
  920.         curr_seg ++;
  921.         if (curr_seg >= afrp->num_segments)
  922.         {
  923.          curr_seg = 0;
  924.         }
  925.     }        
  926. }
  927. /* The following functions are used for analyzing contiguous data with
  928.  * marked IDs.
  929.  */
  930. /* This function creates a new LengthList pattern for each marked ID.
  931.  * After each new list is created, the function checks to see if the
  932.  * new pattern matches any pattern already in the list of patterns seen.
  933.  * If so, the function deletes the new pattern and increments 
  934.  * num_appearances for the pattern in the list, otherwise the function
  935.  * adds the new pattern to the list.
  936.  * When the list is complete, the function finds the pattern with the 
  937.  * most appearances and returns that pattern as the anchor pattern to use
  938.  * when checking sequence data blocks for consistency with one another.
  939.  */
  940. static SLengthListPtr *
  941. s_CreateAnchorPatternForMarkedIDs 
  942. (SAlignRawFilePtr afrp)
  943. {
  944.     SLengthListPtr * list;
  945.     SLengthListPtr * best;
  946.     SLengthListPtr this_pattern;
  947.     char *         cp;
  948.     TLineInfoPtr   lip;
  949.     int            curr_seg;
  950.     if (afrp == NULL) {
  951.         return NULL;
  952.     }
  953.     /* initialize length lists */
  954.     list = (SLengthListPtr *) malloc (afrp->num_segments * sizeof (SLengthListPtr));
  955.     if (list == NULL) 
  956.     {
  957.      return NULL;
  958.     }
  959.     for (curr_seg = 0; curr_seg < afrp->num_segments; curr_seg ++)
  960.     {
  961.      list[curr_seg] = NULL;
  962.     }
  963.     /* initialize best ptrs */
  964.     /* list is one element longer, to hold null terminator */
  965.     best = (SLengthListPtr *) malloc ((afrp->num_segments + 1) * sizeof (SLengthListPtr));
  966.     if (best == NULL) 
  967.     {
  968.      return NULL;
  969.     }
  970.     for (curr_seg = 0; curr_seg < afrp->num_segments + 1; curr_seg ++)
  971.     {
  972.      best[curr_seg] = NULL;
  973.     }
  974.     
  975.     /* initialize pattern */
  976.     this_pattern = NULL;
  977.     curr_seg = 0;
  978.     for (lip = afrp->line_list;
  979.          lip != NULL  &&  ! s_FoundStopLine (lip->data);
  980.          lip = lip->next)
  981.     {
  982.         if (lip->data == NULL) continue;
  983.         if (lip->data [0] == ']' || lip->data [0] == '[') continue;
  984.         if (lip->data [0] == '>') {
  985.             if (this_pattern != NULL) {
  986.                 list [curr_seg] = s_AddLengthList (list [curr_seg], this_pattern);
  987.                 curr_seg ++;
  988.                 if (curr_seg >= afrp->num_segments) 
  989.                 {
  990.                  curr_seg = 0;
  991.                 }
  992.             }
  993.             this_pattern = s_LengthListNew (NULL);
  994.             if (this_pattern == NULL) {
  995.                 for (curr_seg = 0; curr_seg < afrp->num_segments; curr_seg ++)
  996.                 {
  997.                   s_LengthListFree (list [curr_seg]);
  998.                 }
  999.                 free (list);
  1000.                 return NULL;
  1001.             }
  1002.             this_pattern->num_appearances = 1;
  1003.         } else if (this_pattern != NULL) {
  1004.             /* This section gets rid of base pair number comments */
  1005.             cp = lip->data;
  1006.             while ( isspace ((int )*cp)  ||  isdigit ((int )*cp)) {
  1007.                 cp++;
  1008.             }
  1009.             s_AddLengthRepeat (this_pattern, strlen (cp));
  1010.         }
  1011.     }
  1012.     if (this_pattern != NULL) {
  1013.         list[curr_seg] = s_AddLengthList (list [curr_seg], this_pattern);
  1014.     }
  1015.     /* Now find the pattern with the most appearances for each segment*/
  1016.     for (curr_seg = 0; curr_seg < afrp->num_segments; curr_seg++)
  1017.     {
  1018.         for (this_pattern = list [curr_seg];
  1019.              this_pattern != NULL;
  1020.              this_pattern = this_pattern->next)
  1021.         {
  1022.             if (this_pattern->num_appearances == 0) continue;
  1023.             if (best [curr_seg] == NULL 
  1024.               ||  this_pattern->num_appearances > best[curr_seg]->num_appearances)
  1025.             {
  1026.                 best[curr_seg] = this_pattern;
  1027.             }
  1028.             
  1029.         }
  1030.         /* free all patterns before and after anchor pattern */
  1031.         if (best [curr_seg] != NULL) {
  1032.             s_LengthListFree (best [curr_seg]->next);
  1033.             best [curr_seg]->next = NULL;
  1034.         }
  1035.         if (best [curr_seg] != list [curr_seg]) {
  1036.             this_pattern = list [curr_seg];
  1037.             while ( this_pattern != NULL  &&  this_pattern->next != best[curr_seg] ) {
  1038.                 this_pattern = this_pattern->next;
  1039.             }
  1040.             if (this_pattern != NULL) {
  1041.                 this_pattern->next = NULL;
  1042.                 s_LengthListFree (list [curr_seg]);
  1043.             }
  1044.         }
  1045.     }
  1046.     for (curr_seg = 0; curr_seg < afrp->num_segments; curr_seg ++)
  1047.     {
  1048.      if (best[curr_seg] == NULL) 
  1049.      {
  1050.      for (curr_seg = 0; curr_seg < afrp->num_segments; curr_seg ++)
  1051.      {
  1052.      s_LengthListFree (best [curr_seg]);
  1053.      }
  1054.      return NULL;
  1055.      }
  1056.     }
  1057.     
  1058.     return best;
  1059. }
  1060. /* This function removes base pair count comments from the data sections
  1061.  * for contiguous marked ID sequences.
  1062.  */
  1063. static void s_RemoveBasePairCountCommentsFromData (SAlignRawFilePtr afrp)
  1064. {
  1065.     TIntLinkPtr  this_offset, next_offset;
  1066.     TLineInfoPtr lip;
  1067.     int          line_count;
  1068.     char *       cp;
  1069.     if (afrp == NULL  ||  afrp->offset_list == NULL) {
  1070.         return;
  1071.     }
  1072.     this_offset = afrp->offset_list;
  1073.     next_offset = this_offset->next;
  1074.     lip = afrp->line_list;
  1075.     line_count = 0;
  1076.     while (lip != NULL  &&  this_offset != NULL) {
  1077.         if (line_count == this_offset->ival) {
  1078.             while (lip != NULL  && 
  1079.                    (next_offset == NULL
  1080.                    ||  line_count < next_offset->ival - 1)) {
  1081.                 cp = lip->data;
  1082.                 if (cp != NULL) {
  1083.                     cp += strspn (cp, " trn1234567890");
  1084.                     if (cp != lip->data) {
  1085.                         strcpy (lip->data, cp);
  1086.                     }
  1087.                 }
  1088.                 line_count ++;
  1089.                 lip = lip->next;
  1090.             }
  1091.             this_offset = this_offset->next;
  1092.             if (this_offset != NULL) {
  1093.                 next_offset = this_offset->next;
  1094.             }
  1095.         } else {
  1096.             line_count ++;
  1097.             lip = lip->next;
  1098.         }
  1099.     }
  1100. }
  1101.  
  1102. /* This function assumes that the offset_list has already been populated
  1103.  * with the locations of the data blocks.  It analyzes the blocks of data
  1104.  * to find the most frequently occurring pattern of lengths of data and then
  1105.  * uses that pattern to attach the data to the correct IDs and report any
  1106.  * errors in formatting.
  1107.  */
  1108. static void s_ProcessAlignFileRawForMarkedIDs (SAlignRawFilePtr afrp)
  1109. {
  1110.     SLengthListPtr * anchorpattern;
  1111.     if (afrp == NULL) {
  1112.         return;
  1113.     }
  1114.     s_RemoveBasePairCountCommentsFromData (afrp);
  1115.     anchorpattern = s_CreateAnchorPatternForMarkedIDs (afrp);
  1116.     if (anchorpattern == NULL  ||  afrp->offset_list == NULL) {
  1117.         return;
  1118.     }
  1119.     s_CreateSequencesBasedOnTokenPatterns (afrp->line_list, afrp->offset_list,
  1120.                                          anchorpattern, afrp);
  1121. }
  1122. /* The following functions are used for analyzing contiguous sequence data
  1123.  * without marked IDs.
  1124.  */
  1125. /* This function left-shifts a string, character by character. */
  1126. static void
  1127. s_StringLeftShift
  1128. (char * cp_from,
  1129.  char * cp_to)
  1130. {
  1131.     if (cp_from == cp_to  ||  cp_from == NULL  ||  cp_to == NULL) {
  1132.         return;
  1133.     }
  1134.     while (*cp_to != 0) {
  1135.         *cp_from = *cp_to;
  1136.         cp_from++;
  1137.         cp_to++;
  1138.     }
  1139.     *cp_from = 0;
  1140. }
  1141. /* This function removes bracketed comments from a linked list of 
  1142.  * SLineInfo structures.  The function returns a pointer to the
  1143.  * list without the comments.
  1144.  */
  1145. static TLineInfoPtr s_RemoveCommentsFromTokens (TLineInfoPtr list)
  1146. {
  1147.     TLineInfoPtr  lip;
  1148.     int           num_comment_starts;
  1149.     char *        cp_l;
  1150.     char *        cp_r;
  1151.     char *        cp;
  1152.     EBool         in_comment;
  1153.     num_comment_starts = 0;
  1154.     in_comment = eFalse;
  1155.     for (lip = list;  lip != NULL;  lip = lip->next) {
  1156.         if (lip->data == NULL) {
  1157.             lip->delete_me = eTrue;
  1158.         } else {
  1159.             cp_l = NULL;
  1160.             cp_r = NULL;
  1161.             for (cp = lip->data; *cp != 0; cp++) {
  1162.                 if (*cp == ']') {
  1163.                     if (cp_r == NULL) {
  1164.                         s_StringLeftShift (lip->data, cp + 1);
  1165.                         cp = lip->data - 1;
  1166.                     } else {
  1167.                         s_StringLeftShift (cp_r, cp + 1);
  1168.                         cp = cp_r;
  1169.                         if (cp_r > lip->data) {
  1170.                             cp_r --;
  1171.                             while (cp_r >= lip->data  &&  *cp_r != '[') {
  1172.                                 cp_r --;
  1173.                             }
  1174.                             if (cp_r < lip->data) {
  1175.                                 cp_r = NULL;
  1176.                             }
  1177.                         } else {
  1178.                             cp_r = NULL;
  1179.                         }
  1180.                     }
  1181.                     if (num_comment_starts > 0) {
  1182.                         num_comment_starts --;
  1183.                     }
  1184.                 } else if (*cp == '[') {
  1185.                     cp_r = cp;
  1186.                     num_comment_starts ++;
  1187.                 }
  1188.             }
  1189.             if (in_comment) {
  1190.                 if (num_comment_starts == 0) {
  1191.                     in_comment = eFalse;
  1192.                 } else {
  1193.                     lip->delete_me = eTrue;
  1194.                 }
  1195.             } else if (num_comment_starts > 0) {
  1196.                 cp_r = strchr (lip->data, '[');
  1197.                 if (cp_r != NULL) {
  1198.                     *cp_r = 0;
  1199.                 }
  1200.                 in_comment = eTrue;
  1201.             }
  1202.             if (lip->data [0] == 0) {
  1203.                 lip->delete_me = eTrue;
  1204.             }
  1205.         }
  1206.     }
  1207.     list = s_DeleteLineInfos (list);
  1208.     return list;
  1209. }
  1210. /* This function removes Nexus comments from a linked list of SLineInfo
  1211.  * structures.  The function returns a pointer to the list without the
  1212.  * comments.
  1213.  */
  1214. static TLineInfoPtr s_RemoveNexusCommentsFromTokens (TLineInfoPtr list) 
  1215. {
  1216.     TLineInfoPtr lip, start_lip, end_lip;
  1217.     lip = list;
  1218.     start_lip = NULL;
  1219.     end_lip = NULL;
  1220.     while (lip != NULL) {
  1221.         if (s_StringICmp (lip->data, "#NEXUS") == 0) {
  1222.             start_lip = lip;
  1223.             end_lip = lip;
  1224.             while (end_lip != NULL 
  1225.                    &&  s_StringICmp (end_lip->data, "matrix") != 0) {
  1226.                 end_lip = end_lip->next;
  1227.             }
  1228.             if (end_lip != NULL) {
  1229.                 while (start_lip != end_lip) {
  1230.                     start_lip->delete_me = eTrue;
  1231.                     start_lip = start_lip->next;
  1232.                 }
  1233.                 end_lip->delete_me = eTrue;
  1234.                 lip = end_lip->next;
  1235.             } else {
  1236.                 lip = lip->next;
  1237.             }
  1238.         } else {
  1239.             lip = lip->next;
  1240.         }
  1241.     }
  1242.     list = s_DeleteLineInfos (list);
  1243.     return list;
  1244. }
  1245. /* This function finds the number of characters that occur most frequently
  1246.  * in a token and returns a pointer to a SSizeInfo structure that
  1247.  * describes the most common length and the number of times it appears.
  1248.  */
  1249. static TSizeInfoPtr 
  1250. s_FindMostFrequentlyOccurringTokenLength
  1251. (TSizeInfoPtr list,
  1252.  int          not_this_size)
  1253. {
  1254.     TSizeInfoPtr list_ptr, new_list, best_ptr, return_best;
  1255.     new_list = NULL;
  1256.     for (list_ptr = list;  list_ptr != NULL;  list_ptr = list_ptr->next) {
  1257.         if (not_this_size != list_ptr->size_value) {
  1258.             new_list = s_AddSizeInfoAppearances (new_list,
  1259.                                                list_ptr->size_value,
  1260.                                                list_ptr->num_appearances);
  1261.         }
  1262.     }
  1263.     best_ptr = s_GetMostPopularSizeInfo (new_list);
  1264.     return_best = NULL;
  1265.     if (best_ptr != NULL) {
  1266.         return_best = s_SizeInfoNew (NULL);
  1267.         if (return_best != NULL) {
  1268.             return_best->size_value = best_ptr->size_value;
  1269.             return_best->num_appearances = best_ptr->num_appearances;
  1270.         }
  1271.     }
  1272.     s_SizeInfoFree (new_list);
  1273.     return return_best;
  1274. }
  1275. /* This function examines all instances of an anchor pattern in the data
  1276.  * and checks to see if the line immediately after the anchor pattern should
  1277.  * be used as part of the anchor pattern.  This function exists because 
  1278.  * frequently, but not always, contiguous data will consist of multiple lines
  1279.  * of data of the same length (for example, 80 characters), followed by one
  1280.  * shorter line with the remaining data.  We must also make sure that we do
  1281.  * not accidentally include the ID of the next sequence in the data of the
  1282.  * previous sequence.
  1283.  */
  1284. static void 
  1285. s_ExtendAnchorPattern 
  1286. (SLengthListPtr anchorpattern,
  1287.  TSizeInfoPtr   line_lengths)
  1288. {
  1289.     TSizeInfoPtr last_line_lengths, sip, sip_next, twoafter;
  1290.     int         best_last_line_length;
  1291.     int         anchor_line_length;
  1292.     if (anchorpattern == NULL 
  1293.         ||  anchorpattern->lengthrepeats == NULL
  1294.         ||  line_lengths == NULL) {
  1295.        return;
  1296.     }
  1297.     last_line_lengths = NULL;
  1298.     anchor_line_length = anchorpattern->lengthrepeats->size_value;
  1299.     /* also check to make sure that there's more than one line between
  1300.      * this pattern and the next pattern, otherwise the next line is the
  1301.      * ID for the next pattern and shouldn't be included in the anchor
  1302.      */
  1303.     for (sip = line_lengths;  sip != NULL;  sip = sip->next) {
  1304.         if (s_SizeInfoIsEqual (sip, anchorpattern->lengthrepeats)) {
  1305.             sip_next = sip->next;
  1306.             if (sip_next != NULL
  1307.                 &&  sip_next->size_value > 0
  1308.                 &&  sip_next->size_value != anchor_line_length
  1309.                 &&  ((twoafter = sip_next->next) == NULL
  1310.                    ||  twoafter->size_value != anchor_line_length))
  1311.             {
  1312.                 last_line_lengths = s_AddSizeInfo (last_line_lengths,
  1313.                                                  sip_next->size_value);
  1314.             }
  1315.         }
  1316.     }
  1317.     best_last_line_length = s_GetMostPopularSize (last_line_lengths);
  1318.     if (best_last_line_length > 0) {
  1319.         s_AddLengthRepeat (anchorpattern, best_last_line_length);
  1320.     }
  1321.     s_SizeInfoFree (last_line_lengths);
  1322. /* This function looks for the most frequently occurring pattern, where a 
  1323.  * pattern is considered to be N contiguous tokens of M characters.  The
  1324.  * function then checks to see if there is usually a token of a particular
  1325.  * length that immediately follows this pattern that is not the ID for the
  1326.  * next sequence.  If so, this line length is added to the pattern.
  1327.  * The function returns a pointer to this pattern.
  1328.  */
  1329. static SLengthListPtr s_FindMostPopularPattern (TSizeInfoPtr list)
  1330. {
  1331.     SLengthListPtr patternlist, newpattern;
  1332.     TSizeInfoPtr   sip, popular_line_length;
  1333.     SLengthListPtr index, best;
  1334.     int           not_this_length;
  1335.     patternlist = NULL;
  1336.     for (sip = list;  sip != NULL;  sip = sip->next) {
  1337.         if (sip->size_value > 0) {
  1338.             newpattern = s_LengthListNew (NULL);
  1339.             if (newpattern == NULL) {
  1340.                 s_LengthListFree (patternlist);
  1341.                 return NULL;
  1342.             }
  1343.             newpattern->num_appearances = 1;
  1344.             newpattern->lengthrepeats = s_SizeInfoNew (NULL);
  1345.             if (newpattern->lengthrepeats == NULL) {
  1346.                 s_LengthListFree (patternlist);
  1347.                 return NULL;
  1348.             }
  1349.             newpattern->lengthrepeats->size_value = sip->size_value;
  1350.             newpattern->lengthrepeats->num_appearances = sip->num_appearances;
  1351.             patternlist = s_AddLengthList (patternlist, newpattern);
  1352.         }
  1353.     }
  1354.     if (patternlist == NULL) {
  1355.         return NULL;
  1356.     }
  1357.     best = NULL;
  1358.     for (index = patternlist;  index != NULL;  index = index->next) {
  1359.         if (index->lengthrepeats->num_appearances < 2) {
  1360.             continue;
  1361.         }
  1362.         if (best==NULL  ||  best->num_appearances < index->num_appearances) {
  1363.             best = index;
  1364.         } else if (best->num_appearances == index->num_appearances
  1365.             &&  best->lengthrepeats->size_value < 
  1366.                                   index->lengthrepeats->size_value) {
  1367.             best = index;
  1368.         }
  1369.     }
  1370.     /* Free data in list before best pattern */
  1371.     index = patternlist;
  1372.     while ( index != NULL  &&  index->next != best ) {
  1373.          index = index->next;
  1374.     }
  1375.     if (index != NULL) {
  1376.         index->next = NULL;
  1377.         s_LengthListFree (patternlist);
  1378.     }
  1379.     /* Free data in list after best pattern */
  1380.     if (best != NULL) {
  1381.         s_LengthListFree (best->next);
  1382.         best->next = NULL;
  1383.     }
  1384.     popular_line_length = s_FindMostFrequentlyOccurringTokenLength (list, 0);
  1385.     if (best != NULL  &&  best->lengthrepeats != NULL
  1386.       &&  popular_line_length != NULL
  1387.       &&  best->lengthrepeats->size_value == popular_line_length->size_value)
  1388.     {
  1389.         not_this_length = popular_line_length->size_value;
  1390.         s_SizeInfoFree (popular_line_length);
  1391.         popular_line_length = s_FindMostFrequentlyOccurringTokenLength (list,
  1392.                                                         not_this_length);
  1393.     }
  1394.     if (best == NULL
  1395.         ||  (popular_line_length != NULL
  1396.           &&  popular_line_length->size_value > best->lengthrepeats->size_value
  1397.           &&  popular_line_length->num_appearances > best->num_appearances))
  1398.     {
  1399.         if (best == NULL) {
  1400.             best = s_LengthListNew (NULL);
  1401.             if (best == NULL) {
  1402.                 return NULL;
  1403.             }
  1404.         }
  1405.         best->lengthrepeats = s_SizeInfoNew (NULL);
  1406.         if (best->lengthrepeats == NULL) {
  1407.             return NULL;
  1408.         }
  1409.         best->lengthrepeats->size_value = popular_line_length->size_value;
  1410.         best->lengthrepeats->num_appearances = 1;
  1411.     } else {
  1412.         /* extend anchor pattern to include best length of last line */
  1413.         s_ExtendAnchorPattern (best, list);
  1414.     }
  1415.     s_SizeInfoFree (popular_line_length);
  1416.     return best;
  1417. }
  1418. /* This function creates an SIntLink list to describe the locations
  1419.  * of occurrences of the anchorpattern in the SSizeInfo list.
  1420.  * The function returns a pointer to the SIntLink list.
  1421.  */
  1422. static TIntLinkPtr 
  1423. s_CreateOffsetList 
  1424. (TSizeInfoPtr list,
  1425.  SLengthListPtr anchorpattern)
  1426. {
  1427.     int          line_counter;
  1428.     TIntLinkPtr  offset_list, new_offset;
  1429.     TSizeInfoPtr sip, prev_sip;
  1430.     if (list == NULL  ||  anchorpattern == NULL) {
  1431.         return NULL;
  1432.     }
  1433.     line_counter = 0;
  1434.     offset_list = NULL;
  1435.     prev_sip = NULL;
  1436.     for (sip = list;  sip != NULL;  sip = sip->next) {
  1437.         if (s_SizeInfoIsEqual (sip, anchorpattern->lengthrepeats)) {
  1438.             new_offset = s_IntLinkNew (line_counter, offset_list);
  1439.             if (new_offset == NULL) {
  1440.                 s_IntLinkFree (offset_list);
  1441.                 return NULL;
  1442.             }
  1443.             if (offset_list == NULL) {
  1444.                 offset_list = new_offset;
  1445.             }
  1446.         }
  1447.  
  1448.         line_counter += sip->num_appearances;
  1449.         prev_sip = sip;
  1450.     }
  1451.     return offset_list;
  1452. }
  1453. /* This function determines whether or not the number of expected sequence
  1454.  * characters are available starting at a token after line_start and stopping
  1455.  * at least one token before the next known sequence data block in the list.
  1456.  * If so, the function returns the number of the token at which the sequence
  1457.  * data begins.  Otherwise the function returns -1.
  1458.  */
  1459. static int  
  1460. s_ForecastPattern 
  1461. (int          line_start,
  1462.  int          pattern_length,
  1463.  TIntLinkPtr  next_offset,
  1464.  int          sip_offset,
  1465.  TSizeInfoPtr list)
  1466. {
  1467.     int  offset, end_offset;
  1468.     TSizeInfoPtr sip;
  1469.     int  line_counter, num_chars;
  1470.   
  1471.     if (list == NULL) {
  1472.         return -1;
  1473.     }
  1474.     for (offset = sip_offset; offset < list->num_appearances; offset++) {
  1475.         line_counter = line_start + offset;
  1476.         num_chars = list->size_value * (list->num_appearances - offset); 
  1477.         sip = list;
  1478.         while (num_chars < pattern_length
  1479.                 &&  (next_offset == NULL  ||  line_counter < next_offset->ival)
  1480.                 &&  sip->next != NULL)
  1481.         {
  1482.             sip = sip->next;
  1483.             for (end_offset = 0;
  1484.                  end_offset < sip->num_appearances
  1485.                      &&  num_chars < pattern_length
  1486.                      &&  (next_offset == NULL
  1487.                           ||  line_counter < next_offset->ival);
  1488.                  end_offset ++)
  1489.             {
  1490.                 num_chars += sip->size_value;
  1491.                 line_counter ++;
  1492.             }
  1493.         }
  1494.         if (num_chars == pattern_length) {
  1495.             return line_start + offset;
  1496.         }
  1497.     }
  1498.     return -1;
  1499. }
  1500. /* This function examines the offset list and searches for holes where blocks
  1501.  * of sequence data without the exact expected formatting might exist.  The
  1502.  * function adds the offsets of any new blocks to the list and returns a
  1503.  * pointer to the augmented offset list.
  1504.  */
  1505. static TIntLinkPtr 
  1506. s_AugmentOffsetList 
  1507. (TIntLinkPtr    offset_list,
  1508.  TSizeInfoPtr   list,
  1509.  SLengthListPtr anchorpattern)
  1510. {
  1511.     int           pattern_length;
  1512.     TSizeInfoPtr  sip;
  1513.     TIntLinkPtr   prev_offset, next_offset, new_offset;
  1514.     int           line_counter, forecast_position, line_skip;
  1515.     EBool         skipped_previous = eFalse;
  1516.     int           num_chars;
  1517.     if (list == NULL  ||  anchorpattern == NULL) {
  1518.         return offset_list;
  1519.     }
  1520.     pattern_length = 0;
  1521.     for (sip = anchorpattern->lengthrepeats;  sip != NULL;  sip = sip->next) {
  1522.         pattern_length += (sip->size_value * sip->num_appearances);
  1523.     }
  1524.     if (pattern_length == 0) {
  1525.         return offset_list;
  1526.     }
  1527.     prev_offset = NULL;
  1528.     next_offset = offset_list;
  1529.     line_counter = 0;
  1530.     sip = list;
  1531.     while (sip != NULL) {
  1532.         /* if we are somehow out of synch, don't get caught in infinite loop */
  1533.         if (next_offset != NULL  &&  line_counter > next_offset->ival) {
  1534.             next_offset = next_offset->next;
  1535.         } else if (next_offset != NULL  &&  line_counter == next_offset->ival) {
  1536.             skipped_previous = eFalse;
  1537.             prev_offset = next_offset;
  1538.             next_offset = next_offset->next;
  1539.             /* advance sip and line counter past the end of this pattern */
  1540.             num_chars = 0;
  1541.             while (num_chars < pattern_length  &&  sip != NULL) {
  1542.                 num_chars += sip->size_value * sip->num_appearances;
  1543.                 line_counter += sip->num_appearances;
  1544.                 sip = sip->next;
  1545.             }
  1546.         } else if (skipped_previous) {
  1547.             line_skip = 0;
  1548.             while (sip != NULL  &&  line_skip < sip->num_appearances 
  1549.                   &&  (next_offset == NULL
  1550.                        ||  line_counter < next_offset->ival)) {
  1551.                 /* see if we can build a pattern that matches the pattern 
  1552.                  * length we want
  1553.                  */
  1554.                 forecast_position = s_ForecastPattern (line_counter,
  1555.                                                      pattern_length,
  1556.                                                      next_offset, line_skip,
  1557.                                                      sip);
  1558.                 if (forecast_position > 0) {
  1559.                     new_offset = s_IntLinkNew (forecast_position, NULL);
  1560.                     if (new_offset == NULL) {
  1561.                         return NULL;
  1562.                     }
  1563.                     if (prev_offset == NULL) {
  1564.                         new_offset->next = offset_list;
  1565.                         offset_list = new_offset;
  1566.                     } else {
  1567.                         new_offset->next = next_offset;
  1568.                         prev_offset->next = new_offset;
  1569.                     }
  1570.                     prev_offset = new_offset;
  1571.                     /* now advance sip and line counter past the end 
  1572.                      * of the pattern we have just created
  1573.                      */
  1574.                     num_chars = 0;
  1575.                     while (num_chars < pattern_length  &&  sip != NULL) {
  1576.                         for (line_skip = 0;
  1577.                              line_skip < sip->num_appearances
  1578.                                  &&  num_chars < pattern_length;
  1579.                              line_skip++)
  1580.                         {
  1581.                             num_chars += sip->size_value;
  1582.                             line_counter ++;
  1583.                         }
  1584.                         if (line_skip == sip->num_appearances) {
  1585.                             sip = sip->next;
  1586.                             line_skip = 0;
  1587.                         }
  1588.                     }
  1589.                 } else {
  1590.                     line_counter += sip->num_appearances;
  1591.                     sip = sip->next;
  1592.                     line_skip = 0;
  1593.                 }
  1594.             }
  1595.         } else {
  1596.             skipped_previous = eTrue;
  1597.             line_counter += sip->num_appearances;
  1598.             sip = sip->next;
  1599.         }
  1600.     }
  1601.     return offset_list;
  1602. }
  1603. /* This function finds the most frequently occurring distance between
  1604.  * two sequence data blocks and returns that value.
  1605.  */
  1606. static int  s_GetMostPopularPatternLength (TIntLinkPtr offset_list)
  1607. {
  1608.     int          line_counter, best_length;
  1609.     TSizeInfoPtr pattern_length_list;
  1610.     TIntLinkPtr  offset;
  1611.     if (offset_list == NULL) {
  1612.         return -1;
  1613.     }
  1614.     line_counter = -1;
  1615.     pattern_length_list = NULL;
  1616.     for (offset = offset_list;  offset != NULL;  offset = offset->next) {
  1617.         if (line_counter != -1) {
  1618.             pattern_length_list = s_AddSizeInfo (pattern_length_list,
  1619.                                                offset->ival - line_counter);
  1620.         }
  1621.         line_counter = offset->ival;
  1622.     }
  1623.     best_length = s_GetMostPopularSize (pattern_length_list);
  1624.     s_SizeInfoFree (pattern_length_list);
  1625.     return best_length;
  1626. }
  1627. /* This function finds the most frequently appearing number of characters 
  1628.  * in a block of sequence data and returns that value.
  1629.  */
  1630. static int 
  1631. s_GetBestCharacterLength 
  1632. (TLineInfoPtr token_list,
  1633.  TIntLinkPtr  offset_list,
  1634.  int          block_length)
  1635. {
  1636.     TLineInfoPtr lip;
  1637.     TIntLinkPtr  prev_offset, new_offset;
  1638.     int          line_diff, num_chars, best_num_chars;
  1639.     TSizeInfoPtr pattern_length_list = NULL;
  1640.     if (token_list == NULL  ||  offset_list == NULL  ||  block_length < 1) {
  1641.         return -1;
  1642.     }
  1643.     /* get length of well-formatted block size */
  1644.     lip = token_list;
  1645.     prev_offset = NULL;
  1646.     for (new_offset = offset_list;
  1647.          new_offset != NULL  &&  lip != NULL;
  1648.          new_offset = new_offset->next)
  1649.     {
  1650.         if (prev_offset == NULL) {
  1651.             /* skip first tokens */
  1652.             for (line_diff = 0;
  1653.                  line_diff < new_offset->ival  &&  lip != NULL;
  1654.                  line_diff ++)
  1655.             {
  1656.                 lip = lip->next;
  1657.             }
  1658.         }
  1659.         if (prev_offset != NULL) {
  1660.             num_chars = 0;
  1661.             for (line_diff = 0;
  1662.                  line_diff < new_offset->ival - prev_offset->ival
  1663.                      &&  lip != NULL;
  1664.                  line_diff ++)
  1665.             {
  1666.                 if (line_diff < new_offset->ival - prev_offset->ival - 1) {
  1667.                     num_chars += strlen (lip->data);
  1668.                 }
  1669.                 lip = lip->next;
  1670.             }
  1671.             if (new_offset->ival - prev_offset->ival == block_length) {
  1672.                 pattern_length_list = s_AddSizeInfo (pattern_length_list,
  1673.                                                    num_chars);
  1674.             }
  1675.         }
  1676.         prev_offset = new_offset;
  1677.     }
  1678.     best_num_chars = s_GetMostPopularSize (pattern_length_list);
  1679.     if (best_num_chars == 0  &&  pattern_length_list != NULL) {
  1680.         best_num_chars = pattern_length_list->size_value;
  1681.     }
  1682.     s_SizeInfoFree (pattern_length_list);
  1683.     pattern_length_list = NULL;
  1684.     return best_num_chars;
  1685. }
  1686. static int  
  1687. s_CountCharactersBetweenOffsets 
  1688. (TLineInfoPtr list,
  1689.  int          distance,
  1690.  int          desired_num_chars)
  1691. {
  1692.     int          line_diff, num_chars, total_chars, pattern_length, num_starts;
  1693.     TLineInfoPtr lip;
  1694.     TIntLinkPtr  length_list, start_list, start_ptr, length;
  1695.     int          start_of_unknown;
  1696.     int          num_additional_offsets_needed;
  1697.     if (list == NULL  ||  distance == 0  ||  desired_num_chars == 0) {
  1698.         return 0;
  1699.     }
  1700.     /* because the first offset is the start of a known pattern, we should
  1701.      * skip to the end of that pattern and start looking for additional
  1702.      * offsets
  1703.      */
  1704.     total_chars = 0;
  1705.     for (lip = list, line_diff = 0;
  1706.          lip != NULL  &&  line_diff < distance
  1707.              &&  total_chars < desired_num_chars;
  1708.          lip = lip->next, line_diff++) {
  1709.         num_chars = strlen (lip->data);
  1710.         total_chars += num_chars;
  1711.     }
  1712.     while (lip != NULL && line_diff < distance  &&  s_IsBlank (lip->data)) {
  1713.         lip = lip->next;
  1714.         line_diff ++;
  1715.     }
  1716.     /* skip over line we would need for ID */
  1717.     if (lip != NULL) {
  1718.         lip = lip->next;
  1719.         line_diff ++;
  1720.     }
  1721.   
  1722.     if (lip == NULL  ||  line_diff == distance) {
  1723.         return 0;
  1724.     }
  1725.     num_starts = 1;
  1726.     list = lip->next;
  1727.     start_of_unknown = line_diff;
  1728.     length_list = NULL;
  1729.     total_chars = 0;
  1730.     for (lip = list;
  1731.          lip != NULL  &&  line_diff < distance;
  1732.          lip = lip->next, line_diff++)
  1733.     {
  1734.         num_chars = strlen (lip->data);
  1735.         length = s_IntLinkNew (num_chars, length_list);
  1736.         if (length_list == NULL) {
  1737.             length_list = length;
  1738.         }
  1739.         total_chars += num_chars;
  1740.     }
  1741.     /* how many offsets do we need? */
  1742.     num_additional_offsets_needed = (total_chars / desired_num_chars);
  1743.     if (num_additional_offsets_needed == 0) {
  1744.         return 0;
  1745.     }
  1746.     /* Find all the places you could start and get the exact right number
  1747.      * of characters
  1748.      */
  1749.     start_list = NULL;
  1750.     num_starts = 0;
  1751.     pattern_length = 0;
  1752.     for (start_ptr = length_list, line_diff = start_of_unknown;
  1753.          start_ptr != NULL  &&  line_diff < distance
  1754.            &&  pattern_length < distance - line_diff ;
  1755.          start_ptr = start_ptr->next, line_diff++) {
  1756.         num_chars = start_ptr->ival;
  1757.         pattern_length = 1;
  1758.         length = start_ptr->next;
  1759.         while (num_chars < desired_num_chars
  1760.                &&  pattern_length + line_diff < distance
  1761.                &&  length != NULL)
  1762.         {
  1763.             num_chars += length->ival;
  1764.             pattern_length ++;
  1765.             length = length->next;
  1766.         }
  1767.         if (num_chars == desired_num_chars) {
  1768.             length = s_IntLinkNew (line_diff, start_list);
  1769.             if (start_list == NULL) {
  1770.                 start_list = length;
  1771.             }
  1772.             num_starts ++;
  1773.         }
  1774.     }
  1775.     /* now select best set of start points */
  1776.     
  1777.     s_IntLinkFree (length_list);
  1778.     s_IntLinkFree (start_list);
  1779.     return 0;
  1780. }
  1781. /* This function inserts new block locations into the offset_list
  1782.  * by looking for likely starts of abnormal patterns.
  1783.  */
  1784. static void s_InsertNewOffsets
  1785. (TLineInfoPtr token_list,
  1786.  TIntLinkPtr  offset_list,
  1787.  int          block_length,
  1788.  int          best_num_chars,
  1789.  char *       alphabet)
  1790. {
  1791.     TLineInfoPtr lip, prev_start;
  1792.     TIntLinkPtr  prev_offset, new_offset, splice_offset;
  1793.     int          line_diff, num_chars, line_start;
  1794.     if (token_list == NULL  ||  offset_list == NULL
  1795.         ||  block_length < 1  ||  best_num_chars < 1)
  1796.     {
  1797.         return;
  1798.     }
  1799.     lip = token_list;
  1800.     prev_offset = NULL;
  1801.     for (new_offset = offset_list;
  1802.          new_offset != NULL  &&  lip != NULL;
  1803.          new_offset = new_offset->next) {
  1804.         if (prev_offset == NULL) {
  1805.             /* just advance through tokens */
  1806.             for (line_diff = 0;
  1807.                  line_diff < new_offset->ival  &&  lip != NULL;
  1808.                  line_diff ++) {
  1809.                 lip = lip->next;
  1810.             }
  1811.         } else {
  1812.             if (new_offset->ival - prev_offset->ival == block_length) {
  1813.                 /* just advance through tokens */
  1814.                 for (line_diff = 0;
  1815.                      line_diff < new_offset->ival - prev_offset->ival 
  1816.                          &&  lip != NULL;
  1817.                      line_diff ++) {
  1818.                     lip = lip->next;
  1819.                 }
  1820.             } else {
  1821.                 /* look for intermediate breaks */
  1822.                 prev_start = lip;
  1823.                 num_chars = 0;
  1824.                 for (line_diff = 0;
  1825.                      line_diff < new_offset->ival - prev_offset->ival
  1826.                          &&  lip != NULL  &&  num_chars < best_num_chars;
  1827.                      line_diff ++) {
  1828.                     num_chars += strlen (lip->data);
  1829.                     lip = lip->next;
  1830.                 }
  1831.                 if (lip == NULL) {
  1832.                   return;
  1833.                 }
  1834.                 /* set new offset at first line of next pattern */
  1835.                 line_diff ++;
  1836.                 lip = lip->next;
  1837.                 if (line_diff < new_offset->ival - prev_offset->ival) {
  1838.                     line_start = line_diff + prev_offset->ival;
  1839.                     /* advance token pointer to new piece */
  1840.                     while (line_diff < new_offset->ival - prev_offset->ival
  1841.                            &&  lip != NULL)
  1842.                     {
  1843.                         lip = lip->next;
  1844.                         line_diff ++;
  1845.                     }
  1846.                     /* insert new offset value */
  1847.                     splice_offset = s_IntLinkNew (line_start, NULL);
  1848.                     if (splice_offset == NULL) {
  1849.                         return;
  1850.                     }
  1851.                     splice_offset->next = new_offset;
  1852.                     prev_offset->next = splice_offset;
  1853.                     s_CountCharactersBetweenOffsets (lip,
  1854.                                        new_offset->ival - splice_offset->ival,
  1855.                                        best_num_chars);
  1856.                 }
  1857.             }
  1858.         }
  1859.         prev_offset = new_offset;
  1860.     }
  1861.     
  1862.     /* iterate through the last block */
  1863.     for (line_diff = 0;
  1864.          line_diff < block_length && lip != NULL; 
  1865.          line_diff ++) {
  1866.         lip = lip->next;
  1867.     }
  1868.     /* if we have room for one more sequence, or even most of one more sequence, add it */
  1869.     if (lip != NULL  &&  ! s_SkippableString (lip->data)) {
  1870.         splice_offset = s_IntLinkNew (line_diff + prev_offset->ival, prev_offset);
  1871.     }
  1872. }
  1873. /* This function returns true if the string contains digits, false otherwise */
  1874. static EBool s_ContainsDigits (char *data)
  1875. {
  1876.     char *cp;
  1877.     if (data == NULL) return eFalse;
  1878.     for (cp = data; *cp != 0; cp++) {
  1879.         if (isdigit (*cp)) {
  1880.             return eTrue;
  1881.         }
  1882.     }
  1883.     return eFalse;
  1884. }
  1885. /* This function processes the alignment file data by dividing the original
  1886.  * lines into pieces based on whitespace and looking for patterns of length 
  1887.  * in the data. 
  1888.  */
  1889. static void s_ProcessAlignFileRawByLengthPattern (SAlignRawFilePtr afrp)
  1890. {
  1891.     TLineInfoPtr   token_list;
  1892.     SLengthListPtr list;
  1893.     TLineInfoPtr   lip;
  1894.     SLengthListPtr anchorpattern[2];
  1895.     TIntLinkPtr    offset_list;
  1896.     int            best_length;
  1897.     int            best_num_chars;
  1898.     if (afrp == NULL  ||  afrp->line_list == NULL) {
  1899.         return;
  1900.     }
  1901.     token_list = s_BuildTokenList (afrp->line_list);
  1902.     token_list = s_RemoveCommentsFromTokens (token_list);
  1903.     token_list = s_RemoveNexusCommentsFromTokens (token_list);
  1904.     list = s_LengthListNew ( NULL );
  1905.     for (lip = token_list;
  1906.          lip != NULL  &&  ! s_FoundStopLine (lip->data);
  1907.          lip = lip->next)
  1908.     {
  1909.         if (s_SkippableString (lip->data)  ||  s_ContainsDigits(lip->data)) {
  1910.             s_AddLengthRepeat (list, 0);
  1911.         } else {
  1912.             s_AddLengthRepeat (list, strlen (lip->data));
  1913.         }
  1914.     }
  1915.     anchorpattern [0] = s_FindMostPopularPattern (list->lengthrepeats);
  1916.     anchorpattern [1] = NULL;
  1917.     if (anchorpattern [0] == NULL  ||  anchorpattern[0]->lengthrepeats == NULL) {
  1918.         return;
  1919.     }
  1920.     /* find anchor patterns in original list, 
  1921.      * find distances between anchor patterns 
  1922.      */
  1923.     offset_list = s_CreateOffsetList (list->lengthrepeats, anchorpattern[0]);
  1924.     offset_list = s_AugmentOffsetList (offset_list,
  1925.                                      list->lengthrepeats,
  1926.                                      anchorpattern[0]);
  1927.     /* resolve unusual distances between anchor patterns */
  1928.     best_length = s_GetMostPopularPatternLength (offset_list);
  1929.     if (best_length < 1  &&  offset_list != NULL  && offset_list->next != NULL) {
  1930.         best_length = offset_list->next->ival - offset_list->ival;
  1931.     }
  1932.     best_num_chars = s_GetBestCharacterLength (token_list, offset_list,
  1933.                                              best_length);
  1934.     s_InsertNewOffsets (token_list, offset_list, best_length, best_num_chars,
  1935.                       afrp->alphabet);
  1936.     /* use token before each anchor pattern as ID, use tokens for distance
  1937.      * between anchor patterns for sequence data
  1938.      */
  1939.     s_CreateSequencesBasedOnTokenPatterns (token_list, offset_list,
  1940.                                        anchorpattern, afrp);
  1941.   
  1942.     s_LengthListFree (anchorpattern[0]);
  1943.     s_LengthListFree (list);
  1944.     s_LineInfoFree (token_list);
  1945. }
  1946. /* The following functions are used to convert data from the internal
  1947.  * representation into the form that will be passed to the calling
  1948.  * program.  Information from the ID strings is parsed to remove
  1949.  * definition lines and organism information, the gap characters are
  1950.  * standardized to '-', the missing characters are standardizes to 'N',
  1951.  * match characters are replaced with characters from the first record,
  1952.  * and bad characters are reported.
  1953.  */
  1954. /* This function allocates memory for a new AligmentFileData structure
  1955.  * and initializes its member variables.
  1956.  */
  1957. extern TAlignmentFilePtr AlignmentFileNew (void)
  1958. {
  1959.     TAlignmentFilePtr afp;
  1960.     afp = (TAlignmentFilePtr) malloc (sizeof (SAlignmentFile));
  1961.     if (afp == NULL) {
  1962.         return NULL;
  1963.     }
  1964.     afp->num_sequences = 0;
  1965.     afp->num_organisms = 0;
  1966.     afp->num_deflines  = 0;
  1967.     afp->num_segments  = 0;
  1968.     afp->ids           = NULL;
  1969.     afp->sequences     = NULL;
  1970.     afp->organisms     = NULL;
  1971.     afp->deflines      = NULL;
  1972.     return afp;
  1973. }
  1974. /* This function frees the memory associated with an AligmentFileData
  1975.  * structure and its member variables.
  1976.  */
  1977. extern void AlignmentFileFree (TAlignmentFilePtr afp)
  1978. {
  1979.     int  index;
  1980.     if (afp == NULL) {
  1981.         return;
  1982.     }
  1983.     if (afp->ids != NULL) {
  1984.         for (index = 0;  index < afp->num_sequences;  index++) {
  1985.             free (afp->ids [index]);
  1986.         }  
  1987.         free (afp->ids);
  1988.         afp->ids = NULL;
  1989.     }
  1990.     if (afp->sequences != NULL) {
  1991.         for (index = 0;  index < afp->num_sequences;  index++) {
  1992.             free (afp->sequences [index]);
  1993.         }  
  1994.         free (afp->sequences);
  1995.         afp->sequences = NULL;
  1996.     }
  1997.     if (afp->organisms != NULL) {
  1998.         for (index = 0;  index < afp->num_organisms;  index++) {
  1999.             free (afp->organisms [index]);
  2000.         }  
  2001.         free (afp->organisms);
  2002.         afp->sequences = NULL;
  2003.     }
  2004.     if (afp->deflines != NULL) {
  2005.         for (index = 0;  index < afp->num_deflines;  index++) {
  2006.             free (afp->deflines [index]);
  2007.         }  
  2008.         free (afp->deflines);
  2009.         afp->deflines = NULL;
  2010.     }
  2011.     free (afp);
  2012. }
  2013. /* This function parses the identifier string used by the alignment file
  2014.  * to identify a sequence to find the portion of the string that is actually
  2015.  * an ID, as opposed to organism information or definition line.
  2016.  */
  2017. static char * s_GetIdFromString (char * str)
  2018. {
  2019.     char * cp;
  2020.     char * id;
  2021.     int    len;
  2022.     if (str == NULL) {
  2023.         return NULL;
  2024.     }
  2025.     cp = str;
  2026.     cp += strspn (str, " >t");
  2027.     len = strcspn (cp, " trn");
  2028.     if (len == 0) {
  2029.         return NULL;
  2030.     }
  2031.     id = malloc (len + 1);
  2032.     if (id == NULL) {
  2033.         return NULL;
  2034.     }
  2035.     strncpy (id, cp, len);
  2036.     id [ len ] = 0;
  2037.     return id;
  2038. }
  2039. /* This function pulls defline information from the ID string, if there is
  2040.  * any.
  2041.  */
  2042. static char * s_GetDeflineFromIdString (char * str)
  2043. {
  2044.     char * cp;
  2045.     int    len;
  2046.     if (str == NULL) {
  2047.         return NULL;
  2048.     }
  2049.     cp = str;
  2050.     cp += strspn (str, " >t");
  2051.     len = strcspn (cp, " trn");
  2052.     if (len == 0) {
  2053.         return NULL; 
  2054.     }
  2055.     cp += len;
  2056.     len = strspn (cp, " trn");
  2057.     if (len == 0) {
  2058.         return NULL; 
  2059.     }
  2060.     cp += len;
  2061.     if (*cp == 0) {
  2062.         return NULL;
  2063.     }
  2064.     return strdup (cp);
  2065. }
  2066. /* This function takes the ID strings read from the file and parses them
  2067.  * to obtain a defline (if there is extra text after the ID and/or
  2068.  * organism information) and to obtain the actual ID for the sequence.
  2069.  */
  2070. static EBool s_ReprocessIds (SAlignRawFilePtr afrp)
  2071. {
  2072.     TStringCountPtr list, scp;
  2073.     TAlignRawSeqPtr arsp;
  2074.     TLineInfoPtr    lip;
  2075.     char *          id;
  2076.     int             line_num;
  2077.     EBool           rval = eTrue;
  2078.     char *          defline;
  2079.     if (afrp == NULL) {
  2080.         return eFalse;
  2081.     }
  2082.     list = NULL;
  2083.     lip = afrp->deflines;
  2084.     for (arsp = afrp->sequences; arsp != NULL; arsp = arsp->next) {
  2085.         if (arsp->id_lines != NULL) {
  2086.             line_num = arsp->id_lines->ival;
  2087.         } else {
  2088.             line_num = -1;
  2089.         }
  2090.         s_RemoveOrganismCommentFromLine (arsp->id);
  2091.         id = s_GetIdFromString (arsp->id);
  2092.         if (lip == NULL) {
  2093.             defline = s_GetDeflineFromIdString (arsp->id);
  2094.             afrp->deflines = s_AddLineInfo (afrp->deflines, defline,
  2095.                                             line_num, 0);
  2096.             free (defline);
  2097.             afrp->num_deflines ++;
  2098.         }
  2099.         free (arsp->id);
  2100.         arsp->id = id;
  2101.         list = s_AddStringCount (arsp->id, line_num, list);
  2102.     }
  2103.     for (scp = list;  scp != NULL;  scp = scp->next) {
  2104.         if (scp->num_appearances > 1) {
  2105.             rval = eFalse;
  2106.             s_ReportRepeatedId (scp, afrp->report_error,
  2107.                               afrp->report_error_userdata);
  2108.         }
  2109.     }
  2110.     return rval;
  2111. }
  2112. /* This function reports unacceptable characters in a sequence.  Frequently
  2113.  * there will be more than one character of the same kind (for instance,
  2114.  * when the user has incorrectly specified a gap character), so repeated
  2115.  * characters are reported together.  The function advances the data 
  2116.  * position in the SLineInfoReader structure lirp, and returns the
  2117.  * current data position for lirp.
  2118.  */
  2119. static int 
  2120. s_ReportRepeatedBadCharsInSequence
  2121. (TLineInfoReaderPtr   lirp,
  2122.  char *               id,
  2123.  char *               reason,
  2124.  FReportErrorFunction report_error,
  2125.  void *              report_error_userdata)
  2126. {
  2127.     int  bad_line_num, bad_line_offset;
  2128.     int  num_bad_chars;
  2129.     char bad_char, curr_char;
  2130.     int  data_position;
  2131.     bad_line_num = s_LineInfoReaderGetCurrentLineNumber (lirp);
  2132.     bad_line_offset = s_LineInfoReaderGetCurrentLineOffset (lirp);
  2133.     bad_char = *lirp->curr_line_pos;
  2134.     num_bad_chars = 1;
  2135.     data_position = lirp->data_pos + 1;
  2136.     while ((curr_char = s_FindNthDataChar (lirp, data_position)) == bad_char) {
  2137.         num_bad_chars ++;
  2138.         data_position ++;
  2139.     }
  2140.     s_ReportBadCharError (id, bad_char, num_bad_chars,
  2141.                         bad_line_offset, bad_line_num, reason,
  2142.                         report_error, report_error_userdata);
  2143.     return data_position;
  2144. }
  2145. /* This function does context-sensitive replacement of the missing,
  2146.  * match, and gap characters and also identifies bad characters.
  2147.  * Gap characters found in the wrong location in the sequence are
  2148.  * considered an error.  Characters that are not missing, match, or 
  2149.  * gap characters and are not in the specified sequence alphabet are
  2150.  * reported as errors.  Match characters in the first sequence are also
  2151.  * reported as errors.
  2152.  * The function will return eTrue if any errors were found, or eFalse
  2153.  * if there were no errors.
  2154.  */
  2155. static EBool
  2156. s_FindBadDataCharsInSequence
  2157. (TAlignRawSeqPtr      arsp,
  2158.  TAlignRawSeqPtr      master_arsp,
  2159.  TSequenceInfoPtr     sip,
  2160.  FReportErrorFunction report_error,
  2161.  void *               report_error_userdata)
  2162. {
  2163.     TLineInfoReaderPtr lirp, master_lirp;
  2164.     int                data_position;
  2165.     int                middle_start;
  2166.     int                middle_end;
  2167.     char               curr_char, master_char;
  2168.     EBool              found_middle_start;
  2169.     EBool              rval = eFalse;
  2170.     EBool              match_not_in_beginning_gap;
  2171.     EBool              match_not_in_end_gap;
  2172.     if (arsp == NULL  ||  master_arsp == NULL  ||  sip == NULL) {
  2173.         return eTrue;
  2174.     }
  2175.     lirp = s_LineInfoReaderNew (arsp->sequence_data);
  2176.     if (lirp == NULL) {
  2177.         return eTrue;
  2178.     }
  2179.     if (arsp != master_arsp) {
  2180.         master_lirp = s_LineInfoReaderNew (master_arsp->sequence_data);
  2181.         if (master_lirp == NULL) {
  2182.             s_LineInfoReaderFree (lirp);
  2183.             return eTrue;
  2184.         }
  2185.     } else {
  2186.         master_lirp = NULL;
  2187.     }
  2188.     if (strcspn (sip->beginning_gap, sip->match) 
  2189.                   == strlen (sip->beginning_gap)) {
  2190.         match_not_in_beginning_gap = eTrue;
  2191.     } else {
  2192.         match_not_in_beginning_gap = eFalse;
  2193.     }
  2194.     if (strcspn (sip->end_gap, sip->match) == strlen (sip->end_gap)) {
  2195.         match_not_in_end_gap = eTrue;
  2196.     } else {
  2197.         match_not_in_end_gap = eFalse;
  2198.     }
  2199.     /* First, find middle start and end positions and report characters
  2200.      * that are not beginning gap before the middle
  2201.      */
  2202.     found_middle_start = eFalse;
  2203.     data_position = 0;
  2204.     curr_char = s_FindNthDataChar (lirp, data_position);
  2205.     while (curr_char != 0) {
  2206.         if (strchr (sip->alphabet, curr_char) != NULL) {
  2207.             if (! found_middle_start) {
  2208.                 middle_start = data_position;
  2209.                 found_middle_start = eTrue;
  2210.             }
  2211.             middle_end = data_position + 1;
  2212.             data_position ++;
  2213.         } else if (! found_middle_start) {
  2214.             if (match_not_in_beginning_gap
  2215.                 &&  strchr (sip->match, curr_char) != NULL)
  2216.             {
  2217.                 middle_start = data_position;
  2218.                 found_middle_start = eTrue;
  2219.                 middle_end = data_position + 1;
  2220.                 data_position ++;
  2221.             } else if (strchr (sip->beginning_gap, curr_char) == NULL) {
  2222.                 /* Report error - found character that is not beginning gap
  2223.                    in beginning gap */
  2224.                 data_position = s_ReportRepeatedBadCharsInSequence (lirp,
  2225.                                                                   arsp->id,
  2226.                                 "expect only beginning gap characters here",
  2227.                                 report_error, report_error_userdata);
  2228.                 rval = eTrue;
  2229.             } else {
  2230.                 *lirp->curr_line_pos = '-';
  2231.                 data_position ++;
  2232.             }
  2233.         } else {
  2234.             if (match_not_in_end_gap
  2235.                 &&  strchr (sip->match, curr_char) != NULL)
  2236.             {
  2237.                 middle_end = data_position + 1;
  2238.             }
  2239.             data_position ++;
  2240.         }
  2241.         curr_char = s_FindNthDataChar (lirp, data_position);
  2242.     }
  2243.     if (! found_middle_start) {
  2244.         s_ReportMissingSequenceData (arsp->id,
  2245.                                    report_error, report_error_userdata);
  2246.         s_LineInfoReaderFree (lirp);
  2247.         return eTrue;
  2248.     }
  2249.     /* Now complain about bad middle characters */
  2250.     data_position = middle_start;
  2251.     while (data_position < middle_end)
  2252.     {
  2253.         curr_char = s_FindNthDataChar (lirp, data_position);
  2254.         while (data_position < middle_end
  2255.                &&  strchr (sip->alphabet, curr_char) != NULL) {
  2256.             data_position ++;
  2257.             curr_char = s_FindNthDataChar (lirp, data_position);
  2258.         } 
  2259.         if (curr_char == 0  ||  data_position >= middle_end) {
  2260.             /* do nothing, done with middle */
  2261.         } else if (strchr (sip->missing, curr_char) != NULL) {
  2262.             *lirp->curr_line_pos = 'N';
  2263.             data_position ++;
  2264.         } else if (strchr (sip->match, curr_char) != NULL) {
  2265.             master_char = s_FindNthDataChar (master_lirp, data_position);
  2266.             if (master_char == 0) {
  2267.                 /* report error - unable to get master char */
  2268.                 if (master_arsp == arsp) {
  2269.                     data_position = s_ReportRepeatedBadCharsInSequence (lirp,
  2270.                                 arsp->id,
  2271.                                 "can't specify match chars in first sequence",
  2272.                                 report_error, report_error_userdata);
  2273.                 } else {
  2274.                     data_position = s_ReportRepeatedBadCharsInSequence (lirp,
  2275.                                 arsp->id,
  2276.                                 "can't find source for match chars",
  2277.                                 report_error, report_error_userdata);
  2278.                 }
  2279.                 rval = eTrue;
  2280.             } else {
  2281.                 *lirp->curr_line_pos = master_char;
  2282.                 data_position ++;
  2283.             }
  2284.         } else if (strchr (sip->middle_gap, curr_char) != NULL) {
  2285.             *lirp->curr_line_pos = '-';
  2286.             data_position ++;
  2287.         } else {
  2288.             /* Report error - found bad character in middle */
  2289.             data_position = s_ReportRepeatedBadCharsInSequence (lirp,
  2290.                                       arsp->id,
  2291.                                       "expect only sequence, missing, match,"
  2292.                                       " and middle gap characters here",
  2293.                                       report_error, report_error_userdata);
  2294.             rval = eTrue;
  2295.         }
  2296.     }
  2297.     /* Now find and complain about end characters */
  2298.     data_position = middle_end;
  2299.     curr_char = s_FindNthDataChar (lirp, data_position);
  2300.     while (curr_char != 0) {
  2301.         if (strchr (sip->end_gap, curr_char) == NULL) {
  2302.             /* Report error - found bad character in middle */
  2303.             data_position = s_ReportRepeatedBadCharsInSequence (lirp, arsp->id,
  2304.                                       "expect only end gap characters here",
  2305.                                       report_error, report_error_userdata);
  2306.             rval = eTrue;
  2307.         } else {
  2308.             *lirp->curr_line_pos = '-';
  2309.             data_position++;
  2310.         }
  2311.         curr_char = s_FindNthDataChar (lirp, data_position);
  2312.     }
  2313.     s_LineInfoReaderFree (lirp);
  2314.     s_LineInfoReaderFree (master_lirp);
  2315.     return rval;
  2316. }
  2317. /* This function examines each sequence and replaces the special characters
  2318.  * and reports bad characters in each one.  The function will return eTrue
  2319.  * if any of the sequences contained bad characters or eFalse if no errors
  2320.  * were seen.
  2321.  */
  2322. static EBool
  2323. s_s_FindBadDataCharsInSequenceList
  2324. (SAlignRawFilePtr afrp,
  2325.  TSequenceInfoPtr sip)
  2326. {
  2327.     TAlignRawSeqPtr arsp;
  2328.     EBool           rval = eFalse;
  2329.     if (afrp == NULL  ||  afrp->sequences == NULL) {
  2330.         return eTrue;
  2331.     }
  2332.     for (arsp = afrp->sequences; arsp != NULL; arsp = arsp->next) {
  2333.         if (s_FindBadDataCharsInSequence (arsp, afrp->sequences, sip,
  2334.                                         afrp->report_error,
  2335.                                         afrp->report_error_userdata)) {
  2336.             rval = eTrue;
  2337.         }
  2338.     }
  2339.     return rval;
  2340. }
  2341. /* This function examines the organisms listed for the alignment and determines
  2342.  * whether any of the organism names (including the associated comments) are
  2343.  * repeated.
  2344.  */
  2345. static EBool s_AreOrganismsUnique (SAlignRawFilePtr afrp)
  2346. {
  2347.     TLineInfoPtr    this_org, lip;
  2348.     TAlignRawSeqPtr arsp;
  2349.     EBool           are_unique;
  2350.     if (afrp == NULL  ||  afrp->num_organisms == 0
  2351.         ||  afrp->organisms == NULL) {
  2352.         return eFalse;
  2353.     }
  2354.     are_unique = eTrue;
  2355.     for (this_org = afrp->organisms;
  2356.          this_org != NULL;
  2357.          this_org = this_org->next) {
  2358.         lip = afrp->organisms;
  2359.         arsp = afrp->sequences;
  2360.         while (lip != NULL  &&  lip != this_org
  2361.                &&  strcmp (lip->data, this_org->data) != 0  &&  arsp != NULL) {
  2362.             lip = lip->next;
  2363.             arsp = arsp->next;
  2364.         }
  2365.         if (lip != NULL  &&  lip != this_org) {
  2366.             are_unique = eFalse;
  2367.             s_ReportRepeatedOrganismName (arsp->id, this_org->line_num,
  2368.                                         lip->line_num,
  2369.                                         this_org->data,
  2370.                                         afrp->report_error,
  2371.                                         afrp->report_error_userdata);
  2372.         }
  2373.     }
  2374.     return are_unique;
  2375. }
  2376. /* This function reports whether the definition lines are identical for
  2377.  * each sequence or not.
  2378.  */
  2379. static EBool s_AreDeflinesIdentical (SAlignRawFilePtr afrp)
  2380. {
  2381.     TLineInfoPtr    lip;
  2382.     TStringCountPtr list;
  2383.     EBool           rval;
  2384.     if (afrp == NULL) {
  2385.         return eFalse;
  2386.     }
  2387.     list = NULL;
  2388.     for (lip = afrp->deflines;  lip != NULL;  lip = lip->next) {
  2389.         list = s_AddStringCount (lip->data, lip->line_num, list);
  2390.     }
  2391.     rval = eTrue;
  2392.     if (list != NULL  &&  list->next != NULL) {
  2393.         rval = eFalse; 
  2394.         s_ReportDefinitionLineMismatch (afrp->report_error,
  2395.                                       afrp->report_error_userdata);
  2396.         s_ReportDefinitionLines (list, afrp->report_error,
  2397.                                afrp->report_error_userdata);
  2398.     }
  2399.     s_StringCountFree (list);
  2400.     return rval;
  2401. }
  2402. /* This function uses the contents of an SAlignRawFileData structure to
  2403.  * create an SAlignmentFile structure with the appropriate information.
  2404.  */
  2405. static TAlignmentFilePtr
  2406. s_ConvertDataToOutput 
  2407. (SAlignRawFilePtr afrp,
  2408.  TSequenceInfoPtr sip)
  2409. {
  2410.     TAlignRawSeqPtr   arsp;
  2411.     int               index;
  2412.     TSizeInfoPtr    * lengths;
  2413.     int             * best_length;
  2414.     TAlignmentFilePtr afp;
  2415.     TLineInfoPtr      lip;
  2416.     int               curr_seg;
  2417.     if (afrp == NULL  ||  sip == NULL  ||  afrp->sequences == NULL) {
  2418.         return NULL;
  2419.     }
  2420.     afp = AlignmentFileNew ();
  2421.     if (afp == NULL) {
  2422.         return NULL;
  2423.     }
  2424.     afp->num_organisms = afrp->num_organisms;
  2425.     afp->num_deflines = afrp->num_deflines;
  2426.     afp->num_segments = afrp->num_segments;
  2427.     afp->num_sequences = 0;
  2428.     lengths = NULL;
  2429.     for (arsp = afrp->sequences;  arsp != NULL;  arsp = arsp->next) {
  2430.         afp->num_sequences++;
  2431.     }
  2432.     if (afp->num_sequences != afrp->num_organisms
  2433.         && afp->num_sequences / afp->num_segments != afrp->num_organisms) {
  2434.         s_ReportMissingOrganismInfo (afrp->report_error,
  2435.                                    afrp->report_error_userdata);
  2436.     } else {
  2437.         s_AreOrganismsUnique (afrp);
  2438.     }
  2439.     afp->sequences = (char **)malloc (afp->num_sequences 
  2440.                                            * sizeof (char *));
  2441.     if (afp->sequences == NULL) {
  2442.         AlignmentFileFree (afp);
  2443.         return NULL;
  2444.     }
  2445.     afp->ids = (char **)malloc (afp->num_sequences * sizeof (char *));
  2446.     if (afp->ids == NULL) {
  2447.         AlignmentFileFree (afp);
  2448.         return NULL;
  2449.     }
  2450.     if (afp->num_organisms > 0) {
  2451.         afp->organisms = (char **) malloc (afp->num_organisms
  2452.                                                 * sizeof (char *));
  2453.         if (afp->organisms == NULL) {
  2454.             AlignmentFileFree (afp);
  2455.             return NULL;
  2456.         }
  2457.     }
  2458.     if (afp->num_deflines > 0) {
  2459.         afp->deflines = (char **)malloc (afp->num_deflines
  2460.                                               * sizeof (char *));
  2461.         if (afp->deflines == NULL) {
  2462.             AlignmentFileFree (afp);
  2463.             return NULL;
  2464.         }
  2465.     }
  2466.     /* copy in deflines */
  2467.     for (lip = afrp->deflines, index = 0;
  2468.          lip != NULL  &&  index < afp->num_deflines;
  2469.          lip = lip->next, index++) {
  2470.         if (lip->data == NULL) {
  2471.             afp->deflines [index] = NULL;
  2472.         } else {
  2473.             afp->deflines [index] = strdup (lip->data);
  2474.         }
  2475.     }
  2476.     while (index < afp->num_deflines) {
  2477.         afp->deflines [index ++] = NULL;
  2478.     }
  2479.     /* copy in organism information */
  2480.     for (lip = afrp->organisms, index = 0;
  2481.          lip != NULL  &&  index < afp->num_organisms;
  2482.          lip = lip->next, index++) {
  2483.         afp->organisms [index] = strdup (lip->data);
  2484.     }
  2485.   
  2486.     /* we need to store length information about different segments separately */
  2487.     lengths = (TSizeInfoPtr *) malloc (sizeof (TSizeInfoPtr) * afrp->num_segments);
  2488.     if (lengths == NULL) {
  2489.      AlignmentFileFree (afp);
  2490.         return NULL;
  2491.     }
  2492.     best_length = (int *) malloc (sizeof (int) * afrp->num_segments);
  2493.     if (best_length == NULL) {
  2494.      free (lengths);
  2495.      AlignmentFileFree (afp);
  2496.      return NULL;
  2497.     }
  2498.     for (curr_seg = 0; curr_seg < afrp->num_segments; curr_seg ++) {
  2499.      lengths [curr_seg] = NULL;
  2500.      best_length [curr_seg] = 0;
  2501.     }
  2502.     
  2503.     /* copy in sequence data */
  2504.     curr_seg = 0;
  2505.     for (arsp = afrp->sequences, index = 0;
  2506.          arsp != NULL  &&  index < afp->num_sequences;
  2507.          arsp = arsp->next, index++) {
  2508.         afp->sequences [index] = 
  2509.                     s_LineInfoMergeAndStripSpaces (arsp->sequence_data);
  2510.         if (afp->sequences [index] != NULL) {
  2511.             lengths [curr_seg] = s_AddSizeInfo (lengths [curr_seg], strlen (afp->sequences [index]));
  2512.         }
  2513.         afp->ids [index] = strdup (arsp->id);
  2514.         curr_seg ++;
  2515.         if (curr_seg >= afrp->num_segments) {
  2516.          curr_seg = 0;
  2517.         }
  2518.     }
  2519.     for (curr_seg = 0; curr_seg < afrp->num_segments; curr_seg ++)
  2520.     {
  2521.         best_length [curr_seg] = s_GetMostPopularSize (lengths [curr_seg]);
  2522.         if (best_length [curr_seg] == 0  &&  lengths [curr_seg] != NULL) {
  2523.             best_length [curr_seg] = lengths [curr_seg]->size_value;
  2524.         }   
  2525.     }
  2526.     curr_seg = 0;
  2527.     for (index = 0;  index < afp->num_sequences;  index++) {
  2528.         if (afp->sequences [index] == NULL) {
  2529.             s_ReportMissingSequenceData (afp->ids [index],
  2530.                                        afrp->report_error,
  2531.                                        afrp->report_error_userdata);
  2532.         } else if ((int) strlen (afp->sequences [index]) != best_length [curr_seg]) {
  2533.             s_ReportBadSequenceLength (afp->ids [index], best_length [curr_seg],
  2534.                                      strlen (afp->sequences [index]),
  2535.                                      afrp->report_error,
  2536.                                      afrp->report_error_userdata);
  2537.         }
  2538.         curr_seg ++;
  2539.         if (curr_seg >= afrp->num_segments) {
  2540.          curr_seg = 0;
  2541.         }
  2542.     }
  2543.     if (afrp->expected_num_sequence > 0
  2544.       &&  afrp->expected_num_sequence != afp->num_sequences)
  2545.     {
  2546.         s_ReportIncorrectNumberOfSequences (afrp->expected_num_sequence,
  2547.                                           afp->num_sequences,
  2548.                                           afrp->report_error,
  2549.                                           afrp->report_error_userdata);
  2550.     }
  2551.     if (afrp->expected_sequence_len > 0
  2552.       &&  afrp->expected_sequence_len != best_length [0])
  2553.     {
  2554.         s_ReportIncorrectSequenceLength (afrp->expected_sequence_len,
  2555.                                        best_length [0],
  2556.                                        afrp->report_error,
  2557.                                        afrp->report_error_userdata);
  2558.     }
  2559.     
  2560.     free (best_length);
  2561.     for (curr_seg = 0; curr_seg < afrp->num_segments; curr_seg ++)
  2562.     {
  2563.         s_SizeInfoFree (lengths [curr_seg]);
  2564.     }
  2565.     free (lengths);
  2566.     
  2567.     return afp;
  2568. }
  2569. /* This is the function called by the calling program to read an alignment
  2570.  * file.  The readfunc argument is a function pointer supplied by the 
  2571.  * calling program which this library will use to read in data from the
  2572.  * file one line at a time.  The fileuserdata argument is a pointer to 
  2573.  * data used by the calling program's readfunc function and will be passed
  2574.  * back with each call to readfunc.
  2575.  * The errfunc argument is a function pointer supplied by the calling
  2576.  * program for reporting errors.  The erroruserdata argument is a pointer
  2577.  * to data used by the calling program's errfunc function and will be
  2578.  * passed back with each call to readfunc.
  2579.  * The sequence_info argument contains the sequence alphabet and missing,
  2580.  * match, and gap characters to use in interpreting the sequence data.
  2581.  */
  2582. extern TAlignmentFilePtr 
  2583. ReadAlignmentFile 
  2584. (FReadLineFunction readfunc,
  2585.  void * fileuserdata,
  2586.  FReportErrorFunction errfunc,
  2587.  void * erroruserdata,
  2588.  TSequenceInfoPtr sequence_info)
  2589. {
  2590.     SAlignRawFilePtr afrp;
  2591.     TAlignmentFilePtr afp;
  2592.     if (sequence_info == NULL  ||  sequence_info->alphabet == NULL) {
  2593.         return NULL;
  2594.     }
  2595.     afrp = s_ReadAlignFileRaw ( readfunc, fileuserdata, sequence_info,
  2596.                               errfunc, erroruserdata);
  2597.     if (afrp == NULL) {
  2598.         return NULL;
  2599.     }
  2600.     if (afrp->block_size > 1) {
  2601.         s_ProcessAlignRawFileByBlockOffsets (afrp);
  2602.     } else if (afrp->marked_ids) {
  2603.         s_ProcessAlignFileRawForMarkedIDs (afrp);
  2604.     } else {
  2605.         s_ProcessAlignFileRawByLengthPattern (afrp);
  2606.     }
  2607.     s_ReprocessIds (afrp);
  2608. #if 0 /* this step was removed by indexer request */
  2609.     /* Note - have to check deflines after reprocessing IDs */
  2610.     s_AreDeflinesIdentical (afrp);
  2611. #endif
  2612.     if (s_s_FindBadDataCharsInSequenceList (afrp, sequence_info)) {
  2613.         s_AlignFileRawFree (afrp);
  2614.         return NULL;
  2615.     }
  2616.     afp = s_ConvertDataToOutput (afrp, sequence_info);
  2617.     s_AlignFileRawFree (afrp);
  2618.   
  2619.     return afp;
  2620. }
  2621. /*
  2622.  * ===========================================================================
  2623.  * $Log: alnread.c,v $
  2624.  * Revision 1000.1  2004/06/01 19:41:15  gouriano
  2625.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.10
  2626.  *
  2627.  * Revision 1.10  2004/05/20 19:40:24  bollin
  2628.  * Made chnages to allow reading of alignments of segmented sets.
  2629.  * Also added warnings for when organism lines may be present but improperly
  2630.  * formatted.
  2631.  *
  2632.  * Revision 1.9  2004/03/16 21:05:15  bollin
  2633.  * Added some improvements to the portion of the alignment reader that deals
  2634.  * with contiguous alignments that do not have a '>' at the beginning of each
  2635.  * ID.
  2636.  *
  2637.  * Revision 1.8  2004/03/16 16:25:38  bollin
  2638.  * Added function to recognize a file as ASN.1 and reject immediately
  2639.  *
  2640.  * Revision 1.7  2004/03/09 21:27:39  bollin
  2641.  * in s_InsertNewOffsets, if the list ends while searching for the next pattern, exit immediately (prevents NULL pointer access)
  2642.  *
  2643.  * Revision 1.6  2004/03/04 19:15:07  bollin
  2644.  * file reading now skips over multi-line bracketed comments
  2645.  *
  2646.  * Revision 1.5  2004/03/04 16:29:32  bollin
  2647.  * added skip of taxa comment for PAUP format alignment files
  2648.  *
  2649.  * Revision 1.4  2004/02/10 16:15:13  bollin
  2650.  * now checks for unused lines when finding interleaved blocks, will reject and try other methods if unused lines found after first block found.
  2651.  *
  2652.  * Revision 1.3  2004/02/05 16:29:32  bollin
  2653.  * smarter function for skipping NEXUS comment lines
  2654.  *
  2655.  * Revision 1.2  2004/02/04 19:49:11  bollin
  2656.  * fixed infinite loop condition in s_AugmentOffsetList, properly skip over first non-space column when looking for interleaved block patterns in s_ReadAlignFileRaw
  2657.  *
  2658.  * Revision 1.1  2004/02/03 16:47:02  ucko
  2659.  * Add Colleen Bollin's Toolkit-independent alignment reader.
  2660.  *
  2661.  * Revision 1.38  2004/01/30 22:46:08  bollin
  2662.  * renamed defined variable, fixed typo in comment
  2663.  *
  2664.  * Revision 1.37  2004/01/30 21:48:14  bollin
  2665.  * changes for compatibility with Windows
  2666.  *
  2667.  * Revision 1.36  2004/01/30 21:33:41  bollin
  2668.  * replaced strncasecmp and strncase function calls
  2669.  *
  2670.  * Revision 1.35  2004/01/29 19:16:27  bollin
  2671.  * use EBool for boolean values
  2672.  *
  2673.  * Revision 1.34  2004/01/29 17:58:11  bollin
  2674.  * aligned assignment blocks in New functions
  2675.  *
  2676.  * Revision 1.33  2004/01/29 17:43:40  bollin
  2677.  * added directory specification to alnread.h include line
  2678.  *
  2679.  * Revision 1.32  2004/01/29 17:41:29  bollin
  2680.  * added comment block, id tags, log
  2681.  * 
  2682.  * ===========================================================================
  2683.  */