Re: [Evolution-hackers] evolution default timezone



JP Rosevear wrote:
On Tue, 2004-08-03 at 13:04 -0400, William Jon McCann wrote:
I guess a better solution would be to get the Olson string from the system but there doesn't seem to be a reliable way to do that.

I don't believe so, which is why libical has its own zone info.

I think I have a way to do this. It should catch just about every case. The approach is similar to that of the Perl module DateTime::TimeZone::Local.

So far, I have tested it with FC2, RHEL3, and Solaris9.

What do you think?

Jon
GLIB_CFLAGS = `pkg-config --cflags glib-2.0`
GLIB_LDADD = `pkg-config --libs glib-2.0`

CFLAGS = $(GLIB_CFLAGS)
LDFLAGS = $(GLIB_LDADD)

OBJECTS = getzone.o

all: getzone

getzone: $(OBJECTS)
	$(CC) $(OBJECTS) $(LDFLAGS) -o getzone

$(OBJECTS): Makefile

clean:
	-rm -rf getzone $(OBJECTS) *~

.PHONY: clean
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
 * getzone.c: report local timezone
 *
 * Copyright (C) 2004 William Jon McCann
 *
 * 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; either version 2 of the
 * License, or (at your option) any later version.
 *
 * 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 <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#if defined(__linux__)
# include <stdint.h>
#endif

#include <glib.h>
#include <glib/gprintf.h>

#define ETC_LOCALTIME       "/etc/localtime"
#define ETC_TIMEZONE1       "/etc/timezone"
#define ETC_TIMEZONE2       "/etc/TIMEZONE"
#define ETC_SYSCONFIG_CLOCK "/etc/sysconfig/clock"
#define ETC_DEFAULT_INIT    "/etc/default/init"

#if defined(__linux__) || defined(__FreeBSD__)
# define ZONEINFO_DIR      "/usr/share/zoneinfo"
#elif defined(sun)
# define ZONEINFO_DIR      "/usr/share/lib/zoneinfo"
#else
# define ZONEINFO_DIR      "/usr/share/zoneinfo"
#endif

#ifndef O_BINARY
# define O_BINARY 0
#endif

#ifndef word
# define word uintmax_t
#endif

#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
#endif
#if !EXIT_FAILURE
# undef EXIT_FAILURE /* Sony NEWS-OS 4.0C defines EXIT_FAILURE to 0.  */
# define EXIT_FAILURE 1
#endif
#define EXIT_TROUBLE 2

#define DEBUG(x) x

char      *get_timezone_name (void);
static int cmp               (int fd1, int fd2);

/*
 * The following two functions are only needed on systems
 * that have a glib version before 2.4
 */
#if GLIB_CHECK_VERSION(2,4,0)
#else
gchar *
g_file_read_link (const gchar *filename,
                  GError     **error)
{
  gchar *buffer;
  guint size;
  gint read_size;

  size = 256;
  buffer = g_malloc (size);

  while (TRUE)
    {
      read_size = readlink (filename, buffer, size);
      if (read_size < 0) {
        g_free (buffer);
        return NULL;
      }

      if (read_size < size)
        {
          buffer[read_size] = 0;
          return buffer;
        }

      size *= 2;
      buffer = g_realloc (buffer, size);
    }
}
gchar **
g_strsplit_set (const gchar *string,
                const gchar *delimiters,
                gint         max_tokens)
{
  gboolean delim_table[256];
  GSList *tokens, *list;
  gint n_tokens;
  const gchar *s;
  const gchar *current;
  gchar *token;
  gchar **result;

  g_return_val_if_fail (string != NULL, NULL);
  g_return_val_if_fail (delimiters != NULL, NULL);

  if (max_tokens < 1)
    max_tokens = G_MAXINT;

  if (*string == '\0')
    {
      result = g_new (char *, 1);
      result[0] = NULL;
      return result;
    }

  memset (delim_table, FALSE, sizeof (delim_table));
  for (s = delimiters; *s != '\0'; ++s)
    delim_table[*(guchar *)s] = TRUE;

  tokens = NULL;
  n_tokens = 0;

  s = current = string;
  while (*s != '\0')
    {
      if (delim_table[*(guchar *)s] && n_tokens + 1 < max_tokens)
        {
          gchar *token;

          token = g_strndup (current, s - current);
          tokens = g_slist_prepend (tokens, token);
          ++n_tokens;

          current = s + 1;
        }

      ++s;
    }

  token = g_strndup (current, s - current);
  tokens = g_slist_prepend (tokens, token);
  ++n_tokens;

  result = g_new (gchar *, n_tokens + 1);

  result[n_tokens] = NULL;
  for (list = tokens; list != NULL; list = list->next)
    result[--n_tokens] = list->data;

  g_slist_free (tokens);

  return result;
}
#endif

static gboolean
could_be_valid_time_zone (const char *zone)
{
        if (!zone)
                return FALSE;

        if (!strcmp (zone, "local"))
                return FALSE;

        /* TODO: other tests */

        return TRUE;
}

static int
compare_node (const char *file1,
              const char *file2)
{
        struct stat sb1;
        struct stat sb2;

        g_return_val_if_fail (file1 != NULL, -1);
        g_return_val_if_fail (file2 != NULL, -1);

        if (stat (file1, &sb1) < 0)
                return -1;
        if (stat (file2, &sb2) < 0)
                return -1;

        if ((sb1.st_dev == sb2.st_dev) &&
            (sb1.st_ino == sb2.st_ino))
                return 0;               /* Files are the same */

        if (sb1.st_size > sb2.st_size)  /* File 1 is longer */
                return 1;
        if (sb1.st_size < sb2.st_size)  /* File 2 is longer */
                return 2;

        return 3;                       /* Files may differ */
}

static gboolean
compare_files (const char *file1,
               const char *file2)
{
        int fd1, fd2;
        int res;

        fd1 = open (file1, O_RDONLY|O_BINARY);
        if (fd1 < 0)
                return FALSE;

        fd2 = open (file2, O_RDONLY|O_BINARY);
        if (fd2 < 0) {
                close (fd1);
                return FALSE;
        }

        DEBUG(g_message ("comparing: '%s' '%s'", file1, file2));
        res = cmp (fd1, fd2);
        close (fd1);
        close (fd2);

        return res;
}

static char *
search_dir (const char *file,
            const char *path)
{
	GDir       *dir;
	const char *d;
	char       *new  = NULL;
	char       *name = NULL;
	struct stat sb1;
        struct stat sb2;

        if (stat (file, &sb1) < 0)
                return NULL;

	dir = g_dir_open (path, 0, NULL);
	if (dir == NULL)
		return NULL;

	while ( (d = g_dir_read_name (dir)) ) {
		new = g_build_filename (path, d, NULL);
                DEBUG(g_message ("checking '%s'", new));
		if (stat (new, &sb2) == -1) {
                        g_free (new);
			continue;
                }

		if (S_ISDIR (sb2.st_mode)) {
			name = search_dir (file, new);
                        if (name)
                                break;
		} else {
                        if ((sb1.st_dev == sb2.st_dev) &&
                            (sb1.st_ino == sb2.st_ino)) {
                                name = new;
                                break;
                        } else if (sb1.st_size != sb2.st_size) {
                                g_free (new);
                                continue;
                        } else if (compare_files (file, new) == EXIT_SUCCESS) {
                                DEBUG(g_message ("Matched"));
                                name = new;
                                break;
                        }
		}
		g_free (new);
		new = NULL;
	}

	g_dir_close (dir);

	return name;
}

static char *
find_matching_zoneinfo_file (const char *needle,
                             const char *haystack)
{
        int   i;
        char *name = NULL;
        const char *search_order[] = { "America",
                                       "Europe",
                                       "Asia",
                                       "Australia",
                                       "Indian",
                                       "Pacific",
                                       "Atlantic",
                                       "Africa",
                                       "Antarctica",
                                       NULL };

        if (!g_file_test (haystack, G_FILE_TEST_IS_DIR) ||
            !g_file_test (haystack, G_FILE_TEST_EXISTS))
                return NULL;
        
        for (i = 0; search_order[i] != NULL; i++) {
                char *path = g_build_filename (haystack,
                                               search_order[i],
                                               NULL);
                name = search_dir (needle, path);
                g_free (path);
                
                if (name)
                        break;
        }

        return name;
}

static char *
get_name_from_zonefile (const char *filename,
                        const char *zonepath)
{
        char *name  = NULL;
        char *file1 = g_path_get_basename (filename);
        char *path1 = g_path_get_dirname (filename);
        
        /* zone files seem to only go 3 deep */
        if (compare_node (path1, zonepath) == 0) {
                name = g_strdup (file1);
        } else {
                char *file2 = g_path_get_basename (path1);
                char *path2 = g_path_get_dirname (path1);
                
                if (compare_node (path2, zonepath) == 0) {
                        name = g_strjoin ("/", file2, file1, NULL);
                } else {
                        char *file3 = g_path_get_basename (path2);
                        char *path3 = g_path_get_dirname (path2);
                        
                        if (compare_node (path3, zonepath) == 0) {
                                name = g_strjoin ("/", file3, file2, file1, NULL);
                        }
                        g_free (path3);
                        g_free (file3);
                }
                g_free (path2);
                g_free (file2);
        }
        g_free (path1);
        g_free (file1);
        
        return name;
}

static char *
get_name_from_etc_localtime_match (const char *filename)
{
        char *name     = NULL;
        char *zonefile = NULL;

        if (g_file_test (filename, G_FILE_TEST_IS_DIR) ||
            !g_file_test (filename, G_FILE_TEST_EXISTS))
                return NULL;

        zonefile = find_matching_zoneinfo_file (filename, ZONEINFO_DIR);
        if (!zonefile)
                return NULL;

        name = get_name_from_zonefile (zonefile, ZONEINFO_DIR);
        return name;
}

static char *
get_name_from_etc_localtime_symlink (const char *filename)
{
        char *name     = NULL;
        char *zonefile = NULL;
        char *temp1;

        if (g_file_test (filename, G_FILE_TEST_IS_DIR) ||
            !g_file_test (filename, G_FILE_TEST_EXISTS))
                return NULL;

        if (!g_file_test (filename, G_FILE_TEST_IS_SYMLINK))
                return NULL;

        temp1 = g_file_read_link (filename, NULL);
        if (!g_path_is_absolute (temp1)) {
                char *temp2;
                temp2 = g_path_get_dirname (filename);
                zonefile = g_build_filename (temp2, temp1, NULL);
                g_free (temp2);
        } else {
                zonefile = g_strdup (temp1);
        }
        g_free (temp1);

        if (!zonefile)
                return NULL;

        name = get_name_from_zonefile (zonefile, ZONEINFO_DIR);

        g_free (zonefile);

        return name;
}

static char *
get_name_from_etc_timezone1 (const char *filename)
{
        char  *name     = NULL;
        char  *contents = NULL;
        char **lines    = NULL;
        char **parts    = NULL;
        gsize  length;
        int    i;

        if (g_file_test (filename, G_FILE_TEST_IS_DIR) ||
            !g_file_test (filename, G_FILE_TEST_EXISTS))
                return NULL;

        if (!g_file_get_contents (filename,
                                  &contents,
                                  &length,
                                  NULL))
                return NULL;

        lines = g_strsplit_set (contents, "\n", 0);

        for (i = 0; lines[i] != NULL; i++) {
                const char *endpos;
                char       *temp = NULL;

                endpos = strchr (lines[i], '#');
                if (endpos == lines[i])
                        continue;

                if (endpos)
                        temp = g_strndup (lines[i], endpos - lines[i]);
                else
                        temp = g_strdup (lines[i]);

                temp = g_strstrip (temp);
                if (!strcmp (temp, "")) {
                        g_free (temp);
                        continue;
                }

                parts = g_strsplit_set (temp, "\t\r ", 0);
                g_free (temp);

                if (!parts[0])
                        continue;

                name = g_strdup (parts[0]);
                g_strfreev (parts);
                break;
        }

        g_strfreev (lines);
        g_free (contents);

        return name;
}

static char *
get_name_from_etc_timezone2 (const char *filename)
{
        char       *name     = NULL;
        char       *contents = NULL;
        const char *startpos;
        const char *endpos;
        gsize       length;

        if (g_file_test (filename, G_FILE_TEST_IS_DIR) ||
            !g_file_test (filename, G_FILE_TEST_EXISTS))
                return NULL;

        if (!g_file_get_contents (filename,
                                  &contents,
                                  &length,
                                  NULL))
                return NULL;

        startpos = strstr (contents, "TZ=");
        if (!startpos)
                return NULL;
        startpos += 3;

        endpos = strchr (startpos, '\n');
        if (!endpos)
                name = g_strdup (startpos);
        else
                name = g_strndup (startpos, endpos - startpos);

        name = g_strstrip (name);

        g_free (contents);

        return name;
}

static char *
get_name_from_etc_sysconfig_clock (const char *filename)
{
        char       *name     = NULL;
        char       *contents = NULL;
        const char *startpos;
        const char *endpos;
        gsize       length;

        if (g_file_test (filename, G_FILE_TEST_IS_DIR) ||
            !g_file_test (filename, G_FILE_TEST_EXISTS))
                return NULL;

        if (!g_file_get_contents (filename,
                                  &contents,
                                  &length,
                                  NULL))
                return NULL;

        startpos = strstr (contents, "ZONE=\"");
        if (!startpos)
                return NULL;
        startpos += 6;

        endpos = strchr (startpos, '"');
        if (!endpos)
                return NULL;

        name = g_strstrip (g_strndup (startpos, endpos - startpos));
        g_free (contents);

        return name;   
}

char *
get_timezone_name ()
{
        char *name = NULL;

        name = get_name_from_etc_localtime_symlink (ETC_LOCALTIME);
        if (could_be_valid_time_zone (name))
                return name;
        g_free (name);
        
        name = get_name_from_etc_timezone1 (ETC_TIMEZONE1);
        if (could_be_valid_time_zone (name))
                return name;
        g_free (name);
        
        name = get_name_from_etc_timezone2 (ETC_TIMEZONE2);
        if (could_be_valid_time_zone (name))
                return name;
        g_free (name);
        
        name = get_name_from_etc_sysconfig_clock (ETC_SYSCONFIG_CLOCK);
        if (could_be_valid_time_zone (name))
                return name;
        g_free (name);
        
        name = get_name_from_etc_timezone2 (ETC_DEFAULT_INIT);
        if (could_be_valid_time_zone (name))
                return name;
        g_free (name);
        
        name = get_name_from_etc_localtime_match (ETC_LOCALTIME);
        if (could_be_valid_time_zone (name))
                return name;
        g_free (name);

        return NULL;
}

int
main (int argc, char *argv [])
{
        char *name = NULL;

        name = get_timezone_name ();

        if (name) {
                printf ("The timezone is '%s'\n", name);
                g_free (name);
        } else {
                printf ("Could not determine timezone\n");
        }

        return 0;
}


/*
 *  The following functions are based on code from GNU diffutils
 *
 *   Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 2001,
 *   2002 Free Software Foundation, Inc.
 */

/* Read NBYTES bytes from descriptor FD into BUF.
   NBYTES must not be SIZE_MAX.
   Return the number of characters successfully read.
   On error, return SIZE_MAX, setting errno.
   The number returned is always NBYTES unless end-of-file or error.  */

static size_t
block_read (int fd, char *buf, size_t nbytes)
{
        char       *bp      = buf;
        char const *buflim  = buf + nbytes;
        size_t      readlim = SSIZE_MAX;

        do {
                size_t  bytes_to_read = MIN (buflim - bp, readlim);
                ssize_t nread         = read (fd, bp, bytes_to_read);
                if (nread <= 0) {
                        if (nread == 0)
                                break;

                        /* Accommodate Tru64 5.1, which can't read more than INT_MAX
                           bytes at a time.  They call that a 64-bit OS?  */
                        if (errno == EINVAL && INT_MAX < bytes_to_read) {
                                readlim = INT_MAX;
                                continue;
                        }

                        /* This is needed for programs that have signal handlers on
                           older hosts without SA_RESTART.  It also accommodates
                           ancient AIX hosts that set errno to EINTR after uncaught
                           SIGCONT.  See <news:1r77ojINN85n ftp UU NET>
                           (1993-04-22).  */
                        if (! SA_RESTART && errno == EINTR)
                                continue;

                        return SIZE_MAX;
                }
                bp += nread;
        }
        while (bp < buflim);

        return bp - buf;
}

/* Compare two blocks of memory P0 and P1 until they differ.
   If the blocks are not guaranteed to be different, put sentinels at the ends
   of the blocks before calling this function.

   Return the offset of the first byte that differs.  */

static size_t
block_compare (word const *p0, word const *p1)
{
        word const *l0, *l1;
        char const *c0, *c1;

        /* Find the rough position of the first difference by reading words,
           not bytes.  */

        for (l0 = p0, l1 = p1;  *l0 == *l1;  l0++, l1++)
                continue;

        /* Find the exact differing position (endianness independent).  */

        for (c0 = (char const *) l0, c1 = (char const *) l1;
             *c0 == *c1;
             c0++, c1++)
                continue;

        return c0 - (char const *) p0;
}

static int
cmp (int fd1,
     int fd2)
{
        off_t     byte_number = 1;
        uintmax_t remaining   = UINTMAX_MAX;	/* Remaining number of bytes to compare.  */
        size_t    read0, read1;	                /* Number of bytes read from each file. */
        size_t    first_diff;	                /* Offset (0...) in buffers of 1st diff. */
        size_t    smaller;                      /* The lesser of `read0' and `read1'. */
        word     *buffer0;
        word     *buffer1;
        char     *buf0;
        char     *buf1;
        int       ret = EXIT_SUCCESS;
        int       buf_size = 8 * 1024;
        int       words_per_buffer = (buf_size + 2 * sizeof (word) - 1) / sizeof (word);

        buffer0 = (word *) g_malloc (2 * sizeof (word) * words_per_buffer);
        buffer1 = buffer0 + words_per_buffer;
        buf0 = (char *) buffer0;
        buf1 = (char *) buffer1;

        do {
                size_t bytes_to_read = buf_size;

                if (remaining != UINTMAX_MAX) {
                        if (remaining < bytes_to_read)
                                bytes_to_read = remaining;
                        remaining -= bytes_to_read;
                }

                read0 = block_read (fd1, buf0, bytes_to_read);
                if (read0 == SIZE_MAX)
                        return EXIT_TROUBLE;
                read1 = block_read (fd2, buf1, bytes_to_read);
                if (read1 == SIZE_MAX)
                        return EXIT_TROUBLE;

                /* Insert sentinels for the block compare.  */

                buf0[read0] = ~buf1[read0];
                buf1[read1] = ~buf0[read1];

                /* If the line number should be written for differing files,
                   compare the blocks and count the number of newlines
                   simultaneously.  */
                first_diff = block_compare (buffer0, buffer1);

                byte_number += first_diff;
                smaller = MIN (read0, read1);

                if (first_diff < smaller)
                        return EXIT_FAILURE;

                if (read0 != read1)
                        return EXIT_FAILURE;
        }
        while (read0 == buf_size);

        g_free (buffer0);

        return ret;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]