diff options
Diffstat (limited to 'ijs_server.c')
-rw-r--r-- | ijs_server.c | 895 |
1 files changed, 895 insertions, 0 deletions
diff --git a/ijs_server.c b/ijs_server.c new file mode 100644 index 0000000..2365fcd --- /dev/null +++ b/ijs_server.c @@ -0,0 +1,895 @@ +/** + * Copyright (c) 2001-2004 artofcode LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ + +#include <stdlib.h> +#include <stdio.h> +#include "unistd_.h" +#include <string.h> + +#include "ijs.h" +#include "ijs_server.h" + +#define noVERBOSE + +typedef enum { + IJS_N_CHAN_SET = 1, + IJS_BPS_SET = 2, + IJS_CS_SET = 4, + IJS_WIDTH_SET = 8, + IJS_HEIGHT_SET = 16, + IJS_DPI_SET = 32 +} IjsFieldsSet; + +#define IJS_FIELDS_REQUIRED (IJS_N_CHAN_SET | IJS_BPS_SET | IJS_CS_SET | \ + IJS_WIDTH_SET | IJS_HEIGHT_SET | IJS_DPI_SET) + +struct _IjsServerCtx { + int fd_from; + int child_pid; + IjsSendChan send_chan; + IjsRecvChan recv_chan; + int version; + + /* callbacks */ + IjsBeginJobCb *begin_cb; + void *begin_cb_data; + IjsEndJobCb *end_cb; + void *end_cb_data; + IjsQueryStatusCb *status_cb; + void *status_cb_data; + IjsListParamsCb *list_cb; + void *list_cb_data; + IjsEnumParamCb *enum_cb; + void *enum_cb_data; + IjsSetParamCb *set_cb; + void *set_cb_data; + IjsGetParamCb *get_cb; + void *get_cb_data; + + ijs_bool in_job; + IjsJobId job_id; + + IjsPageHeader *ph; + + /* This should be IjsFieldsSet, but David Suffield reports that this + causes problems when compiling with g++. */ + int fields_set; + ijs_bool in_page; + + char *buf; + int buf_size; + int buf_ix; + char *overflow_buf; + int overflow_buf_size; + int overflow_buf_ix; +}; + +static int +ijs_server_dummy_begin_cb (void *begin_cb_data, + IjsServerCtx *ctx, + IjsJobId job_id) +{ + return 0; +} + +static int +ijs_server_dummy_end_cb (void *end_cb_data, + IjsServerCtx *ctx, + IjsJobId job_id) +{ + return 0; +} + +IjsServerCtx * +ijs_server_init (void) +{ + ijs_bool ok = TRUE; + char helo_buf[8]; + char resp_buf[8]; + int nbytes; + IjsServerCtx *ctx = (IjsServerCtx *)malloc (sizeof(IjsServerCtx)); + int fd_from, fd_to; + + memcpy (resp_buf, IJS_RESP_STR, sizeof(resp_buf)); + + fd_from = 0; + fd_to = 1; +#ifdef _MSC_VER + _setmode(fd_from, _O_BINARY); + _setmode(fd_to, _O_BINARY); +#endif +#ifdef VERBOSE + fprintf (stderr, "fd_from = %d, fd_to = %d\n", fd_from, fd_to); +#endif + ijs_recv_init (&ctx->recv_chan, fd_from); + ijs_send_init (&ctx->send_chan, fd_to); + nbytes = read (ctx->recv_chan.fd, helo_buf, sizeof(helo_buf)); + + if (nbytes != sizeof(helo_buf)) + ok = FALSE; + + if (ok) + nbytes = write (ctx->send_chan.fd, resp_buf, sizeof(resp_buf)); + if (nbytes != sizeof(resp_buf)) + ok = FALSE; + + ctx->in_job = FALSE; + ctx->job_id = -1; + + ctx->ph = NULL; + ctx->in_page = FALSE; + ctx->buf = NULL; + ctx->overflow_buf = NULL; + + ctx->begin_cb = ijs_server_dummy_begin_cb; + ctx->end_cb = ijs_server_dummy_end_cb; + + if (ok) + return ctx; + else + { + ijs_server_done (ctx); + return NULL; + } +} + +int +ijs_server_install_begin_cb (IjsServerCtx *ctx, + IjsBeginJobCb *begin_cb, void *begin_cb_data) +{ + ctx->begin_cb = begin_cb; + ctx->begin_cb_data = begin_cb_data; + return 0; +} + +int +ijs_server_install_end_cb (IjsServerCtx *ctx, + IjsEndJobCb *end_cb, void *end_cb_data) +{ + ctx->end_cb = end_cb; + ctx->end_cb_data = end_cb_data; + return 0; +} + +int +ijs_server_install_status_cb (IjsServerCtx *ctx, + IjsQueryStatusCb *status_cb, + void *status_cb_data) +{ + ctx->status_cb = status_cb; + ctx->status_cb_data = status_cb_data; + return 0; +} + +int +ijs_server_install_list_cb (IjsServerCtx *ctx, + IjsListParamsCb *list_cb, void *list_cb_data) +{ + ctx->list_cb = list_cb; + ctx->list_cb_data = list_cb_data; + return 0; +} + +int +ijs_server_install_enum_cb (IjsServerCtx *ctx, + IjsEnumParamCb *enum_cb, void *enum_cb_data) +{ + ctx->enum_cb = enum_cb; + ctx->enum_cb_data = enum_cb_data; + return 0; +} + +int +ijs_server_install_set_cb (IjsServerCtx *ctx, + IjsSetParamCb *set_cb, void *set_cb_data) +{ + ctx->set_cb = set_cb; + ctx->set_cb_data = set_cb_data; + return 0; +} + +int +ijs_server_install_get_cb (IjsServerCtx *ctx, + IjsGetParamCb *get_cb, void *get_cb_data) +{ + ctx->get_cb = get_cb; + ctx->get_cb_data = get_cb_data; + return 0; +} + +static int +ijs_server_ack (IjsServerCtx *ctx) +{ + int status; + + status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK); + if (status < 0) + return status; + return ijs_send_buf (&ctx->send_chan); +} + +void +ijs_server_done (IjsServerCtx *ctx) +{ + /* todo: close channels */ + ijs_server_ack (ctx); + + free (ctx); +} + +static int +ijs_server_nak (IjsServerCtx *ctx, int errorcode) +{ + int status; + + status = ijs_send_begin (&ctx->send_chan, IJS_CMD_NAK); + if (status < 0) + return status; + status = ijs_send_int (&ctx->send_chan, errorcode); + if (status < 0) + return status; + return ijs_send_buf (&ctx->send_chan); +} + +/* The return code is: 0 if ok, positive on normal exit, negative on error */ +typedef int (*ijs_server_proc) (IjsServerCtx *ctx); + +static int +ijs_server_proc_ack (IjsServerCtx *ctx) +{ + /* servers should not get ack commands */ + return IJS_EPROTO; +} + +static int +ijs_server_proc_nak (IjsServerCtx *ctx) +{ + /* servers should not get nak commands */ + return IJS_EPROTO; +} + +static int +ijs_server_proc_ping (IjsServerCtx *ctx) +{ + int status; + int version; + + status = ijs_recv_int (&ctx->recv_chan, &version); + if (status < 0) + return status; + if (version > IJS_VERSION) + version = IJS_VERSION; + ctx->version = version; + +#ifdef VERBOSE + fprintf (stderr, "ping version=%d\n", version); +#endif + status = ijs_send_begin (&ctx->send_chan, IJS_CMD_PONG); + if (status < 0) + return status; + status = ijs_send_int (&ctx->send_chan, IJS_VERSION); + if (status < 0) + return status; + return ijs_send_buf (&ctx->send_chan); +} + +static int +ijs_server_proc_pong (IjsServerCtx *ctx) +{ + /* servers should not get pong commands */ + return IJS_EPROTO; +} + +static int +ijs_server_proc_open (IjsServerCtx *ctx) +{ + /* A server might allocate tables here. */ + return ijs_server_ack (ctx); +} + +static int +ijs_server_proc_close (IjsServerCtx *ctx) +{ + /* A server might deallocate memory here. */ + return ijs_server_ack (ctx); +} + +static int +ijs_server_proc_begin_job (IjsServerCtx *ctx) +{ + int code; + IjsJobId job_id; + + code = ijs_recv_int (&ctx->recv_chan, &job_id); + if (code < 0) return code; + + if (ctx->in_job) + return ijs_server_nak (ctx, IJS_ETOOMANYJOBS); + ctx->in_job = TRUE; + ctx->job_id = job_id; + + return ijs_server_ack (ctx); +} + +static int +ijs_server_proc_end_job (IjsServerCtx *ctx) +{ + int code; + IjsJobId job_id; + + code = ijs_recv_int (&ctx->recv_chan, &job_id); + if (code < 0) return code; + + if (!ctx->in_job || job_id != ctx->job_id) + return ijs_server_nak (ctx, IJS_EJOBID); + ctx->in_job = FALSE; + + return ijs_server_ack (ctx); +} + +static int +ijs_server_proc_cancel_job (IjsServerCtx *ctx) +{ + int code; + IjsJobId job_id; + + code = ijs_recv_int (&ctx->recv_chan, &job_id); + if (code < 0) return code; + + if (!ctx->in_job || job_id != ctx->job_id) + return ijs_server_nak (ctx, IJS_EJOBID); + /* todo: call cancel callback here */ + ctx->in_job = FALSE; + + return ijs_server_ack (ctx); +} + +static int +ijs_server_proc_query_status (IjsServerCtx *ctx) +{ + int code; + IjsJobId job_id; + + code = ijs_recv_int (&ctx->recv_chan, &job_id); + if (code < 0) + return code; + + if (!ctx->in_job || ctx->job_id != job_id) + return ijs_server_nak (ctx, IJS_EJOBID); + + code = ctx->status_cb (ctx->list_cb_data, ctx, job_id); + if (code < 0) + return ijs_server_nak (ctx, code); + else + { + int status; + + status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK); + if (status < 0) + return status; + status = ijs_send_int (&ctx->send_chan, code); + if (status < 0) + return status; + return ijs_send_buf (&ctx->send_chan); + } +} + +static int +ijs_server_proc_list_params (IjsServerCtx *ctx) +{ + int code; + char buf[4096]; + IjsJobId job_id; + + code = ijs_recv_int (&ctx->recv_chan, &job_id); + if (code < 0) + return code; + + if (!ctx->in_job || ctx->job_id != job_id) + return ijs_server_nak (ctx, IJS_EJOBID); + + code = ctx->list_cb (ctx->list_cb_data, ctx, job_id, buf, sizeof(buf)); + if (code < 0) + return ijs_server_nak (ctx, code); + else + { + int status; + + status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK); + if (status < 0) + return status; + status = ijs_send_block (&ctx->send_chan, buf, code); + if (status < 0) + return status; + return ijs_send_buf (&ctx->send_chan); + } +} + +static int +ijs_server_proc_enum_param (IjsServerCtx *ctx) +{ + const char *key; + int key_size; + int code; + char buf[4096]; + IjsJobId job_id; + + code = ijs_recv_int (&ctx->recv_chan, &job_id); + if (code < 0) + return code; + + if (!ctx->in_job || ctx->job_id != job_id) + return ijs_server_nak (ctx, IJS_EJOBID); + + key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx; + key_size = ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx; + if (key_size == 0 || key[key_size - 1]) + return IJS_ESYNTAX; +#ifdef VERBOSE + fprintf (stderr, "ijs_server_proc_enum_param, key_size = %d\n", key_size); +#endif + + code = ctx->enum_cb (ctx->enum_cb_data, ctx, job_id, key, buf, sizeof(buf)); + if (code < 0) + return ijs_server_nak (ctx, code); + else + { + int status; + + status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK); + if (status < 0) + return status; + status = ijs_send_block (&ctx->send_chan, buf, code); + if (status < 0) + return status; + return ijs_send_buf (&ctx->send_chan); + } +} + +static int +ijs_strnlen (const char *s, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (s[i] == 0) + return i; + return size; +} + +static int +ijs_server_parse_int (const char *value, int size, int *result) +{ + int num = 0; + int i; + int sign = 1; + + i = 0; + if (i == size) + return IJS_ESYNTAX; + if (value[i] == '-') + { + sign = -1; + i++; + } + + if (i == size) + return IJS_ESYNTAX; + + for (; i < size; i++) + { + char c = value[i]; + if (c < '0' || c > '9') + return IJS_ESYNTAX; + num = (num * 10) + (c - '0'); + } + *result = num; + return 0; +} + +static int +ijs_server_parse_float (const char *value, int size, double *result) +{ + char buf[256]; + char *tail; + + if (size + 1 > sizeof(buf)) + return IJS_EBUF; + memcpy (buf, value, size); + buf[size] = 0; + *result = strtod (buf, &tail); + if (tail == buf) + return IJS_ESYNTAX; + return 0; +} + +static int +ijs_server_set_param (IjsServerCtx *ctx, IjsJobId job_id, const char *key, + const char *value, int value_size) +{ + int code; + +#ifdef VERBOSE + fprintf (stderr, "set_param %s = ", key); + fwrite (value, 1, value_size, stderr); + fputs ("\n", stderr); +#endif + if (!strcmp (key, "NumChan")) + { + code = ijs_server_parse_int (value, value_size, &ctx->ph->n_chan); + if (code == 0) + ctx->fields_set |= IJS_N_CHAN_SET; + return code; + } + else if (!strcmp (key, "BitsPerSample")) + { + code = ijs_server_parse_int (value, value_size, &ctx->ph->bps); + if (code == 0) + ctx->fields_set |= IJS_BPS_SET; + return code; + } + else if (!strcmp (key, "ColorSpace")) + { + int size = value_size; + + if (size > (int)sizeof(ctx->ph->cs) - 1) + size = sizeof(ctx->ph->cs) - 1; + memcpy (ctx->ph->cs, value, size); + ctx->ph->cs[size] = 0; + ctx->fields_set |= IJS_CS_SET; + return 0; + } + else if (!strcmp (key, "Width")) + { + code = ijs_server_parse_int (value, value_size, &ctx->ph->width); + if (code == 0) + ctx->fields_set |= IJS_WIDTH_SET; + return code; + } + else if (!strcmp (key, "Height")) + { + code = ijs_server_parse_int (value, value_size, &ctx->ph->height); + if (code == 0) + ctx->fields_set |= IJS_HEIGHT_SET; + return code; + } + else if (!strcmp (key, "Dpi")) + { + int x_ix; + + for (x_ix = 0; x_ix < value_size; x_ix++) + if (value[x_ix] == 'x') + break; + if (x_ix == value_size) + return IJS_ESYNTAX; + code = ijs_server_parse_float (value, x_ix, &ctx->ph->xres); + if (code < 0) + return code; + code = ijs_server_parse_float (value + x_ix + 1, value_size - (x_ix + 1), + &ctx->ph->yres); + if (code < 0) + return code; + ctx->fields_set |= IJS_DPI_SET; + return 0; + } + else + { + return ctx->set_cb (ctx->set_cb_data, ctx, job_id, key, value, value_size); + } +} + +static int +ijs_server_proc_set_param (IjsServerCtx *ctx) +{ + const char *key, *value; + int key_size, value_size; + IjsJobId job_id; + int param_size; + int code; + + code = ijs_recv_int (&ctx->recv_chan, &job_id); + if (code < 0) + return code; + + if (!ctx->in_job || ctx->job_id != job_id) + return ijs_server_nak (ctx, IJS_EJOBID); + + code = ijs_recv_int (&ctx->recv_chan, ¶m_size); + if (code < 0) + return code; + if (param_size != ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx) + return IJS_EPROTO; + key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx; + key_size = ijs_strnlen (key, ctx->recv_chan.buf_size); + if (key_size == param_size) + return IJS_EPROTO; + value = key + key_size + 1; + value_size = param_size - (key_size + 1); + code = ijs_server_set_param (ctx, job_id, key, value, value_size); + if (code) + return ijs_server_nak (ctx, code); + else + return ijs_server_ack (ctx); +} + +static int +ijs_server_get_param (IjsServerCtx *ctx, IjsJobId job_id, const char *key, + char *value, int value_size) +{ +#ifdef VERBOSE + fprintf (stderr, "ijs_server_get_param %s\n", key); +#endif + return ctx->get_cb (ctx->get_cb_data, ctx, job_id, key, + value, value_size); +} + +static int +ijs_server_proc_get_param (IjsServerCtx *ctx) +{ + const char *key; + int key_size; + int code; + char buf[4096]; + IjsJobId job_id; + + code = ijs_recv_int (&ctx->recv_chan, &job_id); + if (code < 0) + return code; + + if (!ctx->in_job || ctx->job_id != job_id) + return ijs_server_nak (ctx, IJS_EJOBID); + + key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx; + key_size = ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx; + if (key_size == 0 || key[key_size - 1]) + return IJS_ESYNTAX; +#ifdef VERBOSE + fprintf (stderr, "ijs_server_proc_get_param, key_size = %d\n", key_size); +#endif + + code = ijs_server_get_param (ctx, job_id, key, buf, sizeof(buf)); + if (code < 0) + return ijs_server_nak (ctx, code); + else + { + int status; + + status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK); + if (status < 0) + return status; + status = ijs_send_block (&ctx->send_chan, buf, code); + if (status < 0) + return status; + return ijs_send_buf (&ctx->send_chan); + } +} + +static int +ijs_server_proc_begin_page (IjsServerCtx *ctx) +{ + IjsPageHeader *ph = ctx->ph; + int status = 0; + + if (ph == NULL) + status = IJS_EPROTO; + if ((ctx->fields_set & IJS_FIELDS_REQUIRED) != IJS_FIELDS_REQUIRED) + status = IJS_EPROTO; +#ifdef VERBOSE + fprintf (stderr, "begin page %d %d %d %d %d\n", + ph->n_chan, ph->bps, ph->cs, ph->width, ph->height); +#endif + if (!status) + { + ctx->in_page = TRUE; + return ijs_server_ack (ctx); + } + else + return ijs_server_nak (ctx, status); +} + +static int +ijs_server_read_data (IjsServerCtx *ctx, char *buf, int size) +{ + int status; + + status = ijs_recv_read (&ctx->recv_chan, buf, size); + return (status == size) ? 0 : IJS_EIO; +} + +static int +ijs_server_proc_send_data_block (IjsServerCtx *ctx) +{ + int size; + int status = 0; + IjsJobId job_id; + + status = ijs_recv_int (&ctx->recv_chan, &job_id); + if (status < 0) return status; + + if (!ctx->in_job || job_id != ctx->job_id) + status = IJS_EJOBID; + else if (ctx->buf == NULL) + status = IJS_EPROTO; + + if (!status) status = ijs_recv_int (&ctx->recv_chan, &size); + +#ifdef VERBOSE + fprintf (stderr, "status=%d, send data block id=%d, size=%d\n", + status, job_id, size); +#endif + if (status) + return ijs_server_nak (ctx, status); + + if (size <= ctx->buf_size - ctx->buf_ix) + { + status = ijs_server_read_data (ctx, ctx->buf + ctx->buf_ix, size); + ctx->buf_ix += size; + } + else + { + ctx->overflow_buf_size = size - (ctx->buf_size - ctx->buf_ix); + ctx->overflow_buf = (char *)malloc (ctx->overflow_buf_size); + ctx->overflow_buf_ix = 0; + status = ijs_server_read_data (ctx, ctx->buf + ctx->buf_ix, + ctx->buf_size - ctx->buf_ix); + ctx->buf_ix = ctx->buf_size; + if (!status) + { + status = ijs_server_read_data (ctx, ctx->overflow_buf, + ctx->overflow_buf_size); + } + } + return ijs_server_ack (ctx); +} + +static int +ijs_server_proc_end_page (IjsServerCtx *ctx) +{ +#ifdef VERBOSE + fprintf (stderr, "end page\n"); +#endif + return ijs_server_ack (ctx); +} + +static int +ijs_server_proc_exit (IjsServerCtx *ctx) +{ + return 1; +} + +ijs_server_proc ijs_server_procs[] = { + ijs_server_proc_ack, + ijs_server_proc_nak, + ijs_server_proc_ping, + ijs_server_proc_pong, + ijs_server_proc_open, + ijs_server_proc_close, + ijs_server_proc_begin_job, + ijs_server_proc_end_job, + ijs_server_proc_cancel_job, + ijs_server_proc_query_status, + ijs_server_proc_list_params, + ijs_server_proc_enum_param, + ijs_server_proc_set_param, + ijs_server_proc_get_param, + ijs_server_proc_begin_page, + ijs_server_proc_send_data_block, + ijs_server_proc_end_page, + ijs_server_proc_exit +}; + +int +ijs_server_iter (IjsServerCtx *ctx) +{ + int cmd_num; + int status; + + status = ijs_recv_buf (&ctx->recv_chan); + + if (status < 0) + return status; + + cmd_num = ijs_get_int (ctx->recv_chan.buf); +#ifdef VERBOSE + fprintf (stderr, "command %d, %d bytes\n", cmd_num, ctx->recv_chan.buf_size); +#endif + if (cmd_num < 0 || + cmd_num >= (int)sizeof(ijs_server_procs) / sizeof(ijs_server_procs[0])) + return -1; + return ijs_server_procs[cmd_num] (ctx); +} + +/** + * ijs_server_get_page_header: Get the page header. + * @ctx: The server context. + * @ph: Where to store the page header. + * + * Return value: 0 on success, 1 on normal exit, negative on error. + **/ +int +ijs_server_get_page_header (IjsServerCtx *ctx, IjsPageHeader *ph) +{ + int status; + + ctx->ph = ph; + ctx->in_page = FALSE; + + do + { + status = ijs_server_iter (ctx); + } + while (status == 0 && !ctx->in_page); + + ctx->ph = NULL; + return status; +} + +/** + * ijs_server_get_data: Get data from client. + * @ctx: The server context. + * @buf: Buffer for data being read. + * @size: Size of buf. + * + * Gets data from client. Data is stored in @buf or the + * overflow_buf. + * + * Return value: Number of bytes read, -1 on end of page, or < 0 on + * error. + **/ +int +ijs_server_get_data (IjsServerCtx *ctx, char *buf, int size) +{ + int buf_ix = 0; + int status = 0; + +#ifdef VERBOSE + fprintf (stderr, "ijs_server_get_data %d\n", size); +#endif + + if (ctx->overflow_buf != NULL) + { + int n_bytes = ctx->overflow_buf_size - ctx->overflow_buf_ix; + if (n_bytes > size) + n_bytes = size; + memcpy (buf, ctx->overflow_buf + ctx->overflow_buf_ix, n_bytes); + ctx->overflow_buf_ix += n_bytes; + buf_ix = n_bytes; + if (ctx->overflow_buf_ix == ctx->overflow_buf_size) + { + free (ctx->overflow_buf); + ctx->overflow_buf = NULL; + ctx->overflow_buf_size = 0; + ctx->overflow_buf_ix = 0; + } + } + ctx->buf = buf; + ctx->buf_size = size; + ctx->buf_ix = buf_ix; + while (!status && ctx->buf_ix < size) + { + status = ijs_server_iter (ctx); + } + ctx->buf = NULL; + return status; +} |