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

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: alnread.c,v $
  4.  * PRODUCTION Revision 1000.1  2004/06/01 19:41:15  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.10
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*
  10.  * $Id: alnread.c,v 1000.1 2004/06/01 19:41:15 gouriano Exp $
  11.  *
  12.  * ===========================================================================
  13.  *
  14.  *                            PUBLIC DOMAIN NOTICE
  15.  *               National Center for Biotechnology Information
  16.  *
  17.  *  This software/database is a "United States Government Work" under the
  18.  *  terms of the United States Copyright Act.  It was written as part of
  19.  *  the author's official duties as a United States Government employee and
  20.  *  thus cannot be copyrighted.  This software/database is freely available
  21.  *  to the public for use. The National Library of Medicine and the U.S.
  22.  *  Government have not placed any restriction on its use or reproduction.
  23.  *
  24.  *  Although all reasonable efforts have been taken to ensure the accuracy
  25.  *  and reliability of the software and data, the NLM and the U.S.
  26.  *  Government do not and cannot warrant the performance or results that
  27.  *  may be obtained by using this software or data. The NLM and the U.S.
  28.  *  Government disclaim all warranties, express or implied, including
  29.  *  warranties of performance, merchantability or fitness for any particular
  30.  *  purpose.
  31.  *
  32.  *  Please cite the author in any work or product based on this material.
  33.  *
  34.  * ===========================================================================
  35.  *
  36.  * Authors:  Colleen Bollin
  37.  *
  38.  */
  39. #include <util/creaders/alnread.h>
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <ctype.h>
  44. static const int kMaxPrintedIntLen = 10;
  45. #define MAX_PRINTED_INT_LEN_PLUS_ONE 11
  46. typedef enum {
  47.     eTrue = -1,
  48.     eFalse = 0
  49. } EBool;
  50. /* structures used internally */
  51. typedef struct SLineInfo {
  52.     char *             data;
  53.     int                line_num;
  54.     int                line_offset;
  55.     EBool              delete_me;
  56.     struct SLineInfo * next;
  57. } SLineInfo, * TLineInfoPtr;
  58. typedef struct SLineInfoReader {
  59.     TLineInfoPtr first_line;
  60.     TLineInfoPtr curr_line;
  61.     char *       curr_line_pos;
  62.     int          data_pos;
  63. } SLineInfoReader, * TLineInfoReaderPtr;
  64. typedef struct SIntLink {
  65.     int               ival;
  66.     struct SIntLink * next;
  67. } SIntLink, * TIntLinkPtr;
  68. typedef struct SStringCount {
  69.     char *                string;
  70.     int                   num_appearances;
  71.     TIntLinkPtr           line_numbers;
  72.     struct SStringCount * next;
  73. } SStringCount, * TStringCountPtr;
  74. typedef struct SSizeInfo {
  75.     int                size_value;
  76.     int                num_appearances;
  77.     struct SSizeInfo * next;
  78. } SSizeInfo, * TSizeInfoPtr;
  79. typedef struct SLengthList {
  80.     TSizeInfoPtr         lengthrepeats;
  81.     int                  num_appearances;
  82.     struct SLengthList * next;
  83. } SLengthListData, * SLengthListPtr;
  84.  
  85. typedef struct SCommentLoc {
  86.   char *               start;
  87.   char *               end;
  88.   struct SCommentLoc * next;
  89. } SCommentLoc, * TCommentLocPtr;
  90. typedef struct SBracketedCommentList 
  91. {
  92. TLineInfoPtr                   comment_lines;
  93. struct SBracketedCommentList * next;
  94. } SBracketedCommentList, * TBracketedCommentListPtr;
  95. typedef struct SAlignRawSeq {
  96.     char *                id;
  97.     TLineInfoPtr          sequence_data;
  98.     TIntLinkPtr           id_lines;
  99.     struct SAlignRawSeq * next;
  100. } SAlignRawSeq, * TAlignRawSeqPtr;
  101. typedef struct SAlignFileRaw {
  102.     TLineInfoPtr         line_list;
  103.     TLineInfoPtr         organisms;
  104.     TAlignRawSeqPtr      sequences;
  105.     int                  num_organisms;
  106.     TLineInfoPtr         deflines;
  107.     int                  num_deflines;
  108.     EBool                marked_ids;
  109.     int                  block_size;
  110.     TIntLinkPtr          offset_list;
  111.     FReportErrorFunction report_error;
  112.     void *               report_error_userdata;
  113.     char *               alphabet;
  114.     int                  expected_num_sequence;
  115.     int                  expected_sequence_len;
  116.     int                  num_segments;
  117. } SAlignRawFileData, * SAlignRawFilePtr;
  118. /* These functions are used for storing and transmitting information
  119.  * about errors encountered while reading the alignment data.
  120.  */
  121. /* This function allocates memory for a new error structure and populates
  122.  * the structure with default values.
  123.  * The new structure will be added to the end of the linked list of error
  124.  * structures pointed to by list.
  125.  */
  126. extern TErrorInfoPtr ErrorInfoNew (TErrorInfoPtr list)
  127. {
  128.     TErrorInfoPtr eip, last;
  129.     eip = (TErrorInfoPtr) malloc ( sizeof (SErrorInfo));
  130.     if (eip == NULL) {
  131.         return NULL;
  132.     }
  133.     eip->category = eAlnErr_Unknown;
  134.     eip->line_num = -1;
  135.     eip->id       = NULL;
  136.     eip->message  = NULL;
  137.     eip->next     = NULL;
  138.     last = list;
  139.     while (last != NULL && last->next != NULL) {
  140.         last = last->next;
  141.     }
  142.     if (last != NULL) {
  143.         last->next = eip;
  144.     }
  145.     return eip;
  146. }
  147. /* This function recursively frees the memory associated with a list of
  148.  * error structures as well as the member variables of the error structures.
  149.  */
  150. extern void ErrorInfoFree (TErrorInfoPtr eip)
  151. {
  152.     if (eip == NULL) {
  153.         return;
  154.     }
  155.     ErrorInfoFree (eip->next);
  156.     free (eip->id);
  157.     free (eip->message);
  158.     free (eip);
  159. }
  160. /* This function creates and sends an error message regarding a NEXUS comment
  161.  * character.
  162.  */
  163. static void 
  164. s_ReportCharCommentError 
  165. (char * expected,
  166.  char    seen,
  167.  char * val_name,
  168.  FReportErrorFunction errfunc,
  169.  void *             errdata)
  170. {
  171.     TErrorInfoPtr eip;
  172.     const char * errformat = "Specified %s character does not match NEXUS"
  173.                              " comment in file (specified %s, comment %c)";
  174.     if (errfunc == NULL  ||  val_name == NULL || expected == NULL) {
  175.         return;
  176.     }
  177.     eip = ErrorInfoNew (NULL);
  178.     if (eip != NULL) {
  179.         eip->category = eAlnErr_BadFormat;
  180.         eip->message = (char *) malloc (strlen (errformat) + strlen (val_name)
  181.                                         + strlen (expected) + 2);
  182.         if (eip->message != NULL) {
  183.             sprintf (eip->message, errformat, val_name, expected, seen);
  184.         }
  185.         errfunc (eip, errdata);
  186.     }
  187. }
  188. /* This function creates and sends an error message regarding a character
  189.  * that is unexpected in sequence data.
  190.  */
  191. static void 
  192. s_ReportBadCharError 
  193. (char *  id,
  194.  char    bad_char,
  195.  int     num_bad,
  196.  int     offset,
  197.  int     line_number,
  198.  char *  reason,
  199.  FReportErrorFunction errfunc,
  200.  void *             errdata)
  201. {
  202.     TErrorInfoPtr eip;
  203.     const char *  err_format =
  204.                           "%d bad characters (%c) found at position %d (%s).";
  205.     if (errfunc == NULL  ||  num_bad == 0  ||  bad_char == 0
  206.         ||  reason == NULL) {
  207.         return;
  208.     }
  209.     eip = ErrorInfoNew (NULL);
  210.     if (eip == NULL) {
  211.         return;
  212.     }
  213.     eip->category = eAlnErr_BadData;
  214.     if (id != NULL) eip->id = strdup (id);
  215.     eip->line_num = line_number;
  216.     eip->message = (char *) malloc (strlen (err_format) + 2 * kMaxPrintedIntLen
  217.                                     + strlen (reason) + 3);
  218.     if (eip->message != NULL)
  219.     {
  220.         sprintf (eip->message, err_format, num_bad, bad_char, offset, reason);
  221.     }
  222.     errfunc (eip, errdata);
  223. }
  224.  
  225. /* This function creates and sends an error message regarding an ID that
  226.  * was found in the wrong location.
  227.  */
  228. static void 
  229. s_ReportInconsistentID 
  230. (char *               id,
  231.  int                  line_number,
  232.  FReportErrorFunction report_error,
  233.  void *              report_error_userdata)
  234. {
  235.     TErrorInfoPtr eip;
  236.     if (report_error == NULL) {
  237.         return;
  238.     }
  239.     eip = ErrorInfoNew (NULL);
  240.     if (eip == NULL) {
  241.         return;
  242.     }
  243.     eip->category = eAlnErr_BadFormat;
  244.     eip->id = strdup (id);
  245.     eip->line_num = line_number;
  246.     eip->message = strdup ("Found unexpected ID");
  247.     report_error (eip, report_error_userdata);
  248. }
  249. /* This function creates and sends an error message regarding a line
  250.  * of sequence data that was expected to have a different length.
  251.  */
  252. static void 
  253. s_ReportInconsistentBlockLine 
  254. (char *               id,
  255.  int                  line_number,
  256.  FReportErrorFunction report_error,
  257.  void *              report_error_userdata)
  258. {
  259.     TErrorInfoPtr eip;
  260.     if (report_error == NULL) {
  261.         return;
  262.     }
  263.     eip = ErrorInfoNew (NULL);
  264.     if (eip == NULL) {
  265.         return;
  266.     }
  267.     eip->category = eAlnErr_BadFormat;
  268.     eip->id = strdup (id);
  269.     eip->line_num = line_number;
  270.     eip->message = strdup ("Inconsistent block line formatting");
  271.     report_error (eip, report_error_userdata);
  272. }
  273. /* This function creates and sends an error message regarding mismatched
  274.  * definition lines
  275.  */
  276. static void
  277. s_ReportDefinitionLineMismatch
  278. (FReportErrorFunction report_error,
  279.  void *              report_error_userdata)
  280. {
  281.     TErrorInfoPtr eip;
  282.     if (report_error == NULL) {
  283.         return;
  284.     }
  285.     eip = ErrorInfoNew (NULL);
  286.     if (eip == NULL) {
  287.         return;
  288.     }
  289.     eip->category = eAlnErr_BadData;
  290.     eip->message = strdup ("Mismatched definition lines");
  291.     report_error (eip, report_error_userdata);
  292. }
  293. /* This function recursively creates and sends an error message 
  294.  * regarding the number of times items in list appear.
  295.  */
  296. static void 
  297. s_ReportDefinitionLines 
  298. (TStringCountPtr      list,
  299.  FReportErrorFunction report_error,
  300.  void *              report_error_userdata)
  301. {
  302.     TErrorInfoPtr eip;
  303.     const char *  err_null_format = "Null definition line occurs %d times";
  304.     const char *  err_format = "Definition line %s occurs %d times";
  305.     if (list == NULL  ||  report_error == NULL) {
  306.         return;
  307.     }
  308.     eip = ErrorInfoNew (NULL);
  309.     if (eip == NULL) {
  310.         return;
  311.     }
  312.     eip->category = eAlnErr_BadData;
  313.     if (list->string == NULL) {
  314.         eip->message = malloc (strlen (err_null_format)
  315.                                + kMaxPrintedIntLen + 1);
  316.         if (eip->message != NULL) {
  317.             sprintf (eip->message, err_null_format, list->num_appearances);
  318.         }
  319.     } else {
  320.         eip->message = malloc (strlen (err_format)
  321.                                + strlen (list->string)
  322.                                + kMaxPrintedIntLen + 1);
  323.         if (eip->message != NULL) {
  324.             sprintf (eip->message, err_format, list->string,
  325.                      list->num_appearances);
  326.         }
  327.     }
  328.     report_error (eip, report_error_userdata);
  329.   
  330.     s_ReportDefinitionLines (list->next, report_error, report_error_userdata);
  331. }
  332.   
  333. /* This function creates and sends an error message regarding a line of
  334.  * sequence data that was expected to be a different length.
  335.  */
  336. static void 
  337. s_ReportLineLengthError 
  338. (char *               id,
  339.  TLineInfoPtr         lip,
  340.  int                  expected_length,
  341.  FReportErrorFunction report_error,
  342.  void *               report_error_userdata)
  343. {
  344.     TErrorInfoPtr eip;
  345.     char *        msg;
  346.     const char *  format = "Expected line length %d, actual length %d";
  347.     int           len;
  348.     if (lip == NULL  ||  report_error == NULL) {
  349.         return;
  350.     }
  351.     eip = ErrorInfoNew (NULL);
  352.     if (eip == NULL) {
  353.         return;
  354.     }
  355.     eip->category = eAlnErr_BadFormat;
  356.     eip->id = strdup (id);
  357.     eip->line_num = lip->line_num;
  358.     msg = (char *) malloc (strlen (format) + kMaxPrintedIntLen + 1);
  359.     if (msg != NULL) {
  360.         if (lip->data == NULL) {
  361.             len = 0;
  362.         } else {
  363.             len = strlen (lip->data);
  364.         }
  365.         sprintf (msg, format, expected_length, len);
  366.         eip->message = msg;
  367.     }
  368.     report_error (eip, report_error_userdata);
  369. }
  370. /* This function creates and sends an error message regarding a block of
  371.  * sequence data that was expected to contain more lines.
  372.  */
  373. static void 
  374. s_ReportBlockLengthError 
  375. (char *               id,
  376.  int                  line_num,
  377.  int                  expected_num,
  378.  int                  actual_num,
  379.  FReportErrorFunction report_error,
  380.  void *              report_error_userdata)
  381. {
  382.     TErrorInfoPtr eip;
  383.     const char *  err_format = "Expected %d lines in block, found %d";
  384.     if (report_error == NULL) {
  385.         return;
  386.     }
  387.     eip = ErrorInfoNew (NULL);
  388.     if (eip == NULL) {
  389.         return;
  390.     }
  391.     eip->category = eAlnErr_BadFormat;
  392.     eip->id = strdup (id);
  393.     eip->line_num = line_num;
  394.     eip->message = malloc (strlen (err_format) + 2 * kMaxPrintedIntLen + 1);
  395.     if (eip->message != NULL) {
  396.       sprintf (eip->message, err_format, expected_num, actual_num);
  397.     }
  398.     report_error (eip, report_error_userdata);
  399. }
  400. /* This function creates and sends an error message regarding missing
  401.  * sequence data.
  402.  */
  403. static void
  404. s_ReportMissingSequenceData
  405. (char *               id,
  406.  FReportErrorFunction report_error,
  407.  void *              report_error_userdata)
  408. {
  409.     TErrorInfoPtr eip;
  410.     if (report_error == NULL) {
  411.         return;
  412.     }
  413.     eip = ErrorInfoNew (NULL);
  414.     if (eip == NULL) {
  415.         return;
  416.     }
  417.     eip->category = eAlnErr_Fatal;
  418.     eip->id = strdup (id);
  419.     eip->message = strdup ("No data found");
  420.     report_error (eip, report_error_userdata);
  421. }
  422. /* This function creates and sends an error message indicating that the
  423.  * most common length of the sequences in the file do not match a comment
  424.  * found in the file.
  425.  */
  426. static void 
  427. s_ReportBadSequenceLength 
  428. (char *               id,
  429.  int                  expected_length,
  430.  int                  actual_length,
  431.  FReportErrorFunction report_error,
  432.  void *               report_error_userdata)
  433. {
  434.     TErrorInfoPtr eip;
  435.     const char *  format_str = "Expected sequence length %d, actual length %d";
  436.     if (report_error == NULL) {
  437.         return;
  438.     }
  439.     eip = ErrorInfoNew (NULL);
  440.     if (eip == NULL) {
  441.         return;
  442.     }
  443.     eip->category = eAlnErr_BadFormat;
  444.     eip->id = strdup (id);
  445.     eip->message = malloc (strlen (format_str) + 50);
  446.     if (eip->message != NULL) {
  447.         sprintf (eip->message, format_str, expected_length, actual_length);
  448.     }
  449.     report_error (eip, report_error_userdata);
  450. }
  451. /* This function creates and sends an error message indicating that the
  452.  * number of sequences read does not match a comment in the alignment file.
  453.  */
  454. static void
  455. s_ReportIncorrectNumberOfSequences
  456. (int                  num_expected,
  457.  int                  num_found,
  458.  FReportErrorFunction report_error,
  459.  void *              report_error_userdata)
  460. {
  461.     TErrorInfoPtr eip;
  462.     const char *  err_format = "Expected %d sequences, found %d";
  463.  
  464.     if (report_error == NULL) {
  465.         return;
  466.     }
  467.     eip = ErrorInfoNew (NULL);
  468.     if (eip == NULL) {
  469.         return;
  470.     }
  471.     eip->category = eAlnErr_BadFormat;
  472.     eip->message = (char *) malloc (strlen (err_format) +
  473.                                     2 * kMaxPrintedIntLen + 1);
  474.                                      
  475.     if (eip->message != NULL)
  476.     {
  477.         sprintf (eip->message, err_format, num_expected, num_found);
  478.     }
  479.     report_error (eip, report_error_userdata);
  480. }
  481. static void
  482. s_ReportIncorrectSequenceLength 
  483. (int                 len_expected,
  484.  int                 len_found,
  485.  FReportErrorFunction report_error,
  486.  void *             report_error_userdata)
  487. {
  488.     TErrorInfoPtr eip;
  489.     const char *  err_format = "Expected sequences of length %d, found %d";
  490.     if (report_error == NULL) {
  491.         return;
  492.     }
  493.     eip = ErrorInfoNew (NULL);
  494.     if (eip == NULL) {
  495.         return;
  496.     }
  497.     eip->category = eAlnErr_BadFormat;
  498.     eip->message = (char *)malloc (strlen (err_format)
  499.                                    + 2 * kMaxPrintedIntLen + 1);
  500.     if (eip->message != NULL)
  501.     {
  502.       sprintf (eip->message, err_format, len_expected, len_found);
  503.     }
  504.     report_error (eip, report_error_userdata);
  505. }
  506. /* This function creates and sends an error message regarding a non-unique
  507.  * organism name.
  508.  */
  509. static void
  510. s_ReportRepeatedOrganismName
  511. (char *               id,
  512.  int                  line_num,
  513.  int                  second_line_num,
  514.  char *               org_name,
  515.  FReportErrorFunction report_error,
  516.  void *              report_error_userdata)
  517. {
  518.     TErrorInfoPtr eip;
  519.     const char *  err_format = "Organism name %s also appears at line %d";
  520.     if (report_error == NULL || org_name == NULL) {
  521.         return;
  522.     }
  523.     eip = ErrorInfoNew (NULL);
  524.     if (eip == NULL) {
  525.         return;
  526.     }
  527.     eip->category = eAlnErr_BadData;
  528.     eip->line_num = line_num;
  529.     if (id != NULL ) {
  530.         eip->id = strdup (id);
  531.     }
  532.     eip->message = malloc (strlen (err_format) + strlen (org_name)
  533.                            + kMaxPrintedIntLen + 1);
  534.     if (eip->message != NULL) {
  535.         sprintf (eip->message, err_format, org_name, second_line_num);
  536.     }
  537.     report_error (eip, report_error_userdata);
  538. }
  539. /* This function creates and sends an error message indicating that some or
  540.  * all of the organism information for the sequences are missing.
  541.  */
  542. static void
  543. s_ReportMissingOrganismInfo
  544. (FReportErrorFunction report_error,
  545.  void *             report_error_userdata)
  546. {
  547.     TErrorInfoPtr eip;
  548.     if (report_error == NULL) {
  549.         return;
  550.     }
  551.     eip = ErrorInfoNew (NULL);
  552.     if (eip == NULL) {
  553.         return;
  554.     }
  555.     eip->category = eAlnErr_BadData;
  556.     eip->message = strdup ("Missing organism information");
  557.     report_error (eip, report_error_userdata);
  558. }
  559. /* This function creates and sends an error message regarding an ID that is
  560.  * used for more than one sequence.
  561.  */
  562. static void 
  563. s_ReportRepeatedId 
  564. (TStringCountPtr      scp,
  565.  FReportErrorFunction report_error,
  566.  void *              report_error_userdata)
  567. {
  568.     TErrorInfoPtr  eip;
  569.     const char *   err_format = "ID %s appears in the following locations:";
  570.     char *         cp;
  571.     TIntLinkPtr    line_number;
  572.     if (report_error == NULL  ||  scp == NULL  ||  scp->string == NULL) {
  573.         return;
  574.     }
  575.     eip = ErrorInfoNew (NULL);
  576.     if (eip == NULL) {
  577.         return;
  578.     }
  579.     eip->category = eAlnErr_BadData;
  580.     eip->id = strdup (scp->string);
  581.     if (scp->line_numbers != NULL) {
  582.         eip->line_num = scp->line_numbers->ival;
  583.     }
  584.     eip->message = (char *) malloc ( strlen (err_format)
  585.                                     + strlen (scp->string)
  586.                                     + scp->num_appearances * 15
  587.                                     + 1);
  588.     if (eip->message != NULL) {
  589.         sprintf (eip->message, err_format, scp->string);
  590.         cp = eip->message + strlen (eip->message);
  591.         for (line_number = scp->line_numbers;
  592.              line_number != NULL;
  593.              line_number = line_number->next) {
  594.             sprintf (cp, " %d", line_number->ival);
  595.             cp += strlen (cp);
  596.         }
  597.     }
  598.     report_error (eip, report_error_userdata);
  599. }
  600. /* This function creates and sends an error message indicating that the file
  601.  * being read is an ASN.1 file.
  602.  */
  603. static void 
  604. s_ReportASN1Error 
  605. (FReportErrorFunction errfunc,
  606.  void *             errdata)
  607. {
  608.     TErrorInfoPtr eip;
  609.     const char * msg = "This is an ASN.1 file, "
  610.         "which cannot be read by this function.";
  611.     if (errfunc == NULL) {
  612.         return;
  613.     }
  614.     eip = ErrorInfoNew (NULL);
  615.     if (eip != NULL) {
  616.         eip->category = eAlnErr_BadData;
  617.         eip->message = (char *) malloc (strlen (msg) + 1);
  618.         if (eip->message != NULL) {
  619.             sprintf (eip->message, msg);
  620.         }
  621.         errfunc (eip, errdata);
  622.     }
  623. }
  624. /* This function reports that some sequences are inside brackets (indicating a segmented set)
  625.  * and that some sequences are outside the brackets.
  626.  */
  627. static void 
  628. s_ReportSegmentedAlignmentError 
  629. (TIntLinkPtr          offset_list,
  630.  FReportErrorFunction errfunc,
  631.  void *               errdata)
  632. {
  633.     TErrorInfoPtr eip;
  634.     const char * msg = "This file contains sequences in brackets (indicating "
  635.         "a segmented alignment) as well as sequences not in brackets at lines "
  636.         "%s.  Please either add or remove brackets to correct this problem.";
  637.     int num_lines = 0;
  638.     int         msg_len = 0;
  639.     TIntLinkPtr t;
  640.     char *      line_text_list;
  641.     char *      line_text_list_offset;
  642.     if (errfunc == NULL || offset_list == NULL) {
  643.         return;
  644.     }
  645.     for (t = offset_list; t != NULL; t = t->next)
  646.     {
  647.         num_lines ++;
  648.     }
  649.     msg_len = num_lines * (kMaxPrintedIntLen + 2);
  650.     if (num_lines > 1) 
  651.     {
  652.      msg_len += 4;
  653.     }
  654.     line_text_list = (char *) malloc (msg_len);
  655.     if (line_text_list == NULL) return;
  656.     line_text_list_offset = line_text_list;
  657.     for (t = offset_list; t != NULL; t = t->next)
  658.     {
  659.         if (t->next == NULL)
  660.         {
  661.             sprintf (line_text_list_offset, "%d", t->ival);
  662.         }
  663.         else if (num_lines == 2) 
  664.         {
  665.          sprintf (line_text_list_offset, "%d and ", t->ival);
  666.         }
  667.         else if (t->next->next == NULL)
  668.         {
  669.          sprintf (line_text_list_offset, "%d, and ", t->ival);
  670.         }
  671.         else
  672.         {
  673.          sprintf (line_text_list_offset, "%d, ", t->ival);
  674.         }
  675.         line_text_list_offset += strlen (line_text_list_offset);
  676.     }
  677.     msg_len += strlen (msg) + 1;
  678.     eip = ErrorInfoNew (NULL);
  679.     if (eip != NULL) {
  680.         eip->category = eAlnErr_BadData;
  681.         eip->message = (char *) malloc (msg_len);
  682.         if (eip->message != NULL) {
  683.             sprintf (eip->message, msg, line_text_list);
  684.         }
  685.         errfunc (eip, errdata);
  686.     }
  687.     free (line_text_list);
  688. }
  689. /* This function reports an error if a line looks like it might contain an organism comment
  690.  * but is somehow improperly formatted
  691.  */
  692. static void s_ReportOrgCommentError 
  693. (char *               linestring,
  694.  FReportErrorFunction errfunc,
  695.  void *               errdata)
  696. {
  697.     TErrorInfoPtr eip;
  698.     const char * msg = "This line may contain an improperly formatted organism description.n"
  699.                        "Organism descriptions should be of the form [org=tax name] or [organism=tax name].n";
  700.     
  701.     if (errfunc == NULL || linestring == NULL) {
  702.         return;
  703.     }
  704.                        
  705.     eip = ErrorInfoNew (NULL);
  706.     if (eip != NULL) {
  707.         eip->category = eAlnErr_BadData;
  708.         eip->message = (char *) malloc (strlen (msg) + strlen (linestring) + 1);
  709.         if (eip->message != NULL) {
  710.             strcpy (eip->message, msg);
  711.             strcat (eip->message, linestring);
  712.         }
  713.         errfunc (eip, errdata);
  714.     }
  715. }
  716.  
  717. /* This function reports that the number of segments in an alignment of
  718.  * segmented sets is inconsistent.
  719.  */
  720. static void s_ReportBadNumSegError 
  721. (int                  line_num,
  722.  int                  num_seg,
  723.  int                  num_seg_exp,
  724.  FReportErrorFunction errfunc,
  725.  void *               errdata)
  726. {
  727.     TErrorInfoPtr eip;
  728.     const char * msg = "This segmented set contains a different number of segments (%d) than expected (%d).n";
  729.     
  730.     if (errfunc == NULL) {
  731.         return;
  732.     }
  733.                        
  734.     eip = ErrorInfoNew (NULL);
  735.     if (eip != NULL) {
  736.         eip->line_num = line_num;
  737.         eip->category = eAlnErr_BadData;
  738.         eip->message = (char *) malloc (strlen (msg) + 2 * kMaxPrintedIntLen + 1);
  739.         if (eip->message != NULL) {
  740.             sprintf (eip->message, msg, num_seg, num_seg_exp);
  741.         }
  742.         errfunc (eip, errdata);
  743.     }
  744. }
  745.  
  746. /* This function allocates memory for a SSequenceInfo structure and
  747.  * initializes the member variables.  It returns a pointer to the newly
  748.  * allocated memory.
  749.  */
  750. extern TSequenceInfoPtr SequenceInfoNew (void)
  751. {
  752.     TSequenceInfoPtr sip;
  753.     sip = (TSequenceInfoPtr) malloc (sizeof (SSequenceInfo));
  754.     if (sip == NULL) {
  755.         return NULL;
  756.     }
  757.     sip->missing       = strdup ("?");
  758.     sip->beginning_gap = strdup (".");
  759.     sip->middle_gap    = strdup ("-");
  760.     sip->end_gap       = strdup (".");
  761.     sip->match         = strdup (".");
  762.     sip->alphabet      = NULL;
  763.     return sip;
  764. }
  765. /* This function frees memory associated with the member variables of
  766.  * the SSequenceInfo structure and with the structure itself.
  767.  */
  768. extern void SequenceInfoFree (TSequenceInfoPtr sip)
  769. {
  770.     if (sip == NULL) {
  771.         return;
  772.     }
  773.     free (sip->alphabet);
  774.     free (sip->missing);
  775.     free (sip->beginning_gap);
  776.     free (sip->middle_gap);
  777.     free (sip->end_gap);
  778.     free (sip->match);
  779.     sip->alphabet = NULL;
  780.     free (sip);
  781. }
  782. /* This function creates and sends an error message regarding an unused line.
  783.  */
  784. static void 
  785. s_ReportUnusedLine
  786. (int                  line_num_start,
  787.  int                  line_num_stop,
  788.  TLineInfoPtr         line_val,
  789.  FReportErrorFunction errfunc,
  790.  void *               errdata)
  791. {
  792.     TErrorInfoPtr eip;
  793.     const char * errformat1 = "Line %d could not be assigned to an interleaved block";
  794.     const char * errformat2 = "Lines %d through %d could not be assigned to an interleaved block";
  795.     const char * errformat3 = "Contents of unused line: %s";
  796.     int skip;
  797.     if (errfunc == NULL  ||  line_val == NULL) {
  798.         return;
  799.     }
  800.     eip = ErrorInfoNew (NULL);
  801.     if (eip != NULL) {
  802.         eip->category = eAlnErr_BadFormat;
  803.         eip->line_num = line_num_start;
  804.         if (line_num_start == line_num_stop) {
  805.               eip->message = (char *) malloc (strlen (errformat1)
  806.                                             + kMaxPrintedIntLen + 1);
  807.             if (eip->message != NULL) {
  808.                 sprintf (eip->message, errformat1, line_num_start);
  809.             }
  810.         } else {
  811.             eip->message = (char *) malloc (strlen (errformat2)
  812.                                             + 2 * kMaxPrintedIntLen + 1);
  813.             if (eip->message != NULL) {
  814.                 sprintf (eip->message, errformat2, line_num_start,
  815.                          line_num_stop);
  816.             }
  817.         }
  818.         errfunc (eip, errdata);
  819.     }
  820.     /* report contents of unused lines */
  821.     for (skip = line_num_start;
  822.          skip < line_num_stop + 1  &&  line_val != NULL;
  823.          skip++) {
  824.         if (line_val->data == NULL) {
  825.             continue;
  826.         }
  827.         eip = ErrorInfoNew (NULL);
  828.         if (eip != NULL) {
  829.             eip->category = eAlnErr_BadFormat;
  830.             eip->line_num = skip;
  831.             eip->message = (char *) malloc (strlen (errformat3)
  832.                                             + strlen (line_val->data) + 1);
  833.             if (eip->message != NULL) {
  834.                 sprintf (eip->message, errformat3, line_val->data);
  835.             }
  836.             errfunc (eip, errdata);
  837.         }
  838.         line_val = line_val->next;
  839.     }
  840. }
  841. /* The following functions are used to manage a linked list of integer
  842.  * values.
  843.  */
  844. /* This function creates a new SIntLink structure with a value of ival.
  845.  * The new structure will be placed at the end of list if list is not NULL.
  846.  * The function will return a pointer to the new structure.
  847.  */
  848. static TIntLinkPtr 
  849. s_IntLinkNew 
  850. (int         ival, 
  851.  TIntLinkPtr list)
  852. {
  853.     TIntLinkPtr ilp, last;
  854.     ilp = (TIntLinkPtr) malloc (sizeof (SIntLink));
  855.     if (ilp == NULL) {
  856.         return NULL;
  857.     }
  858.     ilp->ival = ival;
  859.     ilp->next = NULL;
  860.     last = list;
  861.     while (last != NULL && last->next != NULL) {
  862.         last = last->next;
  863.     }
  864.     if (last != NULL) {
  865.         last->next = ilp;
  866.     }
  867.     return ilp;
  868. }
  869. /* This function recursively frees memory associated with a linked list
  870.  * of SIntLink structures.
  871.  */
  872. static void s_IntLinkFree (TIntLinkPtr ilp)
  873. {
  874.     if (ilp == NULL) {
  875.         return;
  876.     }
  877.     s_IntLinkFree (ilp->next);
  878.     free (ilp);
  879. }
  880. /* These functions are used to accumulate and retrieve information on 
  881.  * how often a size of data (number of lines or number of characters) occurs.
  882.  */
  883. /* This function allocates space for a new SSizeInfo structure and 
  884.  * initializes its member variables.  If list is not NULL, the new structure
  885.  * is added to the end of the list.
  886.  * The function returns a pointer to the newly allocated structure.
  887.  */
  888. static TSizeInfoPtr s_SizeInfoNew (TSizeInfoPtr list)
  889. {
  890.     TSizeInfoPtr sip, last;
  891.     sip = (TSizeInfoPtr) malloc (sizeof (SSizeInfo));
  892.     if (sip == NULL) {
  893.         return NULL;
  894.     }
  895.     sip->size_value      = 0;
  896.     sip->num_appearances = 0;
  897.     sip->next            = NULL;
  898.     last = list;
  899.     while (last != NULL && last->next != NULL) {
  900.         last = last->next;
  901.     }
  902.     if (last != NULL) {
  903.         last->next = sip;
  904.     }
  905.     return sip;
  906. }
  907. /* This function recursively frees the memory associated with a linked list
  908.  * of SSizeInfo structures.
  909.  */
  910. static void s_SizeInfoFree (TSizeInfoPtr list)
  911. {
  912.     if (list == NULL) {
  913.         return;
  914.     }
  915.     s_SizeInfoFree (list->next);
  916.     list->next = NULL;
  917.     free (list);
  918. }
  919. /* This function returns eTrue if the two SSizeInfo structures have
  920.  * the same size_value and number of appearances, eFalse otherwise.
  921.  */
  922. static EBool 
  923. s_SizeInfoIsEqual 
  924. (TSizeInfoPtr s1,
  925.  TSizeInfoPtr s2)
  926. {
  927.     if (s1 == NULL
  928.       ||  s2 == NULL
  929.       ||  s1->size_value != s2->size_value
  930.       ||  s1->num_appearances != s2->num_appearances) {
  931.         return eFalse;
  932.     }
  933.     return eTrue;
  934. }
  935. /* This function searches list for a SSizeInfo structure with the
  936.  * same size_value as size_value.  If it finds such a structure, it
  937.  * adds the value of num_appearances to the num_appearances for that
  938.  * structure, otherwise the function creates a new structure at the end
  939.  * of the list with the specified values of size_value and num_appearances.
  940.  * The function returns a pointer to the list of SSizeInfo structures.
  941.  */
  942. static TSizeInfoPtr s_AddSizeInfoAppearances 
  943. (TSizeInfoPtr list,
  944.  int  size_value,
  945.  int  num_appearances)
  946. {
  947.     TSizeInfoPtr p, last;
  948.     last = NULL;
  949.     for (p = list;  p != NULL  &&  p->size_value != size_value;  p = p->next) {
  950.         last = p;
  951.     }
  952.     if (p == NULL) {
  953.         p = (TSizeInfoPtr) malloc (sizeof (SSizeInfo));
  954.         if (p == NULL) {
  955.             return NULL;
  956.         }
  957.         p->size_value = size_value;
  958.         p->num_appearances = num_appearances;
  959.         p->next = 0;
  960.         if (last == NULL) {
  961.             list = p;
  962.         } else {
  963.             last->next = p;
  964.         }
  965.     } else {
  966.         p->num_appearances += num_appearances;
  967.     }
  968.     return list;
  969. }
  970. /* This function searches list for a SSizeInfo structure with the
  971.  * same size_value as size_value.  If it finds such a structure, it
  972.  * adds one to the num_appearances for that structure, otherwise the 
  973.  * function creates a new structure at the end of the list with the 
  974.  * specified values of size_value and num_appearances.
  975.  * The function returns a pointer to the list of SSizeInfo structures.
  976.  */
  977. static TSizeInfoPtr 
  978. s_AddSizeInfo
  979. (TSizeInfoPtr list,
  980.  int  size_value)
  981. {
  982.     return s_AddSizeInfoAppearances (list, size_value, 1);
  983. }
  984. /* This function searches list for the SSizeInfo structure with the
  985.  * highest value for num_appearances.  If more than one structure exists
  986.  * with the highest value for num_appearances, the function chooses the
  987.  * value with the highest value for size_value.  The function returns a
  988.  * pointer to the structure selected based on the above criteria.
  989.  */
  990. static TSizeInfoPtr s_GetMostPopularSizeInfo (TSizeInfoPtr list)
  991. {
  992.     TSizeInfoPtr p, best;
  993.     if (list == NULL) {
  994.         return NULL;
  995.     }
  996.     best = list;
  997.     for (p = list->next;  p != NULL;  p = p->next) {
  998.       if (p->num_appearances > best->num_appearances
  999.           ||  (p->num_appearances == best->num_appearances
  1000.             &&  p->size_value > best->size_value)) {
  1001.           best = p;
  1002.       }
  1003.     }
  1004.     return best;
  1005. }
  1006. /* This function uses s_GetMostPopularSizeInfo function to find the structure
  1007.  * in list that has the highest value for num_appearances and size_value.
  1008.  * If such a structure is found and has a num_appearances value greater than
  1009.  * one, the size_value for that structure will be returned, otherwise the
  1010.  * function returns 0.
  1011.  */
  1012. static int  s_GetMostPopularSize (TSizeInfoPtr list)
  1013. {
  1014.     TSizeInfoPtr best;
  1015.     best = s_GetMostPopularSizeInfo (list);
  1016.     if (best == NULL) {
  1017.         return 0;
  1018.     }
  1019.     if (best->num_appearances > 1) {
  1020.         return best->size_value; 
  1021.     } else {
  1022.         return 0;
  1023.     }
  1024. }
  1025. /* The following functions are used to keep track of patterns of line or
  1026.  * token lengths, which will be used to identify errors in formatting.
  1027.  */
  1028. static SLengthListPtr s_LengthListNew (SLengthListPtr list)
  1029. {
  1030.     SLengthListPtr llp, last;
  1031.     llp = (SLengthListPtr) malloc (sizeof (SLengthListData));
  1032.     if (llp == NULL) {
  1033.         return NULL;
  1034.     }
  1035.     llp->lengthrepeats   = NULL;
  1036.     llp->num_appearances = 0;
  1037.     llp->next            = NULL;
  1038.     last = list;
  1039.     while (last != NULL && last->next != NULL) {
  1040.         last = last->next;
  1041.     }
  1042.     if (last != NULL) {
  1043.         last->next = llp;
  1044.     }
  1045.     return llp;
  1046. }
  1047. /* This function recursively frees memory for a list of SLengthListData
  1048.  * structures and its member variables.
  1049.  */
  1050. static void s_LengthListFree (SLengthListPtr llp)
  1051. {
  1052.     if (llp == NULL) {
  1053.         return;
  1054.     }
  1055.     s_LengthListFree (llp->next);
  1056.     s_SizeInfoFree (llp->lengthrepeats);
  1057.     free (llp);
  1058. }
  1059. /* This function examines the last SSizeInfo structure in the 
  1060.  * lengthrepeats member variable of llp.  If the last structure 
  1061.  * in the list has the same size_value value as the function argument 
  1062.  * size_value, the value of num_appearances for that SizeInforData structure
  1063.  * will be incremented.  Otherwise a new SSizeInfo structure will be
  1064.  * appended to the end of the lengthrepeats list with the specified
  1065.  * size_value and a num_appearances value of 1.
  1066.  */
  1067. static void 
  1068. s_AddLengthRepeat
  1069. (SLengthListPtr llp,
  1070.  int  size_value)
  1071. {
  1072.     TSizeInfoPtr p, last;
  1073.     if (llp == NULL) {
  1074.         return;
  1075.     }
  1076.     last = NULL;
  1077.     for (p = llp->lengthrepeats;  p != NULL;  p = p->next) {
  1078.         last = p;
  1079.     }
  1080.     if (last == NULL  ||  last->size_value != size_value) {
  1081.         p = (TSizeInfoPtr) malloc (sizeof (SSizeInfo));
  1082.         if (p == NULL) {
  1083.             return;
  1084.         }
  1085.         p->size_value = size_value;
  1086.         p->num_appearances = 1;
  1087.         p->next = 0;
  1088.         if (last == NULL) {
  1089.             llp->lengthrepeats = p;
  1090.         } else {
  1091.             last->next = p;
  1092.         }
  1093.     } else {
  1094.         last->num_appearances ++;
  1095.     }
  1096. }
  1097. /* This function examines whether two SLengthListData structures "match" - 
  1098.  * the structures match if each SSizeInfo structure in llp1->lengthrepeats
  1099.  * has the same size_value and num_appearances values as the SSizeInfo
  1100.  * structure in the corresponding list position in llp2->lenghrepeats.
  1101.  * If the two structures match, the function returns eTrue, otherwise the
  1102.  * function returns eFalse.
  1103.  */
  1104. static EBool 
  1105. s_DoLengthPatternsMatch 
  1106. (SLengthListPtr llp1,
  1107.  SLengthListPtr llp2)
  1108. {
  1109.     TSizeInfoPtr sip1, sip2;
  1110.     if (llp1 == NULL  ||  llp2 == NULL
  1111.         ||  llp1->lengthrepeats == NULL
  1112.         ||  llp2->lengthrepeats == NULL) {
  1113.         return eFalse;
  1114.     }
  1115.     for (sip1 = llp1->lengthrepeats, sip2 = llp2->lengthrepeats;
  1116.          sip1 != NULL  &&  sip2 != NULL;
  1117.          sip1 = sip1->next, sip2 = sip2->next) {
  1118.         if ( ! s_SizeInfoIsEqual (sip1, sip2)
  1119.           ||  (sip1->next == NULL  &&  sip2->next != NULL)
  1120.           ||  (sip1->next != NULL  &&  sip2->next == NULL)) {
  1121.             return eFalse;
  1122.         }
  1123.     }
  1124.     return eTrue;
  1125. }
  1126. /* This function examines a list of SLengthListData structures to see if
  1127.  * one of them matches llp.  If so, the value of num_appearances in that
  1128.  * list is incremented by one and llp is freed, otherwise llp is added
  1129.  * to the end of the list.
  1130.  * The function returns a pointer to the list of LenghtListData structures.
  1131.  */
  1132. static SLengthListPtr
  1133. s_AddLengthList
  1134. (SLengthListPtr list,
  1135.  SLengthListPtr llp)
  1136. {
  1137.     SLengthListPtr prev_llp;
  1138.     if (list == NULL) {
  1139.         list = llp;
  1140.     } else {
  1141.         prev_llp = list;
  1142.         while ( prev_llp->next  &&  ! s_DoLengthPatternsMatch (prev_llp, llp)) {
  1143.             prev_llp = prev_llp->next;
  1144.         }
  1145.         if (s_DoLengthPatternsMatch (prev_llp, llp)) {
  1146.             prev_llp->num_appearances ++;
  1147.             s_LengthListFree (llp);
  1148.         } else {
  1149.             prev_llp->next = llp;
  1150.         }
  1151.     }
  1152.     return list;
  1153. }
  1154. /* This function examines the last SLengthListData structure in list to
  1155.  * see if it matches llp.  If so, the function increments the value of
  1156.  * num_appearances for the last SLengthListData structure in list and
  1157.  * frees llp, otherwise the function appends llp to the end of list.
  1158.  * The function returns a pointer to the list of SLengthListData structures.
  1159.  */
  1160. static SLengthListPtr
  1161. s_AddPatternRepeat
  1162. (SLengthListPtr list,
  1163.  SLengthListPtr llp)
  1164. {
  1165.     SLengthListPtr prev_llp;
  1166.     if (list == NULL) {
  1167.         list = llp;
  1168.     } else {
  1169.         prev_llp = list;
  1170.         while ( prev_llp->next != NULL ) {
  1171.              prev_llp = prev_llp->next;
  1172.         }
  1173.         if (s_DoLengthPatternsMatch (prev_llp, llp)) {
  1174.             prev_llp->num_appearances ++;
  1175.             s_LengthListFree (llp);
  1176.         } else {
  1177.             prev_llp->next = llp;
  1178.         }
  1179.     }
  1180.     return list;
  1181. }
  1182. /* This set of functions is used for storing and analyzing individual lines
  1183.  * or tokens from an alignment file.
  1184.  */
  1185. /* This function allocates memory for a new SLineInfo structure and
  1186.  * initializes the structure with a saved copy of string and the specified
  1187.  * values of line_num and line_offset.
  1188.  * The function returns a pointer to the new SLineInfo structure.
  1189.  */
  1190. static TLineInfoPtr
  1191. s_LineInfoNew
  1192. (char * string,
  1193.  int    line_num,
  1194.  int    line_offset)
  1195. {
  1196.     TLineInfoPtr lip;
  1197.     lip = (TLineInfoPtr) malloc (sizeof (SLineInfo));
  1198.     if (lip == NULL) {
  1199.         return NULL;
  1200.     }
  1201.     lip->data = strdup (string);
  1202.     lip->line_num = line_num;
  1203.     lip->line_offset = line_offset;
  1204.     lip->delete_me = eFalse;
  1205.     lip->next = NULL;
  1206.     return lip;
  1207. }
  1208. /* This function recursively frees the memory associated with the structures
  1209.  * and members of the structures in a linked list of SLineInfo structures.
  1210.  */
  1211. static void s_LineInfoFree (TLineInfoPtr lip)
  1212. {
  1213.     if (lip == NULL) {
  1214.         return;
  1215.     }
  1216.     s_LineInfoFree (lip->next);
  1217.     lip->next = NULL;
  1218.     free (lip->data);
  1219.     free (lip);
  1220. }
  1221. /* This function deletes from a linked list of SLineInfo structures
  1222.  * those structures for which the delete_me flag has been set.  The function
  1223.  * returns a pointer to the beginning of the new list.
  1224.  */
  1225. static TLineInfoPtr s_DeleteLineInfos (TLineInfoPtr list)
  1226. {
  1227.     TLineInfoPtr prev = NULL;
  1228.     TLineInfoPtr lip, nextlip;
  1229.     lip = list;
  1230.     while (lip != NULL) {
  1231.         nextlip = lip->next;
  1232.         if (lip->delete_me) {
  1233.             if (prev != NULL) {
  1234.                 prev->next = lip->next;
  1235.             } else {
  1236.                 list = lip->next;
  1237.             }
  1238.             lip->next = NULL;
  1239.             s_LineInfoFree (lip);
  1240.         } else {
  1241.             prev = lip;
  1242.         }
  1243.         lip = nextlip;
  1244.     }
  1245.     return list;
  1246. }
  1247.      
  1248.  
  1249. /* This function creates a new SLineInfo structure, populates it with
  1250.  * a copy of string and the specified line_num and line_offset values,
  1251.  * and appends it to the end of "list" if list is not NULL.
  1252.  * The function will return a pointer to the newly created structure
  1253.  * if list is NULL, otherwise the function will return list.
  1254.  */
  1255. static TLineInfoPtr 
  1256. s_AddLineInfo
  1257. (TLineInfoPtr list,
  1258.  char * string,
  1259.  int    line_num,
  1260.  int    line_offset)
  1261. {
  1262.     TLineInfoPtr lip, p;
  1263.     if (string == NULL) {
  1264.         return list;
  1265.     }
  1266.     lip = s_LineInfoNew (string, line_num, line_offset);
  1267.     if (lip == NULL) {
  1268.         return NULL;
  1269.     }
  1270.     if (list == NULL) {
  1271.         list = lip;
  1272.     } else {
  1273.         p = list;
  1274.         while (p != NULL  &&  p->next != NULL) {
  1275.             p = p->next;
  1276.         }
  1277.         p->next = lip;
  1278.     }
  1279.     return list;
  1280. }
  1281. /* This function creates a new bracketed comment */
  1282. static TBracketedCommentListPtr s_BracketedCommentListNew 
  1283. (TBracketedCommentListPtr list,
  1284.  char * string,
  1285.  int    line_num,
  1286.  int    line_offset)
  1287. {
  1288.     TBracketedCommentListPtr comment;
  1289.     
  1290.     comment = (TBracketedCommentListPtr) malloc (sizeof (SBracketedCommentList));
  1291.     if (comment == NULL) {
  1292.      return NULL;
  1293.     }
  1294.     comment->comment_lines = s_LineInfoNew (string, line_num, line_offset);
  1295.     comment->next = NULL;
  1296.     
  1297.     if (list != NULL) {
  1298.      while (list->next != NULL) {
  1299.      list = list->next;
  1300.      }
  1301.      list->next = comment;
  1302.     }
  1303.     
  1304.     return comment;
  1305. }
  1306. /* This function frees a bracketed comment list. */
  1307. static void s_BracketedCommentListFree (TBracketedCommentListPtr list)
  1308. {
  1309.     if (list == NULL) {
  1310.        return;
  1311.     }
  1312.     s_BracketedCommentListFree (list->next);
  1313.     list->next = NULL;
  1314.     s_LineInfoFree (list->comment_lines);
  1315. }
  1316. /* This function adds a line to a bracketed comment. */
  1317. static void s_BracketedCommentListAddLine 
  1318. (TBracketedCommentListPtr comment,
  1319.  char                   * string,
  1320.  int                      line_num,
  1321.  int                      line_offset)
  1322. {
  1323. if (comment == NULL) {
  1324. return;
  1325. }
  1326.     comment->comment_lines = s_AddLineInfo (comment->comment_lines, string, line_num, line_offset);
  1327. }
  1328. /* This function counts the sequences found in a bracketed comment. */
  1329. static int s_CountSequencesInBracketedComment (TBracketedCommentListPtr comment)
  1330. {
  1331.     TLineInfoPtr lip;
  1332.     int          num_segments = 0;
  1333.     EBool        skipped_line_since_last_defline = eTrue;
  1334.     
  1335. if (comment == NULL || comment->comment_lines == NULL) {
  1336. return 0;
  1337. }
  1338. lip = comment->comment_lines;
  1339. /* First line must be left bracket on a line by itself */
  1340. if (lip->data[0] != '[' || strspn (lip->data + 1, " trn") != strlen (lip->data + 1))
  1341. {
  1342. return 0;
  1343. }
  1344. lip = lip->next;
  1345. while (lip != NULL && lip->next != NULL)
  1346. {
  1347. if (lip->data[0] == '>')
  1348. {
  1349. if (!skipped_line_since_last_defline) 
  1350. {
  1351. return 0;
  1352. }
  1353. else
  1354. {
  1355. num_segments ++;
  1356. skipped_line_since_last_defline = eFalse;
  1357. }
  1358. }
  1359. else 
  1360. {
  1361. skipped_line_since_last_defline = eTrue;
  1362. }
  1363. lip = lip->next;
  1364. }
  1365. /* Last line must be right bracket on a line by itself */
  1366. /* First line must be left bracket on a line by itself */
  1367. if (lip->data[0] != ']' || strspn (lip->data + 1, " trn") != strlen (lip->data + 1))
  1368. {
  1369. return 0;
  1370. }
  1371. return num_segments;
  1372. }
  1373. /* This function counts the number of sequences that appear in
  1374.  * bracketed comments.  If the number of sequences is inconsistent,
  1375.  * the function will issue error messages and return a 1, otherwise
  1376.  * the function will return the number of sequences that appear in
  1377.  * each bracketed comment.
  1378.  */
  1379. static int s_GetNumSegmentsInAlignment 
  1380. (TBracketedCommentListPtr comment_list,
  1381.  FReportErrorFunction     errfunc,
  1382.  void *                   errdata)
  1383. {
  1384.     TBracketedCommentListPtr comment;
  1385.     TSizeInfoPtr             segcount_list = NULL;
  1386.     int                      num_segments = 1;
  1387.     int                      num_segments_this_bracket;
  1388.     int                      num_segments_expected;
  1389.     TSizeInfoPtr             best;
  1390.     
  1391. if (comment_list == NULL)
  1392. {
  1393. return num_segments;
  1394. }
  1395. for (comment = comment_list; comment != NULL; comment = comment->next)
  1396. {
  1397.     num_segments_this_bracket = s_CountSequencesInBracketedComment (comment);
  1398.         segcount_list = s_AddSizeInfoAppearances (segcount_list,
  1399.                                                   num_segments_this_bracket,
  1400.                                                   1);
  1401.         if (comment != comment_list && segcount_list->next != NULL)
  1402.         {
  1403.             best = s_GetMostPopularSizeInfo (segcount_list);
  1404.             num_segments_expected = best->size_value;
  1405.          if (num_segments_expected != num_segments_this_bracket)
  1406.          {
  1407.          s_ReportBadNumSegError (comment->comment_lines->line_num,
  1408.                                  num_segments_this_bracket, num_segments_expected,
  1409.                                  errfunc, errdata);
  1410.          }
  1411.         }
  1412. }
  1413. if (segcount_list != NULL && segcount_list->next == NULL && segcount_list->size_value > 0)
  1414. {
  1415. num_segments = segcount_list->size_value;
  1416. }
  1417. s_SizeInfoFree (segcount_list);
  1418. return num_segments;
  1419. }
  1420. /* This function gets a list of the offsets of the 
  1421.  * sequences in bracketed comments.
  1422.  */
  1423. static TIntLinkPtr GetSegmentOffsetList (TBracketedCommentListPtr comment_list)
  1424. {
  1425. TIntLinkPtr              new_offset, offset_list = NULL;
  1426. TBracketedCommentListPtr comment;
  1427. TLineInfoPtr             lip;
  1428.     if (comment_list == NULL) 
  1429.     {
  1430.      return NULL;
  1431.     }
  1432.     
  1433.     for (comment = comment_list; comment != NULL; comment = comment->next)
  1434.     {
  1435.      if (s_CountSequencesInBracketedComment (comment) == 0) 
  1436.      {
  1437.      continue;
  1438.      }
  1439.      for (lip = comment->comment_lines; lip != NULL; lip = lip->next)
  1440.      {
  1441.      if (lip->data != NULL && lip->data[0] == '>') 
  1442.      {
  1443.                 new_offset = s_IntLinkNew (lip->line_num + 1, offset_list);
  1444.                 if (offset_list == NULL) offset_list = new_offset;
  1445.      }
  1446.         }
  1447.     }
  1448.     return offset_list;
  1449. }
  1450. static char * s_TokenizeString (char * str, char *delimiter, char **last)
  1451. {
  1452.     int skip;
  1453.     int length;
  1454.     if (str == NULL) {
  1455.         str = *last;
  1456.     }
  1457.     if (delimiter == NULL) {
  1458.         *last = NULL;
  1459.         return NULL;
  1460.     }
  1461.     if (str == NULL || *str == 0) {
  1462.         return NULL;
  1463.     }
  1464.     skip = strspn (str, delimiter);
  1465.     str += skip;
  1466.     length = strcspn (str, delimiter);
  1467.     *last = str + length;
  1468.     if (**last != 0) {
  1469.         **last = 0;
  1470.         (*last) ++;
  1471.     }
  1472.     return str;
  1473. }
  1474.   
  1475. /* This function creates a new list of SLineInfo structures by tokenizing
  1476.  * each data element from line_list into multiple tokens at whitespace.
  1477.  * The function returns a pointer to the new list.  The original list is
  1478.  * unchanged.
  1479.  */
  1480. static TLineInfoPtr s_BuildTokenList (TLineInfoPtr line_list)
  1481. {
  1482.     TLineInfoPtr first_token, lip;
  1483.     char *       tmp;
  1484.     char *       piece;
  1485.     char *       last;
  1486.     int          line_pos;
  1487.     first_token = NULL;
  1488.     for (lip = line_list;  lip != NULL;  lip = lip->next) {
  1489.         if (lip->data != NULL  &&  (tmp = strdup (lip->data)) != NULL) {
  1490.             piece = s_TokenizeString (tmp, " tr", &last);
  1491.             while (piece != NULL) {
  1492.                 line_pos = piece - tmp;
  1493.                 line_pos += lip->line_offset;
  1494.                 first_token = s_AddLineInfo (first_token, piece, 
  1495.                                              lip->line_num,
  1496.                                              line_pos);
  1497.                 piece = s_TokenizeString (NULL, " tr", &last);
  1498.             }
  1499.             free (tmp);
  1500.         }
  1501.     }
  1502.     return first_token;
  1503. }
  1504. /* This function takes a list of SLineInfo structures, allocates memory
  1505.  * to hold their contents contiguously, and stores their contents, minus
  1506.  * the whitespace, in the newly allocated memory.
  1507.  * The function returns a pointer to this newly allocated memory.
  1508.  */
  1509. static char * s_LineInfoMergeAndStripSpaces (TLineInfoPtr list)
  1510. {
  1511.     TLineInfoPtr lip;
  1512.     int          len;
  1513.     char *       result;
  1514.     char *       cp_to;
  1515.     char *       cp_from;
  1516.     if (list == NULL) {
  1517.         return NULL;
  1518.     }
  1519.     len = 0;
  1520.     for (lip = list;  lip != NULL;  lip = lip->next) {
  1521.         if (lip->data != NULL) {
  1522.             len += strlen (lip->data);
  1523.         }
  1524.     }
  1525.     result = (char *) malloc (len + 1);
  1526.     if (result == NULL) {
  1527.         return result;
  1528.     }
  1529.     cp_to = result;
  1530.     for (lip = list;  lip != NULL;  lip = lip->next) {
  1531.         if (lip->data != NULL) {
  1532.             cp_from = lip->data;
  1533.             while (*cp_from != 0) {
  1534.                 if (! isspace ((int )*cp_from)) {
  1535.                     *cp_to = *cp_from;
  1536.                     cp_to ++;
  1537.                 }
  1538.                 cp_from ++;
  1539.             }
  1540.         }
  1541.     }
  1542.     *cp_to = 0;
  1543.     return result;
  1544. }
  1545. /* The following functions are used to manage the SLineInfoReader
  1546.  * structure.  The intention is to allow the user to access the data
  1547.  * from a linked list of SLineInfo structures using a given position
  1548.  * in the data based on the number of sequence data characters rather than 
  1549.  * any particular line number or position in the line.  This is useful
  1550.  * for matching up a data position in a record with a match character with
  1551.  * the same data position in the first or master record.  This is also useful
  1552.  * for determining how to interpret special characters that may have
  1553.  * context-sensitive meanings.  For example, a ? could indicate a missing 
  1554.  * character if it is inside a sequence but indicate a gap if it is outside
  1555.  * a sequence.
  1556.  */
  1557. /* This function is used to advance the current data position pointer
  1558.  * for a SLineInfoReader structure past white space and blank lines
  1559.  * in sequence data.
  1560.  */
  1561. static void s_LineInfoReaderAdvancePastSpace (TLineInfoReaderPtr lirp)
  1562. {
  1563.     if (lirp->curr_line_pos == NULL) {
  1564.         return;
  1565.     }
  1566.     while ( isspace ((int ) *lirp->curr_line_pos)
  1567.            ||  *lirp->curr_line_pos == 0) {
  1568.         while ( isspace ((int )*lirp->curr_line_pos)) {
  1569.             lirp->curr_line_pos ++;
  1570.         }
  1571.         if (*lirp->curr_line_pos == 0) {
  1572.             lirp->curr_line = lirp->curr_line->next;
  1573.             while (lirp->curr_line != NULL
  1574.                    &&  lirp->curr_line->data == NULL) {
  1575.                 lirp->curr_line = lirp->curr_line->next;
  1576.             }
  1577.             if (lirp->curr_line == NULL) {
  1578.                 lirp->curr_line_pos = NULL;
  1579.                 return;
  1580.             } else {
  1581.                 lirp->curr_line_pos = lirp->curr_line->data;
  1582.             }
  1583.         }
  1584.     }
  1585. }
  1586. /* This function sets the current data position pointer to the first
  1587.  * non-whitespace character in the sequence data.
  1588.  */
  1589. static void s_LineInfoReaderReset (TLineInfoReaderPtr lirp)
  1590. {
  1591.     if (lirp == NULL) {
  1592.         return;
  1593.     }
  1594.     lirp->curr_line = lirp->first_line;
  1595.     while (lirp->curr_line != NULL  &&  lirp->curr_line->data == NULL) {
  1596.         lirp->curr_line = lirp->curr_line->next;
  1597.     }
  1598.     if (lirp->curr_line == NULL) {
  1599.         lirp->curr_line_pos = NULL;
  1600.         lirp->data_pos = -1;
  1601.     } else {
  1602.         lirp->curr_line_pos = lirp->curr_line->data;
  1603.         s_LineInfoReaderAdvancePastSpace (lirp);
  1604.         if (lirp->curr_line_pos == NULL) {
  1605.             lirp->data_pos = -1;
  1606.         } else {
  1607.             lirp->data_pos = 0;
  1608.         }
  1609.     }
  1610. }
  1611.  
  1612. /* This function creates a new SLineInfoReader structure and initializes
  1613.  * its member variables.  The current data position pointer is set to the
  1614.  * first non-whitespace character in the sequence data, and the data position
  1615.  * counter is set to zero.  The function returns a pointer to the new
  1616.  * LineInfoReader data structure.
  1617.  */
  1618. static TLineInfoReaderPtr s_LineInfoReaderNew (TLineInfoPtr line_list)
  1619. {
  1620.     TLineInfoReaderPtr lirp;
  1621.     if (line_list == NULL) {
  1622.         return NULL;
  1623.     }
  1624.     lirp = (TLineInfoReaderPtr) malloc (sizeof (SLineInfoReader));
  1625.     if (lirp == NULL) {
  1626.         return NULL;
  1627.     }
  1628.     lirp->first_line = line_list;
  1629.     s_LineInfoReaderReset (lirp);
  1630.     return lirp;
  1631. }
  1632. /* This function safely interprets the current line number of the
  1633.  * SLineInfoReader structure.  If the structure is NULL or the
  1634.  * current line is NULL (usually because the data position has been
  1635.  * advanced to the end of the available sequence data), the function
  1636.  * returns -1, since the current data position does not actually exist.
  1637.  * Otherwise, the line number of the character at the current data position
  1638.  * is returned.
  1639.  */
  1640. static int  s_LineInfoReaderGetCurrentLineNumber (TLineInfoReaderPtr lirp)
  1641. {
  1642.     if (lirp == NULL  ||  lirp->curr_line == NULL) {
  1643.         return -1;
  1644.     } else {
  1645.         return lirp->curr_line->line_num;
  1646.     }
  1647. }
  1648. /* This function safely interprets the position of the current data position
  1649.  * of the SLineInfoReader structure.  If the structure is NULL or the
  1650.  * current line is NULL or the current line position is NULL (usually because
  1651.  * the data position has been advanced to the end of the available sequence
  1652.  * data), the function returns -1, since the current data position does not 
  1653.  * actually exist.
  1654.  * Otherwise, the position within the line of the character at the current 
  1655.  * data position is returned.
  1656.  */
  1657. static int  s_LineInfoReaderGetCurrentLineOffset (TLineInfoReaderPtr lirp)
  1658. {
  1659.     if (lirp == NULL  ||  lirp->curr_line == NULL 
  1660.         ||  lirp->curr_line_pos == NULL) {
  1661.         return -1;
  1662.     } else {
  1663.         return lirp->curr_line->line_offset + lirp->curr_line_pos 
  1664.                      - lirp->curr_line->data;
  1665.     }
  1666. }
  1667. /* This function frees the memory associated with the SLineInfoReader
  1668.  * structure.  Notice that this function does NOT free the SLineInfo list.
  1669.  * This is by design.
  1670.  */
  1671. static void s_LineInfoReaderFree (TLineInfoReaderPtr lirp)
  1672. {
  1673.     if (lirp == NULL) {
  1674.         return;
  1675.     }
  1676.     free (lirp);
  1677.     lirp = NULL;
  1678. }
  1679. /* This function retrieves the "pos"th sequence data character from the lines
  1680.  * of sequence data.  If the data position requested is greater than the
  1681.  * current position, the current data pointer will be advanced until the
  1682.  * current position is the requested position or there is no more data.  If
  1683.  * there is no more data, the function returns a 0.  If the data position
  1684.  * requested is lower than the current position, the current position is reset
  1685.  * to the beginning of the sequence and advanced from there.
  1686.  * As a result, it is clearly more efficient to read the data in the forward
  1687.  * direction, but it is still possible to access the data randomly.
  1688.  */
  1689. static char 
  1690. s_FindNthDataChar
  1691. (TLineInfoReaderPtr lirp,
  1692.  int  pos)
  1693. {
  1694.     if (lirp == NULL  ||  lirp->first_line == NULL  ||  pos < 0
  1695.         ||  lirp->data_pos == -1) {
  1696.         return 0;
  1697.     }
  1698.     if (lirp->data_pos == pos) {
  1699.         if (lirp->curr_line_pos == NULL) {
  1700.             return 0;
  1701.         } else {
  1702.             return *lirp->curr_line_pos;
  1703.         }
  1704.     }
  1705.     if (lirp->data_pos > pos) {
  1706.         s_LineInfoReaderReset (lirp);
  1707.     }
  1708.      
  1709.     while (lirp->data_pos < pos  &&  lirp->curr_line != NULL) {
  1710.         lirp->curr_line_pos ++;
  1711.         /* skip over spaces, progress to next line if necessary */
  1712.         s_LineInfoReaderAdvancePastSpace (lirp);
  1713.         lirp->data_pos ++;
  1714.     }
  1715.     if (lirp->curr_line_pos != NULL) {
  1716.         return *lirp->curr_line_pos;
  1717.     } else {
  1718.         return 0;
  1719.     }
  1720. }
  1721. /* The following functions are used to manage the SStringCount structure.
  1722.  * These functions are useful for determining whether a string is unique
  1723.  * or whether only one string is used for a particular purpose.
  1724.  * The structure also tracks the line numbers on which a particular string
  1725.  * appeared.
  1726.  */
  1727. /* This function allocates memory for a new SStringCount structure,
  1728.  * initializes its member variables.  The function also places the 
  1729.  * structure at the end of list if list is not NULL.
  1730.  * The function returns a pointer to the newly allocated SStringCount
  1731.  * structure.
  1732.  */
  1733. static TStringCountPtr s_StringCountNew (TStringCountPtr list)
  1734. {
  1735.     TStringCountPtr new_item, last;
  1736.     new_item = (TStringCountPtr) malloc (sizeof (SStringCount));
  1737.     if (new_item == NULL) {
  1738.         return NULL;
  1739.     }
  1740.     new_item->string          = NULL;
  1741.     new_item->num_appearances = 0;
  1742.     new_item->line_numbers    = NULL;
  1743.     new_item->next            = NULL;
  1744.     last = list;
  1745.     while (last != NULL && last->next != NULL) {
  1746.         last = last->next;
  1747.     }
  1748.     if (last != NULL) {
  1749.         last->next = new_item;
  1750.     }
  1751.     return new_item;
  1752. }
  1753. /* This function recursively frees data associated with the structures
  1754.  * and structure member variables in a linked list of SStringCount
  1755.  * structures.
  1756.  */
  1757. static void s_StringCountFree (TStringCountPtr list)
  1758. {
  1759.     if (list == NULL) {
  1760.         return;
  1761.     }
  1762.     s_StringCountFree (list->next);
  1763.     s_IntLinkFree (list->line_numbers);
  1764.     free (list);
  1765. }
  1766. /* This function searches list to see if the string matches any of the
  1767.  * existing entries.  If so, the num_appearances value for that entry is
  1768.  * increased and the line_num is added to that entry's list of line numbers.
  1769.  * Otherwise a new entry is created at the end of the list.
  1770.  * The function returns list if list was not NULL, or a pointer to the
  1771.  * newly created SStringCount structure otherwise.
  1772.  */
  1773. static TStringCountPtr s_AddStringCount (
  1774.   char *          string,
  1775.   int             line_num,
  1776.   TStringCountPtr list
  1777. )
  1778. {
  1779.     TStringCountPtr  add_to, last;
  1780.     TIntLinkPtr      new_offset;
  1781.     if (string == NULL) {
  1782.         for (add_to = list;
  1783.              add_to != NULL  &&  add_to->string != NULL;
  1784.              add_to = add_to->next) {
  1785.             last = add_to;
  1786.         }
  1787.     } else {
  1788.         for (add_to = list;
  1789.              add_to != NULL
  1790.                &&  (add_to->string == NULL
  1791.                  ||  strcmp (string, add_to->string) != 0);
  1792.              add_to = add_to->next) {
  1793.             last = add_to;
  1794.         }
  1795.     }
  1796.     
  1797.     if (add_to == NULL) {
  1798.         add_to = s_StringCountNew (list);
  1799.         if (list == NULL) list = add_to;
  1800.         if (add_to != NULL) {
  1801.             add_to->string = string;
  1802.         }
  1803.     }
  1804.     if (add_to != NULL) {
  1805.         add_to->num_appearances ++;
  1806.         new_offset = s_IntLinkNew (line_num, add_to->line_numbers);
  1807.         if (add_to->line_numbers == NULL) {
  1808.             add_to->line_numbers = new_offset;
  1809.         }
  1810.     }
  1811.     return list;   
  1812. }
  1813. /* The following functions are replacements for strncasecmp and strcasecmp */
  1814. /* This function returns -1 if str1 is less than str2 in the first cmp_count
  1815.  * characters (using case-insensitive comparisons), 0 if they are equal,
  1816.  * and 1 if str1 is greater than str2.
  1817.  */
  1818. static int s_StringNICmp (char * str1, char *str2, int cmp_count)
  1819. {
  1820.     char * cp1;
  1821.     char * cp2;
  1822.     int    char_count, diff;
  1823.     if (str1 == NULL && str2 == NULL) {
  1824.         return 0;
  1825.     }
  1826.     if (str1 == NULL) {
  1827.         return -1;
  1828.     }
  1829.     if (str2 == NULL) {
  1830.         return 1;
  1831.     }
  1832.     cp1 = str1;
  1833.     cp2 = str2;
  1834.     char_count = 0;
  1835.     while (*cp1 != 0  &&  *cp2 != 0  &&  char_count < cmp_count) {
  1836.         diff = toupper ((int) *cp1) - toupper ((int) *cp2);
  1837.         if (diff != 0) {
  1838.             return diff;
  1839.         }
  1840.         char_count ++;
  1841.         cp1++;
  1842.         cp2++;
  1843.     }
  1844.     if (char_count == cmp_count) {
  1845.         return 0;
  1846.     } else if (*cp1 == 0  &&  *cp2 != 0) {
  1847.         return -1;
  1848.     } else if (*cp1 != 0  && *cp2 == 0) {
  1849.         return 1;
  1850.     } else {
  1851.         return 0;
  1852.     }
  1853. }
  1854. /* This function returns -1 if str1 is less than str2 using case-insensitive
  1855.  * comparisons), 0 if they are equal, and 1 if str1 is greater than str2.
  1856.  */
  1857. static int s_StringICmp (char * str1, char *str2)
  1858. {
  1859.     char * cp1;
  1860.     char * cp2;
  1861.     int    diff;
  1862.     if (str1 == NULL && str2 == NULL) {
  1863.         return 0;
  1864.     }
  1865.     if (str1 == NULL) {
  1866.         return -1;
  1867.     }
  1868.     if (str2 == NULL) {
  1869.         return 1;
  1870.     }
  1871.     cp1 = str1;
  1872.     cp2 = str2;
  1873.     while (*cp1 != 0  &&  *cp2 != 0) {
  1874.         diff = toupper ((int) *cp1) - toupper ((int) *cp2);
  1875.         if (diff != 0) {
  1876.             return diff;
  1877.         }
  1878.         cp1++;
  1879.         cp2++;
  1880.     }
  1881.     if (*cp1 == 0  &&  *cp2 != 0) {
  1882.         return -1;
  1883.     } else if (*cp1 != 0  && *cp2 == 0) {
  1884.         return 1;
  1885.     } else {
  1886.         return 0;
  1887.     }
  1888. }
  1889. /* The following functions are used to analyze specific kinds of lines
  1890.  * found in alignment files for information regarding the number of
  1891.  * expected sequences, the expected length of those sequences, and the
  1892.  * characters used to indicate missing, gap, and match characters.
  1893.  */
  1894. /* This function reads two numbers separated by whitespace from the
  1895.  * beginning of the string and uses them to set the expected number of
  1896.  * sequences and the expected number of characters per sequence.
  1897.  */
  1898. static void
  1899. s_GetFASTAExpectedNumbers
  1900. (char *           str,
  1901.  SAlignRawFilePtr afrp)
  1902. {
  1903.     char *  cp;
  1904.     char *  cpend;
  1905.     char    replace;
  1906.     int     first, second;
  1907.     if (str == NULL  ||  afrp == NULL) {
  1908.         return;
  1909.     }
  1910.     cp = str;
  1911.     while (! isdigit ((int )*cp)  &&  *cp != 0) {
  1912.         cp++;
  1913.     }
  1914.     cpend = cp;
  1915.     while (isdigit ((int )*cpend)  &&  *cpend != 0) {
  1916.         cpend++;
  1917.     }
  1918.     if (cp == cpend) {
  1919.         return;
  1920.     }
  1921.     replace = *cpend;
  1922.     *cpend = 0;
  1923.     first = atol (cp);
  1924.     *cpend = replace;
  1925.     cp = cpend;
  1926.     while (! isdigit ((int )*cp)  &&  *cp != 0) {
  1927.         cp++;
  1928.     }
  1929.     cpend = cp;
  1930.     while (isdigit ((int )*cpend)  &&  *cpend != 0) {
  1931.         cpend++;
  1932.     }
  1933.     if (cp == cpend) {
  1934.         return;
  1935.     }
  1936.     replace = *cpend;
  1937.     *cpend = 0;
  1938.     second = atol (cp);
  1939.     *cpend = replace;
  1940.     if (first > 0  &&  second > 0) {
  1941.         afrp->expected_num_sequence = first;
  1942.         afrp->expected_sequence_len = second;
  1943.     }
  1944.   
  1945. }
  1946. /* This function examines the string str to see if it begins with two
  1947.  * numbers separated by whitespace.  The function returns eTrue if so,
  1948.  * otherwise it returns eFalse.
  1949.  */
  1950. static EBool s_IsTwoNumbersSeparatedBySpace (char * str)
  1951. {
  1952.     char * cp;
  1953.     EBool  found_first_number = eFalse;
  1954.     EBool  found_dividing_space = eFalse;
  1955.     EBool  found_second_number = eFalse;
  1956.     EBool  found_second_number_end = eFalse;
  1957.     if (str == NULL) {
  1958.         return eFalse;
  1959.     }
  1960.     cp = str;
  1961.     while (*cp != 0) {
  1962.         if (! isdigit ((int )*cp)  &&  ! isspace ((int )*cp)) {
  1963.             return eFalse;
  1964.         }
  1965.         if (! found_first_number) {
  1966.             if (! isdigit ((int )*cp)) {
  1967.                 return eFalse;
  1968.             }
  1969.             found_first_number = eTrue;
  1970.         } else if (! found_dividing_space) {
  1971.             if ( isspace ((int ) *cp)) {
  1972.                 found_dividing_space = eTrue;
  1973.             } else if ( ! isdigit ((int )*cp)) {
  1974.                 return eFalse;
  1975.             }
  1976.         } else if (! found_second_number) {
  1977.             if ( isdigit ((int )*cp)) {
  1978.                 found_second_number = eTrue;
  1979.             } else if (! isspace ((int ) *cp)) {
  1980.                 return eFalse;
  1981.             }
  1982.         } else if (! found_second_number_end) {
  1983.             if ( isspace ((int ) *cp)) {
  1984.                 found_second_number_end = eTrue;
  1985.             } else if (! isdigit ((int )*cp)) {
  1986.                 return eFalse;
  1987.             }
  1988.         } else if (! isspace ((int ) *cp)) {
  1989.             return eFalse;
  1990.         }
  1991.         cp++;
  1992.     }
  1993.     if (found_second_number) {
  1994.         return eTrue;
  1995.     }
  1996.     return eFalse;
  1997. }
  1998. /* This function finds a value name in a string, looks for an equals sign
  1999.  * after the value name, and then looks for an integer value after the
  2000.  * equals sign.  If the integer value is found, the function copies the
  2001.  * integer value into the val location and returns eTrue, otherwise the
  2002.  * function returns eFalse.
  2003.  */
  2004. static EBool 
  2005. s_GetOneNexusSizeComment 
  2006. (char * str,
  2007.  char * valname, 
  2008.  int  * val)
  2009. {
  2010.     char   buf[MAX_PRINTED_INT_LEN_PLUS_ONE];
  2011.     char * cpstart;
  2012.     char * cpend;
  2013.     int    maxlen;
  2014.     if (str == NULL  ||  valname == NULL  ||  val == NULL) {
  2015.         return eFalse;
  2016.     }
  2017.     cpstart = strstr (str, valname);
  2018.     if (cpstart == NULL) {
  2019.         return eFalse;
  2020.     }
  2021.     cpstart += strlen (valname);
  2022.     while (*cpstart != 0  &&  isspace ((int )*cpstart)) {
  2023.         cpstart++;
  2024.     }
  2025.     if (*cpstart != '=') {
  2026.         return eFalse;
  2027.     }
  2028.     cpstart ++;
  2029.     while (*cpstart != 0  &&  isspace ((int )*cpstart)) {
  2030.         cpstart++;
  2031.     }
  2032.     if (! isdigit ((int )*cpstart)) {
  2033.         return eFalse;
  2034.     }
  2035.     cpend = cpstart + 1;
  2036.     while ( *cpend != 0  &&  isdigit ((int )*cpend)) {
  2037.         cpend ++;
  2038.     }
  2039.     maxlen = cpend - cpstart;
  2040.     if (maxlen > kMaxPrintedIntLen) maxlen = kMaxPrintedIntLen;
  2041.     strncpy (buf, cpstart, maxlen);
  2042.     buf [maxlen] = 0;
  2043.     *val = atoi (buf);
  2044.     return eTrue;
  2045. }
  2046. /* This function looks for Nexus-style comments to indicate the number of
  2047.  * sequences and the number of characters per sequence expected from this
  2048.  * alignment file.  If the function finds these comments, it returns eTrue,
  2049.  * otherwise it returns eFalse.
  2050.  */
  2051. static void 
  2052. s_GetNexusSizeComments 
  2053. (char *           str,
  2054.  EBool *          found_ntax,
  2055.  EBool *          found_nchar,
  2056.  SAlignRawFilePtr afrp)
  2057. {
  2058.     int  num_sequences;
  2059.     int  num_chars;
  2060.   
  2061.     if (str == NULL  ||  found_nchar == NULL  
  2062.         ||  found_ntax == NULL  ||  afrp == NULL) {
  2063.         return;
  2064.     }
  2065.     if (! *found_ntax  && 
  2066.         (s_GetOneNexusSizeComment (str, "ntax", &num_sequences)
  2067.         ||   s_GetOneNexusSizeComment (str, "NTAX", &num_sequences))) {
  2068.         afrp->expected_num_sequence = num_sequences;
  2069.         *found_ntax = eTrue;
  2070.     }
  2071.     if (! *found_nchar  &&
  2072.         (s_GetOneNexusSizeComment (str, "nchar", &num_chars)
  2073.         ||  s_GetOneNexusSizeComment (str, "NCHAR", &num_chars))) {
  2074.         afrp->expected_sequence_len = num_chars;
  2075.         *found_nchar = eTrue;
  2076.     }
  2077. }
  2078. /* This function looks for characters in Nexus-style comments to 
  2079.  * indicate values for specific kinds of characters (match, missing, gap...).
  2080.  * If the string str contains val_name followed by an equals sign, the function
  2081.  * will return the first non-whitespace character following the equals sign,
  2082.  * otherwise the function will return a 0.
  2083.  */
  2084. static char GetNexusTypechar (char * str, char * val_name)
  2085. {
  2086.     char * cp;
  2087.     char * cpend;
  2088.     if (str == NULL  ||  val_name == NULL) {
  2089.         return 0;
  2090.     }
  2091.     cpend = strstr (str, ";");
  2092.     if (cpend == NULL) {
  2093.         return 0;
  2094.     }
  2095.     cp = strstr (str, val_name);
  2096.     if (cp == NULL || cp > cpend) {
  2097.         return 0;
  2098.     }
  2099.     cp += strlen (val_name);
  2100.     while ( isspace ((int )*cp)) {
  2101.         cp ++;
  2102.     }
  2103.     if (*cp != '=') {
  2104.         return 0;
  2105.     }
  2106.     cp++;
  2107.     while ( isspace ((int )*cp) || *cp == ''') {
  2108.         cp ++;
  2109.     }
  2110.     return *cp;
  2111. }
  2112. /* This function reads a Nexus-style comment line for the characters 
  2113.  * specified for missing, match, and gap and compares the characters from
  2114.  * the comment with the characters specified in sequence_info.  If any
  2115.  * discrepancies are found, the function reports the errors and returns eFalse,
  2116.  * otherwise the function returns eTrue.
  2117.  */ 
  2118. static EBool s_CheckNexusCharInfo 
  2119. (char *               str,
  2120.  TSequenceInfoPtr     sequence_info,
  2121.  FReportErrorFunction errfunc,
  2122.  void *              errdata)
  2123. {
  2124.     char * cp;
  2125.     char   c;
  2126.     if (str == NULL  ||  sequence_info == NULL) {
  2127.         return eFalse;
  2128.     }
  2129.     cp = strstr (str, "format ");
  2130.     if (cp == NULL) {
  2131.         cp = strstr (str, "FORMAT ");
  2132.     }
  2133.     if (cp == NULL) {
  2134.         return eFalse;
  2135.     }
  2136.     if (errfunc == NULL) {
  2137.         return eTrue;
  2138.     }
  2139.     c = GetNexusTypechar (cp + 7, "missing");
  2140.     if (c == 0) {
  2141.         c = GetNexusTypechar (cp + 7, "MISSING");
  2142.     }
  2143.     if (c != 0  &&  sequence_info->missing != NULL
  2144.         &&  strchr (sequence_info->missing, c) == NULL)
  2145.     {
  2146.         s_ReportCharCommentError (sequence_info->missing, c, "MISSING",
  2147.                                 errfunc, errdata);
  2148.     }
  2149.  
  2150.     c = GetNexusTypechar (cp + 7, "gap");
  2151.     if (c == 0) {
  2152.         c = GetNexusTypechar (cp + 7, "GAP");
  2153.     }
  2154.     if (c != 0  &&  sequence_info->middle_gap != NULL
  2155.         &&  strchr (sequence_info->middle_gap, c) == NULL)
  2156.     {
  2157.         s_ReportCharCommentError (sequence_info->middle_gap, c, "GAP",
  2158.                                 errfunc, errdata);
  2159.     }
  2160.  
  2161.     c = GetNexusTypechar (cp + 7, "match");
  2162.     if (c == 0) {
  2163.         c = GetNexusTypechar (cp + 7, "MATCH");
  2164.     }
  2165.     if (c != 0  &&  sequence_info->match != NULL
  2166.         &&  strchr (sequence_info->match, c) == NULL)
  2167.     {
  2168.         s_ReportCharCommentError (sequence_info->match, c, "MATCH",
  2169.                                 errfunc, errdata);
  2170.     }
  2171.     return eTrue;
  2172. /* This function examines the string str to see if it consists entirely of
  2173.  * asterisks, colons, periods, and whitespace.  If so, this line is assumed
  2174.  * to be a Clustal-style consensus line and the function returns eTrue.
  2175.  * otherwise the function returns false;
  2176.  */
  2177. static EBool s_IsConsensusLine (char * str)
  2178. {
  2179.     if (str == NULL 
  2180.         ||  strspn (str, "*:. trn") < strlen (str)
  2181.         ||  strchr (str, '*') == NULL) {
  2182.         return eFalse;
  2183.     } else {
  2184.         return eTrue;
  2185.     } 
  2186. }
  2187. /* This function identifies lines that begin with a NEXUS keyword and end
  2188.  * with a semicolon - they will not contain sequence data.  The function
  2189.  * returns eTrue if the line contains only a NEXUS comment, eFalse otherwise.
  2190.  */
  2191. static EBool s_SkippableNexusComment (char *str)
  2192. {
  2193.     char * last_semicolon;
  2194.     if (str == NULL) {
  2195.         return eFalse;
  2196.     }
  2197.     last_semicolon = strrchr (str, ';');
  2198.     if (last_semicolon == NULL
  2199.         ||  strspn (last_semicolon + 1, " tr") != strlen (last_semicolon + 1)
  2200.         ||  strchr (str, ';') != last_semicolon) {
  2201.         return eFalse;
  2202.     }
  2203.     if (s_StringNICmp (str, "format ", 7) == 0
  2204.         ||  s_StringNICmp (str, "dimensions ", 11) == 0
  2205.         ||  s_StringNICmp (str, "dimensions ", 11) == 0
  2206.         ||  s_StringNICmp (str, "options ", 8) == 0
  2207.         ||  s_StringNICmp (str, "begin characters", 16) == 0
  2208.         ||  s_StringNICmp (str, "begin data", 10) == 0) {
  2209.         return eTrue;
  2210.     } else {
  2211.         return eFalse;
  2212.     }
  2213. }
  2214. /* This function determines whether the contents of str are "skippable"
  2215.  * in that they do not contain sequence data and therefore should not be
  2216.  * considered part of any block patterns or sequence data.
  2217.  */
  2218. static EBool s_SkippableString (char * str)
  2219. {
  2220.     if (str == NULL
  2221.         ||  s_StringNICmp (str, "matrix", 6) == 0
  2222.         ||  s_StringNICmp (str, "#NEXUS", 6) == 0
  2223.         ||  s_StringNICmp (str, "CLUSTAL W", 8) == 0
  2224.         ||  s_SkippableNexusComment (str)
  2225.         ||  s_IsTwoNumbersSeparatedBySpace (str)
  2226.         ||  s_IsConsensusLine (str)
  2227.         ||  str [0] == ';') {
  2228.         return eTrue;
  2229.     } else {
  2230.         return eFalse;
  2231.     }
  2232. }
  2233. /* This function determines whether or not str contains a blank line.
  2234.  */
  2235. static EBool s_IsBlank (char * str)
  2236. {
  2237.     size_t len;
  2238.     if (str == NULL) {
  2239.         return eTrue;
  2240.     }
  2241.     len = strspn (str, " tr");
  2242.     if (len == strlen (str)) {
  2243.         return eTrue;
  2244.     }
  2245.     return eFalse;
  2246. }
  2247. /* This function determines whether or not linestring contains a line
  2248.  * indicating the end of sequence data (organism information and definition
  2249.  * lines may occur after this line).
  2250.  */
  2251. static EBool s_FoundStopLine (char * linestring)
  2252. {
  2253.     if (linestring == NULL) {
  2254.         return eFalse;
  2255.     }
  2256.     if (s_StringNICmp (linestring, "endblock", 8) == 0
  2257.         ||  s_StringNICmp (linestring, "end;", 4) == 0) {
  2258.         return eTrue;
  2259.     }
  2260.     return eFalse;
  2261. }
  2262. /* This function identifies the beginning line of an ASN.1 file, which
  2263.  * cannot be read by the alignment reader.
  2264.  */
  2265. static EBool s_IsASN1 (char * linestring)
  2266. {
  2267.     if (linestring != NULL  &&  strstr (linestring, "::=") != NULL) {
  2268.         return eTrue;
  2269.     } else {
  2270.         return eFalse;
  2271.     }
  2272. }
  2273. /* The following functions are used to locate and read comments enclosed
  2274.  * in brackets.  These comments sometimes include organism information.
  2275.  */
  2276. /* This function frees memory associated with a SCommentLoc structure. */
  2277. static void s_CommentLocFree (TCommentLocPtr clp)
  2278. {
  2279.     if (clp == NULL) {
  2280.         return;
  2281.     }
  2282.     s_CommentLocFree (clp->next);
  2283.     free (clp);
  2284. }
  2285. /* This function finds the first comment enclosed in brackets and creates
  2286.  * a SCommentLoc structure to indicate the position of the comment
  2287.  * in the string.  The function returns a pointer to this structure if a
  2288.  * comment is found or a NULL if the string does not contain a bracketed 
  2289.  * comment.
  2290.  */
  2291. static TCommentLocPtr s_FindComment (char * string)
  2292. {
  2293.     char *         cp_start;
  2294.     char *         cp_end;
  2295.     TCommentLocPtr clp;
  2296.     if (string == NULL) {
  2297.         return NULL;
  2298.     }
  2299.     cp_start = strstr (string, "[");
  2300.     if (cp_start != NULL) {
  2301.         cp_end = strstr (cp_start, "]");
  2302.         if (cp_end != NULL) {
  2303.             clp = (TCommentLocPtr) malloc (sizeof (SCommentLoc));
  2304.             if (clp == NULL) {
  2305.                 return NULL;
  2306.             }
  2307.             clp->start = cp_start;
  2308.             clp->end = cp_end;
  2309.             clp->next = NULL;
  2310.             return clp;
  2311.         }
  2312.     }
  2313.     return NULL;
  2314. }
  2315. /* This function removes a comment from a line. */
  2316. static void s_RemoveCommentFromLine (char * linestring)
  2317. {
  2318.     TCommentLocPtr clp;
  2319.     if (linestring == NULL) {
  2320.         return;
  2321.     }
  2322.     clp = s_FindComment (linestring);
  2323.     while (clp != NULL) {
  2324.         strcpy (clp->start, clp->end + 1);
  2325.         s_CommentLocFree (clp);
  2326.         clp = s_FindComment (linestring);
  2327.     }
  2328.     /* if we have read an organism comment and that's all there was on the
  2329.      * line, get rid of the arrow character as well so it doesn't end up 
  2330.      * in the sequence data
  2331.      */
  2332.     if ( linestring [0] == '>'  &&  linestring [1] == 0) {
  2333.         linestring [0] = 0;
  2334.     }
  2335.     /* if the line now contains only space, truncate it */
  2336.     if (strspn (linestring, " tr") == strlen (linestring)) {
  2337.         linestring [0] = 0;
  2338.     }
  2339.     
  2340. }
  2341. /* This function determines whether or not a comment describes an organism
  2342.  * by looking for org= or organism= inside the brackets.
  2343.  */
  2344. static EBool s_IsOrganismComment (TCommentLocPtr clp)
  2345. {
  2346.     int    len;
  2347.     char * cp;
  2348.     char * cp_end;
  2349.     if (clp == NULL  ||  clp->start == NULL  ||  clp->end == NULL) {
  2350.         return eFalse;
  2351.     }
  2352.  
  2353.     cp = clp->start;
  2354.     if (*cp != '[') {
  2355.         return eFalse;
  2356.     }
  2357.     cp ++;
  2358.     len = strspn ( clp->start, " tr");
  2359.     cp = cp + len;
  2360.     cp_end = strstr (cp, "=");
  2361.     if (cp_end == NULL) {
  2362.         return eFalse;
  2363.     }
  2364.     cp_end --;
  2365.     while (cp_end > cp  &&  isspace ((int )*cp_end)) {
  2366.       cp_end --;
  2367.     }
  2368.     cp_end ++;
  2369.     if ((cp_end - cp == 3  &&  s_StringNICmp (cp, "org", 3) == 0)
  2370.         ||  (cp_end - cp == 8  &&  s_StringNICmp (cp, "organism", 8) == 0)) {
  2371.         return eTrue;
  2372.     }
  2373.     return eFalse;
  2374. }
  2375. /* This function finds an organism comment, which includes the first bracketed
  2376.  * comment with org= or organism=, plus any additional bracketed comments
  2377.  * separated only by whitespace from the org= or organism= comment.
  2378.  * The function returns a pointer to a SCommentLoc structure describing
  2379.  * the location of the organism comment.
  2380.  */
  2381. static TCommentLocPtr s_FindOrganismComment (char * string)
  2382. {
  2383.     TCommentLocPtr clp, next_clp;
  2384.     if (string == NULL) {
  2385.         return NULL;
  2386.     }
  2387.     clp = s_FindComment (string);
  2388.     while (clp != NULL  &&  ! s_IsOrganismComment (clp)) {
  2389.         clp = s_FindComment (clp->end);
  2390.     }
  2391.     if (clp == NULL) {
  2392.         return NULL;
  2393.     }
  2394.     next_clp = s_FindComment (clp->end);
  2395.     while (next_clp != NULL  && 
  2396.         (int) strspn (clp->end + 1, " tr") == next_clp->start - clp->end - 1
  2397.         &&  ! s_IsOrganismComment (next_clp))
  2398.     {
  2399.         clp->end = next_clp->end;
  2400.         next_clp = s_FindComment (clp->end);
  2401.     }
  2402.     return clp;
  2403. }
  2404. /* This function removes an organism comment from a line. */
  2405. static void s_RemoveOrganismCommentFromLine (char * string)
  2406. {
  2407.     TCommentLocPtr clp;
  2408.     while ((clp = s_FindOrganismComment (string)) != NULL) {
  2409.         strcpy (clp->start, clp->end + 1);
  2410.         s_CommentLocFree (clp);
  2411.     }
  2412. }
  2413.  
  2414. /* This function creates an ordered list of comments within an organism
  2415.  * comment and returns a pointer to the first item in the linked list.
  2416.  * In an ordered org name, the org= value appears first, followed by other
  2417.  * bracketed values in alphabetical order.
  2418.  */
  2419. static TCommentLocPtr s_CreateOrderedOrgCommentList (TCommentLocPtr org_clp)
  2420. {
  2421.     TCommentLocPtr clp, prev_clp, next_clp, clp_list, ordered_start;
  2422.     int           next_len, this_len, len;
  2423.   
  2424.     if (org_clp == NULL) {
  2425.         return NULL;
  2426.     }
  2427.     clp_list = s_FindComment (org_clp->start); /* this is the org= */
  2428.     prev_clp = NULL;
  2429.     ordered_start = s_FindComment (clp_list->end);
  2430.     if (ordered_start == NULL) {
  2431.         return clp_list;
  2432.     }
  2433.     clp = s_FindComment (ordered_start->end);
  2434.     while (clp != NULL  &&  clp->start < org_clp->end) {
  2435.         /* insert new comment into list */
  2436.         prev_clp = NULL;
  2437.         next_clp = ordered_start;
  2438.         next_len = next_clp->end - next_clp->start;
  2439.         this_len = clp->end - clp->start;
  2440.         len = next_len > this_len ? next_len : this_len;
  2441.         while (next_clp != NULL
  2442.           &&  strncmp (next_clp->start, clp->start, len) < 0)
  2443.         {
  2444.             prev_clp = next_clp;
  2445.             next_clp = next_clp->next;
  2446.             if (next_clp != NULL) {
  2447.                 next_len = next_clp->end - next_clp->start;
  2448.                 len = next_len > this_len ? next_len : this_len;
  2449.             }
  2450.         }
  2451.         if (prev_clp == NULL) {
  2452.             clp->next = ordered_start;
  2453.             ordered_start = clp;
  2454.         } else {
  2455.             clp->next = prev_clp->next;
  2456.             prev_clp->next = clp;
  2457.         }
  2458.         clp = s_FindComment (clp->end);
  2459.     }
  2460.     clp_list->next = ordered_start;
  2461.     return clp_list;
  2462. }
  2463. /* This function creates an ordered organism name based on the bracketed
  2464.  * comments contained in the location described by org_clp.
  2465.  */
  2466. static char * s_CreateOrderedOrgName (TCommentLocPtr org_clp)
  2467. {
  2468.     TCommentLocPtr clp, clp_list;
  2469.     char *         ordered_org_name;
  2470.     char *         cp;
  2471.     if (org_clp == NULL) {
  2472.         return NULL;
  2473.     }
  2474.     ordered_org_name = malloc (org_clp->end - org_clp->start + 2);
  2475.     if (ordered_org_name == NULL) {
  2476.         return NULL;
  2477.     }
  2478.     ordered_org_name [0] = 0;
  2479.     clp_list = s_CreateOrderedOrgCommentList (org_clp);
  2480.     cp = ordered_org_name;
  2481.     for (clp = clp_list; clp != NULL; clp = clp->next) {
  2482.         strncpy (cp, clp->start, clp->end - clp->start + 1);
  2483.         cp += clp->end - clp->start + 1;
  2484.         *cp = 0;
  2485.     }
  2486.     
  2487.     s_CommentLocFree (clp_list);
  2488.     return ordered_org_name;
  2489. }
  2490. /* This function is used to read any organism names that may appear in
  2491.  * string, including any modifiers that may appear after the organism name.
  2492.  */
  2493. static void s_ReadOrgNamesFromText 
  2494. (char *           string,
  2495.  int              line_num,
  2496.  SAlignRawFilePtr afrp)
  2497. {
  2498.     TCommentLocPtr clp;
  2499.     char *         org_name;
  2500.     char *         cp;
  2501.     char *         defline;
  2502.     char *         comment_end;
  2503.     int            defline_offset;
  2504.   
  2505.     if (string == NULL  ||  afrp == NULL) {
  2506.         return;
  2507.     }
  2508.     clp = s_FindOrganismComment (string);
  2509.     if (clp == NULL && (strstr (string, "org=") != NULL || strstr (string, "organism=") != NULL))
  2510.     {
  2511.       s_ReportOrgCommentError (string, afrp->report_error, afrp->report_error_userdata);
  2512.     }
  2513.     while (clp != NULL) {
  2514.         org_name = s_CreateOrderedOrgName (clp);
  2515.         afrp->organisms = s_AddLineInfo (afrp->organisms, org_name, line_num,
  2516.                                        clp->start - string);
  2517.         free (org_name);
  2518.         afrp->num_organisms ++;
  2519.         defline = NULL;
  2520.         defline_offset = 0;
  2521.         if (*clp->end != 0) {
  2522.             cp = clp->end + 1;
  2523.             cp += strspn (cp, " trn");
  2524.             if (*cp != 0) {
  2525.                 defline = clp->end + 1;
  2526.                 defline_offset = clp->end - string + 1;
  2527.             }
  2528.         }
  2529.         afrp->deflines = s_AddLineInfo (afrp->deflines, defline, line_num,
  2530.                                       defline_offset);
  2531.         afrp->num_deflines ++;
  2532.                                       
  2533.         comment_end = clp->end;
  2534.         s_CommentLocFree (clp);
  2535.         clp = s_FindOrganismComment (comment_end);
  2536.     }
  2537. }
  2538. /* The following group of functions manages the SAlignRawSeq structure,
  2539.  * which is used to track the IDs of sequences in the file, the sequence
  2540.  * characters for those IDs, and the locations of the IDs and sequence
  2541.  * characters.
  2542.  */
  2543. /* This function allocates memory for an SAlignRawSeq structure,
  2544.  * initializes its member variables, and returns a pointer to the newly
  2545.  * allocated structure.
  2546.  */
  2547. static TAlignRawSeqPtr s_AlignRawSeqNew (TAlignRawSeqPtr list)
  2548. {
  2549.     TAlignRawSeqPtr arsp, last;
  2550.     arsp = (TAlignRawSeqPtr)malloc (sizeof (SAlignRawSeq));
  2551.     if (arsp == NULL) {
  2552.         return NULL;
  2553.     }
  2554.     arsp->id            = NULL;
  2555.     arsp->sequence_data = NULL;
  2556.     arsp->id_lines      = NULL;
  2557.     arsp->next          = NULL;
  2558.     last = list;
  2559.     while (last != NULL && last->next != NULL) {
  2560.         last = last->next;
  2561.     }
  2562.     if (last != NULL) {
  2563.         last->next = arsp;
  2564.     }
  2565.     return arsp;
  2566. }
  2567. /* This function frees the memory associated with an SAlignRawSeq
  2568.  * structure's member variables and with the structure itself.
  2569.  */
  2570. static void s_AlignRawSeqFree (TAlignRawSeqPtr arsp)
  2571. {
  2572.     if (arsp == NULL) {
  2573.         return;
  2574.     }
  2575.     s_AlignRawSeqFree (arsp->next);
  2576.     free (arsp->id);
  2577.     s_LineInfoFree (arsp->sequence_data);
  2578.     s_IntLinkFree (arsp->id_lines);
  2579. }
  2580. /* This function returns a pointer to the sequence in list with the specified
  2581.  * ID, unless there is no such sequence, in which case the function returns
  2582.  * NULL.
  2583.  */
  2584. static TAlignRawSeqPtr 
  2585. s_FindAlignRawSeqById 
  2586. (TAlignRawSeqPtr list,
  2587.  char *          id)
  2588. {
  2589.     TAlignRawSeqPtr arsp;
  2590.     for (arsp = list; arsp != NULL; arsp = arsp->next) {
  2591.         if (strcmp (arsp->id, id) == 0) {
  2592.             return arsp;
  2593.         }
  2594.     }
  2595.     return NULL;
  2596. }
  2597. /* This function finds the position of a given ID in the sequence list,
  2598.  * unless the ID is not found in the list, in which case the function returns
  2599.  * -1.
  2600.  */
  2601. static int  
  2602. s_FindAlignRawSeqOffsetById 
  2603. (TAlignRawSeqPtr list, 
  2604.  char *          id)
  2605. {
  2606.     TAlignRawSeqPtr arsp;
  2607.     int             offset;
  2608.     for (arsp = list, offset = 0; arsp != NULL; arsp = arsp->next, offset++) {
  2609.         if (strcmp (arsp->id, id) == 0) {