revising httpd

This commit is contained in:
2013-02-18 13:45:50 +00:00
parent bbdf168ee8
commit 5bc774db3a
56 changed files with 1774 additions and 953 deletions

View File

@ -1,2 +1,2 @@
SUBDIRS = cmn awk sed net
SUBDIRS = cmn awk sed xli http
DIST_SUBDIRS = $(SUBDIRS)

View File

@ -270,7 +270,7 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
SUBDIRS = cmn awk sed net
SUBDIRS = cmn awk sed xli http
DIST_SUBDIRS = $(SUBDIRS)
all: all-recursive

View File

@ -103,7 +103,7 @@ const qse_char_t* qse_awk_dflerrstr (const qse_awk_t* awk, qse_awk_errnum_t errn
QSE_T("illegal operand for increment/decrement operator"),
QSE_T("'@include' not followed by a string"),
QSE_T("include level too deep"),
QSE_T("@word '${0}' not recognized"),
QSE_T("'${0}' not recognized"),
QSE_T("@ not followed by a valid word"),
QSE_T("divide by zero"),

View File

@ -5512,7 +5512,7 @@ static int get_string (
if (c == QSE_CHAR_EOF)
{
SETERR_TOK (awk, QSE_AWK_ESTRNC);
SETERR_LOC (awk, QSE_AWK_ESTRNC, &awk->tok.loc);
return -1;
}
@ -6072,7 +6072,7 @@ retry:
if (c == QSE_CHAR_EOF)
{
SETERR_TOK (awk, QSE_AWK_ESTRNC);
SETERR_LOC (awk, QSE_AWK_ESTRNC, &awk->tok.loc);
return -1;
}

View File

@ -20,6 +20,7 @@
#include <qse/cmn/main.h>
#include <qse/cmn/mbwc.h>
#include "mem.h"
int qse_runmain (

View File

@ -21,10 +21,10 @@
#include <qse/cmn/uri.h>
#include "mem.h"
int qse_mbstouri (const qse_mchar_t* str, qse_uri_t* uri, int flags)
int qse_mbstouri (const qse_mchar_t* str, qse_muri_t* uri, int flags)
{
const qse_mchar_t* ptr, * colon;
qse_uri_t xuri;
qse_muri_t xuri;
QSE_MEMSET (&xuri, 0, QSE_SIZEOF(xuri));
@ -145,10 +145,10 @@ int qse_mbstouri (const qse_mchar_t* str, qse_uri_t* uri, int flags)
/* -------------------------------------------------------- */
int qse_wcstouri (const qse_wchar_t* str, qse_uri_t* uri, int flags)
int qse_wcstouri (const qse_wchar_t* str, qse_wuri_t* uri, int flags)
{
const qse_wchar_t* ptr, * colon;
qse_uri_t xuri;
qse_wuri_t xuri;
QSE_MEMSET (&xuri, 0, QSE_SIZEOF(xuri));

View File

@ -5,8 +5,8 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/include \
-I$(includedir)
lib_LTLIBRARIES = libqsenet.la
libqsenet_la_SOURCES = \
lib_LTLIBRARIES = libqsehttp.la
libqsehttp_la_SOURCES = \
httpd.h \
upxd.h \
http.c \
@ -23,6 +23,6 @@ libqsenet_la_SOURCES = \
httpd-text.c \
upxd.c
libqsenet_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir)
libqsenet_la_LIBADD = -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS)
libqsehttp_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir)
libqsehttp_la_LIBADD = -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS)

View File

@ -34,7 +34,7 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
subdir = lib/net
subdir = lib/http
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/argz.m4 \
@ -79,19 +79,19 @@ am__uninstall_files_from_dir = { \
am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__DEPENDENCIES_1 =
libqsenet_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
libqsehttp_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
am_libqsenet_la_OBJECTS = http.lo htre.lo htrd.lo httpd.lo \
am_libqsehttp_la_OBJECTS = http.lo htre.lo htrd.lo httpd.lo \
httpd-cgi.lo httpd-dir.lo httpd-file.lo httpd-proxy.lo \
httpd-resol.lo httpd-std.lo httpd-task.lo httpd-text.lo \
upxd.lo
libqsenet_la_OBJECTS = $(am_libqsenet_la_OBJECTS)
libqsehttp_la_OBJECTS = $(am_libqsehttp_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
libqsenet_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
libqsehttp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libqsenet_la_LDFLAGS) $(LDFLAGS) -o $@
$(libqsehttp_la_LDFLAGS) $(LDFLAGS) -o $@
DEFAULT_INCLUDES =
depcomp = $(SHELL) $(top_srcdir)/ac/depcomp
am__depfiles_maybe = depfiles
@ -118,8 +118,8 @@ am__v_CCLD_0 = @echo " CCLD " $@;
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
SOURCES = $(libqsenet_la_SOURCES)
DIST_SOURCES = $(libqsenet_la_SOURCES)
SOURCES = $(libqsehttp_la_SOURCES)
DIST_SOURCES = $(libqsehttp_la_SOURCES)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@ -303,8 +303,8 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/include \
-I$(includedir)
lib_LTLIBRARIES = libqsenet.la
libqsenet_la_SOURCES = \
lib_LTLIBRARIES = libqsehttp.la
libqsehttp_la_SOURCES = \
httpd.h \
upxd.h \
http.c \
@ -321,8 +321,8 @@ libqsenet_la_SOURCES = \
httpd-text.c \
upxd.c
libqsenet_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir)
libqsenet_la_LIBADD = -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS)
libqsehttp_la_LDFLAGS = -version-info 1:0:0 -no-undefined -L../cmn -L$(libdir)
libqsehttp_la_LIBADD = -lqsecmn $(SOCKET_LIBS) $(SENDFILE_LIBS) $(SSL_LIBS)
all: all-am
.SUFFIXES:
@ -336,9 +336,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/net/Makefile'; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/http/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/net/Makefile
$(AUTOMAKE) --foreign lib/http/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
@ -388,8 +388,8 @@ clean-libLTLIBRARIES:
echo "rm -f \"$${dir}/so_locations\""; \
rm -f "$${dir}/so_locations"; \
done
libqsenet.la: $(libqsenet_la_OBJECTS) $(libqsenet_la_DEPENDENCIES) $(EXTRA_libqsenet_la_DEPENDENCIES)
$(AM_V_CCLD)$(libqsenet_la_LINK) -rpath $(libdir) $(libqsenet_la_OBJECTS) $(libqsenet_la_LIBADD) $(LIBS)
libqsehttp.la: $(libqsehttp_la_OBJECTS) $(libqsehttp_la_DEPENDENCIES) $(EXTRA_libqsehttp_la_DEPENDENCIES)
$(AM_V_CCLD)$(libqsehttp_la_LINK) -rpath $(libdir) $(libqsehttp_la_OBJECTS) $(libqsehttp_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)

View File

@ -18,7 +18,7 @@
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#include <qse/net/htrd.h>
#include <qse/http/htrd.h>
#include <qse/cmn/chr.h>
#include <qse/cmn/path.h>
#include "../cmn/mem.h"

View File

@ -18,7 +18,7 @@
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#include <qse/net/htre.h>
#include <qse/http/htre.h>
#include "../cmn/mem.h"
static void free_hdrval (qse_htb_t* htb, void* vptr, qse_size_t vlen)

View File

@ -18,7 +18,7 @@
License along with QSE. If not, see <http://www.gnu.org/licenses/>.
*/
#include <qse/net/http.h>
#include <qse/http/http.h>
#include <qse/cmn/str.h>
#include <qse/cmn/chr.h>
#include <qse/cmn/htb.h>

View File

@ -446,13 +446,20 @@ static int cgi_add_env (
if (suffix && suffix[0] != QSE_MT('\0'))
{
const qse_mchar_t* tmp[3];
qse_mchar_t* tr;
tmp[0] = docroot;
tmp[1] = suffix;
tmp[2] = QSE_NULL;
tr = qse_mbsadup (tmp, QSE_NULL, httpd->mmgr);
if (tr)
{
qse_canonmbspath (tr, tr, 0);
qse_env_insertmbs (env, QSE_MT("PATH_TRANSLATED"), tr);
QSE_MMGR_FREE (httpd->mmgr, tr);
}
qse_env_insertmbs (env, QSE_MT("PATH_INFO"), suffix);
qse_env_insertmbsa (env, QSE_MT("PATH_TRANSLATED"), tmp);
}
qse_env_insertmbs (env, QSE_MT("REQUEST_METHOD"), qse_htre_getqmethodname(req));

View File

@ -18,6 +18,7 @@
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#include <qse/http/std.h>
#include "httpd.h"
#include "../cmn/mem.h"
#include <qse/cmn/hton.h>
@ -90,35 +91,18 @@
#define DEFAULT_PORT 80
#define DEFAULT_SECURE_PORT 443
enum server_xtn_cfg_id_t
{
SERVER_XTN_CFG_DOCROOT = 0,
SERVER_XTN_CFG_REALM,
SERVER_XTN_CFG_AUTH, /* basic auth */
SERVER_XTN_CFG_DIRCSS, /* can't be too long due to internal buffer size */
SERVER_XTN_CFG_ERRCSS,
SERVER_XTN_CFG_MAX
};
typedef struct server_xtn_t server_xtn_t;
struct server_xtn_t
{
qse_mchar_t* cfg[SERVER_XTN_CFG_MAX];
union
{
void* a[4];
struct
{
qse_httpd_server_cbstd_t* cbstd;
qse_httpd_server_cgistd_t* cgistd;
qse_httpd_server_mimestd_t* mimestd;
qse_httpd_server_idxstd_t* idxstd;
} s;
} cfg2;
/* private */
qse_httpd_server_predetach_t predetach;
qse_httpd_server_reconfig_t reconfig;
qse_httpd_serverstd_query_t query;
qse_httpd_serverstd_makersrc_t makersrc;
qse_httpd_serverstd_freersrc_t freersrc;
/* temporary buffer to handle authorization */
qse_mxstr_t auth;
};
/* ------------------------------------------------------------------- */
@ -640,7 +624,7 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server)
qse_skad_t addr;
int addrsize;
addrsize = qse_nwadtoskad (&server->nwad, &addr);
addrsize = qse_nwadtoskad (&server->dope.nwad, &addr);
if (addrsize <= -1)
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL);
@ -694,13 +678,13 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server)
setsockopt (fd, SOL_IP, IP_TRANSPARENT, &flag, QSE_SIZEOF(flag));
#endif
if (server->flags & QSE_HTTPD_SERVER_BINDTONWIF)
if (server->dope.flags & QSE_HTTPD_SERVER_BINDTONWIF)
{
#if defined(SO_BINDTODEVICE)
qse_mchar_t tmp[64];
qse_size_t len;
len = qse_nwifindextombs (server->nwif, tmp, QSE_COUNTOF(tmp));
len = qse_nwifindextombs (server->dope.nwif, tmp, QSE_COUNTOF(tmp));
if (len <= 0 || setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE, tmp, len) <= -1)
{
@ -815,7 +799,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n"));
if (qse_skadtonwad (&addr, &client->remote_addr) <= -1)
{
/* TODO: logging */
client->remote_addr = server->nwad;
client->remote_addr = server->dope.nwad;
}
addrlen = QSE_SIZEOF(addr);
@ -823,7 +807,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n"));
qse_skadtonwad (&addr, &client->local_addr) <= -1)
{
/* TODO: logging */
client->local_addr = server->nwad;
client->local_addr = server->dope.nwad;
}
#if defined(SO_ORIGINAL_DST)
@ -1870,7 +1854,7 @@ if (qse_htre_getcontentlen(req) > 0)
task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
}
}
else if (server_xtn->cfg2.s.cbstd->makersrc (httpd, client, req, &rsrc) <= -1)
else if (server_xtn->makersrc (httpd, client, req, &rsrc) <= -1)
{
qse_httpd_discardcontent (httpd, req);
task = qse_httpd_entaskerr (httpd, client, QSE_NULL, 500, req);
@ -1878,8 +1862,8 @@ if (qse_htre_getcontentlen(req) > 0)
else
{
task = qse_httpd_entaskrsrc (httpd, client, QSE_NULL, &rsrc, req);
if (server_xtn->cfg2.s.cbstd->freersrc)
server_xtn->cfg2.s.cbstd->freersrc (httpd, client, req, &rsrc);
if (server_xtn->freersrc)
server_xtn->freersrc (httpd, client, req, &rsrc);
}
if (task == QSE_NULL) goto oops;
}
@ -1934,8 +1918,8 @@ static int format_err (
server_xtn = qse_httpd_getserverxtn (httpd, client->server);
css = server_xtn->cfg[SERVER_XTN_CFG_ERRCSS];
if (!css) css = QSE_MT("");
if (server_xtn->query (httpd, client->server, QSE_NULL, QSE_NULL, QSE_HTTPD_SERVERSTD_ERRCSS, &css) <= -1) css = QSE_NULL;
if (css == QSE_NULL) css = QSE_MT("");
msg = qse_httpstatustombs(code);
@ -1972,8 +1956,8 @@ static int format_dir (
const qse_mchar_t* css;
int is_root = (qse_mbscmp (qpath, QSE_MT("/")) == 0);
css = server_xtn->cfg[SERVER_XTN_CFG_DIRCSS];
if (!css) css = QSE_MT("");
if (server_xtn->query (httpd, client->server, QSE_NULL, QSE_NULL, QSE_HTTPD_SERVERSTD_DIRCSS, &css) <= -1) css = QSE_NULL;
if (css == QSE_NULL) css = QSE_MT("");
/* TODO: html escaping of qpath */
n = snprintf (buf, bufsz,
@ -2119,6 +2103,8 @@ static void free_resource (
QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.script);
if (target->u.cgi.path != qpath)
QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.path);
if (target->u.cgi.shebang)
QSE_MMGR_FREE (httpd->mmgr, (qse_mchar_t*)target->u.cgi.shebang);
break;
@ -2160,85 +2146,141 @@ static qse_mchar_t* merge_paths (
return xpath;
}
static int attempt_cgi (
qse_httpd_t* httpd, const qse_mchar_t* docroot,
qse_mchar_t* xpath, const qse_mchar_t* qpath, const qse_mchar_t* idxfile,
qse_httpd_server_cgistd_t cgistd[], qse_httpd_rsrc_t* target)
static void merge_paths_to_buf (
qse_httpd_t* httpd, const qse_mchar_t* base,
const qse_mchar_t* path, qse_size_t plen, qse_mchar_t* xpath)
{
qse_mchar_t* ext;
qse_mchar_t* script, * suffix;
qse_size_t i;
/* this function merges two path names into a buffer large enough
* to hold the result. it doesn't duplicate the result */
qse_size_t len = 0;
len += qse_mbscpy (&xpath[len], base);
len += qse_mbscpy (&xpath[len], QSE_MT("/"));
len += qse_mbsncpy (&xpath[len], path, plen);
qse_canonmbspath (xpath, xpath, 0);
}
if (idxfile)
struct rsrc_tmp_t
{
const qse_mchar_t* qpath;
const qse_mchar_t* idxfile;
qse_mchar_t* xpath;
const qse_mchar_t* docroot;
const qse_mchar_t* realm;
const qse_mchar_t* auth;
qse_httpd_serverstd_index_t index;
int final_match;
};
static int attempt_cgi (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req,
struct rsrc_tmp_t* tmp, qse_httpd_rsrc_t* target)
{
server_xtn_t* server_xtn;
qse_mchar_t* shebang = QSE_NULL;
qse_mchar_t* suffix = QSE_NULL;
qse_mchar_t* script = QSE_NULL;
qse_httpd_serverstd_cgi_t cgi;
server_xtn = qse_httpd_getserverxtn (httpd, client->server);
if (tmp->final_match)
{
for (i = 0; cgistd[i].ext; i++)
/* it is a final match. tmp->xpath is tmp->docroot + tmp->qpath */
if (server_xtn->query (httpd, client->server, req, tmp->xpath, QSE_HTTPD_SERVERSTD_CGI, &cgi) >= 0 && cgi.cgi)
{
if (qse_mbsend (idxfile, cgistd[i].ext))
if (tmp->idxfile)
{
script = merge_paths (httpd, qpath, idxfile);
if (script == QSE_NULL) return -1;
target->type = QSE_HTTPD_RSRC_CGI;
target->u.cgi.nph = cgistd[i].nph;
target->u.cgi.path = xpath;
target->u.cgi.script = script;
target->u.cgi.suffix = QSE_NULL;
target->u.cgi.docroot = docroot;
target->u.cgi.shebang = cgistd[i].shebang;
return 1;
script = merge_paths (httpd, tmp->qpath, tmp->idxfile);
if (script == QSE_NULL) goto oops;
}
else script = (qse_mchar_t*)tmp->qpath;
if (cgi.shebang)
{
shebang = qse_mbsdup (cgi.shebang, httpd->mmgr);
if (shebang == QSE_NULL) goto oops;
}
goto bingo;
}
}
else
{
for (i = 0; cgistd[i].ext; i++)
/* inspect each segment from the head. */
const qse_mchar_t* ptr;
const qse_mchar_t* slash;
QSE_ASSERT (tmp->qpath[0] == QSE_T('/'));
ptr = tmp->qpath + 1;
while (*ptr != QSE_MT('\0'))
{
/* TODO: attempt other segments if qpath is like
* /abc/x.cgi/y.cgi/ttt. currently, it tries x.cgi only.
* x.cgi could be a directory name .
*/
ext = qse_mbsstr (qpath, cgistd[i].ext);
if (ext && (ext[cgistd[i].len] == QSE_MT('/') ||
ext[cgistd[i].len] == QSE_MT('\0')))
slash = qse_mbschr (ptr, QSE_MT('/'));
if (slash)
{
if (ext[cgistd[i].len] == QSE_MT('/'))
if (slash > ptr)
{
/* it has a path suffix */
script = qse_mbsxdup (qpath, ext - qpath + cgistd[i].len, httpd->mmgr);
suffix = qse_mbsdup (&ext[cgistd[i].len], httpd->mmgr);
if (script == QSE_NULL || suffix == QSE_NULL)
qse_httpd_stat_t st;
int stx;
/* a slash is found and the segment is not empty.
*
* tmp->xpath should be large enough to hold the merge path made of
* the subsegments of the original query path and docroot. */
merge_paths_to_buf (httpd, tmp->docroot, tmp->qpath, slash - tmp->qpath, tmp->xpath);
/* attempt this */
stx = stat_file (httpd, tmp->xpath, &st, 0);
if (stx >= 0 && !st.isdir)
{
if (suffix) QSE_MMGR_FREE (httpd->mmgr, suffix);
if (script) QSE_MMGR_FREE (httpd->mmgr, script);
httpd->errnum = QSE_HTTPD_ENOMEM;
return -1;
if (server_xtn->query (httpd, client->server, req, tmp->xpath, QSE_HTTPD_SERVERSTD_CGI, &cgi) >= 0 && cgi.cgi)
{
script = qse_mbsxdup (tmp->qpath, slash - tmp->qpath , httpd->mmgr);
suffix = qse_mbsdup (slash, httpd->mmgr);
if (!script || !suffix) goto oops;
if (cgi.shebang)
{
shebang = qse_mbsdup (cgi.shebang, httpd->mmgr);
if (shebang == QSE_NULL) goto oops;
}
goto bingo;
}
}
/* drop the suffix part */
xpath[qse_mbslen(xpath) - qse_mbslen(suffix)] = QSE_MT('\0');
}
else
{
/* it has no path suffix */
script = qpath;
suffix = QSE_NULL;
}
target->type = QSE_HTTPD_RSRC_CGI;
target->u.cgi.nph = cgistd[i].nph;
target->u.cgi.path = xpath;
target->u.cgi.script = script;
target->u.cgi.suffix = suffix;
target->u.cgi.docroot = docroot;
target->u.cgi.shebang = cgistd[i].shebang;
return 1;
ptr = slash + 1;
}
else
{
/* no more slash is found. the last segement doesn't have to be checked
* here since it's attempted by the caller. */
break;
}
}
}
return 0;
return 0; /* not a cgi */
bingo:
target->type = QSE_HTTPD_RSRC_CGI;
target->u.cgi.nph = cgi.nph;
target->u.cgi.path = tmp->xpath;
target->u.cgi.script = script;
target->u.cgi.suffix = suffix;
target->u.cgi.docroot = tmp->docroot;
target->u.cgi.shebang = shebang;
return 1;
oops:
httpd->errnum = QSE_HTTPD_ENOMEM;
if (shebang) QSE_MMGR_FREE (httpd->mmgr, shebang);
if (suffix) QSE_MMGR_FREE (httpd->mmgr, suffix);
if (script && script != tmp->qpath) QSE_MMGR_FREE (httpd->mmgr, script);
return -1;
}
static int make_resource (
@ -2246,214 +2288,308 @@ static int make_resource (
qse_htre_t* req, qse_httpd_rsrc_t* target)
{
server_xtn_t* server_xtn;
const qse_mchar_t* qpath;
const qse_mchar_t* idxfile;
qse_mchar_t* xpath;
struct rsrc_tmp_t tmp;
qse_httpd_stat_t st;
qse_size_t i;
int n, stx;
qpath = qse_htre_getqpath(req);
QSE_MEMSET (&tmp, 0, QSE_SIZEOF(tmp));
tmp.qpath = qse_htre_getqpath(req);
QSE_MEMSET (target, 0, QSE_SIZEOF(*target));
qse_printf (QSE_T(">>> MAKING RESOURCE [%hs]\n"), qpath);
qse_printf (QSE_T(">>> MAKING RESOURCE [%hs]\n"), tmp.qpath);
server_xtn = qse_httpd_getserverxtn (httpd, client->server);
if (server_xtn->cfg[SERVER_XTN_CFG_REALM] &&
server_xtn->cfg[SERVER_XTN_CFG_AUTH])
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_DOCROOT, &tmp.docroot) <= -1 ||
server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_REALM, &tmp.realm) <= -1 ||
server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_AUTH, &tmp.auth) <= -1 ||
server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_INDEX, &tmp.index) <= -1)
{
const qse_htre_hdrval_t* auth;
return -1;
}
auth = qse_htre_getheaderval (req, QSE_MT("Authorization"));
if (auth)
/* default to the root directory. */
if (!tmp.docroot) tmp.docroot = QSE_MT("/");
if (tmp.realm && tmp.auth)
{
const qse_htre_hdrval_t* authv;
authv = qse_htre_getheaderval (req, QSE_MT("Authorization"));
if (authv)
{
while (auth->next) auth = auth->next;
while (authv->next) authv = authv->next;
if (qse_mbszcasecmp(auth->ptr, QSE_MT("Basic "), 6) == 0)
if (qse_mbszcasecmp(authv->ptr, QSE_MT("Basic "), 6) == 0)
{
if (qse_mbscmp (&auth->ptr[6], server_xtn->cfg[SERVER_XTN_CFG_AUTH]) == 0) goto auth_ok;
qse_size_t authl, authl2;
/* basic authorization is a base64-encoded string of username:password. */
authl = qse_mbslen(&authv->ptr[6]);
if (authl > server_xtn->auth.len)
{
qse_mchar_t* tmp;
tmp = qse_httpd_reallocmem (httpd, server_xtn->auth.ptr, authl * QSE_SIZEOF(qse_mchar_t));
if (!tmp) return -1;
server_xtn->auth.ptr = tmp;
/* the maximum capacity that can hold the largest authorization value */
server_xtn->auth.len = authl;
}
/* decoding a base64-encoded string result in a shorter value than the input.
* so passing the length of the input(authl) as the output buffer size is ok */
authl2 = qse_debase64 (&authv->ptr[6], authl, server_xtn->auth.ptr, authl, QSE_NULL);
if (qse_mbsxcmp (server_xtn->auth.ptr, authl2, tmp.auth) == 0) goto auth_ok;
}
}
target->type = QSE_HTTPD_RSRC_AUTH;
target->u.auth.realm = server_xtn->cfg[SERVER_XTN_CFG_REALM];
target->u.auth.realm = tmp.realm;
return 0;
}
auth_ok:
idxfile = QSE_NULL;
xpath = merge_paths (httpd, server_xtn->cfg[SERVER_XTN_CFG_DOCROOT], qpath);
if (xpath == QSE_NULL) return -1;
tmp.xpath = merge_paths (httpd, tmp.docroot, tmp.qpath);
if (tmp.xpath == QSE_NULL) return -1;
stx = stat_file (httpd, xpath, &st, 0);
stx = stat_file (httpd, tmp.xpath, &st, 0);
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
if (stx <= -1)
{
/* this OS may fail in stat_file() if the path contains the trailing
* separator. it's beause of the way FindFirstFile() or DosQueryPathInfo()
* is ussed in stat_file(). let me work around it here. */
qse_size_t pl = qse_mbslen(xpath);
if (pl > 1 && xpath[pl - 1] == QSE_MT('/'))
* is used in stat_file(). let me work around it here. */
qse_size_t pl = qse_mbslen(tmp.xpath);
if (pl > 1 && tmp.xpath[pl - 1] == QSE_MT('/'))
{
xpath[pl-1] = QSE_MT('\0');
stx = stat_file (httpd, xpath, &st, 0);
xpath[pl-1] = QSE_MT('/');
tmp.xpath[pl-1] = QSE_MT('\0');
stx = stat_file (httpd, tmp.xpath, &st, 0);
tmp.xpath[pl-1] = QSE_MT('/');
}
}
#endif
if (stx >= 0 && st.isdir)
if (stx >= 0)
{
/* it is a directory */
if (server_xtn->cfg2.s.idxstd)
{
/* try to locate an index file */
for (i = 0; server_xtn->cfg2.s.idxstd[i].name; i++)
/* xpath/qpath is a final match.
* mark that the segments in the query path don't need inspection. */
tmp.final_match = 1;
if (st.isdir)
{
/* it is a directory */
if (tmp.index.count > 0)
{
qse_mchar_t* tpath;
/* try to locate an index file */
qse_size_t i;
const qse_mchar_t* ptr;
tpath = merge_paths (httpd, xpath, server_xtn->cfg2.s.idxstd[i].name);
if (tpath == QSE_NULL)
ptr = tmp.index.files;
for (i = 0; i < tmp.index.count; i++, ptr += qse_mbslen(ptr) + 1)
{
QSE_MMGR_FREE (httpd->mmgr, xpath);
return -1;
qse_mchar_t* tpath;
tpath = merge_paths (httpd, tmp.xpath, ptr);
if (tpath == QSE_NULL)
{
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
return -1;
}
if (httpd->scb->file.stat (httpd, tpath, &st) >= 0 && !st.isdir)
{
/* the index file is found */
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
tmp.xpath = tpath;
tmp.idxfile = ptr;
goto attempt_file;
}
QSE_MMGR_FREE (httpd->mmgr, tpath);
}
if (httpd->scb->file.stat (httpd, tpath, &st) >= 0 && !st.isdir)
{
/* the index file is found */
QSE_MMGR_FREE (httpd->mmgr, xpath);
xpath = tpath;
idxfile = server_xtn->cfg2.s.idxstd[i].name;
goto attempt_file;
}
QSE_MMGR_FREE (httpd->mmgr, tpath);
}
}
target->type = QSE_HTTPD_RSRC_DIR;
target->u.dir.path = xpath;
target->type = QSE_HTTPD_RSRC_DIR;
target->u.dir.path = tmp.xpath;
}
else
{
/* let me treat it as a file. */
goto attempt_file;
}
}
else
{
/* well, stat failed. i don't know if it is a file.
* i must try each segment in the query path. */
attempt_file:
if (server_xtn->cfg2.s.cgistd)
/* check if the request can resolve to a cgi script */
n = attempt_cgi (httpd, client, req, &tmp, target);
if (n <= -1)
{
/* check if the request can resolve to a cgi script */
n = attempt_cgi (httpd, server_xtn->cfg[SERVER_XTN_CFG_DOCROOT],
xpath, qpath, idxfile, server_xtn->cfg2.s.cgistd, target);
if (n <= -1)
{
QSE_MMGR_FREE (httpd->mmgr, xpath);
return -1;
}
if (n >= 1) return 0;
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
return -1;
}
if (n >= 1) return 0;
/* fall back to a normal file. */
target->type = QSE_HTTPD_RSRC_FILE;
target->u.file.path = xpath;
target->u.file.mime = QSE_NULL;
if (server_xtn->cfg2.s.mimestd)
target->u.file.path = tmp.xpath;
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_MIME, &target->u.file.mime) <= -1)
{
for (i = 0; server_xtn->cfg2.s.mimestd[i].ext; i++)
{
/* TODO: require the table sorted and so the binary search */
if (qse_mbsend (qpath, server_xtn->cfg2.s.mimestd[i].ext))
target->u.file.mime = server_xtn->cfg2.s.mimestd[i].type;
}
/* don't care about failure */
target->u.file.mime = QSE_NULL;
}
}
return 0;
}
static qse_httpd_server_cbstd_t server_cbstd =
{
make_resource,
free_resource
};
/* ------------------------------------------------------------------- */
static void predetach_server (qse_httpd_t* httpd, qse_httpd_server_t* server)
{
server_xtn_t* server_xtn;
qse_size_t i;
server_xtn = (server_xtn_t*) qse_httpd_getserverxtn (httpd, server);
if (server_xtn->predetach) server_xtn->predetach (httpd, server);
if (server_xtn->auth.ptr) QSE_MMGR_FREE (httpd->mmgr, server_xtn->auth.ptr);
}
for (i = QSE_COUNTOF(server_xtn->cfg); i > 0; )
static void reconfig_server (qse_httpd_t* httpd, qse_httpd_server_t* server)
{
server_xtn_t* server_xtn;
server_xtn = (server_xtn_t*) qse_httpd_getserverxtn (httpd, server);
if (server_xtn->reconfig) server_xtn->reconfig (httpd, server);
/* nothing more to do here ... */
}
struct mime_tab_t
{
const qse_mchar_t* suffix;
const qse_mchar_t* type;
};
static struct mime_tab_t mimetab[] =
{
{ QSE_MT(".htm"), QSE_MT("text/html") },
{ QSE_MT(".html"), QSE_MT("text/html") },
{ QSE_MT(".txt"), QSE_MT("text/plain") }
};
struct cgi_tab_t
{
const qse_mchar_t* suffix;
qse_httpd_serverstd_cgi_t cgi;
};
static struct cgi_tab_t cgitab[] =
{
{ QSE_MT(".cgi"), { 1, 0, QSE_NULL } },
{ QSE_MT(".nph"), { 1, 1, QSE_NULL } },
};
static int query_server (
qse_httpd_t* httpd, qse_httpd_server_t* server,
qse_htre_t* req, const qse_mchar_t* xpath,
qse_httpd_serverstd_query_code_t code, void* result)
{
qse_size_t i;
switch (code)
{
if (server_xtn->cfg[--i])
case QSE_HTTPD_SERVERSTD_DOCROOT:
case QSE_HTTPD_SERVERSTD_REALM:
case QSE_HTTPD_SERVERSTD_AUTH:
case QSE_HTTPD_SERVERSTD_ERRCSS:
case QSE_HTTPD_SERVERSTD_DIRCSS:
*(const qse_mchar_t**)result = QSE_NULL;
return 0;
case QSE_HTTPD_SERVERSTD_INDEX:
{
QSE_MMGR_FREE (httpd->mmgr, server_xtn->cfg[i]);
server_xtn->cfg[i] = QSE_NULL;
qse_httpd_serverstd_index_t* index = (qse_httpd_serverstd_index_t*)result;
index->count = 2;
index->files = QSE_MT("index.html\0index.cgi\0");
return 0;
}
case QSE_HTTPD_SERVERSTD_CGI:
{
qse_httpd_serverstd_cgi_t* cgi = (qse_httpd_serverstd_cgi_t*)result;
for (i = 0; i < QSE_COUNTOF(cgitab); i++)
{
if (qse_mbsend (xpath, cgitab[i].suffix))
{
QSE_MEMCPY (cgi, &cgitab[i].cgi, QSE_SIZEOF(*cgi));
return 0;
}
}
QSE_MEMSET (cgi,0, QSE_SIZEOF(*cgi));
return 0;
}
case QSE_HTTPD_SERVERSTD_MIME:
/* TODO: binary search if the table is large */
for (i = 0; i < QSE_COUNTOF(mimetab); i++)
{
if (qse_mbsend (xpath, mimetab[i].suffix))
{
*(const qse_mchar_t**)result = mimetab[i].type;
return 0;
}
}
*(const qse_mchar_t**)result = QSE_NULL;
return 0;
}
qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL);
return -1;
}
qse_httpd_server_t* qse_httpd_attachserverstd (
qse_httpd_t* httpd, const qse_char_t* uri,
qse_httpd_server_predetach_t predetach, qse_size_t xtnsize)
qse_httpd_t* httpd, qse_httpd_serverstd_t* server, qse_size_t xtnsize)
{
qse_httpd_server_t server;
qse_httpd_server_dope_t dope;
qse_httpd_server_t* xserver;
server_xtn_t* server_xtn;
qse_mxstr_t ba;
qse_size_t balen2;
/* memcpy here assumes that the top of the dope structure
* is common with the server structure */
QSE_MEMCPY (&dope, server, QSE_SIZEOF(dope));
dope.predetach = predetach_server; /* set my own detaching function */
dope.reconfig = reconfig_server; /* set my own detaching function */
xserver = qse_httpd_attachserver (httpd, &dope, QSE_SIZEOF(*server_xtn) + xtnsize);
if (xserver == QSE_NULL) return QSE_NULL;
server_xtn = qse_httpd_getserverxtn (httpd, xserver);
server_xtn->predetach = server->predetach;
server_xtn->reconfig = server->reconfig;
server_xtn->query = query_server;
server_xtn->makersrc = make_resource;
server_xtn->freersrc = free_resource;
return xserver;
}
qse_httpd_server_t* qse_httpd_attachserverstdwithuri (
qse_httpd_t* httpd, const qse_char_t* uri,
qse_httpd_server_predetach_t predetach,
qse_httpd_server_reconfig_t reconfig,
qse_httpd_serverstd_query_t query,
qse_size_t xtnsize)
{
qse_httpd_serverstd_t server;
qse_uint16_t default_port;
qse_uri_t xuri;
static qse_httpd_server_cgistd_t server_cgistd[] =
{
{ QSE_MT(".cgi"), 4, 0, QSE_NULL },
{ QSE_MT(".nph"), 4, 1, QSE_NULL },
{ QSE_NULL, 0, 0, QSE_NULL }
};
static qse_httpd_server_mimestd_t server_mimestd[] =
{
{ QSE_MT(".html"), QSE_MT("text/html") },
{ QSE_MT(".htm"), QSE_MT("text/htm") },
{ QSE_MT(".txt"), QSE_MT("text/plain") },
{ QSE_MT(".log"), QSE_MT("text/plain") },
{ QSE_MT(".css"), QSE_MT("text/css") },
{ QSE_MT(".xml"), QSE_MT("text/xml") },
{ QSE_MT(".js"), QSE_MT("application/javascript") },
{ QSE_MT(".jpg"), QSE_MT("image/jpeg") },
{ QSE_MT(".png"), QSE_MT("image/png") },
{ QSE_MT(".mp4"), QSE_MT("video/mp4") },
{ QSE_MT(".mp3"), QSE_MT("audio/mpeg") },
{ QSE_MT(".c"), QSE_MT("text/plain") },
{ QSE_MT(".h"), QSE_MT("text/plain") },
{ QSE_MT(".cpp"), QSE_MT("text/plain") },
{ QSE_MT(".hpp"), QSE_MT("text/plain") },
{ QSE_NULL, QSE_NULL }
};
#if defined(QSE_CHAR_IS_MCHAR)
qse_mcstr_t tmp[4] =
{
{ QSE_MT(""), 0 },
{ QSE_MT(":"), 1 },
{ QSE_MT(""), 0 },
{ QSE_NULL, 0 }
};
#else
qse_wcstr_t tmp[4] =
{
{ QSE_WT(""), 0 },
{ QSE_WT(":"), 1 },
{ QSE_WT(""), 0 },
{ QSE_NULL, 0 }
};
#endif
QSE_MEMSET (&server, 0, QSE_SIZEOF(server));
if (qse_strtouri (uri, &xuri, QSE_STRTOURI_NOQUERY) <= -1) goto invalid;
@ -2485,93 +2621,25 @@ qse_httpd_server_t* qse_httpd_attachserverstd (
server.nwad.u.in6.port = qse_hton16(default_port);
}
xserver = qse_httpd_attachserver (
httpd, &server, predetach_server, QSE_SIZEOF(*server_xtn) + xtnsize);
if (xserver == QSE_NULL) return QSE_NULL;
server_xtn = qse_httpd_getserverxtn (httpd, xserver);
if (!xuri.path.ptr)
server.predetach = predetach;
server.reconfig = reconfig;
#if 0
server.docroot = xuri.path;
if (server.docroot.ptr && qse_ismbsdriveabspath((const qse_mchar_t*)server.docroot.ptr + 1))
{
/* the path part is not specified */
#if defined(QSE_CHAR_IS_MCHAR)
xuri.path.ptr = QSE_MT("/");
#else
xuri.path.ptr = QSE_WT("/");
/* if the path name is something like /C:/xxx on support platforms ... */
server.docroot.ptr++;
server.docroot.len--;
}
server.realm = xuri.frag;
server.user = xuri.auth.user;
server.pass = xuri.auth.pass;
#endif
xuri.path.len = 1;
}
if (xuri.auth.user.ptr)
{
tmp[0].ptr = xuri.auth.user.ptr;
tmp[0].len = xuri.auth.user.len;
}
if (xuri.auth.pass.ptr)
{
tmp[2].ptr = xuri.auth.pass.ptr;
tmp[2].len = xuri.auth.pass.len;
}
#if defined(QSE_CHAR_IS_MCHAR)
if (qse_ismbsdriveabspath((const qse_mchar_t*)xuri.path.ptr + 1))
server_xtn->cfg[SERVER_XTN_CFG_DOCROOT] = qse_mbsxdup ((const qse_mchar_t*)xuri.path.ptr + 1, xuri.path.len - 1, httpd->mmgr);
else
server_xtn->cfg[SERVER_XTN_CFG_DOCROOT] = qse_mbsxdup (xuri.path.ptr, xuri.path.len, httpd->mmgr);
if (xuri.frag.ptr) server_xtn->cfg[SERVER_XTN_CFG_REALM] = qse_mbsxdup (xuri.frag.ptr, xuri.frag.len, httpd->mmgr);
ba.ptr = qse_mcstradup (tmp, &ba.len, httpd->mmgr);
#else
if (qse_iswcsdriveabspath((const qse_wchar_t*)xuri.path.ptr + 1))
server_xtn->cfg[SERVER_XTN_CFG_DOCROOT] = qse_wcsntombsdup ((const qse_wchar_t*)xuri.path.ptr + 1, xuri.path.len - 1, QSE_NULL, httpd->mmgr);
else
server_xtn->cfg[SERVER_XTN_CFG_DOCROOT] = qse_wcsntombsdup (xuri.path.ptr, xuri.path.len, QSE_NULL, httpd->mmgr);
if (xuri.frag.ptr) server_xtn->cfg[SERVER_XTN_CFG_REALM] = qse_wcsntombsdup (xuri.frag.ptr, xuri.frag.len, QSE_NULL, httpd->mmgr);
ba.ptr = qse_wcsnatombsdup (tmp, &ba.len, httpd->mmgr);
#endif
if ((!server_xtn->cfg[SERVER_XTN_CFG_DOCROOT]) ||
(xuri.frag.ptr && !server_xtn->cfg[SERVER_XTN_CFG_REALM]) ||
!ba.ptr)
{
if (ba.ptr) QSE_MMGR_FREE (httpd->mmgr, ba.ptr);
goto nomem_after_attach;
}
balen2 = ((ba.len / 3) + 1) * 4;
server_xtn->cfg[SERVER_XTN_CFG_AUTH] = QSE_MMGR_ALLOC (
httpd->mmgr, (balen2 + 1) * QSE_SIZEOF(qse_mchar_t));
if (!server_xtn->cfg[SERVER_XTN_CFG_AUTH])
{
QSE_MMGR_FREE (httpd->mmgr, ba.ptr);
goto nomem_after_attach;
}
qse_enbase64 (
ba.ptr, ba.len,
server_xtn->cfg[SERVER_XTN_CFG_AUTH],
balen2,
&balen2
);
QSE_MMGR_FREE (httpd->mmgr, ba.ptr);
(server_xtn->cfg[SERVER_XTN_CFG_AUTH])[balen2] = QSE_MT('\0');
server_xtn->predetach = predetach;
server_xtn->cfg2.s.cbstd = &server_cbstd;
server_xtn->cfg2.s.cgistd = server_cgistd;
server_xtn->cfg2.s.mimestd = server_mimestd;
server_xtn->cfg2.s.idxstd = QSE_NULL;
return xserver;
return qse_httpd_attachserverstd (httpd, &server, xtnsize);
invalid:
httpd->errnum = QSE_HTTPD_EINVAL;
return QSE_NULL;
nomem_after_attach:
qse_httpd_detachserver (httpd, xserver);
httpd->errnum = QSE_HTTPD_ENOMEM;
return QSE_NULL;
}
int qse_httpd_getserveroptstd (
@ -2584,19 +2652,16 @@ int qse_httpd_getserveroptstd (
switch (id)
{
case QSE_HTTPD_SERVER_DOCROOT:
case QSE_HTTPD_SERVER_REALM:
case QSE_HTTPD_SERVER_AUTH:
case QSE_HTTPD_SERVER_ERRCSS:
case QSE_HTTPD_SERVER_DIRCSS:
*(qse_mchar_t**)value = server_xtn->cfg[id - QSE_HTTPD_SERVER_DOCROOT];
case QSE_HTTPD_SERVERSTD_QUERY:
*(qse_httpd_serverstd_query_t*)value = server_xtn->query;
return 0;
case QSE_HTTPD_SERVER_CBSTD:
case QSE_HTTPD_SERVER_CGISTD:
case QSE_HTTPD_SERVER_MIMESTD:
case QSE_HTTPD_SERVER_IDXSTD:
*(void**)value = (void*)server_xtn->cfg2.a[id - QSE_HTTPD_SERVER_CBSTD];
case QSE_HTTPD_SERVERSTD_MAKERSRC:
*(qse_httpd_serverstd_makersrc_t*)value = server_xtn->makersrc;
return 0;
case QSE_HTTPD_SERVERSTD_FREERSRC:
*(qse_httpd_serverstd_freersrc_t*)value = server_xtn->freersrc;
return 0;
}
@ -2604,47 +2669,27 @@ int qse_httpd_getserveroptstd (
return -1;
}
int qse_httpd_setserveroptstd (
qse_httpd_t* httpd, qse_httpd_server_t* server,
qse_httpd_server_optstd_t id, const void* value)
{
server_xtn_t* server_xtn;
qse_mchar_t* mctmp;
server_xtn = qse_httpd_getserverxtn (httpd, server);
switch (id)
{
case QSE_HTTPD_SERVER_DOCROOT:
case QSE_HTTPD_SERVER_REALM:
case QSE_HTTPD_SERVER_AUTH:
case QSE_HTTPD_SERVER_ERRCSS:
case QSE_HTTPD_SERVER_DIRCSS:
mctmp = (qse_mchar_t*)value;
if(mctmp)
{
mctmp = qse_mbsdup ((qse_mchar_t*)mctmp, httpd->mmgr);
if (mctmp == QSE_NULL)
{
httpd->errnum = QSE_HTTPD_ENOMEM;
return -1;
}
}
if (server_xtn->cfg[id - QSE_HTTPD_SERVER_DOCROOT])
{
QSE_MMGR_FREE (httpd->mmgr,
server_xtn->cfg[id - QSE_HTTPD_SERVER_DOCROOT]);
}
server_xtn->cfg[id - QSE_HTTPD_SERVER_DOCROOT] = mctmp;
case QSE_HTTPD_SERVERSTD_QUERY:
server_xtn->query = (qse_httpd_serverstd_query_t)value;
return 0;
case QSE_HTTPD_SERVER_CBSTD:
case QSE_HTTPD_SERVER_CGISTD:
case QSE_HTTPD_SERVER_MIMESTD:
case QSE_HTTPD_SERVER_IDXSTD:
server_xtn->cfg2.a[id - QSE_HTTPD_SERVER_CBSTD] = value;
case QSE_HTTPD_SERVERSTD_MAKERSRC:
server_xtn->makersrc = (qse_httpd_serverstd_makersrc_t)value;
return 0;
case QSE_HTTPD_SERVERSTD_FREERSRC:
server_xtn->freersrc = (qse_httpd_serverstd_freersrc_t)value;
return 0;
}

View File

@ -89,6 +89,11 @@ void qse_httpd_stop (qse_httpd_t* httpd)
httpd->stopreq = 1;
}
void qse_httpd_reconfig (qse_httpd_t* httpd)
{
httpd->reconfigreq = 1;
}
qse_httpd_errnum_t qse_httpd_geterrnum (qse_httpd_t* httpd)
{
return httpd->errnum;
@ -159,6 +164,14 @@ QSE_INLINE void* qse_httpd_allocmem (qse_httpd_t* httpd, qse_size_t size)
return ptr;
}
QSE_INLINE void* qse_httpd_callocmem (qse_httpd_t* httpd, qse_size_t size)
{
void* ptr = QSE_MMGR_ALLOC (httpd->mmgr, size);
if (ptr == QSE_NULL) httpd->errnum = QSE_HTTPD_ENOMEM;
else QSE_MEMSET (ptr, 0, size);
return ptr;
}
QSE_INLINE void* qse_httpd_reallocmem (
qse_httpd_t* httpd, void* ptr, qse_size_t size)
{
@ -172,6 +185,34 @@ QSE_INLINE void qse_httpd_freemem (qse_httpd_t* httpd, void* ptr)
QSE_MMGR_FREE (httpd->mmgr, ptr);
}
qse_mchar_t* qse_httpd_strtombsdup (qse_httpd_t* httpd, const qse_char_t* str)
{
qse_mchar_t* mptr;
#if defined(QSE_CHAR_IS_MCHAR)
mptr = qse_mbsdup (str, httpd->mmgr);
#else
mptr = qse_wcstombsdup (str, QSE_NULL, httpd->mmgr);
#endif
if (mptr == QSE_NULL) httpd->errnum = QSE_HTTPD_ENOMEM;
return mptr;
}
qse_mchar_t* qse_httpd_strntombsdup (qse_httpd_t* httpd, const qse_char_t* str, qse_size_t len)
{
qse_mchar_t* mptr;
#if defined(QSE_CHAR_IS_MCHAR)
mptr = qse_mbsxdup (str, len, httpd->mmgr);
#else
mptr = qse_wcsntombsdup (str, len, QSE_NULL, httpd->mmgr);
#endif
if (mptr == QSE_NULL) httpd->errnum = QSE_HTTPD_ENOMEM;
return mptr;
}
/* --------------------------------------------------- */
static qse_httpd_task_t* enqueue_task (
@ -434,15 +475,15 @@ static int accept_client (
{
/* TODO: proper logging */
qse_char_t tmp[128];
qse_nwadtostr (&server->nwad, tmp, QSE_COUNTOF(tmp), QSE_NWADTOSTR_ALL);
qse_printf (QSE_T("failed to accept from server %s\n"), tmp);
qse_nwadtostr (&server->dope.nwad, tmp, QSE_COUNTOF(tmp), QSE_NWADTOSTR_ALL);
qse_printf (QSE_T("failed to accept from server [%s] [%d]\n"), tmp, server->handle.i);
return -1;
}
/* TODO: check maximum number of client. if exceed call client.close */
if (server->flags & QSE_HTTPD_SERVER_SECURE) clibuf.status |= CLIENT_SECURE;
if (server->dope.flags & QSE_HTTPD_SERVER_SECURE) clibuf.status |= CLIENT_SECURE;
clibuf.server = server;
client = new_client (httpd, &clibuf);
@ -496,11 +537,11 @@ static void deactivate_servers (qse_httpd_t* httpd)
for (server = httpd->server.list.head; server; server = server->next)
{
if (server->flags & QSE_HTTPD_SERVER_ACTIVE)
if (server->dope.flags & QSE_HTTPD_SERVER_ACTIVE)
{
httpd->scb->mux.delhnd (httpd, httpd->mux, server->handle);
httpd->scb->server.close (httpd, server);
server->flags &= ~QSE_HTTPD_SERVER_ACTIVE;
server->dope.flags &= ~QSE_HTTPD_SERVER_ACTIVE;
httpd->server.nactive--;
}
}
@ -515,13 +556,12 @@ static int activate_servers (qse_httpd_t* httpd)
if (httpd->scb->server.open (httpd, server) <= -1)
{
qse_char_t buf[64];
qse_nwadtostr (&server->nwad,
buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL);
qse_nwadtostr (&server->dope.nwad, buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL);
/*
httpd->rcb->log (httpd, 0,
QSE_T("cannot activate %s"), buf);
httpd->rcb->log (httpd, 0, QSE_T("cannot activate %s"), buf);
*/
qse_printf(QSE_T("cannot activate [%s]\n"), buf);
continue;
}
@ -529,18 +569,17 @@ static int activate_servers (qse_httpd_t* httpd)
httpd, httpd->mux, server->handle, QSE_HTTPD_MUX_READ, server) <= -1)
{
qse_char_t buf[64];
qse_nwadtostr (&server->nwad,
buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL);
qse_nwadtostr (&server->dope.nwad, buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL);
/*
httpd->rcb->log (httpd, 0,
QSE_T("cannot activate %s - "), buf);
httpd->rcb->log (httpd, 0, QSE_T("cannot activate %s - "), buf);
*/
qse_printf(QSE_T("cannot add handle [%s]\n"), buf);
httpd->scb->server.close (httpd, server);
continue;
}
server->flags |= QSE_HTTPD_SERVER_ACTIVE;
server->dope.flags |= QSE_HTTPD_SERVER_ACTIVE;
httpd->server.nactive++;
}
@ -566,20 +605,18 @@ static void free_server_list (qse_httpd_t* httpd)
}
qse_httpd_server_t* qse_httpd_attachserver (
qse_httpd_t* httpd, const qse_httpd_server_t* tmpl,
qse_httpd_server_predetach_t predetach, qse_size_t xtnsize)
qse_httpd_t* httpd, const qse_httpd_server_dope_t* dope, qse_size_t xtnsize)
{
qse_httpd_server_t* server;
server = qse_httpd_allocmem (httpd, QSE_SIZEOF(*server) + xtnsize);
server = qse_httpd_callocmem (httpd, QSE_SIZEOF(*server) + xtnsize);
if (server == QSE_NULL) return QSE_NULL;
QSE_MEMCPY (server, tmpl, QSE_SIZEOF(*server));
QSE_MEMSET (server + 1, 0, xtnsize);
server->type = QSE_HTTPD_SERVER;
server->flags &= ~QSE_HTTPD_SERVER_ACTIVE;
server->predetach = predetach;
/* copy the server dope */
server->dope = *dope;
/* and correct some fields in case the dope contains invalid stuffs */
server->dope.flags &= ~QSE_HTTPD_SERVER_ACTIVE;
/* chain the server to the tail of the list */
server->prev = httpd->server.list.tail;
@ -601,9 +638,9 @@ void qse_httpd_detachserver (qse_httpd_t* httpd, qse_httpd_server_t* server)
prev = server->prev;
next = server->next;
QSE_ASSERT (!(server->flags & QSE_HTTPD_SERVER_ACTIVE));
QSE_ASSERT (!(server->dope.flags & QSE_HTTPD_SERVER_ACTIVE));
if (server->predetach) server->predetach (httpd, server);
if (server->dope.predetach) server->dope.predetach (httpd, server);
qse_httpd_freemem (httpd, server);
httpd->server.navail--;
@ -1112,11 +1149,24 @@ qse_printf (QSE_T("MUX ADDHND CLIENT RW(ENTASK) %d\n"), client->handle.i);
static int dispatch_mux (
qse_httpd_t* httpd, void* mux, qse_ubi_t handle, int mask, void* cbarg)
{
return ((qse_httpd_server_t*)cbarg)->type == QSE_HTTPD_SERVER?
return ((qse_httpd_mate_t*)cbarg)->type == QSE_HTTPD_SERVER?
accept_client (httpd, mux, handle, mask, cbarg):
perform_client_task (httpd, mux, handle, mask, cbarg);
}
static void reconfig_servers (qse_httpd_t* httpd)
{
qse_httpd_server_t* server;
for (server = httpd->server.list.head; server; server = server->next)
{
if (server->dope.flags & QSE_HTTPD_SERVER_ACTIVE)
{
if (server->dope.reconfig) server->dope.reconfig (httpd, server);
}
}
}
int qse_httpd_loop (
qse_httpd_t* httpd, qse_httpd_scb_t* scb,
qse_httpd_rcb_t* rcb, const qse_ntime_t* tmout)
@ -1166,12 +1216,21 @@ int qse_httpd_loop (
count = httpd->scb->mux.poll (httpd, httpd->mux, tmout);
if (count <= -1)
{
xret = -1;
break;
if (httpd->errnum != QSE_HTTPD_EINTR)
{
xret = -1;
break;
}
}
purge_bad_clients (httpd);
purge_idle_clients (httpd);
if (httpd->reconfigreq)
{
reconfig_servers (httpd);
httpd->reconfigreq = 0;
}
}
purge_client_list (httpd);

View File

@ -18,12 +18,12 @@
License along with QSE. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _QSE_LIB_NET_HTTPD_H_
#define _QSE_LIB_NET_HTTPD_H_
#ifndef _QSE_LIB_HTTP_HTTPD_H_
#define _QSE_LIB_HTTP_HTTPD_H_
/* private header file for httpd */
#include <qse/net/httpd.h>
#include <qse/http/httpd.h>
#include <qse/cmn/stdio.h> /* TODO: remove this.. only for debugging at this moment */
@ -39,7 +39,8 @@ struct qse_httpd_t
{
int trait;
} opt;
int stopreq;
int stopreq: 1;
int reconfigreq: 1;
qse_mchar_t sname[128]; /* server name for the server header */
qse_mchar_t gtbuf[10][64]; /* GMT time buffers */

View File

@ -21,7 +21,7 @@
#ifndef _QSE_LIB_NET_UPXD_H_
#define _QSE_LIB_NET_UPXD_H_
#include <qse/net/upxd.h>
#include <qse/http/upxd.h>
#include "../cmn/mem.h"
typedef struct qse_upxd_server_session_t qse_upxd_server_session_t;

View File

@ -39,10 +39,16 @@ const qse_char_t* qse_xli_dflerrstr (
QSE_T("I/O error with file '${0}'"),
QSE_T("error returned by user I/O handler"),
QSE_T("syntax error"),
QSE_T("semicolon expected in place of '${0}'"),
QSE_T("left-brace or equal-sign expected in place of '${0}'"),
QSE_T("right-brace expected in place of '${0}'"),
QSE_T("pair value expected in place of '${0}'")
QSE_T("pair value expected in place of '${0}'"),
QSE_T("string not closed"),
QSE_T("'@include' not followed by a string"),
QSE_T("invalid character '${0}'"),
QSE_T("'${0}' not recognized"),
QSE_T("@ not followed by a valid word")
};
return (errnum >= 0 && errnum < QSE_COUNTOF(errstr))?

View File

@ -48,6 +48,7 @@ enum tok_t
TOK_LBRACE,
TOK_RBRACE,
TOK_EQ,
TOK_COMMA,
TOK_DQSTR,
TOK_SQSTR,
TOK_IDENT,
@ -221,6 +222,7 @@ static int get_symbols (qse_xli_t* xli, qse_cint_t c, qse_xli_tok_t* tok)
static struct ops_t ops[] =
{
{ QSE_T("="), 1, TOK_EQ },
{ QSE_T(","), 1, TOK_COMMA },
{ QSE_T(";"), 1, TOK_SEMICOLON },
{ QSE_T("{"), 1, TOK_LBRACE },
{ QSE_T("}"), 1, TOK_RBRACE },
@ -310,9 +312,7 @@ static int begin_include (qse_xli_t* xli)
);
if (pair == QSE_NULL)
{
#if 0
SETERR_LOC (xli, QSE_XLI_ENOMEM, &xli->ptok.loc);
#endif
qse_xli_seterror (xli, QSE_XLI_ENOMEM, QSE_NULL, &xli->tok.loc);
goto oops;
}
@ -320,13 +320,7 @@ static int begin_include (qse_xli_t* xli)
QSE_HTB_VLEN(pair) = QSE_HTB_KLEN(pair);*/
arg = (qse_xli_io_arg_t*) qse_xli_callocmem (xli, QSE_SIZEOF(*arg));
if (arg == QSE_NULL)
{
#if 0
ADJERR_LOC (xli, &xli->ptok.loc);
#endif
goto oops;
}
if (arg == QSE_NULL) goto oops;
arg->flags = QSE_XLI_IO_INCLUDED;
arg->name = QSE_HTB_KPTR(pair);
@ -411,9 +405,7 @@ retry:
{
/* this directive is empty,
* not followed by a valid word */
#if 0
SETERR_LOC (xli, QSE_XLI_EXKWEM, &(xli)->tok.loc);
#endif
qse_xli_seterror (xli, QSE_XLI_EXKWEM, QSE_NULL, &xli->tok.loc);
return -1;
}
@ -429,9 +421,7 @@ retry:
if (type == TOK_IDENT)
{
/* this directive is not recognized */
#if 0
SETERR_TOK (xli, QSE_XLI_EXKWNR);
#endif
qse_xli_seterror (xli, QSE_XLI_EXKWNR, QSE_STR_CSTR(xli->tok.name), &xli->tok.loc);
return -1;
}
SET_TOKEN_TYPE (xli, tok, type);
@ -465,9 +455,7 @@ retry:
if (c == QSE_CHAR_EOF)
{
/* the string is not closed */
#if 0
SETERR_TOK (xli, QSE_XLI_ESTRNC);
#endif
qse_xli_seterror (xli, QSE_XLI_ESTRNC, QSE_NULL, &xli->tok.loc);
return -1;
}
@ -490,18 +478,18 @@ retry:
/* not handled yet */
if (c == QSE_T('\0'))
{
#if 0
SETERR_ARG_LOC (
xli, QSE_XLI_ELXCHR,
QSE_T("<NUL>"), 5, &tok->loc);
#endif
qse_cstr_t ea;
ea.ptr = QSE_T("<NUL>");
ea.len = 5;
qse_xli_seterror (xli, QSE_XLI_ELXCHR, &ea, &tok->loc);
}
else
{
#if 0
qse_char_t cc = (qse_char_t)c;
SETERR_ARG_LOC (xli, QSE_XLI_ELXCHR, &cc, 1, &tok->loc);
#endif
qse_cstr_t ea;
ea.ptr = &cc;
ea.len = 1;
qse_xli_seterror (xli, QSE_XLI_ELXCHR, &ea, &tok->loc);
}
return -1;
}
@ -518,9 +506,7 @@ retry:
if (skip_semicolon_after_include)
{
/* semiclon has not been skipped yet */
#if 0
qse_xli_seterror (xli, QSE_XLI_ESCOLON, QSE_STR_CSTR(tok->name), &tok->loc);
#endif
return -1;
}
@ -538,7 +524,7 @@ static int read_pair (qse_xli_t* xli, qse_xli_list_t* list)
qse_char_t* name = QSE_NULL;
qse_xli_pair_t* pair;
if (xli->opt.trait & QSE_XLI_NODUPKEY)
if (xli->opt.trait & QSE_XLI_KEYNODUP)
{
qse_xli_atom_t* atom;
@ -566,10 +552,10 @@ static int read_pair (qse_xli_t* xli, qse_xli_list_t* list)
if (get_token (xli) <= -1) goto oops;
if (xli->opt.trait & QSE_XLI_NAMEDKEY)
if (xli->opt.trait & QSE_XLI_KEYNAME)
{
/* the name part must be unique for the same key(s) */
if (MATCH (xli, TOK_SQSTR) || MATCH(xli, TOK_DQSTR))
if (MATCH (xli, TOK_IDENT) || MATCH (xli, TOK_DQSTR) || MATCH (xli, TOK_SQSTR))
{
qse_xli_atom_t* atom;
@ -602,14 +588,42 @@ static int read_pair (qse_xli_t* xli, qse_xli_list_t* list)
{
if (get_token (xli) <= -1) goto oops;
if (MATCH (xli, TOK_SQSTR) || MATCH (xli, TOK_DQSTR))
if (MATCH (xli, TOK_SQSTR) || MATCH (xli, TOK_DQSTR) || MATCH (xli, TOK_IDENT))
{
pair = qse_xli_insertpairwithstr (
xli, list, QSE_NULL, key, name,
QSE_STR_PTR(xli->tok.name), MATCH (xli, TOK_SQSTR));
if (pair == QSE_NULL) goto oops;
if (qse_str_ncpy (xli->tmp[0], QSE_STR_PTR(xli->tok.name), QSE_STR_LEN(xli->tok.name) + 1) == (qse_size_t)-1)
{
qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL);
goto oops;
}
if (get_token (xli) <= -1) goto oops;
if (MATCH(xli, TOK_COMMA))
{
/* multi-segmented string */
do
{
if (get_token (xli) <= -1) goto oops; /* skip the comma */
if (!MATCH (xli, TOK_SQSTR) && !MATCH (xli, TOK_DQSTR) && !MATCH (xli, TOK_IDENT))
{
qse_xli_seterror (xli, QSE_XLI_ESYNTAX, QSE_NULL, &xli->tok.loc);
goto oops;
}
if (qse_str_ncat (xli->tmp[0], QSE_STR_PTR(xli->tok.name), QSE_STR_LEN(xli->tok.name) + 1) == (qse_size_t)-1)
{
qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL);
goto oops;
}
if (get_token (xli) <= -1) goto oops; /* skip the value */
}
while (MATCH (xli, TOK_COMMA));
}
pair = qse_xli_insertpairwithstr (
xli, list, QSE_NULL, key, name, QSE_STR_CSTR(xli->tmp[0]));
if (pair == QSE_NULL) goto oops;
/* semicolon is mandatory for a string */
if (!MATCH (xli, TOK_SEMICOLON))
@ -651,6 +665,15 @@ static int read_pair (qse_xli_t* xli, qse_xli_list_t* list)
if (get_token (xli) <= -1) goto oops;
}
}
else if (MATCH (xli, TOK_SEMICOLON))
{
/* no value has been specified for the pair */
pair = qse_xli_insertpair (xli, list, QSE_NULL, key, name, &xli->xnil);
if (pair == QSE_NULL) goto oops;
/* skip the semicolon */
if (get_token (xli) <= -1) goto oops;
}
else
{
qse_xli_seterror (xli, QSE_XLI_ELBREQ, QSE_STR_CSTR(xli->tok.name), &xli->tok.loc);
@ -677,9 +700,7 @@ static int read_list (qse_xli_t* xli, qse_xli_list_t* list)
if (!MATCH(xli,TOK_SQSTR) && !MATCH(xli,TOK_DQSTR))
{
#if 0
SETERR_LOC (xli, QSE_XLI_EINCLSTR, &xli->ptok.loc);
#endif
qse_xli_seterror (xli, QSE_XLI_EINCLSTR, QSE_NULL, &xli->tok.loc);
return -1;
}
@ -693,7 +714,10 @@ static int read_list (qse_xli_t* xli, qse_xli_list_t* list)
{
if (get_token(xli) <= -1) goto oops;
}
else break;
else
{
break;
}
}
return 0;
@ -733,8 +757,7 @@ int qse_xli_read (qse_xli_t* xli, qse_xli_io_impl_t io)
if (!MATCH (xli, TOK_EOF))
{
/* TODO: set erro code */
qse_printf (QSE_T("NOT ENDING WITH EOF... %s\n"), QSE_STR_PTR(xli->tok.name));
qse_xli_seterror (xli, QSE_XLI_ESYNTAX, QSE_NULL, &xli->tok.loc);
goto oops;
}

View File

@ -24,12 +24,12 @@ int qse_xli_write (qse_xli_t* xli, qse_xli_io_impl_t io)
{
if (io == QSE_NULL)
{
xli->errnum = QSE_XLI_EINVAL;
qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL);
return -1;
}
/* TODO: write data to io stream */
xli->errnum = QSE_XLI_ENOIMPL;
qse_xli_seterrnum (xli, QSE_XLI_ENOIMPL, QSE_NULL);
return -1;
}

View File

@ -19,6 +19,7 @@
*/
#include "xli.h"
#include <qse/cmn/chr.h>
qse_xli_t* qse_xli_open (qse_mmgr_t* mmgr, qse_size_t xtnsize)
{
@ -51,10 +52,18 @@ void qse_xli_close (qse_xli_t* xli)
int qse_xli_init (qse_xli_t* xli, qse_mmgr_t* mmgr)
{
qse_size_t i;
QSE_MEMSET (xli, 0, QSE_SIZEOF(*xli));
xli->mmgr = mmgr;
xli->errstr = qse_xli_dflerrstr;
for (i = 0; i < QSE_COUNTOF(xli->tmp); i++)
{
xli->tmp[i] = qse_str_open (mmgr, 0, 128);
if (xli->tmp[i] == QSE_NULL) goto oops;
}
xli->tok.name = qse_str_open (mmgr, 0, 128);
if (xli->tok.name == QSE_NULL) goto oops;
@ -67,20 +76,34 @@ int qse_xli_init (qse_xli_t* xli, qse_mmgr_t* mmgr)
qse_gethtbmancbs(QSE_HTB_MANCBS_INLINE_KEY_COPIER)
);
xli->root.type = QSE_XLI_LIST;
xli->xnil.type = QSE_XLI_NIL;
return 0;
oops:
qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL);
if (xli->sio_names) qse_htb_close (xli->sio_names);
if (xli->tok.name) qse_str_close (xli->tok.name);
for (i = QSE_COUNTOF(xli->tmp); i > 0; )
{
if (xli->tmp[--i]) qse_str_close (xli->tmp[i]);
}
return -1;
}
void qse_xli_fini (qse_xli_t* xli)
{
qse_size_t i;
qse_xli_clear (xli);
qse_htb_close (xli->sio_names);
qse_str_close (xli->tok.name);
for (i = QSE_COUNTOF(xli->tmp); i > 0; )
{
if (xli->tmp[--i]) qse_str_close (xli->tmp[i]);
}
}
qse_mmgr_t* qse_xli_getmmgr (qse_xli_t* xli)
@ -243,20 +266,20 @@ qse_xli_pair_t* qse_xli_insertpairwithemptylist (
qse_xli_pair_t* qse_xli_insertpairwithstr (
qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer,
const qse_char_t* key, const qse_char_t* name, const qse_char_t* value, int verbatim)
const qse_char_t* key, const qse_char_t* name, const qse_cstr_t* value)
{
qse_xli_str_t* val;
qse_xli_pair_t* tmp;
qse_size_t vlen;
vlen = qse_strlen (value);
val = qse_xli_callocmem (xli, QSE_SIZEOF(*val) + ((vlen + 1) * QSE_SIZEOF(*value)));
val = qse_xli_callocmem (xli, QSE_SIZEOF(*val) + ((value->len + 1) * QSE_SIZEOF(*value->ptr)));
if (val == QSE_NULL) return QSE_NULL;
val->type = QSE_XLI_STR;
qse_strncpy ((qse_char_t*)(val + 1), value->ptr, value->len);
val->ptr = (const qse_char_t*)(val + 1);
val->len = vlen;
val->verbatim = verbatim;
val->len = value->len;
tmp = qse_xli_insertpair (xli, parent, peer, key, name, (qse_xli_val_t*)val);
if (tmp == QSE_NULL) qse_xli_freemem (xli, val);
return tmp;
@ -275,7 +298,6 @@ qse_xli_text_t* qse_xli_inserttext (
text->type = QSE_XLI_TEXT;
text->ptr = (const qse_char_t*)(text + 1);
text->len = slen;
insert_atom (xli, parent, peer, (qse_xli_atom_t*)text);
@ -292,10 +314,13 @@ static void free_atom (qse_xli_t* xli, qse_xli_atom_t* atom)
{
qse_xli_pair_t* pair = (qse_xli_pair_t*)atom;
if (pair->val->type == QSE_XLI_LIST)
free_list (xli, (qse_xli_list_t*)pair->val);
if (pair->val != &xli->xnil)
{
if (pair->val->type == QSE_XLI_LIST)
free_list (xli, (qse_xli_list_t*)pair->val);
QSE_MMGR_FREE (xli->mmgr, pair->val);
QSE_MMGR_FREE (xli->mmgr, pair->val);
}
}
QSE_MMGR_FREE (xli->mmgr, atom);
@ -319,6 +344,186 @@ static void free_list (qse_xli_t* xli, qse_xli_list_t* list)
void qse_xli_clear (qse_xli_t* xli)
{
/* TODO: free data under xli->root */
free_list (xli, &xli->root);
}
static qse_xli_pair_t* find_pair_byname (
qse_xli_t* xli, const qse_xli_list_t* list,
const qse_cstr_t* key, const qse_cstr_t* name)
{
qse_xli_atom_t* p;
/* TODO: speed up. no linear search */
p = list->head;
while (p)
{
if (p->type == QSE_XLI_PAIR)
{
qse_xli_pair_t* pair = (qse_xli_pair_t*)p;
if (qse_strxcmp (key->ptr, key->len, pair->key) == 0)
{
if (name == QSE_NULL ||
qse_strxcmp (name->ptr, name->len, pair->name) == 0) return pair;
}
}
p = p->next;
}
return QSE_NULL;
}
static qse_xli_pair_t* find_pair_byindex (
qse_xli_t* xli, const qse_xli_list_t* list,
const qse_cstr_t* key, qse_size_t index)
{
qse_xli_atom_t* p;
qse_size_t count = 0;
/* TODO: speed up. no linear search */
p = list->head;
while (p)
{
if (p->type == QSE_XLI_PAIR)
{
qse_xli_pair_t* pair = (qse_xli_pair_t*)p;
if (qse_strxcmp (key->ptr, key->len, pair->key) == 0)
{
if (index == count) return pair;
count++;
}
}
p = p->next;
}
return QSE_NULL;
}
qse_xli_pair_t* qse_xli_findpairbyname (
qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* name)
{
const qse_char_t* ptr;
qse_cstr_t seg;
qse_xli_list_t* curlist;
qse_xli_pair_t* pair;
curlist = list? list: &xli->root;
ptr = name;
while (1)
{
seg.ptr = ptr;
while (*ptr != QSE_T('\0') &&
*ptr != QSE_T('.') &&
*ptr != QSE_T('[')) ptr++;
if (ptr == seg.ptr) goto inval;
seg.len = ptr - seg.ptr;
if (curlist->type != QSE_XLI_LIST)
{
/* check the type of curlist. this check is needed
* because of the unconditional switching at the bottom of the
* this loop. this implementation strategy has been chosen
* to provide the segment name easily. */
goto noent;
}
if (*ptr == QSE_T('['))
{
/* index is specified */
ptr++;
if (QSE_ISDIGIT(*ptr))
{
/* numeric index */
qse_size_t index = 0, count = 0;
do
{
index = index * 10 + (*ptr++ - QSE_T('0'));
count++;
}
while (QSE_ISDIGIT(*ptr));
if (*ptr != QSE_T(']')) goto inval;
pair = find_pair_byindex (xli, curlist, &seg, index);
if (pair == QSE_NULL)
{
seg.len += count + 2; /* adjustment for error message */
goto noent;
}
}
else if (QSE_ISALPHA(*ptr))
{
/* word index */
qse_cstr_t idx;
idx.ptr = ptr;
do ptr++; while (QSE_ISALNUM(*ptr) || *ptr == QSE_T('_') || *ptr == QSE_T('-'));
idx.len = ptr - idx.ptr;
if (*ptr != QSE_T(']')) goto inval;
pair = find_pair_byname (xli, curlist, &seg, &idx);
if (pair == QSE_NULL)
{
seg.len += idx.len + 2; /* adjustment for error message */
goto noent;
}
}
else if (*ptr == QSE_T('\'') || *ptr == QSE_T('\"'))
{
qse_cstr_t idx;
qse_char_t cc = *ptr++;
idx.ptr = ptr;
do ptr++; while (*ptr != cc && *ptr != QSE_T('\0'));
idx.len = ptr - idx.ptr;
if (*ptr != cc) goto inval;
if (*++ptr != QSE_T(']')) goto inval;
pair = find_pair_byname (xli, curlist, &seg, &idx);
if (pair == QSE_NULL)
{
seg.len += idx.len + 4; /* adjustment for error message */
goto noent;
}
}
else goto inval;
ptr++; /* skip ] */
if (*ptr == QSE_T('\0')) break; /* no more segments */
else if (*ptr != QSE_T('.')) goto inval;
}
else
{
pair = find_pair_byname (xli, curlist, &seg, QSE_NULL);
if (pair == QSE_NULL) goto noent;
if (*ptr == QSE_T('\0')) break; /* no more segments */
}
/* more segments to handle */
QSE_ASSERT (*ptr == QSE_T('.'));
ptr++;
/* switch to the value regardless of its type.
* check if it is a list in the beginning of the loop
* just after having gotten the next segment name */
curlist = (qse_xli_list_t*)pair->val;
}
return pair;
inval:
qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL);
return QSE_NULL;
noent:
qse_xli_seterrnum (xli, QSE_XLI_ENOENT, &seg);
return QSE_NULL;
}

View File

@ -52,12 +52,14 @@ struct qse_xli_t
qse_xli_ecb_t* ecb;
qse_xli_list_t root;
qse_xli_nil_t xnil;
qse_str_t* tmp[1];
qse_xli_tok_t tok;
struct
{
qse_xli_io_impl_t inf; /* input handler */
qse_xli_io_lxc_t last;
qse_xli_io_lxc_t last;
qse_xli_io_arg_t arg; /* for top level */
qse_xli_io_arg_t* inp; /* current */
} sio;