Anonymous avatar Anonymous committed b516685 Merge

Merged from the main branch

Comments (0)

Files changed (14)

                           Use this if you experience crashes on startup
                           and you are using Suse 9.
 
+  --with-fam              whether to use FAM (or gamin) for file monitoring
+                          (default = NO)
+    FAM seems to work well, but gamin seems to be much less stable. This option
+    is not recommended if FAM is provided by gamin on your system.
+    Note that file monitoring will work even without FAM, so it's unlikely you
+    want to use this option.
+
   --with-python           whether to compile python support (default = YES)
     This option tells whether pygtk bindings should be built. If medit is built
     without python, then python plugins (builtin terminal and project support in
   echo "    use python .............................. $use_python"
   echo "    use xml ................................. $MOO_USE_XML"
   echo "    build pcre library ...................... $MOO_BUILD_PCRE"
+  echo "    use FAM ................................. $MOO_USE_FAM"
   echo
   echo "    prefix .................................. $prefix"
   echo "    editor lang files go to ................. $MOO_TEXT_LANG_FILES_DIR"
+##############################################################################
+# _MOO_AC_CHECK_FAM(action-if-found,action-if-not-found)
+#
+AC_DEFUN_ONCE([_MOO_AC_CHECK_FAM],[
+    moo_ac_save_CFLAGS="$CFLAGS"
+    moo_ac_save_LIBS="$LIBS"
+
+    if test x$FAM_LIBS = x; then
+        FAM_LIBS=-lfam
+    fi
+
+    CFLAGS="$CFLAGS $FAM_CFLAGS"
+    LIBS="$LIBS $FAM_LIBS"
+
+    AC_CHECK_HEADERS(fam.h,[
+        AC_CHECK_FUNCS([FAMMonitorDirectory FAMOpen],[fam_found=yes],[fam_found=no])
+    ],[fam_found=no])
+
+    if test x$fam_found != xno; then
+        AC_SUBST(FAM_CFLAGS)
+        AC_SUBST(FAM_LIBS)
+
+        AC_MSG_CHECKING(for FAM_CFLAGS)
+        if test -z $FAM_CFLAGS; then
+            AC_MSG_RESULT(None)
+        else
+            AC_MSG_RESULT($FAM_CFLAGS)
+        fi
+
+        AC_MSG_CHECKING(for FAM_LIBS)
+        if test -z $FAM_LIBS; then
+            AC_MSG_RESULT(None)
+        else
+            AC_MSG_RESULT($FAM_LIBS)
+        fi
+
+        AC_CHECK_DECL([FAMNoExists],[
+          AC_DEFINE(HAVE_FAMNOEXISTS, 1, [fam.h has FAMNoExists defined])
+          AC_DEFINE(MOO_USE_GAMIN, 1, [whether libfam is provided by gamin])
+        ],[],[#include <fam.h>])
+
+        MOO_FAM_CFLAGS="$FAM_CFLAGS"
+        MOO_FAM_LIBS="$FAM_LIBS"
+        ifelse([$1], , :, [$1])
+    else
+        unset FAM_CFLAGS
+        unset FAM_LIBS
+        MOO_FAM_LIBS=
+        MOO_FAM_CFLAGS=
+        ifelse([$2], , [AC_MSG_ERROR(libfam not found)], [$2])
+    fi
+
+    AC_SUBST(MOO_FAM_CFLAGS)
+    AC_SUBST(MOO_FAM_LIBS)
+    CFLAGS="$moo_ac_save_CFLAGS"
+    LIBS="$moo_ac_save_LIBS"
+])
+
+
+AC_DEFUN_ONCE([MOO_AC_FAM],[
+   AC_REQUIRE([MOO_AC_CHECK_OS])
+
+   AC_ARG_WITH([fam], AC_HELP_STRING([--with-fam], [whether to use fam or gamin for monitoring files in the editor (default = NO)]), [
+           if test x$with_fam = "xyes"; then
+               MOO_USE_FAM="yes"
+           else
+               MOO_USE_FAM="no"
+           fi
+       ],[
+           MOO_USE_FAM="no"
+   ])
+
+   if test x$MOO_OS_UNIX = xyes -a x$MOO_USE_FAM = xyes; then
+       _MOO_AC_CHECK_FAM([moo_has_fam=yes],[moo_has_fam=no])
+       if test x$moo_has_fam = xyes; then
+           MOO_USE_FAM="yes"
+           AC_DEFINE(MOO_USE_FAM, 1, [use libfam for monitoring files])
+       else
+           AC_MSG_ERROR([FAM or gamin not found.])
+       fi
+   fi
+
+    AM_CONDITIONAL(MOO_USE_FAM, test x$MOO_USE_FAM = "xyes")
+])
   MOO_AC_FLAGS
 
   MOO_AC_FUNCS
+  MOO_AC_FAM
   MOO_AC_XML
   MOO_AC_PCRE
   MOO_AC_PYTHON
 
   MOO_CFLAGS="-I`cd "$srcdir/doc" && pwd` $MOO_CFLAGS"
 
+  if test x$MOO_USE_FAM = xyes; then
+    MOO_CFLAGS="$MOO_CFLAGS $MOO_FAM_CFLAGS"
+    MOO_LIBS="$MOO_LIBS $MOO_FAM_LIBS"
+  fi
+
   AC_SUBST(MOO_LIBS)
 ])
 

moo/mooedit/gtksourceview/upstream/gtksourcecontextengine.c

  * But, e.g. if we have a big file, and scroll down, we do want the engine
  * to analyze quickly. Perhaps we want to reinstall first_update in case
  * of expose events or something. */
-#define INCREMENTAL_UPDATE_PRIORITY	GTK_TEXT_VIEW_PRIORITY_VALIDATE
+#define INCREMENTAL_UPDATE_PRIORITY	G_PRIORITY_LOW
 /* Maximal amount of time allowed to spent in one cycle of background idle. */
 #define INCREMENTAL_UPDATE_TIME_SLICE	30
 
+/* Maximal amount of time allowed to spent highlihting a single line. If it
+ * is not enough, then highlighting is disabled. */
+#define MAX_TIME_FOR_ONE_LINE		2000
+
 #define GTK_SOURCE_CONTEXT_ENGINE_ERROR (gtk_source_context_engine_error_quark ())
 
 /* Returns the definition corrsponding to the specified id. */
 	/* Whether or not to actually highlight the buffer. */
 	gboolean		 highlight;
 
+	/* Whether highlighting was disabled because of errors. */
+	gboolean		 disabled;
+
 	/* Region covering the unhighlighted text. */
 	GtkTextRegion		*refresh_region;
 
 	gint end_line;
 	GtkSourceContextEngine *ce = GTK_SOURCE_CONTEXT_ENGINE (engine);
 
-	if (!ce->priv->highlight)
+	if (!ce->priv->highlight || ce->priv->disabled)
 		return;
 
 	invalid_line = get_invalid_line (ce);
 	}
 }
 
+/**
+ * disable_highlighting:
+ *
+ * @ce: #GtkSourceContextEngine.
+ *
+ * Dsiables highlighting in case of errors (currently if highlighting
+ * a single line took too long, so that highlighting doesn't freeze
+ * text editor).
+ */
+static void
+disable_highlighting (GtkSourceContextEngine *ce)
+{
+	if (!ce->priv->disabled)
+	{
+		ce->priv->disabled = TRUE;
+		gtk_source_context_engine_attach_buffer (GTK_SOURCE_ENGINE (ce), NULL);
+		/* FIXME maybe emit some signal here? */
+	}
+}
+
 static void
 set_tag_style_hash_cb (const char             *style,
 		       GSList                 *tags,
 {
 	gint line_pos = 0;
 	GList *end_segments = NULL;
+	GTimer *timer;
 
 	g_assert (SEGMENT_IS_CONTAINER (state));
 
                 ce->priv->hint2 = state->last_child;
         g_assert (!ce->priv->hint2 || ce->priv->hint2->parent == state);
 
+	timer = g_timer_new ();
+
 	/* Find the contexts in the line. */
 	while (line_pos <= line->byte_length)
 	{
 		if (!next_segment (ce, state, line, &line_pos, &new_state))
 			break;
 
+		if (g_timer_elapsed (timer, NULL) * 1000 > MAX_TIME_FOR_ONE_LINE)
+		{
+			g_critical (_("Highlighting a single line took too much time, "
+				      "syntax highlighting will be disabled"));
+			disable_highlighting (ce);
+			break;
+		}
+
 		g_assert (new_state != NULL);
 		g_assert (SEGMENT_IS_CONTAINER (new_state));
 
 			end_segments = g_list_prepend (end_segments, state);
 	}
 
+	g_timer_destroy (timer);
+	if (ce->priv->disabled)
+		return NULL;
+
 	/* Extend current state to the end of line. */
 	segment_extend (state, line->start_at + line->char_length);
 	g_assert (line_pos <= line->byte_length);
 
 		state = analyze_line (ce, state, &line);
 
+		/* At this point analyze_line() could have disabled highlighting */
+		if (ce->priv->disabled)
+			return;
+
 #ifdef ENABLE_CHECK_TREE
 		{
 			Segment *inv = get_invalid_segment (ce);

moo/mooedit/language-specs/csharp.lang

     <style id="error" _name="Error" map-to="def:error"/>
     <style id="type" _name="Data Type" map-to="def:type"/>
     <style id="string" _name="String" map-to="def:string"/>
+    <style id="char" _name="Character" map-to="def:character"/>
+    <style id="escaped-character" _name="Escaped Character" map-to="def:special-char"/>
+    <style id="format" _name="String Format" map-to="def:special-char"/>
     <style id="keyword" _name="Keyword" map-to="def:keyword"/>
     <style id="preprocessor" _name="Preprocessor" map-to="def:preprocessor"/>
     <style id="null-value" _name="Null Value" map-to="def:special-constant"/>
     </context>
 
     <context id="multiline-string" style-ref="string">
-      <start>@"([^"]|"")*</start>
+      <start>@"</start>
       <end>"</end>
-      <include>
-        <context ref="def:escape"/>
-        <context ref="def:line-continue"/>
-      </include>
     </context>
 
     <context id="keywords" style-ref="keyword">
       </match>
     </context>
 
+    <!-- FIXME Taken from C, is it right? -->
+    <define-regex id="escaped-character" extended="true">
+      \\(                   # leading backslash
+      [\\\"\'nrbtfav\?] |   # escaped character
+      [0-7]{1,3} |          # one, two, or three octal digits
+      x[0-9A-Fa-f]+         # 'x' followed by hex digits
+      )
+    </define-regex>
+
+    <context id="string" style-ref="string" end-at-line-end="true">
+      <start>"</start>
+      <end>"</end>
+      <include>
+	<context id="csharp-format" style-ref="format">
+          <match>{[0-9][0-9:\#\%,./cdefgnrxtsuDTFGMY]*}</match>
+        </context>
+        <context id="escaped-character" style-ref="escaped-character">
+          <match>\%{escaped-character}</match>
+        </context>
+        <context ref="def:line-continue"/>
+      </include>
+    </context>
+
+    <context id="char" style-ref="char">
+      <!-- FIXME I don't know C# syntax -->
+      <match>'(\%{escaped-character}|.)'</match>
+    </context>
+
     <context id="c-sharp">
       <include>
-        <context ref="def:string"/>
-        <context ref="def:single-quoted-string"/>
+        <context ref="multiline-string"/>
+        <context ref="string"/>
+        <context ref="char"/>
         <context ref="line-comment"/>
         <context ref="multiline-comment"/>
         <context ref="close-comment-outside-comment"/>
-        <context ref="multiline-string"/>
         <context ref="if-false-comment"/>
         <context ref="preprocessor"/>
         <context ref="keywords"/>

moo/mooedit/language-specs/def.lang

             <match>\%{float}</match>
         </context>
 
-        <!-- FIXME no \n in patterns! -->
+        <!-- FIXME is it working at line end? -->
         <define-regex id="net-address" extended="true" case-sensitive="false">
                 \%[                                         # separator
                 (https?|ftp|nntp|news|javascript|about):    # protocol
-                [^\ \n]* [^\ \n.:;,?&gt;&lt;)]              # address
+                [^\ \\]* [^\ \\.:;,?&gt;&lt;)]              # address
                 (?![a-z0-9_.-])                             # separator
         </define-regex>
 

moo/mooedit/language-specs/dtd.lang

           <context ref="decl-entity"/>
           <context ref="decl-attlist"/>
           <context ref="decl-notation"/>
-          <context ref="error"/>
         </include>
       </context>
     </definitions>

moo/mooedit/language-specs/forth.lang

   </metadata>
 
   <styles>
-    <style id="comment"       _name="Comment"       map-to="def:comment"/>
-    <style id="string"        _name="String"        map-to="def:string"/>
-    <style id="preprocessor"  _name="Preprocessor"  map-to="def:preprocessor"/>
-    <style id="keyword"       _name="Keyword"       map-to="def:statement"/>
-    <style id="type"          _name="Data Type"     map-to="def:type"/>
-    <style id="number"        _name="Number"        map-to="def:decimal"/>
-    <style id="debugs"        _name="Debug Code"    map-to="def:comment"/>
-    <style id="error"         _name="Error text"    map-to="def:error"/>
-    <style id="compiler"      _name="Compiler opt"  map-to="def:identifier"/>
+    <style id="comment"       _name="Comment"            map-to="def:comment"/>
+    <style id="string"        _name="String"             map-to="def:string"/>
+    <style id="preprocessor"  _name="Preprocessor"       map-to="def:preprocessor"/>
+    <style id="keyword"       _name="Keyword"            map-to="def:statement"/>
+    <style id="type"          _name="Data Type"          map-to="def:type"/>
+    <style id="number"        _name="Number"             map-to="def:decimal"/>
+    <style id="debugs"        _name="Debug Code"         map-to="def:comment"/>
+    <style id="error"         _name="Error Text"         map-to="def:error"/>
+    <style id="compiler"      _name="Compiler Directive" map-to="def:identifier"/>
   </styles>
 
   <default-regex-options case-sensitive="false"/>

moo/mooedit/language-specs/html.lang

 
         <context id="script">
             <start>&lt;\s*script\%]</start>
-            <end>&lt;\s*/\s*script\s*&gt;</end>
+            <end>/&gt;|&lt;\s*/\s*script\s*&gt;</end>
 
             <include>
                 <context sub-pattern="0" where="start" style-ref="tag"/>

moo/mooedit/language-specs/php.lang

     <style id="comment" _name="Comment" map-to="def:comment"/>
     <style id="error" _name="Error" map-to="def:error"/>
     <style id="variable" _name="Variable" map-to="def:identifier"/>
+    <style id="identifier" _name="Identifier"/> <!-- map to nothing -->
     <style id="escape" _name="Escaped Character" map-to="def:special-char"/>
     <style id="string" _name="String" map-to="def:string"/>
     <style id="here-doc" _name="Heredoc" map-to="def:string"/>
       </include>
     </context>
 
+    <context id="identifier" style-ref="identifier">
+      <match>[a-zA-Z_][a-zA-Z0-9_]*</match>
+    </context>
+
     <context id="keywords" style-ref="keyword">
       <keyword>abstract</keyword>
       <keyword>and</keyword>
         <context ref="decimal-number"/>
         <context ref="octal-number"/>
         <context ref="hexadecimal-number"/>
+	<context ref="identifier"/>
       </include>
     </context>
 

moo/mooui/mooappabout.c

     g_string_append_printf (text, "libxml2: %s\n", LIBXML_DOTTED_VERSION);
 #endif
 
+#ifdef MOO_USE_FAM
+#ifdef MOO_USE_GAMIN
+    g_string_append_printf (text, "FAM support: gamin\n");
+#else
+    g_string_append_printf (text, "FAM support: yes\n");
+#endif
+#endif
+
     g_string_append (text, "Data dirs: ");
     dirs = moo_get_data_dirs (MOO_DATA_SHARE, NULL);
     for (p = dirs; p && *p; ++p)

moo/mooutils/moofilewatch.c

 #include "config.h"
 #endif
 
+#ifdef MOO_USE_FAM
+/* need PATH_MAX even with -ansi */
+#define _GNU_SOURCE
+#include <fam.h>
+#else
 #define WANT_STAT_MONITOR
+#endif
 
 #ifdef __WIN32__
 #include <windows.h>
     guint id;
     MooFileWatch *watch;
     char *filename;
+#ifdef MOO_USE_FAM
+    int fam_request;
+#endif
 
     MooFileWatchCallback callback;
     GDestroyNotify notify;
     guint ref_count;
     guint id;
     guint stat_timeout;
+#ifdef MOO_USE_FAM
+    FAMConnection fam_connection;
+    guint fam_connection_watch;
+#endif
     GSList      *monitors;
     GHashTable  *requests;  /* int -> Monitor* */
     guint alive : 1;
 static GQuark moo_file_watch_error_quark (void);
 
 
+#ifdef MOO_USE_FAM
+static gboolean watch_fam_start             (MooFileWatch   *watch,
+                                             GError        **error);
+static gboolean watch_fam_shutdown          (MooFileWatch   *watch,
+                                             GError        **error);
+static gboolean watch_fam_start_monitor     (MooFileWatch   *watch,
+                                             Monitor        *monitor,
+                                             GError        **error);
+static void     watch_fam_stop_monitor      (MooFileWatch   *watch,
+                                             Monitor        *monitor);
+#endif /* MOO_USE_FAM */
+
 #ifdef WANT_STAT_MONITOR
 static gboolean watch_stat_start            (MooFileWatch   *watch,
                                              GError        **error);
 
 
 static struct WatchFuncs watch_funcs = {
-#if defined(__WIN32__)
+#if defined(MOO_USE_FAM)
+    watch_fam_start,
+    watch_fam_shutdown,
+    watch_fam_start_monitor,
+    watch_fam_stop_monitor
+#elif defined(__WIN32__)
     watch_win32_start,
     watch_win32_shutdown,
     watch_win32_start_monitor,
 
 
 /*****************************************************************************/
+/* FAM
+ */
+#ifdef MOO_USE_FAM
+
+#define SET_FAM_ERROR(func,error)                       \
+    g_set_error (error, MOO_FILE_WATCH_ERROR,           \
+                 MOO_FILE_WATCH_ERROR_FAILED,           \
+                 #func " failed: %s",                   \
+                 ((FAMErrno && FamErrlist[FAMErrno]) ?  \
+                    FamErrlist[FAMErrno] :              \
+                    "unknown error"))
+
+#define MOO_FAM_SOCKET_WATCH_PRIORITY   G_PRIORITY_DEFAULT
+
+
+static gboolean read_fam_events         (GIOChannel     *source,
+                                         GIOCondition    condition,
+                                         MooFileWatch   *watch);
+
+static gboolean
+watch_fam_start (MooFileWatch   *watch,
+                 GError        **error)
+{
+    GIOChannel *fam_socket;
+
+    if (FAMOpen (&watch->fam_connection) != 0)
+    {
+        SET_FAM_ERROR (FAMOpen, error);
+        return FALSE;
+    }
+
+#ifdef HAVE_FAMNOEXISTS
+    FAMNoExists (&watch->fam_connection);
+#endif
+
+    fam_socket = g_io_channel_unix_new (watch->fam_connection.fd);
+    watch->fam_connection_watch =
+            _moo_io_add_watch_full (fam_socket, MOO_FAM_SOCKET_WATCH_PRIORITY,
+                                    G_IO_IN | G_IO_PRI | G_IO_HUP,
+                                    (GIOFunc) read_fam_events, watch, NULL);
+    g_io_channel_unref (fam_socket);
+
+    return TRUE;
+}
+
+
+static gboolean
+watch_fam_shutdown (MooFileWatch   *watch,
+                    GError        **error)
+{
+    if (watch->fam_connection_watch)
+        g_source_remove (watch->fam_connection_watch);
+
+    if (FAMClose (&watch->fam_connection))
+    {
+        SET_FAM_ERROR (FAMClose, error);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+static gboolean
+watch_fam_start_monitor (MooFileWatch   *watch,
+                         Monitor        *monitor,
+                         GError        **error)
+{
+    FAMRequest fr;
+    int result;
+
+    g_return_val_if_fail (monitor->filename != NULL, FALSE);
+
+    monitor->isdir = g_file_test (monitor->filename, G_FILE_TEST_IS_DIR) != 0;
+    monitor->id = get_new_monitor_id ();
+
+    if (monitor->isdir)
+        result = FAMMonitorDirectory (&watch->fam_connection,
+                                      monitor->filename, &fr,
+                                      GUINT_TO_POINTER (monitor->id));
+    else
+        result = FAMMonitorFile (&watch->fam_connection,
+                                 monitor->filename, &fr,
+                                 GUINT_TO_POINTER (monitor->id));
+
+    if (result != 0)
+    {
+        DEBUG_PRINT ("Connection %d: creating monitor for '%s' failed",
+                     watch->fam_connection.fd, monitor->filename);
+
+        if (monitor->isdir)
+            SET_FAM_ERROR (FAMMonitorDirectory, error);
+        else
+            SET_FAM_ERROR (FAMMonitorFile, error);
+
+        return FALSE;
+    }
+    else
+    {
+        DEBUG_PRINT ("Connection %d: created monitor %d for %s '%s'",
+                     watch->fam_connection.fd, fr.reqnum,
+                     monitor->isdir ? "directory" : "file",
+                     monitor->filename);
+    }
+
+    monitor->fam_request = fr.reqnum;
+    return TRUE;
+}
+
+
+static void
+watch_fam_stop_monitor (MooFileWatch *watch,
+                        Monitor      *monitor)
+{
+    FAMRequest fr;
+    int result;
+
+    g_return_if_fail (monitor != NULL);
+
+    fr.reqnum = monitor->fam_request;
+
+    result = FAMCancelMonitor (&watch->fam_connection, &fr);
+
+    if (result != 0)
+        DEBUG_PRINT ("Connection %d: FAMCancelMonitor for '%s' failed",
+                     watch->fam_connection.fd,
+                     monitor->filename);
+}
+
+
+static void
+do_events (MooFileWatch *watch,
+           GSList       *events)
+{
+    while (events)
+    {
+        MooFileEvent *e;
+        Monitor *monitor;
+
+        e = events->data;
+        events = events->next;
+
+        monitor = g_hash_table_lookup (watch->requests, GUINT_TO_POINTER (e->monitor_id));
+
+        if (!monitor || !monitor->alive)
+            continue;
+
+        moo_file_watch_emit_event (watch, e, monitor);
+    }
+}
+
+static MooFileEvent *
+fam_event_to_file_event (FAMEvent     *fe,
+                         MooFileWatch *watch)
+{
+    Monitor *monitor;
+    const char *filename;
+    MooFileEventCode code;
+
+    monitor = g_hash_table_lookup (watch->requests, fe->userdata);
+
+    if (!monitor)
+        return NULL;
+
+    g_assert (GUINT_TO_POINTER (monitor->id) == fe->userdata);
+    g_assert (monitor->fam_request == fe->fr.reqnum);
+
+    filename = fe->filename;
+
+    switch (fe->code)
+    {
+        case FAMCreated:
+            code = MOO_FILE_EVENT_CREATED;
+            break;
+
+        case FAMChanged:
+            /* Do not emit CHANGED for folders, since we are interested only in
+             * folder contents */
+            if (monitor->isdir)
+                return NULL;
+            code = MOO_FILE_EVENT_CHANGED;
+            break;
+
+        case FAMDeleted:
+            code = MOO_FILE_EVENT_DELETED;
+            break;
+
+        case FAMMoved:
+            /* XXX never happens with FAM, what about gamin? */
+            g_print ("file moved: %s\n", fe->filename);
+            code = MOO_FILE_EVENT_CHANGED;
+            filename = monitor->filename;
+            break;
+
+        case FAMStartExecuting:
+        case FAMStopExecuting:
+        case FAMAcknowledge:
+        case FAMExists:
+        case FAMEndExist:
+            return NULL;
+
+        default:
+            g_warning ("%s: unknown FAM code %d", G_STRLOC, fe->code);
+            return NULL;
+    }
+
+    return moo_file_event_new (filename, monitor->id, code);
+}
+
+static gboolean
+read_fam_events (G_GNUC_UNUSED GIOChannel *source,
+                 GIOCondition    condition,
+                 MooFileWatch   *watch)
+{
+    GError *error = NULL;
+    int result;
+    FAMConnection *connection = &watch->fam_connection;
+    gboolean retval = TRUE;
+    GSList *events = NULL;
+    guint n_events;
+
+    moo_file_watch_ref (watch);
+
+    if (!watch->alive || condition & (G_IO_ERR | G_IO_HUP))
+    {
+        g_warning ("Connection %d: broken FAM socket", connection->fd);
+
+        if (watch->alive && !moo_file_watch_close (watch, &error))
+        {
+            g_warning ("%s: error in moo_file_watch_close()", G_STRLOC);
+            g_warning ("%s: %s", G_STRLOC, error->message);
+            g_error_free (error);
+        }
+
+        retval = FALSE;
+        goto out;
+    }
+
+    for (n_events = 0; !error && n_events < 4096 && (result = FAMPending (connection)); n_events++)
+    {
+        if (result < 0)
+        {
+            SET_FAM_ERROR (FAMPending, &error);
+            DEBUG_PRINT ("Connection %d: FAMPending failed", connection->fd);
+        }
+        else
+        {
+            FAMEvent fe;
+
+            if (FAMNextEvent (connection, &fe) != 1)
+            {
+                SET_FAM_ERROR (FAMNextEvent, &error);
+                DEBUG_PRINT ("Connection %d: FAMNextEvent failed", connection->fd);
+            }
+            else
+            {
+                MooFileEvent *e;
+                if ((e = fam_event_to_file_event (&fe, watch)))
+                    events = g_slist_prepend (events, e);
+            }
+        }
+    }
+
+    events = g_slist_reverse (events);
+    do_events (watch, events);
+    g_slist_foreach (events, (GFunc) moo_file_event_free, NULL);
+    g_slist_free (events);
+
+    if (error)
+    {
+        g_warning ("Connection %d: error: %s", connection->fd, error->message);
+        g_error_free (error);
+        error = NULL;
+
+        if (!moo_file_watch_close (watch, &error))
+        {
+            g_warning ("%s: error in moo_file_watch_close()", G_STRLOC);
+            g_warning ("%s: %s", G_STRLOC, error->message);
+            g_error_free (error);
+        }
+
+        retval = FALSE;
+    }
+
+out:
+    moo_file_watch_unref (watch);
+    return retval;
+}
+
+
+#endif /* MOO_USE_FAM */
+
+
+/*****************************************************************************/
 /* stat()
  */
 

moo/mooutils/moofilewatch.h

  *   See COPYING file that comes with this distribution.
  */
 
-/* Files and directory monitor using stat().
+/* Files and directory monitor. Uses FAM if present, or stat() otherwise.
    On win32 does FindFirstChangeNotification and ReadDirectoryChangesW. */
 
 #ifndef MOO_FILE_WATCH_H
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.