Logo Search packages:      
Sourcecode: bibledit version File versions  Download package

chapters-verses.cpp

/*
    Copyright (C) 2003-2006 Teus Benschop.

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/


#include "libraries.h"
#include "utilities.h"
#include "constants.h"
#include "versification.h"


ustring versification;
ustring book;
int chapter;
int highest_chapter;
vector<ustring> verses;
vector<int> highest_verses;
int highest_verse;
vector<int> expanded_verses;
vector<unsigned int> verses_pointers;


void check_last_chapter ()
{
  // Check whether we have enough chapters in this book.
  if (chapter < highest_chapter)
    output_xml_message (book, highest_chapter, "0", "Not enough chapters or wrong versification");
}


void check_new_chapter ()
{
  // Deal with the first chapter in a book.
  static bool first_chapter_found = false;
  static int previous_chapter;
  if (!first_chapter_found) {
    if (chapter > 1)
      output_xml_message (book, chapter, "0", "First chapter number is too high");
    previous_chapter = chapter - 1;
    first_chapter_found = true;
  }
  
  // Check whether the chapter number follows the previous one.
  if (chapter != (previous_chapter + 1)) {
    ustring message;
    message = "Chapter out of sequence following " + convert_to_string (int (previous_chapter));
    output_xml_message (book, chapter, "0", message);
  }
  
  // Store "previous chapter".
  previous_chapter = chapter;
  
  // Check whether the number is within limits.
  if (chapter > highest_chapter)
    output_xml_message (book, chapter, "0", "Extra chapter or wrong versification");
  
}


void store_expanded_verse (const ustring& verse, unsigned int verses_pointer)
{
  int expanded_verse;
  expanded_verse = 2 * (convert_to_int (verse));
  if (verse.find ("b") == string::npos) {
    expanded_verses.push_back (expanded_verse);
    verses_pointers.push_back (verses_pointer);
  }
  if (verse.find ("a") == string::npos) {
    expanded_verses.push_back (++expanded_verse);
    verses_pointers.push_back (verses_pointer);
  }
}


void check_verses ()
/*
This checks all the verses that are in 'book' and 'chapter'.
It supports sequences in the form of \v 1,2,3.
It supports ranges in the form of \v 1b-3 and \v 2-4a and \v 2b-5a.
*/
{
  // Check whether there are any verses at all.
  if (verses.empty()) {
    output_xml_message (book, chapter, "1", "Chapter has no verses");
    // Stop further processing because the following code assumes there are verses.
    return;
  }

  // Check for verses in chapter 0, which indicates the \c 1 marker wasn't there.
  if ((chapter == 0) && (verses.size() > 1)) {
    output_xml_message (book, chapter, "1", "Chapter marker missing");
  }

  // Transform the verses in the internally used encoding, so as to accomodate
  // for sequences and ranges.
  for (unsigned int i = 0; i < verses.size(); i++) {
    // Do not work in the container, but on a copy.
    ustring vs (verses[i]);
    // If there is a range, take the beginning and the end and fill up in between.
    if (vs.find ("-") != string::npos) {
      size_t position;
      position = vs.find ("-");
      ustring start_range, end_range;
      start_range = vs.substr (0, position);
      vs.erase (0, ++position);
      end_range = vs;
      int start_expanded_verse = 2 * convert_to_int (number_in_string (start_range));
      if (start_range.find ("b") != string::npos)
        start_expanded_verse++;
      // Checking on range start.
      if (start_range.find ("a") != string::npos)
        output_xml_message (book, chapter, verses[i], "Range starts with \"a\"");
      int end_expanded_verse = 2 * convert_to_int (number_in_string (end_range));
      if (end_range.find ("a") == string::npos)
        end_expanded_verse++;
      // Check on range end.
      if (end_range.find ("b") != string::npos)
        output_xml_message (book, chapter, verses[i], "Range ends with \"b\"");
      for (int i2 = start_expanded_verse; i2 <= end_expanded_verse; i2++) {
          expanded_verses.push_back (i2);
        verses_pointers.push_back (i);
      }
    } 
    // If there is a sequence, take each verse in the sequence, and store it.
    else if (vs.find (",") != string::npos) {
      int iterations = 0;
      do {
        // In case of an unusual range formation, do not hang, but give message.
        iterations++;
        if (iterations > 50) {
          output_xml_message (book, chapter, verses[i], "Unusual verse sequence");
          break;
        }
        size_t position = vs.find (",");
        ustring verse;
        if (position == string::npos) {
          verse = vs;
          vs.clear();
        } else {
          verse = vs.substr (0, position);
          vs.erase (0, ++position);
        }
        store_expanded_verse (verse, i);
      } while (!vs.empty());
    }
    // No range and no sequence: a "normal" verse.
    else {
      store_expanded_verse (vs, i);
    }
  }

  // See whether it starts at verse 0 or 1.
  if (expanded_verses[0] > 2)
    output_xml_message (book, chapter, verses[0], "Verse 1 missing");

  // See whether the verses are within the limit.
  for (unsigned int i = 0; i < expanded_verses.size(); i +=2) {
    int verse;
    verse = expanded_verses[i] / 2;
    if (verse > highest_verse) {
      output_xml_message (book, chapter, convert_to_string (verse), "Extra verse or wrong versification");
    }
  }

  // See whether there are verses or bits out of sequence.
  int previous_verse = expanded_verses[0] - 1;
  for (unsigned int i = 0; i < expanded_verses.size(); i++) {
    ustring previous_verse_text;
    unsigned int pointer = verses_pointers[i];
    if (pointer == 0)
      previous_verse_text = "beginning";
    else 
      previous_verse_text = verses[pointer - 1];
    if (expanded_verses[i] != previous_verse + 1) {
      output_xml_message (book, chapter, verses[pointer], "Verse out of sequence following " + previous_verse_text);
    }
    previous_verse = expanded_verses[i];
  }

  // Check whether we have enough verses.
  int highverse = expanded_verses[expanded_verses.size() - 1];
  highverse = highverse / 2;
  if (highverse < highest_verse) {
    output_xml_message (book, chapter, verses[verses.size() - 1], "Not enough verses in chapter");
  }
  
  // Clear storage.
  expanded_verses.clear ();
  verses_pointers.clear ();
}


void start_element_handler (GMarkupParseContext *context,
                            const gchar         *element_name,
                            const gchar        **attribute_names,
                            const gchar        **attribute_values,
                            gpointer             user_data,
                            GError             **error)
{
  string element = element_name;
  
  if (element == BOOK_TAG) {
    // A book starts. Get the name of the book.    
    book = attribute_values[0];
    // Determine last chapter of the book.
    highest_chapter = versification_get_highest_chapter (versification, book);
    // To speed up things, get all highest verses for all chapters in the book.
    highest_verses = versification_get_verses (versification, book);
  } 
  
  else if (element == CHAPTER_TAG) {
    // A chapter starts. Gets its number.
    chapter = convert_to_int (attribute_values[0]);
    // Check whether this chapter is in sequence with the previous one.
    check_new_chapter ();
    // Get highest verse for this chapter;
    if (chapter < 1)
      highest_verse = 0;
    else if ((unsigned int) chapter > highest_verses.size())
      highest_verse = 0;
    else
      highest_verse = highest_verses[chapter - 1];
    // Clear verse storage.
    verses.clear();
  } 
  
  else if (element == VERSE_TAG ) {
    // A verse starts. Store it.
    verses.push_back (attribute_values[0]);
  }
}


void end_element_handler (GMarkupParseContext *context,
                          const gchar         *element_name,
                          gpointer             user_data,
                          GError             **error)
{
  string element = element_name;

  if (element == BOOK_TAG) {
    // We've reached the end of the book. 
    // Check on the number of chapters in this book.
    check_last_chapter ();

  } else if (element == CHAPTER_TAG) {
    // We've reached the end of a chapter.
    // Check on the verses it contains.
    check_verses ();

  } else if (element == VERSE_TAG ) {
    // We are at the end of a verse. No special checking here.
  }
}


void text_handler (GMarkupParseContext *context,
                   const gchar         *text,
                   gsize                text_len,
                   gpointer             user_data,
                   GError             **error)
{
}



void passthrough_handler    (GMarkupParseContext *context,
                             const gchar         *passthrough_text,
                             gsize                text_len,
                             gpointer             user_data,
                             GError             **error)
{
}


void error_handler          (GMarkupParseContext *context,
                             GError              *error,
                             gpointer             user_data)
{
  cerr << error->message << endl;
}


int main (int argc, char *argv[])
{
  // Information provided when no arguments are given.
  if (argc == 1) {
    cout << "sc-chapters-verses reads checking units from stdin," << endl;
    cout << "checks whether the chapters and the verses are correct," << endl;
    cout << "and outputs its report on stdout." << endl;
    cout << "Command line arguments:" << endl;
    cout << "--versification <versification>" << endl;
    // Do not read stdin in this case because it would hang.
    return 0;
  }
  // Process command line arguments.
  for (int i = 1; i < argc; i++) {
    ustring argument;
    argument = argv[i];
    if (argument.length() > 2) {
      if (argument.substr (0, 2) == "--") {
        argument.erase (0, 2);
        if (argument == "versification")
          versification = argv[++i];
      }
    }
  }
  // Read data from stdin.
  GIOChannel* io;
  gchar* text;
  gsize length;
  io = g_io_channel_unix_new (0);
  g_io_channel_read_to_end (io, &text, &length, NULL);
  // Set up parser.
  GMarkupParseContext *context;
  GMarkupParser parser = {
    start_element_handler,
    end_element_handler,
    text_handler,
    passthrough_handler,
    error_handler
  };
  // Parse xml data.
  context = g_markup_parse_context_new (&parser, GMarkupParseFlags (0), NULL, NULL);
  g_markup_parse_context_parse (context, text, length, NULL);
  g_markup_parse_context_end_parse (context, NULL);
  // Free some resources.  
  g_markup_parse_context_free (context);
  g_free (text);
  g_io_channel_unref (io);
  // Ready.
  return 0;
}

Generated by  Doxygen 1.6.0   Back to index