1 /** 2 * 3 * @file hoel.h 4 * @brief Hoel database abstraction library 5 * 6 * hoel.h: structures and functions declarations 7 * 8 * Copyright 2015-2019 Nicolas Mora <mail@babelouest.org> 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public License 12 * as published by the Free Software Foundation; 13 * version 2.1 of the License. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU GENERAL PUBLIC LICENSE for more details. 19 * 20 * You should have received a copy of the GNU General Public 21 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 22 * 23 */ 24 25 #ifndef __HOEL_H__ 26 #define __HOEL_H__ 27 28 #ifdef __cplusplus 29 extern "C" 30 { 31 #endif 32 33 #include "hoel-cfg.h" 34 35 #include <jansson.h> 36 37 #ifndef __USE_XOPEN 38 #define __USE_XOPEN 39 #endif 40 #include <time.h> 41 #include <pthread.h> 42 43 /** Angharad libraries **/ 44 #include <yder.h> 45 #include <orcania.h> 46 47 /** 48 * @defgroup const Constants 49 * @{ 50 */ 51 52 #define HOEL_DB_TYPE_SQLITE 0 53 #define HOEL_DB_TYPE_MARIADB 1 54 #define HOEL_DB_TYPE_PGSQL 2 55 56 #define HOEL_COL_TYPE_INT 0 57 #define HOEL_COL_TYPE_DOUBLE 1 58 #define HOEL_COL_TYPE_TEXT 2 59 #define HOEL_COL_TYPE_DATE 3 60 #define HOEL_COL_TYPE_BLOB 4 61 #define HOEL_COL_TYPE_BOOL 5 62 #define HOEL_COL_TYPE_NULL 5 63 64 #define H_OK 0 /* No error */ 65 #define H_ERROR 1 /* Generic error */ 66 #define H_ERROR_PARAMS 2 /* Error in input parameters */ 67 #define H_ERROR_CONNECTION 3 /* Error in database connection */ 68 #define H_ERROR_QUERY 4 /* Error executing query */ 69 #define H_ERROR_MEMORY 99 /* Error allocating memory */ 70 71 #define H_OPTION_NONE 0x0000 /* Nothing whatsoever */ 72 #define H_OPTION_SELECT 0x0001 /* Execute a SELECT statement */ 73 #define H_OPTION_EXEC 0x0010 /* Execute an INSERT, UPDATE or DELETE statement */ 74 75 /** 76 * @} 77 */ 78 79 /** 80 * @defgroup struct struct _h_data 81 * @{ 82 */ 83 84 /** 85 * handle container 86 */ 87 struct _h_connection { 88 int type; 89 void * connection; 90 }; 91 92 /** 93 * sql value integer type 94 */ 95 struct _h_type_int { 96 long long int value; 97 }; 98 99 /** 100 * sql value double type 101 */ 102 struct _h_type_double { 103 double value; 104 }; 105 106 /** 107 * sql value date/time type 108 */ 109 struct _h_type_datetime { 110 struct tm value; 111 }; 112 113 /** 114 * sql value string type 115 */ 116 struct _h_type_text { 117 size_t length; 118 char * value; 119 }; 120 121 /** 122 * sql value blob type 123 */ 124 struct _h_type_blob { 125 size_t length; 126 void * value; 127 }; 128 129 /** 130 * sql data container 131 */ 132 struct _h_data { 133 int type; 134 void * t_data; 135 }; 136 137 /** 138 * sql result structure 139 */ 140 struct _h_result { 141 unsigned int nb_rows; 142 unsigned int nb_columns; 143 struct _h_data ** data; 144 }; 145 146 /** 147 * @} 148 */ 149 150 /** 151 * @defgroup init Initialize and cosing connection functions 152 * @{ 153 */ 154 155 /** 156 * Close a database connection 157 * @param conn the connection to the database 158 * @return H_OK on success 159 */ 160 int h_close_db(struct _h_connection * conn); 161 162 /** 163 * @} 164 */ 165 166 /** 167 * @defgroup memory Memory management functions 168 * @{ 169 */ 170 171 /** 172 * free data allocated by hoel functions 173 * @param data the data to free 174 */ 175 void h_free(void * data); 176 177 /** 178 * @} 179 */ 180 181 /** 182 * @defgroup escape Escape string functions 183 * @{ 184 */ 185 186 /** 187 * h_escape_string 188 * Escapes a string 189 * @param conn the connection to the database 190 * @param unsafe the string to escape 191 * @return a heap-allocated string 192 * returned value must be h_free'd after use 193 */ 194 char * h_escape_string(const struct _h_connection * conn, const char * unsafe); 195 196 /** 197 * h_escape_string_with_quotes 198 * Escapes a string and returns it ready to be inserted in the query 199 * @param conn the connection to the database 200 * @param unsafe the string to escape 201 * @return a heap-allocated string 202 * returned value must be h_h_free'd after use 203 */ 204 char * h_escape_string_with_quotes(const struct _h_connection * conn, const char * unsafe); 205 206 /** 207 * @} 208 */ 209 210 /** 211 * @defgroup h_result _h_result SQL query management functions 212 * SQL query management for struct _h_result format 213 * @{ 214 */ 215 216 /** 217 * h_execute_query 218 * Execute a query, set the result structure with the returned values if available 219 * if result is NULL, the query is executed but no value will be returned 220 * @param conn the connection to the database 221 * @param query the SQL query to execute 222 * @param result the result structure to fill the result data 223 * @param options option to pass to the database engine 224 * options available 225 * H_OPTION_NONE (0): no option 226 * H_OPTION_SELECT: Execute a prepare statement (sqlite only) 227 * H_OPTION_EXEC: Execute an exec statement (sqlite only) 228 * @return H_OK on success 229 */ 230 int h_execute_query(const struct _h_connection * conn, const char * query, struct _h_result * result, int options); 231 232 /** 233 * h_query_insert 234 * Execute an insert query 235 * @param conn the connection to the database 236 * @param query the SQL query to execute 237 * @return H_OK on success 238 */ 239 int h_query_insert(const struct _h_connection * conn, const char * query); 240 241 /** 242 * h_query_last_insert_id 243 * return the id of the last inserted value 244 * @param conn the connection to the database 245 * @return a pointer to `struct _h_data *` on success, NULL otherwise. 246 */ 247 struct _h_data * h_query_last_insert_id(const struct _h_connection * conn); 248 249 /** 250 * h_query_update 251 * Execute an update query 252 * @param conn the connection to the database 253 * @param query the SQL query to execute 254 * @return H_OK on success 255 */ 256 int h_query_update(const struct _h_connection * conn, const char * query); 257 258 /** 259 * h_query_delete 260 * Execute an delete query 261 * @param conn the connection to the database 262 * @param query the SQL query to execute 263 * @return H_OK on success 264 */ 265 int h_query_delete(const struct _h_connection * conn, const char * query); 266 267 /** 268 * h_query_select 269 * Execute a select query, set the result structure with the returned values 270 * @param conn the connection to the database 271 * @param query the SQL query to execute 272 * @param result the result structure to fill the result data 273 * @return H_OK on success 274 */ 275 int h_query_select(const struct _h_connection * conn, const char * query, struct _h_result * result); 276 277 /** 278 * @} 279 */ 280 281 /** 282 * @defgroup json JSON SQL query management functions 283 * SQL query management for JSON format 284 * @{ 285 */ 286 287 /** 288 * h_execute_query_json 289 * Execute a query, set the returned values in the json result 290 * @param conn the connection to the database 291 * @param query the SQL query to execute 292 * @param j_result a json_t * reference that will be allocated and filled with the result 293 * if the query succeeds and is a SELECT query 294 * @return H_OK on success 295 */ 296 int h_execute_query_json(const struct _h_connection * conn, const char * query, json_t ** j_result); 297 298 /** 299 * h_query_select_json 300 * Execute a select query, set the returned values in the json results 301 * @param conn the connection to the database 302 * @param query the SQL query to execute 303 * @param j_result a json_t * reference that will be allocated and filled with the result if the query succeeds 304 * @return H_OK on success 305 */ 306 int h_query_select_json(const struct _h_connection * conn, const char * query, json_t ** j_result); 307 308 /** 309 * json queries 310 * The following functions run a sql query based on a json_t * object for input parameters 311 * The input parameter is called j_query 312 * If the j_query is well-formed, the query is executed and if available and specified, the result is stored into the j_result object. j_result must be decref'd after use 313 * Also, the sql query generated is stored into generated_query if specified, generated_query must be h_free'd after use 314 * The query execution result is returned by the function 315 * 316 * A j_query has the following form 317 * { 318 * "table": "table_name" // String, mandatory, the table name where the query is executed 319 * "columns": ["col1", "col2"] // Array of strings, available for h_select, optional. If not specified,will be used 320 * "order_by": "col_name [asc|desc]" // String, available for h_select, specify the order by clause, optional 321 * "limit": integer_value // Integer, available for h_select, specify the limit value, optional 322 * "offset" // Integer, available for h_select, specify the limit value, optional but available only if limit is set 323 * "values": [{ // json object or json array of json objects, available for h_insert, mandatory, specify the values to update 324 * "col1": "value1", // Generates col1='value1' for an update query 325 * "col2": value_integer, // Generates col2=value_integer for an update query 326 * "col3", "value3", // Generates col3='value3' for an update query 327 * "col4", null // Generates col4=NULL for an update query 328 * }] 329 * "set": { // json object, available for h_update, mandatory, specify the values to update 330 * "col1": "value1", // Generates col1='value1' for an update query 331 * "col2": value_integer, // Generates col2=value_integer for an update query 332 * "col3", "value3", // Generates col3='value3' for an update query 333 * "col4", null // Generates col4=NULL for an update query 334 * } 335 * "where": { // json object, available for h_select, h_update and h_delete, mandatory, specify the where clause. All clauses are separated with an AND operator 336 * "col1": "value1", // Generates col1='value1' 337 * "col2": value_integer, // Generates col2=value_integer 338 * "col3": null, // Generates col3=NULL 339 * "col4", { // Generates col4<12 340 * "operator": "<", 341 * "value": 12 342 * }, 343 * "col5", { // Generates col5 IS NOT NULL 344 * "operator": "NOT NULL" 345 * }, 346 * "col6", { // Generates col6 LIKE '%value6%' 347 * "operator": "raw", 348 * "value": "LIKE '%value6%'" 349 * } 350 * } 351 * } 352 */ 353 354 /** 355 * h_select 356 * Execute a select query 357 * Uses a json_t * parameter for the query parameters 358 * Store the result of the query in j_result if specified. j_result must be decref'd after use 359 * @param conn the connection to the database 360 * @param j_query the query encapsulated ina JSON object to execute 361 * @param j_result a json_t * reference that will be allocated and filled with the result if the query succeeds 362 * @param generated_query a char * reference that will be allocated by the library and will contain the generated SQL query, 363 * optional, must be h_free'd after use 364 * @return H_OK on success 365 */ 366 int h_select(const struct _h_connection * conn, const json_t * j_query, json_t ** j_result, char ** generated_query); 367 368 /** 369 * h_insert 370 * Execute an insert query 371 * Uses a json_t * parameter for the query parameters 372 * @param conn the connection to the database 373 * @param j_query the query encapsulated ina JSON object to execute 374 * @param generated_query a char * reference that will be allocated by the library and will contain the generated SQL query, 375 * optional, must be h_free'd after use 376 * @return H_OK on success 377 */ 378 int h_insert(const struct _h_connection * conn, const json_t * j_query, char ** generated_query); 379 380 /** 381 * h_last_insert_id 382 * return the id of the last inserted value 383 * return a pointer to `json_t *` on success, NULL otherwise. 384 * The returned value is of type JSON_INTEGER 385 * @param conn the connection to the database 386 * @return a json_t * containing the last insert id in integer format 387 */ 388 json_t * h_last_insert_id(const struct _h_connection * conn); 389 390 /** 391 * h_update 392 * Execute an update query 393 * Uses a json_t * parameter for the query parameters 394 * @param conn the connection to the database 395 * @param j_query the query encapsulated ina JSON object to execute 396 * @param generated_query a char * reference that will be allocated by the library and will contain the generated SQL query, 397 * optional, must be h_free'd after use 398 * @return H_OK on success 399 */ 400 int h_update(const struct _h_connection * conn, const json_t * j_query, char ** generated_query); 401 402 /** 403 * h_delete 404 * Execute a delete query 405 * Uses a json_t * parameter for the query parameters 406 * @param conn the connection to the database 407 * @param j_query the query encapsulated ina JSON object to execute 408 * @param generated_query a char * reference that will be allocated by the library and will contain the generated SQL query, 409 * optional, must be h_free'd after use 410 * @return H_OK on success 411 */ 412 int h_delete(const struct _h_connection * conn, const json_t * j_query, char ** generated_query); 413 414 /** 415 * @} 416 */ 417 418 /** 419 * @defgroup h_result _h_result SQL query management functions 420 * SQL query management for struct _h_result format 421 * @{ 422 */ 423 424 /** 425 * h_clean_result 426 * Free all the memory allocated by the struct _h_result 427 * @param result the result to free 428 * @return H_OK on success 429 */ 430 int h_clean_result(struct _h_result * result); 431 432 /** 433 * h_clean_data 434 * Free memory allocated by the struct _h_data 435 * @param data the data to free 436 * @return H_OK on success 437 */ 438 int h_clean_data(struct _h_data * data); 439 440 /** 441 * h_clean_data_full 442 * Free memory allocated by the struct _h_data and the struct _h_data pointer 443 * @param data the data to free 444 * @return H_OK on success 445 */ 446 int h_clean_data_full(struct _h_data * data); 447 448 /** 449 * @} 450 */ 451 452 /** 453 * @defgroup init Initialize and cosing connection functions 454 * @{ 455 */ 456 457 /** 458 * h_clean_connection 459 * free memory allocated by the struct _h_connection 460 * @param conn the connection to the database 461 * @return H_OK on success 462 */ 463 int h_clean_connection(struct _h_connection * conn); 464 465 /** 466 * h_connect_sqlite 467 * Opens a database connection to a sqlite3 db file 468 * @param db_path the path to the sqlite db file 469 * @return pointer to a struct _h_connection * on sucess, NULL on error 470 */ 471 struct _h_connection * h_connect_sqlite(const char * db_path); 472 473 /** 474 * close a sqlite3 connection 475 * @param conn the connection to the database 476 */ 477 void h_close_sqlite(struct _h_connection * conn); 478 479 /** 480 * @} 481 */ 482 483 /** 484 * @defgroup escape Escape string functions 485 * @{ 486 */ 487 488 /** 489 * escape a string 490 * returned value must be h_free'd after use 491 * This is an internal function, you should use h_escape_string instead 492 * @param conn the connection to the database 493 * @param unsafe the string to escape 494 * @return a heap-allocated string 495 */ 496 char * h_escape_string_sqlite(const struct _h_connection * conn, const char * unsafe); 497 498 /** 499 * escape a string 500 * returned value must be h_free'd after use 501 * This is an internal function, you should use h_escape_string_with_quotes instead 502 * @param conn the connection to the database 503 * @param unsafe the string to escape 504 * @return a heap-allocated string 505 */ 506 char * h_escape_string_with_quotes_sqlite(const struct _h_connection * conn, const char * unsafe); 507 508 /** 509 * @} 510 */ 511 512 /** 513 * @defgroup json JSON SQL query management functions 514 * SQL query management for JSON format 515 * @{ 516 */ 517 518 /** 519 * Return the id of the last inserted value 520 * This is an internal function, you should use h_last_insert_id instead 521 * @param conn the connection to the database 522 * @return a long long int value 523 */ 524 long long int h_last_insert_id_sqlite(const struct _h_connection * conn); 525 526 /** 527 * h_exec_query_sqlite 528 * Execute a query on a sqlite connection 529 * This is an internal function, you should use h_exec_query instead 530 * Should not be executed by the user because all parameters are supposed to be correct 531 * No result is returned, useful for single INSERT, UPDATE or DELETE statements 532 * @param conn the connection to the database 533 * @param query the SQL query to execute 534 * @return H_OK on success 535 */ 536 int h_exec_query_sqlite(const struct _h_connection * conn, const char * query); 537 538 /** 539 * h_execute_query_json_sqlite 540 * Execute a query on a sqlite connection, set the returned values in the json result 541 * This is an internal function, you should use h_execute_query_json instead 542 * Should not be executed by the user because all parameters are supposed to be correct 543 * @param conn the connection to the database 544 * @param query the SQL query to execute 545 * @param j_result a json_t * reference that will be allocated and filled with the result 546 * @return H_OK on success 547 */ 548 int h_execute_query_json_sqlite(const struct _h_connection * conn, const char * query, json_t ** j_result); 549 550 /** 551 * @} 552 */ 553 554 /** 555 * @defgroup h_result _h_result SQL query management functions 556 * SQL query management for struct _h_result format 557 * @{ 558 */ 559 560 /** 561 * h_select_query_sqlite 562 * Execute a select query on a sqlite connection, set the result structure with the returned values 563 * This is an internal function, you should use h_select_query instead 564 * Should not be executed by the user because all parameters are supposed to be correct 565 * if result is NULL, the query is executed but no value will be returned 566 * Useful for SELECT statements 567 * @param conn the connection to the database 568 * @param query the SQL query to execute 569 * @param result a _h_result that will be filled with the result 570 * return H_OK on success 571 */ 572 int h_select_query_sqlite(const struct _h_connection * conn, const char * query, struct _h_result * result); 573 574 /** 575 * @} 576 */ 577 578 /** 579 * @defgroup init Initialize and cosing connection functions 580 * @{ 581 */ 582 583 /** 584 * h_connect_mariadb 585 * Opens a database connection to a mariadb server 586 * @param host the hostname of the database server 587 * @param user the username to connect to the database 588 * @param passwd the password to connect to the database 589 * @param db the database name 590 * @param port the TCP port number for the database connection, 0 means system default 591 * @param unix_socket a UNIX socket to use for the connection, optional 592 * @return pointer to a struct _h_connection * on success, NULL on error 593 */ 594 struct _h_connection * h_connect_mariadb(const char * host, const char * user, const char * passwd, const char * db, const unsigned int port, const char * unix_socket); 595 596 /** 597 * close connection to database 598 */ 599 void h_close_mariadb(struct _h_connection * conn); 600 601 /** 602 * @} 603 */ 604 605 /** 606 * @defgroup escape Escape string functions 607 * @{ 608 */ 609 610 /** 611 * escape a string 612 * This is an internal function, you should use h_escape_string instead 613 * returned value must be h_free'd after use 614 * @param conn the connection to the database 615 * @param unsafe the string to escape 616 * @return a heap-allocated string 617 */ 618 char * h_escape_string_mariadb(const struct _h_connection * conn, const char * unsafe); 619 620 /** 621 * escape a string 622 * This is an internal function, you should use h_escape_string_with_quotes instead 623 * returned value must be h_free'd after use 624 * @param conn the connection to the database 625 * @param unsafe the string to escape 626 * @return a heap-allocated string 627 */ 628 char * h_escape_string_with_quotes_mariadb(const struct _h_connection * conn, const char * unsafe); 629 630 /** 631 * @} 632 */ 633 634 /** 635 * @defgroup json JSON SQL query management functions 636 * SQL query management for JSON format 637 * @{ 638 */ 639 640 /** 641 * Return the id of the last inserted value 642 * This is an internal function, you should use h_last_insert_id instead 643 * @param conn the connection to the database 644 * @return a long long int value 645 */ 646 long long int h_last_insert_id_mariadb(const struct _h_connection * conn); 647 648 /** 649 * Execute a query on a mariadb connection, set the returned values in the json result 650 * This is an internal function, you should use h_execute_query_json instead 651 * Should not be executed by the user because all parameters are supposed to be correct 652 * @param conn the connection to the database 653 * @param query the SQL query to execute 654 * @param j_result a json_t * reference that will be allocated and filled with the result 655 * @return H_OK on success 656 */ 657 int h_execute_query_json_mariadb(const struct _h_connection * conn, const char * query, json_t ** j_result); 658 659 /** 660 * @} 661 */ 662 663 /** 664 * @defgroup h_result _h_result SQL query management functions 665 * SQL query management for struct _h_result format 666 * @{ 667 */ 668 669 /** 670 * h_execute_query_mariadb 671 * Execute a select query on a mariadb connection, set the result structure with the returned values 672 * This is an internal function, you should use h_select_query instead 673 * Should not be executed by the user because all parameters are supposed to be correct 674 * if result is NULL, the query is executed but no value will be returned 675 * Useful for SELECT statements 676 * @param conn the connection to the database 677 * @param query the SQL query to execute 678 * @param result a _h_result that will be filled with the result 679 * @return H_OK on success 680 */ 681 int h_execute_query_mariadb(const struct _h_connection * conn, const char * query, struct _h_result * result); 682 683 /** 684 * h_get_mariadb_value 685 * convert value into a struct _h_data * depening on the m_type given 686 * This is an internal function, you should not use it 687 * returned value must be h_free'd with h_clean_data_full after use 688 * @param value the value to convert 689 * @param length the length of the value 690 * @param m_type the data type 691 * @return a _h_data * contaning the converted value 692 */ 693 struct _h_data * h_get_mariadb_value(const char * value, const unsigned long length, const int m_type); 694 695 /** 696 * @} 697 */ 698 699 /** 700 * @defgroup init Initialize and cosing connection functions 701 * @{ 702 */ 703 704 /** 705 * h_connect_pgsql 706 * Opens a database connection to a PostgreSQL server 707 * @param conninfo the connection info to connect to the pgsql database 708 * @return pointer to a struct _h_connection * on sucess, NULL on error 709 */ 710 struct _h_connection * h_connect_pgsql(const char * conninfo); 711 712 /** 713 * close a pgsql connection 714 * @param conn the connection to the database 715 */ 716 void h_close_pgsql(struct _h_connection * conn); 717 718 /** 719 * @} 720 */ 721 722 /** 723 * @defgroup escape Escape string functions 724 * @{ 725 */ 726 727 /** 728 * escape a string 729 * This is an internal function, you should use h_escape_string instead 730 * returned value must be h_free'd after use 731 * @param conn the connection to the database 732 * @param unsafe the string to escape 733 * @return a heap-allocated string 734 */ 735 char * h_escape_string_pgsql(const struct _h_connection * conn, const char * unsafe); 736 737 /** 738 * escape a string 739 * This is an internal function, you should use h_escape_string_with_quotes instead 740 * returned value must be h_free'd after use 741 * @param conn the connection to the database 742 * @param unsafe the string to escape 743 * @return a heap-allocated string 744 */ 745 char * h_escape_string_with_quotes_pgsql(const struct _h_connection * conn, const char * unsafe); 746 747 /** 748 * @} 749 */ 750 751 /** 752 * @defgroup json JSON SQL query management functions 753 * SQL query management for JSON format 754 * @{ 755 */ 756 757 /** 758 * h_execute_query_json_pgsql 759 * Execute a query on a pgsql connection, set the returned values in the json results 760 * This is an internal function, you should use h_execute_query_json instead 761 * Should not be executed by the user because all parameters are supposed to be correct 762 * @param conn the connection to the database 763 * @param query the SQL query to execute 764 * @param j_result a json_t * reference that will be allocated and filled with the result 765 * @return H_OK on success 766 */ 767 int h_execute_query_json_pgsql(const struct _h_connection * conn, const char * query, json_t ** j_result); 768 769 /** 770 * Return the id of the last inserted value 771 * This is an internal function, you should use h_last_insert_id instead 772 * @param conn the connection to the database 773 * @return a long long int value 774 */ 775 long long int h_last_insert_id_pgsql(const struct _h_connection * conn); 776 777 /** 778 * @} 779 */ 780 781 /** 782 * @defgroup h_result _h_result SQL query management functions 783 * SQL query management for struct _h_result format 784 * @{ 785 */ 786 787 /** 788 * h_execute_query_pgsql 789 * Execute a select query on a pgsql connection, set the result structure with the returned values 790 * This is an internal function, you should use h_select_query instead 791 * Should not be executed by the user because all parameters are supposed to be correct 792 * if result is NULL, the query is executed but no value will be returned 793 * Useful for SELECT statements 794 * @param conn the connection to the database 795 * @param query the SQL query to execute 796 * @param result a _h_result that will be filled with the result 797 * return H_OK on success 798 */ 799 int h_execute_query_pgsql(const struct _h_connection * conn, const char * query, struct _h_result * result); 800 801 /** 802 * @} 803 */ 804 805 #ifdef __cplusplus 806 } 807 #endif 808 809 #endif /* __HOEL_H__ */ 810