c980b2e4b1
When files are opened remotely, always use gtk_window_present() to bring the main window to front or whatever the window manager decides to do (part of #2276179). On Windows, we send a pointer to the main window to the remote instance which then brings the window to the front (grab focus). This should work better than the previous implementation and should avoid the blinking tasklist item. git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@3502 ea778897-0a13-0410-b9d1-a72fbfd435f5
690 lines
16 KiB
C
690 lines
16 KiB
C
/*
|
|
* socket.c - this file is part of Geany, a fast and lightweight IDE
|
|
*
|
|
* Copyright 2006-2009 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
|
|
* Copyright 2006-2009 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
|
|
* Copyright 2006 Hiroyuki Yamamoto (author of Sylpheed)
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
/*
|
|
* Socket setup and messages handling.
|
|
* The socket allows detection and messages to be sent to the first running instance of Geany.
|
|
* Only the first instance loads session files at startup, and opens files from the command-line.
|
|
*/
|
|
|
|
/*
|
|
* Little dev doc:
|
|
* Each command which is sent between two instances (see send_open_command and
|
|
* socket_lock_input_cb) should have the following scheme:
|
|
* command name\n
|
|
* data\n
|
|
* data\n
|
|
* ...
|
|
* .\n
|
|
* The first thing should be the command name followed by the data belonging to the command and
|
|
* to mark the end of data send a single '.'. Each message should be ended with \n.
|
|
* The command window is only available on Windows and takes no additional data, instead it
|
|
* writes back a Windows handle (HWND) for the main window to set it to the foreground (focus).
|
|
*
|
|
* At the moment the commands window, open, line and column are available.
|
|
*
|
|
* About the socket files on Unix-like systems:
|
|
* Geany creates a socket in /tmp (or any other directory returned by g_get_tmp_dir()) and
|
|
* a symlink in the current configuration to the created socket file. The symlink is named
|
|
* geany_socket_<hostname>_<displayname> (displayname is the name of the active X display).
|
|
* If the socket file cannot be created in the temporary directory, Geany creates the socket file
|
|
* directly in the configuration directory as a fallback.
|
|
*
|
|
*/
|
|
|
|
|
|
#include "geany.h"
|
|
|
|
#ifdef HAVE_SOCKET
|
|
|
|
#ifndef G_OS_WIN32
|
|
# include <string.h>
|
|
# include <sys/time.h>
|
|
# include <sys/types.h>
|
|
# include <sys/socket.h>
|
|
# include <sys/un.h>
|
|
# include <netinet/in.h>
|
|
# include <glib/gstdio.h>
|
|
#else
|
|
# include <gdk/gdkwin32.h>
|
|
# include <windows.h>
|
|
# include <winsock2.h>
|
|
# include <ws2tcpip.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "main.h"
|
|
#include "socket.h"
|
|
#include "document.h"
|
|
#include "support.h"
|
|
#include "ui_utils.h"
|
|
#include "utils.h"
|
|
#include "encodings.h"
|
|
|
|
|
|
|
|
#ifdef G_OS_WIN32
|
|
#define REMOTE_CMD_PORT 49876
|
|
#define SOCKET_IS_VALID(s) ((s) != INVALID_SOCKET)
|
|
#else
|
|
#define SOCKET_IS_VALID(s) ((s) >= 0)
|
|
#define INVALID_SOCKET (-1)
|
|
#endif
|
|
|
|
|
|
struct socket_info_struct socket_info;
|
|
|
|
|
|
#ifdef G_OS_WIN32
|
|
static gint socket_fd_connect_inet (gushort port);
|
|
static gint socket_fd_open_inet (gushort port);
|
|
static void socket_init_win32 (void);
|
|
#else
|
|
static gint socket_fd_connect_unix (const gchar *path);
|
|
static gint socket_fd_open_unix (const gchar *path);
|
|
#endif
|
|
|
|
static gint socket_fd_write (gint sock, const gchar *buf, gint len);
|
|
static gint socket_fd_write_all (gint sock, const gchar *buf, gint len);
|
|
static gint socket_fd_gets (gint sock, gchar *buf, gint len);
|
|
static gint socket_fd_check_io (gint fd, GIOCondition cond);
|
|
static gint socket_fd_read (gint sock, gchar *buf, gint len);
|
|
static gint socket_fd_recv (gint fd, gchar *buf, gint len, gint flags);
|
|
static gint socket_fd_close (gint sock);
|
|
|
|
|
|
|
|
void send_open_command(gint sock, gint argc, gchar **argv)
|
|
{
|
|
gint i;
|
|
gchar *filename;
|
|
|
|
g_return_if_fail(argc > 1);
|
|
geany_debug("using running instance of Geany");
|
|
|
|
if (cl_options.goto_line >= 0)
|
|
{
|
|
gchar *line = g_strdup_printf("%d\n", cl_options.goto_line);
|
|
socket_fd_write_all(sock, "line\n", 5);
|
|
socket_fd_write_all(sock, line, strlen(line));
|
|
socket_fd_write_all(sock, ".\n", 2);
|
|
g_free(line);
|
|
}
|
|
|
|
if (cl_options.goto_column >= 0)
|
|
{
|
|
gchar *col = g_strdup_printf("%d\n", cl_options.goto_column);
|
|
socket_fd_write_all(sock, "column\n", 7);
|
|
socket_fd_write_all(sock, col, strlen(col));
|
|
socket_fd_write_all(sock, ".\n", 2);
|
|
g_free(col);
|
|
}
|
|
|
|
socket_fd_write_all(sock, "open\n", 5);
|
|
|
|
for (i = 1; i < argc && argv[i] != NULL; i++)
|
|
{
|
|
filename = main_get_argv_filename(argv[i]);
|
|
|
|
/* if the filename is valid or if a new file should be opened is check on the other side */
|
|
if (filename != NULL)
|
|
{
|
|
socket_fd_write_all(sock, filename, strlen(filename));
|
|
socket_fd_write_all(sock, "\n", 1);
|
|
}
|
|
else
|
|
{
|
|
g_printerr(_("Could not find file '%s'."), filename);
|
|
g_printerr("\n"); /* keep translation from open_cl_files() in main.c. */
|
|
}
|
|
g_free(filename);
|
|
}
|
|
socket_fd_write_all(sock, ".\n", 2);
|
|
}
|
|
|
|
|
|
#ifndef G_OS_WIN32
|
|
static void remove_socket_link_full(void)
|
|
{
|
|
gchar real_path[512];
|
|
gsize len;
|
|
|
|
real_path[0] = '\0';
|
|
|
|
/* read the contents of the symbolic link socket_info.file_name and delete it
|
|
* readlink should return something like "/tmp/geany_socket.499602d2" */
|
|
len = readlink(socket_info.file_name, real_path, sizeof(real_path) - 1);
|
|
if ((gint) len > 0)
|
|
{
|
|
real_path[len] = '\0';
|
|
g_unlink(real_path);
|
|
}
|
|
g_unlink(socket_info.file_name);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* (Unix domain) socket support to replace the old FIFO code
|
|
* (taken from Sylpheed, thanks)
|
|
* Returns the created socket, -1 if an error occurred or -2 if another socket exists and files
|
|
* were sent to it. */
|
|
gint socket_init(gint argc, gchar **argv)
|
|
{
|
|
gint sock;
|
|
#ifdef G_OS_WIN32
|
|
HANDLE hmutex;
|
|
HWND hwnd;
|
|
socket_init_win32();
|
|
hmutex = CreateMutexA(NULL, FALSE, "Geany");
|
|
if (! hmutex)
|
|
{
|
|
geany_debug("cannot create Mutex\n");
|
|
return -1;
|
|
}
|
|
if (GetLastError() != ERROR_ALREADY_EXISTS)
|
|
{
|
|
/* To support multiple instances with different configuration directories (as we do on
|
|
* non-Windows systems) we would need to use different port number s but it might be
|
|
* difficult to get a port number which is unique for a configuration directory (path)
|
|
* and which is unused. This port number has to be guessed by the first and new instance
|
|
* and the only data is the configuration directory path.
|
|
* For now we use one port number, that is we support only one instance at all. */
|
|
sock = socket_fd_open_inet(REMOTE_CMD_PORT);
|
|
if (sock < 0)
|
|
return 0;
|
|
return sock;
|
|
}
|
|
|
|
sock = socket_fd_connect_inet(REMOTE_CMD_PORT);
|
|
if (sock < 0)
|
|
return -1;
|
|
#else
|
|
gchar *display_name = gdk_get_display();
|
|
gchar *hostname = utils_get_hostname();
|
|
gchar *p;
|
|
|
|
if (display_name == NULL)
|
|
display_name = g_strdup("NODISPLAY");
|
|
|
|
/* these lines are taken from dcopc.c in kdelibs */
|
|
if ((p = strrchr(display_name, '.')) > strrchr(display_name, ':') && p != NULL)
|
|
*p = '\0';
|
|
while ((p = strchr(display_name, ':')) != NULL)
|
|
*p = '_';
|
|
|
|
if (socket_info.file_name == NULL)
|
|
socket_info.file_name = g_strdup_printf("%s%cgeany_socket_%s_%s",
|
|
app->configdir, G_DIR_SEPARATOR, hostname, display_name);
|
|
|
|
g_free(display_name);
|
|
g_free(hostname);
|
|
|
|
sock = socket_fd_connect_unix(socket_info.file_name);
|
|
if (sock < 0)
|
|
{
|
|
remove_socket_link_full(); /* deletes the socket file and the symlink */
|
|
return socket_fd_open_unix(socket_info.file_name);
|
|
}
|
|
#endif
|
|
|
|
/* remote command mode, here we have another running instance and want to use it */
|
|
|
|
#ifdef G_OS_WIN32
|
|
/* first we send a request to retrieve the window handle and focus the window */
|
|
socket_fd_write_all(sock, "window\n", 7);
|
|
if (socket_fd_read(sock, (gchar *)&hwnd, sizeof(hwnd)) == sizeof(hwnd))
|
|
SetForegroundWindow(hwnd);
|
|
#endif
|
|
/* now we send the command line args */
|
|
if (argc > 1)
|
|
{
|
|
send_open_command(sock, argc, argv);
|
|
}
|
|
|
|
socket_fd_close(sock);
|
|
return -2;
|
|
}
|
|
|
|
|
|
gint socket_finalize(void)
|
|
{
|
|
if (socket_info.lock_socket < 0) return -1;
|
|
|
|
if (socket_info.lock_socket_tag > 0)
|
|
g_source_remove(socket_info.lock_socket_tag);
|
|
if (socket_info.read_ioc)
|
|
{
|
|
g_io_channel_shutdown(socket_info.read_ioc, FALSE, NULL);
|
|
g_io_channel_unref(socket_info.read_ioc);
|
|
socket_info.read_ioc = NULL;
|
|
}
|
|
|
|
#ifdef G_OS_WIN32
|
|
WSACleanup();
|
|
#else
|
|
if (socket_info.file_name != NULL)
|
|
{
|
|
remove_socket_link_full(); /* deletes the socket file and the symlink */
|
|
g_free(socket_info.file_name);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef G_OS_UNIX
|
|
static gint socket_fd_connect_unix(const gchar *path)
|
|
{
|
|
gint sock;
|
|
struct sockaddr_un addr;
|
|
|
|
sock = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
perror("fd_connect_unix(): socket");
|
|
return -1;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
|
|
|
|
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
|
{
|
|
socket_fd_close(sock);
|
|
return -1;
|
|
}
|
|
|
|
return sock;
|
|
}
|
|
|
|
|
|
static gint socket_fd_open_unix(const gchar *path)
|
|
{
|
|
gint sock;
|
|
struct sockaddr_un addr;
|
|
gint val;
|
|
gchar *real_path;
|
|
|
|
sock = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if (sock < 0)
|
|
{
|
|
perror("sock_open_unix(): socket");
|
|
return -1;
|
|
}
|
|
|
|
val = 1;
|
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
|
|
{
|
|
perror("setsockopt");
|
|
socket_fd_close(sock);
|
|
return -1;
|
|
}
|
|
|
|
/* fix for #1888561:
|
|
* in case the configuration directory is located on a network file system or any other
|
|
* file system which doesn't support sockets, we just link the socket there and create the
|
|
* real socket in the system's tmp directory assuming it supports sockets */
|
|
real_path = g_strdup_printf("%s%cgeany_socket.%08x",
|
|
g_get_tmp_dir(), G_DIR_SEPARATOR, g_random_int());
|
|
|
|
if (utils_is_file_writeable(real_path) != 0)
|
|
{ /* if real_path is not writable for us, fall back to ~/.config/geany/geany_socket_*_* */
|
|
/* instead of creating a symlink and print a warning */
|
|
g_warning("Socket %s could not be written, using %s as fallback.", real_path, path);
|
|
setptr(real_path, g_strdup(path));
|
|
}
|
|
/* create a symlink in e.g. ~/.config/geany/geany_socket_hostname__0 to /tmp/geany_socket.499602d2 */
|
|
else if (symlink(real_path, path) != 0)
|
|
{
|
|
perror("symlink");
|
|
socket_fd_close(sock);
|
|
return -1;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, real_path, sizeof(addr.sun_path) - 1);
|
|
|
|
g_free(real_path);
|
|
|
|
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
|
{
|
|
perror("bind");
|
|
socket_fd_close(sock);
|
|
return -1;
|
|
}
|
|
|
|
if (listen(sock, 1) < 0)
|
|
{
|
|
perror("listen");
|
|
socket_fd_close(sock);
|
|
return -1;
|
|
}
|
|
|
|
return sock;
|
|
}
|
|
#endif
|
|
|
|
static gint socket_fd_close(gint fd)
|
|
{
|
|
#ifdef G_OS_WIN32
|
|
return closesocket(fd);
|
|
#else
|
|
return close(fd);
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef G_OS_WIN32
|
|
static gint socket_fd_open_inet(gushort port)
|
|
{
|
|
SOCKET sock;
|
|
struct sockaddr_in addr;
|
|
gchar val;
|
|
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (! SOCKET_IS_VALID(sock))
|
|
{
|
|
geany_debug("fd_open_inet(): socket() failed: %d\n", WSAGetLastError());
|
|
return -1;
|
|
}
|
|
|
|
val = 1;
|
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
|
|
{
|
|
perror("setsockopt");
|
|
socket_fd_close(sock);
|
|
return -1;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
|
{
|
|
perror("bind");
|
|
socket_fd_close(sock);
|
|
return -1;
|
|
}
|
|
|
|
if (listen(sock, 1) < 0)
|
|
{
|
|
perror("listen");
|
|
socket_fd_close(sock);
|
|
return -1;
|
|
}
|
|
|
|
return sock;
|
|
}
|
|
|
|
|
|
static gint socket_fd_connect_inet(gushort port)
|
|
{
|
|
SOCKET sock;
|
|
struct sockaddr_in addr;
|
|
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (! SOCKET_IS_VALID(sock))
|
|
{
|
|
geany_debug("fd_connect_inet(): socket() failed: %d\n", WSAGetLastError());
|
|
return -1;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
|
{
|
|
socket_fd_close(sock);
|
|
return -1;
|
|
}
|
|
|
|
return sock;
|
|
}
|
|
|
|
|
|
static void socket_init_win32(void)
|
|
{
|
|
WSADATA wsadata;
|
|
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != NO_ERROR)
|
|
geany_debug("WSAStartup() failed\n");
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
static void handle_input_filename(const gchar *buf)
|
|
{
|
|
gchar *utf8_filename, *locale_filename;
|
|
|
|
/* we never know how the input is encoded, so do the best auto detection we can */
|
|
if (! g_utf8_validate(buf, -1, NULL))
|
|
utf8_filename = encodings_convert_to_utf8(buf, (gsize) -1, NULL);
|
|
else
|
|
utf8_filename = g_strdup(buf);
|
|
|
|
locale_filename = utils_get_locale_from_utf8(utf8_filename);
|
|
g_free(utf8_filename);
|
|
if (locale_filename)
|
|
main_handle_filename(locale_filename);
|
|
g_free(locale_filename);
|
|
}
|
|
|
|
|
|
gboolean socket_lock_input_cb(GIOChannel *source, GIOCondition condition, gpointer data)
|
|
{
|
|
gint fd, sock;
|
|
gchar buf[4096];
|
|
struct sockaddr_in caddr;
|
|
guint caddr_len = sizeof(caddr);
|
|
GtkWidget *window = data;
|
|
|
|
fd = g_io_channel_unix_get_fd(source);
|
|
sock = accept(fd, (struct sockaddr *)&caddr, &caddr_len);
|
|
|
|
/* first get the command */
|
|
while (socket_fd_gets(sock, buf, sizeof(buf)) != -1)
|
|
{
|
|
if (strncmp(buf, "open", 4) == 0)
|
|
{
|
|
while (socket_fd_gets(sock, buf, sizeof(buf)) != -1 && *buf != '.')
|
|
{
|
|
handle_input_filename(g_strstrip(buf));
|
|
}
|
|
}
|
|
else if (strncmp(buf, "line", 4) == 0)
|
|
{
|
|
while (socket_fd_gets(sock, buf, sizeof(buf)) != -1 && *buf != '.')
|
|
{
|
|
g_strstrip(buf); /* remove \n char */
|
|
/* on any error we get 0 which should be safe enough as fallback */
|
|
cl_options.goto_line = atoi(buf);
|
|
}
|
|
}
|
|
else if (strncmp(buf, "column", 6) == 0)
|
|
{
|
|
while (socket_fd_gets(sock, buf, sizeof(buf)) != -1 && *buf != '.')
|
|
{
|
|
g_strstrip(buf); /* remove \n char */
|
|
/* on any error we get 0 which should be safe enough as fallback */
|
|
cl_options.goto_column = atoi(buf);
|
|
}
|
|
}
|
|
#ifdef G_OS_WIN32
|
|
else if (strncmp(buf, "window", 6) == 0)
|
|
{
|
|
HWND hwnd = (HWND) gdk_win32_drawable_get_handle(GDK_DRAWABLE(window->window));
|
|
socket_fd_write(sock, (gchar *)&hwnd, sizeof(hwnd));
|
|
|
|
gtk_window_present(GTK_WINDOW(window));
|
|
#ifdef G_OS_WIN32
|
|
gdk_window_show(window->window);
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
socket_fd_close(sock);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gint socket_fd_gets(gint fd, gchar *buf, gint len)
|
|
{
|
|
gchar *newline, *bp = buf;
|
|
gint n;
|
|
|
|
if (--len < 1)
|
|
return -1;
|
|
do
|
|
{
|
|
if ((n = socket_fd_recv(fd, bp, len, MSG_PEEK)) <= 0)
|
|
return -1;
|
|
if ((newline = memchr(bp, '\n', n)) != NULL)
|
|
n = newline - bp + 1;
|
|
if ((n = socket_fd_read(fd, bp, n)) < 0)
|
|
return -1;
|
|
bp += n;
|
|
len -= n;
|
|
} while (! newline && len);
|
|
|
|
*bp = '\0';
|
|
return bp - buf;
|
|
}
|
|
|
|
|
|
static gint socket_fd_recv(gint fd, gchar *buf, gint len, gint flags)
|
|
{
|
|
if (socket_fd_check_io(fd, G_IO_IN) < 0)
|
|
return -1;
|
|
|
|
return recv(fd, buf, len, flags);
|
|
}
|
|
|
|
|
|
static gint socket_fd_read(gint fd, gchar *buf, gint len)
|
|
{
|
|
if (socket_fd_check_io(fd, G_IO_IN) < 0)
|
|
return -1;
|
|
|
|
#ifdef G_OS_WIN32
|
|
return recv(fd, buf, len, 0);
|
|
#else
|
|
return read(fd, buf, len);
|
|
#endif
|
|
}
|
|
|
|
|
|
static gint socket_fd_check_io(gint fd, GIOCondition cond)
|
|
{
|
|
struct timeval timeout;
|
|
fd_set fds;
|
|
#ifdef G_OS_UNIX
|
|
gint flags;
|
|
#endif
|
|
|
|
timeout.tv_sec = 60;
|
|
timeout.tv_usec = 0;
|
|
|
|
#ifdef G_OS_UNIX
|
|
/* checking for non-blocking mode */
|
|
flags = fcntl(fd, F_GETFL, 0);
|
|
if (flags < 0)
|
|
{
|
|
perror("fcntl");
|
|
return 0;
|
|
}
|
|
if ((flags & O_NONBLOCK) != 0)
|
|
return 0;
|
|
#endif
|
|
|
|
FD_ZERO(&fds);
|
|
FD_SET(fd, &fds);
|
|
|
|
if (cond == G_IO_IN)
|
|
{
|
|
select(fd + 1, &fds, NULL, NULL, &timeout);
|
|
}
|
|
else
|
|
{
|
|
select(fd + 1, NULL, &fds, NULL, &timeout);
|
|
}
|
|
|
|
if (FD_ISSET(fd, &fds))
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
geany_debug("Socket IO timeout");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
static gint socket_fd_write_all(gint fd, const gchar *buf, gint len)
|
|
{
|
|
gint n, wrlen = 0;
|
|
|
|
while (len)
|
|
{
|
|
n = socket_fd_write(fd, buf, len);
|
|
if (n <= 0)
|
|
return -1;
|
|
len -= n;
|
|
wrlen += n;
|
|
buf += n;
|
|
}
|
|
|
|
return wrlen;
|
|
}
|
|
|
|
|
|
gint socket_fd_write(gint fd, const gchar *buf, gint len)
|
|
{
|
|
if (socket_fd_check_io(fd, G_IO_OUT) < 0)
|
|
return -1;
|
|
|
|
#ifdef G_OS_WIN32
|
|
return send(fd, buf, len, 0);
|
|
#else
|
|
return write(fd, buf, len);
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
|