added reply handler
This commit is contained in:
		
							
								
								
									
										666
									
								
								hcl/lib/hcl-c.c
									
									
									
									
									
								
							
							
						
						
									
										666
									
								
								hcl/lib/hcl-c.c
									
									
									
									
									
								
							| @ -31,9 +31,11 @@ | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #define HCL_SERVER_TOKEN_NAME_ALIGN 64 | ||||
| #define HCL_SERVER_WID_MAP_ALIGN 512 | ||||
| #define HCL_SERVER_PROTO_REPLY_BUF_SIZE 1300 | ||||
| #define HCL_CLIENT_TOKEN_NAME_ALIGN 64 | ||||
| #define HCL_CLIENT_WID_MAP_ALIGN 512 | ||||
| #define HCL_CLIENT_PROTO_REPLY_BUF_SIZE 1300 | ||||
|  | ||||
| #define HCL_CLIENT_REPLY_MAX_HDRKEY_LEN 128 | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| #	include <windows.h> | ||||
| @ -78,12 +80,18 @@ | ||||
| #	include <poll.h> | ||||
| #endif | ||||
|  | ||||
| enum hcl_client_reply_type_t | ||||
| struct dummy_hcl_xtn_t | ||||
| { | ||||
| 	HCL_CLIENT_REPLY_TYPE_OK = 0, | ||||
| 	HCL_CLIENT_REPLY_TYPE_ERROR = 1 | ||||
| 	hcl_client_t* client; | ||||
| }; | ||||
| typedef enum hcl_client_reply_type_t hcl_client_reply_type_t; | ||||
| typedef struct dummy_hcl_xtn_t dummy_hcl_xtn_t; | ||||
|  | ||||
| enum hcl_client_reply_attr_type_t | ||||
| { | ||||
| 	HCL_CLIENT_REPLY_ATTR_TYPE_UNKNOWN, | ||||
| 	HCL_CLIENT_REPLY_ATTR_TYPE_DATA | ||||
| }; | ||||
| typedef enum hcl_client_reply_attr_type_t hcl_client_reply_attr_type_t; | ||||
|  | ||||
| enum hcl_client_state_t | ||||
| { | ||||
| @ -93,11 +101,14 @@ enum hcl_client_state_t | ||||
| 	HCL_CLIENT_STATE_IN_REPLY_VALUE_UNQUOTED, | ||||
| 	HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED, | ||||
| 	HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED_TRAILER, | ||||
| 	HCL_CLIENT_STATE_IN_HEADER_NAME, | ||||
| 	HCL_CLIENT_STATE_IN_HEADER_VALUE, | ||||
| 	HCL_CLIENT_STATE_IN_DATA, | ||||
| 	HCL_CLIENT_STATE_IN_BINARY_DATA, | ||||
| 	HCL_CLIENT_STATE_IN_CHUNK, | ||||
| 	HCL_CLIENT_STATE_IN_ATTR_KEY, | ||||
| 	HCL_CLIENT_STATE_IN_ATTR_VALUE_START, | ||||
| 	HCL_CLIENT_STATE_IN_ATTR_VALUE_UNQUOTED, | ||||
| 	HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED, | ||||
| 	HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED_TRAILER, | ||||
|  | ||||
| 	HCL_CLIENT_STATE_IN_LENGTH_BOUNDED_DATA, | ||||
| 	HCL_CLIENT_STATE_IN_CHUNKED_DATA | ||||
| }; | ||||
| typedef enum hcl_client_state_t hcl_client_state_t; | ||||
|  | ||||
| @ -105,7 +116,8 @@ struct hcl_client_t | ||||
| { | ||||
| 	hcl_mmgr_t* mmgr; | ||||
| 	hcl_cmgr_t* cmgr; | ||||
| 	/*hcl_t* dummy_hcl;*/ | ||||
| 	hcl_client_prim_t prim; | ||||
| 	hcl_t* dummy_hcl; | ||||
|  | ||||
| 	hcl_errnum_t errnum; | ||||
| 	struct | ||||
| @ -119,12 +131,8 @@ struct hcl_client_t | ||||
| 	{ | ||||
| 		unsigned int trait; | ||||
| 		unsigned int logmask; | ||||
| 		hcl_oow_t worker_max_count; | ||||
| 		hcl_oow_t worker_stack_size; | ||||
| 		hcl_ntime_t worker_idle_timeout; | ||||
| 	} cfg; | ||||
|  | ||||
|  | ||||
| 	struct | ||||
| 	{ | ||||
| 		hcl_bch_t* ptr; | ||||
| @ -135,11 +143,6 @@ struct hcl_client_t | ||||
| 	hcl_client_state_t state; | ||||
| 	struct | ||||
| 	{ | ||||
| 		/* | ||||
| 		hcl_ooch_t* ptr; | ||||
| 		hcl_oow_t len; | ||||
| 		hcl_oow_t capa; | ||||
| 		*/ | ||||
| 		struct | ||||
| 		{ | ||||
| 			hcl_ooch_t* ptr; | ||||
| @ -148,6 +151,12 @@ struct hcl_client_t | ||||
| 		} tok; | ||||
|  | ||||
| 		hcl_client_reply_type_t type; | ||||
| 		hcl_client_reply_attr_type_t last_attr_type; | ||||
| 		struct | ||||
| 		{ | ||||
| 			hcl_ooch_t buf[HCL_CLIENT_REPLY_MAX_HDRKEY_LEN]; | ||||
| 			hcl_oow_t  len; | ||||
| 		} last_attr_key; /* the last attr key shown */ | ||||
|  | ||||
| 		union | ||||
| 		{ | ||||
| @ -160,12 +169,59 @@ struct hcl_client_t | ||||
| 			{ | ||||
| 				int escaped; | ||||
| 			} reply_value_quoted; | ||||
|  | ||||
| 			struct | ||||
| 			{ | ||||
| 				hcl_oow_t nsplen; /* length remembered when the white space was shown */ | ||||
| 			} attr_value_unquoted; | ||||
|  | ||||
| 			struct | ||||
| 			{ | ||||
| 				int escaped; | ||||
| 			} attr_value_quoted; | ||||
|  | ||||
| 			struct | ||||
| 			{ | ||||
| 				hcl_oow_t max; | ||||
| 				hcl_oow_t tally; | ||||
| 			} length_bounded_data; | ||||
|  | ||||
| 			struct | ||||
| 			{ | ||||
| 				int in_data_part; | ||||
| 				int negated; | ||||
| 				hcl_oow_t max; /* chunk length */ | ||||
| 				hcl_oow_t tally;  | ||||
| 				hcl_oow_t total; | ||||
| 				hcl_oow_t clcount;  | ||||
| 			} chunked_data; | ||||
| 		} u; | ||||
| 	} rep; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* ========================================================================= */ | ||||
|  | ||||
| static void log_write_for_dummy (hcl_t* hcl, unsigned int mask, const hcl_ooch_t* msg, hcl_oow_t len) | ||||
| { | ||||
| 	dummy_hcl_xtn_t* xtn = (dummy_hcl_xtn_t*)hcl_getxtn(hcl); | ||||
| 	hcl_client_t* client; | ||||
|  | ||||
| 	client = xtn->client; | ||||
| 	client->prim.log_write (client, mask, msg, len); | ||||
| } | ||||
|  | ||||
| static void syserrstrb (hcl_t* hcl, int syserr, hcl_bch_t* buf, hcl_oow_t len) | ||||
| { | ||||
| #if defined(HAVE_STRERROR_R) | ||||
| 	strerror_r (syserr, buf, len); | ||||
| #else | ||||
| 	/* this may not be thread safe */ | ||||
| 	hcl_copybcstr (buf, len, strerror(syserr)); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /* ========================================================================= */ | ||||
|  | ||||
| static HCL_INLINE int is_spacechar (hcl_bch_t c) | ||||
| { | ||||
| @ -220,23 +276,36 @@ static int add_to_reply_token (hcl_client_t* client, hcl_ooch_t ch) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void print_tok (hcl_client_t* client) | ||||
| static HCL_INLINE int is_token (hcl_client_t* client, const hcl_bch_t* str) | ||||
| { | ||||
| 	hcl_oow_t i; | ||||
| 	printf ("{"); | ||||
| 	for (i = 0; i < client->rep.tok.len; i++) | ||||
| 		printf ("%lc", client->rep.tok.ptr[i]); | ||||
| 	printf ("}\n"); | ||||
| 	return hcl_compoocharsbcstr(client->rep.tok.ptr, client->rep.tok.len, str) == 0; | ||||
| }  | ||||
|  | ||||
| static int handle_char (hcl_client_t* client, hcl_ooci_t c) | ||||
| static HCL_INLINE int is_token_integer (hcl_client_t* client, hcl_oow_t* value) | ||||
| { | ||||
| 	hcl_oow_t i; | ||||
| 	hcl_oow_t v = 0; | ||||
|  | ||||
| 	if (client->rep.tok.len <= 0) return 0; | ||||
|  | ||||
| 	for (i = 0; i < client->rep.tok.len; i++) | ||||
| 	{ | ||||
| 		if (!is_digitchar(client->rep.tok.ptr[i])) return 0; | ||||
| 		v = v * 10 + (client->rep.tok.ptr[i] - '0'); | ||||
| 	} | ||||
|  | ||||
| 	*value = v; | ||||
| 	return 1; | ||||
| }  | ||||
|  | ||||
| static int handle_char (hcl_client_t* client, hcl_ooci_t c, hcl_oow_t nbytes) | ||||
| { | ||||
| 	switch (client->state) | ||||
| 	{ | ||||
| 		case HCL_CLIENT_STATE_START: | ||||
| 			if (c == HCL_OOCI_EOF) | ||||
| 			{ | ||||
| 				printf ("unexpected end of reply\n"); | ||||
| 				hcl_client_seterrbfmt (client, HCL_EFINIS, "unexpected end before reply name"); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else if (c == '.') | ||||
| @ -253,8 +322,7 @@ static int handle_char (hcl_client_t* client, hcl_ooci_t c) | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				/* TODO: set error code? or log error messages? */ | ||||
| 				printf ("reply line not starting with .\n"); | ||||
| 				hcl_client_seterrbfmt (client, HCL_EINVAL, "reply line not starting with a period"); | ||||
| 				goto oops; | ||||
| 			} | ||||
|  | ||||
| @ -266,17 +334,17 @@ static int handle_char (hcl_client_t* client, hcl_ooci_t c) | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				if (hcl_compoocharsbcstr(client->rep.tok.ptr, client->rep.tok.len, ".OK") == 0) | ||||
| 				if (is_token(client, ".OK")) | ||||
| 				{ | ||||
| 					client->rep.type = HCL_CLIENT_REPLY_TYPE_OK; | ||||
| 				} | ||||
| 				else if (hcl_compoocharsbcstr(client->rep.tok.ptr, client->rep.tok.len, ".ERROR") == 0) | ||||
| 				else if (is_token(client, ".ERROR")) | ||||
| 				{ | ||||
| 					client->rep.type = HCL_CLIENT_REPLY_TYPE_ERROR; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					printf ("unknown reply name - [%.*ls]", (int)client->rep.tok.len, client->rep.tok.ptr); | ||||
| 					hcl_client_seterrbfmt (client, HCL_EINVAL, "unknown reply name %.*js", client->rep.tok.len, client->rep.tok.ptr); | ||||
| 					goto oops; | ||||
| 				} | ||||
|  | ||||
| @ -288,9 +356,7 @@ static int handle_char (hcl_client_t* client, hcl_ooci_t c) | ||||
| 		case HCL_CLIENT_STATE_IN_REPLY_VALUE_START: | ||||
| 			if (c == HCL_OOCI_EOF) | ||||
| 			{ | ||||
| 				/* sudden end of EOF without NL */ | ||||
| 				printf ("suddend enf of input without NL\n"); | ||||
| 				/* TOOD: call call back as it it's just a normal reply. and don't goto oops*/ | ||||
| 				hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of reply line without newline"); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else if (is_spacechar(c)) | ||||
| @ -300,16 +366,19 @@ static int handle_char (hcl_client_t* client, hcl_ooci_t c) | ||||
| 			} | ||||
| 			else if (c == '\n') | ||||
| 			{ | ||||
| 				/* no value is specified. switch to a long-format response */ | ||||
| 				client->state = HCL_CLIENT_STATE_IN_HEADER_NAME; | ||||
| 				/* no value is specified. even no whitespaces. | ||||
| 				 * switch to a long-format response */ | ||||
| 				client->prim.start_reply (client, client->rep.type, HCL_NULL, 0); | ||||
|  | ||||
| 				client->state = HCL_CLIENT_STATE_IN_ATTR_KEY; | ||||
| 				HCL_ASSERT (client->dummy_hcl, client->rep.tok.len == 0); | ||||
| 				client->rep.u.reply_value_quoted.escaped = 0; | ||||
| 				break; | ||||
| 			} | ||||
| 			else if (c == '\"') | ||||
| 			{ | ||||
| 				client->state = HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED; | ||||
| 				HCL_ASSERT (client->dummy_hcl, client->rep.tok.len == 0); | ||||
| 				client->rep.u.reply_value_quoted.escaped = 0; | ||||
| 				break; | ||||
| 			} | ||||
| 			else  | ||||
| @ -324,20 +393,13 @@ static int handle_char (hcl_client_t* client, hcl_ooci_t c) | ||||
| 		case HCL_CLIENT_STATE_IN_REPLY_VALUE_UNQUOTED: | ||||
| 			if (c == HCL_OOCI_EOF) | ||||
| 			{ | ||||
| 				/* sudden end of EOF without NL */ | ||||
| 				printf ("suddend enf of input without NL\n"); | ||||
| 				/* TOOD: call call back as it it's just a normal reply. and don't goto oops*/ | ||||
| 				hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of reply line without newline"); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else if (c == '\n') | ||||
| 			{ | ||||
| 				client->rep.tok.len = client->rep.u.reply_value_unquoted.nsplen; | ||||
| printf ("reply value....===>"); | ||||
| print_tok (client); | ||||
| /* TODO: call callback */ | ||||
| 				client->state = HCL_CLIENT_STATE_START; | ||||
| 				clear_reply_token (client); | ||||
| 				break; | ||||
| 				goto reply_value_end; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| @ -349,15 +411,16 @@ print_tok (client); | ||||
| 		case HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED: | ||||
| 			if (c == HCL_OOCI_EOF) | ||||
| 			{ | ||||
| 				/* sudden end of EOF without NL */ | ||||
| 				printf ("suddend enf of input witjpit closing quote. -> total failure\n"); | ||||
| 				hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of reply line without closing quote"); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else  | ||||
| 			{ | ||||
| 				if (client->rep.u.reply_value_quoted.escaped) | ||||
| 				{ | ||||
| 					if (c == 'n') c = '\n'; | ||||
| 					if (c == '\\') c = '\\'; | ||||
| 					else if (c == '\"') c = '\"'; | ||||
| 					else if (c == 'n') c = '\n'; | ||||
| 					else if (c == 'r') c = '\r'; | ||||
| 					/* TODO: more escaping handling */ | ||||
| 				} | ||||
| @ -380,13 +443,19 @@ print_tok (client); | ||||
| 		case HCL_CLIENT_STATE_IN_REPLY_VALUE_QUOTED_TRAILER: | ||||
| 			if (c == HCL_OOCI_EOF) | ||||
| 			{ | ||||
| 				printf ("suddend enf of input witjpit closing quote. -> total failure\n"); | ||||
| 				hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of reply line without newline"); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else if (c == '\n') | ||||
| 			{ | ||||
| printf ("reply value....===>"); | ||||
| print_tok (client); | ||||
| 			reply_value_end: | ||||
| 				/* short-form format. the data pointer is passed to the start_reply  | ||||
| 				 * callback. no end_reply callback is invoked. the data is assumed | ||||
| 				 * to be in UTF-8 encoding. this is different from the data in the | ||||
| 				 * long-format reply which is treated as octet stream */ | ||||
| 				client->prim.start_reply (client, client->rep.type, client->rep.tok.ptr, client->rep.tok.len); | ||||
| 				/* no end_reply() callback for the short-form reply */ | ||||
|  | ||||
| 				client->state = HCL_CLIENT_STATE_START; | ||||
| 				clear_reply_token (client); | ||||
| 				break; | ||||
| @ -398,14 +467,14 @@ print_tok (client); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				printf ("garbage after a quoted reply value [%lc]\n", c); | ||||
| 				hcl_client_seterrbfmt (client, HCL_EINVAL, "garbage after quoted reply value"); | ||||
| 				goto oops; | ||||
| 			} | ||||
|  | ||||
| 		case HCL_CLIENT_STATE_IN_HEADER_NAME: | ||||
| 		case HCL_CLIENT_STATE_IN_ATTR_KEY: | ||||
| 			if (c == HCL_OOCI_EOF) | ||||
| 			{ | ||||
| 				printf ("suddend end of input without a header name\n"); | ||||
| 				hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of attribute line"); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else if (client->rep.tok.len == 0) | ||||
| @ -417,7 +486,7 @@ print_tok (client); | ||||
| 				} | ||||
| 				else if (c != '.') | ||||
| 				{ | ||||
| 					printf ("header not starting with a period\n"); | ||||
| 					hcl_client_seterrbfmt (client, HCL_EINVAL, "attribute name not starting with a period"); | ||||
| 					goto oops; | ||||
| 				} | ||||
|  | ||||
| @ -426,58 +495,192 @@ print_tok (client); | ||||
| 			} | ||||
| 			else	if (is_alphachar(c) || (client->rep.tok.len > 2 && c == '-')) | ||||
| 			{ | ||||
| 				if (client->rep.tok.len >= HCL_CLIENT_REPLY_MAX_HDRKEY_LEN) | ||||
| 				{ | ||||
| 					hcl_client_seterrbfmt (client, HCL_EINVAL, "attribute name too long"); | ||||
| 					goto oops; | ||||
| 				} | ||||
| 				if (add_to_reply_token(client, c) <= -1) goto oops; | ||||
| 				break; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				printf ("JKJJJJJJJJJ=>\n"); | ||||
| 				print_tok(client); | ||||
| 				if (hcl_compoocharsbcstr(client->rep.tok.ptr, client->rep.tok.len, ".ENCODING") == 0) | ||||
| 				if (is_token(client, ".DATA")) | ||||
| 				{ | ||||
| 					printf ("encoding....\n"); | ||||
| 				} | ||||
| 				else if (hcl_compoocharsbcstr(client->rep.tok.ptr, client->rep.tok.len, ".LENGTH") == 0) | ||||
| 				{ | ||||
| 					printf ("length....\n"); | ||||
| 				} | ||||
| 				else if (hcl_compoocharsbcstr(client->rep.tok.ptr, client->rep.tok.len, ".DATA") == 0) | ||||
| 				{ | ||||
| 					printf ("data....\n"); | ||||
| 					client->rep.last_attr_type = HCL_CLIENT_REPLY_ATTR_TYPE_DATA; | ||||
| 				} | ||||
| 				/* PUT more known attribute handling here */ | ||||
| 				else | ||||
| 				{ | ||||
| 					printf ("unknown header ---> [%.*ls]\n", (int)client->rep.tok.len, client->rep.tok.ptr); | ||||
| 					goto oops; | ||||
| 					client->rep.last_attr_type = HCL_CLIENT_REPLY_ATTR_TYPE_UNKNOWN; | ||||
| 				} | ||||
|  | ||||
| 				client->state = HCL_CLIENT_STATE_IN_HEADER_VALUE; | ||||
| 				HCL_ASSERT (client->dummy_hcl, client->rep.tok.len <= HCL_COUNTOF(client->rep.last_attr_key.buf)); | ||||
| 				hcl_copyoochars (client->rep.last_attr_key.buf, client->rep.tok.ptr, client->rep.tok.len); | ||||
| 				client->rep.last_attr_key.len = client->rep.tok.len; | ||||
|  | ||||
| 				client->state = HCL_CLIENT_STATE_IN_ATTR_VALUE_START; | ||||
| 				clear_reply_token (client); | ||||
| 				/* [IMPORTANT] fall thru */ | ||||
| 			} | ||||
|  | ||||
| /* | ||||
| echo -n -e '.OK"YOYO   bc\n\" abc"    \n    .ERROR             "XYZ"\n.OK\n.ENCODING chunked\n' |~/xxx/bin/hclc 11111 | ||||
|  */ | ||||
|  | ||||
| 		case HCL_CLIENT_STATE_IN_HEADER_VALUE: | ||||
| 		case HCL_CLIENT_STATE_IN_ATTR_VALUE_START: | ||||
| 			if (c == HCL_OOCI_EOF) | ||||
| 			{ | ||||
| 				printf ("sudden end of input whthout header value\n"); | ||||
| 				hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end without attribute value"); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else if (is_spacechar(c)) | ||||
| 			{ | ||||
| 				/* do nothing. skip it */ | ||||
| 				break; | ||||
| 			} | ||||
| 			else if (c == '\n') | ||||
| 			{ | ||||
| 				printf ("XXXXXXXXXXXXXXxx\n"); | ||||
| 				hcl_client_seterrbfmt (client, HCL_EINVAL, "no attribute value for %.*js\n", client->rep.last_attr_key.len, client->rep.last_attr_key.buf); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else if (c == '\"') | ||||
| 			{ | ||||
| 				client->state = HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED; | ||||
| 				HCL_ASSERT (client->dummy_hcl, client->rep.tok.len == 0); | ||||
| 				client->rep.u.attr_value_quoted.escaped = 0; | ||||
| 				break; | ||||
| 			} | ||||
| 			else  | ||||
| 			{ | ||||
| 				printf ("add to header value....\n"); | ||||
| 				/* the first value character has been encountered */ | ||||
| 				client->state = HCL_CLIENT_STATE_IN_ATTR_VALUE_UNQUOTED; | ||||
| 				HCL_ASSERT (client->dummy_hcl, client->rep.tok.len == 0); | ||||
| 				client->rep.u.attr_value_unquoted.nsplen = 0; | ||||
| 				/* [IMPORTANT] fall thru */ | ||||
| 			} | ||||
|  | ||||
| 		case HCL_CLIENT_STATE_IN_ATTR_VALUE_UNQUOTED: | ||||
| 			if (c == HCL_OOCI_EOF) | ||||
| 			{ | ||||
| 				hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of attribute line without newline"); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else if (c == '\n') | ||||
| 			{ | ||||
| 				client->rep.tok.len = client->rep.u.attr_value_unquoted.nsplen; | ||||
| 				goto attr_value_end; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				if (add_to_reply_token(client, c) <= -1) goto oops; | ||||
| 				if (!is_spacechar(c)) client->rep.u.attr_value_unquoted.nsplen = client->rep.tok.len; | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			break; | ||||
| 		case HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED: | ||||
| 			if (c == HCL_OOCI_EOF) | ||||
| 			{ | ||||
| 				hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of attribute value without closing quote"); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else  | ||||
| 			{ | ||||
| 				if (client->rep.u.attr_value_quoted.escaped) | ||||
| 				{ | ||||
| 					if (c == '\\') c = '\\'; | ||||
| 					else if (c == '\"') c = '\"'; | ||||
| 					else if (c == 'n') c = '\n'; | ||||
| 					else if (c == 'r') c = '\r'; | ||||
| 					/* TODO: more escaping handling */ | ||||
| 				} | ||||
| 				else if (c == '\\') | ||||
| 				{ | ||||
| 					client->rep.u.attr_value_quoted.escaped = 1; | ||||
| 					break; | ||||
| 				} | ||||
| 				else if (c == '\"') | ||||
| 				{ | ||||
| 					client->state = HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED_TRAILER; | ||||
| 					break; | ||||
| 				} | ||||
|  | ||||
| 				client->rep.u.attr_value_quoted.escaped = 0; | ||||
| 				if (add_to_reply_token(client, c) <= -1) goto oops; | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 		case HCL_CLIENT_STATE_IN_ATTR_VALUE_QUOTED_TRAILER: | ||||
| 			if (c == HCL_OOCI_EOF) | ||||
| 			{ | ||||
| 				hcl_client_seterrbfmt (client, HCL_EFINIS, "sudden end of attribute line without newline"); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 			else if (c == '\n') | ||||
| 			{ | ||||
| 			attr_value_end: | ||||
| 				if (client->prim.feed_attr) | ||||
| 				{ | ||||
| 					hcl_oocs_t key, val; | ||||
| 					key.ptr = client->rep.last_attr_key.buf; | ||||
| 					key.len = client->rep.last_attr_key.len; | ||||
| 					val.ptr = client->rep.tok.ptr; | ||||
| 					val.len = client->rep.tok.len; | ||||
| 					client->prim.feed_attr (client, &key, &val); | ||||
| 				} | ||||
|  | ||||
| 				if (client->rep.last_attr_type == HCL_CLIENT_REPLY_ATTR_TYPE_DATA) | ||||
| 				{ | ||||
| 					hcl_oow_t length; | ||||
|  | ||||
| 					/* this must be the last attr. the trailing part are all data */ | ||||
| 					if (is_token(client, "chunked")) | ||||
| 					{ | ||||
| 						client->state = HCL_CLIENT_STATE_IN_CHUNKED_DATA; | ||||
| 						HCL_MEMSET (&client->rep.u.chunked_data, 0, HCL_SIZEOF(client->rep.u.chunked_data)); | ||||
| 					} | ||||
| 					else if (is_token_integer(client, &length)) | ||||
| 					{ | ||||
| 						if (length > 0) | ||||
| 						{ | ||||
| 							client->state = HCL_CLIENT_STATE_IN_LENGTH_BOUNDED_DATA; | ||||
| 							/* [NOTE] the max length for the length-bounded transfer scheme is limited | ||||
| 							 *        by the system word size as of this implementation */ | ||||
| 							client->rep.u.length_bounded_data.max = length;  | ||||
| 							client->rep.u.length_bounded_data.tally = 0; | ||||
| 						} | ||||
| 						else | ||||
| 						{ | ||||
| 							/* .DATA 0 has been received. this should be end of the reply */ | ||||
| 							client->state = HCL_CLIENT_STATE_START; | ||||
| 						} | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						hcl_client_seterrbfmt (client, HCL_EINVAL, "invalid attribute value for .DATA - %.*js", client->rep.tok.len, client->rep.tok.ptr); | ||||
| 						goto oops; | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					/* continue processing the next attr */ | ||||
| 					client->state = HCL_CLIENT_STATE_IN_ATTR_KEY; | ||||
| 				} | ||||
|  | ||||
| 				clear_reply_token (client); | ||||
| 				break; | ||||
| 			} | ||||
| 			else if (is_spacechar(c)) | ||||
| 			{ | ||||
| 				/* skip white spaces after the closing quote */ | ||||
| 				break; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				hcl_client_seterrbfmt (client, HCL_EINVAL, "garbage after quoted attribute value for %.*js", client->rep.last_attr_key.len, client->rep.last_attr_key.buf); | ||||
| 				goto oops; | ||||
| 			} | ||||
|  | ||||
| 		default: | ||||
| 			/* this function must not be called for .DATA */ | ||||
| 			hcl_client_seterrbfmt (client, HCL_EINTERN, "internal error - must not be called for state %d", client->state); | ||||
| 			goto oops; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| @ -494,18 +697,118 @@ static int feed_reply_data (hcl_client_t* client, const hcl_bch_t* data, hcl_oow | ||||
| 	ptr = data; | ||||
| 	end = ptr + len; | ||||
|  | ||||
| //printf ("<<<%.*s>>>\n", (int)len, data); | ||||
| 	if (client->state == HCL_CLIENT_STATE_IN_BINARY_DATA) | ||||
| 	while (ptr < end) | ||||
| 	{ | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		while (ptr < end) | ||||
| 		if (client->state == HCL_CLIENT_STATE_IN_LENGTH_BOUNDED_DATA) | ||||
| 		{ | ||||
| 			/* the data is treated as raw octets by this client */ | ||||
| 			hcl_oow_t capa, avail, taken; | ||||
|  | ||||
| 			capa = client->rep.u.length_bounded_data.max - client->rep.u.length_bounded_data.tally; | ||||
| 			avail = end - ptr; | ||||
| 			taken = (avail < capa)? avail: capa; | ||||
|  | ||||
| 			if (client->prim.feed_data) | ||||
| 			{ | ||||
| 				client->prim.feed_data (client, ptr, taken); | ||||
| 			} | ||||
|  | ||||
| 			ptr += taken; | ||||
| 			client->rep.u.length_bounded_data.tally = taken; | ||||
| 			if (taken == capa) | ||||
| 			{ | ||||
| 				/* read all data. no more */ | ||||
| 				client->state = HCL_CLIENT_STATE_START; | ||||
| 				client->prim.end_reply (client, HCL_CLIENT_END_REPLY_STATE_OK); | ||||
| 			} | ||||
| 		} | ||||
| 		else if (client->state == HCL_CLIENT_STATE_IN_CHUNKED_DATA) | ||||
| 		{ | ||||
| 			/* the data is treated as raw octets by this client */ | ||||
| 			if (client->rep.u.chunked_data.in_data_part) | ||||
| 			{ | ||||
| 				hcl_oow_t capa, avail, taken; | ||||
|  | ||||
| 				capa = client->rep.u.chunked_data.max - client->rep.u.chunked_data.tally; | ||||
| 				avail = end - ptr; | ||||
| 				taken = (avail < capa)? avail: capa; | ||||
|  | ||||
| 				if (client->prim.feed_data) | ||||
| 				{ | ||||
| 					client->prim.feed_data (client, ptr, taken); | ||||
| 				} | ||||
|  | ||||
| 				ptr += taken; | ||||
| 				client->rep.u.chunked_data.tally += taken; | ||||
| 				client->rep.u.chunked_data.total += taken; | ||||
| 				if (taken == capa) | ||||
| 				{ | ||||
| 					/* finished one chunk */ | ||||
| 					client->rep.u.chunked_data.negated = 0; | ||||
| 					client->rep.u.chunked_data.max = 0; | ||||
| 					client->rep.u.chunked_data.tally = 0; | ||||
| 					client->rep.u.chunked_data.clcount = 0; | ||||
| 					client->rep.u.chunked_data.in_data_part = 0; | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				while (ptr < end) | ||||
| 				{ | ||||
| 					hcl_bchu_t bc = (hcl_bchu_t)*ptr; | ||||
| 					if (bc == '-' && client->rep.u.chunked_data.clcount == 0 && !client->rep.u.chunked_data.negated) | ||||
| 					{ | ||||
| 						client->rep.u.chunked_data.negated = 1; | ||||
| 					} | ||||
| 					else if (bc == ':')  | ||||
| 					{ | ||||
| 						ptr++; | ||||
|  | ||||
| 						if (client->rep.u.chunked_data.clcount == 0) | ||||
| 						{ | ||||
| 							hcl_client_seterrbfmt (client, HCL_EINVAL, "clone without valid chunk length"); | ||||
| 							goto oops; | ||||
| 						} | ||||
|  | ||||
| 						if (client->rep.u.chunked_data.negated) | ||||
| 						{ | ||||
| 							client->prim.end_reply (client, HCL_CLIENT_END_REPLY_STATE_REVOKED); | ||||
| 							client->state = HCL_CLIENT_STATE_START; | ||||
|  | ||||
| 						} | ||||
| 						if (client->rep.u.chunked_data.max == 0) | ||||
| 						{ | ||||
| 							client->prim.end_reply (client, HCL_CLIENT_END_REPLY_STATE_OK); | ||||
| 							client->state = HCL_CLIENT_STATE_START; | ||||
| 						} | ||||
| 						else | ||||
| 						{ | ||||
| 							client->rep.u.chunked_data.in_data_part = 1; | ||||
| 							client->rep.u.chunked_data.tally = 0; | ||||
| 						} | ||||
| 						break; | ||||
| 					} | ||||
| 					else if (is_digitchar(bc))  | ||||
| 					{ | ||||
| 						client->rep.u.chunked_data.max = client->rep.u.chunked_data.max * 10 + (bc - '0'); | ||||
| 						client->rep.u.chunked_data.clcount++; | ||||
| 						ptr++; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						hcl_client_seterrbfmt (client, HCL_EINVAL, "invalid chunk length character  - code[%#x]", bc); | ||||
| 						goto oops; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			hcl_ooci_t c; | ||||
| 			hcl_oow_t bcslen; | ||||
|  | ||||
| 	#if defined(HCL_OOCH_IS_UCH) | ||||
| 			hcl_oow_t bcslen, ucslen; | ||||
| 			hcl_oow_t ucslen; | ||||
| 			hcl_ooch_t uc; | ||||
| 			int n; | ||||
|  | ||||
| @ -518,7 +821,6 @@ static int feed_reply_data (hcl_client_t* client, const hcl_bch_t* data, hcl_oow | ||||
| 				if (n == -3) | ||||
| 				{ | ||||
| 					/* incomplete sequence */ | ||||
| 					printf ("incompelete....feed me more\n"); | ||||
| 					*xlen = ptr - data; | ||||
| 					return 0; /* feed more for incomplete sequence */ | ||||
| 				} | ||||
| @ -527,11 +829,12 @@ static int feed_reply_data (hcl_client_t* client, const hcl_bch_t* data, hcl_oow | ||||
| 			ptr += bcslen; | ||||
| 			c = uc; | ||||
| 	#else | ||||
| 			bcslen = 1; | ||||
| 			c = *ptr++; | ||||
| 	#endif | ||||
|  | ||||
| printf ("[%lc]\n", c); | ||||
| 			if (handle_char(client, c) <= -1) goto oops; | ||||
| //printf ("[%lc]\n", c); | ||||
| 			if (handle_char(client, c, bcslen) <= -1) goto oops; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -540,7 +843,7 @@ printf ("[%lc]\n", c); | ||||
|  | ||||
| oops: | ||||
| 	/* TODO: compute the number of processes bytes so far and return it via a parameter??? */ | ||||
| printf ("feed oops....\n"); | ||||
| /*printf ("feed oops....\n");*/ | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| @ -571,14 +874,10 @@ static int feed_all_reply_data (hcl_client_t* client, hcl_bch_t* buf, hcl_oow_t | ||||
| hcl_client_t* hcl_client_open (hcl_mmgr_t* mmgr, hcl_oow_t xtnsize, hcl_client_prim_t* prim, hcl_errnum_t* errnum) | ||||
| { | ||||
| 	hcl_client_t* client; | ||||
| #if 0 | ||||
| 	hcl_t* hcl; | ||||
| 	hcl_vmprim_t vmprim; | ||||
| 	hcl_tmr_t* tmr; | ||||
| 	dummy_hcl_xtn_t* xtn; | ||||
| 	int pfd[2], fcv; | ||||
| 	unsigned int trait; | ||||
| #endif | ||||
| /*	unsigned int trait;*/ | ||||
|  | ||||
| 	client = (hcl_client_t*)HCL_MMGR_ALLOC(mmgr, HCL_SIZEOF(*client) + xtnsize); | ||||
| 	if (!client)  | ||||
| @ -587,15 +886,9 @@ hcl_client_t* hcl_client_open (hcl_mmgr_t* mmgr, hcl_oow_t xtnsize, hcl_client_p | ||||
| 		return HCL_NULL; | ||||
| 	} | ||||
|  | ||||
| #if 0 | ||||
| 	HCL_MEMSET (&vmprim, 0, HCL_SIZEOF(vmprim)); | ||||
| 	vmprim.log_write = log_write_for_dummy; | ||||
| 	vmprim.syserrstrb = syserrstrb; | ||||
| 	vmprim.dl_open = dl_open; | ||||
| 	vmprim.dl_close = dl_close; | ||||
| 	vmprim.dl_getsym = dl_getsym; | ||||
| 	vmprim.vm_gettime = vm_gettime; | ||||
| 	vmprim.vm_sleep = vm_sleep; | ||||
|  | ||||
| 	hcl = hcl_open(mmgr, HCL_SIZEOF(*xtn), 2048, &vmprim, errnum); | ||||
| 	if (!hcl)  | ||||
| @ -604,35 +897,6 @@ hcl_client_t* hcl_client_open (hcl_mmgr_t* mmgr, hcl_oow_t xtnsize, hcl_client_p | ||||
| 		return HCL_NULL; | ||||
| 	} | ||||
|  | ||||
| 	tmr = hcl_tmr_open(hcl, 0, 1024); /* TOOD: make the timer's default size configurable */ | ||||
| 	if (!tmr) | ||||
| 	{ | ||||
| 		hcl_close (hcl); | ||||
| 		HCL_MMGR_FREE (mmgr, client); | ||||
| 		return HCL_NULL; | ||||
| 	} | ||||
|  | ||||
| 	if (pipe(pfd) <= -1) | ||||
| 	{ | ||||
| 		hcl_tmr_close (tmr); | ||||
| 		hcl_close (hcl); | ||||
| 		HCL_MMGR_FREE (mmgr, client); | ||||
| 		return HCL_NULL; | ||||
| 	} | ||||
|  | ||||
| #if defined(O_CLOEXEC) | ||||
| 	fcv = fcntl(pfd[0], F_GETFD, 0); | ||||
| 	if (fcv >= 0) fcntl(pfd[0], F_SETFD, fcv | O_CLOEXEC); | ||||
| 	fcv = fcntl(pfd[1], F_GETFD, 0); | ||||
| 	if (fcv >= 0) fcntl(pfd[1], F_SETFD, fcv | O_CLOEXEC); | ||||
| #endif | ||||
| #if defined(O_NONBLOCK) | ||||
| 	fcv = fcntl(pfd[0], F_GETFL, 0); | ||||
| 	if (fcv >= 0) fcntl(pfd[0], F_SETFL, fcv | O_NONBLOCK); | ||||
| 	fcv = fcntl(pfd[1], F_GETFL, 0); | ||||
| 	if (fcv >= 0) fcntl(pfd[1], F_SETFL, fcv | O_NONBLOCK); | ||||
| #endif | ||||
| 	 | ||||
| 	xtn = (dummy_hcl_xtn_t*)hcl_getxtn(hcl); | ||||
| 	xtn->client = client; | ||||
|  | ||||
| @ -641,81 +905,42 @@ hcl_client_t* hcl_client_open (hcl_mmgr_t* mmgr, hcl_oow_t xtnsize, hcl_client_p | ||||
| 	client->cmgr = hcl_get_utf8_cmgr(); | ||||
| 	client->prim = *prim; | ||||
| 	client->dummy_hcl = hcl; | ||||
| 	client->tmr = tmr; | ||||
|  | ||||
| 	client->cfg.logmask = ~0u; | ||||
| 	client->cfg.worker_stack_size = 512000UL;  | ||||
| 	client->cfg.actor_heap_size = 512000UL; | ||||
|  | ||||
| 	HCL_INITNTIME (&client->cfg.worker_idle_timeout, 0, 0); | ||||
| 	HCL_INITNTIME (&client->cfg.actor_max_runtime, 0, 0); | ||||
|  | ||||
| 	client->mux_pipe[0] = pfd[0]; | ||||
| 	client->mux_pipe[1] = pfd[1]; | ||||
|  | ||||
| 	client->wid_map.free_first = HCL_SERVER_WID_INVALID; | ||||
| 	client->wid_map.free_last = HCL_SERVER_WID_INVALID; | ||||
| 	 | ||||
| 	pthread_mutex_init (&client->worker_mutex, HCL_NULL); | ||||
| 	pthread_mutex_init (&client->tmr_mutex, HCL_NULL); | ||||
| 	pthread_mutex_init (&client->log_mutex, HCL_NULL); | ||||
|  | ||||
| 	/* the dummy hcl is used for this client to perform primitive operations | ||||
| 	 * such as getting system time or logging. so the heap size doesn't  | ||||
| 	 * need to be changed from the tiny value set above. */ | ||||
| 	hcl_setoption (client->dummy_hcl, HCL_LOG_MASK, &client->cfg.logmask); | ||||
| 	hcl_setcmgr (client->dummy_hcl, client->cmgr); | ||||
| 	hcl_getoption (client->dummy_hcl, HCL_TRAIT, &trait); | ||||
| #if defined(HCL_BUILD_DEBUG) | ||||
| 	if (client->cfg.trait & HCL_SERVER_TRAIT_DEBUG_GC) trait |= HCL_DEBUG_GC; | ||||
| 	if (client->cfg.trait & HCL_SERVER_TRAIT_DEBUG_BIGINT) trait |= HCL_DEBUG_BIGINT; | ||||
| #endif | ||||
| 	hcl_setoption (client->dummy_hcl, HCL_TRAIT, &trait); | ||||
| #else | ||||
|  | ||||
| 	HCL_MEMSET (client, 0, HCL_SIZEOF(*client) + xtnsize); | ||||
| 	client->mmgr = mmgr; | ||||
| 	client->cmgr = hcl_get_utf8_cmgr(); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| 	return client; | ||||
| } | ||||
|  | ||||
| void hcl_client_close (hcl_client_t* client) | ||||
| { | ||||
| 	if (client->rep.tok.ptr) | ||||
| 	{ | ||||
| 		hcl_client_freemem (client, client->rep.tok.ptr); | ||||
| 		client->rep.tok.ptr = HCL_NULL; | ||||
| 		client->rep.tok.len = 0; | ||||
| 		client->rep.tok.capa = 0; | ||||
| 	} | ||||
|  | ||||
| 	hcl_close (client->dummy_hcl); | ||||
| 	HCL_MMGR_FREE (client->mmgr, client); | ||||
| } | ||||
|  | ||||
|  | ||||
| int hcl_client_setoption (hcl_client_t* client, hcl_client_option_t id, const void* value) | ||||
| { | ||||
| 	switch (id) | ||||
| 	{ | ||||
| 		case HCL_CLIENT_TRAIT: | ||||
| 			client->cfg.trait = *(const unsigned int*)value; | ||||
| 		#if 0 | ||||
| 			if (client->dummy_hcl) | ||||
| 			{ | ||||
| 				/* setting this affects the dummy hcl immediately. | ||||
| 				 * existing hcl instances inside worker threads won't get  | ||||
| 				 * affected. new hcl instances to be created later  | ||||
| 				 * is supposed to use the new value */ | ||||
| 				unsigned int trait; | ||||
|  | ||||
| 				hcl_getoption (client->dummy_hcl, HCL_TRAIT, &trait); | ||||
| 			#if defined(HCL_BUILD_DEBUG) | ||||
| 				if (client->cfg.trait & HCL_CLIENT_TRAIT_DEBUG_GC) trait |= HCL_DEBUG_GC; | ||||
| 				if (client->cfg.trait & HCL_CLIENT_TRAIT_DEBUG_BIGINT) trait |= HCL_DEBUG_BIGINT; | ||||
| 			#endif | ||||
| 				hcl_setoption (client->dummy_hcl, HCL_TRAIT, &trait); | ||||
| 			} | ||||
| 		#endif | ||||
| 			return 0; | ||||
|  | ||||
| 		case HCL_CLIENT_LOG_MASK: | ||||
| 			client->cfg.logmask = *(const unsigned int*)value; | ||||
| 		#if 0 | ||||
| 			if (client->dummy_hcl)  | ||||
| 			{ | ||||
| 				/* setting this affects the dummy hcl immediately. | ||||
| @ -724,19 +949,6 @@ int hcl_client_setoption (hcl_client_t* client, hcl_client_option_t id, const vo | ||||
| 				 * is supposed to use the new value */ | ||||
| 				hcl_setoption (client->dummy_hcl, HCL_LOG_MASK, value); | ||||
| 			} | ||||
| 		#endif | ||||
| 			return 0; | ||||
|  | ||||
| 		case HCL_CLIENT_WORKER_MAX_COUNT: | ||||
| 			client->cfg.worker_max_count = *(hcl_oow_t*)value; | ||||
| 			return 0; | ||||
|  | ||||
| 		case HCL_CLIENT_WORKER_STACK_SIZE: | ||||
| 			client->cfg.worker_stack_size = *(hcl_oow_t*)value; | ||||
| 			return 0; | ||||
|  | ||||
| 		case HCL_CLIENT_WORKER_IDLE_TIMEOUT: | ||||
| 			client->cfg.worker_idle_timeout = *(hcl_ntime_t*)value; | ||||
| 			return 0; | ||||
| 	} | ||||
|  | ||||
| @ -755,18 +967,6 @@ int hcl_client_getoption (hcl_client_t* client, hcl_client_option_t id, void* va | ||||
| 		case HCL_CLIENT_LOG_MASK: | ||||
| 			*(unsigned int*)value = client->cfg.logmask; | ||||
| 			return 0; | ||||
|  | ||||
| 		case HCL_CLIENT_WORKER_MAX_COUNT: | ||||
| 			*(hcl_oow_t*)value = client->cfg.worker_max_count; | ||||
| 			return 0; | ||||
|  | ||||
| 		case HCL_CLIENT_WORKER_STACK_SIZE: | ||||
| 			*(hcl_oow_t*)value = client->cfg.worker_stack_size; | ||||
| 			return 0; | ||||
|  | ||||
| 		case HCL_CLIENT_WORKER_IDLE_TIMEOUT: | ||||
| 			*(hcl_ntime_t*)value = client->cfg.worker_idle_timeout; | ||||
| 			return 0; | ||||
| 	}; | ||||
|  | ||||
| 	hcl_client_seterrnum (client, HCL_EINVAL); | ||||
| @ -817,6 +1017,54 @@ void hcl_client_seterrnum (hcl_client_t* client, hcl_errnum_t errnum) | ||||
| 	client->errmsg.len = 0; | ||||
| } | ||||
|  | ||||
| void hcl_client_seterrbfmt (hcl_client_t* client, hcl_errnum_t errnum, const hcl_bch_t* fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
|  | ||||
| 	va_start (ap, fmt); | ||||
| 	hcl_seterrbfmtv (client->dummy_hcl, errnum, fmt, ap); | ||||
| 	va_end (ap); | ||||
|  | ||||
| 	HCL_ASSERT (client->dummy_hcl, HCL_COUNTOF(client->errmsg.buf) == HCL_COUNTOF(client->dummy_hcl->errmsg.buf)); | ||||
| 	client->errnum = errnum; | ||||
| 	hcl_copyoochars (client->errmsg.buf, client->dummy_hcl->errmsg.buf, HCL_COUNTOF(client->errmsg.buf)); | ||||
| 	client->errmsg.len = client->dummy_hcl->errmsg.len; | ||||
| } | ||||
|  | ||||
| void hcl_client_seterrufmt (hcl_client_t* client, hcl_errnum_t errnum, const hcl_uch_t* fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
|  | ||||
| 	va_start (ap, fmt); | ||||
| 	hcl_seterrufmtv (client->dummy_hcl, errnum, fmt, ap); | ||||
| 	va_end (ap); | ||||
|  | ||||
| 	HCL_ASSERT (client->dummy_hcl, HCL_COUNTOF(client->errmsg.buf) == HCL_COUNTOF(client->dummy_hcl->errmsg.buf)); | ||||
| 	client->errnum = errnum; | ||||
| 	hcl_copyoochars (client->errmsg.buf, client->dummy_hcl->errmsg.buf, HCL_COUNTOF(client->errmsg.buf)); | ||||
| 	client->errmsg.len = client->dummy_hcl->errmsg.len; | ||||
| } | ||||
|  | ||||
| /* ========================================================================= */ | ||||
|  | ||||
| void hcl_client_logbfmt (hcl_client_t* client, unsigned int mask, const hcl_bch_t* fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	va_start (ap, fmt); | ||||
| 	hcl_logbfmtv (client->dummy_hcl, mask, fmt, ap); | ||||
| 	va_end (ap); | ||||
| } | ||||
|  | ||||
| void hcl_client_logufmt (hcl_client_t* client, unsigned int mask, const hcl_uch_t* fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	va_start (ap, fmt); | ||||
| 	hcl_logufmtv (client->dummy_hcl, mask, fmt, ap); | ||||
| 	va_end (ap); | ||||
| } | ||||
|  | ||||
| /* ========================================================================= */ | ||||
|  | ||||
| void* hcl_client_allocmem (hcl_client_t* client, hcl_oow_t size) | ||||
| { | ||||
| 	void* ptr; | ||||
| @ -848,7 +1096,6 @@ void hcl_client_freemem (hcl_client_t* client, void* ptr) | ||||
| 	HCL_MMGR_FREE (client->mmgr, ptr); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* ========================================================================= */ | ||||
|  | ||||
| int hcl_client_start (hcl_client_t* client, const hcl_bch_t* addrs) | ||||
| @ -887,7 +1134,6 @@ int hcl_client_start (hcl_client_t* client, const hcl_bch_t* addrs) | ||||
| 		if (offset > 0) HCL_MEMMOVE (&buf[0], &buf[xlen], offset); | ||||
| 	} | ||||
|  | ||||
| printf ("hcl client start returns success\n"); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -36,38 +36,84 @@ enum hcl_client_option_t | ||||
| { | ||||
| 	HCL_CLIENT_TRAIT, | ||||
| 	HCL_CLIENT_LOG_MASK, | ||||
| 	HCL_CLIENT_WORKER_MAX_COUNT, | ||||
| 	HCL_CLIENT_WORKER_STACK_SIZE, | ||||
| 	HCL_CLIENT_WORKER_IDLE_TIMEOUT | ||||
| }; | ||||
| typedef enum hcl_client_option_t hcl_client_option_t; | ||||
|  | ||||
| enum hcl_client_trait_t | ||||
| { | ||||
| #if defined(HCL_BUILD_DEBUG) | ||||
| 	HCL_CLIENT_TRAIT_DEBUG_GC         = (1 << 0), | ||||
| 	HCL_CLIENT_TRAIT_DEBUG_BIGINT     = (1 << 1), | ||||
| #endif | ||||
|  | ||||
| 	HCL_CLIENT_TRAIT_READABLE_PROTO   = (1 << 2), | ||||
| 	HCL_CLIENT_TRAIT_USE_LARGE_PAGES  = (1 << 3) | ||||
| 	/* no trait defined at this moment. XXXX is just a placeholder */ | ||||
| 	HCL_CLIENT_XXXX  = (1 << 0) | ||||
| }; | ||||
| typedef enum hcl_client_trait_t hcl_client_trait_t; | ||||
|  | ||||
| /* ========================================================================= */ | ||||
|  | ||||
| enum hcl_client_reply_type_t | ||||
| { | ||||
| 	HCL_CLIENT_REPLY_TYPE_OK = 0, | ||||
| 	HCL_CLIENT_REPLY_TYPE_ERROR = 1 | ||||
| }; | ||||
| typedef enum hcl_client_reply_type_t hcl_client_reply_type_t; | ||||
|  | ||||
| typedef void (*hcl_client_log_write_t) ( | ||||
| 	hcl_client_t*     client, | ||||
| 	hcl_oow_t         wid, | ||||
| 	unsigned int      mask, | ||||
| 	const hcl_ooch_t* msg, | ||||
| 	hcl_oow_t         len | ||||
| ); | ||||
|  | ||||
| typedef void (*hcl_client_start_reply_t) ( | ||||
| 	hcl_client_t*           client, | ||||
| 	hcl_client_reply_type_t type, | ||||
| 	const hcl_ooch_t*       dptr, | ||||
| 	hcl_oow_t               dlen | ||||
| ); | ||||
|  | ||||
| typedef void (*hcl_client_feed_attr_t) ( | ||||
| 	hcl_client_t*     client, | ||||
| 	const hcl_oocs_t* key, | ||||
| 	const hcl_oocs_t* val | ||||
| ); | ||||
|  | ||||
| typedef void (*hcl_client_start_data_t) ( | ||||
| 	hcl_client_t*     client | ||||
| ); | ||||
|  | ||||
| typedef void (*hcl_client_feed_data_t) ( | ||||
| 	hcl_client_t*     client, | ||||
| 	const void*       ptr, | ||||
| 	hcl_oow_t         len | ||||
| ); | ||||
|  | ||||
| typedef void (*hcl_client_end_data_t) ( | ||||
| 	hcl_client_t*     client | ||||
| ); | ||||
|  | ||||
| enum hcl_client_end_reply_state_t | ||||
| { | ||||
| 	HCL_CLIENT_END_REPLY_STATE_OK, | ||||
| 	HCL_CLIENT_END_REPLY_STATE_REVOKED, | ||||
| 	HCL_CLIENT_END_REPLY_STATE_ERROR | ||||
| }; | ||||
| typedef enum hcl_client_end_reply_state_t hcl_client_end_reply_state_t; | ||||
|  | ||||
| typedef void (*hcl_client_end_reply_t) ( | ||||
| 	hcl_client_t*                client, | ||||
| 	hcl_client_end_reply_state_t state | ||||
| ); | ||||
|  | ||||
| struct hcl_client_prim_t | ||||
| { | ||||
| 	hcl_client_log_write_t log_write; | ||||
| 	hcl_client_log_write_t     log_write; | ||||
|  | ||||
| 	hcl_client_start_reply_t   start_reply;   /* mandatory */ | ||||
| 	hcl_client_feed_attr_t     feed_attr; /* optional */ | ||||
| 	hcl_client_feed_data_t     feed_data;     /* optional */ | ||||
| 	hcl_client_end_reply_t     end_reply;     /* mandatory */ | ||||
| }; | ||||
| typedef struct hcl_client_prim_t hcl_client_prim_t; | ||||
|  | ||||
| /* ========================================================================= */ | ||||
|  | ||||
| #if defined(__cplusplus) | ||||
| extern "C" { | ||||
| @ -141,9 +187,33 @@ HCL_EXPORT void hcl_client_seterrnum ( | ||||
| 	hcl_errnum_t  errnum | ||||
| ); | ||||
|  | ||||
| HCL_EXPORT void hcl_client_seterrbfmt ( | ||||
| 	hcl_client_t*    client, | ||||
| 	hcl_errnum_t     errnum, | ||||
| 	const hcl_bch_t* fmt, | ||||
| 	... | ||||
| ); | ||||
|  | ||||
| HCL_EXPORT void hcl_client_seterrufmt ( | ||||
| 	hcl_client_t*    client, | ||||
| 	hcl_errnum_t     errnum, | ||||
| 	const hcl_uch_t* fmt, | ||||
| 	... | ||||
| ); | ||||
|  | ||||
| HCL_EXPORT void hcl_client_logbfmt ( | ||||
| 	hcl_client_t*    client, | ||||
| 	unsigned int     mask, | ||||
| 	const hcl_bch_t* fmt, | ||||
| 	... | ||||
| ); | ||||
|  | ||||
| HCL_EXPORT void hcl_client_logufmt ( | ||||
| 	hcl_client_t*    client, | ||||
| 	unsigned int     mask, | ||||
| 	const hcl_uch_t* fmt, | ||||
| 	... | ||||
| ); | ||||
|  | ||||
| HCL_EXPORT void* hcl_client_allocmem ( | ||||
| 	hcl_client_t* client, | ||||
| @ -167,7 +237,6 @@ HCL_EXPORT void hcl_client_freemem ( | ||||
| 	void*         ptr | ||||
| ); | ||||
|  | ||||
|  | ||||
| #if defined(__cplusplus) | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -161,7 +161,6 @@ struct dummy_hcl_xtn_t | ||||
| }; | ||||
| typedef struct dummy_hcl_xtn_t dummy_hcl_xtn_t; | ||||
|  | ||||
|  | ||||
| enum hcl_server_proto_token_type_t | ||||
| { | ||||
| 	HCL_SERVER_PROTO_TOKEN_EOF, | ||||
| @ -1111,12 +1110,12 @@ static int write_reply_chunk (hcl_server_proto_t* proto) | ||||
| 		if (proto->reply.nchunks <= 0) | ||||
| 		{ | ||||
| 			/* this is the first chunk */ | ||||
| 			iov[count].iov_base = ".OK\n.ENCODING chunked\n.DATA\n"; | ||||
| 			iov[count++].iov_len = 28; | ||||
| 			iov[count].iov_base = ".OK\n.DATA chunked\n"; | ||||
| 			iov[count++].iov_len = 18; | ||||
| 		} | ||||
|  | ||||
| 		iov[count].iov_base = cl, | ||||
| 		iov[count++].iov_len = snprintf(cl, HCL_SIZEOF(cl), "%s%zu:", (((proto->worker->server->cfg.trait & HCL_SERVER_TRAIT_READABLE_PROTO) && proto->reply.nchunks > 0)? "\n": ""), proto->reply.len);  | ||||
| 		iov[count++].iov_len = snprintf(cl, HCL_SIZEOF(cl), "%zu:", proto->reply.len);  | ||||
| 	} | ||||
| 	iov[count].iov_base = proto->reply.buf; | ||||
| 	iov[count++].iov_len = proto->reply.len; | ||||
| @ -1245,14 +1244,14 @@ int hcl_server_proto_end_reply (hcl_server_proto_t* proto, const hcl_ooch_t* fai | ||||
| 			/* some chunks have beed emitted. but at the end, an error has occurred. | ||||
| 			 * send -1: as the last chunk. the receiver must rub out the reply | ||||
| 			 * buffer received so far and expect the following .ERROR response */ | ||||
| 			static hcl_ooch_t err0[] = { '-','1',':','\n' }; | ||||
| 			static hcl_ooch_t err0[] = { '-','1',':' }; | ||||
| 			if (proto->reply.len > 0 && write_reply_chunk(proto) <= -1) return -1; | ||||
|  | ||||
| 			proto->reply.type = HCL_SERVER_PROTO_REPLY_SIMPLE; /* switch to the simple mode forcibly */ | ||||
| 			proto->reply.nchunks = 0; | ||||
| 			proto->reply.len = 0; | ||||
|  | ||||
| 			if (hcl_server_proto_feed_reply(proto, err0, 4, 0) <= -1) return -1; | ||||
| 			if (hcl_server_proto_feed_reply(proto, err0, 3, 0) <= -1) return -1; | ||||
| 			goto simple_error; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -53,8 +53,7 @@ enum hcl_server_trait_t | ||||
| 	HCL_SERVER_TRAIT_DEBUG_BIGINT     = (1 << 1), | ||||
| #endif | ||||
|  | ||||
| 	HCL_SERVER_TRAIT_READABLE_PROTO   = (1 << 2), | ||||
| 	HCL_SERVER_TRAIT_USE_LARGE_PAGES  = (1 << 3) | ||||
| 	HCL_SERVER_TRAIT_USE_LARGE_PAGES  = (1 << 2) | ||||
| }; | ||||
| typedef enum hcl_server_trait_t hcl_server_trait_t; | ||||
|  | ||||
|  | ||||
							
								
								
									
										141
									
								
								hcl/lib/main-c.c
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								hcl/lib/main-c.c
									
									
									
									
									
								
							| @ -190,7 +190,7 @@ static void flush_log (hcl_client_t* client, int fd) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void log_write (hcl_client_t* client, hcl_oow_t wid, unsigned int mask, const hcl_ooch_t* msg, hcl_oow_t len) | ||||
| static void log_write (hcl_client_t* client, unsigned int mask, const hcl_ooch_t* msg, hcl_oow_t len) | ||||
| { | ||||
| 	hcl_bch_t buf[256]; | ||||
| 	hcl_oow_t ucslen, bcslen; | ||||
| @ -242,15 +242,6 @@ static void log_write (hcl_client_t* client, hcl_oow_t wid, unsigned int mask, c | ||||
| 		} | ||||
|  | ||||
| 		write_log (client, logfd, ts, tslen); | ||||
|  | ||||
| #if 0 | ||||
| 		if (wid != HCL_CLIENT_WID_INVALID) | ||||
| 		{ | ||||
| 			/* TODO: check if the underlying snprintf support %zd */ | ||||
| 			tslen = snprintf (ts, sizeof(ts), "[%zu] ", wid); | ||||
| 			write_log (client, logfd, ts, tslen); | ||||
| 		} | ||||
| #endif | ||||
| 	} | ||||
|  | ||||
| 	if (logfd == xtn->logfd && xtn->logfd_istty) | ||||
| @ -445,38 +436,43 @@ static int handle_logopt (hcl_client_t* client, const hcl_bch_t* str) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| #if defined(HCL_BUILD_DEBUG) | ||||
| static int handle_dbgopt (hcl_client_t* client, const char* str) | ||||
| static void start_reply (hcl_client_t* client, hcl_client_reply_type_t type, const hcl_ooch_t* dptr, hcl_oow_t dlen) | ||||
| { | ||||
| 	const hcl_bch_t* cm, * flt; | ||||
| 	hcl_oow_t len; | ||||
| 	unsigned int trait; | ||||
|  | ||||
| 	hcl_client_getoption (client, HCL_CLIENT_TRAIT, &trait); | ||||
|  | ||||
| 	cm = str - 1; | ||||
| 	do | ||||
| 	if (dptr) | ||||
| 	{ | ||||
| 		flt = cm + 1; | ||||
|  | ||||
| 		cm = hcl_findbcharinbcstr(flt, ','); | ||||
| 		len = cm? (cm - flt): hcl_countbcstr(flt); | ||||
| 		if (hcl_compbcharsbcstr(flt, len, "gc") == 0)  trait |= HCL_CLIENT_TRAIT_DEBUG_GC; | ||||
| 		else if (hcl_compbcharsbcstr(flt, len, "bigint") == 0)  trait |= HCL_CLIENT_TRAIT_DEBUG_BIGINT; | ||||
| 		else | ||||
| 		{ | ||||
| 			fprintf (stderr, "ERROR: unknown debug option value - %.*s\n", (int)len, flt); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		printf ("GOT SHORT-FORM RESPONSE[%d] with data <<%.*ls>>\n", (int)type, (int)dlen, dptr); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		printf ("GOT LONG_FORM RESPONSE[%d]\n", (int)type); | ||||
| 	} | ||||
| 	while (cm); | ||||
|  | ||||
| 	hcl_client_setoption (client, HCL_CLIENT_TRAIT, &trait); | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static void end_reply (hcl_client_t* client, hcl_client_end_reply_state_t state) | ||||
| { | ||||
| 	if (state == HCL_CLIENT_END_REPLY_STATE_ERROR) | ||||
| 	{ | ||||
| printf (">>>>>>>>>>>>>>>>>>>>> reply error....\n"); | ||||
| 	} | ||||
| 	else if (state == HCL_CLIENT_END_REPLY_STATE_REVOKED) | ||||
| 	{ | ||||
| printf (">>>>>>>>>>>>>>>>>>>>>> REPLY revoked....\n"); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| printf (">>>>>>>>>>>>>>>>>>>>> REPLY ENDED OK....\n"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void feed_attr (hcl_client_t* client, const hcl_oocs_t* key, const hcl_oocs_t* val) | ||||
| { | ||||
| printf ("GOT HEADER ====> [%.*ls] ===> [%.*ls]\n", (int)key->len, key->ptr, (int)val->len, val->ptr); | ||||
| } | ||||
|  | ||||
| static void feed_data (hcl_client_t* client, const void* ptr, hcl_oow_t len) | ||||
| { | ||||
| printf ("GOT DATA>>>>>>>>>[%.*s]>>>>>>>\n", (int)len, ptr); | ||||
| } | ||||
| /* ========================================================================= */ | ||||
|  | ||||
| #define MIN_WORKER_STACK_SIZE 512000ul | ||||
| @ -488,14 +484,7 @@ int main (int argc, char* argv[]) | ||||
| 	static hcl_bopt_lng_t lopt[] = | ||||
| 	{ | ||||
| 		{ ":log",                  'l'  }, | ||||
| 		{ "large-pages",           '\0' }, | ||||
| 		{ ":worker-max-count",     '\0' }, | ||||
| 		{ ":worker-stack-size",    '\0' }, | ||||
| 		{ ":worker-idle-timeout",  '\0' }, | ||||
| 	#if defined(HCL_BUILD_DEBUG) | ||||
| 		{ ":debug",       '\0' }, /* NOTE: there is no short option for --debug */ | ||||
| 	#endif | ||||
| 		{ HCL_NULL,       '\0' } | ||||
| 		{ HCL_NULL,                '\0' } | ||||
| 	}; | ||||
| 	static hcl_bopt_t opt = | ||||
| 	{ | ||||
| @ -507,14 +496,7 @@ int main (int argc, char* argv[]) | ||||
| 	client_xtn_t* xtn; | ||||
| 	hcl_client_prim_t client_prim; | ||||
| 	int n; | ||||
|  | ||||
| 	const char* logopt = HCL_NULL; | ||||
| 	const char* dbgopt = HCL_NULL; | ||||
| 	hcl_oow_t worker_max_count = 0; | ||||
| 	hcl_oow_t worker_stack_size = MIN_ACTOR_HEAP_SIZE; | ||||
| 	hcl_ntime_t worker_idle_timeout = { 0, 0 }; | ||||
| 	int large_pages = 0; | ||||
| 	unsigned int trait; | ||||
|  | ||||
| 	setlocale (LC_ALL, ""); | ||||
|  | ||||
| @ -535,30 +517,7 @@ int main (int argc, char* argv[]) | ||||
| 				break; | ||||
|  | ||||
| 			case '\0': | ||||
| 				if (hcl_compbcstr(opt.lngopt, "large-pages") == 0) | ||||
| 				{ | ||||
| 					large_pages = 1; | ||||
| 				} | ||||
| 				else if (hcl_compbcstr(opt.lngopt, "worker-max-count") == 0) | ||||
| 				{ | ||||
| 					worker_max_count = strtoul(opt.arg, HCL_NULL, 0); | ||||
| 				} | ||||
| 				else if (hcl_compbcstr(opt.lngopt, "worker-stack-size") == 0) | ||||
| 				{ | ||||
| 					worker_stack_size = strtoul(opt.arg, HCL_NULL, 0); | ||||
| 					if (worker_stack_size <= MIN_WORKER_STACK_SIZE) worker_stack_size = MIN_WORKER_STACK_SIZE; | ||||
| 				} | ||||
| 				else if (hcl_compbcstr(opt.lngopt, "worker-idle-timeout") == 0) | ||||
| 				{ | ||||
| 					worker_idle_timeout.sec = strtoul(opt.arg, HCL_NULL, 0); | ||||
| 				} | ||||
| 			#if defined(HCL_BUILD_DEBUG) | ||||
| 				else if (hcl_compbcstr(opt.lngopt, "debug") == 0) | ||||
| 				{ | ||||
| 					dbgopt = opt.arg; | ||||
| 				} | ||||
| 			#endif | ||||
| 				else goto print_usage; | ||||
| 				goto print_usage; | ||||
| 				break; | ||||
|  | ||||
| 			case ':': | ||||
| @ -578,6 +537,10 @@ int main (int argc, char* argv[]) | ||||
|  | ||||
| 	memset (&client_prim, 0, HCL_SIZEOF(client_prim)); | ||||
| 	client_prim.log_write = log_write; | ||||
| 	client_prim.start_reply = start_reply; | ||||
| 	client_prim.feed_attr = feed_attr; | ||||
| 	client_prim.feed_data = feed_data; | ||||
| 	client_prim.end_reply = end_reply; | ||||
|  | ||||
| 	client = hcl_client_open(&sys_mmgr, HCL_SIZEOF(client_xtn_t), &client_prim, HCL_NULL); | ||||
| 	if (!client) | ||||
| @ -600,22 +563,6 @@ int main (int argc, char* argv[]) | ||||
| 		xtn->logmask = HCL_LOG_ALL_TYPES | HCL_LOG_ERROR | HCL_LOG_FATAL; | ||||
| 	} | ||||
|  | ||||
| #if defined(HCL_BUILD_DEBUG) | ||||
| 	if (dbgopt) | ||||
| 	{ | ||||
| 		if (handle_dbgopt(client, dbgopt) <= -1) goto oops; | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	hcl_client_getoption (client, HCL_CLIENT_TRAIT, &trait); | ||||
| 	if (large_pages) trait |= HCL_CLIENT_TRAIT_USE_LARGE_PAGES; | ||||
| 	else trait &= ~HCL_CLIENT_TRAIT_USE_LARGE_PAGES; | ||||
| 	hcl_client_setoption (client, HCL_CLIENT_TRAIT, &trait); | ||||
|  | ||||
| 	hcl_client_setoption (client, HCL_CLIENT_WORKER_MAX_COUNT, &worker_max_count); | ||||
| 	hcl_client_setoption (client, HCL_CLIENT_WORKER_STACK_SIZE, &worker_stack_size); | ||||
| 	hcl_client_setoption (client, HCL_CLIENT_WORKER_IDLE_TIMEOUT, &worker_idle_timeout); | ||||
|  | ||||
| 	g_client = client; | ||||
| 	set_signal (SIGINT, handle_sigint); | ||||
| 	set_signal_to_ignore (SIGPIPE); | ||||
| @ -627,13 +574,15 @@ int main (int argc, char* argv[]) | ||||
|  | ||||
| 	if (n <= -1) | ||||
| 	{ | ||||
| 	//	hcl_client_logbfmt (client, HCL_LOG_APP | HCL_LOG_FATAL, "client error[%d] - %js\n", hcl_client_geterrnum(client), hcl_client_geterrmsg(client)); | ||||
| printf ("hcl client error... = %d\n", hcl_client_geterrnum(client)); | ||||
| 		hcl_client_logbfmt (client, HCL_LOG_APP | HCL_LOG_FATAL, "client error[%d] - %js\n", hcl_client_geterrnum(client), hcl_client_geterrmsg(client)); | ||||
| 	} | ||||
|  | ||||
| 	close (xtn->logfd); | ||||
| 	xtn->logfd = -1; | ||||
| 	xtn->logfd_istty = 0; | ||||
| 	if (xtn->logfd >= 0) | ||||
| 	{ | ||||
| 		close (xtn->logfd); | ||||
| 		xtn->logfd = -1; | ||||
| 		xtn->logfd_istty = 0; | ||||
| 	} | ||||
|  | ||||
| 	hcl_client_close (client); | ||||
| 	return n; | ||||
|  | ||||
| @ -667,9 +667,12 @@ int main (int argc, char* argv[]) | ||||
| 		hcl_server_logbfmt (server, HCL_LOG_APP | HCL_LOG_FATAL, "server error[%d] - %js\n", hcl_server_geterrnum(server), hcl_server_geterrmsg(server)); | ||||
| 	} | ||||
|  | ||||
| 	close (xtn->logfd); | ||||
| 	xtn->logfd = -1; | ||||
| 	xtn->logfd_istty = 0; | ||||
| 	if (xtn->logfd >= 0) | ||||
| 	{ | ||||
| 		close (xtn->logfd); | ||||
| 		xtn->logfd = -1; | ||||
| 		xtn->logfd_istty = 0; | ||||
| 	} | ||||
|  | ||||
| 	hcl_server_close (server); | ||||
| 	return n; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user