From: Brian Aker Date: Sun, 27 Nov 2011 04:07:26 +0000 (-0800) Subject: Improve parser error messages. X-Git-Tag: 1.0.3~4^2~3^2~5 X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;h=694f0966e3980f448c0ca9f9504e6d065e35654f;p=m6w6%2Flibmemcached Improve parser error messages. --- diff --git a/clients/memcapable.cc b/clients/memcapable.cc index b325413d..8bce153a 100644 --- a/clients/memcapable.cc +++ b/clients/memcapable.cc @@ -33,7 +33,8 @@ #include #include -#include +#include +#include #include #include #include @@ -132,7 +133,7 @@ static memcached_socket_t set_noblock(void) if (flags == -1) { perror("Failed to get socket flags"); - closesocket(sock); + memcached_close_socket(sock); return INVALID_SOCKET; } @@ -141,7 +142,7 @@ static memcached_socket_t set_noblock(void) if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { perror("Failed to set socket to nonblocking mode"); - closesocket(sock); + memcached_close_socket(sock); return INVALID_SOCKET; } } diff --git a/example/memcached_light.c b/example/memcached_light.c index ed892861..18274378 100644 --- a/example/memcached_light.c +++ b/example/memcached_light.c @@ -35,9 +35,10 @@ #include #include +#include #include -#include "storage.h" -#include "memcached_light.h" +#include "example/storage.h" +#include "example/memcached_light.h" extern memcached_binary_protocol_callback_st interface_v0_impl; extern memcached_binary_protocol_callback_st interface_v1_impl; diff --git a/example/storage.h b/example/storage.h index 12955846..bd1b8780 100644 --- a/example/storage.h +++ b/example/storage.h @@ -1,6 +1,5 @@ /* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#ifndef STORAGE_H -#define STORAGE_H +#pragma once struct item { uint64_t cas; @@ -23,5 +22,3 @@ struct item* create_item(const void* key, size_t nkey, const void *data, bool delete_item(const void* key, size_t nkey); void flush(uint32_t when); void release_item(struct item* item); - -#endif diff --git a/libmemcached/behavior.cc b/libmemcached/behavior.cc index 64456a4b..bb17f2bd 100644 --- a/libmemcached/behavior.cc +++ b/libmemcached/behavior.cc @@ -384,7 +384,7 @@ uint64_t memcached_behavior_get(memcached_st *ptr, if (getsockopt(instance->fd, SOL_SOCKET, SO_SNDBUF, &sock_size, &sock_length) < 0) { - memcached_set_errno(*ptr, errno, MEMCACHED_AT); + memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); return 0; /* Zero means error */ } } @@ -420,10 +420,9 @@ uint64_t memcached_behavior_get(memcached_st *ptr, if (getsockopt(instance->fd, SOL_SOCKET, SO_RCVBUF, &sock_size, &sock_length) < 0) { - memcached_set_errno(*ptr, errno, MEMCACHED_AT); + memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); return 0; /* Zero means error */ } - } return (uint64_t) sock_size; diff --git a/libmemcached/close_socket.hpp b/libmemcached/close_socket.hpp new file mode 100644 index 00000000..0d18857a --- /dev/null +++ b/libmemcached/close_socket.hpp @@ -0,0 +1,79 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * LibMemcached + * + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ + * Copyright (C) 2006-2009 Brian Aker + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * The names of its contributors may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + + +/* To hide the platform differences between MS Windows and Unix, I am + * going to use the Microsoft way and #define the Microsoft-specific + * functions to the unix way. Microsoft use a separate subsystem for sockets, + * but Unix normally just use a filedescriptor on the same functions. It is + * a lot easier to map back to the unix way with macros than going the other + * way without side effect ;-) + */ +#ifdef WIN32 +#include "win32/wrappers.h" +#define get_socket_errno() WSAGetLastError() +#else +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define closesocket(a) close(a) +#define get_socket_errno() errno +#endif + +#ifdef __cplusplus +static inline void memcached_close_socket(int& socket_fd) +{ + closesocket(socket_fd); + socket_fd= INVALID_SOCKET; +} +#endif + +#ifndef HAVE_MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +#ifndef HAVE_MSG_DONTWAIT +#define MSG_DONTWAIT 0 +#endif + +#ifndef HAVE_MSG_MORE +#define MSG_MORE 0 +#endif + + diff --git a/libmemcached/common.h b/libmemcached/common.h index 47735195..ba0b4063 100644 --- a/libmemcached/common.h +++ b/libmemcached/common.h @@ -106,6 +106,7 @@ memcached_return_t memcached_server_execute(memcached_st *ptr, #include #include #include +#include #endif #include #include diff --git a/libmemcached/connect.cc b/libmemcached/connect.cc index 3cc8e245..6cad60d5 100644 --- a/libmemcached/connect.cc +++ b/libmemcached/connect.cc @@ -64,12 +64,14 @@ static memcached_return_t connect_poll(memcached_server_st *server) { int err; socklen_t len= sizeof (err); - (void)getsockopt(server->fd, SOL_SOCKET, SO_ERROR, &err, &len); - - // We check the value to see what happened wth the socket. - if (err == 0) + if (getsockopt(server->fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0) { - return MEMCACHED_SUCCESS; + // We check the value to see what happened wth the socket. + if (err == 0) + { + return MEMCACHED_SUCCESS; + } + errno= err; } return memcached_set_errno(*server, err, MEMCACHED_AT); @@ -99,21 +101,26 @@ static memcached_return_t connect_poll(memcached_server_st *server) if (fds[0].revents & POLLERR) { int err; - socklen_t len= sizeof (err); - (void)getsockopt(server->fd, SOL_SOCKET, SO_ERROR, &err, &len); - memcached_set_errno(*server, (err == 0) ? get_socket_errno() : err, MEMCACHED_AT); - } - else - { - memcached_set_errno(*server, get_socket_errno(), MEMCACHED_AT); + socklen_t len= sizeof(err); + if (getsockopt(server->fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0) + { + if (err == 0) + { + // This should never happen, if it does? Punt. + continue; + } + errno= err; + } } + int local_errno= get_socket_errno(); // We cache in case closesocket() modifies errno + assert_msg(server->fd != INVALID_SOCKET, "poll() was passed an invalid file descriptor"); (void)closesocket(server->fd); server->fd= INVALID_SOCKET; server->state= MEMCACHED_SERVER_STATE_NEW; - return memcached_set_errno(*server, get_socket_errno(), MEMCACHED_AT); + return memcached_set_errno(*server, local_errno, MEMCACHED_AT); } } } diff --git a/libmemcached/csl/context.cc b/libmemcached/csl/context.cc index 36ff2587..1e6e18ae 100644 --- a/libmemcached/csl/context.cc +++ b/libmemcached/csl/context.cc @@ -49,7 +49,13 @@ void Context::abort(const char *error_arg, yytokentype last_token, const char *l return; } - memcached_set_parser_error(*memc, MEMCACHED_AT, "%s", last_token_str ? last_token_str : " "); + if (last_token_str) + { + memcached_set_parser_error(*memc, MEMCACHED_AT, "%s", last_token_str); + return; + } + + memcached_set_parser_error(*memc, MEMCACHED_AT, "unknown parsing error"); } void Context::error(const char *error_arg, yytokentype last_token, const char *last_token_str) @@ -100,3 +106,17 @@ const char *Context::set_hostname(const char *str, size_t size) return _hostname; } +bool Context::set_hash(memcached_hash_t hash) +{ + if (_has_hash) + { + return false; + } + + if ((memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, hash)) != MEMCACHED_SUCCESS) + { + return false; + } + + return _has_hash= true; +} diff --git a/libmemcached/csl/context.h b/libmemcached/csl/context.h index 94f68134..77fac219 100644 --- a/libmemcached/csl/context.h +++ b/libmemcached/csl/context.h @@ -52,7 +52,8 @@ public: memc(NULL), rc(rc_arg), _is_server(false), - _end(false) + _end(false), + _has_hash(false) { _hostname[0]= 0; buf= option_string; @@ -75,6 +76,8 @@ public: _end= true; } + bool set_hash(memcached_hash_t hash); + void set_server() { _is_server= true; @@ -85,14 +88,14 @@ public: _is_server= false; } - bool is_server() + bool is_server() const { return _is_server; } const char *set_hostname(const char *str, size_t size); - const char *hostname() + const char *hostname() const { return _hostname; } @@ -122,4 +125,5 @@ private: bool _is_server; bool _end; char _hostname[NI_MAXHOST]; + bool _has_hash; }; diff --git a/libmemcached/csl/parser.cc b/libmemcached/csl/parser.cc index 855b6fdd..a9fb743a 100644 --- a/libmemcached/csl/parser.cc +++ b/libmemcached/csl/parser.cc @@ -541,12 +541,12 @@ static const yytype_int8 yyrhs[] = static const yytype_uint16 yyrline[] = { 0, 172, 172, 173, 177, 179, 181, 183, 188, 193, - 197, 201, 212, 220, 228, 235, 239, 243, 247, 251, - 263, 270, 281, 288, 295, 302, 308, 312, 316, 320, - 324, 328, 332, 336, 340, 344, 348, 352, 359, 363, - 367, 371, 375, 379, 383, 387, 391, 395, 399, 403, - 410, 411, 416, 417, 422, 426, 430, 434, 438, 442, - 446, 450, 454, 461, 465, 472, 476, 480 + 197, 201, 212, 222, 232, 241, 245, 249, 253, 257, + 269, 282, 295, 302, 309, 318, 324, 328, 332, 336, + 340, 344, 348, 352, 356, 360, 364, 368, 375, 379, + 383, 387, 391, 395, 399, 403, 407, 411, 415, 419, + 426, 427, 432, 433, 438, 442, 446, 450, 454, 458, + 462, 466, 470, 477, 481, 488, 492, 496 }; #endif @@ -1591,7 +1591,7 @@ yyreduce: #line 189 "libmemcached/csl/parser.yy" { context->rc= MEMCACHED_PARSE_USER_ERROR; - parser_abort(context, NULL); + parser_abort(context, "ERROR called directly"); } break; @@ -1620,7 +1620,7 @@ yyreduce: { if ((context->rc= memcached_parse_configure_file(*context->memc, (yyvsp[(3) - (3)].string).c_str, (yyvsp[(3) - (3)].string).size)) != MEMCACHED_SUCCESS) { - parser_abort(context, NULL); + parser_abort(context, "Failed to parse configuration file"); } } break; @@ -1632,7 +1632,9 @@ yyreduce: { if (memcached_failed(context->rc= memcached_server_add_with_weight(context->memc, (yyvsp[(2) - (4)].server).c_str, (yyvsp[(3) - (4)].number), (yyvsp[(4) - (4)].number)))) { - parser_abort(context, NULL); + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "Failed to add server: %s:%u", (yyvsp[(2) - (4)].server).c_str, uint32_t((yyvsp[(3) - (4)].number))); + parser_abort(context, buffer); } context->unset_server(); } @@ -1641,11 +1643,13 @@ yyreduce: case 13: /* Line 1806 of yacc.c */ -#line 221 "libmemcached/csl/parser.yy" +#line 223 "libmemcached/csl/parser.yy" { if (memcached_failed(context->rc= memcached_server_add_with_weight(context->memc, (yyvsp[(2) - (4)].server).c_str, (yyvsp[(3) - (4)].number), (yyvsp[(4) - (4)].number)))) { - parser_abort(context, NULL); + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "Failed to add server: %s:%u", (yyvsp[(2) - (4)].server).c_str, uint32_t((yyvsp[(3) - (4)].number))); + parser_abort(context, buffer); } context->unset_server(); } @@ -1654,11 +1658,13 @@ yyreduce: case 14: /* Line 1806 of yacc.c */ -#line 229 "libmemcached/csl/parser.yy" +#line 233 "libmemcached/csl/parser.yy" { if (memcached_failed(context->rc= memcached_server_add_unix_socket_with_weight(context->memc, (yyvsp[(2) - (3)].string).c_str, (yyvsp[(3) - (3)].number)))) { - parser_abort(context, NULL); + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "Failed to add server: %s", (yyvsp[(2) - (3)].string).c_str); + parser_abort(context, buffer); } } break; @@ -1666,7 +1672,7 @@ yyreduce: case 15: /* Line 1806 of yacc.c */ -#line 236 "libmemcached/csl/parser.yy" +#line 242 "libmemcached/csl/parser.yy" { memcached_set_configuration_file(context->memc, (yyvsp[(2) - (2)].string).c_str, (yyvsp[(2) - (2)].string).size); } @@ -1675,7 +1681,7 @@ yyreduce: case 16: /* Line 1806 of yacc.c */ -#line 240 "libmemcached/csl/parser.yy" +#line 246 "libmemcached/csl/parser.yy" { context->memc->configure.initial_pool_size= (yyvsp[(2) - (2)].number); } @@ -1684,7 +1690,7 @@ yyreduce: case 17: /* Line 1806 of yacc.c */ -#line 244 "libmemcached/csl/parser.yy" +#line 250 "libmemcached/csl/parser.yy" { context->memc->configure.max_pool_size= (yyvsp[(2) - (2)].number); } @@ -1693,7 +1699,7 @@ yyreduce: case 19: /* Line 1806 of yacc.c */ -#line 252 "libmemcached/csl/parser.yy" +#line 258 "libmemcached/csl/parser.yy" { if (memcached_callback_get(context->memc, MEMCACHED_CALLBACK_PREFIX_KEY, NULL)) { @@ -1710,8 +1716,14 @@ yyreduce: case 20: /* Line 1806 of yacc.c */ -#line 264 "libmemcached/csl/parser.yy" +#line 270 "libmemcached/csl/parser.yy" { + // Check to see if DISTRIBUTION has already been set + if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, (yyvsp[(2) - (2)].distribution))) != MEMCACHED_SUCCESS) + { + parser_abort(context, "--DISTRIBUTION can only be called once"); + } + if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, (yyvsp[(2) - (2)].distribution))) != MEMCACHED_SUCCESS) { parser_abort(context, memcached_last_error_message(context->memc));; @@ -1722,15 +1734,17 @@ yyreduce: case 21: /* Line 1806 of yacc.c */ -#line 271 "libmemcached/csl/parser.yy" +#line 283 "libmemcached/csl/parser.yy" { + // Check to see if DISTRIBUTION has already been set if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, (yyvsp[(2) - (4)].distribution))) != MEMCACHED_SUCCESS) { - parser_abort(context, NULL); + parser_abort(context, "--DISTRIBUTION can only be called once"); } + if ((context->rc= memcached_behavior_set_distribution_hash(context->memc, (yyvsp[(4) - (4)].hash))) != MEMCACHED_SUCCESS) { - parser_abort(context, NULL); + parser_abort(context, "Unable to set the hash for the DISTRIBUTION requested"); } } break; @@ -1738,11 +1752,11 @@ yyreduce: case 22: /* Line 1806 of yacc.c */ -#line 282 "libmemcached/csl/parser.yy" +#line 296 "libmemcached/csl/parser.yy" { - if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_HASH, (yyvsp[(2) - (2)].hash))) != MEMCACHED_SUCCESS) + if (context->set_hash((yyvsp[(2) - (2)].hash)) == false) { - parser_abort(context, NULL); + parser_abort(context, "--HASH can only be set once"); } } break; @@ -1750,11 +1764,11 @@ yyreduce: case 23: /* Line 1806 of yacc.c */ -#line 289 "libmemcached/csl/parser.yy" +#line 303 "libmemcached/csl/parser.yy" { if ((context->rc= memcached_behavior_set(context->memc, (yyvsp[(1) - (2)].behavior), (yyvsp[(2) - (2)].number))) != MEMCACHED_SUCCESS) { - parser_abort(context, NULL); + parser_abort(context, "Unable to set behavior"); } } break; @@ -1762,11 +1776,13 @@ yyreduce: case 24: /* Line 1806 of yacc.c */ -#line 296 "libmemcached/csl/parser.yy" +#line 310 "libmemcached/csl/parser.yy" { if ((context->rc= memcached_behavior_set(context->memc, (yyvsp[(1) - (1)].behavior), true)) != MEMCACHED_SUCCESS) { - parser_abort(context, NULL); + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "Could not set: %s", libmemcached_string_behavior((yyvsp[(1) - (1)].behavior))); + parser_abort(context, buffer); } } break; @@ -1774,7 +1790,7 @@ yyreduce: case 25: /* Line 1806 of yacc.c */ -#line 303 "libmemcached/csl/parser.yy" +#line 319 "libmemcached/csl/parser.yy" { } break; @@ -1782,7 +1798,7 @@ yyreduce: case 26: /* Line 1806 of yacc.c */ -#line 309 "libmemcached/csl/parser.yy" +#line 325 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS; } @@ -1791,7 +1807,7 @@ yyreduce: case 27: /* Line 1806 of yacc.c */ -#line 313 "libmemcached/csl/parser.yy" +#line 329 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT; } @@ -1800,7 +1816,7 @@ yyreduce: case 28: /* Line 1806 of yacc.c */ -#line 317 "libmemcached/csl/parser.yy" +#line 333 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK; } @@ -1809,7 +1825,7 @@ yyreduce: case 29: /* Line 1806 of yacc.c */ -#line 321 "libmemcached/csl/parser.yy" +#line 337 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK; } @@ -1818,7 +1834,7 @@ yyreduce: case 30: /* Line 1806 of yacc.c */ -#line 325 "libmemcached/csl/parser.yy" +#line 341 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH; } @@ -1827,7 +1843,7 @@ yyreduce: case 31: /* Line 1806 of yacc.c */ -#line 329 "libmemcached/csl/parser.yy" +#line 345 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS; } @@ -1836,7 +1852,7 @@ yyreduce: case 32: /* Line 1806 of yacc.c */ -#line 333 "libmemcached/csl/parser.yy" +#line 349 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_POLL_TIMEOUT; } @@ -1845,7 +1861,7 @@ yyreduce: case 33: /* Line 1806 of yacc.c */ -#line 337 "libmemcached/csl/parser.yy" +#line 353 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_RCV_TIMEOUT; } @@ -1854,7 +1870,7 @@ yyreduce: case 34: /* Line 1806 of yacc.c */ -#line 341 "libmemcached/csl/parser.yy" +#line 357 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_RETRY_TIMEOUT; } @@ -1863,7 +1879,7 @@ yyreduce: case 35: /* Line 1806 of yacc.c */ -#line 345 "libmemcached/csl/parser.yy" +#line 361 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_SND_TIMEOUT; } @@ -1872,7 +1888,7 @@ yyreduce: case 36: /* Line 1806 of yacc.c */ -#line 349 "libmemcached/csl/parser.yy" +#line 365 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE; } @@ -1881,7 +1897,7 @@ yyreduce: case 37: /* Line 1806 of yacc.c */ -#line 353 "libmemcached/csl/parser.yy" +#line 369 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE; } @@ -1890,7 +1906,7 @@ yyreduce: case 38: /* Line 1806 of yacc.c */ -#line 360 "libmemcached/csl/parser.yy" +#line 376 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_BINARY_PROTOCOL; } @@ -1899,7 +1915,7 @@ yyreduce: case 39: /* Line 1806 of yacc.c */ -#line 364 "libmemcached/csl/parser.yy" +#line 380 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_BUFFER_REQUESTS; } @@ -1908,7 +1924,7 @@ yyreduce: case 40: /* Line 1806 of yacc.c */ -#line 368 "libmemcached/csl/parser.yy" +#line 384 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY; } @@ -1917,7 +1933,7 @@ yyreduce: case 41: /* Line 1806 of yacc.c */ -#line 372 "libmemcached/csl/parser.yy" +#line 388 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_NOREPLY; } @@ -1926,7 +1942,7 @@ yyreduce: case 42: /* Line 1806 of yacc.c */ -#line 376 "libmemcached/csl/parser.yy" +#line 392 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ; } @@ -1935,7 +1951,7 @@ yyreduce: case 43: /* Line 1806 of yacc.c */ -#line 380 "libmemcached/csl/parser.yy" +#line 396 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_SORT_HOSTS; } @@ -1944,7 +1960,7 @@ yyreduce: case 44: /* Line 1806 of yacc.c */ -#line 384 "libmemcached/csl/parser.yy" +#line 400 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_SUPPORT_CAS; } @@ -1953,7 +1969,7 @@ yyreduce: case 45: /* Line 1806 of yacc.c */ -#line 388 "libmemcached/csl/parser.yy" +#line 404 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_TCP_NODELAY; } @@ -1962,7 +1978,7 @@ yyreduce: case 46: /* Line 1806 of yacc.c */ -#line 392 "libmemcached/csl/parser.yy" +#line 408 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_TCP_KEEPALIVE; } @@ -1971,7 +1987,7 @@ yyreduce: case 47: /* Line 1806 of yacc.c */ -#line 396 "libmemcached/csl/parser.yy" +#line 412 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_TCP_KEEPIDLE; } @@ -1980,7 +1996,7 @@ yyreduce: case 48: /* Line 1806 of yacc.c */ -#line 400 "libmemcached/csl/parser.yy" +#line 416 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_USE_UDP; } @@ -1989,7 +2005,7 @@ yyreduce: case 49: /* Line 1806 of yacc.c */ -#line 404 "libmemcached/csl/parser.yy" +#line 420 "libmemcached/csl/parser.yy" { (yyval.behavior)= MEMCACHED_BEHAVIOR_VERIFY_KEY; } @@ -1998,35 +2014,35 @@ yyreduce: case 50: /* Line 1806 of yacc.c */ -#line 410 "libmemcached/csl/parser.yy" +#line 426 "libmemcached/csl/parser.yy" { (yyval.number)= MEMCACHED_DEFAULT_PORT;} break; case 51: /* Line 1806 of yacc.c */ -#line 412 "libmemcached/csl/parser.yy" +#line 428 "libmemcached/csl/parser.yy" { } break; case 52: /* Line 1806 of yacc.c */ -#line 416 "libmemcached/csl/parser.yy" +#line 432 "libmemcached/csl/parser.yy" { (yyval.number)= 1; } break; case 53: /* Line 1806 of yacc.c */ -#line 418 "libmemcached/csl/parser.yy" +#line 434 "libmemcached/csl/parser.yy" { } break; case 54: /* Line 1806 of yacc.c */ -#line 423 "libmemcached/csl/parser.yy" +#line 439 "libmemcached/csl/parser.yy" { (yyval.hash)= MEMCACHED_HASH_MD5; } @@ -2035,7 +2051,7 @@ yyreduce: case 55: /* Line 1806 of yacc.c */ -#line 427 "libmemcached/csl/parser.yy" +#line 443 "libmemcached/csl/parser.yy" { (yyval.hash)= MEMCACHED_HASH_CRC; } @@ -2044,7 +2060,7 @@ yyreduce: case 56: /* Line 1806 of yacc.c */ -#line 431 "libmemcached/csl/parser.yy" +#line 447 "libmemcached/csl/parser.yy" { (yyval.hash)= MEMCACHED_HASH_FNV1_64; } @@ -2053,7 +2069,7 @@ yyreduce: case 57: /* Line 1806 of yacc.c */ -#line 435 "libmemcached/csl/parser.yy" +#line 451 "libmemcached/csl/parser.yy" { (yyval.hash)= MEMCACHED_HASH_FNV1A_64; } @@ -2062,7 +2078,7 @@ yyreduce: case 58: /* Line 1806 of yacc.c */ -#line 439 "libmemcached/csl/parser.yy" +#line 455 "libmemcached/csl/parser.yy" { (yyval.hash)= MEMCACHED_HASH_FNV1_32; } @@ -2071,7 +2087,7 @@ yyreduce: case 59: /* Line 1806 of yacc.c */ -#line 443 "libmemcached/csl/parser.yy" +#line 459 "libmemcached/csl/parser.yy" { (yyval.hash)= MEMCACHED_HASH_FNV1A_32; } @@ -2080,7 +2096,7 @@ yyreduce: case 60: /* Line 1806 of yacc.c */ -#line 447 "libmemcached/csl/parser.yy" +#line 463 "libmemcached/csl/parser.yy" { (yyval.hash)= MEMCACHED_HASH_HSIEH; } @@ -2089,7 +2105,7 @@ yyreduce: case 61: /* Line 1806 of yacc.c */ -#line 451 "libmemcached/csl/parser.yy" +#line 467 "libmemcached/csl/parser.yy" { (yyval.hash)= MEMCACHED_HASH_MURMUR; } @@ -2098,7 +2114,7 @@ yyreduce: case 62: /* Line 1806 of yacc.c */ -#line 455 "libmemcached/csl/parser.yy" +#line 471 "libmemcached/csl/parser.yy" { (yyval.hash)= MEMCACHED_HASH_JENKINS; } @@ -2107,7 +2123,7 @@ yyreduce: case 63: /* Line 1806 of yacc.c */ -#line 462 "libmemcached/csl/parser.yy" +#line 478 "libmemcached/csl/parser.yy" { (yyval.string)= (yyvsp[(1) - (1)].string); } @@ -2116,7 +2132,7 @@ yyreduce: case 64: /* Line 1806 of yacc.c */ -#line 466 "libmemcached/csl/parser.yy" +#line 482 "libmemcached/csl/parser.yy" { (yyval.string)= (yyvsp[(1) - (1)].string); } @@ -2125,7 +2141,7 @@ yyreduce: case 65: /* Line 1806 of yacc.c */ -#line 473 "libmemcached/csl/parser.yy" +#line 489 "libmemcached/csl/parser.yy" { (yyval.distribution)= MEMCACHED_DISTRIBUTION_CONSISTENT; } @@ -2134,7 +2150,7 @@ yyreduce: case 66: /* Line 1806 of yacc.c */ -#line 477 "libmemcached/csl/parser.yy" +#line 493 "libmemcached/csl/parser.yy" { (yyval.distribution)= MEMCACHED_DISTRIBUTION_MODULA; } @@ -2143,7 +2159,7 @@ yyreduce: case 67: /* Line 1806 of yacc.c */ -#line 481 "libmemcached/csl/parser.yy" +#line 497 "libmemcached/csl/parser.yy" { (yyval.distribution)= MEMCACHED_DISTRIBUTION_RANDOM; } @@ -2152,7 +2168,7 @@ yyreduce: /* Line 1806 of yacc.c */ -#line 2156 "libmemcached/csl/parser.cc" +#line 2172 "libmemcached/csl/parser.cc" default: break; } /* User semantic actions sometimes alter yychar, and that requires @@ -2383,7 +2399,7 @@ yyreturn: /* Line 2067 of yacc.c */ -#line 486 "libmemcached/csl/parser.yy" +#line 502 "libmemcached/csl/parser.yy" void Context::start() diff --git a/libmemcached/csl/parser.yy b/libmemcached/csl/parser.yy index 332d323e..48d4d4e9 100644 --- a/libmemcached/csl/parser.yy +++ b/libmemcached/csl/parser.yy @@ -188,7 +188,7 @@ statement: | ERROR { context->rc= MEMCACHED_PARSE_USER_ERROR; - parser_abort(context, NULL); + parser_abort(context, "ERROR called directly"); } | RESET { @@ -202,7 +202,7 @@ statement: { if ((context->rc= memcached_parse_configure_file(*context->memc, $3.c_str, $3.size)) != MEMCACHED_SUCCESS) { - parser_abort(context, NULL); + parser_abort(context, "Failed to parse configuration file"); } } ; @@ -213,7 +213,9 @@ expression: { if (memcached_failed(context->rc= memcached_server_add_with_weight(context->memc, $2.c_str, $3, $4))) { - parser_abort(context, NULL); + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "Failed to add server: %s:%u", $2.c_str, uint32_t($3)); + parser_abort(context, buffer); } context->unset_server(); } @@ -221,7 +223,9 @@ expression: { if (memcached_failed(context->rc= memcached_server_add_with_weight(context->memc, $2.c_str, $3, $4))) { - parser_abort(context, NULL); + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "Failed to add server: %s:%u", $2.c_str, uint32_t($3)); + parser_abort(context, buffer); } context->unset_server(); } @@ -229,7 +233,9 @@ expression: { if (memcached_failed(context->rc= memcached_server_add_unix_socket_with_weight(context->memc, $2.c_str, $3))) { - parser_abort(context, NULL); + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "Failed to add server: %s", $2.c_str); + parser_abort(context, buffer); } } | CONFIGURE_FILE string @@ -262,6 +268,12 @@ behaviors: } | DISTRIBUTION distribution { + // Check to see if DISTRIBUTION has already been set + if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, $2)) != MEMCACHED_SUCCESS) + { + parser_abort(context, "--DISTRIBUTION can only be called once"); + } + if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, $2)) != MEMCACHED_SUCCESS) { parser_abort(context, memcached_last_error_message(context->memc));; @@ -269,34 +281,38 @@ behaviors: } | DISTRIBUTION distribution ',' hash { + // Check to see if DISTRIBUTION has already been set if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, $2)) != MEMCACHED_SUCCESS) { - parser_abort(context, NULL); + parser_abort(context, "--DISTRIBUTION can only be called once"); } + if ((context->rc= memcached_behavior_set_distribution_hash(context->memc, $4)) != MEMCACHED_SUCCESS) { - parser_abort(context, NULL); + parser_abort(context, "Unable to set the hash for the DISTRIBUTION requested"); } } | HASH hash { - if ((context->rc= memcached_behavior_set(context->memc, MEMCACHED_BEHAVIOR_HASH, $2)) != MEMCACHED_SUCCESS) + if (context->set_hash($2) == false) { - parser_abort(context, NULL); + parser_abort(context, "--HASH can only be set once"); } } | behavior_number NUMBER { if ((context->rc= memcached_behavior_set(context->memc, $1, $2)) != MEMCACHED_SUCCESS) { - parser_abort(context, NULL); + parser_abort(context, "Unable to set behavior"); } } | behavior_boolean { if ((context->rc= memcached_behavior_set(context->memc, $1, true)) != MEMCACHED_SUCCESS) { - parser_abort(context, NULL); + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "Could not set: %s", libmemcached_string_behavior($1)); + parser_abort(context, buffer); } } | USER_DATA diff --git a/libmemcached/include.am b/libmemcached/include.am index e09b8a67..0da3de33 100644 --- a/libmemcached/include.am +++ b/libmemcached/include.am @@ -22,6 +22,7 @@ noinst_HEADERS+= \ libmemcached/byteorder.h \ libmemcached/common.h \ libmemcached/continuum.hpp \ + libmemcached/close_socket.hpp \ libmemcached/do.hpp \ libmemcached/error.hpp \ libmemcached/initialize_query.h \ diff --git a/libmemcached/io.cc b/libmemcached/io.cc index 8642c187..85e5ed42 100644 --- a/libmemcached/io.cc +++ b/libmemcached/io.cc @@ -268,16 +268,23 @@ static memcached_return_t io_wait(memcached_server_write_instance_st ptr, { int err; socklen_t len= sizeof (err); - (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len); - memcached_set_errno(*ptr, (err == 0) ? get_socket_errno() : err, MEMCACHED_AT); + if (getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0) + { + if (err == 0) + { + continue; + } + errno= err; + } } else { memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); } + int local_errno= get_socket_errno(); // We cache in case memcached_quit_server() modifies errno memcached_quit_server(ptr, true); - return memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT); + return memcached_set_errno(*ptr, local_errno, MEMCACHED_AT); } } } diff --git a/libmemcachedprotocol/common.h b/libmemcachedprotocol/common.h index 398e7ee2..2e3e7c6d 100644 --- a/libmemcachedprotocol/common.h +++ b/libmemcachedprotocol/common.h @@ -45,6 +45,7 @@ #include #include #include +#include /* * I don't really need the following two functions as function pointers diff --git a/libtest/string.hpp b/libtest/string.hpp index 8c3d50c1..c79fef26 100644 --- a/libtest/string.hpp +++ b/libtest/string.hpp @@ -25,6 +25,7 @@ #include "util/string.hpp" #define test_literal_param util_literal_param +#define test_literal_compare_param util_literal_compare_param #define test_literal_param_size util_literal_param_size #define test_string_make_from_cstr util_string_make_from_cstr #define test_array_length util_array_length diff --git a/tests/libmemcached_world.h b/tests/libmemcached_world.h index 7936a524..09000c36 100644 --- a/tests/libmemcached_world.h +++ b/tests/libmemcached_world.h @@ -249,7 +249,17 @@ static test_return_t _runner_default(libmemcached_test_callback_fn func, libmemc { test_true(container); test_true(container->memc); - return func(container->memc); + test_return_t ret; + try { + ret= func(container->memc); + } + catch (std::exception& e) + { + Error << e.what(); + return TEST_FAILURE; + } + + return ret; } return TEST_SUCCESS; diff --git a/tests/parser.cc b/tests/parser.cc index d514c45f..92ee9547 100644 --- a/tests/parser.cc +++ b/tests/parser.cc @@ -223,7 +223,9 @@ scanner_variable_t hash_strings[]= { { ARRAY, make_scanner_string("--HASH=CRC"), scanner_string_null, NULL }, { ARRAY, make_scanner_string("--HASH=FNV1A_32"), scanner_string_null, NULL }, { ARRAY, make_scanner_string("--HASH=FNV1_32"), scanner_string_null, NULL }, +#if 0 { ARRAY, make_scanner_string("--HASH=JENKINS"), scanner_string_null, NULL }, +#endif { ARRAY, make_scanner_string("--HASH=MD5"), scanner_string_null, NULL }, { NIL, scanner_string_null, scanner_string_null, NULL} }; @@ -234,10 +236,10 @@ static test_return_t _test_option(scanner_variable_t *scanner, bool test_true_op for (scanner_variable_t *ptr= scanner; ptr->type != NIL; ptr++) { memcached_st *memc= memcached(ptr->option.c_str, ptr->option.size); - - // The case that it should have parsed, but it didn't. We will inspect - // for an error with libmemcached_check_configuration() - if (not memc and test_true_opt) + + // The case that it should have parsed, but it didn't. We will inspect for + // an error with libmemcached_check_configuration() + if (memc == NULL and test_true_opt) { char buffer[2048]; bool success= libmemcached_check_configuration(ptr->option.c_str, ptr->option.size, buffer, sizeof(buffer)); @@ -246,6 +248,7 @@ static test_return_t _test_option(scanner_variable_t *scanner, bool test_true_op temp+= " with option string:"; temp+= ptr->option.c_str; test_true_got(success, temp.c_str()); + Error << "Failed for " << temp; return TEST_FAILURE; // The line above should fail since memc should be null } @@ -333,11 +336,9 @@ test_return_t test_namespace_keyword(memcached_st*) test_return_t memcached_create_with_options_with_filename(memcached_st*) { - if (access(SUPPORT_EXAMPLE_CNF, R_OK)) - return TEST_SKIPPED; + test_skip(0, access(SUPPORT_EXAMPLE_CNF, R_OK)); - memcached_st *memc_ptr; - memc_ptr= memcached(test_literal_param("--CONFIGURE-FILE=\"support/example.cnf\"")); + memcached_st *memc_ptr= memcached(test_literal_param("--CONFIGURE-FILE=\"support/example.cnf\"")); test_true_got(memc_ptr, "memcached() failed"); memcached_free(memc_ptr); @@ -346,56 +347,58 @@ test_return_t memcached_create_with_options_with_filename(memcached_st*) test_return_t libmemcached_check_configuration_with_filename_test(memcached_st*) { - if (access(SUPPORT_EXAMPLE_CNF, R_OK)) - return TEST_SKIPPED; + test_skip(0, access(SUPPORT_EXAMPLE_CNF, R_OK)); - memcached_return_t rc; char buffer[BUFSIZ]; - rc= libmemcached_check_configuration(test_literal_param("--CONFIGURE-FILE=\"support/example.cnf\""), buffer, sizeof(buffer)); - test_true_got(rc == MEMCACHED_SUCCESS, (rc == MEMCACHED_ERRNO) ? strerror(errno) : memcached_strerror(NULL, rc)); + test_compare_hint(MEMCACHED_SUCCESS, + libmemcached_check_configuration(test_literal_param("--CONFIGURE-FILE=\"support/example.cnf\""), buffer, sizeof(buffer)), + buffer); - rc= libmemcached_check_configuration(test_literal_param("--CONFIGURE-FILE=support/example.cnf"), buffer, sizeof(buffer)); - test_false_with(rc == MEMCACHED_SUCCESS, memcached_strerror(NULL, rc)); + test_compare_hint(MEMCACHED_SUCCESS, + libmemcached_check_configuration(test_literal_param("--CONFIGURE-FILE=support/example.cnf"), buffer, sizeof(buffer)), + buffer); - rc= libmemcached_check_configuration(test_literal_param("--CONFIGURE-FILE=\"bad-path/example.cnf\""), buffer, sizeof(buffer)); - test_true_got(rc == MEMCACHED_ERRNO, memcached_strerror(NULL, rc)); + test_compare_hint(MEMCACHED_ERRNO, + libmemcached_check_configuration(test_literal_param("--CONFIGURE-FILE=\"bad-path/example.cnf\""), buffer, sizeof(buffer)), + buffer) ; return TEST_SUCCESS; } test_return_t libmemcached_check_configuration_test(memcached_st*) { - memcached_return_t rc; char buffer[BUFSIZ]; - test_compare(MEMCACHED_SUCCESS, libmemcached_check_configuration(test_literal_param("--server=localhost"), buffer, sizeof(buffer))); - rc= libmemcached_check_configuration(test_literal_param("--dude=localhost"), buffer, sizeof(buffer)); - test_false_with(rc == MEMCACHED_SUCCESS, buffer); - test_true(rc == MEMCACHED_PARSE_ERROR); + test_compare_hint(MEMCACHED_PARSE_ERROR, + libmemcached_check_configuration(test_literal_param("--dude=localhost"), buffer, sizeof(buffer)), + buffer); return TEST_SUCCESS; } test_return_t memcached_create_with_options_test(memcached_st*) { - memcached_st *memc_ptr; - memc_ptr= memcached(test_literal_param("--server=localhost")); - test_true_got(memc_ptr, memcached_last_error_message(memc_ptr)); - memcached_free(memc_ptr); + { + memcached_st *memc_ptr; + memc_ptr= memcached(test_literal_param("--server=localhost")); + test_true_got(memc_ptr, memcached_last_error_message(memc_ptr)); + memcached_free(memc_ptr); + } - memc_ptr= memcached(test_literal_param("--dude=localhost")); - test_false_with(memc_ptr, memcached_last_error_message(memc_ptr)); + { + memcached_st *memc_ptr= memcached(test_literal_param("--dude=localhost")); + test_false_with(memc_ptr, memcached_last_error_message(memc_ptr)); + } return TEST_SUCCESS; } test_return_t test_include_keyword(memcached_st*) { - if (access(SUPPORT_EXAMPLE_CNF, R_OK)) - return TEST_SKIPPED; + test_skip(0, access(SUPPORT_EXAMPLE_CNF, R_OK)); char buffer[BUFSIZ]; test_compare(MEMCACHED_SUCCESS, @@ -432,7 +435,7 @@ test_return_t test_error_keyword(memcached_st*) return TEST_SUCCESS; } -#define RANDOM_STRINGS 100 +#define RANDOM_STRINGS 1000 test_return_t random_statement_build_test(memcached_st*) { std::vector option_list; @@ -455,7 +458,26 @@ test_return_t random_statement_build_test(memcached_st*) for (scanner_variable_t *ptr= hash_strings; ptr->type != NIL; ptr++) option_list.push_back(&ptr->option); - bool seen_namespace= false; + std::vector used_list; + used_list.resize(option_list.size()); + + struct used_options_st { + bool has_hash; + bool has_namespace; + bool has_distribution; + bool has_buffer_requests; + bool has_udp; + + used_options_st() : + has_hash(false), + has_namespace(false), + has_distribution(false), + has_buffer_requests(false), + has_udp(false) + { + } + } used_options; + for (uint32_t x= 0; x < RANDOM_STRINGS; x++) { std::string random_options; @@ -463,17 +485,79 @@ test_return_t random_statement_build_test(memcached_st*) uint32_t number_of= random() % option_list.size(); for (uint32_t options= 0; options < number_of; options++) { - std::string random_string= option_list[random() % option_list.size()]->c_str; - bool is_namespace= memcmp(random_string.c_str(), test_literal_param("--NAMESPACE")) == 0; + size_t option_list_position= random() % option_list.size(); - if (is_namespace and seen_namespace) + if (used_list[option_list_position]) { continue; } + used_list[option_list_position]= true; + + std::string random_string= option_list[option_list_position]->c_str; + + if (random_string.compare(0, test_literal_compare_param("--HASH")) == 0) + { + if (used_options.has_hash) + { + continue; + } + + if (used_options.has_distribution) + { + continue; + } + used_options.has_hash= true; + } + + if (random_string.compare(0, test_literal_compare_param("--NAMESPACE")) == 0) + { + if (used_options.has_namespace) + { + continue; + } + used_options.has_namespace= true; + } - if (is_namespace) + if (random_string.compare(0, test_literal_compare_param("--USE-UDP")) == 0) { - seen_namespace= true; + if (used_options.has_udp) + { + continue; + } + used_options.has_udp= true; + + if (used_options.has_buffer_requests) + { + continue; + } + } + + if (random_string.compare(0, test_literal_compare_param("--BUFFER-REQUESTS")) == 0) + { + if (used_options.has_buffer_requests) + { + continue; + } + used_options.has_buffer_requests= true; + + if (used_options.has_udp) + { + continue; + } + } + + if (random_string.compare(0, test_literal_compare_param("--DISTRIBUTION")) == 0) + { + if (used_options.has_distribution) + { + continue; + } + + if (used_options.has_hash) + { + continue; + } + used_options.has_distribution= true; } random_options+= random_string; @@ -485,9 +569,10 @@ test_return_t random_statement_build_test(memcached_st*) continue; } - char buffer[BUFSIZ]; - memcached_return_t rc= libmemcached_check_configuration(random_options.c_str(), random_options.size() -1, buffer, sizeof(buffer)); + random_options.resize(random_options.size() -1); + char buffer[BUFSIZ]; + memcached_return_t rc= libmemcached_check_configuration(random_options.c_str(), random_options.size(), buffer, sizeof(buffer)); if (memcached_failed(rc)) { Error << "libmemcached_check_configuration(" << random_options << ") : " << buffer; diff --git a/util/string.hpp b/util/string.hpp index 58d0ce27..873551e5 100644 --- a/util/string.hpp +++ b/util/string.hpp @@ -47,6 +47,8 @@ #define util_literal_param(X) (X), (static_cast((sizeof(X) - 1))) #define util_literal_param_size(X) static_cast(sizeof(X) - 1) +#define util_literal_compare_param(X) (static_cast((sizeof(X) - 1))), (X) + #define util_string_make_from_cstr(X) (X), ((X) ? strlen(X) : 0) #define util_array_length(__array) sizeof(__array)/sizeof(&__array)