added mio_dupbcstrs() and mio_dupucstrs()
changed mio_svc_htts_start() to access the main processor callback
This commit is contained in:
		@ -748,6 +748,102 @@ static void on_dnc_resolve_brief (mio_svc_dnc_t* dnc, mio_dns_msg_t* reqmsg, mio
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ========================================================================= */
 | 
			
		||||
int process_http_request (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req)
 | 
			
		||||
{
 | 
			
		||||
	mio_t* mio = mio_svc_htts_getmio(htts);
 | 
			
		||||
//	mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck);
 | 
			
		||||
	mio_http_method_t mth;
 | 
			
		||||
 | 
			
		||||
	/* percent-decode the query path to the original buffer
 | 
			
		||||
	 * since i'm not going to need it in the original form
 | 
			
		||||
	 * any more. once it's decoded in the peek mode,
 | 
			
		||||
	 * the decoded query path is made available in the
 | 
			
		||||
	 * non-peek mode as well */
 | 
			
		||||
 | 
			
		||||
	MIO_DEBUG2 (mio, "[RAW-REQ] %s %s\n", mio_htre_getqmethodname(req), mio_htre_getqpath(req));
 | 
			
		||||
 | 
			
		||||
	mio_htre_perdecqpath(req);
 | 
			
		||||
	/* TODO: proper request logging */
 | 
			
		||||
 | 
			
		||||
	MIO_DEBUG2 (mio, "[REQ] %s %s\n", mio_htre_getqmethodname(req), mio_htre_getqpath(req));
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
mio_printf (MIO_T("================================\n"));
 | 
			
		||||
mio_printf (MIO_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d %hs] method[%hs]\n"),
 | 
			
		||||
	(unsigned long)time(NULL),
 | 
			
		||||
	(peek? MIO_MT("PEEK"): MIO_MT("HANDLE")),
 | 
			
		||||
	mio_htre_getqpath(req),
 | 
			
		||||
	mio_htre_getmajorversion(req),
 | 
			
		||||
	mio_htre_getminorversion(req),
 | 
			
		||||
	mio_htre_getverstr(req),
 | 
			
		||||
	mio_htre_getqmethodname(req)
 | 
			
		||||
);
 | 
			
		||||
if (mio_htre_getqparam(req))
 | 
			
		||||
	mio_printf (MIO_T("PARAMS ==> [%hs]\n"), mio_htre_getqparam(req));
 | 
			
		||||
 | 
			
		||||
mio_htb_walk (&req->hdrtab, walk, MIO_NULL);
 | 
			
		||||
if (mio_htre_getcontentlen(req) > 0)
 | 
			
		||||
{
 | 
			
		||||
	mio_printf (MIO_T("CONTENT [%.*S]\n"), (int)mio_htre_getcontentlen(req), mio_htre_getcontentptr(req));
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	mth = mio_htre_getqmethodtype(req);
 | 
			
		||||
	/* determine what to do once the header fields are all received.
 | 
			
		||||
	 * i don't want to delay this until the contents are received.
 | 
			
		||||
	 * if you don't like this behavior, you must implement your own
 | 
			
		||||
	 * callback function for request handling. */
 | 
			
		||||
#if 0
 | 
			
		||||
	/* TODO support X-HTTP-Method-Override */
 | 
			
		||||
	if (data.method == MIO_HTTP_POST)
 | 
			
		||||
	{
 | 
			
		||||
		tmp = mio_htre_getheaderval(req, MIO_MT("X-HTTP-Method-Override"));
 | 
			
		||||
		if (tmp)
 | 
			
		||||
		{
 | 
			
		||||
			/*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
 | 
			
		||||
			data.method = mio_mbstohttpmethod (tmp->ptr);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	if (mth == MIO_HTTP_CONNECT)
 | 
			
		||||
	{
 | 
			
		||||
		/* CONNECT method must not have content set. 
 | 
			
		||||
		 * however, arrange to discard it if so. 
 | 
			
		||||
		 *
 | 
			
		||||
		 * NOTE: CONNECT is implemented to ignore many headers like
 | 
			
		||||
		 *       'Expect: 100-continue' and 'Connection: keep-alive'. */
 | 
			
		||||
		mio_htre_discardcontent (req);
 | 
			
		||||
	}
 | 
			
		||||
	else 
 | 
			
		||||
	{
 | 
			
		||||
/* this part can be checked in actual mio_svc_htts_doXXX() functions.
 | 
			
		||||
 * some doXXX handlers may not require length for POST.
 | 
			
		||||
 * it may be able to simply accept till EOF? or  treat as if CONTENT_LENGTH is 0*/
 | 
			
		||||
		if (mth == MIO_HTTP_POST && !(req->flags & (MIO_HTRE_ATTR_LENGTH | MIO_HTRE_ATTR_CHUNKED)))
 | 
			
		||||
		{
 | 
			
		||||
			/* POST without Content-Length nor not chunked */
 | 
			
		||||
			mio_htre_discardcontent (req); 
 | 
			
		||||
			/* 411 Length Required - can't keep alive. Force disconnect */
 | 
			
		||||
			req->flags &= ~MIO_HTRE_ATTR_KEEPALIVE; /* to cause sendstatus() to close */
 | 
			
		||||
			if (mio_svc_htts_sendstatus(htts, csck, req, 411, MIO_NULL) <= -1) goto oops;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
#endif
 | 
			
		||||
			/*const mio_bch_t* qpath = mio_htre_getqpath(req);*/
 | 
			
		||||
			if (mio_svc_htts_docgi(htts, csck, req, "", mio_htre_getqpath(req)) <= -1) goto oops;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
oops:
 | 
			
		||||
	mio_dev_sck_halt (csck);
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ========================================================================= */
 | 
			
		||||
 | 
			
		||||
static mio_t* g_mio;
 | 
			
		||||
@ -976,7 +1072,7 @@ for (i = 0; i < 5; i++)
 | 
			
		||||
	mio_bcstrtoskad (mio, "127.0.0.1:9988", &htts_bind_addr);
 | 
			
		||||
 | 
			
		||||
	dnc = mio_svc_dnc_start(mio, &servaddr, MIO_NULL, &send_tmout, &reply_tmout, 2); /* option - send to all, send one by one */
 | 
			
		||||
	htts = mio_svc_htts_start(mio, &htts_bind_addr);
 | 
			
		||||
	htts = mio_svc_htts_start(mio, &htts_bind_addr, process_http_request);
 | 
			
		||||
	mio_svc_htts_setservernamewithbcstr (htts, "MIO-HTTP");
 | 
			
		||||
 | 
			
		||||
#if 1
 | 
			
		||||
 | 
			
		||||
@ -1217,43 +1217,9 @@ int mio_htrd_feed (mio_htrd_t* htrd, const mio_bch_t* req, mio_oow_t len, mio_oo
 | 
			
		||||
						/* need to clear request on error? 
 | 
			
		||||
						clear_feed (htrd); */
 | 
			
		||||
						return -1;
 | 
			
		||||
/// TODO: PEEKONLY doens't seem to be neede. delete it...
 | 
			
		||||
					}
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
					if (htrd->option & MIO_HTRD_PEEKONLY)
 | 
			
		||||
					{
 | 
			
		||||
						/* when MIO_HTRD_PEEKONLY is set,
 | 
			
		||||
						 * the peek callback is invoked once
 | 
			
		||||
						 * a complete header is seen. the caller
 | 
			
		||||
						 * should not feed more data by calling
 | 
			
		||||
						 * this function again once the callback is
 | 
			
		||||
						 * invoked. the trailing data is appended
 | 
			
		||||
						 * to the content buffer.
 | 
			
		||||
						 *
 | 
			
		||||
						 * NOTE: if the current feed that completed 
 | 
			
		||||
						 *  the header contains the next request, 
 | 
			
		||||
						 *  the next request is treated as if it 
 | 
			
		||||
						 *  belongs to the current request.
 | 
			
		||||
						 *
 | 
			
		||||
						 * In priciple, this option was added for
 | 
			
		||||
						 * reading CGI outputs. So it comes with
 | 
			
		||||
						 * awkwardity described above.
 | 
			
		||||
						 */
 | 
			
		||||
						if (ptr < end && push_content(htrd, ptr, end - ptr) <= -1) return -1;
 | 
			
		||||
 | 
			
		||||
						/* i don't really know if it is really completed 
 | 
			
		||||
						 * with content. MIO_HTRD_PEEKONLY is not compatible
 | 
			
		||||
						 * with the completed state. anyway, let me complete
 | 
			
		||||
						 * it. */
 | 
			
		||||
						mio_htre_completecontent (&htrd->re);
 | 
			
		||||
 | 
			
		||||
						/* this jump is only to invoke the peek 
 | 
			
		||||
						 * callback. this function should not be fed
 | 
			
		||||
						 * more. */
 | 
			
		||||
						goto feedme_more; 
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					/* carry on processing content body fed together with the header */
 | 
			
		||||
					if (htrd->re.flags & MIO_HTRE_ATTR_CHUNKED)
 | 
			
		||||
					{
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
#include "mio-pro.h" /* for cgi */
 | 
			
		||||
#include "mio-fmt.h"
 | 
			
		||||
#include "mio-chr.h"
 | 
			
		||||
#include "mio-path.h"
 | 
			
		||||
#include "mio-prv.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -35,6 +36,8 @@ struct mio_svc_htts_t
 | 
			
		||||
{
 | 
			
		||||
	MIO_SVC_HEADER;
 | 
			
		||||
 | 
			
		||||
	mio_svc_htts_proc_req_t proc_req;
 | 
			
		||||
 | 
			
		||||
	mio_dev_sck_t* lsck;
 | 
			
		||||
	mio_svc_htts_cli_t cli; /* list head for client list */
 | 
			
		||||
 | 
			
		||||
@ -98,110 +101,11 @@ static int test_func_handler (int rfd, int wfd)
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int process_request (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req)
 | 
			
		||||
{
 | 
			
		||||
	//server_xtn_t* server_xtn = GET_SERVER_XTN(htts, client->server);
 | 
			
		||||
	//mio_htts_task_t* task;
 | 
			
		||||
	mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck);
 | 
			
		||||
	mio_http_method_t mth;
 | 
			
		||||
	//mio_htts_rsrc_t rsrc;
 | 
			
		||||
 | 
			
		||||
	/* percent-decode the query path to the original buffer
 | 
			
		||||
	 * since i'm not going to need it in the original form
 | 
			
		||||
	 * any more. once it's decoded in the peek mode,
 | 
			
		||||
	 * the decoded query path is made available in the
 | 
			
		||||
	 * non-peek mode as well */
 | 
			
		||||
 | 
			
		||||
	MIO_DEBUG2 (htts->mio, "[RAW-REQ] %s %s\n", mio_htre_getqmethodname(req), mio_htre_getqpath(req));
 | 
			
		||||
 | 
			
		||||
	mio_htre_perdecqpath(req);
 | 
			
		||||
	/* TODO: proper request logging */
 | 
			
		||||
 | 
			
		||||
	MIO_DEBUG2 (htts->mio, "[REQ] %s %s\n", mio_htre_getqmethodname(req), mio_htre_getqpath(req));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
mio_printf (MIO_T("================================\n"));
 | 
			
		||||
mio_printf (MIO_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d %hs] method[%hs]\n"),
 | 
			
		||||
	(unsigned long)time(NULL),
 | 
			
		||||
	(peek? MIO_MT("PEEK"): MIO_MT("HANDLE")),
 | 
			
		||||
	mio_htre_getqpath(req),
 | 
			
		||||
	mio_htre_getmajorversion(req),
 | 
			
		||||
	mio_htre_getminorversion(req),
 | 
			
		||||
	mio_htre_getverstr(req),
 | 
			
		||||
	mio_htre_getqmethodname(req)
 | 
			
		||||
);
 | 
			
		||||
if (mio_htre_getqparam(req))
 | 
			
		||||
	mio_printf (MIO_T("PARAMS ==> [%hs]\n"), mio_htre_getqparam(req));
 | 
			
		||||
 | 
			
		||||
mio_htb_walk (&req->hdrtab, walk, MIO_NULL);
 | 
			
		||||
if (mio_htre_getcontentlen(req) > 0)
 | 
			
		||||
{
 | 
			
		||||
	mio_printf (MIO_T("CONTENT [%.*S]\n"), (int)mio_htre_getcontentlen(req), mio_htre_getcontentptr(req));
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	mth = mio_htre_getqmethodtype(req);
 | 
			
		||||
	/* determine what to do once the header fields are all received.
 | 
			
		||||
	 * i don't want to delay this until the contents are received.
 | 
			
		||||
	 * if you don't like this behavior, you must implement your own
 | 
			
		||||
	 * callback function for request handling. */
 | 
			
		||||
#if 0
 | 
			
		||||
	/* TODO support X-HTTP-Method-Override */
 | 
			
		||||
	if (data.method == MIO_HTTP_POST)
 | 
			
		||||
	{
 | 
			
		||||
		tmp = mio_htre_getheaderval(req, MIO_MT("X-HTTP-Method-Override"));
 | 
			
		||||
		if (tmp)
 | 
			
		||||
		{
 | 
			
		||||
			/*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
 | 
			
		||||
			data.method = mio_mbstohttpmethod (tmp->ptr);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	if (mth == MIO_HTTP_CONNECT)
 | 
			
		||||
	{
 | 
			
		||||
		/* CONNECT method must not have content set. 
 | 
			
		||||
		 * however, arrange to discard it if so. 
 | 
			
		||||
		 *
 | 
			
		||||
		 * NOTE: CONNECT is implemented to ignore many headers like
 | 
			
		||||
		 *       'Expect: 100-continue' and 'Connection: keep-alive'. */
 | 
			
		||||
		mio_htre_discardcontent (req);
 | 
			
		||||
	}
 | 
			
		||||
	else 
 | 
			
		||||
	{
 | 
			
		||||
/* this part can be checked in actual mio_svc_htts_doXXX() functions.
 | 
			
		||||
 * some doXXX handlers may not require length for POST.
 | 
			
		||||
 * it may be able to simply accept till EOF? or  treat as if CONTENT_LENGTH is 0*/
 | 
			
		||||
		if (mth == MIO_HTTP_POST && !(req->flags & (MIO_HTRE_ATTR_LENGTH | MIO_HTRE_ATTR_CHUNKED)))
 | 
			
		||||
		{
 | 
			
		||||
			/* POST without Content-Length nor not chunked */
 | 
			
		||||
			mio_htre_discardcontent (req); 
 | 
			
		||||
			/* 411 Length Required - can't keep alive. Force disconnect */
 | 
			
		||||
			req->flags &= ~MIO_HTRE_ATTR_KEEPALIVE; /* to cause sendstatus() to close */
 | 
			
		||||
			if (mio_svc_htts_sendstatus(htts, csck, req, 411, MIO_NULL) <= -1) goto oops;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
#endif
 | 
			
		||||
			/*const mio_bch_t* qpath = mio_htre_getqpath(req);*/
 | 
			
		||||
			if (mio_svc_htts_docgi(htts, csck, req, "") <= -1) goto oops;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
oops:
 | 
			
		||||
	mio_dev_sck_halt (csck);
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int client_htrd_peek_request (mio_htrd_t* htrd, mio_htre_t* req)
 | 
			
		||||
{
 | 
			
		||||
	htrd_xtn_t* htrdxtn = (htrd_xtn_t*)mio_htrd_getxtn(htrd);
 | 
			
		||||
	mio_svc_htts_cli_t* sckxtn = (mio_svc_htts_cli_t*)mio_dev_sck_getxtn(htrdxtn->sck);
 | 
			
		||||
	return process_request(sckxtn->htts, htrdxtn->sck, req);
 | 
			
		||||
	return sckxtn->htts->proc_req(sckxtn->htts, htrdxtn->sck, req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -439,7 +343,7 @@ static void listener_on_disconnect (mio_dev_sck_t* sck)
 | 
			
		||||
 | 
			
		||||
/* ------------------------------------------------------------------------ */
 | 
			
		||||
 | 
			
		||||
mio_svc_htts_t* mio_svc_htts_start (mio_t* mio, const mio_skad_t* bind_addr)
 | 
			
		||||
mio_svc_htts_t* mio_svc_htts_start (mio_t* mio, const mio_skad_t* bind_addr, mio_svc_htts_proc_req_t proc_req)
 | 
			
		||||
{
 | 
			
		||||
	mio_svc_htts_t* htts = MIO_NULL;
 | 
			
		||||
	union
 | 
			
		||||
@ -455,6 +359,7 @@ mio_svc_htts_t* mio_svc_htts_start (mio_t* mio, const mio_skad_t* bind_addr)
 | 
			
		||||
 | 
			
		||||
	htts->mio = mio;
 | 
			
		||||
	htts->svc_stop = mio_svc_htts_stop;
 | 
			
		||||
	htts->proc_req = proc_req;
 | 
			
		||||
 | 
			
		||||
	MIO_MEMSET (&info, 0, MIO_SIZEOF(info));
 | 
			
		||||
	switch (mio_skad_family(bind_addr))
 | 
			
		||||
@ -1314,6 +1219,8 @@ struct cgi_peer_fork_ctx_t
 | 
			
		||||
	mio_svc_htts_cli_t* cli;
 | 
			
		||||
	mio_htre_t* req;
 | 
			
		||||
	const mio_bch_t* docroot;
 | 
			
		||||
	const mio_bch_t* script;
 | 
			
		||||
	mio_bch_t* actual_script;
 | 
			
		||||
};
 | 
			
		||||
typedef struct cgi_peer_fork_ctx_t cgi_peer_fork_ctx_t;
 | 
			
		||||
 | 
			
		||||
@ -1362,24 +1269,27 @@ static int cgi_peer_on_fork (mio_dev_pro_t* pro, void* fork_ctx)
 | 
			
		||||
	cgi_peer_fork_ctx_t* fc = (cgi_peer_fork_ctx_t*)fork_ctx;
 | 
			
		||||
	mio_oow_t content_length;
 | 
			
		||||
	const mio_bch_t* qparam;
 | 
			
		||||
	const char* path;
 | 
			
		||||
	const char* path, * lang;
 | 
			
		||||
	mio_bch_t tmp[256];
 | 
			
		||||
	mio_becs_t dbuf;
 | 
			
		||||
 | 
			
		||||
	qparam = mio_htre_getqparam(fc->req);
 | 
			
		||||
 | 
			
		||||
	path = getenv("PATH");
 | 
			
		||||
	lang = getenv("LANG");
 | 
			
		||||
	clearenv ();
 | 
			
		||||
	if (path) setenv ("PATH", path, 1);
 | 
			
		||||
	if (lang) setenv ("LANG", lang, 1);
 | 
			
		||||
 | 
			
		||||
	setenv ("GATEWAY_INTERFACE", "CGI/1.1", 1);
 | 
			
		||||
 | 
			
		||||
	mio_fmttobcstr (pro->mio, tmp, MIO_COUNTOF(tmp), "HTTP/%d.%d", (int)mio_htre_getmajorversion(fc->req), (int)mio_htre_getminorversion(fc->req));
 | 
			
		||||
	setenv ("SERVER_PROTOCOL", tmp, 1);
 | 
			
		||||
 | 
			
		||||
	//setenv ("SCRIPT_FILENAME",  
 | 
			
		||||
	//setenv ("SCRIPT_NAME",
 | 
			
		||||
	setenv ("DOCUMENT_ROOT", fc->docroot, 1);
 | 
			
		||||
	setenv ("SCRIPT_NAME", fc->script, 1);
 | 
			
		||||
	setenv ("SCRIPT_FILENAME", fc->actual_script, 1);
 | 
			
		||||
	/* TODO: PATH_INFO */
 | 
			
		||||
 | 
			
		||||
	setenv ("REQUEST_METHOD", mio_htre_getqmethodname(fc->req), 1);
 | 
			
		||||
	setenv ("REQUEST_URI", mio_htre_getqpath(fc->req), 1);
 | 
			
		||||
@ -1422,8 +1332,7 @@ static int cgi_peer_on_fork (mio_dev_pro_t* pro, void* fork_ctx)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, const mio_bch_t* docroot)
 | 
			
		||||
int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, const mio_bch_t* docroot, const mio_bch_t* script)
 | 
			
		||||
{
 | 
			
		||||
	mio_t* mio = htts->mio;
 | 
			
		||||
	mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck);
 | 
			
		||||
@ -1439,10 +1348,13 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r
 | 
			
		||||
	fc.cli = cli;
 | 
			
		||||
	fc.req = req;
 | 
			
		||||
	fc.docroot = docroot;
 | 
			
		||||
	fc.script = script;
 | 
			
		||||
	fc.actual_script = mio_svc_htts_dupmergepaths(htts, docroot, script);
 | 
			
		||||
	if (!fc.actual_script) goto oops;
 | 
			
		||||
 | 
			
		||||
	MIO_MEMSET (&mi, 0, MIO_SIZEOF(mi));
 | 
			
		||||
	mi.flags = MIO_DEV_PRO_READOUT | MIO_DEV_PRO_ERRTONUL | MIO_DEV_PRO_WRITEIN /*| MIO_DEV_PRO_FORGET_CHILD*/;
 | 
			
		||||
	mi.cmd = mio_htre_getqpath(req); /* TODO: combine it with docroot */
 | 
			
		||||
	mi.cmd = fc.actual_script;
 | 
			
		||||
	mi.on_read = cgi_peer_on_read;
 | 
			
		||||
	mi.on_write = cgi_peer_on_write;
 | 
			
		||||
	mi.on_close = cgi_peer_on_close;
 | 
			
		||||
@ -1468,10 +1380,6 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r
 | 
			
		||||
	MIO_ASSERT (mio, cli->rsrc == MIO_NULL);
 | 
			
		||||
	MIO_SVC_HTTS_RSRC_ATTACH (cgi_state, cli->rsrc);
 | 
			
		||||
 | 
			
		||||
/* TODO: create cgi environment variables... */
 | 
			
		||||
/* TODO:
 | 
			
		||||
 * never put Expect: 100-continue  to environment variable
 | 
			
		||||
 */
 | 
			
		||||
	if (access(mi.cmd, X_OK) == -1)
 | 
			
		||||
	{
 | 
			
		||||
		cgi_state_send_final_status_to_client (cgi_state, 403); /* 403 Forbidden */
 | 
			
		||||
@ -1588,17 +1496,29 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r
 | 
			
		||||
 | 
			
		||||
	/* TODO: store current input watching state and use it when destroying the cgi_state data */
 | 
			
		||||
	if (mio_dev_sck_read(csck, !(cgi_state->over & CGI_STATE_OVER_READ_FROM_CLIENT)) <= -1) goto oops;
 | 
			
		||||
	mio_freemem (mio, fc.actual_script);
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
oops:
 | 
			
		||||
	MIO_DEBUG2 (mio, "HTTS(%p) - FAILURE in docgi - socket(%p)\n", htts, csck);
 | 
			
		||||
	if (cgi_state) cgi_state_halt_participating_devices (cgi_state);
 | 
			
		||||
	if (fc.actual_script) mio_freemem (mio, fc.actual_script);
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ----------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
int mio_svc_htts_dofile (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, const mio_bch_t* docroot)
 | 
			
		||||
#if 0
 | 
			
		||||
int mio_svc_htts_dothrfunc (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, mio_svc_htts_func_t func)
 | 
			
		||||
{
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* ----------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
int mio_svc_htts_dofile (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, const mio_bch_t* docroot, const mio_bch_t* file)
 | 
			
		||||
{
 | 
			
		||||
	switch (mio_htre_getqmethodtype(req))
 | 
			
		||||
	{
 | 
			
		||||
@ -1668,6 +1588,26 @@ void mio_svc_htts_fmtgmtime (mio_svc_htts_t* htts, const mio_ntime_t* nt, mio_bc
 | 
			
		||||
	mio_fmt_http_time_to_bcstr(nt, buf, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mio_bch_t* mio_svc_htts_dupmergepaths (mio_svc_htts_t* htts, const mio_bch_t* base, const mio_bch_t* path)
 | 
			
		||||
{
 | 
			
		||||
	mio_bch_t* xpath;
 | 
			
		||||
	const mio_bch_t* ta[4];
 | 
			
		||||
	mio_oow_t idx = 0;
 | 
			
		||||
 | 
			
		||||
	ta[idx++] = base;
 | 
			
		||||
	if (path[0] != '\0')
 | 
			
		||||
	{
 | 
			
		||||
		ta[idx++] = "/";
 | 
			
		||||
		ta[idx++] = path;
 | 
			
		||||
	}
 | 
			
		||||
	ta[idx++] = MIO_NULL;
 | 
			
		||||
	xpath = mio_dupbcstrs(htts->mio, ta, MIO_NULL);
 | 
			
		||||
	if (MIO_UNLIKELY(!xpath)) return MIO_NULL;
 | 
			
		||||
 | 
			
		||||
	mio_canon_bcstr_path (xpath, xpath, 0);
 | 
			
		||||
	return xpath;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ----------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
 | 
			
		||||
@ -387,15 +387,7 @@ typedef void (*mio_svc_dnc_on_resolve_t) (
 | 
			
		||||
	mio_oow_t      len
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
#if defined(MIO_HAVE_INLINE)
 | 
			
		||||
static MIO_INLINE mio_t* mio_svc_dns_getmio(mio_svc_dns_t* svc) { return mio_svc_getmio((mio_svc_t*)svc); }
 | 
			
		||||
static MIO_INLINE mio_t* mio_svc_dnc_getmio(mio_svc_dnc_t* svc) { return mio_svc_getmio((mio_svc_t*)svc); }
 | 
			
		||||
static MIO_INLINE mio_t* mio_svc_dnr_getmio(mio_svc_dnr_t* svc) { return mio_svc_getmio((mio_svc_t*)svc); }
 | 
			
		||||
#else
 | 
			
		||||
#	define mio_svc_dns_getmio(svc) mio_svc_getmio(svc)
 | 
			
		||||
#	define mio_svc_dnc_getmio(svc) mio_svc_getmio(svc)
 | 
			
		||||
#	define mio_svc_dnr_getmio(svc) mio_svc_getmio(svc)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
enum mio_svc_dnc_send_flag_t
 | 
			
		||||
{
 | 
			
		||||
@ -476,6 +468,16 @@ MIO_EXPORT void mio_svc_dnc_stop (
 | 
			
		||||
	mio_svc_dnc_t* dnc
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
#if defined(MIO_HAVE_INLINE)
 | 
			
		||||
static MIO_INLINE mio_t* mio_svc_dns_getmio(mio_svc_dns_t* svc) { return mio_svc_getmio((mio_svc_t*)svc); }
 | 
			
		||||
static MIO_INLINE mio_t* mio_svc_dnc_getmio(mio_svc_dnc_t* svc) { return mio_svc_getmio((mio_svc_t*)svc); }
 | 
			
		||||
static MIO_INLINE mio_t* mio_svc_dnr_getmio(mio_svc_dnr_t* svc) { return mio_svc_getmio((mio_svc_t*)svc); }
 | 
			
		||||
#else
 | 
			
		||||
#	define mio_svc_dns_getmio(svc) mio_svc_getmio(svc)
 | 
			
		||||
#	define mio_svc_dnc_getmio(svc) mio_svc_getmio(svc)
 | 
			
		||||
#	define mio_svc_dnr_getmio(svc) mio_svc_getmio(svc)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT mio_dns_msg_t* mio_svc_dnc_sendmsg (
 | 
			
		||||
	mio_svc_dnc_t*         dnc,
 | 
			
		||||
	mio_dns_bhdr_t*        bdns,
 | 
			
		||||
 | 
			
		||||
@ -55,11 +55,10 @@ enum mio_htrd_option_t
 | 
			
		||||
	MIO_HTRD_SKIPEMPTYLINES  = (1 << 0), /**< skip leading empty lines before the initial line */
 | 
			
		||||
	MIO_HTRD_SKIPINITIALLINE = (1 << 1), /**< skip processing an initial line */
 | 
			
		||||
	MIO_HTRD_CANONQPATH      = (1 << 2), /**< canonicalize the query path */
 | 
			
		||||
	MIO_HTRD_PEEKONLY        = (1 << 3), /**< trigger a peek callback after headers without processing contents */
 | 
			
		||||
	MIO_HTRD_REQUEST         = (1 << 4), /**< parse input as a request */
 | 
			
		||||
	MIO_HTRD_RESPONSE        = (1 << 5), /**< parse input as a response */
 | 
			
		||||
	MIO_HTRD_TRAILERS        = (1 << 6), /**< store trailers in a separate table */
 | 
			
		||||
	MIO_HTRD_STRICT          = (1 << 7)  /**< be more picky */
 | 
			
		||||
	MIO_HTRD_REQUEST         = (1 << 3), /**< parse input as a request */
 | 
			
		||||
	MIO_HTRD_RESPONSE        = (1 << 4), /**< parse input as a response */
 | 
			
		||||
	MIO_HTRD_TRAILERS        = (1 << 5), /**< store trailers in a separate table */
 | 
			
		||||
	MIO_HTRD_STRICT          = (1 << 6)  /**< be more picky */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef enum mio_htrd_option_t mio_htrd_option_t;
 | 
			
		||||
 | 
			
		||||
@ -27,8 +27,74 @@
 | 
			
		||||
#ifndef _MIO_HTRE_H_
 | 
			
		||||
#define _MIO_HTRE_H_
 | 
			
		||||
 | 
			
		||||
#include <mio-http.h>
 | 
			
		||||
#include <mio-htb.h>
 | 
			
		||||
#include <mio-ecs.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The mio_http_version_t type defines http version.
 | 
			
		||||
 */
 | 
			
		||||
struct mio_http_version_t
 | 
			
		||||
{
 | 
			
		||||
	short major; /**< major version */
 | 
			
		||||
	short minor; /**< minor version */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct mio_http_version_t mio_http_version_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The mio_http_method_t type defines http methods .
 | 
			
		||||
 */
 | 
			
		||||
enum mio_http_method_t
 | 
			
		||||
{
 | 
			
		||||
	MIO_HTTP_OTHER,
 | 
			
		||||
 | 
			
		||||
	/* rfc 2616 */
 | 
			
		||||
	MIO_HTTP_HEAD,
 | 
			
		||||
	MIO_HTTP_GET,
 | 
			
		||||
	MIO_HTTP_POST,
 | 
			
		||||
	MIO_HTTP_PUT,
 | 
			
		||||
	MIO_HTTP_DELETE,
 | 
			
		||||
	MIO_HTTP_OPTIONS,
 | 
			
		||||
	MIO_HTTP_TRACE,
 | 
			
		||||
	MIO_HTTP_CONNECT
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	/* rfc 2518 */
 | 
			
		||||
	MIO_HTTP_PROPFIND,
 | 
			
		||||
	MIO_HTTP_PROPPATCH,
 | 
			
		||||
	MIO_HTTP_MKCOL,
 | 
			
		||||
	MIO_HTTP_COPY,
 | 
			
		||||
	MIO_HTTP_MOVE,
 | 
			
		||||
	MIO_HTTP_LOCK,
 | 
			
		||||
	MIO_HTTP_UNLOCK,
 | 
			
		||||
 | 
			
		||||
	/* rfc 3253 */
 | 
			
		||||
	MIO_HTTP_VERSION_CONTROL,
 | 
			
		||||
	MIO_HTTP_REPORT,
 | 
			
		||||
	MIO_HTTP_CHECKOUT,
 | 
			
		||||
	MIO_HTTP_CHECKIN,
 | 
			
		||||
	MIO_HTTP_UNCHECKOUT,
 | 
			
		||||
	MIO_HTTP_MKWORKSPACE,
 | 
			
		||||
	MIO_HTTP_UPDATE,
 | 
			
		||||
	MIO_HTTP_LABEL,
 | 
			
		||||
	MIO_HTTP_MERGE,
 | 
			
		||||
	MIO_HTTP_BASELINE_CONTROL,
 | 
			
		||||
	MIO_HTTP_MKACTIVITY,
 | 
			
		||||
	
 | 
			
		||||
	/* microsoft */
 | 
			
		||||
	MIO_HTTP_BPROPFIND,
 | 
			
		||||
	MIO_HTTP_BPROPPATCH,
 | 
			
		||||
	MIO_HTTP_BCOPY,
 | 
			
		||||
	MIO_HTTP_BDELETE,
 | 
			
		||||
	MIO_HTTP_BMOVE,
 | 
			
		||||
	MIO_HTTP_NOTIFY,
 | 
			
		||||
	MIO_HTTP_POLL,
 | 
			
		||||
	MIO_HTTP_SUBSCRIBE,
 | 
			
		||||
	MIO_HTTP_UNSUBSCRIBE,
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef enum mio_http_method_t mio_http_method_t;
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * You should not manipulate an object of the #mio_htre_t 
 | 
			
		||||
@ -37,7 +103,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* header and contents of request/response */
 | 
			
		||||
/*typedef struct mio_htre_t mio_htre_t; <--- defined in mio-http.h TODO: remove recursive definition */
 | 
			
		||||
typedef struct mio_htre_t mio_htre_t;
 | 
			
		||||
typedef struct mio_htre_hdrval_t mio_htre_hdrval_t;
 | 
			
		||||
 | 
			
		||||
enum mio_htre_state_t
 | 
			
		||||
 | 
			
		||||
@ -27,77 +27,12 @@
 | 
			
		||||
 | 
			
		||||
#include <mio-ecs.h>
 | 
			
		||||
#include <mio-sck.h>
 | 
			
		||||
#include <mio-htre.h>
 | 
			
		||||
 | 
			
		||||
/** \file
 | 
			
		||||
 * This file provides basic data types and functions for the http protocol.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The mio_http_version_t type defines http version.
 | 
			
		||||
 */
 | 
			
		||||
struct mio_http_version_t
 | 
			
		||||
{
 | 
			
		||||
	short major; /**< major version */
 | 
			
		||||
	short minor; /**< minor version */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct mio_http_version_t mio_http_version_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The mio_http_method_t type defines http methods .
 | 
			
		||||
 */
 | 
			
		||||
enum mio_http_method_t
 | 
			
		||||
{
 | 
			
		||||
	MIO_HTTP_OTHER,
 | 
			
		||||
 | 
			
		||||
	/* rfc 2616 */
 | 
			
		||||
	MIO_HTTP_HEAD,
 | 
			
		||||
	MIO_HTTP_GET,
 | 
			
		||||
	MIO_HTTP_POST,
 | 
			
		||||
	MIO_HTTP_PUT,
 | 
			
		||||
	MIO_HTTP_DELETE,
 | 
			
		||||
	MIO_HTTP_OPTIONS,
 | 
			
		||||
	MIO_HTTP_TRACE,
 | 
			
		||||
	MIO_HTTP_CONNECT
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	/* rfc 2518 */
 | 
			
		||||
	MIO_HTTP_PROPFIND,
 | 
			
		||||
	MIO_HTTP_PROPPATCH,
 | 
			
		||||
	MIO_HTTP_MKCOL,
 | 
			
		||||
	MIO_HTTP_COPY,
 | 
			
		||||
	MIO_HTTP_MOVE,
 | 
			
		||||
	MIO_HTTP_LOCK,
 | 
			
		||||
	MIO_HTTP_UNLOCK,
 | 
			
		||||
 | 
			
		||||
	/* rfc 3253 */
 | 
			
		||||
	MIO_HTTP_VERSION_CONTROL,
 | 
			
		||||
	MIO_HTTP_REPORT,
 | 
			
		||||
	MIO_HTTP_CHECKOUT,
 | 
			
		||||
	MIO_HTTP_CHECKIN,
 | 
			
		||||
	MIO_HTTP_UNCHECKOUT,
 | 
			
		||||
	MIO_HTTP_MKWORKSPACE,
 | 
			
		||||
	MIO_HTTP_UPDATE,
 | 
			
		||||
	MIO_HTTP_LABEL,
 | 
			
		||||
	MIO_HTTP_MERGE,
 | 
			
		||||
	MIO_HTTP_BASELINE_CONTROL,
 | 
			
		||||
	MIO_HTTP_MKACTIVITY,
 | 
			
		||||
	
 | 
			
		||||
	/* microsoft */
 | 
			
		||||
	MIO_HTTP_BPROPFIND,
 | 
			
		||||
	MIO_HTTP_BPROPPATCH,
 | 
			
		||||
	MIO_HTTP_BCOPY,
 | 
			
		||||
	MIO_HTTP_BDELETE,
 | 
			
		||||
	MIO_HTTP_BMOVE,
 | 
			
		||||
	MIO_HTTP_NOTIFY,
 | 
			
		||||
	MIO_HTTP_POLL,
 | 
			
		||||
	MIO_HTTP_SUBSCRIBE,
 | 
			
		||||
	MIO_HTTP_UNSUBSCRIBE,
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef enum mio_http_method_t mio_http_method_t;
 | 
			
		||||
 | 
			
		||||
/** 
 | 
			
		||||
 * The #mio_http_range_int_t type defines an integer that can represent
 | 
			
		||||
 * a range offset. Depening on the size of #mio_foff_t, it is defined to
 | 
			
		||||
@ -155,7 +90,6 @@ typedef enum mio_perenc_http_opt_t mio_perenc_bcstr_opt_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* -------------------------------------------------------------- */
 | 
			
		||||
typedef struct mio_htre_t mio_htre_t;
 | 
			
		||||
typedef struct mio_svc_htts_t mio_svc_htts_t;
 | 
			
		||||
typedef struct mio_svc_httc_t mio_svc_httc_t;
 | 
			
		||||
 | 
			
		||||
@ -180,6 +114,14 @@ struct mio_svc_htts_rsrc_t
 | 
			
		||||
 | 
			
		||||
#define MIO_SVC_HTTS_RSRC_ATTACH(rsrc, var) do { (var) = (rsrc); ++(rsrc)->rsrc_refcnt; } while(0)
 | 
			
		||||
#define MIO_SVC_HTTS_RSRC_DETACH(rsrc_var) do { if (--(rsrc_var)->rsrc_refcnt == 0) { mio_svc_htts_rsrc_t* __rsrc_tmp = (rsrc_var); (rsrc_var) = MIO_NULL; mio_svc_htts_rsrc_kill(__rsrc_tmp); } else { (rsrc_var) = MIO_NULL; } } while(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef int (*mio_svc_htts_proc_req_t) (
 | 
			
		||||
	mio_svc_htts_t* htts,
 | 
			
		||||
	mio_dev_sck_t*  sck,
 | 
			
		||||
	mio_htre_t*     req
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
/* -------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
#if defined(__cplusplus)
 | 
			
		||||
@ -279,42 +221,32 @@ MIO_EXPORT mio_bch_t* mio_perenc_http_bcstrdup (
 | 
			
		||||
/* ------------------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT mio_svc_htts_t* mio_svc_htts_start (
 | 
			
		||||
	mio_t*            mio,
 | 
			
		||||
	const mio_skad_t* bind_addr
 | 
			
		||||
	mio_t*                   mio,
 | 
			
		||||
	const mio_skad_t*        bind_addr,
 | 
			
		||||
	mio_svc_htts_proc_req_t  proc_req
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT void mio_svc_htts_stop (
 | 
			
		||||
	mio_svc_htts_t* htts
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
#if defined(MIO_HAVE_INLINE)
 | 
			
		||||
static MIO_INLINE mio_t* mio_svc_htts_getmio(mio_svc_htts_t* svc) { return mio_svc_getmio((mio_svc_t*)svc); }
 | 
			
		||||
#else
 | 
			
		||||
#	define mio_svc_htts_getmio(svc) mio_svc_getmio(svc)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT int mio_svc_htts_setservernamewithbcstr (
 | 
			
		||||
	mio_svc_htts_t*  htts,
 | 
			
		||||
	const mio_bch_t* server_name
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT int mio_svc_htts_docgi (
 | 
			
		||||
	mio_svc_htts_t* htts,
 | 
			
		||||
	mio_dev_sck_t*  csck,
 | 
			
		||||
	mio_htre_t*     req,
 | 
			
		||||
	const mio_bch_t* docroot
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT int mio_svc_htts_sendfile (
 | 
			
		||||
	mio_svc_htts_t*           htts,
 | 
			
		||||
	mio_dev_sck_t*            csck,
 | 
			
		||||
	const mio_bch_t*          file_path,
 | 
			
		||||
	int                       status_code,
 | 
			
		||||
	mio_http_method_t         method,
 | 
			
		||||
	const mio_http_version_t* version,
 | 
			
		||||
	int                       keepalive
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT void mio_svc_htts_fmtgmtime (
 | 
			
		||||
	mio_svc_htts_t*           htts,
 | 
			
		||||
	const mio_ntime_t*        nt,
 | 
			
		||||
	mio_bch_t*                buf,
 | 
			
		||||
	mio_oow_t                 len
 | 
			
		||||
	mio_svc_htts_t*  htts,
 | 
			
		||||
	mio_dev_sck_t*   csck,
 | 
			
		||||
	mio_htre_t*      req,
 | 
			
		||||
	const mio_bch_t* docroot,
 | 
			
		||||
	const mio_bch_t* script
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT mio_svc_htts_rsrc_t* mio_svc_htts_rsrc_make (
 | 
			
		||||
@ -335,6 +267,12 @@ MIO_EXPORT void mio_svc_htts_fmtgmtime (
 | 
			
		||||
	mio_oow_t          len
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT mio_bch_t* mio_svc_htts_dupmergepaths (
 | 
			
		||||
	mio_svc_htts_t*    htts,
 | 
			
		||||
	const mio_bch_t*   base,
 | 
			
		||||
	const mio_bch_t*   path
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
#if defined(__cplusplus)
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -1776,7 +1776,6 @@ static int fmt_put_bchars_to_bch_buf (mio_fmtout_t* fmtout, const mio_bch_t* ptr
 | 
			
		||||
	return 1; /* success */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int fmt_put_uchars_to_bch_buf (mio_fmtout_t* fmtout, const mio_uch_t* ptr, mio_oow_t len)
 | 
			
		||||
{
 | 
			
		||||
	fmt_bch_buf_t* b = (fmt_bch_buf_t*)fmtout->ctx;
 | 
			
		||||
 | 
			
		||||
@ -1113,6 +1113,18 @@ MIO_EXPORT mio_bch_t* mio_dupbcstr (
 | 
			
		||||
	mio_oow_t*       bcslen /* [OUT] length */
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT mio_uch_t* mio_dupucstrs (
 | 
			
		||||
	mio_t*           mio,
 | 
			
		||||
	const mio_uch_t* ucs[],
 | 
			
		||||
	mio_oow_t*       ucslen
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
MIO_EXPORT mio_bch_t* mio_dupbcstrs (
 | 
			
		||||
	mio_t*           mio,
 | 
			
		||||
	const mio_bch_t* bcs[],
 | 
			
		||||
	mio_oow_t*       bcslen
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
#if defined(MIO_OOCH_IS_UCH)
 | 
			
		||||
#	define mio_dupoochars(mio,oocs,oocslen) mio_dupuchars(mio,oocs,oocslen)
 | 
			
		||||
#	define mio_dupoocstr(mio,oocs,oocslen) mio_dupucstr(mio,oocs,oocslen)
 | 
			
		||||
 | 
			
		||||
@ -1729,5 +1729,39 @@ mio_bch_t* mio_dupbcstr (mio_t* mio, const mio_bch_t* bcs, mio_oow_t* bcslen)
 | 
			
		||||
	return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mio_uch_t* mio_dupucstrs (mio_t* mio, const mio_uch_t* ucs[], mio_oow_t* ucslen)
 | 
			
		||||
{
 | 
			
		||||
	mio_uch_t* ptr;
 | 
			
		||||
	mio_oow_t len, i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0, len = 0; ucs[i]; i++) len += mio_count_ucstr(ucs[i]);
 | 
			
		||||
 | 
			
		||||
	ptr = (mio_uch_t*)mio_allocmem(mio, (len + 1) * MIO_SIZEOF(mio_uch_t));
 | 
			
		||||
	if (!ptr) return MIO_NULL;
 | 
			
		||||
 | 
			
		||||
	for (i = 0, len = 0; ucs[i]; i++) 
 | 
			
		||||
		len += mio_copy_ucstr_unlimited(&ptr[len], ucs[i]);
 | 
			
		||||
	ptr[len] = '\0';
 | 
			
		||||
 | 
			
		||||
	if (ucslen) *ucslen = len;
 | 
			
		||||
	return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mio_bch_t* mio_dupbcstrs (mio_t* mio, const mio_bch_t* bcs[], mio_oow_t* bcslen)
 | 
			
		||||
{
 | 
			
		||||
	mio_bch_t* ptr;
 | 
			
		||||
	mio_oow_t len, i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0, len = 0; bcs[i]; i++) len += mio_count_bcstr(bcs[i]);
 | 
			
		||||
 | 
			
		||||
	ptr = (mio_bch_t*)mio_allocmem(mio, (len + 1) * MIO_SIZEOF(mio_bch_t));
 | 
			
		||||
	if (!ptr) return MIO_NULL;
 | 
			
		||||
 | 
			
		||||
	for (i = 0, len = 0; bcs[i]; i++) 
 | 
			
		||||
		len += mio_copy_bcstr_unlimited(&ptr[len], bcs[i]);
 | 
			
		||||
	ptr[len] = '\0';
 | 
			
		||||
 | 
			
		||||
	if (bcslen) *bcslen = len;
 | 
			
		||||
	return ptr;
 | 
			
		||||
}
 | 
			
		||||
/* ========================================================================= */
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user