emacs-devel
[Top][All Lists]
Advanced

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

Re: Non-blocking open-network-stream


From: Kim F. Storm
Subject: Re: Non-blocking open-network-stream
Date: 25 Feb 2002 23:38:52 +0100
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.2.50

Here is my second attempt at a patch to support non-blocking
open-network-stream.

If I don't receive any comments on this, I'll install it later
this week.

Index: process.c
===================================================================
RCS file: /cvs/emacs/src/process.c,v
retrieving revision 1.351
diff -c -r1.351 process.c
*** process.c   7 Jan 2002 21:16:38 -0000       1.351
--- process.c   25 Feb 2002 22:33:22 -0000
***************
*** 1,6 ****
  /* Asynchronous subprocess control for GNU Emacs.
!    Copyright (C) 1985, 86, 87, 88, 93, 94, 95, 96, 98, 1999, 2001
!       Free Software Foundation, Inc.
  
  This file is part of GNU Emacs.
  
--- 1,6 ----
  /* Asynchronous subprocess control for GNU Emacs.
!    Copyright (C) 1985, 86, 87, 88, 93, 94, 95, 96, 98, 1999,
!       2001, 2002 Free Software Foundation, Inc.
  
  This file is part of GNU Emacs.
  
***************
*** 112,118 ****
  #include "atimer.h"
  
  Lisp_Object Qprocessp;
! Lisp_Object Qrun, Qstop, Qsignal, Qopen, Qclosed;
  Lisp_Object Qlast_nonmenu_event;
  /* Qexit is declared and initialized in eval.c.  */
  
--- 112,119 ----
  #include "atimer.h"
  
  Lisp_Object Qprocessp;
! Lisp_Object Qrun, Qstop, Qsignal;
! Lisp_Object Qopen, Qclosed, Qconnect, Qfailed;
  Lisp_Object Qlast_nonmenu_event;
  /* Qexit is declared and initialized in eval.c.  */
  
***************
*** 173,178 ****
--- 174,199 ----
  /* Number of events for which the user or sentinel has been notified.  */
  int update_tick;
  
+ /* Define NON_BLOCKING_CONNECT if we can support non-blocking connects.  */
+ 
+ #ifdef BROKEN_NON_BLOCKING_CONNECT
+ #undef NON_BLOCKING_CONNECT
+ #else
+ #ifndef NON_BLOCKING_CONNECT
+ #ifdef HAVE_SOCKETS
+ #ifdef HAVE_SELECT
+ #if defined (HAVE_GETPEERNAME) || defined (GNU_LINUX)
+ #if defined (O_NONBLOCK) || defined (O_NDELAY)
+ #if defined (EWOULDBLOCK) || defined (EINPROGRESS)
+ #define NON_BLOCKING_CONNECT
+ #endif /* EWOULDBLOCK || EINPROGRESS */
+ #endif /* O_NONBLOCK || O_NDELAY */
+ #endif /* HAVE_GETPEERNAME || GNU_LINUX */
+ #endif /* HAVE_SELECT */
+ #endif /* HAVE_SOCKETS */
+ #endif /* NON_BLOCKING_CONNECT */
+ #endif /* BROKEN_NON_BLOCKING_CONNECT */
+ 
  #include "sysselect.h"
  
  extern int keyboard_bit_set P_ ((SELECT_TYPE *));
***************
*** 195,200 ****
--- 216,230 ----
  
  static SELECT_TYPE non_process_wait_mask;
  
+ /* Mask of bits indicating the descriptors that we wait for connect to
+    complete on.  Once they complete, they are removed from this mask
+    and added to the input_wait_mask and non_keyboard_wait_mask.  */
+ 
+ static SELECT_TYPE connect_wait_mask;
+ 
+ /* Number of bits set in connect_wait_mask.  */
+ static int num_pending_connects;
+ 
  /* The largest descriptor currently in use for a process object.  */
  static int max_process_desc;
  
***************
*** 335,340 ****
--- 365,377 ----
        return concat2 (build_string ("exited abnormally with code "),
                      concat2 (string, string2));
      }
+   else if (EQ (symbol, Qfailed))
+     {
+       string = Fnumber_to_string (make_number (code));
+       string2 = build_string ("\n");
+       return concat2 (build_string ("failed with code "),
+                     concat2 (string, string2));
+     }
    else
      return Fcopy_sequence (Fsymbol_name (symbol));
  }
***************
*** 1741,1762 ****
     deactivate and close it via delete-process */
  
  DEFUN ("open-network-stream", Fopen_network_stream, Sopen_network_stream, 
!        4, 4, 0, 
         doc: /* Open a TCP connection for a service to a host.
  Returns a subprocess-object to represent the connection.
  Input and output work as for subprocesses; `delete-process' closes it.
! Args are NAME BUFFER HOST SERVICE.
  NAME is name for process.  It is modified if necessary to make it unique.
  BUFFER is the buffer (or buffer-name) to associate with the process.
   Process output goes at end of that buffer, unless you specify
   an output stream or filter function to handle the output.
   BUFFER may be also nil, meaning that this process is not associated
!  with any buffer
! Third arg is name of the host to connect to, or its IP address.
! Fourth arg SERVICE is name of the service desired, or an integer
! specifying a port number to connect to.  */)
!      (name, buffer, host, service)
!       Lisp_Object name, buffer, host, service;
  {
    Lisp_Object proc;
  #ifdef HAVE_GETADDRINFO
--- 1778,1809 ----
     deactivate and close it via delete-process */
  
  DEFUN ("open-network-stream", Fopen_network_stream, Sopen_network_stream, 
!        4, 7, 0, 
         doc: /* Open a TCP connection for a service to a host.
  Returns a subprocess-object to represent the connection.
  Input and output work as for subprocesses; `delete-process' closes it.
! Args are NAME BUFFER HOST SERVICE FILTER SENTINEL NON-BLOCKING.
  NAME is name for process.  It is modified if necessary to make it unique.
  BUFFER is the buffer (or buffer-name) to associate with the process.
   Process output goes at end of that buffer, unless you specify
   an output stream or filter function to handle the output.
   BUFFER may be also nil, meaning that this process is not associated
!  with any buffer.
! HOST is name of the host to connect to, or its IP address.
! SERVICE is name of the service desired, or an integer specifying a
!  port number to connect to.   
! FILTER and SENTINEL are optional args specifying the filter and
!  sentinel functions associated with the network stream.
! NON-BLOCKING is optional arg requesting an non-blocking connect.
!  When non-nil, open-network-stream will return immediately without
!  waiting for the connection to be made.  Instead, the sentinel function
!  will be called with second arg equal to "run" (if successful) or
!  "failed" when the connect completes.
!  On systems without non-blocking connect, this function waits
!  for the connect to complete and then proceeds emulating a
!  non-blocking connect.  */)
!      (name, buffer, host, service, filter, sentinel, non_blocking)
!       Lisp_Object name, buffer, host, service, filter, sentinel, non_blocking;
  {
    Lisp_Object proc;
  #ifdef HAVE_GETADDRINFO
***************
*** 1773,1789 ****
    int port;
  #endif /* HAVE_GETADDRINFO */
    int s = -1, outch, inch;
!   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
    int retry = 0;
    int count = specpdl_ptr - specpdl;
    int count1;
  
  #ifdef WINDOWSNT
    /* Ensure socket support is loaded if available. */
    init_winsock (TRUE);
  #endif
  
!   GCPRO4 (name, buffer, host, service);
    CHECK_STRING (name);
    CHECK_STRING (host);
  
--- 1820,1846 ----
    int port;
  #endif /* HAVE_GETADDRINFO */
    int s = -1, outch, inch;
!   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
    int retry = 0;
    int count = specpdl_ptr - specpdl;
    int count1;
+   int is_non_blocking;
  
  #ifdef WINDOWSNT
    /* Ensure socket support is loaded if available. */
    init_winsock (TRUE);
  #endif
  
!   non_blocking = (NILP (non_blocking) ? Qnil : Qt);
! #ifdef NON_BLOCKING_CONNECT
!   is_non_blocking = !NILP (non_blocking);
! #else
!   is_non_blocking = 0;
! #endif
! 
!   /* Can only GCPRO 5 variables */
!   sentinel = Fcons (sentinel, filter);
!   GCPRO5 (name, buffer, host, service, sentinel);
    CHECK_STRING (name);
    CHECK_STRING (host);
  
***************
*** 1867,1872 ****
--- 1924,1942 ----
        count1 = specpdl_ptr - specpdl;
        record_unwind_protect (close_file_unwind, make_number (s));
  
+ #ifdef NON_BLOCKING_CONNECT
+       if (is_non_blocking)
+       {
+ #ifdef O_NONBLOCK
+         ret = fcntl (s, F_SETFL, O_NONBLOCK);
+ #else
+         ret = fcntl (s, F_SETFL, O_NDELAY);
+ #endif
+         if (ret < 0)
+           is_non_blocking = 0;
+       }
+ #endif
+ 
      loop:
  
        immediate_quit = 1;
***************
*** 1885,1893 ****
        turn_on_atimers (1);
  
        if (ret == 0 || xerrno == EISCONN)
!       /* The unwind-protect will be discarded afterwards.
!          Likewise for immediate_quit.  */
        break;
  
        immediate_quit = 0;
  
--- 1955,1978 ----
        turn_on_atimers (1);
  
        if (ret == 0 || xerrno == EISCONN)
!       {
!         is_non_blocking = 0;
!         /* The unwind-protect will be discarded afterwards.
!            Likewise for immediate_quit.  */
!         break;
!       }
! 
! #ifdef NON_BLOCKING_CONNECT
! #ifdef EINPROGRESS
!       if (is_non_blocking && xerrno == EINPROGRESS)
!       break;
! #else
! #ifdef EWOULDBLOCK
!       if (is_non_blocking && xerrno == EWOULDBLOCK)
        break;
+ #endif
+ #endif
+ #endif
  
        immediate_quit = 0;
  
***************
*** 1989,2001 ****
    if (interrupt_input)
      unrequest_sigio ();
  
   loop:
  
    immediate_quit = 1;
    QUIT;
  
!   if (connect (s, (struct sockaddr *) &address, sizeof address) == -1
!       && errno != EISCONN)
      {
        int xerrno = errno;
  
--- 2074,2115 ----
    if (interrupt_input)
      unrequest_sigio ();
  
+ #ifdef NON_BLOCKING_CONNECT
+   if (is_non_blocking)
+     {
+ #ifdef O_NONBLOCK
+       ret = fcntl (s, F_SETFL, O_NONBLOCK);
+ #else
+       ret = fcntl (s, F_SETFL, O_NDELAY);
+ #endif
+       if (ret < 0)
+       is_non_blocking = 0;
+     }
+ #endif /* NON_BLOCKING_CONNECT */
+ 
   loop:
  
    immediate_quit = 1;
    QUIT;
  
!   ret = connect (s, (struct sockaddr *) &address, sizeof address);
! 
!   if (ret == 0 || errno == EISCONN)
!     {
!       is_non_blocking = 0;
!     }
! #ifdef NON_BLOCKING_CONNECT
! #ifdef EINPROGRESS
!   else if (is_non_blocking && ret == -1 && errno == EINPROGRESS)
!     ;
! #else
! #ifdef EWOULDBLOCK
!   else if (is_non_blocking && ret == -1 && errno == EWOULDBLOCK)
!     ;
! #endif
! #endif
! #endif
!   else 
      {
        int xerrno = errno;
  
***************
*** 2041,2046 ****
--- 2155,2161 ----
      request_sigio ();
  
  #else /* TERM */
+   is_non_blocking = 0;
    s = connect_server (0);
    if (s < 0)
      report_file_error ("error creating socket", Fcons (name, Qnil));
***************
*** 2068,2082 ****
    XPROCESS (proc)->childp = Fcons (host, Fcons (service, Qnil));
    XPROCESS (proc)->command_channel_p = Qnil;
    XPROCESS (proc)->buffer = buffer;
!   XPROCESS (proc)->sentinel = Qnil;
!   XPROCESS (proc)->filter = Qnil;
    XPROCESS (proc)->command = Qnil;
    XPROCESS (proc)->pid = Qnil;
    XSETINT (XPROCESS (proc)->infd, inch);
    XSETINT (XPROCESS (proc)->outfd, outch);
    XPROCESS (proc)->status = Qrun;
!   FD_SET (inch, &input_wait_mask);
!   FD_SET (inch, &non_keyboard_wait_mask);
    if (inch > max_process_desc)
      max_process_desc = inch;
  
--- 2183,2209 ----
    XPROCESS (proc)->childp = Fcons (host, Fcons (service, Qnil));
    XPROCESS (proc)->command_channel_p = Qnil;
    XPROCESS (proc)->buffer = buffer;
!   XPROCESS (proc)->sentinel = XCAR (sentinel);
!   XPROCESS (proc)->filter = XCDR (sentinel);
    XPROCESS (proc)->command = Qnil;
    XPROCESS (proc)->pid = Qnil;
    XSETINT (XPROCESS (proc)->infd, inch);
    XSETINT (XPROCESS (proc)->outfd, outch);
    XPROCESS (proc)->status = Qrun;
!   if (!NILP (non_blocking))
!     {
!       XPROCESS (proc)->status = Qconnect;
!       if (!FD_ISSET (inch, &connect_wait_mask))
!       {
!         FD_SET (inch, &connect_wait_mask);
!         num_pending_connects++;
!       }
!     }
!   else if (!EQ (XPROCESS (proc)->filter, Qt))
!     {
!       FD_SET (inch, &input_wait_mask);
!       FD_SET (inch, &non_keyboard_wait_mask);
!     }
    if (inch > max_process_desc)
      max_process_desc = inch;
  
***************
*** 2194,2199 ****
--- 2321,2332 ----
        chan_process[inchannel] = Qnil;
        FD_CLR (inchannel, &input_wait_mask);
        FD_CLR (inchannel, &non_keyboard_wait_mask);
+       if (FD_ISSET (inchannel, &connect_wait_mask))
+       {
+         FD_CLR (inchannel, &connect_wait_mask);
+         if (--num_pending_connects < 0)
+           abort ();
+       }
        if (inchannel == max_process_desc)
        {
          int i;
***************
*** 2358,2367 ****
  {
    register int channel, nfds;
    static SELECT_TYPE Available;
    int xerrno;
    Lisp_Object proc;
    EMACS_TIME timeout, end_time;
-   SELECT_TYPE Atemp;
    int wait_channel = -1;
    struct Lisp_Process *wait_proc = 0;
    int got_some_input = 0;
--- 2491,2501 ----
  {
    register int channel, nfds;
    static SELECT_TYPE Available;
+   static SELECT_TYPE Connecting;
+   int check_connect, no_avail;
    int xerrno;
    Lisp_Object proc;
    EMACS_TIME timeout, end_time;
    int wait_channel = -1;
    struct Lisp_Process *wait_proc = 0;
    int got_some_input = 0;
***************
*** 2370,2375 ****
--- 2504,2510 ----
    Lisp_Object wait_for_cell = Qnil;
  
    FD_ZERO (&Available);
+   FD_ZERO (&Connecting);
  
    /* If read_kbd is a process to watch, set wait_proc and wait_channel
       accordingly.  */
***************
*** 2511,2521 ****
         timeout to get our attention.  */
        if (update_tick != process_tick && do_display)
        {
          Atemp = input_wait_mask;
          EMACS_SET_SECS_USECS (timeout, 0, 0);
          if ((select (max (max_process_desc, max_keyboard_desc) + 1,
!                      &Atemp, (SELECT_TYPE *)0, (SELECT_TYPE *)0,
!                      &timeout)
               <= 0))
            {
              /* It's okay for us to do this and then continue with
--- 2646,2660 ----
         timeout to get our attention.  */
        if (update_tick != process_tick && do_display)
        {
+         SELECT_TYPE Atemp, Ctemp;
+ 
          Atemp = input_wait_mask;
+         Ctemp = connect_wait_mask;
          EMACS_SET_SECS_USECS (timeout, 0, 0);
          if ((select (max (max_process_desc, max_keyboard_desc) + 1,
!                      &Atemp, 
!                      (num_pending_connects > 0 ? &Ctemp : (SELECT_TYPE *)0),
!                      (SELECT_TYPE *)0, &timeout)
               <= 0))
            {
              /* It's okay for us to do this and then continue with
***************
*** 2525,2535 ****
            }
        }
  
!       /* Don't wait for output from a non-running process.  */
        if (wait_proc != 0 && !NILP (wait_proc->raw_status_low))
        update_status (wait_proc);
        if (wait_proc != 0
!         && ! EQ (wait_proc->status, Qrun))
        {
          int nread, total_nread = 0;
  
--- 2664,2676 ----
            }
        }
  
!       /* Don't wait for output from a non-running process.  Just
!          read whatever data has already been received.  */
        if (wait_proc != 0 && !NILP (wait_proc->raw_status_low))
        update_status (wait_proc);
        if (wait_proc != 0
!         && ! EQ (wait_proc->status, Qrun)
!         && ! EQ (wait_proc->status, Qconnect))
        {
          int nread, total_nread = 0;
  
***************
*** 2568,2578 ****
        /* Wait till there is something to do */
  
        if (!NILP (wait_for_cell))
!       Available = non_process_wait_mask;
!       else if (! XINT (read_kbd))
!       Available = non_keyboard_wait_mask;
        else
!       Available = input_wait_mask;
  
        /* If frame size has changed or the window is newly mapped,
         redisplay now, before we start to wait.  There is a race
--- 2709,2726 ----
        /* Wait till there is something to do */
  
        if (!NILP (wait_for_cell))
!       {
!         Available = non_process_wait_mask;
!         check_connect = 0;
!       }
        else
!       {
!         if (! XINT (read_kbd))
!           Available = non_keyboard_wait_mask;
!         else
!           Available = input_wait_mask;
!         check_connect = (num_pending_connects > 0);
!       }
  
        /* If frame size has changed or the window is newly mapped,
         redisplay now, before we start to wait.  There is a race
***************
*** 2587,2601 ****
            set_waiting_for_input (&timeout);
        }
  
        if (XINT (read_kbd) && detect_input_pending ())
        {
          nfds = 0;
!         FD_ZERO (&Available);
        }
        else
!       nfds = select (max (max_process_desc, max_keyboard_desc) + 1,
!                      &Available, (SELECT_TYPE *)0, (SELECT_TYPE *)0,
!                      &timeout);
  
        xerrno = errno;
  
--- 2735,2755 ----
            set_waiting_for_input (&timeout);
        }
  
+       no_avail = 0;
        if (XINT (read_kbd) && detect_input_pending ())
        {
          nfds = 0;
!         no_avail = 1;
        }
        else
!       {
!         if (check_connect)
!           Connecting = connect_wait_mask;
!         nfds = select (max (max_process_desc, max_keyboard_desc) + 1,
!                        &Available, 
!                        (check_connect ? &Connecting : (SELECT_TYPE *)0),
!                        (SELECT_TYPE *)0, &timeout);
!       }
  
        xerrno = errno;
  
***************
*** 2611,2617 ****
        if (nfds < 0)
        {
          if (xerrno == EINTR)
!           FD_ZERO (&Available);
  #ifdef ultrix
          /* Ultrix select seems to return ENOMEM when it is
             interrupted.  Treat it just like EINTR.  Bleah.  Note
--- 2765,2771 ----
        if (nfds < 0)
        {
          if (xerrno == EINTR)
!           no_avail = 1;
  #ifdef ultrix
          /* Ultrix select seems to return ENOMEM when it is
             interrupted.  Treat it just like EINTR.  Bleah.  Note
***************
*** 2619,2631 ****
             "__ultrix__"; the latter is only defined under GCC, but
             not by DEC's bundled CC.  -JimB  */
          else if (xerrno == ENOMEM)
!           FD_ZERO (&Available);
  #endif
  #ifdef ALLIANT
          /* This happens for no known reason on ALLIANT.
             I am guessing that this is the right response. -- RMS.  */
          else if (xerrno == EFAULT)
!           FD_ZERO (&Available);
  #endif
          else if (xerrno == EBADF)
            {
--- 2773,2785 ----
             "__ultrix__"; the latter is only defined under GCC, but
             not by DEC's bundled CC.  -JimB  */
          else if (xerrno == ENOMEM)
!           no_avail = 1;
  #endif
  #ifdef ALLIANT
          /* This happens for no known reason on ALLIANT.
             I am guessing that this is the right response. -- RMS.  */
          else if (xerrno == EFAULT)
!           no_avail = 1;
  #endif
          else if (xerrno == EBADF)
            {
***************
*** 2637,2643 ****
                 So, SIGHUP is ignored (see def of PTY_TTY_NAME_SPRINTF
                 in m/ibmrt-aix.h), and here we just ignore the select error.
                 Cleanup occurs c/o status_notify after SIGCLD. */
!             FD_ZERO (&Available); /* Cannot depend on values returned */
  #else
              abort ();
  #endif
--- 2791,2797 ----
                 So, SIGHUP is ignored (see def of PTY_TTY_NAME_SPRINTF
                 in m/ibmrt-aix.h), and here we just ignore the select error.
                 Cleanup occurs c/o status_notify after SIGCLD. */
!             no_avail = 1; /* Cannot depend on values returned */
  #else
              abort ();
  #endif
***************
*** 2645,2653 ****
          else
            error ("select error: %s", emacs_strerror (xerrno));
        }
  #if defined(sun) && !defined(USG5_4)
!       else if (nfds > 0 && keyboard_bit_set (&Available)
!              && interrupt_input)
        /* System sometimes fails to deliver SIGIO.
  
           David J. Mackenzie says that Emacs doesn't compile under
--- 2799,2814 ----
          else
            error ("select error: %s", emacs_strerror (xerrno));
        }
+ 
+       if (no_avail)
+       {
+         FD_ZERO (&Available);
+         check_connect = 0;
+       }
+ 
  #if defined(sun) && !defined(USG5_4)
!       if (nfds > 0 && keyboard_bit_set (&Available)
!         && interrupt_input)
        /* System sometimes fails to deliver SIGIO.
  
           David J. Mackenzie says that Emacs doesn't compile under
***************
*** 2746,2751 ****
--- 2907,2915 ----
        do_pending_window_change (0);
  
        /* Check for data from a process.  */
+       if (no_avail || nfds == 0)
+       continue;
+ 
        /* Really FIRST_PROC_DESC should be 0 on Unix,
         but this is safer in the short run.  */
        for (channel = 0; channel <= max_process_desc; channel++)
***************
*** 2837,2842 ****
--- 3001,3047 ----
                      = Fcons (Qexit, Fcons (make_number (256), Qnil));
                }
            }
+ #ifdef NON_BLOCKING_CONNECT     
+         if (check_connect && FD_ISSET (channel, &Connecting))
+           {
+             struct Lisp_Process *p;
+             struct sockaddr pname;
+             socklen_t pnamelen = sizeof(pname);
+ 
+             FD_CLR (channel, &connect_wait_mask);
+             if (--num_pending_connects < 0)
+               abort ();
+ 
+             proc = chan_process[channel];
+             if (NILP (proc))
+               continue;
+ 
+             p = XPROCESS (proc);
+             XSETINT (p->tick, ++process_tick);
+ 
+             /* If connection failed, getpeername fails */
+             if (getpeername(channel, &pname, &pnamelen) < 0)
+               {
+                 char dummy;
+                 
+                 xerrno = errno;
+                 /* Obtain connect failure code through error slippage.  */
+                 if (errno == ENOTCONN && read(channel, &dummy, 1) < 0)
+                   xerrno = errno;
+                 p->status = Fcons (Qfailed, Fcons (make_number (xerrno), 
Qnil));
+                 deactivate_process (proc);
+               }
+             else
+               {
+                 p->status = Qrun;
+                 if (!EQ (p->filter, Qt))
+                   {
+                     FD_SET (XINT (p->infd), &input_wait_mask);
+                     FD_SET (XINT (p->infd), &non_keyboard_wait_mask);
+                   }
+               }
+           }
+ #endif /* NON_BLOCKING_CONNECT */
        }                       /* end for each file descriptor */
      }                         /* end while exit conditions not met */
  
***************
*** 4419,4424 ****
--- 4624,4630 ----
  
          /* If process is still active, read any output that remains.  */
          while (! EQ (p->filter, Qt)
+                && ! EQ (p->status, Qconnect)
                 && XINT (p->infd) >= 0
                 && read_process_output (proc, XINT (p->infd)) > 0);
  
***************
*** 4653,4658 ****
--- 4859,4868 ----
    staticpro (&Qopen);
    Qclosed = intern ("closed");
    staticpro (&Qclosed);
+   Qconnect = intern ("connect");
+   staticpro (&Qconnect);
+   Qfailed = intern ("failed");
+   staticpro (&Qfailed);
  
    Qlast_nonmenu_event = intern ("last-nonmenu-event");
    staticpro (&Qlast_nonmenu_event);

-- 
Kim F. Storm <address@hidden> http://www.cua.dk




reply via email to

[Prev in Thread] Current Thread [Next in Thread]