gnome-panel r11016 - trunk/applets/clock



Author: vuntz
Date: Tue Apr  8 22:40:11 2008
New Revision: 11016
URL: http://svn.gnome.org/viewvc/gnome-panel?rev=11016&view=rev

Log:
2008-04-08  Vincent Untz  <vuntz gnome org>

	Completely rewrite the way we detect the system timezone. It will work
	on more distributions this way. Also the code is better and it helps
	improve the world.
	Good thing is that now the clock will always immediately update after
	the timezone is updated.

	* Makefile.am: add new files
	* system-timezone.[ch]: new file, implement a singleton that knows how
	to read the system timezone and that monitors it
	* clock-location.[ch]: (clock_location_new): don't get the TZ
	environment variable here
	(files_are_identical):
	(recursive_guess_zone):
	(guess_zone_from_tree):
	(parse_etc_sysconfig_clock):
	(monitor_etc_sysconfig_clock):
	(zone_from_etc_sysconfig_clock):
	(clock_location_guess_zone): killed
	(clock_location_init): updated to create the SystemTimezone object
	(clock_location_finalize): updated
	(clock_location_unset_tz): use system_timezone_get_env() instead of a
	local variable
	(clock_location_is_current_timezone): use system_timezone_get()
	(clock_location_get_offset): use clock_location_unset_tz() instead of
	duplicating code
	(make_current_cb): updated to remove the current timezone handling
	* clock-zoneinfo.h: move SYSTEM_ZONEINFODIR to system-timezone.h
	* clock-zonetable.c: add new include
	* clock.c: add a SystemTimezone object
	(destroy_clock): unref it
	(clock_timezone_changed): new, handle system timezone change
	(fill_clock_applet): create the SystemTimezone object and connect to
	its changed signal

	* clock-marshallers.list: remove marshallers that already exist in glib
	* clock-location.[ch]: (clock_location_class_init): updated for this


Added:
   trunk/applets/clock/system-timezone.c
   trunk/applets/clock/system-timezone.h
Modified:
   trunk/applets/clock/ChangeLog
   trunk/applets/clock/Makefile.am
   trunk/applets/clock/clock-location.c
   trunk/applets/clock/clock-marshallers.list
   trunk/applets/clock/clock-zoneinfo.h
   trunk/applets/clock/clock-zonetable.c
   trunk/applets/clock/clock.c

Modified: trunk/applets/clock/Makefile.am
==============================================================================
--- trunk/applets/clock/Makefile.am	(original)
+++ trunk/applets/clock/Makefile.am	Tue Apr  8 22:40:11 2008
@@ -65,6 +65,8 @@
 	clock-zoneinfo.h	\
 	clock-zonetable.c	\
 	clock-zonetable.h	\
+	system-timezone.c	\
+	system-timzone.h	\
 	$(BUILT_SOURCES)	\
 	$(CALENDAR_SOURCES)	\
 	$(SETTIME_SOURCES)

Modified: trunk/applets/clock/clock-location.c
==============================================================================
--- trunk/applets/clock/clock-location.c	(original)
+++ trunk/applets/clock/clock-location.c	Tue Apr  8 22:40:11 2008
@@ -26,13 +26,15 @@
 #include "clock-location.h"
 #include "clock-marshallers.h"
 #include "set-timezone.h"
+#include "system-timezone.h"
 
 G_DEFINE_TYPE (ClockLocation, clock_location, G_TYPE_OBJECT)
 
 typedef struct {
         gchar *name;
 
-        gchar *sys_timezone;
+        SystemTimezone *systz;
+
         gchar *timezone;
 
         gchar *tzname;
@@ -79,11 +81,6 @@
         priv->name = g_strdup (name);
         priv->timezone = g_strdup (timezone);
 
-        priv->sys_timezone = getenv ("TZ");
-        if (priv->sys_timezone) {
-                priv->sys_timezone = g_strdup (priv->sys_timezone);
-        }
-
         /* initialize priv->tzname */
         clock_location_set_tz (this);
         clock_location_unset_tz (this);
@@ -103,244 +100,7 @@
         return this;
 }
 
-static gboolean
-files_are_identical (const char *localtime, struct stat *localtime_s,
-                     const char *localtime_data, const gsize localtime_len,
-                     char *file, struct stat *file_s)
-{
-        gsize file_len = -1;
-        gchar *file_data = NULL;
-
-        if (localtime_s->st_size != file_s->st_size) {
-                return FALSE;
-        }
-
-        if (!g_file_get_contents (file, &file_data, &file_len, NULL)) {
-                return FALSE;
-        }
-
-        if (localtime_len != file_len) {
-                g_free (file_data);
-                return FALSE;
-        }
-
-        if (memcmp (localtime_data, file_data, localtime_len) == 0) {
-                g_free (file_data);
-                return TRUE;
-        }
-
-        g_free (file_data);
-        return FALSE;
-}
-
-static gchar *
-recursive_guess_zone (const char *localtime, struct stat *localtime_s,
-                      const char *localtime_data, const gsize localtime_len,
-                      char *file, struct stat *file_s, ClockZoneTable *zones)
-{
-        if (S_ISREG (file_s->st_mode)) {
-                gchar *zone = file + strlen (SYSTEM_ZONEINFODIR) + 1;
-
-                /* ignore files that aren't in the Olson database */
-                if (!clock_zonetable_get_zone (zones, zone)) {
-                        return NULL;
-                }
-
-                if (files_are_identical (localtime, localtime_s,
-                                         localtime_data, localtime_len,
-                                         file, file_s)) {
-                        return g_strdup (file + strlen (SYSTEM_ZONEINFODIR) + 1);
-                } else {
-                        return NULL;
-                }
-        } else if (S_ISDIR (file_s->st_mode)) {
-                GDir *dir = NULL;
-                gchar *ret = NULL;
-
-                const gchar *subfile = NULL;
-                gchar *subpath = NULL;
-                struct stat subpath_s;
-
-                dir = g_dir_open (file, 0, NULL);
-                if (dir == NULL) {
-                        return NULL;
-                }
-
-                while ((subfile = g_dir_read_name (dir)) != NULL) {
-                        subpath = g_build_filename (file, subfile, NULL);
-
-                        if (stat (subpath, &subpath_s) == -1) {
-                                continue;
-                        }
-
-                        ret = recursive_guess_zone (localtime, localtime_s,
-                                                    localtime_data, localtime_len,
-                                                    subpath, &subpath_s,
-                                                    zones);
-
-                        g_free (subpath);
-
-                        if (ret != NULL) {
-                                break;
-                        }
-                }
-
-                g_dir_close (dir);
-
-                return ret;
-        }
-
-        return NULL;
-}
-
-static gchar *
-guess_zone_from_tree (const gchar *localtime, ClockZoneTable *zones)
-{
-        int i;
-        struct stat s;
-        struct stat dir_s;
-        char *ret = NULL;
-
-        char *localtime_data = NULL;
-        gsize localtime_len = -1;
-
-        /* walk the zoneinfo tree and compare with
-           /etc/localtime to try to find the current zone */
-
-        i = stat (localtime, &s);
-        if (i == -1 || !S_ISREG (s.st_mode)) {
-                return NULL;
-        }
-
-        i = stat (SYSTEM_ZONEINFODIR, &dir_s);
-        if (i == -1 || !S_ISDIR (dir_s.st_mode)) {
-                return NULL;
-        }
-
-        if (!g_file_get_contents (localtime, &localtime_data,
-                                  &localtime_len, NULL)) {
-                return NULL;
-        }
-
-        ret = recursive_guess_zone (localtime, &s,
-                                    localtime_data, localtime_len,
-                                    SYSTEM_ZONEINFODIR, &dir_s, zones);
-
-        g_free (localtime_data);
-
-        return ret;
-}
-
-static gchar *current_zone = NULL;
 static ClockLocation *current_location = NULL;
-static GFileMonitor *monitor = NULL;
-
-static void
-parse_etc_sysconfig_clock (void)
-{
-	gchar *data;
-	gsize len;
-	gchar **lines;
-	gchar *res;
-	gint i;
-	gchar *p, *q;
-
-	lines = NULL;
-	res = NULL;
-	if (g_file_test ("/etc/sysconfig/clock",
-			 G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
-		if (!g_file_get_contents ("/etc/sysconfig/clock",
-		    			  &data, &len, NULL))
-			goto out;
-
-		lines = g_strsplit (data, "\n", 0);
-		g_free (data);
-
-		for (i = 0; lines[i] && !res; i++) {
-			/* If you are Fedora, uncomment these and comment out the other version
-			   if (g_str_has_prefix (lines[i], "ZONE=")) {
-			   p = lines[i] + strlen ("ZONE=");
-			*/
-			if (g_str_has_prefix (lines[i], "TIMEZONE=")) {
-				p = lines[i] + strlen ("TIMEZONE=");
-				if (p[0] != '\"')
-					goto out;
-				p++;
-				q = strchr (p, '\"');
-				q[0] = '\0';
-				res = g_strdup (p);
-			}
-		}
-	}
-
-out:
-	if (lines)
-		g_strfreev (lines);
-
-	g_free (current_zone);
-	current_zone = res;
-}
-
-static void
-monitor_etc_sysconfig_clock (GFileMonitor *handle,
-			     GFile *file,
-			     GFile *other_file,
-			     GFileMonitorEvent event,
-			     gpointer user_data)
-{
-	parse_etc_sysconfig_clock ();
-}
-
-static const gchar *
-zone_from_etc_sysconfig_clock (void)
-{
-	if (monitor == NULL) {
-		GFile *file;
-
-		parse_etc_sysconfig_clock ();
-		
-		file = g_file_new_for_path ("/etc/sysconfig/clock");
-
-		monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
-					       NULL, NULL);
-
-		g_object_unref (file);
-
-		if (monitor)
-			g_signal_connect (G_OBJECT (monitor), "changed", 
-					  G_CALLBACK (monitor_etc_sysconfig_clock),
-					  NULL);
-	}
-
-	return current_zone;
-}
-
-static gchar *
-clock_location_guess_zone (ClockZoneTable *zones)
-{
-        const char *localtime = "/etc/localtime";
-        gchar *linkfile = NULL;
-        GError *err = NULL;
-	const gchar *zone;
-
-	/* look for /etc/sysconfig/clock */
-	if ((zone = zone_from_etc_sysconfig_clock ())) {
-		return g_strdup (zone);
-	}
-
-        /* guess the current time zone by readlink() on /etc/localtime */
-        linkfile = g_file_read_link (localtime, &err);
-        if (err) {
-                return guess_zone_from_tree (localtime, zones);
-        }
-
-        if (strncmp (linkfile, SYSTEM_ZONEINFODIR,
-                     strlen (SYSTEM_ZONEINFODIR)) == 0) {
-                return g_strdup (linkfile + strlen (SYSTEM_ZONEINFODIR) + 1);
-        }
-
-        return NULL;
-}
 
 static void
 clock_location_class_init (ClockLocationClass *this_class)
@@ -355,7 +115,7 @@
 			      G_SIGNAL_RUN_FIRST,
 			      G_STRUCT_OFFSET (ClockLocationClass, weather_updated),
 			      NULL, NULL,
-			      _clock_marshal_VOID__POINTER,
+			      g_cclosure_marshal_VOID__POINTER,
 			      G_TYPE_NONE, 1, G_TYPE_POINTER);
 
 	location_signals[SET_CURRENT] = 
@@ -364,7 +124,7 @@
 			      G_SIGNAL_RUN_FIRST,
 			      G_STRUCT_OFFSET (ClockLocationClass, set_current),
 			      NULL, NULL,
-			      _clock_marshal_VOID__VOID,
+			      g_cclosure_marshal_VOID__VOID,
 			      G_TYPE_NONE, 0);
 
         g_type_class_add_private (this_class, sizeof (ClockLocationPrivate));
@@ -377,7 +137,8 @@
 
         priv->name = NULL;
 
-        priv->sys_timezone = NULL;
+        priv->systz = system_timezone_new ();
+
         priv->timezone = NULL;
 
         priv->tzname = NULL;
@@ -401,16 +162,16 @@
                 priv->name = NULL;
         }
 
+        if (priv->systz) {
+                g_object_unref (priv->systz);
+                priv->systz = NULL;
+        }
+
         if (priv->timezone) {
                 g_free (priv->timezone);
                 priv->timezone = NULL;
         }
 
-        if (priv->sys_timezone) {
-                g_free (priv->sys_timezone);
-                priv->sys_timezone = NULL;
-        }
-
         if (priv->tzname) {
                 g_free (priv->tzname);
                 priv->tzname = NULL;
@@ -431,12 +192,6 @@
                 priv->weather_timeout = 0;
         }
 
-	if (monitor) {
-		g_file_monitor_cancel (monitor);
-		g_object_unref (monitor);
-		monitor = NULL;
-	}
-
         G_OBJECT_CLASS (clock_location_parent_class)->finalize (g_obj);
 }
 
@@ -560,13 +315,16 @@
 clock_location_unset_tz (ClockLocation *this)
 {
         ClockLocationPrivate *priv = PRIVATE (this);
+        const char *env_timezone;
 
         if (priv->timezone == NULL) {
                 return;
         }
 
-        if (priv->sys_timezone) {
-                setenv ("TZ", priv->sys_timezone, 1);
+        env_timezone = system_timezone_get_env (priv->systz);
+
+        if (env_timezone) {
+                setenv ("TZ", env_timezone, 1);
         } else {
                 unsetenv ("TZ");
         }
@@ -592,7 +350,9 @@
         ClockLocationPrivate *priv = PRIVATE (loc);
 	const char *zone;
 
-	if ((zone = zone_from_etc_sysconfig_clock ()))
+	zone = system_timezone_get (priv->systz);
+
+	if (zone)
 		return strcmp (zone, priv->timezone) == 0;
 	else
 		return clock_location_get_offset (loc) == 0;
@@ -652,12 +412,7 @@
 
         offset = local_timezone - sys_timezone;
 
-        if (priv->sys_timezone) {
-                setenv ("TZ", priv->sys_timezone, 1);
-        } else {
-                unsetenv ("TZ");
-        }
-        tzset();
+        clock_location_unset_tz (loc);
 
         return offset;
 }
@@ -673,16 +428,8 @@
 make_current_cb (gpointer data, GError *error)
 {
 	MakeCurrentData *mcdata = data;
-        ClockLocationPrivate *priv = PRIVATE (mcdata->location);
 
 	if (error == NULL) {
-		/* FIXME this ugly shortcut is necessary until we move the
- 	  	 * current timezone tracking to clock.c and emit the
- 	 	 * signal from there
- 	 	 */
-		g_free (current_zone);
-		current_zone = g_strdup (priv->timezone);
-
 		if (current_location)
 			g_object_remove_weak_pointer (G_OBJECT (current_location), 
 						      (gpointer *)&current_location);

Modified: trunk/applets/clock/clock-marshallers.list
==============================================================================
--- trunk/applets/clock/clock-marshallers.list	(original)
+++ trunk/applets/clock/clock-marshallers.list	Tue Apr  8 22:40:11 2008
@@ -1,6 +1,3 @@
-VOID:VOID
-VOID:OBJECT
-VOID:POINTER
 POINTER:VOID
 VOID:OBJECT,STRING
 INT:VOID

Modified: trunk/applets/clock/clock-zoneinfo.h
==============================================================================
--- trunk/applets/clock/clock-zoneinfo.h	(original)
+++ trunk/applets/clock/clock-zoneinfo.h	Tue Apr  8 22:40:11 2008
@@ -6,12 +6,6 @@
 
 G_BEGIN_DECLS
 
-#ifdef HAVE_SOLARIS
-#define SYSTEM_ZONEINFODIR "/usr/share/lib/zoneinfo/tab"
-#else
-#define SYSTEM_ZONEINFODIR "/usr/share/zoneinfo"
-#endif
-
 #define CLOCK_ZONEINFO_TYPE         (clock_zoneinfo_get_type ())
 #define CLOCK_ZONEINFO(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), CLOCK_ZONEINFO_TYPE, ClockZoneInfo))
 #define CLOCK_ZONEINFO_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), CLOCK_ZONEINFO_TYPE, ClockZoneInfoClass))

Modified: trunk/applets/clock/clock-zonetable.c
==============================================================================
--- trunk/applets/clock/clock-zonetable.c	(original)
+++ trunk/applets/clock/clock-zonetable.c	Tue Apr  8 22:40:11 2008
@@ -11,6 +11,7 @@
 
 #include "clock-country.h"
 #include "clock-zoneinfo.h"
+#include "system-timezone.h"
 
 #ifdef HAVE_SOLARIS
 #define ZONETAB_FILE SYSTEM_ZONEINFODIR"/zone_sun.tab"
@@ -95,7 +96,6 @@
                                                 n_construct_properties,
                                                 construct_properties);
 
-
         clock_zonetable_load_zonetab (CLOCK_ZONETABLE (obj));
         clock_zonetable_load_iso3166 (CLOCK_ZONETABLE (obj));
         /* FIXME: add some file monitoring here to reload the files? */

Modified: trunk/applets/clock/clock.c
==============================================================================
--- trunk/applets/clock/clock.c	(original)
+++ trunk/applets/clock/clock.c	Tue Apr  8 22:40:11 2008
@@ -62,6 +62,7 @@
 #include "clock-zonetable.h"
 #include "obox.h"
 #include "set-timezone.h"
+#include "system-timezone.h"
 
 #define INTERNETSECOND (864)
 #define INTERNETBEAT   (86400)
@@ -191,6 +192,7 @@
 	int                size;
 	GtkAllocation      old_allocation;
 
+	SystemTimezone *systz;
         ClockZoneTable *zones;
 
 	int fixed_width;
@@ -758,6 +760,11 @@
         g_list_free (cd->location_tiles);
         cd->location_tiles = NULL;
 
+	if (cd->systz) {
+		g_object_unref (cd->systz);
+		cd->systz = NULL;
+	}
+
         if (cd->zones) {
                 g_object_unref (cd->zones);
                 cd->zones = NULL;
@@ -2331,6 +2338,17 @@
 }
 
 static void
+clock_timezone_changed (SystemTimezone *systz,
+			const char     *new_tz,
+			ClockData      *cd)
+{
+	/* This will refresh the current location */
+	save_cities_store (cd);
+
+	refresh_clock_timeout (cd);
+}
+
+static void
 parse_and_set_temperature_string (const char *str, ClockData *cd)
 {
         gint value = 0;
@@ -2747,6 +2765,10 @@
                         }
         }
 
+	cd->systz = system_timezone_new ();
+	g_signal_connect (cd->systz, "changed",
+			  G_CALLBACK (clock_timezone_changed), cd);
+
         cd->zones = clock_zonetable_new ();
 
         bonobo_ui_component_set_prop (popup_component,

Added: trunk/applets/clock/system-timezone.c
==============================================================================
--- (empty file)
+++ trunk/applets/clock/system-timezone.c	Tue Apr  8 22:40:11 2008
@@ -0,0 +1,623 @@
+/* To compile a test program, do:
+ * gcc -DSYSTZ_TEST -Wall -o system-timezone `pkg-config --cflags --libs glib-2.0` system-timezone.c
+ */
+
+/* System timezone handling
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * Authors: Vincent Untz <vuntz gnome org>
+ * 
+ * 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.
+ * 
+ * Some code is based on previous code in clock-location.c and on code from
+ * tz.c (shipped with version <= 2.22.0). Those files were under the same
+ * license, with those authors and copyrights:
+ * 
+ * clock-location.c:
+ * ================
+ * No header, but most of the work was done (AFAIK) by
+ * Federico Mena Quintero <federico novell com>
+ * Matthias Clasen <mclasen redhat com>
+ *
+ * tz.c:
+ * ====
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ * Copyright (C) 2004 Sun Microsystems, Inc.
+ *
+ * Authors: Hans Petter Jansson <hpj ximian com>
+ *	    additional functions by Erwann Chenede <erwann chenede sun com>
+ *	    reworked by Vincent Untz <vuntz gnome org>
+ * 
+ * Largely based on Michael Fulbright's work on Anaconda.
+ */
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+/* Files that we look at and that should be monitored */
+#define CHECK_NB 5
+#define ETC_TIMEZONE        "/etc/timezone"
+#define ETC_TIMEZONE_MAJ    "/etc/TIMEZONE"
+#define ETC_SYSCONFIG_CLOCK "/etc/sysconfig/clock"
+#define ETC_CONF_D_CLOCK    "/etc/conf.d/clock"
+#define ETC_LOCALTIME       "/etc/localtime"
+
+#ifndef SYSTZ_TEST
+
+#include <gio/gio.h>
+
+#include "system-timezone.h"
+
+static char *files_to_check[CHECK_NB] = {
+        ETC_TIMEZONE,
+        ETC_TIMEZONE_MAJ,
+        ETC_SYSCONFIG_CLOCK,
+        ETC_CONF_D_CLOCK,
+        ETC_LOCALTIME
+};
+
+G_DEFINE_TYPE (SystemTimezone, system_timezone, G_TYPE_OBJECT)
+
+typedef struct {
+        char *tz;
+        char *env_tz;
+        GFileMonitor *monitors[CHECK_NB];
+} SystemTimezonePrivate;
+
+enum {
+	CHANGED,
+	LAST_SIGNAL
+};
+
+static guint system_timezone_signals[LAST_SIGNAL] = { 0 };
+
+static GObject *system_timezone_constructor (GType                  type,
+                                             guint                  n_construct_properties,
+                                             GObjectConstructParam *construct_properties);
+static void system_timezone_finalize (GObject *obj);
+
+static void system_timezone_monitor_changed (GFileMonitor *handle,
+                                             GFile *file,
+                                             GFile *other_file,
+                                             GFileMonitorEvent event,
+                                             gpointer user_data);
+static char *system_timezone_find (void);
+
+#define PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SYSTEM_TIMEZONE_TYPE, SystemTimezonePrivate))
+
+SystemTimezone *
+system_timezone_new (void)
+{
+        SystemTimezone *systz;
+
+        systz = g_object_new (SYSTEM_TIMEZONE_TYPE, NULL);
+
+        return systz;
+}
+
+const char *
+system_timezone_get (SystemTimezone *systz)
+{
+        SystemTimezonePrivate *priv;
+
+        g_return_val_if_fail (IS_SYSTEM_TIMEZONE (systz), NULL);
+
+        priv = PRIVATE (systz);
+        return priv->tz;
+}
+
+const char *
+system_timezone_get_env (SystemTimezone *systz)
+{
+        SystemTimezonePrivate *priv;
+
+        g_return_val_if_fail (IS_SYSTEM_TIMEZONE (systz), NULL);
+
+        priv = PRIVATE (systz);
+        return priv->env_tz;
+}
+
+static void
+system_timezone_class_init (SystemTimezoneClass *class)
+{
+        GObjectClass *g_obj_class = G_OBJECT_CLASS (class);
+
+        g_obj_class->constructor = system_timezone_constructor;
+        g_obj_class->finalize = system_timezone_finalize;
+
+        system_timezone_signals[CHANGED] =
+		g_signal_new ("changed",
+			      G_OBJECT_CLASS_TYPE (g_obj_class),
+			      G_SIGNAL_RUN_FIRST,
+			      G_STRUCT_OFFSET (SystemTimezoneClass, changed),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE, 1, G_TYPE_STRING);
+
+        g_type_class_add_private (class, sizeof (SystemTimezonePrivate));
+}
+
+static void
+system_timezone_init (SystemTimezone *systz)
+{
+        int i;
+        SystemTimezonePrivate *priv = PRIVATE (systz);
+
+        priv->tz = NULL;
+        priv->env_tz = NULL;
+        for (i = 0; i < CHECK_NB; i++) 
+                priv->monitors[i] = NULL;
+}
+
+static GObject *
+system_timezone_constructor (GType                  type,
+                             guint                  n_construct_properties,
+                             GObjectConstructParam *construct_properties)
+{
+        static GObject *obj = NULL;
+        SystemTimezonePrivate *priv;
+        int i;
+
+        /* This is a singleton, we don't need to have it per-applet */
+        if (obj)
+                return g_object_ref (obj);
+
+        obj = G_OBJECT_CLASS (system_timezone_parent_class)->constructor (
+                                                type,
+                                                n_construct_properties,
+                                                construct_properties);
+
+        priv = PRIVATE (obj);
+
+        priv->tz = system_timezone_find ();
+
+        priv->env_tz = g_strdup (g_getenv ("TZ"));
+
+        for (i = 0; i < CHECK_NB; i++) {
+                GFile *file;
+
+                file = g_file_new_for_path (files_to_check[i]);
+                priv->monitors[i] = g_file_monitor_file (file,
+                                                         G_FILE_MONITOR_NONE,
+                                                         NULL, NULL);
+                g_object_unref (file);
+
+                if (priv->monitors[i])
+                        g_signal_connect (G_OBJECT (priv->monitors[i]),
+                                          "changed", 
+                                          G_CALLBACK (system_timezone_monitor_changed),
+                                          obj);
+        }
+
+        return obj;
+}
+
+static void
+system_timezone_finalize (GObject *obj)
+{
+        int i;
+        SystemTimezonePrivate *priv = PRIVATE (obj);
+
+        if (priv->tz) {
+                g_free (priv->tz);
+                priv->tz = NULL;
+        }
+
+        if (priv->env_tz) {
+                g_free (priv->env_tz);
+                priv->env_tz = NULL;
+        }
+
+        for (i = 0; i < CHECK_NB; i++) {
+                g_object_unref (priv->monitors[i]);
+                priv->monitors[i] = NULL;
+        }
+
+        G_OBJECT_CLASS (system_timezone_parent_class)->finalize (obj);
+}
+
+static void
+system_timezone_monitor_changed (GFileMonitor *handle,
+                                 GFile *file,
+                                 GFile *other_file,
+                                 GFileMonitorEvent event,
+                                 gpointer user_data)
+{
+        SystemTimezonePrivate *priv = PRIVATE (user_data);
+        char *new_tz;
+
+        if (event != G_FILE_MONITOR_EVENT_CHANGED &&
+            event != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT &&
+            event != G_FILE_MONITOR_EVENT_DELETED &&
+            event != G_FILE_MONITOR_EVENT_CREATED)
+                return;
+
+        new_tz = system_timezone_find ();
+
+        g_assert (priv->tz != NULL && new_tz != NULL);
+
+        if (strcmp (priv->tz, new_tz) != 0) {
+                g_free (priv->tz);
+                priv->tz = new_tz;
+
+                g_signal_emit (G_OBJECT (user_data),
+                               system_timezone_signals[CHANGED],
+                               0, priv->tz);
+        } else
+                g_free (new_tz);
+}
+
+#endif /* SYSTZ_TEST */
+
+/* This works for Debian and derivatives (including Ubuntu) */
+static char *
+system_timezone_read_etc_timezone (void)
+{
+        FILE    *etc_timezone;
+        GString *reading;
+        int      c;
+
+        etc_timezone = g_fopen (ETC_TIMEZONE, "r");
+        if (!etc_timezone)
+                return NULL;
+
+        reading = g_string_new ("");
+
+        c = fgetc (etc_timezone);
+        /* only get the first line, we'll validate the value later */
+        while (c != EOF && !g_ascii_isspace (c)) {
+                reading = g_string_append_c (reading, c);
+                c = fgetc (etc_timezone);
+        }
+
+        fclose (etc_timezone);
+
+        if (reading->str && reading->str[0] != '\0')
+                return g_string_free (reading, FALSE);
+        else
+                g_string_free (reading, TRUE);
+
+        return NULL;
+}
+
+/* Read a file that looks like a key-file (but there's no need for groups)
+ * and get the last value for a specific key */
+static char *
+system_timezone_read_key_file (const char *filename,
+                               const char *key)
+{
+        GIOChannel *channel;
+        char       *key_eq;
+        char       *line;
+        char       *retval;
+
+        if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+                return NULL;
+
+        channel = g_io_channel_new_file (filename, "r", NULL);
+        if (!channel)
+                return NULL;
+
+        key_eq = g_strdup_printf ("%s=", key);
+        retval = NULL;
+
+        while (g_io_channel_read_line (channel, &line, NULL,
+                                       NULL, NULL) == G_IO_STATUS_NORMAL) {
+                if (g_str_has_prefix (line, key_eq)) {
+                        char *value;
+                        int   len;
+
+                        value = line + strlen (key_eq);
+                        g_strstrip (value);
+
+                        len = strlen (value);
+
+                        if (value[0] == '\"') {
+                                if (value[len - 1] == '\"') {
+                                        if (retval)
+                                                g_free (retval);
+
+                                        retval = g_strndup (value + 1,
+                                                            len - 2);
+                                }
+                        } else {
+                                if (retval)
+                                        g_free (retval);
+
+                                retval = g_strdup (line + strlen (key_eq));
+                        }
+
+                        g_strstrip (retval);
+                }
+
+                g_free (line);
+        }
+
+        g_free (key_eq);
+        g_io_channel_unref (channel);
+
+        return retval;
+}
+
+/* This works for Solaris/OpenSolaris */
+static char *
+system_timezone_read_etc_TIMEZONE (void)
+{
+        return system_timezone_read_key_file (ETC_TIMEZONE_MAJ,
+                                              "TZ");
+}
+
+/* This works for Fedora and Mandriva */
+static char *
+system_timezone_read_etc_sysconfig_clock (void)
+{
+        return system_timezone_read_key_file (ETC_SYSCONFIG_CLOCK,
+                                              "ZONE");
+}
+
+/* This works for openSUSE */
+static char *
+system_timezone_read_etc_sysconfig_clock_alt (void)
+{
+        return system_timezone_read_key_file (ETC_SYSCONFIG_CLOCK,
+                                              "TIMEZONE");
+}
+
+/* This works for old Gentoo */
+static char *
+system_timezone_read_etc_conf_d_clock (void)
+{
+        return system_timezone_read_key_file (ETC_CONF_D_CLOCK,
+                                              "TIMEZONE");
+}
+
+/* Read the soft symlink from /etc/localtime */
+static char *
+system_timezone_read_etc_localtime_softlink (void)
+{
+        char *file;
+        char *tz;
+
+        if (!g_file_test (ETC_LOCALTIME, G_FILE_TEST_IS_SYMLINK))
+                return NULL;
+
+        tz = NULL;
+
+        file = g_file_read_link (ETC_LOCALTIME, NULL);
+        if (g_str_has_prefix (file, SYSTEM_ZONEINFODIR"/"))
+                tz = g_strdup (file + strlen (SYSTEM_ZONEINFODIR"/"));
+        g_free (file);
+
+        if (!tz || tz[0] == '\0') {
+                g_free (tz);
+                tz = NULL;
+        }
+
+        return tz;
+}
+
+typedef gboolean (*CompareFiles) (struct stat  *a_stat,
+                                  struct stat  *b_stat,
+                                  const char   *a_content,
+                                  unsigned int  a_content_len,
+                                  const char   *b_filename);
+
+static char *
+recursive_compare (struct stat  *localtime_stat,
+                   const char   *localtime_content,
+                   unsigned int  localtime_content_len,
+                   char         *file,
+                   CompareFiles  compare_func)
+{
+        struct stat file_stat;
+
+        if (g_stat (file, &file_stat) != 0)
+                return NULL;
+
+        if (S_ISREG (file_stat.st_mode)) {
+                if (compare_func (localtime_stat,
+                                  &file_stat,
+                                  localtime_content,
+                                  localtime_content_len,
+                                  file))
+                        return g_strdup (file + strlen (SYSTEM_ZONEINFODIR"/"));
+                else
+                        return NULL;
+        } else if (S_ISDIR (file_stat.st_mode)) {
+                GDir       *dir = NULL;
+                char       *ret = NULL;
+                const char *subfile = NULL;
+                char       *subpath = NULL;
+
+                dir = g_dir_open (file, 0, NULL);
+                if (dir == NULL)
+                        return NULL;
+
+                while ((subfile = g_dir_read_name (dir)) != NULL) {
+                        subpath = g_build_filename (file, subfile, NULL);
+
+                        ret = recursive_compare (localtime_stat,
+                                                 localtime_content,
+                                                 localtime_content_len,
+                                                 subpath,
+                                                 compare_func);
+
+                        g_free (subpath);
+
+                        if (ret != NULL)
+                                break;
+                }
+
+                g_dir_close (dir);
+
+                return ret;
+        }
+
+        return NULL;
+}
+
+
+static gboolean
+files_are_identical_inode (struct stat  *a_stat,
+                           struct stat  *b_stat,
+                           const char   *a_content,
+                           unsigned int  a_content_len,
+                           const char   *b_filename)
+{
+        return (a_stat->st_ino == b_stat->st_ino);
+}
+
+
+/* Determine if /etc/localtime is a hard link to some file, by looking at
+ * the inodes */
+static char *
+system_timezone_read_etc_localtime_hardlink (void)
+{
+        struct stat stat_localtime;
+
+        if (g_stat (ETC_LOCALTIME, &stat_localtime) != 0)
+                return NULL;
+
+        if (!S_ISREG (stat_localtime.st_mode))
+                return NULL;
+
+        return recursive_compare (&stat_localtime,
+                                  NULL,
+                                  0,
+                                  SYSTEM_ZONEINFODIR,
+                                  files_are_identical_inode);
+}
+
+static gboolean
+files_are_identical_content (struct stat  *a_stat,
+                             struct stat  *b_stat,
+                             const char   *a_content,
+                             unsigned int  a_content_len,
+                             const char   *b_filename)
+{
+        char         *b_content = NULL;
+        unsigned int  b_content_len = -1;
+        int           cmp;
+
+        if (a_stat->st_size != b_stat->st_size)
+                return FALSE;
+
+        if (!g_file_get_contents (b_filename,
+                                  &b_content, &b_content_len, NULL))
+                return FALSE;
+
+        if (a_content_len != b_content_len) {
+                g_free (b_content);
+                return FALSE;
+        }
+
+        cmp = memcmp (a_content, b_content, a_content_len);
+        g_free (b_content);
+
+        return (cmp == 0);
+}
+
+/* Determine if /etc/localtime is a copy of a timezone file */
+static char *
+system_timezone_read_etc_localtime_content (void)
+{
+        struct stat   stat_localtime;
+        char         *localtime_content = NULL;
+        unsigned int  localtime_content_len = -1;
+
+        if (g_stat (ETC_LOCALTIME, &stat_localtime) != 0)
+                return NULL;
+
+        if (!S_ISREG (stat_localtime.st_mode))
+                return NULL;
+
+        if (!g_file_get_contents (ETC_LOCALTIME,
+                                  &localtime_content,
+                                  &localtime_content_len,
+                                  NULL))
+                return NULL;
+
+        return recursive_compare (&stat_localtime,
+                                  localtime_content,
+                                  localtime_content_len,
+                                  SYSTEM_ZONEINFODIR,
+                                  files_are_identical_content);
+}
+
+typedef char * (*GetSystemTimezone) (void);
+static GetSystemTimezone get_system_timezone_methods[] = {
+        system_timezone_read_etc_timezone,
+        system_timezone_read_etc_TIMEZONE,
+        system_timezone_read_etc_sysconfig_clock,
+        system_timezone_read_etc_sysconfig_clock_alt,
+        system_timezone_read_etc_conf_d_clock,
+        system_timezone_read_etc_localtime_softlink,
+        system_timezone_read_etc_localtime_hardlink,
+        system_timezone_read_etc_localtime_content,
+        NULL
+};
+
+static gboolean
+system_timezone_is_valid (const char *tz)
+{
+        const char *c;
+
+        if (!tz)
+                return FALSE;
+
+        for (c = tz; *c != '\0'; c++) {
+                if (!(g_ascii_isalnum (*c) ||
+                      *c == '/' || *c == '-' || *c == '_'))
+                        return FALSE;
+        }
+
+        return TRUE;
+}
+
+static char *
+system_timezone_find (void)
+{
+        char *tz;
+        int   i;
+
+        for (i = 0; get_system_timezone_methods[i] != NULL; i++) {
+                tz = get_system_timezone_methods[i] ();
+
+                if (system_timezone_is_valid (tz))
+                        return tz;
+
+                g_free (tz);
+        }
+
+        return g_strdup ("UTC");
+}
+
+#ifdef SYSTZ_TEST
+int
+main (int    argc,
+      char **argv)
+{
+        char *tz;
+
+        tz = system_timezone_find ();
+        g_print ("%s\n", tz);
+        g_free (tz);
+
+        return 0;
+}
+#endif /* SYSTZ_TEST */

Added: trunk/applets/clock/system-timezone.h
==============================================================================
--- (empty file)
+++ trunk/applets/clock/system-timezone.h	Tue Apr  8 22:40:11 2008
@@ -0,0 +1,65 @@
+/* System timezone handling
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * Authors: Vincent Untz <vuntz gnome org>
+ * 
+ * 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.
+ */
+
+#ifndef __SYSTEM_TIMEZONE_H__
+#define __SYSTEM_TIMEZONE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef HAVE_SOLARIS
+#define SYSTEM_ZONEINFODIR "/usr/share/lib/zoneinfo/tab"
+#else
+#define SYSTEM_ZONEINFODIR "/usr/share/zoneinfo"
+#endif
+
+
+#define SYSTEM_TIMEZONE_TYPE         (system_timezone_get_type ())
+#define SYSTEM_TIMEZONE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), SYSTEM_TIMEZONE_TYPE, SystemTimezone))
+#define SYSTEM_TIMEZONE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), SYSTEM_TIMEZONE_TYPE, SystemTimezoneClass))
+#define IS_SYSTEM_TIMEZONE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), SYSTEM_TIMEZONE_TYPE))
+#define IS_SYSTEM_TIMEZONE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), SYSTEM_TIMEZONE_TYPE))
+#define SYSTEM_TIMEZONE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SYSTEM_TIMEZONE_TYPE, SystemTimezoneClass))
+
+typedef struct
+{
+        GObject g_object;
+} SystemTimezone;
+
+typedef struct
+{
+        GObjectClass g_object_class;
+
+	void (* changed) (SystemTimezone *systz,
+			  const char     *tz);
+} SystemTimezoneClass;
+
+GType system_timezone_get_type (void);
+
+SystemTimezone *system_timezone_new (void);
+
+const char *system_timezone_get (SystemTimezone *systz);
+const char *system_timezone_get_env (SystemTimezone *systz);
+
+G_END_DECLS
+#endif /* __SYSTEM_TIMEZONE_H__ */



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