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