/*=========================================================================*\ * Buffered input/output routines * Lua methods: * send: unbuffered send using C base_send * receive: buffered read using C base_receive \*=========================================================================*/ #include #include #include "lsbuf.h" /*=========================================================================*\ * Internal function prototypes. \*=========================================================================*/ static int sendraw(lua_State *L, p_buf buf, cchar *data, size_t len, size_t *done); static int recvraw(lua_State *L, p_buf buf, size_t wanted); static int recvdosline(lua_State *L, p_buf buf); static int recvunixline(lua_State *L, p_buf buf); static int recvall(lua_State *L, p_buf buf); static int buf_contents(lua_State *L, p_buf buf, cchar **data, size_t *len); static void buf_skip(lua_State *L, p_buf buf, size_t len); /*=========================================================================*\ * Exported functions \*=========================================================================*/ /*-------------------------------------------------------------------------*\ * Initializes module \*-------------------------------------------------------------------------*/ void buf_open(lua_State *L) { (void) L; } /*-------------------------------------------------------------------------*\ * Initializes C structure * Input * buf: buffer structure to initialize * base: socket object to associate with buffer structure \*-------------------------------------------------------------------------*/ void buf_init(lua_State *L, p_buf buf, p_base base) { (void) L; buf->buf_first = buf->buf_last = 0; buf->buf_base = base; } /*-------------------------------------------------------------------------*\ * Send data through buffered object * Input * buf: buffer structure to be used * Lua Input: self, a_1 [, a_2, a_3 ... a_n] * self: socket object * a_i: strings to be sent. * Lua Returns * On success: nil, followed by the total number of bytes sent * On error: error message \*-------------------------------------------------------------------------*/ int buf_send(lua_State *L, p_buf buf) { int top = lua_gettop(L); size_t total = 0; int err = PRIV_DONE; int arg; p_base base = buf->buf_base; tm_markstart(&base->base_tm); for (arg = 2; arg <= top; arg++) { /* first arg is socket object */ size_t done, len; cchar *data = luaL_opt_lstr(L, arg, NULL, &len); if (!data || err != PRIV_DONE) break; err = sendraw(L, buf, data, len, &done); total += done; } priv_pusherror(L, err); lua_pushnumber(L, total); #ifdef _DEBUG /* push time elapsed during operation as the last return value */ lua_pushnumber(L, tm_getelapsed(&base->base_tm)/1000.0); #endif return lua_gettop(L) - top; } /*-------------------------------------------------------------------------*\ * Receive data from a buffered object * Input * buf: buffer structure to be used * Lua Input: self [pat_1, pat_2 ... pat_n] * self: socket object * pat_i: may be one of the following * "*l": reads a text line, defined as a string of caracters terminates * by a LF character, preceded or not by a CR character. This is * the default pattern * "*lu": reads a text line, terminanted by a CR character only. (Unix mode) * "*a": reads until connection closed * number: reads 'number' characters from the socket object * Lua Returns * On success: one string for each pattern * On error: all strings for which there was no error, followed by one * nil value for the remaining strings, followed by an error code \*-------------------------------------------------------------------------*/ int buf_receive(lua_State *L, p_buf buf) { int top = lua_gettop(L); int arg, err = PRIV_DONE; p_base base = buf->buf_base; tm_markstart(&base->base_tm); /* push default pattern if need be */ if (top < 2) { lua_pushstring(L, "*l"); top++; } /* make sure we have enough stack space */ luaL_check_stack(L, top+LUA_MINSTACK, "too many arguments"); /* receive all patterns */ for (arg = 2; arg <= top && err == PRIV_DONE; arg++) { if (!lua_isnumber(L, arg)) { static cchar *patternnames[] = {"*l", "*lu", "*a", "*w", NULL}; cchar *pattern = luaL_opt_string(L, arg, NULL); /* get next pattern */ switch (luaL_findstring(pattern, patternnames)) { case 0: /* DOS line pattern */ err = recvdosline(L, buf); break; case 1: /* Unix line pattern */ err = recvunixline(L, buf); break; case 2: /* Until closed pattern */ err = recvall(L, buf); break; case 3: /* Word pattern */ luaL_arg_check(L, 0, arg, "word patterns are deprecated"); break; default: /* else it is an error */ luaL_arg_check(L, 0, arg, "invalid receive pattern"); break; } /* raw pattern */ } else err = recvraw(L, buf, (size_t) lua_tonumber(L, arg)); } /* push nil for each pattern after an error */ for ( ; arg <= top; arg++) lua_pushnil(L); /* last return is an error code */ priv_pusherror(L, err); #ifdef _DEBUG /* push time elapsed during operation as the last return value */ lua_pushnumber(L, tm_getelapsed(&base->base_tm)/1000.0); #endif return lua_gettop(L) - top; } /*-------------------------------------------------------------------------*\ * Determines if there is any data in the read buffer * Input * buf: buffer structure to be used * Returns * 1 if empty, 0 if there is data \*-------------------------------------------------------------------------*/ int buf_isempty(lua_State *L, p_buf buf) { (void) L; return buf->buf_first >= buf->buf_last; } /*=========================================================================*\ * Internal functions \*=========================================================================*/ /*-------------------------------------------------------------------------*\ * Sends a raw block of data through a buffered object. * Input * buf: buffer structure to be used * data: data to be sent * len: number of bytes to send * Output * sent: number of bytes sent * Returns * operation error code. \*-------------------------------------------------------------------------*/ static int sendraw(lua_State *L, p_buf buf, cchar *data, size_t len, size_t *sent) { p_base base = buf->buf_base; size_t total = 0; int err = PRIV_DONE; while (total < len && err == PRIV_DONE) { size_t done; err = base->base_send(L, base, data + total, len - total, &done); total += done; } *sent = total; return err; } /*-------------------------------------------------------------------------*\ * Reads a raw block of data from a buffered object. * Input * buf: buffer structure * wanted: number of bytes to be read * Returns * operation error code. \*-------------------------------------------------------------------------*/ static int recvraw(lua_State *L, p_buf buf, size_t wanted) { int err = PRIV_DONE; size_t total = 0; luaL_Buffer b; luaL_buffinit(L, &b); while (total < wanted && err == PRIV_DONE) { size_t len; cchar *data; err = buf_contents(L, buf, &data, &len); len = MIN(len, wanted - total); luaL_addlstring(&b, data, len); buf_skip(L, buf, len); total += len; } luaL_pushresult(&b); return err; } /*-------------------------------------------------------------------------*\ * Reads everything until the connection is closed * Input * buf: buffer structure * Result * operation error code. \*-------------------------------------------------------------------------*/ static int recvall(lua_State *L, p_buf buf) { int err = PRIV_DONE; luaL_Buffer b; luaL_buffinit(L, &b); while (err == PRIV_DONE) { cchar *data; size_t len; err = buf_contents(L, buf, &data, &len); luaL_addlstring(&b, data, len); buf_skip(L, buf, len); } luaL_pushresult(&b); return err; } /*-------------------------------------------------------------------------*\ * Reads a line terminated by a CR LF pair or just by a LF. The CR and LF * are not returned by the function and are discarded from the buffer. * Input * buf: buffer structure * Result * operation error code. PRIV_DONE, PRIV_TIMEOUT or PRIV_CLOSED \*-------------------------------------------------------------------------*/ static int recvdosline(lua_State *L, p_buf buf) { int err = 0; luaL_Buffer b; luaL_buffinit(L, &b); while (err == PRIV_DONE) { size_t len, pos; cchar *data; err = buf_contents(L, buf, &data, &len); pos = 0; while (pos < len && data[pos] != '\n') { /* we ignore all \r's */ if (data[pos] != '\r') luaL_putchar(&b, data[pos]); pos++; } if (pos < len) { /* found '\n' */ buf_skip(L, buf, pos+1); /* skip '\n' too */ break; /* we are done */ } else /* reached the end of the buffer */ buf_skip(L, buf, pos); } luaL_pushresult(&b); return err; } /*-------------------------------------------------------------------------*\ * Reads a line terminated by a LF character, which is not returned by * the function, and is skipped in the buffer. * Input * buf: buffer structure * Returns * operation error code. PRIV_DONE, PRIV_TIMEOUT or PRIV_CLOSED \*-------------------------------------------------------------------------*/ static int recvunixline(lua_State *L, p_buf buf) { int err = PRIV_DONE; luaL_Buffer b; luaL_buffinit(L, &b); while (err == 0) { size_t pos, len; cchar *data; err = buf_contents(L, buf, &data, &len); pos = 0; while (pos < len && data[pos] != '\n') { luaL_putchar(&b, data[pos]); pos++; } if (pos < len) { /* found '\n' */ buf_skip(L, buf, pos+1); /* skip '\n' too */ break; /* we are done */ } else /* reached the end of the buffer */ buf_skip(L, buf, pos); } luaL_pushresult(&b); return err; } /*-------------------------------------------------------------------------*\ * Skips a given number of bytes in read buffer * Input * buf: buffer structure * len: number of bytes to skip \*-------------------------------------------------------------------------*/ static void buf_skip(lua_State *L, p_buf buf, size_t len) { buf->buf_first += len; if (buf_isempty(L, buf)) buf->buf_first = buf->buf_last = 0; } /*-------------------------------------------------------------------------*\ * Return any data available in buffer, or get more data from transport layer * if buffer is empty. * Input * buf: buffer structure * Output * data: pointer to buffer start * len: buffer buffer length * Returns * PRIV_DONE, PRIV_CLOSED, PRIV_TIMEOUT ... \*-------------------------------------------------------------------------*/ static int buf_contents(lua_State *L, p_buf buf, cchar **data, size_t *len) { int err = PRIV_DONE; p_base base = buf->buf_base; if (buf_isempty(L, buf)) { size_t done; err = base->base_receive(L, base, buf->buf_data, BUF_SIZE, &done); buf->buf_first = 0; buf->buf_last = done; } *len = buf->buf_last - buf->buf_first; *data = buf->buf_data + buf->buf_first; return err; }