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

file-grouplist.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Pan - A Newsreader for Gtk+
 * Copyright (C) 2002  Charles Kerr <charles@rebelbase.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <config.h>

#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>

#include <pan/base/base-prefs.h>
#include <pan/base/debug.h>
#include <pan/base/group.h>
#include <pan/base/log.h>
#include <pan/base/pan-i18n.h>
#include <pan/base/pan-glib-extensions.h>
#include <pan/base/server.h>
#include <pan/base/file-headers.h>
#include <pan/base/file-grouplist.h>
#include <pan/base/util-file.h>

/***
****
****  Do We Have Grouplist Files?
****
***/

static ServerGroupsType
file_grouplist_097_style_exists (const Server * server)
{
      gchar fname [PATH_MAX];
      ServerGroupsType retval = 0;

      /* sanity clause */
      g_return_val_if_fail (server_is_valid (server), retval);

      /* check for sub */
      g_snprintf (fname, sizeof(fname), "%s%c%s_sub.idx", get_data_dir(), G_DIR_SEPARATOR, server_get_name (server));
      if (pan_file_exists  (fname))
            retval |= SERVER_GROUPS_SUBSCRIBED;

      /* check for unsub */
      g_snprintf (fname, sizeof(fname), "%s%c%s_unsub.idx", get_data_dir(), G_DIR_SEPARATOR, server_get_name (server));
      if (pan_file_exists  (fname))
            retval |= SERVER_GROUPS_UNSUBSCRIBED;

      /* return results */
      return retval;
}

static gboolean
file_grouplist_096_style_exists (const Server * server)
{
      gchar fname [PATH_MAX];
      gboolean retval;

      g_return_val_if_fail (server_is_valid (server), FALSE);
       
      g_snprintf (fname, sizeof(fname), "%s%c%s.idx", get_data_dir(), G_DIR_SEPARATOR, server_get_name (server));
            retval = pan_file_exists  (fname);

      return retval;
}

ServerGroupsType
file_grouplist_exists (const Server* server)
{
      ServerGroupsType retval = 0;

      /* sanity clause */
      g_return_val_if_fail (server_is_valid (server), retval);

      /* first check the new style... */
      retval = file_grouplist_097_style_exists (server);

      /* fall back on the old style... */
      if (!retval && file_grouplist_096_style_exists (server))
            retval = SERVER_GROUPS_ALL;

      return retval;
}


/***
****
****  LOADING GROUPS
****
***/

static gint
read_group_data (Server        * server,
                 StatusItem    * status,
                 const char    * dat_str,
                 guint           dat_len,
                 const char    * idx,
                 int           * discarded_qty)
{
      gint version;
      GPtrArray * groups;
      const gchar * march_idx = idx;
      gint group_qty = 0;
      debug_enter ("read_group_data");

      /* sanity clause */
      g_return_val_if_fail (server_is_valid (server), 0);
      g_return_val_if_fail (dat_str!=NULL, 0);
      g_return_val_if_fail (idx!=NULL, 0);
      g_return_val_if_fail (discarded_qty!=NULL, 0);

      *discarded_qty = 0;
      version = get_next_token_int (march_idx, '\n', &march_idx);
      groups = g_ptr_array_new ();
      if (version==1 || version==2 || version==3 || version==4 || 
          version==5 || version==6 || version==7 || version==8 ||
          version==9 || version==10 || version==11 || version==12 || version==13)
      {
            long i;
            long qty = get_next_token_long (march_idx, '\n', &march_idx);

            pan_g_ptr_array_reserve (groups, qty);

            if (status != NULL)
                  status_item_emit_init_steps (status, qty);

            for (i=0; i<qty; ++i)
            {
                  const char * newsrc;
                  const char * purged;
                  Group * g;
                  glong l;

                  /* group name */
                  l = get_next_token_long (march_idx, '\n', &march_idx);
                  g = group_new (server, dat_str+l);

                  /* readable name removed in version 12 */
                  if (version<12)
                        skip_next_token (march_idx, '\n', &march_idx);

                  /* description */
                  l = get_next_token_long (march_idx, '\n', &march_idx);
                  if (0<=l && l<dat_len)
                        g->description = g_strdup (dat_str+l);

                  /* download dir */
                  l = get_next_token_long (march_idx, '\n', &march_idx);
                  if (0<=l && l<dat_len)
                        g->download_dir = g_strdup (dat_str+l);

                  /* default charset added in version 11 */
                  if (version>=11) {
                        l = get_next_token_long (march_idx, '\n', &march_idx);
                        if (0<=l && l<dat_len)
                              g->default_charset = g_strdup (dat_str+l);
                  }

                  /* newsrc string */
                  newsrc = NULL;
                  l = get_next_token_long (march_idx, '\n', &march_idx);
                  if (0<=l && l<dat_len)
                        newsrc = dat_str+l;

                  /* purged crosspost string added in version 3 */
                  purged = NULL;
                  if (version>=3) {
                        l = get_next_token_long (march_idx, '\n', &march_idx);
                        if (0<=l && l<dat_len)
                              purged = dat_str+l;
                  }

                  /* filter name added in version 4 */
                  if (version>=4 ) {
                        l = get_next_token_long (march_idx, '\n', &march_idx);
                        if (0<=l && l<dat_len)
                              g->filter_name = g_strdup (dat_str+l);
                  }

                  /* identity name added in version 6 */
                  if (version>=6) {
                        l = get_next_token_long (march_idx, '\n', &march_idx);
                        if (0<=l && l<dat_len)
                              g->identity_name = g_strdup (dat_str+l);
                  }

                  g->flags = (gint8)   get_next_token_int   (march_idx, '\n', &march_idx);

                  if (version>=8)
                        g->filter_show = get_next_token_ulong (march_idx, '\n', &march_idx);
                  else
                        g->filter_show = -1;
                  if (g->filter_show!=FILTER_SHOW_MATCHES && g->filter_show!=FILTER_SHOW_SUBTHREADS && g->filter_show!=FILTER_SHOW_THREAD)
                        g->filter_show = FILTER_SHOW_MATCHES;

                  /* the filter bitfields changed between versions 12 and 13 ...
                      reset the fields for file format versions < 13 */
                  if (version >= 13) {
                        g->filter_bits = (guint32) get_next_token_ulong (march_idx, '\n', &march_idx);
                  } else {
                        skip_next_token (march_idx, '\n', &march_idx);
                        g->filter_bits = ~STATE_FILTER_SCORE_IGNORED;
                  }

                  /* old sort style */
                  if (version >= 10)
                        g->old_sort_style = (gint8) get_next_token_int (march_idx, '\n', &march_idx);

                  g->new_sort_style = (gint8) get_next_token_int (march_idx, '\n', &march_idx);

                  /* compare sort styles */
                  if (version >= 10)
                        if (abs(g->old_sort_style) == abs(g->new_sort_style))
                              g->old_sort_style = 0;

                  g->article_low = (gulong) get_next_token_ulong (march_idx, '\n', &march_idx);

                  if (version>=9)
                        g->loaded_since_last_fetch = get_next_token_int (march_idx, '\n', &march_idx) != 0;
                  else
                        g->loaded_since_last_fetch = FALSE;

                  g->article_high     = (gulong)  get_next_token_ulong (march_idx, '\n', &march_idx);

                  /* filter_bits got widened from guint16 to guint32 in version 7 */
                  if (version<=6)  
                        g->filter_bits |= 0xFFFF8000;

                  /* article_high_old added in version 5 */
                  if (version>=5)
                        g->article_high_old = (gulong)  get_next_token_ulong (march_idx, '\n', &march_idx);
                  else
                        g->article_high_old =  g->article_high;

                  g->article_qty      = (gint32)  get_next_token_int   (march_idx, '\n', &march_idx);
                  g->article_read_qty = (gint32)  get_next_token_int   (march_idx, '\n', &march_idx);

                  /* permissions added in version 2 */
                  if (version==1)
                  {
                        g->permission = 'y'; /* a reasonable default... */
                  }
                  else if (version>=2)
                  {
                        gchar ch = '\0';
                        if (march_idx!=NULL)
                              ch = *march_idx;
                        if (ch!='y' && ch!='n')
                              ch = '?';
                        g->permission = ch;
                        skip_next_token (march_idx, '\n', &march_idx);
                  }

                  group_set_newsrc_read_string (g, newsrc);

                  group_set_purged_string (g, purged);

                  if (group_is_valid (g))
                        g_ptr_array_add (groups, g);
                  else
                        ++(*discarded_qty);

                  if (status != NULL) {
                        status_item_emit_next_step (status);
                        if (!(groups->len % 1536))
                              status_item_emit_status_va (status,
                                    _("Loading %u of %d groups"), groups->len, qty);
                  }
            }
      }
      else /* unsupported version */
      {
            log_add_va (LOG_ERROR, _("Unsupported data version %d for \"%s\" data file.\nAre you running an old version of Pan by accident?"),
                  version,
                  server_get_name (server));
      }

      group_qty = groups->len;

      if (groups->len != 0) {
            GPtrArray * unused = g_ptr_array_new ();
            server_init_groups (server, (Group**)groups->pdata, groups->len, NULL, unused);
            pan_g_ptr_array_foreach (unused, (GFunc)pan_object_unref, NULL);
            g_ptr_array_free (unused, TRUE);
      }

      g_ptr_array_free (groups, TRUE);
      debug_exit ("read_group_data");
      return group_qty;
}

static gint
file_grouplist_load_file (Server * server, StatusItem * status, const gchar * filemarker)
{
      char * dat = NULL;
      char * idx = NULL;
      gsize dat_len = 0;
      gsize idx_len = 0;
      int group_qty = 0;
      char idx_path [PATH_MAX];
      char dat_path [PATH_MAX];
      debug_enter ("file_grouplist_load_file");

      /* sanity clause */
      g_return_val_if_fail (server_is_valid (server), 0);
      g_return_val_if_fail (filemarker!=NULL, 0);

      /* open the index file */
      g_snprintf (idx_path, sizeof(idx_path), "%s%c%s%s.idx", get_data_dir(), G_DIR_SEPARATOR, server_get_name (server), filemarker);
      pan_file_normalize_inplace (idx_path);
      g_file_get_contents (idx_path, &idx, &idx_len, NULL);

      /* open the data file */
      g_snprintf (dat_path, sizeof(dat_path), "%s%c%s%s.dat", get_data_dir(), G_DIR_SEPARATOR, server_get_name (server), filemarker);
      pan_file_normalize_inplace (dat_path);
      g_file_get_contents (dat_path, &dat, &dat_len, NULL);

      /* read the groups */
      if (dat!=NULL && dat_len!=0 && idx!=NULL && idx_len!=0) {
            int discarded_qty = 0;
            group_qty = read_group_data (server, status, dat, dat_len, idx, &discarded_qty);
            if (discarded_qty > 0)
                  log_add_va (LOG_URGENT|LOG_ERROR, _("Skipped %d groups due to possible corruption in \"%s\" and \"%s\".  You may want to delete these files and get a new grouplist from your news server."), discarded_qty, idx_path, dat_path);
      }

      /* cleanup */
      g_free (dat);
      g_free (idx);

      debug_exit ("file_grouplist_load_file");
      return group_qty;
}

void
file_grouplist_load (Server * server, ServerGroupsType group_type, StatusItem * status)
{
      GTimeVal start;
      int group_qty = 0;
      debug_enter ("file_grouplist_load");

      g_return_if_fail (server_is_valid (server));
      g_return_if_fail (group_type!=0);

      if (pstring_equal (&server->name, &INTERNAL_SERVER_NAME))
            return;

      g_get_current_time (&start);

      if (file_grouplist_097_style_exists (server))
      {
            /* load from single files */
            if (group_type & SERVER_GROUPS_SUBSCRIBED)
                  group_qty += file_grouplist_load_file (server, status, "_sub");
            if (group_type & SERVER_GROUPS_UNSUBSCRIBED)
                  group_qty += file_grouplist_load_file (server, status, "_unsub");
      }
      else if (file_grouplist_096_style_exists(server))
      {
            /* load in from Pan 0.9.6-style data file */
            group_qty += file_grouplist_load_file (server, status, "");
            server->_groups_loaded = SERVER_GROUPS_ALL;
      }

      /* stats */ 
      if (group_qty > 0)
      {
            GTimeVal finish;
            double diff;

            g_get_current_time (&finish);
            diff = finish.tv_sec - start.tv_sec;
            diff += (finish.tv_usec - start.tv_usec)/(double)G_USEC_PER_SEC;
            log_add_va (LOG_INFO, _("Loaded %d groups for server \"%s\" in %.1f seconds (%.0f groups/sec)"),
                  group_qty,
                  server_get_name (server),
                  diff,
                  group_qty/(fabs(diff)<0.001?0.001:diff));
      }

      debug_exit ("file_grouplist_load");
}


/***
****
****  SAVING GROUPS
****
***/

static long
write_to_file (FILE * out_fp, long * pos, const char * str)
{
      long retval;

      if (!is_nonempty_string (str))
            retval = -1;
      else {
            retval = *pos;
            *pos += fwrite (str, 1, strlen(str)+1, out_fp);
      }

      return retval;
}

static void
write_groups (FILE * idx_fp, FILE * dat_fp, GPtrArray * groups, StatusItem * status)
{
      guint i;
      glong pos = 0;
      debug_enter ("write_groups");

      /* sanity clause */
      g_return_if_fail (idx_fp!=NULL);
      g_return_if_fail (dat_fp!=NULL);
      g_return_if_fail (groups!=NULL);
      g_return_if_fail (0<=groups->len);

      /* Write the group information... */
      pos = 0;
      fprintf (idx_fp, "13\n"); /* file format version number */
      fprintf (idx_fp, "%u\n", groups->len); /* number of groups */
      for (i=0; i!=groups->len; ++i)
      {
            Group * group = GROUP(g_ptr_array_index (groups, i));
            char * pch = group_get_newsrc_read_string (group);
            char * purged = group_get_purged_string (group);

            /* write the group name */
            const long name_idx = write_to_file (dat_fp, &pos, group->name.str);
            const long desc_idx = write_to_file (dat_fp, &pos, group->description);
            const long path_idx = write_to_file (dat_fp, &pos, group->download_dir);
            const long char_idx = write_to_file (dat_fp, &pos, group->default_charset);
            const long news_idx = write_to_file (dat_fp, &pos, pch);
            const long dead_idx = write_to_file (dat_fp, &pos, purged);
            const long filt_idx = write_to_file (dat_fp, &pos, group->filter_name);
            const long iden_idx = write_to_file (dat_fp, &pos, group->identity_name);

            /* write the non-string fields. */
            fprintf (idx_fp,
                     "%ld\n" "%ld\n" "%ld\n" "%ld\n" "%ld\n" "%ld\n" "%ld\n" "%ld\n"
                     "%u\n" "%lu\n" "%lu\n" "%d\n" "%d\n" "%d\n" "%lu\n" "%lu\n" "%lu\n" "%d\n" "%d\n" "%c\n",
                     name_idx, desc_idx, path_idx, char_idx, news_idx, dead_idx, filt_idx, iden_idx,
                     (unsigned)(group->flags & ~GROUP_NEW),
                     group->filter_show,
                     (gulong)group->filter_bits,
                     (int)group->old_sort_style,
                     (int)group->new_sort_style,
                     (int)(group->loaded_since_last_fetch ? 1 : 0),
                     (unsigned long)group->article_low,
                     (unsigned long)group->article_high,
                   (unsigned long)group->article_high_old,
                     (int)group->article_qty,
                     (int)group->article_read_qty,
                     (char)(group->permission!='\0' ? group->permission : '?'));

            /* let the user know what's going on */
            if (status != NULL) {
                  status_item_emit_next_step (status);
                  if (!(i % 1024))
                        status_item_emit_status_va (status,
                              _("Saved %d of %u groups"),
                              i, groups->len);
            }

            /* cleanup */
            g_free (pch);
            g_free (purged);
      }

      debug_exit ("write_groups");
}

static void
save_groups_to_file (Server * server, GPtrArray * groups, gchar * filemarker, StatusItem * status)
{
      char idx_fname[PATH_MAX];
      char dat_fname[PATH_MAX];
      char tmp_idx_fname[PATH_MAX];
      char tmp_dat_fname[PATH_MAX];
      debug_enter ("save_groups_to_file");

      /* sanity clause */
      g_return_if_fail (server_is_valid (server));
      g_return_if_fail (groups!=NULL);
      g_return_if_fail (is_nonempty_string(filemarker));

      /* build the filenames */
      g_snprintf (idx_fname, sizeof(idx_fname), "%s%c%s_%s.idx", get_data_dir(), G_DIR_SEPARATOR, server_get_name (server), filemarker);
      g_snprintf (dat_fname, sizeof(dat_fname), "%s%c%s_%s.dat", get_data_dir(), G_DIR_SEPARATOR, server_get_name (server), filemarker);
      g_snprintf (tmp_idx_fname, sizeof(tmp_idx_fname), "%s.tmp", idx_fname);
      g_snprintf (tmp_dat_fname, sizeof(tmp_dat_fname), "%s.tmp", dat_fname);

      if (groups->len == 0)
      {
            unlink (idx_fname);
            unlink (dat_fname);
      }
      else
      {
            FILE * idx_fp = fopen (tmp_idx_fname, "w+");
            FILE * dat_fp = fopen (tmp_dat_fname, "w+");
            gboolean ok;

            /* make sure that we were able to create the temp files */
            if (idx_fp==NULL || dat_fp==NULL) {
                  fclose (idx_fp);
                  fclose (dat_fp);
                  g_return_if_fail (0);
            }

            /* save the groups */
            write_groups (idx_fp, dat_fp, groups, status);
            ok = !ferror(idx_fp) && !ferror(dat_fp);
            fclose (idx_fp);
            fclose (dat_fp);

            /* swap the temp files (on success) or delete them (on error) */
            if (ok) {
                  pan_file_swap_datafile (tmp_idx_fname, idx_fname);
                  pan_file_swap_datafile (tmp_dat_fname, dat_fname);
            } else {
                  log_add_va (LOG_ERROR, _("Error saving list of groups to \"%s\" - is the disk full?"), get_data_dir());
                  unlink (tmp_idx_fname);
                  unlink (tmp_dat_fname);
            }
      }

      debug_exit ("save_groups_to_file");
}

void
file_grouplist_save (Server * server, ServerGroupsType group_type, StatusItem * status)
{
      int save_qty = 0;
      GTimeVal start;
      debug_enter ("file_grouplist_save");

      /* sanity clause */
      g_return_if_fail (server_is_valid (server));
      g_return_if_fail ((group_type|SERVER_GROUPS_SUBSCRIBED)||(group_type|SERVER_GROUPS_UNSUBSCRIBED));

      if (pstring_equal (&server->name, &INTERNAL_SERVER_NAME))
            return;

      /**
      ***  Save the Groups
      **/

      g_get_current_time (&start);

      if (group_type & SERVER_GROUPS_SUBSCRIBED)
      {
            GPtrArray * a = server_get_groups (server, SERVER_GROUPS_SUBSCRIBED);
            save_qty += a->len;
            save_groups_to_file (server, a, "sub", status);
            g_ptr_array_free (a, TRUE);
      }
      if (group_type & SERVER_GROUPS_UNSUBSCRIBED)
      {
            GPtrArray * a = server_get_groups (server, SERVER_GROUPS_UNSUBSCRIBED);
            save_qty += a->len;
            save_groups_to_file (server, a, "unsub", status);
            g_ptr_array_free (a, TRUE);
      }

      /* finish timing stats */
      if (save_qty > 0)
      {
            GTimeVal finish;
            double diff;

            g_get_current_time (&finish);
            diff = finish.tv_sec - start.tv_sec;
            diff += (finish.tv_usec - start.tv_usec)/(double)G_USEC_PER_SEC;
            log_add_va (LOG_INFO, _("Saved %d groups in \"%s\" in %.1f seconds (%.0f groups/sec)"),
                  save_qty,
                  server_get_name (server),
                  diff,
                  save_qty/(fabs(diff)<0.001?0.001:diff));
      }

      debug_exit ("file_grouplist_save");
}

/***
****
***/

void
file_grouplist_server_name_changed  (const PString * old_name,
                                     const PString * new_name)
{
      char old_path [PATH_MAX];
      char new_path [PATH_MAX];

      /* sanity clause */
      g_return_if_fail (pstring_is_set (old_name));
      g_return_if_fail (pstring_is_set (new_name));

      /* rename the files: sub idx */
      g_snprintf (old_path, sizeof(old_path), "%s%c%*.*s_sub.idx", get_data_dir(), G_DIR_SEPARATOR,
                  old_name->len, old_name->len, old_name->str);
      if (pan_file_exists (old_path)) {
            g_snprintf (new_path, sizeof(new_path), "%s%c%*.*s_sub.idx", get_data_dir(), G_DIR_SEPARATOR,
                        new_name->len, new_name->len, new_name->str);
            pan_file_rename (old_path, new_path);
      }

      /* rename the files: sub dat */
      g_snprintf (old_path, sizeof(old_path), "%s%c%*.*s_sub.dat", get_data_dir(), G_DIR_SEPARATOR,
                  old_name->len, old_name->len, old_name->str);
      if (pan_file_exists (old_path)) {
            g_snprintf (new_path, sizeof(new_path), "%s%c%*.*s_sub.dat", get_data_dir(), G_DIR_SEPARATOR,
                        new_name->len, new_name->len, new_name->str);
            pan_file_rename (old_path, new_path);
      }

      /* rename the files: unsub idx */
      g_snprintf (old_path, sizeof(old_path), "%s%c%*.*s_unsub.idx", get_data_dir(), G_DIR_SEPARATOR,
                  old_name->len, old_name->len, old_name->str);
      if (pan_file_exists (old_path)) {
            g_snprintf (new_path, sizeof(new_path), "%s%c%*.*s_unsub.idx", get_data_dir(), G_DIR_SEPARATOR,
                        new_name->len, new_name->len, new_name->str);
            pan_file_rename (old_path, new_path);
      }

      /* rename the files: unsub dat */
      g_snprintf (old_path, sizeof(old_path), "%s%c%*.*s_unsub.dat", get_data_dir(), G_DIR_SEPARATOR,
                  old_name->len, old_name->len, old_name->str);
      if (pan_file_exists (old_path)) {
            g_snprintf (new_path, sizeof(new_path), "%s%c%*.*s_unsub.dat", get_data_dir(), G_DIR_SEPARATOR,
                        new_name->len, new_name->len, new_name->str);
            pan_file_rename (old_path, new_path);
      }
}

Generated by  Doxygen 1.6.0   Back to index