rem: "Written by AGMSBayesianSpamServer $Revision: 1.1 $\t"
add: "Written by AGMSBayesianSpamServer $Revision: 1.2 $\t"
context: status_t ABSApp::LoadSaveDatabase (bool DoLoad, char *ErrorMessage){  time_t                             CurrentTime;  FILE                              *DatabaseFile = NULL;  BNode                              DatabaseNode;  BNodeInfo                          DatabaseNodeInfo;  StatisticsMap::iterator            DataIter;  StatisticsMap::iterator            EndIter;  status_t                           ErrorCode;  int                                i;  pair<StatisticsMap::iterator,bool> InsertResult;  char                               LineString [10240];  StatisticsRecord                   Statistics;  const char                        *StringPntr;  char                              *TabPntr;  const char                        *WordPntr;  if (DoLoad)  {    MakeDatabaseEmpty ();    m_DatabaseHasChanged = false; /* In case of early error exit. */  }  else /* Saving the database, backup the old version on disk. */  {    ErrorCode = MakeBackup (ErrorMessage);    if (ErrorCode != B_OK) /* Usually because the directory isn't there. */      return ErrorCode;  }  DatabaseFile = fopen (m_DatabaseFileName.String (), DoLoad ? "rb" : "wb");  if (DatabaseFile == NULL)  {    ErrorCode = errno;    sprintf (ErrorMessage, "Can't open database file \"%s\" for %s",      m_DatabaseFileName.String (), DoLoad ? "reading" : "writing");    goto ErrorExit;  }  /* Process the first line, which identifies the file. */  if (DoLoad)  {    sprintf (ErrorMessage, "Can't read first line of database file \"%s\", "      "expected it to start with \"%s\"",      m_DatabaseFileName.String (), g_DatabaseRecognitionString);    ErrorCode = -1;    if (fgets (LineString, sizeof (LineString), DatabaseFile) == NULL)      goto ErrorExit;    if (strncmp (LineString, g_DatabaseRecognitionString,    strlen (g_DatabaseRecognitionString)) != 0)      goto ErrorExit;  }  else /* Saving */  {    CurrentTime = time (NULL);    if (fprintf (DatabaseFile, "%s V1 (word, age, genuine count, spam count)\t"    "Written by AGMSBayesianSpamServer $Revision: 1.1 $\t"    "Compiled on " __DATE__ " at " __TIME__ "\tThis file saved on %s",    g_DatabaseRecognitionString, ctime (&CurrentTime)) <= 0)    {      ErrorCode = errno;      sprintf (ErrorMessage, "Problems when writing to database file \"%s\"",        m_DatabaseFileName.String ());      goto ErrorExit;    }  }  /* The second line lists the different classifications.  We just check to see  that the first two are Genuine and Spam.  If there are others, they'll be  ignored and lost when the database is saved. */  if (DoLoad)  {    sprintf (ErrorMessage, "Can't read second line of database file \"%s\", "      "expected it to list classifications %s and %s along with their totals",      m_DatabaseFileName.String (), g_ClassifiedGenuine, g_ClassifiedSpam);    ErrorCode = B_BAD_VALUE;    if (fgets (LineString, sizeof (LineString), DatabaseFile) == NULL)      goto ErrorExit;    i = strlen (LineString);    if (i > 0 && LineString[i-1] == '\n')      LineString[i-1] = 0; /* Remove trailing line feed character. */    /* Look for the title word at the start of the line. */    TabPntr = LineString;    for (StringPntr = TabPntr; *TabPntr != 0 && *TabPntr != '\t'; TabPntr++)      ; if (*TabPntr == '\t') *TabPntr++ = 0; /* Stringify up to next tab. */    if (strncmp (StringPntr, "Classifications", 15) != 0)      goto ErrorExit;    /* Look for the Genuine class and count. */    for (StringPntr = TabPntr; *TabPntr != 0 && *TabPntr != '\t'; TabPntr++)      ; if (*TabPntr == '\t') *TabPntr++ = 0; /* Stringify up to next tab. */    if (strcmp (StringPntr, g_ClassifiedGenuine) != 0)      goto ErrorExit;    for (StringPntr = TabPntr; *TabPntr != 0 && *TabPntr != '\t'; TabPntr++)      ; if (*TabPntr == '\t') *TabPntr++ = 0; /* Stringify up to next tab. */    m_TotalGenuineMessages = atoll (StringPntr);    /* Look for the Spam class and count. */    for (StringPntr = TabPntr; *TabPntr != 0 && *TabPntr != '\t'; TabPntr++)      ; if (*TabPntr == '\t') *TabPntr++ = 0; /* Stringify up to next tab. */    if (strcmp (StringPntr, g_ClassifiedSpam) != 0)      goto ErrorExit;    for (StringPntr = TabPntr; *TabPntr != 0 && *TabPntr != '\t'; TabPntr++)      ; if (*TabPntr == '\t') *TabPntr++ = 0; /* Stringify up to next tab. */    m_TotalSpamMessages = atoll (StringPntr);  }  else /* Saving */  {    fprintf (DatabaseFile,      "Classifications and total messages:\t%s\t%lu\t%s\t%lu\n",      g_ClassifiedGenuine, m_TotalGenuineMessages,      g_ClassifiedSpam, m_TotalSpamMessages);  }  /* The remainder of the file is the list of words and statistics.  Each line  has a word, a tab, the time when the word was last changed in the database  (sequence number of message addition, starts at 0 and goes up by one for each  message added to the database), a tab then the number of messages in the  first class (genuine) that had that word, then a tab, then the number of  messages in the second class (spam) with that word, and so on. */  if (DoLoad)  {    while (!feof (DatabaseFile))    {      if (fgets (LineString, sizeof (LineString), DatabaseFile) == NULL)      {        ErrorCode = errno;        if (feof (DatabaseFile))          break;        if (ErrorCode == B_OK)          ErrorCode = -1;        sprintf (ErrorMessage, "Error while reading words and statistics "          "from database file \"%s\"", m_DatabaseFileName.String ());        goto ErrorExit;      }      i = strlen (LineString);      if (i > 0 && LineString[i-1] == '\n')        LineString[i-1] = 0; /* Remove trailing line feed character. */      /* Get the word at the start of the line, save in WordPntr. */      TabPntr = LineString;      for (WordPntr = TabPntr; *TabPntr != 0 && *TabPntr != '\t'; TabPntr++)        ; if (*TabPntr == '\t') *TabPntr++ = 0; /* Stringify up to next tab. */      /* Get the date stamp.  Actually a sequence number, not a date. */      for (StringPntr = TabPntr; *TabPntr != 0 && *TabPntr != '\t'; TabPntr++)        ; if (*TabPntr == '\t') *TabPntr++ = 0; /* Stringify up to next tab. */      Statistics.age = atoll (StringPntr);      /* Get the Genuine count. */      for (StringPntr = TabPntr; *TabPntr != 0 && *TabPntr != '\t'; TabPntr++)        ; if (*TabPntr == '\t') *TabPntr++ = 0; /* Stringify up to next tab. */      Statistics.genuineCount = atoll (StringPntr);      /* Get the Spam count. */      for (StringPntr = TabPntr; *TabPntr != 0 && *TabPntr != '\t'; TabPntr++)        ; if (*TabPntr == '\t') *TabPntr++ = 0; /* Stringify up to next tab. */      Statistics.spamCount = atoll (StringPntr);      /* Ignore empty words, totally unused words and ones which are too long      (avoids lots of length checking everywhere). */      if (WordPntr[0] == 0 || strlen (WordPntr) > g_MaxWordLength ||      (Statistics.genuineCount <= 0 && Statistics.spamCount <= 0))        continue; /* Ignore this line of text, start on next one. */      /* Add the combination to the database. */      InsertResult = m_WordMap.insert (        StatisticsMap::value_type (WordPntr, Statistics));      if (InsertResult.second == false)      {        ErrorCode = B_BAD_VALUE;        sprintf (ErrorMessage, "Error while inserting word \"%s\" from "          "database \"%s\", perhaps it is a duplicate",          WordPntr, m_DatabaseFileName.String ());        goto ErrorExit;      }      m_WordCount++;      /* And the hunt for the oldest word. */      if (Statistics.age < m_OldestAge)        m_OldestAge = Statistics.age;    }  }  else /* Saving, dump all words and statistics to the file. */  {    EndIter = m_WordMap.end ();    for (DataIter = m_WordMap.begin (); DataIter != EndIter; DataIter++)    {      if (fprintf (DatabaseFile, "%s\t%lu\t%lu\t%lu\n",      DataIter->first.c_str (), DataIter->second.age,      DataIter->second.genuineCount, DataIter->second.spamCount) <= 0)      {        ErrorCode = errno;        sprintf (ErrorMessage, "Error while writing word \"%s\" to "          "database \"%s\"",          DataIter->first.c_str(), m_DatabaseFileName.String ());        goto ErrorExit;      }    }  }  /* Set the file type so that the new file gets associated with this program,  and picks up the right icon. */  if (!DoLoad)  {    sprintf (ErrorMessage, "Unable to set attributes (file type) of database "      "file \"%s\"", m_DatabaseFileName.String ());    ErrorCode = DatabaseNode.SetTo (m_DatabaseFileName.String ());    if (ErrorCode != B_OK)      goto ErrorExit;    DatabaseNodeInfo.SetTo (&DatabaseNode);    ErrorCode = DatabaseNodeInfo.SetType (g_ABSDatabaseFileMIMEType);    if (ErrorCode != B_OK)      goto ErrorExit;  }  /* Success! */  m_DatabaseHasChanged = false;  ErrorCode = B_OK;ErrorExit:  if (DatabaseFile != NULL)    fclose (DatabaseFile);  return ErrorCode;}