diff --git a/Makefile.am b/Makefile.am
index e10be9d..96749ac 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,25 +18,15 @@ distuninstallcheck:
                                                                                                                                                              
 distcleancheck_listfiles = find . -type f -print | grep -v 'omf\.out' | grep -v 'legal.xml'
 
-if BUILD_GFLOPPY
-gfloppy = gfloppy
-endif
-
 always_built_SUBDIRS =   \
 	po 		 \
-	logview 	 \
-	gsearchtool	 \
-	gnome-dictionary \
-	gnome-screenshot \
-	baobab
+	logview
 
 SUBDIRS = \
-	$(always_built_SUBDIRS)	\
-	$(gfloppy)
+	$(always_built_SUBDIRS)
 
 DIST_SUBDIRS = \
-	$(always_built_SUBDIRS)	\
-	gfloppy
+	$(always_built_SUBDIRS)
 
 EXTRA_DIST = \
 	gnome-utils.spec.in \
diff --git a/configure.ac b/configure.ac
index 70547af..f9bc205 100644
--- a/configure.ac
+++ b/configure.ac
@@ -85,6 +85,7 @@ GNOMEVFS_REQUIRED=2.8.4
 LIBPANEL_APPLET_REQUIRED=2.13.4
 LIBGTOP_REQUIRED=2.12.0
 GNOMECANVAS_REQUIRED=2.10.2
+GMODULE_REQUIRED=2.10.2
 
 dnl *****************************************
 dnl libgnome, libgnomeui needed for all utils
@@ -149,29 +150,6 @@ PKG_CHECK_MODULES(SCREENSHOT, gio-2.0 >= $GLIB_REQUIRED dnl
 AC_SUBST(SCREENSHOT_CFLAGS)
 AC_SUBST(SCREENSHOT_LIBS)
 
-dnl ****************************************
-dnl HAL for floppy detection in gfloppy
-dnl ****************************************
-AC_ARG_ENABLE(hal,
-              AC_HELP_STRING([--enable-hal], [use HAL if available]), [
-              case "${enableval}" in
-                yes) ENABLE_HAL=yes ;;
-                no)  ENABLE_HAL=no ;;
-                *) AC_MSG_ERROR(bad value ${enableval} for --enable-hal) ;;
-              esac],
-              [ENABLE_HAL=yes]) dnl Default value
-
-if test "x$ENABLE_HAL" = "xyes"; then
-	PKG_CHECK_MODULES(HAL, hal >= 0.5.0,
-		AC_DEFINE(USE_HAL, 1, [defined if using libhal]),
-		[ USE_HAL=""])
-else
-	USE_HAL=""
-fi
-
-AC_SUBST(HAL_CFLAGS)
-AC_SUBST(HAL_LIBS)
-
 dnl *********************************************
 dnl gnome-vfs/mime-data is need for gsearchtool
 dnl *********************************************
@@ -424,6 +402,9 @@ AC_SUBST(GREP_COMMAND)
 dnl ********************
 dnl logview checks
 dnl ********************
+PKG_CHECK_MODULES(GMODULE, gmodule-2.0 >= $GMODULE_REQUIRED)
+AC_SUBST(GMODULE_CFLAGS)
+AC_SUBST(GMODULE_LIBS)
 
 dnl This is where the binary actually resides,
 dnl not the console helper link
@@ -434,7 +415,8 @@ else
 fi
 EXPANDED_LOGVIEWDIR=`eval echo $LOGVIEWDIR_TMP`
 AC_SUBST(EXPANDED_LOGVIEWDIR)
-
+PLUGIN_LIBTOOL_FLAGS="-module -avoid-version"
+AC_SUBST(PLUGIN_LIBTOOL_FLAGS)
 
 dnl ********************
 dnl scrollkeeper checks 
@@ -469,34 +451,13 @@ AC_OUTPUT([
 Makefile
 gnome-utils.spec
 po/Makefile.in
-baobab/Makefile
-baobab/data/Makefile
-baobab/pixmaps/Makefile
-baobab/pixmaps/24x24/Makefile
-baobab/pixmaps/scalable/Makefile
-baobab/src/Makefile
-baobab/help/Makefile
 logview/Makefile
 logview/gnome-system-log-security
 logview/help/Makefile
-gsearchtool/Makefile
-gsearchtool/data/Makefile
-gsearchtool/help/Makefile
-gnome-dictionary/Makefile
-gnome-dictionary/libgdict/Makefile
-gnome-dictionary/libgdict/gdict-version.h
-gnome-dictionary/libgdict/gdict-1.0.pc
-gnome-dictionary/data/Makefile
-gnome-dictionary/docs/Makefile
-gnome-dictionary/docs/reference/Makefile
-gnome-dictionary/docs/reference/gdict/Makefile
-gnome-dictionary/docs/reference/gdict/version.xml
-gnome-dictionary/help/Makefile
-gnome-dictionary/src/Makefile
-gfloppy/Makefile
-gfloppy/doc/Makefile
-gfloppy/src/Makefile
-gnome-screenshot/Makefile
+logview/plugins/Makefile
+logview/docs/Makefile
+logview/docs/reference/Makefile
+logview/docs/reference/logview/Makefile
 ])
 
 dnl <= Configuration summary =>
diff --git a/logview/Makefile.am b/logview/Makefile.am
index d693568..e4a6706 100644
--- a/logview/Makefile.am
+++ b/logview/Makefile.am
@@ -1,6 +1,17 @@
 # Process this file with automake to produce Makefile.in
 
-SUBDIRS = help
+SUBDIRS = . plugins help docs 
+
+gnome_system_log_includedir = $(includedir)/gnome-system-log
+gnome_system_log_include_HEADERS = \
+	logview-log.h		\
+	logview-plugin.h	\
+	logview-iface.h		\
+	logview-iface-io.h	\
+	logview-iface-collector.h	\
+	logview-iface-view.h	\
+	logview-debug.h		\
+	$(NULL)
 
 Utilitiesdir = $(datadir)/applications
 additionaldir  = $(datadir)/gnome-system-log
@@ -10,7 +21,8 @@ INCLUDES = \
 	-DG_LOG_DOMAIN=\"gnome-system-log\"			\
 	-DGNOMELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale"\"	\
 	-DDATADIR=\""$(datadir)"\"				\
-	-DLOGVIEWINSTALLPREFIX=\""$(prefix)/\""			\
+	-DLOGVIEWINSTALLPREFIX=\""$(prefix)/"\"			\
+	-DLOGVIEWPLUGINDIR=\""$(libdir)/gnome-system-log/plugins"\"	\
 	$(SUN_OS)						\
 	$(NULL)
 
@@ -24,12 +36,8 @@ gnome_system_log_SOURCES = 	\
 	calendar.h		\
 	log_repaint.c		\
 	log_repaint.h		\
-	logrtns.c		\
-	logrtns.h		\
 	logview.c		\
 	logview.h		\
-	misc.c			\
-	misc.h			\
 	monitor.c       	\
 	monitor.h		\
 	userprefs.c             \
@@ -37,12 +45,44 @@ gnome_system_log_SOURCES = 	\
 	logview-findbar.c	\
 	logview-findbar.h   \
 	loglist.c       \
-	loglist.h
+	loglist.h	\
+	logview-module.c	\
+	logview-module.h	\
+	logview-plugin-list.c	\
+	logview-plugin-list.h	\
+	logview-plugin.c		\
+	logview-plugin.h		\
+	logview-plugin-manager.c	\
+	logview-plugin-manager.h	\
+	$(NULL)
 
 gnome_system_log_LDADD = \
-	$(GNOME_UTILS_LIBS)
+	$(GNOME_UTILS_LIBS)	\
+	$(GMODULE_LIBS)		\
+	libgnome-system-log.la
 
-man_MANS = gnome-system-log.1
+noinst_LTLIBRARIES = libgnome-system-log.la
+libgnome_system_log_la_SOURCES = \
+	logrtns.c		\
+	logrtns.h		\
+	misc.c			\
+	misc.h			\
+	logview-iface.c		\
+	logview-iface.h		\
+	logview-iface-io.c		\
+	logview-iface-io.h		\
+	logview-iface-collector.c	\
+	logview-iface-collector.h	\
+	logview-iface-view.c	\
+	logview-iface-view.h	\
+	logview-debug.c			\
+	logview-debug.h			\
+	logview-log.h		\
+	$(NULL)
+
+libgnome_system_log_la_LIBADD = \
+	$(GNOME_UTILS_LIBS)	\
+	$(GMODULE_LIBS)
 
 Utilities_in_files = gnome-system-log.desktop.in
 Utilities_DATA = $(Utilities_in_files:.desktop.in=.desktop)
@@ -98,3 +138,4 @@ CLEANFILES = 				\
 dist-hook:
 	cd $(distdir) ; rm -f $(CLEANFILES)
 
+
diff --git a/logview/calendar.c b/logview/calendar.c
index b6ad451..b20cd5c 100644
--- a/logview/calendar.c
+++ b/logview/calendar.c
@@ -64,7 +64,7 @@ calendar_init_data (Calendar *calendar, LogviewWindow *logview)
     g_return_if_fail (IS_CALENDAR (calendar));
 	g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
 
-	g_object_get (G_OBJECT (logview), "days", &(calendar->priv->days), NULL);
+	g_object_get (G_OBJECT (logview->curlog), "days", &(calendar->priv->days), NULL);
     calendar->priv->first_pass = TRUE;
 
     calendar_mark_dates (calendar);
@@ -77,23 +77,6 @@ calendar_month_changed (GtkWidget *widget, gpointer data)
     calendar_mark_dates (CALENDAR (widget));
 }
 
-static Day *
-log_find_day (Log *log, int d, int m, int y)
-{
-    GDate *date;
-    GSList *days;
-    Day *day, *found_day = NULL;
-    
-    date = g_date_new_dmy (d, m+1, y);
-    for (days = log->days; days!=NULL; days=g_slist_next(days)) {
-        day = days->data;
-        if (g_date_compare (day->date, date) == 0) {
-            found_day = day;
-        }
-    }
-    return found_day;
-}
-
 static void
 calendar_day_selected (GtkWidget *widget, LogviewWindow *logview)
 {
@@ -101,6 +84,9 @@ calendar_day_selected (GtkWidget *widget, LogviewWindow *logview)
     guint day, month, year;
     Day *found_day;
     GtkTreePath *path;
+    GList *selected_paths;
+    GtkTreePath *select_first;
+    GDate *date;    
 
     calendar = CALENDAR (widget);
 
@@ -113,11 +99,20 @@ calendar_day_selected (GtkWidget *widget, LogviewWindow *logview)
     }
   
     gtk_calendar_get_date (GTK_CALENDAR (calendar), &year, &month, &day);    
-    found_day = log_find_day (logview->curlog, day, month, year);
+
+    date = g_date_new_dmy (day, month+1, year);
+    found_day = log_find_day (logview->curlog, date);
+    g_date_free (date);
+
+    g_object_get (G_OBJECT (logview->curlog),
+		  "selected-paths", &selected_paths,
+		  NULL);
+    g_assert (selected_paths);
+    select_first = g_list_first (selected_paths)->data;
     if (found_day != NULL) {
         path = found_day->path;
-        if ((gtk_tree_path_compare (path, logview->curlog->selected_range.first) != 0) &&
-            (!gtk_tree_path_is_descendant (logview->curlog->selected_range.first, path)))
+        if ((gtk_tree_path_compare (path, select_first) != 0) &&
+            (!gtk_tree_path_is_descendant (select_first, path)))
             gtk_tree_view_set_cursor (GTK_TREE_VIEW(logview->view), path, NULL, FALSE);
     }
 }
diff --git a/logview/docs/Makefile.am b/logview/docs/Makefile.am
new file mode 100644
index 0000000..86bb4ad
--- /dev/null
+++ b/logview/docs/Makefile.am
@@ -0,0 +1,3 @@
+# Process this file with automake to produce Makefile.in
+
+SUBDIRS = reference
diff --git a/logview/docs/reference/Makefile.am b/logview/docs/reference/Makefile.am
new file mode 100644
index 0000000..048bd8f
--- /dev/null
+++ b/logview/docs/reference/Makefile.am
@@ -0,0 +1,3 @@
+# Process this file with automake to produce Makefile.in
+
+SUBDIRS = logview
diff --git a/logview/docs/reference/logview/Makefile.am b/logview/docs/reference/logview/Makefile.am
new file mode 100644
index 0000000..ee2c82a
--- /dev/null
+++ b/logview/docs/reference/logview/Makefile.am
@@ -0,0 +1,84 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = 1.6
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=logview
+
+# The top-level SGML file. Change it if you want.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting functions and macros.
+DOC_SOURCE_DIR=$(top_srcdir)/logview
+
+# Extra options to supply to gtkdoc-scan.
+SCAN_OPTIONS=
+
+# Extra options to pass to gtkdoc-scangobj
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkdb.
+MKDB_OPTIONS=--sgml-mode --output-format=xml
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref.
+FIXXREF_OPTIONS=
+
+# Used for dependencies.
+HFILE_GLOB=$(top_srcdir)/logview/*.h
+CFILE_GLOB=$(top_srcdir)/logview/*.c
+
+# Header files to ignore when scanning.
+IGNORE_HFILES =			\
+	about.h			\
+	calendar.h		\
+	log_repaint.h		\
+	logview.h		\
+	monitor.h		\
+	userprefs.h		\
+	logview-findbar.h	\
+	loglist.h		\
+	logview-module.h	\
+	logview-plugin.h	\
+	logview-plugin-manager.h	\
+	logview-plugin-list.h
+
+# Images to copy into HTML directory.
+HTML_IMAGES =
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+content_files =
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+INCLUDES=				\
+	$(CFLAGS)			\
+	$(CPPFLAGS)			\
+	$(GNOME_UTILS_CFLAGS)		\
+	-I$(top_srcdir)/logview		\
+	-I$(top_builddir)/logview
+
+GTKDOC_LIBS=			\
+	$(LDFLAGS)		\
+	$(GNOME_UTILS_LIBS)	\
+	$(top_builddir)/logview/libgnome-system-log.la
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST +=
diff --git a/logview/docs/reference/logview/logview-docs.sgml b/logview/docs/reference/logview/logview-docs.sgml
new file mode 100644
index 0000000..e6dc9be
--- /dev/null
+++ b/logview/docs/reference/logview/logview-docs.sgml
@@ -0,0 +1,44 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+    "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!ENTITY index-Object-Tree SYSTEM "xml/tree_index.sgml">
+<!ENTITY LogviewLog SYSTEM "xml/logview-log.xml">
+<!ENTITY LogviewPlugin SYSTEM "xml/logview-plugin.xml">
+<!ENTITY LogviewIface SYSTEM "xml/logview-iface.xml">
+<!ENTITY LogviewIfaceIO SYSTEM "xml/logview-iface-io.xml">
+<!ENTITY LogviewIfaceView SYSTEM "xml/logview-iface-view.xml">
+<!ENTITY LogviewIfaceCollector SYSTEM "xml/logview-iface-collector.xml">
+<!ENTITY LogviewMisc SYSTEM "xml/misc.xml">
+]>
+
+<book id="logview">
+  <bookinfo>
+    <title>GNOME System Log Plugin Interfaces Reference Manual</title>
+    <abstract>
+      <para>This manual documents the Gnome System Log Plugin Interfaces.</para>
+    </abstract>
+  </bookinfo>
+  
+  <preface>
+    <title>Introduction</title>
+    <para>The Gnome System Log Plugin Interfaces enable endusers to implement
+    plugins to display non-ASCII formatted log files.</para>
+    <para>It was created for the GNOME System Log application, but it has been
+    made available for each developer whom wishes to implement plugins for
+    fun.</para>
+  </preface>
+  
+  <reference label="I">
+    <title>API reference</title>
+    &LogviewLog;
+    &LogviewIface;
+    &LogviewIfaceIO;
+    &LogviewIfaceView;
+    &LogviewIfaceCollector;
+  </reference>
+
+  <reference label="II">
+    <title>Miscellaneous Utility Functions</title>
+    &LogviewMisc;
+  </reference>
+</book>
diff --git a/logview/docs/reference/logview/logview.types b/logview/docs/reference/logview/logview.types
new file mode 100644
index 0000000..a303ce1
--- /dev/null
+++ b/logview/docs/reference/logview/logview.types
@@ -0,0 +1,5 @@
+logview_iface_collector_get_type
+logview_iface_view_get_type
+logview_iface_io_get_type
+logview_iface_get_type
+log_get_type
diff --git a/logview/gnome-system-log.desktop.in.in b/logview/gnome-system-log.desktop.in.in
index a06ba81..5a2be33 100644
--- a/logview/gnome-system-log.desktop.in.in
+++ b/logview/gnome-system-log.desktop.in.in
@@ -7,6 +7,7 @@ Terminal=false
 Type=Application
 StartupNotify=true
 Categories=GTK;GNOME;System;Monitor;
+OnlyShowIn=GNOME;
 NotShowIn=KDE;
 X-GNOME-DocPath=gnome-system-log/gnome-system-log.xml
 X-GNOME-Bugzilla-Bugzilla=GNOME
diff --git a/logview/log_repaint.c b/logview/log_repaint.c
index 24ceab0..2c777c2 100644
--- a/logview/log_repaint.c
+++ b/logview/log_repaint.c
@@ -31,38 +31,35 @@
 #include "log_repaint.h"
 #include "misc.h"
 #include "calendar.h"
-
-enum {
-   MESSAGE = 0,
-   DAY_POINTER,
-   LOG_LINE_WEIGHT,
-   LOG_LINE_WEIGHT_SET
-};
+#include "logview-debug.h"
 
 static gboolean busy_cursor = FALSE;
 
 static gboolean
 logview_show_busy_cursor (LogviewWindow *logview)
 {
-  GdkCursor *cursor;
-  if (GTK_WIDGET_VISIBLE (logview->view) && logview->curlog->model == NULL) {
-    cursor = gdk_cursor_new (GDK_WATCH);
-    gdk_window_set_cursor (GTK_WIDGET (logview)->window, cursor);
-    gdk_cursor_unref (cursor);
-    gdk_display_flush (gtk_widget_get_display (GTK_WIDGET (logview)));
-    busy_cursor = TRUE;
-  }
-  return (FALSE);
+	GdkCursor *cursor;
+	GObject *model;
+	g_assert (logview->curlog);
+	g_object_get (G_OBJECT (logview->curlog), "model", &model, NULL);
+	if (GTK_WIDGET_VISIBLE (logview->view) && model == NULL) {
+		cursor = gdk_cursor_new (GDK_WATCH);
+		gdk_window_set_cursor (GTK_WIDGET (logview)->window, cursor);
+		gdk_cursor_unref (cursor);
+		gdk_display_flush (gtk_widget_get_display (GTK_WIDGET (logview)));
+		busy_cursor = TRUE;
+	}
+	return (FALSE);
 }  
 
-static gboolean
+static void
 logview_show_normal_cursor (LogviewWindow *logview)
 {
-  if (busy_cursor) {
-    gdk_window_set_cursor (GTK_WIDGET (logview)->window, NULL);    
-    gdk_display_flush (gtk_widget_get_display (GTK_WIDGET (logview)));
-    busy_cursor = FALSE;
-  }
+	if (busy_cursor) {
+		gdk_window_set_cursor (GTK_WIDGET (logview)->window, NULL);    
+		gdk_display_flush (gtk_widget_get_display (GTK_WIDGET (logview)));
+		busy_cursor = FALSE;
+	}
 }
 
 void
@@ -80,443 +77,296 @@ row_toggled_cb (GtkTreeView *treeview, GtkTreeIter *iter,
 static int
 tree_path_find_row (GtkTreeModel *model, GtkTreePath *path, gboolean has_date)
 {
-    int row = 0;
-    int j;
-    int *indices;
-    GtkTreeIter iter;
-    GtkTreePath *date_path;
-
-    g_assert (GTK_IS_TREE_MODEL (model));
-    g_assert (path);
+	int row = 0;
+	int j;
+	int *indices;
+	GtkTreeIter iter;
+
+	g_assert (GTK_IS_TREE_MODEL (model));
+	g_assert (path);
     
-    indices = gtk_tree_path_get_indices (path);
+	indices = gtk_tree_path_get_indices (path);
     
-    if (has_date) {
-        for (j = 0; j < indices[0]; j++) {
-            date_path = gtk_tree_path_new_from_indices (j, -1);
-            gtk_tree_model_get_iter (model, &iter, date_path);
-            row += gtk_tree_model_iter_n_children (model, &iter);
-        }
-        if (gtk_tree_path_get_depth (path) > 1)
-            row += indices[1];
+	if (has_date) {
+		for (j = 0; j < indices[0]; j++) {
+			GtkTreePath *date_path;
+			date_path = gtk_tree_path_new_from_indices (j, -1);
+			gtk_tree_model_get_iter (model, &iter, date_path);
+			gtk_tree_path_free (date_path);
+			row += gtk_tree_model_iter_n_children (model, &iter);
+		}
+		if (gtk_tree_path_get_depth (path) > 1)
+			row += indices[1];
         
-    } else
-        row = indices[0];
+	} else
+		row = indices[0];
 
-    return (row);
+	return (row);
 }
 
 void
 selection_changed_cb (GtkTreeSelection *selection, gpointer data)
 {
-    int selected_first = -1, selected_last = -1;
-    LogviewWindow *logview = data;
-    GtkTreePath *selected_path;
-    GList *selected_paths, *path;
-    GtkTreeModel *model;
-    gint row;
-    Log *log;
+	LogviewWindow *logview = data;
+	GtkTreePath *selected_path;
+	GList *selected_paths;
+	GtkTreeModel *model;
+	gboolean groupable;
+	Log *log;
 	
-    g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
-    g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
-
-    log = logview->curlog;
-    if (log == NULL)
-      return;
-
-    selected_paths = gtk_tree_selection_get_selected_rows (selection, &model);
+	g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+	g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
 
-    if (selected_paths == NULL)
-        return;    
-
-    for (path = selected_paths; path != NULL; path = g_list_next (path)) {		            
-
-        selected_path = path->data;
-        
-        row = tree_path_find_row (model, selected_path, (log->days != NULL));
-        
-        if (selected_last == -1 || row > selected_last) {
-            selected_last = row;
-        }
-        if (selected_first == -1 || row < selected_first) {
-            selected_first = row;
-        }
-    }
-    
-    log->selected_range.first = gtk_tree_path_copy (g_list_first (selected_paths)->data);
-    log->selected_range.last = gtk_tree_path_copy (g_list_last (selected_paths)->data);
-    log->selected_line_first = selected_first;
-    log->selected_line_last = selected_last;
-
-    g_list_foreach (selected_paths, (GFunc) gtk_tree_path_free, NULL);
-    g_list_free (selected_paths);
-
-    if (log->days) {
-      GtkTreeIter iter;
-      Day *day;
+	log = logview->curlog;
+	if (log == NULL)
+		return;
+	g_object_get (G_OBJECT (log),
+		      "groupable", &groupable,
+		      NULL);
+	selected_paths = gtk_tree_selection_get_selected_rows (selection, &model);
+
+	if (selected_paths == NULL)
+		return;    
+	g_object_set (G_OBJECT (log), "selected-paths", selected_paths, NULL);
+	selected_path = gtk_tree_path_copy (g_list_first (selected_paths)->data);
+
+	if (groupable) {
+		GtkTreeIter iter;
+		Day *day;
       
-      selected_path = gtk_tree_path_copy (log->selected_range.first);
-      if (gtk_tree_path_get_depth (selected_path) > 1)
-        gtk_tree_path_up (selected_path);
-      gtk_tree_model_get_iter (model, &iter, selected_path);
-      gtk_tree_model_get (model, &iter, DAY_POINTER, &day, -1);
-      calendar_select_date (CALENDAR (logview->calendar), day->date);
-      gtk_tree_path_free (selected_path);
-    }
+		if (gtk_tree_path_get_depth (selected_path) > 1)
+			gtk_tree_path_up (selected_path);
+		gtk_tree_model_get_iter (model, &iter, selected_path);
+		gtk_tree_model_get (model, &iter, DAY_POINTER, &day, -1);
+		calendar_select_date (CALENDAR (logview->calendar), day->date);
+	}
+	gtk_tree_path_free (selected_path);
 }
 
 static void
 logview_update_statusbar (LogviewWindow *logview)
 {
-   char *statusbar_text;
-   char *size, *modified, *index;
-   Log *log;
+	char *statusbar_text;
+	char *size, *modified, *index;
+	time_t time;
+	guint64 log_size;
+	guint total_lines;
+	Log *log;
 
-   g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
-   log = logview->curlog;
+	log = logview->curlog;
 
-   if (log == NULL) {
-       gtk_statusbar_pop (GTK_STATUSBAR (logview->statusbar), 0);
-       return;
-   }
+	if (log == NULL) {
+		gtk_statusbar_pop (GTK_STATUSBAR (logview->statusbar), 0);
+		return;
+	}
    
-   /* ctime returned string has "\n\0" causes statusbar display a invalid char */
-   modified = ctime (&(log->stats->file_time));
-   index = strrchr (modified, '\n');
-   if (index && *index != '\0')
-     *index = '\0';
-
-   modified = g_strdup_printf (_("last update: %s"), modified);
-   size = gnome_vfs_format_file_size_for_display (log->stats->file_size);
-   statusbar_text = g_strdup_printf (_("%d lines (%s) - %s"), 
-                                     log->total_lines, size, modified);
-                                     
-   if (statusbar_text) {
-       gtk_statusbar_pop (GTK_STATUSBAR(logview->statusbar), 0);
-       gtk_statusbar_push (GTK_STATUSBAR(logview->statusbar), 0, statusbar_text);
-       g_free (size);
-       g_free (modified);
-       g_free (statusbar_text);
-   }
+	/* ctime returned string has "\n\0" causes statusbar display a invalid char */
+	g_object_get (G_OBJECT (log),
+		      "size", &log_size,
+		      "mtime", (glong *)&time,
+		      "total-lines", &total_lines,
+		      NULL);
+	modified = ctime (&time);
+	index = strrchr (modified, '\n');
+	if (index && *index != '\0')
+		*index = '\0';
+
+	modified = g_strdup_printf (_("last update: %s"), modified);
+	size = gnome_vfs_format_file_size_for_display (log_size);
+	statusbar_text = g_strdup_printf (_("%d lines (%s) - %s"),
+					  total_lines, size, modified);
+	if (statusbar_text) {
+		gtk_statusbar_pop (GTK_STATUSBAR(logview->statusbar), 0);
+		gtk_statusbar_push (GTK_STATUSBAR(logview->statusbar), 0, statusbar_text);
+		g_free (size);
+		g_free (modified);
+		g_free (statusbar_text);
+	}
 }
 
 void
 logview_update_version_bar (LogviewWindow *logview)
 {
-	Log *log;
-	int i;
+	gint i, selected;
 	gchar *label;
-
-	log = logview->curlog;
-    if (log == NULL) {
-        gtk_widget_hide (logview->version_bar);
-        return;
-    }
-
-	if (log->versions > 0 || log->parent_log != NULL) {
-		Log *recent;
-
-		gtk_widget_show_all (logview->version_bar);
-
-		if (log->current_version > 0)
-			recent = log->parent_log;
-		else
-			recent = log;
-			
-		for (i=5; i>-1; i--)
-			gtk_combo_box_remove_text (GTK_COMBO_BOX (logview->version_selector), i);
-
-		gtk_combo_box_append_text (GTK_COMBO_BOX (logview->version_selector), _("Current"));
-
-		for (i=0; i<(recent->versions); i++) {
-			label = g_strdup_printf ("Archive %d", i+1);
-			gtk_combo_box_append_text (GTK_COMBO_BOX (logview->version_selector),
-                                                   label);
-			g_free (label);
+	Log *log;
+	Log **older_logs;
+	gint current_version;
+	gint versions;
+
+	/* recreate the list, and emit the set active item signal */
+	if (logview->curlog == NULL) {
+		gtk_widget_hide_all (logview->version_bar);
+		return;
+	}
+	g_assert (LOGVIEW_IS_LOG (logview->curlog));
+	/* get parent log. if only it hasn't parent log, update achive list. */
+	g_object_get (G_OBJECT (logview->curlog),
+		      "parent", &log,
+		      NULL);
+	/* log is always the parent */
+	if (log == NULL) {
+		log = logview->curlog;
+		/* retrieve children list */
+		g_object_get (G_OBJECT (log),
+			      "archives", &older_logs,
+			      "versions", &versions,
+			      NULL);
+		gtk_widget_hide_all (logview->version_bar);
+		
+		/* the maximum text items of the combo box togather with "Current"
+		   is OLD_LOG_NUM */
+		for (i = OLD_LOG_NUM; i >= 0; i--)
+			gtk_combo_box_remove_text (
+				GTK_COMBO_BOX (logview->version_selector),
+				i);
+
+		gtk_combo_box_append_text (
+			GTK_COMBO_BOX (logview->version_selector),
+			_("Current"));
+
+		if (versions > 0) {
+			for (i=0; i<OLD_LOG_NUM; i++) {
+				gchar *n;
+				if (older_logs[i] == NULL)
+					continue;
+				g_object_get (G_OBJECT (older_logs[i]),
+					      "path", &n,
+					      NULL);
+				g_assert (n);
+				g_free (n);
+				label = g_strdup_printf ("Archive %d", i);
+				gtk_combo_box_append_text (
+					GTK_COMBO_BOX (logview->version_selector),
+					label);
+				g_free (label);
+			}
+			gtk_widget_show_all (logview->version_bar);
 		}
-                
-		gtk_combo_box_set_active (GTK_COMBO_BOX (logview->version_selector), 
-                                          log->current_version);
-
-	} else {
-		gtk_widget_hide (logview->version_bar);
 	}
+	g_object_get (G_OBJECT (log),
+		      "current-version", &current_version, NULL);
+	gtk_combo_box_set_active (GTK_COMBO_BOX (logview->version_selector),
+				  current_version);
 }
 
 static void
-model_fill_date_iter (GtkTreeStore *model, GtkTreeIter *iter, Day *day)
-{
-    gchar *utf8;
-
-    g_assert (GTK_IS_TREE_STORE (model));
-    
-    utf8 = date_get_string (day->date);
-    gtk_tree_store_set (model, iter, MESSAGE, utf8, DAY_POINTER, day, -1);
-    g_free (utf8);
-}
-
-static gboolean
-logview_unbold_rows (Log *log)
-{
-  LogviewWindow *logview;
-  TreePathRange *bold_rows;
-  GtkTreeIter iter;
-  GtkTreePath *path;
-
-  g_assert (log);
-  logview = log->window;
-
-  if (log->bold_rows_list == NULL)
-    return FALSE;
-
-  if (logview->curlog != log)
-    return TRUE;
-  
-  bold_rows = g_list_first (log->bold_rows_list)->data;
-  g_assert (bold_rows != NULL);
-
-  for (path = bold_rows->first; 
-       gtk_tree_path_compare (path, bold_rows->last)<=0;
-       gtk_tree_path_next (path)) {
-    gtk_tree_model_get_iter (log->model, &iter, path);
-    gtk_tree_store_set (GTK_TREE_STORE (log->model), &iter,
-                        LOG_LINE_WEIGHT, PANGO_WEIGHT_NORMAL,
-                        LOG_LINE_WEIGHT_SET, TRUE, -1);
-  }
-  
-  gtk_tree_path_free (bold_rows->first);
-  gtk_tree_path_free (bold_rows->last);
-  log->bold_rows_list = g_list_remove (log->bold_rows_list, bold_rows);
-  g_free (bold_rows);
-
-  return FALSE;
-}
-
-static void
-log_add_new_log_lines (Log *log)
-{
-    GtkTreeIter iter, child_iter, *iter_ptr;
-    GtkTreePath *path;
-    TreePathRange *bold_rows;
-    Day *day;
-    int i;
-    gchar *line;
-
-    g_assert (log);
-
-    if (log_read_new_lines (log) == FALSE)
-      return;
-
-    /* Find the last expandable row */
-    if (log->days) {
-      day = g_slist_last (log->days)->data;
-      gtk_tree_model_get_iter (log->model, &iter, day->path);
-      iter_ptr = &iter;
-    } else {
-      iter_ptr = NULL;
-    }  
- 
-    for (i=log->displayed_lines; i<log->total_lines; i++) {
-
-        line = log->lines[i];
-
-        if (line != NULL) {
-            gtk_tree_store_append (GTK_TREE_STORE (log->model), &child_iter, iter_ptr);
-            gtk_tree_store_set (GTK_TREE_STORE (log->model), &child_iter,
-                                MESSAGE, line, 
-                                DAY_POINTER, NULL,
-                                LOG_LINE_WEIGHT, PANGO_WEIGHT_BOLD,
-                                LOG_LINE_WEIGHT_SET, TRUE, -1);
-            /* Remember the first and last bold lines in the model to
-               unset them later */
-            if (i==log->displayed_lines) {
-              bold_rows = g_new0 (TreePathRange, 1);
-              bold_rows->first = gtk_tree_model_get_path (log->model, &child_iter);
-            }
-            if (i == log->total_lines-1) {
-              bold_rows->last = gtk_tree_model_get_path (log->model, &child_iter);
-            }
-        }
-    }
-    log->displayed_lines = log->total_lines;
-    log->bold_rows_list = g_list_append (log->bold_rows_list, bold_rows);
-
-    g_timeout_add (5000, (GSourceFunc) logview_unbold_rows, log);
-}
-    
-static void
 logview_scroll_and_focus_path (LogviewWindow *logview, Log *log)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
-    
-    if (log == NULL)
-      return;
-
-    if (log->selected_range.first != NULL) {
-      GtkTreeSelection *selection;
-      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (logview->view));
-      gtk_tree_selection_select_range (selection, log->selected_range.first,
-                                       log->selected_range.last);
-    }
-
-    if (log->bold_rows_list != NULL) {
-      TreePathRange *bold_rows;
-      bold_rows = g_list_last (log->bold_rows_list)->data;      
-      gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (logview->view),
-                                    bold_rows->last,
-                                    NULL, TRUE, 1.0, 1);   
-    } else {
-      if (log->visible_first != NULL) {
-      gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (logview->view),
-                                    log->visible_first,
-                                    NULL, TRUE, 0.0, 1);   
-      } 
-    }    
-}
+	LV_MARK;
+	GtkTreePath *last_bold_row;
+	GtkTreePath *visible_first;
+	GList *selected_paths, *idx;
+	GtkTreePath *selected_path;
 
-static void
-log_fill_model_no_date (Log *log, GtkTreeModel *model)
-{
-    int i;
-    gchar *line;
-    GtkTreeIter iter;
+	g_assert (LOGVIEW_IS_WINDOW (logview));
     
-    g_assert (log);
-
-    for (i=log->total_lines-1; i>=0; i--) {
-        line = log->lines[i];
-        gtk_tree_store_prepend (GTK_TREE_STORE (model), &iter, NULL);
-        gtk_tree_store_set (GTK_TREE_STORE (model), &iter,                            
-                            MESSAGE, line, DAY_POINTER, NULL, -1);
-        if (i == (log->total_lines-1)) {
-  	    GtkTreePath *path;
-	    path = gtk_tree_model_get_path (model, &iter);
-            log->selected_range.first = gtk_tree_path_copy (path);
-            log->selected_range.last = gtk_tree_path_copy (path);
-	    log->visible_first = gtk_tree_path_copy (path);
-	    gtk_tree_path_free (path);
-        }
-    }
-}
-
-static void
-log_fill_day_iter (Log *log, GtkTreeModel *model, Day *day, GtkTreeIter *iter)
-{
-  GtkTreeIter child_iter;
-  int i;
-
-  for (i = day->last_line; i >= day->first_line; i--) {
-    gtk_tree_store_prepend (GTK_TREE_STORE (model), &child_iter, iter);
-    gtk_tree_store_set (GTK_TREE_STORE (model), &child_iter,                            
-                        MESSAGE, log->lines[i], DAY_POINTER, NULL, -1);
-  }
-}
-
-static void
-log_fill_model_with_date (Log *log, GtkTreeModel *model)
-{
-    GtkTreeIter iter;
-    GSList *days;
-    Day *day;
-    int i;
-
-    g_assert (log->total_lines > 0);
-
-    /* Cycle on the days in the log */
-    /* It's not worth it to prepend instead of append */
-
-    for (days = log->days; days != NULL; days = g_slist_next (days)) {
-        day = days->data;
- 
-        gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
-        model_fill_date_iter (GTK_TREE_STORE (model), &iter, day);
-        day->path = gtk_tree_model_get_path (model, &iter);
-        
-        log_fill_day_iter (log, model, day, &iter);
-        day->expand = FALSE; 
-        
-        while (gtk_events_pending ())
-          gtk_main_iteration ();
-    }
-
-    day->expand = TRUE;
-    log->selected_range.first = gtk_tree_path_copy (day->path);
-    log->selected_range.last = gtk_tree_path_copy (day->path);
-    log->visible_first = gtk_tree_path_copy (day->path);
+	if (log == NULL)
+		return;
+
+	g_object_get (G_OBJECT (log),
+		      "last-bold-row", &last_bold_row,
+		      "visible-first", &visible_first,
+		      "selected-paths", &selected_paths,
+		      NULL);
+
+	if (selected_paths) {
+		GtkTreeSelection *selection;
+		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (logview->view));
+		for (idx = selected_paths; idx; idx = g_list_next (idx)) {
+			GtkTreePath *selected_path = idx->data;
+			gtk_tree_selection_select_path (selection, selected_path);
+		}
+	}
 
-    log->displayed_lines = log->total_lines;
+	if (last_bold_row != NULL) {
+		gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (logview->view),
+					      last_bold_row,
+					      NULL, TRUE, 1.0, 1);   
+	} else {
+		if (visible_first != NULL) {
+			gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (logview->view),
+						      visible_first,
+						      NULL, TRUE, 0.0, 1);   
+		} 
+	}    
 }
 
 static void
-log_create_model (Log *log)
-{
-  GtkTreeModel *model;
-
-  g_assert (log != NULL);
-  model = GTK_TREE_MODEL(gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER,
-                                                  G_TYPE_INT, G_TYPE_BOOLEAN));
-  if (log->days != NULL)
-    log_fill_model_with_date (log, model);
-  else
-    log_fill_model_no_date (log, model);
-  log->model = model;
-}  
-
-static void
 logview_set_log_model (LogviewWindow *window, Log *log)
 {    
-    GSList *days;
-    Day *day;
-
-    g_assert (LOGVIEW_IS_WINDOW (window));
-    g_assert (log);
-    g_assert (GTK_IS_TREE_MODEL (log->model));
-
-    if (log->filter != NULL)
-        gtk_tree_view_set_model (GTK_TREE_VIEW (window->view), GTK_TREE_MODEL (log->filter));
-    else
-        gtk_tree_view_set_model (GTK_TREE_VIEW (window->view), log->model);
-
-    if (log->days != NULL) {
-        for (days=log->days; days != NULL; days = g_slist_next(days)) {
-            day = days->data;
-            if (day->expand)
-                gtk_tree_view_expand_row (GTK_TREE_VIEW (window->view),
-                                          day->path, FALSE);
-        }
-    }
+	GSList *days, *idx;
+	Day *day;
+	GtkTreeModel *model;
+	GtkTreeModelFilter *filter;
+	gboolean groupable;
+
+	g_assert (LOGVIEW_IS_WINDOW (window));
+	g_assert (log);
+
+	logview_show_busy_cursor (window);
+	g_object_get (G_OBJECT (log),
+		      "filter", &filter,
+		      "model", &model,
+		      "groupable", &groupable,
+		      "days", &days,
+		      NULL);
+
+	g_assert (GTK_IS_TREE_MODEL (model));
+
+	if (filter != NULL)
+		gtk_tree_view_set_model (GTK_TREE_VIEW (window->view),
+					 GTK_TREE_MODEL (filter));
+	else
+		gtk_tree_view_set_model (GTK_TREE_VIEW (window->view),
+					 model);
+
+	if (groupable) {
+		for (idx = days; idx != NULL; idx = g_slist_next(idx)) {
+			day = idx->data;
+			if (day->expand)
+				gtk_tree_view_expand_row (GTK_TREE_VIEW (window->view),
+							  day->path, FALSE);
+		}
+	}
+	logview_show_normal_cursor (window);
 }
 
 void
 logview_repaint (LogviewWindow *logview)
 {
-    Log *log;
-    g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
+	/* if log has updated, get new lines and repaint the view */
+	Log *log;
+	GtkTreeModel *model;
+	gboolean monitoring;
+
+	g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
     
-    log = logview->curlog;
+	log = logview->curlog;
+	if (log == NULL) {
+		gtk_tree_view_set_model (GTK_TREE_VIEW (logview->view), NULL);
+		return;
+	}
 
-    logview_update_statusbar (logview);
-    logview_set_window_title (logview);
-      
-    if (log == NULL) {
-      gtk_tree_view_set_model (GTK_TREE_VIEW (logview->view), NULL);
-      return;
-    }
-
-    if (log->model == NULL) {
-      g_timeout_add (200, (GSourceFunc) logview_show_busy_cursor, logview);
-      log_create_model (log);
-      logview_show_normal_cursor (logview);
-    }      
-            
-    if (log->needs_refresh) {
-      log_add_new_log_lines (log);
-      log->needs_refresh = FALSE;
-    }
-
-    if (gtk_tree_view_get_model (GTK_TREE_VIEW (logview->view)) != log->model)
-      logview_set_log_model (logview, log);
-
-    logview_scroll_and_focus_path (logview, log);
+	g_assert (LOGVIEW_IS_LOG (log));
+	g_object_get (G_OBJECT (log),
+		      "model", &model,
+		      "monitoring", &monitoring,
+		      NULL);
+	g_assert (model);
+	
+	logview_set_window_title (logview);
+
+	if (monitoring && log_has_been_modified (log)) {
+		log_notify (log, TH_EVENT_UPDATE);
+	}
+
+	logview_update_statusbar (logview);
+
+	if (gtk_tree_view_get_model (GTK_TREE_VIEW (logview->view)) != model)
+		logview_set_log_model (logview, log);
+
+	logview_scroll_and_focus_path (logview, log);
 }
diff --git a/logview/log_repaint.h b/logview/log_repaint.h
index 99e3d40..0136bfa 100644
--- a/logview/log_repaint.h
+++ b/logview/log_repaint.h
@@ -20,6 +20,15 @@
 #ifndef __LOG_REPAINT_H__
 #define __LOG_REPAINT_H__
 
+#include "logview.h"
+
+enum {
+	MESSAGE = 0,
+	DAY_POINTER,
+	LOG_LINE_WEIGHT,
+	LOG_LINE_WEIGHT_SET
+};
+
 void logview_repaint (LogviewWindow *window);
 void selection_changed_cb (GtkTreeSelection *selection, gpointer data);
 void row_toggled_cb (GtkTreeView *treeview, GtkTreeIter *iter, 
diff --git a/logview/loglist.c b/logview/loglist.c
index 572df35..6e8049f 100644
--- a/logview/loglist.c
+++ b/logview/loglist.c
@@ -23,14 +23,16 @@
 #include "loglist.h"
 #include "logrtns.h"
 #include <libgnomevfs/gnome-vfs.h>
+#include <logview-debug.h>
 
-struct LogListPriv {
+#define BOLD_LOG_TIME	5000
+
+struct LogListPrv {
 	GtkTreeStore *model;
+	gpointer window;
+	guint unbold_event;
 };
 
-static GObjectClass *parent_class;
-GType loglist_get_type (void);
-
 enum {
 	LOG_NAME = 0,
 	LOG_POINTER,
@@ -38,6 +40,18 @@ enum {
 	LOG_WEIGHT_SET
 };
 
+enum {
+	PROP_0 = 0,
+	PROP_WINDOW
+};
+
+static void loglist_set_property (GObject      *object,
+				  guint         property_id,
+				  const GValue *value,
+				  GParamSpec   *pspec);
+
+G_DEFINE_TYPE (LogList, loglist, GTK_TYPE_TREE_VIEW)
+
 static GtkTreePath *
 loglist_find_directory (LogList *list, gchar *dir)
 {
@@ -48,14 +62,17 @@ loglist_find_directory (LogList *list, gchar *dir)
 	
 	g_assert (LOG_IS_LIST (list));
 
-	model = GTK_TREE_MODEL (list->priv->model);
+	model = GTK_TREE_MODEL (list->prv->model);
 	if (!gtk_tree_model_get_iter_first (model, &iter))
 		return NULL;
 	
 	do {
 		gtk_tree_model_get (model, &iter, LOG_NAME, &iterdir, -1);
 		if (iterdir) {
-			if (g_ascii_strncasecmp (iterdir, dir, -1) == 0) {
+			gint val;
+			val = g_ascii_strncasecmp (iterdir, dir, -1);
+			g_free (iterdir);
+			if (val == 0) {
 				path = gtk_tree_model_get_path (model, &iter);
 				break;
 			}
@@ -76,9 +93,10 @@ loglist_find_log (LogList *list, Log *log)
 	Log *iterlog;
 	
 	g_assert (LOG_IS_LIST (list));
-	g_assert (log != NULL);       	
+	g_assert (log != NULL);
+	LV_MARK;
 
-	model = GTK_TREE_MODEL (list->priv->model);
+	model = GTK_TREE_MODEL (list->prv->model);
 	if (!gtk_tree_model_get_iter_first (model, &iter))
 		return NULL;
 	
@@ -101,29 +119,34 @@ loglist_get_log_iter (LogList *list, Log *log, GtkTreeIter *logiter)
 {
     GtkTreeModel *model;
     GtkTreePath *path = NULL;
+    LV_MARK;
 
     path = loglist_find_log (list, log);
     if (path) {
-	    model = GTK_TREE_MODEL (list->priv->model);
+	    model = GTK_TREE_MODEL (list->prv->model);
 	    gtk_tree_model_get_iter (model, logiter, path);
 	    gtk_tree_path_free (path);
     }
 }
 
-void
-loglist_unbold_log (LogList *list, Log *log)
+static gboolean
+loglist_unbold (LogList *self)
 {
-    GtkTreeModel *model;
-    GtkTreeIter iter;
+	LogviewWindow *logview = LOGVIEW_WINDOW(self->prv->window);
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	LV_MARK;
 
-    g_return_if_fail (LOG_IS_LIST (list));
-    g_return_if_fail (log != NULL);
+	model = GTK_TREE_MODEL (self->prv->model);
 
-    model = GTK_TREE_MODEL (list->priv->model);
+	loglist_get_log_iter (self, logview_get_active_log (logview),
+			      &iter);
+	gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
+			    LOG_WEIGHT_SET, FALSE, -1);
 
-    loglist_get_log_iter (list, log, &iter);
-    gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
-			LOG_WEIGHT_SET, FALSE, -1);
+	self->prv->unbold_event = 0;
+	g_object_unref (self);
+	return FALSE;
 }
 
 void
@@ -131,11 +154,13 @@ loglist_bold_log (LogList *list, Log *log)
 {
     GtkTreeModel *model;
     GtkTreeIter iter;
+    LogviewWindow *logview = LOGVIEW_WINDOW(list->prv->window);
+    LV_MARK;
     
     g_return_if_fail (LOG_IS_LIST (list));
     g_return_if_fail (log != NULL);
 
-    model = GTK_TREE_MODEL (list->priv->model);
+    model = GTK_TREE_MODEL (list->prv->model);
 
     loglist_get_log_iter (list, log, &iter);
     gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 
@@ -143,8 +168,10 @@ loglist_bold_log (LogList *list, Log *log)
 			LOG_WEIGHT_SET, TRUE, -1);
 
     /* if the log is currently shown, put up a timer to unbold it */
-    if (logview_get_active_log (log->window) == log)
-        g_timeout_add (5000, log_unbold, log);
+    if (logview_get_active_log (logview) == log)
+	    g_timeout_add (BOLD_LOG_TIME,
+			   (GSourceFunc) loglist_unbold,
+			   g_object_ref (list));
 }
 
 void
@@ -169,33 +196,36 @@ void
 loglist_remove_log (LogList *list, Log *log)
 {
 	GtkTreeIter iter, parent;
+	gchar *name;
 
-    g_return_if_fail (LOG_IS_LIST (list));
-    g_return_if_fail (log != NULL);
+	g_return_if_fail (LOG_IS_LIST (list));
+	g_return_if_fail (log != NULL);
 
-    loglist_get_log_iter (list, log, &iter);
-    gtk_tree_model_iter_parent (GTK_TREE_MODEL (list->priv->model), &parent, &iter);
+	g_object_get (G_OBJECT (log), "path", &name, NULL);
+	loglist_get_log_iter (list, log, &iter);
+	gtk_tree_model_iter_parent (GTK_TREE_MODEL (list->prv->model), &parent, &iter);
 
-    if (gtk_tree_store_remove (list->priv->model, &iter)) {
-        GtkTreeSelection *selection;
-	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
-	gtk_tree_selection_select_iter (selection, &iter);
-    } else {
-	    if (!gtk_tree_model_iter_has_child (GTK_TREE_MODEL (list->priv->model), &parent)) {
-		    if (gtk_tree_store_remove (list->priv->model, &parent)) {
-			    GtkTreeSelection *selection;
-			    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
-			    gtk_tree_selection_select_iter (selection, &parent);
-		    }
-	    }
-    }
+	if (gtk_tree_store_remove (list->prv->model, &iter)) {
+		GtkTreeSelection *selection;
+		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
+		gtk_tree_selection_select_iter (selection, &iter);
+	} else {
+		if (!gtk_tree_model_iter_has_child (GTK_TREE_MODEL (list->prv->model), &parent)) {
+			if (gtk_tree_store_remove (list->prv->model, &parent)) {
+				GtkTreeSelection *selection;
+				selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
+				gtk_tree_selection_select_iter (selection, &parent);
+			}
+		}
+	}
+	g_free (name);
 }
 
 void
 loglist_add_directory (LogList *list, gchar *dirname, GtkTreeIter *iter)
 {
-	gtk_tree_store_append (list->priv->model, iter, NULL);
-	gtk_tree_store_set (list->priv->model, iter,
+	gtk_tree_store_append (list->prv->model, iter, NULL);
+	gtk_tree_store_set (list->prv->model, iter,
 			    LOG_NAME, dirname, LOG_POINTER, NULL, -1);
 }
 
@@ -209,17 +239,16 @@ loglist_add_log (LogList *list, Log *log)
 	g_return_if_fail (LOG_IS_LIST (list));
 	g_return_if_fail (log != NULL);
 
-	dirname = log_extract_dirname (log);
+	log_extract_filepath (log, &dirname, &filename);
 	path = loglist_find_directory (list, dirname);
 	if (path) {
-		gtk_tree_model_get_iter (GTK_TREE_MODEL (list->priv->model), &iter, path);
+		gtk_tree_model_get_iter (GTK_TREE_MODEL (list->prv->model), &iter, path);
 		gtk_tree_path_free (path);
 	} else
 		loglist_add_directory (list, dirname, &iter);
 
-	filename = log_extract_filename (log);
-	gtk_tree_store_append (list->priv->model, &child_iter, &iter);
-	gtk_tree_store_set (list->priv->model, &child_iter, 
+	gtk_tree_store_append (list->prv->model, &child_iter, &iter);
+	gtk_tree_store_set (list->prv->model, &child_iter, 
                         LOG_NAME, filename, LOG_POINTER, log, -1);
 
 	if (GTK_WIDGET_VISIBLE (GTK_WIDGET (list)))
@@ -230,121 +259,129 @@ loglist_add_log (LogList *list, Log *log)
 }
 
 static void
-loglist_selection_changed (GtkTreeSelection *selection, LogviewWindow *logview)
+loglist_selection_changed (GtkTreeSelection *selection, LogList *self)
 {
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  gboolean bold;
-  gchar *name;
-  Log *log;
-
-  g_assert (LOGVIEW_IS_WINDOW (logview));
-  g_assert (GTK_IS_TREE_SELECTION (selection));  
-
-  if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
-      logview_select_log (logview, NULL);
-      return;
-  }
-
-  gtk_tree_model_get (model, &iter, 
-		      LOG_NAME, &name, 
-		      LOG_POINTER, &log, 
-		      LOG_WEIGHT_SET, &bold, -1);
-  logview_select_log (logview, log);
-  if (bold)
-      g_timeout_add (5000, log_unbold, log);
-}
+	LogviewWindow *logview = LOGVIEW_WINDOW(self->prv->window);
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gboolean bold;
+	Log *log;
 
-void
-loglist_connect (LogList *list, LogviewWindow *logview)
-{
-    GtkTreeSelection *selection;
+	LV_MARK;
+	g_assert (GTK_IS_TREE_SELECTION (selection));
 
-    g_return_if_fail (LOG_IS_LIST (list));
-    g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
+	if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		logview_select_log (logview, NULL);
+		return;
+	}
 
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
-    g_signal_connect (G_OBJECT (selection), "changed",
-                      G_CALLBACK (loglist_selection_changed), logview);
+	gtk_tree_model_get (model, &iter, 
+			    LOG_POINTER, &log, 
+			    LOG_WEIGHT_SET, &bold, -1);
+	/* there is a differnt selection before,
+	   check if there is a pending loglist_unbold */
+	if (logview_get_active_log (logview) != log) {
+		if (self->prv->unbold_event > 0) {
+			gboolean ret = FALSE;
+			ret = g_source_remove (self->prv->unbold_event);
+			g_assert (ret);
+			self->prv->unbold_event = 0;
+		}
+	}
+	logview_select_log (logview, log);
+	if (bold) {
+		self->prv->unbold_event = 
+			g_timeout_add (BOLD_LOG_TIME,
+				       (GSourceFunc) loglist_unbold,
+				       g_object_ref (self));
+	}
 }
 
 static void 
 loglist_init (LogList *list)
 {
-    GtkTreeStore *model;
-    GtkTreeViewColumn *column;
-    GtkTreeSelection *selection;
-    GtkCellRenderer *cell;
+	GtkTreeStore *model;
+	GtkTreeViewColumn *column;
+	GtkTreeSelection *selection;
+	GtkCellRenderer *cell;
 
-    list->priv = g_new0 (LogListPriv, 1);
+	list->prv = g_new0 (LogListPrv, 1);
 
-    model = gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT, G_TYPE_BOOLEAN);
-    gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (model));
-    list->priv->model = model;
-    gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
-    g_object_unref (G_OBJECT (model));
+	model = gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT, G_TYPE_BOOLEAN);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (model));
+	list->prv->model = model;
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
+	g_object_unref (G_OBJECT (model));
 
-    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
-    gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+	g_signal_connect (G_OBJECT (selection), "changed",
+			  G_CALLBACK (loglist_selection_changed), list);
     
-    cell = gtk_cell_renderer_text_new ();
-    column = gtk_tree_view_column_new ();
-    gtk_tree_view_column_pack_start (column, cell, TRUE);
-    gtk_tree_view_column_set_attributes (column, cell,
-					 "text", LOG_NAME,
-					 "weight-set", LOG_WEIGHT_SET,
-					 "weight", LOG_WEIGHT,
-					 NULL);
-
-    gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(list->priv->model), 0, GTK_SORT_ASCENDING);
-    gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
-    gtk_tree_view_set_search_column (GTK_TREE_VIEW (list), -1);
+	cell = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_pack_start (column, cell, TRUE);
+	gtk_tree_view_column_set_attributes (column, cell,
+					     "text", LOG_NAME,
+					     "weight-set", LOG_WEIGHT_SET,
+					     "weight", LOG_WEIGHT,
+					     NULL);
+
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(list->prv->model), 0, GTK_SORT_ASCENDING);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
+	gtk_tree_view_set_search_column (GTK_TREE_VIEW (list), -1);
 }
 
 static void
 loglist_finalize (GObject *object)
 {
 	LogList *list = LOG_LIST (object);
-	g_free (list->priv);
-	parent_class->finalize (object);
+	g_free (list->prv);
+	(*G_OBJECT_CLASS(loglist_parent_class)->finalize) (object); 
 }
 
 static void
 loglist_class_init (LogListClass *klass)
 {
 	GObjectClass *object_class = (GObjectClass *) klass;
-	parent_class = g_type_class_peek_parent (klass);
+	GParamSpec *pspec;
+
 	object_class->finalize = loglist_finalize;
+        object_class->set_property = loglist_set_property;
+
+	pspec = g_param_spec_pointer ("window",
+				      "LogviewWindow pointer",
+				      "Set LogviewWindow pointer",
+				      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+	g_object_class_install_property (object_class,
+					 PROP_WINDOW,
+					 pspec);
 }
 
-GType
-loglist_get_type (void)
+static void
+loglist_set_property (GObject      *object,
+		      guint         property_id,
+		      const GValue *value,
+		      GParamSpec   *pspec)
 {
-	static GType object_type = 0;
-	
-	if (!object_type) {
-		static const GTypeInfo object_info = {
-			sizeof (LogListClass),
-			NULL,		/* base_init */
-			NULL,		/* base_finalize */
-			(GClassInitFunc) loglist_class_init,
-			NULL,/* class_finalize */
-			NULL,		/* class_data */
-			sizeof (LogList),
-			0,              /* n_preallocs */
-			(GInstanceInitFunc) loglist_init
-		};
-
-		object_type = g_type_register_static (GTK_TYPE_TREE_VIEW, "LogList", &object_info, 0);
+	LogList *self = LOG_LIST (object);
+
+	switch (property_id) {
+	case PROP_WINDOW:
+		self->prv->window = g_value_get_pointer (value);
+		g_assert (LOGVIEW_IS_WINDOW(self->prv->window));
+		break;
+	default:
+		/* We don't have any other property... */
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec);
+		break;
 	}
-
-	return object_type;
 }
 
 GtkWidget *
-loglist_new (void)
+loglist_new (gpointer window)
 {
     GtkWidget *widget;
-    widget = g_object_new (LOG_LIST_TYPE, NULL);
+    widget = g_object_new (LOG_LIST_TYPE, "window", window, NULL);
     return widget;
 }
diff --git a/logview/loglist.h b/logview/loglist.h
index c7f766a..b3da652 100644
--- a/logview/loglist.h
+++ b/logview/loglist.h
@@ -17,6 +17,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+
+/* loglist is the left choice panel */
+
 #ifndef __LOG_LIST_H__
 #define __LOG_LIST_H__
 
@@ -27,11 +30,11 @@
 #define LOG_IS_LIST_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((obj), LOG_LIST_TYPE))
 #define LOG_LIST_GET_CLASS(obj)   (GTK_CHECK_GET_CLASS ((obj), LOG_LIST_TYPE, LogListClass))
 
-typedef struct LogListPriv LogListPriv;
+typedef struct LogListPrv LogListPrv;
 typedef struct LogList
 {	
-    GtkTreeView parent_instance;
-    LogListPriv *priv;
+	GtkTreeView parent_instance;
+	LogListPrv *prv;
 }LogList;
 
 typedef struct LogListClass
@@ -40,12 +43,10 @@ typedef struct LogListClass
 }LogListClass;
 
 GType loglist_get_type (void);
-GtkWidget *loglist_new (void);
-void loglist_connect (LogList *list, LogviewWindow *window);
+GtkWidget *loglist_new (gpointer window);
 void loglist_add_log (LogList *list, Log *log);
 void loglist_remove_log (LogList *list, Log *log);
 void loglist_select_log (LogList *list, Log *log);
 void loglist_bold_log (LogList *list, Log *log);
-void loglist_unbold_log (LogList *list, Log *log);
 
 #endif /* __LOG_LIST_H__ */
diff --git a/logview/logrtns.c b/logview/logrtns.c
index 8300142..f61cf06 100644
--- a/logview/logrtns.c
+++ b/logview/logrtns.c
@@ -1,22 +1,22 @@
-/*  ----------------------------------------------------------------------
+/*	----------------------------------------------------------------------
 
-    Copyright (C) 1998  Cesar Miquel  (miquel@df.uba.ar)
+	Copyright (C) 1998	Cesar Miquel  (miquel@df.uba.ar)
 
-    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 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.
+	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., 675 Mass Ave, Cambridge, MA 02139, USA.
+	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., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-    ---------------------------------------------------------------------- */
+	---------------------------------------------------------------------- */
 
 #ifdef __CYGWIN__
 #define timezonevar
@@ -26,594 +26,1262 @@
 #endif
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
-#include <string.h>
+#include <strings.h>
 #include <stdlib.h>
-#include "logview.h"
-#include "logrtns.h"
 #include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include "logview-log.h"
+#include "logrtns.h"
+#include "log_repaint.h"
 #include "misc.h"
 #include "math.h"
+#include "logview-iface-io.h"
+#include "logview-iface-view.h"
+#include "logview-plugin-manager.h"
+#include "logview-debug.h"
+
+#define LINES_INIT_ALLOC	128
+#define LINES_STEP_ALLOC_MUTI_FACTOR	2
+#define BOLD_ROWS_TIME		5000
+#define WRITE_LINES		20
+#define DATA_LOCK(log)		g_mutex_lock(log->prv->data_mutex)
+#define DATA_UNLOCK(log)	g_mutex_unlock(log->prv->data_mutex)
+
+#define LOG_IS_VALID(log)	g_assert (log); \
+				g_assert (LOGVIEW_IS_LOG (log)); \
+				g_assert (log->prv->dispose_has_run == FALSE)
+
+static void log_dispose (Log *log);
+static void log_finalize (Log *log);
+static void log_thread_idle (Log *self);
+static guint log_thread_main_loop (Log *self);
+static ThreadEvent log_thread_pop_event (Log *self);
+static void log_create_model (Log *log);
+static void log_thread_fill_content (Log *log);
+static void log_fill_model_with_date (Log *log, GSList *days);
+static void log_fill_model_no_date (Log *log, guint start, guint end);
+static gboolean log_unbold_rows (Log *log);
+
+static GObjectClass *parent_class = NULL;
+
+enum {
+	PROP_0 = 0,
+	PROP_NAME,
+	PROP_PROTOCOL,
+	PROP_MODEL,
+	PROP_FILTER,
+	PROP_DISPLAY_NAME,
+	PROP_TOTAL_LINES,
+	PROP_IS_MONITORED,
+	PROP_PARENT,
+	PROP_VERSIONS,
+	PROP_CURRENT_VERSION,
+	PROP_ARCHIVES,
+	PROP_DAYS,
+	PROP_VIEW,
+	PROP_VISIBLE_FIRST,
+	PROP_BOLD_FIRST,
+	PROP_BOLD_LAST,
+        PROP_SELECTED_PATHS,
+	PROP_SIZE,
+	PROP_MTIME,
+	PROP_MONITORABLE,
+	PROP_MONITORING,
+	PROP_MONITOR_HANDLE,
+	PROP_GROUPABLE
+};
+
+struct _LogPrivate {
+	gboolean dispose_has_run;
+
+	/* event related */
+	volatile ThreadEvent th_event;
+	GMutex* thread_lock;
+	GMutex* data_mutex;
+	GMutex* event_mutex;
+	GCond* event_cond;
+
+	gchar *log_path;
+	LogProtocol protocol;
+
+	/* content information */
+	GSList *need_free;
+	gchar **lines;
+	guint total_lines; /* current recorded lines in the file */
+	guint alloc_lines; /* capability of max lines in the file */
+	
+	/* isolated calendar */
+	GList *selected_paths;
+
+	/* isolateed repaint */
+	GtkTreePath *first_bold_row;
+	GtkTreePath *last_bold_row;
+	GtkTreeModel *model;
+	GtkWidget *view; /* pointer to the only logview treeview */
+	gint current_version;
+	GtkTreeModelFilter *filter;
+	GSList *days;
+	/* isolated logview */
+	gchar *display_name;
+	gint versions;
+	Log *parent_log;	
+	GtkTreePath *visible_first;
+
+	Log **older_logs;
+	
+	/* Monitor info */
+	gboolean monitored;
+	GnomeVFSMonitorHandle *mon_handle;
+
+	/* plugin */
+	LogviewPlugin* ext_data[LOG_PF_NUM];
+
+	/* write pointer */
+	GtkTreeIter *date_iter;
+	GtkTreeIter *line_iter;
+
+	/* status */
+	gboolean update_status;
+};
 
-char *error_main = N_("One file or more could not be opened");
-
-static LogStats *log_stats_new (char *filename, gboolean show_error);
-
-/* File checking */
+static void
+log_instance_init (GTypeInstance *instance, gpointer g_class_data)
+{
+	Log* self = (Log*) instance;
+	self->prv = g_new0(LogPrivate, 1);
+	self->prv->dispose_has_run = FALSE;
+
+	/* thread related */
+	self->prv->thread_lock = g_mutex_new ();
+	g_assert (self->prv->thread_lock);
+	self->prv->data_mutex = g_mutex_new ();
+	g_assert (self->prv->data_mutex);
+	self->prv->event_mutex = g_mutex_new ();
+	g_assert (self->prv->event_mutex);
+	self->prv->event_cond = g_cond_new ();
+	g_assert (self->prv->event_cond);
+	self->prv->th_event = TH_EVENT_EMPTY;
+
+	self->prv->alloc_lines = LINES_INIT_ALLOC;
+	self->prv->lines = g_malloc0 (LINES_INIT_ALLOC * sizeof (gchar *));
+	g_assert (self->prv->lines);
+	self->prv->older_logs = g_malloc0 (OLD_LOG_NUM * sizeof (Log *));
+	g_assert (self->prv->older_logs);
+	self->prv->lines[0] = NULL;
+	self->prv->line_iter = g_new0 (GtkTreeIter,1);
+}
 
-static gboolean
-file_exist (char *filename, gboolean show_error)
-{
-   GnomeVFSHandle *handle;
-   GnomeVFSResult result;
-   char *secondary = NULL;
-
-   if (filename == NULL)
-       return FALSE;
-
-   result = gnome_vfs_open (&handle, filename, GNOME_VFS_OPEN_READ);
-   if (result != GNOME_VFS_OK) {
-	   if (show_error) {
-		   switch (result) {
-		   case GNOME_VFS_ERROR_ACCESS_DENIED:
-		   case GNOME_VFS_ERROR_NOT_PERMITTED:
-			   secondary = g_strdup_printf (_("%s is not user readable. "
-					 "Either run the program as root or ask the sysadmin to "
-					 "change the permissions on the file.\n"), filename);
-			   break;
-		   case GNOME_VFS_ERROR_TOO_BIG:
-			   secondary = g_strdup_printf (_("%s is too big."), filename);
-			   break;
-		   default:
-			   secondary = g_strdup_printf (_("%s could not be opened."), filename);
-               break;
-		   }
-		   error_dialog_show (NULL, error_main, secondary);
-           g_free (secondary);
-	   }
-	   return FALSE;
-   }
-
-   gnome_vfs_close (handle);
-   return TRUE;
+static void
+log_set_property (GObject      *object,
+		  guint         property_id,
+		  const GValue *value,
+		  GParamSpec   *pspec)
+{
+	Log *self = LOGVIEW_LOG (object);
+
+	LOG_IS_VALID(self);
+
+	DATA_LOCK (self);
+	switch (property_id) {
+	case PROP_NAME:
+		g_free (self->prv->log_path);
+		self->prv->log_path = g_value_dup_string (value);
+		g_assert (self->prv->log_path);
+		g_assert (self->prv->log_path[0] != '\0');
+		LV_INFO ("log.path: %s",self->prv->log_path);
+		break;
+	case PROP_PROTOCOL:
+		self->prv->protocol = g_value_get_ulong (value);
+		LV_INFO ("log.protocol: %d",self->prv->protocol);
+		break;
+	case PROP_CURRENT_VERSION:
+		self->prv->current_version = g_value_get_int (value);
+		break;
+	case PROP_VERSIONS:
+		self->prv->versions = g_value_get_int (value);
+		LV_INFO ("log.versions: %d",self->prv->versions);
+		break;
+	case PROP_VISIBLE_FIRST:
+		if (self->prv->visible_first)
+			gtk_tree_path_free (self->prv->visible_first);
+		self->prv->visible_first = g_value_get_pointer (value);
+		break;
+	case PROP_SELECTED_PATHS: {
+		GList *paths;
+		paths = g_value_get_pointer (value);
+		g_assert (paths);
+		if (self->prv->selected_paths != paths) {
+			g_list_foreach (self->prv->selected_paths, (GFunc) gtk_tree_path_free, NULL);
+			g_list_free (self->prv->selected_paths);
+			self->prv->selected_paths = paths;
+		}
+	}
+		break;
+	case PROP_VIEW:
+		/* can be set only once */
+		g_assert (self->prv->view == NULL);
+		self->prv->view = g_value_get_pointer (value);
+		if (self->prv->view)
+			g_assert (GTK_IS_TREE_VIEW(self->prv->view));
+		break;
+	case PROP_MONITOR_HANDLE:
+		self->prv->mon_handle = g_value_get_pointer (value);
+		break;
+	case PROP_MONITORING:
+		self->prv->monitored = g_value_get_boolean (value);
+		break;
+	case PROP_FILTER:
+		self->prv->filter = g_value_get_pointer (value);
+		break;
+	default:
+		/* We don't have any other property... */
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec);
+		break;
+	}
+	DATA_UNLOCK (self);
 }
 
-static gboolean
-file_is_zipped (char *filename)
+static void
+log_get_property (GObject      *object,
+		  guint         property_id,
+		  GValue       *value,
+		  GParamSpec   *pspec)
 {
-	char *mime_type;
+	Log *self = LOGVIEW_LOG (object);
+
+	LOG_IS_VALID(self);
+
+	DATA_LOCK (self);
+	switch (property_id) {
+	case PROP_NAME:
+		g_value_set_static_string (value, self->prv->log_path);
+		break;
+	case PROP_PROTOCOL:
+		g_value_set_ulong (value, self->prv->protocol);
+		break;
+	case PROP_MODEL:
+		g_value_set_pointer (value, self->prv->model);
+		break;
+	case PROP_PARENT:
+		g_value_set_object (value, self->prv->parent_log);
+		break;
+	case PROP_FILTER:
+		g_value_set_pointer (value, self->prv->filter);
+		break;
+	case PROP_DISPLAY_NAME:
+		g_value_set_string (value, self->prv->display_name);
+		break;
+	case PROP_TOTAL_LINES:
+		g_value_set_uint (value, self->prv->total_lines);
+		break;
+	case PROP_MONITORABLE:
+		g_value_set_boolean (value, logi_can_monitor(LOGVIEW_IFACE_IO(log_io(self))));
+		break;
+	case PROP_MONITORING:
+		g_value_set_boolean (value, self->prv->monitored);
+		break;
+	case PROP_GROUPABLE: {
+		gboolean ret;
+		ret = self->prv->days != NULL;
+		g_value_set_boolean (value, ret);
+	}
+		break;
+	case PROP_VISIBLE_FIRST:
+		g_value_set_pointer (value, self->prv->visible_first);
+		break;
+	case PROP_BOLD_FIRST:
+		g_value_set_pointer (value, self->prv->first_bold_row);
+		break;
+	case PROP_BOLD_LAST:
+		g_value_set_pointer (value, self->prv->last_bold_row);
+		break;
+	case PROP_VERSIONS:
+		g_value_set_int (value, self->prv->versions);
+		break;
+	case PROP_ARCHIVES:
+		g_value_set_pointer (value, self->prv->older_logs);
+		break;
+	case PROP_CURRENT_VERSION:
+		g_value_set_int (value, self->prv->current_version);
+		break;
+	case PROP_DAYS:
+		g_value_set_pointer (value, self->prv->days);
+		break;
+	case PROP_VIEW:
+		g_value_set_pointer (value, self->prv->view);
+		break;
+	case PROP_SELECTED_PATHS:
+		g_value_set_pointer (value, self->prv->selected_paths);
+		break;
+	case PROP_SIZE: {
+		guint64 size = (guint64) logi_get_size (LOGVIEW_IFACE_IO(log_io(self)));
+		g_value_set_uint64 (value, size);
+		break;
+	}
+	case PROP_MTIME: {
+		time_t time = logi_get_modified_time(LOGVIEW_IFACE_IO(log_io(self)));
+		g_value_set_long (value, time);
+		break;
+	}
+	case PROP_MONITOR_HANDLE:
+		g_value_set_pointer (value, self->prv->mon_handle);
+		break;
+	default:
+		/* We don't have any other property... */
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec);
+		break;
+	}
+	DATA_UNLOCK (self);
+}
 
-    if (filename == NULL)
-        return FALSE;
+static void
+log_class_init (gpointer g_class, gpointer g_class_data)
+{
+	GObjectClass *object_class;
+	LogClass *self;
+	GParamSpec *pspec;
+	
+	LV_MARK;
+
+	object_class = G_OBJECT_CLASS (g_class);
+	self = LOGVIEW_LOG_CLASS (g_class);
+	parent_class = g_type_class_peek_parent (g_class);
+
+        object_class->set_property = log_set_property;
+	object_class->get_property = log_get_property;
+	object_class->dispose = (void (*) (GObject*)) log_dispose;
+	object_class->finalize = (void (*) (GObject*)) log_finalize;
+
+	pspec = g_param_spec_string ("path",
+				     "Log arbitrary path",
+				     "Set or get log path",
+				     "" /* default value */,
+				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+	g_object_class_install_property (object_class,
+					 PROP_NAME,
+					 pspec);
+
+	pspec = g_param_spec_string ("display-name",
+				     "Log display name",
+				     "Set or get log display name",
+				     NULL /* default value */,
+				     G_PARAM_READWRITE);
+	g_object_class_install_property (object_class,
+					 PROP_DISPLAY_NAME,
+					 pspec);
+
+	pspec = g_param_spec_ulong ("protocol",
+				    "Log protocol",
+				    "Set or get log protocol",
+				    0,
+				    G_TYPE_ULONG,
+				    UNKNOWN_LOG /* default value */,
+				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property (object_class,
+					 PROP_PROTOCOL,
+					 pspec);
+
+	pspec = g_param_spec_int ("current-version",
+				  "Currently displaying log",
+				  "Set or get current version",
+				  0,
+				  OLD_LOG_NUM,
+				  0 /* default value */,
+				  G_PARAM_READWRITE);
+	g_object_class_install_property (object_class,
+					 PROP_CURRENT_VERSION,
+					 pspec);
+
+	pspec = g_param_spec_int ("versions",
+				  "Log archive numbers",
+				  "Get log archive numbers",
+				  0,
+				  OLD_LOG_NUM,
+				  0 /* default value */,
+				  G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_VERSIONS,
+					 pspec);
+
+        pspec = g_param_spec_pointer ("archives",
+				      "elder logs pointer",
+				      "Get elder logs pointer",
+				      G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_ARCHIVES,
+					 pspec);
+
+	pspec = g_param_spec_uint64 ("size",
+				   "Log file size",
+				   "Get log size",
+				   0,
+				   G_MAXUINT64,
+				   0 /* default value */,
+				   G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_SIZE,
+					 pspec);
+
+	pspec = g_param_spec_uint ("total-lines",
+				   "Log current read lines",
+				   "Get current read lines",
+				   0,
+				   G_MAXUINT,
+				   0 /* default value */,
+				   G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_TOTAL_LINES,
+					 pspec);
+
+	pspec = g_param_spec_long ("mtime",
+				   "Log modified time",
+				   "Get modified time",
+				   0,
+				   G_MAXLONG,
+				   0 /* default value */,
+				   G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_MTIME,
+					 pspec);
+
+	pspec = g_param_spec_boolean ("monitorable",
+				      "Log monitor capable",
+				      "Get or set monitor capable",
+				      FALSE,
+				      G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_MONITORABLE,
+					 pspec);
+
+	pspec = g_param_spec_boolean ("monitoring",
+				      "Is monitor the log",
+				      "Get monitor status",
+				      FALSE,
+				      G_PARAM_READWRITE);
+	g_object_class_install_property (object_class,
+					 PROP_MONITORING,
+					 pspec);
+
+	pspec = g_param_spec_boolean ("groupable",
+				      "Log group capable",
+				      "Get group capable",
+				      FALSE,
+				      G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_GROUPABLE,
+					 pspec);
+
+	pspec = g_param_spec_pointer ("model",
+				      "Log tree model",
+				      "Get log model",
+				      G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_MODEL,
+					 pspec);
+
+	pspec = g_param_spec_pointer ("filter",
+				      "Log tree filter",
+				      "Get log filter",
+				      G_PARAM_READWRITE);
+	g_object_class_install_property (object_class,
+					 PROP_FILTER,
+					 pspec);
+
+	pspec = g_param_spec_object ("parent",
+				     "The parent log of the current log",
+				     "Get parent log",
+				     LOGVIEW_TYPE_LOG,
+				     G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_PARENT,
+					 pspec);
+
+	pspec = g_param_spec_pointer ("days",
+				      "The day list of a log",
+				      "Get log days",
+				      G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_DAYS,
+					 pspec);
+
+	pspec = g_param_spec_pointer ("view",
+				      "Point to the only treeview of the main window",
+				      "Get and set window",
+				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property (object_class,
+					 PROP_VIEW,
+					 pspec);
+
+	pspec = g_param_spec_pointer ("visible-first",
+				      "Log visible first",
+				      "Set or get first visible line",
+				      G_PARAM_READWRITE);
+	g_object_class_install_property (object_class,
+					 PROP_VISIBLE_FIRST,
+					 pspec);
+
+	pspec = g_param_spec_pointer ("first-bold-row",
+				      "first bold row",
+				      "Get first bold row",
+				      G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_BOLD_FIRST,
+					 pspec);
+
+	pspec = g_param_spec_pointer ("last-bold-row",
+				      "last bold row",
+				      "Get last bold row",
+				      G_PARAM_READABLE);
+	g_object_class_install_property (object_class,
+					 PROP_BOLD_LAST,
+					 pspec);
+
+	pspec = g_param_spec_pointer ("selected-paths",
+				      "Log last selected lines",
+				      "Set or get last selected lines",
+				      G_PARAM_READWRITE);
+	g_object_class_install_property (object_class,
+					 PROP_SELECTED_PATHS,
+					 pspec);
+
+
+	pspec = g_param_spec_pointer ("monitor-handle",
+				      "GnomeVFSMonitorHandle used for monitoring this log",
+				      "Set or get last selected line",
+				      G_PARAM_READWRITE);
+	g_object_class_install_property (object_class,
+					 PROP_MONITOR_HANDLE,
+					 pspec);
 
-	mime_type = gnome_vfs_get_mime_type (filename);
-    if (mime_type == NULL)
-        return FALSE;
+}
 
-	if (strcmp (mime_type, "application/x-gzip")==0 ||
-	    strcmp (mime_type, "application/x-zip")==0 ||
-	    strcmp (mime_type, "application/zip")==0) {
-        g_free (mime_type);
-		return TRUE;
-    } else {
-        g_free (mime_type);
-		return FALSE;
-    }
+GType
+log_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (LogClass),
+			NULL,   /* base_init */
+			NULL,   /* base_finalize */
+			log_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			sizeof (Log),
+			0,      /* n_preallocs */
+			log_instance_init    /* instance_init */
+		};		
+		type = g_type_register_static (G_TYPE_OBJECT,
+					       "LogviewLogType",
+					       &info, 0);
+	}
+	return type;
 }
 
-gboolean
-file_is_log (char *filename, gboolean show_error)
+static void
+log_append_garbage (Log *self, gchar *mass)
 {
-    LogStats *stats;
+	LOG_IS_VALID(self);
 
-    if (filename == NULL)
-        return FALSE;
+	self->prv->need_free = g_slist_append (self->prv->need_free, mass);
+}
 
-    stats = log_stats_new (filename, show_error);
-    if (stats==NULL)
-        return FALSE;
-    else {
-        g_free (stats);
-        return TRUE;
-    }
+static void
+log_append_lines (Log *self, gchar **lines, gint len)
+{
+	guint i;
+
+	LOG_IS_VALID(self);
+
+	if (self->prv->total_lines + len >= (self->prv->alloc_lines -1)) {
+		self->prv->alloc_lines *= LINES_STEP_ALLOC_MUTI_FACTOR;
+		self->prv->lines = g_realloc (self->prv->lines,
+					 self->prv->alloc_lines * sizeof (gchar *));
+		g_assert (self->prv->lines);
+	}
+	for (i = 0; i < len; i++) {
+		g_assert (lines[i]);
+		self->prv->lines[self->prv->total_lines++] = lines[i];
+	}
+	self->prv->lines[self->prv->total_lines] = NULL;
+}
+
+static void
+cleanup_binary_chars_in_log (gchar* buffer, gssize read_size)
+{
+	int i;
+	for ( i = 0; i < read_size; i++ ) {
+		if ( buffer[i] == '\0' ) {
+			buffer[i] = '?';
+		}
+	}
 }
 
 /* log functions */
+Day *
+log_find_day (Log *self, const GDate* date)
+{
+	GSList *days;
+	Day *day, *found_day = NULL;
+
+	LOG_IS_VALID(self);
+
+	DATA_LOCK (self);
+	for (days = self->prv->days; days!=NULL; days=g_slist_next(days)) {
+		day = days->data;
+		if (g_date_compare (day->date, date) == 0) {
+			found_day = day;
+		}
+	}
+	DATA_UNLOCK (self);
+	return found_day;
+}
 
-gint 
-days_compare (gconstpointer a, gconstpointer b)
+/* return start index of lines */
+static guint
+log_build_list_index_from_mass (Log *self, const gchar* buffer)
 {
-    const Day *day1 = a, *day2 = b;
-    return (g_date_compare (day1->date, day2->date));
+	gchar *fore_idx, *rear_idx;
+	guint ret = self->prv->total_lines;
+	gchar *cache[LINES_INIT_ALLOC];
+	gint i = 0;
+
+	LV_MARK;
+	LOG_IS_VALID(self);
+        g_assert (buffer);
+
+	DATA_LOCK (self);
+	log_append_garbage (self, (gchar *) buffer);
+	for (rear_idx = fore_idx = (gchar *) buffer; *fore_idx != '\0'; fore_idx++) {
+		/* do not skip empty lines */
+		while (*fore_idx == '\n') {
+			*fore_idx = '\0';
+			if (rear_idx != fore_idx) {
+				cache[i++] = rear_idx;
+				if (i == LINES_INIT_ALLOC) {
+					log_append_lines (self, cache, i);
+					i = 0;
+					DATA_UNLOCK (self);
+					log_thread_idle (self);
+					DATA_LOCK (self);
+				}
+			}
+			rear_idx =  fore_idx + 1;
+		}
+	}
+	if (i > 0)
+		log_append_lines (self, cache, i);
+	DATA_UNLOCK (self);
+	return ret;
 }
 
+/**
+ * log_read:
+ * @self: a #Log instance.
+ * 
+ * If read end of line or zero chars or meats error, return NULL.
+ * 
+ * Returns: the pointer of buffer.
+ */
 gchar *
-string_get_date_string (gchar *line)
+log_read (Log *self)
 {
-    gchar **split, *date_string;
-    gchar *month=NULL, *day=NULL;
-    int i=0;
-
-    if (line == NULL || line[0] == 0)
-        return NULL;
-
-    split = g_strsplit (line, " ", 4);
-    if (split == NULL)
-        return NULL;
-
-    while ((day == NULL || month == NULL) && split[i]!=NULL && i<4) {
-        if (g_str_equal (split[i], "")) {
-            i++;
-            continue;
+	gchar *buffer;
+	off_t len, read_size, fp;
+
+	LV_MARK;
+	LOG_IS_VALID(self);
+
+	g_assert (LOGVIEW_IS_IFACE_IO(log_io (self)));
+	g_assert (LOGVIEW_IS_IFACE_VIEW(log_view (self)));
+	DATA_LOCK (self);
+
+	fp = logi_tell (log_io(self));
+	logi_seek (log_io(self), 0, SEEK_END);
+	len = logi_tell (log_io(self)) - fp;
+	buffer = g_malloc (len * sizeof(gchar) +1);
+	logi_seek (log_io(self), fp, SEEK_SET);
+	read_size = logi_read (log_io(self), buffer, len);
+        if (read_size > 0) {
+		logi_update (log_io(self));
+		buffer [read_size] = '\0';
+		buffer = logi_to_utf8 (log_view (self), buffer, read_size);
+		cleanup_binary_chars_in_log (buffer, read_size);
         }
+	else {
+                /* read_size == 0 || -1, reading finished or error? */
+		g_free (buffer);
+		buffer = NULL;
+	}
+	DATA_UNLOCK (self);
+	return buffer;
+}
 
-        if (month == NULL) {
-            month = split[i++];
-            /* If the first field begins by a number, the date
-               is given in yyyy-mm-dd format */
-            if (!g_ascii_isalpha (month[0]))
-                break;
-            continue;
-        }
+static guint
+log_thread_main_loop (Log *self)
+{
+	ThreadEvent te;
+
+	LOG_IS_VALID(self);
+
+	/* lock thread, prevent free instance before exist from thread */
+	g_mutex_lock (self->prv->thread_lock);
+	while ((te = log_thread_pop_event (self))) {
+		switch (te) {
+		case TH_EVENT_INIT_READ:
+			self->prv->update_status = FALSE;
+			LV_INFO ("%s begin building log lines...",
+				 self->prv->log_path);
+			log_thread_fill_content (self);
+			LV_INFO ("%s finished building log lines,"
+				 "found %ld new lines",
+				 self->prv->log_path,
+				 self->prv->total_lines);
+			break;
+		case TH_EVENT_UPDATE:
+			self->prv->update_status = TRUE;
+			log_thread_fill_content (self);
+			break;
+		case TH_EVENT_EXIT:
+			goto thread_exit;
+			break;
+		default:
+			g_assert_not_reached ();
+		}
+	}
+thread_exit:
+	g_mutex_unlock (self->prv->thread_lock);
+	return 0;
+}
 
-        if (day == NULL)
-            day = split[i];
-        i++;
-    }
-
-    if (i==3)
-        date_string = g_strconcat (month, "  ", day, NULL);
-    else
-        date_string = g_strconcat (month, " ", day, NULL);
-    g_strfreev (split);
-    return (date_string);
-}
-
-/* log_read_dates
-   Read all dates which have a log entry to create calendar.
-   All dates are given with respect to the 1/1/1970
-   and are then corrected to the correct year once we
-   reach the end.
-*/
-GSList *
-log_read_dates (gchar **buffer_lines, time_t current)
-{
-   int offsetyear = 0, current_year;
-   GSList *days = NULL, *days_copy;
-   GDate *date, *newdate;
-   struct tm *tmptm;
-   gchar *date_string;
-   Day *day;
-   gboolean done = FALSE;
-   int i, n, rangemin, rangemax;
-
-   if (buffer_lines == NULL)
-       return NULL;
-
-   n = g_strv_length (buffer_lines);
-
-   tmptm = localtime (&current);
-   current_year = tmptm->tm_year + 1900;
-
-   for (i=0; buffer_lines[i]==NULL; i++);
-
-   /* Start building the list */
-   /* Scanning each line to see if the date changed is too slow, so we proceed
-      in a recursive fashion */
-
-   date = string_get_date (buffer_lines[i]);
-   if ((date==NULL)|| !g_date_valid (date))
-       return NULL;
-
-   g_date_set_year (date, current_year);
-   day = g_new (Day, 1);
-   days = g_slist_append (days, day);
-
-   day->date = date;
-   day->first_line = i;
-   day->last_line = -1;
-   date_string = string_get_date_string (buffer_lines[i]);
-
-   rangemin = 0;
-   rangemax = n-1;
-
-   while (!done) {
-       
-       i = n-1;
-       while (day->last_line < 0) {
-
-           if (g_str_has_prefix (buffer_lines[i], date_string)) {
-               if (i == (n-1)) {
-                   day->last_line = i;
-                   done = TRUE;
-                   break;
-               } else {
-                   if (!g_str_has_prefix (buffer_lines[i+1], date_string)) {
-                       day->last_line = i;
-                       break;
-                   } else {
-                       rangemin = i;
-                       i = floor ( ((float) i + (float) rangemax)/2.);
-                   }
-               }
-           } else {
-               rangemax = i;
-               i = floor (((float) rangemin + (float) i)/2.);               
-           }
-
-       }
-       
-       g_free (date_string);
-
-       if (!done) {
-         /* We need to find the first line now that has a date
-            Logs can have some messages without dates ... */
-         newdate = NULL;
-         while (newdate == NULL && !done) {
-           i++;
-           date_string = string_get_date_string (buffer_lines[i]);
-           if (date_string == NULL)
-             if (i==n-1) {
-               done = TRUE;
-               break;
-             } else
-               continue;
-           newdate = string_get_date (buffer_lines[i]);
-           
-           if (newdate == NULL && i==n-1)
-             done = TRUE;
-         }
-
-         day->last_line = i-1;
-
-         /* Append a day to the list */	
-         if (newdate) {
-           g_date_set_year (newdate, current_year + offsetyear);	
-           if (g_date_compare (newdate, date) < 1) {
-             offsetyear++; /* newdate is next year */
-             g_date_add_years (newdate, 1);
-           }
-           
-           date = newdate;
-           day = g_new (Day, 1);
-           days = g_slist_append (days, day);
-           
-           day->date = date;
-           day->first_line = i;
-           day->last_line = -1;
-           rangemin = i;
-           rangemax = n;
-         }
-       }
-   }
-
-   /* Correct years now. We assume that the last date on the log
-      is the date last accessed */
-
-   for (days_copy = days; days_copy != NULL; days_copy = g_slist_next (days_copy)) {       
-       day = days_copy -> data;
-       g_date_subtract_years (day->date, offsetyear);
-   }
-   
-   /* Sort the days in chronological order */
-   days = g_slist_sort (days, days_compare);
-
-   return (days);
-}
-
-/* 
-   log_stats_new
-   Read the log and get some statistics from it. 
-   Returns NULL if the file is not a log.
-*/
-
-static LogStats *
-log_stats_new (char *filename, gboolean show_error)
-{
-   GnomeVFSResult result;
-   GnomeVFSFileInfo *info;
-   GnomeVFSHandle *handle;
-   GnomeVFSFileSize size;
-   LogStats *stats;
-   char buff[1024];
-   char *found_space;
-
-   if (filename == NULL)
-       return NULL;
-
-   /* Read first line and check that it is text */
-   result = gnome_vfs_open (&handle, filename, GNOME_VFS_OPEN_READ);
-   if (result != GNOME_VFS_OK) {
-	   return NULL;
-   }
-
-   info = gnome_vfs_file_info_new ();
-   result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
-   if (result != GNOME_VFS_OK || info->type != GNOME_VFS_FILE_TYPE_REGULAR) {
-       gnome_vfs_file_info_unref (info);
-	   gnome_vfs_close (handle);
-	   return NULL;
-   }
-
-   result = gnome_vfs_read (handle, buff, sizeof(buff), &size);
-   gnome_vfs_close (handle);
-   if (result != GNOME_VFS_OK) {
-       gnome_vfs_file_info_unref (info);
-	   return NULL;
-   }
-   
-   found_space = g_strstr_len (buff, 1024, " ");
-   if (found_space == NULL) {
-       gnome_vfs_file_info_unref (info);
-	   return NULL;
-   }
-   
-   stats = g_new (LogStats, 1);   
-   stats->file_time = info->mtime;
-   stats->file_size = info->size;
-   gnome_vfs_file_info_unref (info);
-
-   return (stats);
-}
-
-Log *
-log_open (char *filename, gboolean show_error)
-{
-   char *buffer, *zipped_name= NULL, *error_message;
-   GnomeVFSResult result;
-   LogStats *stats;
-   gboolean opened = TRUE;
-   int i, size;
-   GList *days;
-   Log *log;
-   
-   stats = log_stats_new (filename, show_error);
-   if (stats == NULL) {
-       if (file_is_zipped (filename)) {    
-           zipped_name = g_strdup_printf ("%s#gzip:", filename);
-           stats = log_stats_new (filename, show_error);
-           if (stats == NULL) {
-               opened = FALSE;
-           }
-       } else
-           opened = FALSE;
-   }
-   
-   if (opened == FALSE) {
-       error_message = g_strdup_printf (_("%s is not a log file."), filename);
-       goto error;
-   }
-
-   log = g_new0 (Log, 1);   
-   if (log == NULL) {
-       error_message = g_strdup (_("Not enough memory."));
-       goto error;
-   }
-
-   if (!zipped_name) {
-       log->name = g_strdup (filename);
-   } else {
-       log->name = zipped_name;
-       log->display_name = g_strdup (filename);
-   }
-
-   result = gnome_vfs_read_entire_file (log->name, &size, &buffer);
-   buffer[size-1] = 0;
-   if (result != GNOME_VFS_OK) {
-       error_message = g_strdup_printf (_("%s cannot be opened."), log->name);
-       goto error;
-   }
-
-   if (g_get_charset (NULL) == FALSE) {
-       char *buffer2;
-       buffer2 = locale_to_utf8 (buffer);
-       g_free (buffer);
-       buffer = buffer2;
-   }
-
-   log->lines = g_strsplit (buffer, "\n", -1);
-   g_free (buffer);   
-
-   log->total_lines = g_strv_length (log->lines);
-   log->displayed_lines = log->total_lines;
-   log->first_time = TRUE;
-   log->stats = stats;
-   log->model = NULL;
-   log->filter = NULL;
-   log->mon_offset = 0;
-   log->bold_rows_list = NULL;
-
-   /* A log without dates will return NULL */
-   log->days = log_read_dates (log->lines, log->stats->file_time);
-
-   /* Check for older versions of the log */
-   log->versions = 0;
-   log->current_version = 0;
-   log->parent_log = NULL;
-   for (i=1; i<5; i++) {
-       gchar *older_name;
-       older_name = g_strdup_printf ("%s.%d", log->name, i);
-       log->older_logs[i] = log_open (older_name, FALSE);
-       g_free (older_name);
-       if (log->older_logs[i] != NULL) {
-           log->older_logs[i]->parent_log = log;
-           log->older_logs[i]->current_version = i;
-           log->versions++;
-       }
-       else
-           break;
-   }
-
-   return log;
-
-error:
-   if (error_message && show_error) {
-       error_dialog_show (NULL, error_main, error_message);
-   }
-   
-   g_free (error_message);
-   
-   return NULL;
+gboolean
+log_run (Log *self)
+{
+	/* this function should only be ran once */
+	LOG_IS_VALID(self);
+	g_assert (self->prv->model == NULL);
+
+	log_create_model (self);
+	LV_INFO ("[[Log thread run]] %s", self->prv->log_path);
+	if (g_thread_create ((GThreadFunc) log_thread_main_loop, self, TRUE, NULL))
+		return TRUE;
+	else {
+		LV_INFO ("create thread failed", NULL);
+		return FALSE;
+	}
 }
 
 static void
-log_add_lines (Log *log, gchar *buffer)
+log_dispose (Log *self)
 {
-  char *old_buffer, *new_buffer;
+	LV_MARK;
+	if (self->prv->dispose_has_run) {
+		/* If dispose did already run, return. */
+		return;
+	}
+	self->prv->dispose_has_run = TRUE;
+	G_OBJECT_CLASS (parent_class)->dispose (G_OBJECT (self));
+}
 
-  g_assert (log != NULL);
-  g_assert (buffer != NULL);
+static void
+log_finalize (Log *self)
+{
+	gint i;
+	GSList *idx;
+	Day *day;
+	GSList *days;
+
+	LV_MARK;
+	LV_INFO ("[[ log remove ]] %s", self->prv->log_path);
+
+	log_notify (self, TH_EVENT_EXIT);
+	/* make sure thread existed */
+	g_mutex_lock (self->prv->thread_lock);
+	g_mutex_unlock (self->prv->thread_lock);
+	g_mutex_free (self->prv->thread_lock);
+	self->prv->thread_lock = NULL;
+	/* free other locks */
+	g_assert (LOGVIEW_IS_LOG (self));
+	g_cond_free (self->prv->event_cond);
+	self->prv->event_cond = NULL;
+	g_mutex_free (self->prv->event_mutex);
+	self->prv->event_mutex = NULL;
+	g_mutex_free (self->prv->data_mutex);
+	self->prv->data_mutex = NULL;
+	/* Close archive logs if there's some */
+	for (i = 0; i < self->prv->versions; i++)
+		if (self->prv->older_logs[i])
+			g_object_unref (self->prv->older_logs[i]);
+	g_free (self->prv->older_logs);
+
+	if (self->prv->mon_handle != NULL) {
+		gnome_vfs_monitor_cancel (self->prv->mon_handle);
+		self->prv->mon_handle = NULL;
+	}
+
+	if (self->prv->model) {
+		gtk_tree_store_clear (GTK_TREE_STORE (self->prv->model));
+		g_object_unref (self->prv->model);
+	}
+
+	if (self->prv->days != NULL) {
+		for (days = self->prv->days; days != NULL; days = g_slist_next (days)) {
+			day = days->data;
+			g_date_free (day->date);
+			gtk_tree_path_free (day->path);
+			g_free (day);
+		}
+		g_slist_free (self->prv->days);
+		self->prv->days = NULL;
+	}
+
+	if (self->prv->date_iter)
+		gtk_tree_iter_free (self->prv->date_iter);
+	if (self->prv->line_iter)
+		gtk_tree_iter_free (self->prv->line_iter);
+	if (self->prv->visible_first)
+		gtk_tree_path_free (self->prv->visible_first);
+
+	if (self->prv->first_bold_row)
+		gtk_tree_path_free (self->prv->first_bold_row);
+	if (self->prv->last_bold_row)
+		gtk_tree_path_free (self->prv->last_bold_row);
+
+	for (i = 0; i < LOG_PF_NUM; i++) {
+		if (self->prv->ext_data[i] != NULL) {
+			g_object_unref (G_OBJECT (self->prv->ext_data[i]));
+		}
+	}
+
+	for (idx = self->prv->need_free; idx; idx = g_slist_next(idx)) {
+		gchar *mass = (gchar *)idx->data;
+		g_free (mass);
+	}
+
+	g_free (self->prv->lines);
+	self->prv->lines = NULL;
+
+	if (self->prv->display_name)
+		g_free (self->prv->display_name);
+	if (self->prv->log_path)
+		g_free (self->prv->log_path);
+	g_free (self->prv);
+	self->prv = NULL;
+	parent_class->finalize (G_OBJECT (self));
+}
 
-  old_buffer = g_strjoinv ("\n", log->lines);
-  new_buffer = g_strconcat (old_buffer, "\n", buffer, NULL);
-  g_free (old_buffer);
-  
-  g_strfreev (log->lines);
-  log->lines = g_strsplit (new_buffer, "\n", -1);
-  g_free (new_buffer);
+void
+log_extract_filepath (Log* self, gchar** dirname, gchar** filename)
+{
+	LOG_IS_VALID(self);
+	g_assert (LOGVIEW_IS_IFACE_IO(log_io(self)));
 
-  log->total_lines = g_strv_length (log->lines);
+	logi_extract_filepath(LOGVIEW_IFACE_IO(log_io(self)), dirname, filename);
 }
 
-void log_stats_reload (Log *log)
+gboolean
+log_has_been_modified (Log *self)
 {
-  g_free (log->stats);
-  log->stats = log_stats_new (log->name, TRUE);
+	LOG_IS_VALID(self);
+	g_assert (LOGVIEW_IS_IFACE_IO(log_io(self)));
+
+	return logi_has_updated(LOGVIEW_IFACE_IO(log_io(self)));
 }
 
-/* log_read_new_lines */
+gboolean
+log_test_func (Log *self, PluginFunc pf)
+{
+	LOG_IS_VALID(self);
+
+	return self->prv->ext_data[pf] != NULL;
+}
 
-gboolean 
-log_read_new_lines (Log *log)
+void
+log_set_func (Log *self, PluginFunc pf, LogviewPlugin *plugin)
 {
-    GnomeVFSResult result;
-    gchar *buffer;
-    GnomeVFSFileSize newfilesize, read;
-    guint64 size, newsize;
-    
-    g_return_val_if_fail (log!=NULL, FALSE);
-    
-    result = gnome_vfs_seek (log->mon_file_handle, GNOME_VFS_SEEK_END, 0L);
-    result = gnome_vfs_tell (log->mon_file_handle, &newfilesize);
-    size = log->mon_offset;
-    newsize = newfilesize;
-
-    if (newsize > size) {
-      buffer = g_malloc (newsize - size);
-      if (!buffer)
-	return FALSE;
+	LOG_IS_VALID(self);
+	g_assert (plugin != NULL);
 
-      result = gnome_vfs_seek (log->mon_file_handle, GNOME_VFS_SEEK_START, size);
-      if (result != GNOME_VFS_OK)
-	return FALSE;
+	self->prv->ext_data[pf] = plugin;
+}
 
-      result = gnome_vfs_read (log->mon_file_handle, buffer, newsize-size, &read);
-      if (result != GNOME_VFS_OK)
-	return FALSE;
+void
+log_set_child (Log *self, gint index, Log *child)
+{
+	LOG_IS_VALID(self);
+	g_assert (index >=0 && index < OLD_LOG_NUM);
+	g_assert (child && LOGVIEW_IS_LOG (child));
+	g_assert (self->prv->older_logs[index] == NULL);
+
+	self->prv->older_logs[index] = child;
+	self->prv->versions++;
+	child->prv->parent_log = self;
+}
 
-      buffer [newsize-size-1] = 0;
-      log->mon_offset = newsize;      
-      log_add_lines (log, buffer);
-      g_free (buffer);
-      
-      log_stats_reload (log);
-      return TRUE;
-    }
-    return FALSE;
+Log*
+log_get_child (Log *self, gint index)
+{
+	LOG_IS_VALID(self);
+	g_assert (index >=0 && index < OLD_LOG_NUM);
+
+	return self->prv->older_logs[index];
 }
 
-/* 
-   log_unbold is called by a g_timeout
-   set in loglist_bold_log 
-*/
+void
+log_notify (Log *self, ThreadEvent te)
+{
+	g_mutex_lock (self->prv->event_mutex);
+	self->prv->th_event |= te;
+	g_cond_signal (self->prv->event_cond);
+	g_mutex_unlock (self->prv->event_mutex);
+}
 
-gboolean
-log_unbold (gpointer data)
+static ThreadEvent
+log_thread_pop_event (Log *self)
 {
-    LogviewWindow *logview;
-    LogList *list;
-    Log *log = data;
+	ThreadEvent te;
+	LOG_IS_VALID(self);
+
+	g_mutex_lock (self->prv->event_mutex);
+	while (self->prv->th_event == TH_EVENT_EMPTY)
+		g_cond_wait (self->prv->event_cond, self->prv->event_mutex);
+	if (self->prv->th_event & TH_EVENT_EXIT)
+		te =  TH_EVENT_EXIT;
+	else if (self->prv->th_event & TH_EVENT_INIT_READ)
+		te =  TH_EVENT_INIT_READ;
+	else if (self->prv->th_event & TH_EVENT_UPDATE)
+		te =  TH_EVENT_UPDATE;
+	else
+		g_assert_not_reached ();
+	self->prv->th_event ^= te;
+	g_mutex_unlock (self->prv->event_mutex);
+	return te;
+}
 
-    g_return_val_if_fail (log != NULL, FALSE);
+static void
+log_thread_idle (Log *self)
+{
+	g_assert (self);
+	g_assert (LOGVIEW_IS_LOG (self));
+
+	g_usleep (1);
+	g_mutex_lock (self->prv->event_mutex);
+	if (self->prv->th_event & TH_EVENT_EXIT) {
+		g_mutex_unlock (self->prv->event_mutex);
+		g_mutex_unlock (self->prv->thread_lock);
+		g_thread_exit (0);
+	}
+	g_mutex_unlock (self->prv->event_mutex);
+}
 
-    logview = log->window;
+static void
+log_create_model (Log *self)
+{
+	LOG_IS_VALID(self);
+	self->prv->model = GTK_TREE_MODEL(
+		gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER,
+				    G_TYPE_INT, G_TYPE_BOOLEAN)
+		);
+}  
 
-    /* If the log to unbold is not displayed, still wait */
-    if (logview_get_active_log (logview) != log)
-        return TRUE;
+static void
+log_thread_fill_content (Log *self)
+{
+	GSList *days, *idx;
+	GtkTreePath *path = NULL;
+	gchar *buffer;
+	guint start, end;
+	LV_MARK;
+
+	LOG_IS_VALID(self);
+	g_assert (self);
+
+	if (self->prv->update_status) {
+		if (log_has_been_modified (self) == FALSE)
+			return;
+	}
+
+	if ((buffer = log_read (self)) == NULL)
+                return;
+	g_assert (buffer && buffer[0] != '\0');
+
+	start = log_build_list_index_from_mass (self, buffer);
+	g_assert (start <= self->prv->total_lines);
+
+	days = logi_group_lines (log_view (self),
+				 (const gchar **) (self->prv->lines + start));
+	end = self->prv->total_lines;
+
+	if (days) {
+		log_fill_model_with_date (self, days);
+		path = gtk_tree_model_get_path (self->prv->model,
+						self->prv->date_iter);
+		DATA_LOCK (self);
+		self->prv->days = g_slist_concat (self->prv->days, days);
+		DATA_UNLOCK (self);
+	}
+	else {
+		GtkTreeIter iter;
+		log_write_lines_range (self, self->prv->lines, start, end);
+		path = gtk_tree_model_get_path (self->prv->model,
+						self->prv->line_iter);
+	}
+
+	DATA_LOCK (self);
+	if (self->prv->update_status) {
+		/* update */
+		/* Remember the last bold lines in the model to 
+		   unset them later */
+		if (self->prv->last_bold_row)
+			gtk_tree_path_free (self->prv->last_bold_row);
+		self->prv->last_bold_row =
+			gtk_tree_model_get_path (self->prv->model,
+						 self->prv->line_iter);
+		g_timeout_add (BOLD_ROWS_TIME,
+			       (GSourceFunc) log_unbold_rows,
+			       g_object_ref (self));
+	}
+	else {
+		/* initial, remember the first visible line
+		   and selected lines */
+		self->prv->selected_paths =
+			g_list_append (self->prv->selected_paths,
+				       gtk_tree_path_copy (path));
+		if (self->prv->visible_first)
+			gtk_tree_path_free (self->prv->visible_first);
+		self->prv->visible_first = gtk_tree_path_copy (path);
+	}
+	DATA_UNLOCK (self);
+	gtk_tree_path_free (path);
+}
+    
+static void
+log_fill_model_with_date (Log *self, GSList *days)
+{
+	gint i;
+	GSList *iter;
+	Day *day = NULL;
+
+	LOG_IS_VALID(self);
+	g_assert (days);
+
+	for (iter = days; iter; iter = g_slist_next (iter)) {
+		day = (Day *)iter->data;
+
+		log_write_day (self, day);
+		day->path = gtk_tree_model_get_path (self->prv->model, self->prv->date_iter);
+		log_write_lines_range (self, self->prv->lines,
+				       day->first_line,
+				       day->last_line + 1);
+		day->expand = FALSE;
+	}
+	day->expand = TRUE;
+}
 
-    list = logview_get_loglist (logview);
-    loglist_unbold_log (list, log);
-    return FALSE;
+void
+log_write_day (Log *self, Day *day)
+{
+	gchar *old_date_str = NULL;
+	gchar *new_date_str = NULL;
+	gint old_date_len;
+	gint new_date_len;
+	GDate *date;
+
+	LOG_IS_VALID(self);
+        g_assert (day);
+        g_assert (day->date);
+        g_assert (self->prv->model);
+
+	date = day->date;
+
+        g_assert (g_date_valid (date));
+
+        new_date_str = date_to_string (date); /* convert to utf8 */
+        g_assert (new_date_str);
+
+	if (self->prv->date_iter == NULL) {
+		self->prv->date_iter = g_new0 (GtkTreeIter,1);
+		goto log_write_date_first_run;
+	}
+	
+        gtk_tree_model_get (self->prv->model,
+                            self->prv->date_iter,
+                            MESSAGE, &old_date_str,
+                            -1);
+        old_date_len = (gint) g_utf8_strlen (old_date_str, -1);
+        new_date_len = (gint) g_utf8_strlen (new_date_str, -1);
+        if ((old_date_len != new_date_len)
+            || (old_date_len >= new_date_len ?
+                g_ascii_strncasecmp (old_date_str, new_date_str, new_date_len)
+                : g_ascii_strncasecmp (old_date_str, new_date_str, old_date_len)) != 0) {
+                /* new date */
+log_write_date_first_run:
+                gtk_tree_store_append (GTK_TREE_STORE (self->prv->model),
+                                       self->prv->date_iter,
+                                       NULL);
+                gtk_tree_store_set (GTK_TREE_STORE (self->prv->model),
+                                    self->prv->date_iter,
+                                    MESSAGE, new_date_str,
+                                    DAY_POINTER, day,
+                                    -1);
+        }
+        g_free (new_date_str);
+        g_free (old_date_str);
+	log_thread_idle (self);
 }
 
 void
-log_close (Log *log)
-{
-   gint i;
-   Day *day;
-   GSList *days;
-
-   g_return_if_fail (log);
-   
-   /* Close archive logs if there's some */
-   for (i = 0; i < log->versions; i++)
-       log_close (log->older_logs[i]);
-
-   /* Close file - this should not be needed */
-   if (log->mon_file_handle != NULL) {
-	   gnome_vfs_close (log->mon_file_handle);
-	   log->mon_file_handle = NULL;
-   }
-
-   g_object_unref (log->model);
-   g_strfreev (log->lines);
-
-   if (log->days != NULL) {
-       for (days = log->days; days != NULL; days = g_slist_next (days)) {
-           day = days->data;
-           g_date_free (day->date);
-           gtk_tree_path_free (day->path);
-           g_free (day);
-       }
-       g_slist_free (log->days);
-       log->days = NULL;
-   }
-   
-   g_free (log->stats);
-
-   if (log->selected_range.first)
-     gtk_tree_path_free (log->selected_range.first);
-   if (log->selected_range.last)
-     gtk_tree_path_free (log->selected_range.last);
-   if (log->visible_first)
-     gtk_tree_path_free (log->visible_first);
-
-   g_free (log);
-   return;
+log_write_date (Log *self, GDate *date)
+{
+        gchar *old_date_str = NULL;
+        gchar *new_date_str = NULL;
+        gint old_date_len;
+        gint new_date_len;
+
+	LOG_IS_VALID(self);
+        g_assert (date);
+        g_assert (self->prv->model);
+        g_assert (g_date_valid (date));
+
+        new_date_str = date_to_string (date); /* convert to utf8 */
+        g_assert (new_date_str);
+
+	if (self->prv->date_iter == NULL) {
+		self->prv->date_iter = g_new0 (GtkTreeIter,1);
+		goto log_write_date_first_run;
+	}
+	
+        gtk_tree_model_get (self->prv->model,
+                            self->prv->date_iter,
+                            MESSAGE, &old_date_str,
+                            -1);
+        old_date_len = (gint) g_utf8_strlen (old_date_str, -1);
+        new_date_len = (gint) g_utf8_strlen (new_date_str, -1);
+        if ((old_date_len != new_date_len)
+            || (old_date_len >= new_date_len ?
+                g_ascii_strncasecmp (old_date_str, new_date_str, new_date_len)
+                : g_ascii_strncasecmp (old_date_str, new_date_str, old_date_len)) != 0) {
+                /* new date */
+log_write_date_first_run:
+                gtk_tree_store_append (GTK_TREE_STORE (self->prv->model),
+                                       self->prv->date_iter,
+                                       NULL);
+                gtk_tree_store_set (GTK_TREE_STORE (self->prv->model),
+                                    self->prv->date_iter,
+                                    MESSAGE, new_date_str,
+                                    DAY_POINTER, date,
+                                    -1);
+        }
+        g_free (new_date_str);
+        g_free (old_date_str);
+	log_thread_idle (self);
 }
 
-gchar *
-log_extract_filename (Log *log)
+void
+log_write_lines_range (Log *self, gchar **lines, gint start, gint end)
 {
-  GnomeVFSURI *uri;
-  gchar *filename;
+	gint i, n;
+	g_assert (end > start);
+	n = (end-start)/WRITE_LINES;
+	for (i = 0; i < n; i ++) {
+		log_write_lines (self,
+				 lines + start + i*WRITE_LINES,
+				 WRITE_LINES);
+	}
+	log_write_lines (self, lines + start + n*WRITE_LINES,
+			 (end-start)%WRITE_LINES);
+}
 
-  uri = gnome_vfs_uri_new (log->name);
-  filename = gnome_vfs_uri_extract_short_name (uri);
-  gnome_vfs_uri_unref (uri);
+void
+log_write_lines (Log *self, gchar **lines, gint num)
+{
+	gint i;
+	for (i = 0; i < num; i ++)
+		log_write_line (self, lines[i]);
+	log_thread_idle (self);
+}
 
-  return filename;
+void
+log_write_line (Log *self, gchar *line)
+{
+	LOG_IS_VALID(self);
+	g_assert (line);
+	g_assert (self->prv->model);
+	g_assert (self->prv->line_iter);
+
+	gtk_tree_store_append (GTK_TREE_STORE (self->prv->model),
+				self->prv->line_iter,
+				self->prv->date_iter);
+	if (!self->prv->update_status) {
+		/* normal */
+		gtk_tree_store_set (GTK_TREE_STORE (self->prv->model),
+				    self->prv->line_iter,
+				    MESSAGE, line,
+				    DAY_POINTER, NULL,
+				    -1);
+	} else {
+		/* bold */
+	        gtk_tree_store_set (GTK_TREE_STORE (self->prv->model),
+				    self->prv->line_iter,
+	                            MESSAGE, line, 
+	                            DAY_POINTER, NULL,
+	                            LOG_LINE_WEIGHT, PANGO_WEIGHT_BOLD,
+	                            LOG_LINE_WEIGHT_SET, TRUE,
+				    -1);
+		/* Remember the first bold lines in the model to unset them later */
+		if (self->prv->first_bold_row == NULL)
+			self->prv->first_bold_row = gtk_tree_model_get_path (self->prv->model, self->prv->line_iter);
+
+	}
+	log_thread_idle (self);
 }
 
-gchar *
-log_extract_dirname (Log *log)
+static gboolean
+log_unbold_rows (Log *self)
 {
-  GnomeVFSURI *uri;
-  gchar *dirname;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	GtkTreePath *path;
 
-  uri = gnome_vfs_uri_new (log->name);
-  dirname = gnome_vfs_uri_extract_dirname (uri);
-  gnome_vfs_uri_unref (uri);
+	LOG_IS_VALID(self);
 
-  return dirname;
+	g_assert (self->prv->view);
+	if (self->prv->model != 
+	    gtk_tree_view_get_model (GTK_TREE_VIEW(self->prv->view))) {
+		g_object_unref (self);
+		return TRUE;
+	}
+
+	DATA_LOCK (self);
+	if (self->prv->first_bold_row != NULL &&
+	    self->prv->last_bold_row != NULL) {
+		for (path = self->prv->first_bold_row; 
+		     gtk_tree_path_compare (path, self->prv->last_bold_row)<=0;
+		     gtk_tree_path_next (path)) {
+			gtk_tree_model_get_iter (self->prv->model, &iter, path);
+			gtk_tree_store_set (GTK_TREE_STORE (self->prv->model), &iter,
+					    LOG_LINE_WEIGHT, PANGO_WEIGHT_NORMAL,
+					    LOG_LINE_WEIGHT_SET, TRUE,
+					    -1);
+		}
+  
+		gtk_tree_path_free (self->prv->first_bold_row);
+		gtk_tree_path_free (self->prv->last_bold_row);
+		self->prv->first_bold_row = NULL;
+		self->prv->last_bold_row = NULL;
+	}
+	DATA_UNLOCK (self);
+	g_object_unref (self);
+	return FALSE;
 }
+
+void log_lock (Log *self)
+{
+	DATA_LOCK (self);
+}
+
+void log_unlock (Log *self)
+{
+	DATA_UNLOCK (self);
+}
+
diff --git a/logview/logrtns.h b/logview/logrtns.h
index 891869a..20106f5 100644
--- a/logview/logrtns.h
+++ b/logview/logrtns.h
@@ -21,72 +21,19 @@
 #ifndef __LOGRTNS_H__
 #define __LOGRTNS_H__
 
-#include <time.h>
 #include <libgnomevfs/gnome-vfs.h>
+#include "logview-log.h"
 
-typedef struct
-{
-    GDate *date;
-    long first_line, last_line; /* First and last line for this day in the log */
-    gboolean expand;
-    GtkTreePath *path;
-} Day;
+G_BEGIN_DECLS
 
-typedef struct
-{
-    time_t file_time;
-    GnomeVFSFileSize file_size;
-} LogStats;
+/* call by log_repaint */
+Day * log_find_day (Log *self, const GDate* date);
 
-typedef struct TreePathRange
-{
-  GtkTreePath *first;
-  GtkTreePath *last;
-}TreePathRange;
+/* log utilities base on the plugin interfaces */
+void log_extract_filepath (Log* self, gchar** dirname, gchar** filename);
+gboolean log_has_been_modified (Log* self);
+gboolean log_run (Log* self);
 
-typedef struct _log Log;
-struct _log
-{
-	char *name;
-	char *display_name;
-	LogStats *stats;
-
-    GSList *days;
-    gchar **lines;
-	gint selected_line_first;
-	gint selected_line_last;
-	gint total_lines; /* no of lines in the file */
-    gint displayed_lines; /* no of lines displayed now */
-
-	/* Monitor info */
-	GnomeVFSFileSize mon_offset;
-	GnomeVFSMonitorHandle *mon_handle;
-    GnomeVFSHandle *mon_file_handle;
-	gboolean monitored;
-    gboolean needs_refresh;
-
-	gboolean first_time;
-    GtkTreeModel *model;
-    GtkTreeModelFilter *filter;
-    GList *bold_rows_list;
-	TreePathRange selected_range;
-    GtkTreePath *visible_first;
-
-	int versions;
-	int current_version;
-	/* older_logs[0] should not be used */
-	Log *older_logs[5];
-	Log *parent_log;
-	
-    gpointer window;
-};
-
-gboolean file_is_log (char *filename, gboolean show_error);
-Log *log_open (char *filename, gboolean show_error);
-gboolean log_read_new_lines (Log *log);
-gboolean log_unbold (gpointer data);
-void log_close (Log * log);
-gchar *log_extract_filename (Log *log);
-gchar *log_extract_dirname (Log *log);
+G_END_DECLS
 
 #endif /* __LOGRTNS_H__ */
diff --git a/logview/logview-debug.c b/logview/logview-debug.c
new file mode 100644
index 0000000..d3c602b
--- /dev/null
+++ b/logview/logview-debug.c
@@ -0,0 +1,74 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "misc.h"
+#include "logview-debug.h"
+
+#define LOGVIEW_USER_PATH_SUFFIX LOGVIEW_HOME_SUFFIX "/gnome-system-log.log"
+static LogviewLevelFlags debug = LOGVIEW_INFO;
+static FILE *logf;
+
+void
+lv_debug (LogviewLevelFlags level,
+	  const gchar *file,
+	  gint line,
+	  const gchar *function)
+{
+        g_return_if_fail (file && function && line >= 0);
+	if (debug & level) {
+		g_fprintf (logf, "\"%s\" %s:%d\n", file, function, line);
+		fflush (stdout);
+	}
+}
+
+void
+lv_debug_message (LogviewLevelFlags level,
+		  const gchar *file,
+		  gint line,
+		  const gchar *function,
+		  const gchar *format, ...)
+{
+	if (debug & level) {
+		va_list ap;
+		g_return_if_fail (file && function && line >= 0);
+
+		if (debug & LOGVIEW_VERBOSE) {
+			g_fprintf (logf, "\"%s\" %s:%d ", file, function, line);
+			fflush (stdout);
+		}
+
+		va_start (ap, format);
+		g_vfprintf (logf, format, ap);
+		g_fprintf (logf, "\n");
+		va_end (ap);
+		fflush (stdout);
+	}
+}
+
+void
+logview_debug_init ()
+{
+	gchar *f = g_build_path (G_DIR_SEPARATOR_S, (gchar*)g_get_home_dir (),
+			     LOGVIEW_USER_PATH_SUFFIX, NULL);
+	logf = g_fopen (f, "wb");
+	g_free (f);
+
+	if (g_getenv ("LOGVIEW_DEBUG") != NULL)	{
+		debug |= LOGVIEW_ERR | LOGVIEW_VERBOSE;
+	}
+	if (g_getenv ("LOGVIEW_VERBOSE") != NULL) {
+		debug |= LOGVIEW_VERBOSE;
+	}
+}
+
+void
+logview_debug_destroy ()
+{
+	fclose (logf);
+	logf = NULL;
+}
+
diff --git a/logview/logview-debug.h b/logview/logview-debug.h
new file mode 100644
index 0000000..f02a1db
--- /dev/null
+++ b/logview/logview-debug.h
@@ -0,0 +1,87 @@
+#ifndef __LOGVIEW_DEBUG_H__
+#define __LOGVIEW_DEBUG_H__
+#include <glib.h>
+
+/**
+ * LogviewLevelFlags:
+ * @LOGVIEW_INFO: Normal information will be displayed in this level.
+ * @LOGVIEW_VERBOSE: Like %LOGVIEW_INFO, more information like file name,
+ * function name and line number will be displayed in this level.
+ * @LOGVIEW_ERR: Cover all above levels, displays verbose information
+ * for debugging usage.
+ * 
+ * Used to specify the level of the log of gnome-system-log.
+ **/
+typedef enum {
+	LOGVIEW_INFO		= 1 << 0,
+	LOGVIEW_VERBOSE		= 1 << 1,
+	LOGVIEW_ERR		= 1 << 2
+} LogviewLevelFlags;
+
+void logview_debug_init ();
+void logview_debug_destroy ();
+void lv_debug (LogviewLevelFlags level,
+	       const gchar *file,
+	       gint line,
+	       const gchar *function);
+void lv_debug_message (LogviewLevelFlags level,
+		       const gchar *file,
+		       gint line,
+		       const gchar *function,
+		       const gchar *format, ...);
+
+#ifdef ON_SUN_OS
+#define FUNC __func__
+#else
+#define FUNC G_STRFUNC
+#endif
+
+/**
+ * LV_MARK:
+ * 
+ * Dump out information in the %LOGVIEW_ERR level.
+ **/
+#define LV_MARK \
+	lv_debug (LOGVIEW_ERR, __FILE__, __LINE__, FUNC)
+
+/**
+ * LV_INFO:
+ * @FORMAT: a formatted string.
+ * @Varargs: 
+ * 
+ * Dump out information in the %LOGVIEW_VERBOSE and %LOGVIEW_INFO level.
+ **/
+#define LV_INFO(FORMAT, ...)  \
+	lv_debug_message (LOGVIEW_INFO, __FILE__, __LINE__, FUNC, "[II] " FORMAT, __VA_ARGS__)
+
+/**
+ * LV_INFO_WW:
+ * @FORMAT: a formatted string.
+ * @Varargs: 
+ * 
+ * Like LV_INFO(), dump out warnning information.
+ **/
+#define LV_INFO_WW(FORMAT, ...)  \
+	lv_debug_message (LOGVIEW_INFO, __FILE__, __LINE__, FUNC, "[WW] " FORMAT, __VA_ARGS__)
+
+/**
+ * LV_INFO_EE:
+ * @FORMAT: a formatted string.
+ * @Varargs: 
+ * 
+ * Like LV_INFO(), dump out error information.
+ **/
+#define LV_INFO_EE(FORMAT, ...)  \
+	lv_debug_message (LOGVIEW_INFO, __FILE__, __LINE__, FUNC, "[EE] " FORMAT, __VA_ARGS__)
+
+/**
+ * LV_ERR:
+ * @FORMAT: a formatted string.
+ * @Varargs: 
+ * 
+ * Dump out information in the %LOGVIEW_ERR level.
+ **/
+#define LV_ERR(FORMAT, ...)  \
+	lv_debug_message (LOGVIEW_ERR, __FILE__, __LINE__, FUNC, FORMAT, __VA_ARGS__)
+
+#endif
diff --git a/logview/logview-findbar.c b/logview/logview-findbar.c
index c70ff16..8c6477b 100644
--- a/logview/logview-findbar.c
+++ b/logview/logview-findbar.c
@@ -22,6 +22,7 @@
 #include <glib/gi18n.h>
 #include "logview.h"
 #include "logview-findbar.h"
+#include "log_repaint.h"
 
 struct LogviewFindBarPriv
 {
@@ -55,9 +56,15 @@ logview_findbar_clear (GtkWidget *widget, gpointer data)
 {
 	LogviewFindBar *findbar = LOGVIEW_FINDBAR (data);
 	LogviewWindow *logview = LOGVIEW_WINDOW (findbar->priv->logview);
+	GtkTreeModelFilter *filter;
 	Log *log = logview->curlog;
 
-	if (log==NULL || log->filter == NULL)
+	if (log==NULL)
+ 		return;
+	g_object_get (G_OBJECT (log),
+		      "filter", &filter,
+		      NULL);
+	if (filter == NULL)
  		return;
 
 	gtk_entry_set_text (GTK_ENTRY (findbar->priv->entry), "");
@@ -70,21 +77,28 @@ logview_findbar_entry_timeout (gpointer data)
 	LogviewFindBar *findbar = LOGVIEW_FINDBAR (data);
 	LogviewWindow *logview = LOGVIEW_WINDOW (findbar->priv->logview);
 	Log *log = logview->curlog;
+	GtkTreeModel *model;
+	GtkTreeModelFilter *filter;
 	GdkCursor *cursor;
 
+	g_object_get (G_OBJECT (log),
+		      "model", &model,
+		      "filter", &filter,
+		      NULL);
 	cursor = gdk_cursor_new (GDK_WATCH);
 	gdk_window_set_cursor (GTK_WIDGET (logview)->window, cursor);
 	gdk_cursor_unref (cursor);
 	gdk_display_flush (gtk_widget_get_display (GTK_WIDGET (logview)));
 	
-	if (log->filter == NULL) {
+	if (filter == NULL) {
 
-		log->filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (log->model, NULL));
-		gtk_tree_model_filter_set_visible_func (log->filter, iter_is_visible, findbar, NULL);
-		gtk_tree_view_set_model (GTK_TREE_VIEW (logview->view), GTK_TREE_MODEL (log->filter));
+		filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
+		gtk_tree_model_filter_set_visible_func (filter, iter_is_visible, findbar, NULL);
+		gtk_tree_view_set_model (GTK_TREE_VIEW (logview->view), GTK_TREE_MODEL (filter));
+		g_object_set (G_OBJECT (log), "filter", filter, NULL);
 
 	} else {
-		gtk_tree_model_filter_refilter (log->filter);
+		gtk_tree_model_filter_refilter (filter);
 	}
 
 	gtk_tree_view_expand_all (GTK_TREE_VIEW (logview->view));
@@ -103,13 +117,19 @@ logview_findbar_entry_changed_cb (GtkEditable *editable,
 	LogviewFindBar *findbar = LOGVIEW_FINDBAR (data);
 	LogviewWindow *logview = LOGVIEW_WINDOW (findbar->priv->logview);
 	Log *log = logview->curlog;
+	GtkTreeModelFilter *filter;
 	gchar *search_string;    
 
 	search_string = g_strdup (gtk_entry_get_text (GTK_ENTRY (findbar->priv->entry)));
+	g_object_get (G_OBJECT (log),
+		      "filter", &filter,
+		      NULL);
 	
-	if (strlen (search_string) == 0 && log->filter != NULL) {
-		g_object_unref (log->filter);
-		log->filter = NULL;
+	if (strlen (search_string) == 0 && filter != NULL) {
+		g_object_unref (filter);
+		g_object_set (G_OBJECT (log),
+			      "filter", NULL,
+			      NULL);
 		logview_repaint (logview);
 		return;
 	}
@@ -143,7 +163,7 @@ logview_findbar_connect (LogviewFindBar *findbar, LogviewWindow *logview)
 static void 
 logview_findbar_init (LogviewFindBar *findbar)
 {
-	GtkWidget *label, *button;
+	GtkWidget *label;
 	
 	findbar->priv = g_new0 (LogviewFindBarPriv, 1);
 
diff --git a/logview/logview-iface-collector.c b/logview/logview-iface-collector.c
new file mode 100644
index 0000000..3d97d6f
--- /dev/null
+++ b/logview/logview-iface-collector.c
@@ -0,0 +1,99 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#include "logview-iface.h"
+#include "logview-iface-collector.h"
+#include "logview-debug.h"
+
+static void
+logview_iface_base_init (gpointer gclass)
+{
+	static gboolean initialized = FALSE;
+	if (!initialized) {
+		initialized = TRUE;
+	}
+}
+
+static void
+logview_iface_class_init (gpointer g_class, gpointer g_class_data)
+{
+	LogviewIFaceCollector *self = (LogviewIFaceCollector *)g_class;
+	self->config_log = NULL;
+	self->config_log_from_path = NULL;
+	self->config_log = NULL;
+}
+
+GType
+logview_iface_collector_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (LogviewIFaceCollector),
+			logview_iface_base_init,   /* base_init */
+			NULL,   /* base_finalize */
+			logview_iface_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			0,
+			0,      /* n_preallocs */
+			NULL    /* instance_init */
+		};
+		type = g_type_register_static (G_TYPE_INTERFACE,
+					       g_intern_static_string ("LogviewIFaceCollectorType"),
+					       &info, 0);
+		g_type_interface_add_prerequisite (type, LOGVIEW_TYPE_IFACE);
+	}
+	return type;
+}
+
+/**
+ * logi_get_logs:
+ * @self: a #LogviewICollector instance.
+ * 
+ * Gnome System Log get logs by invoking this routine.
+ * 
+ * Returns: a single list, each element is a string represents a log name.
+ **/
+GSList*
+logi_get_logs (LogviewICollector *self)
+{
+	LV_MARK;
+	return LOGVIEW_GET_COLLECTOR_INTERFACE (self)->get_logs(self);
+}
+
+/**
+ * logi_config_log_from_path:
+ * @self: a #LogviewICollector instance.
+ * @log_name: log name which is obtained from logi_get_logs().
+ * 
+ * Gnome System Log tries to config a log instance by passing the log name to this routine.
+ *
+ * Returns: On successful completion, returns a #Log instance; else returns NULL.
+ **/
+Log* 
+logi_config_log_from_path (LogviewICollector *self, const gchar *log_name)
+{
+	LV_MARK;
+	return LOGVIEW_GET_COLLECTOR_INTERFACE (self)->config_log_from_path (self, log_name);
+}
+
+/**
+ * logi_config_log:
+ * @self: a #LogviewICollector instance.
+ * @log: a #Log instance.
+ * 
+ * Gnome System Log tries to config a log instance by passing the @log to this routine.
+ *
+ * Returns: On successful completion, returns TRUE; else returns FALSE.
+ **/
+gboolean
+logi_config_log (LogviewICollector *self, Log *log)
+{
+	LV_MARK;
+	return LOGVIEW_GET_COLLECTOR_INTERFACE (self)->config_log (self, log);
+}
+
diff --git a/logview/logview-iface-collector.h b/logview/logview-iface-collector.h
new file mode 100644
index 0000000..495301a
--- /dev/null
+++ b/logview/logview-iface-collector.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#ifndef __LOGVIEW_IFACE_COLLECTOR_H__
+#define __LOGVIEW_IFACE_COLLECTOR_H__
+
+#include <glib-object.h>
+#include "logview-log.h"
+
+G_BEGIN_DECLS
+
+#define LOGVIEW_TYPE_IFACE_COLLECTOR \
+	(logview_iface_collector_get_type ())
+#define LOGVIEW_IFACE_COLLECTOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj), LOGVIEW_TYPE_IFACE_COLLECTOR, \
+				     LogviewICollector))
+#define LOGVIEW_IS_IFACE_COLLECTOR(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj), LOGVIEW_TYPE_IFACE_COLLECTOR))
+#define LOGVIEW_GET_COLLECTOR_INTERFACE(obj) \
+	(G_TYPE_INSTANCE_GET_INTERFACE((obj), LOGVIEW_TYPE_IFACE_COLLECTOR, \
+					LogviewIFaceCollector))
+
+typedef struct _LogviewICollector LogviewICollector; /* dummy object */
+typedef struct _LogviewIFaceCollector LogviewIFaceCollector;
+
+struct _LogviewIFaceCollector {
+	GTypeInterface parent;
+
+	/* Generate a new list of log names, need be freed */
+	GSList* (*get_logs) (LogviewICollector *self);
+
+	/* Create a log instance from path name, returns NULL if fails. */
+	Log* (*config_log_from_path) (LogviewICollector *self,
+				      const gchar *log_name);
+
+	/* configure a log instance */
+	gboolean (*config_log) (LogviewICollector *self, Log *log);
+};
+
+extern GType logview_iface_collector_get_type (void) G_GNUC_CONST;
+
+/* for logview application internal usage */
+extern GSList* logi_get_logs (LogviewICollector *self);
+extern Log* logi_config_log_from_path (LogviewICollector *self,
+				const gchar *log_name);
+extern gboolean logi_config_log (LogviewICollector *self, Log *log);
+
+G_END_DECLS
+
+#endif /* __LOGVIEW_IFACE_COLLECTOR_H__ */
diff --git a/logview/logview-iface-io.c b/logview/logview-iface-io.c
new file mode 100644
index 0000000..ddfd532
--- /dev/null
+++ b/logview/logview-iface-io.c
@@ -0,0 +1,212 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#include "logview-iface.h"
+#include "logview-iface-io.h"
+#include "logview-debug.h"
+
+static void
+logview_iface_base_init (gpointer gclass)
+{
+	static gboolean initialized = FALSE;
+	if (!initialized) {
+		initialized = TRUE;
+	}
+}
+
+static void
+logview_iface_class_init (gpointer g_class, gpointer g_class_data)
+{
+	LogviewIFaceIO *self = (LogviewIFaceIO *)g_class;
+	self->can_monitor = NULL;
+	self->extract_filepath = NULL;
+	self->has_updated = NULL;
+	self->update = NULL;
+	self->read = NULL;
+	self->seek = NULL;
+	self->tell = NULL;
+	self->get_size = NULL;
+	self->get_modified_time = NULL;
+}
+
+GType
+logview_iface_io_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (LogviewIFaceIO),
+			logview_iface_base_init,   /* base_init */
+			NULL,   /* base_finalize */
+			logview_iface_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			0,
+			0,      /* n_preallocs */
+			NULL    /* instance_init */
+		};
+		type = g_type_register_static (G_TYPE_INTERFACE,
+					       g_intern_static_string ("LogviewIFaceIOType"),
+					       &info, 0);
+		g_type_interface_add_prerequisite (type, LOGVIEW_TYPE_IFACE);
+	}
+	return type;
+}
+
+/**
+ * logi_can_monitor:
+ * @self: a #LogviewIIO instance.
+ * 
+ * Check whether @self can be monitored.
+ * 
+ * Returns: Return TRUE, if it can.
+ **/
+gboolean
+logi_can_monitor (LogviewIIO *self)
+{
+	LogviewIFaceIO *iface = LOGVIEW_GET_IO_INTERFACE (self);
+	LV_MARK;
+	return iface->can_monitor(self);
+}
+
+/**
+ * logi_has_updated:
+ * @self: a #LogviewIIO instance.
+ * 
+ * Check if @self has changed since last reading happens.
+ *
+ * Returns: Return TRUE, if it can.
+ * */
+gboolean
+logi_has_updated (LogviewIIO *self)
+{
+	LV_MARK;
+	return LOGVIEW_GET_IO_INTERFACE (self)->has_updated(self);
+}
+
+/**
+ * logi_has_updated:
+ * @self: a #LogviewIIO instance.
+ * 
+ * Update @self to a new state, do not report True
+ * if call logi_has_updated() any more except a new change is made to @self.
+ */
+void
+logi_update (LogviewIIO *self)
+{
+	LV_MARK;
+	LOGVIEW_GET_IO_INTERFACE (self)->update(self);
+}
+
+/**
+ * logi_read:
+ * @self: a #LogviewIIO instance.
+ * @buffer: a buffer.
+ * @size: the length of the @buffer.
+ * 
+ * read, seek, tell are similiar to Standard C read, fseek, ftell.
+ * whence := SEEK_SET | SEEK_CUR | SEEK_END
+ * returns -1 if met error.
+ * but no errno currently.
+ * 
+ * Returns: Upon successful completion, logi_read() returns the size of
+ * successfully read. If @size is equal to 0, logi_read() returns 0 and
+ * the contents of the @buffer remain unchanged. Otherwise, if a read error
+ * occurs, returns -1.
+ */
+size_t
+logi_read (LogviewIIO *self, void* buffer, size_t size)
+{
+	LV_MARK;
+	return LOGVIEW_GET_IO_INTERFACE (self)->read(self, buffer, size);
+}
+
+/**
+ * logi_seek:
+ * @self: a #LogviewIIO instance.
+ * @offset: a offset.
+ * @whence: like fseek, accept SEEK_SET | SEEK_CUR | SEEK_END.
+ * 
+ * read, seek, tell are similiar to Standard C read, fseek, ftell.
+ * but no errno currently.
+ * 
+ * Returns: -1 if met error.
+ */
+off_t
+logi_seek (LogviewIIO *self, off_t offset, int whence)
+{
+	LV_MARK;
+	return LOGVIEW_GET_IO_INTERFACE (self)->seek(self, offset, whence);
+}
+
+/**
+ * logi_tell:
+ * @self: a #LogviewIIO instance.
+ * 
+ * read, seek, tell are similiar to Standard C read, fseek, ftell.
+ * but no errno currently.
+ * 
+ * Returns: On successful completion, returns the current offset
+ * opposite to the log head; returns -1 if met error.
+ */
+off_t
+logi_tell (LogviewIIO *self)
+{
+	LV_MARK;
+	return LOGVIEW_GET_IO_INTERFACE (self)->tell(self);
+}
+
+/**
+ * logi_get_size:
+ * @self: a #LogviewIIO instance.
+ * 
+ * Depends on logi_update(), report the size in one state of @self.
+ * 
+ * Returns: On successful completion, returns the current size of @self,
+ * else returns 0.
+ **/
+off_t
+logi_get_size (LogviewIIO* self)
+{
+	LV_MARK;
+	return LOGVIEW_GET_IO_INTERFACE (self)->get_size(self);
+}
+
+/**
+ * logi_get_modified_time:
+ * @self: a #LogviewIIO instance.
+ * 
+ * Depends on logi_update(), report the modified time in one state of @self.
+ * 
+ * Returns: On successful completion, returns the current size of @self,
+ * else returns 0.
+ **/
+time_t
+logi_get_modified_time (LogviewIIO* self)
+{
+	LV_MARK;
+	return LOGVIEW_GET_IO_INTERFACE (self)->get_modified_time(self);
+}
+
+/**
+ * logi_extract_filepath:
+ * @self: a #LogviewIIO instance.
+ * @dirname: returns a pointer points to the new alloc buffer
+ * contains the path name of @self.
+ * If passes NULL, the plugin should ignore this argument.
+ * @filename: returns a pointer points to the new alloc buffer
+ * contains the file name of @self.
+ * If passes NULL, the plugin should ignore this argument.
+ * 
+ * Extract the path name and the file name from $self, which will be displayed on
+ * Gnome System Log left tree pane.
+ **/
+void
+logi_extract_filepath (LogviewIIO *self, gchar** dirname, gchar** filename)
+{
+	LV_MARK;
+	LOGVIEW_GET_IO_INTERFACE (self)->extract_filepath(self, dirname, filename);
+}
diff --git a/logview/logview-iface-io.h b/logview/logview-iface-io.h
new file mode 100644
index 0000000..1ae5526
--- /dev/null
+++ b/logview/logview-iface-io.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#ifndef __LOGVIEW_IFACE_IO_H__
+#define __LOGVIEW_IFACE_IO_H__
+
+#include <sys/types.h>
+#include <glib-object.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define LOGVIEW_TYPE_IFACE_IO \
+	(logview_iface_io_get_type ())
+#define LOGVIEW_IFACE_IO(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST ((obj), LOGVIEW_TYPE_IFACE_IO, \
+				     LogviewIIO))
+#define LOGVIEW_IS_IFACE_IO(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), LOGVIEW_TYPE_IFACE_IO))
+#define LOGVIEW_GET_IO_INTERFACE(obj) \
+	(G_TYPE_INSTANCE_GET_INTERFACE ((obj), LOGVIEW_TYPE_IFACE_IO, \
+					LogviewIFaceIO))
+
+typedef struct _LogviewIIO LogviewIIO; /* dummy object */
+typedef struct _LogviewIFaceIO LogviewIFaceIO;
+
+struct _LogviewIFaceIO {
+	GTypeInterface parent;
+
+	gboolean (*can_monitor) (LogviewIIO *self);
+	void (*extract_filepath) (LogviewIIO *self,
+				  gchar** dirname, gchar** filename);
+	gboolean (*has_updated) (LogviewIIO *self);
+	void (*update) (LogviewIIO *self);
+	size_t (*read) (LogviewIIO *self, void* buffer, size_t size);
+	off_t (*seek) (LogviewIIO *self, off_t offset, int whence);
+	off_t (*tell) (LogviewIIO *self);
+	off_t (*get_size) (LogviewIIO* self);
+	time_t (*get_modified_time) (LogviewIIO* self);
+
+};
+
+extern GType logview_iface_io_get_type (void) G_GNUC_CONST;
+
+/* for logview application internal usage */
+extern gboolean logi_can_monitor (LogviewIIO *self);
+extern void logi_extract_filepath (LogviewIIO *self,
+			    gchar** dirname, gchar** filename);
+extern gboolean logi_has_updated (LogviewIIO *self);
+extern void logi_update (LogviewIIO *self);
+extern size_t logi_read (LogviewIIO *self, void* buffer, size_t size);
+extern off_t logi_seek (LogviewIIO *self, off_t offset, int whence);
+extern off_t logi_tell (LogviewIIO *self);
+extern off_t logi_get_size (LogviewIIO* self);
+extern time_t logi_get_modified_time (LogviewIIO* self);
+
+G_END_DECLS
+
+#endif /* __LOGVIEW_IFACE_H__ */
diff --git a/logview/logview-iface-view.c b/logview/logview-iface-view.c
new file mode 100644
index 0000000..5e3b55c
--- /dev/null
+++ b/logview/logview-iface-view.c
@@ -0,0 +1,87 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#include "logview-iface.h"
+#include "logview-iface-view.h"
+#include "logview-debug.h"
+
+static void
+logview_iface_base_init (gpointer gclass)
+{
+	static gboolean initialized = FALSE;
+	if (!initialized) {
+		initialized = TRUE;
+	}
+}
+
+static void
+logview_iface_class_init (gpointer g_class, gpointer g_class_data)
+{
+	LogviewIFaceView *self = (LogviewIFaceView *)g_class;
+	self->group_lines = NULL;
+	self->to_utf8 = NULL;
+}
+
+GType
+logview_iface_view_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (LogviewIFaceView),
+			logview_iface_base_init,   /* base_init */
+			NULL,   /* base_finalize */
+			logview_iface_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			0,
+			0,      /* n_preallocs */
+			NULL    /* instance_init */
+		};
+		type = g_type_register_static (G_TYPE_INTERFACE,
+					       g_intern_static_string ("LogviewIFaceViewType"),
+					       &info, 0);
+		g_type_interface_add_prerequisite (type, LOGVIEW_TYPE_IFACE);
+	}
+	return type;
+}
+
+/**
+ * logi_group_lines:
+ * @self: a #LogviewIView instance.
+ * @lines: an array of strings, terminates in NULL.
+ * 
+ * Passes an array of strings to logi_group_lines(),
+ * the plugin should try to group these strings by date.
+ * 
+ * Returns: a single list with each element is a #Day object.
+ **/
+GSList*
+logi_group_lines (LogviewIView *self, const gchar **lines)
+{
+	LV_MARK;
+	return LOGVIEW_GET_VIEW_INTERFACE (self)->group_lines(self, lines);
+}
+
+/**
+ * logi_to_utf8:
+ * @self: a #LogviewIView instance.
+ * @str: a string.
+ * @size: the length of @str.
+ * 
+ * Gnome System Log will call this routine before displays the string on the screen.
+ * 
+ * Returns: a converted string, which will be freed by Gnome System Log.
+ **/
+gchar*
+logi_to_utf8 (LogviewIView *self, gchar *str, gssize size)
+{
+	LogviewIFaceView *iface = LOGVIEW_GET_VIEW_INTERFACE (self);
+	LV_MARK;
+	if (iface->to_utf8)
+		return iface->to_utf8 (self, str, size);
+	return str;
+}
diff --git a/logview/logview-iface-view.h b/logview/logview-iface-view.h
new file mode 100644
index 0000000..b32330b
--- /dev/null
+++ b/logview/logview-iface-view.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#ifndef __LOGVIEW_IFACE_VIEW_H__
+#define __LOGVIEW_IFACE_VIEW_H__
+
+#include <glib-object.h>
+#include "logview-log.h"
+
+G_BEGIN_DECLS
+
+#define LOGVIEW_TYPE_IFACE_VIEW \
+	(logview_iface_view_get_type ())
+#define LOGVIEW_IFACE_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj), LOGVIEW_TYPE_IFACE_VIEW, \
+				     LogviewIView))
+#define LOGVIEW_IS_IFACE_VIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj), LOGVIEW_TYPE_IFACE_VIEW))
+#define LOGVIEW_GET_VIEW_INTERFACE(obj) \
+	(G_TYPE_INSTANCE_GET_INTERFACE((obj), LOGVIEW_TYPE_IFACE_VIEW, \
+					LogviewIFaceView))
+
+typedef struct _LogviewIView LogviewIView;
+typedef struct _LogviewIFaceView LogviewIFaceView;
+
+struct _LogviewIFaceView {
+	GTypeInterface parent;
+
+	/* 
+	   Create a list, each element is a group entry,
+	   each group entry has a log list, do not free mass.
+	   returns NULL if failed.
+	*/
+	GSList* (*group_lines) (LogviewIView *self, const gchar **lines);
+
+	/* 
+	   If possible, convert a string to utf8, free the original
+	   string, return the new one.
+	*/
+	gchar* (*to_utf8) (LogviewIView *self, gchar *str, gssize size);
+};
+
+extern GType logview_iface_view_get_type (void) G_GNUC_CONST;
+
+/* for logview application internal usage */
+extern GSList* logi_group_lines (LogviewIView *self, const gchar **lines);
+extern gchar* logi_to_utf8 (LogviewIView *self, gchar *str, gssize size);
+
+G_END_DECLS
+
+#endif /* __LOGVIEW_IFACE_VIEW_H__ */
+
diff --git a/logview/logview-iface.c b/logview/logview-iface.c
new file mode 100644
index 0000000..865073a
--- /dev/null
+++ b/logview/logview-iface.c
@@ -0,0 +1,69 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+ 
+#include "logview-iface.h"
+#include "logview-debug.h"
+
+static void
+logview_iface_base_init (gpointer gclass)
+{
+	static gboolean initialized = FALSE;
+	if (!initialized) {
+		initialized = TRUE;
+	}
+}
+
+static void
+logview_iface_class_init (gpointer g_class, gpointer g_class_data)
+{
+	LogviewInterface *self = (LogviewInterface *)g_class;
+	self->can_handle = NULL;
+}
+
+GType
+logview_iface_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (LogviewInterface),
+			logview_iface_base_init,   /* base_init */
+			NULL,   /* base_finalize */
+			logview_iface_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			0,
+			0,      /* n_preallocs */
+			NULL    /* instance_init */
+		};
+		type = g_type_register_static (G_TYPE_INTERFACE,
+					       g_intern_static_string ("LogviewInterfaceType"),
+					       &info, 0);
+	}
+	return type;
+}
+
+/**
+ * logi_can_handle:
+ * @self: a #LogviewIFace instance.
+ * @log: a #Log instance.
+ * 
+ * Check whether the @log can be handled.
+ * 
+ * Returns: If this interface is not overwritten,
+ * it default returns TRUE means the @log can be recognized.
+ **/
+gboolean
+logi_can_handle (LogviewIFace *self, struct _Log* log)
+{
+	LogviewInterface *iface = LOGVIEW_GET_INTERFACE (self);
+	//LV_MARK;
+	if (iface->can_handle)
+		return (iface)->can_handle(self, log);
+	return TRUE;
+}
+
diff --git a/logview/logview-iface.h b/logview/logview-iface.h
new file mode 100644
index 0000000..cb8b2b8
--- /dev/null
+++ b/logview/logview-iface.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#ifndef __LOGVIEW_IFACE_H__
+#define __LOGVIEW_IFACE_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define LOGVIEW_TYPE_IFACE \
+	(logview_iface_get_type ())
+#define LOGVIEW_IFACE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj), LOGVIEW_TYPE_IFACE, \
+				     LogviewIFace))
+#define LOGVIEW_IS_IFACE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj), LOGVIEW_TYPE_IFACE))
+#define LOGVIEW_GET_INTERFACE(obj) \
+	(G_TYPE_INSTANCE_GET_INTERFACE((obj), LOGVIEW_TYPE_IFACE, \
+					LogviewInterface))
+
+struct _Log;
+
+typedef struct _LogviewIFace LogviewIFace; /* dummy object */
+typedef struct _LogviewInterface LogviewInterface;
+
+struct _LogviewInterface {
+	GTypeInterface parent;
+
+	gboolean (*can_handle) (LogviewIFace *self, struct _Log* log);
+};
+
+GType logview_iface_get_type (void);
+
+/* for logview application internal usage */
+gboolean logi_can_handle (LogviewIFace *self, struct _Log* log);
+
+G_END_DECLS
+
+#endif /* __LOGVIEW_IFACE_H__ */
+
diff --git a/logview/logview-log.h b/logview/logview-log.h
new file mode 100644
index 0000000..cadd1e8
--- /dev/null
+++ b/logview/logview-log.h
@@ -0,0 +1,207 @@
+/*  ----------------------------------------------------------------------
+
+    Copyright (C) 1998  Cesar Miquel  (miquel@df.uba.ar)
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    ---------------------------------------------------------------------- */
+
+#ifndef __LOGVIEW_LOG_H__
+#define __LOGVIEW_LOG_H__
+
+#include <time.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+#include "logview-plugin.h"
+#include "logview-iface.h"
+
+G_BEGIN_DECLS
+
+#define LOGVIEW_TYPE_LOG		(log_get_type ())
+#define LOGVIEW_LOG(obj)		(G_TYPE_CHECK_INSTANCE_CAST((obj), LOGVIEW_TYPE_LOG, Log))
+#define LOGVIEW_LOG_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST((klass), LOGVIEW_TYPE_LOG, LogClass))
+#define LOGVIEW_IS_LOG(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj), LOGVIEW_TYPE_LOG))
+#define LOGVIEW_IS_LOG_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), LOGVIEW_TYPE_LOG))
+#define LOGVIEW_LOG_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), LOGVIEW_TYPE_LOG, LogClass))
+
+typedef struct _Log Log;
+typedef struct _LogClass LogClass;
+typedef struct _LogPrivate LogPrivate;
+
+struct _Log
+{
+	GObject parent;
+	/*< private >*/
+	LogPrivate *prv;
+};
+
+struct _LogClass {
+	GObjectClass parent;
+};
+
+/**
+ * LogProtocol:
+ * @UNKNOWN_LOG: represents the log can't be recognized.
+ * But still has the opportunity to be handled by other plugins.
+ * @BAD_LOG: represents the log is not a log or a bad log.
+ * If in one time a log is marked with this, it's permanently 
+ * ignored and removed from the log name list.
+ * @PLAIN_LOG: a ASCII log.
+ * @GZIP_LOG: a zipped or gzipped log.
+ * @PIPE_LOG: a log can be open by other command.
+ * @USER_DEFINED_LOG: a plugin can define a log protocol for itself, 
+ * e.g. %USER_DEFINED_LOG + 1.
+ */
+typedef enum {
+	UNKNOWN_LOG=0,
+	BAD_LOG,
+	PLAIN_LOG,
+	GZIP_LOG,
+	PIPE_LOG,
+	USER_DEFINED_LOG
+} LogProtocol;
+
+typedef enum {
+	TH_EVENT_EMPTY		= 0,
+	TH_EVENT_INIT_READ	= 1<<1,
+	TH_EVENT_UPDATE		= 1<<2,
+	TH_EVENT_EXIT		= 1<<8
+} ThreadEvent;
+
+/**
+ * Day:
+ * @date: a date, without time value.
+ * @first_line: First line for this day in the log.
+ * @last_line: Last line for this day in the log.
+ * @expand: Collapse all the logs in this @date if %FALSE, else expand all.
+ * @path: Gtk tree path of the day item display in main view of the log.
+ * 
+ * A structure used for grouping and recording the logs by date.
+ */
+typedef struct _Day
+{
+    GDate *date;
+    long first_line, last_line;
+    gboolean expand;
+    GtkTreePath *path;
+} Day;
+
+#define OLD_LOG_NUM	5
+
+/**
+ * log_io:
+ * @log: a #Log instance.
+ * 
+ * Returns: the %PF_LOG_IO type plugin.
+ */
+#define log_io(log)		(LOGVIEW_IFACE_IO (log->prv->ext_data [PF_LOG_IO]))
+
+/**
+ * log_view:
+ * @log: a #Log instance.
+ * 
+ * Returns: the %PF_LOG_VIEW type plugin.
+ */
+#define log_view(log)		(LOGVIEW_IFACE_VIEW (log->prv->ext_data [PF_LOG_VIEW]))
+
+/**
+ * log_name:
+ * @self: a #Log instance.
+ * 
+ * Get the log name.
+ * 
+ * Returns: a constant string which should not be free.
+ */
+
+/**
+ * log_get_protocol:
+ * @self: a #Log instance.
+ * 
+ * Get the #LogProtocol value of self's.
+ * 
+ * Returns: the #LogProtocol value.
+ */
+
+/**
+ * log_set_protocol:
+ * @self: a #Log instance.
+ * @protocol: a #LogProtocol value.
+ * 
+ * Set the #LogProtocol value of self's.
+ */
+
+/**
+ * is_log:
+ * @log: a #Log instance.
+ * 
+ * Determining whether @log is a #Log instance.
+ * 
+ * Returns: TRUE if it is.
+ */
+
+
+/**
+ * log_set_child:
+ * @self: a #Log instance.
+ * @index: the child log id. it should be no less than zero and less #OLD_LOG_NUM
+ * @child: a #Log instance.
+ * 
+ * Access child log instances.
+ */
+void log_set_child (Log *self, gint index, Log *child);
+
+/**
+ * log_get_child:
+ * @self: a #Log instance.
+ * @index: the child log id. it should be no less than zero and less #OLD_LOG_NUM
+ * 
+ * Access child log instances.
+ * 
+ * Returns: the child #Log instance.
+ */
+Log* log_get_child (Log *self, gint index);
+
+/**
+ * log_test_func:
+ * @self: a #Log instance.
+ * @pf: a #PluginFunc index.
+ * 
+ * Returns: if a #pf type plugin is assigned to #self, returns TRUE.
+ */
+gboolean log_test_func (Log *self, PluginFunc pf);
+
+/**
+ * log_set_func:
+ * @self: a #Log instance.
+ * @pf: a #PluginFunc index.
+ * @plugin: a #LogviewPlugin instance.
+ * 
+ * Assign a #pf type plugin to #self.
+ */
+void log_set_func (Log *self, PluginFunc pf, LogviewPlugin *plugin);
+
+void log_notify (Log *self, ThreadEvent te);
+gchar *log_read (Log *self);
+void log_write_line (Log *self, gchar *line);
+void log_write_lines (Log *self, gchar **lines, gint num);
+void log_write_lines_range (Log *self, gchar **lines, gint start, gint end);
+void log_write_date (Log *self, GDate *date);
+void log_write_day (Log *self, Day *day);
+
+extern GType log_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __LOGVIEW_LOG_H__ */
diff --git a/logview/logview-module.c b/logview/logview-module.c
new file mode 100644
index 0000000..a231ec0
--- /dev/null
+++ b/logview/logview-module.c
@@ -0,0 +1,160 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+ 
+#include "logview-module.h"
+#include "logview-debug.h"
+
+/* default routines for implement the plugin interfaces */
+static gboolean logview_module_load (LogviewModule* self);
+static void logview_module_unload (LogviewModule* self);
+static void logview_module_class_init (gpointer g_class, gpointer g_class_data);
+static void logview_module_instance_init (GTypeInstance *instance, gpointer g_class);
+
+static GObjectClass *parent_class = NULL;
+
+struct _LogviewModulePrivate {
+	GModule *handle;
+	GType type;
+	LogviewPluginInfo *info;
+};
+
+static void
+module_finalize (LogviewModule* self)
+{
+	g_free (self->prv);
+	parent_class->finalize (G_OBJECT (self));
+}
+
+static void
+logview_module_class_init (gpointer g_class, gpointer g_class_data)
+{
+	GObjectClass *object_class;
+	GTypeModuleClass *self;
+
+	object_class = G_OBJECT_CLASS (g_class);
+	self = G_TYPE_MODULE_CLASS (g_class);
+	parent_class = g_type_class_peek_parent (g_class);
+	object_class->finalize = (void (*)(GObject*)) module_finalize;
+
+	self->load = (gboolean (*)(GTypeModule*)) logview_module_load;
+	self->unload = (void (*)(GTypeModule*)) logview_module_unload;
+}
+
+static void
+logview_module_instance_init (GTypeInstance *instance, gpointer g_class)
+{
+	LogviewModule *self = LOGVIEW_MODULE (instance);
+	self->prv = g_new0 (LogviewModulePrivate, 1);
+}
+
+GType
+logview_module_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (LogviewModuleClass),
+			NULL,   /* base_init */
+			NULL,   /* base_finalize */
+			logview_module_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			sizeof (LogviewModule),
+			0,      /* n_preallocs */
+			logview_module_instance_init    /* instance_init */
+		};
+		type = g_type_register_static (G_TYPE_TYPE_MODULE, "LogviewModuleType", &info, 0);
+	}
+	return type;
+}
+
+LogviewModule*
+logview_new_module (const gchar* module_path)
+{
+	LogviewModule* new_module = NULL;
+
+	LV_MARK;
+	g_assert (module_path != NULL);
+	new_module = g_object_new (LOGVIEW_TYPE_MODULE, NULL);
+	g_type_module_set_name (G_TYPE_MODULE (new_module), module_path);
+	return new_module;
+}
+
+LogviewPlugin*
+logview_module_new_plugin (const LogviewModule* self)
+{
+	LogviewPlugin *plugin;
+	LV_MARK;
+	g_assert (self->prv->type != 0);
+	plugin = g_object_new (self->prv->type, NULL);
+	g_assert (LOGVIEW_IS_PLUGIN(plugin));
+	return LOGVIEW_PLUGIN (plugin);
+}
+
+static gboolean
+logview_module_load (LogviewModule* self)
+{
+	GType (*register_logview_plugin)(GTypeModule*);
+	LogviewPluginInfo *(*logview_plugin_info)();
+
+	LV_MARK;
+
+	self->prv->handle = g_module_open (logview_module_name (self), G_MODULE_BIND_LOCAL);
+	if (self->prv->handle !=NULL) {
+		gboolean hit;
+		hit = g_module_symbol (self->prv->handle, "register_logview_plugin", (gpointer*)(&register_logview_plugin));
+		g_return_val_if_fail (hit != FALSE, FALSE);
+		hit = g_module_symbol (self->prv->handle, "logview_plugin_info", (gpointer*)(&logview_plugin_info));
+		g_return_val_if_fail (hit != FALSE, FALSE);
+
+		self->prv->type = (*register_logview_plugin) ((GTypeModule*) self);
+		self->prv->info = (*logview_plugin_info) ();
+		
+		g_return_val_if_fail (self->prv->type != 0, FALSE);
+		g_return_val_if_fail (self->prv->info != NULL, FALSE);
+		return TRUE;
+	}
+	else {
+		LV_ERR ("logview_module_load error: %s", g_module_error());
+	}
+	return FALSE;
+}
+
+static void
+logview_module_unload (LogviewModule* self)
+{
+	LV_MARK;
+	g_module_close (self->prv->handle);
+	self->prv->handle = NULL;
+	self->prv->type = 0;
+	self->prv->info = NULL;
+}
+
+const gchar*
+logview_module_name (LogviewModule *self)
+{
+	g_assert (G_IS_TYPE_MODULE (self));
+	return G_TYPE_MODULE (self)->name;
+}
+
+GType
+logview_module_type (LogviewModule *self)
+{
+	return self->prv->type;
+}
+
+PluginPrio
+logview_module_priority (LogviewModule *self)
+{
+	return self->prv->info->prio;
+}
+
+const gchar*
+logview_module_desc (LogviewModule *self)
+{
+	return self->prv->info->desc;
+}
diff --git a/logview/logview-module.h b/logview/logview-module.h
new file mode 100644
index 0000000..52fe388
--- /dev/null
+++ b/logview/logview-module.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#ifndef __LOGVIEW_MODULE_H__
+#define __LOGVIEW_MODULE_H__
+
+#include <glib-object.h>
+#include <gmodule.h>
+#include "logview-plugin.h"
+
+#define LOGVIEW_TYPE_MODULE		(logview_module_get_type ())
+#define LOGVIEW_MODULE(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), LOGVIEW_TYPE_MODULE, LogviewModule))
+#define LOGVIEW_MODULE_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), LOGVIEW_TYPE_MODULE, LogviewModuleClass))
+#define LOGVIEW_IS_MODULE(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), LOGVIEW_TYPE_MODULE))
+#define LOGVIEW_IS_MODULE_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), LOGVIEW_TYPE_MODULE))
+#define LOGVIEW_MODULE_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), LOGVIEW_TYPE_MODULE, LogviewModuleClass))
+
+typedef struct _LogviewModule LogviewModule;
+typedef struct _LogviewModuleClass LogviewModuleClass;
+typedef struct _LogviewModulePrivate LogviewModulePrivate;
+
+struct _LogviewModule{
+	GTypeModule parent;
+	/* instance members */
+/* private */
+	LogviewModulePrivate *prv;
+};
+
+struct _LogviewModuleClass {
+	GTypeModuleClass parent;
+	/* class members */
+};
+
+
+/* For the core application which should implemete the level to call plugins */
+GType logview_module_get_type (void);
+/* return the name of the module, should not be freed */
+const gchar* logview_module_name (LogviewModule *self);
+GType logview_module_type (LogviewModule *self);
+PluginPrio logview_module_priority (LogviewModule *self);
+const gchar* logview_module_desc (LogviewModule *self);
+/* return a new module */
+LogviewModule* logview_new_module (const gchar* module_path);
+/* return a new plugin */
+LogviewPlugin* logview_module_new_plugin (const LogviewModule* self);
+
+#endif /* __LOGVIEW_MODULE_H__ */
+
diff --git a/logview/logview-plugin-list.c b/logview/logview-plugin-list.c
new file mode 100644
index 0000000..3b79b02
--- /dev/null
+++ b/logview/logview-plugin-list.c
@@ -0,0 +1,280 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include "logview-plugin-list.h"
+#include "logview-plugin-manager.h"
+#include "logview-module.h"
+#include "misc.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+static void logview_plugin_list_init (LogviewPluginList *plugin_list);
+
+#define GLADE_HOOKUP_OBJECT(component,widget,name) \
+  g_object_set_data_full (G_OBJECT (component), name, \
+    gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref)
+
+#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \
+  g_object_set_data (G_OBJECT (component), name, widget)
+
+enum {
+	NAME_COL,
+	PRI_COL,
+	DESC_COL,
+	PATH_COL,
+	N_COLS
+};
+
+static void
+logview_plugin_list_sync_cb (const LogviewModule *module, gpointer data)
+{
+	GtkTreeIter iter;
+	gchar *name, *desc, *path;
+	gint pri;
+	LogviewPluginList *self = (LogviewPluginList*)data;
+
+	path = logview_module_name (module);
+	pri = logview_module_priority (module);
+	desc = logview_module_desc (module);
+	name = log_extract_filename (path);
+
+	gtk_tree_store_append (self->list, &iter, NULL);
+	gtk_tree_store_set (self->list, &iter,
+			    NAME_COL, name,
+			    PRI_COL, pri,
+			    DESC_COL, desc,
+			    PATH_COL, path,
+			    -1);
+	g_free (name);
+}
+
+static void
+logview_plugin_list_sync (LogviewPluginList *self)
+{
+	gtk_widget_hide_all (self->scrolledwindow);
+	gtk_widget_hide_all (self->label);
+	gtk_tree_store_clear (self->list);
+	if (pluginmgr_plugin_amount () > 0) {
+		pluginmgr_modules_iterate (logview_plugin_list_sync_cb, self);
+		gtk_widget_show_all (self->scrolledwindow);
+	}
+	else
+		gtk_widget_show_all (self->label);
+
+}
+
+void
+logview_plugin_list_show (LogviewPluginList *self, GtkWidget *window)
+{
+	g_return_if_fail (GTK_IS_WINDOW (window));
+	gtk_widget_show_all (self->dialog);
+	logview_plugin_list_sync (self);
+}
+
+static gint
+logview_plugin_list_cmp (GtkTreeModel *model,
+			    GtkTreeIter *a,
+			    GtkTreeIter *b,
+			    gpointer user_data)
+{
+	gint col_id = GPOINTER_TO_INT(user_data);
+	gint ret;
+
+	switch (col_id) {
+	case PATH_COL:
+	case NAME_COL: {
+		gchar *stra, *strb;
+		gtk_tree_model_get (model, a, (gint)user_data, &stra, -1);
+		gtk_tree_model_get (model, b, (gint)user_data, &strb, -1);
+		ret = g_ascii_strcasecmp (stra, strb);
+		g_free (stra);
+		g_free (strb);
+	}
+		break;
+	case PRI_COL: {
+		gint ia, ib;
+		gtk_tree_model_get (model, a, (gint)user_data, &ia, -1);
+		gtk_tree_model_get (model, b, (gint)user_data, &ib, -1);
+		ret = ia - ib;
+	}
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+	return ret;
+}
+
+static void
+logview_plugin_list_init (LogviewPluginList *self)
+{
+	GtkWidget *dialog_vbox3;
+	GtkWidget *dialog_action_area3;
+	GtkWidget *exist_button;
+	GtkTreeViewColumn *name_col, *pri_col, *path_col, *desc_col;
+	GtkCellRenderer *text_renderer;
+	GtkWidget *view;
+
+	self->list = gtk_tree_store_new (N_COLS,
+					 G_TYPE_STRING,
+					 G_TYPE_INT,
+					 G_TYPE_STRING,
+					 G_TYPE_STRING);
+	text_renderer = gtk_cell_renderer_text_new ();
+
+	view = g_object_new (GTK_TYPE_TREE_VIEW,
+			     "model", self->list,
+			     "rules-hint", TRUE,
+			     "headers-clickable", TRUE,
+			     "reorderable", TRUE,
+			     NULL);
+
+	name_col = gtk_tree_view_column_new_with_attributes (_("Plugin Name"),
+							     text_renderer,
+							     "text", NAME_COL,
+							     NULL);
+	gtk_tree_view_column_set_sort_column_id (name_col, NAME_COL);
+
+	g_object_set (name_col,
+		      "resizable", TRUE,
+		      "clickable", TRUE,
+		      "sort-indicator", TRUE,
+		      "reorderable", TRUE,
+		      NULL);
+	pri_col = gtk_tree_view_column_new_with_attributes (_("Priority"),
+							     text_renderer,
+							     "text", PRI_COL,
+							     NULL);
+	gtk_tree_view_column_set_sort_column_id (pri_col, PRI_COL);
+
+	g_object_set (pri_col,
+		      "resizable", TRUE,
+		      "clickable", TRUE,
+		      "sort-indicator", TRUE,
+		      "reorderable", TRUE,
+		      NULL);
+	desc_col = gtk_tree_view_column_new_with_attributes (_("Description"),
+							     text_renderer,
+							     "text", DESC_COL,
+							     NULL);
+	g_object_set (desc_col,
+		      "resizable", TRUE,
+		      "reorderable", TRUE,
+		      NULL);
+
+	path_col = gtk_tree_view_column_new_with_attributes (_("Path"),
+							     text_renderer,
+							     "text", PATH_COL,
+							     NULL);
+	g_object_set (path_col,
+		      "resizable", TRUE,
+		      "clickable", TRUE,
+		      "sort-indicator", TRUE,
+		      "reorderable", TRUE,
+		      NULL);
+	gtk_tree_view_column_set_sort_column_id (path_col, PATH_COL);
+
+	gtk_tree_sortable_set_sort_func	(GTK_TREE_SORTABLE(self->list),
+					 NAME_COL,
+					 (GtkTreeIterCompareFunc) logview_plugin_list_cmp,
+					 GINT_TO_POINTER(NAME_COL),
+					 NULL);
+	gtk_tree_sortable_set_sort_func	(GTK_TREE_SORTABLE(self->list),
+					 PATH_COL,
+					 (GtkTreeIterCompareFunc) logview_plugin_list_cmp,
+					 GINT_TO_POINTER(PATH_COL),
+					 NULL);
+	gtk_tree_sortable_set_sort_func	(GTK_TREE_SORTABLE(self->list),
+					 PRI_COL,
+					 (GtkTreeIterCompareFunc) logview_plugin_list_cmp,
+					 GINT_TO_POINTER(PRI_COL),
+					 NULL);
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(self->list), NAME_COL, GTK_SORT_ASCENDING);
+
+	gtk_tree_view_append_column (view, name_col);
+	gtk_tree_view_append_column (view, pri_col);
+	gtk_tree_view_append_column (view, desc_col);
+	gtk_tree_view_append_column (view, path_col);
+
+	self->dialog = gtk_dialog_new ();
+	gtk_dialog_set_has_separator (GTK_DIALOG (self->dialog), FALSE);
+	gtk_widget_set_size_request (self->dialog, 800, 400);
+	gtk_window_set_destroy_with_parent (GTK_WINDOW (self->dialog), TRUE);
+	gtk_window_set_title (GTK_WINDOW (self->dialog), _("Log Viewer Plugins"));
+	gtk_window_set_position (GTK_WINDOW (self->dialog),
+				 GTK_WIN_POS_CENTER_ON_PARENT);
+	gtk_window_set_modal (GTK_WINDOW (self->dialog), TRUE);
+	gtk_window_set_resizable (GTK_WINDOW (self->dialog), TRUE);
+	gtk_window_set_type_hint (GTK_WINDOW (self->dialog),
+				  GDK_WINDOW_TYPE_HINT_DIALOG);
+	gtk_window_set_skip_taskbar_hint (GTK_WINDOW (self->dialog), TRUE);
+
+	dialog_vbox3 = GTK_DIALOG (self->dialog)->vbox;
+
+	self->label = gtk_label_new (_("No plugins installed!"));
+	gtk_widget_show (self->label);
+	gtk_box_pack_start (GTK_BOX (dialog_vbox3), self->label, TRUE, TRUE, 0);
+
+	self->scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
+	gtk_box_pack_start (GTK_BOX (dialog_vbox3), self->scrolledwindow,
+			    TRUE, TRUE, 0);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self->scrolledwindow),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (self->scrolledwindow),
+					     GTK_SHADOW_NONE);
+	gtk_container_add (GTK_CONTAINER (self->scrolledwindow), view);
+
+	dialog_action_area3 = GTK_DIALOG (self->dialog)->action_area;
+	gtk_container_set_border_width (GTK_CONTAINER (self->scrolledwindow), 12);
+	gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area3),
+				   GTK_BUTTONBOX_END);
+
+	exist_button = gtk_button_new_from_stock ("gtk-close");
+	gtk_dialog_add_action_widget (GTK_DIALOG (self->dialog),
+				      exist_button, GTK_RESPONSE_CLOSE);
+	GTK_WIDGET_SET_FLAGS (exist_button, GTK_CAN_DEFAULT);
+
+	/* Store pointers to all widgets, for use by lookup_widget(). */
+	GLADE_HOOKUP_OBJECT_NO_REF (self->dialog, dialog_vbox3, "dialog_vbox3");
+	GLADE_HOOKUP_OBJECT (self->dialog, self->scrolledwindow, "scrolledwindow");
+	GLADE_HOOKUP_OBJECT (self->dialog, view, "treeview1");
+	GLADE_HOOKUP_OBJECT_NO_REF (self->dialog, dialog_action_area3, "dialog_action_area3");
+	GLADE_HOOKUP_OBJECT (self->dialog, exist_button, "exist_button");
+	GLADE_HOOKUP_OBJECT (self->dialog, self->label, "label1");
+
+	g_signal_connect_swapped (exist_button, "clicked",
+				  G_CALLBACK(gtk_widget_hide_all),
+				  self->dialog);
+	g_signal_connect_swapped (self->dialog, "delete-event",
+				  G_CALLBACK(gtk_widget_hide_on_delete),
+				  self->dialog);
+
+}
+
+LogviewPluginList *
+logview_plugin_list_new (void)
+{
+	LogviewPluginList * list;
+	list = g_new0 (LogviewPluginList, 1);
+	logview_plugin_list_init (list);
+	return list;
+}
+
+void
+logview_plugin_list_delete (LogviewPluginList * list)
+{
+	g_free (list);
+}
diff --git a/logview/logview-plugin-list.h b/logview/logview-plugin-list.h
new file mode 100644
index 0000000..64a0d98
--- /dev/null
+++ b/logview/logview-plugin-list.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#ifndef __LOGVIEW_PLUGIN_LIST_H__
+#define __LOGVIEW_PLUGIN_LIST_H__
+
+#include <gtk/gtk.h>
+
+typedef struct _LogviewPluginList LogviewPluginList;
+struct _LogviewPluginList {
+	GtkTreeStore *list;
+	GtkWidget *scrolledwindow;
+	GtkWidget *label;
+	GtkWidget *dialog;
+};
+
+void logview_plugin_list_show (LogviewPluginList *self, GtkWidget *window);
+LogviewPluginList *logview_plugin_list_new (void);
+void logview_plugin_list_delete (LogviewPluginList *);
+
+#endif /* __LOGVIEW_PLUGIN_LIST_H__ */
diff --git a/logview/logview-plugin-manager.c b/logview/logview-plugin-manager.c
new file mode 100644
index 0000000..58cf886
--- /dev/null
+++ b/logview/logview-plugin-manager.c
@@ -0,0 +1,529 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+ 
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/utsname.h>
+#include "logview-module.h"
+#include "logview-plugin-manager.h"
+#include "logview-iface-io.h"
+#include "logview-iface-view.h"
+#include "logview-iface-collector.h"
+#include "logview-debug.h"
+#include "misc.h"
+
+#define LOGVIEW_TYPE_PLUGINMGR		(logview_pluginmgr_get_type ())
+#define LOGVIEW_PLUGINMGR(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), LOGVIEW_TYPE_PLUGINMGR, LogviewPluginMgr))
+#define LOGVIEW_PLUGINMGR_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), LOGVIEW_TYPE_PLUGINMGR, LogviewPluginMgrClass))
+#define LOGVIEW_IS_PLUGINMGR(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), LOGVIEW_TYPE_PLUGINMGR))
+#define LOGVIEW_IS_PLUGINMGR_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), LOGVIEW_TYPE_PLUGINMGR))
+#define LOGVIEW_PLUGINMGR_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), LOGVIEW_TYPE_PLUGINMGR, LogviewPluginMgrClass))
+
+static GType logview_pluginmgr_get_type (void);
+static void pluginmgr_class_init (gpointer g_class, gpointer g_class_data);
+static void pluginmgr_instance_init (GTypeInstance *instance, gpointer g_class);
+
+static gboolean load_module(LogviewPluginMgr *self, const gchar* module_path);
+static void unload_module (LogviewPluginMgr *self, LogviewModule* plugin_info);
+static gboolean load_modules (LogviewPluginMgr *self);
+static void unload_modules (LogviewPluginMgr *self);
+
+static gboolean register_module (LogviewPluginMgr *self, LogviewModule* plugin_info);
+static gboolean is_plugin_file (const gchar* filepath);
+static Log* pluginmgr_new_log (Log *log);
+/*-------------------------------------------------------------------------------*/
+static gint module_prio_cmpfunc (gconstpointer a, gconstpointer b, gpointer user_data);
+static gint module_cmpfunc (gconstpointer a, gconstpointer b);
+
+static GObjectClass *parent_class = NULL;
+static LogviewPluginMgr *pluginmgr_instance = NULL;
+
+struct _LogviewPluginMgrPrivate {
+	GType typehash[MGR_PF_NUM];
+	GSList *all_modules;
+	GSList *modules[MGR_PF_NUM];
+	GSList *plugin_full_paths;
+};
+
+static GType
+logview_pluginmgr_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (LogviewPluginMgrClass),
+			NULL,   /* base_init */
+			NULL,   /* base_finalize */
+			pluginmgr_class_init,	/* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			sizeof (LogviewPluginMgr),
+			0,      /* n_preallocs */
+			pluginmgr_instance_init    /* instance_init */
+		};
+		type = g_type_register_static (G_TYPE_OBJECT,
+					       "LogviewPluginMgrType",
+					       &info, 0);
+	}
+	return type;
+}
+
+static void
+pluginmgr_instance_init (GTypeInstance *instance, gpointer g_class)
+{
+	LogviewPluginMgr *self = LOGVIEW_PLUGINMGR (instance);
+	self->prv = g_new0 (LogviewPluginMgrPrivate,1);
+	self->prv->typehash[PF_LOG_IO] = LOGVIEW_TYPE_IFACE_IO;
+	self->prv->typehash[PF_LOG_VIEW] = LOGVIEW_TYPE_IFACE_VIEW;
+	self->prv->typehash[PF_LOG_COLLECTOR] = LOGVIEW_TYPE_IFACE_COLLECTOR;
+}
+
+static void
+pluginmgr_finalize (LogviewPluginMgr *self)
+{
+	GSList *idx;
+
+	LV_MARK;
+	
+	LOGVIEW_PLUGINMGR_GET_CLASS (self)->unload_modules (self);
+
+	if (self->prv->plugin_full_paths) {
+		for (idx = self->prv->plugin_full_paths; idx; idx = g_slist_next(idx)) {
+			gchar *path = (gchar *)idx->data;
+			g_free (path);
+		}
+		g_slist_free (self->prv->plugin_full_paths);
+	}
+
+	g_free (self->prv);
+	parent_class->finalize (G_OBJECT (self));
+}
+
+static gboolean
+load_modules (LogviewPluginMgr *self)
+{
+	LogviewPluginMgrClass* klass;
+	GSList* plugin_paths;
+	GSList* idx;
+
+	LV_MARK;
+	if (!g_module_supported())
+		return FALSE;
+	
+	klass = LOGVIEW_PLUGINMGR_GET_CLASS(self);
+	plugin_paths = get_plugin_paths();
+
+	g_return_val_if_fail (plugin_paths != NULL, FALSE);
+
+	for (idx = plugin_paths; idx; idx = idx->next) {
+		/* load plugins */
+		GDir *dir;
+		gchar* plugin_full_path;
+		gchar* search_path = idx->data;
+		
+		dir = g_dir_open (search_path, 0, NULL);
+		if (dir != NULL) {
+			while ((plugin_full_path = (gchar *) g_dir_read_name (dir)) != NULL) {
+				plugin_full_path = g_build_filename (search_path, plugin_full_path, NULL);
+				if (klass->load_module (self, plugin_full_path)) {
+					LV_INFO ("module %s loaded successfully",
+						 plugin_full_path);
+				}
+				g_free (plugin_full_path);
+			}
+			g_dir_close (dir);
+		}
+		else
+			return FALSE;
+	}
+	return TRUE;
+}
+
+/* load all modules in the paths */
+static gboolean
+load_module (LogviewPluginMgr *self, const gchar* module_path)
+{
+	LogviewModule* module = NULL;
+
+	LV_MARK;
+	if (!is_plugin_file (module_path)) {
+		return FALSE;
+	}
+
+	module = logview_new_module (module_path);
+	g_assert (module != NULL);
+	
+	if (!g_type_module_use ((GTypeModule*) module)) {
+		LV_INFO ("module %s loaded failed: %s",
+			 logview_module_name (module), g_module_error ());
+		g_object_unref (module);
+		return FALSE;
+	}
+
+	if (register_module (self, module))
+		return TRUE;
+	else {
+		g_type_module_unuse ((GTypeModule*) module);
+		return FALSE;
+	}	
+}
+
+/* destroy all modules in the paths
+ * must be called after destroy all plugin instances
+ */
+static void
+unload_modules(LogviewPluginMgr *self)
+{
+	PluginFunc i;
+	GSList* idx;
+	LV_MARK;
+	
+	for (idx = self->prv->all_modules; idx != NULL; idx = idx->next) {
+		LogviewModule *module = LOGVIEW_MODULE (idx->data);
+		g_assert (module != NULL);
+		LV_INFO ("unload|module: %s", logview_module_name (module));
+		g_type_module_unuse ((GTypeModule*) module);
+	}
+	g_slist_free (self->prv->all_modules);
+	self->prv->all_modules = NULL;
+
+	for (i = 0; i < MGR_PF_NUM; i++) {
+		if (self->prv->modules[i]) {
+			g_slist_free (self->prv->modules[i]);
+			self->prv->modules[i]  = NULL;
+		}
+	}
+}
+
+static gboolean
+register_module (LogviewPluginMgr *self, LogviewModule* module)
+{
+	GType mt = logview_module_type (module);
+	PluginFunc i;
+	gboolean ret = FALSE;
+
+	LV_MARK;
+	for (i = 0; i < MGR_PF_NUM; i++) {
+		if (g_type_is_a (mt, self->prv->typehash[i])) {
+			LV_INFO ("module %s, derived from type %s",
+				 logview_module_name (module),
+				 g_type_name (self->prv->typehash[i]));
+
+			if (g_slist_find_custom (self->prv->modules[i],
+						 module, module_cmpfunc) == NULL) {
+				self->prv->modules[i] = 
+					g_slist_insert_sorted_with_data (
+						self->prv->modules[i], 
+						module, module_prio_cmpfunc, NULL);
+				ret = TRUE;
+			}
+		}
+	}
+	if (ret) {
+		self->prv->all_modules = g_slist_append (self->prv->all_modules, module);
+	}
+	return ret;
+}
+
+static void
+unload_module (LogviewPluginMgr *self, LogviewModule* module)
+{
+	GType mt = logview_module_type (module);
+	PluginFunc i;
+	GSList* idx = NULL;
+	LV_MARK;
+
+	for (i = 0; i < MGR_PF_NUM; i++) {
+		if (g_type_is_a (mt, self->prv->typehash[i])) {
+			idx = g_slist_find (self->prv->modules[i], module);
+			g_assert (idx != NULL);
+			self->prv->modules[i] =	
+				g_slist_delete_link (self->prv->modules[i], idx);
+		}
+	}
+	idx = g_slist_find (self->prv->all_modules, module);
+	g_assert (idx != NULL);
+	self->prv->all_modules = g_slist_delete_link (self->prv->all_modules, idx);
+	g_type_module_unuse ((GTypeModule*) module);
+}
+
+static gint
+module_cmpfunc (gconstpointer a, gconstpointer b)
+{
+	return g_ascii_strcasecmp (logview_module_name (LOGVIEW_MODULE(a)), 
+				   logview_module_name (LOGVIEW_MODULE(b)));
+}
+
+static gint
+module_prio_cmpfunc (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+	PluginPrio ap = logview_module_priority (LOGVIEW_MODULE (a));
+	PluginPrio bp = logview_module_priority (LOGVIEW_MODULE (b));
+	/* from high priory to low */
+	return ap > bp ? -1 : (ap == bp ? 0 : 1);
+}
+
+static gboolean
+is_plugin_file (const gchar* filepath)
+{
+	gchar* suffix[] = {
+		G_MODULE_SUFFIX,
+		NULL};
+	gchar ext[32];
+	gint i;
+	for (i = 0; suffix[i] != NULL; i++) {
+		g_sprintf (ext, ".%s", suffix[i]);
+		if (g_str_has_suffix (filepath, ext)) {
+			if (g_access (filepath, R_OK) == 0) {
+				return TRUE;
+			}
+			return FALSE;
+		}
+	}
+	return FALSE;
+}
+
+void
+iterate (LogviewPluginMgr *self, ModuleHandleCB cb, gpointer data)
+{
+	GSList *idx;
+	for (idx = self->prv->all_modules; idx; idx = g_slist_next(idx)) {
+			cb (LOGVIEW_MODULE (idx->data), data);
+	}
+}
+
+static void
+pluginmgr_class_init (gpointer g_class, gpointer g_class_data)
+{
+	GObjectClass *object_class;
+	LogviewPluginMgrClass *self = LOGVIEW_PLUGINMGR_CLASS (g_class);
+
+	object_class = G_OBJECT_CLASS (g_class);
+	parent_class = g_type_class_peek_parent (g_class);
+	object_class->finalize = (void(*)(GObject*)) pluginmgr_finalize;
+
+	self->load_module = load_module;
+	self->unload_module = unload_module;
+	self->load_modules = load_modules;
+	self->unload_modules = unload_modules;
+	self->iterate = iterate;
+
+}
+
+GSList *
+pluginmgr_get_all_logs ()
+{
+	GSList *all_logs = NULL;
+	GSList *idx = NULL;
+	
+	LV_MARK;
+	
+	for (idx = pluginmgr_instance->prv->modules[PF_LOG_COLLECTOR];
+	     idx != NULL; idx = idx->next) {
+		GSList *logs, *i;
+		LogviewPlugin *plugin;
+		g_assert (LOGVIEW_IS_MODULE (idx->data));
+		plugin = logview_module_new_plugin (LOGVIEW_MODULE (idx->data));
+		g_assert (LOGVIEW_IS_IFACE_COLLECTOR (plugin));
+		logs = logi_get_logs (LOGVIEW_IFACE_COLLECTOR (plugin));
+		g_object_unref (plugin);
+		if (logs) {
+			for (i = logs; i; i = g_slist_next(i)) {
+				if (g_slist_find_custom (all_logs, i->data,
+							 (GCompareFunc) g_ascii_strcasecmp)== NULL) {
+					all_logs = g_slist_append (all_logs, i->data);
+					LV_INFO ("all logs: %s", i->data);
+				}
+				else
+					g_free (i->data);
+			}
+			g_slist_free (logs);
+		}
+	}
+	
+	return all_logs;
+}
+
+static void
+pluginmgr_handle_derived_log (const Log* parent, PluginFunc pf, LogviewModule *module)
+{
+	int i;
+	int versions;
+	Log **older_logs;
+
+	LV_MARK;
+	g_object_get (G_OBJECT (parent),
+		      "versions", &versions,
+		      "archives", &older_logs,
+		      NULL);
+	if (versions > 0) {
+		for (i=0; i<OLD_LOG_NUM; i++) {
+			if (older_logs[i]) {
+				LogviewPlugin *plugin;
+				plugin = logview_module_new_plugin (module);
+				if (logi_can_handle (LOGVIEW_IFACE (plugin),
+						     older_logs[i])) {
+					if (logview_plugin_init (plugin)) {
+						log_set_func (older_logs[i],pf,plugin);
+					}
+				}
+			}
+		}
+	}
+}
+
+/* try each plugin to find one which can handle the log */
+static LogviewPlugin*
+pluginmgr_probe_plugin (const Log* log, PluginFunc pf)
+{
+	/* do not check dependency right now */
+	GSList *idx;
+	LogviewPlugin *plugin;
+	
+	LV_MARK;
+	
+	if (pluginmgr_instance->prv->modules[pf] != NULL) {
+		for (idx = pluginmgr_instance->prv->modules[pf]; idx != NULL; idx=idx->next) {
+			LogviewModule *module;
+			module = LOGVIEW_MODULE (idx->data);
+			LV_ERR ("probing module %s", logview_module_name (module));
+			plugin = logview_module_new_plugin (module);
+			g_return_val_if_fail (plugin != NULL, NULL);
+			g_return_val_if_fail (LOGVIEW_IS_PLUGIN (plugin), NULL);
+			g_return_val_if_fail (LOGVIEW_IS_IFACE (plugin), NULL);
+			if (logi_can_handle (LOGVIEW_IFACE (plugin), (struct _Log *) log)) {
+				if (logview_plugin_init (plugin)) {
+					pluginmgr_handle_derived_log (log, pf, module);
+					return plugin;
+				}
+			}
+			g_object_unref (plugin);
+		}
+	}
+	return NULL;
+}
+
+gboolean pluginmgr_load_modules ()
+{
+	return LOGVIEW_PLUGINMGR_GET_CLASS (pluginmgr_instance)->load_modules (pluginmgr_instance);
+}
+
+void pluginmgr_unload_modules ()
+{
+	LOGVIEW_PLUGINMGR_GET_CLASS (pluginmgr_instance)->unload_modules (pluginmgr_instance);
+}
+
+Log*
+pluginmgr_new_log_from_path (const gchar* log_path)
+{
+	Log *log = NULL;
+	GSList *idx = NULL;
+	LV_MARK;
+	log_error_init (log_path);
+	for (idx = pluginmgr_instance->prv->modules[PF_LOG_COLLECTOR]; 
+	     idx != NULL; idx = idx->next) {
+		LogviewPlugin *plugin;
+		g_assert (LOGVIEW_IS_MODULE (idx->data));
+		plugin = logview_module_new_plugin (LOGVIEW_MODULE (idx->data));
+		g_assert (LOGVIEW_IS_IFACE_COLLECTOR (plugin));
+		if (logi_can_handle (LOGVIEW_IFACE (plugin), log)) {
+			log = logi_config_log_from_path (
+				LOGVIEW_IFACE_COLLECTOR (plugin), log_path);
+		}
+		g_object_unref (plugin);
+		if (log != NULL)
+			return pluginmgr_new_log (log);
+	}
+	return NULL;
+}
+
+static Log*
+pluginmgr_new_log (Log *log)
+{
+	LogviewPlugin *plugin;
+	Log **older_logs;
+	int i;
+
+	LV_MARK;
+	
+	g_assert (log);
+	g_object_get (G_OBJECT (log),
+		      "archives", &older_logs,
+		      NULL);
+	if (! log_test_func (log, PF_LOG_IO)) {
+		plugin = pluginmgr_probe_plugin (log, PF_LOG_IO);
+		if (plugin != NULL) {
+			log_set_func (log,PF_LOG_IO,plugin);
+		}
+		else {
+			g_object_unref (log);
+			return NULL;
+		}
+	}
+
+	for (i = PF_LOG_IO + 1; i < LOG_PF_NUM; i++) {
+		if (! log_test_func (log, i)) {
+			plugin = pluginmgr_probe_plugin (log, i);
+			if (plugin != NULL) {
+				log_set_func (log,i,plugin);
+			}
+		}
+	}
+
+	/* Check for older versions of the log */
+	for (i=0; i<OLD_LOG_NUM; i++) {
+		if (older_logs[i] &&
+		    LOGVIEW_IS_LOG(older_logs[i]))
+			pluginmgr_new_log (older_logs[i]);
+	}
+	g_assert (LOGVIEW_IS_LOG (log));
+	log_run (log);
+	log_notify (log, TH_EVENT_INIT_READ);
+	return log;
+}
+
+
+/* global functions */
+
+void
+pluginmgr_destroy ()
+{
+	LV_MARK;
+	if (pluginmgr_instance != NULL) {
+		g_object_unref (pluginmgr_instance);
+		pluginmgr_instance = NULL;
+	}
+}
+
+void
+pluginmgr_init()
+{
+	LV_MARK;
+	if (pluginmgr_instance == NULL) {
+		pluginmgr_instance = g_object_new (LOGVIEW_TYPE_PLUGINMGR, NULL);
+	}
+}
+
+void
+pluginmgr_modules_iterate (ModuleHandleCB cb, gpointer data)
+{
+	LOGVIEW_PLUGINMGR_GET_CLASS (pluginmgr_instance)->iterate (pluginmgr_instance,
+								   cb, data);
+}
+
+gint
+pluginmgr_plugin_amount ()
+{
+	gint i, n;
+	for (i = 0, n = 0; i < MGR_PF_NUM; i++) {
+		n += g_slist_length (pluginmgr_instance->prv->modules[i]);
+	}
+	return n;
+}
diff --git a/logview/logview-plugin-manager.h b/logview/logview-plugin-manager.h
new file mode 100644
index 0000000..52d9fda
--- /dev/null
+++ b/logview/logview-plugin-manager.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+ 
+#ifndef __LOGVIEW_PLUGINMGR_H__
+#define __LOGVIEW_PLUGINMGR_H__
+#include <glib-object.h>
+#include <glib.h>
+#include "logview-module.h"
+#include "logrtns.h"
+
+typedef struct _LogviewPluginMgr LogviewPluginMgr;
+typedef struct _LogviewPluginMgrClass LogviewPluginMgrClass;
+typedef struct _LogviewPluginMgrPrivate LogviewPluginMgrPrivate;
+
+typedef void (*ModuleHandleCB) (const LogviewModule *module, gpointer data);
+
+struct _LogviewPluginMgr {
+	GObject parent;
+	/* instance members */
+/* private */
+	LogviewPluginMgrPrivate *prv;
+};
+
+struct _LogviewPluginMgrClass {
+	GObjectClass parent;
+
+	/* class members */
+
+	/* handle one module */
+	gboolean (*load_module) (LogviewPluginMgr *self, const gchar *module_path);
+	void (*unload_module) (LogviewPluginMgr *self, LogviewModule *module);
+
+	/* handle all module in paths */
+	gboolean (*load_modules)(LogviewPluginMgr *self);
+	void (*unload_modules)(LogviewPluginMgr *self);
+
+	/* collect all the path names in Plugin paths.
+	   return a list, do not try to free it.
+	 */
+	GSList* (*compose_search_plugin_paths)(LogviewPluginMgr *self);
+	
+	void (*iterate)(LogviewPluginMgr *self, ModuleHandleCB cb, gpointer data);
+};
+
+/* For the core application which should implemete the level to call plugins */
+
+//void logview_plugins_detect (const gchar* plugin_patchs);
+
+
+extern void pluginmgr_init ();
+extern void pluginmgr_destroy ();
+extern gboolean pluginmgr_load_modules ();
+extern void pluginmgr_unload_modules ();
+extern gint pluginmgr_plugin_amount();
+
+extern GSList* pluginmgr_get_all_logs ();
+extern Log* pluginmgr_new_log_from_path (const gchar* log_path);
+extern void pluginmgr_modules_iterate (ModuleHandleCB cb, gpointer data);
+
+/* routines provided for internal usage */
+
+#endif /* __LOGVIEW_PLUGINMGR_H__ */
+
diff --git a/logview/logview-plugin.c b/logview/logview-plugin.c
new file mode 100644
index 0000000..6a3beed
--- /dev/null
+++ b/logview/logview-plugin.c
@@ -0,0 +1,105 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+ 
+#include "logview-plugin.h"
+#include "logview-debug.h"
+
+static GObjectClass *parent_class = NULL;
+static void logview_plugin_finalize (GObject *object);
+
+static void
+logview_plugin_instance_init (LogviewPlugin *self, gpointer g_class_data)
+{
+	LV_MARK;
+}
+
+static void
+logview_plugin_class_init (gpointer g_class, gpointer g_class_data)
+{
+	GObjectClass *object_class;
+	LogviewPluginClass *self;
+	
+	LV_MARK;
+
+	object_class = G_OBJECT_CLASS (g_class);
+	self = LOGVIEW_PLUGIN_CLASS (g_class);
+	parent_class = g_type_class_peek_parent (g_class);
+	self->plugin_init = NULL;
+	self->plugin_destroy = NULL;
+	object_class->finalize = logview_plugin_finalize;
+}
+
+GType
+logview_plugin_get_type (void)
+{
+	static GType type = 0;
+	LV_MARK;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (LogviewPluginClass),
+			NULL,   /* base_init */
+			NULL,   /* base_finalize */
+			logview_plugin_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			sizeof (LogviewPlugin),
+			0,      /* n_preallocs */
+			(GInstanceInitFunc) logview_plugin_instance_init    /* instance_init */
+		};		
+		type = g_type_register_static (G_TYPE_OBJECT,
+					       "LogviewPluginType",
+					       &info, 0);
+	}
+	return type;
+}
+
+static void
+logview_plugin_finalize (GObject *object)
+{
+	LV_MARK;
+	if (LOGVIEW_PLUGIN_GET_CLASS (object)->plugin_destroy)
+		LOGVIEW_PLUGIN_GET_CLASS (object)->plugin_destroy (LOGVIEW_PLUGIN (object));
+	parent_class->finalize (object);
+}
+
+/**
+ * logview_plugin_init:
+ * @self: a #LogviewPlugin instance.
+ * 
+ * plugin_init default returns TRUE if it is not overriden.
+ * If it returns FALSE, the plugin will be skipped.
+ * 
+ * This method will be called by manager after interface can_handle
+ * is called. The plugin should be initialized at this time. 
+ * If returns TRUE, this plugin will be assigned to log.
+ * 
+ * Returns: Upon a successful completion or it is not be overloaded, returns TRUE.
+ */
+gboolean
+logview_plugin_init (LogviewPlugin* self)
+{
+	LV_MARK;
+	if (LOGVIEW_PLUGIN_GET_CLASS(self)->plugin_init)
+		return LOGVIEW_PLUGIN_GET_CLASS(self)->plugin_init (self);
+	return TRUE;
+}
+
+/**
+ * logview_plugin_destroy:
+ * @self: a #LogviewPlugin instance.
+ * 
+ * Log instance will automatically destroy all plugins it has.
+ * This method will be automatically called when it's unrefed.
+ */
+void
+logview_plugin_destroy (LogviewPlugin* self)
+{
+	LV_MARK;
+	if (LOGVIEW_PLUGIN_GET_CLASS(self)->plugin_destroy)
+		LOGVIEW_PLUGIN_GET_CLASS(self)->plugin_destroy (self);
+}
+
diff --git a/logview/logview-plugin.h b/logview/logview-plugin.h
new file mode 100644
index 0000000..16fca0b
--- /dev/null
+++ b/logview/logview-plugin.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#ifndef __LOGVIEW_PLUGIN_H__
+#define __LOGVIEW_PLUGIN_H__
+
+#include <glib-object.h>
+#include <gmodule.h>
+#include "logview-iface.h"
+
+G_BEGIN_DECLS
+
+#define LOGVIEW_TYPE_PLUGIN		(logview_plugin_get_type ())
+#define LOGVIEW_PLUGIN(obj)		(G_TYPE_CHECK_INSTANCE_CAST((obj), LOGVIEW_TYPE_PLUGIN, LogviewPlugin))
+#define LOGVIEW_PLUGIN_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST((klass), LOGVIEW_TYPE_PLUGIN, LogviewPluginClass))
+#define LOGVIEW_IS_PLUGIN(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj), LOGVIEW_TYPE_PLUGIN))
+#define LOGVIEW_IS_PLUGIN_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), LOGVIEW_TYPE_PLUGIN))
+#define LOGVIEW_PLUGIN_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), LOGVIEW_TYPE_PLUGIN, LogviewPluginClass))
+
+typedef struct _LogviewPluginInfo LogviewPluginInfo;
+typedef struct _LogviewPlugin LogviewPlugin;
+typedef struct _LogviewPluginClass LogviewPluginClass;
+
+/**
+ * PluginPrio:
+ * @PLUG_PRIO_BASE: the base priority. if a plugin is explicited defined
+ * a priority by redefine #PLUGIN_PRIO, it will be assigned to %PLUG_PRIO_USER.
+ * @PLUG_PRIO_USER: the base line of priority which user should use,
+ * e.g. %PLUG_PRIO_USER + 1
+ * 
+ * A plugin with a high priority will be used first.
+ */
+typedef enum {
+	PLUG_PRIO_BASE=0,
+	PLUG_PRIO_USER=10
+} PluginPrio;
+
+/*< private >*/
+typedef enum {
+	PF_LOG_IO=0,
+	PF_LOG_VIEW,
+	PF_LOG_COLLECTOR,
+	PF_LOG_NUM
+} PluginFunc;
+
+/*< private >*/
+#define LOG_PF_NUM PF_LOG_COLLECTOR
+/*< private >*/
+#define MGR_PF_NUM PF_LOG_NUM
+
+/*< private >*/
+struct _LogviewPluginInfo {
+	PluginPrio prio;
+	gchar *desc;
+};
+
+/*< public >*/
+struct _LogviewPlugin {
+	/*< private >*/
+	GObject parent;
+};
+
+struct _LogviewPluginClass {
+	GObjectClass parent;
+	
+/* virtual public */
+	gboolean (*plugin_init) (LogviewPlugin* self);
+	void (*plugin_destroy) (LogviewPlugin* self);
+};
+
+GType logview_plugin_get_type (void);
+gboolean logview_plugin_init (LogviewPlugin* self);
+void logview_plugin_destroy (LogviewPlugin* self);
+
+/**
+ * PLUGIN_PRIO:
+ * 
+ * Define plugin priority, can be redefined by users.
+ * 
+ * PLUGIN_PRIO should be defined great or equal to PLUG_PRIO_USER,
+ * so that your plugin can be probed first.
+ * 
+ * e.g. %PLUG_PRIO_USER + 1
+ */
+#define PLUGIN_PRIO	PLUG_PRIO_USER
+
+/**
+ * PLUGIN_DESC:
+ * 
+ * A brief description for the plugin is needed
+ * for displaying the plugin information in GUI.
+ * 
+ * e.g. PLUGIN_DESC	"For ..."
+ */
+#define PLUGIN_DESC	"Unknown plugin."
+
+/**
+ * LOGVIEW_INIT_PLUGIN:
+ * @PluginTypeFunc: a function reports the #GType of the plugin.
+ * 
+ * A macro exports some useful functions. Therefor core application
+ * can get some useful information to load the plugin.
+ */
+#define LOGVIEW_INIT_PLUGIN(PluginTypeFunc)	\
+G_MODULE_EXPORT LogviewPluginInfo* logview_plugin_info (void);		\
+G_MODULE_EXPORT LogviewPluginInfo* logview_plugin_info (void)		\
+{									\
+	static LogviewPluginInfo __plugin_info;				\
+	__plugin_info.prio = PLUGIN_PRIO;				\
+	__plugin_info.desc = PLUGIN_DESC;				\
+	return &__plugin_info;						\
+}									\
+G_MODULE_EXPORT GType register_logview_plugin (GTypeModule* module);	\
+G_MODULE_EXPORT GType register_logview_plugin (GTypeModule* module)	\
+{									\
+	return PluginTypeFunc (module);					\
+}
+
+
+G_END_DECLS
+
+#endif /* __LOGVIEW_PLUGIN_H__ */
+
diff --git a/logview/logview.c b/logview/logview.c
index d1bdbaa..91ce390 100644
--- a/logview/logview.c
+++ b/logview/logview.c
@@ -1,22 +1,22 @@
 /*  ----------------------------------------------------------------------
 
-    Copyright (C) 1998  Cesar Miquel  (miquel@df.uba.ar)
+Copyright (C) 1998  Cesar Miquel  (miquel@df.uba.ar)
 
-    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 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.
+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., 675 Mass Ave, Cambridge, MA 02139, USA.
+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., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-    ---------------------------------------------------------------------- */
+---------------------------------------------------------------------- */
 
 #include <config.h>
 #include <stdlib.h>
@@ -33,19 +33,21 @@
 #include "logview-findbar.h"
 #include "userprefs.h"
 #include "calendar.h"
+#include "logview-debug.h"
+#include "logview-plugin-manager.h"
 
 #define APP_NAME _("System Log Viewer")
 
 enum {
-  PROP_0,
-  PROP_DAYS,
+	PROP_0,
+	PROP_DAYS,
 };
 
 enum {
-  LOG_LINE_TEXT = 0,
-  LOG_LINE_POINTER,
-  LOG_LINE_WEIGHT,
-  LOG_LINE_WEIGHT_SET
+	LOG_LINE_TEXT = 0,
+	LOG_LINE_POINTER,
+	LOG_LINE_WEIGHT1,
+	LOG_LINE_WEIGHT_SET1
 };
 
 static GObjectClass *parent_class;
@@ -76,8 +78,7 @@ static void logview_help (GtkAction *action, GtkWidget *parent_window);
 static void logview_copy (GtkAction *action, LogviewWindow *logview);
 static void logview_select_all (GtkAction *action, LogviewWindow *logview);
 static Log *logview_find_log_from_name (LogviewWindow *logview, gchar *name);
-
-static void logview_window_get_property	(GObject *object, guint param_id, GValue *value, GParamSpec *pspec);
+static void logview_plugin_dialog_show (GtkAction *action, LogviewWindow *logview);
 
 /* Menus */
 
@@ -115,14 +116,16 @@ static GtkActionEntry entries[] = {
 	  G_CALLBACK (logview_help) },
 	{ "AboutAction", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("Show the about dialog for the log viewer"), 
 	  G_CALLBACK (logview_about) },
+	{ "PluginList", NULL, N_("_Plugins"), NULL, N_("Show the information of the loaded plugins"), 
+	  G_CALLBACK (logview_plugin_dialog_show) },
 
 };
 
 static GtkToggleActionEntry toggle_entries[] = {
-    { "ShowStatusBar", NULL, N_("_Statusbar"), NULL, N_("Show Status Bar"),
-      G_CALLBACK (logview_toggle_statusbar), TRUE },
+	{ "ShowStatusBar", NULL, N_("_Statusbar"), NULL, N_("Show Status Bar"),
+	  G_CALLBACK (logview_toggle_statusbar), TRUE },
 	{ "ShowSidebar", NULL, N_("Side _Pane"), "F9", N_("Show Side Pane"), 
-		G_CALLBACK (logview_toggle_sidebar), TRUE },
+	  G_CALLBACK (logview_toggle_sidebar), TRUE },
 	{ "MonitorLogs", NULL, N_("_Monitor"), "<control>M", N_("Monitor Current Log"),
 	  G_CALLBACK (logview_toggle_monitor), TRUE },
 	{"ShowCalendar", NULL,  N_("Ca_lendar"), "<control>L", N_("Show Calendar Log"), 
@@ -130,202 +133,240 @@ static GtkToggleActionEntry toggle_entries[] = {
 };
 
 static const char *ui_description = 
-	"<ui>"
-	"	<menubar name='LogviewMenu'>"
-	"		<menu action='FileMenu'>"
-	"			<menuitem action='OpenLog'/>"
-	"			<menuitem action='CloseLog'/>"
-	"			<menuitem action='Quit'/>"
-	"		</menu>"
-	"		<menu action='EditMenu'>"
-	"			<menuitem action='Copy'/>"
-	"	    	<menuitem action='SelectAll'/>"
-	"		</menu>"
-	"		<menu action='ViewMenu'>"
-	"			<menuitem action='MonitorLogs'/>"
-	"			<separator/>"
-	"			<menuitem action='ShowStatusBar'/>"
-	"			<menuitem action='ShowSidebar'/>"
-	"			<menuitem action='ShowCalendar'/>"
-	"			<separator/>"
-	"			<menuitem action='Search'/>"
-	"			<menuitem action='CollapseAll'/>"
-	"			<separator/>"
-	"			<menuitem action='ViewZoomIn'/>"
-	"			<menuitem action='ViewZoomOut'/>"
-	"			<menuitem action='ViewZoom100'/>"
-	"		</menu>"
-	"		<menu action='HelpMenu'>"
-	"			<menuitem action='HelpContents'/>"
-	"			<menuitem action='AboutAction'/>"
-	"		</menu>"
-	"	</menubar>"
-	"</ui>";
+"<ui>"
+"	<menubar name='LogviewMenu'>"
+"		<menu action='FileMenu'>"
+"			<menuitem action='OpenLog'/>"
+"			<menuitem action='CloseLog'/>"
+"			<menuitem action='Quit'/>"
+"		</menu>"
+"		<menu action='EditMenu'>"
+"			<menuitem action='Copy'/>"
+"	    	<menuitem action='SelectAll'/>"
+"		</menu>"
+"		<menu action='ViewMenu'>"
+"			<menuitem action='MonitorLogs'/>"
+"			<separator/>"
+"			<menuitem action='ShowStatusBar'/>"
+"			<menuitem action='ShowSidebar'/>"
+"			<menuitem action='ShowCalendar'/>"
+"			<separator/>"
+"			<menuitem action='Search'/>"
+"			<menuitem action='CollapseAll'/>"
+"			<separator/>"
+"			<menuitem action='ViewZoomIn'/>"
+"			<menuitem action='ViewZoomOut'/>"
+"			<menuitem action='ViewZoom100'/>"
+"			<separator/>"
+"			<menuitem action='PluginList'/>"
+"		</menu>"
+"		<menu action='HelpMenu'>"
+"			<menuitem action='HelpContents'/>"
+"			<menuitem action='AboutAction'/>"
+"		</menu>"
+"	</menubar>"
+"</ui>";
 
 /* public interface */
 
 Log *
 logview_get_active_log (LogviewWindow *logview)
 {
-    g_return_val_if_fail (LOGVIEW_IS_WINDOW (logview), NULL);
-    return logview->curlog;
+	g_return_val_if_fail (LOGVIEW_IS_WINDOW (logview), NULL);
+	return logview->curlog;
 }
 
 LogList *
 logview_get_loglist (LogviewWindow *logview)
 {
-    g_return_val_if_fail (LOGVIEW_IS_WINDOW (logview), NULL);
-    return LOG_LIST (logview->loglist);
+	g_return_val_if_fail (LOGVIEW_IS_WINDOW (logview), NULL);
+	return LOG_LIST (logview->loglist);
 }
 
 int
 logview_count_logs (LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
-    return g_slist_length (logview->logs);
+	g_assert (LOGVIEW_IS_WINDOW (logview));
+	return g_slist_length (logview->logs);
 }
 
 static void
 logview_store_visible_range (LogviewWindow *logview)
 {
-  GtkTreePath *first, *last;
-  Log *log = logview->curlog;
-  if (log == NULL)
-    return;
+	GtkTreePath *first = NULL;
+	Log *log = logview->curlog;
+	if (log == NULL)
+		return;
 
-  if (log->visible_first)
-    gtk_tree_path_free (log->visible_first);
+	if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (logview->view),
+					     &first, NULL))
+		g_object_set (G_OBJECT (log), "visible-first", first, NULL);
+}
 
-  if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (logview->view),
-                                       &first, NULL))
-    log->visible_first = first;
+static void
+logview_update_other_components (LogviewWindow *logview)
+{
+	logview_set_window_title (logview);
+	logview_menus_set_state (logview);
+	logview_calendar_set_state (logview);
+	logview_repaint (logview);
+	logview_update_findbar_visibility (logview);
 }
 
 void
 logview_select_log (LogviewWindow *logview, Log *log)
 {
-    g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
+	LV_MARK;
+	g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
+	if (!GTK_WIDGET_VISIBLE (GTK_WIDGET (logview->view)))
+		return;
   
-    logview_store_visible_range (logview);
-
-    logview->curlog = log;
-    logview_menus_set_state (logview);
-    logview_calendar_set_state (logview);
-    logview_repaint (logview);
-    logview_update_findbar_visibility (logview);
-    
-    logview_update_version_bar (logview);
-    logview_save_prefs (logview); 
-    gtk_widget_grab_focus (logview->view);
-} 
+	logview_store_visible_range (logview);
+
+	logview->curlog = log;
+	/* 
+	   update version bar first, because the user may choose a child log
+	   of logview->curlog, so to avoid update other components multi times
+	   give the update responsibility to the change signal of version bar.
+
+	   Note that if choose a log path, then blank out all the Gui.
+	*/
+	logview_update_version_bar (logview);
+	if (logview->curlog == NULL) {
+		logview_update_other_components (logview);
+	}
+	gtk_widget_grab_focus (logview->view);
+}
 
 void
 logview_add_log_from_name (LogviewWindow *logview, gchar *file)
 {
 	Log *log;
 
-    g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
-    g_return_if_fail (file);
+	g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
+	g_return_if_fail (file);
 
-    log = log_open (file, TRUE);
-    if (log != NULL)
-      logview_add_log (logview, log);
+	log = pluginmgr_new_log_from_path (file);
+	if (log != NULL)
+		logview_add_log (logview, log);
+	else
+		error_dialog_show (NULL, _("Add log error."), log_error());
 }
 
 void
 logview_add_logs_from_names (LogviewWindow *logview, GSList *lognames, gchar *selected)
 {
-    GSList *list;
-    Log *log, *curlog = NULL;
+	GSList *list;
+	Log *log, *curlog = NULL;
+	gchar *log_name;
 	
-    g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
-
-    if (lognames == NULL)
-        return;
-
-    for (list = lognames; list != NULL; list = g_slist_next (list)) {
-      log = log_open (list->data, FALSE);
-      if (log != NULL) {
-	logview_add_log (logview, log);
-	if (selected!=NULL && g_strncasecmp (log->name, selected, -1)==0)
-	  curlog = log;
-      }
-    }
+	g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
+
+	if (lognames == NULL)
+		return;
+
+	/* remove log_open, open by pluginmgr_get_all_logs or loglist? */
+	for (list = lognames; list != NULL; list = g_slist_next (list)) {
+		log = pluginmgr_new_log_from_path ((gchar*)list->data);
+		if (log != NULL) {
+			logview_add_log (logview, log);
+			g_object_get (G_OBJECT (log), "path", &log_name, NULL);
+			if (selected!=NULL && 
+			    (g_strncasecmp (log_name, selected, -1) == 0))
+				curlog = log;
+			g_free (log_name);
+		}
+		else
+			error_dialog_show (NULL, _("Add log error."), log_error());
+	}
         
-    if (curlog)
-        loglist_select_log (LOG_LIST (logview->loglist), curlog);
+	if (curlog)
+		loglist_select_log (LOG_LIST (logview->loglist), curlog);
 
-    gtk_tree_view_expand_all (GTK_TREE_VIEW (logview->loglist));
+	gtk_tree_view_expand_all (GTK_TREE_VIEW (logview->loglist));
 }
 
 void
 logview_menus_set_state (LogviewWindow *logview)
 {
-    Log *log;
-    gboolean calendar_active = FALSE, monitor_active = FALSE;
-    GtkWidget *widget;
-
-    g_assert (LOGVIEW_IS_WINDOW (logview));
-    log = logview->curlog;
-
-    if (log) {
-        monitor_active = (log->display_name == NULL);
-        calendar_active = (log->days != NULL);
-        logview_menu_item_toggle_set_active (logview, "/LogviewMenu/ViewMenu/MonitorLogs", logview->curlog->monitored);
-    }
+	Log *log;
+	gboolean monitorable;
+	gboolean monitoring;
+	gboolean groupable;
+	gboolean calendar_active = FALSE, monitor_active = FALSE;
+
+	g_assert (LOGVIEW_IS_WINDOW (logview));
+	log = logview->curlog;
+	if (log) {
+		g_assert (LOGVIEW_IS_LOG (log));
+		g_object_get (G_OBJECT (log),
+			      "monitorable", &monitorable,
+			      "monitoring", &monitoring,
+			      "groupable", &groupable,
+			      NULL);
+
+		monitor_active = monitorable;
+		calendar_active = groupable;
+		logview_menu_item_toggle_set_active (logview,
+						     "/LogviewMenu/ViewMenu/MonitorLogs",
+						     monitoring);
+	}
     
-    logview_menu_item_set_state (logview, "/LogviewMenu/ViewMenu/MonitorLogs", monitor_active);
-    logview_menu_item_set_state (logview, "/LogviewMenu/ViewMenu/ShowCalendar", calendar_active);
-    logview_menu_item_set_state (logview, "/LogviewMenu/FileMenu/CloseLog", (log != NULL));
-    logview_menu_item_set_state (logview, "/LogviewMenu/ViewMenu/CollapseAll", calendar_active);
-    logview_menu_item_set_state (logview, "/LogviewMenu/ViewMenu/Search", (log != NULL));
-    logview_menu_item_set_state (logview, "/LogviewMenu/EditMenu/Copy", (log != NULL));
-    logview_menu_item_set_state (logview, "/LogviewMenu/EditMenu/SelectAll", (log != NULL));
+	logview_menu_item_set_state (logview, "/LogviewMenu/ViewMenu/MonitorLogs", monitor_active);
+	logview_menu_item_set_state (logview, "/LogviewMenu/ViewMenu/ShowCalendar", calendar_active);
+	logview_menu_item_set_state (logview, "/LogviewMenu/FileMenu/CloseLog", (log != NULL));
+	logview_menu_item_set_state (logview, "/LogviewMenu/ViewMenu/CollapseAll", calendar_active);
+	logview_menu_item_set_state (logview, "/LogviewMenu/ViewMenu/Search", (log != NULL));
+	logview_menu_item_set_state (logview, "/LogviewMenu/EditMenu/Copy", (log != NULL));
+	logview_menu_item_set_state (logview, "/LogviewMenu/EditMenu/SelectAll", (log != NULL));
 }
 
 void
 logview_set_window_title (LogviewWindow *logview)
 {
-    gchar *window_title;
-    gchar *logname;
-    Log *log;
-
-    g_assert (LOGVIEW_IS_WINDOW (logview));
-
-    log = logview->curlog;
-    if (log == NULL)
-      return;
-
-    if (log->name != NULL) {
-
-      if (log->display_name != NULL)
-	logname = log->display_name;
-      else
-	logname = log->name;
-      
-      if (log->monitored) 
-	window_title = g_strdup_printf (_("%s (monitored) - %s"), logname, APP_NAME);
-      else
-	window_title = g_strdup_printf ("%s - %s", logname, APP_NAME);
-
-    }
-    else
-      window_title = g_strdup_printf (APP_NAME);
-
-    gtk_window_set_title (GTK_WINDOW (logview), window_title);
-    g_free (window_title);
+	gchar *window_title;
+	gchar *logname;
+	gboolean monitoring;
+	Log *log;
+
+	g_assert (LOGVIEW_IS_WINDOW (logview));
+
+	log = logview->curlog;
+	logname = NULL;
+
+	if (log != NULL) {
+		g_object_get (G_OBJECT (log),
+			      "display-name", &logname,
+			      "monitoring", &monitoring,
+			      NULL);
+		if (logname == NULL) {
+			g_object_get (G_OBJECT (log),
+				      "path", &logname,
+				      NULL);
+		}
+		if (monitoring)
+			window_title = g_strdup_printf (_("%s (monitored) - %s"), logname, APP_NAME);
+		else
+			window_title = g_strdup_printf (_("%s - %s"), logname, APP_NAME);
+	}
+	else
+		window_title = g_strdup_printf (APP_NAME);
+
+	gtk_window_set_title (GTK_WINDOW (logview), window_title);
+	g_free (window_title);
+	g_free (logname);
 }
 
 void
 logview_show_main_content (LogviewWindow *logview)
 {
-  g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
-  gtk_widget_show (logview->calendar);
-  gtk_widget_show (logview->loglist);
-  gtk_widget_show (logview->sidebar);
-  gtk_widget_show (logview->view);
-  gtk_widget_show (logview->hpaned);
-  gtk_widget_show (logview->statusbar);
+	g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
+	gtk_widget_show (logview->calendar);
+	gtk_widget_show (logview->loglist);
+	gtk_widget_show (logview->sidebar);
+	gtk_widget_show (logview->view);
+	gtk_widget_show (logview->hpaned);
+	gtk_widget_show (logview->statusbar);
 }
 
 /* private functions */
@@ -334,12 +375,18 @@ static void
 logview_update_findbar_visibility (LogviewWindow *logview)
 {
 	Log *log = logview->curlog;
-    if (log == NULL) {
-        gtk_widget_hide (logview->find_bar);
-        return;
-    }
+	GtkTreeModelFilter *filter;
+
+	if (log == NULL) {
+		gtk_widget_hide (logview->find_bar);
+		return;
+	}
+
+	g_object_get (G_OBJECT (log),
+		      "filter", &filter,
+		      NULL);
 
-	if (log->filter != NULL)
+	if (filter != NULL)
 		gtk_widget_show (logview->find_bar);
 	else
 		gtk_widget_hide (logview->find_bar);
@@ -348,208 +395,279 @@ logview_update_findbar_visibility (LogviewWindow *logview)
 static void
 logview_save_prefs (LogviewWindow *logview)
 {
-    GSList *list;
-    Log *log;
+	GSList *list;
+	Log *log, *parent;
 
-    g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
+	LV_MARK;
+	g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
 
-    if (GTK_WIDGET_VISIBLE (GTK_WIDGET (logview->loglist))) {
-        prefs_free_loglist();
-        for (list = logview->logs; list != NULL; list = g_slist_next (list)) {
-            log = list->data;
-            prefs_store_log (log->name);
-        }
+	if (GTK_WIDGET_VISIBLE (GTK_WIDGET (logview->loglist))) {
+		prefs_free_loglist();
+		for (list = logview->logs; list != NULL; list = g_slist_next (list)) {
+			gchar *name = NULL;
+			g_assert (LOGVIEW_IS_LOG (list->data));
+			log = LOGVIEW_LOG (list->data);
+			g_object_get (G_OBJECT (log), "path", &name, NULL);
+			prefs_store_log (name);
+			g_free (name);
+		}
         
-        if (logview->curlog) {
-	  if (logview->curlog->parent_log)
-            prefs_store_active_log (logview->curlog->parent_log->name);
-	  else
-	    prefs_store_active_log (logview->curlog->name);
+		if (logview->curlog) {
+			gchar *log_name, *parent_name;
+			log_name = parent_name = NULL;
+			g_assert (LOGVIEW_IS_LOG (logview->curlog));
+			g_object_get (G_OBJECT (logview->curlog),
+				      "path", &log_name,
+				      "parent", &parent,
+				      NULL);
+			if (parent) {
+				g_assert (LOGVIEW_IS_LOG (parent));
+				g_object_get (G_OBJECT (parent),
+					      "path", &parent_name,
+					      NULL);
+				prefs_store_active_log (parent_name);
+				g_free (parent_name);
+			}
+			else {
+				prefs_store_active_log (log_name);
+			}
+			g_free (log_name);
+		}
+		prefs_store_fontsize (logview->fontsize);
+		prefs_save ();
 	}
-        prefs_store_fontsize (logview->fontsize);
-	prefs_save ();
-    }
 }
 
 static void
 logview_destroy (GObject *object, LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
-
-    if (logview->curlog) {
-        if (logview->curlog->monitored)
-            monitor_stop (logview->curlog);
-    }
+	GSList* idx;
+	gboolean monitoring;
+	g_assert (LOGVIEW_IS_WINDOW (logview));
+	LV_MARK;
+
+	if (logview->curlog) {
+		g_object_get (G_OBJECT (logview->curlog), "monitoring", &monitoring, NULL);
+		if (monitoring)
+			monitor_stop (logview->curlog);
+	}
 
-    logview_save_prefs (logview);
-    prefs_free_loglist ();
+	logview_save_prefs (logview);
+	prefs_free_loglist();
+
+	logview_select_log (logview, NULL);
+	for (idx = logview->logs; idx != NULL; idx = idx->next) {
+		Log *log;
+		g_assert (LOGVIEW_IS_LOG (idx->data));
+		log = LOGVIEW_LOG (idx->data);
+		g_object_get (G_OBJECT (log), "monitoring", &monitoring, NULL);
+		if (monitoring)
+			monitor_stop (log);
+	}
+	g_slist_foreach (logview->logs, (GFunc) g_object_unref, NULL);
+	g_slist_free (logview->logs);
 
-    if (gtk_main_level() > 0)
-      gtk_main_quit ();
-    else
-      exit (0);
+	if (gtk_main_level() > 0)
+		gtk_main_quit ();
+	else {
+		pluginmgr_destroy ();
+		logview_debug_destroy ();
+		exit (0);
+	}
 }
 
 static void
 logview_add_log (LogviewWindow *logview, Log *log)
 {
-    g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
-    g_return_if_fail (log);
+	g_return_if_fail (LOGVIEW_IS_WINDOW (logview));
+	g_return_if_fail (log);
 
-    logview->logs = g_slist_append (logview->logs, log);
-    loglist_add_log (LOG_LIST(logview->loglist), log);
-    log->window = logview;
-    monitor_start (log);
+	logview->logs = g_slist_append (logview->logs, log);
+	loglist_add_log (LOG_LIST(logview->loglist), log);
+	g_object_set (G_OBJECT (log), "view", logview->view, NULL);
+	monitor_start (log);
+	logview_select_log (logview, log);
 }
 
 
 static Log *
 logview_find_log_from_name (LogviewWindow *logview, gchar *name)
 {
-  GSList *list;
-  Log *log;
-
-  if (logview == NULL || name == NULL)
-      return NULL;
-
-  for (list = logview->logs; list != NULL; list = g_slist_next (list)) {
-    log = list->data;
-    if (g_ascii_strncasecmp (log->name, name, 255) == 0) {
-      return log;
-    }
-  }
-  return NULL;
+	GSList *list;
+	Log *log;
+
+	if (logview == NULL || name == NULL)
+		return NULL;
+
+	for (list = logview->logs; list != NULL; list = g_slist_next (list)) {
+		gchar *log_name;
+		log = list->data;
+		g_object_get (G_OBJECT (log), "path", &log_name, NULL);
+		if (g_ascii_strncasecmp (log_name , name, 255) == 0) {
+			g_free (log_name);
+			return log;
+		}
+		g_free (log_name);
+	}
+	return NULL;
 }
 
 static void
 logview_version_selector_changed (GtkComboBox *version_selector, gpointer user_data)
 {
 	LogviewWindow *logview = user_data;
-	Log *log = logview->curlog;
-	int selected;
+	Log *log, **old_logs;
+	int selected, archive_id, current_version, versions;
+	gchar *archive_n = NULL;
 
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
+	if (logview->curlog == NULL)
+		return;
+	g_assert (LOGVIEW_IS_LOG (logview->curlog));
+	/* first, get the parent log */
+	g_object_get (G_OBJECT (logview->curlog),
+		      "parent", &log,
+		      NULL);
+	if (log == NULL)
+		log = logview->curlog;
+	g_object_get (G_OBJECT (log),
+		      "archives", &old_logs,
+		      "current-version", &current_version,
+		      NULL);
 
 	selected = gtk_combo_box_get_active (version_selector);
-
-	if (selected == log->current_version)
-		return;
+	/* return if already selected and 
+	   recorded pointer is equal to the current log or child log */
+	if (selected > 0) {
+		archive_n = gtk_combo_box_get_active_text (version_selector);
+		sscanf (archive_n, "%*s%d", &archive_id);
+		g_free (archive_n);
+	}
 
 	/* select a new version */
-	if (selected == 0) {
-		logview_select_log (logview, log->parent_log);
+	if (selected >= 0)
+		g_object_set (G_OBJECT (log),
+			      "current-version", selected,
+			      NULL);
+	if (selected <= 0) {
+		logview->curlog = log;
 	} else {
-		Log *new;
-		if (log->parent_log) {
-			new = log->parent_log->older_logs[selected];
-		} else {
-			new = log->older_logs[selected];
-		}
-
-		logview_select_log (logview, new);
+		logview->curlog = old_logs[archive_id];
 	}
+	/* must update the gui due to currently log is switched */
+	logview_update_other_components (logview);
 }
 
 static void
 logview_close_log (GtkAction *action, LogviewWindow *logview)
 {
-    Log *log;
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	Log *log;
+	gboolean monitoring;
+	LV_MARK;
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
-    if (logview->curlog == NULL)
-        return;
+	if (logview->curlog == NULL)
+		return;
 
-    if (logview->curlog->monitored) {
-        GtkAction *action = gtk_ui_manager_get_action (logview->ui_manager, "/LogviewMenu/ViewMenu/MonitorLogs");
-        gtk_action_activate (action);
-    }
+	g_object_get (G_OBJECT (logview->curlog), "monitoring", &monitoring, NULL);
+	if (monitoring) {
+		GtkAction *action = gtk_ui_manager_get_action (logview->ui_manager, "/LogviewMenu/ViewMenu/MonitorLogs");
+		gtk_action_activate (action);
+	}
     
-    gtk_widget_hide (logview->find_bar);
+	gtk_widget_hide (logview->find_bar);
 
-    log = logview->curlog;
-    logview->curlog = NULL;
+	/* loglist can only remove parent log */
+	g_object_get (G_OBJECT (logview->curlog), "parent", &log, NULL);
+	if (log ==  NULL)
+		log = logview->curlog;
+	logview->curlog = NULL;
 
-    logview->logs = g_slist_remove (logview->logs, log);
-    log_close (log);
-    loglist_remove_log (LOG_LIST (logview->loglist), log);
+	logview->logs = g_slist_remove (logview->logs, log);
+	loglist_remove_log (LOG_LIST (logview->loglist), log);
+	g_object_unref (log);
 }
 
 static void
 logview_file_selected_cb (GtkWidget *chooser, gint response, LogviewWindow *logview)
 {
-   char *f;
+	char *f;
 
-   g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
-   gtk_widget_hide (GTK_WIDGET (chooser));
-   if (response != GTK_RESPONSE_OK)
-	   return;
-   
-   f = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
-
-   if (f != NULL) {
-       /* Check if the log is not already opened */
-       GSList *list;
-       Log *log, *tl;
-	   for (list = logview->logs; list != NULL; list = g_slist_next (list)) {
-		   log = list->data;
-		   if (g_ascii_strncasecmp (log->name, f, 255) == 0) {
-		     loglist_select_log (LOG_LIST (logview->loglist), log);
-		     return;
-		   }
-	   }
-
-	   if ((tl = log_open (f, TRUE)) != NULL)
-	     logview_add_log (logview, tl);
-   }
+	gtk_widget_hide (GTK_WIDGET (chooser));
+	if (response != GTK_RESPONSE_OK)
+		return;
    
-   g_free (f);
+	f = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
+
+	if (f != NULL) {
+		/* Check if the log is not already opened */
+		GSList *list;
+		Log *log, *tl;
+		for (list = logview->logs; list != NULL; list = g_slist_next (list)) {
+			gchar *log_name;
+			log = list->data;
+			g_object_get (G_OBJECT (log), "path", &log_name, NULL);
+			if (g_ascii_strncasecmp (log_name, f, 255) == 0) {
+				loglist_select_log (LOG_LIST (logview->loglist), log);
+				g_free (log_name);
+				return;
+			}
+			g_free (log_name);
+		}
 
+		if ((tl = pluginmgr_new_log_from_path (f)) != NULL)
+			logview_add_log (logview, tl);
+		else
+			error_dialog_show (NULL, _("Add log error."), log_error());
+	}
+	g_free (f);
 }
 
 static void
 logview_open_log (GtkAction *action, LogviewWindow *logview)
 {
-   static GtkWidget *chooser = NULL;
+	static GtkWidget *chooser = NULL;
 
-   g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
    
-   if (chooser == NULL) {
-	   chooser = gtk_file_chooser_dialog_new (_("Open Log"),
-						  GTK_WINDOW (logview),
-						  GTK_FILE_CHOOSER_ACTION_OPEN,
-						  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-						  GTK_STOCK_OPEN, GTK_RESPONSE_OK,
-						  NULL);
-	   gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
-	   gtk_window_set_modal (GTK_WINDOW (chooser), TRUE);
-	   g_signal_connect (G_OBJECT (chooser), "response",
-			     G_CALLBACK (logview_file_selected_cb), logview);
-	   g_signal_connect (G_OBJECT (chooser), "destroy",
-			     G_CALLBACK (gtk_widget_destroyed), &chooser);
-	   if (prefs_get_active_log () != NULL)
-		   gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (chooser), 
-						  prefs_get_active_log ());
-   }
-
-   gtk_window_present (GTK_WINDOW (chooser));
+	if (chooser == NULL) {
+		chooser = gtk_file_chooser_dialog_new (_("Open Log"),
+						       GTK_WINDOW (logview),
+						       GTK_FILE_CHOOSER_ACTION_OPEN,
+						       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+						       GTK_STOCK_OPEN, GTK_RESPONSE_OK,
+						       NULL);
+		gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
+		gtk_window_set_modal (GTK_WINDOW (chooser), TRUE);
+		g_signal_connect (G_OBJECT (chooser), "response",
+				  G_CALLBACK (logview_file_selected_cb), logview);
+		g_signal_connect (G_OBJECT (chooser), "destroy",
+				  G_CALLBACK (gtk_widget_destroyed), &chooser);
+		if (prefs_get_active_log () != NULL)
+			gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (chooser), 
+						       prefs_get_active_log ());
+	}
+
+	gtk_window_present (GTK_WINDOW (chooser));
 }
 
 static void
 logview_toggle_statusbar (GtkAction *action, LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
-    if (GTK_WIDGET_VISIBLE (logview->statusbar))
-        gtk_widget_hide (logview->statusbar);
-    else
-        gtk_widget_show (logview->statusbar);
+	if (GTK_WIDGET_VISIBLE (logview->statusbar))
+		gtk_widget_hide (logview->statusbar);
+	else
+		gtk_widget_show (logview->statusbar);
 }
 
 static void
 logview_toggle_sidebar (GtkAction *action, LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
 	if (GTK_WIDGET_VISIBLE (logview->sidebar))
 		gtk_widget_hide (logview->sidebar);
@@ -560,7 +678,7 @@ logview_toggle_sidebar (GtkAction *action, LogviewWindow *logview)
 static void 
 logview_toggle_calendar (GtkAction *action, LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
 	if (GTK_WIDGET_VISIBLE (logview->calendar))
 		gtk_widget_hide (logview->calendar);
@@ -576,37 +694,46 @@ logview_toggle_calendar (GtkAction *action, LogviewWindow *logview)
 static void 
 logview_collapse_rows (GtkAction *action, LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
-    gtk_tree_view_collapse_all (GTK_TREE_VIEW (logview->view));
+	gtk_tree_view_collapse_all (GTK_TREE_VIEW (logview->view));
 }
 
 static void
 logview_toggle_monitor (GtkAction *action, LogviewWindow *logview)
 {
-    Log *log;
-
-    g_assert (LOGVIEW_IS_WINDOW (logview));
-
-    log = logview->curlog;
-    if ((log == NULL) || (log->display_name))
-        return;
+	Log *log;
+	gchar *display_name;
+	gboolean monitoring;
+
+	g_assert (LOGVIEW_IS_WINDOW (logview));
+
+	log = logview->curlog;
+	g_object_get (G_OBJECT (log),
+		      "display-name", &display_name,
+		      "monitoring", &monitoring,
+		      NULL);
+	if ((log == NULL) || display_name) {
+		g_free (display_name);
+		return;
+	}
+	g_free (display_name);
 
-    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) &&
-        (log->monitored))
-        return;
+	if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) &&
+	    monitoring)
+		return;
 
-    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))==FALSE &&
-        (!log->monitored))
-        return;
+	if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))==FALSE &&
+	    !monitoring)
+		return;
 
-    if (log->monitored)
-        monitor_stop (log);
-    else
-        monitor_start (log);
+	if (monitoring)
+		monitor_stop (log);
+	else
+		monitor_start (log);
     
-    logview_set_window_title (logview);
-    logview_menus_set_state (logview);
+	logview_set_window_title (logview);
+	logview_menus_set_state (logview);
 }
 
 #define DEFAULT_LOGVIEW_FONT "Monospace 10"
@@ -635,7 +762,7 @@ logview_set_fontsize (LogviewWindow *logview)
 	PangoFontDescription *fontdesc;
 	PangoContext *context;
 	
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
 	context = gtk_widget_get_pango_context (logview->view);
 	fontdesc = pango_context_get_font_description (context);
@@ -647,7 +774,7 @@ logview_set_fontsize (LogviewWindow *logview)
 static void
 logview_bigger_text (GtkAction *action, LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
 	logview->fontsize = MIN (logview->fontsize + 1, 24);
 	logview_set_fontsize (logview);
@@ -656,7 +783,7 @@ logview_bigger_text (GtkAction *action, LogviewWindow *logview)
 static void
 logview_smaller_text (GtkAction *action, LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
 	logview->fontsize = MAX (logview->fontsize-1, 6);
 	logview_set_fontsize (logview);
@@ -665,7 +792,7 @@ logview_smaller_text (GtkAction *action, LogviewWindow *logview)
 static void
 logview_normal_text (GtkAction *action, LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
 	logview->fontsize = logview->original_fontsize;
 	logview_set_fontsize (logview);
@@ -674,47 +801,56 @@ logview_normal_text (GtkAction *action, LogviewWindow *logview)
 static void
 logview_search (GtkAction *action, LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
     
-    gtk_widget_show_all (logview->find_bar);
-    logview_findbar_grab_focus (LOGVIEW_FINDBAR (logview->find_bar));
+	gtk_widget_show_all (logview->find_bar);
+	logview_findbar_grab_focus (LOGVIEW_FINDBAR (logview->find_bar));
 }
 
 static void
 logview_copy (GtkAction *action, LogviewWindow *logview)
 {
 	gchar *text, **lines;
-	int nline, i, l1, l2;
-    gchar *line;
-    Log *log;
-    GtkClipboard *clipboard;
-
-    g_assert (LOGVIEW_IS_WINDOW (logview));
-
-    log = logview->curlog;
-    if (log == NULL)
-        return;
-
-    l1 = log->selected_line_first;
-    l2 = log->selected_line_last;
-    if (l1 == -1 || l2 == -1)
-        return;
-
-    nline = l2 - l1 + 1;
-    lines = g_new0(gchar *, (nline + 1));
-    for (i=0; i<=nline; i++) {
-        line = log->lines[l1+i];
-        lines[i] = g_strdup (line);
-    }
-    lines[nline] = NULL;
-    text = g_strjoinv ("\n", lines);
-
-    clipboard = gtk_widget_get_clipboard (GTK_WIDGET (logview->view),
-                                          GDK_SELECTION_CLIPBOARD);
-    gtk_clipboard_set_text (clipboard, text, -1);
-
-    g_free (text);
-    g_strfreev (lines);
+	int nline, i;
+	gchar *line;
+	GtkTreeIter iter;
+	GtkTreePath *selected_path;
+	Log *log;
+	GtkClipboard *clipboard;
+	GList *selected_paths, *idx;
+	GtkTreeModel *model;
+
+	g_assert (LOGVIEW_IS_WINDOW (logview));
+
+	log = logview->curlog;
+	g_assert (LOGVIEW_IS_LOG (log));
+
+	g_object_get (G_OBJECT (log),
+		      "selected-paths", &selected_paths,
+		      "model", &model,
+		      NULL);
+
+	if (selected_paths == NULL)
+		return;
+
+	nline = g_list_length (selected_paths);
+	lines = g_new0(gchar *, (nline + 1));
+	lines[nline] = NULL;
+	for (i = 0, idx = selected_paths; idx != NULL; idx = g_list_next (idx), i++) {
+		selected_path = idx->data;
+		gtk_tree_model_get_iter (model, &iter, selected_path);
+                gtk_tree_model_get (model,
+				    &iter,
+				    MESSAGE, &lines[i],
+				    -1);
+	}
+	text = g_strjoinv ("\n", lines);
+	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (logview->view),
+					      GDK_SELECTION_CLIPBOARD);
+	gtk_clipboard_set_text (clipboard, text, -1);
+
+	g_free (text);
+	g_strfreev (lines);
 }
 
 static void
@@ -722,7 +858,7 @@ logview_select_all (GtkAction *action, LogviewWindow *logview)
 {
 	GtkTreeSelection *selection;
 
-    g_assert (LOGVIEW_IS_WINDOW (logview));
+	g_assert (LOGVIEW_IS_WINDOW (logview));
 
 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (logview->view));
 	gtk_tree_selection_select_all (selection);
@@ -731,12 +867,12 @@ logview_select_all (GtkAction *action, LogviewWindow *logview)
 static void
 logview_menu_item_toggle_set_active (LogviewWindow *logview, char *path, gboolean state)
 {
-    GtkToggleAction *action;
+	GtkToggleAction *action;
     
-    g_assert (path);
+	g_assert (path);
     
-    action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (logview->ui_manager, path));
-    gtk_toggle_action_set_active (action, state);
+	action = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (logview->ui_manager, path));
+	gtk_toggle_action_set_active (action, state);
 }
 
 static void
@@ -753,22 +889,26 @@ logview_menu_item_set_state (LogviewWindow *logview, char *path, gboolean state)
 static void
 logview_calendar_set_state (LogviewWindow *logview)
 {
-    g_assert (LOGVIEW_IS_WINDOW (logview));
-
-    if (logview->curlog) {
-        if (logview->curlog->days != NULL)
-            calendar_init_data (CALENDAR (logview->calendar), logview);
-        gtk_widget_set_sensitive (logview->calendar, (logview->curlog->days != NULL));
-    } else
-        gtk_widget_set_sensitive (logview->calendar, FALSE);
+	g_assert (LOGVIEW_IS_WINDOW (logview));
+	gboolean groupable;
+	
+	if (logview->curlog) {
+		g_object_get (G_OBJECT (logview->curlog),
+			      "groupable", &groupable,
+			      NULL);
+		if (groupable)
+			calendar_init_data (CALENDAR (logview->calendar), logview);
+		gtk_widget_set_sensitive (logview->calendar, groupable);
+	} else
+		gtk_widget_set_sensitive (logview->calendar, FALSE);
 }
 
 static void
 logview_help (GtkAction *action, GtkWidget *parent_window)
 {
-    GError *error = NULL;                                                                                
-    gnome_help_display_desktop_on_screen (NULL, "gnome-system-log", "gnome-system-log", NULL,
-                                          gtk_widget_get_screen (GTK_WIDGET(parent_window)), &error);
+	GError *error = NULL;                                                                                
+	gnome_help_display_desktop_on_screen (NULL, "gnome-system-log", "gnome-system-log", NULL,
+					      gtk_widget_get_screen (GTK_WIDGET(parent_window)), &error);
 	if (error) {
 		error_dialog_show (GTK_WIDGET(parent_window), _("There was an error displaying help."), error->message);
 		g_error_free (error);
@@ -777,188 +917,175 @@ logview_help (GtkAction *action, GtkWidget *parent_window)
 
 static gboolean 
 window_size_changed_cb (GtkWidget *widget, GdkEventConfigure *event, 
-				 gpointer data)
+			gpointer data)
 {
-    LogviewWindow *logview = LOGVIEW_WINDOW (data);
+	LogviewWindow *logview = LOGVIEW_WINDOW (data);
     
-    g_assert (LOGVIEW_IS_WINDOW (logview));
-    prefs_store_window_size (GTK_WIDGET(logview));
-    return FALSE;
+	g_assert (LOGVIEW_IS_WINDOW (logview));
+	prefs_store_window_size (GTK_WIDGET(logview));
+	return FALSE;
 }
 
 static void
 logview_window_finalize (GObject *object)
 {
-    LogviewWindow *logview = LOGVIEW_WINDOW(object);
+	LogviewWindow *logview = LOGVIEW_WINDOW(object);
+
+	logview_plugin_list_delete (logview->plugin_list);
 
-    g_object_unref (logview->ui_manager);
-    parent_class->finalize (object);
+	g_object_unref (logview->ui_manager);
+	parent_class->finalize (object);
 }
 
 static void
 logview_init (LogviewWindow *logview)
 {
-   GtkWidget *vbox;
-   GtkTreeStore *tree_store;
-   GtkTreeSelection *selection;
-   GtkTreeViewColumn *column;
-   GtkCellRenderer *renderer;
-   gint i;
-   GtkActionGroup *action_group;
-   GtkAccelGroup *accel_group;
-   GError *error = NULL;
-   GtkWidget *menubar;
-   GtkWidget *hpaned;
-   GtkWidget *label;
-   GtkWidget *main_view;
-   GtkWidget *loglist_scrolled, *scrolled;
-   PangoContext *context;
-   PangoFontDescription *fontdesc;
-   gchar *monospace_font_name;
-
-   gtk_window_set_default_size (GTK_WINDOW (logview), prefs_get_width (), prefs_get_height ());
-
-   vbox = gtk_vbox_new (FALSE, 0);
-   gtk_container_add (GTK_CONTAINER (logview), vbox);
-
-   /* Create menus */
-   action_group = gtk_action_group_new ("LogviewMenuActions");
-   gtk_action_group_set_translation_domain (action_group, NULL);
-   gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), logview);
-   gtk_action_group_add_toggle_actions(action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), logview);
-
-   logview->ui_manager = gtk_ui_manager_new ();
-
-   gtk_ui_manager_insert_action_group (logview->ui_manager, action_group, 0);
+	GtkWidget *vbox;
+	GtkTreeSelection *selection;
+	GtkTreeViewColumn *column;
+	GtkCellRenderer *renderer;
+	GtkActionGroup *action_group;
+	GtkAccelGroup *accel_group;
+	GError *error = NULL;
+	GtkWidget *menubar;
+	GtkWidget *hpaned;
+	GtkWidget *label;
+	GtkWidget *main_view;
+	GtkWidget *loglist_scrolled, *scrolled;
+	PangoContext *context;
+	PangoFontDescription *fontdesc;
+	gchar *monospace_font_name;
+
+	gtk_window_set_default_size (GTK_WINDOW (logview), prefs_get_width (), prefs_get_height ());
+
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_container_add (GTK_CONTAINER (logview), vbox);
+
+	/* Create menus */
+	action_group = gtk_action_group_new ("LogviewMenuActions");
+	gtk_action_group_set_translation_domain (action_group, NULL);
+	gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), logview);
+	gtk_action_group_add_toggle_actions(action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), logview);
+
+	logview->ui_manager = gtk_ui_manager_new ();
+
+	gtk_ui_manager_insert_action_group (logview->ui_manager, action_group, 0);
    
-   accel_group = gtk_ui_manager_get_accel_group (logview->ui_manager);
-   gtk_window_add_accel_group (GTK_WINDOW (logview), accel_group);
+	accel_group = gtk_ui_manager_get_accel_group (logview->ui_manager);
+	gtk_window_add_accel_group (GTK_WINDOW (logview), accel_group);
    
-   if (!gtk_ui_manager_add_ui_from_string (logview->ui_manager, ui_description, -1, &error)) {
-     logview->ui_manager = NULL;
-     return;
-   }
+	if (!gtk_ui_manager_add_ui_from_string (logview->ui_manager, ui_description, -1, &error)) {
+		logview->ui_manager = NULL;
+		return;
+	}
    
-   menubar = gtk_ui_manager_get_widget (logview->ui_manager, "/LogviewMenu");
-   gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
+	menubar = gtk_ui_manager_get_widget (logview->ui_manager, "/LogviewMenu");
+	gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
 
-   /* panes */
-   hpaned = gtk_hpaned_new ();
-   gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
+	/* panes */
+	hpaned = gtk_hpaned_new ();
+	gtk_box_pack_start (GTK_BOX (vbox), hpaned, TRUE, TRUE, 0);
 
-   /* First pane : sidebar (list of logs + calendar) */
-   logview->sidebar = gtk_vbox_new (FALSE, 0);
-   
-   logview->calendar = calendar_new ();
-   calendar_connect (CALENDAR (logview->calendar), logview);
-   gtk_box_pack_end (GTK_BOX (logview->sidebar), GTK_WIDGET(logview->calendar), FALSE, FALSE, 0);
+	/* First pane : sidebar (list of logs + calendar) */
+	logview->sidebar = gtk_vbox_new (FALSE, 0);
    
-   /* log list */
-   loglist_scrolled = gtk_scrolled_window_new (NULL, NULL);
-   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (loglist_scrolled),
-                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
-   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (loglist_scrolled),
-                                        GTK_SHADOW_ETCHED_IN);
-   logview->loglist = loglist_new ();
-   gtk_container_add (GTK_CONTAINER (loglist_scrolled), logview->loglist);
-   gtk_box_pack_start (GTK_BOX (logview->sidebar), loglist_scrolled, TRUE, TRUE, 0);
-   gtk_paned_pack1 (GTK_PANED (hpaned), logview->sidebar, FALSE, FALSE);
-   loglist_connect (LOG_LIST(logview->loglist), logview);
-
-   /* Second pane : log */
-   main_view = gtk_vbox_new (FALSE, 0);
-   gtk_paned_pack2 (GTK_PANED (hpaned), GTK_WIDGET (main_view), TRUE, TRUE);
-
-   /* Scrolled window for the main view */
-   scrolled = gtk_scrolled_window_new (NULL, NULL);
-   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
-               GTK_POLICY_AUTOMATIC,
-               GTK_POLICY_AUTOMATIC);
-   gtk_box_pack_start (GTK_BOX(main_view), scrolled, TRUE, TRUE, 0);
-
-   /* Main Tree View */
-   logview->view = gtk_tree_view_new ();
-   gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (logview->view), FALSE);
-   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (logview->view), FALSE);
-
-   /* Use the desktop monospace font */
-   monospace_font_name = prefs_get_monospace ();
-   logview_set_font (logview, monospace_font_name);
-   g_free (monospace_font_name);
-
-   renderer = gtk_cell_renderer_text_new ();
-   column = gtk_tree_view_column_new ();
-   gtk_tree_view_column_pack_start (column, renderer, TRUE);
-   gtk_tree_view_column_set_attributes (column, renderer, 
-                                        "text", LOG_LINE_TEXT, 
-                                        "weight", LOG_LINE_WEIGHT,
-                                        "weight-set", LOG_LINE_WEIGHT_SET,
-                                        NULL);
-   //gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
-   gtk_tree_view_append_column (GTK_TREE_VIEW (logview->view), column);
-
-   /* Version selector */
-   logview->version_bar = gtk_hbox_new (FALSE, 0);
-   gtk_container_set_border_width (GTK_CONTAINER (logview->version_bar), 3);
-   logview->version_selector = gtk_combo_box_new_text ();
-   g_signal_connect (G_OBJECT (logview->version_selector), "changed",
-                     G_CALLBACK (logview_version_selector_changed), logview);
-   label = gtk_label_new (_("Version: "));
+	logview->calendar = calendar_new ();
+	calendar_connect (CALENDAR (logview->calendar), logview);
+	gtk_box_pack_end (GTK_BOX (logview->sidebar), GTK_WIDGET(logview->calendar), FALSE, FALSE, 0);
    
-   gtk_box_pack_end (GTK_BOX(logview->version_bar), logview->version_selector, FALSE, FALSE, 0);
-   gtk_box_pack_end (GTK_BOX(logview->version_bar), label, FALSE, FALSE, 0);
-   gtk_box_pack_end (GTK_BOX(main_view), logview->version_bar, FALSE, FALSE, 0);
+	/* log list */
+	loglist_scrolled = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (loglist_scrolled),
+					GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (loglist_scrolled),
+					     GTK_SHADOW_ETCHED_IN);
+	logview->loglist = loglist_new ((gpointer)logview);
+	gtk_container_add (GTK_CONTAINER (loglist_scrolled), logview->loglist);
+	gtk_box_pack_start (GTK_BOX (logview->sidebar), loglist_scrolled, TRUE, TRUE, 0);
+	gtk_paned_pack1 (GTK_PANED (hpaned), logview->sidebar, FALSE, FALSE);
+
+	/* Second pane : log */
+	main_view = gtk_vbox_new (FALSE, 0);
+	gtk_paned_pack2 (GTK_PANED (hpaned), GTK_WIDGET (main_view), TRUE, TRUE);
+
+	/* Scrolled window for the main view */
+	scrolled = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+	gtk_box_pack_start (GTK_BOX(main_view), scrolled, TRUE, TRUE, 0);
+
+	/* Main Tree View */
+	logview->view = gtk_tree_view_new ();
+	gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (logview->view), FALSE);
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (logview->view), FALSE);
+
+	/* Use the desktop monospace font */
+	monospace_font_name = prefs_get_monospace ();
+	logview_set_font (logview, monospace_font_name);
+	g_free (monospace_font_name);
+
+	renderer = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+	gtk_tree_view_column_set_attributes (column, renderer, 
+					     "text", LOG_LINE_TEXT, 
+					     "weight", LOG_LINE_WEIGHT1,
+					     "weight-set", LOG_LINE_WEIGHT_SET1,
+					     NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (logview->view), column);
+
+	/* Version selector */
+	logview->version_bar = gtk_hbox_new (FALSE, 0);
+	gtk_container_set_border_width (GTK_CONTAINER (logview->version_bar), 3);
+	logview->version_selector = gtk_combo_box_new_text ();
+	g_signal_connect (G_OBJECT (logview->version_selector), "changed",
+			  G_CALLBACK (logview_version_selector_changed), logview);
+	label = gtk_label_new (_("Version: "));
    
-   logview->find_bar = logview_findbar_new ();
-   gtk_box_pack_end (GTK_BOX (main_view), logview->find_bar, FALSE, FALSE, 0);
-   logview_findbar_connect (LOGVIEW_FINDBAR (logview->find_bar), logview);
-
-   /* Remember the original font size */
-   context = gtk_widget_get_pango_context (logview->view);
-   fontdesc = pango_context_get_font_description (context);
-   logview->original_fontsize = pango_font_description_get_size (fontdesc) / PANGO_SCALE;
-   logview->fontsize = logview->original_fontsize;
-
-   gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (logview->view));
-   gtk_widget_show_all (scrolled);
+	gtk_box_pack_end (GTK_BOX(logview->version_bar), logview->version_selector, FALSE, FALSE, 0);
+	gtk_box_pack_end (GTK_BOX(logview->version_bar), label, FALSE, FALSE, 0);
+	gtk_box_pack_end (GTK_BOX(main_view), logview->version_bar, FALSE, FALSE, 0);
    
-   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (logview->view));
-   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
-
-   /* Add signal handlers */
-   g_signal_connect (G_OBJECT (selection), "changed",
-                     G_CALLBACK (selection_changed_cb), logview);
-   g_signal_connect (G_OBJECT (logview->view), "row-expanded",
-                     G_CALLBACK (row_toggled_cb), logview);
-   g_signal_connect (G_OBJECT (logview->view), "row-collapsed",
-                     G_CALLBACK (row_toggled_cb), logview);
-   g_signal_connect (G_OBJECT (logview), "configure_event",
-                     G_CALLBACK (window_size_changed_cb), logview);
-
-   /* Status area at bottom */
-   logview->statusbar = gtk_statusbar_new ();
-   gtk_box_pack_start (GTK_BOX (vbox), logview->statusbar, FALSE, FALSE, 0);
-
-   gtk_widget_show (menubar);
-   gtk_widget_show (loglist_scrolled);
-   gtk_widget_show (main_view);
-   gtk_widget_show (vbox);
-   logview->hpaned = hpaned;
-}
+	logview->find_bar = logview_findbar_new ();
+	gtk_box_pack_end (GTK_BOX (main_view), logview->find_bar, FALSE, FALSE, 0);
+	logview_findbar_connect (LOGVIEW_FINDBAR (logview->find_bar), logview);
 
-static void
-logview_window_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec)
-{
-  LogviewWindow *logview = LOGVIEW_WINDOW (object);
-  
-  switch (param_id) {
-  case PROP_DAYS:
-    g_value_set_pointer (value, logview->curlog->days);
-    break;
-  default:
-    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-    break;
-  }
+	/* Remember the original font size */
+	context = gtk_widget_get_pango_context (logview->view);
+	fontdesc = pango_context_get_font_description (context);
+	logview->original_fontsize = pango_font_description_get_size (fontdesc) / PANGO_SCALE;
+	logview->fontsize = logview->original_fontsize;
+
+	gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (logview->view));
+	gtk_widget_show_all (scrolled);
+   
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (logview->view));
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+
+	/* Add signal handlers */
+	g_signal_connect (G_OBJECT (selection), "changed",
+			  G_CALLBACK (selection_changed_cb), logview);
+	g_signal_connect (G_OBJECT (logview->view), "row-expanded",
+			  G_CALLBACK (row_toggled_cb), logview);
+	g_signal_connect (G_OBJECT (logview->view), "row-collapsed",
+			  G_CALLBACK (row_toggled_cb), logview);
+	g_signal_connect (G_OBJECT (logview), "configure_event",
+			  G_CALLBACK (window_size_changed_cb), logview);
+
+	/* Status area at bottom */
+	logview->statusbar = gtk_statusbar_new ();
+	gtk_box_pack_start (GTK_BOX (vbox), logview->statusbar, FALSE, FALSE, 0);
+
+	gtk_widget_show (menubar);
+	gtk_widget_show (loglist_scrolled);
+	gtk_widget_show (main_view);
+	gtk_widget_show (vbox);
+	logview->hpaned = hpaned;
+	logview->plugin_list = logview_plugin_list_new ();
+
+	/* set logview pointer */
+	monitor_set_window ((gpointer)logview);
 }
 
 static void
@@ -967,14 +1094,7 @@ logview_window_class_init (LogviewWindowClass *klass)
 	GObjectClass *object_class = (GObjectClass *) klass;
 
 	object_class->finalize = logview_window_finalize;
-	object_class->get_property = logview_window_get_property;
 	parent_class = g_type_class_peek_parent (klass);
-
-	g_object_class_install_property (object_class, PROP_DAYS,
-					 g_param_spec_pointer ("days",
-					 _("Days"),
-					 _("Pointer towards a GSList of days for the current log."),
-					 (G_PARAM_READABLE)));
 }
 
 GType
@@ -1004,21 +1124,28 @@ logview_window_get_type (void)
 GtkWidget *
 logview_window_new ()
 {
-   LogviewWindow *logview;
-   GtkWidget *window;
+	LogviewWindow *logview;
+	GtkWidget *window;
 
-   window = g_object_new (LOGVIEW_TYPE_WINDOW, NULL);
-   logview = LOGVIEW_WINDOW (window);
-   if (logview->ui_manager == NULL)
-     return NULL;
+	window = g_object_new (LOGVIEW_TYPE_WINDOW, NULL);
+	logview = LOGVIEW_WINDOW (window);
+	if (logview->ui_manager == NULL)
+		return NULL;
 
-   logview->logs = NULL;
+	logview->logs = NULL;
 
-   gtk_ui_manager_set_add_tearoffs (logview->ui_manager, 
-                                    prefs_get_have_tearoff ());
+	gtk_ui_manager_set_add_tearoffs (logview->ui_manager, 
+					 prefs_get_have_tearoff ());
 
-   g_signal_connect (GTK_OBJECT (window), "destroy",
-                     G_CALLBACK (logview_destroy), logview);
+	g_signal_connect (GTK_OBJECT (window), "destroy",
+			  G_CALLBACK (logview_destroy), logview);
 
-   return window;
+	return window;
+}
+
+static void
+logview_plugin_dialog_show (GtkAction *action, LogviewWindow *logview)
+{
+	g_assert (LOGVIEW_IS_WINDOW (logview));
+	logview_plugin_list_show (logview->plugin_list, GTK_WIDGET (logview));
 }
diff --git a/logview/logview.h b/logview/logview.h
index dacfa2e..1e826d6 100644
--- a/logview/logview.h
+++ b/logview/logview.h
@@ -23,6 +23,7 @@
 #define __LOGVIEW_H__
 
 #include "logrtns.h"
+#include "logview-plugin-list.h"
 
 #define MAX_VERSIONS             5
 
@@ -39,19 +40,20 @@ typedef struct _LogviewWindowClass LogviewWindowClass;
 struct _LogviewWindow {
 	GtkWindow parent_instance;
 
-	GtkWidget *view;		
+	GtkWidget *view;
 	GtkWidget *statusbar;
 	GtkUIManager *ui_manager;
 
 	GtkWidget *calendar;
 	GtkWidget *find_bar;
 	GtkWidget *loglist;
-	GtkWidget *sidebar; 
+	GtkWidget *sidebar;
 	GtkWidget *version_bar;
 	GtkWidget *version_selector;
-        GtkWidget *hpaned;
-    
-        GSList *logs;
+	GtkWidget *hpaned;
+	LogviewPluginList *plugin_list;
+	
+	GSList *logs;
 	Log *curlog;
 
 	int original_fontsize, fontsize;
diff --git a/logview/main.c b/logview/main.c
index 37242b8..05e17af 100644
--- a/logview/main.c
+++ b/logview/main.c
@@ -36,6 +36,8 @@
 #include "logview.h"
 #include "misc.h"
 #include "userprefs.h"
+#include "logview-plugin-manager.h"
+#include "logview-debug.h"
 
 static gboolean show_version = FALSE;
 
@@ -86,7 +88,6 @@ save_session_cb (GnomeClient        *gnome_client,
 	gchar **argv;
 	gint numlogs;
 	GSList *logs;
-	Log *log;
 	gint i = 0;
 
 	g_assert (LOGVIEW_IS_WINDOW (logview));
@@ -98,12 +99,11 @@ save_session_cb (GnomeClient        *gnome_client,
 	argv = g_new0 (gchar *, numlogs + 2);
 	argv[i++] = g_get_prgname();
 
-	for (logs = logview->logs; logs != NULL; logs = logs->next) {
+	for (i = 1, logs = logview->logs; logs != NULL; logs = logs->next) {
 		Log *log = (Log *) logs->data;
 
 		g_assert (log != NULL);
-
-		argv[i++] = g_strdup (log->name);
+		g_object_get (G_OBJECT (log), "path", &argv[i++], NULL);
 	}
 	
         argv[i] = NULL;
@@ -120,7 +120,6 @@ int
 main (int argc, char *argv[])
 {
 	GnomeClient *gnome_client;
-	GError *error;
 	GnomeProgram *program;
 	GOptionContext *context;
 	LogviewWindow *logview;
@@ -129,10 +128,6 @@ main (int argc, char *argv[])
 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 	textdomain (GETTEXT_PACKAGE);
 
-	error_dialog_queue (TRUE);
-	
-	gnome_vfs_init ();
-	prefs_init ();
 	context = logview_init_options ();
 	
 	program = gnome_program_init ("gnome-system-log", VERSION,
@@ -140,20 +135,42 @@ main (int argc, char *argv[])
 				      argc, argv,
 				      GNOME_PARAM_APP_DATADIR, DATADIR,
 				      GNOME_PARAM_GOPTION_CONTEXT, context,
-				      NULL);
+				      GNOME_PARAM_NONE);
 	
 	g_set_application_name (_("Log Viewer"));
 
 	if (show_version)
 		logview_show_version_and_quit ();
-	
+
+	error_dialog_queue (TRUE);
+
+	g_type_init ();
+	if (! g_thread_supported ())
+		g_thread_init (NULL);
+
+	if (!create_home_dir ()) {
+		g_printf (_("Fatal error! Can't open $HOME/%s.\n"),
+			  LOGVIEW_HOME_SUFFIX);
+		exit (1);
+	}
+
+	gnome_vfs_init ();
+	logview_debug_init ();
+	pluginmgr_init ();
+	if (!pluginmgr_load_modules ()) {
+		error_dialog_show (NULL,
+				   _("Unable to load modules."),
+				   NULL);
+		exit (1);
+	}
+	prefs_init ();
+
 	/* Open regular logs and add each log passed as a parameter */
 	logview = LOGVIEW_WINDOW (logview_window_new ());
 	if (!logview) {
 		error_dialog_show (NULL,
 				   _("Unable to create user interface."),
 				   NULL);
-		
 		exit (0);
 	}
 
@@ -174,7 +191,7 @@ main (int argc, char *argv[])
 	} else {
 		gint i;
 		
-		for (i = 1; i < argc; i++)
+ 		for (i = 1; i < argc; i++) 
 			logview_add_log_from_name (logview, argv[i]);
 	}
 
@@ -191,6 +208,10 @@ main (int argc, char *argv[])
 	}
 
 	gtk_main ();
-   
+
+	pluginmgr_destroy ();
+	logview_debug_destroy ();
+	gnome_vfs_shutdown ();
 	return EXIT_SUCCESS;
 }
+
diff --git a/logview/misc.c b/logview/misc.c
index 09db2c7..57cf343 100644
--- a/logview/misc.c
+++ b/logview/misc.c
@@ -18,24 +18,45 @@
 
     ---------------------------------------------------------------------- */
 
+#define _XOPEN_SOURCE 500
+
 #ifdef __CYGWIN__
 #define timezonevar
 #endif
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
-#define _XOPEN_SOURCE
+
 #include <time.h>
 #include <string.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
 #endif
 
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include "logrtns.h"
+#include "logview-plugin-manager.h"
+#include "logview-debug.h"
+#include "misc.h"
+
+#define LOGVIEW_DATA_PLUGIN_PATH LOGVIEWPLUGINDIR
+#define LOGVIEW_USER_PLUGIN_PATH_SUFFIX LOGVIEW_HOME_SUFFIX"/plugins"
 
+char *error_main = N_("One file or more could not be opened");
 static gboolean queue_err_messages = FALSE;
 static GSList *msg_queue_main = NULL, *msg_queue_sec = NULL;
 const char *month[12] =
@@ -44,7 +65,7 @@ const char *month[12] =
  N_("November"), N_("December")};
 
 static void
-error_dialog_run (GtkWidget *window, const char *main, char *secondary)
+error_dialog_run (GtkWidget *window, const char *main, const char *secondary)
 {
 	GtkWidget *dialog;
 	dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window),
@@ -60,13 +81,13 @@ error_dialog_run (GtkWidget *window, const char *main, char *secondary)
 }
 
 void
-error_dialog_show (GtkWidget *window, char *main, char *secondary)
+error_dialog_show (GtkWidget *window, const char *main, const char *secondary)
 {
-    if (queue_err_messages) {
-        msg_queue_main = g_slist_append (msg_queue_main, g_strdup (main));
-        msg_queue_sec = g_slist_append (msg_queue_sec, g_strdup (secondary));
-    } else
-        error_dialog_run (window, main, secondary);
+	if (queue_err_messages) {
+		msg_queue_main = g_slist_append (msg_queue_main, g_strdup (main));
+		msg_queue_sec = g_slist_append (msg_queue_sec, g_strdup (secondary));
+	} else
+		error_dialog_run (window, main, secondary);
 }
 
 void
@@ -116,63 +137,599 @@ error_dialog_show_queued (void)
 	}
 }
 
+char*
+error_system_string (void)
+{
+	return strerror (errno);
+}
+
 char *
 locale_to_utf8 (const char *in)
 {
-  char *out;
+	char *out;
   
-  if (g_utf8_validate (in, -1, NULL))
-	out = g_strdup (in);
-  else {
-	out = g_locale_to_utf8 (in, -1, NULL, NULL, NULL);
-	if (out == NULL)
-	  out = g_strdup ("?");
-  }
+	if (g_utf8_validate (in, -1, NULL))
+		out = g_strdup (in);
+	else {
+		out = g_locale_to_utf8 (in, -1, NULL, NULL, NULL);
+		if (out == NULL)
+			out = g_strdup ("?");
+	}
 
-  return out;
+	return out;
 }
 
 GDate *
 string_get_date (char *line)
 {
-    GDate *date;
-    struct tm tp;
-    char *cp;
-    
-    if (line == NULL || line[0] == 0)
-        return NULL;
-
-    cp = strptime (line, "%b %d", &tp);
-    if (cp == NULL) {
-        cp = strptime (line, "%F", &tp);
-        if (cp == NULL) {
-            return NULL;
-        }
-    }
+	GDate *date;
+	struct tm tp;
+	char *cp;
+	char *tm_locale = NULL;
+
+	if (line == NULL || line[0] == '\0')
+		return NULL;
+	
+	if (tm_locale != NULL) {
+change_locale:
+		tm_locale = setlocale (LC_TIME, NULL);
+		setlocale (LC_TIME, "C");
+	}
 
-    date = g_date_new_dmy (tp.tm_mday, tp.tm_mon+1, 70);
-    return date;
+	if ((cp = strptime (line, "%b %d", &tp)) == NULL)
+		if ((cp = strptime (line, "%a %b %d", &tp)) == NULL)
+			if ((cp = strptime (line, "%F", &tp)) == NULL) {
+				if (tm_locale == NULL)
+					goto change_locale;
+				else {
+					setlocale (LC_TIME, tm_locale);
+					return NULL;
+				}
+			}
+
+	if (tm_locale != NULL) {
+		setlocale (LC_TIME, tm_locale);
+	}
+
+	date = g_date_new_dmy (tp.tm_mday, tp.tm_mon+1, 70);
+	return date;
 }
 
 char *
-date_get_string (GDate *date)
+date_to_string (GDate *date)
 {
-   char buf[512];
-   char *utf8;
+	char buf[512];
+	char *utf8;
 
-   if (date == NULL || !g_date_valid (date)) {
-       utf8 = g_strdup(_("Invalid date"));
-       return utf8;
-   }
+	if (date == NULL || !g_date_valid (date)) {
+		utf8 = g_strdup(_("Invalid date"));
+		return utf8;
+	}
    
-   /* Translators: Only date format, time will be bogus */
-   if (g_date_strftime (buf, sizeof (buf), _("%x"), date) == 0) {
-       int m = g_date_get_month (date);
-       int d = g_date_get_day (date);
-       /* If we fail just use the US format */
-       utf8 = g_strdup_printf ("%s %d", _(month[(int) m-1]), d);
-   } else
-       utf8 = locale_to_utf8 (buf);
+	/* Translators: Only date format, time will be bogus */
+	if (g_date_strftime (buf, sizeof (buf), _("%x"), date) == 0) {
+		int m = g_date_get_month (date);
+		int d = g_date_get_day (date);
+		/* If we fail just use the US format */
+		utf8 = g_strdup_printf ("%s %d", _(month[(int) m-1]), d);
+	} else
+		utf8 = locale_to_utf8 (buf);
    
-   return utf8;
+	return utf8;
 }
+
+/* File checking */
+
+gboolean
+try_to_open (const gchar *filename, gboolean show_error)
+{
+	GnomeVFSHandle *handle;
+	GnomeVFSResult result;
+	char *secondary = NULL;
+
+	if (filename == NULL)
+	   return FALSE;
+
+	result = gnome_vfs_open (&handle, filename, GNOME_VFS_OPEN_READ);
+	if (result != GNOME_VFS_OK) {
+	   if (show_error) {
+		   switch (result) {
+		   case GNOME_VFS_ERROR_ACCESS_DENIED:
+		   case GNOME_VFS_ERROR_NOT_PERMITTED:
+			   secondary = g_strdup_printf (_("%s is not user readable. "
+					 "Either run the program as root or ask the sysadmin to "
+					 "change the permissions on the file.\n"), filename);
+			   break;
+		   case GNOME_VFS_ERROR_TOO_BIG:
+			   secondary = g_strdup_printf (_("%s is too big."), filename);
+			   break;
+		   default:
+			   secondary = g_strdup_printf (_("%s could not be opened."), filename);
+			   break;
+		   }
+		   error_dialog_show (NULL, error_main, secondary);
+		   g_free (secondary);
+	   }
+	   return FALSE;
+	}
+
+	gnome_vfs_close (handle);
+	return TRUE;
+}
+
+gchar *
+log_extract_dirname (const gchar *logname)
+{
+	GnomeVFSURI *uri;
+	gchar *dirname;
+
+	uri = gnome_vfs_uri_new (logname);
+	dirname = gnome_vfs_uri_extract_dirname (uri);
+	gnome_vfs_uri_unref (uri);
+
+	return dirname;
+}
+
+gchar *
+log_extract_filename (const gchar *logname)
+{
+	GnomeVFSURI *uri;
+	gchar *filename;
+
+	uri = gnome_vfs_uri_new (logname);
+	filename = gnome_vfs_uri_extract_short_name (uri);
+	gnome_vfs_uri_unref (uri);
+
+	return filename;
+}
+
+void
+extract_filepath (const gchar *logname, gchar** dirname, gchar** filename)
+{
+	GnomeVFSURI *uri;
+
+	uri = gnome_vfs_uri_new (logname);
+	if (dirname != NULL) *dirname = gnome_vfs_uri_extract_dirname (uri);
+	if (filename != NULL) *filename = gnome_vfs_uri_extract_short_name (uri);
+	gnome_vfs_uri_unref (uri);	
+}
+
+gchar *
+get_date_string (gchar *line)
+{
+	gchar **split, *date_string;
+	gchar *month=NULL, *day=NULL;
+	int i=0;
+
+	if (line == NULL || line[0] == 0)
+		return NULL;
+
+	split = g_strsplit (line, " ", 4);
+	if (split == NULL)
+		return NULL;
+
+	while ((day == NULL || month == NULL) && split[i]!=NULL && i<4) {
+		if (g_str_equal (split[i], "")) {
+			i++;
+			continue;
+		}
+
+		if (month == NULL) {
+			month = split[i++];
+			/* If the first field begins by a number, the date
+			   is given in yyyy-mm-dd format */
+			if (!g_ascii_isalpha (month[0]))
+				break;
+			continue;
+		}
+
+		if (day == NULL)
+			day = split[i];
+		i++;
+	}
+
+	if (i==3)
+		date_string = g_strconcat (month, "  ", day, NULL);
+	else
+		date_string = g_strconcat (month, " ", day, NULL);
+	g_strfreev (split);
+	return (date_string);
+}
+
+LogProtocol
+file_protocol (const char *filename)
+{
+	LogProtocol protocol;
+
+	LV_MARK;
+	g_assert (filename);
+	g_assert (filename[0] != '\0');
+	
+	if (local_file_exist (filename)) {
+		char *mime_type = gnome_vfs_get_mime_type (filename);
+		LV_INFO ("[protocol] %s detected mime: %s",
+			 filename,
+			 mime_type != NULL ? mime_type : "(nil)");
+		if (mime_type == NULL) {
+			protocol = UNKNOWN_LOG;
+		} else if (g_str_has_prefix (mime_type, "text/")) {
+			protocol = PLAIN_LOG;
+		} else if (strcmp (mime_type, "application/octet-stream")==0) {
+			protocol =  PIPE_LOG;
+		} else if (strcmp (mime_type, "application/x-gzip")==0 ||
+			   strcmp (mime_type, "application/x-zip")==0 ||
+			   strcmp (mime_type, "application/zip")==0) {
+			protocol =  GZIP_LOG;
+		} else if (g_str_has_prefix (mime_type, "x-directory/") ||
+			   g_str_has_prefix (mime_type, "application/x-executable")) {
+			protocol = BAD_LOG;
+		} else {
+			protocol = UNKNOWN_LOG;
+		}
+		g_free (mime_type);
+	} else {
+		protocol = UNKNOWN_LOG;
+	}
+	LV_INFO ("[protocol] %s assigned protocol: %d", filename, protocol);
+	return protocol;
+}
+
+gboolean
+local_file_can_executed (const gchar *filename)
+{
+	struct stat buf;
+	if (stat (filename, &buf) == 0) {
+		return buf.st_uid == getuid() || buf.st_uid == 0;
+	}
+}
+
+gboolean
+local_file_exist (const gchar *filename)
+{
+	struct stat buf;
+	if (stat (filename, &buf) == 0) {
+		return S_ISBLK (buf.st_mode) ||
+			S_ISCHR (buf.st_mode) ||
+			S_ISDIR (buf.st_mode) ||
+			S_ISFIFO (buf.st_mode) ||
+			S_ISREG (buf.st_mode) ||
+			S_ISSOCK (buf.st_mode);
+	}
+	return FALSE;
+}
+
+gchar *
+processor_arch (void)
+{
+	static gchar cpu_arch[BUFSIZ] = {0};
+	/* get system ARCH for Solaris*/
+	if (*cpu_arch == 0) {
+#ifdef ON_SUN_OS
+#include <sys/systeminfo.h>
+	if (sysinfo(SI_ARCHITECTURE, cpu_arch, sizeof (cpu_arch)) == -1) {
+		LV_INFO_EE ("get sysinfo failed: %s", error_system_string ());
+	}
+#else
+#include <sys/utsname.h>
+	struct utsname buf;
+	if (uname(&buf) == -1) {
+		LV_INFO_EE ("get sysinfo failed: %s", error_system_string ());
+	}
+	strcpy(cpu_arch, buf.machine);
+#endif
+	}
+	LV_ERR ("ARCH = %s", cpu_arch);
+	return cpu_arch;
+}
+
+G_CONST_RETURN GSList*
+get_plugin_paths()
+{
+	static GSList *plugin_paths = NULL;
+	struct stat buf;
+	gchar* user_plugin_path = NULL;
+
+	if (plugin_paths)
+		return plugin_paths;
+
+	/* get user plugin path */
+	user_plugin_path = g_build_path (G_DIR_SEPARATOR_S,
+					 (gchar*)g_get_home_dir (),
+					 LOGVIEW_USER_PLUGIN_PATH_SUFFIX,
+					 processor_arch (),
+					 NULL);
+	if (stat (user_plugin_path, &buf) == 0) {
+		if (S_ISDIR (buf.st_mode)) {
+			plugin_paths = g_slist_append (plugin_paths,
+						       user_plugin_path);
+		} else {
+			g_free (user_plugin_path);
+		}
+	}
+	else if (errno == ENOENT &&
+		 g_mkdir_with_parents (user_plugin_path, get_umask()) == 0) {
+			plugin_paths = g_slist_append (plugin_paths,
+						       user_plugin_path);
+	}
+	else {
+		LV_ERR ("err|create user plugin path: %s", user_plugin_path);
+		g_free (user_plugin_path);
+		return NULL;
+	}
+	
+	/* get default plugin path */
+	if (stat (LOGVIEW_DATA_PLUGIN_PATH, &buf) == 0) {
+		if (S_ISDIR (buf.st_mode)) {
+			plugin_paths = g_slist_append(plugin_paths,
+						      (gpointer*)g_strdup (LOGVIEW_DATA_PLUGIN_PATH));
+		}
+	}
+	return plugin_paths;
+}
+
+/*
+ * Alternative popen due to the security issue
+ */
+
+typedef struct {
+	int fd;
+	pid_t pid;
+} pf_node; /* pair of pipe and file description */
+
+static GSList *pf_list = NULL;
+static void
+pf_insert (int fd, pid_t pid)
+{
+	pf_node *node = g_new0 (pf_node, 1);
+	g_assert (node);
+	node->fd = fd;
+	node->pid = pid;
+	pf_list = g_slist_prepend (pf_list, node);
+}
+
+static pid_t
+pf_delete (int fd)
+{
+	GSList *idx = NULL;
+	pid_t pid = -1;
+	for (idx = pf_list; idx; idx=g_slist_next (idx)) {
+		if (((pf_node *)idx->data)->fd == fd) {
+			pid = ((pf_node *)idx->data)->pid;
+			g_free (idx->data);
+			pf_list = g_slist_delete_link (pf_list, idx);
+			break;
+		}
+	}
+	return pid;
+}
+
+static GStaticMutex pipe_mutex = G_STATIC_MUTEX_INIT;
+#define PIPE_MUTEX_LOCK() (g_static_mutex_lock (&pipe_mutex))
+#define PIPE_MUTEX_UNLOCK() (g_static_mutex_unlock (&pipe_mutex))
+/**
+ * _security_popen_raw:
+ * @cmd: 
+ * @argv: directly pass to execve, so should be NULL terminated.
+ * @envp: directly pass to execve.
+ * 
+ * Only support read.
+ * 
+ * Note that, the path is restricted in "/usr/bin:/usr/sbin".
+ * 
+ * RETURNS: the pointer of the file stream structure.
+ */
+static FILE*
+_security_popen (const char *path, char *const argv[], char *const envp[])
+{
+	int p[2];
+	pid_t pid;
+	FILE* iop;
+
+	if (path == NULL || argv == NULL)
+		return NULL;
+
+	if (pipe (p) < 0)
+		return NULL;
+
+	if ((iop = fdopen (p[0], "r")) == NULL) {
+		close (p[0]);
+		close (p[1]);
+		return NULL;
+	}
+
+	PIPE_MUTEX_LOCK();
+
+	if ((pid = vfork()) == 0) {
+		/* child */
+		GSList *idx = NULL;
+		int error;
+		close (p[0]);
+		if (p[1] != STDOUT_FILENO) {
+			if (dup2 (p[1], STDOUT_FILENO) == -1){
+				perror ("dup2(3C)");
+				exit (errno);
+			}
+			close (p[1]);
+		}
+
+		/* close all fds */
+		for (idx = pf_list; idx; idx = g_slist_next (idx)) {
+			close (((pf_node *)idx->data)->fd);
+			g_free (idx->data);
+		}
+		g_slist_free (pf_list);
+
+		error = execve (path, argv, envp);
+		if ( error == -1) {
+			perror ("execve(2)");
+			exit (errno);
+		}
+		exit (127);
+	}
+	else if (pid < 0) {
+		PIPE_MUTEX_UNLOCK();
+		close (p[0]);
+		close (p[1]);
+		fclose (iop);
+		return NULL;
+	}
+	/* parent */
+	PIPE_MUTEX_UNLOCK();
+	close (p[1]);
+	pf_insert (p[0], pid);
+	return iop;
+}
+
+/**
+ * security_pclose:
+ * @fp:
+ * 
+ */
+int
+security_pclose (FILE *fp)
+{
+#ifdef ON_SUN_OS
+	pid_t pid;
+	int status;
+
+	if (fp == NULL)
+		return -1;
+
+	pid = pf_delete (fileno (fp));
+
+	fclose (fp);
+	if (pid == -1)
+		return -1;
+        while (waitpid(pid, &status, 0) < 0) {
+                if (errno != EINTR) {
+                        status = -1;
+                        break;
+                }
+        }
+        return status;
+#else
+	return pclose (fp);
+#endif
+}
+
+/**
+ * security_popen:
+ * @command: the command need be executed.
+ * @mode: for compatible usage in Linux, this function will call popen().
+ * In Solaris, it's useless.
+ * 
+ * Only support read.
+ * 
+ * Note that, the path is restricted in "/bin:/sbin:/usr/bin:/usr/sbin".
+ * 
+ * RETURNS: the pointer of the file stream structure.
+ */
+FILE*
+security_popen (const char *command, const char *mode)
+{
+#ifdef ON_SUN_OS
+	static char *const res_path[] = {"PATH=/bin:/sbin:/usr/bin:/usr/sbin", NULL};
+	static const char *shellenv = "SHELL";
+	static const char *pfsh_path = "/bin/pfsh";
+	static const char *pfsh = "pfsh";
+	static const char *pfcsh_path = "/bin/pfcsh";
+	static const char *pfcsh = "pfcsh";
+	static const char *pfksh_path = "/bin/pfksh";
+	static const char *pfksh = "pfksh";
+	static const char *sh_opt = "-c";
+	char *argve[4];
+	char *shpath = NULL;
+	char *shell = NULL;
+
+	shpath = (char*) pfsh_path;
+	shell = (char*) pfsh;
+	argve[0] = (char *) shell;
+	argve[1] = (char *) sh_opt;
+	argve[2] = (char *) command;
+	argve[3] = NULL;
+	return _security_popen (shpath, (char* const*)argve, res_path);
+#else
+	return popen (command, mode);
+#endif
+}
+
+static gchar *err_msg = NULL;
+G_CONST_RETURN gchar *
+log_error (void)
+{
+	return err_msg;
+}
+
+void
+log_error_init (const gchar *log_path)
+{
+	if (err_msg)
+		g_free (err_msg);
+	err_msg = g_strdup_printf ("%s :\n", log_path);
+}
+
+void
+log_error_append (const gchar *msg)
+{
+	gchar *tmp;
+	g_assert (msg);
+	tmp = g_strdup_printf ("%s\t%s\n", err_msg, msg);
+	g_free (err_msg);
+	err_msg = tmp;
+}
+
+void
+log_error_appendv (const gchar *format, ...)
+{
+	gchar *tmp, *err;
+	va_list ap;
+
+	g_assert (format);
+
+	va_start (ap, format);
+	err= g_strdup_vprintf (format, ap);
+	va_end (ap);
+
+	tmp = g_strdup_printf ("%s\t%s\n", err_msg, err);
+
+	g_free (err);
+	g_free (err_msg);
+	err_msg = tmp;
+}
+
+gint
+get_umask ()
+{
+	static gint current_umask=0;
+	if (current_umask == 0) {
+		mode_t umode = 0;
+		umode = umask (umode);
+		umask (umode);
+		current_umask = umode ^ 0777;
+	}
+	return current_umask;
+}
+
+gboolean
+create_home_dir (void)
+{
+	struct stat buf;
+	gboolean ret = FALSE;
+	gchar* logview_home = NULL;
+
+	/* get user plugin path */
+	logview_home = g_build_path (G_DIR_SEPARATOR_S,
+					 (gchar*)g_get_home_dir (),
+					 LOGVIEW_HOME_SUFFIX,
+					 NULL);
+	if (stat (logview_home, &buf) == 0) {
+		if (S_ISDIR (buf.st_mode) && (S_IRWXU & buf.st_mode)) {
+			ret = TRUE;
+		}
+	} else if (errno == ENOENT &&
+		   g_mkdir_with_parents (logview_home, get_umask()) == 0) {
+		ret = TRUE;
+	}
+	return ret;
+}
+
diff --git a/logview/misc.h b/logview/misc.h
index c0eb5ee..37d4e01 100644
--- a/logview/misc.h
+++ b/logview/misc.h
@@ -20,14 +20,41 @@
 #ifndef __LOG_MISC_H__
 #define __LOG_MISC_H__
 
-void error_dialog_show (GtkWidget *window, char *main, char *secondary);
+#include "logview-log.h"
+
+#define LOGVIEW_HOME_SUFFIX ".gnome2/gnome-system-log"
+
+void error_dialog_show (GtkWidget *window, const char *main, const char *secondary);
 void error_dialog_queue (gboolean do_queue);
 void error_dialog_show_queued (void);
+char* error_system_string (void);
 
 char *locale_to_utf8 (const char *in);
 GDate *string_get_date (char *line);
 int string_get_month (const char *str);
-char *date_get_string (GDate *date);
+char *date_to_string (GDate *date);
+
+gboolean file_is_log (char *filename, gboolean show_error);
+gchar *log_extract_dirname (const gchar *logname);
+gchar *log_extract_filename (const gchar *logname);
+
+void extract_filepath (const gchar *logname, gchar** dirname, gchar** filename);
+LogProtocol file_protocol (const char *filename);
+gboolean try_to_open (const gchar *filename, gboolean show_error);
+gboolean local_file_exist (const gchar *filename);
+gboolean local_file_can_executed (const gchar *filename);
+
+gchar * get_date_string (gchar *line);
+gchar * processor_arch (void);
+G_CONST_RETURN GSList* get_plugin_paths();
+FILE* security_popen (const char *cmd, const char *mode);
+int security_pclose (FILE *fp);
+
+G_CONST_RETURN gchar *log_error (void);
+void log_error_append (const gchar *msg);
+void log_error_init (const gchar *log_path);
+gint get_umask ();
+gboolean create_home_dir (void);
 
 #endif /* __LOG_MISC_H__ */
 
diff --git a/logview/monitor.c b/logview/monitor.c
index a156033..ff03b48 100644
--- a/logview/monitor.c
+++ b/logview/monitor.c
@@ -1,98 +1,163 @@
-/*  ----------------------------------------------------------------------
+/*	----------------------------------------------------------------------
 
-    Copyright (C) 1998  Cesar Miquel  (miquel@df.uba.ar)
+Copyright (C) 1998	Cesar Miquel  (miquel@df.uba.ar)
 
-    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 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.
+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., 675 Mass Ave, Cambridge, MA 02139, USA.
+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., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-    ---------------------------------------------------------------------- */
+---------------------------------------------------------------------- */
 
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
+#include <libgnomevfs/gnome-vfs-ops.h>
 #include "logview.h"
 #include "logrtns.h"
+#include "log_repaint.h"
 #include "monitor.h"
-#include <libgnomevfs/gnome-vfs-ops.h>
+#include "misc.h"
+#include "logview-debug.h"
+
+#define CHECK_FILE_PERIOD	10000
+
+static gpointer window = NULL;
 
 void
 monitor_stop (Log *log)
 {
-  g_return_if_fail (log);
-
-  if (log->mon_handle != NULL) {
-      gnome_vfs_monitor_cancel (log->mon_handle);
-      log->mon_handle = NULL;
-      log->mon_offset = 0;
-      
-      gnome_vfs_close (log->mon_file_handle);
-      log->mon_file_handle = NULL;
-      
-      log->monitored = FALSE;
-  }
+	GnomeVFSMonitorHandle *mon_handle;
+	LV_MARK;
+	g_assert (LOGVIEW_IS_LOG (log));
+
+	g_object_get (G_OBJECT (log),
+		      "monitor-handle", &mon_handle, NULL);
+	if (mon_handle != NULL) {
+		gnome_vfs_monitor_cancel (mon_handle);
+		g_object_set (G_OBJECT (log),
+			      "monitor-handle", NULL, NULL);
+	}
+	g_object_set (G_OBJECT (log),
+		      "monitoring", FALSE, NULL);
+	g_object_unref (log);
 }
 
 static void
 monitor_callback (GnomeVFSMonitorHandle *handle, const gchar *monitor_uri,
-                  const gchar *info_uri, GnomeVFSMonitorEventType event_type,
-                  gpointer data)
+		  const gchar *info_uri, GnomeVFSMonitorEventType event_type,
+		  gpointer data)
+{
+	LogviewWindow *logview = LOGVIEW_WINDOW(window);
+	Log *log = data;
+	
+	LV_MARK;
+	g_assert (LOGVIEW_IS_LOG (log));
+	
+	loglist_bold_log (LOG_LIST (logview->loglist), log);
+	if (logview->curlog == log)
+		logview_repaint (logview);
+}
+
+/* ----------------------------------------------------------------------
+NAME:	monitor_log_was_modified
+DESCRIPTION: Returns true if modified flag in log changed. It also
+changes the modified flag. This function is called in unexpected time,
+so the passed in log must be referenced. And only unreferenced when timer
+is invalid.
+---------------------------------------------------------------------- */
+
+static gboolean
+monitor_log_was_modified (Log *log)
 {
-  LogviewWindow *logview;
-  Log *log = data;
-  
-  g_assert (log);
-
-  logview  = LOGVIEW_WINDOW (log->window);
-  loglist_bold_log (LOG_LIST (logview->loglist), log);
-  log->needs_refresh = TRUE;
-  if (logview->curlog == log)
-    logview_repaint (logview);
+	gboolean monitoring;
+	LV_MARK;
+	g_assert (LOGVIEW_IS_LOG (log));
+	g_object_get (G_OBJECT (log), "monitoring", &monitoring, NULL);
+	if (! monitoring) {
+		g_object_unref (log);
+		return FALSE;
+	}
+	if (log_has_been_modified (log)) {
+		monitor_callback(NULL, NULL, NULL, 0, log);
+	}
+	g_object_unref (log);
+	return TRUE;
 }
 
 void
 monitor_start (Log *log)
 {
-  GnomeVFSResult result;
-  GnomeVFSFileSize size;
-  gchar *main = NULL, *second = NULL;
-	 
-  g_return_if_fail (log);
-
-  result = gnome_vfs_open (&(log->mon_file_handle), log->name, 
-                           GNOME_VFS_OPEN_READ);
-  result = gnome_vfs_seek (log->mon_file_handle, GNOME_VFS_SEEK_END, 0L);
-  result = gnome_vfs_tell (log->mon_file_handle, &size);
-  log->mon_offset = size;
-
-  result = gnome_vfs_monitor_add (&(log->mon_handle), log->name,
-                         GNOME_VFS_MONITOR_FILE, monitor_callback,
-                         log);
-
-  if (result != GNOME_VFS_OK) {
-    main = g_strdup (_("This file cannot be monitored."));
-    switch (result) {
-    case GNOME_VFS_ERROR_NOT_SUPPORTED :
-        second = g_strdup (_("File monitoring is not supported on this file system.\n"));
-    default:
-        second = g_strdup (_("Gnome-VFS Error.\n"));
-    }
-    error_dialog_show (NULL, main, second);
-    g_free (main);
-    g_free (second);
-    gnome_vfs_close (log->mon_file_handle);
-    return;
-  }
-
-  log->monitored = TRUE;
+	GnomeVFSResult result;
+	gchar *first = NULL, *second = NULL;
+	gboolean monitorable;
+	gchar *name;
+	GnomeVFSMonitorHandle *mon_handle;
+
+	LV_MARK;
+	g_assert (LOGVIEW_IS_LOG (log));
+	
+	g_object_ref (log);
+	g_object_get (G_OBJECT (log),
+		      "monitorable", &monitorable,
+		      "monitor-handle", &mon_handle,
+		      "path", &name,
+		      NULL);
+	if (!monitorable) {
+		g_free (name);
+		g_object_unref (log);
+		return;
+	}
+	
+	result = gnome_vfs_monitor_add (&mon_handle, name,
+					GNOME_VFS_MONITOR_FILE,
+					monitor_callback, log);
+	if (result != GNOME_VFS_OK) {
+		if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) {
+			mon_handle = NULL;
+			g_timeout_add (CHECK_FILE_PERIOD,
+				       (GSourceFunc) monitor_log_was_modified,
+				       g_object_ref(log));
+		}
+		else {
+			goto gnome_vfs_err;
+		}
+	}
+	if (mon_handle != NULL) {
+		g_object_set (G_OBJECT (log),
+			      "monitor-handle", mon_handle, NULL);
+	}
+
+	g_object_set (G_OBJECT (log),
+		      "monitoring", TRUE, NULL);
+	g_free (name);
+	return;
+
+gnome_vfs_err:
+	first = g_strdup (_("Gnome-VFS error."));
+	second = g_strdup_printf ("%s, (%d): %s",
+				  name,
+				  result,
+				  gnome_vfs_result_to_string (result));
+	error_dialog_show (NULL, first, second);
+	g_free (first);
+	g_free (second);
+	g_free (name);
+	g_object_unref (log);
+}
+
+void
+monitor_set_window (gpointer win)
+{
+	g_assert (window == NULL);
+	window = win;
 }
diff --git a/logview/monitor.h b/logview/monitor.h
index 30f9360..76fd3a0 100644
--- a/logview/monitor.h
+++ b/logview/monitor.h
@@ -20,6 +20,7 @@
 #ifndef __LOG_MONITOR_H__
 #define __LOG_MONITOR_H__
 
+void monitor_set_window (gpointer win);
 void monitor_start (Log *log);
 void monitor_stop (Log *log);
 
diff --git a/logview/plugins/Makefile.am b/logview/plugins/Makefile.am
new file mode 100644
index 0000000..26a821b
--- /dev/null
+++ b/logview/plugins/Makefile.am
@@ -0,0 +1,63 @@
+plugindir = $(libdir)/gnome-system-log/plugins
+
+NULL =
+
+if FALSE
+libtest_la_LDFLAGS	= \
+	$(PLUGIN_LIBTOOL_FLAGS) $(GNOME_UTILS_LIBS) $(GMODULE_LIBS)
+plugin_LTLIBRARIES =	libtest.la
+libtest_la_SOURCES	= test.c
+endif
+
+libgrablogs_la_LDFLAGS	= \
+	$(PLUGIN_LIBTOOL_FLAGS) $(GNOME_UTILS_LIBS) $(GMODULE_LIBS)
+libplainlog_la_LDFLAGS	= \
+	$(PLUGIN_LIBTOOL_FLAGS) $(GNOME_UTILS_LIBS) $(GMODULE_LIBS)
+libpipelog_la_LDFLAGS	= \
+	$(PLUGIN_LIBTOOL_FLAGS) $(GNOME_UTILS_LIBS) $(GMODULE_LIBS)
+libbaseview_la_LDFLAGS	= \
+	$(PLUGIN_LIBTOOL_FLAGS) $(GNOME_UTILS_LIBS) $(GMODULE_LIBS)
+
+plugin_LTLIBRARIES = \
+	libgrablogs.la	\
+	libplainlog.la	\
+	libpipelog.la	\
+	libbaseview.la
+
+libgrablogs_la_SOURCES	= grablogs.c
+libplainlog_la_SOURCES	= plainlog.c
+libpipelog_la_SOURCES	= pipelog.c
+libbaseview_la_SOURCES	= baseview.c
+
+.PHONY: always
+
+additionaldir  = $(plugindir)
+additional_DATA  = \
+	grablogs.conf	\
+	pipelog.conf
+
+EXTRA_DIST = \
+	$(additional_DATA)
+
+#
+# This part allows people to build their own plugins in here.
+# Yes, it's a mess.
+#
+SUFFIXES: .c .so
+.c.so:
+	$(LIBTOOL) --mode=compile $(CC) -DHAVE_CONFIG_H -I$(top_srcdir) $(AM_CPPFLAGS) $(CFLAGS) -c $< -o tmp$@.lo $(GNOME_UTILS_CFLAGS)
+	$(LIBTOOL) --mode=link    $(CC) $(CFLAGS) -o libtmp$@.la -rpath $(plugindir) tmp$@.lo $(LIBS) $(LDFLAGS) -module -avoid-version $(GNOME_UTILS_LIBS)
+	@rm -f tmp$@.lo tmp$@.o libtmp$@.la
+	@cp .libs/libtmp$@.so* $@
+	@rm -f .libs/libtmp$@.*
+
+
+AM_CPPFLAGS = \
+	-DDATADIR=\"$(datadir)\"				\
+	-I$(top_builddir)/logview				\
+	-I$(top_srcdir)/logview					\
+	$(GNOME_UTILS_CFLAGS)					\
+	$(SUN_OS)						\
+	$(NULL)
+
+
diff --git a/logview/plugins/baseview.c b/logview/plugins/baseview.c
new file mode 100644
index 0000000..c1abfc4
--- /dev/null
+++ b/logview/plugins/baseview.c
@@ -0,0 +1,289 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+ 
+#include <glib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <glib.h>
+#include "logview-plugin.h"
+#include "logview-iface-view.h"
+#include "logview-debug.h"
+#include "logview-log.h"
+#include "misc.h"
+
+
+#define PLUGIN_BASEVIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST ((obj), PLUGIN_TYPE_BASEVIEW, Baseview))
+#define PLUGIN_BASEVIEW_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST ((klass), PLUGIN_TYPE_BASEVIEW, BaseviewClass))
+#define BASEVIEW_IS_PLUGIN(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), PLUGIN_TYPE_BASEVIEW))
+#define BASEVIEW_IS_PLUGIN_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_TYPE ((klass), PLUGIN_TYPE_BASEVIEW))
+#define PLUGIN_BASEVIEW_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS ((obj), PLUGIN_TYPE_BASEVIEW, BaseviewClass))
+
+#undef PLUGIN_PRIO
+#define PLUGIN_PRIO	PLUG_PRIO_BASE
+#undef PLUGIN_DESC
+#define PLUGIN_DESC	"Group by date and try to convert to UTF-8"
+
+typedef struct _Baseview Baseview;
+typedef struct _BaseviewClass BaseviewClass;
+
+struct _Baseview {
+	LogviewPlugin parent;
+};
+
+struct _BaseviewClass {
+	LogviewPluginClass parent;
+};
+
+
+/* class implement */
+static void iface_init (gpointer g_iface, gpointer iface_data);
+static void view_init (gpointer g_iface, gpointer iface_data);
+
+static void
+baseview_class_init (gpointer g_class, gpointer g_class_data)
+{
+	LV_MARK;
+}
+
+static void
+baseview_instance_init (GTypeInstance *instance, gpointer g_class_data)
+{
+	LV_MARK;
+}
+
+static GType
+baseview_get_type (GTypeModule* module)
+{
+	static GType type = 0;
+	LV_MARK;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (BaseviewClass),
+			NULL,   /* base_init */
+			NULL,   /* base_finalize */
+			baseview_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			sizeof (Baseview),
+			0,      /* n_preallocs */
+			baseview_instance_init    /* instance_init */
+		};
+		static const GInterfaceInfo iface_info = {
+			iface_init,	/* interface_init */
+			NULL,		/* interface_finalize */
+			NULL		/* interface_data */
+		};
+		static const GInterfaceInfo view_info = {
+			view_init,	/* interface_init */
+			NULL,		/* interface_finalize */
+			NULL		/* interface_data */
+		};
+		type = g_type_module_register_type (module,
+						    LOGVIEW_TYPE_PLUGIN,
+						    "BaseviewType",
+						    &info,
+						    0);
+		g_type_module_add_interface (module, type, LOGVIEW_TYPE_IFACE,
+			&iface_info);
+		g_type_module_add_interface (module, type, LOGVIEW_TYPE_IFACE_VIEW,
+			&view_info);
+	}
+	return type;
+}
+
+/* interfaces */
+static gboolean
+can_handle(Baseview *self, Log *log)
+{
+	LV_MARK;
+	return TRUE;
+}
+
+static gint 
+days_compare (gconstpointer a, gconstpointer b)
+{
+	const Day *day1 = a, *day2 = b;
+	return (g_date_compare (day1->date, day2->date));
+}
+
+/* log_read_dates
+   Read all dates which have a log entry to create calendar.
+   All dates are given with respect to the 1/1/1970
+   and are then corrected to the correct year once we
+   reach the end.
+*/
+
+static GSList *
+baseview_read_dates (LogviewIView *self, gchar **buffer_lines, time_t current)
+{
+	int offsetyear = 0, current_year;
+	GSList *days = NULL, *days_copy;
+	GDate *date, *newdate;
+	struct tm *tmptm;
+	gchar *date_string;
+	Day *day;
+	gboolean done = FALSE;
+	int i, n, rangemin, rangemax;
+
+	if (buffer_lines == NULL)
+		return NULL;
+
+	n = g_strv_length (buffer_lines);
+	for (i=0; buffer_lines[i] == NULL && i < n; i++);
+	if (i == n)
+		return NULL;
+
+	tmptm = localtime (&current);
+	current_year = tmptm->tm_year + 1900;
+
+	/* Start building the list */
+	/* Scanning each line to see if the date changed is too slow,
+	   so we proceed in a recursive fashion */
+
+	date = string_get_date (buffer_lines[i]);
+	if ((date==NULL)|| !g_date_valid (date)) {
+		return NULL;
+	}
+
+	g_date_set_year (date, current_year);
+	day = g_new0 (Day, 1);
+	days = g_slist_append (days, day);
+
+	day->date = date;
+	day->first_line = i;
+	day->last_line = -1;
+	date_string = get_date_string (buffer_lines[i]);
+
+	rangemin = 0;
+	rangemax = n-1;
+
+	while (!done) {
+	   
+		i = n-1;
+		while (day->last_line < 0) {
+
+			if (g_str_has_prefix (buffer_lines[i], date_string)) {
+				if (i == (n-1)) {
+					day->last_line = i;
+					done = TRUE;
+					break;
+				} else {
+					if (!g_str_has_prefix (buffer_lines[i+1], date_string)) {
+						day->last_line = i;
+						break;
+					} else {
+						rangemin = i;
+						i = (int) ( ((float) i + (float) rangemax)/2.);
+					}
+				}
+			} else {
+				rangemax = i;
+				i = (int) (((float) rangemin + (float) i)/2.);				
+			}
+
+		}
+	   
+		g_free (date_string);
+
+		if (!done) {
+			/* We need to find the first line now that has a date
+			   Logs can have some messages without dates ... */
+			newdate = NULL;
+			while (newdate == NULL && !done && i < n) {
+				i++;
+				date_string = get_date_string (buffer_lines[i]);
+				if (date_string == NULL)
+					continue;
+				g_assert (newdate == NULL);
+				newdate = string_get_date (buffer_lines[i]);
+		   
+				if (newdate == NULL && i==n-1)
+					done = TRUE;
+			}
+
+			day->last_line = i-1;
+
+			/* Append a day to the list */	
+			if (newdate) {
+				g_date_set_year (newdate, current_year + offsetyear);	
+				if (g_date_compare (newdate, date) < 1) {
+					offsetyear++; /* newdate is next year */
+					g_date_add_years (newdate, 1);
+				}
+		   
+				date = newdate;
+				day = g_new0 (Day, 1);
+				days = g_slist_append (days, day);
+		   
+				day->date = date;
+				day->first_line = i;
+				day->last_line = -1;
+				rangemin = i;
+				rangemax = n;
+			}
+		}
+	}
+
+	/* Correct years now. We assume that the last date on the log
+	   is the date last accessed */
+
+	for (days_copy = days; days_copy != NULL; days_copy = g_slist_next (days_copy)) {	   
+		day = days_copy -> data;
+		g_date_subtract_years (day->date, offsetyear);
+	}
+   
+	/* Sort the days in chronological order */
+	days = g_slist_sort (days, days_compare);
+
+	return (days);
+}
+
+static GSList*
+group_lines (LogviewIView *self, const gchar **lines)
+{
+	LV_MARK;
+	return baseview_read_dates (self, (gchar **)lines, time (NULL));
+}
+
+static void
+iface_init (gpointer g_iface, gpointer iface_data)
+{
+	LogviewInterface *iface = (LogviewInterface*)g_iface;
+	LV_MARK;
+	iface->can_handle = (gboolean (*) (LogviewIFace*, Log*)) can_handle;
+}
+
+static gchar*
+to_utf8 (LogviewIView *self, gchar *str)
+{
+	char *out;
+	if (g_utf8_validate (str, -1, NULL))
+		out = str;
+	else {
+		if ((out = g_locale_to_utf8 (str, -1, NULL, NULL, NULL)) != NULL)
+			g_free (str);
+		else
+			out = str;
+	}
+	return out;
+}
+
+static void
+view_init (gpointer g_iface, gpointer iface_data)
+{
+	LogviewIFaceView *self = (LogviewIFaceView*)g_iface;
+	LV_MARK;
+	self->group_lines = (GSList* (*) (LogviewIView*, const gchar**)) group_lines;
+	self->to_utf8 = (gchar* (*) (LogviewIView*, gchar*, gssize)) to_utf8;
+}
+
+LOGVIEW_INIT_PLUGIN (baseview_get_type)
+
diff --git a/logview/plugins/grablogs.c b/logview/plugins/grablogs.c
new file mode 100644
index 0000000..efcf1d6
--- /dev/null
+++ b/logview/plugins/grablogs.c
@@ -0,0 +1,496 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+ 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <glib.h>
+#include <locale.h>
+#include "logview-plugin.h"
+#include "logview-iface-collector.h"
+#include "logview-debug.h"
+#include "logview-log.h"
+#include "misc.h"
+
+#define PLUGIN_GRABLOGS(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST ((obj), PLUGIN_TYPE_GRABLOGS, PluginGrabLogs))
+#define PLUGIN_GRABLOGS_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST ((klass), PLUGIN_TYPE_GRABLOGS, PluginGrabLogsClass))
+#define GRABLOGS_IS_PLUGIN(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), PLUGIN_TYPE_GRABLOGS))
+#define GRABLOGS_IS_PLUGIN_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_TYPE ((klass), PLUGIN_TYPE_GRABLOGS))
+#define PLUGIN_GRABLOGS_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS ((obj), PLUGIN_TYPE_GRABLOGS, PluginGrabLogsClass))
+
+#undef PLUGIN_PRIO
+#define PLUGIN_PRIO	PLUG_PRIO_BASE
+#undef PLUGIN_DESC
+#define PLUGIN_DESC	"For colloct log files from shell commands"
+
+#ifdef ON_SUN_OS
+#define FILE_CMD "file"
+#else
+#define FILE_CMD "file"
+#endif
+#define GRABLOGS_CONF	"grablogs.conf"
+#define CAT_CMD		"[commands]"
+#define CAT_LOG		"[logs]"
+#define CAT_CONF	"[configs]"
+
+
+typedef GSList* (*Parse_All) (FILE*);
+typedef GSList* (*Parse_Line) (FILE*, Parse_All);
+typedef struct _PluginGrabLogs PluginGrabLogs;
+typedef struct _PluginGrabLogsClass PluginGrabLogsClass;
+
+struct _PluginGrabLogs {
+	LogviewPlugin parent;
+};
+
+struct _PluginGrabLogsClass {
+	LogviewPluginClass parent;
+};
+
+/* class implement */
+static void iface_init (gpointer g_iface, gpointer iface_data);
+static void collector_init (gpointer g_iface, gpointer iface_data);
+
+static struct sigaction old_sa;
+static gchar* category;
+
+static void
+grablogs_class_init (gpointer g_class, gpointer g_class_data)
+{
+	LV_MARK;
+}
+
+static void
+grablogs_instance_init (GTypeInstance *instance, gpointer g_class_data)
+{
+	LV_MARK;
+}
+
+static GType
+grablogs_get_type (GTypeModule* module)
+{
+	static GType type = 0;
+	LV_MARK;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (PluginGrabLogsClass),
+			NULL,   /* base_init */
+			NULL,   /* base_finalize */
+			grablogs_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			sizeof (PluginGrabLogs),
+			0,      /* n_preallocs */
+			grablogs_instance_init    /* instance_init */
+		};
+		static const GInterfaceInfo iface_info = {
+			iface_init,	/* interface_init */
+			NULL,		/* interface_finalize */
+			NULL		/* interface_data */
+		};
+		static const GInterfaceInfo collector_info = {
+			collector_init,	/* interface_init */
+			NULL,		/* interface_finalize */
+			NULL		/* interface_data */
+		};
+		type = g_type_module_register_type (module,
+						    LOGVIEW_TYPE_PLUGIN,
+						    "GrablogsType",
+						    &info,
+						    0);
+		g_type_module_add_interface (module, type, LOGVIEW_TYPE_IFACE,
+			&iface_info);
+		g_type_module_add_interface (module, type, LOGVIEW_TYPE_IFACE_COLLECTOR,
+			&collector_info);
+	}
+	return type;
+}
+
+static GSList*
+get_logs (const gchar* logcfg, Parse_All cp)
+{
+	GSList* loglist = NULL;
+	FILE* handle;
+	int (*lclose) (FILE*) = NULL;
+	
+	LV_MARK;
+	/*
+	 * lines in [command] will be run in popen
+	 */
+	if (category && g_ascii_strcasecmp(g_strstrip(category),
+					   CAT_CMD) == 0) {
+		LV_INFO ("[grablogs-PIPE-open] %s", logcfg);
+		handle = security_popen (logcfg, "r");
+		lclose = security_pclose;
+	}
+	else if (local_file_exist (logcfg)
+		 && local_file_can_executed (logcfg)) {
+		LV_INFO ("[grablogs-FILE-open] %s", logcfg);
+		handle = fopen (logcfg, "r");
+		lclose = fclose;
+	}
+	else
+		goto get_logs_err;
+
+	if (handle != NULL) {
+		LV_INFO ("[grablogs-open] %s", logcfg);
+		loglist = cp (handle);
+		if (lclose (handle) == -1)
+			goto get_logs_err;
+		LV_INFO ("[grablogs-close] %s", logcfg);
+	}
+	else {
+get_logs_err:
+		LV_INFO_EE ("[grablogs handles]: \"%s\", msg: %s",
+			    logcfg, error_system_string ());
+		return NULL;
+	}
+	return loglist;
+}
+
+static gchar*
+get_line (FILE *handle)
+{
+	glong inc = BUFSIZ, size = inc;
+	glong p = -1;
+	gchar *buf = (gchar *) g_malloc (size * sizeof (gchar));
+	g_assert (handle);
+	g_assert (buf);
+	do {
+		g_assert (p < size);
+		if (++p == size) {
+			size += inc;
+			buf = (gchar *) g_realloc (buf, size * sizeof(gchar));
+			g_assert (buf);
+		}
+		buf[p] = (gchar)getc (handle);
+	} while (buf[p] != '\n' && buf[p] != EOF);
+	if (buf[p] != EOF) {
+		buf[p] = '\0';
+	} else if (feof (handle) == 0 && p > 0) {
+		buf[p] = '\0';
+	} else if (feof (handle) != 0) {
+		LV_INFO_EE ("get_line error: %s", error_system_string());
+		g_free (buf);
+		buf = NULL;
+	} else {
+		g_free (buf);
+		buf = NULL;
+	}
+	LV_ERR ("get_line %s", buf? buf:"EOFed");
+	return buf;
+}
+
+/* one log per line */
+static GSList*
+cp_logline (FILE *handle)
+{
+	gchar *buf;
+	GSList *logfiles = NULL;
+	LV_MARK;
+	while ((buf = get_line(handle)) != NULL) {
+		if (g_str_has_prefix (g_strchug (buf), "#")) {
+			g_free (buf);
+			continue;
+		}
+		if (g_str_has_prefix(g_strstrip(buf), "[")) {
+			g_free (category);
+			category = buf;
+			break;
+		}
+		if (strlen (buf) != 0)
+			logfiles = g_slist_append (logfiles, g_strdup (buf));
+		g_free (buf);
+	}
+	return logfiles;
+}
+
+
+static GSList*
+cp_logconf (FILE *handle)
+{
+	gchar *buf, *p;
+	GSList *logfiles = NULL;
+	LV_MARK;
+	while ((buf = get_line(handle)) != NULL) {
+		gchar **list;
+		gint i;
+		for (p = buf; g_ascii_isspace(*p); ++p);
+		if (*p == '\0' || *p == '#' || *p == '\n') {
+			g_free (buf);
+			continue;
+		}
+		list = g_strsplit_set (p, ", -\t()\n'`", 0);
+		for (i = 0; list[i]; ++i) {
+			if (*list[i] == '/' &&
+			    g_slist_find_custom(logfiles, list[i],
+						(GCompareFunc)g_ascii_strcasecmp) == NULL) {
+				logfiles = g_slist_append (logfiles,
+							   g_strdup(list[i]));
+			}
+		}
+		g_strfreev(list);
+		g_free (buf);
+	}
+	return logfiles;
+}
+
+/* one shell or command per line
+ * run each line
+ * one log per line of output
+ */
+static GSList*
+cp_shell_per_line (FILE *handle)
+{
+	gchar *buf = NULL;
+	GSList *all_logs = NULL;
+	LV_MARK;
+	while ((buf = get_line(handle)) != NULL) {
+		if (g_str_has_prefix (g_strchug (buf), "#")) {
+			g_free (buf);
+			continue;
+		}
+		if (g_str_has_prefix(g_strchug (buf), "[")) {
+			g_free (category);
+			category = buf;
+			break;
+		}
+		if (strlen (buf) != 0) {
+			GSList *idx;
+			GSList *logfiles = NULL;
+			logfiles = get_logs (buf, cp_logline);
+			for (idx = logfiles; idx; idx=g_slist_next(idx)) {
+				if (g_slist_find_custom (all_logs, idx->data,
+							 (GCompareFunc)g_ascii_strcasecmp)
+				    == NULL) {
+					all_logs = g_slist_append (all_logs,
+								   idx->data);
+				}
+				else
+					g_free (idx->data);
+			}
+			if (logfiles)
+				g_slist_free (logfiles);
+		}
+		g_free (buf);
+	}
+	return all_logs;
+}
+
+static GSList*
+cp_conf_per_line (FILE *handle)
+{
+	gchar *buf = NULL;
+	GSList *all_logs = NULL;
+	LV_MARK;
+	while ((buf = get_line(handle)) != NULL) {
+		if (g_str_has_prefix (g_strchug (buf), "#")) {
+			g_free (buf);
+			continue;
+		}
+		if (g_str_has_prefix(g_strchug (buf), "[")) {
+			g_free (category);
+			category = buf;
+			break;
+		}
+		if (strlen (buf) != 0) {
+			GSList *idx;
+			GSList *logfiles = NULL;
+			logfiles = get_logs (buf, cp_logconf);
+			for (idx = logfiles; idx; idx=g_slist_next(idx)) {
+				if (g_slist_find_custom (all_logs, idx->data,
+							 (GCompareFunc)g_ascii_strcasecmp)
+				    == NULL) {
+					all_logs = g_slist_append (all_logs,
+								   idx->data);
+				}
+				else
+					g_free (idx->data);
+			}
+			if (logfiles)
+				g_slist_free (logfiles);
+		}
+		g_free (buf);
+	}
+	return all_logs;
+}
+
+static GSList*
+cp_grab_conf (FILE *handle)
+{
+	GSList *all_logs = NULL;
+	LV_MARK;
+
+	/* A line without any categories will be skipped */
+	category = get_line(handle);
+	while (feof (handle) == 0) {
+		if (g_str_has_prefix (g_strchug (category), "#"))
+			goto next_line;
+		if (category) {
+			if (g_ascii_strcasecmp(g_strstrip(category),
+					       CAT_CMD) == 0) {
+				LV_INFO ("grab category %s, enter", category);
+				all_logs = g_slist_concat (all_logs,
+							   cp_shell_per_line (handle));
+			}
+			else if (g_ascii_strcasecmp(g_strstrip(category),
+						    CAT_LOG) == 0) {
+				LV_INFO ("grab category %s, enter", category);
+				all_logs = g_slist_concat (all_logs,
+							   cp_logline (handle));
+			}
+			else if (g_ascii_strcasecmp(g_strstrip(category),
+						    CAT_CONF) == 0) {
+				LV_INFO ("grab category %s, enter", category);
+				all_logs = g_slist_concat (all_logs,
+							   cp_conf_per_line (handle));
+			}
+			else
+				goto next_line;
+		}
+		else {
+next_line:
+			g_free (category);
+			category = get_line(handle);
+		}
+	}
+	g_free (category);
+	return all_logs;
+}
+
+static GSList*
+get_all_logs (PluginGrabLogs *self)
+{
+	GSList *plugin_paths, *i;
+	GSList *all_logs = NULL;
+	LV_MARK;
+
+	plugin_paths = (GSList *) get_plugin_paths ();
+	category = NULL;
+	for (i = plugin_paths; i; i=g_slist_next(i)) {
+		gchar* path = g_build_path (G_DIR_SEPARATOR_S,
+					    i->data, GRABLOGS_CONF, NULL);
+		if (local_file_exist (path)
+		    && local_file_can_executed (path)) {
+			GSList *logfiles, *idx;
+			logfiles = get_logs (path, cp_grab_conf);
+			for (idx = logfiles; idx; idx = g_slist_next(idx)) {
+				if (g_slist_find_custom (all_logs, idx->data,
+							 (GCompareFunc)g_ascii_strcasecmp) == NULL) {
+					all_logs = g_slist_append (all_logs, idx->data);
+					LV_ERR ("add %s", idx->data);
+				}
+				else {
+					LV_ERR ("del %s", idx->data);
+					g_free (idx->data);
+				}
+			}
+			g_slist_free (logfiles);
+			/* break to search the sequent path of GRABLOGS_CONF */
+			g_free (path);
+			break;
+		} else {
+			g_free (path);
+		}
+	}
+	return all_logs;
+}
+
+static void
+config_child_log (Log *log)
+{
+	/* Check for older versions of the log */
+	gchar *log_name = NULL;
+	gint i;
+	g_object_get (G_OBJECT (log), "path", &log_name, NULL);
+	for (i=0; i<OLD_LOG_NUM; i++) {
+		gboolean flag = FALSE;
+		gchar *older_name;
+		Log *child;
+		older_name = g_strdup_printf ("%s.%d", log_name, i);
+		if (local_file_exist (older_name)) {
+			flag = TRUE;
+		} else {
+			g_free (older_name);
+			older_name = g_strdup_printf ("%s.%d.gz", log_name, i);
+			if (local_file_exist (older_name)) {
+				flag = TRUE;
+			}
+		}
+		if (flag) {
+			child = g_object_new (LOGVIEW_TYPE_LOG,
+					      "path", older_name,
+					      "protocol", file_protocol (older_name),
+					      NULL);
+			log_set_child (log, i, child);
+		}
+		g_free (older_name);
+	}
+	g_free (log_name);
+}
+
+static Log*
+config_log_from_path (PluginGrabLogs *self, const gchar *log_path)
+{
+	Log *log;
+	LogProtocol protocol;
+	gchar *utf8_name = NULL;
+	gchar *name;
+	gchar *message;
+
+	protocol = file_protocol (log_path);
+	switch (protocol) {
+	case UNKNOWN_LOG:
+	case PLAIN_LOG:
+	case GZIP_LOG:
+	case PIPE_LOG:
+		log = g_object_new (LOGVIEW_TYPE_LOG,
+				    "path", log_path,
+				    "protocol", protocol,
+				    NULL);
+		config_child_log (log);
+		return log;
+	default:;
+	}
+	utf8_name = locale_to_utf8 (log_path);
+	name = utf8_name ? utf8_name : log_path;
+	/* SUN_BRANDING */
+	message = g_strdup_printf (_("Unknown file type: %d"), protocol);
+	log_error_appendv ("grablogs - %s - %s", name, message);
+	g_free (message);
+	g_free (utf8_name);
+	return NULL;
+}
+
+static void
+iface_init (gpointer g_iface, gpointer iface_data)
+{
+	LogviewInterface *iface = (LogviewInterface*) g_iface;
+	LV_MARK;
+	iface->can_handle = NULL; /* dummy */
+}
+
+static void
+collector_init (gpointer g_iface, gpointer iface_data)
+{
+	LogviewIFaceCollector *iface = (LogviewIFaceCollector*) g_iface;
+	LV_MARK;
+	iface->get_logs = (GSList* (*) (LogviewICollector*)) get_all_logs;
+	iface->config_log_from_path = (Log* (*) (LogviewICollector*, const gchar*))config_log_from_path;
+}
+
+LOGVIEW_INIT_PLUGIN (grablogs_get_type)
+
diff --git a/logview/plugins/grablogs.conf b/logview/plugins/grablogs.conf
new file mode 100644
index 0000000..78841cc
--- /dev/null
+++ b/logview/plugins/grablogs.conf
@@ -0,0 +1,10 @@
+[commands]
+for i in `svcs -aH -o FMRI | grep -v lrc `; do svcprop -p restarter/logfile $i 2>/dev/null || svcprop -q -p restarter/alt_logfile $i 2>/dev/null ; done
+
+[logs]
+/var/adm/messages
+/var/adm/wtmpx
+/var/adm/utmpx
+/var/log/Xorg.0.log
+/var/log/syslog
+
diff --git a/logview/plugins/pipelog.c b/logview/plugins/pipelog.c
new file mode 100644
index 0000000..56de934
--- /dev/null
+++ b/logview/plugins/pipelog.c
@@ -0,0 +1,428 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include "logview-iface-io.h"
+#include "misc.h"
+#include "logview-debug.h"
+#include "logview-log.h"
+#include "logview-plugin.h"
+
+#define PLUGIN_PIPELOG(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj), PLUGIN_TYPE_PIPELOG, PluginPipelog))
+#define PLUGIN_PIPELOG_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST((klass), PLUGIN_TYPE_PIPELOG, PluginPipelogClass))
+#define PIPELOG_IS_PLUGIN(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj), PLUGIN_TYPE_PIPELOG))
+#define PIPELOG_IS_PLUGIN_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_TYPE((klass), PLUGIN_TYPE_PIPELOG))
+#define PLUGIN_PIPELOG_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS((obj), PLUGIN_TYPE_PIPELOG, PluginPipelogClass))
+
+#undef PLUGIN_DESC
+#define PLUGIN_DESC	"For non-ASCII formatted log files"
+#undef PLUGIN_PRIO
+#define PLUGIN_PRIO (PLUG_PRIO_BASE + 1)
+
+typedef struct _PluginPipelog PluginPipelog;
+typedef struct _PluginPipelogClass PluginPipelogClass;
+
+struct _PluginPipelog {
+	LogviewPlugin parent;
+
+	Log *log;
+
+	gchar *log_name;
+	LogProtocol protocol;
+
+	gchar *pipe;
+	time_t file_time;
+	GnomeVFSFileSize file_size;
+	FILE *file_handle;
+	gchar *pbuffer;
+	gssize psize;
+	gssize poffset;
+};
+
+struct _PluginPipelogClass {
+	LogviewPluginClass parent;
+};
+
+static GObjectClass *parent_class = NULL;
+
+/* default routines for implement the plugin interfaces */
+static void iface_init (gpointer g_iface, gpointer iface_data);
+static void io_init (gpointer g_iface, gpointer iface_data);
+static gboolean plugin_init (PluginPipelog* self);
+static void plugin_destroy (PluginPipelog* self);
+
+/* interfaces */
+static gboolean can_handle(PluginPipelog *self, Log *log);
+static gboolean can_monitor(PluginPipelog *self);
+static gboolean has_updated(PluginPipelog *self);
+static gboolean update(PluginPipelog *self);
+static void logf_extract_filepath (PluginPipelog *self, gchar** dirname, gchar** filename);
+static size_t logf_read (PluginPipelog *self, void* buffer, size_t size);
+static off_t logf_seek (PluginPipelog *self, off_t offset, int whence);
+static off_t logf_tell (PluginPipelog *self);
+static time_t get_modified_time (PluginPipelog* self);
+static off_t get_size (PluginPipelog* self);
+
+/* others */
+static gboolean seek_pipe_log (PluginPipelog* self, const gchar *filename);
+
+#define PIPELOG_CONF	"pipelog.conf"
+
+/* class implement */
+static void
+pipelog_finalize (PluginPipelog *self)
+{
+	g_free (self->log_name);
+}
+
+static void
+pipelog_class_init (gpointer g_class, gpointer g_class_data)
+{
+	LV_MARK;
+	LogviewPluginClass *parent = LOGVIEW_PLUGIN_CLASS (g_class);
+	parent_class = g_type_class_peek_parent (g_class);
+	G_OBJECT_CLASS (g_class)->finalize = (void (*) (GObject*)) pipelog_finalize;
+
+	parent->plugin_init = (gboolean (*) (LogviewPlugin*)) plugin_init;
+	parent->plugin_destroy = (void (*) (LogviewPlugin*)) plugin_destroy;
+}
+
+static void
+pipelog_instance_init (GTypeInstance *instance, gpointer g_class_data)
+{
+	PluginPipelog *self;
+	LV_MARK;
+	self = (PluginPipelog*) instance;
+	self->pipe = NULL;
+	self->pbuffer = NULL;
+	self->psize = 0;
+	self->poffset = 0;
+	self->log_name = NULL;
+}
+
+static GType
+pipelog_get_type (GTypeModule* module)
+{
+	static GType type = 0;
+	LV_MARK;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (PluginPipelogClass),
+			NULL,   /* base_init */
+			NULL,   /* base_finalize */
+			pipelog_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			sizeof (PluginPipelog),
+			0,      /* n_preallocs */
+			pipelog_instance_init    /* instance_init */
+		};
+		static const GInterfaceInfo iface_info = {
+			iface_init,	/* interface_init */
+			NULL,		/* interface_finalize */
+			NULL		/* interface_data */
+		};
+		static const GInterfaceInfo io_info = {
+			io_init,	/* interface_init */
+			NULL,		/* interface_finalize */
+			NULL		/* interface_data */
+		};
+		type = g_type_module_register_type (module,
+						    LOGVIEW_TYPE_PLUGIN,
+						    "PipelogType",
+						    &info,
+						    0);
+		g_type_module_add_interface (module, type, LOGVIEW_TYPE_IFACE,
+					     &iface_info);
+		g_type_module_add_interface (module, type,
+					     LOGVIEW_TYPE_IFACE_IO, &io_info);
+	}
+	return type;
+}
+
+static void load_file (PluginPipelog* self)
+{
+	gint inc = 5*BUFSIZ, p =0, size = 0;
+
+	LV_MARK;
+	self->pbuffer = malloc (inc * sizeof (gchar) + 1);
+	while ((size = fread(self->pbuffer + p,
+			     sizeof (gchar),
+			     inc,
+			     self->file_handle)) == inc) {
+		p += inc;
+		self->pbuffer = realloc (self->pbuffer, p + inc + 1);
+	}
+	*(self->pbuffer + p + size) = '\0';
+	self->psize = p + size;
+}
+
+static gboolean
+plugin_init (PluginPipelog* self)
+{
+	gchar *utf8_name = NULL;
+	gchar *name;
+
+	LV_MARK;
+	self->file_handle = security_popen ((const char *)self->pipe, "r");
+	if (self->file_handle) {
+		load_file (self);
+		return update (self);
+	}
+
+	if (!self->log_name) {
+		/* SUN_BRANDING */
+		name = _("NULL Filename");
+	} else {
+		utf8_name = locale_to_utf8 (self->log_name);
+		name = utf8_name ? utf8_name : self->log_name;
+	}
+	/* SUN_BRANDING */
+	log_error_appendv ("pipelog - %s - %s", name, _("popen failed"));
+	g_free (utf8_name);
+	return FALSE;
+}
+
+static void
+plugin_destroy (PluginPipelog* self)
+{
+	LV_MARK;
+	if (self->file_handle != NULL) {
+		security_pclose (self->file_handle);
+	}
+	if (self->pipe)
+		g_free (self->pipe);
+	if (self->pbuffer)
+		g_free (self->pbuffer);
+}
+
+
+/* interfaces */
+static gboolean
+can_handle(PluginPipelog *self, Log *log)
+{
+	gchar *utf8_name = NULL;
+	gchar *name;
+	gchar *message = NULL;
+
+	LV_MARK;
+	g_object_get (G_OBJECT (log),
+		      "path", &self->log_name,
+		      "protocol", &self->protocol,
+		      NULL);
+
+	if (self->log_name == NULL) {
+		/* SUN_BRANDING */
+		log_error_appendv ("pipelog - %s", _("NULL Filename"));
+		return FALSE;
+	}
+	switch (self->protocol) {
+	case UNKNOWN_LOG:
+	case PLAIN_LOG:
+	case PIPE_LOG:
+		if (seek_pipe_log (self, self->log_name)) {
+			self->log = log;
+			self->protocol = PIPE_LOG;
+			g_object_set (G_OBJECT (log),
+				      "protocol", self->protocol,
+				      NULL);
+			return TRUE;
+		} else {
+			utf8_name = locale_to_utf8 (self->log_name);
+			name = (utf8_name) ? (utf8_name) : self->log_name;
+			/* SUN_BRANDING */
+			log_error_appendv ("pipelog - %s - %s", name, _("Cannot open"));
+			g_free (utf8_name);
+			return FALSE;
+		}
+	default:;
+	}
+	utf8_name = locale_to_utf8 (self->log_name);
+	name = (utf8_name) ? (utf8_name) : self->log_name;
+	/* SUN_BRANDING */
+	message = g_strdup_printf (_("Unknown file type: %d"), self->protocol);
+	log_error_appendv ("pipelog - %s - %s", name, message);
+	g_free (message);
+	g_free (utf8_name);
+	return FALSE;
+}
+
+static gboolean
+can_monitor(PluginPipelog *self)
+{
+	LV_MARK;
+	return FALSE;
+}
+
+static gboolean
+has_updated(PluginPipelog *self)
+{
+	LV_MARK;
+	return FALSE;
+}
+
+static gboolean
+update(PluginPipelog *self)
+{
+	struct stat buf;
+	LV_MARK;
+	g_assert (self->log_name != NULL);
+	if (stat (self->log_name, &buf) == 0) {
+		self->file_time = buf.st_mtime;
+		self->file_size = buf.st_size;
+	}
+	else {
+		LV_INFO_EE ("%s stat: %s", self->log_name,
+			    error_system_string());
+		self->file_time = 0;
+		self->file_size = 0;
+	}
+	return TRUE;
+}
+
+static size_t
+logf_read (PluginPipelog *self, void* buffer, size_t size)
+{
+	gssize lsize = 0;
+	LV_MARK;
+	g_assert (size >= 0 && buffer);
+	g_assert (self->psize >=0 && self->pbuffer);
+	g_assert (self->poffset >=0 && self->poffset <= self->psize);
+	lsize = self->psize - self->poffset;
+	if (size > lsize)
+		size = lsize;
+	bcopy (self->pbuffer, buffer, size * sizeof(gchar));
+	self->poffset += size;
+	return size;
+}
+
+static off_t
+logf_seek (PluginPipelog *self, off_t offset, int whence)
+{
+	g_assert (self->psize >=0 && self->pbuffer);
+	g_assert (self->poffset >=0 && self->poffset <= self->psize);
+	switch (whence) {
+	case SEEK_SET:
+		self->poffset = offset;
+		break;
+	case SEEK_CUR:
+		self->poffset += offset;
+		break;
+	case SEEK_END:
+		self->poffset = self->psize;
+		break;
+	default:
+		return -1;
+	}
+	return logf_tell(self);
+}
+
+static off_t
+logf_tell (PluginPipelog *self)
+{
+	g_assert (self->psize >=0 && self->pbuffer);
+	g_assert (self->poffset >=0 && self->poffset <= self->psize);
+	return (off_t)self->poffset;
+}
+
+static void
+logf_extract_filepath (PluginPipelog *self, gchar** dirname, gchar** filename)
+{
+	extract_filepath (self->log_name, dirname, filename);
+}
+
+static off_t
+get_size (PluginPipelog* self)
+{
+	return self->file_size;
+}
+
+static time_t
+get_modified_time (PluginPipelog* self)
+{
+	return self->file_time;
+}
+
+static void
+io_init (gpointer g_iface, gpointer iface_data)
+{
+	LogviewIFaceIO *iface = (LogviewIFaceIO*)g_iface;
+	LV_MARK;
+	iface->can_monitor = (gboolean (*) (LogviewIIO*)) can_monitor;
+	iface->extract_filepath = (void (*) (LogviewIIO*, gchar**, gchar**)) logf_extract_filepath;
+	iface->has_updated = (gboolean (*) (LogviewIIO*)) has_updated;
+	iface->update = (void (*) (LogviewIIO*)) update;
+	iface->read = (size_t (*) (LogviewIIO*, void*, size_t)) logf_read;
+	iface->seek = (off_t (*) (LogviewIIO*, off_t, int)) logf_seek;
+	iface->tell = (off_t (*) (LogviewIIO*)) logf_tell;
+	iface->get_size = (off_t (*) (LogviewIIO*)) get_size;
+	iface->get_modified_time = (time_t (*) (LogviewIIO*)) get_modified_time;
+}
+
+static void
+iface_init (gpointer g_iface, gpointer iface_data)
+{
+	LogviewInterface *iface = (LogviewInterface*)g_iface;
+	LV_MARK;
+	iface->can_handle = (gboolean (*) (LogviewIFace*, Log*)) can_handle;
+}
+
+static gboolean
+seek_pipe_log (PluginPipelog* self, const gchar *filename)
+{
+	GSList *plugin_paths, *i;
+	gboolean hint = FALSE;
+	gchar cbuf[BUFSIZ];
+	LV_MARK;
+
+	plugin_paths = (GSList *) get_plugin_paths ();
+
+	for (i = plugin_paths; !hint && i; i=g_slist_next(i)) {
+		gchar* path = g_build_path (G_DIR_SEPARATOR_S, i->data,
+					    PIPELOG_CONF, NULL);
+		if (local_file_exist (path)
+		    && local_file_can_executed (path)) {
+			FILE *handle = fopen (path, "r");
+			if (handle != NULL) {
+				while (!hint && fgets(cbuf, sizeof(cbuf), handle) != NULL) {
+					gchar *str, *fp;
+					g_strdelimit (cbuf, "\t\n", ' ');
+					str = g_strchug (cbuf);
+					if (g_str_has_prefix (str, "#"))
+						continue;
+					fp = g_strstr_len (str, strlen (str), filename);
+					if (fp != NULL) {
+						self->pipe = g_strdup_printf (
+							g_strstrip(fp + strlen (filename) + 1),
+							filename);
+						LV_INFO ("found pipe log : %s", self->pipe);
+						hint = TRUE;
+					}
+				}
+				fclose (handle);
+			}
+		}
+		g_free (path);
+	}
+	return hint;
+}
+
+LOGVIEW_INIT_PLUGIN (pipelog_get_type)
+
diff --git a/logview/plugins/pipelog.conf b/logview/plugins/pipelog.conf
new file mode 100644
index 0000000..fea6731
--- /dev/null
+++ b/logview/plugins/pipelog.conf
@@ -0,0 +1,2 @@
+/var/adm/wtmpx	last -f %s
+/var/adm/utmpx	last -f %s
diff --git a/logview/plugins/plainlog.c b/logview/plugins/plainlog.c
new file mode 100644
index 0000000..f8f1fec
--- /dev/null
+++ b/logview/plugins/plainlog.c
@@ -0,0 +1,413 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2006 Lin Ma <Lin.Ma@sun.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include "logview-iface-io.h"
+#include "misc.h"
+#include "logview-debug.h"
+#include "logview-log.h"
+#include "logview-plugin.h"
+
+#define PLUGIN_PLAINLOG(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj), PLUGIN_TYPE_PLAINLOG, PluginPlainlog))
+#define PLUGIN_PLAINLOG_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST((klass), PLUGIN_TYPE_PLAINLOG, PluginPlainlogClass))
+#define PLAINLOG_IS_PLUGIN(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj), PLUGIN_TYPE_PLAINLOG))
+#define PLAINLOG_IS_PLUGIN_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_TYPE((klass), PLUGIN_TYPE_PLAINLOG))
+#define PLUGIN_PLAINLOG_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS((obj), PLUGIN_TYPE_PLAINLOG, PluginPlainlogClass))
+
+#undef PLUGIN_PRIO
+#define PLUGIN_PRIO	PLUG_PRIO_BASE
+#undef PLUGIN_DESC
+#define PLUGIN_DESC	"For ASCII formatted log files"
+
+typedef struct _PluginPlainLog PluginPlainLog;
+typedef struct _PluginPlainLogClass PluginPlainLogClass;
+
+struct _PluginPlainLog {
+	LogviewPlugin parent;
+
+	Log *log;
+
+	gchar *log_name;
+	LogProtocol protocol;
+	
+	time_t file_time;
+	GnomeVFSFileSize file_size;
+	GnomeVFSHandle *file_handle;
+};
+
+struct _PluginPlainLogClass {
+	LogviewPluginClass parent;
+};
+
+static GObjectClass *parent_class = NULL;
+
+/* default routines for implement the plugin interfaces */
+static void iface_init (gpointer g_iface, gpointer iface_data);
+static void io_init (gpointer g_iface, gpointer iface_data);
+static gboolean plugin_init (PluginPlainLog* self);
+static void plugin_destroy (PluginPlainLog* self);
+
+/* interfaces */
+static gboolean can_handle(PluginPlainLog *self, Log *log);
+static gboolean can_monitor(PluginPlainLog *self);
+static gboolean has_updated(PluginPlainLog *self);
+static gboolean update(PluginPlainLog *self);
+static void logf_extract_filepath (PluginPlainLog *self, gchar** dirname, gchar** filename);
+static size_t logf_read (PluginPlainLog *self, void* buffer, size_t size);
+static off_t logf_seek (PluginPlainLog *self, off_t offset, int whence);
+static off_t logf_tell (PluginPlainLog *self);
+static time_t get_modified_time (PluginPlainLog* self);
+static off_t get_size (PluginPlainLog* self);
+
+/* others */
+static gboolean regular_open (PluginPlainLog *self);
+static void regular_close (PluginPlainLog *self);
+
+/* class implement */
+static void
+plainlog_finalize (PluginPlainLog *self)
+{
+	g_free (self->log_name);
+}
+
+static void
+plainlog_class_init (gpointer g_class, gpointer g_class_data)
+{
+	LV_MARK;
+	LogviewPluginClass *parent = LOGVIEW_PLUGIN_CLASS (g_class);
+	parent_class = g_type_class_peek_parent (g_class);
+	G_OBJECT_CLASS (g_class)->finalize = (void (*) (GObject*)) plainlog_finalize;
+
+	parent->plugin_init = (gboolean (*) (LogviewPlugin*)) plugin_init;
+	parent->plugin_destroy = (void (*) (LogviewPlugin*)) plugin_destroy;
+}
+
+static void
+plainlog_instance_init (GTypeInstance *instance, gpointer g_class_data)
+{
+	PluginPlainLog *self;
+	LV_MARK;
+	self = (PluginPlainLog*) instance;
+	self->log_name = NULL;
+}
+
+static GType
+plainlog_get_type (GTypeModule* module)
+{
+	static GType type = 0;
+	LV_MARK;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			/* You fill this structure. */
+			sizeof (PluginPlainLogClass),
+			NULL,   /* base_init */
+			NULL,   /* base_finalize */
+			plainlog_class_init,   /* class_init */
+			NULL,   /* class_finalize */
+			NULL,   /* class_data */
+			sizeof (PluginPlainLog),
+			0,      /* n_preallocs */
+			plainlog_instance_init    /* instance_init */
+		};
+		static const GInterfaceInfo iface_info = {
+			iface_init,	/* interface_init */
+			NULL,		/* interface_finalize */
+			NULL		/* interface_data */
+		};
+		static const GInterfaceInfo io_info = {
+			io_init,	/* interface_init */
+			NULL,		/* interface_finalize */
+			NULL		/* interface_data */
+		};
+		type = g_type_module_register_type (module,
+						    LOGVIEW_TYPE_PLUGIN,
+						    "PlainlogType",
+						    &info,
+						    0);
+		g_type_module_add_interface (module, type, LOGVIEW_TYPE_IFACE,
+			&iface_info);
+		g_type_module_add_interface (module, type, LOGVIEW_TYPE_IFACE_IO,
+			&io_info);
+	}
+	return type;
+}
+
+static gboolean
+plugin_init (PluginPlainLog* self)
+{
+	gchar *utf8_name = NULL;
+	gchar *name;
+
+	LV_MARK;
+	if (regular_open (self))
+		return update (self);
+	utf8_name = locale_to_utf8 (self->log_name);
+	name = utf8_name ? utf8_name : self->log_name;
+	/* SUN_BRANDING */
+	log_error_appendv ("plainlog - %s - %s", name, _("Cannot open"));
+	g_free (utf8_name);
+	return FALSE;
+}
+
+static void
+plugin_destroy (PluginPlainLog* self)
+{
+	LV_MARK;
+	if (self->file_handle != NULL) {
+		regular_close (self);
+	}
+}
+
+/* interfaces */
+static gboolean
+can_handle(PluginPlainLog *self, Log *log)
+{
+	gchar *utf8_name = NULL;
+	gchar *name;
+	gchar *message = NULL;
+
+	LV_MARK;
+
+	g_object_get (G_OBJECT (log),
+		      "path", &self->log_name,
+		      "protocol", &self->protocol,
+		      NULL);
+	if (self->log_name == NULL) {
+		/* SUN_BRANDING */
+		log_error_appendv ("plainlog - %s", _("NULL Filename"));
+		return FALSE;
+	}
+	if (self->protocol == PLAIN_LOG ||
+	    self->protocol == GZIP_LOG) {
+		self->log = log;
+		return TRUE;
+	} else if (self->protocol == UNKNOWN_LOG) {
+		/* try to open */
+		if (try_to_open (self->log_name, FALSE)) {
+			self->log = log;
+			self->protocol = PLAIN_LOG;
+			g_object_set (G_OBJECT (log),
+				      "protocol", self->protocol,
+				      NULL);
+			return TRUE;
+		}
+		else {
+			utf8_name = locale_to_utf8 (self->log_name);
+			name = utf8_name ? utf8_name : self->log_name;
+			/* SUN_BRANDING */
+			log_error_appendv ("plainlog - %s - %s", name, _("Cannot open"));
+			g_free (utf8_name);
+			return FALSE;
+		}
+	}
+	utf8_name = locale_to_utf8 (self->log_name);
+	name = utf8_name ? utf8_name : self->log_name;
+	/* SUN_BRANDING */
+	message = g_strdup_printf (_("Unknown file type: %d"), self->protocol);
+	log_error_appendv ("plainlog - %s - %s", name, message);
+	g_free (message);
+	g_free (utf8_name);
+	return FALSE;
+}
+
+static gboolean
+can_monitor(PluginPlainLog *self)
+{
+	LV_MARK;
+	switch (self->protocol) {
+	case PLAIN_LOG:
+		return TRUE;
+	default:
+		return FALSE;
+	}
+}
+
+static gboolean
+has_updated(PluginPlainLog *self)
+{
+	struct stat buf;
+	LV_MARK;
+
+	if (stat (self->log_name, &buf) == 0) {
+		if (buf.st_mtime != self->file_time) {
+			return TRUE;
+		}
+	} else {
+		LV_INFO_EE ("%s stat: %s", self->log_name,
+			    error_system_string());
+	}
+	return FALSE;
+}
+
+static gboolean
+update(PluginPlainLog *self)
+{
+	struct stat buf;
+	LV_MARK;
+	g_assert (self->log_name != NULL);
+	if (stat (self->log_name, &buf) == 0) {
+		self->file_time = buf.st_mtime;
+		self->file_size = buf.st_size;
+		return TRUE;
+	} else {
+		LV_INFO_EE ("%s stat: %s", self->log_name,
+			    error_system_string());
+	}
+	return FALSE;
+}
+
+static size_t
+logf_read (PluginPlainLog *self, void* buffer, size_t size)
+{
+	GnomeVFSResult result;
+	GnomeVFSFileSize read_size;
+	
+	LV_MARK;
+	g_return_val_if_fail (self->file_handle !=NULL, -1);
+	result = gnome_vfs_read (self->file_handle,
+				 buffer,
+				 (GnomeVFSFileSize)size,
+				 &read_size);
+	if ( result != GNOME_VFS_OK) {
+		LV_INFO_EE ("%s: (%d), %s",
+			    self->log_name,
+			    result, gnome_vfs_result_to_string (result));
+		return -1;
+	}
+	return (size_t)read_size;
+}
+
+static off_t
+logf_seek (PluginPlainLog *self, off_t offset, int whence)
+{
+	GnomeVFSSeekPosition sp;
+	switch (whence) {
+	case SEEK_SET:
+		sp = GNOME_VFS_SEEK_START;
+		break;
+	case SEEK_CUR:
+		sp = GNOME_VFS_SEEK_CURRENT;
+		break;
+	case SEEK_END:
+		sp = GNOME_VFS_SEEK_END;
+		break;
+	default:
+		return -1;
+	}
+	if (gnome_vfs_seek (self->file_handle, sp, (GnomeVFSFileOffset)offset) == GNOME_VFS_OK) {
+		return logf_tell(self);
+	}
+	else
+		return -1;
+}
+
+static off_t
+logf_tell (PluginPlainLog *self)
+{
+	GnomeVFSFileSize size;
+	if (gnome_vfs_tell (self->file_handle, &size) == GNOME_VFS_OK) {
+		return (off_t)size;
+	}
+	return -1;
+}
+
+static void
+logf_extract_filepath (PluginPlainLog *self, gchar** dirname, gchar** filename)
+{
+	extract_filepath (self->log_name, dirname, filename);
+}
+
+static off_t
+get_size (PluginPlainLog* self)
+{
+	return self->file_size;
+}
+
+static time_t
+get_modified_time (PluginPlainLog* self)
+{
+	return self->file_time;
+}
+
+static void
+io_init (gpointer g_iface, gpointer iface_data)
+{
+	LogviewIFaceIO *iface = (LogviewIFaceIO*) g_iface;
+	LV_MARK;
+	iface->can_monitor = (gboolean (*) (LogviewIIO*)) can_monitor;
+	iface->extract_filepath = (void (*) (LogviewIIO*, gchar**, gchar**)) logf_extract_filepath;
+	iface->has_updated = (gboolean (*) (LogviewIIO*)) has_updated;
+	iface->update = (void (*) (LogviewIIO*)) update;
+	iface->read = (size_t (*) (LogviewIIO*, void*, size_t)) logf_read;
+	iface->seek = (off_t (*) (LogviewIIO*, off_t, int)) logf_seek;
+	iface->tell = (off_t (*) (LogviewIIO*)) logf_tell;
+	iface->get_size = (off_t (*) (LogviewIIO*)) get_size;
+	iface->get_modified_time = (time_t (*) (LogviewIIO*)) get_modified_time;
+}
+
+static void
+iface_init (gpointer g_iface, gpointer iface_data)
+{
+	LogviewInterface *iface = (LogviewInterface*)g_iface;
+	LV_MARK;
+	iface->can_handle = (gboolean (*) (LogviewIFace*, Log*)) can_handle;
+}
+
+/* log functions */
+static gboolean
+regular_open (PluginPlainLog *self)
+{
+	char *file_name;
+	GnomeVFSResult result;
+
+	if (self->protocol == GZIP_LOG) {
+		file_name = g_strdup_printf ("%s#gzip:", self->log_name);
+	}
+	else {
+		file_name = g_strdup (self->log_name);
+	}
+	result = gnome_vfs_open (&self->file_handle,
+				 file_name, GNOME_VFS_OPEN_READ);
+	g_free (file_name);
+	if (result != GNOME_VFS_OK) {
+		LV_INFO_EE ("%s: (%d), %s",
+			    self->log_name,
+			    result, gnome_vfs_result_to_string (result));
+		self->file_handle = NULL;
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void
+regular_close (PluginPlainLog *self)
+{
+	GnomeVFSResult result;
+
+	result = gnome_vfs_close (self->file_handle);
+	if (result != GNOME_VFS_OK) {
+		LV_INFO_EE ("%s: (%d), %s",
+			    self->log_name,
+			    result, gnome_vfs_result_to_string (result));
+	}
+}
+
+LOGVIEW_INIT_PLUGIN (plainlog_get_type)
diff --git a/logview/userprefs.c b/logview/userprefs.c
index c205a6d..35419f5 100644
--- a/logview/userprefs.c
+++ b/logview/userprefs.c
@@ -27,9 +27,11 @@
 #include <gtk/gtk.h>
 #include <gconf/gconf-client.h>
 #include <libgnomevfs/gnome-vfs.h>
-#include "userprefs.h"
 #include <sys/stat.h>
+#include "userprefs.h"
 #include "logview.h"
+#include "logview-plugin-manager.h"
+#include "logview-debug.h"
 
 #define LOGVIEW_DEFAULT_HEIGHT 400
 #define LOGVIEW_DEFAULT_WIDTH 600
@@ -49,94 +51,18 @@
 static GConfClient *gconf_client = NULL;
 static UserPrefs *prefs;
 
-static GSList*
-parse_syslog(gchar *syslog_file) {
-
-	/* Most of this stolen from sysklogd sources */
-
-	char *logfile = NULL;
-	char cbuf[BUFSIZ];
-	char *cline, *p;
-	FILE *cf;
-	GSList *logfiles = NULL;
-	if ((cf = fopen(syslog_file, "r")) == NULL) {
-		return NULL;
-	}
-	cline = cbuf;
-	while (fgets(cline, sizeof(cbuf) - (cline - cbuf), cf) != NULL) {
-		gchar **list;
-		gint i;
-		for (p = cline; g_ascii_isspace(*p); ++p);
-		if (*p == '\0' || *p == '#' || *p == '\n')
-			continue;
-		list = g_strsplit_set (p, ", -\t()\n", 0);
-		for (i = 0; list[i]; ++i) {
-			if (*list[i] == '/' &&
-				g_slist_find_custom(logfiles, list[i],
-				(GCompareFunc)g_ascii_strcasecmp) == NULL) {
-					logfiles = g_slist_insert (logfiles,
-						g_strdup(list[i]), 0);
-			}
-		}
-		g_strfreev(list);
-	}
-	fclose(cf);
-	return logfiles;
-}
-
 static void
 prefs_create_defaults (UserPrefs *p)
 {
-	int i;
-	gchar *logfiles[] = {
-		"/var/log/sys.log",
-#ifndef ON_SUN_OS
-		"/var/log/messages",
-		"/var/log/secure",
-		"/var/log/maillog",
-		"/var/log/cron",
-		"/var/log/Xorg.0.log",
-		"/var/log/XFree86.0.log",
-		"/var/log/auth.log",
-		"/var/log/cups/error_log",
-#else
-		"/var/adm/messages",
-		"/var/adm/sulog",
-		"/var/log/authlog",
-		"/var/log/brlog",
-		"/var/log/postrun.log",
-		"/var/log/scrollkeeper.log",
-		"/var/log/snmpd.log",
-		"/var/log/sysidconfig.log",
-		"/var/log/swupas/swupas.log",
-		"/var/log/swupas/swupas.error.log",
-#endif
-	NULL};
-	struct stat filestat;
-	GSList *logs = NULL;
-	GnomeVFSResult result;
-	GnomeVFSHandle *handle;
+	GSList* idx;
 
-	g_assert (p != NULL);
-	
-	/* For first time running, try parsing various logfiles */
-	/* Try to parse syslog.conf to get logfile names */
+	LV_MARK;
+	g_assert (p->logs == NULL);
+	p->logfile = NULL;
+	p->logs = pluginmgr_get_all_logs ();
 
-	result = gnome_vfs_open (&handle, "/etc/syslog.conf", GNOME_VFS_OPEN_READ);
-	if (result == GNOME_VFS_OK) {
-		gnome_vfs_close (handle);
-		logs = parse_syslog ("/etc/syslog.conf");
-	}
-	
-	for (i=0; logfiles[i]; i++) {
-	    if (g_slist_find_custom(logs, logfiles[i], (GCompareFunc)g_ascii_strcasecmp) == NULL &&
-		file_is_log (logfiles[i], FALSE))
-		logs = g_slist_insert (logs, g_strdup(logfiles[i]), 0);
-	}
-
-	if (logs != NULL) {
-		p->logs = logs;
-		p->logfile = logs->data;
+	for (idx = p->logs; idx != NULL; idx = idx->next) {
+		LV_ERR ("add|log: %s", (gchar*) idx->data);
 	}
 }
 
@@ -159,22 +85,23 @@ prefs_load (void)
 					 &err);
 	if (p->logs == NULL)
 		prefs_create_defaults (p);
+	if (p->logs)
+		p->logfile = p->logs->data;
 	
 	logfile = NULL;
 	logfile = gconf_client_get_string (gconf_client, GCONF_LOGFILE, NULL);
-	if (logfile && logfile[0] != '\0' && file_is_log (logfile, FALSE)) {
-		gboolean found;
+	if (logfile && logfile[0] != '\0') {
 		GSList *iter;
 		
-		p->logfile = g_strdup (logfile);
-		g_free (logfile);
-	
-		for (found = FALSE, iter = p->logs;
-		     iter != NULL;
+		for (iter = p->logs; iter != NULL; 
 		     iter = g_slist_next (iter)) {
-			if (g_ascii_strncasecmp (iter->data, p->logfile, 255) == 0)
-				found = TRUE;
+			if (g_ascii_strncasecmp (iter->data, 
+						 logfile, 255) == 0) {
+				p->logfile = iter->data;
+				break;
+			}
 		}
+		g_free (logfile);
 	}
 
 	width = gconf_client_get_int (gconf_client, GCONF_WIDTH_KEY, NULL);
@@ -184,6 +111,7 @@ prefs_load (void)
 	p->width = (width == 0 ? LOGVIEW_DEFAULT_WIDTH : width);
 	p->height = (height == 0 ? LOGVIEW_DEFAULT_HEIGHT : height);
 	p->fontsize = fontsize;
+	p->logfile = g_strdup (p->logfile);
 
 	return p;
 }
@@ -260,22 +188,38 @@ prefs_get_height (void)
 void
 prefs_free_loglist ()
 {
-	g_slist_free (prefs->logs);
-	prefs->logs = NULL;
+	GSList *idx;
+	if (prefs->logs) {
+		for (idx = prefs->logs; idx; idx = g_slist_next(idx)) {
+			g_free ((gchar*)idx->data);
+		}
+		g_slist_free (prefs->logs);
+		prefs->logs = NULL;
+	}
+	if (prefs->logfile) {
+		g_free (prefs->logfile);
+		prefs->logfile = NULL;
+	}
 }
 
 void
-prefs_store_log (gchar *name)
+prefs_store_log (const gchar *name)
 {
+	LV_INFO ("[Prefs Store] %s", name);
 	if (name && name[0] != '\0')
-		prefs->logs = g_slist_append (prefs->logs, name);
+		prefs->logs = g_slist_append (prefs->logs, g_strdup (name));
 }
 
 void
-prefs_store_active_log (gchar *name)
+prefs_store_active_log (const gchar *name)
 {
 	/* name can be NULL if no active log */
-	prefs->logfile = name;
+	if (prefs->logfile)
+		g_free (prefs->logfile);
+	if (name)
+		prefs->logfile = g_strdup(name);
+	else
+		prefs->logfile = NULL;
 }
 
 void
@@ -287,8 +231,6 @@ prefs_store_fontsize (int fontsize)
 void
 prefs_save (void)
 {
-	GSList *logs;
-
 	g_assert (gconf_client != NULL);
 
 	if (prefs->logfile) {
diff --git a/logview/userprefs.h b/logview/userprefs.h
index 1cf10fb..174165e 100644
--- a/logview/userprefs.h
+++ b/logview/userprefs.h
@@ -33,8 +33,8 @@ gchar *prefs_get_active_log (void);
 GSList *prefs_get_logs (void);
 int prefs_get_width (void);
 int prefs_get_height (void);
-void prefs_store_log (gchar *name);
-void prefs_store_active_log (gchar *name);
+void prefs_store_log (const gchar *name);
+void prefs_store_active_log (const gchar *name);
 void prefs_store_fontsize (int fontsize);
 void prefs_store_window_size (GtkWidget *window);
 void prefs_save (void);
