1/*
2 * Copyright 1990 Network Computing Devices
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Network Computing Devices not be
9 * used in advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.  Network Computing
11 * Devices makes no representations about the suitability of this software
12 * for any purpose.  It is provided "as is" without express or implied
13 * warranty.
14 *
15 * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
17 * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
18 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
19 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
20 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
21 * OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Author:  	Dave Lemke, Network Computing Devices, Inc
24 */
25/*
26 * font server i/o routines
27 */
28
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32#include "libxfontint.h"
33
34#ifdef WIN32
35#define _WILLWINSOCK_
36#include	"X11/Xwindows.h"
37#endif
38
39#define FONT_t
40#define TRANS_CLIENT
41#include 	"X11/Xtrans/Xtrans.h"
42#include	"X11/Xpoll.h"
43#include	<X11/fonts/FS.h>
44#include	<X11/fonts/FSproto.h>
45#include	<X11/fonts/fontmisc.h>
46#include	<X11/fonts/fontstruct.h>
47#include	"fservestr.h"
48
49#include	<stdio.h>
50#include	<signal.h>
51#include	<sys/types.h>
52#if !defined(WIN32)
53#include	<sys/socket.h>
54#endif
55#include	<errno.h>
56#ifdef WIN32
57#define EWOULDBLOCK WSAEWOULDBLOCK
58#undef EINTR
59#define EINTR WSAEINTR
60#endif
61
62
63
64static int  padlength[4] = {0, 3, 2, 1};
65
66static int
67_fs_resize (FSBufPtr buf, long size);
68
69static void
70_fs_downsize (FSBufPtr buf, long size);
71
72int
73_fs_poll_connect (XtransConnInfo trans_conn, int timeout)
74{
75    fd_set	    w_mask;
76    struct timeval  tv;
77    int		    fs_fd = _FontTransGetConnectionNumber (trans_conn);
78    int		    ret;
79
80    do
81    {
82	tv.tv_usec = 0;
83	tv.tv_sec = timeout;
84	FD_ZERO (&w_mask);
85	FD_SET (fs_fd, &w_mask);
86	ret = Select (fs_fd + 1, NULL, &w_mask, NULL, &tv);
87    } while (ret < 0 && ECHECK(EINTR));
88    if (ret == 0)
89	return FSIO_BLOCK;
90    if (ret < 0)
91	return FSIO_ERROR;
92    return FSIO_READY;
93}
94
95XtransConnInfo
96_fs_connect(char *servername, int *err)
97{
98    XtransConnInfo  trans_conn;		/* transport connection object */
99    int		    ret;
100    int		    i = 0;
101    int		    retries = 5;
102
103    /*
104     * Open the network connection.
105     */
106    if( (trans_conn=_FontTransOpenCOTSClient(servername)) == NULL )
107    {
108	*err = FSIO_ERROR;
109	return 0;
110    }
111
112    /*
113     * Set the connection non-blocking since we use select() to block.
114     */
115
116    _FontTransSetOption(trans_conn, TRANS_NONBLOCKING, 1);
117
118    do {
119	i = _FontTransConnect(trans_conn,servername);
120    } while ((i == TRANS_TRY_CONNECT_AGAIN) && (retries-- > 0));
121
122    if (i < 0)
123    {
124	if (i == TRANS_IN_PROGRESS)
125	    ret = FSIO_BLOCK;
126	else
127	    ret = FSIO_ERROR;
128    }
129    else
130	ret = FSIO_READY;
131
132    if (ret == FSIO_ERROR)
133    {
134	_FontTransClose(trans_conn);
135	trans_conn = 0;
136    }
137
138    *err = ret;
139    return trans_conn;
140}
141
142static int
143_fs_fill (FSFpePtr conn)
144{
145    long    avail;
146    long    bytes_read;
147    Bool    waited = FALSE;
148
149    if (_fs_flush (conn) < 0)
150	return FSIO_ERROR;
151    /*
152     * Don't go overboard here; stop reading when we've
153     * got enough to satisfy the pending request
154     */
155    while ((conn->inNeed - (conn->inBuf.insert - conn->inBuf.remove)) > 0)
156    {
157	avail = conn->inBuf.size - conn->inBuf.insert;
158	/*
159	 * For SVR4 with a unix-domain connection, ETEST() after selecting
160	 * readable means the server has died.  To do this here, we look for
161	 * two consecutive reads returning ETEST().
162	 */
163	ESET (0);
164	bytes_read =_FontTransRead(conn->trans_conn,
165				   conn->inBuf.buf + conn->inBuf.insert,
166				   avail);
167	if (bytes_read > 0) {
168	    conn->inBuf.insert += bytes_read;
169	    waited = FALSE;
170	}
171	else
172	{
173	    if (bytes_read == 0 || ETEST ())
174	    {
175		if (!waited)
176		{
177		    waited = TRUE;
178		    if (_fs_wait_for_readable (conn, 0) == FSIO_BLOCK)
179			return FSIO_BLOCK;
180		    continue;
181		}
182	    }
183	    if (!ECHECK(EINTR))
184	    {
185	        _fs_connection_died (conn);
186	        return FSIO_ERROR;
187	    }
188	}
189    }
190    return FSIO_READY;
191}
192
193/*
194 * Make space and return whether data have already arrived
195 */
196
197int
198_fs_start_read (FSFpePtr conn, long size, char **buf)
199{
200    int	    ret;
201
202    conn->inNeed = size;
203    if (fs_inqueued(conn) < size)
204    {
205	if (_fs_resize (&conn->inBuf, size) != FSIO_READY)
206	{
207	    _fs_connection_died (conn);
208	    return FSIO_ERROR;
209	}
210	ret = _fs_fill (conn);
211	if (ret == FSIO_ERROR)
212	    return ret;
213	if (ret == FSIO_BLOCK || fs_inqueued(conn) < size)
214	    return FSIO_BLOCK;
215    }
216    if (buf)
217	*buf = conn->inBuf.buf + conn->inBuf.remove;
218    return FSIO_READY;
219}
220
221void
222_fs_done_read (FSFpePtr conn, long size)
223{
224    if (conn->inBuf.insert - conn->inBuf.remove < size)
225    {
226#ifdef DEBUG
227	fprintf (stderr, "_fs_done_read skipping to many bytes\n");
228#endif
229	return;
230    }
231    conn->inBuf.remove += size;
232    conn->inNeed -= size;
233    _fs_downsize (&conn->inBuf, FS_BUF_MAX);
234}
235
236long
237_fs_pad_length (long len)
238{
239    return len + padlength[len&3];
240}
241
242int
243_fs_flush (FSFpePtr conn)
244{
245    long    bytes_written;
246    long    remain;
247
248    /* XXX - hack.  The right fix is to remember that the font server
249       has gone away when we first discovered it. */
250    if (conn->fs_fd < 0)
251	return FSIO_ERROR;
252
253    while ((remain = conn->outBuf.insert - conn->outBuf.remove) > 0)
254    {
255	bytes_written = _FontTransWrite(conn->trans_conn,
256					conn->outBuf.buf + conn->outBuf.remove,
257					(int) remain);
258	if (bytes_written > 0)
259	{
260	    conn->outBuf.remove += bytes_written;
261	}
262	else
263	{
264	    if (bytes_written == 0 || ETEST ())
265	    {
266		conn->brokenWriteTime = GetTimeInMillis () + FS_FLUSH_POLL;
267		_fs_mark_block (conn, FS_BROKEN_WRITE);
268		break;
269	    }
270	    if (!ECHECK (EINTR))
271	    {
272		_fs_connection_died (conn);
273		return FSIO_ERROR;
274	    }
275	}
276    }
277    if (conn->outBuf.remove == conn->outBuf.insert)
278    {
279	_fs_unmark_block (conn, FS_BROKEN_WRITE|FS_PENDING_WRITE);
280	if (conn->outBuf.size > FS_BUF_INC)
281	    conn->outBuf.buf = realloc (conn->outBuf.buf, FS_BUF_INC);
282	conn->outBuf.remove = conn->outBuf.insert = 0;
283    }
284    return FSIO_READY;
285}
286
287static int
288_fs_resize (FSBufPtr buf, long size)
289{
290    char    *new;
291    long    new_size;
292
293    if (buf->remove)
294    {
295	if (buf->remove != buf->insert)
296	{
297	    memmove (buf->buf,
298		     buf->buf + buf->remove,
299		     buf->insert - buf->remove);
300	}
301	buf->insert -= buf->remove;
302	buf->remove = 0;
303    }
304    if (buf->size - buf->remove < size)
305    {
306	new_size = ((buf->remove + size + FS_BUF_INC) / FS_BUF_INC) * FS_BUF_INC;
307	new = realloc (buf->buf, new_size);
308	if (!new)
309	    return FSIO_ERROR;
310	buf->buf = new;
311	buf->size = new_size;
312    }
313    return FSIO_READY;
314}
315
316static void
317_fs_downsize (FSBufPtr buf, long size)
318{
319    if (buf->insert == buf->remove)
320    {
321	buf->insert = buf->remove = 0;
322	if (buf->size > size)
323	{
324	    buf->buf = realloc (buf->buf, size);
325	    buf->size = size;
326	}
327    }
328}
329
330void
331_fs_io_reinit (FSFpePtr conn)
332{
333    conn->outBuf.insert = conn->outBuf.remove = 0;
334    _fs_downsize (&conn->outBuf, FS_BUF_INC);
335    conn->inBuf.insert = conn->inBuf.remove = 0;
336    _fs_downsize (&conn->inBuf, FS_BUF_MAX);
337}
338
339Bool
340_fs_io_init (FSFpePtr conn)
341{
342    conn->outBuf.insert = conn->outBuf.remove = 0;
343    conn->outBuf.buf = malloc (FS_BUF_INC);
344    if (!conn->outBuf.buf)
345	return FALSE;
346    conn->outBuf.size = FS_BUF_INC;
347
348    conn->inBuf.insert = conn->inBuf.remove = 0;
349    conn->inBuf.buf = malloc (FS_BUF_INC);
350    if (!conn->inBuf.buf)
351    {
352	free (conn->outBuf.buf);
353	conn->outBuf.buf = 0;
354	return FALSE;
355    }
356    conn->inBuf.size = FS_BUF_INC;
357
358    return TRUE;
359}
360
361void
362_fs_io_fini (FSFpePtr conn)
363{
364    if (conn->outBuf.buf)
365	free (conn->outBuf.buf);
366    if (conn->inBuf.buf)
367	free (conn->inBuf.buf);
368}
369
370static int
371_fs_do_write(FSFpePtr conn, const char *data, long len, long size)
372{
373    if (size == 0) {
374#ifdef DEBUG
375	fprintf(stderr, "tried to write 0 bytes \n");
376#endif
377	return FSIO_READY;
378    }
379
380    if (conn->fs_fd == -1)
381	return FSIO_ERROR;
382
383    while (conn->outBuf.insert + size > conn->outBuf.size)
384    {
385	if (_fs_flush (conn) < 0)
386	    return FSIO_ERROR;
387	if (_fs_resize (&conn->outBuf, size) < 0)
388	{
389	    _fs_connection_died (conn);
390	    return FSIO_ERROR;
391	}
392    }
393    memcpy (conn->outBuf.buf + conn->outBuf.insert, data, len);
394    /* Clear pad data */
395    memset (conn->outBuf.buf + conn->outBuf.insert + len, 0, size - len);
396    conn->outBuf.insert += size;
397    _fs_mark_block (conn, FS_PENDING_WRITE);
398    return FSIO_READY;
399}
400
401/*
402 * Write the indicated bytes
403 */
404int
405_fs_write (FSFpePtr conn, const char *data, long len)
406{
407    return _fs_do_write (conn, data, len, len);
408}
409
410/*
411 * Write the indicated bytes adding any appropriate pad
412 */
413int
414_fs_write_pad(FSFpePtr conn, const char *data, long len)
415{
416    return _fs_do_write (conn, data, len, len + padlength[len & 3]);
417}
418
419int
420_fs_wait_for_readable(FSFpePtr conn, int ms)
421{
422    fd_set	r_mask;
423    fd_set	e_mask;
424    int         result;
425    struct timeval  tv;
426
427    for (;;) {
428	if (conn->fs_fd < 0)
429	    return FSIO_ERROR;
430	FD_ZERO(&r_mask);
431	FD_ZERO(&e_mask);
432	tv.tv_sec = ms / 1000;
433	tv.tv_usec = (ms % 1000) * 1000;
434	FD_SET(conn->fs_fd, &r_mask);
435	FD_SET(conn->fs_fd, &e_mask);
436	result = Select(conn->fs_fd + 1, &r_mask, NULL, &e_mask, &tv);
437	if (result < 0)
438	{
439	    if (ECHECK(EINTR) || ECHECK(EAGAIN))
440		continue;
441	    else
442		return FSIO_ERROR;
443	}
444	if (result == 0)
445	    return FSIO_BLOCK;
446	if (FD_ISSET(conn->fs_fd, &r_mask))
447	    return FSIO_READY;
448	return FSIO_ERROR;
449    }
450}
451